2 * Wininet - Http Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
10 * Copyright 2011 Jacek Caban for CodeWeavers
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
37 #include <sys/types.h>
38 #ifdef HAVE_SYS_SOCKET_H
39 # include <sys/socket.h>
41 #ifdef HAVE_ARPA_INET_H
42 # include <arpa/inet.h>
61 #define NO_SHLWAPI_STREAM
62 #define NO_SHLWAPI_REG
63 #define NO_SHLWAPI_STRFCNS
64 #define NO_SHLWAPI_GDI
69 #include "cryptuiapi.h"
72 #include "wine/debug.h"
73 #include "wine/exception.h"
74 #include "wine/unicode.h"
76 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
78 static const WCHAR g_szHttp1_0
[] = {'H','T','T','P','/','1','.','0',0};
79 static const WCHAR g_szHttp1_1
[] = {'H','T','T','P','/','1','.','1',0};
80 static const WCHAR szOK
[] = {'O','K',0};
81 static const WCHAR szDefaultHeader
[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
82 static const WCHAR hostW
[] = { 'H','o','s','t',0 };
83 static const WCHAR szAuthorization
[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
84 static const WCHAR szProxy_Authorization
[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
85 static const WCHAR szStatus
[] = { 'S','t','a','t','u','s',0 };
86 static const WCHAR szKeepAlive
[] = {'K','e','e','p','-','A','l','i','v','e',0};
87 static const WCHAR szGET
[] = { 'G','E','T', 0 };
88 static const WCHAR szHEAD
[] = { 'H','E','A','D', 0 };
89 static const WCHAR szCrLf
[] = {'\r','\n', 0};
91 static const WCHAR szAccept
[] = { 'A','c','c','e','p','t',0 };
92 static const WCHAR szAccept_Charset
[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
93 static const WCHAR szAccept_Encoding
[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
94 static const WCHAR szAccept_Language
[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
95 static const WCHAR szAccept_Ranges
[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
96 static const WCHAR szAge
[] = { 'A','g','e',0 };
97 static const WCHAR szAllow
[] = { 'A','l','l','o','w',0 };
98 static const WCHAR szCache_Control
[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
99 static const WCHAR szConnection
[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
100 static const WCHAR szContent_Base
[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
101 static const WCHAR szContent_Encoding
[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
102 static const WCHAR szContent_ID
[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
103 static const WCHAR szContent_Language
[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
104 static const WCHAR szContent_Length
[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
105 static const WCHAR szContent_Location
[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
106 static const WCHAR szContent_MD5
[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
107 static const WCHAR szContent_Range
[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
108 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 };
109 static const WCHAR szContent_Type
[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
110 static const WCHAR szCookie
[] = { 'C','o','o','k','i','e',0 };
111 static const WCHAR szDate
[] = { 'D','a','t','e',0 };
112 static const WCHAR szFrom
[] = { 'F','r','o','m',0 };
113 static const WCHAR szETag
[] = { 'E','T','a','g',0 };
114 static const WCHAR szExpect
[] = { 'E','x','p','e','c','t',0 };
115 static const WCHAR szExpires
[] = { 'E','x','p','i','r','e','s',0 };
116 static const WCHAR szIf_Match
[] = { 'I','f','-','M','a','t','c','h',0 };
117 static const WCHAR szIf_Modified_Since
[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
118 static const WCHAR szIf_None_Match
[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
119 static const WCHAR szIf_Range
[] = { 'I','f','-','R','a','n','g','e',0 };
120 static const WCHAR szIf_Unmodified_Since
[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
121 static const WCHAR szLast_Modified
[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
122 static const WCHAR szLocation
[] = { 'L','o','c','a','t','i','o','n',0 };
123 static const WCHAR szMax_Forwards
[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
124 static const WCHAR szMime_Version
[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
125 static const WCHAR szPragma
[] = { 'P','r','a','g','m','a',0 };
126 static const WCHAR szProxy_Authenticate
[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
127 static const WCHAR szProxy_Connection
[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
128 static const WCHAR szPublic
[] = { 'P','u','b','l','i','c',0 };
129 static const WCHAR szRange
[] = { 'R','a','n','g','e',0 };
130 static const WCHAR szReferer
[] = { 'R','e','f','e','r','e','r',0 };
131 static const WCHAR szRetry_After
[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
132 static const WCHAR szServer
[] = { 'S','e','r','v','e','r',0 };
133 static const WCHAR szSet_Cookie
[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
134 static const WCHAR szTransfer_Encoding
[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
135 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 };
136 static const WCHAR szUpgrade
[] = { 'U','p','g','r','a','d','e',0 };
137 static const WCHAR szURI
[] = { 'U','R','I',0 };
138 static const WCHAR szUser_Agent
[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
139 static const WCHAR szVary
[] = { 'V','a','r','y',0 };
140 static const WCHAR szVia
[] = { 'V','i','a',0 };
141 static const WCHAR szWarning
[] = { 'W','a','r','n','i','n','g',0 };
142 static const WCHAR szWWW_Authenticate
[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
144 #define HTTP_REFERER szReferer
145 #define HTTP_ACCEPT szAccept
146 #define HTTP_USERAGENT szUser_Agent
148 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
149 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
150 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
151 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
153 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
154 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
156 #define COLLECT_TIME 60000
158 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
169 unsigned int auth_data_len
;
170 BOOL finished
; /* finished authenticating */
174 typedef struct _basicAuthorizationData
181 UINT authorizationLen
;
182 } basicAuthorizationData
;
184 typedef struct _authorizationData
198 static struct list basicAuthorizationCache
= LIST_INIT(basicAuthorizationCache
);
199 static struct list authorizationCache
= LIST_INIT(authorizationCache
);
201 static CRITICAL_SECTION authcache_cs
;
202 static CRITICAL_SECTION_DEBUG critsect_debug
=
205 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
206 0, 0, { (DWORD_PTR
)(__FILE__
": authcache_cs") }
208 static CRITICAL_SECTION authcache_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
210 static BOOL
HTTP_GetResponseHeaders(http_request_t
*req
, BOOL clear
);
211 static DWORD
HTTP_ProcessHeader(http_request_t
*req
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
);
212 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
);
213 static DWORD
HTTP_InsertCustomHeader(http_request_t
*req
, LPHTTPHEADERW lpHdr
);
214 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*req
, LPCWSTR lpszField
, INT index
, BOOL Request
);
215 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*req
, DWORD index
);
216 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
);
217 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*, DWORD
, LPVOID
, LPDWORD
, LPDWORD
);
218 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*req
, LPCWSTR lpszUrl
);
219 static UINT
HTTP_DecodeBase64(LPCWSTR base64
, LPSTR bin
);
220 static BOOL
HTTP_VerifyValidHeader(http_request_t
*req
, LPCWSTR field
);
222 static CRITICAL_SECTION connection_pool_cs
;
223 static CRITICAL_SECTION_DEBUG connection_pool_debug
=
225 0, 0, &connection_pool_cs
,
226 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
227 0, 0, { (DWORD_PTR
)(__FILE__
": connection_pool_cs") }
229 static CRITICAL_SECTION connection_pool_cs
= { &connection_pool_debug
, -1, 0, 0, 0, 0 };
231 static struct list connection_pool
= LIST_INIT(connection_pool
);
232 static BOOL collector_running
;
234 void server_addref(server_t
*server
)
236 InterlockedIncrement(&server
->ref
);
239 void server_release(server_t
*server
)
241 if(InterlockedDecrement(&server
->ref
))
245 server
->keep_until
= GetTickCount64() + COLLECT_TIME
;
248 static server_t
*get_server(const WCHAR
*name
, INTERNET_PORT port
)
250 server_t
*iter
, *server
= NULL
;
252 EnterCriticalSection(&connection_pool_cs
);
254 LIST_FOR_EACH_ENTRY(iter
, &connection_pool
, server_t
, entry
) {
255 if(iter
->port
== port
&& !strcmpW(iter
->name
, name
)) {
257 server_addref(server
);
263 server
= heap_alloc(sizeof(*server
));
265 server
->addr_len
= 0;
268 list_init(&server
->conn_pool
);
269 server
->name
= heap_strdupW(name
);
271 list_add_head(&connection_pool
, &server
->entry
);
279 LeaveCriticalSection(&connection_pool_cs
);
284 BOOL
collect_connections(BOOL collect_all
)
286 netconn_t
*netconn
, *netconn_safe
;
287 server_t
*server
, *server_safe
;
288 BOOL remaining
= FALSE
;
291 now
= GetTickCount64();
293 LIST_FOR_EACH_ENTRY_SAFE(server
, server_safe
, &connection_pool
, server_t
, entry
) {
294 LIST_FOR_EACH_ENTRY_SAFE(netconn
, netconn_safe
, &server
->conn_pool
, netconn_t
, pool_entry
) {
295 if(collect_all
|| netconn
->keep_until
< now
) {
296 TRACE("freeing %p\n", netconn
);
297 list_remove(&netconn
->pool_entry
);
298 free_netconn(netconn
);
305 if(collect_all
|| server
->keep_until
< now
) {
306 list_remove(&server
->entry
);
308 heap_free(server
->name
);
319 static DWORD WINAPI
collect_connections_proc(void *arg
)
321 BOOL remaining_conns
;
324 /* FIXME: Use more sophisticated method */
327 EnterCriticalSection(&connection_pool_cs
);
329 remaining_conns
= collect_connections(FALSE
);
331 collector_running
= FALSE
;
333 LeaveCriticalSection(&connection_pool_cs
);
334 }while(remaining_conns
);
336 FreeLibraryAndExitThread(WININET_hModule
, 0);
339 static LPHTTPHEADERW
HTTP_GetHeader(http_request_t
*req
, LPCWSTR head
)
342 HeaderIndex
= HTTP_GetCustomHeaderIndex(req
, head
, 0, TRUE
);
343 if (HeaderIndex
== -1)
346 return &req
->custHeaders
[HeaderIndex
];
355 struct data_stream_vtbl_t
{
356 DWORD (*get_avail_data
)(data_stream_t
*,http_request_t
*);
357 BOOL (*end_of_data
)(data_stream_t
*,http_request_t
*);
358 DWORD (*read
)(data_stream_t
*,http_request_t
*,BYTE
*,DWORD
,DWORD
*,read_mode_t
);
359 BOOL (*drain_content
)(data_stream_t
*,http_request_t
*);
360 void (*destroy
)(data_stream_t
*);
364 data_stream_t data_stream
;
366 BYTE buf
[READ_BUFFER_SIZE
];
372 static inline void destroy_data_stream(data_stream_t
*stream
)
374 stream
->vtbl
->destroy(stream
);
377 static void reset_data_stream(http_request_t
*req
)
379 destroy_data_stream(req
->data_stream
);
380 req
->data_stream
= &req
->netconn_stream
.data_stream
;
381 req
->read_pos
= req
->read_size
= req
->netconn_stream
.content_read
= 0;
382 req
->read_chunked
= req
->read_gzip
= FALSE
;
388 data_stream_t stream
;
389 data_stream_t
*parent_stream
;
391 BYTE buf
[READ_BUFFER_SIZE
];
397 static DWORD
gzip_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
399 /* Allow reading only from read buffer */
403 static BOOL
gzip_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
405 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
406 return gzip_stream
->end_of_data
;
409 static DWORD
gzip_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
410 DWORD
*read
, read_mode_t read_mode
)
412 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
413 z_stream
*zstream
= &gzip_stream
->zstream
;
414 DWORD current_read
, ret_read
= 0;
417 DWORD res
= ERROR_SUCCESS
;
419 while(size
&& !gzip_stream
->end_of_data
) {
420 end
= gzip_stream
->parent_stream
->vtbl
->end_of_data(gzip_stream
->parent_stream
, req
);
422 if(gzip_stream
->buf_size
<= 64 && !end
) {
423 if(gzip_stream
->buf_pos
) {
424 if(gzip_stream
->buf_size
)
425 memmove(gzip_stream
->buf
, gzip_stream
->buf
+gzip_stream
->buf_pos
, gzip_stream
->buf_size
);
426 gzip_stream
->buf_pos
= 0;
428 res
= gzip_stream
->parent_stream
->vtbl
->read(gzip_stream
->parent_stream
, req
, gzip_stream
->buf
+gzip_stream
->buf_size
,
429 sizeof(gzip_stream
->buf
)-gzip_stream
->buf_size
, ¤t_read
, read_mode
);
430 gzip_stream
->buf_size
+= current_read
;
431 if(res
!= ERROR_SUCCESS
)
433 end
= gzip_stream
->parent_stream
->vtbl
->end_of_data(gzip_stream
->parent_stream
, req
);
434 if(!current_read
&& !end
) {
435 if(read_mode
!= READMODE_NOBLOCK
) {
436 WARN("unexpected end of data\n");
437 gzip_stream
->end_of_data
= TRUE
;
441 if(gzip_stream
->buf_size
<= 64 && !end
)
445 zstream
->next_in
= gzip_stream
->buf
+gzip_stream
->buf_pos
;
446 zstream
->avail_in
= gzip_stream
->buf_size
-(end
? 0 : 64);
447 zstream
->next_out
= buf
+ret_read
;
448 zstream
->avail_out
= size
;
449 zres
= inflate(&gzip_stream
->zstream
, 0);
450 current_read
= size
- zstream
->avail_out
;
451 size
-= current_read
;
452 ret_read
+= current_read
;
453 gzip_stream
->buf_size
-= zstream
->next_in
- (gzip_stream
->buf
+gzip_stream
->buf_pos
);
454 gzip_stream
->buf_pos
= zstream
->next_in
-gzip_stream
->buf
;
455 if(zres
== Z_STREAM_END
) {
456 TRACE("end of data\n");
457 gzip_stream
->end_of_data
= TRUE
;
459 }else if(zres
!= Z_OK
) {
460 WARN("inflate failed %d: %s\n", zres
, debugstr_a(zstream
->msg
));
462 res
= ERROR_INTERNET_DECODING_FAILED
;
466 if(ret_read
&& read_mode
== READMODE_ASYNC
)
467 read_mode
= READMODE_NOBLOCK
;
470 TRACE("read %u bytes\n", ret_read
);
475 static BOOL
gzip_drain_content(data_stream_t
*stream
, http_request_t
*req
)
477 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
478 return gzip_stream
->parent_stream
->vtbl
->drain_content(gzip_stream
->parent_stream
, req
);
481 static void gzip_destroy(data_stream_t
*stream
)
483 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
485 destroy_data_stream(gzip_stream
->parent_stream
);
487 if(!gzip_stream
->end_of_data
)
488 inflateEnd(&gzip_stream
->zstream
);
489 heap_free(gzip_stream
);
492 static const data_stream_vtbl_t gzip_stream_vtbl
= {
500 static voidpf
wininet_zalloc(voidpf opaque
, uInt items
, uInt size
)
502 return heap_alloc(items
*size
);
505 static void wininet_zfree(voidpf opaque
, voidpf address
)
510 static DWORD
init_gzip_stream(http_request_t
*req
)
512 gzip_stream_t
*gzip_stream
;
515 gzip_stream
= heap_alloc_zero(sizeof(gzip_stream_t
));
517 return ERROR_OUTOFMEMORY
;
519 gzip_stream
->stream
.vtbl
= &gzip_stream_vtbl
;
520 gzip_stream
->zstream
.zalloc
= wininet_zalloc
;
521 gzip_stream
->zstream
.zfree
= wininet_zfree
;
523 zres
= inflateInit2(&gzip_stream
->zstream
, 0x1f);
525 ERR("inflateInit failed: %d\n", zres
);
526 heap_free(gzip_stream
);
527 return ERROR_OUTOFMEMORY
;
530 index
= HTTP_GetCustomHeaderIndex(req
, szContent_Length
, 0, FALSE
);
532 HTTP_DeleteCustomHeader(req
, index
);
535 memcpy(gzip_stream
->buf
, req
->read_buf
+req
->read_pos
, req
->read_size
);
536 gzip_stream
->buf_size
= req
->read_size
;
537 req
->read_pos
= req
->read_size
= 0;
540 req
->read_gzip
= TRUE
;
541 gzip_stream
->parent_stream
= req
->data_stream
;
542 req
->data_stream
= &gzip_stream
->stream
;
543 return ERROR_SUCCESS
;
548 static DWORD
init_gzip_stream(http_request_t
*req
)
550 ERR("gzip stream not supported, missing zlib.\n");
551 return ERROR_SUCCESS
;
556 /***********************************************************************
557 * HTTP_Tokenize (internal)
559 * Tokenize a string, allocating memory for the tokens.
561 static LPWSTR
* HTTP_Tokenize(LPCWSTR string
, LPCWSTR token_string
)
563 LPWSTR
* token_array
;
570 /* empty string has no tokens */
574 for (i
= 0; string
[i
]; i
++)
576 if (!strncmpW(string
+i
, token_string
, strlenW(token_string
)))
580 /* we want to skip over separators, but not the null terminator */
581 for (j
= 0; j
< strlenW(token_string
) - 1; j
++)
589 /* add 1 for terminating NULL */
590 token_array
= heap_alloc((tokens
+1) * sizeof(*token_array
));
591 token_array
[tokens
] = NULL
;
594 for (i
= 0; i
< tokens
; i
++)
597 next_token
= strstrW(string
, token_string
);
598 if (!next_token
) next_token
= string
+strlenW(string
);
599 len
= next_token
- string
;
600 token_array
[i
] = heap_alloc((len
+1)*sizeof(WCHAR
));
601 memcpy(token_array
[i
], string
, len
*sizeof(WCHAR
));
602 token_array
[i
][len
] = '\0';
603 string
= next_token
+strlenW(token_string
);
608 /***********************************************************************
609 * HTTP_FreeTokens (internal)
611 * Frees memory returned from HTTP_Tokenize.
613 static void HTTP_FreeTokens(LPWSTR
* token_array
)
616 for (i
= 0; token_array
[i
]; i
++) heap_free(token_array
[i
]);
617 heap_free(token_array
);
620 static void HTTP_FixURL(http_request_t
*request
)
622 static const WCHAR szSlash
[] = { '/',0 };
623 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/', 0 };
625 /* If we don't have a path we set it to root */
626 if (NULL
== request
->path
)
627 request
->path
= heap_strdupW(szSlash
);
628 else /* remove \r and \n*/
630 int nLen
= strlenW(request
->path
);
631 while ((nLen
>0 ) && ((request
->path
[nLen
-1] == '\r')||(request
->path
[nLen
-1] == '\n')))
634 request
->path
[nLen
]='\0';
636 /* Replace '\' with '/' */
639 if (request
->path
[nLen
] == '\\') request
->path
[nLen
]='/';
643 if(CSTR_EQUAL
!= CompareStringW( LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
644 request
->path
, strlenW(request
->path
), szHttp
, strlenW(szHttp
) )
645 && request
->path
[0] != '/') /* not an absolute path ?? --> fix it !! */
647 WCHAR
*fixurl
= heap_alloc((strlenW(request
->path
) + 2)*sizeof(WCHAR
));
649 strcpyW(fixurl
+ 1, request
->path
);
650 heap_free( request
->path
);
651 request
->path
= fixurl
;
655 static LPWSTR
HTTP_BuildHeaderRequestString( http_request_t
*request
, LPCWSTR verb
, LPCWSTR path
, LPCWSTR version
)
657 LPWSTR requestString
;
663 static const WCHAR szSpace
[] = { ' ',0 };
664 static const WCHAR szColon
[] = { ':',' ',0 };
665 static const WCHAR sztwocrlf
[] = {'\r','\n','\r','\n', 0};
667 /* allocate space for an array of all the string pointers to be added */
668 len
= (request
->nCustHeaders
)*4 + 10;
669 req
= heap_alloc(len
*sizeof(LPCWSTR
));
671 /* add the verb, path and HTTP version string */
679 /* Append custom request headers */
680 for (i
= 0; i
< request
->nCustHeaders
; i
++)
682 if (request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
685 req
[n
++] = request
->custHeaders
[i
].lpszField
;
687 req
[n
++] = request
->custHeaders
[i
].lpszValue
;
689 TRACE("Adding custom header %s (%s)\n",
690 debugstr_w(request
->custHeaders
[i
].lpszField
),
691 debugstr_w(request
->custHeaders
[i
].lpszValue
));
696 ERR("oops. buffer overrun\n");
699 requestString
= HTTP_build_req( req
, 4 );
703 * Set (header) termination string for request
704 * Make sure there's exactly two new lines at the end of the request
706 p
= &requestString
[strlenW(requestString
)-1];
707 while ( (*p
== '\n') || (*p
== '\r') )
709 strcpyW( p
+1, sztwocrlf
);
711 return requestString
;
714 static void HTTP_ProcessCookies( http_request_t
*request
)
718 LPHTTPHEADERW setCookieHeader
;
720 if(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
)
723 while((HeaderIndex
= HTTP_GetCustomHeaderIndex(request
, szSet_Cookie
, numCookies
++, FALSE
)) != -1)
729 setCookieHeader
= &request
->custHeaders
[HeaderIndex
];
731 if (!setCookieHeader
->lpszValue
)
734 host
= HTTP_GetHeader(request
, hostW
);
738 data
= strchrW(setCookieHeader
->lpszValue
, '=');
742 name
= heap_strndupW(setCookieHeader
->lpszValue
, data
-setCookieHeader
->lpszValue
);
747 set_cookie(host
->lpszValue
, request
->path
, name
, data
);
752 static void strip_spaces(LPWSTR start
)
757 while (*str
== ' ' && *str
!= '\0')
761 memmove(start
, str
, sizeof(WCHAR
) * (strlenW(str
) + 1));
763 end
= start
+ strlenW(start
) - 1;
764 while (end
>= start
&& *end
== ' ')
771 static inline BOOL
is_basic_auth_value( LPCWSTR pszAuthValue
, LPWSTR
*pszRealm
)
773 static const WCHAR szBasic
[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
774 static const WCHAR szRealm
[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
776 is_basic
= !strncmpiW(pszAuthValue
, szBasic
, ARRAYSIZE(szBasic
)) &&
777 ((pszAuthValue
[ARRAYSIZE(szBasic
)] == ' ') || !pszAuthValue
[ARRAYSIZE(szBasic
)]);
778 if (is_basic
&& pszRealm
)
781 LPCWSTR ptr
= &pszAuthValue
[ARRAYSIZE(szBasic
)];
785 token
= strchrW(ptr
,'=');
789 while (*realm
== ' ' && *realm
!= '\0')
791 if(!strncmpiW(realm
, szRealm
, ARRAYSIZE(szRealm
)) &&
792 (realm
[ARRAYSIZE(szRealm
)] == ' ' || realm
[ARRAYSIZE(szRealm
)] == '='))
795 while (*token
== ' ' && *token
!= '\0')
799 *pszRealm
= heap_strdupW(token
);
800 strip_spaces(*pszRealm
);
807 static void destroy_authinfo( struct HttpAuthInfo
*authinfo
)
809 if (!authinfo
) return;
811 if (SecIsValidHandle(&authinfo
->ctx
))
812 DeleteSecurityContext(&authinfo
->ctx
);
813 if (SecIsValidHandle(&authinfo
->cred
))
814 FreeCredentialsHandle(&authinfo
->cred
);
816 heap_free(authinfo
->auth_data
);
817 heap_free(authinfo
->scheme
);
821 static UINT
retrieve_cached_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR
*auth_data
)
823 basicAuthorizationData
*ad
;
826 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host
),debugstr_w(realm
));
828 EnterCriticalSection(&authcache_cs
);
829 LIST_FOR_EACH_ENTRY(ad
, &basicAuthorizationCache
, basicAuthorizationData
, entry
)
831 if (!strcmpiW(host
,ad
->host
) && !strcmpW(realm
,ad
->realm
))
833 TRACE("Authorization found in cache\n");
834 *auth_data
= heap_alloc(ad
->authorizationLen
);
835 memcpy(*auth_data
,ad
->authorization
,ad
->authorizationLen
);
836 rc
= ad
->authorizationLen
;
840 LeaveCriticalSection(&authcache_cs
);
844 static void cache_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR auth_data
, UINT auth_data_len
)
847 basicAuthorizationData
* ad
= NULL
;
849 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host
),debugstr_w(realm
),debugstr_an(auth_data
,auth_data_len
));
851 EnterCriticalSection(&authcache_cs
);
852 LIST_FOR_EACH(cursor
, &basicAuthorizationCache
)
854 basicAuthorizationData
*check
= LIST_ENTRY(cursor
,basicAuthorizationData
,entry
);
855 if (!strcmpiW(host
,check
->host
) && !strcmpW(realm
,check
->realm
))
864 TRACE("Found match in cache, replacing\n");
865 heap_free(ad
->authorization
);
866 ad
->authorization
= heap_alloc(auth_data_len
);
867 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
868 ad
->authorizationLen
= auth_data_len
;
872 ad
= heap_alloc(sizeof(basicAuthorizationData
));
873 ad
->host
= heap_strdupW(host
);
874 ad
->realm
= heap_strdupW(realm
);
875 ad
->authorization
= heap_alloc(auth_data_len
);
876 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
877 ad
->authorizationLen
= auth_data_len
;
878 list_add_head(&basicAuthorizationCache
,&ad
->entry
);
879 TRACE("authorization cached\n");
881 LeaveCriticalSection(&authcache_cs
);
884 static BOOL
retrieve_cached_authorization(LPWSTR host
, LPWSTR scheme
,
885 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
887 authorizationData
*ad
;
889 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
891 EnterCriticalSection(&authcache_cs
);
892 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
) {
893 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
894 TRACE("Authorization found in cache\n");
896 nt_auth_identity
->User
= heap_strdupW(ad
->user
);
897 nt_auth_identity
->Password
= heap_strdupW(ad
->password
);
898 nt_auth_identity
->Domain
= heap_alloc(sizeof(WCHAR
)*ad
->domain_len
);
899 if(!nt_auth_identity
->User
|| !nt_auth_identity
->Password
||
900 (!nt_auth_identity
->Domain
&& ad
->domain_len
)) {
901 heap_free(nt_auth_identity
->User
);
902 heap_free(nt_auth_identity
->Password
);
903 heap_free(nt_auth_identity
->Domain
);
907 nt_auth_identity
->Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
908 nt_auth_identity
->UserLength
= ad
->user_len
;
909 nt_auth_identity
->PasswordLength
= ad
->password_len
;
910 memcpy(nt_auth_identity
->Domain
, ad
->domain
, sizeof(WCHAR
)*ad
->domain_len
);
911 nt_auth_identity
->DomainLength
= ad
->domain_len
;
912 LeaveCriticalSection(&authcache_cs
);
916 LeaveCriticalSection(&authcache_cs
);
921 static void cache_authorization(LPWSTR host
, LPWSTR scheme
,
922 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
924 authorizationData
*ad
;
927 TRACE("Caching authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
929 EnterCriticalSection(&authcache_cs
);
930 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
)
931 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
938 heap_free(ad
->password
);
939 heap_free(ad
->domain
);
941 ad
= heap_alloc(sizeof(authorizationData
));
943 LeaveCriticalSection(&authcache_cs
);
947 ad
->host
= heap_strdupW(host
);
948 ad
->scheme
= heap_strdupW(scheme
);
949 list_add_head(&authorizationCache
, &ad
->entry
);
952 ad
->user
= heap_strndupW(nt_auth_identity
->User
, nt_auth_identity
->UserLength
);
953 ad
->password
= heap_strndupW(nt_auth_identity
->Password
, nt_auth_identity
->PasswordLength
);
954 ad
->domain
= heap_strndupW(nt_auth_identity
->Domain
, nt_auth_identity
->DomainLength
);
955 ad
->user_len
= nt_auth_identity
->UserLength
;
956 ad
->password_len
= nt_auth_identity
->PasswordLength
;
957 ad
->domain_len
= nt_auth_identity
->DomainLength
;
959 if(!ad
->host
|| !ad
->scheme
|| !ad
->user
|| !ad
->password
960 || (nt_auth_identity
->Domain
&& !ad
->domain
)) {
962 heap_free(ad
->scheme
);
964 heap_free(ad
->password
);
965 heap_free(ad
->domain
);
966 list_remove(&ad
->entry
);
970 LeaveCriticalSection(&authcache_cs
);
973 static BOOL
HTTP_DoAuthorization( http_request_t
*request
, LPCWSTR pszAuthValue
,
974 struct HttpAuthInfo
**ppAuthInfo
,
975 LPWSTR domain_and_username
, LPWSTR password
,
978 SECURITY_STATUS sec_status
;
979 struct HttpAuthInfo
*pAuthInfo
= *ppAuthInfo
;
981 LPWSTR szRealm
= NULL
;
983 TRACE("%s\n", debugstr_w(pszAuthValue
));
990 pAuthInfo
= heap_alloc(sizeof(*pAuthInfo
));
994 SecInvalidateHandle(&pAuthInfo
->cred
);
995 SecInvalidateHandle(&pAuthInfo
->ctx
);
996 memset(&pAuthInfo
->exp
, 0, sizeof(pAuthInfo
->exp
));
998 pAuthInfo
->auth_data
= NULL
;
999 pAuthInfo
->auth_data_len
= 0;
1000 pAuthInfo
->finished
= FALSE
;
1002 if (is_basic_auth_value(pszAuthValue
,NULL
))
1004 static const WCHAR szBasic
[] = {'B','a','s','i','c',0};
1005 pAuthInfo
->scheme
= heap_strdupW(szBasic
);
1006 if (!pAuthInfo
->scheme
)
1008 heap_free(pAuthInfo
);
1015 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity
;
1017 pAuthInfo
->scheme
= heap_strdupW(pszAuthValue
);
1018 if (!pAuthInfo
->scheme
)
1020 heap_free(pAuthInfo
);
1024 if (domain_and_username
)
1026 WCHAR
*user
= strchrW(domain_and_username
, '\\');
1027 WCHAR
*domain
= domain_and_username
;
1029 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1031 pAuthData
= &nt_auth_identity
;
1036 user
= domain_and_username
;
1040 nt_auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
1041 nt_auth_identity
.User
= user
;
1042 nt_auth_identity
.UserLength
= strlenW(nt_auth_identity
.User
);
1043 nt_auth_identity
.Domain
= domain
;
1044 nt_auth_identity
.DomainLength
= domain
? user
- domain
- 1 : 0;
1045 nt_auth_identity
.Password
= password
;
1046 nt_auth_identity
.PasswordLength
= strlenW(nt_auth_identity
.Password
);
1048 cache_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
);
1050 else if(retrieve_cached_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
))
1051 pAuthData
= &nt_auth_identity
;
1053 /* use default credentials */
1056 sec_status
= AcquireCredentialsHandleW(NULL
, pAuthInfo
->scheme
,
1057 SECPKG_CRED_OUTBOUND
, NULL
,
1059 NULL
, &pAuthInfo
->cred
,
1062 if(pAuthData
&& !domain_and_username
) {
1063 heap_free(nt_auth_identity
.User
);
1064 heap_free(nt_auth_identity
.Domain
);
1065 heap_free(nt_auth_identity
.Password
);
1068 if (sec_status
== SEC_E_OK
)
1070 PSecPkgInfoW sec_pkg_info
;
1071 sec_status
= QuerySecurityPackageInfoW(pAuthInfo
->scheme
, &sec_pkg_info
);
1072 if (sec_status
== SEC_E_OK
)
1074 pAuthInfo
->max_token
= sec_pkg_info
->cbMaxToken
;
1075 FreeContextBuffer(sec_pkg_info
);
1078 if (sec_status
!= SEC_E_OK
)
1080 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1081 debugstr_w(pAuthInfo
->scheme
), sec_status
);
1082 heap_free(pAuthInfo
->scheme
);
1083 heap_free(pAuthInfo
);
1087 *ppAuthInfo
= pAuthInfo
;
1089 else if (pAuthInfo
->finished
)
1092 if ((strlenW(pszAuthValue
) < strlenW(pAuthInfo
->scheme
)) ||
1093 strncmpiW(pszAuthValue
, pAuthInfo
->scheme
, strlenW(pAuthInfo
->scheme
)))
1095 ERR("authentication scheme changed from %s to %s\n",
1096 debugstr_w(pAuthInfo
->scheme
), debugstr_w(pszAuthValue
));
1100 if (is_basic_auth_value(pszAuthValue
,&szRealm
))
1104 char *auth_data
= NULL
;
1105 UINT auth_data_len
= 0;
1107 TRACE("basic authentication realm %s\n",debugstr_w(szRealm
));
1109 if (!domain_and_username
)
1111 if (host
&& szRealm
)
1112 auth_data_len
= retrieve_cached_basic_authorization(host
, szRealm
,&auth_data
);
1113 if (auth_data_len
== 0)
1121 userlen
= WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, lstrlenW(domain_and_username
), NULL
, 0, NULL
, NULL
);
1122 passlen
= WideCharToMultiByte(CP_UTF8
, 0, password
, lstrlenW(password
), NULL
, 0, NULL
, NULL
);
1124 /* length includes a nul terminator, which will be re-used for the ':' */
1125 auth_data
= heap_alloc(userlen
+ 1 + passlen
);
1132 WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, -1, auth_data
, userlen
, NULL
, NULL
);
1133 auth_data
[userlen
] = ':';
1134 WideCharToMultiByte(CP_UTF8
, 0, password
, -1, &auth_data
[userlen
+1], passlen
, NULL
, NULL
);
1135 auth_data_len
= userlen
+ 1 + passlen
;
1136 if (host
&& szRealm
)
1137 cache_basic_authorization(host
, szRealm
, auth_data
, auth_data_len
);
1140 pAuthInfo
->auth_data
= auth_data
;
1141 pAuthInfo
->auth_data_len
= auth_data_len
;
1142 pAuthInfo
->finished
= TRUE
;
1148 LPCWSTR pszAuthData
;
1149 SecBufferDesc out_desc
, in_desc
;
1151 unsigned char *buffer
;
1152 ULONG context_req
= ISC_REQ_CONNECTION
| ISC_REQ_USE_DCE_STYLE
|
1153 ISC_REQ_MUTUAL_AUTH
| ISC_REQ_DELEGATE
;
1155 in
.BufferType
= SECBUFFER_TOKEN
;
1159 in_desc
.ulVersion
= 0;
1160 in_desc
.cBuffers
= 1;
1161 in_desc
.pBuffers
= &in
;
1163 pszAuthData
= pszAuthValue
+ strlenW(pAuthInfo
->scheme
);
1164 if (*pszAuthData
== ' ')
1167 in
.cbBuffer
= HTTP_DecodeBase64(pszAuthData
, NULL
);
1168 in
.pvBuffer
= heap_alloc(in
.cbBuffer
);
1169 HTTP_DecodeBase64(pszAuthData
, in
.pvBuffer
);
1172 buffer
= heap_alloc(pAuthInfo
->max_token
);
1174 out
.BufferType
= SECBUFFER_TOKEN
;
1175 out
.cbBuffer
= pAuthInfo
->max_token
;
1176 out
.pvBuffer
= buffer
;
1178 out_desc
.ulVersion
= 0;
1179 out_desc
.cBuffers
= 1;
1180 out_desc
.pBuffers
= &out
;
1182 sec_status
= InitializeSecurityContextW(first
? &pAuthInfo
->cred
: NULL
,
1183 first
? NULL
: &pAuthInfo
->ctx
,
1184 first
? request
->session
->serverName
: NULL
,
1185 context_req
, 0, SECURITY_NETWORK_DREP
,
1186 in
.pvBuffer
? &in_desc
: NULL
,
1187 0, &pAuthInfo
->ctx
, &out_desc
,
1188 &pAuthInfo
->attr
, &pAuthInfo
->exp
);
1189 if (sec_status
== SEC_E_OK
)
1191 pAuthInfo
->finished
= TRUE
;
1192 pAuthInfo
->auth_data
= out
.pvBuffer
;
1193 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
1194 TRACE("sending last auth packet\n");
1196 else if (sec_status
== SEC_I_CONTINUE_NEEDED
)
1198 pAuthInfo
->auth_data
= out
.pvBuffer
;
1199 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
1200 TRACE("sending next auth packet\n");
1204 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status
);
1205 heap_free(out
.pvBuffer
);
1206 destroy_authinfo(pAuthInfo
);
1215 /***********************************************************************
1216 * HTTP_HttpAddRequestHeadersW (internal)
1218 static DWORD
HTTP_HttpAddRequestHeadersW(http_request_t
*request
,
1219 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1224 DWORD len
, res
= ERROR_HTTP_INVALID_HEADER
;
1226 TRACE("copying header: %s\n", debugstr_wn(lpszHeader
, dwHeaderLength
));
1228 if( dwHeaderLength
== ~0U )
1229 len
= strlenW(lpszHeader
);
1231 len
= dwHeaderLength
;
1232 buffer
= heap_alloc(sizeof(WCHAR
)*(len
+1));
1233 lstrcpynW( buffer
, lpszHeader
, len
+ 1);
1239 LPWSTR
* pFieldAndValue
;
1241 lpszEnd
= lpszStart
;
1243 while (*lpszEnd
!= '\0')
1245 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1250 if (*lpszStart
== '\0')
1253 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1256 lpszEnd
++; /* Jump over newline */
1258 TRACE("interpreting header %s\n", debugstr_w(lpszStart
));
1259 if (*lpszStart
== '\0')
1261 /* Skip 0-length headers */
1262 lpszStart
= lpszEnd
;
1263 res
= ERROR_SUCCESS
;
1266 pFieldAndValue
= HTTP_InterpretHttpHeader(lpszStart
);
1269 res
= HTTP_VerifyValidHeader(request
, pFieldAndValue
[0]);
1270 if (res
== ERROR_SUCCESS
)
1271 res
= HTTP_ProcessHeader(request
, pFieldAndValue
[0],
1272 pFieldAndValue
[1], dwModifier
| HTTP_ADDHDR_FLAG_REQ
);
1273 HTTP_FreeTokens(pFieldAndValue
);
1276 lpszStart
= lpszEnd
;
1277 } while (res
== ERROR_SUCCESS
);
1283 /***********************************************************************
1284 * HttpAddRequestHeadersW (WININET.@)
1286 * Adds one or more HTTP header to the request handler
1289 * On Windows if dwHeaderLength includes the trailing '\0', then
1290 * HttpAddRequestHeadersW() adds it too. However this results in an
1291 * invalid Http header which is rejected by some servers so we probably
1292 * don't need to match Windows on that point.
1299 BOOL WINAPI
HttpAddRequestHeadersW(HINTERNET hHttpRequest
,
1300 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1302 http_request_t
*request
;
1303 DWORD res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
1305 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_wn(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1310 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
1311 if (request
&& request
->hdr
.htype
== WH_HHTTPREQ
)
1312 res
= HTTP_HttpAddRequestHeadersW( request
, lpszHeader
, dwHeaderLength
, dwModifier
);
1314 WININET_Release( &request
->hdr
);
1316 if(res
!= ERROR_SUCCESS
)
1318 return res
== ERROR_SUCCESS
;
1321 /***********************************************************************
1322 * HttpAddRequestHeadersA (WININET.@)
1324 * Adds one or more HTTP header to the request handler
1331 BOOL WINAPI
HttpAddRequestHeadersA(HINTERNET hHttpRequest
,
1332 LPCSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1338 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_an(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1340 len
= MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, NULL
, 0 );
1341 hdr
= heap_alloc(len
*sizeof(WCHAR
));
1342 MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, hdr
, len
);
1343 if( dwHeaderLength
!= ~0U )
1344 dwHeaderLength
= len
;
1346 r
= HttpAddRequestHeadersW( hHttpRequest
, hdr
, dwHeaderLength
, dwModifier
);
1352 static void free_accept_types( WCHAR
**accept_types
)
1354 WCHAR
*ptr
, **types
= accept_types
;
1357 while ((ptr
= *types
))
1362 heap_free( accept_types
);
1365 static WCHAR
**convert_accept_types( const char **accept_types
)
1368 const char **types
= accept_types
;
1370 BOOL invalid_pointer
= FALSE
;
1372 if (!types
) return NULL
;
1378 /* find out how many there are */
1379 if (*types
&& **types
)
1381 TRACE("accept type: %s\n", debugstr_a(*types
));
1387 WARN("invalid accept type pointer\n");
1388 invalid_pointer
= TRUE
;
1393 if (invalid_pointer
) return NULL
;
1394 if (!(typesW
= heap_alloc( sizeof(WCHAR
*) * (count
+ 1) ))) return NULL
;
1396 types
= accept_types
;
1399 if (*types
&& **types
) typesW
[count
++] = heap_strdupAtoW( *types
);
1402 typesW
[count
] = NULL
;
1406 /***********************************************************************
1407 * HttpOpenRequestA (WININET.@)
1409 * Open a HTTP request handle
1412 * HINTERNET a HTTP request handle on success
1416 HINTERNET WINAPI
HttpOpenRequestA(HINTERNET hHttpSession
,
1417 LPCSTR lpszVerb
, LPCSTR lpszObjectName
, LPCSTR lpszVersion
,
1418 LPCSTR lpszReferrer
, LPCSTR
*lpszAcceptTypes
,
1419 DWORD dwFlags
, DWORD_PTR dwContext
)
1421 LPWSTR szVerb
= NULL
, szObjectName
= NULL
;
1422 LPWSTR szVersion
= NULL
, szReferrer
= NULL
, *szAcceptTypes
= NULL
;
1423 HINTERNET rc
= FALSE
;
1425 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1426 debugstr_a(lpszVerb
), debugstr_a(lpszObjectName
),
1427 debugstr_a(lpszVersion
), debugstr_a(lpszReferrer
), lpszAcceptTypes
,
1428 dwFlags
, dwContext
);
1432 szVerb
= heap_strdupAtoW(lpszVerb
);
1439 szObjectName
= heap_strdupAtoW(lpszObjectName
);
1440 if ( !szObjectName
)
1446 szVersion
= heap_strdupAtoW(lpszVersion
);
1453 szReferrer
= heap_strdupAtoW(lpszReferrer
);
1458 szAcceptTypes
= convert_accept_types( lpszAcceptTypes
);
1459 rc
= HttpOpenRequestW(hHttpSession
, szVerb
, szObjectName
, szVersion
, szReferrer
,
1460 (const WCHAR
**)szAcceptTypes
, dwFlags
, dwContext
);
1463 free_accept_types(szAcceptTypes
);
1464 heap_free(szReferrer
);
1465 heap_free(szVersion
);
1466 heap_free(szObjectName
);
1471 /***********************************************************************
1474 static UINT
HTTP_EncodeBase64( LPCSTR bin
, unsigned int len
, LPWSTR base64
)
1477 static const CHAR HTTP_Base64Enc
[] =
1478 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1482 /* first 6 bits, all from bin[0] */
1483 base64
[n
++] = HTTP_Base64Enc
[(bin
[0] & 0xfc) >> 2];
1484 x
= (bin
[0] & 3) << 4;
1486 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1489 base64
[n
++] = HTTP_Base64Enc
[x
];
1494 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[1]&0xf0) >> 4 ) ];
1495 x
= ( bin
[1] & 0x0f ) << 2;
1497 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1500 base64
[n
++] = HTTP_Base64Enc
[x
];
1504 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[2]&0xc0 ) >> 6 ) ];
1506 /* last 6 bits, all from bin [2] */
1507 base64
[n
++] = HTTP_Base64Enc
[ bin
[2] & 0x3f ];
1515 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1516 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1517 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1518 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1519 static const signed char HTTP_Base64Dec
[256] =
1521 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1522 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1523 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1524 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1525 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1526 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1527 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1528 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1529 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1530 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1531 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1532 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1533 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1534 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1535 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1536 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1537 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1538 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1539 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1540 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1541 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1542 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1543 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1544 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1545 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1546 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1550 /***********************************************************************
1553 static UINT
HTTP_DecodeBase64( LPCWSTR base64
, LPSTR bin
)
1561 if (base64
[0] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1562 ((in
[0] = HTTP_Base64Dec
[base64
[0]]) == -1) ||
1563 base64
[1] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1564 ((in
[1] = HTTP_Base64Dec
[base64
[1]]) == -1))
1566 WARN("invalid base64: %s\n", debugstr_w(base64
));
1570 bin
[n
] = (unsigned char) (in
[0] << 2 | in
[1] >> 4);
1573 if ((base64
[2] == '=') && (base64
[3] == '='))
1575 if (base64
[2] > ARRAYSIZE(HTTP_Base64Dec
) ||
1576 ((in
[2] = HTTP_Base64Dec
[base64
[2]]) == -1))
1578 WARN("invalid base64: %s\n", debugstr_w(&base64
[2]));
1582 bin
[n
] = (unsigned char) (in
[1] << 4 | in
[2] >> 2);
1585 if (base64
[3] == '=')
1587 if (base64
[3] > ARRAYSIZE(HTTP_Base64Dec
) ||
1588 ((in
[3] = HTTP_Base64Dec
[base64
[3]]) == -1))
1590 WARN("invalid base64: %s\n", debugstr_w(&base64
[3]));
1594 bin
[n
] = (unsigned char) (((in
[2] << 6) & 0xc0) | in
[3]);
1603 /***********************************************************************
1604 * HTTP_InsertAuthorization
1606 * Insert or delete the authorization field in the request header.
1608 static BOOL
HTTP_InsertAuthorization( http_request_t
*request
, struct HttpAuthInfo
*pAuthInfo
, LPCWSTR header
)
1612 static const WCHAR wszSpace
[] = {' ',0};
1613 static const WCHAR wszBasic
[] = {'B','a','s','i','c',0};
1615 WCHAR
*authorization
= NULL
;
1617 if (pAuthInfo
->auth_data_len
)
1619 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1620 len
= strlenW(pAuthInfo
->scheme
)+1+((pAuthInfo
->auth_data_len
+2)*4)/3;
1621 authorization
= heap_alloc((len
+1)*sizeof(WCHAR
));
1625 strcpyW(authorization
, pAuthInfo
->scheme
);
1626 strcatW(authorization
, wszSpace
);
1627 HTTP_EncodeBase64(pAuthInfo
->auth_data
,
1628 pAuthInfo
->auth_data_len
,
1629 authorization
+strlenW(authorization
));
1631 /* clear the data as it isn't valid now that it has been sent to the
1632 * server, unless it's Basic authentication which doesn't do
1633 * connection tracking */
1634 if (strcmpiW(pAuthInfo
->scheme
, wszBasic
))
1636 heap_free(pAuthInfo
->auth_data
);
1637 pAuthInfo
->auth_data
= NULL
;
1638 pAuthInfo
->auth_data_len
= 0;
1642 TRACE("Inserting authorization: %s\n", debugstr_w(authorization
));
1644 HTTP_ProcessHeader(request
, header
, authorization
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
1645 heap_free(authorization
);
1650 static WCHAR
*HTTP_BuildProxyRequestUrl(http_request_t
*req
)
1652 static const WCHAR slash
[] = { '/',0 };
1653 static const WCHAR format
[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1654 static const WCHAR formatSSL
[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1655 http_session_t
*session
= req
->session
;
1656 WCHAR new_location
[INTERNET_MAX_URL_LENGTH
], *url
;
1659 size
= sizeof(new_location
);
1660 if (HTTP_HttpQueryInfoW(req
, HTTP_QUERY_LOCATION
, new_location
, &size
, NULL
) == ERROR_SUCCESS
)
1662 URL_COMPONENTSW UrlComponents
;
1664 if (!(url
= heap_alloc(size
+ sizeof(WCHAR
)))) return NULL
;
1665 strcpyW( url
, new_location
);
1667 ZeroMemory(&UrlComponents
,sizeof(URL_COMPONENTSW
));
1668 if(InternetCrackUrlW(url
, 0, 0, &UrlComponents
)) goto done
;
1672 size
= 16; /* "https://" + sizeof(port#) + ":/\0" */
1673 size
+= strlenW( session
->hostName
) + strlenW( req
->path
);
1675 if (!(url
= heap_alloc(size
* sizeof(WCHAR
)))) return NULL
;
1677 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1678 sprintfW( url
, formatSSL
, session
->hostName
, session
->hostPort
);
1680 sprintfW( url
, format
, session
->hostName
, session
->hostPort
);
1681 if (req
->path
[0] != '/') strcatW( url
, slash
);
1682 strcatW( url
, req
->path
);
1685 TRACE("url=%s\n", debugstr_w(url
));
1689 /***********************************************************************
1690 * HTTP_DealWithProxy
1692 static BOOL
HTTP_DealWithProxy(appinfo_t
*hIC
, http_session_t
*session
, http_request_t
*request
)
1694 WCHAR buf
[INTERNET_MAX_HOST_NAME_LENGTH
];
1695 WCHAR protoProxy
[INTERNET_MAX_URL_LENGTH
];
1696 DWORD protoProxyLen
= INTERNET_MAX_URL_LENGTH
;
1697 WCHAR proxy
[INTERNET_MAX_URL_LENGTH
];
1698 static WCHAR szNul
[] = { 0 };
1699 URL_COMPONENTSW UrlComponents
;
1700 static const WCHAR protoHttp
[] = { 'h','t','t','p',0 };
1701 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/',0 };
1702 static const WCHAR szFormat
[] = { 'h','t','t','p',':','/','/','%','s',0 };
1704 memset( &UrlComponents
, 0, sizeof UrlComponents
);
1705 UrlComponents
.dwStructSize
= sizeof UrlComponents
;
1706 UrlComponents
.lpszHostName
= buf
;
1707 UrlComponents
.dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
1709 if (!INTERNET_FindProxyForProtocol(hIC
->proxy
, protoHttp
, protoProxy
, &protoProxyLen
))
1711 if( CSTR_EQUAL
!= CompareStringW(LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
1712 protoProxy
,strlenW(szHttp
),szHttp
,strlenW(szHttp
)) )
1713 sprintfW(proxy
, szFormat
, protoProxy
);
1715 strcpyW(proxy
, protoProxy
);
1716 if( !InternetCrackUrlW(proxy
, 0, 0, &UrlComponents
) )
1718 if( UrlComponents
.dwHostNameLength
== 0 )
1721 if( !request
->path
)
1722 request
->path
= szNul
;
1724 if(UrlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
1725 UrlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
1727 heap_free(session
->serverName
);
1728 session
->serverName
= heap_strdupW(UrlComponents
.lpszHostName
);
1729 session
->serverPort
= UrlComponents
.nPort
;
1731 TRACE("proxy server=%s port=%d\n", debugstr_w(session
->serverName
), session
->serverPort
);
1735 static DWORD
HTTP_ResolveName(http_request_t
*request
, server_t
*server
)
1740 if(server
->addr_len
)
1741 return ERROR_SUCCESS
;
1743 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1744 INTERNET_STATUS_RESOLVING_NAME
,
1746 (strlenW(server
->name
)+1) * sizeof(WCHAR
));
1748 addr_len
= sizeof(server
->addr
);
1749 if (!GetAddress(server
->name
, server
->port
, (struct sockaddr
*)&server
->addr
, &addr_len
))
1750 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1752 switch(server
->addr
.ss_family
) {
1754 addr
= &((struct sockaddr_in
*)&server
->addr
)->sin_addr
;
1757 addr
= &((struct sockaddr_in6
*)&server
->addr
)->sin6_addr
;
1760 WARN("unsupported family %d\n", server
->addr
.ss_family
);
1761 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1764 server
->addr_len
= addr_len
;
1765 inet_ntop(server
->addr
.ss_family
, addr
, server
->addr_str
, sizeof(server
->addr_str
));
1766 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1767 INTERNET_STATUS_NAME_RESOLVED
,
1768 server
->addr_str
, strlen(server
->addr_str
)+1);
1770 TRACE("resolved %s to %s\n", debugstr_w(server
->name
), server
->addr_str
);
1771 return ERROR_SUCCESS
;
1774 static BOOL
HTTP_GetRequestURL(http_request_t
*req
, LPWSTR buf
)
1776 static const WCHAR http
[] = { 'h','t','t','p',':','/','/',0 };
1777 static const WCHAR https
[] = { 'h','t','t','p','s',':','/','/',0 };
1778 static const WCHAR slash
[] = { '/',0 };
1779 LPHTTPHEADERW host_header
;
1782 host_header
= HTTP_GetHeader(req
, hostW
);
1786 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1790 strcpyW(buf
, scheme
);
1791 strcatW(buf
, host_header
->lpszValue
);
1792 if (req
->path
[0] != '/')
1793 strcatW(buf
, slash
);
1794 strcatW(buf
, req
->path
);
1799 /***********************************************************************
1800 * HTTPREQ_Destroy (internal)
1802 * Deallocate request handle
1805 static void HTTPREQ_Destroy(object_header_t
*hdr
)
1807 http_request_t
*request
= (http_request_t
*) hdr
;
1812 if(request
->hCacheFile
) {
1813 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1815 CloseHandle(request
->hCacheFile
);
1817 if(HTTP_GetRequestURL(request
, url
)) {
1820 headersLen
= request
->rawHeaders
? strlenW(request
->rawHeaders
) : 0;
1821 CommitUrlCacheEntryW(url
, request
->cacheFile
, request
->expires
,
1822 request
->last_modified
, NORMAL_CACHE_ENTRY
,
1823 request
->rawHeaders
, headersLen
, NULL
, 0);
1826 heap_free(request
->cacheFile
);
1828 request
->read_section
.DebugInfo
->Spare
[0] = 0;
1829 DeleteCriticalSection( &request
->read_section
);
1830 WININET_Release(&request
->session
->hdr
);
1832 destroy_authinfo(request
->authInfo
);
1833 destroy_authinfo(request
->proxyAuthInfo
);
1835 heap_free(request
->path
);
1836 heap_free(request
->verb
);
1837 heap_free(request
->rawHeaders
);
1838 heap_free(request
->version
);
1839 heap_free(request
->statusText
);
1841 for (i
= 0; i
< request
->nCustHeaders
; i
++)
1843 heap_free(request
->custHeaders
[i
].lpszField
);
1844 heap_free(request
->custHeaders
[i
].lpszValue
);
1846 destroy_data_stream(request
->data_stream
);
1847 heap_free(request
->custHeaders
);
1850 static void http_release_netconn(http_request_t
*req
, BOOL reuse
)
1852 TRACE("%p %p\n",req
, req
->netconn
);
1857 if(reuse
&& req
->netconn
->keep_alive
) {
1860 EnterCriticalSection(&connection_pool_cs
);
1862 list_add_head(&req
->netconn
->server
->conn_pool
, &req
->netconn
->pool_entry
);
1863 req
->netconn
->keep_until
= GetTickCount64() + COLLECT_TIME
;
1864 req
->netconn
= NULL
;
1866 run_collector
= !collector_running
;
1867 collector_running
= TRUE
;
1869 LeaveCriticalSection(&connection_pool_cs
);
1872 HANDLE thread
= NULL
;
1875 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
, (const WCHAR
*)WININET_hModule
, &module
);
1877 thread
= CreateThread(NULL
, 0, collect_connections_proc
, NULL
, 0, NULL
);
1879 EnterCriticalSection(&connection_pool_cs
);
1880 collector_running
= FALSE
;
1881 LeaveCriticalSection(&connection_pool_cs
);
1884 FreeLibrary(module
);
1890 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
1891 INTERNET_STATUS_CLOSING_CONNECTION
, 0, 0);
1893 free_netconn(req
->netconn
);
1894 req
->netconn
= NULL
;
1896 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
1897 INTERNET_STATUS_CONNECTION_CLOSED
, 0, 0);
1900 static void drain_content(http_request_t
*req
)
1904 if (!req
->netconn
) return;
1906 if (req
->contentLength
== -1)
1908 else if(!strcmpW(req
->verb
, szHEAD
))
1911 try_reuse
= req
->data_stream
->vtbl
->drain_content(req
->data_stream
, req
);
1913 http_release_netconn(req
, try_reuse
);
1916 static BOOL
HTTP_KeepAlive(http_request_t
*request
)
1918 WCHAR szVersion
[10];
1919 WCHAR szConnectionResponse
[20];
1920 DWORD dwBufferSize
= sizeof(szVersion
);
1921 BOOL keepalive
= FALSE
;
1923 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1924 * the connection is keep-alive by default */
1925 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_VERSION
, szVersion
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1926 && !strcmpiW(szVersion
, g_szHttp1_1
))
1931 dwBufferSize
= sizeof(szConnectionResponse
);
1932 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_PROXY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1933 || HTTP_HttpQueryInfoW(request
, HTTP_QUERY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
1935 keepalive
= !strcmpiW(szConnectionResponse
, szKeepAlive
);
1941 static void HTTPREQ_CloseConnection(object_header_t
*hdr
)
1943 http_request_t
*req
= (http_request_t
*)hdr
;
1948 static DWORD
HTTPREQ_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
1950 http_request_t
*req
= (http_request_t
*)hdr
;
1953 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO
:
1955 http_session_t
*session
= req
->session
;
1956 INTERNET_DIAGNOSTIC_SOCKET_INFO
*info
= buffer
;
1958 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1960 if (*size
< sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
))
1961 return ERROR_INSUFFICIENT_BUFFER
;
1962 *size
= sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
);
1963 /* FIXME: can't get a SOCKET from our connection since we don't use
1967 /* FIXME: get source port from req->netConnection */
1968 info
->SourcePort
= 0;
1969 info
->DestPort
= session
->hostPort
;
1971 if (HTTP_KeepAlive(req
))
1972 info
->Flags
|= IDSI_FLAG_KEEP_ALIVE
;
1973 if (session
->appInfo
->proxy
&& session
->appInfo
->proxy
[0] != 0)
1974 info
->Flags
|= IDSI_FLAG_PROXY
;
1975 if (req
->netconn
->useSSL
)
1976 info
->Flags
|= IDSI_FLAG_SECURE
;
1978 return ERROR_SUCCESS
;
1981 case INTERNET_OPTION_SECURITY_FLAGS
:
1985 if (*size
< sizeof(ULONG
))
1986 return ERROR_INSUFFICIENT_BUFFER
;
1988 *size
= sizeof(DWORD
);
1990 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1991 flags
|= SECURITY_FLAG_SECURE
;
1992 flags
|= req
->security_flags
;
1994 int bits
= NETCON_GetCipherStrength(req
->netconn
);
1996 flags
|= SECURITY_FLAG_STRENGTH_STRONG
;
1997 else if (bits
>= 56)
1998 flags
|= SECURITY_FLAG_STRENGTH_MEDIUM
;
2000 flags
|= SECURITY_FLAG_STRENGTH_WEAK
;
2002 *(DWORD
*)buffer
= flags
;
2003 return ERROR_SUCCESS
;
2006 case INTERNET_OPTION_HANDLE_TYPE
:
2007 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2009 if (*size
< sizeof(ULONG
))
2010 return ERROR_INSUFFICIENT_BUFFER
;
2012 *size
= sizeof(DWORD
);
2013 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_HTTP_REQUEST
;
2014 return ERROR_SUCCESS
;
2016 case INTERNET_OPTION_URL
: {
2017 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2022 static const WCHAR httpW
[] = {'h','t','t','p',':','/','/',0};
2024 TRACE("INTERNET_OPTION_URL\n");
2026 host
= HTTP_GetHeader(req
, hostW
);
2027 strcpyW(url
, httpW
);
2028 strcatW(url
, host
->lpszValue
);
2029 if (NULL
!= (pch
= strchrW(url
+ strlenW(httpW
), ':')))
2031 strcatW(url
, req
->path
);
2033 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url
));
2036 len
= (strlenW(url
)+1) * sizeof(WCHAR
);
2038 return ERROR_INSUFFICIENT_BUFFER
;
2041 strcpyW(buffer
, url
);
2042 return ERROR_SUCCESS
;
2044 len
= WideCharToMultiByte(CP_ACP
, 0, url
, -1, buffer
, *size
, NULL
, NULL
);
2046 return ERROR_INSUFFICIENT_BUFFER
;
2049 return ERROR_SUCCESS
;
2053 case INTERNET_OPTION_CACHE_TIMESTAMPS
: {
2054 INTERNET_CACHE_ENTRY_INFOW
*info
;
2055 INTERNET_CACHE_TIMESTAMPS
*ts
= buffer
;
2056 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2057 DWORD nbytes
, error
;
2060 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2062 if (*size
< sizeof(*ts
))
2064 *size
= sizeof(*ts
);
2065 return ERROR_INSUFFICIENT_BUFFER
;
2068 HTTP_GetRequestURL(req
, url
);
2069 ret
= GetUrlCacheEntryInfoW(url
, NULL
, &nbytes
);
2070 error
= GetLastError();
2071 if (!ret
&& error
== ERROR_INSUFFICIENT_BUFFER
)
2073 if (!(info
= heap_alloc(nbytes
)))
2074 return ERROR_OUTOFMEMORY
;
2076 GetUrlCacheEntryInfoW(url
, info
, &nbytes
);
2078 ts
->ftExpires
= info
->ExpireTime
;
2079 ts
->ftLastModified
= info
->LastModifiedTime
;
2082 *size
= sizeof(*ts
);
2083 return ERROR_SUCCESS
;
2088 case INTERNET_OPTION_DATAFILE_NAME
: {
2091 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2093 if(!req
->cacheFile
) {
2095 return ERROR_INTERNET_ITEM_NOT_FOUND
;
2099 req_size
= (lstrlenW(req
->cacheFile
)+1) * sizeof(WCHAR
);
2100 if(*size
< req_size
)
2101 return ERROR_INSUFFICIENT_BUFFER
;
2104 memcpy(buffer
, req
->cacheFile
, *size
);
2105 return ERROR_SUCCESS
;
2107 req_size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
, -1, NULL
, 0, NULL
, NULL
);
2108 if (req_size
> *size
)
2109 return ERROR_INSUFFICIENT_BUFFER
;
2111 *size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
,
2112 -1, buffer
, *size
, NULL
, NULL
);
2113 return ERROR_SUCCESS
;
2117 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT
: {
2118 PCCERT_CONTEXT context
;
2120 if(*size
< sizeof(INTERNET_CERTIFICATE_INFOA
)) {
2121 *size
= sizeof(INTERNET_CERTIFICATE_INFOA
);
2122 return ERROR_INSUFFICIENT_BUFFER
;
2125 context
= (PCCERT_CONTEXT
)NETCON_GetCert(req
->netconn
);
2127 INTERNET_CERTIFICATE_INFOA
*info
= (INTERNET_CERTIFICATE_INFOA
*)buffer
;
2130 memset(info
, 0, sizeof(INTERNET_CERTIFICATE_INFOW
));
2131 info
->ftExpiry
= context
->pCertInfo
->NotAfter
;
2132 info
->ftStart
= context
->pCertInfo
->NotBefore
;
2133 len
= CertNameToStrA(context
->dwCertEncodingType
,
2134 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
2135 info
->lpszSubjectInfo
= LocalAlloc(0, len
);
2136 if(info
->lpszSubjectInfo
)
2137 CertNameToStrA(context
->dwCertEncodingType
,
2138 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
,
2139 info
->lpszSubjectInfo
, len
);
2140 len
= CertNameToStrA(context
->dwCertEncodingType
,
2141 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
2142 info
->lpszIssuerInfo
= LocalAlloc(0, len
);
2143 if(info
->lpszIssuerInfo
)
2144 CertNameToStrA(context
->dwCertEncodingType
,
2145 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
,
2146 info
->lpszIssuerInfo
, len
);
2147 info
->dwKeySize
= NETCON_GetCipherStrength(req
->netconn
);
2148 CertFreeCertificateContext(context
);
2149 return ERROR_SUCCESS
;
2154 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
2157 static DWORD
HTTPREQ_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
2159 http_request_t
*req
= (http_request_t
*)hdr
;
2162 case INTERNET_OPTION_SECURITY_FLAGS
:
2166 if (!buffer
|| size
!= sizeof(DWORD
))
2167 return ERROR_INVALID_PARAMETER
;
2168 flags
= *(DWORD
*)buffer
;
2169 TRACE("%08x\n", flags
);
2170 req
->security_flags
= flags
;
2172 req
->netconn
->security_flags
= flags
;
2173 return ERROR_SUCCESS
;
2175 case INTERNET_OPTION_SEND_TIMEOUT
:
2176 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
2177 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
2179 if (size
!= sizeof(DWORD
))
2180 return ERROR_INVALID_PARAMETER
;
2183 FIXME("unsupported without active connection\n");
2184 return ERROR_SUCCESS
;
2187 return NETCON_set_timeout(req
->netconn
, option
== INTERNET_OPTION_SEND_TIMEOUT
,
2190 case INTERNET_OPTION_USERNAME
:
2191 heap_free(req
->session
->userName
);
2192 if (!(req
->session
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2193 return ERROR_SUCCESS
;
2195 case INTERNET_OPTION_PASSWORD
:
2196 heap_free(req
->session
->password
);
2197 if (!(req
->session
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2198 return ERROR_SUCCESS
;
2199 case INTERNET_OPTION_HTTP_DECODING
:
2200 if(size
!= sizeof(BOOL
))
2201 return ERROR_INVALID_PARAMETER
;
2202 req
->decoding
= *(BOOL
*)buffer
;
2203 return ERROR_SUCCESS
;
2206 return ERROR_INTERNET_INVALID_OPTION
;
2209 /* read some more data into the read buffer (the read section must be held) */
2210 static DWORD
read_more_data( http_request_t
*req
, int maxlen
)
2217 /* move existing data to the start of the buffer */
2219 memmove( req
->read_buf
, req
->read_buf
+ req
->read_pos
, req
->read_size
);
2223 if (maxlen
== -1) maxlen
= sizeof(req
->read_buf
);
2225 res
= NETCON_recv( req
->netconn
, req
->read_buf
+ req
->read_size
,
2226 maxlen
- req
->read_size
, 0, &len
);
2227 if(res
== ERROR_SUCCESS
)
2228 req
->read_size
+= len
;
2233 /* remove some amount of data from the read buffer (the read section must be held) */
2234 static void remove_data( http_request_t
*req
, int count
)
2236 if (!(req
->read_size
-= count
)) req
->read_pos
= 0;
2237 else req
->read_pos
+= count
;
2240 static BOOL
read_line( http_request_t
*req
, LPSTR buffer
, DWORD
*len
)
2242 int count
, bytes_read
, pos
= 0;
2245 EnterCriticalSection( &req
->read_section
);
2248 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
2252 count
= eol
- (req
->read_buf
+ req
->read_pos
);
2253 bytes_read
= count
+ 1;
2255 else count
= bytes_read
= req
->read_size
;
2257 count
= min( count
, *len
- pos
);
2258 memcpy( buffer
+ pos
, req
->read_buf
+ req
->read_pos
, count
);
2260 remove_data( req
, bytes_read
);
2263 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
|| !req
->read_size
)
2266 TRACE( "returning empty string %u\n", res
);
2267 LeaveCriticalSection( &req
->read_section
);
2268 INTERNET_SetLastError(res
);
2272 LeaveCriticalSection( &req
->read_section
);
2276 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
2279 buffer
[*len
- 1] = 0;
2280 TRACE( "returning %s\n", debugstr_a(buffer
));
2284 /* check if we have reached the end of the data to read (the read section must be held) */
2285 static BOOL
end_of_read_data( http_request_t
*req
)
2287 return !req
->read_size
&& req
->data_stream
->vtbl
->end_of_data(req
->data_stream
, req
);
2290 /* fetch some more data into the read buffer (the read section must be held) */
2291 static DWORD
refill_read_buffer(http_request_t
*req
, read_mode_t read_mode
, DWORD
*read_bytes
)
2295 if(req
->read_size
== sizeof(req
->read_buf
))
2296 return ERROR_SUCCESS
;
2300 memmove(req
->read_buf
, req
->read_buf
+req
->read_pos
, req
->read_size
);
2304 res
= req
->data_stream
->vtbl
->read(req
->data_stream
, req
, req
->read_buf
+req
->read_size
,
2305 sizeof(req
->read_buf
)-req
->read_size
, &read
, read_mode
);
2306 req
->read_size
+= read
;
2308 TRACE("read %u bytes, read_size %u\n", read
, req
->read_size
);
2314 /* return the size of data available to be read immediately (the read section must be held) */
2315 static DWORD
get_avail_data( http_request_t
*req
)
2317 return req
->read_size
+ req
->data_stream
->vtbl
->get_avail_data(req
->data_stream
, req
);
2320 static DWORD
netconn_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
2322 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2326 NETCON_query_data_available(req
->netconn
, &avail
);
2327 return netconn_stream
->content_length
== ~0u
2329 : min(avail
, netconn_stream
->content_length
-netconn_stream
->content_read
);
2332 static BOOL
netconn_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
2334 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2335 return netconn_stream
->content_read
== netconn_stream
->content_length
|| !req
->netconn
;
2338 static DWORD
netconn_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
2339 DWORD
*read
, read_mode_t read_mode
)
2341 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2344 size
= min(size
, netconn_stream
->content_length
-netconn_stream
->content_read
);
2346 if(read_mode
== READMODE_NOBLOCK
)
2347 size
= min(size
, netconn_get_avail_data(stream
, req
));
2349 if(size
&& req
->netconn
) {
2350 if(NETCON_recv(req
->netconn
, buf
, size
, read_mode
== READMODE_SYNC
? MSG_WAITALL
: 0, &len
) != ERROR_SUCCESS
)
2353 netconn_stream
->content_length
= netconn_stream
->content_read
;
2356 netconn_stream
->content_read
+= *read
= len
;
2357 TRACE("read %u bytes\n", len
);
2358 return ERROR_SUCCESS
;
2361 static BOOL
netconn_drain_content(data_stream_t
*stream
, http_request_t
*req
)
2363 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2368 if(netconn_end_of_data(stream
, req
))
2372 avail
= netconn_get_avail_data(stream
, req
);
2376 if(NETCON_recv(req
->netconn
, buf
, min(avail
, sizeof(buf
)), 0, &len
) != ERROR_SUCCESS
)
2379 netconn_stream
->content_read
+= len
;
2380 }while(netconn_stream
->content_read
< netconn_stream
->content_length
);
2385 static void netconn_destroy(data_stream_t
*stream
)
2389 static const data_stream_vtbl_t netconn_stream_vtbl
= {
2390 netconn_get_avail_data
,
2391 netconn_end_of_data
,
2393 netconn_drain_content
,
2397 /* read some more data into the read buffer (the read section must be held) */
2398 static DWORD
read_more_chunked_data(chunked_stream_t
*stream
, http_request_t
*req
, int maxlen
)
2403 if (stream
->buf_pos
)
2405 /* move existing data to the start of the buffer */
2406 if(stream
->buf_size
)
2407 memmove(stream
->buf
, stream
->buf
+ stream
->buf_pos
, stream
->buf_size
);
2408 stream
->buf_pos
= 0;
2411 if (maxlen
== -1) maxlen
= sizeof(stream
->buf
);
2413 res
= NETCON_recv( req
->netconn
, stream
->buf
+ stream
->buf_size
,
2414 maxlen
- stream
->buf_size
, 0, &len
);
2415 if(res
== ERROR_SUCCESS
)
2416 stream
->buf_size
+= len
;
2421 /* remove some amount of data from the read buffer (the read section must be held) */
2422 static void remove_chunked_data(chunked_stream_t
*stream
, int count
)
2424 if (!(stream
->buf_size
-= count
)) stream
->buf_pos
= 0;
2425 else stream
->buf_pos
+= count
;
2428 /* discard data contents until we reach end of line (the read section must be held) */
2429 static DWORD
discard_chunked_eol(chunked_stream_t
*stream
, http_request_t
*req
)
2435 BYTE
*eol
= memchr(stream
->buf
+ stream
->buf_pos
, '\n', stream
->buf_size
);
2438 remove_chunked_data(stream
, (eol
+ 1) - (stream
->buf
+ stream
->buf_pos
));
2441 stream
->buf_pos
= stream
->buf_size
= 0; /* discard everything */
2442 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2443 } while (stream
->buf_size
);
2444 return ERROR_SUCCESS
;
2447 /* read the size of the next chunk (the read section must be held) */
2448 static DWORD
start_next_chunk(chunked_stream_t
*stream
, http_request_t
*req
)
2451 DWORD chunk_size
= 0, res
;
2453 if(stream
->chunk_size
!= ~0u && (res
= discard_chunked_eol(stream
, req
)) != ERROR_SUCCESS
)
2458 while (stream
->buf_size
)
2460 char ch
= stream
->buf
[stream
->buf_pos
];
2461 if (ch
>= '0' && ch
<= '9') chunk_size
= chunk_size
* 16 + ch
- '0';
2462 else if (ch
>= 'a' && ch
<= 'f') chunk_size
= chunk_size
* 16 + ch
- 'a' + 10;
2463 else if (ch
>= 'A' && ch
<= 'F') chunk_size
= chunk_size
* 16 + ch
- 'A' + 10;
2464 else if (ch
== ';' || ch
== '\r' || ch
== '\n')
2466 TRACE( "reading %u byte chunk\n", chunk_size
);
2467 stream
->chunk_size
= chunk_size
;
2468 req
->contentLength
+= chunk_size
;
2469 return discard_chunked_eol(stream
, req
);
2471 remove_chunked_data(stream
, 1);
2473 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2474 if (!stream
->buf_size
)
2476 stream
->chunk_size
= 0;
2477 return ERROR_SUCCESS
;
2482 static DWORD
chunked_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
2484 /* Allow reading only from read buffer */
2488 static BOOL
chunked_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
2490 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2491 return !chunked_stream
->chunk_size
;
2494 static DWORD
chunked_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
2495 DWORD
*read
, read_mode_t read_mode
)
2497 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2498 DWORD read_bytes
= 0, ret_read
= 0, res
= ERROR_SUCCESS
;
2500 if(chunked_stream
->chunk_size
== ~0u) {
2501 res
= start_next_chunk(chunked_stream
, req
);
2502 if(res
!= ERROR_SUCCESS
)
2506 while(size
&& chunked_stream
->chunk_size
) {
2507 if(chunked_stream
->buf_size
) {
2508 read_bytes
= min(size
, min(chunked_stream
->buf_size
, chunked_stream
->chunk_size
));
2510 /* this could block */
2511 if(read_mode
== READMODE_NOBLOCK
&& read_bytes
== chunked_stream
->chunk_size
)
2514 memcpy(buf
+ret_read
, chunked_stream
->buf
+chunked_stream
->buf_pos
, read_bytes
);
2515 remove_chunked_data(chunked_stream
, read_bytes
);
2517 read_bytes
= min(size
, chunked_stream
->chunk_size
);
2519 if(read_mode
== READMODE_NOBLOCK
) {
2522 if(!NETCON_query_data_available(req
->netconn
, &avail
) || !avail
)
2524 if(read_bytes
> avail
)
2527 /* this could block */
2528 if(read_bytes
== chunked_stream
->chunk_size
)
2532 res
= NETCON_recv(req
->netconn
, (char *)buf
+ret_read
, read_bytes
, 0, (int*)&read_bytes
);
2533 if(res
!= ERROR_SUCCESS
)
2537 chunked_stream
->chunk_size
-= read_bytes
;
2539 ret_read
+= read_bytes
;
2540 if(!chunked_stream
->chunk_size
) {
2541 assert(read_mode
!= READMODE_NOBLOCK
);
2542 res
= start_next_chunk(chunked_stream
, req
);
2543 if(res
!= ERROR_SUCCESS
)
2547 if(read_mode
== READMODE_ASYNC
)
2548 read_mode
= READMODE_NOBLOCK
;
2551 TRACE("read %u bytes\n", ret_read
);
2556 static BOOL
chunked_drain_content(data_stream_t
*stream
, http_request_t
*req
)
2558 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2560 /* FIXME: we can do better */
2561 return !chunked_stream
->chunk_size
;
2564 static void chunked_destroy(data_stream_t
*stream
)
2566 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2567 heap_free(chunked_stream
);
2570 static const data_stream_vtbl_t chunked_stream_vtbl
= {
2571 chunked_get_avail_data
,
2572 chunked_end_of_data
,
2574 chunked_drain_content
,
2578 /* set the request content length based on the headers */
2579 static DWORD
set_content_length(http_request_t
*request
, DWORD status_code
)
2581 static const WCHAR szChunked
[] = {'c','h','u','n','k','e','d',0};
2585 if(status_code
== HTTP_STATUS_NO_CONTENT
) {
2586 request
->contentLength
= request
->netconn_stream
.content_length
= 0;
2587 return ERROR_SUCCESS
;
2590 size
= sizeof(request
->contentLength
);
2591 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_CONTENT_LENGTH
,
2592 &request
->contentLength
, &size
, NULL
) != ERROR_SUCCESS
)
2593 request
->contentLength
= ~0u;
2594 request
->netconn_stream
.content_length
= request
->contentLength
;
2595 request
->netconn_stream
.content_read
= request
->read_size
;
2597 size
= sizeof(encoding
);
2598 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_TRANSFER_ENCODING
, encoding
, &size
, NULL
) == ERROR_SUCCESS
&&
2599 !strcmpiW(encoding
, szChunked
))
2601 chunked_stream_t
*chunked_stream
;
2603 chunked_stream
= heap_alloc(sizeof(*chunked_stream
));
2605 return ERROR_OUTOFMEMORY
;
2607 chunked_stream
->data_stream
.vtbl
= &chunked_stream_vtbl
;
2608 chunked_stream
->buf_size
= chunked_stream
->buf_pos
= 0;
2609 chunked_stream
->chunk_size
= ~0u;
2611 if(request
->read_size
) {
2612 memcpy(chunked_stream
->buf
, request
->read_buf
+request
->read_pos
, request
->read_size
);
2613 chunked_stream
->buf_size
= request
->read_size
;
2614 request
->read_size
= request
->read_pos
= 0;
2617 request
->data_stream
= &chunked_stream
->data_stream
;
2618 request
->contentLength
= ~0u;
2619 request
->read_chunked
= TRUE
;
2622 if(request
->decoding
) {
2625 static const WCHAR gzipW
[] = {'g','z','i','p',0};
2627 encoding_idx
= HTTP_GetCustomHeaderIndex(request
, szContent_Encoding
, 0, FALSE
);
2628 if(encoding_idx
!= -1 && !strcmpiW(request
->custHeaders
[encoding_idx
].lpszValue
, gzipW
))
2629 return init_gzip_stream(request
);
2632 return ERROR_SUCCESS
;
2635 static void send_request_complete(http_request_t
*req
, DWORD_PTR result
, DWORD error
)
2637 INTERNET_ASYNC_RESULT iar
;
2639 iar
.dwResult
= result
;
2640 iar
.dwError
= error
;
2642 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2643 sizeof(INTERNET_ASYNC_RESULT
));
2646 static void HTTP_ReceiveRequestData(http_request_t
*req
, BOOL first_notif
)
2648 DWORD res
, read
= 0, avail
= 0;
2653 EnterCriticalSection( &req
->read_section
);
2655 mode
= first_notif
&& req
->read_size
? READMODE_NOBLOCK
: READMODE_ASYNC
;
2656 res
= refill_read_buffer(req
, mode
, &read
);
2657 if(res
== ERROR_SUCCESS
&& !first_notif
)
2658 avail
= get_avail_data(req
);
2660 LeaveCriticalSection( &req
->read_section
);
2662 if(res
!= ERROR_SUCCESS
|| (mode
!= READMODE_NOBLOCK
&& !read
)) {
2663 WARN("res %u read %u, closing connection\n", res
, read
);
2664 http_release_netconn(req
, FALSE
);
2667 if(res
== ERROR_SUCCESS
)
2668 send_request_complete(req
, req
->session
->hdr
.dwInternalFlags
& INET_OPENURL
? (DWORD_PTR
)req
->hdr
.hInternet
: 1, avail
);
2670 send_request_complete(req
, 0, res
);
2673 /* read data from the http connection (the read section must be held) */
2674 static DWORD
HTTPREQ_Read(http_request_t
*req
, void *buffer
, DWORD size
, DWORD
*read
, BOOL sync
)
2676 DWORD current_read
= 0, ret_read
= 0;
2677 read_mode_t read_mode
;
2678 DWORD res
= ERROR_SUCCESS
;
2680 read_mode
= req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
? READMODE_ASYNC
: READMODE_SYNC
;
2682 EnterCriticalSection( &req
->read_section
);
2684 if(req
->read_size
) {
2685 ret_read
= min(size
, req
->read_size
);
2686 memcpy(buffer
, req
->read_buf
+req
->read_pos
, ret_read
);
2687 req
->read_size
-= ret_read
;
2688 req
->read_pos
+= ret_read
;
2689 if(read_mode
== READMODE_ASYNC
)
2690 read_mode
= READMODE_NOBLOCK
;
2693 if(ret_read
< size
) {
2694 res
= req
->data_stream
->vtbl
->read(req
->data_stream
, req
, (BYTE
*)buffer
+ret_read
, size
-ret_read
, ¤t_read
, read_mode
);
2695 ret_read
+= current_read
;
2698 LeaveCriticalSection( &req
->read_section
);
2701 TRACE( "retrieved %u bytes (%u)\n", ret_read
, req
->contentLength
);
2703 if(req
->hCacheFile
&& res
== ERROR_SUCCESS
&& ret_read
) {
2707 res
= WriteFile(req
->hCacheFile
, buffer
, ret_read
, &written
, NULL
);
2709 WARN("WriteFile failed: %u\n", GetLastError());
2712 if(size
&& !ret_read
)
2713 http_release_netconn(req
, res
== ERROR_SUCCESS
);
2719 static DWORD
HTTPREQ_ReadFile(object_header_t
*hdr
, void *buffer
, DWORD size
, DWORD
*read
)
2721 http_request_t
*req
= (http_request_t
*)hdr
;
2724 EnterCriticalSection( &req
->read_section
);
2725 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2726 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2728 res
= HTTPREQ_Read(req
, buffer
, size
, read
, TRUE
);
2729 if(res
== ERROR_SUCCESS
)
2731 LeaveCriticalSection( &req
->read_section
);
2736 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST
*workRequest
)
2738 struct WORKREQ_INTERNETREADFILEEXA
const *data
= &workRequest
->u
.InternetReadFileExA
;
2739 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2742 TRACE("INTERNETREADFILEEXA %p\n", workRequest
->hdr
);
2744 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2745 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2747 send_request_complete(req
, res
== ERROR_SUCCESS
, res
);
2750 static DWORD
HTTPREQ_ReadFileExA(object_header_t
*hdr
, INTERNET_BUFFERSA
*buffers
,
2751 DWORD flags
, DWORD_PTR context
)
2753 http_request_t
*req
= (http_request_t
*)hdr
;
2754 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2756 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2757 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2759 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2760 return ERROR_INVALID_PARAMETER
;
2762 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2764 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2766 WORKREQUEST workRequest
;
2768 if (TryEnterCriticalSection( &req
->read_section
))
2770 if (get_avail_data(req
))
2772 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2773 &buffers
->dwBufferLength
, FALSE
);
2774 size
= buffers
->dwBufferLength
;
2775 LeaveCriticalSection( &req
->read_section
);
2778 LeaveCriticalSection( &req
->read_section
);
2781 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExAProc
;
2782 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2783 workRequest
.u
.InternetReadFileExA
.lpBuffersOut
= buffers
;
2785 INTERNET_AsyncCall(&workRequest
);
2787 return ERROR_IO_PENDING
;
2791 size
= buffers
->dwBufferLength
;
2793 EnterCriticalSection( &req
->read_section
);
2794 if(hdr
->dwError
== ERROR_SUCCESS
)
2795 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2796 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2797 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2800 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2801 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2802 if(res
!= ERROR_SUCCESS
)
2805 read
+= buffers
->dwBufferLength
;
2806 if(read
== size
|| end_of_read_data(req
))
2809 LeaveCriticalSection( &req
->read_section
);
2811 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2812 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2813 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2814 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2816 EnterCriticalSection( &req
->read_section
);
2819 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2820 hdr
->dwError
= ERROR_SUCCESS
;
2822 error
= hdr
->dwError
;
2824 LeaveCriticalSection( &req
->read_section
);
2825 size
= buffers
->dwBufferLength
;
2826 buffers
->dwBufferLength
= read
;
2829 if (res
== ERROR_SUCCESS
) {
2830 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2831 &size
, sizeof(size
));
2834 return res
==ERROR_SUCCESS
? error
: res
;
2837 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST
*workRequest
)
2839 struct WORKREQ_INTERNETREADFILEEXW
const *data
= &workRequest
->u
.InternetReadFileExW
;
2840 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2843 TRACE("INTERNETREADFILEEXW %p\n", workRequest
->hdr
);
2845 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2846 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2848 send_request_complete(req
, res
== ERROR_SUCCESS
, res
);
2851 static DWORD
HTTPREQ_ReadFileExW(object_header_t
*hdr
, INTERNET_BUFFERSW
*buffers
,
2852 DWORD flags
, DWORD_PTR context
)
2855 http_request_t
*req
= (http_request_t
*)hdr
;
2856 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2858 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2859 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2861 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2862 return ERROR_INVALID_PARAMETER
;
2864 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2866 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2868 WORKREQUEST workRequest
;
2870 if (TryEnterCriticalSection( &req
->read_section
))
2872 if (get_avail_data(req
))
2874 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2875 &buffers
->dwBufferLength
, FALSE
);
2876 size
= buffers
->dwBufferLength
;
2877 LeaveCriticalSection( &req
->read_section
);
2880 LeaveCriticalSection( &req
->read_section
);
2883 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExWProc
;
2884 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2885 workRequest
.u
.InternetReadFileExW
.lpBuffersOut
= buffers
;
2887 INTERNET_AsyncCall(&workRequest
);
2889 return ERROR_IO_PENDING
;
2893 size
= buffers
->dwBufferLength
;
2895 EnterCriticalSection( &req
->read_section
);
2896 if(hdr
->dwError
== ERROR_SUCCESS
)
2897 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2898 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2899 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2902 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2903 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2904 if(res
!= ERROR_SUCCESS
)
2907 read
+= buffers
->dwBufferLength
;
2908 if(read
== size
|| end_of_read_data(req
))
2911 LeaveCriticalSection( &req
->read_section
);
2913 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2914 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2915 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2916 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2918 EnterCriticalSection( &req
->read_section
);
2921 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2922 hdr
->dwError
= ERROR_SUCCESS
;
2924 error
= hdr
->dwError
;
2926 LeaveCriticalSection( &req
->read_section
);
2927 size
= buffers
->dwBufferLength
;
2928 buffers
->dwBufferLength
= read
;
2931 if (res
== ERROR_SUCCESS
) {
2932 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2933 &size
, sizeof(size
));
2936 return res
==ERROR_SUCCESS
? error
: res
;
2939 static DWORD
HTTPREQ_WriteFile(object_header_t
*hdr
, const void *buffer
, DWORD size
, DWORD
*written
)
2942 http_request_t
*request
= (http_request_t
*)hdr
;
2944 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
2947 res
= NETCON_send(request
->netconn
, buffer
, size
, 0, (LPINT
)written
);
2948 if (res
== ERROR_SUCCESS
)
2949 request
->bytesWritten
+= *written
;
2951 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_SENT
, written
, sizeof(DWORD
));
2955 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST
*workRequest
)
2957 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2959 HTTP_ReceiveRequestData(req
, FALSE
);
2962 static DWORD
HTTPREQ_QueryDataAvailable(object_header_t
*hdr
, DWORD
*available
, DWORD flags
, DWORD_PTR ctx
)
2964 http_request_t
*req
= (http_request_t
*)hdr
;
2966 TRACE("(%p %p %x %lx)\n", req
, available
, flags
, ctx
);
2968 if (req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
2970 WORKREQUEST workRequest
;
2972 /* never wait, if we can't enter the section we queue an async request right away */
2973 if (TryEnterCriticalSection( &req
->read_section
))
2975 refill_read_buffer(req
, READMODE_NOBLOCK
, NULL
);
2976 if ((*available
= get_avail_data( req
))) goto done
;
2977 if (end_of_read_data( req
)) goto done
;
2978 LeaveCriticalSection( &req
->read_section
);
2981 workRequest
.asyncproc
= HTTPREQ_AsyncQueryDataAvailableProc
;
2982 workRequest
.hdr
= WININET_AddRef( &req
->hdr
);
2984 INTERNET_AsyncCall(&workRequest
);
2986 return ERROR_IO_PENDING
;
2989 EnterCriticalSection( &req
->read_section
);
2991 if (!(*available
= get_avail_data( req
)) && !end_of_read_data( req
))
2993 refill_read_buffer( req
, READMODE_ASYNC
, NULL
);
2994 *available
= get_avail_data( req
);
2998 LeaveCriticalSection( &req
->read_section
);
3000 TRACE( "returning %u\n", *available
);
3001 return ERROR_SUCCESS
;
3004 static const object_vtbl_t HTTPREQVtbl
= {
3006 HTTPREQ_CloseConnection
,
3007 HTTPREQ_QueryOption
,
3010 HTTPREQ_ReadFileExA
,
3011 HTTPREQ_ReadFileExW
,
3013 HTTPREQ_QueryDataAvailable
,
3017 /***********************************************************************
3018 * HTTP_HttpOpenRequestW (internal)
3020 * Open a HTTP request handle
3023 * HINTERNET a HTTP request handle on success
3027 static DWORD
HTTP_HttpOpenRequestW(http_session_t
*session
,
3028 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
3029 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
3030 DWORD dwFlags
, DWORD_PTR dwContext
, HINTERNET
*ret
)
3032 appinfo_t
*hIC
= session
->appInfo
;
3033 http_request_t
*request
;
3034 DWORD len
, res
= ERROR_SUCCESS
;
3038 request
= alloc_object(&session
->hdr
, &HTTPREQVtbl
, sizeof(http_request_t
));
3040 return ERROR_OUTOFMEMORY
;
3042 request
->hdr
.htype
= WH_HHTTPREQ
;
3043 request
->hdr
.dwFlags
= dwFlags
;
3044 request
->hdr
.dwContext
= dwContext
;
3045 request
->contentLength
= ~0u;
3047 request
->netconn_stream
.data_stream
.vtbl
= &netconn_stream_vtbl
;
3048 request
->data_stream
= &request
->netconn_stream
.data_stream
;
3050 InitializeCriticalSection( &request
->read_section
);
3051 request
->read_section
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": http_request_t.read_section");
3053 WININET_AddRef( &session
->hdr
);
3054 request
->session
= session
;
3055 list_add_head( &session
->hdr
.children
, &request
->hdr
.entry
);
3057 if (dwFlags
& INTERNET_FLAG_IGNORE_CERT_CN_INVALID
)
3058 request
->security_flags
|= SECURITY_FLAG_IGNORE_CERT_CN_INVALID
;
3059 if (dwFlags
& INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
)
3060 request
->security_flags
|= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
3062 if (lpszObjectName
&& *lpszObjectName
) {
3066 rc
= UrlEscapeW(lpszObjectName
, NULL
, &len
, URL_ESCAPE_SPACES_ONLY
);
3067 if (rc
!= E_POINTER
)
3068 len
= strlenW(lpszObjectName
)+1;
3069 request
->path
= heap_alloc(len
*sizeof(WCHAR
));
3070 rc
= UrlEscapeW(lpszObjectName
, request
->path
, &len
,
3071 URL_ESCAPE_SPACES_ONLY
);
3074 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName
),rc
);
3075 strcpyW(request
->path
,lpszObjectName
);
3078 static const WCHAR slashW
[] = {'/',0};
3080 request
->path
= heap_strdupW(slashW
);
3083 if (lpszReferrer
&& *lpszReferrer
)
3084 HTTP_ProcessHeader(request
, HTTP_REFERER
, lpszReferrer
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3086 if (lpszAcceptTypes
)
3089 for (i
= 0; lpszAcceptTypes
[i
]; i
++)
3091 if (!*lpszAcceptTypes
[i
]) continue;
3092 HTTP_ProcessHeader(request
, HTTP_ACCEPT
, lpszAcceptTypes
[i
],
3093 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
|
3094 HTTP_ADDHDR_FLAG_REQ
|
3095 (i
== 0 ? HTTP_ADDHDR_FLAG_REPLACE
: 0));
3099 request
->verb
= heap_strdupW(lpszVerb
&& *lpszVerb
? lpszVerb
: szGET
);
3100 request
->version
= heap_strdupW(lpszVersion
? lpszVersion
: g_szHttp1_1
);
3102 if (session
->hostPort
!= INTERNET_INVALID_PORT_NUMBER
&&
3103 session
->hostPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
3104 session
->hostPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3108 static const WCHAR host_formatW
[] = {'%','s',':','%','u',0};
3110 host_name
= heap_alloc((strlenW(session
->hostName
) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR
));
3112 res
= ERROR_OUTOFMEMORY
;
3116 sprintfW(host_name
, host_formatW
, session
->hostName
, session
->hostPort
);
3117 HTTP_ProcessHeader(request
, hostW
, host_name
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3118 heap_free(host_name
);
3121 HTTP_ProcessHeader(request
, hostW
, session
->hostName
,
3122 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3124 if (session
->serverPort
== INTERNET_INVALID_PORT_NUMBER
)
3125 session
->serverPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
3126 INTERNET_DEFAULT_HTTPS_PORT
:
3127 INTERNET_DEFAULT_HTTP_PORT
);
3129 if (session
->hostPort
== INTERNET_INVALID_PORT_NUMBER
)
3130 session
->hostPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
3131 INTERNET_DEFAULT_HTTPS_PORT
:
3132 INTERNET_DEFAULT_HTTP_PORT
);
3134 if (hIC
->proxy
&& hIC
->proxy
[0])
3135 HTTP_DealWithProxy( hIC
, session
, request
);
3137 INTERNET_SendCallback(&session
->hdr
, dwContext
,
3138 INTERNET_STATUS_HANDLE_CREATED
, &request
->hdr
.hInternet
,
3142 TRACE("<-- %u (%p)\n", res
, request
);
3144 if(res
!= ERROR_SUCCESS
) {
3145 WININET_Release( &request
->hdr
);
3150 *ret
= request
->hdr
.hInternet
;
3151 return ERROR_SUCCESS
;
3154 /***********************************************************************
3155 * HttpOpenRequestW (WININET.@)
3157 * Open a HTTP request handle
3160 * HINTERNET a HTTP request handle on success
3164 HINTERNET WINAPI
HttpOpenRequestW(HINTERNET hHttpSession
,
3165 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
3166 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
3167 DWORD dwFlags
, DWORD_PTR dwContext
)
3169 http_session_t
*session
;
3170 HINTERNET handle
= NULL
;
3173 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
3174 debugstr_w(lpszVerb
), debugstr_w(lpszObjectName
),
3175 debugstr_w(lpszVersion
), debugstr_w(lpszReferrer
), lpszAcceptTypes
,
3176 dwFlags
, dwContext
);
3177 if(lpszAcceptTypes
!=NULL
)
3180 for(i
=0;lpszAcceptTypes
[i
]!=NULL
;i
++)
3181 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes
[i
]));
3184 session
= (http_session_t
*) get_handle_object( hHttpSession
);
3185 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
3187 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3192 * My tests seem to show that the windows version does not
3193 * become asynchronous until after this point. And anyhow
3194 * if this call was asynchronous then how would you get the
3195 * necessary HINTERNET pointer returned by this function.
3198 res
= HTTP_HttpOpenRequestW(session
, lpszVerb
, lpszObjectName
,
3199 lpszVersion
, lpszReferrer
, lpszAcceptTypes
,
3200 dwFlags
, dwContext
, &handle
);
3203 WININET_Release( &session
->hdr
);
3204 TRACE("returning %p\n", handle
);
3205 if(res
!= ERROR_SUCCESS
)
3210 static const LPCWSTR header_lookup
[] = {
3211 szMime_Version
, /* HTTP_QUERY_MIME_VERSION = 0 */
3212 szContent_Type
, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3213 szContent_Transfer_Encoding
,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3214 szContent_ID
, /* HTTP_QUERY_CONTENT_ID = 3 */
3215 NULL
, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3216 szContent_Length
, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3217 szContent_Language
, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3218 szAllow
, /* HTTP_QUERY_ALLOW = 7 */
3219 szPublic
, /* HTTP_QUERY_PUBLIC = 8 */
3220 szDate
, /* HTTP_QUERY_DATE = 9 */
3221 szExpires
, /* HTTP_QUERY_EXPIRES = 10 */
3222 szLast_Modified
, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3223 NULL
, /* HTTP_QUERY_MESSAGE_ID = 12 */
3224 szURI
, /* HTTP_QUERY_URI = 13 */
3225 szFrom
, /* HTTP_QUERY_DERIVED_FROM = 14 */
3226 NULL
, /* HTTP_QUERY_COST = 15 */
3227 NULL
, /* HTTP_QUERY_LINK = 16 */
3228 szPragma
, /* HTTP_QUERY_PRAGMA = 17 */
3229 NULL
, /* HTTP_QUERY_VERSION = 18 */
3230 szStatus
, /* HTTP_QUERY_STATUS_CODE = 19 */
3231 NULL
, /* HTTP_QUERY_STATUS_TEXT = 20 */
3232 NULL
, /* HTTP_QUERY_RAW_HEADERS = 21 */
3233 NULL
, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3234 szConnection
, /* HTTP_QUERY_CONNECTION = 23 */
3235 szAccept
, /* HTTP_QUERY_ACCEPT = 24 */
3236 szAccept_Charset
, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3237 szAccept_Encoding
, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3238 szAccept_Language
, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3239 szAuthorization
, /* HTTP_QUERY_AUTHORIZATION = 28 */
3240 szContent_Encoding
, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3241 NULL
, /* HTTP_QUERY_FORWARDED = 30 */
3242 NULL
, /* HTTP_QUERY_FROM = 31 */
3243 szIf_Modified_Since
, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3244 szLocation
, /* HTTP_QUERY_LOCATION = 33 */
3245 NULL
, /* HTTP_QUERY_ORIG_URI = 34 */
3246 szReferer
, /* HTTP_QUERY_REFERER = 35 */
3247 szRetry_After
, /* HTTP_QUERY_RETRY_AFTER = 36 */
3248 szServer
, /* HTTP_QUERY_SERVER = 37 */
3249 NULL
, /* HTTP_TITLE = 38 */
3250 szUser_Agent
, /* HTTP_QUERY_USER_AGENT = 39 */
3251 szWWW_Authenticate
, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3252 szProxy_Authenticate
, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3253 szAccept_Ranges
, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3254 szSet_Cookie
, /* HTTP_QUERY_SET_COOKIE = 43 */
3255 szCookie
, /* HTTP_QUERY_COOKIE = 44 */
3256 NULL
, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3257 NULL
, /* HTTP_QUERY_REFRESH = 46 */
3258 NULL
, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3259 szAge
, /* HTTP_QUERY_AGE = 48 */
3260 szCache_Control
, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3261 szContent_Base
, /* HTTP_QUERY_CONTENT_BASE = 50 */
3262 szContent_Location
, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3263 szContent_MD5
, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3264 szContent_Range
, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3265 szETag
, /* HTTP_QUERY_ETAG = 54 */
3266 hostW
, /* HTTP_QUERY_HOST = 55 */
3267 szIf_Match
, /* HTTP_QUERY_IF_MATCH = 56 */
3268 szIf_None_Match
, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3269 szIf_Range
, /* HTTP_QUERY_IF_RANGE = 58 */
3270 szIf_Unmodified_Since
, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3271 szMax_Forwards
, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3272 szProxy_Authorization
, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3273 szRange
, /* HTTP_QUERY_RANGE = 62 */
3274 szTransfer_Encoding
, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3275 szUpgrade
, /* HTTP_QUERY_UPGRADE = 64 */
3276 szVary
, /* HTTP_QUERY_VARY = 65 */
3277 szVia
, /* HTTP_QUERY_VIA = 66 */
3278 szWarning
, /* HTTP_QUERY_WARNING = 67 */
3279 szExpect
, /* HTTP_QUERY_EXPECT = 68 */
3280 szProxy_Connection
, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3281 szUnless_Modified_Since
, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3284 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3286 /***********************************************************************
3287 * HTTP_HttpQueryInfoW (internal)
3289 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*request
, DWORD dwInfoLevel
,
3290 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3292 LPHTTPHEADERW lphttpHdr
= NULL
;
3293 BOOL request_only
= dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
;
3294 INT requested_index
= lpdwIndex
? *lpdwIndex
: 0;
3295 DWORD level
= (dwInfoLevel
& ~HTTP_QUERY_MODIFIER_FLAGS_MASK
);
3298 /* Find requested header structure */
3301 case HTTP_QUERY_CUSTOM
:
3302 if (!lpBuffer
) return ERROR_INVALID_PARAMETER
;
3303 index
= HTTP_GetCustomHeaderIndex(request
, lpBuffer
, requested_index
, request_only
);
3305 case HTTP_QUERY_RAW_HEADERS_CRLF
:
3309 DWORD res
= ERROR_INVALID_PARAMETER
;
3312 headers
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
3314 headers
= request
->rawHeaders
;
3317 len
= strlenW(headers
) * sizeof(WCHAR
);
3319 if (len
+ sizeof(WCHAR
) > *lpdwBufferLength
)
3321 len
+= sizeof(WCHAR
);
3322 res
= ERROR_INSUFFICIENT_BUFFER
;
3327 memcpy(lpBuffer
, headers
, len
+ sizeof(WCHAR
));
3330 len
= strlenW(szCrLf
) * sizeof(WCHAR
);
3331 memcpy(lpBuffer
, szCrLf
, sizeof(szCrLf
));
3333 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
/ sizeof(WCHAR
)));
3334 res
= ERROR_SUCCESS
;
3336 *lpdwBufferLength
= len
;
3338 if (request_only
) heap_free(headers
);
3341 case HTTP_QUERY_RAW_HEADERS
:
3343 LPWSTR
* ppszRawHeaderLines
= HTTP_Tokenize(request
->rawHeaders
, szCrLf
);
3345 LPWSTR pszString
= lpBuffer
;
3347 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3348 size
+= strlenW(ppszRawHeaderLines
[i
]) + 1;
3350 if (size
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3352 HTTP_FreeTokens(ppszRawHeaderLines
);
3353 *lpdwBufferLength
= (size
+ 1) * sizeof(WCHAR
);
3354 return ERROR_INSUFFICIENT_BUFFER
;
3358 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3360 DWORD len
= strlenW(ppszRawHeaderLines
[i
]);
3361 memcpy(pszString
, ppszRawHeaderLines
[i
], (len
+1)*sizeof(WCHAR
));
3365 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, size
));
3367 *lpdwBufferLength
= size
* sizeof(WCHAR
);
3368 HTTP_FreeTokens(ppszRawHeaderLines
);
3370 return ERROR_SUCCESS
;
3372 case HTTP_QUERY_STATUS_TEXT
:
3373 if (request
->statusText
)
3375 DWORD len
= strlenW(request
->statusText
);
3376 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3378 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3379 return ERROR_INSUFFICIENT_BUFFER
;
3383 memcpy(lpBuffer
, request
->statusText
, (len
+ 1) * sizeof(WCHAR
));
3384 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3386 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3387 return ERROR_SUCCESS
;
3390 case HTTP_QUERY_VERSION
:
3391 if (request
->version
)
3393 DWORD len
= strlenW(request
->version
);
3394 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3396 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3397 return ERROR_INSUFFICIENT_BUFFER
;
3401 memcpy(lpBuffer
, request
->version
, (len
+ 1) * sizeof(WCHAR
));
3402 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3404 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3405 return ERROR_SUCCESS
;
3408 case HTTP_QUERY_CONTENT_ENCODING
:
3409 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[request
->read_gzip
? HTTP_QUERY_CONTENT_TYPE
: level
],
3410 requested_index
,request_only
);
3413 assert (LAST_TABLE_HEADER
== (HTTP_QUERY_UNLESS_MODIFIED_SINCE
+ 1));
3415 if (level
< LAST_TABLE_HEADER
&& header_lookup
[level
])
3416 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[level
],
3417 requested_index
,request_only
);
3421 lphttpHdr
= &request
->custHeaders
[index
];
3423 /* Ensure header satisfies requested attributes */
3425 ((dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
) &&
3426 (~lphttpHdr
->wFlags
& HDR_ISREQUEST
)))
3428 return ERROR_HTTP_HEADER_NOT_FOUND
;
3431 if (lpdwIndex
&& level
!= HTTP_QUERY_STATUS_CODE
) (*lpdwIndex
)++;
3433 /* coalesce value to requested type */
3434 if (dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
&& lpBuffer
)
3436 *(int *)lpBuffer
= atoiW(lphttpHdr
->lpszValue
);
3437 TRACE(" returning number: %d\n", *(int *)lpBuffer
);
3439 else if (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
&& lpBuffer
)
3445 tmpTime
= ConvertTimeString(lphttpHdr
->lpszValue
);
3447 tmpTM
= *gmtime(&tmpTime
);
3448 STHook
= (SYSTEMTIME
*)lpBuffer
;
3449 STHook
->wDay
= tmpTM
.tm_mday
;
3450 STHook
->wHour
= tmpTM
.tm_hour
;
3451 STHook
->wMilliseconds
= 0;
3452 STHook
->wMinute
= tmpTM
.tm_min
;
3453 STHook
->wDayOfWeek
= tmpTM
.tm_wday
;
3454 STHook
->wMonth
= tmpTM
.tm_mon
+ 1;
3455 STHook
->wSecond
= tmpTM
.tm_sec
;
3456 STHook
->wYear
= tmpTM
.tm_year
;
3458 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3459 STHook
->wYear
, STHook
->wMonth
, STHook
->wDay
, STHook
->wDayOfWeek
,
3460 STHook
->wHour
, STHook
->wMinute
, STHook
->wSecond
, STHook
->wMilliseconds
);
3462 else if (lphttpHdr
->lpszValue
)
3464 DWORD len
= (strlenW(lphttpHdr
->lpszValue
) + 1) * sizeof(WCHAR
);
3466 if (len
> *lpdwBufferLength
)
3468 *lpdwBufferLength
= len
;
3469 return ERROR_INSUFFICIENT_BUFFER
;
3473 memcpy(lpBuffer
, lphttpHdr
->lpszValue
, len
);
3474 TRACE("! returning string: %s\n", debugstr_w(lpBuffer
));
3476 *lpdwBufferLength
= len
- sizeof(WCHAR
);
3478 return ERROR_SUCCESS
;
3481 /***********************************************************************
3482 * HttpQueryInfoW (WININET.@)
3484 * Queries for information about an HTTP request
3491 BOOL WINAPI
HttpQueryInfoW(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3492 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3494 http_request_t
*request
;
3497 if (TRACE_ON(wininet
)) {
3498 #define FE(x) { x, #x }
3499 static const wininet_flag_info query_flags
[] = {
3500 FE(HTTP_QUERY_MIME_VERSION
),
3501 FE(HTTP_QUERY_CONTENT_TYPE
),
3502 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING
),
3503 FE(HTTP_QUERY_CONTENT_ID
),
3504 FE(HTTP_QUERY_CONTENT_DESCRIPTION
),
3505 FE(HTTP_QUERY_CONTENT_LENGTH
),
3506 FE(HTTP_QUERY_CONTENT_LANGUAGE
),
3507 FE(HTTP_QUERY_ALLOW
),
3508 FE(HTTP_QUERY_PUBLIC
),
3509 FE(HTTP_QUERY_DATE
),
3510 FE(HTTP_QUERY_EXPIRES
),
3511 FE(HTTP_QUERY_LAST_MODIFIED
),
3512 FE(HTTP_QUERY_MESSAGE_ID
),
3514 FE(HTTP_QUERY_DERIVED_FROM
),
3515 FE(HTTP_QUERY_COST
),
3516 FE(HTTP_QUERY_LINK
),
3517 FE(HTTP_QUERY_PRAGMA
),
3518 FE(HTTP_QUERY_VERSION
),
3519 FE(HTTP_QUERY_STATUS_CODE
),
3520 FE(HTTP_QUERY_STATUS_TEXT
),
3521 FE(HTTP_QUERY_RAW_HEADERS
),
3522 FE(HTTP_QUERY_RAW_HEADERS_CRLF
),
3523 FE(HTTP_QUERY_CONNECTION
),
3524 FE(HTTP_QUERY_ACCEPT
),
3525 FE(HTTP_QUERY_ACCEPT_CHARSET
),
3526 FE(HTTP_QUERY_ACCEPT_ENCODING
),
3527 FE(HTTP_QUERY_ACCEPT_LANGUAGE
),
3528 FE(HTTP_QUERY_AUTHORIZATION
),
3529 FE(HTTP_QUERY_CONTENT_ENCODING
),
3530 FE(HTTP_QUERY_FORWARDED
),
3531 FE(HTTP_QUERY_FROM
),
3532 FE(HTTP_QUERY_IF_MODIFIED_SINCE
),
3533 FE(HTTP_QUERY_LOCATION
),
3534 FE(HTTP_QUERY_ORIG_URI
),
3535 FE(HTTP_QUERY_REFERER
),
3536 FE(HTTP_QUERY_RETRY_AFTER
),
3537 FE(HTTP_QUERY_SERVER
),
3538 FE(HTTP_QUERY_TITLE
),
3539 FE(HTTP_QUERY_USER_AGENT
),
3540 FE(HTTP_QUERY_WWW_AUTHENTICATE
),
3541 FE(HTTP_QUERY_PROXY_AUTHENTICATE
),
3542 FE(HTTP_QUERY_ACCEPT_RANGES
),
3543 FE(HTTP_QUERY_SET_COOKIE
),
3544 FE(HTTP_QUERY_COOKIE
),
3545 FE(HTTP_QUERY_REQUEST_METHOD
),
3546 FE(HTTP_QUERY_REFRESH
),
3547 FE(HTTP_QUERY_CONTENT_DISPOSITION
),
3549 FE(HTTP_QUERY_CACHE_CONTROL
),
3550 FE(HTTP_QUERY_CONTENT_BASE
),
3551 FE(HTTP_QUERY_CONTENT_LOCATION
),
3552 FE(HTTP_QUERY_CONTENT_MD5
),
3553 FE(HTTP_QUERY_CONTENT_RANGE
),
3554 FE(HTTP_QUERY_ETAG
),
3555 FE(HTTP_QUERY_HOST
),
3556 FE(HTTP_QUERY_IF_MATCH
),
3557 FE(HTTP_QUERY_IF_NONE_MATCH
),
3558 FE(HTTP_QUERY_IF_RANGE
),
3559 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE
),
3560 FE(HTTP_QUERY_MAX_FORWARDS
),
3561 FE(HTTP_QUERY_PROXY_AUTHORIZATION
),
3562 FE(HTTP_QUERY_RANGE
),
3563 FE(HTTP_QUERY_TRANSFER_ENCODING
),
3564 FE(HTTP_QUERY_UPGRADE
),
3565 FE(HTTP_QUERY_VARY
),
3567 FE(HTTP_QUERY_WARNING
),
3568 FE(HTTP_QUERY_CUSTOM
)
3570 static const wininet_flag_info modifier_flags
[] = {
3571 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS
),
3572 FE(HTTP_QUERY_FLAG_SYSTEMTIME
),
3573 FE(HTTP_QUERY_FLAG_NUMBER
),
3574 FE(HTTP_QUERY_FLAG_COALESCE
)
3577 DWORD info_mod
= dwInfoLevel
& HTTP_QUERY_MODIFIER_FLAGS_MASK
;
3578 DWORD info
= dwInfoLevel
& HTTP_QUERY_HEADER_MASK
;
3581 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest
, dwInfoLevel
, info
);
3582 TRACE(" Attribute:");
3583 for (i
= 0; i
< (sizeof(query_flags
) / sizeof(query_flags
[0])); i
++) {
3584 if (query_flags
[i
].val
== info
) {
3585 TRACE(" %s", query_flags
[i
].name
);
3589 if (i
== (sizeof(query_flags
) / sizeof(query_flags
[0]))) {
3590 TRACE(" Unknown (%08x)", info
);
3593 TRACE(" Modifier:");
3594 for (i
= 0; i
< (sizeof(modifier_flags
) / sizeof(modifier_flags
[0])); i
++) {
3595 if (modifier_flags
[i
].val
& info_mod
) {
3596 TRACE(" %s", modifier_flags
[i
].name
);
3597 info_mod
&= ~ modifier_flags
[i
].val
;
3602 TRACE(" Unknown (%08x)", info_mod
);
3607 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
3608 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
3610 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3614 if (lpBuffer
== NULL
)
3615 *lpdwBufferLength
= 0;
3616 res
= HTTP_HttpQueryInfoW( request
, dwInfoLevel
,
3617 lpBuffer
, lpdwBufferLength
, lpdwIndex
);
3621 WININET_Release( &request
->hdr
);
3623 TRACE("%u <--\n", res
);
3624 if(res
!= ERROR_SUCCESS
)
3626 return res
== ERROR_SUCCESS
;
3629 /***********************************************************************
3630 * HttpQueryInfoA (WININET.@)
3632 * Queries for information about an HTTP request
3639 BOOL WINAPI
HttpQueryInfoA(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3640 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3646 if((dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) ||
3647 (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
))
3649 return HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, lpBuffer
,
3650 lpdwBufferLength
, lpdwIndex
);
3656 len
= (*lpdwBufferLength
)*sizeof(WCHAR
);
3657 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3659 alloclen
= MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, NULL
, 0 ) * sizeof(WCHAR
);
3665 bufferW
= heap_alloc(alloclen
);
3666 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3667 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3668 MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, bufferW
, alloclen
/ sizeof(WCHAR
) );
3675 result
= HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, bufferW
,
3679 len
= WideCharToMultiByte( CP_ACP
,0, bufferW
, len
/ sizeof(WCHAR
) + 1,
3680 lpBuffer
, *lpdwBufferLength
, NULL
, NULL
);
3681 *lpdwBufferLength
= len
- 1;
3683 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer
));
3686 /* since the strings being returned from HttpQueryInfoW should be
3687 * only ASCII characters, it is reasonable to assume that all of
3688 * the Unicode characters can be reduced to a single byte */
3689 *lpdwBufferLength
= len
/ sizeof(WCHAR
);
3691 heap_free( bufferW
);
3695 /***********************************************************************
3696 * HTTP_GetRedirectURL (internal)
3698 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*request
, LPCWSTR lpszUrl
)
3700 static WCHAR szHttp
[] = {'h','t','t','p',0};
3701 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3702 http_session_t
*session
= request
->session
;
3703 URL_COMPONENTSW urlComponents
;
3704 DWORD url_length
= 0;
3706 LPWSTR combined_url
;
3708 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3709 urlComponents
.lpszScheme
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) ? szHttps
: szHttp
;
3710 urlComponents
.dwSchemeLength
= 0;
3711 urlComponents
.lpszHostName
= session
->hostName
;
3712 urlComponents
.dwHostNameLength
= 0;
3713 urlComponents
.nPort
= session
->hostPort
;
3714 urlComponents
.lpszUserName
= session
->userName
;
3715 urlComponents
.dwUserNameLength
= 0;
3716 urlComponents
.lpszPassword
= NULL
;
3717 urlComponents
.dwPasswordLength
= 0;
3718 urlComponents
.lpszUrlPath
= request
->path
;
3719 urlComponents
.dwUrlPathLength
= 0;
3720 urlComponents
.lpszExtraInfo
= NULL
;
3721 urlComponents
.dwExtraInfoLength
= 0;
3723 if (!InternetCreateUrlW(&urlComponents
, 0, NULL
, &url_length
) &&
3724 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3727 orig_url
= heap_alloc(url_length
);
3729 /* convert from bytes to characters */
3730 url_length
= url_length
/ sizeof(WCHAR
) - 1;
3731 if (!InternetCreateUrlW(&urlComponents
, 0, orig_url
, &url_length
))
3733 heap_free(orig_url
);
3738 if (!InternetCombineUrlW(orig_url
, lpszUrl
, NULL
, &url_length
, ICU_ENCODE_SPACES_ONLY
) &&
3739 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3741 heap_free(orig_url
);
3744 combined_url
= heap_alloc(url_length
* sizeof(WCHAR
));
3746 if (!InternetCombineUrlW(orig_url
, lpszUrl
, combined_url
, &url_length
, ICU_ENCODE_SPACES_ONLY
))
3748 heap_free(orig_url
);
3749 heap_free(combined_url
);
3752 heap_free(orig_url
);
3753 return combined_url
;
3757 /***********************************************************************
3758 * HTTP_HandleRedirect (internal)
3760 static DWORD
HTTP_HandleRedirect(http_request_t
*request
, LPCWSTR lpszUrl
)
3762 http_session_t
*session
= request
->session
;
3763 appinfo_t
*hIC
= session
->appInfo
;
3764 BOOL using_proxy
= hIC
->proxy
&& hIC
->proxy
[0];
3765 WCHAR path
[INTERNET_MAX_PATH_LENGTH
];
3770 /* if it's an absolute path, keep the same session info */
3771 lstrcpynW(path
, lpszUrl
, INTERNET_MAX_URL_LENGTH
);
3775 URL_COMPONENTSW urlComponents
;
3776 WCHAR protocol
[INTERNET_MAX_SCHEME_LENGTH
];
3777 WCHAR hostName
[INTERNET_MAX_HOST_NAME_LENGTH
];
3778 WCHAR userName
[INTERNET_MAX_USER_NAME_LENGTH
];
3779 static WCHAR szHttp
[] = {'h','t','t','p',0};
3780 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3786 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3787 urlComponents
.lpszScheme
= protocol
;
3788 urlComponents
.dwSchemeLength
= INTERNET_MAX_SCHEME_LENGTH
;
3789 urlComponents
.lpszHostName
= hostName
;
3790 urlComponents
.dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
3791 urlComponents
.lpszUserName
= userName
;
3792 urlComponents
.dwUserNameLength
= INTERNET_MAX_USER_NAME_LENGTH
;
3793 urlComponents
.lpszPassword
= NULL
;
3794 urlComponents
.dwPasswordLength
= 0;
3795 urlComponents
.lpszUrlPath
= path
;
3796 urlComponents
.dwUrlPathLength
= INTERNET_MAX_PATH_LENGTH
;
3797 urlComponents
.lpszExtraInfo
= NULL
;
3798 urlComponents
.dwExtraInfoLength
= 0;
3799 if(!InternetCrackUrlW(lpszUrl
, strlenW(lpszUrl
), 0, &urlComponents
))
3800 return INTERNET_GetLastError();
3802 if (!strncmpW(szHttp
, urlComponents
.lpszScheme
, strlenW(szHttp
)) &&
3803 (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3805 TRACE("redirect from secure page to non-secure page\n");
3806 /* FIXME: warn about from secure redirect to non-secure page */
3807 request
->hdr
.dwFlags
&= ~INTERNET_FLAG_SECURE
;
3809 if (!strncmpW(szHttps
, urlComponents
.lpszScheme
, strlenW(szHttps
)) &&
3810 !(request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3812 TRACE("redirect from non-secure page to secure page\n");
3813 /* FIXME: notify about redirect to secure page */
3814 request
->hdr
.dwFlags
|= INTERNET_FLAG_SECURE
;
3817 if (urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3819 if (lstrlenW(protocol
)>4) /*https*/
3820 urlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
3822 urlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
3827 * This upsets redirects to binary files on sourceforge.net
3828 * and gives an html page instead of the target file
3829 * Examination of the HTTP request sent by native wininet.dll
3830 * reveals that it doesn't send a referrer in that case.
3831 * Maybe there's a flag that enables this, or maybe a referrer
3832 * shouldn't be added in case of a redirect.
3835 /* consider the current host as the referrer */
3836 if (session
->lpszServerName
&& *session
->lpszServerName
)
3837 HTTP_ProcessHeader(request
, HTTP_REFERER
, session
->lpszServerName
,
3838 HTTP_ADDHDR_FLAG_REQ
|HTTP_ADDREQ_FLAG_REPLACE
|
3839 HTTP_ADDHDR_FLAG_ADD_IF_NEW
);
3842 heap_free(session
->hostName
);
3843 if (urlComponents
.nPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
3844 urlComponents
.nPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3847 static const WCHAR fmt
[] = {'%','s',':','%','u',0};
3848 len
= lstrlenW(hostName
);
3849 len
+= 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3850 session
->hostName
= heap_alloc(len
*sizeof(WCHAR
));
3851 sprintfW(session
->hostName
, fmt
, hostName
, urlComponents
.nPort
);
3854 session
->hostName
= heap_strdupW(hostName
);
3856 HTTP_ProcessHeader(request
, hostW
, session
->hostName
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDREQ_FLAG_REPLACE
| HTTP_ADDHDR_FLAG_REQ
);
3858 heap_free(session
->userName
);
3859 session
->userName
= NULL
;
3861 session
->userName
= heap_strdupW(userName
);
3863 reset_data_stream(request
);
3866 if(strcmpiW(session
->serverName
, hostName
)) {
3867 heap_free(session
->serverName
);
3868 session
->serverName
= heap_strdupW(hostName
);
3870 session
->serverPort
= urlComponents
.nPort
;
3873 heap_free(request
->path
);
3880 rc
= UrlEscapeW(path
, NULL
, &needed
, URL_ESCAPE_SPACES_ONLY
);
3881 if (rc
!= E_POINTER
)
3882 needed
= strlenW(path
)+1;
3883 request
->path
= heap_alloc(needed
*sizeof(WCHAR
));
3884 rc
= UrlEscapeW(path
, request
->path
, &needed
,
3885 URL_ESCAPE_SPACES_ONLY
);
3888 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path
),rc
);
3889 strcpyW(request
->path
,path
);
3893 /* Remove custom content-type/length headers on redirects. */
3894 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Type
, 0, TRUE
);
3896 HTTP_DeleteCustomHeader(request
, index
);
3897 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Length
, 0, TRUE
);
3899 HTTP_DeleteCustomHeader(request
, index
);
3901 return ERROR_SUCCESS
;
3904 /***********************************************************************
3905 * HTTP_build_req (internal)
3907 * concatenate all the strings in the request together
3909 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
)
3914 for( t
= list
; *t
; t
++ )
3915 len
+= strlenW( *t
);
3918 str
= heap_alloc(len
*sizeof(WCHAR
));
3921 for( t
= list
; *t
; t
++ )
3927 static DWORD
HTTP_SecureProxyConnect(http_request_t
*request
)
3930 LPWSTR requestString
;
3936 static const WCHAR szConnect
[] = {'C','O','N','N','E','C','T',0};
3937 static const WCHAR szFormat
[] = {'%','s',':','%','u',0};
3938 http_session_t
*session
= request
->session
;
3942 lpszPath
= heap_alloc((lstrlenW( session
->hostName
) + 13)*sizeof(WCHAR
));
3943 sprintfW( lpszPath
, szFormat
, session
->hostName
, session
->hostPort
);
3944 requestString
= HTTP_BuildHeaderRequestString( request
, szConnect
, lpszPath
, g_szHttp1_1
);
3945 heap_free( lpszPath
);
3947 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3948 NULL
, 0, NULL
, NULL
);
3949 len
--; /* the nul terminator isn't needed */
3950 ascii_req
= heap_alloc(len
);
3951 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1, ascii_req
, len
, NULL
, NULL
);
3952 heap_free( requestString
);
3954 TRACE("full request -> %s\n", debugstr_an( ascii_req
, len
) );
3956 res
= NETCON_send( request
->netconn
, ascii_req
, len
, 0, &cnt
);
3957 heap_free( ascii_req
);
3958 if (res
!= ERROR_SUCCESS
)
3961 responseLen
= HTTP_GetResponseHeaders( request
, TRUE
);
3963 return ERROR_HTTP_INVALID_HEADER
;
3965 return ERROR_SUCCESS
;
3968 static void HTTP_InsertCookies(http_request_t
*request
)
3970 DWORD cookie_size
, size
, cnt
= 0;
3974 static const WCHAR cookieW
[] = {'C','o','o','k','i','e',':',' ',0};
3976 host
= HTTP_GetHeader(request
, hostW
);
3980 if(!get_cookie(host
->lpszValue
, request
->path
, NULL
, &cookie_size
))
3983 size
= sizeof(cookieW
) + cookie_size
* sizeof(WCHAR
) + sizeof(szCrLf
);
3984 if(!(cookies
= heap_alloc(size
)))
3987 cnt
+= sprintfW(cookies
, cookieW
);
3988 get_cookie(host
->lpszValue
, request
->path
, cookies
+cnt
, &cookie_size
);
3989 strcatW(cookies
, szCrLf
);
3991 HTTP_HttpAddRequestHeadersW(request
, cookies
, strlenW(cookies
), HTTP_ADDREQ_FLAG_REPLACE
);
3996 static WORD
HTTP_ParseWkday(LPCWSTR day
)
3998 static const WCHAR days
[7][4] = {{ 's','u','n',0 },
4006 for (i
= 0; i
< sizeof(days
)/sizeof(*days
); i
++)
4007 if (!strcmpiW(day
, days
[i
]))
4014 static WORD
HTTP_ParseMonth(LPCWSTR month
)
4016 static const WCHAR jan
[] = { 'j','a','n',0 };
4017 static const WCHAR feb
[] = { 'f','e','b',0 };
4018 static const WCHAR mar
[] = { 'm','a','r',0 };
4019 static const WCHAR apr
[] = { 'a','p','r',0 };
4020 static const WCHAR may
[] = { 'm','a','y',0 };
4021 static const WCHAR jun
[] = { 'j','u','n',0 };
4022 static const WCHAR jul
[] = { 'j','u','l',0 };
4023 static const WCHAR aug
[] = { 'a','u','g',0 };
4024 static const WCHAR sep
[] = { 's','e','p',0 };
4025 static const WCHAR oct
[] = { 'o','c','t',0 };
4026 static const WCHAR nov
[] = { 'n','o','v',0 };
4027 static const WCHAR dec
[] = { 'd','e','c',0 };
4029 if (!strcmpiW(month
, jan
)) return 1;
4030 if (!strcmpiW(month
, feb
)) return 2;
4031 if (!strcmpiW(month
, mar
)) return 3;
4032 if (!strcmpiW(month
, apr
)) return 4;
4033 if (!strcmpiW(month
, may
)) return 5;
4034 if (!strcmpiW(month
, jun
)) return 6;
4035 if (!strcmpiW(month
, jul
)) return 7;
4036 if (!strcmpiW(month
, aug
)) return 8;
4037 if (!strcmpiW(month
, sep
)) return 9;
4038 if (!strcmpiW(month
, oct
)) return 10;
4039 if (!strcmpiW(month
, nov
)) return 11;
4040 if (!strcmpiW(month
, dec
)) return 12;
4045 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4046 * optionally preceded by whitespace.
4047 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4048 * st, and sets *str to the first character after the time format.
4050 static BOOL
HTTP_ParseTime(SYSTEMTIME
*st
, LPCWSTR
*str
)
4056 while (isspaceW(*ptr
))
4059 num
= strtoulW(ptr
, &nextPtr
, 10);
4060 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
4062 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4067 ERR("unexpected hour in time format %s\n", debugstr_w(ptr
));
4071 st
->wHour
= (WORD
)num
;
4072 num
= strtoulW(ptr
, &nextPtr
, 10);
4073 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
4075 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4080 ERR("unexpected minute in time format %s\n", debugstr_w(ptr
));
4084 st
->wMinute
= (WORD
)num
;
4085 num
= strtoulW(ptr
, &nextPtr
, 10);
4086 if (!nextPtr
|| nextPtr
<= ptr
)
4088 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4093 ERR("unexpected second in time format %s\n", debugstr_w(ptr
));
4098 st
->wSecond
= (WORD
)num
;
4102 static BOOL
HTTP_ParseDateAsAsctime(LPCWSTR value
, FILETIME
*ft
)
4104 static const WCHAR gmt
[]= { 'G','M','T',0 };
4105 WCHAR day
[4], *dayPtr
, month
[4], *monthPtr
, *nextPtr
;
4107 SYSTEMTIME st
= { 0 };
4110 for (ptr
= value
, dayPtr
= day
; *ptr
&& !isspaceW(*ptr
) &&
4111 dayPtr
- day
< sizeof(day
) / sizeof(day
[0]) - 1; ptr
++, dayPtr
++)
4114 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4115 if (st
.wDayOfWeek
>= 7)
4117 ERR("unexpected weekday %s\n", debugstr_w(day
));
4121 while (isspaceW(*ptr
))
4124 for (monthPtr
= month
; !isspace(*ptr
) &&
4125 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4129 st
.wMonth
= HTTP_ParseMonth(month
);
4130 if (!st
.wMonth
|| st
.wMonth
> 12)
4132 ERR("unexpected month %s\n", debugstr_w(month
));
4136 while (isspaceW(*ptr
))
4139 num
= strtoulW(ptr
, &nextPtr
, 10);
4140 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4142 ERR("unexpected day %s\n", debugstr_w(ptr
));
4146 st
.wDay
= (WORD
)num
;
4148 while (isspaceW(*ptr
))
4151 if (!HTTP_ParseTime(&st
, &ptr
))
4154 while (isspaceW(*ptr
))
4157 num
= strtoulW(ptr
, &nextPtr
, 10);
4158 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4160 ERR("unexpected year %s\n", debugstr_w(ptr
));
4164 st
.wYear
= (WORD
)num
;
4166 while (isspaceW(*ptr
))
4169 /* asctime() doesn't report a timezone, but some web servers do, so accept
4170 * with or without GMT.
4172 if (*ptr
&& strcmpW(ptr
, gmt
))
4174 ERR("unexpected timezone %s\n", debugstr_w(ptr
));
4177 return SystemTimeToFileTime(&st
, ft
);
4180 static BOOL
HTTP_ParseRfc1123Date(LPCWSTR value
, FILETIME
*ft
)
4182 static const WCHAR gmt
[]= { 'G','M','T',0 };
4183 WCHAR
*nextPtr
, day
[4], month
[4], *monthPtr
;
4186 SYSTEMTIME st
= { 0 };
4188 ptr
= strchrW(value
, ',');
4191 if (ptr
- value
!= 3)
4193 WARN("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4196 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4198 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4199 if (st
.wDayOfWeek
> 6)
4201 WARN("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4206 while (isspaceW(*ptr
))
4209 num
= strtoulW(ptr
, &nextPtr
, 10);
4210 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4212 WARN("unexpected day %s\n", debugstr_w(value
));
4216 st
.wDay
= (WORD
)num
;
4218 while (isspaceW(*ptr
))
4221 for (monthPtr
= month
; !isspace(*ptr
) &&
4222 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4226 st
.wMonth
= HTTP_ParseMonth(month
);
4227 if (!st
.wMonth
|| st
.wMonth
> 12)
4229 WARN("unexpected month %s\n", debugstr_w(month
));
4233 while (isspaceW(*ptr
))
4236 num
= strtoulW(ptr
, &nextPtr
, 10);
4237 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4239 ERR("unexpected year %s\n", debugstr_w(value
));
4243 st
.wYear
= (WORD
)num
;
4245 if (!HTTP_ParseTime(&st
, &ptr
))
4248 while (isspaceW(*ptr
))
4251 if (strcmpW(ptr
, gmt
))
4253 ERR("unexpected time zone %s\n", debugstr_w(ptr
));
4256 return SystemTimeToFileTime(&st
, ft
);
4259 static WORD
HTTP_ParseWeekday(LPCWSTR day
)
4261 static const WCHAR days
[7][10] = {{ 's','u','n','d','a','y',0 },
4262 { 'm','o','n','d','a','y',0 },
4263 { 't','u','e','s','d','a','y',0 },
4264 { 'w','e','d','n','e','s','d','a','y',0 },
4265 { 't','h','u','r','s','d','a','y',0 },
4266 { 'f','r','i','d','a','y',0 },
4267 { 's','a','t','u','r','d','a','y',0 }};
4269 for (i
= 0; i
< sizeof(days
)/sizeof(*days
); i
++)
4270 if (!strcmpiW(day
, days
[i
]))
4277 static BOOL
HTTP_ParseRfc850Date(LPCWSTR value
, FILETIME
*ft
)
4279 static const WCHAR gmt
[]= { 'G','M','T',0 };
4280 WCHAR
*nextPtr
, day
[10], month
[4], *monthPtr
;
4283 SYSTEMTIME st
= { 0 };
4285 ptr
= strchrW(value
, ',');
4288 if (ptr
- value
== 3)
4290 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4292 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4293 if (st
.wDayOfWeek
> 6)
4295 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4299 else if (ptr
- value
< sizeof(day
) / sizeof(day
[0]))
4301 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4302 day
[ptr
- value
+ 1] = 0;
4303 st
.wDayOfWeek
= HTTP_ParseWeekday(day
);
4304 if (st
.wDayOfWeek
> 6)
4306 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4312 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4317 while (isspaceW(*ptr
))
4320 num
= strtoulW(ptr
, &nextPtr
, 10);
4321 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4323 ERR("unexpected day %s\n", debugstr_w(value
));
4327 st
.wDay
= (WORD
)num
;
4331 ERR("unexpected month format %s\n", debugstr_w(ptr
));
4336 for (monthPtr
= month
; *ptr
!= '-' &&
4337 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4341 st
.wMonth
= HTTP_ParseMonth(month
);
4342 if (!st
.wMonth
|| st
.wMonth
> 12)
4344 ERR("unexpected month %s\n", debugstr_w(month
));
4350 ERR("unexpected year format %s\n", debugstr_w(ptr
));
4355 num
= strtoulW(ptr
, &nextPtr
, 10);
4356 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4358 ERR("unexpected year %s\n", debugstr_w(value
));
4362 st
.wYear
= (WORD
)num
;
4364 if (!HTTP_ParseTime(&st
, &ptr
))
4367 while (isspaceW(*ptr
))
4370 if (strcmpW(ptr
, gmt
))
4372 ERR("unexpected time zone %s\n", debugstr_w(ptr
));
4375 return SystemTimeToFileTime(&st
, ft
);
4378 static BOOL
HTTP_ParseDate(LPCWSTR value
, FILETIME
*ft
)
4380 static const WCHAR zero
[] = { '0',0 };
4383 if (!strcmpW(value
, zero
))
4385 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
4388 else if (strchrW(value
, ','))
4390 ret
= HTTP_ParseRfc1123Date(value
, ft
);
4393 ret
= HTTP_ParseRfc850Date(value
, ft
);
4395 ERR("unexpected date format %s\n", debugstr_w(value
));
4400 ret
= HTTP_ParseDateAsAsctime(value
, ft
);
4402 ERR("unexpected date format %s\n", debugstr_w(value
));
4407 static void HTTP_ProcessExpires(http_request_t
*request
)
4409 BOOL expirationFound
= FALSE
;
4412 /* Look for a Cache-Control header with a max-age directive, as it takes
4413 * precedence over the Expires header.
4415 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szCache_Control
, 0, FALSE
);
4416 if (headerIndex
!= -1)
4418 LPHTTPHEADERW ccHeader
= &request
->custHeaders
[headerIndex
];
4421 for (ptr
= ccHeader
->lpszValue
; ptr
&& *ptr
; )
4423 LPWSTR comma
= strchrW(ptr
, ','), end
, equal
;
4428 end
= ptr
+ strlenW(ptr
);
4429 for (equal
= end
- 1; equal
> ptr
&& *equal
!= '='; equal
--)
4433 static const WCHAR max_age
[] = {
4434 'm','a','x','-','a','g','e',0 };
4436 if (!strncmpiW(ptr
, max_age
, equal
- ptr
- 1))
4441 age
= strtoulW(equal
+ 1, &nextPtr
, 10);
4442 if (nextPtr
> equal
+ 1)
4446 NtQuerySystemTime( &ft
);
4447 /* Age is in seconds, FILETIME resolution is in
4448 * 100 nanosecond intervals.
4450 ft
.QuadPart
+= age
* (ULONGLONG
)1000000;
4451 request
->expires
.dwLowDateTime
= ft
.u
.LowPart
;
4452 request
->expires
.dwHighDateTime
= ft
.u
.HighPart
;
4453 expirationFound
= TRUE
;
4460 while (isspaceW(*ptr
))
4467 if (!expirationFound
)
4469 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szExpires
, 0, FALSE
);
4470 if (headerIndex
!= -1)
4472 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4475 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4477 expirationFound
= TRUE
;
4478 request
->expires
= ft
;
4482 if (!expirationFound
)
4486 /* With no known age, default to 10 minutes until expiration. */
4487 NtQuerySystemTime( &t
);
4488 t
.QuadPart
+= 10 * 60 * (ULONGLONG
)10000000;
4489 request
->expires
.dwLowDateTime
= t
.u
.LowPart
;
4490 request
->expires
.dwHighDateTime
= t
.u
.HighPart
;
4494 static void HTTP_ProcessLastModified(http_request_t
*request
)
4498 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szLast_Modified
, 0, FALSE
);
4499 if (headerIndex
!= -1)
4501 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4504 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4505 request
->last_modified
= ft
;
4509 static void http_process_keep_alive(http_request_t
*req
)
4513 index
= HTTP_GetCustomHeaderIndex(req
, szConnection
, 0, FALSE
);
4515 req
->netconn
->keep_alive
= !strcmpiW(req
->custHeaders
[index
].lpszValue
, szKeepAlive
);
4517 req
->netconn
->keep_alive
= !strcmpiW(req
->version
, g_szHttp1_1
);
4520 static void HTTP_CacheRequest(http_request_t
*request
)
4522 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
4523 WCHAR cacheFileName
[MAX_PATH
+1];
4526 b
= HTTP_GetRequestURL(request
, url
);
4528 WARN("Could not get URL\n");
4532 b
= CreateUrlCacheEntryW(url
, request
->contentLength
, NULL
, cacheFileName
, 0);
4534 heap_free(request
->cacheFile
);
4535 CloseHandle(request
->hCacheFile
);
4537 request
->cacheFile
= heap_strdupW(cacheFileName
);
4538 request
->hCacheFile
= CreateFileW(request
->cacheFile
, GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
4539 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
4540 if(request
->hCacheFile
== INVALID_HANDLE_VALUE
) {
4541 WARN("Could not create file: %u\n", GetLastError());
4542 request
->hCacheFile
= NULL
;
4545 WARN("Could not create cache entry: %08x\n", GetLastError());
4549 static DWORD
open_http_connection(http_request_t
*request
, BOOL
*reusing
)
4551 const BOOL is_https
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) != 0;
4552 http_session_t
*session
= request
->session
;
4553 netconn_t
*netconn
= NULL
;
4557 assert(!request
->netconn
);
4558 reset_data_stream(request
);
4560 server
= get_server(session
->serverName
, session
->serverPort
);
4562 return ERROR_OUTOFMEMORY
;
4564 res
= HTTP_ResolveName(request
, server
);
4565 if(res
!= ERROR_SUCCESS
) {
4566 server_release(server
);
4570 EnterCriticalSection(&connection_pool_cs
);
4572 while(!list_empty(&server
->conn_pool
)) {
4573 netconn
= LIST_ENTRY(list_head(&server
->conn_pool
), netconn_t
, pool_entry
);
4574 list_remove(&netconn
->pool_entry
);
4576 if(NETCON_is_alive(netconn
))
4579 TRACE("connection %p closed during idle\n", netconn
);
4580 free_netconn(netconn
);
4584 LeaveCriticalSection(&connection_pool_cs
);
4587 TRACE("<-- reusing %p netconn\n", netconn
);
4588 request
->netconn
= netconn
;
4590 return ERROR_SUCCESS
;
4593 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4594 INTERNET_STATUS_CONNECTING_TO_SERVER
,
4596 strlen(server
->addr_str
)+1);
4598 res
= create_netconn(is_https
, server
, request
->security_flags
, &netconn
);
4599 server_release(server
);
4600 if(res
!= ERROR_SUCCESS
) {
4601 ERR("create_netconn failed: %u\n", res
);
4605 request
->netconn
= netconn
;
4607 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4608 INTERNET_STATUS_CONNECTED_TO_SERVER
,
4609 server
->addr_str
, strlen(server
->addr_str
)+1);
4612 /* Note: we differ from Microsoft's WinINet here. they seem to have
4613 * a bug that causes no status callbacks to be sent when starting
4614 * a tunnel to a proxy server using the CONNECT verb. i believe our
4615 * behaviour to be more correct and to not cause any incompatibilities
4616 * because using a secure connection through a proxy server is a rare
4617 * case that would be hard for anyone to depend on */
4618 if(session
->appInfo
->proxy
)
4619 res
= HTTP_SecureProxyConnect(request
);
4620 if(res
== ERROR_SUCCESS
)
4621 res
= NETCON_secure_connect(request
->netconn
, session
->hostName
);
4622 if(res
!= ERROR_SUCCESS
)
4624 WARN("Couldn't connect securely to host\n");
4626 if((request
->hdr
.ErrorMask
&INTERNET_ERROR_MASK_COMBINED_SEC_CERT
) && (
4627 res
== ERROR_INTERNET_SEC_CERT_DATE_INVALID
4628 || res
== ERROR_INTERNET_INVALID_CA
4629 || res
== ERROR_INTERNET_SEC_CERT_NO_REV
4630 || res
== ERROR_INTERNET_SEC_CERT_REV_FAILED
4631 || res
== ERROR_INTERNET_SEC_CERT_REVOKED
4632 || res
== ERROR_INTERNET_SEC_INVALID_CERT
4633 || res
== ERROR_INTERNET_SEC_CERT_CN_INVALID
))
4634 res
= ERROR_INTERNET_SEC_CERT_ERRORS
;
4638 if(res
!= ERROR_SUCCESS
) {
4639 http_release_netconn(request
, FALSE
);
4644 TRACE("Created connection to %s: %p\n", debugstr_w(server
->name
), netconn
);
4645 return ERROR_SUCCESS
;
4648 /***********************************************************************
4649 * HTTP_HttpSendRequestW (internal)
4651 * Sends the specified request to the HTTP server
4654 * ERROR_SUCCESS on success
4655 * win32 error code on failure
4658 static DWORD
HTTP_HttpSendRequestW(http_request_t
*request
, LPCWSTR lpszHeaders
,
4659 DWORD dwHeaderLength
, LPVOID lpOptional
, DWORD dwOptionalLength
,
4660 DWORD dwContentLength
, BOOL bEndRequest
)
4663 BOOL redirected
= FALSE
;
4664 LPWSTR requestString
= NULL
;
4667 static const WCHAR szPost
[] = { 'P','O','S','T',0 };
4668 static const WCHAR szContentLength
[] =
4669 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4670 WCHAR contentLengthStr
[sizeof szContentLength
/2 /* includes \r\n */ + 20 /* int */ ];
4673 TRACE("--> %p\n", request
);
4675 assert(request
->hdr
.htype
== WH_HHTTPREQ
);
4677 /* if the verb is NULL default to GET */
4679 request
->verb
= heap_strdupW(szGET
);
4681 if (dwContentLength
|| strcmpW(request
->verb
, szGET
))
4683 sprintfW(contentLengthStr
, szContentLength
, dwContentLength
);
4684 HTTP_HttpAddRequestHeadersW(request
, contentLengthStr
, -1L, HTTP_ADDREQ_FLAG_REPLACE
);
4685 request
->bytesToWrite
= dwContentLength
;
4687 if (request
->session
->appInfo
->agent
)
4689 WCHAR
*agent_header
;
4690 static const WCHAR user_agent
[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4693 len
= strlenW(request
->session
->appInfo
->agent
) + strlenW(user_agent
);
4694 agent_header
= heap_alloc(len
* sizeof(WCHAR
));
4695 sprintfW(agent_header
, user_agent
, request
->session
->appInfo
->agent
);
4697 HTTP_HttpAddRequestHeadersW(request
, agent_header
, strlenW(agent_header
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4698 heap_free(agent_header
);
4700 if (request
->hdr
.dwFlags
& INTERNET_FLAG_PRAGMA_NOCACHE
)
4702 static const WCHAR pragma_nocache
[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4703 HTTP_HttpAddRequestHeadersW(request
, pragma_nocache
, strlenW(pragma_nocache
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4705 if ((request
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
) && !strcmpW(request
->verb
, szPost
))
4707 static const WCHAR cache_control
[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4708 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4709 HTTP_HttpAddRequestHeadersW(request
, cache_control
, strlenW(cache_control
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4715 BOOL reusing_connection
;
4720 /* like native, just in case the caller forgot to call InternetReadFile
4721 * for all the data */
4722 drain_content(request
);
4724 request
->contentLength
= ~0u;
4725 request
->bytesToWrite
= 0;
4728 if (TRACE_ON(wininet
))
4730 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4731 TRACE("Going to url %s %s\n", debugstr_w(Host
->lpszValue
), debugstr_w(request
->path
));
4734 HTTP_FixURL(request
);
4735 if (request
->hdr
.dwFlags
& INTERNET_FLAG_KEEP_CONNECTION
)
4737 HTTP_ProcessHeader(request
, szConnection
, szKeepAlive
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
4739 HTTP_InsertAuthorization(request
, request
->authInfo
, szAuthorization
);
4740 HTTP_InsertAuthorization(request
, request
->proxyAuthInfo
, szProxy_Authorization
);
4742 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
))
4743 HTTP_InsertCookies(request
);
4745 /* add the headers the caller supplied */
4746 if( lpszHeaders
&& dwHeaderLength
)
4748 HTTP_HttpAddRequestHeadersW(request
, lpszHeaders
, dwHeaderLength
,
4749 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REPLACE
);
4752 if (request
->session
->appInfo
->proxy
&& request
->session
->appInfo
->proxy
[0])
4754 WCHAR
*url
= HTTP_BuildProxyRequestUrl(request
);
4755 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, url
, request
->version
);
4759 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
4762 TRACE("Request header -> %s\n", debugstr_w(requestString
) );
4764 if ((res
= open_http_connection(request
, &reusing_connection
)) != ERROR_SUCCESS
)
4767 /* send the request as ASCII, tack on the optional data */
4768 if (!lpOptional
|| redirected
)
4769 dwOptionalLength
= 0;
4770 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4771 NULL
, 0, NULL
, NULL
);
4772 ascii_req
= heap_alloc(len
+ dwOptionalLength
);
4773 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4774 ascii_req
, len
, NULL
, NULL
);
4776 memcpy( &ascii_req
[len
-1], lpOptional
, dwOptionalLength
);
4777 len
= (len
+ dwOptionalLength
- 1);
4779 TRACE("full request -> %s\n", debugstr_a(ascii_req
) );
4781 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4782 INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
4784 res
= NETCON_send(request
->netconn
, ascii_req
, len
, 0, &cnt
);
4785 heap_free( ascii_req
);
4786 if(res
!= ERROR_SUCCESS
) {
4787 TRACE("send failed: %u\n", res
);
4788 if(!reusing_connection
)
4790 http_release_netconn(request
, FALSE
);
4795 request
->bytesWritten
= dwOptionalLength
;
4797 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4798 INTERNET_STATUS_REQUEST_SENT
,
4799 &len
, sizeof(DWORD
));
4806 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4807 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
4809 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
4810 /* FIXME: We should know that connection is closed before sending
4811 * headers. Otherwise wrong callbacks are executed */
4812 if(!responseLen
&& reusing_connection
) {
4813 TRACE("Connection closed by server, reconnecting\n");
4814 http_release_netconn(request
, FALSE
);
4819 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4820 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
,
4823 http_process_keep_alive(request
);
4824 HTTP_ProcessCookies(request
);
4825 HTTP_ProcessExpires(request
);
4826 HTTP_ProcessLastModified(request
);
4828 dwBufferSize
= sizeof(dwStatusCode
);
4829 if (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
,
4830 &dwStatusCode
,&dwBufferSize
,NULL
) != ERROR_SUCCESS
)
4833 res
= set_content_length(request
, dwStatusCode
);
4834 if(res
!= ERROR_SUCCESS
)
4836 if(!request
->contentLength
)
4837 http_release_netconn(request
, TRUE
);
4839 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
) && responseLen
)
4841 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
4842 dwBufferSize
=sizeof(szNewLocation
);
4843 if ((dwStatusCode
== HTTP_STATUS_REDIRECT
||
4844 dwStatusCode
== HTTP_STATUS_MOVED
||
4845 dwStatusCode
== HTTP_STATUS_REDIRECT_KEEP_VERB
||
4846 dwStatusCode
== HTTP_STATUS_REDIRECT_METHOD
) &&
4847 HTTP_HttpQueryInfoW(request
,HTTP_QUERY_LOCATION
,szNewLocation
,&dwBufferSize
,NULL
) == ERROR_SUCCESS
)
4849 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
) &&
4850 dwStatusCode
!= HTTP_STATUS_REDIRECT_KEEP_VERB
)
4852 heap_free(request
->verb
);
4853 request
->verb
= heap_strdupW(szGET
);
4855 drain_content(request
);
4856 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
4858 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
4859 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
4860 res
= HTTP_HandleRedirect(request
, new_url
);
4861 if (res
== ERROR_SUCCESS
)
4863 heap_free(requestString
);
4866 heap_free( new_url
);
4871 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTH
) && res
== ERROR_SUCCESS
)
4873 WCHAR szAuthValue
[2048];
4875 if (dwStatusCode
== HTTP_STATUS_DENIED
)
4877 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4879 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_WWW_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4881 if (HTTP_DoAuthorization(request
, szAuthValue
,
4883 request
->session
->userName
,
4884 request
->session
->password
,
4887 heap_free(requestString
);
4894 TRACE("Cleaning wrong authorization data\n");
4895 destroy_authinfo(request
->authInfo
);
4896 request
->authInfo
= NULL
;
4899 if (dwStatusCode
== HTTP_STATUS_PROXY_AUTH_REQ
)
4902 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_PROXY_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4904 if (HTTP_DoAuthorization(request
, szAuthValue
,
4905 &request
->proxyAuthInfo
,
4906 request
->session
->appInfo
->proxyUsername
,
4907 request
->session
->appInfo
->proxyPassword
,
4916 TRACE("Cleaning wrong proxy authorization data\n");
4917 destroy_authinfo(request
->proxyAuthInfo
);
4918 request
->proxyAuthInfo
= NULL
;
4924 res
= ERROR_SUCCESS
;
4928 if(res
== ERROR_SUCCESS
)
4929 HTTP_CacheRequest(request
);
4932 heap_free(requestString
);
4934 /* TODO: send notification for P3P header */
4936 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4938 if (res
== ERROR_SUCCESS
) {
4939 if(bEndRequest
&& request
->contentLength
&& request
->bytesWritten
== request
->bytesToWrite
)
4940 HTTP_ReceiveRequestData(request
, TRUE
);
4942 send_request_complete(request
,
4943 request
->session
->hdr
.dwInternalFlags
& INET_OPENURL
? (DWORD_PTR
)request
->hdr
.hInternet
: 1, 0);
4945 send_request_complete(request
, 0, res
);
4953 /***********************************************************************
4955 * Helper functions for the HttpSendRequest(Ex) functions
4958 static void AsyncHttpSendRequestProc(WORKREQUEST
*workRequest
)
4960 struct WORKREQ_HTTPSENDREQUESTW
const *req
= &workRequest
->u
.HttpSendRequestW
;
4961 http_request_t
*request
= (http_request_t
*) workRequest
->hdr
;
4963 TRACE("%p\n", request
);
4965 HTTP_HttpSendRequestW(request
, req
->lpszHeader
,
4966 req
->dwHeaderLength
, req
->lpOptional
, req
->dwOptionalLength
,
4967 req
->dwContentLength
, req
->bEndRequest
);
4969 heap_free(req
->lpszHeader
);
4973 static DWORD
HTTP_HttpEndRequestW(http_request_t
*request
, DWORD dwFlags
, DWORD_PTR dwContext
)
4976 DWORD dwCode
, dwCodeLength
;
4978 DWORD res
= ERROR_SUCCESS
;
4980 if(!request
->netconn
) {
4981 WARN("Not connected\n");
4982 send_request_complete(request
, 0, ERROR_INTERNET_OPERATION_CANCELLED
);
4983 return ERROR_INTERNET_OPERATION_CANCELLED
;
4986 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4987 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
4989 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
4991 res
= ERROR_HTTP_HEADER_NOT_FOUND
;
4993 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4994 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
, sizeof(DWORD
));
4996 /* process cookies here. Is this right? */
4997 http_process_keep_alive(request
);
4998 HTTP_ProcessCookies(request
);
4999 HTTP_ProcessExpires(request
);
5000 HTTP_ProcessLastModified(request
);
5002 dwCodeLength
= sizeof(dwCode
);
5003 if (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
,
5004 &dwCode
,&dwCodeLength
,NULL
) != ERROR_SUCCESS
)
5007 if ((res
= set_content_length( request
, dwCode
)) == ERROR_SUCCESS
) {
5008 if(!request
->contentLength
)
5009 http_release_netconn(request
, TRUE
);
5012 if (res
== ERROR_SUCCESS
&& !(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
))
5014 if (dwCode
== HTTP_STATUS_REDIRECT
||
5015 dwCode
== HTTP_STATUS_MOVED
||
5016 dwCode
== HTTP_STATUS_REDIRECT_METHOD
||
5017 dwCode
== HTTP_STATUS_REDIRECT_KEEP_VERB
)
5019 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
5020 dwBufferSize
=sizeof(szNewLocation
);
5021 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_LOCATION
, szNewLocation
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
5023 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
) &&
5024 dwCode
!= HTTP_STATUS_REDIRECT_KEEP_VERB
)
5026 heap_free(request
->verb
);
5027 request
->verb
= heap_strdupW(szGET
);
5029 drain_content(request
);
5030 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
5032 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
5033 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
5034 res
= HTTP_HandleRedirect(request
, new_url
);
5035 if (res
== ERROR_SUCCESS
)
5036 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, TRUE
);
5037 heap_free( new_url
);
5043 if (res
== ERROR_SUCCESS
&& request
->contentLength
)
5044 HTTP_ReceiveRequestData(request
, TRUE
);
5046 send_request_complete(request
, res
== ERROR_SUCCESS
, res
);
5051 /***********************************************************************
5052 * HttpEndRequestA (WININET.@)
5054 * Ends an HTTP request that was started by HttpSendRequestEx
5057 * TRUE if successful
5061 BOOL WINAPI
HttpEndRequestA(HINTERNET hRequest
,
5062 LPINTERNET_BUFFERSA lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
5064 TRACE("(%p, %p, %08x, %08lx)\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
5068 SetLastError(ERROR_INVALID_PARAMETER
);
5072 return HttpEndRequestW(hRequest
, NULL
, dwFlags
, dwContext
);
5075 static void AsyncHttpEndRequestProc(WORKREQUEST
*work
)
5077 struct WORKREQ_HTTPENDREQUESTW
const *req
= &work
->u
.HttpEndRequestW
;
5078 http_request_t
*request
= (http_request_t
*)work
->hdr
;
5080 TRACE("%p\n", request
);
5082 HTTP_HttpEndRequestW(request
, req
->dwFlags
, req
->dwContext
);
5085 /***********************************************************************
5086 * HttpEndRequestW (WININET.@)
5088 * Ends an HTTP request that was started by HttpSendRequestEx
5091 * TRUE if successful
5095 BOOL WINAPI
HttpEndRequestW(HINTERNET hRequest
,
5096 LPINTERNET_BUFFERSW lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
5098 http_request_t
*request
;
5101 TRACE("%p %p %x %lx -->\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
5105 SetLastError(ERROR_INVALID_PARAMETER
);
5109 request
= (http_request_t
*) get_handle_object( hRequest
);
5111 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5113 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
5115 WININET_Release( &request
->hdr
);
5118 request
->hdr
.dwFlags
|= dwFlags
;
5120 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5123 struct WORKREQ_HTTPENDREQUESTW
*work_endrequest
;
5125 work
.asyncproc
= AsyncHttpEndRequestProc
;
5126 work
.hdr
= WININET_AddRef( &request
->hdr
);
5128 work_endrequest
= &work
.u
.HttpEndRequestW
;
5129 work_endrequest
->dwFlags
= dwFlags
;
5130 work_endrequest
->dwContext
= dwContext
;
5132 INTERNET_AsyncCall(&work
);
5133 res
= ERROR_IO_PENDING
;
5136 res
= HTTP_HttpEndRequestW(request
, dwFlags
, dwContext
);
5138 WININET_Release( &request
->hdr
);
5139 TRACE("%u <--\n", res
);
5140 if(res
!= ERROR_SUCCESS
)
5142 return res
== ERROR_SUCCESS
;
5145 /***********************************************************************
5146 * HttpSendRequestExA (WININET.@)
5148 * Sends the specified request to the HTTP server and allows chunked
5153 * Failure: FALSE, call GetLastError() for more information.
5155 BOOL WINAPI
HttpSendRequestExA(HINTERNET hRequest
,
5156 LPINTERNET_BUFFERSA lpBuffersIn
,
5157 LPINTERNET_BUFFERSA lpBuffersOut
,
5158 DWORD dwFlags
, DWORD_PTR dwContext
)
5160 INTERNET_BUFFERSW BuffersInW
;
5163 LPWSTR header
= NULL
;
5165 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
5166 lpBuffersOut
, dwFlags
, dwContext
);
5170 BuffersInW
.dwStructSize
= sizeof(LPINTERNET_BUFFERSW
);
5171 if (lpBuffersIn
->lpcszHeader
)
5173 headerlen
= MultiByteToWideChar(CP_ACP
,0,lpBuffersIn
->lpcszHeader
,
5174 lpBuffersIn
->dwHeadersLength
,0,0);
5175 header
= heap_alloc(headerlen
*sizeof(WCHAR
));
5176 if (!(BuffersInW
.lpcszHeader
= header
))
5178 SetLastError(ERROR_OUTOFMEMORY
);
5181 BuffersInW
.dwHeadersLength
= MultiByteToWideChar(CP_ACP
, 0,
5182 lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
5186 BuffersInW
.lpcszHeader
= NULL
;
5187 BuffersInW
.dwHeadersTotal
= lpBuffersIn
->dwHeadersTotal
;
5188 BuffersInW
.lpvBuffer
= lpBuffersIn
->lpvBuffer
;
5189 BuffersInW
.dwBufferLength
= lpBuffersIn
->dwBufferLength
;
5190 BuffersInW
.dwBufferTotal
= lpBuffersIn
->dwBufferTotal
;
5191 BuffersInW
.Next
= NULL
;
5194 rc
= HttpSendRequestExW(hRequest
, lpBuffersIn
? &BuffersInW
: NULL
, NULL
, dwFlags
, dwContext
);
5200 /***********************************************************************
5201 * HttpSendRequestExW (WININET.@)
5203 * Sends the specified request to the HTTP server and allows chunked
5208 * Failure: FALSE, call GetLastError() for more information.
5210 BOOL WINAPI
HttpSendRequestExW(HINTERNET hRequest
,
5211 LPINTERNET_BUFFERSW lpBuffersIn
,
5212 LPINTERNET_BUFFERSW lpBuffersOut
,
5213 DWORD dwFlags
, DWORD_PTR dwContext
)
5215 http_request_t
*request
;
5216 http_session_t
*session
;
5220 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
5221 lpBuffersOut
, dwFlags
, dwContext
);
5223 request
= (http_request_t
*) get_handle_object( hRequest
);
5225 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5227 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5231 session
= request
->session
;
5232 assert(session
->hdr
.htype
== WH_HHTTPSESSION
);
5233 hIC
= session
->appInfo
;
5234 assert(hIC
->hdr
.htype
== WH_HINIT
);
5236 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5238 WORKREQUEST workRequest
;
5239 struct WORKREQ_HTTPSENDREQUESTW
*req
;
5241 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
5242 workRequest
.hdr
= WININET_AddRef( &request
->hdr
);
5243 req
= &workRequest
.u
.HttpSendRequestW
;
5248 if (lpBuffersIn
->lpcszHeader
)
5250 if (lpBuffersIn
->dwHeadersLength
== ~0u)
5251 size
= (strlenW( lpBuffersIn
->lpcszHeader
) + 1) * sizeof(WCHAR
);
5253 size
= lpBuffersIn
->dwHeadersLength
* sizeof(WCHAR
);
5255 req
->lpszHeader
= heap_alloc(size
);
5256 memcpy( req
->lpszHeader
, lpBuffersIn
->lpcszHeader
, size
);
5258 else req
->lpszHeader
= NULL
;
5260 req
->dwHeaderLength
= size
/ sizeof(WCHAR
);
5261 req
->lpOptional
= lpBuffersIn
->lpvBuffer
;
5262 req
->dwOptionalLength
= lpBuffersIn
->dwBufferLength
;
5263 req
->dwContentLength
= lpBuffersIn
->dwBufferTotal
;
5267 req
->lpszHeader
= NULL
;
5268 req
->dwHeaderLength
= 0;
5269 req
->lpOptional
= NULL
;
5270 req
->dwOptionalLength
= 0;
5271 req
->dwContentLength
= 0;
5274 req
->bEndRequest
= FALSE
;
5276 INTERNET_AsyncCall(&workRequest
);
5278 * This is from windows.
5280 res
= ERROR_IO_PENDING
;
5285 res
= HTTP_HttpSendRequestW(request
, lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
5286 lpBuffersIn
->lpvBuffer
, lpBuffersIn
->dwBufferLength
,
5287 lpBuffersIn
->dwBufferTotal
, FALSE
);
5289 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, FALSE
);
5294 WININET_Release( &request
->hdr
);
5298 return res
== ERROR_SUCCESS
;
5301 /***********************************************************************
5302 * HttpSendRequestW (WININET.@)
5304 * Sends the specified request to the HTTP server
5311 BOOL WINAPI
HttpSendRequestW(HINTERNET hHttpRequest
, LPCWSTR lpszHeaders
,
5312 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
5314 http_request_t
*request
;
5315 http_session_t
*session
= NULL
;
5316 appinfo_t
*hIC
= NULL
;
5317 DWORD res
= ERROR_SUCCESS
;
5319 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest
,
5320 debugstr_wn(lpszHeaders
, dwHeaderLength
), dwHeaderLength
, lpOptional
, dwOptionalLength
);
5322 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
5323 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5325 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5329 session
= request
->session
;
5330 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
5332 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5336 hIC
= session
->appInfo
;
5337 if (NULL
== hIC
|| hIC
->hdr
.htype
!= WH_HINIT
)
5339 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5343 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5345 WORKREQUEST workRequest
;
5346 struct WORKREQ_HTTPSENDREQUESTW
*req
;
5348 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
5349 workRequest
.hdr
= WININET_AddRef( &request
->hdr
);
5350 req
= &workRequest
.u
.HttpSendRequestW
;
5355 if (dwHeaderLength
== ~0u) size
= (strlenW(lpszHeaders
) + 1) * sizeof(WCHAR
);
5356 else size
= dwHeaderLength
* sizeof(WCHAR
);
5358 req
->lpszHeader
= heap_alloc(size
);
5359 memcpy(req
->lpszHeader
, lpszHeaders
, size
);
5362 req
->lpszHeader
= 0;
5363 req
->dwHeaderLength
= dwHeaderLength
;
5364 req
->lpOptional
= lpOptional
;
5365 req
->dwOptionalLength
= dwOptionalLength
;
5366 req
->dwContentLength
= dwOptionalLength
;
5367 req
->bEndRequest
= TRUE
;
5369 INTERNET_AsyncCall(&workRequest
);
5371 * This is from windows.
5373 res
= ERROR_IO_PENDING
;
5377 res
= HTTP_HttpSendRequestW(request
, lpszHeaders
,
5378 dwHeaderLength
, lpOptional
, dwOptionalLength
,
5379 dwOptionalLength
, TRUE
);
5383 WININET_Release( &request
->hdr
);
5386 return res
== ERROR_SUCCESS
;
5389 /***********************************************************************
5390 * HttpSendRequestA (WININET.@)
5392 * Sends the specified request to the HTTP server
5399 BOOL WINAPI
HttpSendRequestA(HINTERNET hHttpRequest
, LPCSTR lpszHeaders
,
5400 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
5403 LPWSTR szHeaders
=NULL
;
5404 DWORD nLen
=dwHeaderLength
;
5405 if(lpszHeaders
!=NULL
)
5407 nLen
=MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,NULL
,0);
5408 szHeaders
= heap_alloc(nLen
*sizeof(WCHAR
));
5409 MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,szHeaders
,nLen
);
5411 result
= HttpSendRequestW(hHttpRequest
, szHeaders
, nLen
, lpOptional
, dwOptionalLength
);
5412 heap_free(szHeaders
);
5416 /***********************************************************************
5417 * HTTPSESSION_Destroy (internal)
5419 * Deallocate session handle
5422 static void HTTPSESSION_Destroy(object_header_t
*hdr
)
5424 http_session_t
*session
= (http_session_t
*) hdr
;
5426 TRACE("%p\n", session
);
5428 WININET_Release(&session
->appInfo
->hdr
);
5430 heap_free(session
->hostName
);
5431 heap_free(session
->serverName
);
5432 heap_free(session
->password
);
5433 heap_free(session
->userName
);
5436 static DWORD
HTTPSESSION_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
5439 case INTERNET_OPTION_HANDLE_TYPE
:
5440 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5442 if (*size
< sizeof(ULONG
))
5443 return ERROR_INSUFFICIENT_BUFFER
;
5445 *size
= sizeof(DWORD
);
5446 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_CONNECT_HTTP
;
5447 return ERROR_SUCCESS
;
5450 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
5453 static DWORD
HTTPSESSION_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
5455 http_session_t
*ses
= (http_session_t
*)hdr
;
5458 case INTERNET_OPTION_USERNAME
:
5460 heap_free(ses
->userName
);
5461 if (!(ses
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5462 return ERROR_SUCCESS
;
5464 case INTERNET_OPTION_PASSWORD
:
5466 heap_free(ses
->password
);
5467 if (!(ses
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5468 return ERROR_SUCCESS
;
5473 return ERROR_INTERNET_INVALID_OPTION
;
5476 static const object_vtbl_t HTTPSESSIONVtbl
= {
5477 HTTPSESSION_Destroy
,
5479 HTTPSESSION_QueryOption
,
5480 HTTPSESSION_SetOption
,
5489 /***********************************************************************
5490 * HTTP_Connect (internal)
5492 * Create http session handle
5495 * HINTERNET a session handle on success
5499 DWORD
HTTP_Connect(appinfo_t
*hIC
, LPCWSTR lpszServerName
,
5500 INTERNET_PORT serverPort
, LPCWSTR lpszUserName
,
5501 LPCWSTR lpszPassword
, DWORD dwFlags
, DWORD_PTR dwContext
,
5502 DWORD dwInternalFlags
, HINTERNET
*ret
)
5504 http_session_t
*session
= NULL
;
5508 if (!lpszServerName
|| !lpszServerName
[0])
5509 return ERROR_INVALID_PARAMETER
;
5511 assert( hIC
->hdr
.htype
== WH_HINIT
);
5513 session
= alloc_object(&hIC
->hdr
, &HTTPSESSIONVtbl
, sizeof(http_session_t
));
5515 return ERROR_OUTOFMEMORY
;
5518 * According to my tests. The name is not resolved until a request is sent
5521 session
->hdr
.htype
= WH_HHTTPSESSION
;
5522 session
->hdr
.dwFlags
= dwFlags
;
5523 session
->hdr
.dwContext
= dwContext
;
5524 session
->hdr
.dwInternalFlags
|= dwInternalFlags
;
5526 WININET_AddRef( &hIC
->hdr
);
5527 session
->appInfo
= hIC
;
5528 list_add_head( &hIC
->hdr
.children
, &session
->hdr
.entry
);
5530 if(hIC
->proxy
&& hIC
->accessType
== INTERNET_OPEN_TYPE_PROXY
) {
5531 if(hIC
->proxyBypass
)
5532 FIXME("Proxy bypass is ignored.\n");
5534 session
->serverName
= heap_strdupW(lpszServerName
);
5535 session
->hostName
= heap_strdupW(lpszServerName
);
5536 if (lpszUserName
&& lpszUserName
[0])
5537 session
->userName
= heap_strdupW(lpszUserName
);
5538 if (lpszPassword
&& lpszPassword
[0])
5539 session
->password
= heap_strdupW(lpszPassword
);
5540 session
->serverPort
= serverPort
;
5541 session
->hostPort
= serverPort
;
5543 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5544 if (!(session
->hdr
.dwInternalFlags
& INET_OPENURL
))
5546 INTERNET_SendCallback(&hIC
->hdr
, dwContext
,
5547 INTERNET_STATUS_HANDLE_CREATED
, &session
->hdr
.hInternet
,
5552 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5556 TRACE("%p --> %p\n", hIC
, session
);
5558 *ret
= session
->hdr
.hInternet
;
5559 return ERROR_SUCCESS
;
5562 /***********************************************************************
5563 * HTTP_clear_response_headers (internal)
5565 * clear out any old response headers
5567 static void HTTP_clear_response_headers( http_request_t
*request
)
5571 for( i
=0; i
<request
->nCustHeaders
; i
++)
5573 if( !request
->custHeaders
[i
].lpszField
)
5575 if( !request
->custHeaders
[i
].lpszValue
)
5577 if ( request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
5579 HTTP_DeleteCustomHeader( request
, i
);
5584 /***********************************************************************
5585 * HTTP_GetResponseHeaders (internal)
5587 * Read server response
5594 static INT
HTTP_GetResponseHeaders(http_request_t
*request
, BOOL clear
)
5597 WCHAR buffer
[MAX_REPLY_LEN
];
5598 DWORD buflen
= MAX_REPLY_LEN
;
5599 BOOL bSuccess
= FALSE
;
5601 char bufferA
[MAX_REPLY_LEN
];
5602 LPWSTR status_code
= NULL
, status_text
= NULL
;
5603 DWORD cchMaxRawHeaders
= 1024;
5604 LPWSTR lpszRawHeaders
= NULL
;
5606 DWORD cchRawHeaders
= 0;
5607 BOOL codeHundred
= FALSE
;
5611 if(!request
->netconn
)
5615 static const WCHAR szHundred
[] = {'1','0','0',0};
5617 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5619 buflen
= MAX_REPLY_LEN
;
5620 if (!read_line(request
, bufferA
, &buflen
))
5623 /* clear old response headers (eg. from a redirect response) */
5625 HTTP_clear_response_headers( request
);
5630 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5631 /* check is this a status code line? */
5632 if (!strncmpW(buffer
, g_szHttp1_0
, 4))
5634 /* split the version from the status code */
5635 status_code
= strchrW( buffer
, ' ' );
5640 /* split the status code from the status text */
5641 status_text
= strchrW( status_code
, ' ' );
5646 TRACE("version [%s] status code [%s] status text [%s]\n",
5647 debugstr_w(buffer
), debugstr_w(status_code
), debugstr_w(status_text
) );
5649 codeHundred
= (!strcmpW(status_code
, szHundred
));
5651 else if (!codeHundred
)
5653 WARN("No status line at head of response (%s)\n", debugstr_w(buffer
));
5655 heap_free(request
->version
);
5656 heap_free(request
->statusText
);
5658 request
->version
= heap_strdupW(g_szHttp1_0
);
5659 request
->statusText
= heap_strdupW(szOK
);
5661 heap_free(request
->rawHeaders
);
5662 request
->rawHeaders
= heap_strdupW(szDefaultHeader
);
5667 } while (codeHundred
);
5669 /* Add status code */
5670 HTTP_ProcessHeader(request
, szStatus
, status_code
,
5671 HTTP_ADDHDR_FLAG_REPLACE
);
5673 heap_free(request
->version
);
5674 heap_free(request
->statusText
);
5676 request
->version
= heap_strdupW(buffer
);
5677 request
->statusText
= heap_strdupW(status_text
);
5679 /* Restore the spaces */
5680 *(status_code
-1) = ' ';
5681 *(status_text
-1) = ' ';
5683 /* regenerate raw headers */
5684 lpszRawHeaders
= heap_alloc((cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5685 if (!lpszRawHeaders
) goto lend
;
5687 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5688 cchMaxRawHeaders
*= 2;
5689 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5690 if (temp
== NULL
) goto lend
;
5691 lpszRawHeaders
= temp
;
5692 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5693 cchRawHeaders
+= (buflen
-1);
5694 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5695 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5696 lpszRawHeaders
[cchRawHeaders
] = '\0';
5698 /* Parse each response line */
5701 buflen
= MAX_REPLY_LEN
;
5702 if (read_line(request
, bufferA
, &buflen
))
5704 LPWSTR
* pFieldAndValue
;
5706 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA
));
5708 if (!bufferA
[0]) break;
5709 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5711 pFieldAndValue
= HTTP_InterpretHttpHeader(buffer
);
5714 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5715 cchMaxRawHeaders
*= 2;
5716 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5717 if (temp
== NULL
) goto lend
;
5718 lpszRawHeaders
= temp
;
5719 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5720 cchRawHeaders
+= (buflen
-1);
5721 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5722 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5723 lpszRawHeaders
[cchRawHeaders
] = '\0';
5725 HTTP_ProcessHeader(request
, pFieldAndValue
[0], pFieldAndValue
[1],
5726 HTTP_ADDREQ_FLAG_ADD
);
5728 HTTP_FreeTokens(pFieldAndValue
);
5739 /* make sure the response header is terminated with an empty line. Some apps really
5740 truly care about that empty line being there for some reason. Just add it to the
5742 if (cchRawHeaders
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5744 cchMaxRawHeaders
= cchRawHeaders
+ strlenW(szCrLf
);
5745 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5746 if (temp
== NULL
) goto lend
;
5747 lpszRawHeaders
= temp
;
5750 memcpy(&lpszRawHeaders
[cchRawHeaders
], szCrLf
, sizeof(szCrLf
));
5752 heap_free(request
->rawHeaders
);
5753 request
->rawHeaders
= lpszRawHeaders
;
5754 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders
));
5764 heap_free(lpszRawHeaders
);
5769 /***********************************************************************
5770 * HTTP_InterpretHttpHeader (internal)
5772 * Parse server response
5776 * Pointer to array of field, value, NULL on success.
5779 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
)
5781 LPWSTR
* pTokenPair
;
5785 pTokenPair
= heap_alloc_zero(sizeof(*pTokenPair
)*3);
5787 pszColon
= strchrW(buffer
, ':');
5788 /* must have two tokens */
5791 HTTP_FreeTokens(pTokenPair
);
5793 TRACE("No ':' in line: %s\n", debugstr_w(buffer
));
5797 pTokenPair
[0] = heap_alloc((pszColon
- buffer
+ 1) * sizeof(WCHAR
));
5800 HTTP_FreeTokens(pTokenPair
);
5803 memcpy(pTokenPair
[0], buffer
, (pszColon
- buffer
) * sizeof(WCHAR
));
5804 pTokenPair
[0][pszColon
- buffer
] = '\0';
5808 len
= strlenW(pszColon
);
5809 pTokenPair
[1] = heap_alloc((len
+ 1) * sizeof(WCHAR
));
5812 HTTP_FreeTokens(pTokenPair
);
5815 memcpy(pTokenPair
[1], pszColon
, (len
+ 1) * sizeof(WCHAR
));
5817 strip_spaces(pTokenPair
[0]);
5818 strip_spaces(pTokenPair
[1]);
5820 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair
[0]), debugstr_w(pTokenPair
[1]));
5824 /***********************************************************************
5825 * HTTP_ProcessHeader (internal)
5827 * Stuff header into header tables according to <dwModifier>
5831 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5833 static DWORD
HTTP_ProcessHeader(http_request_t
*request
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
)
5835 LPHTTPHEADERW lphttpHdr
= NULL
;
5837 BOOL request_only
= dwModifier
& HTTP_ADDHDR_FLAG_REQ
;
5838 DWORD res
= ERROR_HTTP_INVALID_HEADER
;
5840 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field
), debugstr_w(value
), dwModifier
);
5842 /* REPLACE wins out over ADD */
5843 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5844 dwModifier
&= ~HTTP_ADDHDR_FLAG_ADD
;
5846 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD
)
5849 index
= HTTP_GetCustomHeaderIndex(request
, field
, 0, request_only
);
5853 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD_IF_NEW
)
5854 return ERROR_HTTP_INVALID_HEADER
;
5855 lphttpHdr
= &request
->custHeaders
[index
];
5861 hdr
.lpszField
= (LPWSTR
)field
;
5862 hdr
.lpszValue
= (LPWSTR
)value
;
5863 hdr
.wFlags
= hdr
.wCount
= 0;
5865 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5866 hdr
.wFlags
|= HDR_ISREQUEST
;
5868 return HTTP_InsertCustomHeader(request
, &hdr
);
5870 /* no value to delete */
5871 else return ERROR_SUCCESS
;
5873 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5874 lphttpHdr
->wFlags
|= HDR_ISREQUEST
;
5876 lphttpHdr
->wFlags
&= ~HDR_ISREQUEST
;
5878 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5880 HTTP_DeleteCustomHeader( request
, index
);
5886 hdr
.lpszField
= (LPWSTR
)field
;
5887 hdr
.lpszValue
= (LPWSTR
)value
;
5888 hdr
.wFlags
= hdr
.wCount
= 0;
5890 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5891 hdr
.wFlags
|= HDR_ISREQUEST
;
5893 return HTTP_InsertCustomHeader(request
, &hdr
);
5896 return ERROR_SUCCESS
;
5898 else if (dwModifier
& COALESCEFLAGS
)
5903 INT origlen
= strlenW(lphttpHdr
->lpszValue
);
5904 INT valuelen
= strlenW(value
);
5906 if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
)
5909 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
5911 else if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON
)
5914 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
5917 len
= origlen
+ valuelen
+ ((ch
> 0) ? 2 : 0);
5919 lpsztmp
= heap_realloc(lphttpHdr
->lpszValue
, (len
+1)*sizeof(WCHAR
));
5922 lphttpHdr
->lpszValue
= lpsztmp
;
5923 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5926 lphttpHdr
->lpszValue
[origlen
] = ch
;
5928 lphttpHdr
->lpszValue
[origlen
] = ' ';
5932 memcpy(&lphttpHdr
->lpszValue
[origlen
], value
, valuelen
*sizeof(WCHAR
));
5933 lphttpHdr
->lpszValue
[len
] = '\0';
5934 res
= ERROR_SUCCESS
;
5938 WARN("heap_realloc (%d bytes) failed\n",len
+1);
5939 res
= ERROR_OUTOFMEMORY
;
5942 TRACE("<-- %d\n", res
);
5946 /***********************************************************************
5947 * HTTP_GetCustomHeaderIndex (internal)
5949 * Return index of custom header from header array
5952 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*request
, LPCWSTR lpszField
,
5953 int requested_index
, BOOL request_only
)
5957 TRACE("%s, %d, %d\n", debugstr_w(lpszField
), requested_index
, request_only
);
5959 for (index
= 0; index
< request
->nCustHeaders
; index
++)
5961 if (strcmpiW(request
->custHeaders
[index
].lpszField
, lpszField
))
5964 if (request_only
&& !(request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
5967 if (!request_only
&& (request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
5970 if (requested_index
== 0)
5975 if (index
>= request
->nCustHeaders
)
5978 TRACE("Return: %d\n", index
);
5983 /***********************************************************************
5984 * HTTP_InsertCustomHeader (internal)
5986 * Insert header into array
5989 static DWORD
HTTP_InsertCustomHeader(http_request_t
*request
, LPHTTPHEADERW lpHdr
)
5992 LPHTTPHEADERW lph
= NULL
;
5994 TRACE("--> %s: %s\n", debugstr_w(lpHdr
->lpszField
), debugstr_w(lpHdr
->lpszValue
));
5995 count
= request
->nCustHeaders
+ 1;
5997 lph
= heap_realloc_zero(request
->custHeaders
, sizeof(HTTPHEADERW
) * count
);
5999 lph
= heap_alloc_zero(sizeof(HTTPHEADERW
) * count
);
6002 return ERROR_OUTOFMEMORY
;
6004 request
->custHeaders
= lph
;
6005 request
->custHeaders
[count
-1].lpszField
= heap_strdupW(lpHdr
->lpszField
);
6006 request
->custHeaders
[count
-1].lpszValue
= heap_strdupW(lpHdr
->lpszValue
);
6007 request
->custHeaders
[count
-1].wFlags
= lpHdr
->wFlags
;
6008 request
->custHeaders
[count
-1].wCount
= lpHdr
->wCount
;
6009 request
->nCustHeaders
++;
6011 return ERROR_SUCCESS
;
6015 /***********************************************************************
6016 * HTTP_DeleteCustomHeader (internal)
6018 * Delete header from array
6019 * If this function is called, the indexs may change.
6021 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*request
, DWORD index
)
6023 if( request
->nCustHeaders
<= 0 )
6025 if( index
>= request
->nCustHeaders
)
6027 request
->nCustHeaders
--;
6029 heap_free(request
->custHeaders
[index
].lpszField
);
6030 heap_free(request
->custHeaders
[index
].lpszValue
);
6032 memmove( &request
->custHeaders
[index
], &request
->custHeaders
[index
+1],
6033 (request
->nCustHeaders
- index
)* sizeof(HTTPHEADERW
) );
6034 memset( &request
->custHeaders
[request
->nCustHeaders
], 0, sizeof(HTTPHEADERW
) );
6040 /***********************************************************************
6041 * HTTP_VerifyValidHeader (internal)
6043 * Verify the given header is not invalid for the given http request
6046 static BOOL
HTTP_VerifyValidHeader(http_request_t
*request
, LPCWSTR field
)
6048 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6049 if (!strcmpW(request
->version
, g_szHttp1_0
) && !strcmpiW(field
, szAccept_Encoding
))
6050 return ERROR_HTTP_INVALID_HEADER
;
6052 return ERROR_SUCCESS
;
6055 /***********************************************************************
6056 * IsHostInProxyBypassList (@)
6061 BOOL WINAPI
IsHostInProxyBypassList(DWORD flags
, LPCSTR szHost
, DWORD length
)
6063 FIXME("STUB: flags=%d host=%s length=%d\n",flags
,szHost
,length
);
6067 /***********************************************************************
6068 * InternetShowSecurityInfoByURLA (@)
6070 BOOL WINAPI
InternetShowSecurityInfoByURLA(LPCSTR url
, HWND window
)
6072 FIXME("stub: %s %p\n", url
, window
);
6076 /***********************************************************************
6077 * InternetShowSecurityInfoByURLW (@)
6079 BOOL WINAPI
InternetShowSecurityInfoByURLW(LPCWSTR url
, HWND window
)
6081 FIXME("stub: %s %p\n", debugstr_w(url
), window
);
6085 /***********************************************************************
6086 * ShowX509EncodedCertificate (@)
6088 DWORD WINAPI
ShowX509EncodedCertificate(HWND parent
, LPBYTE cert
, DWORD len
)
6090 PCCERT_CONTEXT certContext
= CertCreateCertificateContext(X509_ASN_ENCODING
,
6096 CRYPTUI_VIEWCERTIFICATE_STRUCTW view
;
6098 memset(&view
, 0, sizeof(view
));
6099 view
.hwndParent
= parent
;
6100 view
.pCertContext
= certContext
;
6101 if (CryptUIDlgViewCertificateW(&view
, NULL
))
6102 ret
= ERROR_SUCCESS
;
6104 ret
= GetLastError();
6105 CertFreeCertificateContext(certContext
);
6108 ret
= GetLastError();