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 #define COM_NO_WINDOWS_H
34 //#include "wine/port.h"
36 //#include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
58 //#include "winerror.h"
60 #define NO_SHLWAPI_STREAM
61 #define NO_SHLWAPI_REG
62 #define NO_SHLWAPI_STRFCNS
63 #define NO_SHLWAPI_GDI
66 //#include "wincrypt.h"
68 #include <cryptuiapi.h>
70 #if defined(__MINGW32__) || defined (_MSC_VER)
75 #include <wine/debug.h>
76 #include <wine/exception.h>
77 //#include "wine/unicode.h"
80 #include "inet_ntop.c"
82 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
84 static const WCHAR g_szHttp1_0
[] = {'H','T','T','P','/','1','.','0',0};
85 static const WCHAR g_szHttp1_1
[] = {'H','T','T','P','/','1','.','1',0};
86 static const WCHAR szOK
[] = {'O','K',0};
87 static const WCHAR szDefaultHeader
[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
88 static const WCHAR hostW
[] = { 'H','o','s','t',0 };
89 static const WCHAR szAuthorization
[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
90 static const WCHAR szProxy_Authorization
[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
91 static const WCHAR szStatus
[] = { 'S','t','a','t','u','s',0 };
92 static const WCHAR szKeepAlive
[] = {'K','e','e','p','-','A','l','i','v','e',0};
93 static const WCHAR szGET
[] = { 'G','E','T', 0 };
94 static const WCHAR szHEAD
[] = { 'H','E','A','D', 0 };
95 static const WCHAR szCrLf
[] = {'\r','\n', 0};
97 static const WCHAR szAccept
[] = { 'A','c','c','e','p','t',0 };
98 static const WCHAR szAccept_Charset
[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
99 static const WCHAR szAccept_Encoding
[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
100 static const WCHAR szAccept_Language
[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
101 static const WCHAR szAccept_Ranges
[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
102 static const WCHAR szAge
[] = { 'A','g','e',0 };
103 static const WCHAR szAllow
[] = { 'A','l','l','o','w',0 };
104 static const WCHAR szCache_Control
[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
105 static const WCHAR szConnection
[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
106 static const WCHAR szContent_Base
[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
107 static const WCHAR szContent_Encoding
[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
108 static const WCHAR szContent_ID
[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
109 static const WCHAR szContent_Language
[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
110 static const WCHAR szContent_Length
[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
111 static const WCHAR szContent_Location
[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
112 static const WCHAR szContent_MD5
[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
113 static const WCHAR szContent_Range
[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
114 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 };
115 static const WCHAR szContent_Type
[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
116 static const WCHAR szCookie
[] = { 'C','o','o','k','i','e',0 };
117 static const WCHAR szDate
[] = { 'D','a','t','e',0 };
118 static const WCHAR szFrom
[] = { 'F','r','o','m',0 };
119 static const WCHAR szETag
[] = { 'E','T','a','g',0 };
120 static const WCHAR szExpect
[] = { 'E','x','p','e','c','t',0 };
121 static const WCHAR szExpires
[] = { 'E','x','p','i','r','e','s',0 };
122 static const WCHAR szIf_Match
[] = { 'I','f','-','M','a','t','c','h',0 };
123 static const WCHAR szIf_Modified_Since
[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
124 static const WCHAR szIf_None_Match
[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
125 static const WCHAR szIf_Range
[] = { 'I','f','-','R','a','n','g','e',0 };
126 static const WCHAR szIf_Unmodified_Since
[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
127 static const WCHAR szLast_Modified
[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
128 static const WCHAR szLocation
[] = { 'L','o','c','a','t','i','o','n',0 };
129 static const WCHAR szMax_Forwards
[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
130 static const WCHAR szMime_Version
[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
131 static const WCHAR szPragma
[] = { 'P','r','a','g','m','a',0 };
132 static const WCHAR szProxy_Authenticate
[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
133 static const WCHAR szProxy_Connection
[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
134 static const WCHAR szPublic
[] = { 'P','u','b','l','i','c',0 };
135 static const WCHAR szRange
[] = { 'R','a','n','g','e',0 };
136 static const WCHAR szReferer
[] = { 'R','e','f','e','r','e','r',0 };
137 static const WCHAR szRetry_After
[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
138 static const WCHAR szServer
[] = { 'S','e','r','v','e','r',0 };
139 static const WCHAR szSet_Cookie
[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
140 static const WCHAR szTransfer_Encoding
[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
141 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 };
142 static const WCHAR szUpgrade
[] = { 'U','p','g','r','a','d','e',0 };
143 static const WCHAR szURI
[] = { 'U','R','I',0 };
144 static const WCHAR szUser_Agent
[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
145 static const WCHAR szVary
[] = { 'V','a','r','y',0 };
146 static const WCHAR szVia
[] = { 'V','i','a',0 };
147 static const WCHAR szWarning
[] = { 'W','a','r','n','i','n','g',0 };
148 static const WCHAR szWWW_Authenticate
[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
150 #define HTTP_REFERER szReferer
151 #define HTTP_ACCEPT szAccept
152 #define HTTP_USERAGENT szUser_Agent
154 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
155 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
156 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
157 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
158 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
159 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
160 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
162 #define COLLECT_TIME 60000
164 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
175 unsigned int auth_data_len
;
176 BOOL finished
; /* finished authenticating */
180 typedef struct _basicAuthorizationData
187 UINT authorizationLen
;
188 } basicAuthorizationData
;
190 typedef struct _authorizationData
204 static struct list basicAuthorizationCache
= LIST_INIT(basicAuthorizationCache
);
205 static struct list authorizationCache
= LIST_INIT(authorizationCache
);
207 static CRITICAL_SECTION authcache_cs
;
208 static CRITICAL_SECTION_DEBUG critsect_debug
=
211 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
212 0, 0, { (DWORD_PTR
)(__FILE__
": authcache_cs") }
214 static CRITICAL_SECTION authcache_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
216 static BOOL
HTTP_GetResponseHeaders(http_request_t
*req
, BOOL clear
);
217 static DWORD
HTTP_ProcessHeader(http_request_t
*req
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
);
218 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
);
219 static DWORD
HTTP_InsertCustomHeader(http_request_t
*req
, LPHTTPHEADERW lpHdr
);
220 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*req
, LPCWSTR lpszField
, INT index
, BOOL Request
);
221 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*req
, DWORD index
);
222 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
);
223 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*, DWORD
, LPVOID
, LPDWORD
, LPDWORD
);
224 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*req
, LPCWSTR lpszUrl
);
225 static UINT
HTTP_DecodeBase64(LPCWSTR base64
, LPSTR bin
);
226 static BOOL
HTTP_VerifyValidHeader(http_request_t
*req
, LPCWSTR field
);
228 static CRITICAL_SECTION connection_pool_cs
;
229 static CRITICAL_SECTION_DEBUG connection_pool_debug
=
231 0, 0, &connection_pool_cs
,
232 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
233 0, 0, { (DWORD_PTR
)(__FILE__
": connection_pool_cs") }
235 static CRITICAL_SECTION connection_pool_cs
= { &connection_pool_debug
, -1, 0, 0, 0, 0 };
237 static struct list connection_pool
= LIST_INIT(connection_pool
);
238 static BOOL collector_running
;
240 void server_addref(server_t
*server
)
242 InterlockedIncrement(&server
->ref
);
245 void server_release(server_t
*server
)
247 if(InterlockedDecrement(&server
->ref
))
252 server
->keep_until
= (DWORD64
)GetTickCount() + COLLECT_TIME
;
254 EnterCriticalSection(&connection_pool_cs
);
255 list_remove(&server
->entry
);
256 LeaveCriticalSection(&connection_pool_cs
);
258 heap_free(server
->name
);
263 static server_t
*get_server(const WCHAR
*name
, INTERNET_PORT port
)
265 server_t
*iter
, *server
= NULL
;
267 EnterCriticalSection(&connection_pool_cs
);
269 LIST_FOR_EACH_ENTRY(iter
, &connection_pool
, server_t
, entry
) {
270 if(iter
->port
== port
&& !strcmpW(iter
->name
, name
)) {
272 server_addref(server
);
278 server
= heap_alloc(sizeof(*server
));
280 server
->addr_len
= 0;
283 list_init(&server
->conn_pool
);
284 server
->name
= heap_strdupW(name
);
286 list_add_head(&connection_pool
, &server
->entry
);
294 LeaveCriticalSection(&connection_pool_cs
);
299 BOOL
collect_connections(BOOL collect_all
)
301 netconn_t
*netconn
, *netconn_safe
;
302 server_t
*server
, *server_safe
;
303 BOOL remaining
= FALSE
;
306 now
= GetTickCount();
308 LIST_FOR_EACH_ENTRY_SAFE(server
, server_safe
, &connection_pool
, server_t
, entry
) {
309 LIST_FOR_EACH_ENTRY_SAFE(netconn
, netconn_safe
, &server
->conn_pool
, netconn_t
, pool_entry
) {
310 if(collect_all
|| netconn
->keep_until
< now
) {
311 TRACE("freeing %p\n", netconn
);
312 list_remove(&netconn
->pool_entry
);
313 free_netconn(netconn
);
320 if(collect_all
|| server
->keep_until
< now
) {
321 list_remove(&server
->entry
);
323 heap_free(server
->name
);
334 static DWORD WINAPI
collect_connections_proc(void *arg
)
336 BOOL remaining_conns
;
339 /* FIXME: Use more sophisticated method */
342 EnterCriticalSection(&connection_pool_cs
);
344 remaining_conns
= collect_connections(FALSE
);
346 collector_running
= FALSE
;
348 LeaveCriticalSection(&connection_pool_cs
);
349 }while(remaining_conns
);
351 FreeLibraryAndExitThread(WININET_hModule
, 0);
354 static LPHTTPHEADERW
HTTP_GetHeader(http_request_t
*req
, LPCWSTR head
)
357 HeaderIndex
= HTTP_GetCustomHeaderIndex(req
, head
, 0, TRUE
);
358 if (HeaderIndex
== -1)
361 return &req
->custHeaders
[HeaderIndex
];
370 struct data_stream_vtbl_t
{
371 DWORD (*get_avail_data
)(data_stream_t
*,http_request_t
*);
372 BOOL (*end_of_data
)(data_stream_t
*,http_request_t
*);
373 DWORD (*read
)(data_stream_t
*,http_request_t
*,BYTE
*,DWORD
,DWORD
*,read_mode_t
);
374 BOOL (*drain_content
)(data_stream_t
*,http_request_t
*);
375 void (*destroy
)(data_stream_t
*);
379 data_stream_t data_stream
;
381 BYTE buf
[READ_BUFFER_SIZE
];
387 static inline void destroy_data_stream(data_stream_t
*stream
)
389 stream
->vtbl
->destroy(stream
);
392 static void reset_data_stream(http_request_t
*req
)
394 destroy_data_stream(req
->data_stream
);
395 req
->data_stream
= &req
->netconn_stream
.data_stream
;
396 req
->read_pos
= req
->read_size
= req
->netconn_stream
.content_read
= 0;
397 req
->read_chunked
= req
->read_gzip
= FALSE
;
403 data_stream_t stream
;
404 data_stream_t
*parent_stream
;
406 BYTE buf
[READ_BUFFER_SIZE
];
412 static DWORD
gzip_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
414 /* Allow reading only from read buffer */
418 static BOOL
gzip_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
420 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
421 return gzip_stream
->end_of_data
;
424 static DWORD
gzip_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
425 DWORD
*read
, read_mode_t read_mode
)
427 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
428 z_stream
*zstream
= &gzip_stream
->zstream
;
429 DWORD current_read
, ret_read
= 0;
432 DWORD res
= ERROR_SUCCESS
;
434 while(size
&& !gzip_stream
->end_of_data
) {
435 end
= gzip_stream
->parent_stream
->vtbl
->end_of_data(gzip_stream
->parent_stream
, req
);
437 if(gzip_stream
->buf_size
<= 64 && !end
) {
438 if(gzip_stream
->buf_pos
) {
439 if(gzip_stream
->buf_size
)
440 memmove(gzip_stream
->buf
, gzip_stream
->buf
+gzip_stream
->buf_pos
, gzip_stream
->buf_size
);
441 gzip_stream
->buf_pos
= 0;
443 res
= gzip_stream
->parent_stream
->vtbl
->read(gzip_stream
->parent_stream
, req
, gzip_stream
->buf
+gzip_stream
->buf_size
,
444 sizeof(gzip_stream
->buf
)-gzip_stream
->buf_size
, ¤t_read
, read_mode
);
445 gzip_stream
->buf_size
+= current_read
;
446 if(res
!= ERROR_SUCCESS
)
448 end
= gzip_stream
->parent_stream
->vtbl
->end_of_data(gzip_stream
->parent_stream
, req
);
449 if(!current_read
&& !end
) {
450 if(read_mode
!= READMODE_NOBLOCK
) {
451 WARN("unexpected end of data\n");
452 gzip_stream
->end_of_data
= TRUE
;
456 if(gzip_stream
->buf_size
<= 64 && !end
)
460 zstream
->next_in
= gzip_stream
->buf
+gzip_stream
->buf_pos
;
461 zstream
->avail_in
= gzip_stream
->buf_size
-(end
? 0 : 64);
462 zstream
->next_out
= buf
+ret_read
;
463 zstream
->avail_out
= size
;
464 zres
= inflate(&gzip_stream
->zstream
, 0);
465 current_read
= size
- zstream
->avail_out
;
466 size
-= current_read
;
467 ret_read
+= current_read
;
468 gzip_stream
->buf_size
-= zstream
->next_in
- (gzip_stream
->buf
+gzip_stream
->buf_pos
);
469 gzip_stream
->buf_pos
= zstream
->next_in
-gzip_stream
->buf
;
470 if(zres
== Z_STREAM_END
) {
471 TRACE("end of data\n");
472 gzip_stream
->end_of_data
= TRUE
;
474 }else if(zres
!= Z_OK
) {
475 WARN("inflate failed %d: %s\n", zres
, debugstr_a(zstream
->msg
));
477 res
= ERROR_INTERNET_DECODING_FAILED
;
481 if(ret_read
&& read_mode
== READMODE_ASYNC
)
482 read_mode
= READMODE_NOBLOCK
;
485 TRACE("read %u bytes\n", ret_read
);
490 static BOOL
gzip_drain_content(data_stream_t
*stream
, http_request_t
*req
)
492 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
493 return gzip_stream
->parent_stream
->vtbl
->drain_content(gzip_stream
->parent_stream
, req
);
496 static void gzip_destroy(data_stream_t
*stream
)
498 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
500 destroy_data_stream(gzip_stream
->parent_stream
);
502 if(!gzip_stream
->end_of_data
)
503 inflateEnd(&gzip_stream
->zstream
);
504 heap_free(gzip_stream
);
507 static const data_stream_vtbl_t gzip_stream_vtbl
= {
515 static voidpf
wininet_zalloc(voidpf opaque
, uInt items
, uInt size
)
517 return heap_alloc(items
*size
);
520 static void wininet_zfree(voidpf opaque
, voidpf address
)
525 static DWORD
init_gzip_stream(http_request_t
*req
)
527 gzip_stream_t
*gzip_stream
;
530 gzip_stream
= heap_alloc_zero(sizeof(gzip_stream_t
));
532 return ERROR_OUTOFMEMORY
;
534 gzip_stream
->stream
.vtbl
= &gzip_stream_vtbl
;
535 gzip_stream
->zstream
.zalloc
= wininet_zalloc
;
536 gzip_stream
->zstream
.zfree
= wininet_zfree
;
538 zres
= inflateInit2(&gzip_stream
->zstream
, 0x1f);
540 ERR("inflateInit failed: %d\n", zres
);
541 heap_free(gzip_stream
);
542 return ERROR_OUTOFMEMORY
;
545 index
= HTTP_GetCustomHeaderIndex(req
, szContent_Length
, 0, FALSE
);
547 HTTP_DeleteCustomHeader(req
, index
);
550 memcpy(gzip_stream
->buf
, req
->read_buf
+req
->read_pos
, req
->read_size
);
551 gzip_stream
->buf_size
= req
->read_size
;
552 req
->read_pos
= req
->read_size
= 0;
555 req
->read_gzip
= TRUE
;
556 gzip_stream
->parent_stream
= req
->data_stream
;
557 req
->data_stream
= &gzip_stream
->stream
;
558 return ERROR_SUCCESS
;
563 static DWORD
init_gzip_stream(http_request_t
*req
)
565 ERR("gzip stream not supported, missing zlib.\n");
566 return ERROR_SUCCESS
;
571 /***********************************************************************
572 * HTTP_Tokenize (internal)
574 * Tokenize a string, allocating memory for the tokens.
576 static LPWSTR
* HTTP_Tokenize(LPCWSTR string
, LPCWSTR token_string
)
578 LPWSTR
* token_array
;
585 /* empty string has no tokens */
589 for (i
= 0; string
[i
]; i
++)
591 if (!strncmpW(string
+i
, token_string
, strlenW(token_string
)))
595 /* we want to skip over separators, but not the null terminator */
596 for (j
= 0; j
< strlenW(token_string
) - 1; j
++)
604 /* add 1 for terminating NULL */
605 token_array
= heap_alloc((tokens
+1) * sizeof(*token_array
));
606 token_array
[tokens
] = NULL
;
609 for (i
= 0; i
< tokens
; i
++)
612 next_token
= strstrW(string
, token_string
);
613 if (!next_token
) next_token
= string
+strlenW(string
);
614 len
= next_token
- string
;
615 token_array
[i
] = heap_alloc((len
+1)*sizeof(WCHAR
));
616 memcpy(token_array
[i
], string
, len
*sizeof(WCHAR
));
617 token_array
[i
][len
] = '\0';
618 string
= next_token
+strlenW(token_string
);
623 /***********************************************************************
624 * HTTP_FreeTokens (internal)
626 * Frees memory returned from HTTP_Tokenize.
628 static void HTTP_FreeTokens(LPWSTR
* token_array
)
631 for (i
= 0; token_array
[i
]; i
++) heap_free(token_array
[i
]);
632 heap_free(token_array
);
635 static void HTTP_FixURL(http_request_t
*request
)
637 static const WCHAR szSlash
[] = { '/',0 };
638 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/', 0 };
640 /* If we don't have a path we set it to root */
641 if (NULL
== request
->path
)
642 request
->path
= heap_strdupW(szSlash
);
643 else /* remove \r and \n*/
645 int nLen
= strlenW(request
->path
);
646 while ((nLen
>0 ) && ((request
->path
[nLen
-1] == '\r')||(request
->path
[nLen
-1] == '\n')))
649 request
->path
[nLen
]='\0';
651 /* Replace '\' with '/' */
654 if (request
->path
[nLen
] == '\\') request
->path
[nLen
]='/';
658 if(CSTR_EQUAL
!= CompareStringW( LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
659 request
->path
, strlenW(request
->path
), szHttp
, strlenW(szHttp
) )
660 && request
->path
[0] != '/') /* not an absolute path ?? --> fix it !! */
662 WCHAR
*fixurl
= heap_alloc((strlenW(request
->path
) + 2)*sizeof(WCHAR
));
664 strcpyW(fixurl
+ 1, request
->path
);
665 heap_free( request
->path
);
666 request
->path
= fixurl
;
670 static LPWSTR
HTTP_BuildHeaderRequestString( http_request_t
*request
, LPCWSTR verb
, LPCWSTR path
, LPCWSTR version
)
672 LPWSTR requestString
;
678 static const WCHAR szSpace
[] = { ' ',0 };
679 static const WCHAR szColon
[] = { ':',' ',0 };
680 static const WCHAR sztwocrlf
[] = {'\r','\n','\r','\n', 0};
682 /* allocate space for an array of all the string pointers to be added */
683 len
= (request
->nCustHeaders
)*4 + 10;
684 req
= heap_alloc(len
*sizeof(LPCWSTR
));
686 /* add the verb, path and HTTP version string */
694 /* Append custom request headers */
695 for (i
= 0; i
< request
->nCustHeaders
; i
++)
697 if (request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
700 req
[n
++] = request
->custHeaders
[i
].lpszField
;
702 req
[n
++] = request
->custHeaders
[i
].lpszValue
;
704 TRACE("Adding custom header %s (%s)\n",
705 debugstr_w(request
->custHeaders
[i
].lpszField
),
706 debugstr_w(request
->custHeaders
[i
].lpszValue
));
711 ERR("oops. buffer overrun\n");
714 requestString
= HTTP_build_req( req
, 4 );
718 * Set (header) termination string for request
719 * Make sure there's exactly two new lines at the end of the request
721 p
= &requestString
[strlenW(requestString
)-1];
722 while ( (*p
== '\n') || (*p
== '\r') )
724 strcpyW( p
+1, sztwocrlf
);
726 return requestString
;
729 static void HTTP_ProcessCookies( http_request_t
*request
)
733 LPHTTPHEADERW setCookieHeader
;
735 if(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
)
738 while((HeaderIndex
= HTTP_GetCustomHeaderIndex(request
, szSet_Cookie
, numCookies
++, FALSE
)) != -1)
744 setCookieHeader
= &request
->custHeaders
[HeaderIndex
];
746 if (!setCookieHeader
->lpszValue
)
749 host
= HTTP_GetHeader(request
, hostW
);
753 data
= strchrW(setCookieHeader
->lpszValue
, '=');
757 name
= heap_strndupW(setCookieHeader
->lpszValue
, data
-setCookieHeader
->lpszValue
);
762 set_cookie(host
->lpszValue
, request
->path
, name
, data
);
767 static void strip_spaces(LPWSTR start
)
772 while (*str
== ' ' && *str
!= '\0')
776 memmove(start
, str
, sizeof(WCHAR
) * (strlenW(str
) + 1));
778 end
= start
+ strlenW(start
) - 1;
779 while (end
>= start
&& *end
== ' ')
786 static inline BOOL
is_basic_auth_value( LPCWSTR pszAuthValue
, LPWSTR
*pszRealm
)
788 static const WCHAR szBasic
[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
789 static const WCHAR szRealm
[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
791 is_basic
= !strncmpiW(pszAuthValue
, szBasic
, ARRAYSIZE(szBasic
)) &&
792 ((pszAuthValue
[ARRAYSIZE(szBasic
)] == ' ') || !pszAuthValue
[ARRAYSIZE(szBasic
)]);
793 if (is_basic
&& pszRealm
)
796 LPCWSTR ptr
= &pszAuthValue
[ARRAYSIZE(szBasic
)];
800 token
= strchrW(ptr
,'=');
804 while (*realm
== ' ' && *realm
!= '\0')
806 if(!strncmpiW(realm
, szRealm
, ARRAYSIZE(szRealm
)) &&
807 (realm
[ARRAYSIZE(szRealm
)] == ' ' || realm
[ARRAYSIZE(szRealm
)] == '='))
810 while (*token
== ' ' && *token
!= '\0')
814 *pszRealm
= heap_strdupW(token
);
815 strip_spaces(*pszRealm
);
822 static void destroy_authinfo( struct HttpAuthInfo
*authinfo
)
824 if (!authinfo
) return;
826 if (SecIsValidHandle(&authinfo
->ctx
))
827 DeleteSecurityContext(&authinfo
->ctx
);
828 if (SecIsValidHandle(&authinfo
->cred
))
829 FreeCredentialsHandle(&authinfo
->cred
);
831 heap_free(authinfo
->auth_data
);
832 heap_free(authinfo
->scheme
);
836 static UINT
retrieve_cached_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR
*auth_data
)
838 basicAuthorizationData
*ad
;
841 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host
),debugstr_w(realm
));
843 EnterCriticalSection(&authcache_cs
);
844 LIST_FOR_EACH_ENTRY(ad
, &basicAuthorizationCache
, basicAuthorizationData
, entry
)
846 if (!strcmpiW(host
,ad
->host
) && !strcmpW(realm
,ad
->realm
))
848 TRACE("Authorization found in cache\n");
849 *auth_data
= heap_alloc(ad
->authorizationLen
);
850 memcpy(*auth_data
,ad
->authorization
,ad
->authorizationLen
);
851 rc
= ad
->authorizationLen
;
855 LeaveCriticalSection(&authcache_cs
);
859 static void cache_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR auth_data
, UINT auth_data_len
)
862 basicAuthorizationData
* ad
= NULL
;
864 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host
),debugstr_w(realm
),debugstr_an(auth_data
,auth_data_len
));
866 EnterCriticalSection(&authcache_cs
);
867 LIST_FOR_EACH(cursor
, &basicAuthorizationCache
)
869 basicAuthorizationData
*check
= LIST_ENTRY(cursor
,basicAuthorizationData
,entry
);
870 if (!strcmpiW(host
,check
->host
) && !strcmpW(realm
,check
->realm
))
879 TRACE("Found match in cache, replacing\n");
880 heap_free(ad
->authorization
);
881 ad
->authorization
= heap_alloc(auth_data_len
);
882 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
883 ad
->authorizationLen
= auth_data_len
;
887 ad
= heap_alloc(sizeof(basicAuthorizationData
));
888 ad
->host
= heap_strdupW(host
);
889 ad
->realm
= heap_strdupW(realm
);
890 ad
->authorization
= heap_alloc(auth_data_len
);
891 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
892 ad
->authorizationLen
= auth_data_len
;
893 list_add_head(&basicAuthorizationCache
,&ad
->entry
);
894 TRACE("authorization cached\n");
896 LeaveCriticalSection(&authcache_cs
);
899 static BOOL
retrieve_cached_authorization(LPWSTR host
, LPWSTR scheme
,
900 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
902 authorizationData
*ad
;
904 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
906 EnterCriticalSection(&authcache_cs
);
907 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
) {
908 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
909 TRACE("Authorization found in cache\n");
911 nt_auth_identity
->User
= heap_strdupW(ad
->user
);
912 nt_auth_identity
->Password
= heap_strdupW(ad
->password
);
913 nt_auth_identity
->Domain
= heap_alloc(sizeof(WCHAR
)*ad
->domain_len
);
914 if(!nt_auth_identity
->User
|| !nt_auth_identity
->Password
||
915 (!nt_auth_identity
->Domain
&& ad
->domain_len
)) {
916 heap_free(nt_auth_identity
->User
);
917 heap_free(nt_auth_identity
->Password
);
918 heap_free(nt_auth_identity
->Domain
);
922 nt_auth_identity
->Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
923 nt_auth_identity
->UserLength
= ad
->user_len
;
924 nt_auth_identity
->PasswordLength
= ad
->password_len
;
925 memcpy(nt_auth_identity
->Domain
, ad
->domain
, sizeof(WCHAR
)*ad
->domain_len
);
926 nt_auth_identity
->DomainLength
= ad
->domain_len
;
927 LeaveCriticalSection(&authcache_cs
);
931 LeaveCriticalSection(&authcache_cs
);
936 static void cache_authorization(LPWSTR host
, LPWSTR scheme
,
937 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
939 authorizationData
*ad
;
942 TRACE("Caching authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
944 EnterCriticalSection(&authcache_cs
);
945 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
)
946 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
953 heap_free(ad
->password
);
954 heap_free(ad
->domain
);
956 ad
= heap_alloc(sizeof(authorizationData
));
958 LeaveCriticalSection(&authcache_cs
);
962 ad
->host
= heap_strdupW(host
);
963 ad
->scheme
= heap_strdupW(scheme
);
964 list_add_head(&authorizationCache
, &ad
->entry
);
967 ad
->user
= heap_strndupW(nt_auth_identity
->User
, nt_auth_identity
->UserLength
);
968 ad
->password
= heap_strndupW(nt_auth_identity
->Password
, nt_auth_identity
->PasswordLength
);
969 ad
->domain
= heap_strndupW(nt_auth_identity
->Domain
, nt_auth_identity
->DomainLength
);
970 ad
->user_len
= nt_auth_identity
->UserLength
;
971 ad
->password_len
= nt_auth_identity
->PasswordLength
;
972 ad
->domain_len
= nt_auth_identity
->DomainLength
;
974 if(!ad
->host
|| !ad
->scheme
|| !ad
->user
|| !ad
->password
975 || (nt_auth_identity
->Domain
&& !ad
->domain
)) {
977 heap_free(ad
->scheme
);
979 heap_free(ad
->password
);
980 heap_free(ad
->domain
);
981 list_remove(&ad
->entry
);
985 LeaveCriticalSection(&authcache_cs
);
988 static BOOL
HTTP_DoAuthorization( http_request_t
*request
, LPCWSTR pszAuthValue
,
989 struct HttpAuthInfo
**ppAuthInfo
,
990 LPWSTR domain_and_username
, LPWSTR password
,
993 SECURITY_STATUS sec_status
;
994 struct HttpAuthInfo
*pAuthInfo
= *ppAuthInfo
;
996 LPWSTR szRealm
= NULL
;
998 TRACE("%s\n", debugstr_w(pszAuthValue
));
1005 pAuthInfo
= heap_alloc(sizeof(*pAuthInfo
));
1009 SecInvalidateHandle(&pAuthInfo
->cred
);
1010 SecInvalidateHandle(&pAuthInfo
->ctx
);
1011 memset(&pAuthInfo
->exp
, 0, sizeof(pAuthInfo
->exp
));
1012 pAuthInfo
->attr
= 0;
1013 pAuthInfo
->auth_data
= NULL
;
1014 pAuthInfo
->auth_data_len
= 0;
1015 pAuthInfo
->finished
= FALSE
;
1017 if (is_basic_auth_value(pszAuthValue
,NULL
))
1019 static const WCHAR szBasic
[] = {'B','a','s','i','c',0};
1020 pAuthInfo
->scheme
= heap_strdupW(szBasic
);
1021 if (!pAuthInfo
->scheme
)
1023 heap_free(pAuthInfo
);
1030 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity
;
1032 pAuthInfo
->scheme
= heap_strdupW(pszAuthValue
);
1033 if (!pAuthInfo
->scheme
)
1035 heap_free(pAuthInfo
);
1039 if (domain_and_username
)
1041 WCHAR
*user
= strchrW(domain_and_username
, '\\');
1042 WCHAR
*domain
= domain_and_username
;
1044 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1046 pAuthData
= &nt_auth_identity
;
1051 user
= domain_and_username
;
1055 nt_auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
1056 nt_auth_identity
.User
= user
;
1057 nt_auth_identity
.UserLength
= strlenW(nt_auth_identity
.User
);
1058 nt_auth_identity
.Domain
= domain
;
1059 nt_auth_identity
.DomainLength
= domain
? user
- domain
- 1 : 0;
1060 nt_auth_identity
.Password
= password
;
1061 nt_auth_identity
.PasswordLength
= strlenW(nt_auth_identity
.Password
);
1063 cache_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
);
1065 else if(retrieve_cached_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
))
1066 pAuthData
= &nt_auth_identity
;
1068 /* use default credentials */
1071 sec_status
= AcquireCredentialsHandleW(NULL
, pAuthInfo
->scheme
,
1072 SECPKG_CRED_OUTBOUND
, NULL
,
1074 NULL
, &pAuthInfo
->cred
,
1077 if(pAuthData
&& !domain_and_username
) {
1078 heap_free(nt_auth_identity
.User
);
1079 heap_free(nt_auth_identity
.Domain
);
1080 heap_free(nt_auth_identity
.Password
);
1083 if (sec_status
== SEC_E_OK
)
1085 PSecPkgInfoW sec_pkg_info
;
1086 sec_status
= QuerySecurityPackageInfoW(pAuthInfo
->scheme
, &sec_pkg_info
);
1087 if (sec_status
== SEC_E_OK
)
1089 pAuthInfo
->max_token
= sec_pkg_info
->cbMaxToken
;
1090 FreeContextBuffer(sec_pkg_info
);
1093 if (sec_status
!= SEC_E_OK
)
1095 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1096 debugstr_w(pAuthInfo
->scheme
), sec_status
);
1097 heap_free(pAuthInfo
->scheme
);
1098 heap_free(pAuthInfo
);
1102 *ppAuthInfo
= pAuthInfo
;
1104 else if (pAuthInfo
->finished
)
1107 if ((strlenW(pszAuthValue
) < strlenW(pAuthInfo
->scheme
)) ||
1108 strncmpiW(pszAuthValue
, pAuthInfo
->scheme
, strlenW(pAuthInfo
->scheme
)))
1110 ERR("authentication scheme changed from %s to %s\n",
1111 debugstr_w(pAuthInfo
->scheme
), debugstr_w(pszAuthValue
));
1115 if (is_basic_auth_value(pszAuthValue
,&szRealm
))
1119 char *auth_data
= NULL
;
1120 UINT auth_data_len
= 0;
1122 TRACE("basic authentication realm %s\n",debugstr_w(szRealm
));
1124 if (!domain_and_username
)
1126 if (host
&& szRealm
)
1127 auth_data_len
= retrieve_cached_basic_authorization(host
, szRealm
,&auth_data
);
1128 if (auth_data_len
== 0)
1136 userlen
= WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, lstrlenW(domain_and_username
), NULL
, 0, NULL
, NULL
);
1137 passlen
= WideCharToMultiByte(CP_UTF8
, 0, password
, lstrlenW(password
), NULL
, 0, NULL
, NULL
);
1139 /* length includes a nul terminator, which will be re-used for the ':' */
1140 auth_data
= heap_alloc(userlen
+ 1 + passlen
);
1147 WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, -1, auth_data
, userlen
, NULL
, NULL
);
1148 auth_data
[userlen
] = ':';
1149 WideCharToMultiByte(CP_UTF8
, 0, password
, -1, &auth_data
[userlen
+1], passlen
, NULL
, NULL
);
1150 auth_data_len
= userlen
+ 1 + passlen
;
1151 if (host
&& szRealm
)
1152 cache_basic_authorization(host
, szRealm
, auth_data
, auth_data_len
);
1155 pAuthInfo
->auth_data
= auth_data
;
1156 pAuthInfo
->auth_data_len
= auth_data_len
;
1157 pAuthInfo
->finished
= TRUE
;
1163 LPCWSTR pszAuthData
;
1164 SecBufferDesc out_desc
, in_desc
;
1166 unsigned char *buffer
;
1167 ULONG context_req
= ISC_REQ_CONNECTION
| ISC_REQ_USE_DCE_STYLE
|
1168 ISC_REQ_MUTUAL_AUTH
| ISC_REQ_DELEGATE
;
1170 in
.BufferType
= SECBUFFER_TOKEN
;
1174 in_desc
.ulVersion
= 0;
1175 in_desc
.cBuffers
= 1;
1176 in_desc
.pBuffers
= &in
;
1178 pszAuthData
= pszAuthValue
+ strlenW(pAuthInfo
->scheme
);
1179 if (*pszAuthData
== ' ')
1182 in
.cbBuffer
= HTTP_DecodeBase64(pszAuthData
, NULL
);
1183 in
.pvBuffer
= heap_alloc(in
.cbBuffer
);
1184 HTTP_DecodeBase64(pszAuthData
, in
.pvBuffer
);
1187 buffer
= heap_alloc(pAuthInfo
->max_token
);
1189 out
.BufferType
= SECBUFFER_TOKEN
;
1190 out
.cbBuffer
= pAuthInfo
->max_token
;
1191 out
.pvBuffer
= buffer
;
1193 out_desc
.ulVersion
= 0;
1194 out_desc
.cBuffers
= 1;
1195 out_desc
.pBuffers
= &out
;
1197 sec_status
= InitializeSecurityContextW(first
? &pAuthInfo
->cred
: NULL
,
1198 first
? NULL
: &pAuthInfo
->ctx
,
1199 first
? request
->session
->serverName
: NULL
,
1200 context_req
, 0, SECURITY_NETWORK_DREP
,
1201 in
.pvBuffer
? &in_desc
: NULL
,
1202 0, &pAuthInfo
->ctx
, &out_desc
,
1203 &pAuthInfo
->attr
, &pAuthInfo
->exp
);
1204 if (sec_status
== SEC_E_OK
)
1206 pAuthInfo
->finished
= TRUE
;
1207 pAuthInfo
->auth_data
= out
.pvBuffer
;
1208 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
1209 TRACE("sending last auth packet\n");
1211 else if (sec_status
== SEC_I_CONTINUE_NEEDED
)
1213 pAuthInfo
->auth_data
= out
.pvBuffer
;
1214 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
1215 TRACE("sending next auth packet\n");
1219 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status
);
1220 heap_free(out
.pvBuffer
);
1221 destroy_authinfo(pAuthInfo
);
1230 /***********************************************************************
1231 * HTTP_HttpAddRequestHeadersW (internal)
1233 static DWORD
HTTP_HttpAddRequestHeadersW(http_request_t
*request
,
1234 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1239 DWORD len
, res
= ERROR_HTTP_INVALID_HEADER
;
1241 TRACE("copying header: %s\n", debugstr_wn(lpszHeader
, dwHeaderLength
));
1243 if( dwHeaderLength
== ~0U )
1244 len
= strlenW(lpszHeader
);
1246 len
= dwHeaderLength
;
1247 buffer
= heap_alloc(sizeof(WCHAR
)*(len
+1));
1248 lstrcpynW( buffer
, lpszHeader
, len
+ 1);
1254 LPWSTR
* pFieldAndValue
;
1256 lpszEnd
= lpszStart
;
1258 while (*lpszEnd
!= '\0')
1260 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1265 if (*lpszStart
== '\0')
1268 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1271 lpszEnd
++; /* Jump over newline */
1273 TRACE("interpreting header %s\n", debugstr_w(lpszStart
));
1274 if (*lpszStart
== '\0')
1276 /* Skip 0-length headers */
1277 lpszStart
= lpszEnd
;
1278 res
= ERROR_SUCCESS
;
1281 pFieldAndValue
= HTTP_InterpretHttpHeader(lpszStart
);
1284 res
= HTTP_VerifyValidHeader(request
, pFieldAndValue
[0]);
1285 if (res
== ERROR_SUCCESS
)
1286 res
= HTTP_ProcessHeader(request
, pFieldAndValue
[0],
1287 pFieldAndValue
[1], dwModifier
| HTTP_ADDHDR_FLAG_REQ
);
1288 HTTP_FreeTokens(pFieldAndValue
);
1291 lpszStart
= lpszEnd
;
1292 } while (res
== ERROR_SUCCESS
);
1298 /***********************************************************************
1299 * HttpAddRequestHeadersW (WININET.@)
1301 * Adds one or more HTTP header to the request handler
1304 * On Windows if dwHeaderLength includes the trailing '\0', then
1305 * HttpAddRequestHeadersW() adds it too. However this results in an
1306 * invalid Http header which is rejected by some servers so we probably
1307 * don't need to match Windows on that point.
1314 BOOL WINAPI
HttpAddRequestHeadersW(HINTERNET hHttpRequest
,
1315 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1317 http_request_t
*request
;
1318 DWORD res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
1320 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_wn(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1325 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
1326 if (request
&& request
->hdr
.htype
== WH_HHTTPREQ
)
1327 res
= HTTP_HttpAddRequestHeadersW( request
, lpszHeader
, dwHeaderLength
, dwModifier
);
1329 WININET_Release( &request
->hdr
);
1331 if(res
!= ERROR_SUCCESS
)
1333 return res
== ERROR_SUCCESS
;
1336 /***********************************************************************
1337 * HttpAddRequestHeadersA (WININET.@)
1339 * Adds one or more HTTP header to the request handler
1346 BOOL WINAPI
HttpAddRequestHeadersA(HINTERNET hHttpRequest
,
1347 LPCSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1353 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_an(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1355 len
= MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, NULL
, 0 );
1356 hdr
= heap_alloc(len
*sizeof(WCHAR
));
1357 MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, hdr
, len
);
1358 if( dwHeaderLength
!= ~0U )
1359 dwHeaderLength
= len
;
1361 r
= HttpAddRequestHeadersW( hHttpRequest
, hdr
, dwHeaderLength
, dwModifier
);
1367 static void free_accept_types( WCHAR
**accept_types
)
1369 WCHAR
*ptr
, **types
= accept_types
;
1372 while ((ptr
= *types
))
1377 heap_free( accept_types
);
1380 static WCHAR
**convert_accept_types( const char **accept_types
)
1383 const char **types
= accept_types
;
1385 BOOL invalid_pointer
= FALSE
;
1387 if (!types
) return NULL
;
1393 /* find out how many there are */
1394 if (*types
&& **types
)
1396 TRACE("accept type: %s\n", debugstr_a(*types
));
1402 WARN("invalid accept type pointer\n");
1403 invalid_pointer
= TRUE
;
1408 if (invalid_pointer
) return NULL
;
1409 if (!(typesW
= heap_alloc( sizeof(WCHAR
*) * (count
+ 1) ))) return NULL
;
1411 types
= accept_types
;
1414 if (*types
&& **types
) typesW
[count
++] = heap_strdupAtoW( *types
);
1417 typesW
[count
] = NULL
;
1421 /***********************************************************************
1422 * HttpOpenRequestA (WININET.@)
1424 * Open a HTTP request handle
1427 * HINTERNET a HTTP request handle on success
1431 HINTERNET WINAPI
HttpOpenRequestA(HINTERNET hHttpSession
,
1432 LPCSTR lpszVerb
, LPCSTR lpszObjectName
, LPCSTR lpszVersion
,
1433 LPCSTR lpszReferrer
, LPCSTR
*lpszAcceptTypes
,
1434 DWORD dwFlags
, DWORD_PTR dwContext
)
1436 LPWSTR szVerb
= NULL
, szObjectName
= NULL
;
1437 LPWSTR szVersion
= NULL
, szReferrer
= NULL
, *szAcceptTypes
= NULL
;
1438 HINTERNET rc
= FALSE
;
1440 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1441 debugstr_a(lpszVerb
), debugstr_a(lpszObjectName
),
1442 debugstr_a(lpszVersion
), debugstr_a(lpszReferrer
), lpszAcceptTypes
,
1443 dwFlags
, dwContext
);
1447 szVerb
= heap_strdupAtoW(lpszVerb
);
1454 szObjectName
= heap_strdupAtoW(lpszObjectName
);
1455 if ( !szObjectName
)
1461 szVersion
= heap_strdupAtoW(lpszVersion
);
1468 szReferrer
= heap_strdupAtoW(lpszReferrer
);
1473 szAcceptTypes
= convert_accept_types( lpszAcceptTypes
);
1474 rc
= HttpOpenRequestW(hHttpSession
, szVerb
, szObjectName
, szVersion
, szReferrer
,
1475 (const WCHAR
**)szAcceptTypes
, dwFlags
, dwContext
);
1478 free_accept_types(szAcceptTypes
);
1479 heap_free(szReferrer
);
1480 heap_free(szVersion
);
1481 heap_free(szObjectName
);
1486 /***********************************************************************
1489 static UINT
HTTP_EncodeBase64( LPCSTR bin
, unsigned int len
, LPWSTR base64
)
1492 static const CHAR HTTP_Base64Enc
[] =
1493 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1497 /* first 6 bits, all from bin[0] */
1498 base64
[n
++] = HTTP_Base64Enc
[(bin
[0] & 0xfc) >> 2];
1499 x
= (bin
[0] & 3) << 4;
1501 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1504 base64
[n
++] = HTTP_Base64Enc
[x
];
1509 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[1]&0xf0) >> 4 ) ];
1510 x
= ( bin
[1] & 0x0f ) << 2;
1512 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1515 base64
[n
++] = HTTP_Base64Enc
[x
];
1519 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[2]&0xc0 ) >> 6 ) ];
1521 /* last 6 bits, all from bin [2] */
1522 base64
[n
++] = HTTP_Base64Enc
[ bin
[2] & 0x3f ];
1530 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1531 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1532 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1533 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1534 static const signed char HTTP_Base64Dec
[256] =
1536 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1537 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1538 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1539 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1540 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1541 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1542 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1543 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1544 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1545 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1546 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1547 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1548 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1549 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1550 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1551 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1552 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1553 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1554 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1555 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1556 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1557 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1558 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1559 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1560 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1561 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1565 /***********************************************************************
1568 static UINT
HTTP_DecodeBase64( LPCWSTR base64
, LPSTR bin
)
1576 if (base64
[0] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1577 ((in
[0] = HTTP_Base64Dec
[base64
[0]]) == -1) ||
1578 base64
[1] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1579 ((in
[1] = HTTP_Base64Dec
[base64
[1]]) == -1))
1581 WARN("invalid base64: %s\n", debugstr_w(base64
));
1585 bin
[n
] = (unsigned char) (in
[0] << 2 | in
[1] >> 4);
1588 if ((base64
[2] == '=') && (base64
[3] == '='))
1590 if (base64
[2] > ARRAYSIZE(HTTP_Base64Dec
) ||
1591 ((in
[2] = HTTP_Base64Dec
[base64
[2]]) == -1))
1593 WARN("invalid base64: %s\n", debugstr_w(&base64
[2]));
1597 bin
[n
] = (unsigned char) (in
[1] << 4 | in
[2] >> 2);
1600 if (base64
[3] == '=')
1602 if (base64
[3] > ARRAYSIZE(HTTP_Base64Dec
) ||
1603 ((in
[3] = HTTP_Base64Dec
[base64
[3]]) == -1))
1605 WARN("invalid base64: %s\n", debugstr_w(&base64
[3]));
1609 bin
[n
] = (unsigned char) (((in
[2] << 6) & 0xc0) | in
[3]);
1618 /***********************************************************************
1619 * HTTP_InsertAuthorization
1621 * Insert or delete the authorization field in the request header.
1623 static BOOL
HTTP_InsertAuthorization( http_request_t
*request
, struct HttpAuthInfo
*pAuthInfo
, LPCWSTR header
)
1627 static const WCHAR wszSpace
[] = {' ',0};
1628 static const WCHAR wszBasic
[] = {'B','a','s','i','c',0};
1630 WCHAR
*authorization
= NULL
;
1632 if (pAuthInfo
->auth_data_len
)
1634 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1635 len
= strlenW(pAuthInfo
->scheme
)+1+((pAuthInfo
->auth_data_len
+2)*4)/3;
1636 authorization
= heap_alloc((len
+1)*sizeof(WCHAR
));
1640 strcpyW(authorization
, pAuthInfo
->scheme
);
1641 strcatW(authorization
, wszSpace
);
1642 HTTP_EncodeBase64(pAuthInfo
->auth_data
,
1643 pAuthInfo
->auth_data_len
,
1644 authorization
+strlenW(authorization
));
1646 /* clear the data as it isn't valid now that it has been sent to the
1647 * server, unless it's Basic authentication which doesn't do
1648 * connection tracking */
1649 if (strcmpiW(pAuthInfo
->scheme
, wszBasic
))
1651 heap_free(pAuthInfo
->auth_data
);
1652 pAuthInfo
->auth_data
= NULL
;
1653 pAuthInfo
->auth_data_len
= 0;
1657 TRACE("Inserting authorization: %s\n", debugstr_w(authorization
));
1659 HTTP_ProcessHeader(request
, header
, authorization
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
1660 heap_free(authorization
);
1665 static WCHAR
*HTTP_BuildProxyRequestUrl(http_request_t
*req
)
1667 static const WCHAR slash
[] = { '/',0 };
1668 static const WCHAR format
[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1669 static const WCHAR formatSSL
[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1670 http_session_t
*session
= req
->session
;
1671 WCHAR new_location
[INTERNET_MAX_URL_LENGTH
], *url
;
1674 size
= sizeof(new_location
);
1675 if (HTTP_HttpQueryInfoW(req
, HTTP_QUERY_LOCATION
, new_location
, &size
, NULL
) == ERROR_SUCCESS
)
1677 URL_COMPONENTSW UrlComponents
;
1679 if (!(url
= heap_alloc(size
+ sizeof(WCHAR
)))) return NULL
;
1680 strcpyW( url
, new_location
);
1682 ZeroMemory(&UrlComponents
,sizeof(URL_COMPONENTSW
));
1683 if(InternetCrackUrlW(url
, 0, 0, &UrlComponents
)) goto done
;
1687 size
= 16; /* "https://" + sizeof(port#) + ":/\0" */
1688 size
+= strlenW( session
->hostName
) + strlenW( req
->path
);
1690 if (!(url
= heap_alloc(size
* sizeof(WCHAR
)))) return NULL
;
1692 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1693 sprintfW( url
, formatSSL
, session
->hostName
, session
->hostPort
);
1695 sprintfW( url
, format
, session
->hostName
, session
->hostPort
);
1696 if (req
->path
[0] != '/') strcatW( url
, slash
);
1697 strcatW( url
, req
->path
);
1700 TRACE("url=%s\n", debugstr_w(url
));
1704 /***********************************************************************
1705 * HTTP_DealWithProxy
1707 static BOOL
HTTP_DealWithProxy(appinfo_t
*hIC
, http_session_t
*session
, http_request_t
*request
)
1709 WCHAR buf
[INTERNET_MAX_HOST_NAME_LENGTH
];
1710 WCHAR protoProxy
[INTERNET_MAX_URL_LENGTH
];
1711 DWORD protoProxyLen
= INTERNET_MAX_URL_LENGTH
;
1712 WCHAR proxy
[INTERNET_MAX_URL_LENGTH
];
1713 static WCHAR szNul
[] = { 0 };
1714 URL_COMPONENTSW UrlComponents
;
1715 static const WCHAR protoHttp
[] = { 'h','t','t','p',0 };
1716 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/',0 };
1717 static const WCHAR szFormat
[] = { 'h','t','t','p',':','/','/','%','s',0 };
1719 memset( &UrlComponents
, 0, sizeof UrlComponents
);
1720 UrlComponents
.dwStructSize
= sizeof UrlComponents
;
1721 UrlComponents
.lpszHostName
= buf
;
1722 UrlComponents
.dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
1724 if (!INTERNET_FindProxyForProtocol(hIC
->proxy
, protoHttp
, protoProxy
, &protoProxyLen
))
1726 if( CSTR_EQUAL
!= CompareStringW(LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
1727 protoProxy
,strlenW(szHttp
),szHttp
,strlenW(szHttp
)) )
1728 sprintfW(proxy
, szFormat
, protoProxy
);
1730 strcpyW(proxy
, protoProxy
);
1731 if( !InternetCrackUrlW(proxy
, 0, 0, &UrlComponents
) )
1733 if( UrlComponents
.dwHostNameLength
== 0 )
1736 if( !request
->path
)
1737 request
->path
= szNul
;
1739 if(UrlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
1740 UrlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
1742 heap_free(session
->serverName
);
1743 session
->serverName
= heap_strdupW(UrlComponents
.lpszHostName
);
1744 session
->serverPort
= UrlComponents
.nPort
;
1746 TRACE("proxy server=%s port=%d\n", debugstr_w(session
->serverName
), session
->serverPort
);
1750 static DWORD
HTTP_ResolveName(http_request_t
*request
, server_t
*server
)
1755 if(server
->addr_len
)
1756 return ERROR_SUCCESS
;
1758 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1759 INTERNET_STATUS_RESOLVING_NAME
,
1761 (strlenW(server
->name
)+1) * sizeof(WCHAR
));
1763 addr_len
= sizeof(server
->addr
);
1764 if (!GetAddress(server
->name
, server
->port
, (struct sockaddr
*)&server
->addr
, &addr_len
))
1765 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1767 switch(server
->addr
.ss_family
) {
1769 addr
= &((struct sockaddr_in
*)&server
->addr
)->sin_addr
;
1772 addr
= &((struct sockaddr_in6
*)&server
->addr
)->sin6_addr
;
1775 WARN("unsupported family %d\n", server
->addr
.ss_family
);
1776 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1779 server
->addr_len
= addr_len
;
1780 inet_ntop(server
->addr
.ss_family
, addr
, server
->addr_str
, sizeof(server
->addr_str
));
1781 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1782 INTERNET_STATUS_NAME_RESOLVED
,
1783 server
->addr_str
, strlen(server
->addr_str
)+1);
1785 TRACE("resolved %s to %s\n", debugstr_w(server
->name
), server
->addr_str
);
1786 return ERROR_SUCCESS
;
1789 static BOOL
HTTP_GetRequestURL(http_request_t
*req
, LPWSTR buf
)
1791 static const WCHAR http
[] = { 'h','t','t','p',':','/','/',0 };
1792 static const WCHAR https
[] = { 'h','t','t','p','s',':','/','/',0 };
1793 static const WCHAR slash
[] = { '/',0 };
1794 LPHTTPHEADERW host_header
;
1797 host_header
= HTTP_GetHeader(req
, hostW
);
1801 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1805 strcpyW(buf
, scheme
);
1806 strcatW(buf
, host_header
->lpszValue
);
1807 if (req
->path
[0] != '/')
1808 strcatW(buf
, slash
);
1809 strcatW(buf
, req
->path
);
1814 /***********************************************************************
1815 * HTTPREQ_Destroy (internal)
1817 * Deallocate request handle
1820 static void HTTPREQ_Destroy(object_header_t
*hdr
)
1822 http_request_t
*request
= (http_request_t
*) hdr
;
1827 if(request
->hCacheFile
) {
1828 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1830 CloseHandle(request
->hCacheFile
);
1832 if(HTTP_GetRequestURL(request
, url
)) {
1835 headersLen
= request
->rawHeaders
? strlenW(request
->rawHeaders
) : 0;
1836 CommitUrlCacheEntryW(url
, request
->cacheFile
, request
->expires
,
1837 request
->last_modified
, NORMAL_CACHE_ENTRY
,
1838 request
->rawHeaders
, headersLen
, NULL
, 0);
1841 heap_free(request
->cacheFile
);
1843 request
->read_section
.DebugInfo
->Spare
[0] = 0;
1844 DeleteCriticalSection( &request
->read_section
);
1845 WININET_Release(&request
->session
->hdr
);
1847 destroy_authinfo(request
->authInfo
);
1848 destroy_authinfo(request
->proxyAuthInfo
);
1850 heap_free(request
->path
);
1851 heap_free(request
->verb
);
1852 heap_free(request
->rawHeaders
);
1853 heap_free(request
->version
);
1854 heap_free(request
->statusText
);
1856 for (i
= 0; i
< request
->nCustHeaders
; i
++)
1858 heap_free(request
->custHeaders
[i
].lpszField
);
1859 heap_free(request
->custHeaders
[i
].lpszValue
);
1861 destroy_data_stream(request
->data_stream
);
1862 heap_free(request
->custHeaders
);
1865 static void http_release_netconn(http_request_t
*req
, BOOL reuse
)
1867 TRACE("%p %p\n",req
, req
->netconn
);
1873 if(reuse
&& req
->netconn
->keep_alive
) {
1876 EnterCriticalSection(&connection_pool_cs
);
1878 list_add_head(&req
->netconn
->server
->conn_pool
, &req
->netconn
->pool_entry
);
1879 req
->netconn
->keep_until
= (DWORD64
)GetTickCount() + COLLECT_TIME
;
1880 req
->netconn
= NULL
;
1882 run_collector
= !collector_running
;
1883 collector_running
= TRUE
;
1885 LeaveCriticalSection(&connection_pool_cs
);
1888 HANDLE thread
= NULL
;
1891 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
, (const WCHAR
*)WININET_hModule
, &module
);
1893 thread
= CreateThread(NULL
, 0, collect_connections_proc
, NULL
, 0, NULL
);
1895 EnterCriticalSection(&connection_pool_cs
);
1896 collector_running
= FALSE
;
1897 LeaveCriticalSection(&connection_pool_cs
);
1900 FreeLibrary(module
);
1903 CloseHandle(thread
);
1908 // silence unused function warning
1909 (void)collect_connections_proc
;
1912 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
1913 INTERNET_STATUS_CLOSING_CONNECTION
, 0, 0);
1915 free_netconn(req
->netconn
);
1916 req
->netconn
= NULL
;
1918 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
1919 INTERNET_STATUS_CONNECTION_CLOSED
, 0, 0);
1922 static void drain_content(http_request_t
*req
)
1926 if (!req
->netconn
) return;
1928 if (req
->contentLength
== -1)
1930 else if(!strcmpW(req
->verb
, szHEAD
))
1933 try_reuse
= req
->data_stream
->vtbl
->drain_content(req
->data_stream
, req
);
1935 http_release_netconn(req
, try_reuse
);
1938 static BOOL
HTTP_KeepAlive(http_request_t
*request
)
1940 WCHAR szVersion
[10];
1941 WCHAR szConnectionResponse
[20];
1942 DWORD dwBufferSize
= sizeof(szVersion
);
1943 BOOL keepalive
= FALSE
;
1945 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1946 * the connection is keep-alive by default */
1947 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_VERSION
, szVersion
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1948 && !strcmpiW(szVersion
, g_szHttp1_1
))
1953 dwBufferSize
= sizeof(szConnectionResponse
);
1954 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_PROXY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1955 || HTTP_HttpQueryInfoW(request
, HTTP_QUERY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
1957 keepalive
= !strcmpiW(szConnectionResponse
, szKeepAlive
);
1963 static void HTTPREQ_CloseConnection(object_header_t
*hdr
)
1965 http_request_t
*req
= (http_request_t
*)hdr
;
1970 static DWORD
HTTPREQ_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
1972 http_request_t
*req
= (http_request_t
*)hdr
;
1975 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO
:
1977 http_session_t
*session
= req
->session
;
1978 INTERNET_DIAGNOSTIC_SOCKET_INFO
*info
= buffer
;
1980 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1982 if (*size
< sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
))
1983 return ERROR_INSUFFICIENT_BUFFER
;
1984 *size
= sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
);
1985 /* FIXME: can't get a SOCKET from our connection since we don't use
1989 /* FIXME: get source port from req->netConnection */
1990 info
->SourcePort
= 0;
1991 info
->DestPort
= session
->hostPort
;
1993 if (HTTP_KeepAlive(req
))
1994 info
->Flags
|= IDSI_FLAG_KEEP_ALIVE
;
1995 if (session
->appInfo
->proxy
&& session
->appInfo
->proxy
[0] != 0)
1996 info
->Flags
|= IDSI_FLAG_PROXY
;
1997 if (req
->netconn
->useSSL
)
1998 info
->Flags
|= IDSI_FLAG_SECURE
;
2000 return ERROR_SUCCESS
;
2003 case INTERNET_OPTION_SECURITY_FLAGS
:
2007 if (*size
< sizeof(ULONG
))
2008 return ERROR_INSUFFICIENT_BUFFER
;
2010 *size
= sizeof(DWORD
);
2012 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
2013 flags
|= SECURITY_FLAG_SECURE
;
2014 flags
|= req
->security_flags
;
2016 int bits
= NETCON_GetCipherStrength(req
->netconn
);
2018 flags
|= SECURITY_FLAG_STRENGTH_STRONG
;
2019 else if (bits
>= 56)
2020 flags
|= SECURITY_FLAG_STRENGTH_MEDIUM
;
2022 flags
|= SECURITY_FLAG_STRENGTH_WEAK
;
2024 *(DWORD
*)buffer
= flags
;
2025 return ERROR_SUCCESS
;
2028 case INTERNET_OPTION_HANDLE_TYPE
:
2029 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2031 if (*size
< sizeof(ULONG
))
2032 return ERROR_INSUFFICIENT_BUFFER
;
2034 *size
= sizeof(DWORD
);
2035 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_HTTP_REQUEST
;
2036 return ERROR_SUCCESS
;
2038 case INTERNET_OPTION_URL
: {
2039 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2044 static const WCHAR httpW
[] = {'h','t','t','p',':','/','/',0};
2046 TRACE("INTERNET_OPTION_URL\n");
2048 host
= HTTP_GetHeader(req
, hostW
);
2049 strcpyW(url
, httpW
);
2050 strcatW(url
, host
->lpszValue
);
2051 if (NULL
!= (pch
= strchrW(url
+ strlenW(httpW
), ':')))
2053 strcatW(url
, req
->path
);
2055 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url
));
2058 len
= (strlenW(url
)+1) * sizeof(WCHAR
);
2060 return ERROR_INSUFFICIENT_BUFFER
;
2063 strcpyW(buffer
, url
);
2064 return ERROR_SUCCESS
;
2066 len
= WideCharToMultiByte(CP_ACP
, 0, url
, -1, buffer
, *size
, NULL
, NULL
);
2068 return ERROR_INSUFFICIENT_BUFFER
;
2071 return ERROR_SUCCESS
;
2075 case INTERNET_OPTION_CACHE_TIMESTAMPS
: {
2076 INTERNET_CACHE_ENTRY_INFOW
*info
;
2077 INTERNET_CACHE_TIMESTAMPS
*ts
= buffer
;
2078 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2079 DWORD nbytes
, error
;
2082 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2084 if (*size
< sizeof(*ts
))
2086 *size
= sizeof(*ts
);
2087 return ERROR_INSUFFICIENT_BUFFER
;
2090 HTTP_GetRequestURL(req
, url
);
2091 ret
= GetUrlCacheEntryInfoW(url
, NULL
, &nbytes
);
2092 error
= GetLastError();
2093 if (!ret
&& error
== ERROR_INSUFFICIENT_BUFFER
)
2095 if (!(info
= heap_alloc(nbytes
)))
2096 return ERROR_OUTOFMEMORY
;
2098 GetUrlCacheEntryInfoW(url
, info
, &nbytes
);
2100 ts
->ftExpires
= info
->ExpireTime
;
2101 ts
->ftLastModified
= info
->LastModifiedTime
;
2104 *size
= sizeof(*ts
);
2105 return ERROR_SUCCESS
;
2110 case INTERNET_OPTION_DATAFILE_NAME
: {
2113 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2115 if(!req
->cacheFile
) {
2117 return ERROR_INTERNET_ITEM_NOT_FOUND
;
2121 req_size
= (lstrlenW(req
->cacheFile
)+1) * sizeof(WCHAR
);
2122 if(*size
< req_size
)
2123 return ERROR_INSUFFICIENT_BUFFER
;
2126 memcpy(buffer
, req
->cacheFile
, *size
);
2127 return ERROR_SUCCESS
;
2129 req_size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
, -1, NULL
, 0, NULL
, NULL
);
2130 if (req_size
> *size
)
2131 return ERROR_INSUFFICIENT_BUFFER
;
2133 *size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
,
2134 -1, buffer
, *size
, NULL
, NULL
);
2135 return ERROR_SUCCESS
;
2139 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT
: {
2140 PCCERT_CONTEXT context
;
2142 if(*size
< sizeof(INTERNET_CERTIFICATE_INFOA
)) {
2143 *size
= sizeof(INTERNET_CERTIFICATE_INFOA
);
2144 return ERROR_INSUFFICIENT_BUFFER
;
2147 context
= (PCCERT_CONTEXT
)NETCON_GetCert(req
->netconn
);
2149 INTERNET_CERTIFICATE_INFOA
*info
= (INTERNET_CERTIFICATE_INFOA
*)buffer
;
2152 memset(info
, 0, sizeof(*info
));
2153 info
->ftExpiry
= context
->pCertInfo
->NotAfter
;
2154 info
->ftStart
= context
->pCertInfo
->NotBefore
;
2155 len
= CertNameToStrA(context
->dwCertEncodingType
,
2156 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
2157 info
->lpszSubjectInfo
= LocalAlloc(0, len
);
2158 if(info
->lpszSubjectInfo
)
2159 CertNameToStrA(context
->dwCertEncodingType
,
2160 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
,
2161 info
->lpszSubjectInfo
, len
);
2162 len
= CertNameToStrA(context
->dwCertEncodingType
,
2163 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
2164 info
->lpszIssuerInfo
= LocalAlloc(0, len
);
2165 if(info
->lpszIssuerInfo
)
2166 CertNameToStrA(context
->dwCertEncodingType
,
2167 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
,
2168 info
->lpszIssuerInfo
, len
);
2169 info
->dwKeySize
= NETCON_GetCipherStrength(req
->netconn
);
2170 CertFreeCertificateContext(context
);
2171 return ERROR_SUCCESS
;
2173 return ERROR_NOT_SUPPORTED
;
2175 case INTERNET_OPTION_CONNECT_TIMEOUT
:
2176 if (*size
< sizeof(DWORD
))
2177 return ERROR_INSUFFICIENT_BUFFER
;
2179 *size
= sizeof(DWORD
);
2180 *(DWORD
*)buffer
= req
->connect_timeout
;
2181 return ERROR_SUCCESS
;
2184 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
2187 static DWORD
HTTPREQ_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
2189 http_request_t
*req
= (http_request_t
*)hdr
;
2192 case INTERNET_OPTION_SECURITY_FLAGS
:
2196 if (!buffer
|| size
!= sizeof(DWORD
))
2197 return ERROR_INVALID_PARAMETER
;
2198 flags
= *(DWORD
*)buffer
;
2199 TRACE("%08x\n", flags
);
2200 req
->security_flags
= flags
;
2202 req
->netconn
->security_flags
= flags
;
2203 return ERROR_SUCCESS
;
2205 case INTERNET_OPTION_CONNECT_TIMEOUT
:
2206 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2207 req
->connect_timeout
= *(DWORD
*)buffer
;
2208 return ERROR_SUCCESS
;
2210 case INTERNET_OPTION_SEND_TIMEOUT
:
2211 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2212 req
->send_timeout
= *(DWORD
*)buffer
;
2213 return ERROR_SUCCESS
;
2215 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
2216 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2217 req
->receive_timeout
= *(DWORD
*)buffer
;
2218 return ERROR_SUCCESS
;
2220 case INTERNET_OPTION_USERNAME
:
2221 heap_free(req
->session
->userName
);
2222 if (!(req
->session
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2223 return ERROR_SUCCESS
;
2225 case INTERNET_OPTION_PASSWORD
:
2226 heap_free(req
->session
->password
);
2227 if (!(req
->session
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2228 return ERROR_SUCCESS
;
2229 case INTERNET_OPTION_HTTP_DECODING
:
2230 if(size
!= sizeof(BOOL
))
2231 return ERROR_INVALID_PARAMETER
;
2232 req
->decoding
= *(BOOL
*)buffer
;
2233 return ERROR_SUCCESS
;
2236 return INET_SetOption(hdr
, option
, buffer
, size
);
2239 /* read some more data into the read buffer (the read section must be held) */
2240 static DWORD
read_more_data( http_request_t
*req
, int maxlen
)
2247 /* move existing data to the start of the buffer */
2249 memmove( req
->read_buf
, req
->read_buf
+ req
->read_pos
, req
->read_size
);
2253 if (maxlen
== -1) maxlen
= sizeof(req
->read_buf
);
2255 res
= NETCON_recv( req
->netconn
, req
->read_buf
+ req
->read_size
,
2256 maxlen
- req
->read_size
, 0, &len
);
2257 if(res
== ERROR_SUCCESS
)
2258 req
->read_size
+= len
;
2263 /* remove some amount of data from the read buffer (the read section must be held) */
2264 static void remove_data( http_request_t
*req
, int count
)
2266 if (!(req
->read_size
-= count
)) req
->read_pos
= 0;
2267 else req
->read_pos
+= count
;
2270 static BOOL
read_line( http_request_t
*req
, LPSTR buffer
, DWORD
*len
)
2272 int count
, bytes_read
, pos
= 0;
2275 EnterCriticalSection( &req
->read_section
);
2278 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
2282 count
= eol
- (req
->read_buf
+ req
->read_pos
);
2283 bytes_read
= count
+ 1;
2285 else count
= bytes_read
= req
->read_size
;
2287 count
= min( count
, *len
- pos
);
2288 memcpy( buffer
+ pos
, req
->read_buf
+ req
->read_pos
, count
);
2290 remove_data( req
, bytes_read
);
2293 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
|| !req
->read_size
)
2296 TRACE( "returning empty string %u\n", res
);
2297 LeaveCriticalSection( &req
->read_section
);
2298 INTERNET_SetLastError(res
);
2302 LeaveCriticalSection( &req
->read_section
);
2306 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
2309 buffer
[*len
- 1] = 0;
2310 TRACE( "returning %s\n", debugstr_a(buffer
));
2314 /* check if we have reached the end of the data to read (the read section must be held) */
2315 static BOOL
end_of_read_data( http_request_t
*req
)
2317 return !req
->read_size
&& req
->data_stream
->vtbl
->end_of_data(req
->data_stream
, req
);
2320 /* fetch some more data into the read buffer (the read section must be held) */
2321 static DWORD
refill_read_buffer(http_request_t
*req
, read_mode_t read_mode
, DWORD
*read_bytes
)
2323 DWORD res
, read
=0, want
;
2325 if(req
->read_size
== sizeof(req
->read_buf
))
2326 return ERROR_SUCCESS
;
2330 memmove(req
->read_buf
, req
->read_buf
+req
->read_pos
, req
->read_size
);
2334 want
= sizeof(req
->read_buf
) - req
->read_size
;
2335 res
= req
->data_stream
->vtbl
->read(req
->data_stream
, req
, req
->read_buf
+req
->read_size
,
2336 want
, &read
, read_mode
);
2337 assert(read
<= want
);
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 DWORD avail
= netconn_get_avail_data(stream
, req
);
2384 if(size
&& req
->netconn
) {
2385 if(NETCON_recv(req
->netconn
, buf
, size
, read_mode
== READMODE_SYNC
? MSG_WAITALL
: 0, &len
) != ERROR_SUCCESS
)
2388 netconn_stream
->content_length
= netconn_stream
->content_read
;
2391 netconn_stream
->content_read
+= *read
= len
;
2392 TRACE("read %u bytes\n", len
);
2393 return ERROR_SUCCESS
;
2396 static BOOL
netconn_drain_content(data_stream_t
*stream
, http_request_t
*req
)
2398 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2403 if(netconn_end_of_data(stream
, req
))
2407 avail
= netconn_get_avail_data(stream
, req
);
2411 if(NETCON_recv(req
->netconn
, buf
, min(avail
, sizeof(buf
)), 0, &len
) != ERROR_SUCCESS
)
2414 netconn_stream
->content_read
+= len
;
2415 }while(netconn_stream
->content_read
< netconn_stream
->content_length
);
2420 static void netconn_destroy(data_stream_t
*stream
)
2424 static const data_stream_vtbl_t netconn_stream_vtbl
= {
2425 netconn_get_avail_data
,
2426 netconn_end_of_data
,
2428 netconn_drain_content
,
2432 /* read some more data into the read buffer (the read section must be held) */
2433 static DWORD
read_more_chunked_data(chunked_stream_t
*stream
, http_request_t
*req
, int maxlen
)
2438 if (stream
->buf_pos
)
2440 /* move existing data to the start of the buffer */
2441 if(stream
->buf_size
)
2442 memmove(stream
->buf
, stream
->buf
+ stream
->buf_pos
, stream
->buf_size
);
2443 stream
->buf_pos
= 0;
2446 if (maxlen
== -1) maxlen
= sizeof(stream
->buf
);
2448 res
= NETCON_recv( req
->netconn
, stream
->buf
+ stream
->buf_size
,
2449 maxlen
- stream
->buf_size
, 0, &len
);
2450 if(res
== ERROR_SUCCESS
)
2451 stream
->buf_size
+= len
;
2456 /* remove some amount of data from the read buffer (the read section must be held) */
2457 static void remove_chunked_data(chunked_stream_t
*stream
, int count
)
2459 if (!(stream
->buf_size
-= count
)) stream
->buf_pos
= 0;
2460 else stream
->buf_pos
+= count
;
2463 /* discard data contents until we reach end of line (the read section must be held) */
2464 static DWORD
discard_chunked_eol(chunked_stream_t
*stream
, http_request_t
*req
)
2470 BYTE
*eol
= memchr(stream
->buf
+ stream
->buf_pos
, '\n', stream
->buf_size
);
2473 remove_chunked_data(stream
, (eol
+ 1) - (stream
->buf
+ stream
->buf_pos
));
2476 stream
->buf_pos
= stream
->buf_size
= 0; /* discard everything */
2477 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2478 } while (stream
->buf_size
);
2479 return ERROR_SUCCESS
;
2482 /* read the size of the next chunk (the read section must be held) */
2483 static DWORD
start_next_chunk(chunked_stream_t
*stream
, http_request_t
*req
)
2486 DWORD chunk_size
= 0, res
;
2488 if(stream
->chunk_size
!= ~0u && (res
= discard_chunked_eol(stream
, req
)) != ERROR_SUCCESS
)
2493 while (stream
->buf_size
)
2495 char ch
= stream
->buf
[stream
->buf_pos
];
2496 if (ch
>= '0' && ch
<= '9') chunk_size
= chunk_size
* 16 + ch
- '0';
2497 else if (ch
>= 'a' && ch
<= 'f') chunk_size
= chunk_size
* 16 + ch
- 'a' + 10;
2498 else if (ch
>= 'A' && ch
<= 'F') chunk_size
= chunk_size
* 16 + ch
- 'A' + 10;
2499 else if (ch
== ';' || ch
== '\r' || ch
== '\n')
2501 TRACE( "reading %u byte chunk\n", chunk_size
);
2502 stream
->chunk_size
= chunk_size
;
2503 req
->contentLength
+= chunk_size
;
2504 return discard_chunked_eol(stream
, req
);
2506 remove_chunked_data(stream
, 1);
2508 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2509 if (!stream
->buf_size
)
2511 stream
->chunk_size
= 0;
2512 return ERROR_SUCCESS
;
2517 static DWORD
chunked_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
2519 /* Allow reading only from read buffer */
2523 static BOOL
chunked_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
2525 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2526 return !chunked_stream
->chunk_size
;
2529 static DWORD
chunked_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
2530 DWORD
*read
, read_mode_t read_mode
)
2532 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2533 DWORD read_bytes
= 0, ret_read
= 0, res
= ERROR_SUCCESS
;
2535 if(chunked_stream
->chunk_size
== ~0u) {
2536 res
= start_next_chunk(chunked_stream
, req
);
2537 if(res
!= ERROR_SUCCESS
)
2541 while(size
&& chunked_stream
->chunk_size
) {
2542 if(chunked_stream
->buf_size
) {
2543 read_bytes
= min(size
, min(chunked_stream
->buf_size
, chunked_stream
->chunk_size
));
2545 /* this could block */
2546 if(read_mode
== READMODE_NOBLOCK
&& read_bytes
== chunked_stream
->chunk_size
)
2549 memcpy(buf
+ret_read
, chunked_stream
->buf
+chunked_stream
->buf_pos
, read_bytes
);
2550 remove_chunked_data(chunked_stream
, read_bytes
);
2552 read_bytes
= min(size
, chunked_stream
->chunk_size
);
2554 if(read_mode
== READMODE_NOBLOCK
) {
2557 if(!NETCON_query_data_available(req
->netconn
, &avail
) || !avail
)
2559 if(read_bytes
> avail
)
2562 /* this could block */
2563 if(read_bytes
== chunked_stream
->chunk_size
)
2567 res
= NETCON_recv(req
->netconn
, (char *)buf
+ret_read
, read_bytes
, 0, (int*)&read_bytes
);
2568 if(res
!= ERROR_SUCCESS
)
2572 chunked_stream
->chunk_size
-= read_bytes
;
2574 ret_read
+= read_bytes
;
2575 if(!chunked_stream
->chunk_size
) {
2576 assert(read_mode
!= READMODE_NOBLOCK
);
2577 res
= start_next_chunk(chunked_stream
, req
);
2578 if(res
!= ERROR_SUCCESS
)
2582 if(read_mode
== READMODE_ASYNC
)
2583 read_mode
= READMODE_NOBLOCK
;
2586 TRACE("read %u bytes\n", ret_read
);
2591 static BOOL
chunked_drain_content(data_stream_t
*stream
, http_request_t
*req
)
2593 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2595 /* FIXME: we can do better */
2596 return !chunked_stream
->chunk_size
;
2599 static void chunked_destroy(data_stream_t
*stream
)
2601 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2602 heap_free(chunked_stream
);
2605 static const data_stream_vtbl_t chunked_stream_vtbl
= {
2606 chunked_get_avail_data
,
2607 chunked_end_of_data
,
2609 chunked_drain_content
,
2613 /* set the request content length based on the headers */
2614 static DWORD
set_content_length(http_request_t
*request
)
2616 static const WCHAR szChunked
[] = {'c','h','u','n','k','e','d',0};
2620 if(request
->status_code
== HTTP_STATUS_NO_CONTENT
) {
2621 request
->contentLength
= request
->netconn_stream
.content_length
= 0;
2622 return ERROR_SUCCESS
;
2625 size
= sizeof(request
->contentLength
);
2626 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_CONTENT_LENGTH
,
2627 &request
->contentLength
, &size
, NULL
) != ERROR_SUCCESS
)
2628 request
->contentLength
= ~0u;
2629 request
->netconn_stream
.content_length
= request
->contentLength
;
2630 request
->netconn_stream
.content_read
= request
->read_size
;
2632 size
= sizeof(encoding
);
2633 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_TRANSFER_ENCODING
, encoding
, &size
, NULL
) == ERROR_SUCCESS
&&
2634 !strcmpiW(encoding
, szChunked
))
2636 chunked_stream_t
*chunked_stream
;
2638 chunked_stream
= heap_alloc(sizeof(*chunked_stream
));
2640 return ERROR_OUTOFMEMORY
;
2642 chunked_stream
->data_stream
.vtbl
= &chunked_stream_vtbl
;
2643 chunked_stream
->buf_size
= chunked_stream
->buf_pos
= 0;
2644 chunked_stream
->chunk_size
= ~0u;
2646 if(request
->read_size
) {
2647 memcpy(chunked_stream
->buf
, request
->read_buf
+request
->read_pos
, request
->read_size
);
2648 chunked_stream
->buf_size
= request
->read_size
;
2649 request
->read_size
= request
->read_pos
= 0;
2652 request
->data_stream
= &chunked_stream
->data_stream
;
2653 request
->contentLength
= ~0u;
2654 request
->read_chunked
= TRUE
;
2657 if(request
->decoding
) {
2660 static const WCHAR gzipW
[] = {'g','z','i','p',0};
2662 encoding_idx
= HTTP_GetCustomHeaderIndex(request
, szContent_Encoding
, 0, FALSE
);
2663 if(encoding_idx
!= -1 && !strcmpiW(request
->custHeaders
[encoding_idx
].lpszValue
, gzipW
))
2664 return init_gzip_stream(request
);
2667 return ERROR_SUCCESS
;
2670 static void send_request_complete(http_request_t
*req
, DWORD_PTR result
, DWORD error
)
2672 INTERNET_ASYNC_RESULT iar
;
2674 iar
.dwResult
= result
;
2675 iar
.dwError
= error
;
2677 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2678 sizeof(INTERNET_ASYNC_RESULT
));
2681 static void HTTP_ReceiveRequestData(http_request_t
*req
, BOOL first_notif
)
2683 DWORD res
, read
= 0, avail
= 0;
2688 EnterCriticalSection( &req
->read_section
);
2690 mode
= first_notif
&& req
->read_size
? READMODE_NOBLOCK
: READMODE_ASYNC
;
2691 res
= refill_read_buffer(req
, mode
, &read
);
2692 if(res
== ERROR_SUCCESS
&& !first_notif
)
2693 avail
= get_avail_data(req
);
2695 LeaveCriticalSection( &req
->read_section
);
2697 if(res
!= ERROR_SUCCESS
|| (mode
!= READMODE_NOBLOCK
&& !read
)) {
2698 WARN("res %u read %u, closing connection\n", res
, read
);
2699 http_release_netconn(req
, FALSE
);
2702 if(res
== ERROR_SUCCESS
)
2703 send_request_complete(req
, req
->session
->hdr
.dwInternalFlags
& INET_OPENURL
? (DWORD_PTR
)req
->hdr
.hInternet
: 1, avail
);
2705 send_request_complete(req
, 0, res
);
2708 /* read data from the http connection (the read section must be held) */
2709 static DWORD
HTTPREQ_Read(http_request_t
*req
, void *buffer
, DWORD size
, DWORD
*read
, BOOL sync
)
2711 DWORD current_read
= 0, ret_read
= 0;
2712 read_mode_t read_mode
;
2713 DWORD res
= ERROR_SUCCESS
;
2715 read_mode
= req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
? READMODE_ASYNC
: READMODE_SYNC
;
2717 EnterCriticalSection( &req
->read_section
);
2719 if(req
->read_size
) {
2720 ret_read
= min(size
, req
->read_size
);
2721 memcpy(buffer
, req
->read_buf
+req
->read_pos
, ret_read
);
2722 req
->read_size
-= ret_read
;
2723 req
->read_pos
+= ret_read
;
2724 if(read_mode
== READMODE_ASYNC
)
2725 read_mode
= READMODE_NOBLOCK
;
2728 if(ret_read
< size
) {
2729 res
= req
->data_stream
->vtbl
->read(req
->data_stream
, req
, (BYTE
*)buffer
+ret_read
, size
-ret_read
, ¤t_read
, read_mode
);
2730 ret_read
+= current_read
;
2733 LeaveCriticalSection( &req
->read_section
);
2736 TRACE( "retrieved %u bytes (%u)\n", ret_read
, req
->contentLength
);
2738 if(req
->hCacheFile
&& res
== ERROR_SUCCESS
&& ret_read
) {
2742 res
= WriteFile(req
->hCacheFile
, buffer
, ret_read
, &written
, NULL
);
2744 WARN("WriteFile failed: %u\n", GetLastError());
2747 if(size
&& !ret_read
)
2748 http_release_netconn(req
, res
== ERROR_SUCCESS
);
2754 static DWORD
HTTPREQ_ReadFile(object_header_t
*hdr
, void *buffer
, DWORD size
, DWORD
*read
)
2756 http_request_t
*req
= (http_request_t
*)hdr
;
2759 EnterCriticalSection( &req
->read_section
);
2760 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2761 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2763 res
= HTTPREQ_Read(req
, buffer
, size
, read
, TRUE
);
2764 if(res
== ERROR_SUCCESS
)
2766 LeaveCriticalSection( &req
->read_section
);
2771 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST
*workRequest
)
2773 struct WORKREQ_INTERNETREADFILEEXA
const *data
= &workRequest
->u
.InternetReadFileExA
;
2774 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2777 TRACE("INTERNETREADFILEEXA %p\n", workRequest
->hdr
);
2779 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2780 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2782 send_request_complete(req
, res
== ERROR_SUCCESS
, res
);
2785 static DWORD
HTTPREQ_ReadFileExA(object_header_t
*hdr
, INTERNET_BUFFERSA
*buffers
,
2786 DWORD flags
, DWORD_PTR context
)
2788 http_request_t
*req
= (http_request_t
*)hdr
;
2789 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2791 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2792 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2794 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2795 return ERROR_INVALID_PARAMETER
;
2797 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2799 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2801 WORKREQUEST workRequest
;
2803 if (TryEnterCriticalSection( &req
->read_section
))
2805 if (get_avail_data(req
))
2807 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2808 &buffers
->dwBufferLength
, FALSE
);
2809 size
= buffers
->dwBufferLength
;
2810 LeaveCriticalSection( &req
->read_section
);
2813 LeaveCriticalSection( &req
->read_section
);
2816 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExAProc
;
2817 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2818 workRequest
.u
.InternetReadFileExA
.lpBuffersOut
= buffers
;
2820 INTERNET_AsyncCall(&workRequest
);
2822 return ERROR_IO_PENDING
;
2826 size
= buffers
->dwBufferLength
;
2828 EnterCriticalSection( &req
->read_section
);
2829 if(hdr
->dwError
== ERROR_SUCCESS
)
2830 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2831 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2832 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2835 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2836 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2837 if(res
!= ERROR_SUCCESS
)
2840 read
+= buffers
->dwBufferLength
;
2841 if(read
== size
|| end_of_read_data(req
))
2844 LeaveCriticalSection( &req
->read_section
);
2846 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2847 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2848 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2849 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2851 EnterCriticalSection( &req
->read_section
);
2854 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2855 hdr
->dwError
= ERROR_SUCCESS
;
2857 error
= hdr
->dwError
;
2859 LeaveCriticalSection( &req
->read_section
);
2860 size
= buffers
->dwBufferLength
;
2861 buffers
->dwBufferLength
= read
;
2864 if (res
== ERROR_SUCCESS
) {
2865 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2866 &size
, sizeof(size
));
2869 return res
==ERROR_SUCCESS
? error
: res
;
2872 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST
*workRequest
)
2874 struct WORKREQ_INTERNETREADFILEEXW
const *data
= &workRequest
->u
.InternetReadFileExW
;
2875 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2878 TRACE("INTERNETREADFILEEXW %p\n", workRequest
->hdr
);
2880 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2881 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2883 send_request_complete(req
, res
== ERROR_SUCCESS
, res
);
2886 static DWORD
HTTPREQ_ReadFileExW(object_header_t
*hdr
, INTERNET_BUFFERSW
*buffers
,
2887 DWORD flags
, DWORD_PTR context
)
2890 http_request_t
*req
= (http_request_t
*)hdr
;
2891 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2893 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2894 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2896 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2897 return ERROR_INVALID_PARAMETER
;
2899 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2901 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2903 WORKREQUEST workRequest
;
2905 if (TryEnterCriticalSection( &req
->read_section
))
2907 if (get_avail_data(req
))
2909 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2910 &buffers
->dwBufferLength
, FALSE
);
2911 size
= buffers
->dwBufferLength
;
2912 LeaveCriticalSection( &req
->read_section
);
2915 LeaveCriticalSection( &req
->read_section
);
2918 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExWProc
;
2919 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2920 workRequest
.u
.InternetReadFileExW
.lpBuffersOut
= buffers
;
2922 INTERNET_AsyncCall(&workRequest
);
2924 return ERROR_IO_PENDING
;
2928 size
= buffers
->dwBufferLength
;
2930 EnterCriticalSection( &req
->read_section
);
2931 if(hdr
->dwError
== ERROR_SUCCESS
)
2932 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2933 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2934 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2937 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2938 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2939 if(res
!= ERROR_SUCCESS
)
2942 read
+= buffers
->dwBufferLength
;
2943 if(read
== size
|| end_of_read_data(req
))
2946 LeaveCriticalSection( &req
->read_section
);
2948 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2949 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2950 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2951 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2953 EnterCriticalSection( &req
->read_section
);
2956 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2957 hdr
->dwError
= ERROR_SUCCESS
;
2959 error
= hdr
->dwError
;
2961 LeaveCriticalSection( &req
->read_section
);
2962 size
= buffers
->dwBufferLength
;
2963 buffers
->dwBufferLength
= read
;
2966 if (res
== ERROR_SUCCESS
) {
2967 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2968 &size
, sizeof(size
));
2971 return res
==ERROR_SUCCESS
? error
: res
;
2974 static DWORD
HTTPREQ_WriteFile(object_header_t
*hdr
, const void *buffer
, DWORD size
, DWORD
*written
)
2977 http_request_t
*request
= (http_request_t
*)hdr
;
2979 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
2982 res
= NETCON_send(request
->netconn
, buffer
, size
, 0, (LPINT
)written
);
2983 if (res
== ERROR_SUCCESS
)
2984 request
->bytesWritten
+= *written
;
2986 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_SENT
, written
, sizeof(DWORD
));
2990 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST
*workRequest
)
2992 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2994 HTTP_ReceiveRequestData(req
, FALSE
);
2997 static DWORD
HTTPREQ_QueryDataAvailable(object_header_t
*hdr
, DWORD
*available
, DWORD flags
, DWORD_PTR ctx
)
2999 http_request_t
*req
= (http_request_t
*)hdr
;
3001 TRACE("(%p %p %x %lx)\n", req
, available
, flags
, ctx
);
3003 if (req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3005 WORKREQUEST workRequest
;
3007 /* never wait, if we can't enter the section we queue an async request right away */
3008 if (TryEnterCriticalSection( &req
->read_section
))
3010 refill_read_buffer(req
, READMODE_NOBLOCK
, NULL
);
3011 if ((*available
= get_avail_data( req
))) goto done
;
3012 if (end_of_read_data( req
)) goto done
;
3013 LeaveCriticalSection( &req
->read_section
);
3016 workRequest
.asyncproc
= HTTPREQ_AsyncQueryDataAvailableProc
;
3017 workRequest
.hdr
= WININET_AddRef( &req
->hdr
);
3019 INTERNET_AsyncCall(&workRequest
);
3021 return ERROR_IO_PENDING
;
3024 EnterCriticalSection( &req
->read_section
);
3026 if (!(*available
= get_avail_data( req
)) && !end_of_read_data( req
))
3028 refill_read_buffer( req
, READMODE_ASYNC
, NULL
);
3029 *available
= get_avail_data( req
);
3033 LeaveCriticalSection( &req
->read_section
);
3035 TRACE( "returning %u\n", *available
);
3036 return ERROR_SUCCESS
;
3039 static const object_vtbl_t HTTPREQVtbl
= {
3041 HTTPREQ_CloseConnection
,
3042 HTTPREQ_QueryOption
,
3045 HTTPREQ_ReadFileExA
,
3046 HTTPREQ_ReadFileExW
,
3048 HTTPREQ_QueryDataAvailable
,
3052 /***********************************************************************
3053 * HTTP_HttpOpenRequestW (internal)
3055 * Open a HTTP request handle
3058 * HINTERNET a HTTP request handle on success
3062 static DWORD
HTTP_HttpOpenRequestW(http_session_t
*session
,
3063 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
3064 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
3065 DWORD dwFlags
, DWORD_PTR dwContext
, HINTERNET
*ret
)
3067 appinfo_t
*hIC
= session
->appInfo
;
3068 http_request_t
*request
;
3069 DWORD len
, res
= ERROR_SUCCESS
;
3073 request
= alloc_object(&session
->hdr
, &HTTPREQVtbl
, sizeof(http_request_t
));
3075 return ERROR_OUTOFMEMORY
;
3077 request
->hdr
.htype
= WH_HHTTPREQ
;
3078 request
->hdr
.dwFlags
= dwFlags
;
3079 request
->hdr
.dwContext
= dwContext
;
3080 request
->contentLength
= ~0u;
3082 request
->netconn_stream
.data_stream
.vtbl
= &netconn_stream_vtbl
;
3083 request
->data_stream
= &request
->netconn_stream
.data_stream
;
3084 request
->connect_timeout
= session
->connect_timeout
;
3085 request
->send_timeout
= session
->send_timeout
;
3086 request
->receive_timeout
= session
->receive_timeout
;
3088 InitializeCriticalSection( &request
->read_section
);
3089 request
->read_section
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": http_request_t.read_section");
3091 WININET_AddRef( &session
->hdr
);
3092 request
->session
= session
;
3093 list_add_head( &session
->hdr
.children
, &request
->hdr
.entry
);
3095 if (dwFlags
& INTERNET_FLAG_IGNORE_CERT_CN_INVALID
)
3096 request
->security_flags
|= SECURITY_FLAG_IGNORE_CERT_CN_INVALID
;
3097 if (dwFlags
& INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
)
3098 request
->security_flags
|= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
3100 if (lpszObjectName
&& *lpszObjectName
) {
3104 rc
= UrlEscapeW(lpszObjectName
, NULL
, &len
, URL_ESCAPE_SPACES_ONLY
);
3105 if (rc
!= E_POINTER
)
3106 len
= strlenW(lpszObjectName
)+1;
3107 request
->path
= heap_alloc(len
*sizeof(WCHAR
));
3108 rc
= UrlEscapeW(lpszObjectName
, request
->path
, &len
,
3109 URL_ESCAPE_SPACES_ONLY
);
3112 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName
),rc
);
3113 strcpyW(request
->path
,lpszObjectName
);
3116 static const WCHAR slashW
[] = {'/',0};
3118 request
->path
= heap_strdupW(slashW
);
3121 if (lpszReferrer
&& *lpszReferrer
)
3122 HTTP_ProcessHeader(request
, HTTP_REFERER
, lpszReferrer
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3124 if (lpszAcceptTypes
)
3127 for (i
= 0; lpszAcceptTypes
[i
]; i
++)
3129 if (!*lpszAcceptTypes
[i
]) continue;
3130 HTTP_ProcessHeader(request
, HTTP_ACCEPT
, lpszAcceptTypes
[i
],
3131 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
|
3132 HTTP_ADDHDR_FLAG_REQ
|
3133 (i
== 0 ? HTTP_ADDHDR_FLAG_REPLACE
: 0));
3137 request
->verb
= heap_strdupW(lpszVerb
&& *lpszVerb
? lpszVerb
: szGET
);
3138 request
->version
= heap_strdupW(lpszVersion
? lpszVersion
: g_szHttp1_1
);
3140 if (session
->hostPort
!= INTERNET_INVALID_PORT_NUMBER
&&
3141 session
->hostPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
3142 session
->hostPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3146 static const WCHAR host_formatW
[] = {'%','s',':','%','u',0};
3148 host_name
= heap_alloc((strlenW(session
->hostName
) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR
));
3150 res
= ERROR_OUTOFMEMORY
;
3154 sprintfW(host_name
, host_formatW
, session
->hostName
, session
->hostPort
);
3155 HTTP_ProcessHeader(request
, hostW
, host_name
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3156 heap_free(host_name
);
3159 HTTP_ProcessHeader(request
, hostW
, session
->hostName
,
3160 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3162 if (session
->serverPort
== INTERNET_INVALID_PORT_NUMBER
)
3163 session
->serverPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
3164 INTERNET_DEFAULT_HTTPS_PORT
:
3165 INTERNET_DEFAULT_HTTP_PORT
);
3167 if (session
->hostPort
== INTERNET_INVALID_PORT_NUMBER
)
3168 session
->hostPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
3169 INTERNET_DEFAULT_HTTPS_PORT
:
3170 INTERNET_DEFAULT_HTTP_PORT
);
3172 if (hIC
->proxy
&& hIC
->proxy
[0])
3173 HTTP_DealWithProxy( hIC
, session
, request
);
3175 INTERNET_SendCallback(&session
->hdr
, dwContext
,
3176 INTERNET_STATUS_HANDLE_CREATED
, &request
->hdr
.hInternet
,
3180 TRACE("<-- %u (%p)\n", res
, request
);
3182 if(res
!= ERROR_SUCCESS
) {
3183 WININET_Release( &request
->hdr
);
3188 *ret
= request
->hdr
.hInternet
;
3189 return ERROR_SUCCESS
;
3192 /***********************************************************************
3193 * HttpOpenRequestW (WININET.@)
3195 * Open a HTTP request handle
3198 * HINTERNET a HTTP request handle on success
3202 HINTERNET WINAPI
HttpOpenRequestW(HINTERNET hHttpSession
,
3203 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
3204 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
3205 DWORD dwFlags
, DWORD_PTR dwContext
)
3207 http_session_t
*session
;
3208 HINTERNET handle
= NULL
;
3211 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
3212 debugstr_w(lpszVerb
), debugstr_w(lpszObjectName
),
3213 debugstr_w(lpszVersion
), debugstr_w(lpszReferrer
), lpszAcceptTypes
,
3214 dwFlags
, dwContext
);
3215 if(lpszAcceptTypes
!=NULL
)
3218 for(i
=0;lpszAcceptTypes
[i
]!=NULL
;i
++)
3219 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes
[i
]));
3222 session
= (http_session_t
*) get_handle_object( hHttpSession
);
3223 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
3225 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3230 * My tests seem to show that the windows version does not
3231 * become asynchronous until after this point. And anyhow
3232 * if this call was asynchronous then how would you get the
3233 * necessary HINTERNET pointer returned by this function.
3236 res
= HTTP_HttpOpenRequestW(session
, lpszVerb
, lpszObjectName
,
3237 lpszVersion
, lpszReferrer
, lpszAcceptTypes
,
3238 dwFlags
, dwContext
, &handle
);
3241 WININET_Release( &session
->hdr
);
3242 TRACE("returning %p\n", handle
);
3243 if(res
!= ERROR_SUCCESS
)
3248 static const LPCWSTR header_lookup
[] = {
3249 szMime_Version
, /* HTTP_QUERY_MIME_VERSION = 0 */
3250 szContent_Type
, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3251 szContent_Transfer_Encoding
,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3252 szContent_ID
, /* HTTP_QUERY_CONTENT_ID = 3 */
3253 NULL
, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3254 szContent_Length
, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3255 szContent_Language
, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3256 szAllow
, /* HTTP_QUERY_ALLOW = 7 */
3257 szPublic
, /* HTTP_QUERY_PUBLIC = 8 */
3258 szDate
, /* HTTP_QUERY_DATE = 9 */
3259 szExpires
, /* HTTP_QUERY_EXPIRES = 10 */
3260 szLast_Modified
, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3261 NULL
, /* HTTP_QUERY_MESSAGE_ID = 12 */
3262 szURI
, /* HTTP_QUERY_URI = 13 */
3263 szFrom
, /* HTTP_QUERY_DERIVED_FROM = 14 */
3264 NULL
, /* HTTP_QUERY_COST = 15 */
3265 NULL
, /* HTTP_QUERY_LINK = 16 */
3266 szPragma
, /* HTTP_QUERY_PRAGMA = 17 */
3267 NULL
, /* HTTP_QUERY_VERSION = 18 */
3268 szStatus
, /* HTTP_QUERY_STATUS_CODE = 19 */
3269 NULL
, /* HTTP_QUERY_STATUS_TEXT = 20 */
3270 NULL
, /* HTTP_QUERY_RAW_HEADERS = 21 */
3271 NULL
, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3272 szConnection
, /* HTTP_QUERY_CONNECTION = 23 */
3273 szAccept
, /* HTTP_QUERY_ACCEPT = 24 */
3274 szAccept_Charset
, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3275 szAccept_Encoding
, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3276 szAccept_Language
, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3277 szAuthorization
, /* HTTP_QUERY_AUTHORIZATION = 28 */
3278 szContent_Encoding
, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3279 NULL
, /* HTTP_QUERY_FORWARDED = 30 */
3280 NULL
, /* HTTP_QUERY_FROM = 31 */
3281 szIf_Modified_Since
, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3282 szLocation
, /* HTTP_QUERY_LOCATION = 33 */
3283 NULL
, /* HTTP_QUERY_ORIG_URI = 34 */
3284 szReferer
, /* HTTP_QUERY_REFERER = 35 */
3285 szRetry_After
, /* HTTP_QUERY_RETRY_AFTER = 36 */
3286 szServer
, /* HTTP_QUERY_SERVER = 37 */
3287 NULL
, /* HTTP_TITLE = 38 */
3288 szUser_Agent
, /* HTTP_QUERY_USER_AGENT = 39 */
3289 szWWW_Authenticate
, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3290 szProxy_Authenticate
, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3291 szAccept_Ranges
, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3292 szSet_Cookie
, /* HTTP_QUERY_SET_COOKIE = 43 */
3293 szCookie
, /* HTTP_QUERY_COOKIE = 44 */
3294 NULL
, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3295 NULL
, /* HTTP_QUERY_REFRESH = 46 */
3296 NULL
, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3297 szAge
, /* HTTP_QUERY_AGE = 48 */
3298 szCache_Control
, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3299 szContent_Base
, /* HTTP_QUERY_CONTENT_BASE = 50 */
3300 szContent_Location
, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3301 szContent_MD5
, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3302 szContent_Range
, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3303 szETag
, /* HTTP_QUERY_ETAG = 54 */
3304 hostW
, /* HTTP_QUERY_HOST = 55 */
3305 szIf_Match
, /* HTTP_QUERY_IF_MATCH = 56 */
3306 szIf_None_Match
, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3307 szIf_Range
, /* HTTP_QUERY_IF_RANGE = 58 */
3308 szIf_Unmodified_Since
, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3309 szMax_Forwards
, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3310 szProxy_Authorization
, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3311 szRange
, /* HTTP_QUERY_RANGE = 62 */
3312 szTransfer_Encoding
, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3313 szUpgrade
, /* HTTP_QUERY_UPGRADE = 64 */
3314 szVary
, /* HTTP_QUERY_VARY = 65 */
3315 szVia
, /* HTTP_QUERY_VIA = 66 */
3316 szWarning
, /* HTTP_QUERY_WARNING = 67 */
3317 szExpect
, /* HTTP_QUERY_EXPECT = 68 */
3318 szProxy_Connection
, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3319 szUnless_Modified_Since
, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3322 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3324 /***********************************************************************
3325 * HTTP_HttpQueryInfoW (internal)
3327 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*request
, DWORD dwInfoLevel
,
3328 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3330 LPHTTPHEADERW lphttpHdr
= NULL
;
3331 BOOL request_only
= dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
;
3332 INT requested_index
= lpdwIndex
? *lpdwIndex
: 0;
3333 DWORD level
= (dwInfoLevel
& ~HTTP_QUERY_MODIFIER_FLAGS_MASK
);
3336 /* Find requested header structure */
3339 case HTTP_QUERY_CUSTOM
:
3340 if (!lpBuffer
) return ERROR_INVALID_PARAMETER
;
3341 index
= HTTP_GetCustomHeaderIndex(request
, lpBuffer
, requested_index
, request_only
);
3343 case HTTP_QUERY_RAW_HEADERS_CRLF
:
3347 DWORD res
= ERROR_INVALID_PARAMETER
;
3350 headers
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
3352 headers
= request
->rawHeaders
;
3355 len
= strlenW(headers
) * sizeof(WCHAR
);
3357 if (len
+ sizeof(WCHAR
) > *lpdwBufferLength
)
3359 len
+= sizeof(WCHAR
);
3360 res
= ERROR_INSUFFICIENT_BUFFER
;
3365 memcpy(lpBuffer
, headers
, len
+ sizeof(WCHAR
));
3368 len
= strlenW(szCrLf
) * sizeof(WCHAR
);
3369 memcpy(lpBuffer
, szCrLf
, sizeof(szCrLf
));
3371 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
/ sizeof(WCHAR
)));
3372 res
= ERROR_SUCCESS
;
3374 *lpdwBufferLength
= len
;
3376 if (request_only
) heap_free(headers
);
3379 case HTTP_QUERY_RAW_HEADERS
:
3381 LPWSTR
* ppszRawHeaderLines
= HTTP_Tokenize(request
->rawHeaders
, szCrLf
);
3383 LPWSTR pszString
= lpBuffer
;
3385 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3386 size
+= strlenW(ppszRawHeaderLines
[i
]) + 1;
3388 if (size
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3390 HTTP_FreeTokens(ppszRawHeaderLines
);
3391 *lpdwBufferLength
= (size
+ 1) * sizeof(WCHAR
);
3392 return ERROR_INSUFFICIENT_BUFFER
;
3396 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3398 DWORD len
= strlenW(ppszRawHeaderLines
[i
]);
3399 memcpy(pszString
, ppszRawHeaderLines
[i
], (len
+1)*sizeof(WCHAR
));
3403 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, size
));
3405 *lpdwBufferLength
= size
* sizeof(WCHAR
);
3406 HTTP_FreeTokens(ppszRawHeaderLines
);
3408 return ERROR_SUCCESS
;
3410 case HTTP_QUERY_STATUS_TEXT
:
3411 if (request
->statusText
)
3413 DWORD len
= strlenW(request
->statusText
);
3414 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3416 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3417 return ERROR_INSUFFICIENT_BUFFER
;
3421 memcpy(lpBuffer
, request
->statusText
, (len
+ 1) * sizeof(WCHAR
));
3422 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3424 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3425 return ERROR_SUCCESS
;
3428 case HTTP_QUERY_VERSION
:
3429 if (request
->version
)
3431 DWORD len
= strlenW(request
->version
);
3432 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3434 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3435 return ERROR_INSUFFICIENT_BUFFER
;
3439 memcpy(lpBuffer
, request
->version
, (len
+ 1) * sizeof(WCHAR
));
3440 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3442 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3443 return ERROR_SUCCESS
;
3446 case HTTP_QUERY_CONTENT_ENCODING
:
3447 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[request
->read_gzip
? HTTP_QUERY_CONTENT_TYPE
: level
],
3448 requested_index
,request_only
);
3450 case HTTP_QUERY_STATUS_CODE
: {
3451 DWORD res
= ERROR_SUCCESS
;
3453 if(request_only
|| requested_index
)
3456 if(dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) {
3457 if(*lpdwBufferLength
>= sizeof(DWORD
))
3458 *(DWORD
*)lpBuffer
= request
->status_code
;
3460 res
= ERROR_INSUFFICIENT_BUFFER
;
3461 *lpdwBufferLength
= sizeof(DWORD
);
3465 static const WCHAR formatW
[] = {'%','u',0};
3467 size
= (sprintfW(buf
, formatW
, request
->status_code
)+1) * sizeof(WCHAR
);
3469 if(size
<= *lpdwBufferLength
)
3470 memcpy(lpBuffer
, buf
, size
);
3472 res
= ERROR_INSUFFICIENT_BUFFER
;
3474 *lpdwBufferLength
= size
;
3479 assert (LAST_TABLE_HEADER
== (HTTP_QUERY_UNLESS_MODIFIED_SINCE
+ 1));
3481 if (level
< LAST_TABLE_HEADER
&& header_lookup
[level
])
3482 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[level
],
3483 requested_index
,request_only
);
3487 lphttpHdr
= &request
->custHeaders
[index
];
3489 /* Ensure header satisfies requested attributes */
3491 ((dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
) &&
3492 (~lphttpHdr
->wFlags
& HDR_ISREQUEST
)))
3494 return ERROR_HTTP_HEADER_NOT_FOUND
;
3497 if (lpdwIndex
) (*lpdwIndex
)++;
3499 /* coalesce value to requested type */
3500 if (dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
&& lpBuffer
)
3502 *(int *)lpBuffer
= atoiW(lphttpHdr
->lpszValue
);
3503 TRACE(" returning number: %d\n", *(int *)lpBuffer
);
3505 else if (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
&& lpBuffer
)
3511 tmpTime
= ConvertTimeString(lphttpHdr
->lpszValue
);
3513 tmpTM
= *gmtime(&tmpTime
);
3514 STHook
= (SYSTEMTIME
*)lpBuffer
;
3515 STHook
->wDay
= tmpTM
.tm_mday
;
3516 STHook
->wHour
= tmpTM
.tm_hour
;
3517 STHook
->wMilliseconds
= 0;
3518 STHook
->wMinute
= tmpTM
.tm_min
;
3519 STHook
->wDayOfWeek
= tmpTM
.tm_wday
;
3520 STHook
->wMonth
= tmpTM
.tm_mon
+ 1;
3521 STHook
->wSecond
= tmpTM
.tm_sec
;
3522 STHook
->wYear
= tmpTM
.tm_year
;
3524 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3525 STHook
->wYear
, STHook
->wMonth
, STHook
->wDay
, STHook
->wDayOfWeek
,
3526 STHook
->wHour
, STHook
->wMinute
, STHook
->wSecond
, STHook
->wMilliseconds
);
3528 else if (lphttpHdr
->lpszValue
)
3530 DWORD len
= (strlenW(lphttpHdr
->lpszValue
) + 1) * sizeof(WCHAR
);
3532 if (len
> *lpdwBufferLength
)
3534 *lpdwBufferLength
= len
;
3535 return ERROR_INSUFFICIENT_BUFFER
;
3539 memcpy(lpBuffer
, lphttpHdr
->lpszValue
, len
);
3540 TRACE("! returning string: %s\n", debugstr_w(lpBuffer
));
3542 *lpdwBufferLength
= len
- sizeof(WCHAR
);
3544 return ERROR_SUCCESS
;
3547 /***********************************************************************
3548 * HttpQueryInfoW (WININET.@)
3550 * Queries for information about an HTTP request
3557 BOOL WINAPI
HttpQueryInfoW(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3558 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3560 http_request_t
*request
;
3563 if (TRACE_ON(wininet
)) {
3564 #define FE(x) { x, #x }
3565 static const wininet_flag_info query_flags
[] = {
3566 FE(HTTP_QUERY_MIME_VERSION
),
3567 FE(HTTP_QUERY_CONTENT_TYPE
),
3568 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING
),
3569 FE(HTTP_QUERY_CONTENT_ID
),
3570 FE(HTTP_QUERY_CONTENT_DESCRIPTION
),
3571 FE(HTTP_QUERY_CONTENT_LENGTH
),
3572 FE(HTTP_QUERY_CONTENT_LANGUAGE
),
3573 FE(HTTP_QUERY_ALLOW
),
3574 FE(HTTP_QUERY_PUBLIC
),
3575 FE(HTTP_QUERY_DATE
),
3576 FE(HTTP_QUERY_EXPIRES
),
3577 FE(HTTP_QUERY_LAST_MODIFIED
),
3578 FE(HTTP_QUERY_MESSAGE_ID
),
3580 FE(HTTP_QUERY_DERIVED_FROM
),
3581 FE(HTTP_QUERY_COST
),
3582 FE(HTTP_QUERY_LINK
),
3583 FE(HTTP_QUERY_PRAGMA
),
3584 FE(HTTP_QUERY_VERSION
),
3585 FE(HTTP_QUERY_STATUS_CODE
),
3586 FE(HTTP_QUERY_STATUS_TEXT
),
3587 FE(HTTP_QUERY_RAW_HEADERS
),
3588 FE(HTTP_QUERY_RAW_HEADERS_CRLF
),
3589 FE(HTTP_QUERY_CONNECTION
),
3590 FE(HTTP_QUERY_ACCEPT
),
3591 FE(HTTP_QUERY_ACCEPT_CHARSET
),
3592 FE(HTTP_QUERY_ACCEPT_ENCODING
),
3593 FE(HTTP_QUERY_ACCEPT_LANGUAGE
),
3594 FE(HTTP_QUERY_AUTHORIZATION
),
3595 FE(HTTP_QUERY_CONTENT_ENCODING
),
3596 FE(HTTP_QUERY_FORWARDED
),
3597 FE(HTTP_QUERY_FROM
),
3598 FE(HTTP_QUERY_IF_MODIFIED_SINCE
),
3599 FE(HTTP_QUERY_LOCATION
),
3600 FE(HTTP_QUERY_ORIG_URI
),
3601 FE(HTTP_QUERY_REFERER
),
3602 FE(HTTP_QUERY_RETRY_AFTER
),
3603 FE(HTTP_QUERY_SERVER
),
3604 FE(HTTP_QUERY_TITLE
),
3605 FE(HTTP_QUERY_USER_AGENT
),
3606 FE(HTTP_QUERY_WWW_AUTHENTICATE
),
3607 FE(HTTP_QUERY_PROXY_AUTHENTICATE
),
3608 FE(HTTP_QUERY_ACCEPT_RANGES
),
3609 FE(HTTP_QUERY_SET_COOKIE
),
3610 FE(HTTP_QUERY_COOKIE
),
3611 FE(HTTP_QUERY_REQUEST_METHOD
),
3612 FE(HTTP_QUERY_REFRESH
),
3613 FE(HTTP_QUERY_CONTENT_DISPOSITION
),
3615 FE(HTTP_QUERY_CACHE_CONTROL
),
3616 FE(HTTP_QUERY_CONTENT_BASE
),
3617 FE(HTTP_QUERY_CONTENT_LOCATION
),
3618 FE(HTTP_QUERY_CONTENT_MD5
),
3619 FE(HTTP_QUERY_CONTENT_RANGE
),
3620 FE(HTTP_QUERY_ETAG
),
3621 FE(HTTP_QUERY_HOST
),
3622 FE(HTTP_QUERY_IF_MATCH
),
3623 FE(HTTP_QUERY_IF_NONE_MATCH
),
3624 FE(HTTP_QUERY_IF_RANGE
),
3625 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE
),
3626 FE(HTTP_QUERY_MAX_FORWARDS
),
3627 FE(HTTP_QUERY_PROXY_AUTHORIZATION
),
3628 FE(HTTP_QUERY_RANGE
),
3629 FE(HTTP_QUERY_TRANSFER_ENCODING
),
3630 FE(HTTP_QUERY_UPGRADE
),
3631 FE(HTTP_QUERY_VARY
),
3633 FE(HTTP_QUERY_WARNING
),
3634 FE(HTTP_QUERY_CUSTOM
)
3636 static const wininet_flag_info modifier_flags
[] = {
3637 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS
),
3638 FE(HTTP_QUERY_FLAG_SYSTEMTIME
),
3639 FE(HTTP_QUERY_FLAG_NUMBER
),
3640 FE(HTTP_QUERY_FLAG_COALESCE
)
3643 DWORD info_mod
= dwInfoLevel
& HTTP_QUERY_MODIFIER_FLAGS_MASK
;
3644 DWORD info
= dwInfoLevel
& HTTP_QUERY_HEADER_MASK
;
3647 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest
, dwInfoLevel
, info
);
3648 TRACE(" Attribute:");
3649 for (i
= 0; i
< (sizeof(query_flags
) / sizeof(query_flags
[0])); i
++) {
3650 if (query_flags
[i
].val
== info
) {
3651 TRACE(" %s", query_flags
[i
].name
);
3655 if (i
== (sizeof(query_flags
) / sizeof(query_flags
[0]))) {
3656 TRACE(" Unknown (%08x)", info
);
3659 TRACE(" Modifier:");
3660 for (i
= 0; i
< (sizeof(modifier_flags
) / sizeof(modifier_flags
[0])); i
++) {
3661 if (modifier_flags
[i
].val
& info_mod
) {
3662 TRACE(" %s", modifier_flags
[i
].name
);
3663 info_mod
&= ~ modifier_flags
[i
].val
;
3668 TRACE(" Unknown (%08x)", info_mod
);
3673 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
3674 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
3676 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3680 if (lpBuffer
== NULL
)
3681 *lpdwBufferLength
= 0;
3682 res
= HTTP_HttpQueryInfoW( request
, dwInfoLevel
,
3683 lpBuffer
, lpdwBufferLength
, lpdwIndex
);
3687 WININET_Release( &request
->hdr
);
3689 TRACE("%u <--\n", res
);
3690 if(res
!= ERROR_SUCCESS
)
3692 return res
== ERROR_SUCCESS
;
3695 /***********************************************************************
3696 * HttpQueryInfoA (WININET.@)
3698 * Queries for information about an HTTP request
3705 BOOL WINAPI
HttpQueryInfoA(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3706 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3712 if((dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) ||
3713 (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
))
3715 return HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, lpBuffer
,
3716 lpdwBufferLength
, lpdwIndex
);
3722 len
= (*lpdwBufferLength
)*sizeof(WCHAR
);
3723 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3725 alloclen
= MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, NULL
, 0 ) * sizeof(WCHAR
);
3731 bufferW
= heap_alloc(alloclen
);
3732 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3733 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3734 MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, bufferW
, alloclen
/ sizeof(WCHAR
) );
3741 result
= HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, bufferW
,
3745 len
= WideCharToMultiByte( CP_ACP
,0, bufferW
, len
/ sizeof(WCHAR
) + 1,
3746 lpBuffer
, *lpdwBufferLength
, NULL
, NULL
);
3747 *lpdwBufferLength
= len
- 1;
3749 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer
));
3752 /* since the strings being returned from HttpQueryInfoW should be
3753 * only ASCII characters, it is reasonable to assume that all of
3754 * the Unicode characters can be reduced to a single byte */
3755 *lpdwBufferLength
= len
/ sizeof(WCHAR
);
3757 heap_free( bufferW
);
3761 /***********************************************************************
3762 * HTTP_GetRedirectURL (internal)
3764 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*request
, LPCWSTR lpszUrl
)
3766 static WCHAR szHttp
[] = {'h','t','t','p',0};
3767 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3768 http_session_t
*session
= request
->session
;
3769 URL_COMPONENTSW urlComponents
;
3770 DWORD url_length
= 0;
3772 LPWSTR combined_url
;
3774 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3775 urlComponents
.lpszScheme
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) ? szHttps
: szHttp
;
3776 urlComponents
.dwSchemeLength
= 0;
3777 urlComponents
.lpszHostName
= session
->hostName
;
3778 urlComponents
.dwHostNameLength
= 0;
3779 urlComponents
.nPort
= session
->hostPort
;
3780 urlComponents
.lpszUserName
= session
->userName
;
3781 urlComponents
.dwUserNameLength
= 0;
3782 urlComponents
.lpszPassword
= NULL
;
3783 urlComponents
.dwPasswordLength
= 0;
3784 urlComponents
.lpszUrlPath
= request
->path
;
3785 urlComponents
.dwUrlPathLength
= 0;
3786 urlComponents
.lpszExtraInfo
= NULL
;
3787 urlComponents
.dwExtraInfoLength
= 0;
3789 if (!InternetCreateUrlW(&urlComponents
, 0, NULL
, &url_length
) &&
3790 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3793 orig_url
= heap_alloc(url_length
);
3795 /* convert from bytes to characters */
3796 url_length
= url_length
/ sizeof(WCHAR
) - 1;
3797 if (!InternetCreateUrlW(&urlComponents
, 0, orig_url
, &url_length
))
3799 heap_free(orig_url
);
3804 if (!InternetCombineUrlW(orig_url
, lpszUrl
, NULL
, &url_length
, ICU_ENCODE_SPACES_ONLY
) &&
3805 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3807 heap_free(orig_url
);
3810 combined_url
= heap_alloc(url_length
* sizeof(WCHAR
));
3812 if (!InternetCombineUrlW(orig_url
, lpszUrl
, combined_url
, &url_length
, ICU_ENCODE_SPACES_ONLY
))
3814 heap_free(orig_url
);
3815 heap_free(combined_url
);
3818 heap_free(orig_url
);
3819 return combined_url
;
3823 /***********************************************************************
3824 * HTTP_HandleRedirect (internal)
3826 static DWORD
HTTP_HandleRedirect(http_request_t
*request
, LPCWSTR lpszUrl
)
3828 http_session_t
*session
= request
->session
;
3829 appinfo_t
*hIC
= session
->appInfo
;
3830 BOOL using_proxy
= hIC
->proxy
&& hIC
->proxy
[0];
3831 WCHAR path
[INTERNET_MAX_PATH_LENGTH
];
3836 /* if it's an absolute path, keep the same session info */
3837 lstrcpynW(path
, lpszUrl
, INTERNET_MAX_URL_LENGTH
);
3841 URL_COMPONENTSW urlComponents
;
3842 WCHAR protocol
[INTERNET_MAX_SCHEME_LENGTH
];
3843 WCHAR hostName
[INTERNET_MAX_HOST_NAME_LENGTH
];
3844 WCHAR userName
[INTERNET_MAX_USER_NAME_LENGTH
];
3845 BOOL custom_port
= FALSE
;
3847 static WCHAR httpW
[] = {'h','t','t','p',0};
3848 static WCHAR httpsW
[] = {'h','t','t','p','s',0};
3854 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3855 urlComponents
.lpszScheme
= protocol
;
3856 urlComponents
.dwSchemeLength
= INTERNET_MAX_SCHEME_LENGTH
;
3857 urlComponents
.lpszHostName
= hostName
;
3858 urlComponents
.dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
3859 urlComponents
.lpszUserName
= userName
;
3860 urlComponents
.dwUserNameLength
= INTERNET_MAX_USER_NAME_LENGTH
;
3861 urlComponents
.lpszPassword
= NULL
;
3862 urlComponents
.dwPasswordLength
= 0;
3863 urlComponents
.lpszUrlPath
= path
;
3864 urlComponents
.dwUrlPathLength
= INTERNET_MAX_PATH_LENGTH
;
3865 urlComponents
.lpszExtraInfo
= NULL
;
3866 urlComponents
.dwExtraInfoLength
= 0;
3867 if(!InternetCrackUrlW(lpszUrl
, strlenW(lpszUrl
), 0, &urlComponents
))
3868 return INTERNET_GetLastError();
3870 if(!strcmpiW(protocol
, httpW
)) {
3871 if(request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) {
3872 TRACE("redirect from secure page to non-secure page\n");
3873 /* FIXME: warn about from secure redirect to non-secure page */
3874 request
->hdr
.dwFlags
&= ~INTERNET_FLAG_SECURE
;
3877 if(urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3878 urlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
3879 else if(urlComponents
.nPort
!= INTERNET_DEFAULT_HTTP_PORT
)
3881 }else if(!strcmpiW(protocol
, httpsW
)) {
3882 if(!(request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)) {
3883 TRACE("redirect from non-secure page to secure page\n");
3884 /* FIXME: notify about redirect to secure page */
3885 request
->hdr
.dwFlags
|= INTERNET_FLAG_SECURE
;
3888 if(urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3889 urlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
3890 else if(urlComponents
.nPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3894 heap_free(session
->hostName
);
3898 static const WCHAR fmt
[] = {'%','s',':','%','u',0};
3899 len
= lstrlenW(hostName
);
3900 len
+= 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3901 session
->hostName
= heap_alloc(len
*sizeof(WCHAR
));
3902 sprintfW(session
->hostName
, fmt
, hostName
, urlComponents
.nPort
);
3905 session
->hostName
= heap_strdupW(hostName
);
3907 HTTP_ProcessHeader(request
, hostW
, session
->hostName
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDREQ_FLAG_REPLACE
| HTTP_ADDHDR_FLAG_REQ
);
3909 heap_free(session
->userName
);
3910 session
->userName
= NULL
;
3912 session
->userName
= heap_strdupW(userName
);
3914 reset_data_stream(request
);
3917 if(strcmpiW(session
->serverName
, hostName
)) {
3918 heap_free(session
->serverName
);
3919 session
->serverName
= heap_strdupW(hostName
);
3921 session
->serverPort
= urlComponents
.nPort
;
3924 heap_free(request
->path
);
3931 rc
= UrlEscapeW(path
, NULL
, &needed
, URL_ESCAPE_SPACES_ONLY
);
3932 if (rc
!= E_POINTER
)
3933 needed
= strlenW(path
)+1;
3934 request
->path
= heap_alloc(needed
*sizeof(WCHAR
));
3935 rc
= UrlEscapeW(path
, request
->path
, &needed
,
3936 URL_ESCAPE_SPACES_ONLY
);
3939 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path
),rc
);
3940 strcpyW(request
->path
,path
);
3944 /* Remove custom content-type/length headers on redirects. */
3945 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Type
, 0, TRUE
);
3947 HTTP_DeleteCustomHeader(request
, index
);
3948 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Length
, 0, TRUE
);
3950 HTTP_DeleteCustomHeader(request
, index
);
3952 return ERROR_SUCCESS
;
3955 /***********************************************************************
3956 * HTTP_build_req (internal)
3958 * concatenate all the strings in the request together
3960 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
)
3965 for( t
= list
; *t
; t
++ )
3966 len
+= strlenW( *t
);
3969 str
= heap_alloc(len
*sizeof(WCHAR
));
3972 for( t
= list
; *t
; t
++ )
3978 static DWORD
HTTP_SecureProxyConnect(http_request_t
*request
)
3981 LPWSTR requestString
;
3987 static const WCHAR szConnect
[] = {'C','O','N','N','E','C','T',0};
3988 static const WCHAR szFormat
[] = {'%','s',':','%','u',0};
3989 http_session_t
*session
= request
->session
;
3993 lpszPath
= heap_alloc((lstrlenW( session
->hostName
) + 13)*sizeof(WCHAR
));
3994 sprintfW( lpszPath
, szFormat
, session
->hostName
, session
->hostPort
);
3995 requestString
= HTTP_BuildHeaderRequestString( request
, szConnect
, lpszPath
, g_szHttp1_1
);
3996 heap_free( lpszPath
);
3998 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3999 NULL
, 0, NULL
, NULL
);
4000 len
--; /* the nul terminator isn't needed */
4001 ascii_req
= heap_alloc(len
);
4002 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1, ascii_req
, len
, NULL
, NULL
);
4003 heap_free( requestString
);
4005 TRACE("full request -> %s\n", debugstr_an( ascii_req
, len
) );
4007 NETCON_set_timeout( request
->netconn
, TRUE
, request
->send_timeout
);
4008 res
= NETCON_send( request
->netconn
, ascii_req
, len
, 0, &cnt
);
4009 heap_free( ascii_req
);
4010 if (res
!= ERROR_SUCCESS
)
4013 responseLen
= HTTP_GetResponseHeaders( request
, TRUE
);
4015 return ERROR_HTTP_INVALID_HEADER
;
4017 return ERROR_SUCCESS
;
4020 static void HTTP_InsertCookies(http_request_t
*request
)
4022 DWORD cookie_size
, size
, cnt
= 0;
4026 static const WCHAR cookieW
[] = {'C','o','o','k','i','e',':',' ',0};
4028 host
= HTTP_GetHeader(request
, hostW
);
4032 if(!get_cookie(host
->lpszValue
, request
->path
, NULL
, &cookie_size
))
4035 size
= sizeof(cookieW
) + cookie_size
* sizeof(WCHAR
) + sizeof(szCrLf
);
4036 if(!(cookies
= heap_alloc(size
)))
4039 cnt
+= sprintfW(cookies
, cookieW
);
4040 get_cookie(host
->lpszValue
, request
->path
, cookies
+cnt
, &cookie_size
);
4041 strcatW(cookies
, szCrLf
);
4043 HTTP_HttpAddRequestHeadersW(request
, cookies
, strlenW(cookies
), HTTP_ADDREQ_FLAG_REPLACE
);
4048 static WORD
HTTP_ParseWkday(LPCWSTR day
)
4050 static const WCHAR days
[7][4] = {{ 's','u','n',0 },
4058 for (i
= 0; i
< sizeof(days
)/sizeof(*days
); i
++)
4059 if (!strcmpiW(day
, days
[i
]))
4066 static WORD
HTTP_ParseMonth(LPCWSTR month
)
4068 static const WCHAR jan
[] = { 'j','a','n',0 };
4069 static const WCHAR feb
[] = { 'f','e','b',0 };
4070 static const WCHAR mar
[] = { 'm','a','r',0 };
4071 static const WCHAR apr
[] = { 'a','p','r',0 };
4072 static const WCHAR may
[] = { 'm','a','y',0 };
4073 static const WCHAR jun
[] = { 'j','u','n',0 };
4074 static const WCHAR jul
[] = { 'j','u','l',0 };
4075 static const WCHAR aug
[] = { 'a','u','g',0 };
4076 static const WCHAR sep
[] = { 's','e','p',0 };
4077 static const WCHAR oct
[] = { 'o','c','t',0 };
4078 static const WCHAR nov
[] = { 'n','o','v',0 };
4079 static const WCHAR dec
[] = { 'd','e','c',0 };
4081 if (!strcmpiW(month
, jan
)) return 1;
4082 if (!strcmpiW(month
, feb
)) return 2;
4083 if (!strcmpiW(month
, mar
)) return 3;
4084 if (!strcmpiW(month
, apr
)) return 4;
4085 if (!strcmpiW(month
, may
)) return 5;
4086 if (!strcmpiW(month
, jun
)) return 6;
4087 if (!strcmpiW(month
, jul
)) return 7;
4088 if (!strcmpiW(month
, aug
)) return 8;
4089 if (!strcmpiW(month
, sep
)) return 9;
4090 if (!strcmpiW(month
, oct
)) return 10;
4091 if (!strcmpiW(month
, nov
)) return 11;
4092 if (!strcmpiW(month
, dec
)) return 12;
4097 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4098 * optionally preceded by whitespace.
4099 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4100 * st, and sets *str to the first character after the time format.
4102 static BOOL
HTTP_ParseTime(SYSTEMTIME
*st
, LPCWSTR
*str
)
4108 while (isspaceW(*ptr
))
4111 num
= strtoulW(ptr
, &nextPtr
, 10);
4112 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
4114 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4119 ERR("unexpected hour in time format %s\n", debugstr_w(ptr
));
4123 st
->wHour
= (WORD
)num
;
4124 num
= strtoulW(ptr
, &nextPtr
, 10);
4125 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
4127 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4132 ERR("unexpected minute in time format %s\n", debugstr_w(ptr
));
4136 st
->wMinute
= (WORD
)num
;
4137 num
= strtoulW(ptr
, &nextPtr
, 10);
4138 if (!nextPtr
|| nextPtr
<= ptr
)
4140 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4145 ERR("unexpected second in time format %s\n", debugstr_w(ptr
));
4150 st
->wSecond
= (WORD
)num
;
4154 static BOOL
HTTP_ParseDateAsAsctime(LPCWSTR value
, FILETIME
*ft
)
4156 static const WCHAR gmt
[]= { 'G','M','T',0 };
4157 WCHAR day
[4], *dayPtr
, month
[4], *monthPtr
, *nextPtr
;
4159 SYSTEMTIME st
= { 0 };
4162 for (ptr
= value
, dayPtr
= day
; *ptr
&& !isspaceW(*ptr
) &&
4163 dayPtr
- day
< sizeof(day
) / sizeof(day
[0]) - 1; ptr
++, dayPtr
++)
4166 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4167 if (st
.wDayOfWeek
>= 7)
4169 ERR("unexpected weekday %s\n", debugstr_w(day
));
4173 while (isspaceW(*ptr
))
4176 for (monthPtr
= month
; !isspace(*ptr
) &&
4177 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4181 st
.wMonth
= HTTP_ParseMonth(month
);
4182 if (!st
.wMonth
|| st
.wMonth
> 12)
4184 ERR("unexpected month %s\n", debugstr_w(month
));
4188 while (isspaceW(*ptr
))
4191 num
= strtoulW(ptr
, &nextPtr
, 10);
4192 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4194 ERR("unexpected day %s\n", debugstr_w(ptr
));
4198 st
.wDay
= (WORD
)num
;
4200 while (isspaceW(*ptr
))
4203 if (!HTTP_ParseTime(&st
, &ptr
))
4206 while (isspaceW(*ptr
))
4209 num
= strtoulW(ptr
, &nextPtr
, 10);
4210 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4212 ERR("unexpected year %s\n", debugstr_w(ptr
));
4216 st
.wYear
= (WORD
)num
;
4218 while (isspaceW(*ptr
))
4221 /* asctime() doesn't report a timezone, but some web servers do, so accept
4222 * with or without GMT.
4224 if (*ptr
&& strcmpW(ptr
, gmt
))
4226 ERR("unexpected timezone %s\n", debugstr_w(ptr
));
4229 return SystemTimeToFileTime(&st
, ft
);
4232 static BOOL
HTTP_ParseRfc1123Date(LPCWSTR value
, FILETIME
*ft
)
4234 static const WCHAR gmt
[]= { 'G','M','T',0 };
4235 WCHAR
*nextPtr
, day
[4], month
[4], *monthPtr
;
4238 SYSTEMTIME st
= { 0 };
4240 ptr
= strchrW(value
, ',');
4243 if (ptr
- value
!= 3)
4245 WARN("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4248 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4250 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4251 if (st
.wDayOfWeek
> 6)
4253 WARN("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4258 while (isspaceW(*ptr
))
4261 num
= strtoulW(ptr
, &nextPtr
, 10);
4262 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4264 WARN("unexpected day %s\n", debugstr_w(value
));
4268 st
.wDay
= (WORD
)num
;
4270 while (isspaceW(*ptr
))
4273 for (monthPtr
= month
; !isspace(*ptr
) &&
4274 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4278 st
.wMonth
= HTTP_ParseMonth(month
);
4279 if (!st
.wMonth
|| st
.wMonth
> 12)
4281 WARN("unexpected month %s\n", debugstr_w(month
));
4285 while (isspaceW(*ptr
))
4288 num
= strtoulW(ptr
, &nextPtr
, 10);
4289 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4291 ERR("unexpected year %s\n", debugstr_w(value
));
4295 st
.wYear
= (WORD
)num
;
4297 if (!HTTP_ParseTime(&st
, &ptr
))
4300 while (isspaceW(*ptr
))
4303 if (strcmpW(ptr
, gmt
))
4305 ERR("unexpected time zone %s\n", debugstr_w(ptr
));
4308 return SystemTimeToFileTime(&st
, ft
);
4311 static WORD
HTTP_ParseWeekday(LPCWSTR day
)
4313 static const WCHAR days
[7][10] = {{ 's','u','n','d','a','y',0 },
4314 { 'm','o','n','d','a','y',0 },
4315 { 't','u','e','s','d','a','y',0 },
4316 { 'w','e','d','n','e','s','d','a','y',0 },
4317 { 't','h','u','r','s','d','a','y',0 },
4318 { 'f','r','i','d','a','y',0 },
4319 { 's','a','t','u','r','d','a','y',0 }};
4321 for (i
= 0; i
< sizeof(days
)/sizeof(*days
); i
++)
4322 if (!strcmpiW(day
, days
[i
]))
4329 static BOOL
HTTP_ParseRfc850Date(LPCWSTR value
, FILETIME
*ft
)
4331 static const WCHAR gmt
[]= { 'G','M','T',0 };
4332 WCHAR
*nextPtr
, day
[10], month
[4], *monthPtr
;
4335 SYSTEMTIME st
= { 0 };
4337 ptr
= strchrW(value
, ',');
4340 if (ptr
- value
== 3)
4342 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4344 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4345 if (st
.wDayOfWeek
> 6)
4347 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4351 else if (ptr
- value
< sizeof(day
) / sizeof(day
[0]))
4353 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4354 day
[ptr
- value
+ 1] = 0;
4355 st
.wDayOfWeek
= HTTP_ParseWeekday(day
);
4356 if (st
.wDayOfWeek
> 6)
4358 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4364 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4369 while (isspaceW(*ptr
))
4372 num
= strtoulW(ptr
, &nextPtr
, 10);
4373 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4375 ERR("unexpected day %s\n", debugstr_w(value
));
4379 st
.wDay
= (WORD
)num
;
4383 ERR("unexpected month format %s\n", debugstr_w(ptr
));
4388 for (monthPtr
= month
; *ptr
!= '-' &&
4389 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4393 st
.wMonth
= HTTP_ParseMonth(month
);
4394 if (!st
.wMonth
|| st
.wMonth
> 12)
4396 ERR("unexpected month %s\n", debugstr_w(month
));
4402 ERR("unexpected year format %s\n", debugstr_w(ptr
));
4407 num
= strtoulW(ptr
, &nextPtr
, 10);
4408 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4410 ERR("unexpected year %s\n", debugstr_w(value
));
4414 st
.wYear
= (WORD
)num
;
4416 if (!HTTP_ParseTime(&st
, &ptr
))
4419 while (isspaceW(*ptr
))
4422 if (strcmpW(ptr
, gmt
))
4424 ERR("unexpected time zone %s\n", debugstr_w(ptr
));
4427 return SystemTimeToFileTime(&st
, ft
);
4430 static BOOL
HTTP_ParseDate(LPCWSTR value
, FILETIME
*ft
)
4432 static const WCHAR zero
[] = { '0',0 };
4435 if (!strcmpW(value
, zero
))
4437 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
4440 else if (strchrW(value
, ','))
4442 ret
= HTTP_ParseRfc1123Date(value
, ft
);
4445 ret
= HTTP_ParseRfc850Date(value
, ft
);
4447 ERR("unexpected date format %s\n", debugstr_w(value
));
4452 ret
= HTTP_ParseDateAsAsctime(value
, ft
);
4454 ERR("unexpected date format %s\n", debugstr_w(value
));
4459 static void HTTP_ProcessExpires(http_request_t
*request
)
4461 BOOL expirationFound
= FALSE
;
4464 /* Look for a Cache-Control header with a max-age directive, as it takes
4465 * precedence over the Expires header.
4467 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szCache_Control
, 0, FALSE
);
4468 if (headerIndex
!= -1)
4470 LPHTTPHEADERW ccHeader
= &request
->custHeaders
[headerIndex
];
4473 for (ptr
= ccHeader
->lpszValue
; ptr
&& *ptr
; )
4475 LPWSTR comma
= strchrW(ptr
, ','), end
, equal
;
4480 end
= ptr
+ strlenW(ptr
);
4481 for (equal
= end
- 1; equal
> ptr
&& *equal
!= '='; equal
--)
4485 static const WCHAR max_age
[] = {
4486 'm','a','x','-','a','g','e',0 };
4488 if (!strncmpiW(ptr
, max_age
, equal
- ptr
- 1))
4493 age
= strtoulW(equal
+ 1, &nextPtr
, 10);
4494 if (nextPtr
> equal
+ 1)
4498 NtQuerySystemTime( &ft
);
4499 /* Age is in seconds, FILETIME resolution is in
4500 * 100 nanosecond intervals.
4502 ft
.QuadPart
+= age
* (ULONGLONG
)1000000;
4503 request
->expires
.dwLowDateTime
= ft
.u
.LowPart
;
4504 request
->expires
.dwHighDateTime
= ft
.u
.HighPart
;
4505 expirationFound
= TRUE
;
4512 while (isspaceW(*ptr
))
4519 if (!expirationFound
)
4521 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szExpires
, 0, FALSE
);
4522 if (headerIndex
!= -1)
4524 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4527 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4529 expirationFound
= TRUE
;
4530 request
->expires
= ft
;
4534 if (!expirationFound
)
4538 /* With no known age, default to 10 minutes until expiration. */
4539 NtQuerySystemTime( &t
);
4540 t
.QuadPart
+= 10 * 60 * (ULONGLONG
)10000000;
4541 request
->expires
.dwLowDateTime
= t
.u
.LowPart
;
4542 request
->expires
.dwHighDateTime
= t
.u
.HighPart
;
4546 static void HTTP_ProcessLastModified(http_request_t
*request
)
4550 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szLast_Modified
, 0, FALSE
);
4551 if (headerIndex
!= -1)
4553 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4556 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4557 request
->last_modified
= ft
;
4561 static void http_process_keep_alive(http_request_t
*req
)
4565 index
= HTTP_GetCustomHeaderIndex(req
, szConnection
, 0, FALSE
);
4567 req
->netconn
->keep_alive
= !strcmpiW(req
->custHeaders
[index
].lpszValue
, szKeepAlive
);
4569 req
->netconn
->keep_alive
= !strcmpiW(req
->version
, g_szHttp1_1
);
4572 static void HTTP_CacheRequest(http_request_t
*request
)
4574 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
4575 WCHAR cacheFileName
[MAX_PATH
+1];
4578 b
= HTTP_GetRequestURL(request
, url
);
4580 WARN("Could not get URL\n");
4584 b
= CreateUrlCacheEntryW(url
, request
->contentLength
, NULL
, cacheFileName
, 0);
4586 heap_free(request
->cacheFile
);
4587 CloseHandle(request
->hCacheFile
);
4589 request
->cacheFile
= heap_strdupW(cacheFileName
);
4590 request
->hCacheFile
= CreateFileW(request
->cacheFile
, GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
4591 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
4592 if(request
->hCacheFile
== INVALID_HANDLE_VALUE
) {
4593 WARN("Could not create file: %u\n", GetLastError());
4594 request
->hCacheFile
= NULL
;
4597 WARN("Could not create cache entry: %08x\n", GetLastError());
4601 static DWORD
open_http_connection(http_request_t
*request
, BOOL
*reusing
)
4603 const BOOL is_https
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) != 0;
4604 http_session_t
*session
= request
->session
;
4605 netconn_t
*netconn
= NULL
;
4609 assert(!request
->netconn
);
4610 reset_data_stream(request
);
4612 server
= get_server(session
->serverName
, session
->serverPort
);
4614 return ERROR_OUTOFMEMORY
;
4616 res
= HTTP_ResolveName(request
, server
);
4617 if(res
!= ERROR_SUCCESS
) {
4618 server_release(server
);
4622 EnterCriticalSection(&connection_pool_cs
);
4624 while(!list_empty(&server
->conn_pool
)) {
4625 netconn
= LIST_ENTRY(list_head(&server
->conn_pool
), netconn_t
, pool_entry
);
4626 list_remove(&netconn
->pool_entry
);
4628 if(NETCON_is_alive(netconn
))
4631 TRACE("connection %p closed during idle\n", netconn
);
4632 free_netconn(netconn
);
4636 LeaveCriticalSection(&connection_pool_cs
);
4639 TRACE("<-- reusing %p netconn\n", netconn
);
4640 request
->netconn
= netconn
;
4642 return ERROR_SUCCESS
;
4645 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4646 INTERNET_STATUS_CONNECTING_TO_SERVER
,
4648 strlen(server
->addr_str
)+1);
4650 res
= create_netconn(is_https
, server
, request
->security_flags
, request
->connect_timeout
, &netconn
);
4651 server_release(server
);
4652 if(res
!= ERROR_SUCCESS
) {
4653 ERR("create_netconn failed: %u\n", res
);
4657 request
->netconn
= netconn
;
4659 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4660 INTERNET_STATUS_CONNECTED_TO_SERVER
,
4661 server
->addr_str
, strlen(server
->addr_str
)+1);
4664 /* Note: we differ from Microsoft's WinINet here. they seem to have
4665 * a bug that causes no status callbacks to be sent when starting
4666 * a tunnel to a proxy server using the CONNECT verb. i believe our
4667 * behaviour to be more correct and to not cause any incompatibilities
4668 * because using a secure connection through a proxy server is a rare
4669 * case that would be hard for anyone to depend on */
4670 if(session
->appInfo
->proxy
)
4671 res
= HTTP_SecureProxyConnect(request
);
4672 if(res
== ERROR_SUCCESS
)
4673 res
= NETCON_secure_connect(request
->netconn
);
4674 if(res
!= ERROR_SUCCESS
)
4676 WARN("Couldn't connect securely to host\n");
4678 if((request
->hdr
.ErrorMask
&INTERNET_ERROR_MASK_COMBINED_SEC_CERT
) && (
4679 res
== ERROR_INTERNET_SEC_CERT_DATE_INVALID
4680 || res
== ERROR_INTERNET_INVALID_CA
4681 || res
== ERROR_INTERNET_SEC_CERT_NO_REV
4682 || res
== ERROR_INTERNET_SEC_CERT_REV_FAILED
4683 || res
== ERROR_INTERNET_SEC_CERT_REVOKED
4684 || res
== ERROR_INTERNET_SEC_INVALID_CERT
4685 || res
== ERROR_INTERNET_SEC_CERT_CN_INVALID
))
4686 res
= ERROR_INTERNET_SEC_CERT_ERRORS
;
4690 if(res
!= ERROR_SUCCESS
) {
4691 http_release_netconn(request
, FALSE
);
4696 TRACE("Created connection to %s: %p\n", debugstr_w(server
->name
), netconn
);
4697 return ERROR_SUCCESS
;
4700 /***********************************************************************
4701 * HTTP_HttpSendRequestW (internal)
4703 * Sends the specified request to the HTTP server
4706 * ERROR_SUCCESS on success
4707 * win32 error code on failure
4710 static DWORD
HTTP_HttpSendRequestW(http_request_t
*request
, LPCWSTR lpszHeaders
,
4711 DWORD dwHeaderLength
, LPVOID lpOptional
, DWORD dwOptionalLength
,
4712 DWORD dwContentLength
, BOOL bEndRequest
)
4715 BOOL redirected
= FALSE
;
4716 LPWSTR requestString
= NULL
;
4719 static const WCHAR szPost
[] = { 'P','O','S','T',0 };
4720 static const WCHAR szContentLength
[] =
4721 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4722 WCHAR contentLengthStr
[sizeof szContentLength
/2 /* includes \r\n */ + 20 /* int */ ];
4725 TRACE("--> %p\n", request
);
4727 assert(request
->hdr
.htype
== WH_HHTTPREQ
);
4729 /* if the verb is NULL default to GET */
4731 request
->verb
= heap_strdupW(szGET
);
4733 if (dwContentLength
|| strcmpW(request
->verb
, szGET
))
4735 sprintfW(contentLengthStr
, szContentLength
, dwContentLength
);
4736 HTTP_HttpAddRequestHeadersW(request
, contentLengthStr
, -1L, HTTP_ADDREQ_FLAG_REPLACE
);
4737 request
->bytesToWrite
= dwContentLength
;
4739 if (request
->session
->appInfo
->agent
)
4741 WCHAR
*agent_header
;
4742 static const WCHAR user_agent
[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4745 len
= strlenW(request
->session
->appInfo
->agent
) + strlenW(user_agent
);
4746 agent_header
= heap_alloc(len
* sizeof(WCHAR
));
4747 sprintfW(agent_header
, user_agent
, request
->session
->appInfo
->agent
);
4749 HTTP_HttpAddRequestHeadersW(request
, agent_header
, strlenW(agent_header
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4750 heap_free(agent_header
);
4752 if (request
->hdr
.dwFlags
& INTERNET_FLAG_PRAGMA_NOCACHE
)
4754 static const WCHAR pragma_nocache
[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4755 HTTP_HttpAddRequestHeadersW(request
, pragma_nocache
, strlenW(pragma_nocache
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4757 if ((request
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
) && !strcmpW(request
->verb
, szPost
))
4759 static const WCHAR cache_control
[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4760 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4761 HTTP_HttpAddRequestHeadersW(request
, cache_control
, strlenW(cache_control
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4764 /* add the headers the caller supplied */
4765 if( lpszHeaders
&& dwHeaderLength
)
4766 HTTP_HttpAddRequestHeadersW(request
, lpszHeaders
, dwHeaderLength
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REPLACE
);
4771 BOOL reusing_connection
;
4776 /* like native, just in case the caller forgot to call InternetReadFile
4777 * for all the data */
4778 drain_content(request
);
4780 request
->contentLength
= ~0u;
4781 request
->bytesToWrite
= 0;
4784 if (TRACE_ON(wininet
))
4786 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4787 TRACE("Going to url %s %s\n", debugstr_w(Host
->lpszValue
), debugstr_w(request
->path
));
4790 HTTP_FixURL(request
);
4791 if (request
->hdr
.dwFlags
& INTERNET_FLAG_KEEP_CONNECTION
)
4793 HTTP_ProcessHeader(request
, szConnection
, szKeepAlive
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
4795 HTTP_InsertAuthorization(request
, request
->authInfo
, szAuthorization
);
4796 HTTP_InsertAuthorization(request
, request
->proxyAuthInfo
, szProxy_Authorization
);
4798 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
))
4799 HTTP_InsertCookies(request
);
4801 if (request
->session
->appInfo
->proxy
&& request
->session
->appInfo
->proxy
[0])
4803 WCHAR
*url
= HTTP_BuildProxyRequestUrl(request
);
4804 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, url
, request
->version
);
4808 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
4811 TRACE("Request header -> %s\n", debugstr_w(requestString
) );
4813 if ((res
= open_http_connection(request
, &reusing_connection
)) != ERROR_SUCCESS
)
4816 /* send the request as ASCII, tack on the optional data */
4817 if (!lpOptional
|| redirected
)
4818 dwOptionalLength
= 0;
4819 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4820 NULL
, 0, NULL
, NULL
);
4821 ascii_req
= heap_alloc(len
+ dwOptionalLength
);
4822 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4823 ascii_req
, len
, NULL
, NULL
);
4825 memcpy( &ascii_req
[len
-1], lpOptional
, dwOptionalLength
);
4826 len
= (len
+ dwOptionalLength
- 1);
4828 TRACE("full request -> %s\n", debugstr_a(ascii_req
) );
4830 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4831 INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
4833 NETCON_set_timeout( request
->netconn
, TRUE
, request
->send_timeout
);
4834 res
= NETCON_send(request
->netconn
, ascii_req
, len
, 0, &cnt
);
4835 heap_free( ascii_req
);
4836 if(res
!= ERROR_SUCCESS
) {
4837 TRACE("send failed: %u\n", res
);
4838 if(!reusing_connection
)
4840 http_release_netconn(request
, FALSE
);
4845 request
->bytesWritten
= dwOptionalLength
;
4847 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4848 INTERNET_STATUS_REQUEST_SENT
,
4849 &len
, sizeof(DWORD
));
4855 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4856 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
4858 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
4859 /* FIXME: We should know that connection is closed before sending
4860 * headers. Otherwise wrong callbacks are executed */
4861 if(!responseLen
&& reusing_connection
) {
4862 TRACE("Connection closed by server, reconnecting\n");
4863 http_release_netconn(request
, FALSE
);
4868 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4869 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
,
4872 http_process_keep_alive(request
);
4873 HTTP_ProcessCookies(request
);
4874 HTTP_ProcessExpires(request
);
4875 HTTP_ProcessLastModified(request
);
4877 res
= set_content_length(request
);
4878 if(res
!= ERROR_SUCCESS
)
4880 if(!request
->contentLength
)
4881 http_release_netconn(request
, TRUE
);
4883 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
) && responseLen
)
4885 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
4886 dwBufferSize
=sizeof(szNewLocation
);
4887 switch(request
->status_code
) {
4888 case HTTP_STATUS_REDIRECT
:
4889 case HTTP_STATUS_MOVED
:
4890 case HTTP_STATUS_REDIRECT_KEEP_VERB
:
4891 case HTTP_STATUS_REDIRECT_METHOD
:
4892 if(HTTP_HttpQueryInfoW(request
,HTTP_QUERY_LOCATION
,szNewLocation
,&dwBufferSize
,NULL
) != ERROR_SUCCESS
)
4895 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
) &&
4896 request
->status_code
!= HTTP_STATUS_REDIRECT_KEEP_VERB
)
4898 heap_free(request
->verb
);
4899 request
->verb
= heap_strdupW(szGET
);
4901 drain_content(request
);
4902 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
4904 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
4905 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
4906 res
= HTTP_HandleRedirect(request
, new_url
);
4907 if (res
== ERROR_SUCCESS
)
4909 heap_free(requestString
);
4912 heap_free( new_url
);
4917 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTH
) && res
== ERROR_SUCCESS
)
4919 WCHAR szAuthValue
[2048];
4921 if (request
->status_code
== HTTP_STATUS_DENIED
)
4923 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4925 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_WWW_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4927 if (HTTP_DoAuthorization(request
, szAuthValue
,
4929 request
->session
->userName
,
4930 request
->session
->password
,
4933 heap_free(requestString
);
4940 TRACE("Cleaning wrong authorization data\n");
4941 destroy_authinfo(request
->authInfo
);
4942 request
->authInfo
= NULL
;
4945 if (request
->status_code
== HTTP_STATUS_PROXY_AUTH_REQ
)
4948 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_PROXY_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4950 if (HTTP_DoAuthorization(request
, szAuthValue
,
4951 &request
->proxyAuthInfo
,
4952 request
->session
->appInfo
->proxyUsername
,
4953 request
->session
->appInfo
->proxyPassword
,
4962 TRACE("Cleaning wrong proxy authorization data\n");
4963 destroy_authinfo(request
->proxyAuthInfo
);
4964 request
->proxyAuthInfo
= NULL
;
4970 res
= ERROR_SUCCESS
;
4974 if(res
== ERROR_SUCCESS
)
4975 HTTP_CacheRequest(request
);
4978 heap_free(requestString
);
4980 /* TODO: send notification for P3P header */
4982 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4984 if (res
== ERROR_SUCCESS
) {
4985 if(bEndRequest
&& request
->contentLength
&& request
->bytesWritten
== request
->bytesToWrite
)
4986 HTTP_ReceiveRequestData(request
, TRUE
);
4988 send_request_complete(request
,
4989 request
->session
->hdr
.dwInternalFlags
& INET_OPENURL
? (DWORD_PTR
)request
->hdr
.hInternet
: 1, 0);
4991 send_request_complete(request
, 0, res
);
4999 /***********************************************************************
5001 * Helper functions for the HttpSendRequest(Ex) functions
5004 static void AsyncHttpSendRequestProc(WORKREQUEST
*workRequest
)
5006 struct WORKREQ_HTTPSENDREQUESTW
const *req
= &workRequest
->u
.HttpSendRequestW
;
5007 http_request_t
*request
= (http_request_t
*) workRequest
->hdr
;
5009 TRACE("%p\n", request
);
5011 HTTP_HttpSendRequestW(request
, req
->lpszHeader
,
5012 req
->dwHeaderLength
, req
->lpOptional
, req
->dwOptionalLength
,
5013 req
->dwContentLength
, req
->bEndRequest
);
5015 heap_free(req
->lpszHeader
);
5019 static DWORD
HTTP_HttpEndRequestW(http_request_t
*request
, DWORD dwFlags
, DWORD_PTR dwContext
)
5023 DWORD res
= ERROR_SUCCESS
;
5025 if(!request
->netconn
) {
5026 WARN("Not connected\n");
5027 send_request_complete(request
, 0, ERROR_INTERNET_OPERATION_CANCELLED
);
5028 return ERROR_INTERNET_OPERATION_CANCELLED
;
5031 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
5032 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
5034 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
5036 res
= ERROR_HTTP_HEADER_NOT_FOUND
;
5038 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
5039 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
, sizeof(DWORD
));
5041 /* process cookies here. Is this right? */
5042 http_process_keep_alive(request
);
5043 HTTP_ProcessCookies(request
);
5044 HTTP_ProcessExpires(request
);
5045 HTTP_ProcessLastModified(request
);
5047 if ((res
= set_content_length(request
)) == ERROR_SUCCESS
) {
5048 if(!request
->contentLength
)
5049 http_release_netconn(request
, TRUE
);
5052 if (res
== ERROR_SUCCESS
&& !(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
))
5054 switch(request
->status_code
) {
5055 case HTTP_STATUS_REDIRECT
:
5056 case HTTP_STATUS_MOVED
:
5057 case HTTP_STATUS_REDIRECT_METHOD
:
5058 case HTTP_STATUS_REDIRECT_KEEP_VERB
: {
5059 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
5060 dwBufferSize
=sizeof(szNewLocation
);
5061 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_LOCATION
, szNewLocation
, &dwBufferSize
, NULL
) != ERROR_SUCCESS
)
5064 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
) &&
5065 request
->status_code
!= HTTP_STATUS_REDIRECT_KEEP_VERB
)
5067 heap_free(request
->verb
);
5068 request
->verb
= heap_strdupW(szGET
);
5070 drain_content(request
);
5071 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
5073 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
5074 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
5075 res
= HTTP_HandleRedirect(request
, new_url
);
5076 if (res
== ERROR_SUCCESS
)
5077 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, TRUE
);
5078 heap_free( new_url
);
5084 if (res
== ERROR_SUCCESS
&& request
->contentLength
)
5085 HTTP_ReceiveRequestData(request
, TRUE
);
5087 send_request_complete(request
, res
== ERROR_SUCCESS
, res
);
5092 /***********************************************************************
5093 * HttpEndRequestA (WININET.@)
5095 * Ends an HTTP request that was started by HttpSendRequestEx
5098 * TRUE if successful
5102 BOOL WINAPI
HttpEndRequestA(HINTERNET hRequest
,
5103 LPINTERNET_BUFFERSA lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
5105 TRACE("(%p, %p, %08x, %08lx)\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
5109 SetLastError(ERROR_INVALID_PARAMETER
);
5113 return HttpEndRequestW(hRequest
, NULL
, dwFlags
, dwContext
);
5116 static void AsyncHttpEndRequestProc(WORKREQUEST
*work
)
5118 struct WORKREQ_HTTPENDREQUESTW
const *req
= &work
->u
.HttpEndRequestW
;
5119 http_request_t
*request
= (http_request_t
*)work
->hdr
;
5121 TRACE("%p\n", request
);
5123 HTTP_HttpEndRequestW(request
, req
->dwFlags
, req
->dwContext
);
5126 /***********************************************************************
5127 * HttpEndRequestW (WININET.@)
5129 * Ends an HTTP request that was started by HttpSendRequestEx
5132 * TRUE if successful
5136 BOOL WINAPI
HttpEndRequestW(HINTERNET hRequest
,
5137 LPINTERNET_BUFFERSW lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
5139 http_request_t
*request
;
5142 TRACE("%p %p %x %lx -->\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
5146 SetLastError(ERROR_INVALID_PARAMETER
);
5150 request
= (http_request_t
*) get_handle_object( hRequest
);
5152 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5154 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
5156 WININET_Release( &request
->hdr
);
5159 request
->hdr
.dwFlags
|= dwFlags
;
5161 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5164 struct WORKREQ_HTTPENDREQUESTW
*work_endrequest
;
5166 work
.asyncproc
= AsyncHttpEndRequestProc
;
5167 work
.hdr
= WININET_AddRef( &request
->hdr
);
5169 work_endrequest
= &work
.u
.HttpEndRequestW
;
5170 work_endrequest
->dwFlags
= dwFlags
;
5171 work_endrequest
->dwContext
= dwContext
;
5173 INTERNET_AsyncCall(&work
);
5174 res
= ERROR_IO_PENDING
;
5177 res
= HTTP_HttpEndRequestW(request
, dwFlags
, dwContext
);
5179 WININET_Release( &request
->hdr
);
5180 TRACE("%u <--\n", res
);
5181 if(res
!= ERROR_SUCCESS
)
5183 return res
== ERROR_SUCCESS
;
5186 /***********************************************************************
5187 * HttpSendRequestExA (WININET.@)
5189 * Sends the specified request to the HTTP server and allows chunked
5194 * Failure: FALSE, call GetLastError() for more information.
5196 BOOL WINAPI
HttpSendRequestExA(HINTERNET hRequest
,
5197 LPINTERNET_BUFFERSA lpBuffersIn
,
5198 LPINTERNET_BUFFERSA lpBuffersOut
,
5199 DWORD dwFlags
, DWORD_PTR dwContext
)
5201 INTERNET_BUFFERSW BuffersInW
;
5204 LPWSTR header
= NULL
;
5206 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
5207 lpBuffersOut
, dwFlags
, dwContext
);
5211 BuffersInW
.dwStructSize
= sizeof(LPINTERNET_BUFFERSW
);
5212 if (lpBuffersIn
->lpcszHeader
)
5214 headerlen
= MultiByteToWideChar(CP_ACP
,0,lpBuffersIn
->lpcszHeader
,
5215 lpBuffersIn
->dwHeadersLength
,0,0);
5216 header
= heap_alloc(headerlen
*sizeof(WCHAR
));
5217 if (!(BuffersInW
.lpcszHeader
= header
))
5219 SetLastError(ERROR_OUTOFMEMORY
);
5222 BuffersInW
.dwHeadersLength
= MultiByteToWideChar(CP_ACP
, 0,
5223 lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
5227 BuffersInW
.lpcszHeader
= NULL
;
5228 BuffersInW
.dwHeadersTotal
= lpBuffersIn
->dwHeadersTotal
;
5229 BuffersInW
.lpvBuffer
= lpBuffersIn
->lpvBuffer
;
5230 BuffersInW
.dwBufferLength
= lpBuffersIn
->dwBufferLength
;
5231 BuffersInW
.dwBufferTotal
= lpBuffersIn
->dwBufferTotal
;
5232 BuffersInW
.Next
= NULL
;
5235 rc
= HttpSendRequestExW(hRequest
, lpBuffersIn
? &BuffersInW
: NULL
, NULL
, dwFlags
, dwContext
);
5241 /***********************************************************************
5242 * HttpSendRequestExW (WININET.@)
5244 * Sends the specified request to the HTTP server and allows chunked
5249 * Failure: FALSE, call GetLastError() for more information.
5251 BOOL WINAPI
HttpSendRequestExW(HINTERNET hRequest
,
5252 LPINTERNET_BUFFERSW lpBuffersIn
,
5253 LPINTERNET_BUFFERSW lpBuffersOut
,
5254 DWORD dwFlags
, DWORD_PTR dwContext
)
5256 http_request_t
*request
;
5257 http_session_t
*session
;
5261 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
5262 lpBuffersOut
, dwFlags
, dwContext
);
5264 request
= (http_request_t
*) get_handle_object( hRequest
);
5266 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5268 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5272 session
= request
->session
;
5273 assert(session
->hdr
.htype
== WH_HHTTPSESSION
);
5274 hIC
= session
->appInfo
;
5275 assert(hIC
->hdr
.htype
== WH_HINIT
);
5277 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5279 WORKREQUEST workRequest
;
5280 struct WORKREQ_HTTPSENDREQUESTW
*req
;
5282 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
5283 workRequest
.hdr
= WININET_AddRef( &request
->hdr
);
5284 req
= &workRequest
.u
.HttpSendRequestW
;
5289 if (lpBuffersIn
->lpcszHeader
)
5291 if (lpBuffersIn
->dwHeadersLength
== ~0u)
5292 size
= (strlenW( lpBuffersIn
->lpcszHeader
) + 1) * sizeof(WCHAR
);
5294 size
= lpBuffersIn
->dwHeadersLength
* sizeof(WCHAR
);
5296 req
->lpszHeader
= heap_alloc(size
);
5297 memcpy( req
->lpszHeader
, lpBuffersIn
->lpcszHeader
, size
);
5299 else req
->lpszHeader
= NULL
;
5301 req
->dwHeaderLength
= size
/ sizeof(WCHAR
);
5302 req
->lpOptional
= lpBuffersIn
->lpvBuffer
;
5303 req
->dwOptionalLength
= lpBuffersIn
->dwBufferLength
;
5304 req
->dwContentLength
= lpBuffersIn
->dwBufferTotal
;
5308 req
->lpszHeader
= NULL
;
5309 req
->dwHeaderLength
= 0;
5310 req
->lpOptional
= NULL
;
5311 req
->dwOptionalLength
= 0;
5312 req
->dwContentLength
= 0;
5315 req
->bEndRequest
= FALSE
;
5317 INTERNET_AsyncCall(&workRequest
);
5319 * This is from windows.
5321 res
= ERROR_IO_PENDING
;
5326 res
= HTTP_HttpSendRequestW(request
, lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
5327 lpBuffersIn
->lpvBuffer
, lpBuffersIn
->dwBufferLength
,
5328 lpBuffersIn
->dwBufferTotal
, FALSE
);
5330 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, FALSE
);
5335 WININET_Release( &request
->hdr
);
5339 return res
== ERROR_SUCCESS
;
5342 /***********************************************************************
5343 * HttpSendRequestW (WININET.@)
5345 * Sends the specified request to the HTTP server
5352 BOOL WINAPI
HttpSendRequestW(HINTERNET hHttpRequest
, LPCWSTR lpszHeaders
,
5353 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
5355 http_request_t
*request
;
5356 http_session_t
*session
= NULL
;
5357 appinfo_t
*hIC
= NULL
;
5358 DWORD res
= ERROR_SUCCESS
;
5360 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest
,
5361 debugstr_wn(lpszHeaders
, dwHeaderLength
), dwHeaderLength
, lpOptional
, dwOptionalLength
);
5363 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
5364 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5366 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5370 session
= request
->session
;
5371 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
5373 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5377 hIC
= session
->appInfo
;
5378 if (NULL
== hIC
|| hIC
->hdr
.htype
!= WH_HINIT
)
5380 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5384 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5386 WORKREQUEST workRequest
;
5387 struct WORKREQ_HTTPSENDREQUESTW
*req
;
5389 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
5390 workRequest
.hdr
= WININET_AddRef( &request
->hdr
);
5391 req
= &workRequest
.u
.HttpSendRequestW
;
5396 if (dwHeaderLength
== ~0u) size
= (strlenW(lpszHeaders
) + 1) * sizeof(WCHAR
);
5397 else size
= dwHeaderLength
* sizeof(WCHAR
);
5399 req
->lpszHeader
= heap_alloc(size
);
5400 memcpy(req
->lpszHeader
, lpszHeaders
, size
);
5403 req
->lpszHeader
= 0;
5404 req
->dwHeaderLength
= dwHeaderLength
;
5405 req
->lpOptional
= lpOptional
;
5406 req
->dwOptionalLength
= dwOptionalLength
;
5407 req
->dwContentLength
= dwOptionalLength
;
5408 req
->bEndRequest
= TRUE
;
5410 INTERNET_AsyncCall(&workRequest
);
5412 * This is from windows.
5414 res
= ERROR_IO_PENDING
;
5418 res
= HTTP_HttpSendRequestW(request
, lpszHeaders
,
5419 dwHeaderLength
, lpOptional
, dwOptionalLength
,
5420 dwOptionalLength
, TRUE
);
5424 WININET_Release( &request
->hdr
);
5427 return res
== ERROR_SUCCESS
;
5430 /***********************************************************************
5431 * HttpSendRequestA (WININET.@)
5433 * Sends the specified request to the HTTP server
5440 BOOL WINAPI
HttpSendRequestA(HINTERNET hHttpRequest
, LPCSTR lpszHeaders
,
5441 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
5444 LPWSTR szHeaders
=NULL
;
5445 DWORD nLen
=dwHeaderLength
;
5446 if(lpszHeaders
!=NULL
)
5448 nLen
=MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,NULL
,0);
5449 szHeaders
= heap_alloc(nLen
*sizeof(WCHAR
));
5450 MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,szHeaders
,nLen
);
5452 result
= HttpSendRequestW(hHttpRequest
, szHeaders
, nLen
, lpOptional
, dwOptionalLength
);
5453 heap_free(szHeaders
);
5457 /***********************************************************************
5458 * HTTPSESSION_Destroy (internal)
5460 * Deallocate session handle
5463 static void HTTPSESSION_Destroy(object_header_t
*hdr
)
5465 http_session_t
*session
= (http_session_t
*) hdr
;
5467 TRACE("%p\n", session
);
5469 WININET_Release(&session
->appInfo
->hdr
);
5471 heap_free(session
->hostName
);
5472 heap_free(session
->serverName
);
5473 heap_free(session
->password
);
5474 heap_free(session
->userName
);
5477 static DWORD
HTTPSESSION_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
5479 http_session_t
*ses
= (http_session_t
*)hdr
;
5482 case INTERNET_OPTION_HANDLE_TYPE
:
5483 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5485 if (*size
< sizeof(ULONG
))
5486 return ERROR_INSUFFICIENT_BUFFER
;
5488 *size
= sizeof(DWORD
);
5489 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_CONNECT_HTTP
;
5490 return ERROR_SUCCESS
;
5491 case INTERNET_OPTION_CONNECT_TIMEOUT
:
5492 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5494 if (*size
< sizeof(DWORD
))
5495 return ERROR_INSUFFICIENT_BUFFER
;
5497 *size
= sizeof(DWORD
);
5498 *(DWORD
*)buffer
= ses
->connect_timeout
;
5499 return ERROR_SUCCESS
;
5501 case INTERNET_OPTION_SEND_TIMEOUT
:
5502 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5504 if (*size
< sizeof(DWORD
))
5505 return ERROR_INSUFFICIENT_BUFFER
;
5507 *size
= sizeof(DWORD
);
5508 *(DWORD
*)buffer
= ses
->send_timeout
;
5509 return ERROR_SUCCESS
;
5511 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
5512 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5514 if (*size
< sizeof(DWORD
))
5515 return ERROR_INSUFFICIENT_BUFFER
;
5517 *size
= sizeof(DWORD
);
5518 *(DWORD
*)buffer
= ses
->receive_timeout
;
5519 return ERROR_SUCCESS
;
5522 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
5525 static DWORD
HTTPSESSION_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
5527 http_session_t
*ses
= (http_session_t
*)hdr
;
5530 case INTERNET_OPTION_USERNAME
:
5532 heap_free(ses
->userName
);
5533 if (!(ses
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5534 return ERROR_SUCCESS
;
5536 case INTERNET_OPTION_PASSWORD
:
5538 heap_free(ses
->password
);
5539 if (!(ses
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5540 return ERROR_SUCCESS
;
5542 case INTERNET_OPTION_CONNECT_TIMEOUT
:
5544 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5545 ses
->connect_timeout
= *(DWORD
*)buffer
;
5546 return ERROR_SUCCESS
;
5548 case INTERNET_OPTION_SEND_TIMEOUT
:
5550 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5551 ses
->send_timeout
= *(DWORD
*)buffer
;
5552 return ERROR_SUCCESS
;
5554 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
5556 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5557 ses
->receive_timeout
= *(DWORD
*)buffer
;
5558 return ERROR_SUCCESS
;
5563 return INET_SetOption(hdr
, option
, buffer
, size
);
5566 static const object_vtbl_t HTTPSESSIONVtbl
= {
5567 HTTPSESSION_Destroy
,
5569 HTTPSESSION_QueryOption
,
5570 HTTPSESSION_SetOption
,
5579 /***********************************************************************
5580 * HTTP_Connect (internal)
5582 * Create http session handle
5585 * HINTERNET a session handle on success
5589 DWORD
HTTP_Connect(appinfo_t
*hIC
, LPCWSTR lpszServerName
,
5590 INTERNET_PORT serverPort
, LPCWSTR lpszUserName
,
5591 LPCWSTR lpszPassword
, DWORD dwFlags
, DWORD_PTR dwContext
,
5592 DWORD dwInternalFlags
, HINTERNET
*ret
)
5594 http_session_t
*session
= NULL
;
5598 if (!lpszServerName
|| !lpszServerName
[0])
5599 return ERROR_INVALID_PARAMETER
;
5601 assert( hIC
->hdr
.htype
== WH_HINIT
);
5603 session
= alloc_object(&hIC
->hdr
, &HTTPSESSIONVtbl
, sizeof(http_session_t
));
5605 return ERROR_OUTOFMEMORY
;
5608 * According to my tests. The name is not resolved until a request is sent
5611 session
->hdr
.htype
= WH_HHTTPSESSION
;
5612 session
->hdr
.dwFlags
= dwFlags
;
5613 session
->hdr
.dwContext
= dwContext
;
5614 session
->hdr
.dwInternalFlags
|= dwInternalFlags
;
5616 WININET_AddRef( &hIC
->hdr
);
5617 session
->appInfo
= hIC
;
5618 list_add_head( &hIC
->hdr
.children
, &session
->hdr
.entry
);
5620 if(hIC
->proxy
&& hIC
->accessType
== INTERNET_OPEN_TYPE_PROXY
) {
5621 if(hIC
->proxyBypass
)
5622 FIXME("Proxy bypass is ignored.\n");
5624 session
->serverName
= heap_strdupW(lpszServerName
);
5625 session
->hostName
= heap_strdupW(lpszServerName
);
5626 if (lpszUserName
&& lpszUserName
[0])
5627 session
->userName
= heap_strdupW(lpszUserName
);
5628 if (lpszPassword
&& lpszPassword
[0])
5629 session
->password
= heap_strdupW(lpszPassword
);
5630 session
->serverPort
= serverPort
;
5631 session
->hostPort
= serverPort
;
5632 session
->connect_timeout
= INFINITE
;
5633 session
->send_timeout
= INFINITE
;
5634 session
->receive_timeout
= INFINITE
;
5636 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5637 if (!(session
->hdr
.dwInternalFlags
& INET_OPENURL
))
5639 INTERNET_SendCallback(&hIC
->hdr
, dwContext
,
5640 INTERNET_STATUS_HANDLE_CREATED
, &session
->hdr
.hInternet
,
5645 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5649 TRACE("%p --> %p\n", hIC
, session
);
5651 *ret
= session
->hdr
.hInternet
;
5652 return ERROR_SUCCESS
;
5655 /***********************************************************************
5656 * HTTP_clear_response_headers (internal)
5658 * clear out any old response headers
5660 static void HTTP_clear_response_headers( http_request_t
*request
)
5664 for( i
=0; i
<request
->nCustHeaders
; i
++)
5666 if( !request
->custHeaders
[i
].lpszField
)
5668 if( !request
->custHeaders
[i
].lpszValue
)
5670 if ( request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
5672 HTTP_DeleteCustomHeader( request
, i
);
5677 /***********************************************************************
5678 * HTTP_GetResponseHeaders (internal)
5680 * Read server response
5687 static INT
HTTP_GetResponseHeaders(http_request_t
*request
, BOOL clear
)
5690 WCHAR buffer
[MAX_REPLY_LEN
];
5691 DWORD buflen
= MAX_REPLY_LEN
;
5692 BOOL bSuccess
= FALSE
;
5694 char bufferA
[MAX_REPLY_LEN
];
5695 LPWSTR status_code
= NULL
, status_text
= NULL
;
5696 DWORD cchMaxRawHeaders
= 1024;
5697 LPWSTR lpszRawHeaders
= NULL
;
5699 DWORD cchRawHeaders
= 0;
5700 BOOL codeHundred
= FALSE
;
5704 if(!request
->netconn
)
5707 NETCON_set_timeout( request
->netconn
, FALSE
, request
->receive_timeout
);
5709 static const WCHAR szHundred
[] = {'1','0','0',0};
5711 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5713 buflen
= MAX_REPLY_LEN
;
5714 if (!read_line(request
, bufferA
, &buflen
))
5717 /* clear old response headers (eg. from a redirect response) */
5719 HTTP_clear_response_headers( request
);
5724 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5725 /* check is this a status code line? */
5726 if (!strncmpW(buffer
, g_szHttp1_0
, 4))
5728 /* split the version from the status code */
5729 status_code
= strchrW( buffer
, ' ' );
5734 /* split the status code from the status text */
5735 status_text
= strchrW( status_code
, ' ' );
5740 request
->status_code
= atoiW(status_code
);
5742 TRACE("version [%s] status code [%s] status text [%s]\n",
5743 debugstr_w(buffer
), debugstr_w(status_code
), debugstr_w(status_text
) );
5745 codeHundred
= (!strcmpW(status_code
, szHundred
));
5747 else if (!codeHundred
)
5749 WARN("No status line at head of response (%s)\n", debugstr_w(buffer
));
5751 heap_free(request
->version
);
5752 heap_free(request
->statusText
);
5754 request
->status_code
= HTTP_STATUS_OK
;
5755 request
->version
= heap_strdupW(g_szHttp1_0
);
5756 request
->statusText
= heap_strdupW(szOK
);
5758 heap_free(request
->rawHeaders
);
5759 request
->rawHeaders
= heap_strdupW(szDefaultHeader
);
5764 } while (codeHundred
);
5766 /* Add status code */
5767 HTTP_ProcessHeader(request
, szStatus
, status_code
,
5768 HTTP_ADDHDR_FLAG_REPLACE
);
5770 heap_free(request
->version
);
5771 heap_free(request
->statusText
);
5773 request
->version
= heap_strdupW(buffer
);
5774 request
->statusText
= heap_strdupW(status_text
);
5776 /* Restore the spaces */
5777 *(status_code
-1) = ' ';
5778 *(status_text
-1) = ' ';
5780 /* regenerate raw headers */
5781 lpszRawHeaders
= heap_alloc((cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5782 if (!lpszRawHeaders
) goto lend
;
5784 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5785 cchMaxRawHeaders
*= 2;
5786 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5787 if (temp
== NULL
) goto lend
;
5788 lpszRawHeaders
= temp
;
5789 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5790 cchRawHeaders
+= (buflen
-1);
5791 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5792 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5793 lpszRawHeaders
[cchRawHeaders
] = '\0';
5795 /* Parse each response line */
5798 buflen
= MAX_REPLY_LEN
;
5799 if (read_line(request
, bufferA
, &buflen
))
5801 LPWSTR
* pFieldAndValue
;
5803 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA
));
5805 if (!bufferA
[0]) break;
5806 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5808 pFieldAndValue
= HTTP_InterpretHttpHeader(buffer
);
5811 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5812 cchMaxRawHeaders
*= 2;
5813 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5814 if (temp
== NULL
) goto lend
;
5815 lpszRawHeaders
= temp
;
5816 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5817 cchRawHeaders
+= (buflen
-1);
5818 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5819 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5820 lpszRawHeaders
[cchRawHeaders
] = '\0';
5822 HTTP_ProcessHeader(request
, pFieldAndValue
[0], pFieldAndValue
[1],
5823 HTTP_ADDREQ_FLAG_ADD
);
5825 HTTP_FreeTokens(pFieldAndValue
);
5836 /* make sure the response header is terminated with an empty line. Some apps really
5837 truly care about that empty line being there for some reason. Just add it to the
5839 if (cchRawHeaders
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5841 cchMaxRawHeaders
= cchRawHeaders
+ strlenW(szCrLf
);
5842 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5843 if (temp
== NULL
) goto lend
;
5844 lpszRawHeaders
= temp
;
5847 memcpy(&lpszRawHeaders
[cchRawHeaders
], szCrLf
, sizeof(szCrLf
));
5849 heap_free(request
->rawHeaders
);
5850 request
->rawHeaders
= lpszRawHeaders
;
5851 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders
));
5861 heap_free(lpszRawHeaders
);
5866 /***********************************************************************
5867 * HTTP_InterpretHttpHeader (internal)
5869 * Parse server response
5873 * Pointer to array of field, value, NULL on success.
5876 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
)
5878 LPWSTR
* pTokenPair
;
5882 pTokenPair
= heap_alloc_zero(sizeof(*pTokenPair
)*3);
5884 pszColon
= strchrW(buffer
, ':');
5885 /* must have two tokens */
5888 HTTP_FreeTokens(pTokenPair
);
5890 TRACE("No ':' in line: %s\n", debugstr_w(buffer
));
5894 pTokenPair
[0] = heap_alloc((pszColon
- buffer
+ 1) * sizeof(WCHAR
));
5897 HTTP_FreeTokens(pTokenPair
);
5900 memcpy(pTokenPair
[0], buffer
, (pszColon
- buffer
) * sizeof(WCHAR
));
5901 pTokenPair
[0][pszColon
- buffer
] = '\0';
5905 len
= strlenW(pszColon
);
5906 pTokenPair
[1] = heap_alloc((len
+ 1) * sizeof(WCHAR
));
5909 HTTP_FreeTokens(pTokenPair
);
5912 memcpy(pTokenPair
[1], pszColon
, (len
+ 1) * sizeof(WCHAR
));
5914 strip_spaces(pTokenPair
[0]);
5915 strip_spaces(pTokenPair
[1]);
5917 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair
[0]), debugstr_w(pTokenPair
[1]));
5921 /***********************************************************************
5922 * HTTP_ProcessHeader (internal)
5924 * Stuff header into header tables according to <dwModifier>
5928 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5930 static DWORD
HTTP_ProcessHeader(http_request_t
*request
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
)
5932 LPHTTPHEADERW lphttpHdr
= NULL
;
5934 BOOL request_only
= dwModifier
& HTTP_ADDHDR_FLAG_REQ
;
5935 DWORD res
= ERROR_HTTP_INVALID_HEADER
;
5937 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field
), debugstr_w(value
), dwModifier
);
5939 /* REPLACE wins out over ADD */
5940 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5941 dwModifier
&= ~HTTP_ADDHDR_FLAG_ADD
;
5943 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD
)
5946 index
= HTTP_GetCustomHeaderIndex(request
, field
, 0, request_only
);
5950 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD_IF_NEW
)
5951 return ERROR_HTTP_INVALID_HEADER
;
5952 lphttpHdr
= &request
->custHeaders
[index
];
5958 hdr
.lpszField
= (LPWSTR
)field
;
5959 hdr
.lpszValue
= (LPWSTR
)value
;
5960 hdr
.wFlags
= hdr
.wCount
= 0;
5962 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5963 hdr
.wFlags
|= HDR_ISREQUEST
;
5965 return HTTP_InsertCustomHeader(request
, &hdr
);
5967 /* no value to delete */
5968 else return ERROR_SUCCESS
;
5970 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5971 lphttpHdr
->wFlags
|= HDR_ISREQUEST
;
5973 lphttpHdr
->wFlags
&= ~HDR_ISREQUEST
;
5975 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5977 HTTP_DeleteCustomHeader( request
, index
);
5983 hdr
.lpszField
= (LPWSTR
)field
;
5984 hdr
.lpszValue
= (LPWSTR
)value
;
5985 hdr
.wFlags
= hdr
.wCount
= 0;
5987 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5988 hdr
.wFlags
|= HDR_ISREQUEST
;
5990 return HTTP_InsertCustomHeader(request
, &hdr
);
5993 return ERROR_SUCCESS
;
5995 else if (dwModifier
& COALESCEFLAGS
)
6000 INT origlen
= strlenW(lphttpHdr
->lpszValue
);
6001 INT valuelen
= strlenW(value
);
6003 if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
)
6006 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
6008 else if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON
)
6011 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
6014 len
= origlen
+ valuelen
+ ((ch
> 0) ? 2 : 0);
6016 lpsztmp
= heap_realloc(lphttpHdr
->lpszValue
, (len
+1)*sizeof(WCHAR
));
6019 lphttpHdr
->lpszValue
= lpsztmp
;
6020 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6023 lphttpHdr
->lpszValue
[origlen
] = ch
;
6025 lphttpHdr
->lpszValue
[origlen
] = ' ';
6029 memcpy(&lphttpHdr
->lpszValue
[origlen
], value
, valuelen
*sizeof(WCHAR
));
6030 lphttpHdr
->lpszValue
[len
] = '\0';
6031 res
= ERROR_SUCCESS
;
6035 WARN("heap_realloc (%d bytes) failed\n",len
+1);
6036 res
= ERROR_OUTOFMEMORY
;
6039 TRACE("<-- %d\n", res
);
6043 /***********************************************************************
6044 * HTTP_GetCustomHeaderIndex (internal)
6046 * Return index of custom header from header array
6049 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*request
, LPCWSTR lpszField
,
6050 int requested_index
, BOOL request_only
)
6054 TRACE("%s, %d, %d\n", debugstr_w(lpszField
), requested_index
, request_only
);
6056 for (index
= 0; index
< request
->nCustHeaders
; index
++)
6058 if (strcmpiW(request
->custHeaders
[index
].lpszField
, lpszField
))
6061 if (request_only
&& !(request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
6064 if (!request_only
&& (request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
6067 if (requested_index
== 0)
6072 if (index
>= request
->nCustHeaders
)
6075 TRACE("Return: %d\n", index
);
6080 /***********************************************************************
6081 * HTTP_InsertCustomHeader (internal)
6083 * Insert header into array
6086 static DWORD
HTTP_InsertCustomHeader(http_request_t
*request
, LPHTTPHEADERW lpHdr
)
6089 LPHTTPHEADERW lph
= NULL
;
6091 TRACE("--> %s: %s\n", debugstr_w(lpHdr
->lpszField
), debugstr_w(lpHdr
->lpszValue
));
6092 count
= request
->nCustHeaders
+ 1;
6094 lph
= heap_realloc_zero(request
->custHeaders
, sizeof(HTTPHEADERW
) * count
);
6096 lph
= heap_alloc_zero(sizeof(HTTPHEADERW
) * count
);
6099 return ERROR_OUTOFMEMORY
;
6101 request
->custHeaders
= lph
;
6102 request
->custHeaders
[count
-1].lpszField
= heap_strdupW(lpHdr
->lpszField
);
6103 request
->custHeaders
[count
-1].lpszValue
= heap_strdupW(lpHdr
->lpszValue
);
6104 request
->custHeaders
[count
-1].wFlags
= lpHdr
->wFlags
;
6105 request
->custHeaders
[count
-1].wCount
= lpHdr
->wCount
;
6106 request
->nCustHeaders
++;
6108 return ERROR_SUCCESS
;
6112 /***********************************************************************
6113 * HTTP_DeleteCustomHeader (internal)
6115 * Delete header from array
6116 * If this function is called, the indexs may change.
6118 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*request
, DWORD index
)
6120 if( request
->nCustHeaders
<= 0 )
6122 if( index
>= request
->nCustHeaders
)
6124 request
->nCustHeaders
--;
6126 heap_free(request
->custHeaders
[index
].lpszField
);
6127 heap_free(request
->custHeaders
[index
].lpszValue
);
6129 memmove( &request
->custHeaders
[index
], &request
->custHeaders
[index
+1],
6130 (request
->nCustHeaders
- index
)* sizeof(HTTPHEADERW
) );
6131 memset( &request
->custHeaders
[request
->nCustHeaders
], 0, sizeof(HTTPHEADERW
) );
6137 /***********************************************************************
6138 * HTTP_VerifyValidHeader (internal)
6140 * Verify the given header is not invalid for the given http request
6143 static BOOL
HTTP_VerifyValidHeader(http_request_t
*request
, LPCWSTR field
)
6145 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6146 if (!strcmpW(request
->version
, g_szHttp1_0
) && !strcmpiW(field
, szAccept_Encoding
))
6147 return ERROR_HTTP_INVALID_HEADER
;
6149 return ERROR_SUCCESS
;
6152 /***********************************************************************
6153 * IsHostInProxyBypassList (@)
6158 BOOL WINAPI
IsHostInProxyBypassList(DWORD flags
, LPCSTR szHost
, DWORD length
)
6160 FIXME("STUB: flags=%d host=%s length=%d\n",flags
,szHost
,length
);
6164 /***********************************************************************
6165 * InternetShowSecurityInfoByURLA (@)
6167 BOOL WINAPI
InternetShowSecurityInfoByURLA(LPCSTR url
, HWND window
)
6169 FIXME("stub: %s %p\n", url
, window
);
6173 /***********************************************************************
6174 * InternetShowSecurityInfoByURLW (@)
6176 BOOL WINAPI
InternetShowSecurityInfoByURLW(LPCWSTR url
, HWND window
)
6178 FIXME("stub: %s %p\n", debugstr_w(url
), window
);
6182 /***********************************************************************
6183 * ShowX509EncodedCertificate (@)
6185 DWORD WINAPI
ShowX509EncodedCertificate(HWND parent
, LPBYTE cert
, DWORD len
)
6187 PCCERT_CONTEXT certContext
= CertCreateCertificateContext(X509_ASN_ENCODING
,
6193 CRYPTUI_VIEWCERTIFICATE_STRUCTW view
;
6195 memset(&view
, 0, sizeof(view
));
6196 view
.hwndParent
= parent
;
6197 view
.pCertContext
= certContext
;
6198 if (CryptUIDlgViewCertificateW(&view
, NULL
))
6199 ret
= ERROR_SUCCESS
;
6201 ret
= GetLastError();
6202 CertFreeCertificateContext(certContext
);
6205 ret
= GetLastError();