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
);
1887 CloseHandle(thread
);
1892 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
1893 INTERNET_STATUS_CLOSING_CONNECTION
, 0, 0);
1895 free_netconn(req
->netconn
);
1896 req
->netconn
= NULL
;
1898 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
1899 INTERNET_STATUS_CONNECTION_CLOSED
, 0, 0);
1902 static void drain_content(http_request_t
*req
)
1906 if (!req
->netconn
) return;
1908 if (req
->contentLength
== -1)
1910 else if(!strcmpW(req
->verb
, szHEAD
))
1913 try_reuse
= req
->data_stream
->vtbl
->drain_content(req
->data_stream
, req
);
1915 http_release_netconn(req
, try_reuse
);
1918 static BOOL
HTTP_KeepAlive(http_request_t
*request
)
1920 WCHAR szVersion
[10];
1921 WCHAR szConnectionResponse
[20];
1922 DWORD dwBufferSize
= sizeof(szVersion
);
1923 BOOL keepalive
= FALSE
;
1925 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1926 * the connection is keep-alive by default */
1927 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_VERSION
, szVersion
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1928 && !strcmpiW(szVersion
, g_szHttp1_1
))
1933 dwBufferSize
= sizeof(szConnectionResponse
);
1934 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_PROXY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1935 || HTTP_HttpQueryInfoW(request
, HTTP_QUERY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
1937 keepalive
= !strcmpiW(szConnectionResponse
, szKeepAlive
);
1943 static void HTTPREQ_CloseConnection(object_header_t
*hdr
)
1945 http_request_t
*req
= (http_request_t
*)hdr
;
1950 static DWORD
HTTPREQ_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
1952 http_request_t
*req
= (http_request_t
*)hdr
;
1955 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO
:
1957 http_session_t
*session
= req
->session
;
1958 INTERNET_DIAGNOSTIC_SOCKET_INFO
*info
= buffer
;
1960 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1962 if (*size
< sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
))
1963 return ERROR_INSUFFICIENT_BUFFER
;
1964 *size
= sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
);
1965 /* FIXME: can't get a SOCKET from our connection since we don't use
1969 /* FIXME: get source port from req->netConnection */
1970 info
->SourcePort
= 0;
1971 info
->DestPort
= session
->hostPort
;
1973 if (HTTP_KeepAlive(req
))
1974 info
->Flags
|= IDSI_FLAG_KEEP_ALIVE
;
1975 if (session
->appInfo
->proxy
&& session
->appInfo
->proxy
[0] != 0)
1976 info
->Flags
|= IDSI_FLAG_PROXY
;
1977 if (req
->netconn
->useSSL
)
1978 info
->Flags
|= IDSI_FLAG_SECURE
;
1980 return ERROR_SUCCESS
;
1983 case INTERNET_OPTION_SECURITY_FLAGS
:
1987 if (*size
< sizeof(ULONG
))
1988 return ERROR_INSUFFICIENT_BUFFER
;
1990 *size
= sizeof(DWORD
);
1992 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1993 flags
|= SECURITY_FLAG_SECURE
;
1994 flags
|= req
->security_flags
;
1996 int bits
= NETCON_GetCipherStrength(req
->netconn
);
1998 flags
|= SECURITY_FLAG_STRENGTH_STRONG
;
1999 else if (bits
>= 56)
2000 flags
|= SECURITY_FLAG_STRENGTH_MEDIUM
;
2002 flags
|= SECURITY_FLAG_STRENGTH_WEAK
;
2004 *(DWORD
*)buffer
= flags
;
2005 return ERROR_SUCCESS
;
2008 case INTERNET_OPTION_HANDLE_TYPE
:
2009 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2011 if (*size
< sizeof(ULONG
))
2012 return ERROR_INSUFFICIENT_BUFFER
;
2014 *size
= sizeof(DWORD
);
2015 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_HTTP_REQUEST
;
2016 return ERROR_SUCCESS
;
2018 case INTERNET_OPTION_URL
: {
2019 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2024 static const WCHAR httpW
[] = {'h','t','t','p',':','/','/',0};
2026 TRACE("INTERNET_OPTION_URL\n");
2028 host
= HTTP_GetHeader(req
, hostW
);
2029 strcpyW(url
, httpW
);
2030 strcatW(url
, host
->lpszValue
);
2031 if (NULL
!= (pch
= strchrW(url
+ strlenW(httpW
), ':')))
2033 strcatW(url
, req
->path
);
2035 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url
));
2038 len
= (strlenW(url
)+1) * sizeof(WCHAR
);
2040 return ERROR_INSUFFICIENT_BUFFER
;
2043 strcpyW(buffer
, url
);
2044 return ERROR_SUCCESS
;
2046 len
= WideCharToMultiByte(CP_ACP
, 0, url
, -1, buffer
, *size
, NULL
, NULL
);
2048 return ERROR_INSUFFICIENT_BUFFER
;
2051 return ERROR_SUCCESS
;
2055 case INTERNET_OPTION_CACHE_TIMESTAMPS
: {
2056 INTERNET_CACHE_ENTRY_INFOW
*info
;
2057 INTERNET_CACHE_TIMESTAMPS
*ts
= buffer
;
2058 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2059 DWORD nbytes
, error
;
2062 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2064 if (*size
< sizeof(*ts
))
2066 *size
= sizeof(*ts
);
2067 return ERROR_INSUFFICIENT_BUFFER
;
2070 HTTP_GetRequestURL(req
, url
);
2071 ret
= GetUrlCacheEntryInfoW(url
, NULL
, &nbytes
);
2072 error
= GetLastError();
2073 if (!ret
&& error
== ERROR_INSUFFICIENT_BUFFER
)
2075 if (!(info
= heap_alloc(nbytes
)))
2076 return ERROR_OUTOFMEMORY
;
2078 GetUrlCacheEntryInfoW(url
, info
, &nbytes
);
2080 ts
->ftExpires
= info
->ExpireTime
;
2081 ts
->ftLastModified
= info
->LastModifiedTime
;
2084 *size
= sizeof(*ts
);
2085 return ERROR_SUCCESS
;
2090 case INTERNET_OPTION_DATAFILE_NAME
: {
2093 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2095 if(!req
->cacheFile
) {
2097 return ERROR_INTERNET_ITEM_NOT_FOUND
;
2101 req_size
= (lstrlenW(req
->cacheFile
)+1) * sizeof(WCHAR
);
2102 if(*size
< req_size
)
2103 return ERROR_INSUFFICIENT_BUFFER
;
2106 memcpy(buffer
, req
->cacheFile
, *size
);
2107 return ERROR_SUCCESS
;
2109 req_size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
, -1, NULL
, 0, NULL
, NULL
);
2110 if (req_size
> *size
)
2111 return ERROR_INSUFFICIENT_BUFFER
;
2113 *size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
,
2114 -1, buffer
, *size
, NULL
, NULL
);
2115 return ERROR_SUCCESS
;
2119 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT
: {
2120 PCCERT_CONTEXT context
;
2122 if(*size
< sizeof(INTERNET_CERTIFICATE_INFOA
)) {
2123 *size
= sizeof(INTERNET_CERTIFICATE_INFOA
);
2124 return ERROR_INSUFFICIENT_BUFFER
;
2127 context
= (PCCERT_CONTEXT
)NETCON_GetCert(req
->netconn
);
2129 INTERNET_CERTIFICATE_INFOA
*info
= (INTERNET_CERTIFICATE_INFOA
*)buffer
;
2132 memset(info
, 0, sizeof(INTERNET_CERTIFICATE_INFOW
));
2133 info
->ftExpiry
= context
->pCertInfo
->NotAfter
;
2134 info
->ftStart
= context
->pCertInfo
->NotBefore
;
2135 len
= CertNameToStrA(context
->dwCertEncodingType
,
2136 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
2137 info
->lpszSubjectInfo
= LocalAlloc(0, len
);
2138 if(info
->lpszSubjectInfo
)
2139 CertNameToStrA(context
->dwCertEncodingType
,
2140 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
,
2141 info
->lpszSubjectInfo
, len
);
2142 len
= CertNameToStrA(context
->dwCertEncodingType
,
2143 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
2144 info
->lpszIssuerInfo
= LocalAlloc(0, len
);
2145 if(info
->lpszIssuerInfo
)
2146 CertNameToStrA(context
->dwCertEncodingType
,
2147 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
,
2148 info
->lpszIssuerInfo
, len
);
2149 info
->dwKeySize
= NETCON_GetCipherStrength(req
->netconn
);
2150 CertFreeCertificateContext(context
);
2151 return ERROR_SUCCESS
;
2154 case INTERNET_OPTION_CONNECT_TIMEOUT
:
2155 if (*size
< sizeof(DWORD
))
2156 return ERROR_INSUFFICIENT_BUFFER
;
2158 *size
= sizeof(DWORD
);
2159 *(DWORD
*)buffer
= req
->connect_timeout
;
2160 return ERROR_SUCCESS
;
2163 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
2166 static DWORD
HTTPREQ_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
2168 http_request_t
*req
= (http_request_t
*)hdr
;
2171 case INTERNET_OPTION_SECURITY_FLAGS
:
2175 if (!buffer
|| size
!= sizeof(DWORD
))
2176 return ERROR_INVALID_PARAMETER
;
2177 flags
= *(DWORD
*)buffer
;
2178 TRACE("%08x\n", flags
);
2179 req
->security_flags
= flags
;
2181 req
->netconn
->security_flags
= flags
;
2182 return ERROR_SUCCESS
;
2184 case INTERNET_OPTION_CONNECT_TIMEOUT
:
2185 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2186 req
->connect_timeout
= *(DWORD
*)buffer
;
2187 return ERROR_SUCCESS
;
2189 case INTERNET_OPTION_SEND_TIMEOUT
:
2190 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2191 req
->send_timeout
= *(DWORD
*)buffer
;
2192 return ERROR_SUCCESS
;
2194 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
2195 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2196 req
->receive_timeout
= *(DWORD
*)buffer
;
2197 return ERROR_SUCCESS
;
2199 case INTERNET_OPTION_USERNAME
:
2200 heap_free(req
->session
->userName
);
2201 if (!(req
->session
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2202 return ERROR_SUCCESS
;
2204 case INTERNET_OPTION_PASSWORD
:
2205 heap_free(req
->session
->password
);
2206 if (!(req
->session
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2207 return ERROR_SUCCESS
;
2208 case INTERNET_OPTION_HTTP_DECODING
:
2209 if(size
!= sizeof(BOOL
))
2210 return ERROR_INVALID_PARAMETER
;
2211 req
->decoding
= *(BOOL
*)buffer
;
2212 return ERROR_SUCCESS
;
2215 return INET_SetOption(hdr
, option
, buffer
, size
);
2218 /* read some more data into the read buffer (the read section must be held) */
2219 static DWORD
read_more_data( http_request_t
*req
, int maxlen
)
2226 /* move existing data to the start of the buffer */
2228 memmove( req
->read_buf
, req
->read_buf
+ req
->read_pos
, req
->read_size
);
2232 if (maxlen
== -1) maxlen
= sizeof(req
->read_buf
);
2234 res
= NETCON_recv( req
->netconn
, req
->read_buf
+ req
->read_size
,
2235 maxlen
- req
->read_size
, 0, &len
);
2236 if(res
== ERROR_SUCCESS
)
2237 req
->read_size
+= len
;
2242 /* remove some amount of data from the read buffer (the read section must be held) */
2243 static void remove_data( http_request_t
*req
, int count
)
2245 if (!(req
->read_size
-= count
)) req
->read_pos
= 0;
2246 else req
->read_pos
+= count
;
2249 static BOOL
read_line( http_request_t
*req
, LPSTR buffer
, DWORD
*len
)
2251 int count
, bytes_read
, pos
= 0;
2254 EnterCriticalSection( &req
->read_section
);
2257 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
2261 count
= eol
- (req
->read_buf
+ req
->read_pos
);
2262 bytes_read
= count
+ 1;
2264 else count
= bytes_read
= req
->read_size
;
2266 count
= min( count
, *len
- pos
);
2267 memcpy( buffer
+ pos
, req
->read_buf
+ req
->read_pos
, count
);
2269 remove_data( req
, bytes_read
);
2272 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
|| !req
->read_size
)
2275 TRACE( "returning empty string %u\n", res
);
2276 LeaveCriticalSection( &req
->read_section
);
2277 INTERNET_SetLastError(res
);
2281 LeaveCriticalSection( &req
->read_section
);
2285 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
2288 buffer
[*len
- 1] = 0;
2289 TRACE( "returning %s\n", debugstr_a(buffer
));
2293 /* check if we have reached the end of the data to read (the read section must be held) */
2294 static BOOL
end_of_read_data( http_request_t
*req
)
2296 return !req
->read_size
&& req
->data_stream
->vtbl
->end_of_data(req
->data_stream
, req
);
2299 /* fetch some more data into the read buffer (the read section must be held) */
2300 static DWORD
refill_read_buffer(http_request_t
*req
, read_mode_t read_mode
, DWORD
*read_bytes
)
2304 if(req
->read_size
== sizeof(req
->read_buf
))
2305 return ERROR_SUCCESS
;
2309 memmove(req
->read_buf
, req
->read_buf
+req
->read_pos
, req
->read_size
);
2313 res
= req
->data_stream
->vtbl
->read(req
->data_stream
, req
, req
->read_buf
+req
->read_size
,
2314 sizeof(req
->read_buf
)-req
->read_size
, &read
, read_mode
);
2315 req
->read_size
+= read
;
2317 TRACE("read %u bytes, read_size %u\n", read
, req
->read_size
);
2323 /* return the size of data available to be read immediately (the read section must be held) */
2324 static DWORD
get_avail_data( http_request_t
*req
)
2326 return req
->read_size
+ req
->data_stream
->vtbl
->get_avail_data(req
->data_stream
, req
);
2329 static DWORD
netconn_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
2331 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2335 NETCON_query_data_available(req
->netconn
, &avail
);
2336 return netconn_stream
->content_length
== ~0u
2338 : min(avail
, netconn_stream
->content_length
-netconn_stream
->content_read
);
2341 static BOOL
netconn_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
2343 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2344 return netconn_stream
->content_read
== netconn_stream
->content_length
|| !req
->netconn
;
2347 static DWORD
netconn_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
2348 DWORD
*read
, read_mode_t read_mode
)
2350 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2353 size
= min(size
, netconn_stream
->content_length
-netconn_stream
->content_read
);
2355 if(read_mode
== READMODE_NOBLOCK
)
2356 size
= min(size
, netconn_get_avail_data(stream
, req
));
2358 if(size
&& req
->netconn
) {
2359 if(NETCON_recv(req
->netconn
, buf
, size
, read_mode
== READMODE_SYNC
? MSG_WAITALL
: 0, &len
) != ERROR_SUCCESS
)
2362 netconn_stream
->content_length
= netconn_stream
->content_read
;
2365 netconn_stream
->content_read
+= *read
= len
;
2366 TRACE("read %u bytes\n", len
);
2367 return ERROR_SUCCESS
;
2370 static BOOL
netconn_drain_content(data_stream_t
*stream
, http_request_t
*req
)
2372 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2377 if(netconn_end_of_data(stream
, req
))
2381 avail
= netconn_get_avail_data(stream
, req
);
2385 if(NETCON_recv(req
->netconn
, buf
, min(avail
, sizeof(buf
)), 0, &len
) != ERROR_SUCCESS
)
2388 netconn_stream
->content_read
+= len
;
2389 }while(netconn_stream
->content_read
< netconn_stream
->content_length
);
2394 static void netconn_destroy(data_stream_t
*stream
)
2398 static const data_stream_vtbl_t netconn_stream_vtbl
= {
2399 netconn_get_avail_data
,
2400 netconn_end_of_data
,
2402 netconn_drain_content
,
2406 /* read some more data into the read buffer (the read section must be held) */
2407 static DWORD
read_more_chunked_data(chunked_stream_t
*stream
, http_request_t
*req
, int maxlen
)
2412 if (stream
->buf_pos
)
2414 /* move existing data to the start of the buffer */
2415 if(stream
->buf_size
)
2416 memmove(stream
->buf
, stream
->buf
+ stream
->buf_pos
, stream
->buf_size
);
2417 stream
->buf_pos
= 0;
2420 if (maxlen
== -1) maxlen
= sizeof(stream
->buf
);
2422 res
= NETCON_recv( req
->netconn
, stream
->buf
+ stream
->buf_size
,
2423 maxlen
- stream
->buf_size
, 0, &len
);
2424 if(res
== ERROR_SUCCESS
)
2425 stream
->buf_size
+= len
;
2430 /* remove some amount of data from the read buffer (the read section must be held) */
2431 static void remove_chunked_data(chunked_stream_t
*stream
, int count
)
2433 if (!(stream
->buf_size
-= count
)) stream
->buf_pos
= 0;
2434 else stream
->buf_pos
+= count
;
2437 /* discard data contents until we reach end of line (the read section must be held) */
2438 static DWORD
discard_chunked_eol(chunked_stream_t
*stream
, http_request_t
*req
)
2444 BYTE
*eol
= memchr(stream
->buf
+ stream
->buf_pos
, '\n', stream
->buf_size
);
2447 remove_chunked_data(stream
, (eol
+ 1) - (stream
->buf
+ stream
->buf_pos
));
2450 stream
->buf_pos
= stream
->buf_size
= 0; /* discard everything */
2451 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2452 } while (stream
->buf_size
);
2453 return ERROR_SUCCESS
;
2456 /* read the size of the next chunk (the read section must be held) */
2457 static DWORD
start_next_chunk(chunked_stream_t
*stream
, http_request_t
*req
)
2460 DWORD chunk_size
= 0, res
;
2462 if(stream
->chunk_size
!= ~0u && (res
= discard_chunked_eol(stream
, req
)) != ERROR_SUCCESS
)
2467 while (stream
->buf_size
)
2469 char ch
= stream
->buf
[stream
->buf_pos
];
2470 if (ch
>= '0' && ch
<= '9') chunk_size
= chunk_size
* 16 + ch
- '0';
2471 else if (ch
>= 'a' && ch
<= 'f') chunk_size
= chunk_size
* 16 + ch
- 'a' + 10;
2472 else if (ch
>= 'A' && ch
<= 'F') chunk_size
= chunk_size
* 16 + ch
- 'A' + 10;
2473 else if (ch
== ';' || ch
== '\r' || ch
== '\n')
2475 TRACE( "reading %u byte chunk\n", chunk_size
);
2476 stream
->chunk_size
= chunk_size
;
2477 req
->contentLength
+= chunk_size
;
2478 return discard_chunked_eol(stream
, req
);
2480 remove_chunked_data(stream
, 1);
2482 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2483 if (!stream
->buf_size
)
2485 stream
->chunk_size
= 0;
2486 return ERROR_SUCCESS
;
2491 static DWORD
chunked_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
2493 /* Allow reading only from read buffer */
2497 static BOOL
chunked_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
2499 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2500 return !chunked_stream
->chunk_size
;
2503 static DWORD
chunked_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
2504 DWORD
*read
, read_mode_t read_mode
)
2506 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2507 DWORD read_bytes
= 0, ret_read
= 0, res
= ERROR_SUCCESS
;
2509 if(chunked_stream
->chunk_size
== ~0u) {
2510 res
= start_next_chunk(chunked_stream
, req
);
2511 if(res
!= ERROR_SUCCESS
)
2515 while(size
&& chunked_stream
->chunk_size
) {
2516 if(chunked_stream
->buf_size
) {
2517 read_bytes
= min(size
, min(chunked_stream
->buf_size
, chunked_stream
->chunk_size
));
2519 /* this could block */
2520 if(read_mode
== READMODE_NOBLOCK
&& read_bytes
== chunked_stream
->chunk_size
)
2523 memcpy(buf
+ret_read
, chunked_stream
->buf
+chunked_stream
->buf_pos
, read_bytes
);
2524 remove_chunked_data(chunked_stream
, read_bytes
);
2526 read_bytes
= min(size
, chunked_stream
->chunk_size
);
2528 if(read_mode
== READMODE_NOBLOCK
) {
2531 if(!NETCON_query_data_available(req
->netconn
, &avail
) || !avail
)
2533 if(read_bytes
> avail
)
2536 /* this could block */
2537 if(read_bytes
== chunked_stream
->chunk_size
)
2541 res
= NETCON_recv(req
->netconn
, (char *)buf
+ret_read
, read_bytes
, 0, (int*)&read_bytes
);
2542 if(res
!= ERROR_SUCCESS
)
2546 chunked_stream
->chunk_size
-= read_bytes
;
2548 ret_read
+= read_bytes
;
2549 if(!chunked_stream
->chunk_size
) {
2550 assert(read_mode
!= READMODE_NOBLOCK
);
2551 res
= start_next_chunk(chunked_stream
, req
);
2552 if(res
!= ERROR_SUCCESS
)
2556 if(read_mode
== READMODE_ASYNC
)
2557 read_mode
= READMODE_NOBLOCK
;
2560 TRACE("read %u bytes\n", ret_read
);
2565 static BOOL
chunked_drain_content(data_stream_t
*stream
, http_request_t
*req
)
2567 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2569 /* FIXME: we can do better */
2570 return !chunked_stream
->chunk_size
;
2573 static void chunked_destroy(data_stream_t
*stream
)
2575 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2576 heap_free(chunked_stream
);
2579 static const data_stream_vtbl_t chunked_stream_vtbl
= {
2580 chunked_get_avail_data
,
2581 chunked_end_of_data
,
2583 chunked_drain_content
,
2587 /* set the request content length based on the headers */
2588 static DWORD
set_content_length(http_request_t
*request
, DWORD status_code
)
2590 static const WCHAR szChunked
[] = {'c','h','u','n','k','e','d',0};
2594 if(status_code
== HTTP_STATUS_NO_CONTENT
) {
2595 request
->contentLength
= request
->netconn_stream
.content_length
= 0;
2596 return ERROR_SUCCESS
;
2599 size
= sizeof(request
->contentLength
);
2600 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_CONTENT_LENGTH
,
2601 &request
->contentLength
, &size
, NULL
) != ERROR_SUCCESS
)
2602 request
->contentLength
= ~0u;
2603 request
->netconn_stream
.content_length
= request
->contentLength
;
2604 request
->netconn_stream
.content_read
= request
->read_size
;
2606 size
= sizeof(encoding
);
2607 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_TRANSFER_ENCODING
, encoding
, &size
, NULL
) == ERROR_SUCCESS
&&
2608 !strcmpiW(encoding
, szChunked
))
2610 chunked_stream_t
*chunked_stream
;
2612 chunked_stream
= heap_alloc(sizeof(*chunked_stream
));
2614 return ERROR_OUTOFMEMORY
;
2616 chunked_stream
->data_stream
.vtbl
= &chunked_stream_vtbl
;
2617 chunked_stream
->buf_size
= chunked_stream
->buf_pos
= 0;
2618 chunked_stream
->chunk_size
= ~0u;
2620 if(request
->read_size
) {
2621 memcpy(chunked_stream
->buf
, request
->read_buf
+request
->read_pos
, request
->read_size
);
2622 chunked_stream
->buf_size
= request
->read_size
;
2623 request
->read_size
= request
->read_pos
= 0;
2626 request
->data_stream
= &chunked_stream
->data_stream
;
2627 request
->contentLength
= ~0u;
2628 request
->read_chunked
= TRUE
;
2631 if(request
->decoding
) {
2634 static const WCHAR gzipW
[] = {'g','z','i','p',0};
2636 encoding_idx
= HTTP_GetCustomHeaderIndex(request
, szContent_Encoding
, 0, FALSE
);
2637 if(encoding_idx
!= -1 && !strcmpiW(request
->custHeaders
[encoding_idx
].lpszValue
, gzipW
))
2638 return init_gzip_stream(request
);
2641 return ERROR_SUCCESS
;
2644 static void send_request_complete(http_request_t
*req
, DWORD_PTR result
, DWORD error
)
2646 INTERNET_ASYNC_RESULT iar
;
2648 iar
.dwResult
= result
;
2649 iar
.dwError
= error
;
2651 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2652 sizeof(INTERNET_ASYNC_RESULT
));
2655 static void HTTP_ReceiveRequestData(http_request_t
*req
, BOOL first_notif
)
2657 DWORD res
, read
= 0, avail
= 0;
2662 EnterCriticalSection( &req
->read_section
);
2664 mode
= first_notif
&& req
->read_size
? READMODE_NOBLOCK
: READMODE_ASYNC
;
2665 res
= refill_read_buffer(req
, mode
, &read
);
2666 if(res
== ERROR_SUCCESS
&& !first_notif
)
2667 avail
= get_avail_data(req
);
2669 LeaveCriticalSection( &req
->read_section
);
2671 if(res
!= ERROR_SUCCESS
|| (mode
!= READMODE_NOBLOCK
&& !read
)) {
2672 WARN("res %u read %u, closing connection\n", res
, read
);
2673 http_release_netconn(req
, FALSE
);
2676 if(res
== ERROR_SUCCESS
)
2677 send_request_complete(req
, req
->session
->hdr
.dwInternalFlags
& INET_OPENURL
? (DWORD_PTR
)req
->hdr
.hInternet
: 1, avail
);
2679 send_request_complete(req
, 0, res
);
2682 /* read data from the http connection (the read section must be held) */
2683 static DWORD
HTTPREQ_Read(http_request_t
*req
, void *buffer
, DWORD size
, DWORD
*read
, BOOL sync
)
2685 DWORD current_read
= 0, ret_read
= 0;
2686 read_mode_t read_mode
;
2687 DWORD res
= ERROR_SUCCESS
;
2689 read_mode
= req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
? READMODE_ASYNC
: READMODE_SYNC
;
2691 EnterCriticalSection( &req
->read_section
);
2693 if(req
->read_size
) {
2694 ret_read
= min(size
, req
->read_size
);
2695 memcpy(buffer
, req
->read_buf
+req
->read_pos
, ret_read
);
2696 req
->read_size
-= ret_read
;
2697 req
->read_pos
+= ret_read
;
2698 if(read_mode
== READMODE_ASYNC
)
2699 read_mode
= READMODE_NOBLOCK
;
2702 if(ret_read
< size
) {
2703 res
= req
->data_stream
->vtbl
->read(req
->data_stream
, req
, (BYTE
*)buffer
+ret_read
, size
-ret_read
, ¤t_read
, read_mode
);
2704 ret_read
+= current_read
;
2707 LeaveCriticalSection( &req
->read_section
);
2710 TRACE( "retrieved %u bytes (%u)\n", ret_read
, req
->contentLength
);
2712 if(req
->hCacheFile
&& res
== ERROR_SUCCESS
&& ret_read
) {
2716 res
= WriteFile(req
->hCacheFile
, buffer
, ret_read
, &written
, NULL
);
2718 WARN("WriteFile failed: %u\n", GetLastError());
2721 if(size
&& !ret_read
)
2722 http_release_netconn(req
, res
== ERROR_SUCCESS
);
2728 static DWORD
HTTPREQ_ReadFile(object_header_t
*hdr
, void *buffer
, DWORD size
, DWORD
*read
)
2730 http_request_t
*req
= (http_request_t
*)hdr
;
2733 EnterCriticalSection( &req
->read_section
);
2734 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2735 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2737 res
= HTTPREQ_Read(req
, buffer
, size
, read
, TRUE
);
2738 if(res
== ERROR_SUCCESS
)
2740 LeaveCriticalSection( &req
->read_section
);
2745 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST
*workRequest
)
2747 struct WORKREQ_INTERNETREADFILEEXA
const *data
= &workRequest
->u
.InternetReadFileExA
;
2748 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2751 TRACE("INTERNETREADFILEEXA %p\n", workRequest
->hdr
);
2753 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2754 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2756 send_request_complete(req
, res
== ERROR_SUCCESS
, res
);
2759 static DWORD
HTTPREQ_ReadFileExA(object_header_t
*hdr
, INTERNET_BUFFERSA
*buffers
,
2760 DWORD flags
, DWORD_PTR context
)
2762 http_request_t
*req
= (http_request_t
*)hdr
;
2763 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2765 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2766 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2768 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2769 return ERROR_INVALID_PARAMETER
;
2771 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2773 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2775 WORKREQUEST workRequest
;
2777 if (TryEnterCriticalSection( &req
->read_section
))
2779 if (get_avail_data(req
))
2781 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2782 &buffers
->dwBufferLength
, FALSE
);
2783 size
= buffers
->dwBufferLength
;
2784 LeaveCriticalSection( &req
->read_section
);
2787 LeaveCriticalSection( &req
->read_section
);
2790 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExAProc
;
2791 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2792 workRequest
.u
.InternetReadFileExA
.lpBuffersOut
= buffers
;
2794 INTERNET_AsyncCall(&workRequest
);
2796 return ERROR_IO_PENDING
;
2800 size
= buffers
->dwBufferLength
;
2802 EnterCriticalSection( &req
->read_section
);
2803 if(hdr
->dwError
== ERROR_SUCCESS
)
2804 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2805 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2806 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2809 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2810 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2811 if(res
!= ERROR_SUCCESS
)
2814 read
+= buffers
->dwBufferLength
;
2815 if(read
== size
|| end_of_read_data(req
))
2818 LeaveCriticalSection( &req
->read_section
);
2820 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2821 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2822 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2823 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2825 EnterCriticalSection( &req
->read_section
);
2828 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2829 hdr
->dwError
= ERROR_SUCCESS
;
2831 error
= hdr
->dwError
;
2833 LeaveCriticalSection( &req
->read_section
);
2834 size
= buffers
->dwBufferLength
;
2835 buffers
->dwBufferLength
= read
;
2838 if (res
== ERROR_SUCCESS
) {
2839 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2840 &size
, sizeof(size
));
2843 return res
==ERROR_SUCCESS
? error
: res
;
2846 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST
*workRequest
)
2848 struct WORKREQ_INTERNETREADFILEEXW
const *data
= &workRequest
->u
.InternetReadFileExW
;
2849 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2852 TRACE("INTERNETREADFILEEXW %p\n", workRequest
->hdr
);
2854 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2855 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2857 send_request_complete(req
, res
== ERROR_SUCCESS
, res
);
2860 static DWORD
HTTPREQ_ReadFileExW(object_header_t
*hdr
, INTERNET_BUFFERSW
*buffers
,
2861 DWORD flags
, DWORD_PTR context
)
2864 http_request_t
*req
= (http_request_t
*)hdr
;
2865 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2867 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2868 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2870 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2871 return ERROR_INVALID_PARAMETER
;
2873 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2875 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2877 WORKREQUEST workRequest
;
2879 if (TryEnterCriticalSection( &req
->read_section
))
2881 if (get_avail_data(req
))
2883 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2884 &buffers
->dwBufferLength
, FALSE
);
2885 size
= buffers
->dwBufferLength
;
2886 LeaveCriticalSection( &req
->read_section
);
2889 LeaveCriticalSection( &req
->read_section
);
2892 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExWProc
;
2893 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2894 workRequest
.u
.InternetReadFileExW
.lpBuffersOut
= buffers
;
2896 INTERNET_AsyncCall(&workRequest
);
2898 return ERROR_IO_PENDING
;
2902 size
= buffers
->dwBufferLength
;
2904 EnterCriticalSection( &req
->read_section
);
2905 if(hdr
->dwError
== ERROR_SUCCESS
)
2906 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2907 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2908 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2911 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2912 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2913 if(res
!= ERROR_SUCCESS
)
2916 read
+= buffers
->dwBufferLength
;
2917 if(read
== size
|| end_of_read_data(req
))
2920 LeaveCriticalSection( &req
->read_section
);
2922 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2923 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2924 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2925 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2927 EnterCriticalSection( &req
->read_section
);
2930 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2931 hdr
->dwError
= ERROR_SUCCESS
;
2933 error
= hdr
->dwError
;
2935 LeaveCriticalSection( &req
->read_section
);
2936 size
= buffers
->dwBufferLength
;
2937 buffers
->dwBufferLength
= read
;
2940 if (res
== ERROR_SUCCESS
) {
2941 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2942 &size
, sizeof(size
));
2945 return res
==ERROR_SUCCESS
? error
: res
;
2948 static DWORD
HTTPREQ_WriteFile(object_header_t
*hdr
, const void *buffer
, DWORD size
, DWORD
*written
)
2951 http_request_t
*request
= (http_request_t
*)hdr
;
2953 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
2956 res
= NETCON_send(request
->netconn
, buffer
, size
, 0, (LPINT
)written
);
2957 if (res
== ERROR_SUCCESS
)
2958 request
->bytesWritten
+= *written
;
2960 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_SENT
, written
, sizeof(DWORD
));
2964 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST
*workRequest
)
2966 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2968 HTTP_ReceiveRequestData(req
, FALSE
);
2971 static DWORD
HTTPREQ_QueryDataAvailable(object_header_t
*hdr
, DWORD
*available
, DWORD flags
, DWORD_PTR ctx
)
2973 http_request_t
*req
= (http_request_t
*)hdr
;
2975 TRACE("(%p %p %x %lx)\n", req
, available
, flags
, ctx
);
2977 if (req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
2979 WORKREQUEST workRequest
;
2981 /* never wait, if we can't enter the section we queue an async request right away */
2982 if (TryEnterCriticalSection( &req
->read_section
))
2984 refill_read_buffer(req
, READMODE_NOBLOCK
, NULL
);
2985 if ((*available
= get_avail_data( req
))) goto done
;
2986 if (end_of_read_data( req
)) goto done
;
2987 LeaveCriticalSection( &req
->read_section
);
2990 workRequest
.asyncproc
= HTTPREQ_AsyncQueryDataAvailableProc
;
2991 workRequest
.hdr
= WININET_AddRef( &req
->hdr
);
2993 INTERNET_AsyncCall(&workRequest
);
2995 return ERROR_IO_PENDING
;
2998 EnterCriticalSection( &req
->read_section
);
3000 if (!(*available
= get_avail_data( req
)) && !end_of_read_data( req
))
3002 refill_read_buffer( req
, READMODE_ASYNC
, NULL
);
3003 *available
= get_avail_data( req
);
3007 LeaveCriticalSection( &req
->read_section
);
3009 TRACE( "returning %u\n", *available
);
3010 return ERROR_SUCCESS
;
3013 static const object_vtbl_t HTTPREQVtbl
= {
3015 HTTPREQ_CloseConnection
,
3016 HTTPREQ_QueryOption
,
3019 HTTPREQ_ReadFileExA
,
3020 HTTPREQ_ReadFileExW
,
3022 HTTPREQ_QueryDataAvailable
,
3026 /***********************************************************************
3027 * HTTP_HttpOpenRequestW (internal)
3029 * Open a HTTP request handle
3032 * HINTERNET a HTTP request handle on success
3036 static DWORD
HTTP_HttpOpenRequestW(http_session_t
*session
,
3037 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
3038 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
3039 DWORD dwFlags
, DWORD_PTR dwContext
, HINTERNET
*ret
)
3041 appinfo_t
*hIC
= session
->appInfo
;
3042 http_request_t
*request
;
3043 DWORD len
, res
= ERROR_SUCCESS
;
3047 request
= alloc_object(&session
->hdr
, &HTTPREQVtbl
, sizeof(http_request_t
));
3049 return ERROR_OUTOFMEMORY
;
3051 request
->hdr
.htype
= WH_HHTTPREQ
;
3052 request
->hdr
.dwFlags
= dwFlags
;
3053 request
->hdr
.dwContext
= dwContext
;
3054 request
->contentLength
= ~0u;
3056 request
->netconn_stream
.data_stream
.vtbl
= &netconn_stream_vtbl
;
3057 request
->data_stream
= &request
->netconn_stream
.data_stream
;
3058 request
->connect_timeout
= session
->connect_timeout
;
3059 request
->send_timeout
= session
->send_timeout
;
3060 request
->receive_timeout
= session
->receive_timeout
;
3062 InitializeCriticalSection( &request
->read_section
);
3063 request
->read_section
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": http_request_t.read_section");
3065 WININET_AddRef( &session
->hdr
);
3066 request
->session
= session
;
3067 list_add_head( &session
->hdr
.children
, &request
->hdr
.entry
);
3069 if (dwFlags
& INTERNET_FLAG_IGNORE_CERT_CN_INVALID
)
3070 request
->security_flags
|= SECURITY_FLAG_IGNORE_CERT_CN_INVALID
;
3071 if (dwFlags
& INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
)
3072 request
->security_flags
|= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
3074 if (lpszObjectName
&& *lpszObjectName
) {
3078 rc
= UrlEscapeW(lpszObjectName
, NULL
, &len
, URL_ESCAPE_SPACES_ONLY
);
3079 if (rc
!= E_POINTER
)
3080 len
= strlenW(lpszObjectName
)+1;
3081 request
->path
= heap_alloc(len
*sizeof(WCHAR
));
3082 rc
= UrlEscapeW(lpszObjectName
, request
->path
, &len
,
3083 URL_ESCAPE_SPACES_ONLY
);
3086 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName
),rc
);
3087 strcpyW(request
->path
,lpszObjectName
);
3090 static const WCHAR slashW
[] = {'/',0};
3092 request
->path
= heap_strdupW(slashW
);
3095 if (lpszReferrer
&& *lpszReferrer
)
3096 HTTP_ProcessHeader(request
, HTTP_REFERER
, lpszReferrer
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3098 if (lpszAcceptTypes
)
3101 for (i
= 0; lpszAcceptTypes
[i
]; i
++)
3103 if (!*lpszAcceptTypes
[i
]) continue;
3104 HTTP_ProcessHeader(request
, HTTP_ACCEPT
, lpszAcceptTypes
[i
],
3105 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
|
3106 HTTP_ADDHDR_FLAG_REQ
|
3107 (i
== 0 ? HTTP_ADDHDR_FLAG_REPLACE
: 0));
3111 request
->verb
= heap_strdupW(lpszVerb
&& *lpszVerb
? lpszVerb
: szGET
);
3112 request
->version
= heap_strdupW(lpszVersion
? lpszVersion
: g_szHttp1_1
);
3114 if (session
->hostPort
!= INTERNET_INVALID_PORT_NUMBER
&&
3115 session
->hostPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
3116 session
->hostPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3120 static const WCHAR host_formatW
[] = {'%','s',':','%','u',0};
3122 host_name
= heap_alloc((strlenW(session
->hostName
) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR
));
3124 res
= ERROR_OUTOFMEMORY
;
3128 sprintfW(host_name
, host_formatW
, session
->hostName
, session
->hostPort
);
3129 HTTP_ProcessHeader(request
, hostW
, host_name
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3130 heap_free(host_name
);
3133 HTTP_ProcessHeader(request
, hostW
, session
->hostName
,
3134 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3136 if (session
->serverPort
== INTERNET_INVALID_PORT_NUMBER
)
3137 session
->serverPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
3138 INTERNET_DEFAULT_HTTPS_PORT
:
3139 INTERNET_DEFAULT_HTTP_PORT
);
3141 if (session
->hostPort
== INTERNET_INVALID_PORT_NUMBER
)
3142 session
->hostPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
3143 INTERNET_DEFAULT_HTTPS_PORT
:
3144 INTERNET_DEFAULT_HTTP_PORT
);
3146 if (hIC
->proxy
&& hIC
->proxy
[0])
3147 HTTP_DealWithProxy( hIC
, session
, request
);
3149 INTERNET_SendCallback(&session
->hdr
, dwContext
,
3150 INTERNET_STATUS_HANDLE_CREATED
, &request
->hdr
.hInternet
,
3154 TRACE("<-- %u (%p)\n", res
, request
);
3156 if(res
!= ERROR_SUCCESS
) {
3157 WININET_Release( &request
->hdr
);
3162 *ret
= request
->hdr
.hInternet
;
3163 return ERROR_SUCCESS
;
3166 /***********************************************************************
3167 * HttpOpenRequestW (WININET.@)
3169 * Open a HTTP request handle
3172 * HINTERNET a HTTP request handle on success
3176 HINTERNET WINAPI
HttpOpenRequestW(HINTERNET hHttpSession
,
3177 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
3178 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
3179 DWORD dwFlags
, DWORD_PTR dwContext
)
3181 http_session_t
*session
;
3182 HINTERNET handle
= NULL
;
3185 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
3186 debugstr_w(lpszVerb
), debugstr_w(lpszObjectName
),
3187 debugstr_w(lpszVersion
), debugstr_w(lpszReferrer
), lpszAcceptTypes
,
3188 dwFlags
, dwContext
);
3189 if(lpszAcceptTypes
!=NULL
)
3192 for(i
=0;lpszAcceptTypes
[i
]!=NULL
;i
++)
3193 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes
[i
]));
3196 session
= (http_session_t
*) get_handle_object( hHttpSession
);
3197 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
3199 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3204 * My tests seem to show that the windows version does not
3205 * become asynchronous until after this point. And anyhow
3206 * if this call was asynchronous then how would you get the
3207 * necessary HINTERNET pointer returned by this function.
3210 res
= HTTP_HttpOpenRequestW(session
, lpszVerb
, lpszObjectName
,
3211 lpszVersion
, lpszReferrer
, lpszAcceptTypes
,
3212 dwFlags
, dwContext
, &handle
);
3215 WININET_Release( &session
->hdr
);
3216 TRACE("returning %p\n", handle
);
3217 if(res
!= ERROR_SUCCESS
)
3222 static const LPCWSTR header_lookup
[] = {
3223 szMime_Version
, /* HTTP_QUERY_MIME_VERSION = 0 */
3224 szContent_Type
, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3225 szContent_Transfer_Encoding
,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3226 szContent_ID
, /* HTTP_QUERY_CONTENT_ID = 3 */
3227 NULL
, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3228 szContent_Length
, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3229 szContent_Language
, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3230 szAllow
, /* HTTP_QUERY_ALLOW = 7 */
3231 szPublic
, /* HTTP_QUERY_PUBLIC = 8 */
3232 szDate
, /* HTTP_QUERY_DATE = 9 */
3233 szExpires
, /* HTTP_QUERY_EXPIRES = 10 */
3234 szLast_Modified
, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3235 NULL
, /* HTTP_QUERY_MESSAGE_ID = 12 */
3236 szURI
, /* HTTP_QUERY_URI = 13 */
3237 szFrom
, /* HTTP_QUERY_DERIVED_FROM = 14 */
3238 NULL
, /* HTTP_QUERY_COST = 15 */
3239 NULL
, /* HTTP_QUERY_LINK = 16 */
3240 szPragma
, /* HTTP_QUERY_PRAGMA = 17 */
3241 NULL
, /* HTTP_QUERY_VERSION = 18 */
3242 szStatus
, /* HTTP_QUERY_STATUS_CODE = 19 */
3243 NULL
, /* HTTP_QUERY_STATUS_TEXT = 20 */
3244 NULL
, /* HTTP_QUERY_RAW_HEADERS = 21 */
3245 NULL
, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3246 szConnection
, /* HTTP_QUERY_CONNECTION = 23 */
3247 szAccept
, /* HTTP_QUERY_ACCEPT = 24 */
3248 szAccept_Charset
, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3249 szAccept_Encoding
, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3250 szAccept_Language
, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3251 szAuthorization
, /* HTTP_QUERY_AUTHORIZATION = 28 */
3252 szContent_Encoding
, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3253 NULL
, /* HTTP_QUERY_FORWARDED = 30 */
3254 NULL
, /* HTTP_QUERY_FROM = 31 */
3255 szIf_Modified_Since
, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3256 szLocation
, /* HTTP_QUERY_LOCATION = 33 */
3257 NULL
, /* HTTP_QUERY_ORIG_URI = 34 */
3258 szReferer
, /* HTTP_QUERY_REFERER = 35 */
3259 szRetry_After
, /* HTTP_QUERY_RETRY_AFTER = 36 */
3260 szServer
, /* HTTP_QUERY_SERVER = 37 */
3261 NULL
, /* HTTP_TITLE = 38 */
3262 szUser_Agent
, /* HTTP_QUERY_USER_AGENT = 39 */
3263 szWWW_Authenticate
, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3264 szProxy_Authenticate
, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3265 szAccept_Ranges
, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3266 szSet_Cookie
, /* HTTP_QUERY_SET_COOKIE = 43 */
3267 szCookie
, /* HTTP_QUERY_COOKIE = 44 */
3268 NULL
, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3269 NULL
, /* HTTP_QUERY_REFRESH = 46 */
3270 NULL
, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3271 szAge
, /* HTTP_QUERY_AGE = 48 */
3272 szCache_Control
, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3273 szContent_Base
, /* HTTP_QUERY_CONTENT_BASE = 50 */
3274 szContent_Location
, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3275 szContent_MD5
, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3276 szContent_Range
, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3277 szETag
, /* HTTP_QUERY_ETAG = 54 */
3278 hostW
, /* HTTP_QUERY_HOST = 55 */
3279 szIf_Match
, /* HTTP_QUERY_IF_MATCH = 56 */
3280 szIf_None_Match
, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3281 szIf_Range
, /* HTTP_QUERY_IF_RANGE = 58 */
3282 szIf_Unmodified_Since
, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3283 szMax_Forwards
, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3284 szProxy_Authorization
, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3285 szRange
, /* HTTP_QUERY_RANGE = 62 */
3286 szTransfer_Encoding
, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3287 szUpgrade
, /* HTTP_QUERY_UPGRADE = 64 */
3288 szVary
, /* HTTP_QUERY_VARY = 65 */
3289 szVia
, /* HTTP_QUERY_VIA = 66 */
3290 szWarning
, /* HTTP_QUERY_WARNING = 67 */
3291 szExpect
, /* HTTP_QUERY_EXPECT = 68 */
3292 szProxy_Connection
, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3293 szUnless_Modified_Since
, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3296 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3298 /***********************************************************************
3299 * HTTP_HttpQueryInfoW (internal)
3301 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*request
, DWORD dwInfoLevel
,
3302 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3304 LPHTTPHEADERW lphttpHdr
= NULL
;
3305 BOOL request_only
= dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
;
3306 INT requested_index
= lpdwIndex
? *lpdwIndex
: 0;
3307 DWORD level
= (dwInfoLevel
& ~HTTP_QUERY_MODIFIER_FLAGS_MASK
);
3310 /* Find requested header structure */
3313 case HTTP_QUERY_CUSTOM
:
3314 if (!lpBuffer
) return ERROR_INVALID_PARAMETER
;
3315 index
= HTTP_GetCustomHeaderIndex(request
, lpBuffer
, requested_index
, request_only
);
3317 case HTTP_QUERY_RAW_HEADERS_CRLF
:
3321 DWORD res
= ERROR_INVALID_PARAMETER
;
3324 headers
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
3326 headers
= request
->rawHeaders
;
3329 len
= strlenW(headers
) * sizeof(WCHAR
);
3331 if (len
+ sizeof(WCHAR
) > *lpdwBufferLength
)
3333 len
+= sizeof(WCHAR
);
3334 res
= ERROR_INSUFFICIENT_BUFFER
;
3339 memcpy(lpBuffer
, headers
, len
+ sizeof(WCHAR
));
3342 len
= strlenW(szCrLf
) * sizeof(WCHAR
);
3343 memcpy(lpBuffer
, szCrLf
, sizeof(szCrLf
));
3345 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
/ sizeof(WCHAR
)));
3346 res
= ERROR_SUCCESS
;
3348 *lpdwBufferLength
= len
;
3350 if (request_only
) heap_free(headers
);
3353 case HTTP_QUERY_RAW_HEADERS
:
3355 LPWSTR
* ppszRawHeaderLines
= HTTP_Tokenize(request
->rawHeaders
, szCrLf
);
3357 LPWSTR pszString
= lpBuffer
;
3359 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3360 size
+= strlenW(ppszRawHeaderLines
[i
]) + 1;
3362 if (size
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3364 HTTP_FreeTokens(ppszRawHeaderLines
);
3365 *lpdwBufferLength
= (size
+ 1) * sizeof(WCHAR
);
3366 return ERROR_INSUFFICIENT_BUFFER
;
3370 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3372 DWORD len
= strlenW(ppszRawHeaderLines
[i
]);
3373 memcpy(pszString
, ppszRawHeaderLines
[i
], (len
+1)*sizeof(WCHAR
));
3377 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, size
));
3379 *lpdwBufferLength
= size
* sizeof(WCHAR
);
3380 HTTP_FreeTokens(ppszRawHeaderLines
);
3382 return ERROR_SUCCESS
;
3384 case HTTP_QUERY_STATUS_TEXT
:
3385 if (request
->statusText
)
3387 DWORD len
= strlenW(request
->statusText
);
3388 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3390 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3391 return ERROR_INSUFFICIENT_BUFFER
;
3395 memcpy(lpBuffer
, request
->statusText
, (len
+ 1) * sizeof(WCHAR
));
3396 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3398 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3399 return ERROR_SUCCESS
;
3402 case HTTP_QUERY_VERSION
:
3403 if (request
->version
)
3405 DWORD len
= strlenW(request
->version
);
3406 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3408 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3409 return ERROR_INSUFFICIENT_BUFFER
;
3413 memcpy(lpBuffer
, request
->version
, (len
+ 1) * sizeof(WCHAR
));
3414 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3416 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3417 return ERROR_SUCCESS
;
3420 case HTTP_QUERY_CONTENT_ENCODING
:
3421 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[request
->read_gzip
? HTTP_QUERY_CONTENT_TYPE
: level
],
3422 requested_index
,request_only
);
3425 assert (LAST_TABLE_HEADER
== (HTTP_QUERY_UNLESS_MODIFIED_SINCE
+ 1));
3427 if (level
< LAST_TABLE_HEADER
&& header_lookup
[level
])
3428 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[level
],
3429 requested_index
,request_only
);
3433 lphttpHdr
= &request
->custHeaders
[index
];
3435 /* Ensure header satisfies requested attributes */
3437 ((dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
) &&
3438 (~lphttpHdr
->wFlags
& HDR_ISREQUEST
)))
3440 return ERROR_HTTP_HEADER_NOT_FOUND
;
3443 if (lpdwIndex
&& level
!= HTTP_QUERY_STATUS_CODE
) (*lpdwIndex
)++;
3445 /* coalesce value to requested type */
3446 if (dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
&& lpBuffer
)
3448 *(int *)lpBuffer
= atoiW(lphttpHdr
->lpszValue
);
3449 TRACE(" returning number: %d\n", *(int *)lpBuffer
);
3451 else if (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
&& lpBuffer
)
3457 tmpTime
= ConvertTimeString(lphttpHdr
->lpszValue
);
3459 tmpTM
= *gmtime(&tmpTime
);
3460 STHook
= (SYSTEMTIME
*)lpBuffer
;
3461 STHook
->wDay
= tmpTM
.tm_mday
;
3462 STHook
->wHour
= tmpTM
.tm_hour
;
3463 STHook
->wMilliseconds
= 0;
3464 STHook
->wMinute
= tmpTM
.tm_min
;
3465 STHook
->wDayOfWeek
= tmpTM
.tm_wday
;
3466 STHook
->wMonth
= tmpTM
.tm_mon
+ 1;
3467 STHook
->wSecond
= tmpTM
.tm_sec
;
3468 STHook
->wYear
= tmpTM
.tm_year
;
3470 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3471 STHook
->wYear
, STHook
->wMonth
, STHook
->wDay
, STHook
->wDayOfWeek
,
3472 STHook
->wHour
, STHook
->wMinute
, STHook
->wSecond
, STHook
->wMilliseconds
);
3474 else if (lphttpHdr
->lpszValue
)
3476 DWORD len
= (strlenW(lphttpHdr
->lpszValue
) + 1) * sizeof(WCHAR
);
3478 if (len
> *lpdwBufferLength
)
3480 *lpdwBufferLength
= len
;
3481 return ERROR_INSUFFICIENT_BUFFER
;
3485 memcpy(lpBuffer
, lphttpHdr
->lpszValue
, len
);
3486 TRACE("! returning string: %s\n", debugstr_w(lpBuffer
));
3488 *lpdwBufferLength
= len
- sizeof(WCHAR
);
3490 return ERROR_SUCCESS
;
3493 /***********************************************************************
3494 * HttpQueryInfoW (WININET.@)
3496 * Queries for information about an HTTP request
3503 BOOL WINAPI
HttpQueryInfoW(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3504 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3506 http_request_t
*request
;
3509 if (TRACE_ON(wininet
)) {
3510 #define FE(x) { x, #x }
3511 static const wininet_flag_info query_flags
[] = {
3512 FE(HTTP_QUERY_MIME_VERSION
),
3513 FE(HTTP_QUERY_CONTENT_TYPE
),
3514 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING
),
3515 FE(HTTP_QUERY_CONTENT_ID
),
3516 FE(HTTP_QUERY_CONTENT_DESCRIPTION
),
3517 FE(HTTP_QUERY_CONTENT_LENGTH
),
3518 FE(HTTP_QUERY_CONTENT_LANGUAGE
),
3519 FE(HTTP_QUERY_ALLOW
),
3520 FE(HTTP_QUERY_PUBLIC
),
3521 FE(HTTP_QUERY_DATE
),
3522 FE(HTTP_QUERY_EXPIRES
),
3523 FE(HTTP_QUERY_LAST_MODIFIED
),
3524 FE(HTTP_QUERY_MESSAGE_ID
),
3526 FE(HTTP_QUERY_DERIVED_FROM
),
3527 FE(HTTP_QUERY_COST
),
3528 FE(HTTP_QUERY_LINK
),
3529 FE(HTTP_QUERY_PRAGMA
),
3530 FE(HTTP_QUERY_VERSION
),
3531 FE(HTTP_QUERY_STATUS_CODE
),
3532 FE(HTTP_QUERY_STATUS_TEXT
),
3533 FE(HTTP_QUERY_RAW_HEADERS
),
3534 FE(HTTP_QUERY_RAW_HEADERS_CRLF
),
3535 FE(HTTP_QUERY_CONNECTION
),
3536 FE(HTTP_QUERY_ACCEPT
),
3537 FE(HTTP_QUERY_ACCEPT_CHARSET
),
3538 FE(HTTP_QUERY_ACCEPT_ENCODING
),
3539 FE(HTTP_QUERY_ACCEPT_LANGUAGE
),
3540 FE(HTTP_QUERY_AUTHORIZATION
),
3541 FE(HTTP_QUERY_CONTENT_ENCODING
),
3542 FE(HTTP_QUERY_FORWARDED
),
3543 FE(HTTP_QUERY_FROM
),
3544 FE(HTTP_QUERY_IF_MODIFIED_SINCE
),
3545 FE(HTTP_QUERY_LOCATION
),
3546 FE(HTTP_QUERY_ORIG_URI
),
3547 FE(HTTP_QUERY_REFERER
),
3548 FE(HTTP_QUERY_RETRY_AFTER
),
3549 FE(HTTP_QUERY_SERVER
),
3550 FE(HTTP_QUERY_TITLE
),
3551 FE(HTTP_QUERY_USER_AGENT
),
3552 FE(HTTP_QUERY_WWW_AUTHENTICATE
),
3553 FE(HTTP_QUERY_PROXY_AUTHENTICATE
),
3554 FE(HTTP_QUERY_ACCEPT_RANGES
),
3555 FE(HTTP_QUERY_SET_COOKIE
),
3556 FE(HTTP_QUERY_COOKIE
),
3557 FE(HTTP_QUERY_REQUEST_METHOD
),
3558 FE(HTTP_QUERY_REFRESH
),
3559 FE(HTTP_QUERY_CONTENT_DISPOSITION
),
3561 FE(HTTP_QUERY_CACHE_CONTROL
),
3562 FE(HTTP_QUERY_CONTENT_BASE
),
3563 FE(HTTP_QUERY_CONTENT_LOCATION
),
3564 FE(HTTP_QUERY_CONTENT_MD5
),
3565 FE(HTTP_QUERY_CONTENT_RANGE
),
3566 FE(HTTP_QUERY_ETAG
),
3567 FE(HTTP_QUERY_HOST
),
3568 FE(HTTP_QUERY_IF_MATCH
),
3569 FE(HTTP_QUERY_IF_NONE_MATCH
),
3570 FE(HTTP_QUERY_IF_RANGE
),
3571 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE
),
3572 FE(HTTP_QUERY_MAX_FORWARDS
),
3573 FE(HTTP_QUERY_PROXY_AUTHORIZATION
),
3574 FE(HTTP_QUERY_RANGE
),
3575 FE(HTTP_QUERY_TRANSFER_ENCODING
),
3576 FE(HTTP_QUERY_UPGRADE
),
3577 FE(HTTP_QUERY_VARY
),
3579 FE(HTTP_QUERY_WARNING
),
3580 FE(HTTP_QUERY_CUSTOM
)
3582 static const wininet_flag_info modifier_flags
[] = {
3583 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS
),
3584 FE(HTTP_QUERY_FLAG_SYSTEMTIME
),
3585 FE(HTTP_QUERY_FLAG_NUMBER
),
3586 FE(HTTP_QUERY_FLAG_COALESCE
)
3589 DWORD info_mod
= dwInfoLevel
& HTTP_QUERY_MODIFIER_FLAGS_MASK
;
3590 DWORD info
= dwInfoLevel
& HTTP_QUERY_HEADER_MASK
;
3593 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest
, dwInfoLevel
, info
);
3594 TRACE(" Attribute:");
3595 for (i
= 0; i
< (sizeof(query_flags
) / sizeof(query_flags
[0])); i
++) {
3596 if (query_flags
[i
].val
== info
) {
3597 TRACE(" %s", query_flags
[i
].name
);
3601 if (i
== (sizeof(query_flags
) / sizeof(query_flags
[0]))) {
3602 TRACE(" Unknown (%08x)", info
);
3605 TRACE(" Modifier:");
3606 for (i
= 0; i
< (sizeof(modifier_flags
) / sizeof(modifier_flags
[0])); i
++) {
3607 if (modifier_flags
[i
].val
& info_mod
) {
3608 TRACE(" %s", modifier_flags
[i
].name
);
3609 info_mod
&= ~ modifier_flags
[i
].val
;
3614 TRACE(" Unknown (%08x)", info_mod
);
3619 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
3620 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
3622 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3626 if (lpBuffer
== NULL
)
3627 *lpdwBufferLength
= 0;
3628 res
= HTTP_HttpQueryInfoW( request
, dwInfoLevel
,
3629 lpBuffer
, lpdwBufferLength
, lpdwIndex
);
3633 WININET_Release( &request
->hdr
);
3635 TRACE("%u <--\n", res
);
3636 if(res
!= ERROR_SUCCESS
)
3638 return res
== ERROR_SUCCESS
;
3641 /***********************************************************************
3642 * HttpQueryInfoA (WININET.@)
3644 * Queries for information about an HTTP request
3651 BOOL WINAPI
HttpQueryInfoA(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3652 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3658 if((dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) ||
3659 (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
))
3661 return HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, lpBuffer
,
3662 lpdwBufferLength
, lpdwIndex
);
3668 len
= (*lpdwBufferLength
)*sizeof(WCHAR
);
3669 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3671 alloclen
= MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, NULL
, 0 ) * sizeof(WCHAR
);
3677 bufferW
= heap_alloc(alloclen
);
3678 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3679 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3680 MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, bufferW
, alloclen
/ sizeof(WCHAR
) );
3687 result
= HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, bufferW
,
3691 len
= WideCharToMultiByte( CP_ACP
,0, bufferW
, len
/ sizeof(WCHAR
) + 1,
3692 lpBuffer
, *lpdwBufferLength
, NULL
, NULL
);
3693 *lpdwBufferLength
= len
- 1;
3695 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer
));
3698 /* since the strings being returned from HttpQueryInfoW should be
3699 * only ASCII characters, it is reasonable to assume that all of
3700 * the Unicode characters can be reduced to a single byte */
3701 *lpdwBufferLength
= len
/ sizeof(WCHAR
);
3703 heap_free( bufferW
);
3707 /***********************************************************************
3708 * HTTP_GetRedirectURL (internal)
3710 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*request
, LPCWSTR lpszUrl
)
3712 static WCHAR szHttp
[] = {'h','t','t','p',0};
3713 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3714 http_session_t
*session
= request
->session
;
3715 URL_COMPONENTSW urlComponents
;
3716 DWORD url_length
= 0;
3718 LPWSTR combined_url
;
3720 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3721 urlComponents
.lpszScheme
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) ? szHttps
: szHttp
;
3722 urlComponents
.dwSchemeLength
= 0;
3723 urlComponents
.lpszHostName
= session
->hostName
;
3724 urlComponents
.dwHostNameLength
= 0;
3725 urlComponents
.nPort
= session
->hostPort
;
3726 urlComponents
.lpszUserName
= session
->userName
;
3727 urlComponents
.dwUserNameLength
= 0;
3728 urlComponents
.lpszPassword
= NULL
;
3729 urlComponents
.dwPasswordLength
= 0;
3730 urlComponents
.lpszUrlPath
= request
->path
;
3731 urlComponents
.dwUrlPathLength
= 0;
3732 urlComponents
.lpszExtraInfo
= NULL
;
3733 urlComponents
.dwExtraInfoLength
= 0;
3735 if (!InternetCreateUrlW(&urlComponents
, 0, NULL
, &url_length
) &&
3736 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3739 orig_url
= heap_alloc(url_length
);
3741 /* convert from bytes to characters */
3742 url_length
= url_length
/ sizeof(WCHAR
) - 1;
3743 if (!InternetCreateUrlW(&urlComponents
, 0, orig_url
, &url_length
))
3745 heap_free(orig_url
);
3750 if (!InternetCombineUrlW(orig_url
, lpszUrl
, NULL
, &url_length
, ICU_ENCODE_SPACES_ONLY
) &&
3751 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3753 heap_free(orig_url
);
3756 combined_url
= heap_alloc(url_length
* sizeof(WCHAR
));
3758 if (!InternetCombineUrlW(orig_url
, lpszUrl
, combined_url
, &url_length
, ICU_ENCODE_SPACES_ONLY
))
3760 heap_free(orig_url
);
3761 heap_free(combined_url
);
3764 heap_free(orig_url
);
3765 return combined_url
;
3769 /***********************************************************************
3770 * HTTP_HandleRedirect (internal)
3772 static DWORD
HTTP_HandleRedirect(http_request_t
*request
, LPCWSTR lpszUrl
)
3774 http_session_t
*session
= request
->session
;
3775 appinfo_t
*hIC
= session
->appInfo
;
3776 BOOL using_proxy
= hIC
->proxy
&& hIC
->proxy
[0];
3777 WCHAR path
[INTERNET_MAX_PATH_LENGTH
];
3782 /* if it's an absolute path, keep the same session info */
3783 lstrcpynW(path
, lpszUrl
, INTERNET_MAX_URL_LENGTH
);
3787 URL_COMPONENTSW urlComponents
;
3788 WCHAR protocol
[INTERNET_MAX_SCHEME_LENGTH
];
3789 WCHAR hostName
[INTERNET_MAX_HOST_NAME_LENGTH
];
3790 WCHAR userName
[INTERNET_MAX_USER_NAME_LENGTH
];
3791 BOOL custom_port
= FALSE
;
3793 static WCHAR httpW
[] = {'h','t','t','p',0};
3794 static WCHAR httpsW
[] = {'h','t','t','p','s',0};
3800 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3801 urlComponents
.lpszScheme
= protocol
;
3802 urlComponents
.dwSchemeLength
= INTERNET_MAX_SCHEME_LENGTH
;
3803 urlComponents
.lpszHostName
= hostName
;
3804 urlComponents
.dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
3805 urlComponents
.lpszUserName
= userName
;
3806 urlComponents
.dwUserNameLength
= INTERNET_MAX_USER_NAME_LENGTH
;
3807 urlComponents
.lpszPassword
= NULL
;
3808 urlComponents
.dwPasswordLength
= 0;
3809 urlComponents
.lpszUrlPath
= path
;
3810 urlComponents
.dwUrlPathLength
= INTERNET_MAX_PATH_LENGTH
;
3811 urlComponents
.lpszExtraInfo
= NULL
;
3812 urlComponents
.dwExtraInfoLength
= 0;
3813 if(!InternetCrackUrlW(lpszUrl
, strlenW(lpszUrl
), 0, &urlComponents
))
3814 return INTERNET_GetLastError();
3816 if(!strcmpiW(protocol
, httpW
)) {
3817 if(request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) {
3818 TRACE("redirect from secure page to non-secure page\n");
3819 /* FIXME: warn about from secure redirect to non-secure page */
3820 request
->hdr
.dwFlags
&= ~INTERNET_FLAG_SECURE
;
3823 if(urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3824 urlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
3825 else if(urlComponents
.nPort
!= INTERNET_DEFAULT_HTTP_PORT
)
3827 }else if(!strcmpiW(protocol
, httpsW
)) {
3828 if(!(request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)) {
3829 TRACE("redirect from non-secure page to secure page\n");
3830 /* FIXME: notify about redirect to secure page */
3831 request
->hdr
.dwFlags
|= INTERNET_FLAG_SECURE
;
3834 if(urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3835 urlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
3836 else if(urlComponents
.nPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3840 heap_free(session
->hostName
);
3844 static const WCHAR fmt
[] = {'%','s',':','%','u',0};
3845 len
= lstrlenW(hostName
);
3846 len
+= 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3847 session
->hostName
= heap_alloc(len
*sizeof(WCHAR
));
3848 sprintfW(session
->hostName
, fmt
, hostName
, urlComponents
.nPort
);
3851 session
->hostName
= heap_strdupW(hostName
);
3853 HTTP_ProcessHeader(request
, hostW
, session
->hostName
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDREQ_FLAG_REPLACE
| HTTP_ADDHDR_FLAG_REQ
);
3855 heap_free(session
->userName
);
3856 session
->userName
= NULL
;
3858 session
->userName
= heap_strdupW(userName
);
3860 reset_data_stream(request
);
3863 if(strcmpiW(session
->serverName
, hostName
)) {
3864 heap_free(session
->serverName
);
3865 session
->serverName
= heap_strdupW(hostName
);
3867 session
->serverPort
= urlComponents
.nPort
;
3870 heap_free(request
->path
);
3877 rc
= UrlEscapeW(path
, NULL
, &needed
, URL_ESCAPE_SPACES_ONLY
);
3878 if (rc
!= E_POINTER
)
3879 needed
= strlenW(path
)+1;
3880 request
->path
= heap_alloc(needed
*sizeof(WCHAR
));
3881 rc
= UrlEscapeW(path
, request
->path
, &needed
,
3882 URL_ESCAPE_SPACES_ONLY
);
3885 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path
),rc
);
3886 strcpyW(request
->path
,path
);
3890 /* Remove custom content-type/length headers on redirects. */
3891 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Type
, 0, TRUE
);
3893 HTTP_DeleteCustomHeader(request
, index
);
3894 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Length
, 0, TRUE
);
3896 HTTP_DeleteCustomHeader(request
, index
);
3898 return ERROR_SUCCESS
;
3901 /***********************************************************************
3902 * HTTP_build_req (internal)
3904 * concatenate all the strings in the request together
3906 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
)
3911 for( t
= list
; *t
; t
++ )
3912 len
+= strlenW( *t
);
3915 str
= heap_alloc(len
*sizeof(WCHAR
));
3918 for( t
= list
; *t
; t
++ )
3924 static DWORD
HTTP_SecureProxyConnect(http_request_t
*request
)
3927 LPWSTR requestString
;
3933 static const WCHAR szConnect
[] = {'C','O','N','N','E','C','T',0};
3934 static const WCHAR szFormat
[] = {'%','s',':','%','u',0};
3935 http_session_t
*session
= request
->session
;
3939 lpszPath
= heap_alloc((lstrlenW( session
->hostName
) + 13)*sizeof(WCHAR
));
3940 sprintfW( lpszPath
, szFormat
, session
->hostName
, session
->hostPort
);
3941 requestString
= HTTP_BuildHeaderRequestString( request
, szConnect
, lpszPath
, g_szHttp1_1
);
3942 heap_free( lpszPath
);
3944 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3945 NULL
, 0, NULL
, NULL
);
3946 len
--; /* the nul terminator isn't needed */
3947 ascii_req
= heap_alloc(len
);
3948 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1, ascii_req
, len
, NULL
, NULL
);
3949 heap_free( requestString
);
3951 TRACE("full request -> %s\n", debugstr_an( ascii_req
, len
) );
3953 NETCON_set_timeout( request
->netconn
, TRUE
, request
->send_timeout
);
3954 res
= NETCON_send( request
->netconn
, ascii_req
, len
, 0, &cnt
);
3955 heap_free( ascii_req
);
3956 if (res
!= ERROR_SUCCESS
)
3959 responseLen
= HTTP_GetResponseHeaders( request
, TRUE
);
3961 return ERROR_HTTP_INVALID_HEADER
;
3963 return ERROR_SUCCESS
;
3966 static void HTTP_InsertCookies(http_request_t
*request
)
3968 DWORD cookie_size
, size
, cnt
= 0;
3972 static const WCHAR cookieW
[] = {'C','o','o','k','i','e',':',' ',0};
3974 host
= HTTP_GetHeader(request
, hostW
);
3978 if(!get_cookie(host
->lpszValue
, request
->path
, NULL
, &cookie_size
))
3981 size
= sizeof(cookieW
) + cookie_size
* sizeof(WCHAR
) + sizeof(szCrLf
);
3982 if(!(cookies
= heap_alloc(size
)))
3985 cnt
+= sprintfW(cookies
, cookieW
);
3986 get_cookie(host
->lpszValue
, request
->path
, cookies
+cnt
, &cookie_size
);
3987 strcatW(cookies
, szCrLf
);
3989 HTTP_HttpAddRequestHeadersW(request
, cookies
, strlenW(cookies
), HTTP_ADDREQ_FLAG_REPLACE
);
3994 static WORD
HTTP_ParseWkday(LPCWSTR day
)
3996 static const WCHAR days
[7][4] = {{ 's','u','n',0 },
4004 for (i
= 0; i
< sizeof(days
)/sizeof(*days
); i
++)
4005 if (!strcmpiW(day
, days
[i
]))
4012 static WORD
HTTP_ParseMonth(LPCWSTR month
)
4014 static const WCHAR jan
[] = { 'j','a','n',0 };
4015 static const WCHAR feb
[] = { 'f','e','b',0 };
4016 static const WCHAR mar
[] = { 'm','a','r',0 };
4017 static const WCHAR apr
[] = { 'a','p','r',0 };
4018 static const WCHAR may
[] = { 'm','a','y',0 };
4019 static const WCHAR jun
[] = { 'j','u','n',0 };
4020 static const WCHAR jul
[] = { 'j','u','l',0 };
4021 static const WCHAR aug
[] = { 'a','u','g',0 };
4022 static const WCHAR sep
[] = { 's','e','p',0 };
4023 static const WCHAR oct
[] = { 'o','c','t',0 };
4024 static const WCHAR nov
[] = { 'n','o','v',0 };
4025 static const WCHAR dec
[] = { 'd','e','c',0 };
4027 if (!strcmpiW(month
, jan
)) return 1;
4028 if (!strcmpiW(month
, feb
)) return 2;
4029 if (!strcmpiW(month
, mar
)) return 3;
4030 if (!strcmpiW(month
, apr
)) return 4;
4031 if (!strcmpiW(month
, may
)) return 5;
4032 if (!strcmpiW(month
, jun
)) return 6;
4033 if (!strcmpiW(month
, jul
)) return 7;
4034 if (!strcmpiW(month
, aug
)) return 8;
4035 if (!strcmpiW(month
, sep
)) return 9;
4036 if (!strcmpiW(month
, oct
)) return 10;
4037 if (!strcmpiW(month
, nov
)) return 11;
4038 if (!strcmpiW(month
, dec
)) return 12;
4043 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4044 * optionally preceded by whitespace.
4045 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4046 * st, and sets *str to the first character after the time format.
4048 static BOOL
HTTP_ParseTime(SYSTEMTIME
*st
, LPCWSTR
*str
)
4054 while (isspaceW(*ptr
))
4057 num
= strtoulW(ptr
, &nextPtr
, 10);
4058 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
4060 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4065 ERR("unexpected hour in time format %s\n", debugstr_w(ptr
));
4069 st
->wHour
= (WORD
)num
;
4070 num
= strtoulW(ptr
, &nextPtr
, 10);
4071 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
4073 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4078 ERR("unexpected minute in time format %s\n", debugstr_w(ptr
));
4082 st
->wMinute
= (WORD
)num
;
4083 num
= strtoulW(ptr
, &nextPtr
, 10);
4084 if (!nextPtr
|| nextPtr
<= ptr
)
4086 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4091 ERR("unexpected second in time format %s\n", debugstr_w(ptr
));
4096 st
->wSecond
= (WORD
)num
;
4100 static BOOL
HTTP_ParseDateAsAsctime(LPCWSTR value
, FILETIME
*ft
)
4102 static const WCHAR gmt
[]= { 'G','M','T',0 };
4103 WCHAR day
[4], *dayPtr
, month
[4], *monthPtr
, *nextPtr
;
4105 SYSTEMTIME st
= { 0 };
4108 for (ptr
= value
, dayPtr
= day
; *ptr
&& !isspaceW(*ptr
) &&
4109 dayPtr
- day
< sizeof(day
) / sizeof(day
[0]) - 1; ptr
++, dayPtr
++)
4112 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4113 if (st
.wDayOfWeek
>= 7)
4115 ERR("unexpected weekday %s\n", debugstr_w(day
));
4119 while (isspaceW(*ptr
))
4122 for (monthPtr
= month
; !isspace(*ptr
) &&
4123 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4127 st
.wMonth
= HTTP_ParseMonth(month
);
4128 if (!st
.wMonth
|| st
.wMonth
> 12)
4130 ERR("unexpected month %s\n", debugstr_w(month
));
4134 while (isspaceW(*ptr
))
4137 num
= strtoulW(ptr
, &nextPtr
, 10);
4138 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4140 ERR("unexpected day %s\n", debugstr_w(ptr
));
4144 st
.wDay
= (WORD
)num
;
4146 while (isspaceW(*ptr
))
4149 if (!HTTP_ParseTime(&st
, &ptr
))
4152 while (isspaceW(*ptr
))
4155 num
= strtoulW(ptr
, &nextPtr
, 10);
4156 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4158 ERR("unexpected year %s\n", debugstr_w(ptr
));
4162 st
.wYear
= (WORD
)num
;
4164 while (isspaceW(*ptr
))
4167 /* asctime() doesn't report a timezone, but some web servers do, so accept
4168 * with or without GMT.
4170 if (*ptr
&& strcmpW(ptr
, gmt
))
4172 ERR("unexpected timezone %s\n", debugstr_w(ptr
));
4175 return SystemTimeToFileTime(&st
, ft
);
4178 static BOOL
HTTP_ParseRfc1123Date(LPCWSTR value
, FILETIME
*ft
)
4180 static const WCHAR gmt
[]= { 'G','M','T',0 };
4181 WCHAR
*nextPtr
, day
[4], month
[4], *monthPtr
;
4184 SYSTEMTIME st
= { 0 };
4186 ptr
= strchrW(value
, ',');
4189 if (ptr
- value
!= 3)
4191 WARN("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4194 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4196 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4197 if (st
.wDayOfWeek
> 6)
4199 WARN("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4204 while (isspaceW(*ptr
))
4207 num
= strtoulW(ptr
, &nextPtr
, 10);
4208 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4210 WARN("unexpected day %s\n", debugstr_w(value
));
4214 st
.wDay
= (WORD
)num
;
4216 while (isspaceW(*ptr
))
4219 for (monthPtr
= month
; !isspace(*ptr
) &&
4220 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4224 st
.wMonth
= HTTP_ParseMonth(month
);
4225 if (!st
.wMonth
|| st
.wMonth
> 12)
4227 WARN("unexpected month %s\n", debugstr_w(month
));
4231 while (isspaceW(*ptr
))
4234 num
= strtoulW(ptr
, &nextPtr
, 10);
4235 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4237 ERR("unexpected year %s\n", debugstr_w(value
));
4241 st
.wYear
= (WORD
)num
;
4243 if (!HTTP_ParseTime(&st
, &ptr
))
4246 while (isspaceW(*ptr
))
4249 if (strcmpW(ptr
, gmt
))
4251 ERR("unexpected time zone %s\n", debugstr_w(ptr
));
4254 return SystemTimeToFileTime(&st
, ft
);
4257 static WORD
HTTP_ParseWeekday(LPCWSTR day
)
4259 static const WCHAR days
[7][10] = {{ 's','u','n','d','a','y',0 },
4260 { 'm','o','n','d','a','y',0 },
4261 { 't','u','e','s','d','a','y',0 },
4262 { 'w','e','d','n','e','s','d','a','y',0 },
4263 { 't','h','u','r','s','d','a','y',0 },
4264 { 'f','r','i','d','a','y',0 },
4265 { 's','a','t','u','r','d','a','y',0 }};
4267 for (i
= 0; i
< sizeof(days
)/sizeof(*days
); i
++)
4268 if (!strcmpiW(day
, days
[i
]))
4275 static BOOL
HTTP_ParseRfc850Date(LPCWSTR value
, FILETIME
*ft
)
4277 static const WCHAR gmt
[]= { 'G','M','T',0 };
4278 WCHAR
*nextPtr
, day
[10], month
[4], *monthPtr
;
4281 SYSTEMTIME st
= { 0 };
4283 ptr
= strchrW(value
, ',');
4286 if (ptr
- value
== 3)
4288 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4290 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4291 if (st
.wDayOfWeek
> 6)
4293 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4297 else if (ptr
- value
< sizeof(day
) / sizeof(day
[0]))
4299 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4300 day
[ptr
- value
+ 1] = 0;
4301 st
.wDayOfWeek
= HTTP_ParseWeekday(day
);
4302 if (st
.wDayOfWeek
> 6)
4304 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4310 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4315 while (isspaceW(*ptr
))
4318 num
= strtoulW(ptr
, &nextPtr
, 10);
4319 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4321 ERR("unexpected day %s\n", debugstr_w(value
));
4325 st
.wDay
= (WORD
)num
;
4329 ERR("unexpected month format %s\n", debugstr_w(ptr
));
4334 for (monthPtr
= month
; *ptr
!= '-' &&
4335 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4339 st
.wMonth
= HTTP_ParseMonth(month
);
4340 if (!st
.wMonth
|| st
.wMonth
> 12)
4342 ERR("unexpected month %s\n", debugstr_w(month
));
4348 ERR("unexpected year format %s\n", debugstr_w(ptr
));
4353 num
= strtoulW(ptr
, &nextPtr
, 10);
4354 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4356 ERR("unexpected year %s\n", debugstr_w(value
));
4360 st
.wYear
= (WORD
)num
;
4362 if (!HTTP_ParseTime(&st
, &ptr
))
4365 while (isspaceW(*ptr
))
4368 if (strcmpW(ptr
, gmt
))
4370 ERR("unexpected time zone %s\n", debugstr_w(ptr
));
4373 return SystemTimeToFileTime(&st
, ft
);
4376 static BOOL
HTTP_ParseDate(LPCWSTR value
, FILETIME
*ft
)
4378 static const WCHAR zero
[] = { '0',0 };
4381 if (!strcmpW(value
, zero
))
4383 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
4386 else if (strchrW(value
, ','))
4388 ret
= HTTP_ParseRfc1123Date(value
, ft
);
4391 ret
= HTTP_ParseRfc850Date(value
, ft
);
4393 ERR("unexpected date format %s\n", debugstr_w(value
));
4398 ret
= HTTP_ParseDateAsAsctime(value
, ft
);
4400 ERR("unexpected date format %s\n", debugstr_w(value
));
4405 static void HTTP_ProcessExpires(http_request_t
*request
)
4407 BOOL expirationFound
= FALSE
;
4410 /* Look for a Cache-Control header with a max-age directive, as it takes
4411 * precedence over the Expires header.
4413 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szCache_Control
, 0, FALSE
);
4414 if (headerIndex
!= -1)
4416 LPHTTPHEADERW ccHeader
= &request
->custHeaders
[headerIndex
];
4419 for (ptr
= ccHeader
->lpszValue
; ptr
&& *ptr
; )
4421 LPWSTR comma
= strchrW(ptr
, ','), end
, equal
;
4426 end
= ptr
+ strlenW(ptr
);
4427 for (equal
= end
- 1; equal
> ptr
&& *equal
!= '='; equal
--)
4431 static const WCHAR max_age
[] = {
4432 'm','a','x','-','a','g','e',0 };
4434 if (!strncmpiW(ptr
, max_age
, equal
- ptr
- 1))
4439 age
= strtoulW(equal
+ 1, &nextPtr
, 10);
4440 if (nextPtr
> equal
+ 1)
4444 NtQuerySystemTime( &ft
);
4445 /* Age is in seconds, FILETIME resolution is in
4446 * 100 nanosecond intervals.
4448 ft
.QuadPart
+= age
* (ULONGLONG
)1000000;
4449 request
->expires
.dwLowDateTime
= ft
.u
.LowPart
;
4450 request
->expires
.dwHighDateTime
= ft
.u
.HighPart
;
4451 expirationFound
= TRUE
;
4458 while (isspaceW(*ptr
))
4465 if (!expirationFound
)
4467 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szExpires
, 0, FALSE
);
4468 if (headerIndex
!= -1)
4470 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4473 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4475 expirationFound
= TRUE
;
4476 request
->expires
= ft
;
4480 if (!expirationFound
)
4484 /* With no known age, default to 10 minutes until expiration. */
4485 NtQuerySystemTime( &t
);
4486 t
.QuadPart
+= 10 * 60 * (ULONGLONG
)10000000;
4487 request
->expires
.dwLowDateTime
= t
.u
.LowPart
;
4488 request
->expires
.dwHighDateTime
= t
.u
.HighPart
;
4492 static void HTTP_ProcessLastModified(http_request_t
*request
)
4496 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szLast_Modified
, 0, FALSE
);
4497 if (headerIndex
!= -1)
4499 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4502 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4503 request
->last_modified
= ft
;
4507 static void http_process_keep_alive(http_request_t
*req
)
4511 index
= HTTP_GetCustomHeaderIndex(req
, szConnection
, 0, FALSE
);
4513 req
->netconn
->keep_alive
= !strcmpiW(req
->custHeaders
[index
].lpszValue
, szKeepAlive
);
4515 req
->netconn
->keep_alive
= !strcmpiW(req
->version
, g_szHttp1_1
);
4518 static void HTTP_CacheRequest(http_request_t
*request
)
4520 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
4521 WCHAR cacheFileName
[MAX_PATH
+1];
4524 b
= HTTP_GetRequestURL(request
, url
);
4526 WARN("Could not get URL\n");
4530 b
= CreateUrlCacheEntryW(url
, request
->contentLength
, NULL
, cacheFileName
, 0);
4532 heap_free(request
->cacheFile
);
4533 CloseHandle(request
->hCacheFile
);
4535 request
->cacheFile
= heap_strdupW(cacheFileName
);
4536 request
->hCacheFile
= CreateFileW(request
->cacheFile
, GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
4537 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
4538 if(request
->hCacheFile
== INVALID_HANDLE_VALUE
) {
4539 WARN("Could not create file: %u\n", GetLastError());
4540 request
->hCacheFile
= NULL
;
4543 WARN("Could not create cache entry: %08x\n", GetLastError());
4547 static DWORD
open_http_connection(http_request_t
*request
, BOOL
*reusing
)
4549 const BOOL is_https
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) != 0;
4550 http_session_t
*session
= request
->session
;
4551 netconn_t
*netconn
= NULL
;
4555 assert(!request
->netconn
);
4556 reset_data_stream(request
);
4558 server
= get_server(session
->serverName
, session
->serverPort
);
4560 return ERROR_OUTOFMEMORY
;
4562 res
= HTTP_ResolveName(request
, server
);
4563 if(res
!= ERROR_SUCCESS
) {
4564 server_release(server
);
4568 EnterCriticalSection(&connection_pool_cs
);
4570 while(!list_empty(&server
->conn_pool
)) {
4571 netconn
= LIST_ENTRY(list_head(&server
->conn_pool
), netconn_t
, pool_entry
);
4572 list_remove(&netconn
->pool_entry
);
4574 if(NETCON_is_alive(netconn
))
4577 TRACE("connection %p closed during idle\n", netconn
);
4578 free_netconn(netconn
);
4582 LeaveCriticalSection(&connection_pool_cs
);
4585 TRACE("<-- reusing %p netconn\n", netconn
);
4586 request
->netconn
= netconn
;
4588 return ERROR_SUCCESS
;
4591 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4592 INTERNET_STATUS_CONNECTING_TO_SERVER
,
4594 strlen(server
->addr_str
)+1);
4596 res
= create_netconn(is_https
, server
, request
->security_flags
, request
->connect_timeout
, &netconn
);
4597 server_release(server
);
4598 if(res
!= ERROR_SUCCESS
) {
4599 ERR("create_netconn failed: %u\n", res
);
4603 request
->netconn
= netconn
;
4605 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4606 INTERNET_STATUS_CONNECTED_TO_SERVER
,
4607 server
->addr_str
, strlen(server
->addr_str
)+1);
4610 /* Note: we differ from Microsoft's WinINet here. they seem to have
4611 * a bug that causes no status callbacks to be sent when starting
4612 * a tunnel to a proxy server using the CONNECT verb. i believe our
4613 * behaviour to be more correct and to not cause any incompatibilities
4614 * because using a secure connection through a proxy server is a rare
4615 * case that would be hard for anyone to depend on */
4616 if(session
->appInfo
->proxy
)
4617 res
= HTTP_SecureProxyConnect(request
);
4618 if(res
== ERROR_SUCCESS
)
4619 res
= NETCON_secure_connect(request
->netconn
, session
->hostName
);
4620 if(res
!= ERROR_SUCCESS
)
4622 WARN("Couldn't connect securely to host\n");
4624 if((request
->hdr
.ErrorMask
&INTERNET_ERROR_MASK_COMBINED_SEC_CERT
) && (
4625 res
== ERROR_INTERNET_SEC_CERT_DATE_INVALID
4626 || res
== ERROR_INTERNET_INVALID_CA
4627 || res
== ERROR_INTERNET_SEC_CERT_NO_REV
4628 || res
== ERROR_INTERNET_SEC_CERT_REV_FAILED
4629 || res
== ERROR_INTERNET_SEC_CERT_REVOKED
4630 || res
== ERROR_INTERNET_SEC_INVALID_CERT
4631 || res
== ERROR_INTERNET_SEC_CERT_CN_INVALID
))
4632 res
= ERROR_INTERNET_SEC_CERT_ERRORS
;
4636 if(res
!= ERROR_SUCCESS
) {
4637 http_release_netconn(request
, FALSE
);
4642 TRACE("Created connection to %s: %p\n", debugstr_w(server
->name
), netconn
);
4643 return ERROR_SUCCESS
;
4646 /***********************************************************************
4647 * HTTP_HttpSendRequestW (internal)
4649 * Sends the specified request to the HTTP server
4652 * ERROR_SUCCESS on success
4653 * win32 error code on failure
4656 static DWORD
HTTP_HttpSendRequestW(http_request_t
*request
, LPCWSTR lpszHeaders
,
4657 DWORD dwHeaderLength
, LPVOID lpOptional
, DWORD dwOptionalLength
,
4658 DWORD dwContentLength
, BOOL bEndRequest
)
4661 BOOL redirected
= FALSE
;
4662 LPWSTR requestString
= NULL
;
4665 static const WCHAR szPost
[] = { 'P','O','S','T',0 };
4666 static const WCHAR szContentLength
[] =
4667 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4668 WCHAR contentLengthStr
[sizeof szContentLength
/2 /* includes \r\n */ + 20 /* int */ ];
4671 TRACE("--> %p\n", request
);
4673 assert(request
->hdr
.htype
== WH_HHTTPREQ
);
4675 /* if the verb is NULL default to GET */
4677 request
->verb
= heap_strdupW(szGET
);
4679 if (dwContentLength
|| strcmpW(request
->verb
, szGET
))
4681 sprintfW(contentLengthStr
, szContentLength
, dwContentLength
);
4682 HTTP_HttpAddRequestHeadersW(request
, contentLengthStr
, -1L, HTTP_ADDREQ_FLAG_REPLACE
);
4683 request
->bytesToWrite
= dwContentLength
;
4685 if (request
->session
->appInfo
->agent
)
4687 WCHAR
*agent_header
;
4688 static const WCHAR user_agent
[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4691 len
= strlenW(request
->session
->appInfo
->agent
) + strlenW(user_agent
);
4692 agent_header
= heap_alloc(len
* sizeof(WCHAR
));
4693 sprintfW(agent_header
, user_agent
, request
->session
->appInfo
->agent
);
4695 HTTP_HttpAddRequestHeadersW(request
, agent_header
, strlenW(agent_header
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4696 heap_free(agent_header
);
4698 if (request
->hdr
.dwFlags
& INTERNET_FLAG_PRAGMA_NOCACHE
)
4700 static const WCHAR pragma_nocache
[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4701 HTTP_HttpAddRequestHeadersW(request
, pragma_nocache
, strlenW(pragma_nocache
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4703 if ((request
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
) && !strcmpW(request
->verb
, szPost
))
4705 static const WCHAR cache_control
[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4706 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4707 HTTP_HttpAddRequestHeadersW(request
, cache_control
, strlenW(cache_control
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4710 /* add the headers the caller supplied */
4711 if( lpszHeaders
&& dwHeaderLength
)
4712 HTTP_HttpAddRequestHeadersW(request
, lpszHeaders
, dwHeaderLength
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REPLACE
);
4717 BOOL reusing_connection
;
4722 /* like native, just in case the caller forgot to call InternetReadFile
4723 * for all the data */
4724 drain_content(request
);
4726 request
->contentLength
= ~0u;
4727 request
->bytesToWrite
= 0;
4730 if (TRACE_ON(wininet
))
4732 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4733 TRACE("Going to url %s %s\n", debugstr_w(Host
->lpszValue
), debugstr_w(request
->path
));
4736 HTTP_FixURL(request
);
4737 if (request
->hdr
.dwFlags
& INTERNET_FLAG_KEEP_CONNECTION
)
4739 HTTP_ProcessHeader(request
, szConnection
, szKeepAlive
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
4741 HTTP_InsertAuthorization(request
, request
->authInfo
, szAuthorization
);
4742 HTTP_InsertAuthorization(request
, request
->proxyAuthInfo
, szProxy_Authorization
);
4744 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
))
4745 HTTP_InsertCookies(request
);
4747 if (request
->session
->appInfo
->proxy
&& request
->session
->appInfo
->proxy
[0])
4749 WCHAR
*url
= HTTP_BuildProxyRequestUrl(request
);
4750 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, url
, request
->version
);
4754 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
4757 TRACE("Request header -> %s\n", debugstr_w(requestString
) );
4759 if ((res
= open_http_connection(request
, &reusing_connection
)) != ERROR_SUCCESS
)
4762 /* send the request as ASCII, tack on the optional data */
4763 if (!lpOptional
|| redirected
)
4764 dwOptionalLength
= 0;
4765 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4766 NULL
, 0, NULL
, NULL
);
4767 ascii_req
= heap_alloc(len
+ dwOptionalLength
);
4768 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4769 ascii_req
, len
, NULL
, NULL
);
4771 memcpy( &ascii_req
[len
-1], lpOptional
, dwOptionalLength
);
4772 len
= (len
+ dwOptionalLength
- 1);
4774 TRACE("full request -> %s\n", debugstr_a(ascii_req
) );
4776 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4777 INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
4779 NETCON_set_timeout( request
->netconn
, TRUE
, request
->send_timeout
);
4780 res
= NETCON_send(request
->netconn
, ascii_req
, len
, 0, &cnt
);
4781 heap_free( ascii_req
);
4782 if(res
!= ERROR_SUCCESS
) {
4783 TRACE("send failed: %u\n", res
);
4784 if(!reusing_connection
)
4786 http_release_netconn(request
, FALSE
);
4791 request
->bytesWritten
= dwOptionalLength
;
4793 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4794 INTERNET_STATUS_REQUEST_SENT
,
4795 &len
, sizeof(DWORD
));
4802 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4803 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
4805 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
4806 /* FIXME: We should know that connection is closed before sending
4807 * headers. Otherwise wrong callbacks are executed */
4808 if(!responseLen
&& reusing_connection
) {
4809 TRACE("Connection closed by server, reconnecting\n");
4810 http_release_netconn(request
, FALSE
);
4815 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4816 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
,
4819 http_process_keep_alive(request
);
4820 HTTP_ProcessCookies(request
);
4821 HTTP_ProcessExpires(request
);
4822 HTTP_ProcessLastModified(request
);
4824 dwBufferSize
= sizeof(dwStatusCode
);
4825 if (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
,
4826 &dwStatusCode
,&dwBufferSize
,NULL
) != ERROR_SUCCESS
)
4829 res
= set_content_length(request
, dwStatusCode
);
4830 if(res
!= ERROR_SUCCESS
)
4832 if(!request
->contentLength
)
4833 http_release_netconn(request
, TRUE
);
4835 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
) && responseLen
)
4837 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
4838 dwBufferSize
=sizeof(szNewLocation
);
4839 if ((dwStatusCode
== HTTP_STATUS_REDIRECT
||
4840 dwStatusCode
== HTTP_STATUS_MOVED
||
4841 dwStatusCode
== HTTP_STATUS_REDIRECT_KEEP_VERB
||
4842 dwStatusCode
== HTTP_STATUS_REDIRECT_METHOD
) &&
4843 HTTP_HttpQueryInfoW(request
,HTTP_QUERY_LOCATION
,szNewLocation
,&dwBufferSize
,NULL
) == ERROR_SUCCESS
)
4845 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
) &&
4846 dwStatusCode
!= HTTP_STATUS_REDIRECT_KEEP_VERB
)
4848 heap_free(request
->verb
);
4849 request
->verb
= heap_strdupW(szGET
);
4851 drain_content(request
);
4852 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
4854 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
4855 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
4856 res
= HTTP_HandleRedirect(request
, new_url
);
4857 if (res
== ERROR_SUCCESS
)
4859 heap_free(requestString
);
4862 heap_free( new_url
);
4867 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTH
) && res
== ERROR_SUCCESS
)
4869 WCHAR szAuthValue
[2048];
4871 if (dwStatusCode
== HTTP_STATUS_DENIED
)
4873 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4875 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_WWW_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4877 if (HTTP_DoAuthorization(request
, szAuthValue
,
4879 request
->session
->userName
,
4880 request
->session
->password
,
4883 heap_free(requestString
);
4890 TRACE("Cleaning wrong authorization data\n");
4891 destroy_authinfo(request
->authInfo
);
4892 request
->authInfo
= NULL
;
4895 if (dwStatusCode
== HTTP_STATUS_PROXY_AUTH_REQ
)
4898 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_PROXY_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4900 if (HTTP_DoAuthorization(request
, szAuthValue
,
4901 &request
->proxyAuthInfo
,
4902 request
->session
->appInfo
->proxyUsername
,
4903 request
->session
->appInfo
->proxyPassword
,
4912 TRACE("Cleaning wrong proxy authorization data\n");
4913 destroy_authinfo(request
->proxyAuthInfo
);
4914 request
->proxyAuthInfo
= NULL
;
4920 res
= ERROR_SUCCESS
;
4924 if(res
== ERROR_SUCCESS
)
4925 HTTP_CacheRequest(request
);
4928 heap_free(requestString
);
4930 /* TODO: send notification for P3P header */
4932 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4934 if (res
== ERROR_SUCCESS
) {
4935 if(bEndRequest
&& request
->contentLength
&& request
->bytesWritten
== request
->bytesToWrite
)
4936 HTTP_ReceiveRequestData(request
, TRUE
);
4938 send_request_complete(request
,
4939 request
->session
->hdr
.dwInternalFlags
& INET_OPENURL
? (DWORD_PTR
)request
->hdr
.hInternet
: 1, 0);
4941 send_request_complete(request
, 0, res
);
4949 /***********************************************************************
4951 * Helper functions for the HttpSendRequest(Ex) functions
4954 static void AsyncHttpSendRequestProc(WORKREQUEST
*workRequest
)
4956 struct WORKREQ_HTTPSENDREQUESTW
const *req
= &workRequest
->u
.HttpSendRequestW
;
4957 http_request_t
*request
= (http_request_t
*) workRequest
->hdr
;
4959 TRACE("%p\n", request
);
4961 HTTP_HttpSendRequestW(request
, req
->lpszHeader
,
4962 req
->dwHeaderLength
, req
->lpOptional
, req
->dwOptionalLength
,
4963 req
->dwContentLength
, req
->bEndRequest
);
4965 heap_free(req
->lpszHeader
);
4969 static DWORD
HTTP_HttpEndRequestW(http_request_t
*request
, DWORD dwFlags
, DWORD_PTR dwContext
)
4972 DWORD dwCode
, dwCodeLength
;
4974 DWORD res
= ERROR_SUCCESS
;
4976 if(!request
->netconn
) {
4977 WARN("Not connected\n");
4978 send_request_complete(request
, 0, ERROR_INTERNET_OPERATION_CANCELLED
);
4979 return ERROR_INTERNET_OPERATION_CANCELLED
;
4982 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4983 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
4985 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
4987 res
= ERROR_HTTP_HEADER_NOT_FOUND
;
4989 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4990 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
, sizeof(DWORD
));
4992 /* process cookies here. Is this right? */
4993 http_process_keep_alive(request
);
4994 HTTP_ProcessCookies(request
);
4995 HTTP_ProcessExpires(request
);
4996 HTTP_ProcessLastModified(request
);
4998 dwCodeLength
= sizeof(dwCode
);
4999 if (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
,
5000 &dwCode
,&dwCodeLength
,NULL
) != ERROR_SUCCESS
)
5003 if ((res
= set_content_length( request
, dwCode
)) == ERROR_SUCCESS
) {
5004 if(!request
->contentLength
)
5005 http_release_netconn(request
, TRUE
);
5008 if (res
== ERROR_SUCCESS
&& !(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
))
5010 if (dwCode
== HTTP_STATUS_REDIRECT
||
5011 dwCode
== HTTP_STATUS_MOVED
||
5012 dwCode
== HTTP_STATUS_REDIRECT_METHOD
||
5013 dwCode
== HTTP_STATUS_REDIRECT_KEEP_VERB
)
5015 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
5016 dwBufferSize
=sizeof(szNewLocation
);
5017 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_LOCATION
, szNewLocation
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
5019 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
) &&
5020 dwCode
!= HTTP_STATUS_REDIRECT_KEEP_VERB
)
5022 heap_free(request
->verb
);
5023 request
->verb
= heap_strdupW(szGET
);
5025 drain_content(request
);
5026 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
5028 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
5029 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
5030 res
= HTTP_HandleRedirect(request
, new_url
);
5031 if (res
== ERROR_SUCCESS
)
5032 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, TRUE
);
5033 heap_free( new_url
);
5039 if (res
== ERROR_SUCCESS
&& request
->contentLength
)
5040 HTTP_ReceiveRequestData(request
, TRUE
);
5042 send_request_complete(request
, res
== ERROR_SUCCESS
, res
);
5047 /***********************************************************************
5048 * HttpEndRequestA (WININET.@)
5050 * Ends an HTTP request that was started by HttpSendRequestEx
5053 * TRUE if successful
5057 BOOL WINAPI
HttpEndRequestA(HINTERNET hRequest
,
5058 LPINTERNET_BUFFERSA lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
5060 TRACE("(%p, %p, %08x, %08lx)\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
5064 SetLastError(ERROR_INVALID_PARAMETER
);
5068 return HttpEndRequestW(hRequest
, NULL
, dwFlags
, dwContext
);
5071 static void AsyncHttpEndRequestProc(WORKREQUEST
*work
)
5073 struct WORKREQ_HTTPENDREQUESTW
const *req
= &work
->u
.HttpEndRequestW
;
5074 http_request_t
*request
= (http_request_t
*)work
->hdr
;
5076 TRACE("%p\n", request
);
5078 HTTP_HttpEndRequestW(request
, req
->dwFlags
, req
->dwContext
);
5081 /***********************************************************************
5082 * HttpEndRequestW (WININET.@)
5084 * Ends an HTTP request that was started by HttpSendRequestEx
5087 * TRUE if successful
5091 BOOL WINAPI
HttpEndRequestW(HINTERNET hRequest
,
5092 LPINTERNET_BUFFERSW lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
5094 http_request_t
*request
;
5097 TRACE("%p %p %x %lx -->\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
5101 SetLastError(ERROR_INVALID_PARAMETER
);
5105 request
= (http_request_t
*) get_handle_object( hRequest
);
5107 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5109 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
5111 WININET_Release( &request
->hdr
);
5114 request
->hdr
.dwFlags
|= dwFlags
;
5116 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5119 struct WORKREQ_HTTPENDREQUESTW
*work_endrequest
;
5121 work
.asyncproc
= AsyncHttpEndRequestProc
;
5122 work
.hdr
= WININET_AddRef( &request
->hdr
);
5124 work_endrequest
= &work
.u
.HttpEndRequestW
;
5125 work_endrequest
->dwFlags
= dwFlags
;
5126 work_endrequest
->dwContext
= dwContext
;
5128 INTERNET_AsyncCall(&work
);
5129 res
= ERROR_IO_PENDING
;
5132 res
= HTTP_HttpEndRequestW(request
, dwFlags
, dwContext
);
5134 WININET_Release( &request
->hdr
);
5135 TRACE("%u <--\n", res
);
5136 if(res
!= ERROR_SUCCESS
)
5138 return res
== ERROR_SUCCESS
;
5141 /***********************************************************************
5142 * HttpSendRequestExA (WININET.@)
5144 * Sends the specified request to the HTTP server and allows chunked
5149 * Failure: FALSE, call GetLastError() for more information.
5151 BOOL WINAPI
HttpSendRequestExA(HINTERNET hRequest
,
5152 LPINTERNET_BUFFERSA lpBuffersIn
,
5153 LPINTERNET_BUFFERSA lpBuffersOut
,
5154 DWORD dwFlags
, DWORD_PTR dwContext
)
5156 INTERNET_BUFFERSW BuffersInW
;
5159 LPWSTR header
= NULL
;
5161 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
5162 lpBuffersOut
, dwFlags
, dwContext
);
5166 BuffersInW
.dwStructSize
= sizeof(LPINTERNET_BUFFERSW
);
5167 if (lpBuffersIn
->lpcszHeader
)
5169 headerlen
= MultiByteToWideChar(CP_ACP
,0,lpBuffersIn
->lpcszHeader
,
5170 lpBuffersIn
->dwHeadersLength
,0,0);
5171 header
= heap_alloc(headerlen
*sizeof(WCHAR
));
5172 if (!(BuffersInW
.lpcszHeader
= header
))
5174 SetLastError(ERROR_OUTOFMEMORY
);
5177 BuffersInW
.dwHeadersLength
= MultiByteToWideChar(CP_ACP
, 0,
5178 lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
5182 BuffersInW
.lpcszHeader
= NULL
;
5183 BuffersInW
.dwHeadersTotal
= lpBuffersIn
->dwHeadersTotal
;
5184 BuffersInW
.lpvBuffer
= lpBuffersIn
->lpvBuffer
;
5185 BuffersInW
.dwBufferLength
= lpBuffersIn
->dwBufferLength
;
5186 BuffersInW
.dwBufferTotal
= lpBuffersIn
->dwBufferTotal
;
5187 BuffersInW
.Next
= NULL
;
5190 rc
= HttpSendRequestExW(hRequest
, lpBuffersIn
? &BuffersInW
: NULL
, NULL
, dwFlags
, dwContext
);
5196 /***********************************************************************
5197 * HttpSendRequestExW (WININET.@)
5199 * Sends the specified request to the HTTP server and allows chunked
5204 * Failure: FALSE, call GetLastError() for more information.
5206 BOOL WINAPI
HttpSendRequestExW(HINTERNET hRequest
,
5207 LPINTERNET_BUFFERSW lpBuffersIn
,
5208 LPINTERNET_BUFFERSW lpBuffersOut
,
5209 DWORD dwFlags
, DWORD_PTR dwContext
)
5211 http_request_t
*request
;
5212 http_session_t
*session
;
5216 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
5217 lpBuffersOut
, dwFlags
, dwContext
);
5219 request
= (http_request_t
*) get_handle_object( hRequest
);
5221 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5223 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5227 session
= request
->session
;
5228 assert(session
->hdr
.htype
== WH_HHTTPSESSION
);
5229 hIC
= session
->appInfo
;
5230 assert(hIC
->hdr
.htype
== WH_HINIT
);
5232 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5234 WORKREQUEST workRequest
;
5235 struct WORKREQ_HTTPSENDREQUESTW
*req
;
5237 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
5238 workRequest
.hdr
= WININET_AddRef( &request
->hdr
);
5239 req
= &workRequest
.u
.HttpSendRequestW
;
5244 if (lpBuffersIn
->lpcszHeader
)
5246 if (lpBuffersIn
->dwHeadersLength
== ~0u)
5247 size
= (strlenW( lpBuffersIn
->lpcszHeader
) + 1) * sizeof(WCHAR
);
5249 size
= lpBuffersIn
->dwHeadersLength
* sizeof(WCHAR
);
5251 req
->lpszHeader
= heap_alloc(size
);
5252 memcpy( req
->lpszHeader
, lpBuffersIn
->lpcszHeader
, size
);
5254 else req
->lpszHeader
= NULL
;
5256 req
->dwHeaderLength
= size
/ sizeof(WCHAR
);
5257 req
->lpOptional
= lpBuffersIn
->lpvBuffer
;
5258 req
->dwOptionalLength
= lpBuffersIn
->dwBufferLength
;
5259 req
->dwContentLength
= lpBuffersIn
->dwBufferTotal
;
5263 req
->lpszHeader
= NULL
;
5264 req
->dwHeaderLength
= 0;
5265 req
->lpOptional
= NULL
;
5266 req
->dwOptionalLength
= 0;
5267 req
->dwContentLength
= 0;
5270 req
->bEndRequest
= FALSE
;
5272 INTERNET_AsyncCall(&workRequest
);
5274 * This is from windows.
5276 res
= ERROR_IO_PENDING
;
5281 res
= HTTP_HttpSendRequestW(request
, lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
5282 lpBuffersIn
->lpvBuffer
, lpBuffersIn
->dwBufferLength
,
5283 lpBuffersIn
->dwBufferTotal
, FALSE
);
5285 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, FALSE
);
5290 WININET_Release( &request
->hdr
);
5294 return res
== ERROR_SUCCESS
;
5297 /***********************************************************************
5298 * HttpSendRequestW (WININET.@)
5300 * Sends the specified request to the HTTP server
5307 BOOL WINAPI
HttpSendRequestW(HINTERNET hHttpRequest
, LPCWSTR lpszHeaders
,
5308 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
5310 http_request_t
*request
;
5311 http_session_t
*session
= NULL
;
5312 appinfo_t
*hIC
= NULL
;
5313 DWORD res
= ERROR_SUCCESS
;
5315 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest
,
5316 debugstr_wn(lpszHeaders
, dwHeaderLength
), dwHeaderLength
, lpOptional
, dwOptionalLength
);
5318 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
5319 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5321 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5325 session
= request
->session
;
5326 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
5328 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5332 hIC
= session
->appInfo
;
5333 if (NULL
== hIC
|| hIC
->hdr
.htype
!= WH_HINIT
)
5335 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5339 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5341 WORKREQUEST workRequest
;
5342 struct WORKREQ_HTTPSENDREQUESTW
*req
;
5344 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
5345 workRequest
.hdr
= WININET_AddRef( &request
->hdr
);
5346 req
= &workRequest
.u
.HttpSendRequestW
;
5351 if (dwHeaderLength
== ~0u) size
= (strlenW(lpszHeaders
) + 1) * sizeof(WCHAR
);
5352 else size
= dwHeaderLength
* sizeof(WCHAR
);
5354 req
->lpszHeader
= heap_alloc(size
);
5355 memcpy(req
->lpszHeader
, lpszHeaders
, size
);
5358 req
->lpszHeader
= 0;
5359 req
->dwHeaderLength
= dwHeaderLength
;
5360 req
->lpOptional
= lpOptional
;
5361 req
->dwOptionalLength
= dwOptionalLength
;
5362 req
->dwContentLength
= dwOptionalLength
;
5363 req
->bEndRequest
= TRUE
;
5365 INTERNET_AsyncCall(&workRequest
);
5367 * This is from windows.
5369 res
= ERROR_IO_PENDING
;
5373 res
= HTTP_HttpSendRequestW(request
, lpszHeaders
,
5374 dwHeaderLength
, lpOptional
, dwOptionalLength
,
5375 dwOptionalLength
, TRUE
);
5379 WININET_Release( &request
->hdr
);
5382 return res
== ERROR_SUCCESS
;
5385 /***********************************************************************
5386 * HttpSendRequestA (WININET.@)
5388 * Sends the specified request to the HTTP server
5395 BOOL WINAPI
HttpSendRequestA(HINTERNET hHttpRequest
, LPCSTR lpszHeaders
,
5396 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
5399 LPWSTR szHeaders
=NULL
;
5400 DWORD nLen
=dwHeaderLength
;
5401 if(lpszHeaders
!=NULL
)
5403 nLen
=MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,NULL
,0);
5404 szHeaders
= heap_alloc(nLen
*sizeof(WCHAR
));
5405 MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,szHeaders
,nLen
);
5407 result
= HttpSendRequestW(hHttpRequest
, szHeaders
, nLen
, lpOptional
, dwOptionalLength
);
5408 heap_free(szHeaders
);
5412 /***********************************************************************
5413 * HTTPSESSION_Destroy (internal)
5415 * Deallocate session handle
5418 static void HTTPSESSION_Destroy(object_header_t
*hdr
)
5420 http_session_t
*session
= (http_session_t
*) hdr
;
5422 TRACE("%p\n", session
);
5424 WININET_Release(&session
->appInfo
->hdr
);
5426 heap_free(session
->hostName
);
5427 heap_free(session
->serverName
);
5428 heap_free(session
->password
);
5429 heap_free(session
->userName
);
5432 static DWORD
HTTPSESSION_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
5434 http_session_t
*ses
= (http_session_t
*)hdr
;
5437 case INTERNET_OPTION_HANDLE_TYPE
:
5438 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5440 if (*size
< sizeof(ULONG
))
5441 return ERROR_INSUFFICIENT_BUFFER
;
5443 *size
= sizeof(DWORD
);
5444 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_CONNECT_HTTP
;
5445 return ERROR_SUCCESS
;
5446 case INTERNET_OPTION_CONNECT_TIMEOUT
:
5447 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5449 if (*size
< sizeof(DWORD
))
5450 return ERROR_INSUFFICIENT_BUFFER
;
5452 *size
= sizeof(DWORD
);
5453 *(DWORD
*)buffer
= ses
->connect_timeout
;
5454 return ERROR_SUCCESS
;
5456 case INTERNET_OPTION_SEND_TIMEOUT
:
5457 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5459 if (*size
< sizeof(DWORD
))
5460 return ERROR_INSUFFICIENT_BUFFER
;
5462 *size
= sizeof(DWORD
);
5463 *(DWORD
*)buffer
= ses
->send_timeout
;
5464 return ERROR_SUCCESS
;
5466 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
5467 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5469 if (*size
< sizeof(DWORD
))
5470 return ERROR_INSUFFICIENT_BUFFER
;
5472 *size
= sizeof(DWORD
);
5473 *(DWORD
*)buffer
= ses
->receive_timeout
;
5474 return ERROR_SUCCESS
;
5477 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
5480 static DWORD
HTTPSESSION_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
5482 http_session_t
*ses
= (http_session_t
*)hdr
;
5485 case INTERNET_OPTION_USERNAME
:
5487 heap_free(ses
->userName
);
5488 if (!(ses
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5489 return ERROR_SUCCESS
;
5491 case INTERNET_OPTION_PASSWORD
:
5493 heap_free(ses
->password
);
5494 if (!(ses
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5495 return ERROR_SUCCESS
;
5497 case INTERNET_OPTION_CONNECT_TIMEOUT
:
5499 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5500 ses
->connect_timeout
= *(DWORD
*)buffer
;
5501 return ERROR_SUCCESS
;
5503 case INTERNET_OPTION_SEND_TIMEOUT
:
5505 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5506 ses
->send_timeout
= *(DWORD
*)buffer
;
5507 return ERROR_SUCCESS
;
5509 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
5511 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5512 ses
->receive_timeout
= *(DWORD
*)buffer
;
5513 return ERROR_SUCCESS
;
5518 return INET_SetOption(hdr
, option
, buffer
, size
);
5521 static const object_vtbl_t HTTPSESSIONVtbl
= {
5522 HTTPSESSION_Destroy
,
5524 HTTPSESSION_QueryOption
,
5525 HTTPSESSION_SetOption
,
5534 /***********************************************************************
5535 * HTTP_Connect (internal)
5537 * Create http session handle
5540 * HINTERNET a session handle on success
5544 DWORD
HTTP_Connect(appinfo_t
*hIC
, LPCWSTR lpszServerName
,
5545 INTERNET_PORT serverPort
, LPCWSTR lpszUserName
,
5546 LPCWSTR lpszPassword
, DWORD dwFlags
, DWORD_PTR dwContext
,
5547 DWORD dwInternalFlags
, HINTERNET
*ret
)
5549 http_session_t
*session
= NULL
;
5553 if (!lpszServerName
|| !lpszServerName
[0])
5554 return ERROR_INVALID_PARAMETER
;
5556 assert( hIC
->hdr
.htype
== WH_HINIT
);
5558 session
= alloc_object(&hIC
->hdr
, &HTTPSESSIONVtbl
, sizeof(http_session_t
));
5560 return ERROR_OUTOFMEMORY
;
5563 * According to my tests. The name is not resolved until a request is sent
5566 session
->hdr
.htype
= WH_HHTTPSESSION
;
5567 session
->hdr
.dwFlags
= dwFlags
;
5568 session
->hdr
.dwContext
= dwContext
;
5569 session
->hdr
.dwInternalFlags
|= dwInternalFlags
;
5571 WININET_AddRef( &hIC
->hdr
);
5572 session
->appInfo
= hIC
;
5573 list_add_head( &hIC
->hdr
.children
, &session
->hdr
.entry
);
5575 if(hIC
->proxy
&& hIC
->accessType
== INTERNET_OPEN_TYPE_PROXY
) {
5576 if(hIC
->proxyBypass
)
5577 FIXME("Proxy bypass is ignored.\n");
5579 session
->serverName
= heap_strdupW(lpszServerName
);
5580 session
->hostName
= heap_strdupW(lpszServerName
);
5581 if (lpszUserName
&& lpszUserName
[0])
5582 session
->userName
= heap_strdupW(lpszUserName
);
5583 if (lpszPassword
&& lpszPassword
[0])
5584 session
->password
= heap_strdupW(lpszPassword
);
5585 session
->serverPort
= serverPort
;
5586 session
->hostPort
= serverPort
;
5587 session
->connect_timeout
= INFINITE
;
5588 session
->send_timeout
= INFINITE
;
5589 session
->receive_timeout
= INFINITE
;
5591 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5592 if (!(session
->hdr
.dwInternalFlags
& INET_OPENURL
))
5594 INTERNET_SendCallback(&hIC
->hdr
, dwContext
,
5595 INTERNET_STATUS_HANDLE_CREATED
, &session
->hdr
.hInternet
,
5600 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5604 TRACE("%p --> %p\n", hIC
, session
);
5606 *ret
= session
->hdr
.hInternet
;
5607 return ERROR_SUCCESS
;
5610 /***********************************************************************
5611 * HTTP_clear_response_headers (internal)
5613 * clear out any old response headers
5615 static void HTTP_clear_response_headers( http_request_t
*request
)
5619 for( i
=0; i
<request
->nCustHeaders
; i
++)
5621 if( !request
->custHeaders
[i
].lpszField
)
5623 if( !request
->custHeaders
[i
].lpszValue
)
5625 if ( request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
5627 HTTP_DeleteCustomHeader( request
, i
);
5632 /***********************************************************************
5633 * HTTP_GetResponseHeaders (internal)
5635 * Read server response
5642 static INT
HTTP_GetResponseHeaders(http_request_t
*request
, BOOL clear
)
5645 WCHAR buffer
[MAX_REPLY_LEN
];
5646 DWORD buflen
= MAX_REPLY_LEN
;
5647 BOOL bSuccess
= FALSE
;
5649 char bufferA
[MAX_REPLY_LEN
];
5650 LPWSTR status_code
= NULL
, status_text
= NULL
;
5651 DWORD cchMaxRawHeaders
= 1024;
5652 LPWSTR lpszRawHeaders
= NULL
;
5654 DWORD cchRawHeaders
= 0;
5655 BOOL codeHundred
= FALSE
;
5659 if(!request
->netconn
)
5662 NETCON_set_timeout( request
->netconn
, FALSE
, request
->receive_timeout
);
5664 static const WCHAR szHundred
[] = {'1','0','0',0};
5666 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5668 buflen
= MAX_REPLY_LEN
;
5669 if (!read_line(request
, bufferA
, &buflen
))
5672 /* clear old response headers (eg. from a redirect response) */
5674 HTTP_clear_response_headers( request
);
5679 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5680 /* check is this a status code line? */
5681 if (!strncmpW(buffer
, g_szHttp1_0
, 4))
5683 /* split the version from the status code */
5684 status_code
= strchrW( buffer
, ' ' );
5689 /* split the status code from the status text */
5690 status_text
= strchrW( status_code
, ' ' );
5695 TRACE("version [%s] status code [%s] status text [%s]\n",
5696 debugstr_w(buffer
), debugstr_w(status_code
), debugstr_w(status_text
) );
5698 codeHundred
= (!strcmpW(status_code
, szHundred
));
5700 else if (!codeHundred
)
5702 WARN("No status line at head of response (%s)\n", debugstr_w(buffer
));
5704 heap_free(request
->version
);
5705 heap_free(request
->statusText
);
5707 request
->version
= heap_strdupW(g_szHttp1_0
);
5708 request
->statusText
= heap_strdupW(szOK
);
5710 heap_free(request
->rawHeaders
);
5711 request
->rawHeaders
= heap_strdupW(szDefaultHeader
);
5716 } while (codeHundred
);
5718 /* Add status code */
5719 HTTP_ProcessHeader(request
, szStatus
, status_code
,
5720 HTTP_ADDHDR_FLAG_REPLACE
);
5722 heap_free(request
->version
);
5723 heap_free(request
->statusText
);
5725 request
->version
= heap_strdupW(buffer
);
5726 request
->statusText
= heap_strdupW(status_text
);
5728 /* Restore the spaces */
5729 *(status_code
-1) = ' ';
5730 *(status_text
-1) = ' ';
5732 /* regenerate raw headers */
5733 lpszRawHeaders
= heap_alloc((cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5734 if (!lpszRawHeaders
) goto lend
;
5736 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5737 cchMaxRawHeaders
*= 2;
5738 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5739 if (temp
== NULL
) goto lend
;
5740 lpszRawHeaders
= temp
;
5741 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5742 cchRawHeaders
+= (buflen
-1);
5743 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5744 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5745 lpszRawHeaders
[cchRawHeaders
] = '\0';
5747 /* Parse each response line */
5750 buflen
= MAX_REPLY_LEN
;
5751 if (read_line(request
, bufferA
, &buflen
))
5753 LPWSTR
* pFieldAndValue
;
5755 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA
));
5757 if (!bufferA
[0]) break;
5758 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5760 pFieldAndValue
= HTTP_InterpretHttpHeader(buffer
);
5763 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5764 cchMaxRawHeaders
*= 2;
5765 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5766 if (temp
== NULL
) goto lend
;
5767 lpszRawHeaders
= temp
;
5768 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5769 cchRawHeaders
+= (buflen
-1);
5770 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5771 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5772 lpszRawHeaders
[cchRawHeaders
] = '\0';
5774 HTTP_ProcessHeader(request
, pFieldAndValue
[0], pFieldAndValue
[1],
5775 HTTP_ADDREQ_FLAG_ADD
);
5777 HTTP_FreeTokens(pFieldAndValue
);
5788 /* make sure the response header is terminated with an empty line. Some apps really
5789 truly care about that empty line being there for some reason. Just add it to the
5791 if (cchRawHeaders
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5793 cchMaxRawHeaders
= cchRawHeaders
+ strlenW(szCrLf
);
5794 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5795 if (temp
== NULL
) goto lend
;
5796 lpszRawHeaders
= temp
;
5799 memcpy(&lpszRawHeaders
[cchRawHeaders
], szCrLf
, sizeof(szCrLf
));
5801 heap_free(request
->rawHeaders
);
5802 request
->rawHeaders
= lpszRawHeaders
;
5803 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders
));
5813 heap_free(lpszRawHeaders
);
5818 /***********************************************************************
5819 * HTTP_InterpretHttpHeader (internal)
5821 * Parse server response
5825 * Pointer to array of field, value, NULL on success.
5828 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
)
5830 LPWSTR
* pTokenPair
;
5834 pTokenPair
= heap_alloc_zero(sizeof(*pTokenPair
)*3);
5836 pszColon
= strchrW(buffer
, ':');
5837 /* must have two tokens */
5840 HTTP_FreeTokens(pTokenPair
);
5842 TRACE("No ':' in line: %s\n", debugstr_w(buffer
));
5846 pTokenPair
[0] = heap_alloc((pszColon
- buffer
+ 1) * sizeof(WCHAR
));
5849 HTTP_FreeTokens(pTokenPair
);
5852 memcpy(pTokenPair
[0], buffer
, (pszColon
- buffer
) * sizeof(WCHAR
));
5853 pTokenPair
[0][pszColon
- buffer
] = '\0';
5857 len
= strlenW(pszColon
);
5858 pTokenPair
[1] = heap_alloc((len
+ 1) * sizeof(WCHAR
));
5861 HTTP_FreeTokens(pTokenPair
);
5864 memcpy(pTokenPair
[1], pszColon
, (len
+ 1) * sizeof(WCHAR
));
5866 strip_spaces(pTokenPair
[0]);
5867 strip_spaces(pTokenPair
[1]);
5869 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair
[0]), debugstr_w(pTokenPair
[1]));
5873 /***********************************************************************
5874 * HTTP_ProcessHeader (internal)
5876 * Stuff header into header tables according to <dwModifier>
5880 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5882 static DWORD
HTTP_ProcessHeader(http_request_t
*request
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
)
5884 LPHTTPHEADERW lphttpHdr
= NULL
;
5886 BOOL request_only
= dwModifier
& HTTP_ADDHDR_FLAG_REQ
;
5887 DWORD res
= ERROR_HTTP_INVALID_HEADER
;
5889 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field
), debugstr_w(value
), dwModifier
);
5891 /* REPLACE wins out over ADD */
5892 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5893 dwModifier
&= ~HTTP_ADDHDR_FLAG_ADD
;
5895 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD
)
5898 index
= HTTP_GetCustomHeaderIndex(request
, field
, 0, request_only
);
5902 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD_IF_NEW
)
5903 return ERROR_HTTP_INVALID_HEADER
;
5904 lphttpHdr
= &request
->custHeaders
[index
];
5910 hdr
.lpszField
= (LPWSTR
)field
;
5911 hdr
.lpszValue
= (LPWSTR
)value
;
5912 hdr
.wFlags
= hdr
.wCount
= 0;
5914 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5915 hdr
.wFlags
|= HDR_ISREQUEST
;
5917 return HTTP_InsertCustomHeader(request
, &hdr
);
5919 /* no value to delete */
5920 else return ERROR_SUCCESS
;
5922 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5923 lphttpHdr
->wFlags
|= HDR_ISREQUEST
;
5925 lphttpHdr
->wFlags
&= ~HDR_ISREQUEST
;
5927 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5929 HTTP_DeleteCustomHeader( request
, index
);
5935 hdr
.lpszField
= (LPWSTR
)field
;
5936 hdr
.lpszValue
= (LPWSTR
)value
;
5937 hdr
.wFlags
= hdr
.wCount
= 0;
5939 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5940 hdr
.wFlags
|= HDR_ISREQUEST
;
5942 return HTTP_InsertCustomHeader(request
, &hdr
);
5945 return ERROR_SUCCESS
;
5947 else if (dwModifier
& COALESCEFLAGS
)
5952 INT origlen
= strlenW(lphttpHdr
->lpszValue
);
5953 INT valuelen
= strlenW(value
);
5955 if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
)
5958 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
5960 else if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON
)
5963 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
5966 len
= origlen
+ valuelen
+ ((ch
> 0) ? 2 : 0);
5968 lpsztmp
= heap_realloc(lphttpHdr
->lpszValue
, (len
+1)*sizeof(WCHAR
));
5971 lphttpHdr
->lpszValue
= lpsztmp
;
5972 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5975 lphttpHdr
->lpszValue
[origlen
] = ch
;
5977 lphttpHdr
->lpszValue
[origlen
] = ' ';
5981 memcpy(&lphttpHdr
->lpszValue
[origlen
], value
, valuelen
*sizeof(WCHAR
));
5982 lphttpHdr
->lpszValue
[len
] = '\0';
5983 res
= ERROR_SUCCESS
;
5987 WARN("heap_realloc (%d bytes) failed\n",len
+1);
5988 res
= ERROR_OUTOFMEMORY
;
5991 TRACE("<-- %d\n", res
);
5995 /***********************************************************************
5996 * HTTP_GetCustomHeaderIndex (internal)
5998 * Return index of custom header from header array
6001 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*request
, LPCWSTR lpszField
,
6002 int requested_index
, BOOL request_only
)
6006 TRACE("%s, %d, %d\n", debugstr_w(lpszField
), requested_index
, request_only
);
6008 for (index
= 0; index
< request
->nCustHeaders
; index
++)
6010 if (strcmpiW(request
->custHeaders
[index
].lpszField
, lpszField
))
6013 if (request_only
&& !(request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
6016 if (!request_only
&& (request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
6019 if (requested_index
== 0)
6024 if (index
>= request
->nCustHeaders
)
6027 TRACE("Return: %d\n", index
);
6032 /***********************************************************************
6033 * HTTP_InsertCustomHeader (internal)
6035 * Insert header into array
6038 static DWORD
HTTP_InsertCustomHeader(http_request_t
*request
, LPHTTPHEADERW lpHdr
)
6041 LPHTTPHEADERW lph
= NULL
;
6043 TRACE("--> %s: %s\n", debugstr_w(lpHdr
->lpszField
), debugstr_w(lpHdr
->lpszValue
));
6044 count
= request
->nCustHeaders
+ 1;
6046 lph
= heap_realloc_zero(request
->custHeaders
, sizeof(HTTPHEADERW
) * count
);
6048 lph
= heap_alloc_zero(sizeof(HTTPHEADERW
) * count
);
6051 return ERROR_OUTOFMEMORY
;
6053 request
->custHeaders
= lph
;
6054 request
->custHeaders
[count
-1].lpszField
= heap_strdupW(lpHdr
->lpszField
);
6055 request
->custHeaders
[count
-1].lpszValue
= heap_strdupW(lpHdr
->lpszValue
);
6056 request
->custHeaders
[count
-1].wFlags
= lpHdr
->wFlags
;
6057 request
->custHeaders
[count
-1].wCount
= lpHdr
->wCount
;
6058 request
->nCustHeaders
++;
6060 return ERROR_SUCCESS
;
6064 /***********************************************************************
6065 * HTTP_DeleteCustomHeader (internal)
6067 * Delete header from array
6068 * If this function is called, the indexs may change.
6070 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*request
, DWORD index
)
6072 if( request
->nCustHeaders
<= 0 )
6074 if( index
>= request
->nCustHeaders
)
6076 request
->nCustHeaders
--;
6078 heap_free(request
->custHeaders
[index
].lpszField
);
6079 heap_free(request
->custHeaders
[index
].lpszValue
);
6081 memmove( &request
->custHeaders
[index
], &request
->custHeaders
[index
+1],
6082 (request
->nCustHeaders
- index
)* sizeof(HTTPHEADERW
) );
6083 memset( &request
->custHeaders
[request
->nCustHeaders
], 0, sizeof(HTTPHEADERW
) );
6089 /***********************************************************************
6090 * HTTP_VerifyValidHeader (internal)
6092 * Verify the given header is not invalid for the given http request
6095 static BOOL
HTTP_VerifyValidHeader(http_request_t
*request
, LPCWSTR field
)
6097 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6098 if (!strcmpW(request
->version
, g_szHttp1_0
) && !strcmpiW(field
, szAccept_Encoding
))
6099 return ERROR_HTTP_INVALID_HEADER
;
6101 return ERROR_SUCCESS
;
6104 /***********************************************************************
6105 * IsHostInProxyBypassList (@)
6110 BOOL WINAPI
IsHostInProxyBypassList(DWORD flags
, LPCSTR szHost
, DWORD length
)
6112 FIXME("STUB: flags=%d host=%s length=%d\n",flags
,szHost
,length
);
6116 /***********************************************************************
6117 * InternetShowSecurityInfoByURLA (@)
6119 BOOL WINAPI
InternetShowSecurityInfoByURLA(LPCSTR url
, HWND window
)
6121 FIXME("stub: %s %p\n", url
, window
);
6125 /***********************************************************************
6126 * InternetShowSecurityInfoByURLW (@)
6128 BOOL WINAPI
InternetShowSecurityInfoByURLW(LPCWSTR url
, HWND window
)
6130 FIXME("stub: %s %p\n", debugstr_w(url
), window
);
6134 /***********************************************************************
6135 * ShowX509EncodedCertificate (@)
6137 DWORD WINAPI
ShowX509EncodedCertificate(HWND parent
, LPBYTE cert
, DWORD len
)
6139 PCCERT_CONTEXT certContext
= CertCreateCertificateContext(X509_ASN_ENCODING
,
6145 CRYPTUI_VIEWCERTIFICATE_STRUCTW view
;
6147 memset(&view
, 0, sizeof(view
));
6148 view
.hwndParent
= parent
;
6149 view
.pCertContext
= certContext
;
6150 if (CryptUIDlgViewCertificateW(&view
, NULL
))
6151 ret
= ERROR_SUCCESS
;
6153 ret
= GetLastError();
6154 CertFreeCertificateContext(certContext
);
6157 ret
= GetLastError();