2 * Wininet - Http Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
10 * Copyright 2011 Jacek Caban for CodeWeavers
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
37 #include <sys/types.h>
38 #ifdef HAVE_SYS_SOCKET_H
39 # include <sys/socket.h>
41 #ifdef HAVE_ARPA_INET_H
42 # include <arpa/inet.h>
61 #define NO_SHLWAPI_STREAM
62 #define NO_SHLWAPI_REG
63 #define NO_SHLWAPI_STRFCNS
64 #define NO_SHLWAPI_GDI
70 #include "wine/debug.h"
71 #include "wine/exception.h"
72 #include "wine/unicode.h"
74 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
76 static const WCHAR g_szHttp1_0
[] = {'H','T','T','P','/','1','.','0',0};
77 static const WCHAR g_szHttp1_1
[] = {'H','T','T','P','/','1','.','1',0};
78 static const WCHAR szOK
[] = {'O','K',0};
79 static const WCHAR szDefaultHeader
[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
80 static const WCHAR hostW
[] = { 'H','o','s','t',0 };
81 static const WCHAR szAuthorization
[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
82 static const WCHAR szProxy_Authorization
[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
83 static const WCHAR szStatus
[] = { 'S','t','a','t','u','s',0 };
84 static const WCHAR szKeepAlive
[] = {'K','e','e','p','-','A','l','i','v','e',0};
85 static const WCHAR szGET
[] = { 'G','E','T', 0 };
86 static const WCHAR szHEAD
[] = { 'H','E','A','D', 0 };
87 static const WCHAR szCrLf
[] = {'\r','\n', 0};
89 static const WCHAR szAccept
[] = { 'A','c','c','e','p','t',0 };
90 static const WCHAR szAccept_Charset
[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
91 static const WCHAR szAccept_Encoding
[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
92 static const WCHAR szAccept_Language
[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
93 static const WCHAR szAccept_Ranges
[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
94 static const WCHAR szAge
[] = { 'A','g','e',0 };
95 static const WCHAR szAllow
[] = { 'A','l','l','o','w',0 };
96 static const WCHAR szCache_Control
[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
97 static const WCHAR szConnection
[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
98 static const WCHAR szContent_Base
[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
99 static const WCHAR szContent_Encoding
[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
100 static const WCHAR szContent_ID
[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
101 static const WCHAR szContent_Language
[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
102 static const WCHAR szContent_Length
[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
103 static const WCHAR szContent_Location
[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
104 static const WCHAR szContent_MD5
[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
105 static const WCHAR szContent_Range
[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
106 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 };
107 static const WCHAR szContent_Type
[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
108 static const WCHAR szCookie
[] = { 'C','o','o','k','i','e',0 };
109 static const WCHAR szDate
[] = { 'D','a','t','e',0 };
110 static const WCHAR szFrom
[] = { 'F','r','o','m',0 };
111 static const WCHAR szETag
[] = { 'E','T','a','g',0 };
112 static const WCHAR szExpect
[] = { 'E','x','p','e','c','t',0 };
113 static const WCHAR szExpires
[] = { 'E','x','p','i','r','e','s',0 };
114 static const WCHAR szIf_Match
[] = { 'I','f','-','M','a','t','c','h',0 };
115 static const WCHAR szIf_Modified_Since
[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
116 static const WCHAR szIf_None_Match
[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
117 static const WCHAR szIf_Range
[] = { 'I','f','-','R','a','n','g','e',0 };
118 static const WCHAR szIf_Unmodified_Since
[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
119 static const WCHAR szLast_Modified
[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
120 static const WCHAR szLocation
[] = { 'L','o','c','a','t','i','o','n',0 };
121 static const WCHAR szMax_Forwards
[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
122 static const WCHAR szMime_Version
[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
123 static const WCHAR szPragma
[] = { 'P','r','a','g','m','a',0 };
124 static const WCHAR szProxy_Authenticate
[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
125 static const WCHAR szProxy_Connection
[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
126 static const WCHAR szPublic
[] = { 'P','u','b','l','i','c',0 };
127 static const WCHAR szRange
[] = { 'R','a','n','g','e',0 };
128 static const WCHAR szReferer
[] = { 'R','e','f','e','r','e','r',0 };
129 static const WCHAR szRetry_After
[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
130 static const WCHAR szServer
[] = { 'S','e','r','v','e','r',0 };
131 static const WCHAR szSet_Cookie
[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
132 static const WCHAR szTransfer_Encoding
[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
133 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 };
134 static const WCHAR szUpgrade
[] = { 'U','p','g','r','a','d','e',0 };
135 static const WCHAR szURI
[] = { 'U','R','I',0 };
136 static const WCHAR szUser_Agent
[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
137 static const WCHAR szVary
[] = { 'V','a','r','y',0 };
138 static const WCHAR szVia
[] = { 'V','i','a',0 };
139 static const WCHAR szWarning
[] = { 'W','a','r','n','i','n','g',0 };
140 static const WCHAR szWWW_Authenticate
[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
142 #define HTTP_REFERER szReferer
143 #define HTTP_ACCEPT szAccept
144 #define HTTP_USERAGENT szUser_Agent
146 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
147 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
148 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
150 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
151 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
152 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
154 #define COLLECT_TIME 60000
156 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
167 unsigned int auth_data_len
;
168 BOOL finished
; /* finished authenticating */
172 typedef struct _basicAuthorizationData
179 UINT authorizationLen
;
180 } basicAuthorizationData
;
182 typedef struct _authorizationData
196 static struct list basicAuthorizationCache
= LIST_INIT(basicAuthorizationCache
);
197 static struct list authorizationCache
= LIST_INIT(authorizationCache
);
199 static CRITICAL_SECTION authcache_cs
;
200 static CRITICAL_SECTION_DEBUG critsect_debug
=
203 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
204 0, 0, { (DWORD_PTR
)(__FILE__
": authcache_cs") }
206 static CRITICAL_SECTION authcache_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
208 static BOOL
HTTP_GetResponseHeaders(http_request_t
*req
, BOOL clear
);
209 static DWORD
HTTP_ProcessHeader(http_request_t
*req
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
);
210 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
);
211 static DWORD
HTTP_InsertCustomHeader(http_request_t
*req
, LPHTTPHEADERW lpHdr
);
212 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*req
, LPCWSTR lpszField
, INT index
, BOOL Request
);
213 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*req
, DWORD index
);
214 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
);
215 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*, DWORD
, LPVOID
, LPDWORD
, LPDWORD
);
216 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*req
, LPCWSTR lpszUrl
);
217 static UINT
HTTP_DecodeBase64(LPCWSTR base64
, LPSTR bin
);
218 static BOOL
HTTP_VerifyValidHeader(http_request_t
*req
, LPCWSTR field
);
220 static CRITICAL_SECTION connection_pool_cs
;
221 static CRITICAL_SECTION_DEBUG connection_pool_debug
=
223 0, 0, &connection_pool_cs
,
224 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
225 0, 0, { (DWORD_PTR
)(__FILE__
": connection_pool_cs") }
227 static CRITICAL_SECTION connection_pool_cs
= { &connection_pool_debug
, -1, 0, 0, 0, 0 };
229 static struct list connection_pool
= LIST_INIT(connection_pool
);
230 static BOOL collector_running
;
232 void server_addref(server_t
*server
)
234 InterlockedIncrement(&server
->ref
);
237 void server_release(server_t
*server
)
239 if(InterlockedDecrement(&server
->ref
))
243 server
->keep_until
= GetTickCount64() + COLLECT_TIME
;
246 static server_t
*get_server(const WCHAR
*name
, INTERNET_PORT port
)
248 server_t
*iter
, *server
= NULL
;
250 EnterCriticalSection(&connection_pool_cs
);
252 LIST_FOR_EACH_ENTRY(iter
, &connection_pool
, server_t
, entry
) {
253 if(iter
->port
== port
&& !strcmpW(iter
->name
, name
)) {
255 server_addref(server
);
261 server
= heap_alloc(sizeof(*server
));
263 server
->addr_len
= 0;
266 list_init(&server
->conn_pool
);
267 server
->name
= heap_strdupW(name
);
269 list_add_head(&connection_pool
, &server
->entry
);
277 LeaveCriticalSection(&connection_pool_cs
);
282 BOOL
collect_connections(BOOL collect_all
)
284 netconn_t
*netconn
, *netconn_safe
;
285 server_t
*server
, *server_safe
;
286 BOOL remaining
= FALSE
;
289 now
= GetTickCount64();
291 LIST_FOR_EACH_ENTRY_SAFE(server
, server_safe
, &connection_pool
, server_t
, entry
) {
292 LIST_FOR_EACH_ENTRY_SAFE(netconn
, netconn_safe
, &server
->conn_pool
, netconn_t
, pool_entry
) {
293 if(collect_all
|| netconn
->keep_until
< now
) {
294 TRACE("freeing %p\n", netconn
);
295 list_remove(&netconn
->pool_entry
);
296 free_netconn(netconn
);
303 if(collect_all
|| server
->keep_until
< now
) {
304 list_remove(&server
->entry
);
306 heap_free(server
->name
);
317 static DWORD WINAPI
collect_connections_proc(void *arg
)
319 BOOL remaining_conns
;
322 /* FIXME: Use more sophisticated method */
325 EnterCriticalSection(&connection_pool_cs
);
327 remaining_conns
= collect_connections(FALSE
);
329 collector_running
= FALSE
;
331 LeaveCriticalSection(&connection_pool_cs
);
332 }while(remaining_conns
);
334 FreeLibraryAndExitThread(WININET_hModule
, 0);
337 static LPHTTPHEADERW
HTTP_GetHeader(http_request_t
*req
, LPCWSTR head
)
340 HeaderIndex
= HTTP_GetCustomHeaderIndex(req
, head
, 0, TRUE
);
341 if (HeaderIndex
== -1)
344 return &req
->custHeaders
[HeaderIndex
];
353 struct data_stream_vtbl_t
{
354 DWORD (*get_avail_data
)(data_stream_t
*,http_request_t
*);
355 BOOL (*end_of_data
)(data_stream_t
*,http_request_t
*);
356 DWORD (*read
)(data_stream_t
*,http_request_t
*,BYTE
*,DWORD
,DWORD
*,read_mode_t
);
357 BOOL (*drain_content
)(data_stream_t
*,http_request_t
*);
358 void (*destroy
)(data_stream_t
*);
362 data_stream_t data_stream
;
364 BYTE buf
[READ_BUFFER_SIZE
];
370 static inline void destroy_data_stream(data_stream_t
*stream
)
372 stream
->vtbl
->destroy(stream
);
375 static void reset_data_stream(http_request_t
*req
)
377 destroy_data_stream(req
->data_stream
);
378 req
->data_stream
= &req
->netconn_stream
.data_stream
;
379 req
->read_pos
= req
->read_size
= req
->netconn_stream
.content_read
= 0;
380 req
->read_chunked
= req
->read_gzip
= FALSE
;
386 data_stream_t stream
;
387 data_stream_t
*parent_stream
;
389 BYTE buf
[READ_BUFFER_SIZE
];
395 static DWORD
gzip_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
397 /* Allow reading only from read buffer */
401 static BOOL
gzip_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
403 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
404 return gzip_stream
->end_of_data
;
407 static DWORD
gzip_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
408 DWORD
*read
, read_mode_t read_mode
)
410 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
411 z_stream
*zstream
= &gzip_stream
->zstream
;
412 DWORD current_read
, ret_read
= 0;
415 DWORD res
= ERROR_SUCCESS
;
417 while(size
&& !gzip_stream
->end_of_data
) {
418 end
= gzip_stream
->parent_stream
->vtbl
->end_of_data(gzip_stream
->parent_stream
, req
);
420 if(gzip_stream
->buf_size
<= 64 && !end
) {
421 if(gzip_stream
->buf_pos
) {
422 if(gzip_stream
->buf_size
)
423 memmove(gzip_stream
->buf
, gzip_stream
->buf
+gzip_stream
->buf_pos
, gzip_stream
->buf_size
);
424 gzip_stream
->buf_pos
= 0;
426 res
= gzip_stream
->parent_stream
->vtbl
->read(gzip_stream
->parent_stream
, req
, gzip_stream
->buf
+gzip_stream
->buf_size
,
427 sizeof(gzip_stream
->buf
)-gzip_stream
->buf_size
, ¤t_read
, read_mode
);
428 gzip_stream
->buf_size
+= current_read
;
429 if(res
!= ERROR_SUCCESS
)
431 end
= gzip_stream
->parent_stream
->vtbl
->end_of_data(gzip_stream
->parent_stream
, req
);
432 if(!current_read
&& !end
) {
433 if(read_mode
!= READMODE_NOBLOCK
) {
434 WARN("unexpected end of data\n");
435 gzip_stream
->end_of_data
= TRUE
;
439 if(gzip_stream
->buf_size
<= 64 && !end
)
443 zstream
->next_in
= gzip_stream
->buf
+gzip_stream
->buf_pos
;
444 zstream
->avail_in
= gzip_stream
->buf_size
-(end
? 0 : 64);
445 zstream
->next_out
= buf
+ret_read
;
446 zstream
->avail_out
= size
;
447 zres
= inflate(&gzip_stream
->zstream
, 0);
448 current_read
= size
- zstream
->avail_out
;
449 size
-= current_read
;
450 ret_read
+= current_read
;
451 gzip_stream
->buf_size
-= zstream
->next_in
- (gzip_stream
->buf
+gzip_stream
->buf_pos
);
452 gzip_stream
->buf_pos
= zstream
->next_in
-gzip_stream
->buf
;
453 if(zres
== Z_STREAM_END
) {
454 TRACE("end of data\n");
455 gzip_stream
->end_of_data
= TRUE
;
457 }else if(zres
!= Z_OK
) {
458 WARN("inflate failed %d: %s\n", zres
, debugstr_a(zstream
->msg
));
460 res
= ERROR_INTERNET_DECODING_FAILED
;
464 if(ret_read
&& read_mode
== READMODE_ASYNC
)
465 read_mode
= READMODE_NOBLOCK
;
468 TRACE("read %u bytes\n", ret_read
);
473 static BOOL
gzip_drain_content(data_stream_t
*stream
, http_request_t
*req
)
475 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
476 return gzip_stream
->parent_stream
->vtbl
->drain_content(gzip_stream
->parent_stream
, req
);
479 static void gzip_destroy(data_stream_t
*stream
)
481 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
483 destroy_data_stream(gzip_stream
->parent_stream
);
485 if(!gzip_stream
->end_of_data
)
486 inflateEnd(&gzip_stream
->zstream
);
487 heap_free(gzip_stream
);
490 static const data_stream_vtbl_t gzip_stream_vtbl
= {
498 static voidpf
wininet_zalloc(voidpf opaque
, uInt items
, uInt size
)
500 return heap_alloc(items
*size
);
503 static void wininet_zfree(voidpf opaque
, voidpf address
)
508 static DWORD
init_gzip_stream(http_request_t
*req
)
510 gzip_stream_t
*gzip_stream
;
513 gzip_stream
= heap_alloc_zero(sizeof(gzip_stream_t
));
515 return ERROR_OUTOFMEMORY
;
517 gzip_stream
->stream
.vtbl
= &gzip_stream_vtbl
;
518 gzip_stream
->zstream
.zalloc
= wininet_zalloc
;
519 gzip_stream
->zstream
.zfree
= wininet_zfree
;
521 zres
= inflateInit2(&gzip_stream
->zstream
, 0x1f);
523 ERR("inflateInit failed: %d\n", zres
);
524 heap_free(gzip_stream
);
525 return ERROR_OUTOFMEMORY
;
528 index
= HTTP_GetCustomHeaderIndex(req
, szContent_Length
, 0, FALSE
);
530 HTTP_DeleteCustomHeader(req
, index
);
533 memcpy(gzip_stream
->buf
, req
->read_buf
+req
->read_pos
, req
->read_size
);
534 gzip_stream
->buf_size
= req
->read_size
;
535 req
->read_pos
= req
->read_size
= 0;
538 req
->read_gzip
= TRUE
;
539 gzip_stream
->parent_stream
= req
->data_stream
;
540 req
->data_stream
= &gzip_stream
->stream
;
541 return ERROR_SUCCESS
;
546 static DWORD
init_gzip_stream(http_request_t
*req
)
548 ERR("gzip stream not supported, missing zlib.\n");
549 return ERROR_SUCCESS
;
554 /***********************************************************************
555 * HTTP_Tokenize (internal)
557 * Tokenize a string, allocating memory for the tokens.
559 static LPWSTR
* HTTP_Tokenize(LPCWSTR string
, LPCWSTR token_string
)
561 LPWSTR
* token_array
;
568 /* empty string has no tokens */
572 for (i
= 0; string
[i
]; i
++)
574 if (!strncmpW(string
+i
, token_string
, strlenW(token_string
)))
578 /* we want to skip over separators, but not the null terminator */
579 for (j
= 0; j
< strlenW(token_string
) - 1; j
++)
587 /* add 1 for terminating NULL */
588 token_array
= heap_alloc((tokens
+1) * sizeof(*token_array
));
589 token_array
[tokens
] = NULL
;
592 for (i
= 0; i
< tokens
; i
++)
595 next_token
= strstrW(string
, token_string
);
596 if (!next_token
) next_token
= string
+strlenW(string
);
597 len
= next_token
- string
;
598 token_array
[i
] = heap_alloc((len
+1)*sizeof(WCHAR
));
599 memcpy(token_array
[i
], string
, len
*sizeof(WCHAR
));
600 token_array
[i
][len
] = '\0';
601 string
= next_token
+strlenW(token_string
);
606 /***********************************************************************
607 * HTTP_FreeTokens (internal)
609 * Frees memory returned from HTTP_Tokenize.
611 static void HTTP_FreeTokens(LPWSTR
* token_array
)
614 for (i
= 0; token_array
[i
]; i
++) heap_free(token_array
[i
]);
615 heap_free(token_array
);
618 static void HTTP_FixURL(http_request_t
*request
)
620 static const WCHAR szSlash
[] = { '/',0 };
621 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/', 0 };
623 /* If we don't have a path we set it to root */
624 if (NULL
== request
->path
)
625 request
->path
= heap_strdupW(szSlash
);
626 else /* remove \r and \n*/
628 int nLen
= strlenW(request
->path
);
629 while ((nLen
>0 ) && ((request
->path
[nLen
-1] == '\r')||(request
->path
[nLen
-1] == '\n')))
632 request
->path
[nLen
]='\0';
634 /* Replace '\' with '/' */
637 if (request
->path
[nLen
] == '\\') request
->path
[nLen
]='/';
641 if(CSTR_EQUAL
!= CompareStringW( LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
642 request
->path
, strlenW(request
->path
), szHttp
, strlenW(szHttp
) )
643 && request
->path
[0] != '/') /* not an absolute path ?? --> fix it !! */
645 WCHAR
*fixurl
= heap_alloc((strlenW(request
->path
) + 2)*sizeof(WCHAR
));
647 strcpyW(fixurl
+ 1, request
->path
);
648 heap_free( request
->path
);
649 request
->path
= fixurl
;
653 static LPWSTR
HTTP_BuildHeaderRequestString( http_request_t
*request
, LPCWSTR verb
, LPCWSTR path
, LPCWSTR version
)
655 LPWSTR requestString
;
661 static const WCHAR szSpace
[] = { ' ',0 };
662 static const WCHAR szColon
[] = { ':',' ',0 };
663 static const WCHAR sztwocrlf
[] = {'\r','\n','\r','\n', 0};
665 /* allocate space for an array of all the string pointers to be added */
666 len
= (request
->nCustHeaders
)*4 + 10;
667 req
= heap_alloc(len
*sizeof(LPCWSTR
));
669 /* add the verb, path and HTTP version string */
677 /* Append custom request headers */
678 for (i
= 0; i
< request
->nCustHeaders
; i
++)
680 if (request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
683 req
[n
++] = request
->custHeaders
[i
].lpszField
;
685 req
[n
++] = request
->custHeaders
[i
].lpszValue
;
687 TRACE("Adding custom header %s (%s)\n",
688 debugstr_w(request
->custHeaders
[i
].lpszField
),
689 debugstr_w(request
->custHeaders
[i
].lpszValue
));
694 ERR("oops. buffer overrun\n");
697 requestString
= HTTP_build_req( req
, 4 );
701 * Set (header) termination string for request
702 * Make sure there's exactly two new lines at the end of the request
704 p
= &requestString
[strlenW(requestString
)-1];
705 while ( (*p
== '\n') || (*p
== '\r') )
707 strcpyW( p
+1, sztwocrlf
);
709 return requestString
;
712 static void HTTP_ProcessCookies( http_request_t
*request
)
716 LPHTTPHEADERW setCookieHeader
;
718 if(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
)
721 while((HeaderIndex
= HTTP_GetCustomHeaderIndex(request
, szSet_Cookie
, numCookies
++, FALSE
)) != -1)
727 setCookieHeader
= &request
->custHeaders
[HeaderIndex
];
729 if (!setCookieHeader
->lpszValue
)
732 host
= HTTP_GetHeader(request
, hostW
);
736 data
= strchrW(setCookieHeader
->lpszValue
, '=');
740 name
= heap_strndupW(setCookieHeader
->lpszValue
, data
-setCookieHeader
->lpszValue
);
745 set_cookie(host
->lpszValue
, request
->path
, name
, data
);
750 static void strip_spaces(LPWSTR start
)
755 while (*str
== ' ' && *str
!= '\0')
759 memmove(start
, str
, sizeof(WCHAR
) * (strlenW(str
) + 1));
761 end
= start
+ strlenW(start
) - 1;
762 while (end
>= start
&& *end
== ' ')
769 static inline BOOL
is_basic_auth_value( LPCWSTR pszAuthValue
, LPWSTR
*pszRealm
)
771 static const WCHAR szBasic
[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
772 static const WCHAR szRealm
[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
774 is_basic
= !strncmpiW(pszAuthValue
, szBasic
, ARRAYSIZE(szBasic
)) &&
775 ((pszAuthValue
[ARRAYSIZE(szBasic
)] == ' ') || !pszAuthValue
[ARRAYSIZE(szBasic
)]);
776 if (is_basic
&& pszRealm
)
779 LPCWSTR ptr
= &pszAuthValue
[ARRAYSIZE(szBasic
)];
783 token
= strchrW(ptr
,'=');
787 while (*realm
== ' ' && *realm
!= '\0')
789 if(!strncmpiW(realm
, szRealm
, ARRAYSIZE(szRealm
)) &&
790 (realm
[ARRAYSIZE(szRealm
)] == ' ' || realm
[ARRAYSIZE(szRealm
)] == '='))
793 while (*token
== ' ' && *token
!= '\0')
797 *pszRealm
= heap_strdupW(token
);
798 strip_spaces(*pszRealm
);
805 static void destroy_authinfo( struct HttpAuthInfo
*authinfo
)
807 if (!authinfo
) return;
809 if (SecIsValidHandle(&authinfo
->ctx
))
810 DeleteSecurityContext(&authinfo
->ctx
);
811 if (SecIsValidHandle(&authinfo
->cred
))
812 FreeCredentialsHandle(&authinfo
->cred
);
814 heap_free(authinfo
->auth_data
);
815 heap_free(authinfo
->scheme
);
819 static UINT
retrieve_cached_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR
*auth_data
)
821 basicAuthorizationData
*ad
;
824 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host
),debugstr_w(realm
));
826 EnterCriticalSection(&authcache_cs
);
827 LIST_FOR_EACH_ENTRY(ad
, &basicAuthorizationCache
, basicAuthorizationData
, entry
)
829 if (!strcmpiW(host
,ad
->host
) && !strcmpW(realm
,ad
->realm
))
831 TRACE("Authorization found in cache\n");
832 *auth_data
= heap_alloc(ad
->authorizationLen
);
833 memcpy(*auth_data
,ad
->authorization
,ad
->authorizationLen
);
834 rc
= ad
->authorizationLen
;
838 LeaveCriticalSection(&authcache_cs
);
842 static void cache_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR auth_data
, UINT auth_data_len
)
845 basicAuthorizationData
* ad
= NULL
;
847 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host
),debugstr_w(realm
),debugstr_an(auth_data
,auth_data_len
));
849 EnterCriticalSection(&authcache_cs
);
850 LIST_FOR_EACH(cursor
, &basicAuthorizationCache
)
852 basicAuthorizationData
*check
= LIST_ENTRY(cursor
,basicAuthorizationData
,entry
);
853 if (!strcmpiW(host
,check
->host
) && !strcmpW(realm
,check
->realm
))
862 TRACE("Found match in cache, replacing\n");
863 heap_free(ad
->authorization
);
864 ad
->authorization
= heap_alloc(auth_data_len
);
865 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
866 ad
->authorizationLen
= auth_data_len
;
870 ad
= heap_alloc(sizeof(basicAuthorizationData
));
871 ad
->host
= heap_strdupW(host
);
872 ad
->realm
= heap_strdupW(realm
);
873 ad
->authorization
= heap_alloc(auth_data_len
);
874 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
875 ad
->authorizationLen
= auth_data_len
;
876 list_add_head(&basicAuthorizationCache
,&ad
->entry
);
877 TRACE("authorization cached\n");
879 LeaveCriticalSection(&authcache_cs
);
882 static BOOL
retrieve_cached_authorization(LPWSTR host
, LPWSTR scheme
,
883 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
885 authorizationData
*ad
;
887 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
889 EnterCriticalSection(&authcache_cs
);
890 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
) {
891 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
892 TRACE("Authorization found in cache\n");
894 nt_auth_identity
->User
= heap_strdupW(ad
->user
);
895 nt_auth_identity
->Password
= heap_strdupW(ad
->password
);
896 nt_auth_identity
->Domain
= heap_alloc(sizeof(WCHAR
)*ad
->domain_len
);
897 if(!nt_auth_identity
->User
|| !nt_auth_identity
->Password
||
898 (!nt_auth_identity
->Domain
&& ad
->domain_len
)) {
899 heap_free(nt_auth_identity
->User
);
900 heap_free(nt_auth_identity
->Password
);
901 heap_free(nt_auth_identity
->Domain
);
905 nt_auth_identity
->Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
906 nt_auth_identity
->UserLength
= ad
->user_len
;
907 nt_auth_identity
->PasswordLength
= ad
->password_len
;
908 memcpy(nt_auth_identity
->Domain
, ad
->domain
, sizeof(WCHAR
)*ad
->domain_len
);
909 nt_auth_identity
->DomainLength
= ad
->domain_len
;
910 LeaveCriticalSection(&authcache_cs
);
914 LeaveCriticalSection(&authcache_cs
);
919 static void cache_authorization(LPWSTR host
, LPWSTR scheme
,
920 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
922 authorizationData
*ad
;
925 TRACE("Caching authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
927 EnterCriticalSection(&authcache_cs
);
928 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
)
929 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
936 heap_free(ad
->password
);
937 heap_free(ad
->domain
);
939 ad
= heap_alloc(sizeof(authorizationData
));
941 LeaveCriticalSection(&authcache_cs
);
945 ad
->host
= heap_strdupW(host
);
946 ad
->scheme
= heap_strdupW(scheme
);
947 list_add_head(&authorizationCache
, &ad
->entry
);
950 ad
->user
= heap_strndupW(nt_auth_identity
->User
, nt_auth_identity
->UserLength
);
951 ad
->password
= heap_strndupW(nt_auth_identity
->Password
, nt_auth_identity
->PasswordLength
);
952 ad
->domain
= heap_strndupW(nt_auth_identity
->Domain
, nt_auth_identity
->DomainLength
);
953 ad
->user_len
= nt_auth_identity
->UserLength
;
954 ad
->password_len
= nt_auth_identity
->PasswordLength
;
955 ad
->domain_len
= nt_auth_identity
->DomainLength
;
957 if(!ad
->host
|| !ad
->scheme
|| !ad
->user
|| !ad
->password
958 || (nt_auth_identity
->Domain
&& !ad
->domain
)) {
960 heap_free(ad
->scheme
);
962 heap_free(ad
->password
);
963 heap_free(ad
->domain
);
964 list_remove(&ad
->entry
);
968 LeaveCriticalSection(&authcache_cs
);
971 static BOOL
HTTP_DoAuthorization( http_request_t
*request
, LPCWSTR pszAuthValue
,
972 struct HttpAuthInfo
**ppAuthInfo
,
973 LPWSTR domain_and_username
, LPWSTR password
,
976 SECURITY_STATUS sec_status
;
977 struct HttpAuthInfo
*pAuthInfo
= *ppAuthInfo
;
979 LPWSTR szRealm
= NULL
;
981 TRACE("%s\n", debugstr_w(pszAuthValue
));
988 pAuthInfo
= heap_alloc(sizeof(*pAuthInfo
));
992 SecInvalidateHandle(&pAuthInfo
->cred
);
993 SecInvalidateHandle(&pAuthInfo
->ctx
);
994 memset(&pAuthInfo
->exp
, 0, sizeof(pAuthInfo
->exp
));
996 pAuthInfo
->auth_data
= NULL
;
997 pAuthInfo
->auth_data_len
= 0;
998 pAuthInfo
->finished
= FALSE
;
1000 if (is_basic_auth_value(pszAuthValue
,NULL
))
1002 static const WCHAR szBasic
[] = {'B','a','s','i','c',0};
1003 pAuthInfo
->scheme
= heap_strdupW(szBasic
);
1004 if (!pAuthInfo
->scheme
)
1006 heap_free(pAuthInfo
);
1013 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity
;
1015 pAuthInfo
->scheme
= heap_strdupW(pszAuthValue
);
1016 if (!pAuthInfo
->scheme
)
1018 heap_free(pAuthInfo
);
1022 if (domain_and_username
)
1024 WCHAR
*user
= strchrW(domain_and_username
, '\\');
1025 WCHAR
*domain
= domain_and_username
;
1027 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1029 pAuthData
= &nt_auth_identity
;
1034 user
= domain_and_username
;
1038 nt_auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
1039 nt_auth_identity
.User
= user
;
1040 nt_auth_identity
.UserLength
= strlenW(nt_auth_identity
.User
);
1041 nt_auth_identity
.Domain
= domain
;
1042 nt_auth_identity
.DomainLength
= domain
? user
- domain
- 1 : 0;
1043 nt_auth_identity
.Password
= password
;
1044 nt_auth_identity
.PasswordLength
= strlenW(nt_auth_identity
.Password
);
1046 cache_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
);
1048 else if(retrieve_cached_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
))
1049 pAuthData
= &nt_auth_identity
;
1051 /* use default credentials */
1054 sec_status
= AcquireCredentialsHandleW(NULL
, pAuthInfo
->scheme
,
1055 SECPKG_CRED_OUTBOUND
, NULL
,
1057 NULL
, &pAuthInfo
->cred
,
1060 if(pAuthData
&& !domain_and_username
) {
1061 heap_free(nt_auth_identity
.User
);
1062 heap_free(nt_auth_identity
.Domain
);
1063 heap_free(nt_auth_identity
.Password
);
1066 if (sec_status
== SEC_E_OK
)
1068 PSecPkgInfoW sec_pkg_info
;
1069 sec_status
= QuerySecurityPackageInfoW(pAuthInfo
->scheme
, &sec_pkg_info
);
1070 if (sec_status
== SEC_E_OK
)
1072 pAuthInfo
->max_token
= sec_pkg_info
->cbMaxToken
;
1073 FreeContextBuffer(sec_pkg_info
);
1076 if (sec_status
!= SEC_E_OK
)
1078 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1079 debugstr_w(pAuthInfo
->scheme
), sec_status
);
1080 heap_free(pAuthInfo
->scheme
);
1081 heap_free(pAuthInfo
);
1085 *ppAuthInfo
= pAuthInfo
;
1087 else if (pAuthInfo
->finished
)
1090 if ((strlenW(pszAuthValue
) < strlenW(pAuthInfo
->scheme
)) ||
1091 strncmpiW(pszAuthValue
, pAuthInfo
->scheme
, strlenW(pAuthInfo
->scheme
)))
1093 ERR("authentication scheme changed from %s to %s\n",
1094 debugstr_w(pAuthInfo
->scheme
), debugstr_w(pszAuthValue
));
1098 if (is_basic_auth_value(pszAuthValue
,&szRealm
))
1102 char *auth_data
= NULL
;
1103 UINT auth_data_len
= 0;
1105 TRACE("basic authentication realm %s\n",debugstr_w(szRealm
));
1107 if (!domain_and_username
)
1109 if (host
&& szRealm
)
1110 auth_data_len
= retrieve_cached_basic_authorization(host
, szRealm
,&auth_data
);
1111 if (auth_data_len
== 0)
1119 userlen
= WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, lstrlenW(domain_and_username
), NULL
, 0, NULL
, NULL
);
1120 passlen
= WideCharToMultiByte(CP_UTF8
, 0, password
, lstrlenW(password
), NULL
, 0, NULL
, NULL
);
1122 /* length includes a nul terminator, which will be re-used for the ':' */
1123 auth_data
= heap_alloc(userlen
+ 1 + passlen
);
1130 WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, -1, auth_data
, userlen
, NULL
, NULL
);
1131 auth_data
[userlen
] = ':';
1132 WideCharToMultiByte(CP_UTF8
, 0, password
, -1, &auth_data
[userlen
+1], passlen
, NULL
, NULL
);
1133 auth_data_len
= userlen
+ 1 + passlen
;
1134 if (host
&& szRealm
)
1135 cache_basic_authorization(host
, szRealm
, auth_data
, auth_data_len
);
1138 pAuthInfo
->auth_data
= auth_data
;
1139 pAuthInfo
->auth_data_len
= auth_data_len
;
1140 pAuthInfo
->finished
= TRUE
;
1146 LPCWSTR pszAuthData
;
1147 SecBufferDesc out_desc
, in_desc
;
1149 unsigned char *buffer
;
1150 ULONG context_req
= ISC_REQ_CONNECTION
| ISC_REQ_USE_DCE_STYLE
|
1151 ISC_REQ_MUTUAL_AUTH
| ISC_REQ_DELEGATE
;
1153 in
.BufferType
= SECBUFFER_TOKEN
;
1157 in_desc
.ulVersion
= 0;
1158 in_desc
.cBuffers
= 1;
1159 in_desc
.pBuffers
= &in
;
1161 pszAuthData
= pszAuthValue
+ strlenW(pAuthInfo
->scheme
);
1162 if (*pszAuthData
== ' ')
1165 in
.cbBuffer
= HTTP_DecodeBase64(pszAuthData
, NULL
);
1166 in
.pvBuffer
= heap_alloc(in
.cbBuffer
);
1167 HTTP_DecodeBase64(pszAuthData
, in
.pvBuffer
);
1170 buffer
= heap_alloc(pAuthInfo
->max_token
);
1172 out
.BufferType
= SECBUFFER_TOKEN
;
1173 out
.cbBuffer
= pAuthInfo
->max_token
;
1174 out
.pvBuffer
= buffer
;
1176 out_desc
.ulVersion
= 0;
1177 out_desc
.cBuffers
= 1;
1178 out_desc
.pBuffers
= &out
;
1180 sec_status
= InitializeSecurityContextW(first
? &pAuthInfo
->cred
: NULL
,
1181 first
? NULL
: &pAuthInfo
->ctx
,
1182 first
? request
->session
->serverName
: NULL
,
1183 context_req
, 0, SECURITY_NETWORK_DREP
,
1184 in
.pvBuffer
? &in_desc
: NULL
,
1185 0, &pAuthInfo
->ctx
, &out_desc
,
1186 &pAuthInfo
->attr
, &pAuthInfo
->exp
);
1187 if (sec_status
== SEC_E_OK
)
1189 pAuthInfo
->finished
= TRUE
;
1190 pAuthInfo
->auth_data
= out
.pvBuffer
;
1191 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
1192 TRACE("sending last auth packet\n");
1194 else if (sec_status
== SEC_I_CONTINUE_NEEDED
)
1196 pAuthInfo
->auth_data
= out
.pvBuffer
;
1197 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
1198 TRACE("sending next auth packet\n");
1202 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status
);
1203 heap_free(out
.pvBuffer
);
1204 destroy_authinfo(pAuthInfo
);
1213 /***********************************************************************
1214 * HTTP_HttpAddRequestHeadersW (internal)
1216 static DWORD
HTTP_HttpAddRequestHeadersW(http_request_t
*request
,
1217 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1222 DWORD len
, res
= ERROR_HTTP_INVALID_HEADER
;
1224 TRACE("copying header: %s\n", debugstr_wn(lpszHeader
, dwHeaderLength
));
1226 if( dwHeaderLength
== ~0U )
1227 len
= strlenW(lpszHeader
);
1229 len
= dwHeaderLength
;
1230 buffer
= heap_alloc(sizeof(WCHAR
)*(len
+1));
1231 lstrcpynW( buffer
, lpszHeader
, len
+ 1);
1237 LPWSTR
* pFieldAndValue
;
1239 lpszEnd
= lpszStart
;
1241 while (*lpszEnd
!= '\0')
1243 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1248 if (*lpszStart
== '\0')
1251 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1254 lpszEnd
++; /* Jump over newline */
1256 TRACE("interpreting header %s\n", debugstr_w(lpszStart
));
1257 if (*lpszStart
== '\0')
1259 /* Skip 0-length headers */
1260 lpszStart
= lpszEnd
;
1261 res
= ERROR_SUCCESS
;
1264 pFieldAndValue
= HTTP_InterpretHttpHeader(lpszStart
);
1267 res
= HTTP_VerifyValidHeader(request
, pFieldAndValue
[0]);
1268 if (res
== ERROR_SUCCESS
)
1269 res
= HTTP_ProcessHeader(request
, pFieldAndValue
[0],
1270 pFieldAndValue
[1], dwModifier
| HTTP_ADDHDR_FLAG_REQ
);
1271 HTTP_FreeTokens(pFieldAndValue
);
1274 lpszStart
= lpszEnd
;
1275 } while (res
== ERROR_SUCCESS
);
1281 /***********************************************************************
1282 * HttpAddRequestHeadersW (WININET.@)
1284 * Adds one or more HTTP header to the request handler
1287 * On Windows if dwHeaderLength includes the trailing '\0', then
1288 * HttpAddRequestHeadersW() adds it too. However this results in an
1289 * invalid Http header which is rejected by some servers so we probably
1290 * don't need to match Windows on that point.
1297 BOOL WINAPI
HttpAddRequestHeadersW(HINTERNET hHttpRequest
,
1298 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1300 http_request_t
*request
;
1301 DWORD res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
1303 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_wn(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1308 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
1309 if (request
&& request
->hdr
.htype
== WH_HHTTPREQ
)
1310 res
= HTTP_HttpAddRequestHeadersW( request
, lpszHeader
, dwHeaderLength
, dwModifier
);
1312 WININET_Release( &request
->hdr
);
1314 if(res
!= ERROR_SUCCESS
)
1316 return res
== ERROR_SUCCESS
;
1319 /***********************************************************************
1320 * HttpAddRequestHeadersA (WININET.@)
1322 * Adds one or more HTTP header to the request handler
1329 BOOL WINAPI
HttpAddRequestHeadersA(HINTERNET hHttpRequest
,
1330 LPCSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1336 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_an(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1338 len
= MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, NULL
, 0 );
1339 hdr
= heap_alloc(len
*sizeof(WCHAR
));
1340 MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, hdr
, len
);
1341 if( dwHeaderLength
!= ~0U )
1342 dwHeaderLength
= len
;
1344 r
= HttpAddRequestHeadersW( hHttpRequest
, hdr
, dwHeaderLength
, dwModifier
);
1350 /***********************************************************************
1351 * HttpOpenRequestA (WININET.@)
1353 * Open a HTTP request handle
1356 * HINTERNET a HTTP request handle on success
1360 HINTERNET WINAPI
HttpOpenRequestA(HINTERNET hHttpSession
,
1361 LPCSTR lpszVerb
, LPCSTR lpszObjectName
, LPCSTR lpszVersion
,
1362 LPCSTR lpszReferrer
, LPCSTR
*lpszAcceptTypes
,
1363 DWORD dwFlags
, DWORD_PTR dwContext
)
1365 LPWSTR szVerb
= NULL
, szObjectName
= NULL
;
1366 LPWSTR szVersion
= NULL
, szReferrer
= NULL
, *szAcceptTypes
= NULL
;
1367 INT acceptTypesCount
;
1368 HINTERNET rc
= FALSE
;
1371 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1372 debugstr_a(lpszVerb
), debugstr_a(lpszObjectName
),
1373 debugstr_a(lpszVersion
), debugstr_a(lpszReferrer
), lpszAcceptTypes
,
1374 dwFlags
, dwContext
);
1378 szVerb
= heap_strdupAtoW(lpszVerb
);
1385 szObjectName
= heap_strdupAtoW(lpszObjectName
);
1386 if ( !szObjectName
)
1392 szVersion
= heap_strdupAtoW(lpszVersion
);
1399 szReferrer
= heap_strdupAtoW(lpszReferrer
);
1404 if (lpszAcceptTypes
)
1406 acceptTypesCount
= 0;
1407 types
= lpszAcceptTypes
;
1412 /* find out how many there are */
1413 if (*types
&& **types
)
1415 TRACE("accept type: %s\n", debugstr_a(*types
));
1421 WARN("invalid accept type pointer\n");
1426 szAcceptTypes
= heap_alloc(sizeof(WCHAR
*) * (acceptTypesCount
+1));
1427 if (!szAcceptTypes
) goto end
;
1429 acceptTypesCount
= 0;
1430 types
= lpszAcceptTypes
;
1435 if (*types
&& **types
)
1436 szAcceptTypes
[acceptTypesCount
++] = heap_strdupAtoW(*types
);
1440 /* ignore invalid pointer */
1445 szAcceptTypes
[acceptTypesCount
] = NULL
;
1448 rc
= HttpOpenRequestW(hHttpSession
, szVerb
, szObjectName
,
1449 szVersion
, szReferrer
,
1450 (LPCWSTR
*)szAcceptTypes
, dwFlags
, dwContext
);
1455 acceptTypesCount
= 0;
1456 while (szAcceptTypes
[acceptTypesCount
])
1458 heap_free(szAcceptTypes
[acceptTypesCount
]);
1461 heap_free(szAcceptTypes
);
1463 heap_free(szReferrer
);
1464 heap_free(szVersion
);
1465 heap_free(szObjectName
);
1470 /***********************************************************************
1473 static UINT
HTTP_EncodeBase64( LPCSTR bin
, unsigned int len
, LPWSTR base64
)
1476 static const CHAR HTTP_Base64Enc
[] =
1477 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1481 /* first 6 bits, all from bin[0] */
1482 base64
[n
++] = HTTP_Base64Enc
[(bin
[0] & 0xfc) >> 2];
1483 x
= (bin
[0] & 3) << 4;
1485 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1488 base64
[n
++] = HTTP_Base64Enc
[x
];
1493 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[1]&0xf0) >> 4 ) ];
1494 x
= ( bin
[1] & 0x0f ) << 2;
1496 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1499 base64
[n
++] = HTTP_Base64Enc
[x
];
1503 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[2]&0xc0 ) >> 6 ) ];
1505 /* last 6 bits, all from bin [2] */
1506 base64
[n
++] = HTTP_Base64Enc
[ bin
[2] & 0x3f ];
1514 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1515 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1516 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1517 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1518 static const signed char HTTP_Base64Dec
[256] =
1520 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1521 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1522 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1523 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1524 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1525 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1526 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1527 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1528 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1529 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1530 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1531 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1532 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1533 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1534 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1535 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1536 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1537 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1538 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1539 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1540 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1541 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1542 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1543 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1544 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1545 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1549 /***********************************************************************
1552 static UINT
HTTP_DecodeBase64( LPCWSTR base64
, LPSTR bin
)
1560 if (base64
[0] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1561 ((in
[0] = HTTP_Base64Dec
[base64
[0]]) == -1) ||
1562 base64
[1] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1563 ((in
[1] = HTTP_Base64Dec
[base64
[1]]) == -1))
1565 WARN("invalid base64: %s\n", debugstr_w(base64
));
1569 bin
[n
] = (unsigned char) (in
[0] << 2 | in
[1] >> 4);
1572 if ((base64
[2] == '=') && (base64
[3] == '='))
1574 if (base64
[2] > ARRAYSIZE(HTTP_Base64Dec
) ||
1575 ((in
[2] = HTTP_Base64Dec
[base64
[2]]) == -1))
1577 WARN("invalid base64: %s\n", debugstr_w(&base64
[2]));
1581 bin
[n
] = (unsigned char) (in
[1] << 4 | in
[2] >> 2);
1584 if (base64
[3] == '=')
1586 if (base64
[3] > ARRAYSIZE(HTTP_Base64Dec
) ||
1587 ((in
[3] = HTTP_Base64Dec
[base64
[3]]) == -1))
1589 WARN("invalid base64: %s\n", debugstr_w(&base64
[3]));
1593 bin
[n
] = (unsigned char) (((in
[2] << 6) & 0xc0) | in
[3]);
1602 /***********************************************************************
1603 * HTTP_InsertAuthorization
1605 * Insert or delete the authorization field in the request header.
1607 static BOOL
HTTP_InsertAuthorization( http_request_t
*request
, struct HttpAuthInfo
*pAuthInfo
, LPCWSTR header
)
1611 static const WCHAR wszSpace
[] = {' ',0};
1612 static const WCHAR wszBasic
[] = {'B','a','s','i','c',0};
1614 WCHAR
*authorization
= NULL
;
1616 if (pAuthInfo
->auth_data_len
)
1618 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1619 len
= strlenW(pAuthInfo
->scheme
)+1+((pAuthInfo
->auth_data_len
+2)*4)/3;
1620 authorization
= heap_alloc((len
+1)*sizeof(WCHAR
));
1624 strcpyW(authorization
, pAuthInfo
->scheme
);
1625 strcatW(authorization
, wszSpace
);
1626 HTTP_EncodeBase64(pAuthInfo
->auth_data
,
1627 pAuthInfo
->auth_data_len
,
1628 authorization
+strlenW(authorization
));
1630 /* clear the data as it isn't valid now that it has been sent to the
1631 * server, unless it's Basic authentication which doesn't do
1632 * connection tracking */
1633 if (strcmpiW(pAuthInfo
->scheme
, wszBasic
))
1635 heap_free(pAuthInfo
->auth_data
);
1636 pAuthInfo
->auth_data
= NULL
;
1637 pAuthInfo
->auth_data_len
= 0;
1641 TRACE("Inserting authorization: %s\n", debugstr_w(authorization
));
1643 HTTP_ProcessHeader(request
, header
, authorization
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
1644 heap_free(authorization
);
1649 static WCHAR
*HTTP_BuildProxyRequestUrl(http_request_t
*req
)
1651 static const WCHAR slash
[] = { '/',0 };
1652 static const WCHAR format
[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1653 static const WCHAR formatSSL
[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1654 http_session_t
*session
= req
->session
;
1655 WCHAR new_location
[INTERNET_MAX_URL_LENGTH
], *url
;
1658 size
= sizeof(new_location
);
1659 if (HTTP_HttpQueryInfoW(req
, HTTP_QUERY_LOCATION
, new_location
, &size
, NULL
) == ERROR_SUCCESS
)
1661 URL_COMPONENTSW UrlComponents
;
1663 if (!(url
= heap_alloc(size
+ sizeof(WCHAR
)))) return NULL
;
1664 strcpyW( url
, new_location
);
1666 ZeroMemory(&UrlComponents
,sizeof(URL_COMPONENTSW
));
1667 if(InternetCrackUrlW(url
, 0, 0, &UrlComponents
)) goto done
;
1671 size
= 16; /* "https://" + sizeof(port#) + ":/\0" */
1672 size
+= strlenW( session
->hostName
) + strlenW( req
->path
);
1674 if (!(url
= heap_alloc(size
* sizeof(WCHAR
)))) return NULL
;
1676 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1677 sprintfW( url
, formatSSL
, session
->hostName
, session
->hostPort
);
1679 sprintfW( url
, format
, session
->hostName
, session
->hostPort
);
1680 if (req
->path
[0] != '/') strcatW( url
, slash
);
1681 strcatW( url
, req
->path
);
1684 TRACE("url=%s\n", debugstr_w(url
));
1688 /***********************************************************************
1689 * HTTP_DealWithProxy
1691 static BOOL
HTTP_DealWithProxy(appinfo_t
*hIC
, http_session_t
*session
, http_request_t
*request
)
1693 WCHAR buf
[INTERNET_MAX_HOST_NAME_LENGTH
];
1694 WCHAR protoProxy
[INTERNET_MAX_URL_LENGTH
];
1695 DWORD protoProxyLen
= INTERNET_MAX_URL_LENGTH
;
1696 WCHAR proxy
[INTERNET_MAX_URL_LENGTH
];
1697 static WCHAR szNul
[] = { 0 };
1698 URL_COMPONENTSW UrlComponents
;
1699 static const WCHAR protoHttp
[] = { 'h','t','t','p',0 };
1700 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/',0 };
1701 static const WCHAR szFormat
[] = { 'h','t','t','p',':','/','/','%','s',0 };
1703 memset( &UrlComponents
, 0, sizeof UrlComponents
);
1704 UrlComponents
.dwStructSize
= sizeof UrlComponents
;
1705 UrlComponents
.lpszHostName
= buf
;
1706 UrlComponents
.dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
1708 if (!INTERNET_FindProxyForProtocol(hIC
->proxy
, protoHttp
, protoProxy
, &protoProxyLen
))
1710 if( CSTR_EQUAL
!= CompareStringW(LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
1711 protoProxy
,strlenW(szHttp
),szHttp
,strlenW(szHttp
)) )
1712 sprintfW(proxy
, szFormat
, protoProxy
);
1714 strcpyW(proxy
, protoProxy
);
1715 if( !InternetCrackUrlW(proxy
, 0, 0, &UrlComponents
) )
1717 if( UrlComponents
.dwHostNameLength
== 0 )
1720 if( !request
->path
)
1721 request
->path
= szNul
;
1723 if(UrlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
1724 UrlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
1726 heap_free(session
->serverName
);
1727 session
->serverName
= heap_strdupW(UrlComponents
.lpszHostName
);
1728 session
->serverPort
= UrlComponents
.nPort
;
1730 TRACE("proxy server=%s port=%d\n", debugstr_w(session
->serverName
), session
->serverPort
);
1734 static DWORD
HTTP_ResolveName(http_request_t
*request
, server_t
*server
)
1739 if(server
->addr_len
)
1740 return ERROR_SUCCESS
;
1742 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1743 INTERNET_STATUS_RESOLVING_NAME
,
1745 (strlenW(server
->name
)+1) * sizeof(WCHAR
));
1747 addr_len
= sizeof(server
->addr
);
1748 if (!GetAddress(server
->name
, server
->port
, (struct sockaddr
*)&server
->addr
, &addr_len
))
1749 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1751 switch(server
->addr
.ss_family
) {
1753 addr
= &((struct sockaddr_in
*)&server
->addr
)->sin_addr
;
1756 addr
= &((struct sockaddr_in6
*)&server
->addr
)->sin6_addr
;
1759 WARN("unsupported family %d\n", server
->addr
.ss_family
);
1760 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1763 server
->addr_len
= addr_len
;
1764 inet_ntop(server
->addr
.ss_family
, addr
, server
->addr_str
, sizeof(server
->addr_str
));
1765 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1766 INTERNET_STATUS_NAME_RESOLVED
,
1767 server
->addr_str
, strlen(server
->addr_str
)+1);
1769 TRACE("resolved %s to %s\n", debugstr_w(server
->name
), server
->addr_str
);
1770 return ERROR_SUCCESS
;
1773 static BOOL
HTTP_GetRequestURL(http_request_t
*req
, LPWSTR buf
)
1775 static const WCHAR http
[] = { 'h','t','t','p',':','/','/',0 };
1776 static const WCHAR https
[] = { 'h','t','t','p','s',':','/','/',0 };
1777 static const WCHAR slash
[] = { '/',0 };
1778 LPHTTPHEADERW host_header
;
1781 host_header
= HTTP_GetHeader(req
, hostW
);
1785 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1789 strcpyW(buf
, scheme
);
1790 strcatW(buf
, host_header
->lpszValue
);
1791 if (req
->path
[0] != '/')
1792 strcatW(buf
, slash
);
1793 strcatW(buf
, req
->path
);
1798 /***********************************************************************
1799 * HTTPREQ_Destroy (internal)
1801 * Deallocate request handle
1804 static void HTTPREQ_Destroy(object_header_t
*hdr
)
1806 http_request_t
*request
= (http_request_t
*) hdr
;
1811 if(request
->hCacheFile
) {
1812 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1814 CloseHandle(request
->hCacheFile
);
1816 if(HTTP_GetRequestURL(request
, url
)) {
1819 headersLen
= request
->rawHeaders
? strlenW(request
->rawHeaders
) : 0;
1820 CommitUrlCacheEntryW(url
, request
->cacheFile
, request
->expires
,
1821 request
->last_modified
, NORMAL_CACHE_ENTRY
,
1822 request
->rawHeaders
, headersLen
, NULL
, 0);
1825 heap_free(request
->cacheFile
);
1827 DeleteCriticalSection( &request
->read_section
);
1828 WININET_Release(&request
->session
->hdr
);
1830 destroy_authinfo(request
->authInfo
);
1831 destroy_authinfo(request
->proxyAuthInfo
);
1833 heap_free(request
->path
);
1834 heap_free(request
->verb
);
1835 heap_free(request
->rawHeaders
);
1836 heap_free(request
->version
);
1837 heap_free(request
->statusText
);
1839 for (i
= 0; i
< request
->nCustHeaders
; i
++)
1841 heap_free(request
->custHeaders
[i
].lpszField
);
1842 heap_free(request
->custHeaders
[i
].lpszValue
);
1844 destroy_data_stream(request
->data_stream
);
1845 heap_free(request
->custHeaders
);
1848 static void http_release_netconn(http_request_t
*req
, BOOL reuse
)
1850 TRACE("%p %p\n",req
, req
->netconn
);
1855 if(reuse
&& req
->netconn
->keep_alive
) {
1858 EnterCriticalSection(&connection_pool_cs
);
1860 list_add_head(&req
->netconn
->server
->conn_pool
, &req
->netconn
->pool_entry
);
1861 req
->netconn
->keep_until
= GetTickCount64() + COLLECT_TIME
;
1862 req
->netconn
= NULL
;
1864 run_collector
= !collector_running
;
1865 collector_running
= TRUE
;
1867 LeaveCriticalSection(&connection_pool_cs
);
1870 HANDLE thread
= NULL
;
1873 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
, (const WCHAR
*)WININET_hModule
, &module
);
1875 thread
= CreateThread(NULL
, 0, collect_connections_proc
, NULL
, 0, NULL
);
1877 EnterCriticalSection(&connection_pool_cs
);
1878 collector_running
= FALSE
;
1879 LeaveCriticalSection(&connection_pool_cs
);
1882 FreeLibrary(module
);
1888 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
1889 INTERNET_STATUS_CLOSING_CONNECTION
, 0, 0);
1891 free_netconn(req
->netconn
);
1892 req
->netconn
= NULL
;
1894 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
1895 INTERNET_STATUS_CONNECTION_CLOSED
, 0, 0);
1898 static void drain_content(http_request_t
*req
)
1902 if (!req
->netconn
) return;
1904 if (req
->contentLength
== -1)
1906 else if(!strcmpW(req
->verb
, szHEAD
))
1909 try_reuse
= req
->data_stream
->vtbl
->drain_content(req
->data_stream
, req
);
1911 http_release_netconn(req
, try_reuse
);
1914 static BOOL
HTTP_KeepAlive(http_request_t
*request
)
1916 WCHAR szVersion
[10];
1917 WCHAR szConnectionResponse
[20];
1918 DWORD dwBufferSize
= sizeof(szVersion
);
1919 BOOL keepalive
= FALSE
;
1921 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1922 * the connection is keep-alive by default */
1923 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_VERSION
, szVersion
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1924 && !strcmpiW(szVersion
, g_szHttp1_1
))
1929 dwBufferSize
= sizeof(szConnectionResponse
);
1930 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_PROXY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1931 || HTTP_HttpQueryInfoW(request
, HTTP_QUERY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
1933 keepalive
= !strcmpiW(szConnectionResponse
, szKeepAlive
);
1939 static void HTTPREQ_CloseConnection(object_header_t
*hdr
)
1941 http_request_t
*req
= (http_request_t
*)hdr
;
1946 static DWORD
HTTPREQ_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
1948 http_request_t
*req
= (http_request_t
*)hdr
;
1951 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO
:
1953 http_session_t
*session
= req
->session
;
1954 INTERNET_DIAGNOSTIC_SOCKET_INFO
*info
= buffer
;
1956 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1958 if (*size
< sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
))
1959 return ERROR_INSUFFICIENT_BUFFER
;
1960 *size
= sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
);
1961 /* FIXME: can't get a SOCKET from our connection since we don't use
1965 /* FIXME: get source port from req->netConnection */
1966 info
->SourcePort
= 0;
1967 info
->DestPort
= session
->hostPort
;
1969 if (HTTP_KeepAlive(req
))
1970 info
->Flags
|= IDSI_FLAG_KEEP_ALIVE
;
1971 if (session
->appInfo
->proxy
&& session
->appInfo
->proxy
[0] != 0)
1972 info
->Flags
|= IDSI_FLAG_PROXY
;
1973 if (req
->netconn
->useSSL
)
1974 info
->Flags
|= IDSI_FLAG_SECURE
;
1976 return ERROR_SUCCESS
;
1979 case INTERNET_OPTION_SECURITY_FLAGS
:
1983 if (*size
< sizeof(ULONG
))
1984 return ERROR_INSUFFICIENT_BUFFER
;
1986 *size
= sizeof(DWORD
);
1988 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1989 flags
|= SECURITY_FLAG_SECURE
;
1990 flags
|= req
->security_flags
;
1992 int bits
= NETCON_GetCipherStrength(req
->netconn
);
1994 flags
|= SECURITY_FLAG_STRENGTH_STRONG
;
1995 else if (bits
>= 56)
1996 flags
|= SECURITY_FLAG_STRENGTH_MEDIUM
;
1998 flags
|= SECURITY_FLAG_STRENGTH_WEAK
;
2000 *(DWORD
*)buffer
= flags
;
2001 return ERROR_SUCCESS
;
2004 case INTERNET_OPTION_HANDLE_TYPE
:
2005 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2007 if (*size
< sizeof(ULONG
))
2008 return ERROR_INSUFFICIENT_BUFFER
;
2010 *size
= sizeof(DWORD
);
2011 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_HTTP_REQUEST
;
2012 return ERROR_SUCCESS
;
2014 case INTERNET_OPTION_URL
: {
2015 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2020 static const WCHAR httpW
[] = {'h','t','t','p',':','/','/',0};
2022 TRACE("INTERNET_OPTION_URL\n");
2024 host
= HTTP_GetHeader(req
, hostW
);
2025 strcpyW(url
, httpW
);
2026 strcatW(url
, host
->lpszValue
);
2027 if (NULL
!= (pch
= strchrW(url
+ strlenW(httpW
), ':')))
2029 strcatW(url
, req
->path
);
2031 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url
));
2034 len
= (strlenW(url
)+1) * sizeof(WCHAR
);
2036 return ERROR_INSUFFICIENT_BUFFER
;
2039 strcpyW(buffer
, url
);
2040 return ERROR_SUCCESS
;
2042 len
= WideCharToMultiByte(CP_ACP
, 0, url
, -1, buffer
, *size
, NULL
, NULL
);
2044 return ERROR_INSUFFICIENT_BUFFER
;
2047 return ERROR_SUCCESS
;
2051 case INTERNET_OPTION_CACHE_TIMESTAMPS
: {
2052 INTERNET_CACHE_ENTRY_INFOW
*info
;
2053 INTERNET_CACHE_TIMESTAMPS
*ts
= buffer
;
2054 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2055 DWORD nbytes
, error
;
2058 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2060 if (*size
< sizeof(*ts
))
2062 *size
= sizeof(*ts
);
2063 return ERROR_INSUFFICIENT_BUFFER
;
2066 HTTP_GetRequestURL(req
, url
);
2067 ret
= GetUrlCacheEntryInfoW(url
, NULL
, &nbytes
);
2068 error
= GetLastError();
2069 if (!ret
&& error
== ERROR_INSUFFICIENT_BUFFER
)
2071 if (!(info
= heap_alloc(nbytes
)))
2072 return ERROR_OUTOFMEMORY
;
2074 GetUrlCacheEntryInfoW(url
, info
, &nbytes
);
2076 ts
->ftExpires
= info
->ExpireTime
;
2077 ts
->ftLastModified
= info
->LastModifiedTime
;
2080 *size
= sizeof(*ts
);
2081 return ERROR_SUCCESS
;
2086 case INTERNET_OPTION_DATAFILE_NAME
: {
2089 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2091 if(!req
->cacheFile
) {
2093 return ERROR_INTERNET_ITEM_NOT_FOUND
;
2097 req_size
= (lstrlenW(req
->cacheFile
)+1) * sizeof(WCHAR
);
2098 if(*size
< req_size
)
2099 return ERROR_INSUFFICIENT_BUFFER
;
2102 memcpy(buffer
, req
->cacheFile
, *size
);
2103 return ERROR_SUCCESS
;
2105 req_size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
, -1, NULL
, 0, NULL
, NULL
);
2106 if (req_size
> *size
)
2107 return ERROR_INSUFFICIENT_BUFFER
;
2109 *size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
,
2110 -1, buffer
, *size
, NULL
, NULL
);
2111 return ERROR_SUCCESS
;
2115 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT
: {
2116 PCCERT_CONTEXT context
;
2118 if(*size
< sizeof(INTERNET_CERTIFICATE_INFOA
)) {
2119 *size
= sizeof(INTERNET_CERTIFICATE_INFOA
);
2120 return ERROR_INSUFFICIENT_BUFFER
;
2123 context
= (PCCERT_CONTEXT
)NETCON_GetCert(req
->netconn
);
2125 INTERNET_CERTIFICATE_INFOA
*info
= (INTERNET_CERTIFICATE_INFOA
*)buffer
;
2128 memset(info
, 0, sizeof(INTERNET_CERTIFICATE_INFOW
));
2129 info
->ftExpiry
= context
->pCertInfo
->NotAfter
;
2130 info
->ftStart
= context
->pCertInfo
->NotBefore
;
2131 len
= CertNameToStrA(context
->dwCertEncodingType
,
2132 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
2133 info
->lpszSubjectInfo
= LocalAlloc(0, len
);
2134 if(info
->lpszSubjectInfo
)
2135 CertNameToStrA(context
->dwCertEncodingType
,
2136 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
,
2137 info
->lpszSubjectInfo
, len
);
2138 len
= CertNameToStrA(context
->dwCertEncodingType
,
2139 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
2140 info
->lpszIssuerInfo
= LocalAlloc(0, len
);
2141 if(info
->lpszIssuerInfo
)
2142 CertNameToStrA(context
->dwCertEncodingType
,
2143 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
,
2144 info
->lpszIssuerInfo
, len
);
2145 info
->dwKeySize
= NETCON_GetCipherStrength(req
->netconn
);
2146 CertFreeCertificateContext(context
);
2147 return ERROR_SUCCESS
;
2152 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
2155 static DWORD
HTTPREQ_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
2157 http_request_t
*req
= (http_request_t
*)hdr
;
2160 case INTERNET_OPTION_SECURITY_FLAGS
:
2164 if (!buffer
|| size
!= sizeof(DWORD
))
2165 return ERROR_INVALID_PARAMETER
;
2166 flags
= *(DWORD
*)buffer
;
2167 TRACE("%08x\n", flags
);
2168 req
->security_flags
= flags
;
2170 req
->netconn
->security_flags
= flags
;
2171 return ERROR_SUCCESS
;
2173 case INTERNET_OPTION_SEND_TIMEOUT
:
2174 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
2175 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
2177 if (size
!= sizeof(DWORD
))
2178 return ERROR_INVALID_PARAMETER
;
2181 FIXME("unsupported without active connection\n");
2182 return ERROR_SUCCESS
;
2185 return NETCON_set_timeout(req
->netconn
, option
== INTERNET_OPTION_SEND_TIMEOUT
,
2188 case INTERNET_OPTION_USERNAME
:
2189 heap_free(req
->session
->userName
);
2190 if (!(req
->session
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2191 return ERROR_SUCCESS
;
2193 case INTERNET_OPTION_PASSWORD
:
2194 heap_free(req
->session
->password
);
2195 if (!(req
->session
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2196 return ERROR_SUCCESS
;
2197 case INTERNET_OPTION_HTTP_DECODING
:
2198 if(size
!= sizeof(BOOL
))
2199 return ERROR_INVALID_PARAMETER
;
2200 req
->decoding
= *(BOOL
*)buffer
;
2201 return ERROR_SUCCESS
;
2204 return ERROR_INTERNET_INVALID_OPTION
;
2207 /* read some more data into the read buffer (the read section must be held) */
2208 static DWORD
read_more_data( http_request_t
*req
, int maxlen
)
2215 /* move existing data to the start of the buffer */
2217 memmove( req
->read_buf
, req
->read_buf
+ req
->read_pos
, req
->read_size
);
2221 if (maxlen
== -1) maxlen
= sizeof(req
->read_buf
);
2223 res
= NETCON_recv( req
->netconn
, req
->read_buf
+ req
->read_size
,
2224 maxlen
- req
->read_size
, 0, &len
);
2225 if(res
== ERROR_SUCCESS
)
2226 req
->read_size
+= len
;
2231 /* remove some amount of data from the read buffer (the read section must be held) */
2232 static void remove_data( http_request_t
*req
, int count
)
2234 if (!(req
->read_size
-= count
)) req
->read_pos
= 0;
2235 else req
->read_pos
+= count
;
2238 static BOOL
read_line( http_request_t
*req
, LPSTR buffer
, DWORD
*len
)
2240 int count
, bytes_read
, pos
= 0;
2243 EnterCriticalSection( &req
->read_section
);
2246 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
2250 count
= eol
- (req
->read_buf
+ req
->read_pos
);
2251 bytes_read
= count
+ 1;
2253 else count
= bytes_read
= req
->read_size
;
2255 count
= min( count
, *len
- pos
);
2256 memcpy( buffer
+ pos
, req
->read_buf
+ req
->read_pos
, count
);
2258 remove_data( req
, bytes_read
);
2261 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
|| !req
->read_size
)
2264 TRACE( "returning empty string %u\n", res
);
2265 LeaveCriticalSection( &req
->read_section
);
2266 INTERNET_SetLastError(res
);
2270 LeaveCriticalSection( &req
->read_section
);
2274 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
2277 buffer
[*len
- 1] = 0;
2278 TRACE( "returning %s\n", debugstr_a(buffer
));
2282 /* check if we have reached the end of the data to read (the read section must be held) */
2283 static BOOL
end_of_read_data( http_request_t
*req
)
2285 return !req
->read_size
&& req
->data_stream
->vtbl
->end_of_data(req
->data_stream
, req
);
2288 /* fetch some more data into the read buffer (the read section must be held) */
2289 static DWORD
refill_read_buffer(http_request_t
*req
, read_mode_t read_mode
, DWORD
*read_bytes
)
2293 if(req
->read_size
== sizeof(req
->read_buf
))
2294 return ERROR_SUCCESS
;
2298 memmove(req
->read_buf
, req
->read_buf
+req
->read_pos
, req
->read_size
);
2302 res
= req
->data_stream
->vtbl
->read(req
->data_stream
, req
, req
->read_buf
+req
->read_size
,
2303 sizeof(req
->read_buf
)-req
->read_size
, &read
, read_mode
);
2304 req
->read_size
+= read
;
2306 TRACE("read %u bytes, read_size %u\n", read
, req
->read_size
);
2312 /* return the size of data available to be read immediately (the read section must be held) */
2313 static DWORD
get_avail_data( http_request_t
*req
)
2315 return req
->read_size
+ req
->data_stream
->vtbl
->get_avail_data(req
->data_stream
, req
);
2318 static DWORD
netconn_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
2320 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2324 NETCON_query_data_available(req
->netconn
, &avail
);
2325 return netconn_stream
->content_length
== ~0u
2327 : min(avail
, netconn_stream
->content_length
-netconn_stream
->content_read
);
2330 static BOOL
netconn_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
2332 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2333 return netconn_stream
->content_read
== netconn_stream
->content_length
|| !req
->netconn
;
2336 static DWORD
netconn_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
2337 DWORD
*read
, read_mode_t read_mode
)
2339 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2342 size
= min(size
, netconn_stream
->content_length
-netconn_stream
->content_read
);
2344 if(read_mode
== READMODE_NOBLOCK
)
2345 size
= min(size
, netconn_get_avail_data(stream
, req
));
2347 if(size
&& req
->netconn
) {
2348 if(NETCON_recv(req
->netconn
, buf
, size
, read_mode
== READMODE_SYNC
? MSG_WAITALL
: 0, &len
) != ERROR_SUCCESS
)
2351 netconn_stream
->content_length
= netconn_stream
->content_read
;
2354 netconn_stream
->content_read
+= *read
= len
;
2355 TRACE("read %u bytes\n", len
);
2356 return ERROR_SUCCESS
;
2359 static BOOL
netconn_drain_content(data_stream_t
*stream
, http_request_t
*req
)
2361 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2366 if(netconn_end_of_data(stream
, req
))
2370 avail
= netconn_get_avail_data(stream
, req
);
2374 if(NETCON_recv(req
->netconn
, buf
, min(avail
, sizeof(buf
)), 0, &len
) != ERROR_SUCCESS
)
2377 netconn_stream
->content_read
+= len
;
2378 }while(netconn_stream
->content_read
< netconn_stream
->content_length
);
2383 static void netconn_destroy(data_stream_t
*stream
)
2387 static const data_stream_vtbl_t netconn_stream_vtbl
= {
2388 netconn_get_avail_data
,
2389 netconn_end_of_data
,
2391 netconn_drain_content
,
2395 /* read some more data into the read buffer (the read section must be held) */
2396 static DWORD
read_more_chunked_data(chunked_stream_t
*stream
, http_request_t
*req
, int maxlen
)
2401 if (stream
->buf_pos
)
2403 /* move existing data to the start of the buffer */
2404 if(stream
->buf_size
)
2405 memmove(stream
->buf
, stream
->buf
+ stream
->buf_pos
, stream
->buf_size
);
2406 stream
->buf_pos
= 0;
2409 if (maxlen
== -1) maxlen
= sizeof(stream
->buf
);
2411 res
= NETCON_recv( req
->netconn
, stream
->buf
+ stream
->buf_size
,
2412 maxlen
- stream
->buf_size
, 0, &len
);
2413 if(res
== ERROR_SUCCESS
)
2414 stream
->buf_size
+= len
;
2419 /* remove some amount of data from the read buffer (the read section must be held) */
2420 static void remove_chunked_data(chunked_stream_t
*stream
, int count
)
2422 if (!(stream
->buf_size
-= count
)) stream
->buf_pos
= 0;
2423 else stream
->buf_pos
+= count
;
2426 /* discard data contents until we reach end of line (the read section must be held) */
2427 static DWORD
discard_chunked_eol(chunked_stream_t
*stream
, http_request_t
*req
)
2433 BYTE
*eol
= memchr(stream
->buf
+ stream
->buf_pos
, '\n', stream
->buf_size
);
2436 remove_chunked_data(stream
, (eol
+ 1) - (stream
->buf
+ stream
->buf_pos
));
2439 stream
->buf_pos
= stream
->buf_size
= 0; /* discard everything */
2440 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2441 } while (stream
->buf_size
);
2442 return ERROR_SUCCESS
;
2445 /* read the size of the next chunk (the read section must be held) */
2446 static DWORD
start_next_chunk(chunked_stream_t
*stream
, http_request_t
*req
)
2449 DWORD chunk_size
= 0, res
;
2451 if(stream
->chunk_size
!= ~0u && (res
= discard_chunked_eol(stream
, req
)) != ERROR_SUCCESS
)
2456 while (stream
->buf_size
)
2458 char ch
= stream
->buf
[stream
->buf_pos
];
2459 if (ch
>= '0' && ch
<= '9') chunk_size
= chunk_size
* 16 + ch
- '0';
2460 else if (ch
>= 'a' && ch
<= 'f') chunk_size
= chunk_size
* 16 + ch
- 'a' + 10;
2461 else if (ch
>= 'A' && ch
<= 'F') chunk_size
= chunk_size
* 16 + ch
- 'A' + 10;
2462 else if (ch
== ';' || ch
== '\r' || ch
== '\n')
2464 TRACE( "reading %u byte chunk\n", chunk_size
);
2465 stream
->chunk_size
= chunk_size
;
2466 req
->contentLength
+= chunk_size
;
2467 return discard_chunked_eol(stream
, req
);
2469 remove_chunked_data(stream
, 1);
2471 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2472 if (!stream
->buf_size
)
2474 stream
->chunk_size
= 0;
2475 return ERROR_SUCCESS
;
2480 static DWORD
chunked_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
2482 /* Allow reading only from read buffer */
2486 static BOOL
chunked_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
2488 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2489 return !chunked_stream
->chunk_size
;
2492 static DWORD
chunked_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
2493 DWORD
*read
, read_mode_t read_mode
)
2495 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2496 DWORD read_bytes
= 0, ret_read
= 0, res
= ERROR_SUCCESS
;
2498 if(chunked_stream
->chunk_size
== ~0u) {
2499 res
= start_next_chunk(chunked_stream
, req
);
2500 if(res
!= ERROR_SUCCESS
)
2504 while(size
&& chunked_stream
->chunk_size
) {
2505 if(chunked_stream
->buf_size
) {
2506 read_bytes
= min(size
, min(chunked_stream
->buf_size
, chunked_stream
->chunk_size
));
2508 /* this could block */
2509 if(read_mode
== READMODE_NOBLOCK
&& read_bytes
== chunked_stream
->chunk_size
)
2512 memcpy(buf
+ret_read
, chunked_stream
->buf
+chunked_stream
->buf_pos
, read_bytes
);
2513 remove_chunked_data(chunked_stream
, read_bytes
);
2515 read_bytes
= min(size
, chunked_stream
->chunk_size
);
2517 if(read_mode
== READMODE_NOBLOCK
) {
2520 if(!NETCON_query_data_available(req
->netconn
, &avail
) || !avail
)
2522 if(read_bytes
> avail
)
2525 /* this could block */
2526 if(read_bytes
== chunked_stream
->chunk_size
)
2530 res
= NETCON_recv(req
->netconn
, (char *)buf
+ret_read
, read_bytes
, 0, (int*)&read_bytes
);
2531 if(res
!= ERROR_SUCCESS
)
2535 chunked_stream
->chunk_size
-= read_bytes
;
2537 ret_read
+= read_bytes
;
2538 if(!chunked_stream
->chunk_size
) {
2539 assert(read_mode
!= READMODE_NOBLOCK
);
2540 res
= start_next_chunk(chunked_stream
, req
);
2541 if(res
!= ERROR_SUCCESS
)
2545 if(read_mode
== READMODE_ASYNC
)
2546 read_mode
= READMODE_NOBLOCK
;
2549 TRACE("read %u bytes\n", ret_read
);
2554 static BOOL
chunked_drain_content(data_stream_t
*stream
, http_request_t
*req
)
2556 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2558 /* FIXME: we can do better */
2559 return !chunked_stream
->chunk_size
;
2562 static void chunked_destroy(data_stream_t
*stream
)
2564 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2565 heap_free(chunked_stream
);
2568 static const data_stream_vtbl_t chunked_stream_vtbl
= {
2569 chunked_get_avail_data
,
2570 chunked_end_of_data
,
2572 chunked_drain_content
,
2576 /* set the request content length based on the headers */
2577 static DWORD
set_content_length(http_request_t
*request
, DWORD status_code
)
2579 static const WCHAR szChunked
[] = {'c','h','u','n','k','e','d',0};
2583 if(status_code
== HTTP_STATUS_NO_CONTENT
) {
2584 request
->contentLength
= request
->netconn_stream
.content_length
= 0;
2585 return ERROR_SUCCESS
;
2588 size
= sizeof(request
->contentLength
);
2589 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_CONTENT_LENGTH
,
2590 &request
->contentLength
, &size
, NULL
) != ERROR_SUCCESS
)
2591 request
->contentLength
= ~0u;
2592 request
->netconn_stream
.content_length
= request
->contentLength
;
2593 request
->netconn_stream
.content_read
= request
->read_size
;
2595 size
= sizeof(encoding
);
2596 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_TRANSFER_ENCODING
, encoding
, &size
, NULL
) == ERROR_SUCCESS
&&
2597 !strcmpiW(encoding
, szChunked
))
2599 chunked_stream_t
*chunked_stream
;
2601 chunked_stream
= heap_alloc(sizeof(*chunked_stream
));
2603 return ERROR_OUTOFMEMORY
;
2605 chunked_stream
->data_stream
.vtbl
= &chunked_stream_vtbl
;
2606 chunked_stream
->buf_size
= chunked_stream
->buf_pos
= 0;
2607 chunked_stream
->chunk_size
= ~0u;
2609 if(request
->read_size
) {
2610 memcpy(chunked_stream
->buf
, request
->read_buf
+request
->read_pos
, request
->read_size
);
2611 chunked_stream
->buf_size
= request
->read_size
;
2612 request
->read_size
= request
->read_pos
= 0;
2615 request
->data_stream
= &chunked_stream
->data_stream
;
2616 request
->contentLength
= ~0u;
2617 request
->read_chunked
= TRUE
;
2620 if(request
->decoding
) {
2623 static const WCHAR gzipW
[] = {'g','z','i','p',0};
2625 encoding_idx
= HTTP_GetCustomHeaderIndex(request
, szContent_Encoding
, 0, FALSE
);
2626 if(encoding_idx
!= -1 && !strcmpiW(request
->custHeaders
[encoding_idx
].lpszValue
, gzipW
))
2627 return init_gzip_stream(request
);
2630 return ERROR_SUCCESS
;
2633 static void send_request_complete(http_request_t
*req
, DWORD_PTR result
, DWORD error
)
2635 INTERNET_ASYNC_RESULT iar
;
2637 iar
.dwResult
= result
;
2638 iar
.dwError
= error
;
2640 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2641 sizeof(INTERNET_ASYNC_RESULT
));
2644 static void HTTP_ReceiveRequestData(http_request_t
*req
, BOOL first_notif
)
2646 DWORD res
, read
= 0, avail
= 0;
2651 EnterCriticalSection( &req
->read_section
);
2653 mode
= first_notif
&& req
->read_size
? READMODE_NOBLOCK
: READMODE_ASYNC
;
2654 res
= refill_read_buffer(req
, mode
, &read
);
2655 if(res
== ERROR_SUCCESS
&& !first_notif
)
2656 avail
= get_avail_data(req
);
2658 LeaveCriticalSection( &req
->read_section
);
2660 if(res
!= ERROR_SUCCESS
|| (mode
!= READMODE_NOBLOCK
&& !read
)) {
2661 WARN("res %u read %u, closing connection\n", res
, read
);
2662 http_release_netconn(req
, FALSE
);
2665 if(res
== ERROR_SUCCESS
)
2666 send_request_complete(req
, req
->session
->hdr
.dwInternalFlags
& INET_OPENURL
? (DWORD_PTR
)req
->hdr
.hInternet
: 1, avail
);
2668 send_request_complete(req
, 0, res
);
2671 /* read data from the http connection (the read section must be held) */
2672 static DWORD
HTTPREQ_Read(http_request_t
*req
, void *buffer
, DWORD size
, DWORD
*read
, BOOL sync
)
2674 DWORD current_read
= 0, ret_read
= 0;
2675 read_mode_t read_mode
;
2676 DWORD res
= ERROR_SUCCESS
;
2678 read_mode
= req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
? READMODE_ASYNC
: READMODE_SYNC
;
2680 EnterCriticalSection( &req
->read_section
);
2682 if(req
->read_size
) {
2683 ret_read
= min(size
, req
->read_size
);
2684 memcpy(buffer
, req
->read_buf
+req
->read_pos
, ret_read
);
2685 req
->read_size
-= ret_read
;
2686 req
->read_pos
+= ret_read
;
2687 if(read_mode
== READMODE_ASYNC
)
2688 read_mode
= READMODE_NOBLOCK
;
2691 if(ret_read
< size
) {
2692 res
= req
->data_stream
->vtbl
->read(req
->data_stream
, req
, (BYTE
*)buffer
+ret_read
, size
-ret_read
, ¤t_read
, read_mode
);
2693 ret_read
+= current_read
;
2696 LeaveCriticalSection( &req
->read_section
);
2699 TRACE( "retrieved %u bytes (%u)\n", ret_read
, req
->contentLength
);
2701 if(req
->hCacheFile
&& res
== ERROR_SUCCESS
&& ret_read
) {
2705 res
= WriteFile(req
->hCacheFile
, buffer
, ret_read
, &written
, NULL
);
2707 WARN("WriteFile failed: %u\n", GetLastError());
2710 if(size
&& !ret_read
)
2711 http_release_netconn(req
, res
== ERROR_SUCCESS
);
2717 static DWORD
HTTPREQ_ReadFile(object_header_t
*hdr
, void *buffer
, DWORD size
, DWORD
*read
)
2719 http_request_t
*req
= (http_request_t
*)hdr
;
2722 EnterCriticalSection( &req
->read_section
);
2723 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2724 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2726 res
= HTTPREQ_Read(req
, buffer
, size
, read
, TRUE
);
2727 if(res
== ERROR_SUCCESS
)
2729 LeaveCriticalSection( &req
->read_section
);
2734 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST
*workRequest
)
2736 struct WORKREQ_INTERNETREADFILEEXA
const *data
= &workRequest
->u
.InternetReadFileExA
;
2737 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2740 TRACE("INTERNETREADFILEEXA %p\n", workRequest
->hdr
);
2742 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2743 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2745 send_request_complete(req
, res
== ERROR_SUCCESS
, res
);
2748 static DWORD
HTTPREQ_ReadFileExA(object_header_t
*hdr
, INTERNET_BUFFERSA
*buffers
,
2749 DWORD flags
, DWORD_PTR context
)
2751 http_request_t
*req
= (http_request_t
*)hdr
;
2752 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2754 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2755 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2757 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2758 return ERROR_INVALID_PARAMETER
;
2760 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2762 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2764 WORKREQUEST workRequest
;
2766 if (TryEnterCriticalSection( &req
->read_section
))
2768 if (get_avail_data(req
))
2770 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2771 &buffers
->dwBufferLength
, FALSE
);
2772 size
= buffers
->dwBufferLength
;
2773 LeaveCriticalSection( &req
->read_section
);
2776 LeaveCriticalSection( &req
->read_section
);
2779 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExAProc
;
2780 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2781 workRequest
.u
.InternetReadFileExA
.lpBuffersOut
= buffers
;
2783 INTERNET_AsyncCall(&workRequest
);
2785 return ERROR_IO_PENDING
;
2789 size
= buffers
->dwBufferLength
;
2791 EnterCriticalSection( &req
->read_section
);
2792 if(hdr
->dwError
== ERROR_SUCCESS
)
2793 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2794 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2795 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2798 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2799 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2800 if(res
!= ERROR_SUCCESS
)
2803 read
+= buffers
->dwBufferLength
;
2804 if(read
== size
|| end_of_read_data(req
))
2807 LeaveCriticalSection( &req
->read_section
);
2809 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2810 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2811 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2812 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2814 EnterCriticalSection( &req
->read_section
);
2817 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2818 hdr
->dwError
= ERROR_SUCCESS
;
2820 error
= hdr
->dwError
;
2822 LeaveCriticalSection( &req
->read_section
);
2823 size
= buffers
->dwBufferLength
;
2824 buffers
->dwBufferLength
= read
;
2827 if (res
== ERROR_SUCCESS
) {
2828 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2829 &size
, sizeof(size
));
2832 return res
==ERROR_SUCCESS
? error
: res
;
2835 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST
*workRequest
)
2837 struct WORKREQ_INTERNETREADFILEEXW
const *data
= &workRequest
->u
.InternetReadFileExW
;
2838 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2841 TRACE("INTERNETREADFILEEXW %p\n", workRequest
->hdr
);
2843 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2844 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2846 send_request_complete(req
, res
== ERROR_SUCCESS
, res
);
2849 static DWORD
HTTPREQ_ReadFileExW(object_header_t
*hdr
, INTERNET_BUFFERSW
*buffers
,
2850 DWORD flags
, DWORD_PTR context
)
2853 http_request_t
*req
= (http_request_t
*)hdr
;
2854 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2856 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2857 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2859 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2860 return ERROR_INVALID_PARAMETER
;
2862 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2864 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2866 WORKREQUEST workRequest
;
2868 if (TryEnterCriticalSection( &req
->read_section
))
2870 if (get_avail_data(req
))
2872 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2873 &buffers
->dwBufferLength
, FALSE
);
2874 size
= buffers
->dwBufferLength
;
2875 LeaveCriticalSection( &req
->read_section
);
2878 LeaveCriticalSection( &req
->read_section
);
2881 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExWProc
;
2882 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2883 workRequest
.u
.InternetReadFileExW
.lpBuffersOut
= buffers
;
2885 INTERNET_AsyncCall(&workRequest
);
2887 return ERROR_IO_PENDING
;
2891 size
= buffers
->dwBufferLength
;
2893 EnterCriticalSection( &req
->read_section
);
2894 if(hdr
->dwError
== ERROR_SUCCESS
)
2895 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2896 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2897 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2900 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2901 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2902 if(res
!= ERROR_SUCCESS
)
2905 read
+= buffers
->dwBufferLength
;
2906 if(read
== size
|| end_of_read_data(req
))
2909 LeaveCriticalSection( &req
->read_section
);
2911 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2912 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2913 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2914 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2916 EnterCriticalSection( &req
->read_section
);
2919 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2920 hdr
->dwError
= ERROR_SUCCESS
;
2922 error
= hdr
->dwError
;
2924 LeaveCriticalSection( &req
->read_section
);
2925 size
= buffers
->dwBufferLength
;
2926 buffers
->dwBufferLength
= read
;
2929 if (res
== ERROR_SUCCESS
) {
2930 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2931 &size
, sizeof(size
));
2934 return res
==ERROR_SUCCESS
? error
: res
;
2937 static DWORD
HTTPREQ_WriteFile(object_header_t
*hdr
, const void *buffer
, DWORD size
, DWORD
*written
)
2940 http_request_t
*request
= (http_request_t
*)hdr
;
2942 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
2945 res
= NETCON_send(request
->netconn
, buffer
, size
, 0, (LPINT
)written
);
2946 if (res
== ERROR_SUCCESS
)
2947 request
->bytesWritten
+= *written
;
2949 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_SENT
, written
, sizeof(DWORD
));
2953 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST
*workRequest
)
2955 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2957 HTTP_ReceiveRequestData(req
, FALSE
);
2960 static DWORD
HTTPREQ_QueryDataAvailable(object_header_t
*hdr
, DWORD
*available
, DWORD flags
, DWORD_PTR ctx
)
2962 http_request_t
*req
= (http_request_t
*)hdr
;
2964 TRACE("(%p %p %x %lx)\n", req
, available
, flags
, ctx
);
2966 if (req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
2968 WORKREQUEST workRequest
;
2970 /* never wait, if we can't enter the section we queue an async request right away */
2971 if (TryEnterCriticalSection( &req
->read_section
))
2973 refill_read_buffer(req
, READMODE_NOBLOCK
, NULL
);
2974 if ((*available
= get_avail_data( req
))) goto done
;
2975 if (end_of_read_data( req
)) goto done
;
2976 LeaveCriticalSection( &req
->read_section
);
2979 workRequest
.asyncproc
= HTTPREQ_AsyncQueryDataAvailableProc
;
2980 workRequest
.hdr
= WININET_AddRef( &req
->hdr
);
2982 INTERNET_AsyncCall(&workRequest
);
2984 return ERROR_IO_PENDING
;
2987 EnterCriticalSection( &req
->read_section
);
2989 if (!(*available
= get_avail_data( req
)) && !end_of_read_data( req
))
2991 refill_read_buffer( req
, READMODE_ASYNC
, NULL
);
2992 *available
= get_avail_data( req
);
2996 LeaveCriticalSection( &req
->read_section
);
2998 TRACE( "returning %u\n", *available
);
2999 return ERROR_SUCCESS
;
3002 static const object_vtbl_t HTTPREQVtbl
= {
3004 HTTPREQ_CloseConnection
,
3005 HTTPREQ_QueryOption
,
3008 HTTPREQ_ReadFileExA
,
3009 HTTPREQ_ReadFileExW
,
3011 HTTPREQ_QueryDataAvailable
,
3015 /***********************************************************************
3016 * HTTP_HttpOpenRequestW (internal)
3018 * Open a HTTP request handle
3021 * HINTERNET a HTTP request handle on success
3025 static DWORD
HTTP_HttpOpenRequestW(http_session_t
*session
,
3026 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
3027 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
3028 DWORD dwFlags
, DWORD_PTR dwContext
, HINTERNET
*ret
)
3030 appinfo_t
*hIC
= session
->appInfo
;
3031 http_request_t
*request
;
3032 DWORD len
, res
= ERROR_SUCCESS
;
3036 request
= alloc_object(&session
->hdr
, &HTTPREQVtbl
, sizeof(http_request_t
));
3038 return ERROR_OUTOFMEMORY
;
3040 request
->hdr
.htype
= WH_HHTTPREQ
;
3041 request
->hdr
.dwFlags
= dwFlags
;
3042 request
->hdr
.dwContext
= dwContext
;
3043 request
->contentLength
= ~0u;
3045 request
->netconn_stream
.data_stream
.vtbl
= &netconn_stream_vtbl
;
3046 request
->data_stream
= &request
->netconn_stream
.data_stream
;
3048 InitializeCriticalSection( &request
->read_section
);
3050 WININET_AddRef( &session
->hdr
);
3051 request
->session
= session
;
3052 list_add_head( &session
->hdr
.children
, &request
->hdr
.entry
);
3054 if (dwFlags
& INTERNET_FLAG_IGNORE_CERT_CN_INVALID
)
3055 request
->security_flags
|= SECURITY_FLAG_IGNORE_CERT_CN_INVALID
;
3056 if (dwFlags
& INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
)
3057 request
->security_flags
|= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
3059 if (lpszObjectName
&& *lpszObjectName
) {
3063 rc
= UrlEscapeW(lpszObjectName
, NULL
, &len
, URL_ESCAPE_SPACES_ONLY
);
3064 if (rc
!= E_POINTER
)
3065 len
= strlenW(lpszObjectName
)+1;
3066 request
->path
= heap_alloc(len
*sizeof(WCHAR
));
3067 rc
= UrlEscapeW(lpszObjectName
, request
->path
, &len
,
3068 URL_ESCAPE_SPACES_ONLY
);
3071 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName
),rc
);
3072 strcpyW(request
->path
,lpszObjectName
);
3075 static const WCHAR slashW
[] = {'/',0};
3077 request
->path
= heap_strdupW(slashW
);
3080 if (lpszReferrer
&& *lpszReferrer
)
3081 HTTP_ProcessHeader(request
, HTTP_REFERER
, lpszReferrer
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3083 if (lpszAcceptTypes
)
3086 for (i
= 0; lpszAcceptTypes
[i
]; i
++)
3088 if (!*lpszAcceptTypes
[i
]) continue;
3089 HTTP_ProcessHeader(request
, HTTP_ACCEPT
, lpszAcceptTypes
[i
],
3090 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
|
3091 HTTP_ADDHDR_FLAG_REQ
|
3092 (i
== 0 ? HTTP_ADDHDR_FLAG_REPLACE
: 0));
3096 request
->verb
= heap_strdupW(lpszVerb
&& *lpszVerb
? lpszVerb
: szGET
);
3097 request
->version
= heap_strdupW(lpszVersion
? lpszVersion
: g_szHttp1_1
);
3099 if (session
->hostPort
!= INTERNET_INVALID_PORT_NUMBER
&&
3100 session
->hostPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
3101 session
->hostPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3105 static const WCHAR host_formatW
[] = {'%','s',':','%','u',0};
3107 host_name
= heap_alloc((strlenW(session
->hostName
) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR
));
3109 res
= ERROR_OUTOFMEMORY
;
3113 sprintfW(host_name
, host_formatW
, session
->hostName
, session
->hostPort
);
3114 HTTP_ProcessHeader(request
, hostW
, host_name
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3115 heap_free(host_name
);
3118 HTTP_ProcessHeader(request
, hostW
, session
->hostName
,
3119 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3121 if (session
->serverPort
== INTERNET_INVALID_PORT_NUMBER
)
3122 session
->serverPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
3123 INTERNET_DEFAULT_HTTPS_PORT
:
3124 INTERNET_DEFAULT_HTTP_PORT
);
3126 if (session
->hostPort
== INTERNET_INVALID_PORT_NUMBER
)
3127 session
->hostPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
3128 INTERNET_DEFAULT_HTTPS_PORT
:
3129 INTERNET_DEFAULT_HTTP_PORT
);
3131 if (hIC
->proxy
&& hIC
->proxy
[0])
3132 HTTP_DealWithProxy( hIC
, session
, request
);
3134 INTERNET_SendCallback(&session
->hdr
, dwContext
,
3135 INTERNET_STATUS_HANDLE_CREATED
, &request
->hdr
.hInternet
,
3139 TRACE("<-- %u (%p)\n", res
, request
);
3141 if(res
!= ERROR_SUCCESS
) {
3142 WININET_Release( &request
->hdr
);
3147 *ret
= request
->hdr
.hInternet
;
3148 return ERROR_SUCCESS
;
3151 /***********************************************************************
3152 * HttpOpenRequestW (WININET.@)
3154 * Open a HTTP request handle
3157 * HINTERNET a HTTP request handle on success
3161 HINTERNET WINAPI
HttpOpenRequestW(HINTERNET hHttpSession
,
3162 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
3163 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
3164 DWORD dwFlags
, DWORD_PTR dwContext
)
3166 http_session_t
*session
;
3167 HINTERNET handle
= NULL
;
3170 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
3171 debugstr_w(lpszVerb
), debugstr_w(lpszObjectName
),
3172 debugstr_w(lpszVersion
), debugstr_w(lpszReferrer
), lpszAcceptTypes
,
3173 dwFlags
, dwContext
);
3174 if(lpszAcceptTypes
!=NULL
)
3177 for(i
=0;lpszAcceptTypes
[i
]!=NULL
;i
++)
3178 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes
[i
]));
3181 session
= (http_session_t
*) get_handle_object( hHttpSession
);
3182 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
3184 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3189 * My tests seem to show that the windows version does not
3190 * become asynchronous until after this point. And anyhow
3191 * if this call was asynchronous then how would you get the
3192 * necessary HINTERNET pointer returned by this function.
3195 res
= HTTP_HttpOpenRequestW(session
, lpszVerb
, lpszObjectName
,
3196 lpszVersion
, lpszReferrer
, lpszAcceptTypes
,
3197 dwFlags
, dwContext
, &handle
);
3200 WININET_Release( &session
->hdr
);
3201 TRACE("returning %p\n", handle
);
3202 if(res
!= ERROR_SUCCESS
)
3207 static const LPCWSTR header_lookup
[] = {
3208 szMime_Version
, /* HTTP_QUERY_MIME_VERSION = 0 */
3209 szContent_Type
, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3210 szContent_Transfer_Encoding
,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3211 szContent_ID
, /* HTTP_QUERY_CONTENT_ID = 3 */
3212 NULL
, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3213 szContent_Length
, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3214 szContent_Language
, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3215 szAllow
, /* HTTP_QUERY_ALLOW = 7 */
3216 szPublic
, /* HTTP_QUERY_PUBLIC = 8 */
3217 szDate
, /* HTTP_QUERY_DATE = 9 */
3218 szExpires
, /* HTTP_QUERY_EXPIRES = 10 */
3219 szLast_Modified
, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3220 NULL
, /* HTTP_QUERY_MESSAGE_ID = 12 */
3221 szURI
, /* HTTP_QUERY_URI = 13 */
3222 szFrom
, /* HTTP_QUERY_DERIVED_FROM = 14 */
3223 NULL
, /* HTTP_QUERY_COST = 15 */
3224 NULL
, /* HTTP_QUERY_LINK = 16 */
3225 szPragma
, /* HTTP_QUERY_PRAGMA = 17 */
3226 NULL
, /* HTTP_QUERY_VERSION = 18 */
3227 szStatus
, /* HTTP_QUERY_STATUS_CODE = 19 */
3228 NULL
, /* HTTP_QUERY_STATUS_TEXT = 20 */
3229 NULL
, /* HTTP_QUERY_RAW_HEADERS = 21 */
3230 NULL
, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3231 szConnection
, /* HTTP_QUERY_CONNECTION = 23 */
3232 szAccept
, /* HTTP_QUERY_ACCEPT = 24 */
3233 szAccept_Charset
, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3234 szAccept_Encoding
, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3235 szAccept_Language
, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3236 szAuthorization
, /* HTTP_QUERY_AUTHORIZATION = 28 */
3237 szContent_Encoding
, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3238 NULL
, /* HTTP_QUERY_FORWARDED = 30 */
3239 NULL
, /* HTTP_QUERY_FROM = 31 */
3240 szIf_Modified_Since
, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3241 szLocation
, /* HTTP_QUERY_LOCATION = 33 */
3242 NULL
, /* HTTP_QUERY_ORIG_URI = 34 */
3243 szReferer
, /* HTTP_QUERY_REFERER = 35 */
3244 szRetry_After
, /* HTTP_QUERY_RETRY_AFTER = 36 */
3245 szServer
, /* HTTP_QUERY_SERVER = 37 */
3246 NULL
, /* HTTP_TITLE = 38 */
3247 szUser_Agent
, /* HTTP_QUERY_USER_AGENT = 39 */
3248 szWWW_Authenticate
, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3249 szProxy_Authenticate
, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3250 szAccept_Ranges
, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3251 szSet_Cookie
, /* HTTP_QUERY_SET_COOKIE = 43 */
3252 szCookie
, /* HTTP_QUERY_COOKIE = 44 */
3253 NULL
, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3254 NULL
, /* HTTP_QUERY_REFRESH = 46 */
3255 NULL
, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3256 szAge
, /* HTTP_QUERY_AGE = 48 */
3257 szCache_Control
, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3258 szContent_Base
, /* HTTP_QUERY_CONTENT_BASE = 50 */
3259 szContent_Location
, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3260 szContent_MD5
, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3261 szContent_Range
, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3262 szETag
, /* HTTP_QUERY_ETAG = 54 */
3263 hostW
, /* HTTP_QUERY_HOST = 55 */
3264 szIf_Match
, /* HTTP_QUERY_IF_MATCH = 56 */
3265 szIf_None_Match
, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3266 szIf_Range
, /* HTTP_QUERY_IF_RANGE = 58 */
3267 szIf_Unmodified_Since
, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3268 szMax_Forwards
, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3269 szProxy_Authorization
, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3270 szRange
, /* HTTP_QUERY_RANGE = 62 */
3271 szTransfer_Encoding
, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3272 szUpgrade
, /* HTTP_QUERY_UPGRADE = 64 */
3273 szVary
, /* HTTP_QUERY_VARY = 65 */
3274 szVia
, /* HTTP_QUERY_VIA = 66 */
3275 szWarning
, /* HTTP_QUERY_WARNING = 67 */
3276 szExpect
, /* HTTP_QUERY_EXPECT = 68 */
3277 szProxy_Connection
, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3278 szUnless_Modified_Since
, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3281 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3283 /***********************************************************************
3284 * HTTP_HttpQueryInfoW (internal)
3286 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*request
, DWORD dwInfoLevel
,
3287 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3289 LPHTTPHEADERW lphttpHdr
= NULL
;
3290 BOOL request_only
= dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
;
3291 INT requested_index
= lpdwIndex
? *lpdwIndex
: 0;
3292 DWORD level
= (dwInfoLevel
& ~HTTP_QUERY_MODIFIER_FLAGS_MASK
);
3295 /* Find requested header structure */
3298 case HTTP_QUERY_CUSTOM
:
3299 if (!lpBuffer
) return ERROR_INVALID_PARAMETER
;
3300 index
= HTTP_GetCustomHeaderIndex(request
, lpBuffer
, requested_index
, request_only
);
3302 case HTTP_QUERY_RAW_HEADERS_CRLF
:
3306 DWORD res
= ERROR_INVALID_PARAMETER
;
3309 headers
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
3311 headers
= request
->rawHeaders
;
3314 len
= strlenW(headers
) * sizeof(WCHAR
);
3316 if (len
+ sizeof(WCHAR
) > *lpdwBufferLength
)
3318 len
+= sizeof(WCHAR
);
3319 res
= ERROR_INSUFFICIENT_BUFFER
;
3324 memcpy(lpBuffer
, headers
, len
+ sizeof(WCHAR
));
3327 len
= strlenW(szCrLf
) * sizeof(WCHAR
);
3328 memcpy(lpBuffer
, szCrLf
, sizeof(szCrLf
));
3330 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
/ sizeof(WCHAR
)));
3331 res
= ERROR_SUCCESS
;
3333 *lpdwBufferLength
= len
;
3335 if (request_only
) heap_free(headers
);
3338 case HTTP_QUERY_RAW_HEADERS
:
3340 LPWSTR
* ppszRawHeaderLines
= HTTP_Tokenize(request
->rawHeaders
, szCrLf
);
3342 LPWSTR pszString
= lpBuffer
;
3344 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3345 size
+= strlenW(ppszRawHeaderLines
[i
]) + 1;
3347 if (size
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3349 HTTP_FreeTokens(ppszRawHeaderLines
);
3350 *lpdwBufferLength
= (size
+ 1) * sizeof(WCHAR
);
3351 return ERROR_INSUFFICIENT_BUFFER
;
3355 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3357 DWORD len
= strlenW(ppszRawHeaderLines
[i
]);
3358 memcpy(pszString
, ppszRawHeaderLines
[i
], (len
+1)*sizeof(WCHAR
));
3362 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, size
));
3364 *lpdwBufferLength
= size
* sizeof(WCHAR
);
3365 HTTP_FreeTokens(ppszRawHeaderLines
);
3367 return ERROR_SUCCESS
;
3369 case HTTP_QUERY_STATUS_TEXT
:
3370 if (request
->statusText
)
3372 DWORD len
= strlenW(request
->statusText
);
3373 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3375 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3376 return ERROR_INSUFFICIENT_BUFFER
;
3380 memcpy(lpBuffer
, request
->statusText
, (len
+ 1) * sizeof(WCHAR
));
3381 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3383 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3384 return ERROR_SUCCESS
;
3387 case HTTP_QUERY_VERSION
:
3388 if (request
->version
)
3390 DWORD len
= strlenW(request
->version
);
3391 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3393 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3394 return ERROR_INSUFFICIENT_BUFFER
;
3398 memcpy(lpBuffer
, request
->version
, (len
+ 1) * sizeof(WCHAR
));
3399 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3401 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3402 return ERROR_SUCCESS
;
3405 case HTTP_QUERY_CONTENT_ENCODING
:
3406 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[request
->read_gzip
? HTTP_QUERY_CONTENT_TYPE
: level
],
3407 requested_index
,request_only
);
3410 assert (LAST_TABLE_HEADER
== (HTTP_QUERY_UNLESS_MODIFIED_SINCE
+ 1));
3412 if (level
< LAST_TABLE_HEADER
&& header_lookup
[level
])
3413 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[level
],
3414 requested_index
,request_only
);
3418 lphttpHdr
= &request
->custHeaders
[index
];
3420 /* Ensure header satisfies requested attributes */
3422 ((dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
) &&
3423 (~lphttpHdr
->wFlags
& HDR_ISREQUEST
)))
3425 return ERROR_HTTP_HEADER_NOT_FOUND
;
3428 if (lpdwIndex
&& level
!= HTTP_QUERY_STATUS_CODE
) (*lpdwIndex
)++;
3430 /* coalesce value to requested type */
3431 if (dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
&& lpBuffer
)
3433 *(int *)lpBuffer
= atoiW(lphttpHdr
->lpszValue
);
3434 TRACE(" returning number: %d\n", *(int *)lpBuffer
);
3436 else if (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
&& lpBuffer
)
3442 tmpTime
= ConvertTimeString(lphttpHdr
->lpszValue
);
3444 tmpTM
= *gmtime(&tmpTime
);
3445 STHook
= (SYSTEMTIME
*)lpBuffer
;
3446 STHook
->wDay
= tmpTM
.tm_mday
;
3447 STHook
->wHour
= tmpTM
.tm_hour
;
3448 STHook
->wMilliseconds
= 0;
3449 STHook
->wMinute
= tmpTM
.tm_min
;
3450 STHook
->wDayOfWeek
= tmpTM
.tm_wday
;
3451 STHook
->wMonth
= tmpTM
.tm_mon
+ 1;
3452 STHook
->wSecond
= tmpTM
.tm_sec
;
3453 STHook
->wYear
= tmpTM
.tm_year
;
3455 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3456 STHook
->wYear
, STHook
->wMonth
, STHook
->wDay
, STHook
->wDayOfWeek
,
3457 STHook
->wHour
, STHook
->wMinute
, STHook
->wSecond
, STHook
->wMilliseconds
);
3459 else if (lphttpHdr
->lpszValue
)
3461 DWORD len
= (strlenW(lphttpHdr
->lpszValue
) + 1) * sizeof(WCHAR
);
3463 if (len
> *lpdwBufferLength
)
3465 *lpdwBufferLength
= len
;
3466 return ERROR_INSUFFICIENT_BUFFER
;
3470 memcpy(lpBuffer
, lphttpHdr
->lpszValue
, len
);
3471 TRACE("! returning string: %s\n", debugstr_w(lpBuffer
));
3473 *lpdwBufferLength
= len
- sizeof(WCHAR
);
3475 return ERROR_SUCCESS
;
3478 /***********************************************************************
3479 * HttpQueryInfoW (WININET.@)
3481 * Queries for information about an HTTP request
3488 BOOL WINAPI
HttpQueryInfoW(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3489 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3491 http_request_t
*request
;
3494 if (TRACE_ON(wininet
)) {
3495 #define FE(x) { x, #x }
3496 static const wininet_flag_info query_flags
[] = {
3497 FE(HTTP_QUERY_MIME_VERSION
),
3498 FE(HTTP_QUERY_CONTENT_TYPE
),
3499 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING
),
3500 FE(HTTP_QUERY_CONTENT_ID
),
3501 FE(HTTP_QUERY_CONTENT_DESCRIPTION
),
3502 FE(HTTP_QUERY_CONTENT_LENGTH
),
3503 FE(HTTP_QUERY_CONTENT_LANGUAGE
),
3504 FE(HTTP_QUERY_ALLOW
),
3505 FE(HTTP_QUERY_PUBLIC
),
3506 FE(HTTP_QUERY_DATE
),
3507 FE(HTTP_QUERY_EXPIRES
),
3508 FE(HTTP_QUERY_LAST_MODIFIED
),
3509 FE(HTTP_QUERY_MESSAGE_ID
),
3511 FE(HTTP_QUERY_DERIVED_FROM
),
3512 FE(HTTP_QUERY_COST
),
3513 FE(HTTP_QUERY_LINK
),
3514 FE(HTTP_QUERY_PRAGMA
),
3515 FE(HTTP_QUERY_VERSION
),
3516 FE(HTTP_QUERY_STATUS_CODE
),
3517 FE(HTTP_QUERY_STATUS_TEXT
),
3518 FE(HTTP_QUERY_RAW_HEADERS
),
3519 FE(HTTP_QUERY_RAW_HEADERS_CRLF
),
3520 FE(HTTP_QUERY_CONNECTION
),
3521 FE(HTTP_QUERY_ACCEPT
),
3522 FE(HTTP_QUERY_ACCEPT_CHARSET
),
3523 FE(HTTP_QUERY_ACCEPT_ENCODING
),
3524 FE(HTTP_QUERY_ACCEPT_LANGUAGE
),
3525 FE(HTTP_QUERY_AUTHORIZATION
),
3526 FE(HTTP_QUERY_CONTENT_ENCODING
),
3527 FE(HTTP_QUERY_FORWARDED
),
3528 FE(HTTP_QUERY_FROM
),
3529 FE(HTTP_QUERY_IF_MODIFIED_SINCE
),
3530 FE(HTTP_QUERY_LOCATION
),
3531 FE(HTTP_QUERY_ORIG_URI
),
3532 FE(HTTP_QUERY_REFERER
),
3533 FE(HTTP_QUERY_RETRY_AFTER
),
3534 FE(HTTP_QUERY_SERVER
),
3535 FE(HTTP_QUERY_TITLE
),
3536 FE(HTTP_QUERY_USER_AGENT
),
3537 FE(HTTP_QUERY_WWW_AUTHENTICATE
),
3538 FE(HTTP_QUERY_PROXY_AUTHENTICATE
),
3539 FE(HTTP_QUERY_ACCEPT_RANGES
),
3540 FE(HTTP_QUERY_SET_COOKIE
),
3541 FE(HTTP_QUERY_COOKIE
),
3542 FE(HTTP_QUERY_REQUEST_METHOD
),
3543 FE(HTTP_QUERY_REFRESH
),
3544 FE(HTTP_QUERY_CONTENT_DISPOSITION
),
3546 FE(HTTP_QUERY_CACHE_CONTROL
),
3547 FE(HTTP_QUERY_CONTENT_BASE
),
3548 FE(HTTP_QUERY_CONTENT_LOCATION
),
3549 FE(HTTP_QUERY_CONTENT_MD5
),
3550 FE(HTTP_QUERY_CONTENT_RANGE
),
3551 FE(HTTP_QUERY_ETAG
),
3552 FE(HTTP_QUERY_HOST
),
3553 FE(HTTP_QUERY_IF_MATCH
),
3554 FE(HTTP_QUERY_IF_NONE_MATCH
),
3555 FE(HTTP_QUERY_IF_RANGE
),
3556 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE
),
3557 FE(HTTP_QUERY_MAX_FORWARDS
),
3558 FE(HTTP_QUERY_PROXY_AUTHORIZATION
),
3559 FE(HTTP_QUERY_RANGE
),
3560 FE(HTTP_QUERY_TRANSFER_ENCODING
),
3561 FE(HTTP_QUERY_UPGRADE
),
3562 FE(HTTP_QUERY_VARY
),
3564 FE(HTTP_QUERY_WARNING
),
3565 FE(HTTP_QUERY_CUSTOM
)
3567 static const wininet_flag_info modifier_flags
[] = {
3568 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS
),
3569 FE(HTTP_QUERY_FLAG_SYSTEMTIME
),
3570 FE(HTTP_QUERY_FLAG_NUMBER
),
3571 FE(HTTP_QUERY_FLAG_COALESCE
)
3574 DWORD info_mod
= dwInfoLevel
& HTTP_QUERY_MODIFIER_FLAGS_MASK
;
3575 DWORD info
= dwInfoLevel
& HTTP_QUERY_HEADER_MASK
;
3578 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest
, dwInfoLevel
, info
);
3579 TRACE(" Attribute:");
3580 for (i
= 0; i
< (sizeof(query_flags
) / sizeof(query_flags
[0])); i
++) {
3581 if (query_flags
[i
].val
== info
) {
3582 TRACE(" %s", query_flags
[i
].name
);
3586 if (i
== (sizeof(query_flags
) / sizeof(query_flags
[0]))) {
3587 TRACE(" Unknown (%08x)", info
);
3590 TRACE(" Modifier:");
3591 for (i
= 0; i
< (sizeof(modifier_flags
) / sizeof(modifier_flags
[0])); i
++) {
3592 if (modifier_flags
[i
].val
& info_mod
) {
3593 TRACE(" %s", modifier_flags
[i
].name
);
3594 info_mod
&= ~ modifier_flags
[i
].val
;
3599 TRACE(" Unknown (%08x)", info_mod
);
3604 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
3605 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
3607 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3611 if (lpBuffer
== NULL
)
3612 *lpdwBufferLength
= 0;
3613 res
= HTTP_HttpQueryInfoW( request
, dwInfoLevel
,
3614 lpBuffer
, lpdwBufferLength
, lpdwIndex
);
3618 WININET_Release( &request
->hdr
);
3620 TRACE("%u <--\n", res
);
3621 if(res
!= ERROR_SUCCESS
)
3623 return res
== ERROR_SUCCESS
;
3626 /***********************************************************************
3627 * HttpQueryInfoA (WININET.@)
3629 * Queries for information about an HTTP request
3636 BOOL WINAPI
HttpQueryInfoA(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3637 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3643 if((dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) ||
3644 (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
))
3646 return HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, lpBuffer
,
3647 lpdwBufferLength
, lpdwIndex
);
3653 len
= (*lpdwBufferLength
)*sizeof(WCHAR
);
3654 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3656 alloclen
= MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, NULL
, 0 ) * sizeof(WCHAR
);
3662 bufferW
= heap_alloc(alloclen
);
3663 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3664 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3665 MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, bufferW
, alloclen
/ sizeof(WCHAR
) );
3672 result
= HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, bufferW
,
3676 len
= WideCharToMultiByte( CP_ACP
,0, bufferW
, len
/ sizeof(WCHAR
) + 1,
3677 lpBuffer
, *lpdwBufferLength
, NULL
, NULL
);
3678 *lpdwBufferLength
= len
- 1;
3680 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer
));
3683 /* since the strings being returned from HttpQueryInfoW should be
3684 * only ASCII characters, it is reasonable to assume that all of
3685 * the Unicode characters can be reduced to a single byte */
3686 *lpdwBufferLength
= len
/ sizeof(WCHAR
);
3688 heap_free( bufferW
);
3692 /***********************************************************************
3693 * HTTP_GetRedirectURL (internal)
3695 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*request
, LPCWSTR lpszUrl
)
3697 static WCHAR szHttp
[] = {'h','t','t','p',0};
3698 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3699 http_session_t
*session
= request
->session
;
3700 URL_COMPONENTSW urlComponents
;
3701 DWORD url_length
= 0;
3703 LPWSTR combined_url
;
3705 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3706 urlComponents
.lpszScheme
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) ? szHttps
: szHttp
;
3707 urlComponents
.dwSchemeLength
= 0;
3708 urlComponents
.lpszHostName
= session
->hostName
;
3709 urlComponents
.dwHostNameLength
= 0;
3710 urlComponents
.nPort
= session
->hostPort
;
3711 urlComponents
.lpszUserName
= session
->userName
;
3712 urlComponents
.dwUserNameLength
= 0;
3713 urlComponents
.lpszPassword
= NULL
;
3714 urlComponents
.dwPasswordLength
= 0;
3715 urlComponents
.lpszUrlPath
= request
->path
;
3716 urlComponents
.dwUrlPathLength
= 0;
3717 urlComponents
.lpszExtraInfo
= NULL
;
3718 urlComponents
.dwExtraInfoLength
= 0;
3720 if (!InternetCreateUrlW(&urlComponents
, 0, NULL
, &url_length
) &&
3721 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3724 orig_url
= heap_alloc(url_length
);
3726 /* convert from bytes to characters */
3727 url_length
= url_length
/ sizeof(WCHAR
) - 1;
3728 if (!InternetCreateUrlW(&urlComponents
, 0, orig_url
, &url_length
))
3730 heap_free(orig_url
);
3735 if (!InternetCombineUrlW(orig_url
, lpszUrl
, NULL
, &url_length
, ICU_ENCODE_SPACES_ONLY
) &&
3736 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3738 heap_free(orig_url
);
3741 combined_url
= heap_alloc(url_length
* sizeof(WCHAR
));
3743 if (!InternetCombineUrlW(orig_url
, lpszUrl
, combined_url
, &url_length
, ICU_ENCODE_SPACES_ONLY
))
3745 heap_free(orig_url
);
3746 heap_free(combined_url
);
3749 heap_free(orig_url
);
3750 return combined_url
;
3754 /***********************************************************************
3755 * HTTP_HandleRedirect (internal)
3757 static DWORD
HTTP_HandleRedirect(http_request_t
*request
, LPCWSTR lpszUrl
)
3759 http_session_t
*session
= request
->session
;
3760 appinfo_t
*hIC
= session
->appInfo
;
3761 BOOL using_proxy
= hIC
->proxy
&& hIC
->proxy
[0];
3762 WCHAR path
[INTERNET_MAX_PATH_LENGTH
];
3767 /* if it's an absolute path, keep the same session info */
3768 lstrcpynW(path
, lpszUrl
, INTERNET_MAX_URL_LENGTH
);
3772 URL_COMPONENTSW urlComponents
;
3773 WCHAR protocol
[INTERNET_MAX_SCHEME_LENGTH
];
3774 WCHAR hostName
[INTERNET_MAX_HOST_NAME_LENGTH
];
3775 WCHAR userName
[INTERNET_MAX_USER_NAME_LENGTH
];
3776 static WCHAR szHttp
[] = {'h','t','t','p',0};
3777 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3783 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3784 urlComponents
.lpszScheme
= protocol
;
3785 urlComponents
.dwSchemeLength
= INTERNET_MAX_SCHEME_LENGTH
;
3786 urlComponents
.lpszHostName
= hostName
;
3787 urlComponents
.dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
3788 urlComponents
.lpszUserName
= userName
;
3789 urlComponents
.dwUserNameLength
= INTERNET_MAX_USER_NAME_LENGTH
;
3790 urlComponents
.lpszPassword
= NULL
;
3791 urlComponents
.dwPasswordLength
= 0;
3792 urlComponents
.lpszUrlPath
= path
;
3793 urlComponents
.dwUrlPathLength
= INTERNET_MAX_PATH_LENGTH
;
3794 urlComponents
.lpszExtraInfo
= NULL
;
3795 urlComponents
.dwExtraInfoLength
= 0;
3796 if(!InternetCrackUrlW(lpszUrl
, strlenW(lpszUrl
), 0, &urlComponents
))
3797 return INTERNET_GetLastError();
3799 if (!strncmpW(szHttp
, urlComponents
.lpszScheme
, strlenW(szHttp
)) &&
3800 (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3802 TRACE("redirect from secure page to non-secure page\n");
3803 /* FIXME: warn about from secure redirect to non-secure page */
3804 request
->hdr
.dwFlags
&= ~INTERNET_FLAG_SECURE
;
3806 if (!strncmpW(szHttps
, urlComponents
.lpszScheme
, strlenW(szHttps
)) &&
3807 !(request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3809 TRACE("redirect from non-secure page to secure page\n");
3810 /* FIXME: notify about redirect to secure page */
3811 request
->hdr
.dwFlags
|= INTERNET_FLAG_SECURE
;
3814 if (urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3816 if (lstrlenW(protocol
)>4) /*https*/
3817 urlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
3819 urlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
3824 * This upsets redirects to binary files on sourceforge.net
3825 * and gives an html page instead of the target file
3826 * Examination of the HTTP request sent by native wininet.dll
3827 * reveals that it doesn't send a referrer in that case.
3828 * Maybe there's a flag that enables this, or maybe a referrer
3829 * shouldn't be added in case of a redirect.
3832 /* consider the current host as the referrer */
3833 if (session
->lpszServerName
&& *session
->lpszServerName
)
3834 HTTP_ProcessHeader(request
, HTTP_REFERER
, session
->lpszServerName
,
3835 HTTP_ADDHDR_FLAG_REQ
|HTTP_ADDREQ_FLAG_REPLACE
|
3836 HTTP_ADDHDR_FLAG_ADD_IF_NEW
);
3839 heap_free(session
->hostName
);
3840 if (urlComponents
.nPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
3841 urlComponents
.nPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3844 static const WCHAR fmt
[] = {'%','s',':','%','u',0};
3845 len
= lstrlenW(hostName
);
3846 len
+= 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3847 session
->hostName
= heap_alloc(len
*sizeof(WCHAR
));
3848 sprintfW(session
->hostName
, fmt
, hostName
, urlComponents
.nPort
);
3851 session
->hostName
= heap_strdupW(hostName
);
3853 HTTP_ProcessHeader(request
, hostW
, session
->hostName
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDREQ_FLAG_REPLACE
| HTTP_ADDHDR_FLAG_REQ
);
3855 heap_free(session
->userName
);
3856 session
->userName
= NULL
;
3858 session
->userName
= heap_strdupW(userName
);
3860 reset_data_stream(request
);
3863 if(strcmpiW(session
->serverName
, hostName
)) {
3864 heap_free(session
->serverName
);
3865 session
->serverName
= heap_strdupW(hostName
);
3867 session
->serverPort
= urlComponents
.nPort
;
3870 heap_free(request
->path
);
3877 rc
= UrlEscapeW(path
, NULL
, &needed
, URL_ESCAPE_SPACES_ONLY
);
3878 if (rc
!= E_POINTER
)
3879 needed
= strlenW(path
)+1;
3880 request
->path
= heap_alloc(needed
*sizeof(WCHAR
));
3881 rc
= UrlEscapeW(path
, request
->path
, &needed
,
3882 URL_ESCAPE_SPACES_ONLY
);
3885 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path
),rc
);
3886 strcpyW(request
->path
,path
);
3890 /* Remove custom content-type/length headers on redirects. */
3891 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Type
, 0, TRUE
);
3893 HTTP_DeleteCustomHeader(request
, index
);
3894 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Length
, 0, TRUE
);
3896 HTTP_DeleteCustomHeader(request
, index
);
3898 return ERROR_SUCCESS
;
3901 /***********************************************************************
3902 * HTTP_build_req (internal)
3904 * concatenate all the strings in the request together
3906 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
)
3911 for( t
= list
; *t
; t
++ )
3912 len
+= strlenW( *t
);
3915 str
= heap_alloc(len
*sizeof(WCHAR
));
3918 for( t
= list
; *t
; t
++ )
3924 static DWORD
HTTP_SecureProxyConnect(http_request_t
*request
)
3927 LPWSTR requestString
;
3933 static const WCHAR szConnect
[] = {'C','O','N','N','E','C','T',0};
3934 static const WCHAR szFormat
[] = {'%','s',':','%','u',0};
3935 http_session_t
*session
= request
->session
;
3939 lpszPath
= heap_alloc((lstrlenW( session
->hostName
) + 13)*sizeof(WCHAR
));
3940 sprintfW( lpszPath
, szFormat
, session
->hostName
, session
->hostPort
);
3941 requestString
= HTTP_BuildHeaderRequestString( request
, szConnect
, lpszPath
, g_szHttp1_1
);
3942 heap_free( lpszPath
);
3944 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3945 NULL
, 0, NULL
, NULL
);
3946 len
--; /* the nul terminator isn't needed */
3947 ascii_req
= heap_alloc(len
);
3948 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1, ascii_req
, len
, NULL
, NULL
);
3949 heap_free( requestString
);
3951 TRACE("full request -> %s\n", debugstr_an( ascii_req
, len
) );
3953 res
= NETCON_send( request
->netconn
, ascii_req
, len
, 0, &cnt
);
3954 heap_free( ascii_req
);
3955 if (res
!= ERROR_SUCCESS
)
3958 responseLen
= HTTP_GetResponseHeaders( request
, TRUE
);
3960 return ERROR_HTTP_INVALID_HEADER
;
3962 return ERROR_SUCCESS
;
3965 static void HTTP_InsertCookies(http_request_t
*request
)
3967 DWORD cookie_size
, size
, cnt
= 0;
3971 static const WCHAR cookieW
[] = {'C','o','o','k','i','e',':',' ',0};
3973 host
= HTTP_GetHeader(request
, hostW
);
3977 if(!get_cookie(host
->lpszValue
, request
->path
, NULL
, &cookie_size
))
3980 size
= sizeof(cookieW
) + cookie_size
* sizeof(WCHAR
) + sizeof(szCrLf
);
3981 if(!(cookies
= heap_alloc(size
)))
3984 cnt
+= sprintfW(cookies
, cookieW
);
3985 get_cookie(host
->lpszValue
, request
->path
, cookies
+cnt
, &cookie_size
);
3986 strcatW(cookies
, szCrLf
);
3988 HTTP_HttpAddRequestHeadersW(request
, cookies
, strlenW(cookies
), HTTP_ADDREQ_FLAG_REPLACE
);
3993 static WORD
HTTP_ParseDay(LPCWSTR day
)
3995 static const WCHAR days
[7][4] = {{ 's','u','n',0 },
4003 for (i
= 0; i
< sizeof(days
)/sizeof(*days
); i
++)
4004 if (!strcmpiW(day
, days
[i
]))
4011 static WORD
HTTP_ParseMonth(LPCWSTR month
)
4013 static const WCHAR jan
[] = { 'j','a','n',0 };
4014 static const WCHAR feb
[] = { 'f','e','b',0 };
4015 static const WCHAR mar
[] = { 'm','a','r',0 };
4016 static const WCHAR apr
[] = { 'a','p','r',0 };
4017 static const WCHAR may
[] = { 'm','a','y',0 };
4018 static const WCHAR jun
[] = { 'j','u','n',0 };
4019 static const WCHAR jul
[] = { 'j','u','l',0 };
4020 static const WCHAR aug
[] = { 'a','u','g',0 };
4021 static const WCHAR sep
[] = { 's','e','p',0 };
4022 static const WCHAR oct
[] = { 'o','c','t',0 };
4023 static const WCHAR nov
[] = { 'n','o','v',0 };
4024 static const WCHAR dec
[] = { 'd','e','c',0 };
4026 if (!strcmpiW(month
, jan
)) return 1;
4027 if (!strcmpiW(month
, feb
)) return 2;
4028 if (!strcmpiW(month
, mar
)) return 3;
4029 if (!strcmpiW(month
, apr
)) return 4;
4030 if (!strcmpiW(month
, may
)) return 5;
4031 if (!strcmpiW(month
, jun
)) return 6;
4032 if (!strcmpiW(month
, jul
)) return 7;
4033 if (!strcmpiW(month
, aug
)) return 8;
4034 if (!strcmpiW(month
, sep
)) return 9;
4035 if (!strcmpiW(month
, oct
)) return 10;
4036 if (!strcmpiW(month
, nov
)) return 11;
4037 if (!strcmpiW(month
, dec
)) return 12;
4042 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4043 * optionally preceded by whitespace.
4044 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4045 * st, and sets *str to the first character after the time format.
4047 static BOOL
HTTP_ParseTime(SYSTEMTIME
*st
, LPCWSTR
*str
)
4053 while (isspaceW(*ptr
))
4056 num
= strtoulW(ptr
, &nextPtr
, 10);
4057 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
4059 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4064 ERR("unexpected hour in time format %s\n", debugstr_w(ptr
));
4068 st
->wHour
= (WORD
)num
;
4069 num
= strtoulW(ptr
, &nextPtr
, 10);
4070 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
4072 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4077 ERR("unexpected minute in time format %s\n", debugstr_w(ptr
));
4081 st
->wMinute
= (WORD
)num
;
4082 num
= strtoulW(ptr
, &nextPtr
, 10);
4083 if (!nextPtr
|| nextPtr
<= ptr
)
4085 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4090 ERR("unexpected second in time format %s\n", debugstr_w(ptr
));
4095 st
->wSecond
= (WORD
)num
;
4099 static BOOL
HTTP_ParseDateAsAsctime(LPCWSTR value
, FILETIME
*ft
)
4101 static const WCHAR gmt
[]= { 'G','M','T',0 };
4102 WCHAR day
[4], *dayPtr
, month
[4], *monthPtr
, *nextPtr
;
4104 SYSTEMTIME st
= { 0 };
4107 for (ptr
= value
, dayPtr
= day
; *ptr
&& !isspaceW(*ptr
) &&
4108 dayPtr
- day
< sizeof(day
) / sizeof(day
[0]) - 1; ptr
++, dayPtr
++)
4111 st
.wDayOfWeek
= HTTP_ParseDay(day
);
4112 if (st
.wDayOfWeek
>= 7)
4114 ERR("unexpected weekday %s\n", debugstr_w(day
));
4118 while (isspaceW(*ptr
))
4121 for (monthPtr
= month
; !isspace(*ptr
) &&
4122 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4126 st
.wMonth
= HTTP_ParseMonth(month
);
4127 if (!st
.wMonth
|| st
.wMonth
> 12)
4129 ERR("unexpected month %s\n", debugstr_w(month
));
4133 while (isspaceW(*ptr
))
4136 num
= strtoulW(ptr
, &nextPtr
, 10);
4137 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4139 ERR("unexpected day %s\n", debugstr_w(ptr
));
4143 st
.wDay
= (WORD
)num
;
4145 while (isspaceW(*ptr
))
4148 if (!HTTP_ParseTime(&st
, &ptr
))
4151 while (isspaceW(*ptr
))
4154 num
= strtoulW(ptr
, &nextPtr
, 10);
4155 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4157 ERR("unexpected year %s\n", debugstr_w(ptr
));
4161 st
.wYear
= (WORD
)num
;
4163 while (isspaceW(*ptr
))
4166 /* asctime() doesn't report a timezone, but some web servers do, so accept
4167 * with or without GMT.
4169 if (*ptr
&& strcmpW(ptr
, gmt
))
4171 ERR("unexpected timezone %s\n", debugstr_w(ptr
));
4174 return SystemTimeToFileTime(&st
, ft
);
4177 static BOOL
HTTP_ParseRfc1123Date(LPCWSTR value
, FILETIME
*ft
)
4179 static const WCHAR gmt
[]= { 'G','M','T',0 };
4180 WCHAR
*nextPtr
, day
[4], month
[4], *monthPtr
;
4183 SYSTEMTIME st
= { 0 };
4185 ptr
= strchrW(value
, ',');
4188 if (ptr
- value
!= 3)
4190 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4193 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4195 st
.wDayOfWeek
= HTTP_ParseDay(day
);
4196 if (st
.wDayOfWeek
> 6)
4198 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4203 while (isspaceW(*ptr
))
4206 num
= strtoulW(ptr
, &nextPtr
, 10);
4207 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4209 ERR("unexpected day %s\n", debugstr_w(value
));
4213 st
.wDay
= (WORD
)num
;
4215 while (isspaceW(*ptr
))
4218 for (monthPtr
= month
; !isspace(*ptr
) &&
4219 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4223 st
.wMonth
= HTTP_ParseMonth(month
);
4224 if (!st
.wMonth
|| st
.wMonth
> 12)
4226 ERR("unexpected month %s\n", debugstr_w(month
));
4230 while (isspaceW(*ptr
))
4233 num
= strtoulW(ptr
, &nextPtr
, 10);
4234 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4236 ERR("unexpected year %s\n", debugstr_w(value
));
4240 st
.wYear
= (WORD
)num
;
4242 if (!HTTP_ParseTime(&st
, &ptr
))
4245 while (isspaceW(*ptr
))
4248 if (strcmpW(ptr
, gmt
))
4250 ERR("unexpected time zone %s\n", debugstr_w(ptr
));
4253 return SystemTimeToFileTime(&st
, ft
);
4256 /* FIXME: only accepts dates in RFC 1123 format and asctime() format,
4257 * which may not be the only formats actually seen in the wild.
4258 * http://www.hackcraft.net/web/datetime/ suggests at least RFC 850 dates
4259 * should be accepted as well.
4261 static BOOL
HTTP_ParseDate(LPCWSTR value
, FILETIME
*ft
)
4263 static const WCHAR zero
[] = { '0',0 };
4266 if (!strcmpW(value
, zero
))
4268 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
4271 else if (strchrW(value
, ','))
4272 ret
= HTTP_ParseRfc1123Date(value
, ft
);
4275 ret
= HTTP_ParseDateAsAsctime(value
, ft
);
4277 ERR("unexpected date format %s\n", debugstr_w(value
));
4282 static void HTTP_ProcessExpires(http_request_t
*request
)
4284 BOOL expirationFound
= FALSE
;
4287 /* Look for a Cache-Control header with a max-age directive, as it takes
4288 * precedence over the Expires header.
4290 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szCache_Control
, 0, FALSE
);
4291 if (headerIndex
!= -1)
4293 LPHTTPHEADERW ccHeader
= &request
->custHeaders
[headerIndex
];
4296 for (ptr
= ccHeader
->lpszValue
; ptr
&& *ptr
; )
4298 LPWSTR comma
= strchrW(ptr
, ','), end
, equal
;
4303 end
= ptr
+ strlenW(ptr
);
4304 for (equal
= end
- 1; equal
> ptr
&& *equal
!= '='; equal
--)
4308 static const WCHAR max_age
[] = {
4309 'm','a','x','-','a','g','e',0 };
4311 if (!strncmpiW(ptr
, max_age
, equal
- ptr
- 1))
4316 age
= strtoulW(equal
+ 1, &nextPtr
, 10);
4317 if (nextPtr
> equal
+ 1)
4321 NtQuerySystemTime( &ft
);
4322 /* Age is in seconds, FILETIME resolution is in
4323 * 100 nanosecond intervals.
4325 ft
.QuadPart
+= age
* (ULONGLONG
)1000000;
4326 request
->expires
.dwLowDateTime
= ft
.u
.LowPart
;
4327 request
->expires
.dwHighDateTime
= ft
.u
.HighPart
;
4328 expirationFound
= TRUE
;
4335 while (isspaceW(*ptr
))
4342 if (!expirationFound
)
4344 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szExpires
, 0, FALSE
);
4345 if (headerIndex
!= -1)
4347 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4350 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4352 expirationFound
= TRUE
;
4353 request
->expires
= ft
;
4357 if (!expirationFound
)
4361 /* With no known age, default to 10 minutes until expiration. */
4362 NtQuerySystemTime( &t
);
4363 t
.QuadPart
+= 10 * 60 * (ULONGLONG
)10000000;
4364 request
->expires
.dwLowDateTime
= t
.u
.LowPart
;
4365 request
->expires
.dwHighDateTime
= t
.u
.HighPart
;
4369 static void HTTP_ProcessLastModified(http_request_t
*request
)
4373 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szLast_Modified
, 0, FALSE
);
4374 if (headerIndex
!= -1)
4376 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4379 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4380 request
->last_modified
= ft
;
4384 static void http_process_keep_alive(http_request_t
*req
)
4388 index
= HTTP_GetCustomHeaderIndex(req
, szConnection
, 0, FALSE
);
4390 req
->netconn
->keep_alive
= !strcmpiW(req
->custHeaders
[index
].lpszValue
, szKeepAlive
);
4392 req
->netconn
->keep_alive
= !strcmpiW(req
->version
, g_szHttp1_1
);
4395 static void HTTP_CacheRequest(http_request_t
*request
)
4397 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
4398 WCHAR cacheFileName
[MAX_PATH
+1];
4401 b
= HTTP_GetRequestURL(request
, url
);
4403 WARN("Could not get URL\n");
4407 b
= CreateUrlCacheEntryW(url
, request
->contentLength
, NULL
, cacheFileName
, 0);
4409 heap_free(request
->cacheFile
);
4410 CloseHandle(request
->hCacheFile
);
4412 request
->cacheFile
= heap_strdupW(cacheFileName
);
4413 request
->hCacheFile
= CreateFileW(request
->cacheFile
, GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
4414 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
4415 if(request
->hCacheFile
== INVALID_HANDLE_VALUE
) {
4416 WARN("Could not create file: %u\n", GetLastError());
4417 request
->hCacheFile
= NULL
;
4420 WARN("Could not create cache entry: %08x\n", GetLastError());
4424 static DWORD
open_http_connection(http_request_t
*request
, BOOL
*reusing
)
4426 const BOOL is_https
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) != 0;
4427 http_session_t
*session
= request
->session
;
4428 netconn_t
*netconn
= NULL
;
4432 assert(!request
->netconn
);
4433 reset_data_stream(request
);
4435 server
= get_server(session
->serverName
, session
->serverPort
);
4437 return ERROR_OUTOFMEMORY
;
4439 res
= HTTP_ResolveName(request
, server
);
4440 if(res
!= ERROR_SUCCESS
) {
4441 server_release(server
);
4445 EnterCriticalSection(&connection_pool_cs
);
4447 while(!list_empty(&server
->conn_pool
)) {
4448 netconn
= LIST_ENTRY(list_head(&server
->conn_pool
), netconn_t
, pool_entry
);
4449 list_remove(&netconn
->pool_entry
);
4451 if(NETCON_is_alive(netconn
))
4454 TRACE("connection %p closed during idle\n", netconn
);
4455 free_netconn(netconn
);
4459 LeaveCriticalSection(&connection_pool_cs
);
4462 TRACE("<-- reusing %p netconn\n", netconn
);
4463 request
->netconn
= netconn
;
4465 return ERROR_SUCCESS
;
4468 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4469 INTERNET_STATUS_CONNECTING_TO_SERVER
,
4471 strlen(server
->addr_str
)+1);
4473 res
= create_netconn(is_https
, server
, request
->security_flags
, &netconn
);
4474 server_release(server
);
4475 if(res
!= ERROR_SUCCESS
) {
4476 ERR("create_netconn failed: %u\n", res
);
4480 request
->netconn
= netconn
;
4482 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4483 INTERNET_STATUS_CONNECTED_TO_SERVER
,
4484 server
->addr_str
, strlen(server
->addr_str
)+1);
4487 /* Note: we differ from Microsoft's WinINet here. they seem to have
4488 * a bug that causes no status callbacks to be sent when starting
4489 * a tunnel to a proxy server using the CONNECT verb. i believe our
4490 * behaviour to be more correct and to not cause any incompatibilities
4491 * because using a secure connection through a proxy server is a rare
4492 * case that would be hard for anyone to depend on */
4493 if(session
->appInfo
->proxy
)
4494 res
= HTTP_SecureProxyConnect(request
);
4495 if(res
== ERROR_SUCCESS
)
4496 res
= NETCON_secure_connect(request
->netconn
, session
->hostName
);
4497 if(res
!= ERROR_SUCCESS
)
4499 WARN("Couldn't connect securely to host\n");
4501 if((request
->hdr
.ErrorMask
&INTERNET_ERROR_MASK_COMBINED_SEC_CERT
) && (
4502 res
== ERROR_INTERNET_SEC_CERT_DATE_INVALID
4503 || res
== ERROR_INTERNET_INVALID_CA
4504 || res
== ERROR_INTERNET_SEC_CERT_NO_REV
4505 || res
== ERROR_INTERNET_SEC_CERT_REV_FAILED
4506 || res
== ERROR_INTERNET_SEC_CERT_REVOKED
4507 || res
== ERROR_INTERNET_SEC_INVALID_CERT
4508 || res
== ERROR_INTERNET_SEC_CERT_CN_INVALID
))
4509 res
= ERROR_INTERNET_SEC_CERT_ERRORS
;
4513 if(res
!= ERROR_SUCCESS
) {
4514 http_release_netconn(request
, FALSE
);
4519 TRACE("Created connection to %s: %p\n", debugstr_w(server
->name
), netconn
);
4520 return ERROR_SUCCESS
;
4523 /***********************************************************************
4524 * HTTP_HttpSendRequestW (internal)
4526 * Sends the specified request to the HTTP server
4529 * ERROR_SUCCESS on success
4530 * win32 error code on failure
4533 static DWORD
HTTP_HttpSendRequestW(http_request_t
*request
, LPCWSTR lpszHeaders
,
4534 DWORD dwHeaderLength
, LPVOID lpOptional
, DWORD dwOptionalLength
,
4535 DWORD dwContentLength
, BOOL bEndRequest
)
4538 BOOL redirected
= FALSE
;
4539 LPWSTR requestString
= NULL
;
4542 static const WCHAR szPost
[] = { 'P','O','S','T',0 };
4543 static const WCHAR szContentLength
[] =
4544 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4545 WCHAR contentLengthStr
[sizeof szContentLength
/2 /* includes \r\n */ + 20 /* int */ ];
4548 TRACE("--> %p\n", request
);
4550 assert(request
->hdr
.htype
== WH_HHTTPREQ
);
4552 /* if the verb is NULL default to GET */
4554 request
->verb
= heap_strdupW(szGET
);
4556 if (dwContentLength
|| strcmpW(request
->verb
, szGET
))
4558 sprintfW(contentLengthStr
, szContentLength
, dwContentLength
);
4559 HTTP_HttpAddRequestHeadersW(request
, contentLengthStr
, -1L, HTTP_ADDREQ_FLAG_REPLACE
);
4560 request
->bytesToWrite
= dwContentLength
;
4562 if (request
->session
->appInfo
->agent
)
4564 WCHAR
*agent_header
;
4565 static const WCHAR user_agent
[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4568 len
= strlenW(request
->session
->appInfo
->agent
) + strlenW(user_agent
);
4569 agent_header
= heap_alloc(len
* sizeof(WCHAR
));
4570 sprintfW(agent_header
, user_agent
, request
->session
->appInfo
->agent
);
4572 HTTP_HttpAddRequestHeadersW(request
, agent_header
, strlenW(agent_header
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4573 heap_free(agent_header
);
4575 if (request
->hdr
.dwFlags
& INTERNET_FLAG_PRAGMA_NOCACHE
)
4577 static const WCHAR pragma_nocache
[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4578 HTTP_HttpAddRequestHeadersW(request
, pragma_nocache
, strlenW(pragma_nocache
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4580 if ((request
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
) && !strcmpW(request
->verb
, szPost
))
4582 static const WCHAR cache_control
[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4583 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4584 HTTP_HttpAddRequestHeadersW(request
, cache_control
, strlenW(cache_control
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4590 BOOL reusing_connection
;
4595 /* like native, just in case the caller forgot to call InternetReadFile
4596 * for all the data */
4597 drain_content(request
);
4599 request
->contentLength
= ~0u;
4600 request
->bytesToWrite
= 0;
4603 if (TRACE_ON(wininet
))
4605 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4606 TRACE("Going to url %s %s\n", debugstr_w(Host
->lpszValue
), debugstr_w(request
->path
));
4609 HTTP_FixURL(request
);
4610 if (request
->hdr
.dwFlags
& INTERNET_FLAG_KEEP_CONNECTION
)
4612 HTTP_ProcessHeader(request
, szConnection
, szKeepAlive
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
4614 HTTP_InsertAuthorization(request
, request
->authInfo
, szAuthorization
);
4615 HTTP_InsertAuthorization(request
, request
->proxyAuthInfo
, szProxy_Authorization
);
4617 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
))
4618 HTTP_InsertCookies(request
);
4620 /* add the headers the caller supplied */
4621 if( lpszHeaders
&& dwHeaderLength
)
4623 HTTP_HttpAddRequestHeadersW(request
, lpszHeaders
, dwHeaderLength
,
4624 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REPLACE
);
4627 if (request
->session
->appInfo
->proxy
&& request
->session
->appInfo
->proxy
[0])
4629 WCHAR
*url
= HTTP_BuildProxyRequestUrl(request
);
4630 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, url
, request
->version
);
4634 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
4637 TRACE("Request header -> %s\n", debugstr_w(requestString
) );
4639 if ((res
= open_http_connection(request
, &reusing_connection
)) != ERROR_SUCCESS
)
4642 /* send the request as ASCII, tack on the optional data */
4643 if (!lpOptional
|| redirected
)
4644 dwOptionalLength
= 0;
4645 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4646 NULL
, 0, NULL
, NULL
);
4647 ascii_req
= heap_alloc(len
+ dwOptionalLength
);
4648 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4649 ascii_req
, len
, NULL
, NULL
);
4651 memcpy( &ascii_req
[len
-1], lpOptional
, dwOptionalLength
);
4652 len
= (len
+ dwOptionalLength
- 1);
4654 TRACE("full request -> %s\n", debugstr_a(ascii_req
) );
4656 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4657 INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
4659 res
= NETCON_send(request
->netconn
, ascii_req
, len
, 0, &cnt
);
4660 heap_free( ascii_req
);
4661 if(res
!= ERROR_SUCCESS
) {
4662 TRACE("send failed: %u\n", res
);
4663 if(!reusing_connection
)
4665 http_release_netconn(request
, FALSE
);
4670 request
->bytesWritten
= dwOptionalLength
;
4672 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4673 INTERNET_STATUS_REQUEST_SENT
,
4674 &len
, sizeof(DWORD
));
4681 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4682 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
4684 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
4685 /* FIXME: We should know that connection is closed before sending
4686 * headers. Otherwise wrong callbacks are executed */
4687 if(!responseLen
&& reusing_connection
) {
4688 TRACE("Connection closed by server, reconnecting\n");
4689 http_release_netconn(request
, FALSE
);
4694 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4695 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
,
4698 http_process_keep_alive(request
);
4699 HTTP_ProcessCookies(request
);
4700 HTTP_ProcessExpires(request
);
4701 HTTP_ProcessLastModified(request
);
4703 dwBufferSize
= sizeof(dwStatusCode
);
4704 if (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
,
4705 &dwStatusCode
,&dwBufferSize
,NULL
) != ERROR_SUCCESS
)
4708 res
= set_content_length(request
, dwStatusCode
);
4709 if(res
!= ERROR_SUCCESS
)
4711 if(!request
->contentLength
)
4712 http_release_netconn(request
, TRUE
);
4714 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
) && responseLen
)
4716 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
4717 dwBufferSize
=sizeof(szNewLocation
);
4718 if ((dwStatusCode
== HTTP_STATUS_REDIRECT
||
4719 dwStatusCode
== HTTP_STATUS_MOVED
||
4720 dwStatusCode
== HTTP_STATUS_REDIRECT_KEEP_VERB
||
4721 dwStatusCode
== HTTP_STATUS_REDIRECT_METHOD
) &&
4722 HTTP_HttpQueryInfoW(request
,HTTP_QUERY_LOCATION
,szNewLocation
,&dwBufferSize
,NULL
) == ERROR_SUCCESS
)
4724 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
) &&
4725 dwStatusCode
!= HTTP_STATUS_REDIRECT_KEEP_VERB
)
4727 heap_free(request
->verb
);
4728 request
->verb
= heap_strdupW(szGET
);
4730 drain_content(request
);
4731 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
4733 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
4734 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
4735 res
= HTTP_HandleRedirect(request
, new_url
);
4736 if (res
== ERROR_SUCCESS
)
4738 heap_free(requestString
);
4741 heap_free( new_url
);
4746 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTH
) && res
== ERROR_SUCCESS
)
4748 WCHAR szAuthValue
[2048];
4750 if (dwStatusCode
== HTTP_STATUS_DENIED
)
4752 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4754 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_WWW_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4756 if (HTTP_DoAuthorization(request
, szAuthValue
,
4758 request
->session
->userName
,
4759 request
->session
->password
,
4762 heap_free(requestString
);
4769 TRACE("Cleaning wrong authorization data\n");
4770 destroy_authinfo(request
->authInfo
);
4771 request
->authInfo
= NULL
;
4774 if (dwStatusCode
== HTTP_STATUS_PROXY_AUTH_REQ
)
4777 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_PROXY_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4779 if (HTTP_DoAuthorization(request
, szAuthValue
,
4780 &request
->proxyAuthInfo
,
4781 request
->session
->appInfo
->proxyUsername
,
4782 request
->session
->appInfo
->proxyPassword
,
4791 TRACE("Cleaning wrong proxy authorization data\n");
4792 destroy_authinfo(request
->proxyAuthInfo
);
4793 request
->proxyAuthInfo
= NULL
;
4799 res
= ERROR_SUCCESS
;
4803 if(res
== ERROR_SUCCESS
)
4804 HTTP_CacheRequest(request
);
4807 heap_free(requestString
);
4809 /* TODO: send notification for P3P header */
4811 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4813 if (res
== ERROR_SUCCESS
) {
4814 if(request
->contentLength
&& request
->bytesWritten
== request
->bytesToWrite
)
4815 HTTP_ReceiveRequestData(request
, TRUE
);
4817 send_request_complete(request
,
4818 request
->session
->hdr
.dwInternalFlags
& INET_OPENURL
? (DWORD_PTR
)request
->hdr
.hInternet
: 1, 0);
4820 send_request_complete(request
, 0, res
);
4828 /***********************************************************************
4830 * Helper functions for the HttpSendRequest(Ex) functions
4833 static void AsyncHttpSendRequestProc(WORKREQUEST
*workRequest
)
4835 struct WORKREQ_HTTPSENDREQUESTW
const *req
= &workRequest
->u
.HttpSendRequestW
;
4836 http_request_t
*request
= (http_request_t
*) workRequest
->hdr
;
4838 TRACE("%p\n", request
);
4840 HTTP_HttpSendRequestW(request
, req
->lpszHeader
,
4841 req
->dwHeaderLength
, req
->lpOptional
, req
->dwOptionalLength
,
4842 req
->dwContentLength
, req
->bEndRequest
);
4844 heap_free(req
->lpszHeader
);
4848 static DWORD
HTTP_HttpEndRequestW(http_request_t
*request
, DWORD dwFlags
, DWORD_PTR dwContext
)
4851 DWORD dwCode
, dwCodeLength
;
4853 DWORD res
= ERROR_SUCCESS
;
4855 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4856 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
4858 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
4860 res
= ERROR_HTTP_HEADER_NOT_FOUND
;
4862 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4863 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
, sizeof(DWORD
));
4865 /* process cookies here. Is this right? */
4866 http_process_keep_alive(request
);
4867 HTTP_ProcessCookies(request
);
4868 HTTP_ProcessExpires(request
);
4869 HTTP_ProcessLastModified(request
);
4871 dwCodeLength
= sizeof(dwCode
);
4872 if (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
,
4873 &dwCode
,&dwCodeLength
,NULL
) != ERROR_SUCCESS
)
4876 if ((res
= set_content_length( request
, dwCode
)) == ERROR_SUCCESS
) {
4877 if(!request
->contentLength
)
4878 http_release_netconn(request
, TRUE
);
4881 if (res
== ERROR_SUCCESS
&& !(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
))
4883 if (dwCode
== HTTP_STATUS_REDIRECT
||
4884 dwCode
== HTTP_STATUS_MOVED
||
4885 dwCode
== HTTP_STATUS_REDIRECT_METHOD
||
4886 dwCode
== HTTP_STATUS_REDIRECT_KEEP_VERB
)
4888 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
4889 dwBufferSize
=sizeof(szNewLocation
);
4890 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_LOCATION
, szNewLocation
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
4892 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
) &&
4893 dwCode
!= HTTP_STATUS_REDIRECT_KEEP_VERB
)
4895 heap_free(request
->verb
);
4896 request
->verb
= heap_strdupW(szGET
);
4898 drain_content(request
);
4899 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
4901 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
4902 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
4903 res
= HTTP_HandleRedirect(request
, new_url
);
4904 if (res
== ERROR_SUCCESS
)
4905 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, TRUE
);
4906 heap_free( new_url
);
4912 if (res
== ERROR_SUCCESS
&& request
->contentLength
)
4913 HTTP_ReceiveRequestData(request
, TRUE
);
4915 send_request_complete(request
, res
== ERROR_SUCCESS
, res
);
4920 /***********************************************************************
4921 * HttpEndRequestA (WININET.@)
4923 * Ends an HTTP request that was started by HttpSendRequestEx
4926 * TRUE if successful
4930 BOOL WINAPI
HttpEndRequestA(HINTERNET hRequest
,
4931 LPINTERNET_BUFFERSA lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
4933 TRACE("(%p, %p, %08x, %08lx)\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
4937 SetLastError(ERROR_INVALID_PARAMETER
);
4941 return HttpEndRequestW(hRequest
, NULL
, dwFlags
, dwContext
);
4944 static void AsyncHttpEndRequestProc(WORKREQUEST
*work
)
4946 struct WORKREQ_HTTPENDREQUESTW
const *req
= &work
->u
.HttpEndRequestW
;
4947 http_request_t
*request
= (http_request_t
*)work
->hdr
;
4949 TRACE("%p\n", request
);
4951 HTTP_HttpEndRequestW(request
, req
->dwFlags
, req
->dwContext
);
4954 /***********************************************************************
4955 * HttpEndRequestW (WININET.@)
4957 * Ends an HTTP request that was started by HttpSendRequestEx
4960 * TRUE if successful
4964 BOOL WINAPI
HttpEndRequestW(HINTERNET hRequest
,
4965 LPINTERNET_BUFFERSW lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
4967 http_request_t
*request
;
4970 TRACE("%p %p %x %lx -->\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
4974 SetLastError(ERROR_INVALID_PARAMETER
);
4978 request
= (http_request_t
*) get_handle_object( hRequest
);
4980 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
4982 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
4984 WININET_Release( &request
->hdr
);
4987 request
->hdr
.dwFlags
|= dwFlags
;
4989 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4992 struct WORKREQ_HTTPENDREQUESTW
*work_endrequest
;
4994 work
.asyncproc
= AsyncHttpEndRequestProc
;
4995 work
.hdr
= WININET_AddRef( &request
->hdr
);
4997 work_endrequest
= &work
.u
.HttpEndRequestW
;
4998 work_endrequest
->dwFlags
= dwFlags
;
4999 work_endrequest
->dwContext
= dwContext
;
5001 INTERNET_AsyncCall(&work
);
5002 res
= ERROR_IO_PENDING
;
5005 res
= HTTP_HttpEndRequestW(request
, dwFlags
, dwContext
);
5007 WININET_Release( &request
->hdr
);
5008 TRACE("%u <--\n", res
);
5009 if(res
!= ERROR_SUCCESS
)
5011 return res
== ERROR_SUCCESS
;
5014 /***********************************************************************
5015 * HttpSendRequestExA (WININET.@)
5017 * Sends the specified request to the HTTP server and allows chunked
5022 * Failure: FALSE, call GetLastError() for more information.
5024 BOOL WINAPI
HttpSendRequestExA(HINTERNET hRequest
,
5025 LPINTERNET_BUFFERSA lpBuffersIn
,
5026 LPINTERNET_BUFFERSA lpBuffersOut
,
5027 DWORD dwFlags
, DWORD_PTR dwContext
)
5029 INTERNET_BUFFERSW BuffersInW
;
5032 LPWSTR header
= NULL
;
5034 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
5035 lpBuffersOut
, dwFlags
, dwContext
);
5039 BuffersInW
.dwStructSize
= sizeof(LPINTERNET_BUFFERSW
);
5040 if (lpBuffersIn
->lpcszHeader
)
5042 headerlen
= MultiByteToWideChar(CP_ACP
,0,lpBuffersIn
->lpcszHeader
,
5043 lpBuffersIn
->dwHeadersLength
,0,0);
5044 header
= heap_alloc(headerlen
*sizeof(WCHAR
));
5045 if (!(BuffersInW
.lpcszHeader
= header
))
5047 SetLastError(ERROR_OUTOFMEMORY
);
5050 BuffersInW
.dwHeadersLength
= MultiByteToWideChar(CP_ACP
, 0,
5051 lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
5055 BuffersInW
.lpcszHeader
= NULL
;
5056 BuffersInW
.dwHeadersTotal
= lpBuffersIn
->dwHeadersTotal
;
5057 BuffersInW
.lpvBuffer
= lpBuffersIn
->lpvBuffer
;
5058 BuffersInW
.dwBufferLength
= lpBuffersIn
->dwBufferLength
;
5059 BuffersInW
.dwBufferTotal
= lpBuffersIn
->dwBufferTotal
;
5060 BuffersInW
.Next
= NULL
;
5063 rc
= HttpSendRequestExW(hRequest
, lpBuffersIn
? &BuffersInW
: NULL
, NULL
, dwFlags
, dwContext
);
5069 /***********************************************************************
5070 * HttpSendRequestExW (WININET.@)
5072 * Sends the specified request to the HTTP server and allows chunked
5077 * Failure: FALSE, call GetLastError() for more information.
5079 BOOL WINAPI
HttpSendRequestExW(HINTERNET hRequest
,
5080 LPINTERNET_BUFFERSW lpBuffersIn
,
5081 LPINTERNET_BUFFERSW lpBuffersOut
,
5082 DWORD dwFlags
, DWORD_PTR dwContext
)
5084 http_request_t
*request
;
5085 http_session_t
*session
;
5089 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
5090 lpBuffersOut
, dwFlags
, dwContext
);
5092 request
= (http_request_t
*) get_handle_object( hRequest
);
5094 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5096 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5100 session
= request
->session
;
5101 assert(session
->hdr
.htype
== WH_HHTTPSESSION
);
5102 hIC
= session
->appInfo
;
5103 assert(hIC
->hdr
.htype
== WH_HINIT
);
5105 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5107 WORKREQUEST workRequest
;
5108 struct WORKREQ_HTTPSENDREQUESTW
*req
;
5110 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
5111 workRequest
.hdr
= WININET_AddRef( &request
->hdr
);
5112 req
= &workRequest
.u
.HttpSendRequestW
;
5117 if (lpBuffersIn
->lpcszHeader
)
5119 if (lpBuffersIn
->dwHeadersLength
== ~0u)
5120 size
= (strlenW( lpBuffersIn
->lpcszHeader
) + 1) * sizeof(WCHAR
);
5122 size
= lpBuffersIn
->dwHeadersLength
* sizeof(WCHAR
);
5124 req
->lpszHeader
= heap_alloc(size
);
5125 memcpy( req
->lpszHeader
, lpBuffersIn
->lpcszHeader
, size
);
5127 else req
->lpszHeader
= NULL
;
5129 req
->dwHeaderLength
= size
/ sizeof(WCHAR
);
5130 req
->lpOptional
= lpBuffersIn
->lpvBuffer
;
5131 req
->dwOptionalLength
= lpBuffersIn
->dwBufferLength
;
5132 req
->dwContentLength
= lpBuffersIn
->dwBufferTotal
;
5136 req
->lpszHeader
= NULL
;
5137 req
->dwHeaderLength
= 0;
5138 req
->lpOptional
= NULL
;
5139 req
->dwOptionalLength
= 0;
5140 req
->dwContentLength
= 0;
5143 req
->bEndRequest
= FALSE
;
5145 INTERNET_AsyncCall(&workRequest
);
5147 * This is from windows.
5149 res
= ERROR_IO_PENDING
;
5154 res
= HTTP_HttpSendRequestW(request
, lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
5155 lpBuffersIn
->lpvBuffer
, lpBuffersIn
->dwBufferLength
,
5156 lpBuffersIn
->dwBufferTotal
, FALSE
);
5158 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, FALSE
);
5163 WININET_Release( &request
->hdr
);
5167 return res
== ERROR_SUCCESS
;
5170 /***********************************************************************
5171 * HttpSendRequestW (WININET.@)
5173 * Sends the specified request to the HTTP server
5180 BOOL WINAPI
HttpSendRequestW(HINTERNET hHttpRequest
, LPCWSTR lpszHeaders
,
5181 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
5183 http_request_t
*request
;
5184 http_session_t
*session
= NULL
;
5185 appinfo_t
*hIC
= NULL
;
5186 DWORD res
= ERROR_SUCCESS
;
5188 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest
,
5189 debugstr_wn(lpszHeaders
, dwHeaderLength
), dwHeaderLength
, lpOptional
, dwOptionalLength
);
5191 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
5192 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5194 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5198 session
= request
->session
;
5199 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
5201 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5205 hIC
= session
->appInfo
;
5206 if (NULL
== hIC
|| hIC
->hdr
.htype
!= WH_HINIT
)
5208 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5212 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5214 WORKREQUEST workRequest
;
5215 struct WORKREQ_HTTPSENDREQUESTW
*req
;
5217 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
5218 workRequest
.hdr
= WININET_AddRef( &request
->hdr
);
5219 req
= &workRequest
.u
.HttpSendRequestW
;
5224 if (dwHeaderLength
== ~0u) size
= (strlenW(lpszHeaders
) + 1) * sizeof(WCHAR
);
5225 else size
= dwHeaderLength
* sizeof(WCHAR
);
5227 req
->lpszHeader
= heap_alloc(size
);
5228 memcpy(req
->lpszHeader
, lpszHeaders
, size
);
5231 req
->lpszHeader
= 0;
5232 req
->dwHeaderLength
= dwHeaderLength
;
5233 req
->lpOptional
= lpOptional
;
5234 req
->dwOptionalLength
= dwOptionalLength
;
5235 req
->dwContentLength
= dwOptionalLength
;
5236 req
->bEndRequest
= TRUE
;
5238 INTERNET_AsyncCall(&workRequest
);
5240 * This is from windows.
5242 res
= ERROR_IO_PENDING
;
5246 res
= HTTP_HttpSendRequestW(request
, lpszHeaders
,
5247 dwHeaderLength
, lpOptional
, dwOptionalLength
,
5248 dwOptionalLength
, TRUE
);
5252 WININET_Release( &request
->hdr
);
5255 return res
== ERROR_SUCCESS
;
5258 /***********************************************************************
5259 * HttpSendRequestA (WININET.@)
5261 * Sends the specified request to the HTTP server
5268 BOOL WINAPI
HttpSendRequestA(HINTERNET hHttpRequest
, LPCSTR lpszHeaders
,
5269 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
5272 LPWSTR szHeaders
=NULL
;
5273 DWORD nLen
=dwHeaderLength
;
5274 if(lpszHeaders
!=NULL
)
5276 nLen
=MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,NULL
,0);
5277 szHeaders
= heap_alloc(nLen
*sizeof(WCHAR
));
5278 MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,szHeaders
,nLen
);
5280 result
= HttpSendRequestW(hHttpRequest
, szHeaders
, nLen
, lpOptional
, dwOptionalLength
);
5281 heap_free(szHeaders
);
5285 /***********************************************************************
5286 * HTTPSESSION_Destroy (internal)
5288 * Deallocate session handle
5291 static void HTTPSESSION_Destroy(object_header_t
*hdr
)
5293 http_session_t
*session
= (http_session_t
*) hdr
;
5295 TRACE("%p\n", session
);
5297 WININET_Release(&session
->appInfo
->hdr
);
5299 heap_free(session
->hostName
);
5300 heap_free(session
->serverName
);
5301 heap_free(session
->password
);
5302 heap_free(session
->userName
);
5305 static DWORD
HTTPSESSION_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
5308 case INTERNET_OPTION_HANDLE_TYPE
:
5309 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5311 if (*size
< sizeof(ULONG
))
5312 return ERROR_INSUFFICIENT_BUFFER
;
5314 *size
= sizeof(DWORD
);
5315 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_CONNECT_HTTP
;
5316 return ERROR_SUCCESS
;
5319 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
5322 static DWORD
HTTPSESSION_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
5324 http_session_t
*ses
= (http_session_t
*)hdr
;
5327 case INTERNET_OPTION_USERNAME
:
5329 heap_free(ses
->userName
);
5330 if (!(ses
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5331 return ERROR_SUCCESS
;
5333 case INTERNET_OPTION_PASSWORD
:
5335 heap_free(ses
->password
);
5336 if (!(ses
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5337 return ERROR_SUCCESS
;
5342 return ERROR_INTERNET_INVALID_OPTION
;
5345 static const object_vtbl_t HTTPSESSIONVtbl
= {
5346 HTTPSESSION_Destroy
,
5348 HTTPSESSION_QueryOption
,
5349 HTTPSESSION_SetOption
,
5358 /***********************************************************************
5359 * HTTP_Connect (internal)
5361 * Create http session handle
5364 * HINTERNET a session handle on success
5368 DWORD
HTTP_Connect(appinfo_t
*hIC
, LPCWSTR lpszServerName
,
5369 INTERNET_PORT serverPort
, LPCWSTR lpszUserName
,
5370 LPCWSTR lpszPassword
, DWORD dwFlags
, DWORD_PTR dwContext
,
5371 DWORD dwInternalFlags
, HINTERNET
*ret
)
5373 http_session_t
*session
= NULL
;
5377 if (!lpszServerName
|| !lpszServerName
[0])
5378 return ERROR_INVALID_PARAMETER
;
5380 assert( hIC
->hdr
.htype
== WH_HINIT
);
5382 session
= alloc_object(&hIC
->hdr
, &HTTPSESSIONVtbl
, sizeof(http_session_t
));
5384 return ERROR_OUTOFMEMORY
;
5387 * According to my tests. The name is not resolved until a request is sent
5390 session
->hdr
.htype
= WH_HHTTPSESSION
;
5391 session
->hdr
.dwFlags
= dwFlags
;
5392 session
->hdr
.dwContext
= dwContext
;
5393 session
->hdr
.dwInternalFlags
|= dwInternalFlags
;
5395 WININET_AddRef( &hIC
->hdr
);
5396 session
->appInfo
= hIC
;
5397 list_add_head( &hIC
->hdr
.children
, &session
->hdr
.entry
);
5399 if(hIC
->proxy
&& hIC
->accessType
== INTERNET_OPEN_TYPE_PROXY
) {
5400 if(hIC
->proxyBypass
)
5401 FIXME("Proxy bypass is ignored.\n");
5403 session
->serverName
= heap_strdupW(lpszServerName
);
5404 session
->hostName
= heap_strdupW(lpszServerName
);
5405 if (lpszUserName
&& lpszUserName
[0])
5406 session
->userName
= heap_strdupW(lpszUserName
);
5407 if (lpszPassword
&& lpszPassword
[0])
5408 session
->password
= heap_strdupW(lpszPassword
);
5409 session
->serverPort
= serverPort
;
5410 session
->hostPort
= serverPort
;
5412 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5413 if (!(session
->hdr
.dwInternalFlags
& INET_OPENURL
))
5415 INTERNET_SendCallback(&hIC
->hdr
, dwContext
,
5416 INTERNET_STATUS_HANDLE_CREATED
, &session
->hdr
.hInternet
,
5421 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5425 TRACE("%p --> %p\n", hIC
, session
);
5427 *ret
= session
->hdr
.hInternet
;
5428 return ERROR_SUCCESS
;
5431 /***********************************************************************
5432 * HTTP_clear_response_headers (internal)
5434 * clear out any old response headers
5436 static void HTTP_clear_response_headers( http_request_t
*request
)
5440 for( i
=0; i
<request
->nCustHeaders
; i
++)
5442 if( !request
->custHeaders
[i
].lpszField
)
5444 if( !request
->custHeaders
[i
].lpszValue
)
5446 if ( request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
5448 HTTP_DeleteCustomHeader( request
, i
);
5453 /***********************************************************************
5454 * HTTP_GetResponseHeaders (internal)
5456 * Read server response
5463 static INT
HTTP_GetResponseHeaders(http_request_t
*request
, BOOL clear
)
5466 WCHAR buffer
[MAX_REPLY_LEN
];
5467 DWORD buflen
= MAX_REPLY_LEN
;
5468 BOOL bSuccess
= FALSE
;
5470 char bufferA
[MAX_REPLY_LEN
];
5471 LPWSTR status_code
= NULL
, status_text
= NULL
;
5472 DWORD cchMaxRawHeaders
= 1024;
5473 LPWSTR lpszRawHeaders
= NULL
;
5475 DWORD cchRawHeaders
= 0;
5476 BOOL codeHundred
= FALSE
;
5480 if(!request
->netconn
)
5484 static const WCHAR szHundred
[] = {'1','0','0',0};
5486 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5488 buflen
= MAX_REPLY_LEN
;
5489 if (!read_line(request
, bufferA
, &buflen
))
5492 /* clear old response headers (eg. from a redirect response) */
5494 HTTP_clear_response_headers( request
);
5499 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5500 /* check is this a status code line? */
5501 if (!strncmpW(buffer
, g_szHttp1_0
, 4))
5503 /* split the version from the status code */
5504 status_code
= strchrW( buffer
, ' ' );
5509 /* split the status code from the status text */
5510 status_text
= strchrW( status_code
, ' ' );
5515 TRACE("version [%s] status code [%s] status text [%s]\n",
5516 debugstr_w(buffer
), debugstr_w(status_code
), debugstr_w(status_text
) );
5518 codeHundred
= (!strcmpW(status_code
, szHundred
));
5520 else if (!codeHundred
)
5522 WARN("No status line at head of response (%s)\n", debugstr_w(buffer
));
5524 heap_free(request
->version
);
5525 heap_free(request
->statusText
);
5527 request
->version
= heap_strdupW(g_szHttp1_0
);
5528 request
->statusText
= heap_strdupW(szOK
);
5530 heap_free(request
->rawHeaders
);
5531 request
->rawHeaders
= heap_strdupW(szDefaultHeader
);
5536 } while (codeHundred
);
5538 /* Add status code */
5539 HTTP_ProcessHeader(request
, szStatus
, status_code
,
5540 HTTP_ADDHDR_FLAG_REPLACE
);
5542 heap_free(request
->version
);
5543 heap_free(request
->statusText
);
5545 request
->version
= heap_strdupW(buffer
);
5546 request
->statusText
= heap_strdupW(status_text
);
5548 /* Restore the spaces */
5549 *(status_code
-1) = ' ';
5550 *(status_text
-1) = ' ';
5552 /* regenerate raw headers */
5553 lpszRawHeaders
= heap_alloc((cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5554 if (!lpszRawHeaders
) goto lend
;
5556 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5557 cchMaxRawHeaders
*= 2;
5558 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5559 if (temp
== NULL
) goto lend
;
5560 lpszRawHeaders
= temp
;
5561 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5562 cchRawHeaders
+= (buflen
-1);
5563 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5564 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5565 lpszRawHeaders
[cchRawHeaders
] = '\0';
5567 /* Parse each response line */
5570 buflen
= MAX_REPLY_LEN
;
5571 if (read_line(request
, bufferA
, &buflen
))
5573 LPWSTR
* pFieldAndValue
;
5575 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA
));
5577 if (!bufferA
[0]) break;
5578 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5580 pFieldAndValue
= HTTP_InterpretHttpHeader(buffer
);
5583 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5584 cchMaxRawHeaders
*= 2;
5585 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5586 if (temp
== NULL
) goto lend
;
5587 lpszRawHeaders
= temp
;
5588 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5589 cchRawHeaders
+= (buflen
-1);
5590 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5591 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5592 lpszRawHeaders
[cchRawHeaders
] = '\0';
5594 HTTP_ProcessHeader(request
, pFieldAndValue
[0], pFieldAndValue
[1],
5595 HTTP_ADDREQ_FLAG_ADD
);
5597 HTTP_FreeTokens(pFieldAndValue
);
5608 /* make sure the response header is terminated with an empty line. Some apps really
5609 truly care about that empty line being there for some reason. Just add it to the
5611 if (cchRawHeaders
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5613 cchMaxRawHeaders
= cchRawHeaders
+ strlenW(szCrLf
);
5614 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5615 if (temp
== NULL
) goto lend
;
5616 lpszRawHeaders
= temp
;
5619 memcpy(&lpszRawHeaders
[cchRawHeaders
], szCrLf
, sizeof(szCrLf
));
5621 heap_free(request
->rawHeaders
);
5622 request
->rawHeaders
= lpszRawHeaders
;
5623 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders
));
5633 heap_free(lpszRawHeaders
);
5638 /***********************************************************************
5639 * HTTP_InterpretHttpHeader (internal)
5641 * Parse server response
5645 * Pointer to array of field, value, NULL on success.
5648 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
)
5650 LPWSTR
* pTokenPair
;
5654 pTokenPair
= heap_alloc_zero(sizeof(*pTokenPair
)*3);
5656 pszColon
= strchrW(buffer
, ':');
5657 /* must have two tokens */
5660 HTTP_FreeTokens(pTokenPair
);
5662 TRACE("No ':' in line: %s\n", debugstr_w(buffer
));
5666 pTokenPair
[0] = heap_alloc((pszColon
- buffer
+ 1) * sizeof(WCHAR
));
5669 HTTP_FreeTokens(pTokenPair
);
5672 memcpy(pTokenPair
[0], buffer
, (pszColon
- buffer
) * sizeof(WCHAR
));
5673 pTokenPair
[0][pszColon
- buffer
] = '\0';
5677 len
= strlenW(pszColon
);
5678 pTokenPair
[1] = heap_alloc((len
+ 1) * sizeof(WCHAR
));
5681 HTTP_FreeTokens(pTokenPair
);
5684 memcpy(pTokenPair
[1], pszColon
, (len
+ 1) * sizeof(WCHAR
));
5686 strip_spaces(pTokenPair
[0]);
5687 strip_spaces(pTokenPair
[1]);
5689 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair
[0]), debugstr_w(pTokenPair
[1]));
5693 /***********************************************************************
5694 * HTTP_ProcessHeader (internal)
5696 * Stuff header into header tables according to <dwModifier>
5700 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5702 static DWORD
HTTP_ProcessHeader(http_request_t
*request
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
)
5704 LPHTTPHEADERW lphttpHdr
= NULL
;
5706 BOOL request_only
= dwModifier
& HTTP_ADDHDR_FLAG_REQ
;
5707 DWORD res
= ERROR_HTTP_INVALID_HEADER
;
5709 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field
), debugstr_w(value
), dwModifier
);
5711 /* REPLACE wins out over ADD */
5712 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5713 dwModifier
&= ~HTTP_ADDHDR_FLAG_ADD
;
5715 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD
)
5718 index
= HTTP_GetCustomHeaderIndex(request
, field
, 0, request_only
);
5722 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD_IF_NEW
)
5723 return ERROR_HTTP_INVALID_HEADER
;
5724 lphttpHdr
= &request
->custHeaders
[index
];
5730 hdr
.lpszField
= (LPWSTR
)field
;
5731 hdr
.lpszValue
= (LPWSTR
)value
;
5732 hdr
.wFlags
= hdr
.wCount
= 0;
5734 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5735 hdr
.wFlags
|= HDR_ISREQUEST
;
5737 return HTTP_InsertCustomHeader(request
, &hdr
);
5739 /* no value to delete */
5740 else return ERROR_SUCCESS
;
5742 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5743 lphttpHdr
->wFlags
|= HDR_ISREQUEST
;
5745 lphttpHdr
->wFlags
&= ~HDR_ISREQUEST
;
5747 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5749 HTTP_DeleteCustomHeader( request
, index
);
5755 hdr
.lpszField
= (LPWSTR
)field
;
5756 hdr
.lpszValue
= (LPWSTR
)value
;
5757 hdr
.wFlags
= hdr
.wCount
= 0;
5759 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5760 hdr
.wFlags
|= HDR_ISREQUEST
;
5762 return HTTP_InsertCustomHeader(request
, &hdr
);
5765 return ERROR_SUCCESS
;
5767 else if (dwModifier
& COALESCEFLAGS
)
5772 INT origlen
= strlenW(lphttpHdr
->lpszValue
);
5773 INT valuelen
= strlenW(value
);
5775 if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
)
5778 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
5780 else if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON
)
5783 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
5786 len
= origlen
+ valuelen
+ ((ch
> 0) ? 2 : 0);
5788 lpsztmp
= heap_realloc(lphttpHdr
->lpszValue
, (len
+1)*sizeof(WCHAR
));
5791 lphttpHdr
->lpszValue
= lpsztmp
;
5792 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5795 lphttpHdr
->lpszValue
[origlen
] = ch
;
5797 lphttpHdr
->lpszValue
[origlen
] = ' ';
5801 memcpy(&lphttpHdr
->lpszValue
[origlen
], value
, valuelen
*sizeof(WCHAR
));
5802 lphttpHdr
->lpszValue
[len
] = '\0';
5803 res
= ERROR_SUCCESS
;
5807 WARN("heap_realloc (%d bytes) failed\n",len
+1);
5808 res
= ERROR_OUTOFMEMORY
;
5811 TRACE("<-- %d\n", res
);
5815 /***********************************************************************
5816 * HTTP_GetCustomHeaderIndex (internal)
5818 * Return index of custom header from header array
5821 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*request
, LPCWSTR lpszField
,
5822 int requested_index
, BOOL request_only
)
5826 TRACE("%s, %d, %d\n", debugstr_w(lpszField
), requested_index
, request_only
);
5828 for (index
= 0; index
< request
->nCustHeaders
; index
++)
5830 if (strcmpiW(request
->custHeaders
[index
].lpszField
, lpszField
))
5833 if (request_only
&& !(request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
5836 if (!request_only
&& (request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
5839 if (requested_index
== 0)
5844 if (index
>= request
->nCustHeaders
)
5847 TRACE("Return: %d\n", index
);
5852 /***********************************************************************
5853 * HTTP_InsertCustomHeader (internal)
5855 * Insert header into array
5858 static DWORD
HTTP_InsertCustomHeader(http_request_t
*request
, LPHTTPHEADERW lpHdr
)
5861 LPHTTPHEADERW lph
= NULL
;
5863 TRACE("--> %s: %s\n", debugstr_w(lpHdr
->lpszField
), debugstr_w(lpHdr
->lpszValue
));
5864 count
= request
->nCustHeaders
+ 1;
5866 lph
= heap_realloc_zero(request
->custHeaders
, sizeof(HTTPHEADERW
) * count
);
5868 lph
= heap_alloc_zero(sizeof(HTTPHEADERW
) * count
);
5871 return ERROR_OUTOFMEMORY
;
5873 request
->custHeaders
= lph
;
5874 request
->custHeaders
[count
-1].lpszField
= heap_strdupW(lpHdr
->lpszField
);
5875 request
->custHeaders
[count
-1].lpszValue
= heap_strdupW(lpHdr
->lpszValue
);
5876 request
->custHeaders
[count
-1].wFlags
= lpHdr
->wFlags
;
5877 request
->custHeaders
[count
-1].wCount
= lpHdr
->wCount
;
5878 request
->nCustHeaders
++;
5880 return ERROR_SUCCESS
;
5884 /***********************************************************************
5885 * HTTP_DeleteCustomHeader (internal)
5887 * Delete header from array
5888 * If this function is called, the indexs may change.
5890 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*request
, DWORD index
)
5892 if( request
->nCustHeaders
<= 0 )
5894 if( index
>= request
->nCustHeaders
)
5896 request
->nCustHeaders
--;
5898 heap_free(request
->custHeaders
[index
].lpszField
);
5899 heap_free(request
->custHeaders
[index
].lpszValue
);
5901 memmove( &request
->custHeaders
[index
], &request
->custHeaders
[index
+1],
5902 (request
->nCustHeaders
- index
)* sizeof(HTTPHEADERW
) );
5903 memset( &request
->custHeaders
[request
->nCustHeaders
], 0, sizeof(HTTPHEADERW
) );
5909 /***********************************************************************
5910 * HTTP_VerifyValidHeader (internal)
5912 * Verify the given header is not invalid for the given http request
5915 static BOOL
HTTP_VerifyValidHeader(http_request_t
*request
, LPCWSTR field
)
5917 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5918 if (!strcmpW(request
->version
, g_szHttp1_0
) && !strcmpiW(field
, szAccept_Encoding
))
5919 return ERROR_HTTP_INVALID_HEADER
;
5921 return ERROR_SUCCESS
;
5924 /***********************************************************************
5925 * IsHostInProxyBypassList (@)
5930 BOOL WINAPI
IsHostInProxyBypassList(DWORD flags
, LPCSTR szHost
, DWORD length
)
5932 FIXME("STUB: flags=%d host=%s length=%d\n",flags
,szHost
,length
);
5936 /***********************************************************************
5937 * InternetShowSecurityInfoByURLA (@)
5939 BOOL WINAPI
InternetShowSecurityInfoByURLA(LPCSTR url
, HWND window
)
5941 FIXME("stub: %s %p\n", url
, window
);
5945 /***********************************************************************
5946 * InternetShowSecurityInfoByURLW (@)
5948 BOOL WINAPI
InternetShowSecurityInfoByURLW(LPCWSTR url
, HWND window
)
5950 FIXME("stub: %s %p\n", debugstr_w(url
), window
);