2 * Wininet - Http Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
10 * Copyright 2011 Jacek Caban for CodeWeavers
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
37 #include <sys/types.h>
38 #ifdef HAVE_SYS_SOCKET_H
39 # include <sys/socket.h>
41 #ifdef HAVE_ARPA_INET_H
42 # include <arpa/inet.h>
61 #define NO_SHLWAPI_STREAM
62 #define NO_SHLWAPI_REG
63 #define NO_SHLWAPI_STRFCNS
64 #define NO_SHLWAPI_GDI
70 #include "wine/debug.h"
71 #include "wine/exception.h"
72 #include "wine/unicode.h"
74 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
76 static const WCHAR g_szHttp1_0
[] = {'H','T','T','P','/','1','.','0',0};
77 static const WCHAR g_szHttp1_1
[] = {'H','T','T','P','/','1','.','1',0};
78 static const WCHAR szOK
[] = {'O','K',0};
79 static const WCHAR szDefaultHeader
[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
80 static const WCHAR hostW
[] = { 'H','o','s','t',0 };
81 static const WCHAR szAuthorization
[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
82 static const WCHAR szProxy_Authorization
[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
83 static const WCHAR szStatus
[] = { 'S','t','a','t','u','s',0 };
84 static const WCHAR szKeepAlive
[] = {'K','e','e','p','-','A','l','i','v','e',0};
85 static const WCHAR szGET
[] = { 'G','E','T', 0 };
86 static const WCHAR szHEAD
[] = { 'H','E','A','D', 0 };
87 static const WCHAR szCrLf
[] = {'\r','\n', 0};
89 static const WCHAR szAccept
[] = { 'A','c','c','e','p','t',0 };
90 static const WCHAR szAccept_Charset
[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
91 static const WCHAR szAccept_Encoding
[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
92 static const WCHAR szAccept_Language
[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
93 static const WCHAR szAccept_Ranges
[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
94 static const WCHAR szAge
[] = { 'A','g','e',0 };
95 static const WCHAR szAllow
[] = { 'A','l','l','o','w',0 };
96 static const WCHAR szCache_Control
[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
97 static const WCHAR szConnection
[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
98 static const WCHAR szContent_Base
[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
99 static const WCHAR szContent_Encoding
[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
100 static const WCHAR szContent_ID
[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
101 static const WCHAR szContent_Language
[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
102 static const WCHAR szContent_Length
[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
103 static const WCHAR szContent_Location
[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
104 static const WCHAR szContent_MD5
[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
105 static const WCHAR szContent_Range
[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
106 static const WCHAR szContent_Transfer_Encoding
[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
107 static const WCHAR szContent_Type
[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
108 static const WCHAR szCookie
[] = { 'C','o','o','k','i','e',0 };
109 static const WCHAR szDate
[] = { 'D','a','t','e',0 };
110 static const WCHAR szFrom
[] = { 'F','r','o','m',0 };
111 static const WCHAR szETag
[] = { 'E','T','a','g',0 };
112 static const WCHAR szExpect
[] = { 'E','x','p','e','c','t',0 };
113 static const WCHAR szExpires
[] = { 'E','x','p','i','r','e','s',0 };
114 static const WCHAR szIf_Match
[] = { 'I','f','-','M','a','t','c','h',0 };
115 static const WCHAR szIf_Modified_Since
[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
116 static const WCHAR szIf_None_Match
[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
117 static const WCHAR szIf_Range
[] = { 'I','f','-','R','a','n','g','e',0 };
118 static const WCHAR szIf_Unmodified_Since
[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
119 static const WCHAR szLast_Modified
[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
120 static const WCHAR szLocation
[] = { 'L','o','c','a','t','i','o','n',0 };
121 static const WCHAR szMax_Forwards
[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
122 static const WCHAR szMime_Version
[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
123 static const WCHAR szPragma
[] = { 'P','r','a','g','m','a',0 };
124 static const WCHAR szProxy_Authenticate
[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
125 static const WCHAR szProxy_Connection
[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
126 static const WCHAR szPublic
[] = { 'P','u','b','l','i','c',0 };
127 static const WCHAR szRange
[] = { 'R','a','n','g','e',0 };
128 static const WCHAR szReferer
[] = { 'R','e','f','e','r','e','r',0 };
129 static const WCHAR szRetry_After
[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
130 static const WCHAR szServer
[] = { 'S','e','r','v','e','r',0 };
131 static const WCHAR szSet_Cookie
[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
132 static const WCHAR szTransfer_Encoding
[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
133 static const WCHAR szUnless_Modified_Since
[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
134 static const WCHAR szUpgrade
[] = { 'U','p','g','r','a','d','e',0 };
135 static const WCHAR szURI
[] = { 'U','R','I',0 };
136 static const WCHAR szUser_Agent
[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
137 static const WCHAR szVary
[] = { 'V','a','r','y',0 };
138 static const WCHAR szVia
[] = { 'V','i','a',0 };
139 static const WCHAR szWarning
[] = { 'W','a','r','n','i','n','g',0 };
140 static const WCHAR szWWW_Authenticate
[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
142 #define MAXHOSTNAME 100
143 #define MAX_FIELD_VALUE_LEN 256
144 #define MAX_FIELD_LEN 256
146 #define HTTP_REFERER szReferer
147 #define HTTP_ACCEPT szAccept
148 #define HTTP_USERAGENT szUser_Agent
150 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
151 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
152 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
153 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
154 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
155 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
156 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
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 DWORD
HTTP_OpenConnection(http_request_t
*req
);
211 static BOOL
HTTP_GetResponseHeaders(http_request_t
*req
, BOOL clear
);
212 static DWORD
HTTP_ProcessHeader(http_request_t
*req
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
);
213 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
);
214 static DWORD
HTTP_InsertCustomHeader(http_request_t
*req
, LPHTTPHEADERW lpHdr
);
215 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*req
, LPCWSTR lpszField
, INT index
, BOOL Request
);
216 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*req
, DWORD index
);
217 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
);
218 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*, DWORD
, LPVOID
, LPDWORD
, LPDWORD
);
219 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*req
, LPCWSTR lpszUrl
);
220 static UINT
HTTP_DecodeBase64(LPCWSTR base64
, LPSTR bin
);
221 static BOOL
HTTP_VerifyValidHeader(http_request_t
*req
, LPCWSTR field
);
222 static void HTTP_DrainContent(http_request_t
*req
);
223 static BOOL
HTTP_FinishedReading(http_request_t
*req
);
225 static LPHTTPHEADERW
HTTP_GetHeader(http_request_t
*req
, LPCWSTR head
)
228 HeaderIndex
= HTTP_GetCustomHeaderIndex(req
, head
, 0, TRUE
);
229 if (HeaderIndex
== -1)
232 return &req
->custHeaders
[HeaderIndex
];
241 struct data_stream_vtbl_t
{
242 DWORD (*get_avail_data
)(data_stream_t
*,http_request_t
*);
243 BOOL (*end_of_data
)(data_stream_t
*,http_request_t
*);
244 DWORD (*read
)(data_stream_t
*,http_request_t
*,BYTE
*,DWORD
,DWORD
*,read_mode_t
);
245 void (*destroy
)(data_stream_t
*);
249 data_stream_t data_stream
;
251 BYTE buf
[READ_BUFFER_SIZE
];
257 static void inline destroy_data_stream(data_stream_t
*stream
)
259 stream
->vtbl
->destroy(stream
);
262 static void reset_data_stream(http_request_t
*req
)
264 destroy_data_stream(req
->data_stream
);
265 req
->data_stream
= &req
->netconn_stream
.data_stream
;
266 req
->read_pos
= req
->read_size
= req
->netconn_stream
.content_read
= 0;
267 req
->read_chunked
= req
->read_gzip
= FALSE
;
273 data_stream_t stream
;
274 data_stream_t
*parent_stream
;
276 BYTE buf
[READ_BUFFER_SIZE
];
282 static DWORD
gzip_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
284 /* Allow reading only from read buffer */
288 static BOOL
gzip_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
290 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
291 return gzip_stream
->end_of_data
;
294 static DWORD
gzip_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
295 DWORD
*read
, read_mode_t read_mode
)
297 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
298 z_stream
*zstream
= &gzip_stream
->zstream
;
299 DWORD current_read
, ret_read
= 0;
302 DWORD res
= ERROR_SUCCESS
;
304 while(size
&& !gzip_stream
->end_of_data
) {
305 end
= gzip_stream
->parent_stream
->vtbl
->end_of_data(gzip_stream
->parent_stream
, req
);
307 if(gzip_stream
->buf_size
<= 64 && !end
) {
308 if(gzip_stream
->buf_pos
) {
309 if(gzip_stream
->buf_size
)
310 memmove(gzip_stream
->buf
, gzip_stream
->buf
+gzip_stream
->buf_pos
, gzip_stream
->buf_size
);
311 gzip_stream
->buf_pos
= 0;
313 res
= gzip_stream
->parent_stream
->vtbl
->read(gzip_stream
->parent_stream
, req
, gzip_stream
->buf
+gzip_stream
->buf_size
,
314 sizeof(gzip_stream
->buf
)-gzip_stream
->buf_size
, ¤t_read
, read_mode
);
315 gzip_stream
->buf_size
+= current_read
;
316 if(res
!= ERROR_SUCCESS
)
318 end
= gzip_stream
->parent_stream
->vtbl
->end_of_data(gzip_stream
->parent_stream
, req
);
319 if(!current_read
&& !end
) {
320 if(read_mode
!= READMODE_NOBLOCK
) {
321 WARN("unexpected end of data\n");
322 gzip_stream
->end_of_data
= TRUE
;
326 if(gzip_stream
->buf_size
<= 64 && !end
)
330 zstream
->next_in
= gzip_stream
->buf
+gzip_stream
->buf_pos
;
331 zstream
->avail_in
= gzip_stream
->buf_size
-(end
? 0 : 64);
332 zstream
->next_out
= buf
+ret_read
;
333 zstream
->avail_out
= size
;
334 zres
= inflate(&gzip_stream
->zstream
, 0);
335 current_read
= size
- zstream
->avail_out
;
336 size
-= current_read
;
337 ret_read
+= current_read
;
338 gzip_stream
->buf_size
-= zstream
->next_in
- (gzip_stream
->buf
+gzip_stream
->buf_pos
);
339 gzip_stream
->buf_pos
= zstream
->next_in
-gzip_stream
->buf
;
340 if(zres
== Z_STREAM_END
) {
341 TRACE("end of data\n");
342 gzip_stream
->end_of_data
= TRUE
;
344 }else if(zres
!= Z_OK
) {
345 WARN("inflate failed %d: %s\n", zres
, debugstr_a(zstream
->msg
));
347 res
= ERROR_INTERNET_DECODING_FAILED
;
351 if(ret_read
&& read_mode
== READMODE_ASYNC
)
352 read_mode
= READMODE_NOBLOCK
;
355 TRACE("read %u bytes\n", ret_read
);
360 static void gzip_destroy(data_stream_t
*stream
)
362 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
364 destroy_data_stream(gzip_stream
->parent_stream
);
366 if(!gzip_stream
->end_of_data
)
367 inflateEnd(&gzip_stream
->zstream
);
368 heap_free(gzip_stream
);
371 static const data_stream_vtbl_t gzip_stream_vtbl
= {
378 static voidpf
wininet_zalloc(voidpf opaque
, uInt items
, uInt size
)
380 return HeapAlloc(GetProcessHeap(), 0, items
*size
);
383 static void wininet_zfree(voidpf opaque
, voidpf address
)
385 HeapFree(GetProcessHeap(), 0, address
);
388 static DWORD
init_gzip_stream(http_request_t
*req
)
390 gzip_stream_t
*gzip_stream
;
393 gzip_stream
= heap_alloc_zero(sizeof(gzip_stream_t
));
395 return ERROR_OUTOFMEMORY
;
397 gzip_stream
->stream
.vtbl
= &gzip_stream_vtbl
;
398 gzip_stream
->zstream
.zalloc
= wininet_zalloc
;
399 gzip_stream
->zstream
.zfree
= wininet_zfree
;
401 zres
= inflateInit2(&gzip_stream
->zstream
, 0x1f);
403 ERR("inflateInit failed: %d\n", zres
);
404 HeapFree(GetProcessHeap(), 0, gzip_stream
);
405 return ERROR_OUTOFMEMORY
;
408 index
= HTTP_GetCustomHeaderIndex(req
, szContent_Length
, 0, FALSE
);
410 HTTP_DeleteCustomHeader(req
, index
);
413 memcpy(gzip_stream
->buf
, req
->read_buf
+req
->read_pos
, req
->read_size
);
414 gzip_stream
->buf_size
= req
->read_size
;
415 req
->read_pos
= req
->read_size
= 0;
418 req
->read_gzip
= TRUE
;
419 gzip_stream
->parent_stream
= req
->data_stream
;
420 req
->data_stream
= &gzip_stream
->stream
;
421 return ERROR_SUCCESS
;
426 static DWORD
init_gzip_stream(http_request_t
*req
)
428 ERR("gzip stream not supported, missing zlib.\n");
429 return ERROR_SUCCESS
;
434 /***********************************************************************
435 * HTTP_Tokenize (internal)
437 * Tokenize a string, allocating memory for the tokens.
439 static LPWSTR
* HTTP_Tokenize(LPCWSTR string
, LPCWSTR token_string
)
441 LPWSTR
* token_array
;
448 /* empty string has no tokens */
452 for (i
= 0; string
[i
]; i
++)
454 if (!strncmpW(string
+i
, token_string
, strlenW(token_string
)))
458 /* we want to skip over separators, but not the null terminator */
459 for (j
= 0; j
< strlenW(token_string
) - 1; j
++)
467 /* add 1 for terminating NULL */
468 token_array
= HeapAlloc(GetProcessHeap(), 0, (tokens
+1) * sizeof(*token_array
));
469 token_array
[tokens
] = NULL
;
472 for (i
= 0; i
< tokens
; i
++)
475 next_token
= strstrW(string
, token_string
);
476 if (!next_token
) next_token
= string
+strlenW(string
);
477 len
= next_token
- string
;
478 token_array
[i
] = HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
479 memcpy(token_array
[i
], string
, len
*sizeof(WCHAR
));
480 token_array
[i
][len
] = '\0';
481 string
= next_token
+strlenW(token_string
);
486 /***********************************************************************
487 * HTTP_FreeTokens (internal)
489 * Frees memory returned from HTTP_Tokenize.
491 static void HTTP_FreeTokens(LPWSTR
* token_array
)
494 for (i
= 0; token_array
[i
]; i
++)
495 HeapFree(GetProcessHeap(), 0, token_array
[i
]);
496 HeapFree(GetProcessHeap(), 0, token_array
);
499 static void HTTP_FixURL(http_request_t
*request
)
501 static const WCHAR szSlash
[] = { '/',0 };
502 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/', 0 };
504 /* If we don't have a path we set it to root */
505 if (NULL
== request
->path
)
506 request
->path
= heap_strdupW(szSlash
);
507 else /* remove \r and \n*/
509 int nLen
= strlenW(request
->path
);
510 while ((nLen
>0 ) && ((request
->path
[nLen
-1] == '\r')||(request
->path
[nLen
-1] == '\n')))
513 request
->path
[nLen
]='\0';
515 /* Replace '\' with '/' */
518 if (request
->path
[nLen
] == '\\') request
->path
[nLen
]='/';
522 if(CSTR_EQUAL
!= CompareStringW( LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
523 request
->path
, strlenW(request
->path
), szHttp
, strlenW(szHttp
) )
524 && request
->path
[0] != '/') /* not an absolute path ?? --> fix it !! */
526 WCHAR
*fixurl
= HeapAlloc(GetProcessHeap(), 0,
527 (strlenW(request
->path
) + 2)*sizeof(WCHAR
));
529 strcpyW(fixurl
+ 1, request
->path
);
530 HeapFree( GetProcessHeap(), 0, request
->path
);
531 request
->path
= fixurl
;
535 static LPWSTR
HTTP_BuildHeaderRequestString( http_request_t
*request
, LPCWSTR verb
, LPCWSTR path
, LPCWSTR version
)
537 LPWSTR requestString
;
543 static const WCHAR szSpace
[] = { ' ',0 };
544 static const WCHAR szColon
[] = { ':',' ',0 };
545 static const WCHAR sztwocrlf
[] = {'\r','\n','\r','\n', 0};
547 /* allocate space for an array of all the string pointers to be added */
548 len
= (request
->nCustHeaders
)*4 + 10;
549 req
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(LPCWSTR
) );
551 /* add the verb, path and HTTP version string */
559 /* Append custom request headers */
560 for (i
= 0; i
< request
->nCustHeaders
; i
++)
562 if (request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
565 req
[n
++] = request
->custHeaders
[i
].lpszField
;
567 req
[n
++] = request
->custHeaders
[i
].lpszValue
;
569 TRACE("Adding custom header %s (%s)\n",
570 debugstr_w(request
->custHeaders
[i
].lpszField
),
571 debugstr_w(request
->custHeaders
[i
].lpszValue
));
576 ERR("oops. buffer overrun\n");
579 requestString
= HTTP_build_req( req
, 4 );
580 HeapFree( GetProcessHeap(), 0, req
);
583 * Set (header) termination string for request
584 * Make sure there's exactly two new lines at the end of the request
586 p
= &requestString
[strlenW(requestString
)-1];
587 while ( (*p
== '\n') || (*p
== '\r') )
589 strcpyW( p
+1, sztwocrlf
);
591 return requestString
;
594 static void HTTP_ProcessCookies( http_request_t
*request
)
598 LPHTTPHEADERW setCookieHeader
;
600 while((HeaderIndex
= HTTP_GetCustomHeaderIndex(request
, szSet_Cookie
, numCookies
, FALSE
)) != -1)
602 setCookieHeader
= &request
->custHeaders
[HeaderIndex
];
604 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
) && setCookieHeader
->lpszValue
)
607 static const WCHAR szFmt
[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
611 Host
= HTTP_GetHeader(request
, hostW
);
612 len
= lstrlenW(Host
->lpszValue
) + 9 + lstrlenW(request
->path
);
613 buf_url
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
614 sprintfW(buf_url
, szFmt
, Host
->lpszValue
, request
->path
);
615 InternetSetCookieW(buf_url
, NULL
, setCookieHeader
->lpszValue
);
617 HeapFree(GetProcessHeap(), 0, buf_url
);
623 static void strip_spaces(LPWSTR start
)
628 while (*str
== ' ' && *str
!= '\0')
632 memmove(start
, str
, sizeof(WCHAR
) * (strlenW(str
) + 1));
634 end
= start
+ strlenW(start
) - 1;
635 while (end
>= start
&& *end
== ' ')
642 static inline BOOL
is_basic_auth_value( LPCWSTR pszAuthValue
, LPWSTR
*pszRealm
)
644 static const WCHAR szBasic
[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
645 static const WCHAR szRealm
[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
647 is_basic
= !strncmpiW(pszAuthValue
, szBasic
, ARRAYSIZE(szBasic
)) &&
648 ((pszAuthValue
[ARRAYSIZE(szBasic
)] == ' ') || !pszAuthValue
[ARRAYSIZE(szBasic
)]);
649 if (is_basic
&& pszRealm
)
652 LPCWSTR ptr
= &pszAuthValue
[ARRAYSIZE(szBasic
)];
656 token
= strchrW(ptr
,'=');
660 while (*realm
== ' ' && *realm
!= '\0')
662 if(!strncmpiW(realm
, szRealm
, ARRAYSIZE(szRealm
)) &&
663 (realm
[ARRAYSIZE(szRealm
)] == ' ' || realm
[ARRAYSIZE(szRealm
)] == '='))
666 while (*token
== ' ' && *token
!= '\0')
670 *pszRealm
= heap_strdupW(token
);
671 strip_spaces(*pszRealm
);
678 static void destroy_authinfo( struct HttpAuthInfo
*authinfo
)
680 if (!authinfo
) return;
682 if (SecIsValidHandle(&authinfo
->ctx
))
683 DeleteSecurityContext(&authinfo
->ctx
);
684 if (SecIsValidHandle(&authinfo
->cred
))
685 FreeCredentialsHandle(&authinfo
->cred
);
687 HeapFree(GetProcessHeap(), 0, authinfo
->auth_data
);
688 HeapFree(GetProcessHeap(), 0, authinfo
->scheme
);
689 HeapFree(GetProcessHeap(), 0, authinfo
);
692 static UINT
retrieve_cached_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR
*auth_data
)
694 basicAuthorizationData
*ad
;
697 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host
),debugstr_w(realm
));
699 EnterCriticalSection(&authcache_cs
);
700 LIST_FOR_EACH_ENTRY(ad
, &basicAuthorizationCache
, basicAuthorizationData
, entry
)
702 if (!strcmpiW(host
,ad
->host
) && !strcmpW(realm
,ad
->realm
))
704 TRACE("Authorization found in cache\n");
705 *auth_data
= HeapAlloc(GetProcessHeap(),0,ad
->authorizationLen
);
706 memcpy(*auth_data
,ad
->authorization
,ad
->authorizationLen
);
707 rc
= ad
->authorizationLen
;
711 LeaveCriticalSection(&authcache_cs
);
715 static void cache_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR auth_data
, UINT auth_data_len
)
718 basicAuthorizationData
* ad
= NULL
;
720 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host
),debugstr_w(realm
),debugstr_an(auth_data
,auth_data_len
));
722 EnterCriticalSection(&authcache_cs
);
723 LIST_FOR_EACH(cursor
, &basicAuthorizationCache
)
725 basicAuthorizationData
*check
= LIST_ENTRY(cursor
,basicAuthorizationData
,entry
);
726 if (!strcmpiW(host
,check
->host
) && !strcmpW(realm
,check
->realm
))
735 TRACE("Found match in cache, replacing\n");
736 HeapFree(GetProcessHeap(),0,ad
->authorization
);
737 ad
->authorization
= HeapAlloc(GetProcessHeap(),0,auth_data_len
);
738 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
739 ad
->authorizationLen
= auth_data_len
;
743 ad
= HeapAlloc(GetProcessHeap(),0,sizeof(basicAuthorizationData
));
744 ad
->host
= heap_strdupW(host
);
745 ad
->host
= heap_strdupW(realm
);
746 ad
->authorization
= HeapAlloc(GetProcessHeap(),0,auth_data_len
);
747 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
748 ad
->authorizationLen
= auth_data_len
;
749 list_add_head(&basicAuthorizationCache
,&ad
->entry
);
750 TRACE("authorization cached\n");
752 LeaveCriticalSection(&authcache_cs
);
755 static BOOL
retrieve_cached_authorization(LPWSTR host
, LPWSTR scheme
,
756 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
758 authorizationData
*ad
;
760 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
762 EnterCriticalSection(&authcache_cs
);
763 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
) {
764 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
765 TRACE("Authorization found in cache\n");
767 nt_auth_identity
->User
= heap_strdupW(ad
->user
);
768 nt_auth_identity
->Password
= heap_strdupW(ad
->password
);
769 nt_auth_identity
->Domain
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*ad
->domain_len
);
770 if(!nt_auth_identity
->User
|| !nt_auth_identity
->Password
||
771 (!nt_auth_identity
->Domain
&& ad
->domain_len
)) {
772 HeapFree(GetProcessHeap(), 0, nt_auth_identity
->User
);
773 HeapFree(GetProcessHeap(), 0, nt_auth_identity
->Password
);
774 HeapFree(GetProcessHeap(), 0, nt_auth_identity
->Domain
);
778 nt_auth_identity
->Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
779 nt_auth_identity
->UserLength
= ad
->user_len
;
780 nt_auth_identity
->PasswordLength
= ad
->password_len
;
781 memcpy(nt_auth_identity
->Domain
, ad
->domain
, sizeof(WCHAR
)*ad
->domain_len
);
782 nt_auth_identity
->DomainLength
= ad
->domain_len
;
783 LeaveCriticalSection(&authcache_cs
);
787 LeaveCriticalSection(&authcache_cs
);
792 static void cache_authorization(LPWSTR host
, LPWSTR scheme
,
793 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
795 authorizationData
*ad
;
798 TRACE("Caching authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
800 EnterCriticalSection(&authcache_cs
);
801 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
)
802 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
808 HeapFree(GetProcessHeap(), 0, ad
->user
);
809 HeapFree(GetProcessHeap(), 0, ad
->password
);
810 HeapFree(GetProcessHeap(), 0, ad
->domain
);
812 ad
= HeapAlloc(GetProcessHeap(), 0, sizeof(authorizationData
));
814 LeaveCriticalSection(&authcache_cs
);
818 ad
->host
= heap_strdupW(host
);
819 ad
->scheme
= heap_strdupW(scheme
);
820 list_add_head(&authorizationCache
, &ad
->entry
);
823 ad
->user
= heap_strndupW(nt_auth_identity
->User
, nt_auth_identity
->UserLength
);
824 ad
->password
= heap_strndupW(nt_auth_identity
->Password
, nt_auth_identity
->PasswordLength
);
825 ad
->domain
= heap_strndupW(nt_auth_identity
->Domain
, nt_auth_identity
->DomainLength
);
826 ad
->user_len
= nt_auth_identity
->UserLength
;
827 ad
->password_len
= nt_auth_identity
->PasswordLength
;
828 ad
->domain_len
= nt_auth_identity
->DomainLength
;
830 if(!ad
->host
|| !ad
->scheme
|| !ad
->user
|| !ad
->password
831 || (nt_auth_identity
->Domain
&& !ad
->domain
)) {
832 HeapFree(GetProcessHeap(), 0, ad
->host
);
833 HeapFree(GetProcessHeap(), 0, ad
->scheme
);
834 HeapFree(GetProcessHeap(), 0, ad
->user
);
835 HeapFree(GetProcessHeap(), 0, ad
->password
);
836 HeapFree(GetProcessHeap(), 0, ad
->domain
);
837 list_remove(&ad
->entry
);
838 HeapFree(GetProcessHeap(), 0, ad
);
841 LeaveCriticalSection(&authcache_cs
);
844 static BOOL
HTTP_DoAuthorization( http_request_t
*request
, LPCWSTR pszAuthValue
,
845 struct HttpAuthInfo
**ppAuthInfo
,
846 LPWSTR domain_and_username
, LPWSTR password
,
849 SECURITY_STATUS sec_status
;
850 struct HttpAuthInfo
*pAuthInfo
= *ppAuthInfo
;
852 LPWSTR szRealm
= NULL
;
854 TRACE("%s\n", debugstr_w(pszAuthValue
));
861 pAuthInfo
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo
));
865 SecInvalidateHandle(&pAuthInfo
->cred
);
866 SecInvalidateHandle(&pAuthInfo
->ctx
);
867 memset(&pAuthInfo
->exp
, 0, sizeof(pAuthInfo
->exp
));
869 pAuthInfo
->auth_data
= NULL
;
870 pAuthInfo
->auth_data_len
= 0;
871 pAuthInfo
->finished
= FALSE
;
873 if (is_basic_auth_value(pszAuthValue
,NULL
))
875 static const WCHAR szBasic
[] = {'B','a','s','i','c',0};
876 pAuthInfo
->scheme
= heap_strdupW(szBasic
);
877 if (!pAuthInfo
->scheme
)
879 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
886 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity
;
888 pAuthInfo
->scheme
= heap_strdupW(pszAuthValue
);
889 if (!pAuthInfo
->scheme
)
891 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
895 if (domain_and_username
)
897 WCHAR
*user
= strchrW(domain_and_username
, '\\');
898 WCHAR
*domain
= domain_and_username
;
900 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
902 pAuthData
= &nt_auth_identity
;
907 user
= domain_and_username
;
911 nt_auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
912 nt_auth_identity
.User
= user
;
913 nt_auth_identity
.UserLength
= strlenW(nt_auth_identity
.User
);
914 nt_auth_identity
.Domain
= domain
;
915 nt_auth_identity
.DomainLength
= domain
? user
- domain
- 1 : 0;
916 nt_auth_identity
.Password
= password
;
917 nt_auth_identity
.PasswordLength
= strlenW(nt_auth_identity
.Password
);
919 cache_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
);
921 else if(retrieve_cached_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
))
922 pAuthData
= &nt_auth_identity
;
924 /* use default credentials */
927 sec_status
= AcquireCredentialsHandleW(NULL
, pAuthInfo
->scheme
,
928 SECPKG_CRED_OUTBOUND
, NULL
,
930 NULL
, &pAuthInfo
->cred
,
933 if(pAuthData
&& !domain_and_username
) {
934 HeapFree(GetProcessHeap(), 0, nt_auth_identity
.User
);
935 HeapFree(GetProcessHeap(), 0, nt_auth_identity
.Domain
);
936 HeapFree(GetProcessHeap(), 0, nt_auth_identity
.Password
);
939 if (sec_status
== SEC_E_OK
)
941 PSecPkgInfoW sec_pkg_info
;
942 sec_status
= QuerySecurityPackageInfoW(pAuthInfo
->scheme
, &sec_pkg_info
);
943 if (sec_status
== SEC_E_OK
)
945 pAuthInfo
->max_token
= sec_pkg_info
->cbMaxToken
;
946 FreeContextBuffer(sec_pkg_info
);
949 if (sec_status
!= SEC_E_OK
)
951 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
952 debugstr_w(pAuthInfo
->scheme
), sec_status
);
953 HeapFree(GetProcessHeap(), 0, pAuthInfo
->scheme
);
954 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
958 *ppAuthInfo
= pAuthInfo
;
960 else if (pAuthInfo
->finished
)
963 if ((strlenW(pszAuthValue
) < strlenW(pAuthInfo
->scheme
)) ||
964 strncmpiW(pszAuthValue
, pAuthInfo
->scheme
, strlenW(pAuthInfo
->scheme
)))
966 ERR("authentication scheme changed from %s to %s\n",
967 debugstr_w(pAuthInfo
->scheme
), debugstr_w(pszAuthValue
));
971 if (is_basic_auth_value(pszAuthValue
,&szRealm
))
975 char *auth_data
= NULL
;
976 UINT auth_data_len
= 0;
978 TRACE("basic authentication realm %s\n",debugstr_w(szRealm
));
980 if (!domain_and_username
)
983 auth_data_len
= retrieve_cached_basic_authorization(host
, szRealm
,&auth_data
);
984 if (auth_data_len
== 0)
986 HeapFree(GetProcessHeap(),0,szRealm
);
992 userlen
= WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, lstrlenW(domain_and_username
), NULL
, 0, NULL
, NULL
);
993 passlen
= WideCharToMultiByte(CP_UTF8
, 0, password
, lstrlenW(password
), NULL
, 0, NULL
, NULL
);
995 /* length includes a nul terminator, which will be re-used for the ':' */
996 auth_data
= HeapAlloc(GetProcessHeap(), 0, userlen
+ 1 + passlen
);
999 HeapFree(GetProcessHeap(),0,szRealm
);
1003 WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, -1, auth_data
, userlen
, NULL
, NULL
);
1004 auth_data
[userlen
] = ':';
1005 WideCharToMultiByte(CP_UTF8
, 0, password
, -1, &auth_data
[userlen
+1], passlen
, NULL
, NULL
);
1006 auth_data_len
= userlen
+ 1 + passlen
;
1007 if (host
&& szRealm
)
1008 cache_basic_authorization(host
, szRealm
, auth_data
, auth_data_len
);
1011 pAuthInfo
->auth_data
= auth_data
;
1012 pAuthInfo
->auth_data_len
= auth_data_len
;
1013 pAuthInfo
->finished
= TRUE
;
1014 HeapFree(GetProcessHeap(),0,szRealm
);
1020 LPCWSTR pszAuthData
;
1021 SecBufferDesc out_desc
, in_desc
;
1023 unsigned char *buffer
;
1024 ULONG context_req
= ISC_REQ_CONNECTION
| ISC_REQ_USE_DCE_STYLE
|
1025 ISC_REQ_MUTUAL_AUTH
| ISC_REQ_DELEGATE
;
1027 in
.BufferType
= SECBUFFER_TOKEN
;
1031 in_desc
.ulVersion
= 0;
1032 in_desc
.cBuffers
= 1;
1033 in_desc
.pBuffers
= &in
;
1035 pszAuthData
= pszAuthValue
+ strlenW(pAuthInfo
->scheme
);
1036 if (*pszAuthData
== ' ')
1039 in
.cbBuffer
= HTTP_DecodeBase64(pszAuthData
, NULL
);
1040 in
.pvBuffer
= HeapAlloc(GetProcessHeap(), 0, in
.cbBuffer
);
1041 HTTP_DecodeBase64(pszAuthData
, in
.pvBuffer
);
1044 buffer
= HeapAlloc(GetProcessHeap(), 0, pAuthInfo
->max_token
);
1046 out
.BufferType
= SECBUFFER_TOKEN
;
1047 out
.cbBuffer
= pAuthInfo
->max_token
;
1048 out
.pvBuffer
= buffer
;
1050 out_desc
.ulVersion
= 0;
1051 out_desc
.cBuffers
= 1;
1052 out_desc
.pBuffers
= &out
;
1054 sec_status
= InitializeSecurityContextW(first
? &pAuthInfo
->cred
: NULL
,
1055 first
? NULL
: &pAuthInfo
->ctx
,
1056 first
? request
->session
->serverName
: NULL
,
1057 context_req
, 0, SECURITY_NETWORK_DREP
,
1058 in
.pvBuffer
? &in_desc
: NULL
,
1059 0, &pAuthInfo
->ctx
, &out_desc
,
1060 &pAuthInfo
->attr
, &pAuthInfo
->exp
);
1061 if (sec_status
== SEC_E_OK
)
1063 pAuthInfo
->finished
= TRUE
;
1064 pAuthInfo
->auth_data
= out
.pvBuffer
;
1065 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
1066 TRACE("sending last auth packet\n");
1068 else if (sec_status
== SEC_I_CONTINUE_NEEDED
)
1070 pAuthInfo
->auth_data
= out
.pvBuffer
;
1071 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
1072 TRACE("sending next auth packet\n");
1076 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status
);
1077 HeapFree(GetProcessHeap(), 0, out
.pvBuffer
);
1078 destroy_authinfo(pAuthInfo
);
1087 /***********************************************************************
1088 * HTTP_HttpAddRequestHeadersW (internal)
1090 static DWORD
HTTP_HttpAddRequestHeadersW(http_request_t
*request
,
1091 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1096 DWORD len
, res
= ERROR_HTTP_INVALID_HEADER
;
1098 TRACE("copying header: %s\n", debugstr_wn(lpszHeader
, dwHeaderLength
));
1100 if( dwHeaderLength
== ~0U )
1101 len
= strlenW(lpszHeader
);
1103 len
= dwHeaderLength
;
1104 buffer
= HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR
)*(len
+1) );
1105 lstrcpynW( buffer
, lpszHeader
, len
+ 1);
1111 LPWSTR
* pFieldAndValue
;
1113 lpszEnd
= lpszStart
;
1115 while (*lpszEnd
!= '\0')
1117 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1122 if (*lpszStart
== '\0')
1125 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1128 lpszEnd
++; /* Jump over newline */
1130 TRACE("interpreting header %s\n", debugstr_w(lpszStart
));
1131 if (*lpszStart
== '\0')
1133 /* Skip 0-length headers */
1134 lpszStart
= lpszEnd
;
1135 res
= ERROR_SUCCESS
;
1138 pFieldAndValue
= HTTP_InterpretHttpHeader(lpszStart
);
1141 res
= HTTP_VerifyValidHeader(request
, pFieldAndValue
[0]);
1142 if (res
== ERROR_SUCCESS
)
1143 res
= HTTP_ProcessHeader(request
, pFieldAndValue
[0],
1144 pFieldAndValue
[1], dwModifier
| HTTP_ADDHDR_FLAG_REQ
);
1145 HTTP_FreeTokens(pFieldAndValue
);
1148 lpszStart
= lpszEnd
;
1149 } while (res
== ERROR_SUCCESS
);
1151 HeapFree(GetProcessHeap(), 0, buffer
);
1156 /***********************************************************************
1157 * HttpAddRequestHeadersW (WININET.@)
1159 * Adds one or more HTTP header to the request handler
1162 * On Windows if dwHeaderLength includes the trailing '\0', then
1163 * HttpAddRequestHeadersW() adds it too. However this results in an
1164 * invalid Http header which is rejected by some servers so we probably
1165 * don't need to match Windows on that point.
1172 BOOL WINAPI
HttpAddRequestHeadersW(HINTERNET hHttpRequest
,
1173 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1175 http_request_t
*request
;
1176 DWORD res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
1178 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_wn(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1183 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
1184 if (request
&& request
->hdr
.htype
== WH_HHTTPREQ
)
1185 res
= HTTP_HttpAddRequestHeadersW( request
, lpszHeader
, dwHeaderLength
, dwModifier
);
1187 WININET_Release( &request
->hdr
);
1189 if(res
!= ERROR_SUCCESS
)
1191 return res
== ERROR_SUCCESS
;
1194 /***********************************************************************
1195 * HttpAddRequestHeadersA (WININET.@)
1197 * Adds one or more HTTP header to the request handler
1204 BOOL WINAPI
HttpAddRequestHeadersA(HINTERNET hHttpRequest
,
1205 LPCSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1211 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_an(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1213 len
= MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, NULL
, 0 );
1214 hdr
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
1215 MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, hdr
, len
);
1216 if( dwHeaderLength
!= ~0U )
1217 dwHeaderLength
= len
;
1219 r
= HttpAddRequestHeadersW( hHttpRequest
, hdr
, dwHeaderLength
, dwModifier
);
1221 HeapFree( GetProcessHeap(), 0, hdr
);
1226 /***********************************************************************
1227 * HttpOpenRequestA (WININET.@)
1229 * Open a HTTP request handle
1232 * HINTERNET a HTTP request handle on success
1236 HINTERNET WINAPI
HttpOpenRequestA(HINTERNET hHttpSession
,
1237 LPCSTR lpszVerb
, LPCSTR lpszObjectName
, LPCSTR lpszVersion
,
1238 LPCSTR lpszReferrer
, LPCSTR
*lpszAcceptTypes
,
1239 DWORD dwFlags
, DWORD_PTR dwContext
)
1241 LPWSTR szVerb
= NULL
, szObjectName
= NULL
;
1242 LPWSTR szVersion
= NULL
, szReferrer
= NULL
, *szAcceptTypes
= NULL
;
1243 INT acceptTypesCount
;
1244 HINTERNET rc
= FALSE
;
1247 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1248 debugstr_a(lpszVerb
), debugstr_a(lpszObjectName
),
1249 debugstr_a(lpszVersion
), debugstr_a(lpszReferrer
), lpszAcceptTypes
,
1250 dwFlags
, dwContext
);
1254 szVerb
= heap_strdupAtoW(lpszVerb
);
1261 szObjectName
= heap_strdupAtoW(lpszObjectName
);
1262 if ( !szObjectName
)
1268 szVersion
= heap_strdupAtoW(lpszVersion
);
1275 szReferrer
= heap_strdupAtoW(lpszReferrer
);
1280 if (lpszAcceptTypes
)
1282 acceptTypesCount
= 0;
1283 types
= lpszAcceptTypes
;
1288 /* find out how many there are */
1289 if (*types
&& **types
)
1291 TRACE("accept type: %s\n", debugstr_a(*types
));
1297 WARN("invalid accept type pointer\n");
1302 szAcceptTypes
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*) * (acceptTypesCount
+1));
1303 if (!szAcceptTypes
) goto end
;
1305 acceptTypesCount
= 0;
1306 types
= lpszAcceptTypes
;
1311 if (*types
&& **types
)
1312 szAcceptTypes
[acceptTypesCount
++] = heap_strdupAtoW(*types
);
1316 /* ignore invalid pointer */
1321 szAcceptTypes
[acceptTypesCount
] = NULL
;
1324 rc
= HttpOpenRequestW(hHttpSession
, szVerb
, szObjectName
,
1325 szVersion
, szReferrer
,
1326 (LPCWSTR
*)szAcceptTypes
, dwFlags
, dwContext
);
1331 acceptTypesCount
= 0;
1332 while (szAcceptTypes
[acceptTypesCount
])
1334 HeapFree(GetProcessHeap(), 0, szAcceptTypes
[acceptTypesCount
]);
1337 HeapFree(GetProcessHeap(), 0, szAcceptTypes
);
1339 HeapFree(GetProcessHeap(), 0, szReferrer
);
1340 HeapFree(GetProcessHeap(), 0, szVersion
);
1341 HeapFree(GetProcessHeap(), 0, szObjectName
);
1342 HeapFree(GetProcessHeap(), 0, szVerb
);
1347 /***********************************************************************
1350 static UINT
HTTP_EncodeBase64( LPCSTR bin
, unsigned int len
, LPWSTR base64
)
1353 static const CHAR HTTP_Base64Enc
[] =
1354 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1358 /* first 6 bits, all from bin[0] */
1359 base64
[n
++] = HTTP_Base64Enc
[(bin
[0] & 0xfc) >> 2];
1360 x
= (bin
[0] & 3) << 4;
1362 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1365 base64
[n
++] = HTTP_Base64Enc
[x
];
1370 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[1]&0xf0) >> 4 ) ];
1371 x
= ( bin
[1] & 0x0f ) << 2;
1373 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1376 base64
[n
++] = HTTP_Base64Enc
[x
];
1380 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[2]&0xc0 ) >> 6 ) ];
1382 /* last 6 bits, all from bin [2] */
1383 base64
[n
++] = HTTP_Base64Enc
[ bin
[2] & 0x3f ];
1391 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1392 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1393 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1394 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1395 static const signed char HTTP_Base64Dec
[256] =
1397 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1398 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1399 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1400 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1401 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1402 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1403 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1404 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1405 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1406 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1407 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1408 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1409 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1410 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1411 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1412 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1413 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1414 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1415 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1416 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1417 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1418 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1419 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1420 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1421 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1422 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1426 /***********************************************************************
1429 static UINT
HTTP_DecodeBase64( LPCWSTR base64
, LPSTR bin
)
1437 if (base64
[0] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1438 ((in
[0] = HTTP_Base64Dec
[base64
[0]]) == -1) ||
1439 base64
[1] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1440 ((in
[1] = HTTP_Base64Dec
[base64
[1]]) == -1))
1442 WARN("invalid base64: %s\n", debugstr_w(base64
));
1446 bin
[n
] = (unsigned char) (in
[0] << 2 | in
[1] >> 4);
1449 if ((base64
[2] == '=') && (base64
[3] == '='))
1451 if (base64
[2] > ARRAYSIZE(HTTP_Base64Dec
) ||
1452 ((in
[2] = HTTP_Base64Dec
[base64
[2]]) == -1))
1454 WARN("invalid base64: %s\n", debugstr_w(&base64
[2]));
1458 bin
[n
] = (unsigned char) (in
[1] << 4 | in
[2] >> 2);
1461 if (base64
[3] == '=')
1463 if (base64
[3] > ARRAYSIZE(HTTP_Base64Dec
) ||
1464 ((in
[3] = HTTP_Base64Dec
[base64
[3]]) == -1))
1466 WARN("invalid base64: %s\n", debugstr_w(&base64
[3]));
1470 bin
[n
] = (unsigned char) (((in
[2] << 6) & 0xc0) | in
[3]);
1479 /***********************************************************************
1480 * HTTP_InsertAuthorization
1482 * Insert or delete the authorization field in the request header.
1484 static BOOL
HTTP_InsertAuthorization( http_request_t
*request
, struct HttpAuthInfo
*pAuthInfo
, LPCWSTR header
)
1488 static const WCHAR wszSpace
[] = {' ',0};
1489 static const WCHAR wszBasic
[] = {'B','a','s','i','c',0};
1491 WCHAR
*authorization
= NULL
;
1493 if (pAuthInfo
->auth_data_len
)
1495 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1496 len
= strlenW(pAuthInfo
->scheme
)+1+((pAuthInfo
->auth_data_len
+2)*4)/3;
1497 authorization
= HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
1501 strcpyW(authorization
, pAuthInfo
->scheme
);
1502 strcatW(authorization
, wszSpace
);
1503 HTTP_EncodeBase64(pAuthInfo
->auth_data
,
1504 pAuthInfo
->auth_data_len
,
1505 authorization
+strlenW(authorization
));
1507 /* clear the data as it isn't valid now that it has been sent to the
1508 * server, unless it's Basic authentication which doesn't do
1509 * connection tracking */
1510 if (strcmpiW(pAuthInfo
->scheme
, wszBasic
))
1512 HeapFree(GetProcessHeap(), 0, pAuthInfo
->auth_data
);
1513 pAuthInfo
->auth_data
= NULL
;
1514 pAuthInfo
->auth_data_len
= 0;
1518 TRACE("Inserting authorization: %s\n", debugstr_w(authorization
));
1520 HTTP_ProcessHeader(request
, header
, authorization
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
1522 HeapFree(GetProcessHeap(), 0, authorization
);
1527 static WCHAR
*HTTP_BuildProxyRequestUrl(http_request_t
*req
)
1529 WCHAR new_location
[INTERNET_MAX_URL_LENGTH
], *url
;
1532 size
= sizeof(new_location
);
1533 if (HTTP_HttpQueryInfoW(req
, HTTP_QUERY_LOCATION
, new_location
, &size
, NULL
) == ERROR_SUCCESS
)
1535 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
+ sizeof(WCHAR
) ))) return NULL
;
1536 strcpyW( url
, new_location
);
1540 static const WCHAR slash
[] = { '/',0 };
1541 static const WCHAR format
[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1542 static const WCHAR formatSSL
[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1543 http_session_t
*session
= req
->session
;
1545 size
= 16; /* "https://" + sizeof(port#) + ":/\0" */
1546 size
+= strlenW( session
->hostName
) + strlenW( req
->path
);
1548 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
* sizeof(WCHAR
) ))) return NULL
;
1550 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1551 sprintfW( url
, formatSSL
, session
->hostName
, session
->hostPort
);
1553 sprintfW( url
, format
, session
->hostName
, session
->hostPort
);
1554 if (req
->path
[0] != '/') strcatW( url
, slash
);
1555 strcatW( url
, req
->path
);
1557 TRACE("url=%s\n", debugstr_w(url
));
1561 /***********************************************************************
1562 * HTTP_DealWithProxy
1564 static BOOL
HTTP_DealWithProxy(appinfo_t
*hIC
, http_session_t
*session
, http_request_t
*request
)
1566 WCHAR buf
[MAXHOSTNAME
];
1567 WCHAR protoProxy
[MAXHOSTNAME
+ 15];
1568 DWORD protoProxyLen
= sizeof(protoProxy
) / sizeof(protoProxy
[0]);
1569 WCHAR proxy
[MAXHOSTNAME
+ 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1570 static WCHAR szNul
[] = { 0 };
1571 URL_COMPONENTSW UrlComponents
;
1572 static const WCHAR protoHttp
[] = { 'h','t','t','p',0 };
1573 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/',0 };
1574 static const WCHAR szFormat
[] = { 'h','t','t','p',':','/','/','%','s',0 };
1576 memset( &UrlComponents
, 0, sizeof UrlComponents
);
1577 UrlComponents
.dwStructSize
= sizeof UrlComponents
;
1578 UrlComponents
.lpszHostName
= buf
;
1579 UrlComponents
.dwHostNameLength
= MAXHOSTNAME
;
1581 if (!INTERNET_FindProxyForProtocol(hIC
->proxy
, protoHttp
, protoProxy
, &protoProxyLen
))
1583 if( CSTR_EQUAL
!= CompareStringW(LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
1584 protoProxy
,strlenW(szHttp
),szHttp
,strlenW(szHttp
)) )
1585 sprintfW(proxy
, szFormat
, protoProxy
);
1587 strcpyW(proxy
, protoProxy
);
1588 if( !InternetCrackUrlW(proxy
, 0, 0, &UrlComponents
) )
1590 if( UrlComponents
.dwHostNameLength
== 0 )
1593 if( !request
->path
)
1594 request
->path
= szNul
;
1596 if(UrlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
1597 UrlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
1599 HeapFree(GetProcessHeap(), 0, session
->serverName
);
1600 session
->serverName
= heap_strdupW(UrlComponents
.lpszHostName
);
1601 session
->serverPort
= UrlComponents
.nPort
;
1603 TRACE("proxy server=%s port=%d\n", debugstr_w(session
->serverName
), session
->serverPort
);
1607 #ifndef INET6_ADDRSTRLEN
1608 #define INET6_ADDRSTRLEN 46
1611 static DWORD
HTTP_ResolveName(http_request_t
*request
)
1613 char szaddr
[INET6_ADDRSTRLEN
];
1614 http_session_t
*session
= request
->session
;
1617 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1618 INTERNET_STATUS_RESOLVING_NAME
,
1619 session
->serverName
,
1620 (strlenW(session
->serverName
)+1) * sizeof(WCHAR
));
1622 session
->sa_len
= sizeof(session
->socketAddress
);
1623 if (!GetAddress(session
->serverName
, session
->serverPort
,
1624 (struct sockaddr
*)&session
->socketAddress
, &session
->sa_len
))
1625 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1627 switch (session
->socketAddress
.ss_family
)
1630 addr
= &((struct sockaddr_in
*)&session
->socketAddress
)->sin_addr
;
1633 addr
= &((struct sockaddr_in6
*)&session
->socketAddress
)->sin6_addr
;
1636 WARN("unsupported family %d\n", session
->socketAddress
.ss_family
);
1637 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1639 inet_ntop(session
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
1640 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1641 INTERNET_STATUS_NAME_RESOLVED
,
1642 szaddr
, strlen(szaddr
)+1);
1644 TRACE("resolved %s to %s\n", debugstr_w(session
->serverName
), szaddr
);
1645 return ERROR_SUCCESS
;
1648 static BOOL
HTTP_GetRequestURL(http_request_t
*req
, LPWSTR buf
)
1650 static const WCHAR http
[] = { 'h','t','t','p',':','/','/',0 };
1651 static const WCHAR https
[] = { 'h','t','t','p','s',':','/','/',0 };
1652 static const WCHAR slash
[] = { '/',0 };
1653 LPHTTPHEADERW host_header
;
1656 host_header
= HTTP_GetHeader(req
, hostW
);
1660 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1664 strcpyW(buf
, scheme
);
1665 strcatW(buf
, host_header
->lpszValue
);
1666 if (req
->path
[0] != '/')
1667 strcatW(buf
, slash
);
1668 strcatW(buf
, req
->path
);
1673 /***********************************************************************
1674 * HTTPREQ_Destroy (internal)
1676 * Deallocate request handle
1679 static void HTTPREQ_Destroy(object_header_t
*hdr
)
1681 http_request_t
*request
= (http_request_t
*) hdr
;
1686 if(request
->hCacheFile
) {
1687 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1689 CloseHandle(request
->hCacheFile
);
1691 if(HTTP_GetRequestURL(request
, url
)) {
1694 headersLen
= request
->rawHeaders
? strlenW(request
->rawHeaders
) : 0;
1695 CommitUrlCacheEntryW(url
, request
->cacheFile
, request
->expires
,
1696 request
->last_modified
, NORMAL_CACHE_ENTRY
,
1697 request
->rawHeaders
, headersLen
, NULL
, 0);
1701 HeapFree(GetProcessHeap(), 0, request
->cacheFile
);
1703 DeleteCriticalSection( &request
->read_section
);
1704 WININET_Release(&request
->session
->hdr
);
1706 destroy_authinfo(request
->authInfo
);
1707 destroy_authinfo(request
->proxyAuthInfo
);
1709 HeapFree(GetProcessHeap(), 0, request
->path
);
1710 HeapFree(GetProcessHeap(), 0, request
->verb
);
1711 HeapFree(GetProcessHeap(), 0, request
->rawHeaders
);
1712 HeapFree(GetProcessHeap(), 0, request
->version
);
1713 HeapFree(GetProcessHeap(), 0, request
->statusText
);
1715 for (i
= 0; i
< request
->nCustHeaders
; i
++)
1717 HeapFree(GetProcessHeap(), 0, request
->custHeaders
[i
].lpszField
);
1718 HeapFree(GetProcessHeap(), 0, request
->custHeaders
[i
].lpszValue
);
1721 destroy_data_stream(request
->data_stream
);
1722 HeapFree(GetProcessHeap(), 0, request
->custHeaders
);
1725 static void HTTPREQ_CloseConnection(object_header_t
*hdr
)
1727 http_request_t
*request
= (http_request_t
*) hdr
;
1729 TRACE("%p\n",request
);
1731 if (!NETCON_connected(&request
->netConnection
))
1734 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1735 INTERNET_STATUS_CLOSING_CONNECTION
, 0, 0);
1737 NETCON_close(&request
->netConnection
);
1739 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1740 INTERNET_STATUS_CONNECTION_CLOSED
, 0, 0);
1743 static BOOL
HTTP_KeepAlive(http_request_t
*request
)
1745 WCHAR szVersion
[10];
1746 WCHAR szConnectionResponse
[20];
1747 DWORD dwBufferSize
= sizeof(szVersion
);
1748 BOOL keepalive
= FALSE
;
1750 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1751 * the connection is keep-alive by default */
1752 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_VERSION
, szVersion
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1753 && !strcmpiW(szVersion
, g_szHttp1_1
))
1758 dwBufferSize
= sizeof(szConnectionResponse
);
1759 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_PROXY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1760 || HTTP_HttpQueryInfoW(request
, HTTP_QUERY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
1762 keepalive
= !strcmpiW(szConnectionResponse
, szKeepAlive
);
1768 static DWORD
HTTPREQ_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
1770 http_request_t
*req
= (http_request_t
*)hdr
;
1773 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO
:
1775 http_session_t
*session
= req
->session
;
1776 INTERNET_DIAGNOSTIC_SOCKET_INFO
*info
= buffer
;
1778 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1780 if (*size
< sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
))
1781 return ERROR_INSUFFICIENT_BUFFER
;
1782 *size
= sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
);
1783 /* FIXME: can't get a SOCKET from our connection since we don't use
1787 /* FIXME: get source port from req->netConnection */
1788 info
->SourcePort
= 0;
1789 info
->DestPort
= session
->hostPort
;
1791 if (HTTP_KeepAlive(req
))
1792 info
->Flags
|= IDSI_FLAG_KEEP_ALIVE
;
1793 if (session
->appInfo
->proxy
&& session
->appInfo
->proxy
[0] != 0)
1794 info
->Flags
|= IDSI_FLAG_PROXY
;
1795 if (req
->netConnection
.useSSL
)
1796 info
->Flags
|= IDSI_FLAG_SECURE
;
1798 return ERROR_SUCCESS
;
1801 case INTERNET_OPTION_SECURITY_FLAGS
:
1806 if (*size
< sizeof(ULONG
))
1807 return ERROR_INSUFFICIENT_BUFFER
;
1809 *size
= sizeof(DWORD
);
1811 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1812 flags
|= SECURITY_FLAG_SECURE
;
1813 flags
|= req
->netConnection
.security_flags
;
1814 bits
= NETCON_GetCipherStrength(&req
->netConnection
);
1816 flags
|= SECURITY_FLAG_STRENGTH_STRONG
;
1817 else if (bits
>= 56)
1818 flags
|= SECURITY_FLAG_STRENGTH_MEDIUM
;
1820 flags
|= SECURITY_FLAG_STRENGTH_WEAK
;
1821 *(DWORD
*)buffer
= flags
;
1822 return ERROR_SUCCESS
;
1825 case INTERNET_OPTION_HANDLE_TYPE
:
1826 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1828 if (*size
< sizeof(ULONG
))
1829 return ERROR_INSUFFICIENT_BUFFER
;
1831 *size
= sizeof(DWORD
);
1832 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_HTTP_REQUEST
;
1833 return ERROR_SUCCESS
;
1835 case INTERNET_OPTION_URL
: {
1836 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1841 static const WCHAR httpW
[] = {'h','t','t','p',':','/','/',0};
1843 TRACE("INTERNET_OPTION_URL\n");
1845 host
= HTTP_GetHeader(req
, hostW
);
1846 strcpyW(url
, httpW
);
1847 strcatW(url
, host
->lpszValue
);
1848 if (NULL
!= (pch
= strchrW(url
+ strlenW(httpW
), ':')))
1850 strcatW(url
, req
->path
);
1852 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url
));
1855 len
= (strlenW(url
)+1) * sizeof(WCHAR
);
1857 return ERROR_INSUFFICIENT_BUFFER
;
1860 strcpyW(buffer
, url
);
1861 return ERROR_SUCCESS
;
1863 len
= WideCharToMultiByte(CP_ACP
, 0, url
, -1, buffer
, *size
, NULL
, NULL
);
1865 return ERROR_INSUFFICIENT_BUFFER
;
1868 return ERROR_SUCCESS
;
1872 case INTERNET_OPTION_CACHE_TIMESTAMPS
: {
1873 INTERNET_CACHE_ENTRY_INFOW
*info
;
1874 INTERNET_CACHE_TIMESTAMPS
*ts
= buffer
;
1875 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1876 DWORD nbytes
, error
;
1879 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1881 if (*size
< sizeof(*ts
))
1883 *size
= sizeof(*ts
);
1884 return ERROR_INSUFFICIENT_BUFFER
;
1887 HTTP_GetRequestURL(req
, url
);
1888 ret
= GetUrlCacheEntryInfoW(url
, NULL
, &nbytes
);
1889 error
= GetLastError();
1890 if (!ret
&& error
== ERROR_INSUFFICIENT_BUFFER
)
1892 if (!(info
= HeapAlloc(GetProcessHeap(), 0, nbytes
)))
1893 return ERROR_OUTOFMEMORY
;
1895 GetUrlCacheEntryInfoW(url
, info
, &nbytes
);
1897 ts
->ftExpires
= info
->ExpireTime
;
1898 ts
->ftLastModified
= info
->LastModifiedTime
;
1900 HeapFree(GetProcessHeap(), 0, info
);
1901 *size
= sizeof(*ts
);
1902 return ERROR_SUCCESS
;
1907 case INTERNET_OPTION_DATAFILE_NAME
: {
1910 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1912 if(!req
->cacheFile
) {
1914 return ERROR_INTERNET_ITEM_NOT_FOUND
;
1918 req_size
= (lstrlenW(req
->cacheFile
)+1) * sizeof(WCHAR
);
1919 if(*size
< req_size
)
1920 return ERROR_INSUFFICIENT_BUFFER
;
1923 memcpy(buffer
, req
->cacheFile
, *size
);
1924 return ERROR_SUCCESS
;
1926 req_size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
, -1, NULL
, 0, NULL
, NULL
);
1927 if (req_size
> *size
)
1928 return ERROR_INSUFFICIENT_BUFFER
;
1930 *size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
,
1931 -1, buffer
, *size
, NULL
, NULL
);
1932 return ERROR_SUCCESS
;
1936 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT
: {
1937 PCCERT_CONTEXT context
;
1939 if(*size
< sizeof(INTERNET_CERTIFICATE_INFOA
)) {
1940 *size
= sizeof(INTERNET_CERTIFICATE_INFOA
);
1941 return ERROR_INSUFFICIENT_BUFFER
;
1944 context
= (PCCERT_CONTEXT
)NETCON_GetCert(&(req
->netConnection
));
1946 INTERNET_CERTIFICATE_INFOA
*info
= (INTERNET_CERTIFICATE_INFOA
*)buffer
;
1949 memset(info
, 0, sizeof(INTERNET_CERTIFICATE_INFOW
));
1950 info
->ftExpiry
= context
->pCertInfo
->NotAfter
;
1951 info
->ftStart
= context
->pCertInfo
->NotBefore
;
1952 len
= CertNameToStrA(context
->dwCertEncodingType
,
1953 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1954 info
->lpszSubjectInfo
= LocalAlloc(0, len
);
1955 if(info
->lpszSubjectInfo
)
1956 CertNameToStrA(context
->dwCertEncodingType
,
1957 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
,
1958 info
->lpszSubjectInfo
, len
);
1959 len
= CertNameToStrA(context
->dwCertEncodingType
,
1960 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1961 info
->lpszIssuerInfo
= LocalAlloc(0, len
);
1962 if(info
->lpszIssuerInfo
)
1963 CertNameToStrA(context
->dwCertEncodingType
,
1964 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
,
1965 info
->lpszIssuerInfo
, len
);
1966 info
->dwKeySize
= NETCON_GetCipherStrength(&req
->netConnection
);
1967 CertFreeCertificateContext(context
);
1968 return ERROR_SUCCESS
;
1973 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
1976 static DWORD
HTTPREQ_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
1978 http_request_t
*req
= (http_request_t
*)hdr
;
1981 case INTERNET_OPTION_SECURITY_FLAGS
:
1985 if (!buffer
|| size
!= sizeof(DWORD
))
1986 return ERROR_INVALID_PARAMETER
;
1987 flags
= *(DWORD
*)buffer
;
1988 TRACE("%08x\n", flags
);
1989 req
->netConnection
.security_flags
= flags
;
1990 return ERROR_SUCCESS
;
1992 case INTERNET_OPTION_SEND_TIMEOUT
:
1993 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
1994 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1996 if (size
!= sizeof(DWORD
))
1997 return ERROR_INVALID_PARAMETER
;
1999 return NETCON_set_timeout(&req
->netConnection
, option
== INTERNET_OPTION_SEND_TIMEOUT
,
2002 case INTERNET_OPTION_USERNAME
:
2003 HeapFree(GetProcessHeap(), 0, req
->session
->userName
);
2004 if (!(req
->session
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2005 return ERROR_SUCCESS
;
2007 case INTERNET_OPTION_PASSWORD
:
2008 HeapFree(GetProcessHeap(), 0, req
->session
->password
);
2009 if (!(req
->session
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2010 return ERROR_SUCCESS
;
2011 case INTERNET_OPTION_HTTP_DECODING
:
2012 if(size
!= sizeof(BOOL
))
2013 return ERROR_INVALID_PARAMETER
;
2014 req
->decoding
= *(BOOL
*)buffer
;
2015 return ERROR_SUCCESS
;
2018 return ERROR_INTERNET_INVALID_OPTION
;
2021 /* read some more data into the read buffer (the read section must be held) */
2022 static DWORD
read_more_data( http_request_t
*req
, int maxlen
)
2029 /* move existing data to the start of the buffer */
2031 memmove( req
->read_buf
, req
->read_buf
+ req
->read_pos
, req
->read_size
);
2035 if (maxlen
== -1) maxlen
= sizeof(req
->read_buf
);
2037 res
= NETCON_recv( &req
->netConnection
, req
->read_buf
+ req
->read_size
,
2038 maxlen
- req
->read_size
, 0, &len
);
2039 if(res
== ERROR_SUCCESS
)
2040 req
->read_size
+= len
;
2045 /* remove some amount of data from the read buffer (the read section must be held) */
2046 static void remove_data( http_request_t
*req
, int count
)
2048 if (!(req
->read_size
-= count
)) req
->read_pos
= 0;
2049 else req
->read_pos
+= count
;
2052 static BOOL
read_line( http_request_t
*req
, LPSTR buffer
, DWORD
*len
)
2054 int count
, bytes_read
, pos
= 0;
2057 EnterCriticalSection( &req
->read_section
);
2060 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
2064 count
= eol
- (req
->read_buf
+ req
->read_pos
);
2065 bytes_read
= count
+ 1;
2067 else count
= bytes_read
= req
->read_size
;
2069 count
= min( count
, *len
- pos
);
2070 memcpy( buffer
+ pos
, req
->read_buf
+ req
->read_pos
, count
);
2072 remove_data( req
, bytes_read
);
2075 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
|| !req
->read_size
)
2078 TRACE( "returning empty string\n" );
2079 LeaveCriticalSection( &req
->read_section
);
2080 INTERNET_SetLastError(res
);
2084 LeaveCriticalSection( &req
->read_section
);
2088 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
2091 buffer
[*len
- 1] = 0;
2092 TRACE( "returning %s\n", debugstr_a(buffer
));
2096 /* check if we have reached the end of the data to read (the read section must be held) */
2097 static BOOL
end_of_read_data( http_request_t
*req
)
2099 return !req
->read_size
&& req
->data_stream
->vtbl
->end_of_data(req
->data_stream
, req
);
2102 /* fetch some more data into the read buffer (the read section must be held) */
2103 static DWORD
refill_read_buffer(http_request_t
*req
, read_mode_t read_mode
)
2107 if(req
->read_size
== sizeof(req
->read_buf
))
2108 return ERROR_SUCCESS
;
2112 memmove(req
->read_buf
, req
->read_buf
+req
->read_pos
, req
->read_size
);
2116 res
= req
->data_stream
->vtbl
->read(req
->data_stream
, req
, req
->read_buf
+req
->read_size
,
2117 sizeof(req
->read_buf
)-req
->read_size
, &read
, read_mode
);
2118 req
->read_size
+= read
;
2120 TRACE("read %u bytes, read_size %u\n", read
, req
->read_size
);
2124 /* return the size of data available to be read immediately (the read section must be held) */
2125 static DWORD
get_avail_data( http_request_t
*req
)
2127 return req
->read_size
+ req
->data_stream
->vtbl
->get_avail_data(req
->data_stream
, req
);
2130 static DWORD
netconn_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
2134 NETCON_query_data_available(&req
->netConnection
, &avail
);
2138 static BOOL
netconn_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
2140 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2141 return netconn_stream
->content_read
== netconn_stream
->content_length
;
2144 static DWORD
netconn_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
2145 DWORD
*read
, read_mode_t read_mode
)
2147 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2151 size
= min(size
, netconn_stream
->content_length
-netconn_stream
->content_read
);
2153 if(read_mode
== READMODE_NOBLOCK
)
2154 size
= min(size
, netconn_get_avail_data(stream
, req
));
2157 if(NETCON_recv(&req
->netConnection
, buf
+ ret_read
, size
,
2158 read_mode
== READMODE_SYNC
? MSG_WAITALL
: 0, &len
) == ERROR_SUCCESS
)
2162 netconn_stream
->content_read
+= *read
= ret_read
;
2163 TRACE("read %u bytes\n", ret_read
);
2164 return ERROR_SUCCESS
;
2167 static void netconn_destroy(data_stream_t
*stream
)
2171 static const data_stream_vtbl_t netconn_stream_vtbl
= {
2172 netconn_get_avail_data
,
2173 netconn_end_of_data
,
2178 /* read some more data into the read buffer (the read section must be held) */
2179 static DWORD
read_more_chunked_data(chunked_stream_t
*stream
, http_request_t
*req
, int maxlen
)
2184 if (stream
->buf_pos
)
2186 /* move existing data to the start of the buffer */
2187 if(stream
->buf_size
)
2188 memmove(stream
->buf
, stream
->buf
+ stream
->buf_pos
, stream
->buf_size
);
2189 stream
->buf_pos
= 0;
2192 if (maxlen
== -1) maxlen
= sizeof(stream
->buf
);
2194 res
= NETCON_recv( &req
->netConnection
, stream
->buf
+ stream
->buf_size
,
2195 maxlen
- stream
->buf_size
, 0, &len
);
2196 if(res
== ERROR_SUCCESS
)
2197 stream
->buf_size
+= len
;
2202 /* remove some amount of data from the read buffer (the read section must be held) */
2203 static void remove_chunked_data(chunked_stream_t
*stream
, int count
)
2205 if (!(stream
->buf_size
-= count
)) stream
->buf_pos
= 0;
2206 else stream
->buf_pos
+= count
;
2209 /* discard data contents until we reach end of line (the read section must be held) */
2210 static DWORD
discard_chunked_eol(chunked_stream_t
*stream
, http_request_t
*req
)
2216 BYTE
*eol
= memchr(stream
->buf
+ stream
->buf_pos
, '\n', stream
->buf_size
);
2219 remove_chunked_data(stream
, (eol
+ 1) - (stream
->buf
+ stream
->buf_pos
));
2222 stream
->buf_pos
= stream
->buf_size
= 0; /* discard everything */
2223 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2224 } while (stream
->buf_size
);
2225 return ERROR_SUCCESS
;
2228 /* read the size of the next chunk (the read section must be held) */
2229 static DWORD
start_next_chunk(chunked_stream_t
*stream
, http_request_t
*req
)
2232 DWORD chunk_size
= 0, res
;
2234 if(stream
->chunk_size
!= ~0u && (res
= discard_chunked_eol(stream
, req
)) != ERROR_SUCCESS
)
2239 while (stream
->buf_size
)
2241 char ch
= stream
->buf
[stream
->buf_pos
];
2242 if (ch
>= '0' && ch
<= '9') chunk_size
= chunk_size
* 16 + ch
- '0';
2243 else if (ch
>= 'a' && ch
<= 'f') chunk_size
= chunk_size
* 16 + ch
- 'a' + 10;
2244 else if (ch
>= 'A' && ch
<= 'F') chunk_size
= chunk_size
* 16 + ch
- 'A' + 10;
2245 else if (ch
== ';' || ch
== '\r' || ch
== '\n')
2247 TRACE( "reading %u byte chunk\n", chunk_size
);
2248 stream
->chunk_size
= chunk_size
;
2249 req
->contentLength
+= chunk_size
;
2250 return discard_chunked_eol(stream
, req
);
2252 remove_chunked_data(stream
, 1);
2254 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2255 if (!stream
->buf_size
)
2257 stream
->chunk_size
= 0;
2258 return ERROR_SUCCESS
;
2263 static DWORD
chunked_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
2265 /* Allow reading only from read buffer */
2269 static BOOL
chunked_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
2271 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2272 return !chunked_stream
->chunk_size
;
2275 static DWORD
chunked_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
2276 DWORD
*read
, read_mode_t read_mode
)
2278 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2279 DWORD read_bytes
= 0, ret_read
= 0, res
= ERROR_SUCCESS
;
2281 if(chunked_stream
->chunk_size
== ~0u) {
2282 res
= start_next_chunk(chunked_stream
, req
);
2283 if(res
!= ERROR_SUCCESS
)
2287 while(size
&& chunked_stream
->chunk_size
) {
2288 if(chunked_stream
->buf_size
) {
2289 read_bytes
= min(size
, min(chunked_stream
->buf_size
, chunked_stream
->chunk_size
));
2291 /* this could block */
2292 if(read_mode
== READMODE_NOBLOCK
&& read_bytes
== chunked_stream
->chunk_size
)
2295 memcpy(buf
+ret_read
, chunked_stream
->buf
+chunked_stream
->buf_pos
, read_bytes
);
2296 remove_chunked_data(chunked_stream
, read_bytes
);
2298 read_bytes
= min(size
, chunked_stream
->chunk_size
);
2300 if(read_mode
== READMODE_NOBLOCK
) {
2303 if(!NETCON_query_data_available(&req
->netConnection
, &avail
) || !avail
)
2305 if(read_bytes
> avail
)
2308 /* this could block */
2309 if(read_bytes
== chunked_stream
->chunk_size
)
2313 res
= NETCON_recv(&req
->netConnection
, (char *)buf
+ret_read
, read_bytes
, 0, (int*)&read_bytes
);
2314 if(res
!= ERROR_SUCCESS
)
2318 chunked_stream
->chunk_size
-= read_bytes
;
2320 ret_read
+= read_bytes
;
2321 if(!chunked_stream
->chunk_size
) {
2322 assert(read_mode
!= READMODE_NOBLOCK
);
2323 res
= start_next_chunk(chunked_stream
, req
);
2324 if(res
!= ERROR_SUCCESS
)
2328 if(read_mode
== READMODE_ASYNC
)
2329 read_mode
= READMODE_NOBLOCK
;
2332 TRACE("read %u bytes\n", ret_read
);
2337 static void chunked_destroy(data_stream_t
*stream
)
2339 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2340 heap_free(chunked_stream
);
2343 static const data_stream_vtbl_t chunked_stream_vtbl
= {
2344 chunked_get_avail_data
,
2345 chunked_end_of_data
,
2350 /* set the request content length based on the headers */
2351 static DWORD
set_content_length(http_request_t
*request
)
2353 static const WCHAR szChunked
[] = {'c','h','u','n','k','e','d',0};
2357 size
= sizeof(request
->contentLength
);
2358 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_CONTENT_LENGTH
,
2359 &request
->contentLength
, &size
, NULL
) != ERROR_SUCCESS
)
2360 request
->contentLength
= ~0u;
2361 request
->netconn_stream
.content_length
= request
->contentLength
;
2362 request
->netconn_stream
.content_read
= request
->read_size
;
2364 size
= sizeof(encoding
);
2365 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_TRANSFER_ENCODING
, encoding
, &size
, NULL
) == ERROR_SUCCESS
&&
2366 !strcmpiW(encoding
, szChunked
))
2368 chunked_stream_t
*chunked_stream
;
2370 chunked_stream
= heap_alloc(sizeof(*chunked_stream
));
2372 return ERROR_OUTOFMEMORY
;
2374 chunked_stream
->data_stream
.vtbl
= &chunked_stream_vtbl
;
2375 chunked_stream
->buf_size
= chunked_stream
->buf_pos
= 0;
2376 chunked_stream
->chunk_size
= ~0u;
2378 if(request
->read_size
) {
2379 memcpy(chunked_stream
->buf
, request
->read_buf
+request
->read_pos
, request
->read_size
);
2380 chunked_stream
->buf_size
= request
->read_size
;
2381 request
->read_size
= request
->read_pos
= 0;
2384 request
->data_stream
= &chunked_stream
->data_stream
;
2385 request
->contentLength
= ~0u;
2386 request
->read_chunked
= TRUE
;
2389 if(request
->decoding
) {
2392 static const WCHAR gzipW
[] = {'g','z','i','p',0};
2394 encoding_idx
= HTTP_GetCustomHeaderIndex(request
, szContent_Encoding
, 0, FALSE
);
2395 if(encoding_idx
!= -1 && !strcmpiW(request
->custHeaders
[encoding_idx
].lpszValue
, gzipW
))
2396 return init_gzip_stream(request
);
2399 return ERROR_SUCCESS
;
2402 static void HTTP_ReceiveRequestData(http_request_t
*req
, BOOL first_notif
)
2404 INTERNET_ASYNC_RESULT iar
;
2409 EnterCriticalSection( &req
->read_section
);
2410 if ((res
= refill_read_buffer(req
, READMODE_ASYNC
)) == ERROR_SUCCESS
) {
2411 iar
.dwResult
= (DWORD_PTR
)req
->hdr
.hInternet
;
2412 iar
.dwError
= first_notif
? 0 : get_avail_data(req
);
2413 if(!first_notif
&& !iar
.dwError
)
2414 ERR("No data reported!\n");
2419 LeaveCriticalSection( &req
->read_section
);
2421 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2422 sizeof(INTERNET_ASYNC_RESULT
));
2425 /* read data from the http connection (the read section must be held) */
2426 static DWORD
HTTPREQ_Read(http_request_t
*req
, void *buffer
, DWORD size
, DWORD
*read
, BOOL sync
)
2428 DWORD current_read
= 0, ret_read
= 0;
2429 read_mode_t read_mode
;
2430 DWORD res
= ERROR_SUCCESS
;
2432 read_mode
= req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
? READMODE_ASYNC
: READMODE_SYNC
;
2434 EnterCriticalSection( &req
->read_section
);
2436 if(req
->read_size
) {
2437 ret_read
= min(size
, req
->read_size
);
2438 memcpy(buffer
, req
->read_buf
+req
->read_pos
, ret_read
);
2439 req
->read_size
-= ret_read
;
2440 req
->read_pos
+= ret_read
;
2441 if(read_mode
== READMODE_ASYNC
)
2442 read_mode
= READMODE_NOBLOCK
;
2445 if(ret_read
< size
) {
2446 res
= req
->data_stream
->vtbl
->read(req
->data_stream
, req
, (BYTE
*)buffer
+ret_read
, size
-ret_read
, ¤t_read
, read_mode
);
2447 ret_read
+= current_read
;
2450 LeaveCriticalSection( &req
->read_section
);
2453 TRACE( "retrieved %u bytes (%u)\n", ret_read
, req
->contentLength
);
2455 if(req
->hCacheFile
&& res
== ERROR_SUCCESS
&& ret_read
) {
2459 res
= WriteFile(req
->hCacheFile
, buffer
, ret_read
, &written
, NULL
);
2461 WARN("WriteFile failed: %u\n", GetLastError());
2464 if(end_of_read_data(req
))
2465 HTTP_FinishedReading(req
);
2471 static DWORD
HTTPREQ_ReadFile(object_header_t
*hdr
, void *buffer
, DWORD size
, DWORD
*read
)
2473 http_request_t
*req
= (http_request_t
*)hdr
;
2476 EnterCriticalSection( &req
->read_section
);
2477 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2478 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2480 res
= HTTPREQ_Read(req
, buffer
, size
, read
, TRUE
);
2481 if(res
== ERROR_SUCCESS
)
2483 LeaveCriticalSection( &req
->read_section
);
2488 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST
*workRequest
)
2490 struct WORKREQ_INTERNETREADFILEEXA
const *data
= &workRequest
->u
.InternetReadFileExA
;
2491 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2492 INTERNET_ASYNC_RESULT iar
;
2495 TRACE("INTERNETREADFILEEXA %p\n", workRequest
->hdr
);
2497 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2498 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2500 iar
.dwResult
= res
== ERROR_SUCCESS
;
2503 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2504 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2505 sizeof(INTERNET_ASYNC_RESULT
));
2508 static DWORD
HTTPREQ_ReadFileExA(object_header_t
*hdr
, INTERNET_BUFFERSA
*buffers
,
2509 DWORD flags
, DWORD_PTR context
)
2511 http_request_t
*req
= (http_request_t
*)hdr
;
2512 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2514 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2515 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2517 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2518 return ERROR_INVALID_PARAMETER
;
2520 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2522 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2524 WORKREQUEST workRequest
;
2526 if (TryEnterCriticalSection( &req
->read_section
))
2528 if (get_avail_data(req
))
2530 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2531 &buffers
->dwBufferLength
, FALSE
);
2532 size
= buffers
->dwBufferLength
;
2533 LeaveCriticalSection( &req
->read_section
);
2536 LeaveCriticalSection( &req
->read_section
);
2539 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExAProc
;
2540 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2541 workRequest
.u
.InternetReadFileExA
.lpBuffersOut
= buffers
;
2543 INTERNET_AsyncCall(&workRequest
);
2545 return ERROR_IO_PENDING
;
2549 size
= buffers
->dwBufferLength
;
2551 EnterCriticalSection( &req
->read_section
);
2552 if(hdr
->dwError
== ERROR_SUCCESS
)
2553 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2554 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2555 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2558 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2559 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2560 if(res
!= ERROR_SUCCESS
)
2563 read
+= buffers
->dwBufferLength
;
2564 if(read
== size
|| end_of_read_data(req
))
2567 LeaveCriticalSection( &req
->read_section
);
2569 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2570 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2571 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2572 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2574 EnterCriticalSection( &req
->read_section
);
2577 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2578 hdr
->dwError
= ERROR_SUCCESS
;
2580 error
= hdr
->dwError
;
2582 LeaveCriticalSection( &req
->read_section
);
2583 size
= buffers
->dwBufferLength
;
2584 buffers
->dwBufferLength
= read
;
2587 if (res
== ERROR_SUCCESS
) {
2588 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2589 &size
, sizeof(size
));
2592 return res
==ERROR_SUCCESS
? error
: res
;
2595 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST
*workRequest
)
2597 struct WORKREQ_INTERNETREADFILEEXW
const *data
= &workRequest
->u
.InternetReadFileExW
;
2598 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2599 INTERNET_ASYNC_RESULT iar
;
2602 TRACE("INTERNETREADFILEEXW %p\n", workRequest
->hdr
);
2604 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2605 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2607 iar
.dwResult
= res
== ERROR_SUCCESS
;
2610 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2611 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2612 sizeof(INTERNET_ASYNC_RESULT
));
2615 static DWORD
HTTPREQ_ReadFileExW(object_header_t
*hdr
, INTERNET_BUFFERSW
*buffers
,
2616 DWORD flags
, DWORD_PTR context
)
2619 http_request_t
*req
= (http_request_t
*)hdr
;
2620 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2622 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2623 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2625 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2626 return ERROR_INVALID_PARAMETER
;
2628 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2630 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2632 WORKREQUEST workRequest
;
2634 if (TryEnterCriticalSection( &req
->read_section
))
2636 if (get_avail_data(req
))
2638 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2639 &buffers
->dwBufferLength
, FALSE
);
2640 size
= buffers
->dwBufferLength
;
2641 LeaveCriticalSection( &req
->read_section
);
2644 LeaveCriticalSection( &req
->read_section
);
2647 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExWProc
;
2648 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2649 workRequest
.u
.InternetReadFileExW
.lpBuffersOut
= buffers
;
2651 INTERNET_AsyncCall(&workRequest
);
2653 return ERROR_IO_PENDING
;
2657 size
= buffers
->dwBufferLength
;
2659 EnterCriticalSection( &req
->read_section
);
2660 if(hdr
->dwError
== ERROR_SUCCESS
)
2661 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2662 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2663 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2666 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2667 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2668 if(res
!= ERROR_SUCCESS
)
2671 read
+= buffers
->dwBufferLength
;
2672 if(read
== size
|| end_of_read_data(req
))
2675 LeaveCriticalSection( &req
->read_section
);
2677 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2678 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2679 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2680 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2682 EnterCriticalSection( &req
->read_section
);
2685 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2686 hdr
->dwError
= ERROR_SUCCESS
;
2688 error
= hdr
->dwError
;
2690 LeaveCriticalSection( &req
->read_section
);
2691 size
= buffers
->dwBufferLength
;
2692 buffers
->dwBufferLength
= read
;
2695 if (res
== ERROR_SUCCESS
) {
2696 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2697 &size
, sizeof(size
));
2700 return res
==ERROR_SUCCESS
? error
: res
;
2703 static DWORD
HTTPREQ_WriteFile(object_header_t
*hdr
, const void *buffer
, DWORD size
, DWORD
*written
)
2706 http_request_t
*request
= (http_request_t
*)hdr
;
2708 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
2711 res
= NETCON_send(&request
->netConnection
, buffer
, size
, 0, (LPINT
)written
);
2712 if (res
== ERROR_SUCCESS
)
2713 request
->bytesWritten
+= *written
;
2715 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_SENT
, written
, sizeof(DWORD
));
2719 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST
*workRequest
)
2721 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2723 HTTP_ReceiveRequestData(req
, FALSE
);
2726 static DWORD
HTTPREQ_QueryDataAvailable(object_header_t
*hdr
, DWORD
*available
, DWORD flags
, DWORD_PTR ctx
)
2728 http_request_t
*req
= (http_request_t
*)hdr
;
2730 TRACE("(%p %p %x %lx)\n", req
, available
, flags
, ctx
);
2732 if (req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
2734 WORKREQUEST workRequest
;
2736 /* never wait, if we can't enter the section we queue an async request right away */
2737 if (TryEnterCriticalSection( &req
->read_section
))
2739 refill_read_buffer(req
, READMODE_NOBLOCK
);
2740 if ((*available
= get_avail_data( req
))) goto done
;
2741 if (end_of_read_data( req
)) goto done
;
2742 LeaveCriticalSection( &req
->read_section
);
2745 workRequest
.asyncproc
= HTTPREQ_AsyncQueryDataAvailableProc
;
2746 workRequest
.hdr
= WININET_AddRef( &req
->hdr
);
2748 INTERNET_AsyncCall(&workRequest
);
2750 return ERROR_IO_PENDING
;
2753 EnterCriticalSection( &req
->read_section
);
2755 if (!(*available
= get_avail_data( req
)) && !end_of_read_data( req
))
2757 refill_read_buffer( req
, READMODE_ASYNC
);
2758 *available
= get_avail_data( req
);
2762 LeaveCriticalSection( &req
->read_section
);
2764 TRACE( "returning %u\n", *available
);
2765 return ERROR_SUCCESS
;
2768 static const object_vtbl_t HTTPREQVtbl
= {
2770 HTTPREQ_CloseConnection
,
2771 HTTPREQ_QueryOption
,
2774 HTTPREQ_ReadFileExA
,
2775 HTTPREQ_ReadFileExW
,
2777 HTTPREQ_QueryDataAvailable
,
2781 /***********************************************************************
2782 * HTTP_HttpOpenRequestW (internal)
2784 * Open a HTTP request handle
2787 * HINTERNET a HTTP request handle on success
2791 static DWORD
HTTP_HttpOpenRequestW(http_session_t
*session
,
2792 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
2793 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
2794 DWORD dwFlags
, DWORD_PTR dwContext
, HINTERNET
*ret
)
2796 appinfo_t
*hIC
= NULL
;
2797 http_request_t
*request
;
2798 LPWSTR lpszHostName
= NULL
;
2799 static const WCHAR szHostForm
[] = {'%','s',':','%','u',0};
2804 assert( session
->hdr
.htype
== WH_HHTTPSESSION
);
2805 hIC
= session
->appInfo
;
2807 request
= alloc_object(&session
->hdr
, &HTTPREQVtbl
, sizeof(http_request_t
));
2809 return ERROR_OUTOFMEMORY
;
2811 request
->hdr
.htype
= WH_HHTTPREQ
;
2812 request
->hdr
.dwFlags
= dwFlags
;
2813 request
->hdr
.dwContext
= dwContext
;
2814 request
->contentLength
= ~0u;
2816 request
->netconn_stream
.data_stream
.vtbl
= &netconn_stream_vtbl
;
2817 request
->data_stream
= &request
->netconn_stream
.data_stream
;
2819 InitializeCriticalSection( &request
->read_section
);
2821 WININET_AddRef( &session
->hdr
);
2822 request
->session
= session
;
2823 list_add_head( &session
->hdr
.children
, &request
->hdr
.entry
);
2825 lpszHostName
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
) *
2826 (strlenW(session
->hostName
) + 7 /* length of ":65535" + 1 */));
2827 if (NULL
== lpszHostName
)
2829 res
= ERROR_OUTOFMEMORY
;
2833 if ((res
= NETCON_init(&request
->netConnection
, dwFlags
& INTERNET_FLAG_SECURE
)) != ERROR_SUCCESS
)
2836 if (lpszObjectName
&& *lpszObjectName
) {
2840 rc
= UrlEscapeW(lpszObjectName
, NULL
, &len
, URL_ESCAPE_SPACES_ONLY
);
2841 if (rc
!= E_POINTER
)
2842 len
= strlenW(lpszObjectName
)+1;
2843 request
->path
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
2844 rc
= UrlEscapeW(lpszObjectName
, request
->path
, &len
,
2845 URL_ESCAPE_SPACES_ONLY
);
2848 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName
),rc
);
2849 strcpyW(request
->path
,lpszObjectName
);
2852 static const WCHAR slashW
[] = {'/',0};
2854 request
->path
= heap_strdupW(slashW
);
2857 if (lpszReferrer
&& *lpszReferrer
)
2858 HTTP_ProcessHeader(request
, HTTP_REFERER
, lpszReferrer
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2860 if (lpszAcceptTypes
)
2863 for (i
= 0; lpszAcceptTypes
[i
]; i
++)
2865 if (!*lpszAcceptTypes
[i
]) continue;
2866 HTTP_ProcessHeader(request
, HTTP_ACCEPT
, lpszAcceptTypes
[i
],
2867 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
|
2868 HTTP_ADDHDR_FLAG_REQ
|
2869 (i
== 0 ? HTTP_ADDHDR_FLAG_REPLACE
: 0));
2873 request
->verb
= heap_strdupW(lpszVerb
&& *lpszVerb
? lpszVerb
: szGET
);
2874 request
->version
= heap_strdupW(lpszVersion
? lpszVersion
: g_szHttp1_1
);
2876 if (session
->hostPort
!= INTERNET_INVALID_PORT_NUMBER
&&
2877 session
->hostPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
2878 session
->hostPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
2880 sprintfW(lpszHostName
, szHostForm
, session
->hostName
, session
->hostPort
);
2881 HTTP_ProcessHeader(request
, hostW
, lpszHostName
,
2882 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2885 HTTP_ProcessHeader(request
, hostW
, session
->hostName
,
2886 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2888 if (session
->serverPort
== INTERNET_INVALID_PORT_NUMBER
)
2889 session
->serverPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2890 INTERNET_DEFAULT_HTTPS_PORT
:
2891 INTERNET_DEFAULT_HTTP_PORT
);
2893 if (session
->hostPort
== INTERNET_INVALID_PORT_NUMBER
)
2894 session
->hostPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2895 INTERNET_DEFAULT_HTTPS_PORT
:
2896 INTERNET_DEFAULT_HTTP_PORT
);
2898 if (NULL
!= hIC
->proxy
&& hIC
->proxy
[0] != 0)
2899 HTTP_DealWithProxy( hIC
, session
, request
);
2901 INTERNET_SendCallback(&session
->hdr
, dwContext
,
2902 INTERNET_STATUS_HANDLE_CREATED
, &request
->hdr
.hInternet
,
2906 TRACE("<-- %u (%p)\n", res
, request
);
2908 HeapFree(GetProcessHeap(), 0, lpszHostName
);
2909 if(res
!= ERROR_SUCCESS
) {
2910 WININET_Release( &request
->hdr
);
2915 *ret
= request
->hdr
.hInternet
;
2916 return ERROR_SUCCESS
;
2919 /***********************************************************************
2920 * HttpOpenRequestW (WININET.@)
2922 * Open a HTTP request handle
2925 * HINTERNET a HTTP request handle on success
2929 HINTERNET WINAPI
HttpOpenRequestW(HINTERNET hHttpSession
,
2930 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
2931 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
2932 DWORD dwFlags
, DWORD_PTR dwContext
)
2934 http_session_t
*session
;
2935 HINTERNET handle
= NULL
;
2938 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
2939 debugstr_w(lpszVerb
), debugstr_w(lpszObjectName
),
2940 debugstr_w(lpszVersion
), debugstr_w(lpszReferrer
), lpszAcceptTypes
,
2941 dwFlags
, dwContext
);
2942 if(lpszAcceptTypes
!=NULL
)
2945 for(i
=0;lpszAcceptTypes
[i
]!=NULL
;i
++)
2946 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes
[i
]));
2949 session
= (http_session_t
*) get_handle_object( hHttpSession
);
2950 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
2952 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
2957 * My tests seem to show that the windows version does not
2958 * become asynchronous until after this point. And anyhow
2959 * if this call was asynchronous then how would you get the
2960 * necessary HINTERNET pointer returned by this function.
2963 res
= HTTP_HttpOpenRequestW(session
, lpszVerb
, lpszObjectName
,
2964 lpszVersion
, lpszReferrer
, lpszAcceptTypes
,
2965 dwFlags
, dwContext
, &handle
);
2968 WININET_Release( &session
->hdr
);
2969 TRACE("returning %p\n", handle
);
2970 if(res
!= ERROR_SUCCESS
)
2975 /* read any content returned by the server so that the connection can be
2977 static void HTTP_DrainContent(http_request_t
*req
)
2981 if (!NETCON_connected(&req
->netConnection
)) return;
2983 if (req
->contentLength
== -1)
2985 NETCON_close(&req
->netConnection
);
2988 if (!strcmpW(req
->verb
, szHEAD
)) return;
2993 if (HTTPREQ_Read(req
, buffer
, sizeof(buffer
), &bytes_read
, TRUE
) != ERROR_SUCCESS
)
2995 } while (bytes_read
);
2998 static const LPCWSTR header_lookup
[] = {
2999 szMime_Version
, /* HTTP_QUERY_MIME_VERSION = 0 */
3000 szContent_Type
, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3001 szContent_Transfer_Encoding
,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3002 szContent_ID
, /* HTTP_QUERY_CONTENT_ID = 3 */
3003 NULL
, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3004 szContent_Length
, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3005 szContent_Language
, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3006 szAllow
, /* HTTP_QUERY_ALLOW = 7 */
3007 szPublic
, /* HTTP_QUERY_PUBLIC = 8 */
3008 szDate
, /* HTTP_QUERY_DATE = 9 */
3009 szExpires
, /* HTTP_QUERY_EXPIRES = 10 */
3010 szLast_Modified
, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3011 NULL
, /* HTTP_QUERY_MESSAGE_ID = 12 */
3012 szURI
, /* HTTP_QUERY_URI = 13 */
3013 szFrom
, /* HTTP_QUERY_DERIVED_FROM = 14 */
3014 NULL
, /* HTTP_QUERY_COST = 15 */
3015 NULL
, /* HTTP_QUERY_LINK = 16 */
3016 szPragma
, /* HTTP_QUERY_PRAGMA = 17 */
3017 NULL
, /* HTTP_QUERY_VERSION = 18 */
3018 szStatus
, /* HTTP_QUERY_STATUS_CODE = 19 */
3019 NULL
, /* HTTP_QUERY_STATUS_TEXT = 20 */
3020 NULL
, /* HTTP_QUERY_RAW_HEADERS = 21 */
3021 NULL
, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3022 szConnection
, /* HTTP_QUERY_CONNECTION = 23 */
3023 szAccept
, /* HTTP_QUERY_ACCEPT = 24 */
3024 szAccept_Charset
, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3025 szAccept_Encoding
, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3026 szAccept_Language
, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3027 szAuthorization
, /* HTTP_QUERY_AUTHORIZATION = 28 */
3028 szContent_Encoding
, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3029 NULL
, /* HTTP_QUERY_FORWARDED = 30 */
3030 NULL
, /* HTTP_QUERY_FROM = 31 */
3031 szIf_Modified_Since
, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3032 szLocation
, /* HTTP_QUERY_LOCATION = 33 */
3033 NULL
, /* HTTP_QUERY_ORIG_URI = 34 */
3034 szReferer
, /* HTTP_QUERY_REFERER = 35 */
3035 szRetry_After
, /* HTTP_QUERY_RETRY_AFTER = 36 */
3036 szServer
, /* HTTP_QUERY_SERVER = 37 */
3037 NULL
, /* HTTP_TITLE = 38 */
3038 szUser_Agent
, /* HTTP_QUERY_USER_AGENT = 39 */
3039 szWWW_Authenticate
, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3040 szProxy_Authenticate
, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3041 szAccept_Ranges
, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3042 szSet_Cookie
, /* HTTP_QUERY_SET_COOKIE = 43 */
3043 szCookie
, /* HTTP_QUERY_COOKIE = 44 */
3044 NULL
, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3045 NULL
, /* HTTP_QUERY_REFRESH = 46 */
3046 NULL
, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3047 szAge
, /* HTTP_QUERY_AGE = 48 */
3048 szCache_Control
, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3049 szContent_Base
, /* HTTP_QUERY_CONTENT_BASE = 50 */
3050 szContent_Location
, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3051 szContent_MD5
, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3052 szContent_Range
, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3053 szETag
, /* HTTP_QUERY_ETAG = 54 */
3054 hostW
, /* HTTP_QUERY_HOST = 55 */
3055 szIf_Match
, /* HTTP_QUERY_IF_MATCH = 56 */
3056 szIf_None_Match
, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3057 szIf_Range
, /* HTTP_QUERY_IF_RANGE = 58 */
3058 szIf_Unmodified_Since
, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3059 szMax_Forwards
, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3060 szProxy_Authorization
, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3061 szRange
, /* HTTP_QUERY_RANGE = 62 */
3062 szTransfer_Encoding
, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3063 szUpgrade
, /* HTTP_QUERY_UPGRADE = 64 */
3064 szVary
, /* HTTP_QUERY_VARY = 65 */
3065 szVia
, /* HTTP_QUERY_VIA = 66 */
3066 szWarning
, /* HTTP_QUERY_WARNING = 67 */
3067 szExpect
, /* HTTP_QUERY_EXPECT = 68 */
3068 szProxy_Connection
, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3069 szUnless_Modified_Since
, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3072 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3074 /***********************************************************************
3075 * HTTP_HttpQueryInfoW (internal)
3077 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*request
, DWORD dwInfoLevel
,
3078 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3080 LPHTTPHEADERW lphttpHdr
= NULL
;
3081 BOOL request_only
= dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
;
3082 INT requested_index
= lpdwIndex
? *lpdwIndex
: 0;
3083 DWORD level
= (dwInfoLevel
& ~HTTP_QUERY_MODIFIER_FLAGS_MASK
);
3086 /* Find requested header structure */
3089 case HTTP_QUERY_CUSTOM
:
3090 if (!lpBuffer
) return ERROR_INVALID_PARAMETER
;
3091 index
= HTTP_GetCustomHeaderIndex(request
, lpBuffer
, requested_index
, request_only
);
3093 case HTTP_QUERY_RAW_HEADERS_CRLF
:
3097 DWORD res
= ERROR_INVALID_PARAMETER
;
3100 headers
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
3102 headers
= request
->rawHeaders
;
3105 len
= strlenW(headers
) * sizeof(WCHAR
);
3107 if (len
+ sizeof(WCHAR
) > *lpdwBufferLength
)
3109 len
+= sizeof(WCHAR
);
3110 res
= ERROR_INSUFFICIENT_BUFFER
;
3115 memcpy(lpBuffer
, headers
, len
+ sizeof(WCHAR
));
3118 len
= strlenW(szCrLf
) * sizeof(WCHAR
);
3119 memcpy(lpBuffer
, szCrLf
, sizeof(szCrLf
));
3121 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
/ sizeof(WCHAR
)));
3122 res
= ERROR_SUCCESS
;
3124 *lpdwBufferLength
= len
;
3127 HeapFree(GetProcessHeap(), 0, headers
);
3130 case HTTP_QUERY_RAW_HEADERS
:
3132 LPWSTR
* ppszRawHeaderLines
= HTTP_Tokenize(request
->rawHeaders
, szCrLf
);
3134 LPWSTR pszString
= lpBuffer
;
3136 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3137 size
+= strlenW(ppszRawHeaderLines
[i
]) + 1;
3139 if (size
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3141 HTTP_FreeTokens(ppszRawHeaderLines
);
3142 *lpdwBufferLength
= (size
+ 1) * sizeof(WCHAR
);
3143 return ERROR_INSUFFICIENT_BUFFER
;
3147 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3149 DWORD len
= strlenW(ppszRawHeaderLines
[i
]);
3150 memcpy(pszString
, ppszRawHeaderLines
[i
], (len
+1)*sizeof(WCHAR
));
3154 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, size
));
3156 *lpdwBufferLength
= size
* sizeof(WCHAR
);
3157 HTTP_FreeTokens(ppszRawHeaderLines
);
3159 return ERROR_SUCCESS
;
3161 case HTTP_QUERY_STATUS_TEXT
:
3162 if (request
->statusText
)
3164 DWORD len
= strlenW(request
->statusText
);
3165 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3167 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3168 return ERROR_INSUFFICIENT_BUFFER
;
3172 memcpy(lpBuffer
, request
->statusText
, (len
+ 1) * sizeof(WCHAR
));
3173 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3175 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3176 return ERROR_SUCCESS
;
3179 case HTTP_QUERY_VERSION
:
3180 if (request
->version
)
3182 DWORD len
= strlenW(request
->version
);
3183 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3185 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3186 return ERROR_INSUFFICIENT_BUFFER
;
3190 memcpy(lpBuffer
, request
->version
, (len
+ 1) * sizeof(WCHAR
));
3191 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3193 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3194 return ERROR_SUCCESS
;
3197 case HTTP_QUERY_CONTENT_ENCODING
:
3198 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[request
->read_gzip
? HTTP_QUERY_CONTENT_TYPE
: level
],
3199 requested_index
,request_only
);
3202 assert (LAST_TABLE_HEADER
== (HTTP_QUERY_UNLESS_MODIFIED_SINCE
+ 1));
3204 if (level
< LAST_TABLE_HEADER
&& header_lookup
[level
])
3205 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[level
],
3206 requested_index
,request_only
);
3210 lphttpHdr
= &request
->custHeaders
[index
];
3212 /* Ensure header satisfies requested attributes */
3214 ((dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
) &&
3215 (~lphttpHdr
->wFlags
& HDR_ISREQUEST
)))
3217 return ERROR_HTTP_HEADER_NOT_FOUND
;
3220 if (lpdwIndex
&& level
!= HTTP_QUERY_STATUS_CODE
) (*lpdwIndex
)++;
3222 /* coalesce value to requested type */
3223 if (dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
&& lpBuffer
)
3225 *(int *)lpBuffer
= atoiW(lphttpHdr
->lpszValue
);
3226 TRACE(" returning number: %d\n", *(int *)lpBuffer
);
3228 else if (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
&& lpBuffer
)
3234 tmpTime
= ConvertTimeString(lphttpHdr
->lpszValue
);
3236 tmpTM
= *gmtime(&tmpTime
);
3237 STHook
= (SYSTEMTIME
*)lpBuffer
;
3238 STHook
->wDay
= tmpTM
.tm_mday
;
3239 STHook
->wHour
= tmpTM
.tm_hour
;
3240 STHook
->wMilliseconds
= 0;
3241 STHook
->wMinute
= tmpTM
.tm_min
;
3242 STHook
->wDayOfWeek
= tmpTM
.tm_wday
;
3243 STHook
->wMonth
= tmpTM
.tm_mon
+ 1;
3244 STHook
->wSecond
= tmpTM
.tm_sec
;
3245 STHook
->wYear
= tmpTM
.tm_year
;
3247 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3248 STHook
->wYear
, STHook
->wMonth
, STHook
->wDay
, STHook
->wDayOfWeek
,
3249 STHook
->wHour
, STHook
->wMinute
, STHook
->wSecond
, STHook
->wMilliseconds
);
3251 else if (lphttpHdr
->lpszValue
)
3253 DWORD len
= (strlenW(lphttpHdr
->lpszValue
) + 1) * sizeof(WCHAR
);
3255 if (len
> *lpdwBufferLength
)
3257 *lpdwBufferLength
= len
;
3258 return ERROR_INSUFFICIENT_BUFFER
;
3262 memcpy(lpBuffer
, lphttpHdr
->lpszValue
, len
);
3263 TRACE("! returning string: %s\n", debugstr_w(lpBuffer
));
3265 *lpdwBufferLength
= len
- sizeof(WCHAR
);
3267 return ERROR_SUCCESS
;
3270 /***********************************************************************
3271 * HttpQueryInfoW (WININET.@)
3273 * Queries for information about an HTTP request
3280 BOOL WINAPI
HttpQueryInfoW(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3281 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3283 http_request_t
*request
;
3286 if (TRACE_ON(wininet
)) {
3287 #define FE(x) { x, #x }
3288 static const wininet_flag_info query_flags
[] = {
3289 FE(HTTP_QUERY_MIME_VERSION
),
3290 FE(HTTP_QUERY_CONTENT_TYPE
),
3291 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING
),
3292 FE(HTTP_QUERY_CONTENT_ID
),
3293 FE(HTTP_QUERY_CONTENT_DESCRIPTION
),
3294 FE(HTTP_QUERY_CONTENT_LENGTH
),
3295 FE(HTTP_QUERY_CONTENT_LANGUAGE
),
3296 FE(HTTP_QUERY_ALLOW
),
3297 FE(HTTP_QUERY_PUBLIC
),
3298 FE(HTTP_QUERY_DATE
),
3299 FE(HTTP_QUERY_EXPIRES
),
3300 FE(HTTP_QUERY_LAST_MODIFIED
),
3301 FE(HTTP_QUERY_MESSAGE_ID
),
3303 FE(HTTP_QUERY_DERIVED_FROM
),
3304 FE(HTTP_QUERY_COST
),
3305 FE(HTTP_QUERY_LINK
),
3306 FE(HTTP_QUERY_PRAGMA
),
3307 FE(HTTP_QUERY_VERSION
),
3308 FE(HTTP_QUERY_STATUS_CODE
),
3309 FE(HTTP_QUERY_STATUS_TEXT
),
3310 FE(HTTP_QUERY_RAW_HEADERS
),
3311 FE(HTTP_QUERY_RAW_HEADERS_CRLF
),
3312 FE(HTTP_QUERY_CONNECTION
),
3313 FE(HTTP_QUERY_ACCEPT
),
3314 FE(HTTP_QUERY_ACCEPT_CHARSET
),
3315 FE(HTTP_QUERY_ACCEPT_ENCODING
),
3316 FE(HTTP_QUERY_ACCEPT_LANGUAGE
),
3317 FE(HTTP_QUERY_AUTHORIZATION
),
3318 FE(HTTP_QUERY_CONTENT_ENCODING
),
3319 FE(HTTP_QUERY_FORWARDED
),
3320 FE(HTTP_QUERY_FROM
),
3321 FE(HTTP_QUERY_IF_MODIFIED_SINCE
),
3322 FE(HTTP_QUERY_LOCATION
),
3323 FE(HTTP_QUERY_ORIG_URI
),
3324 FE(HTTP_QUERY_REFERER
),
3325 FE(HTTP_QUERY_RETRY_AFTER
),
3326 FE(HTTP_QUERY_SERVER
),
3327 FE(HTTP_QUERY_TITLE
),
3328 FE(HTTP_QUERY_USER_AGENT
),
3329 FE(HTTP_QUERY_WWW_AUTHENTICATE
),
3330 FE(HTTP_QUERY_PROXY_AUTHENTICATE
),
3331 FE(HTTP_QUERY_ACCEPT_RANGES
),
3332 FE(HTTP_QUERY_SET_COOKIE
),
3333 FE(HTTP_QUERY_COOKIE
),
3334 FE(HTTP_QUERY_REQUEST_METHOD
),
3335 FE(HTTP_QUERY_REFRESH
),
3336 FE(HTTP_QUERY_CONTENT_DISPOSITION
),
3338 FE(HTTP_QUERY_CACHE_CONTROL
),
3339 FE(HTTP_QUERY_CONTENT_BASE
),
3340 FE(HTTP_QUERY_CONTENT_LOCATION
),
3341 FE(HTTP_QUERY_CONTENT_MD5
),
3342 FE(HTTP_QUERY_CONTENT_RANGE
),
3343 FE(HTTP_QUERY_ETAG
),
3344 FE(HTTP_QUERY_HOST
),
3345 FE(HTTP_QUERY_IF_MATCH
),
3346 FE(HTTP_QUERY_IF_NONE_MATCH
),
3347 FE(HTTP_QUERY_IF_RANGE
),
3348 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE
),
3349 FE(HTTP_QUERY_MAX_FORWARDS
),
3350 FE(HTTP_QUERY_PROXY_AUTHORIZATION
),
3351 FE(HTTP_QUERY_RANGE
),
3352 FE(HTTP_QUERY_TRANSFER_ENCODING
),
3353 FE(HTTP_QUERY_UPGRADE
),
3354 FE(HTTP_QUERY_VARY
),
3356 FE(HTTP_QUERY_WARNING
),
3357 FE(HTTP_QUERY_CUSTOM
)
3359 static const wininet_flag_info modifier_flags
[] = {
3360 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS
),
3361 FE(HTTP_QUERY_FLAG_SYSTEMTIME
),
3362 FE(HTTP_QUERY_FLAG_NUMBER
),
3363 FE(HTTP_QUERY_FLAG_COALESCE
)
3366 DWORD info_mod
= dwInfoLevel
& HTTP_QUERY_MODIFIER_FLAGS_MASK
;
3367 DWORD info
= dwInfoLevel
& HTTP_QUERY_HEADER_MASK
;
3370 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest
, dwInfoLevel
, info
);
3371 TRACE(" Attribute:");
3372 for (i
= 0; i
< (sizeof(query_flags
) / sizeof(query_flags
[0])); i
++) {
3373 if (query_flags
[i
].val
== info
) {
3374 TRACE(" %s", query_flags
[i
].name
);
3378 if (i
== (sizeof(query_flags
) / sizeof(query_flags
[0]))) {
3379 TRACE(" Unknown (%08x)", info
);
3382 TRACE(" Modifier:");
3383 for (i
= 0; i
< (sizeof(modifier_flags
) / sizeof(modifier_flags
[0])); i
++) {
3384 if (modifier_flags
[i
].val
& info_mod
) {
3385 TRACE(" %s", modifier_flags
[i
].name
);
3386 info_mod
&= ~ modifier_flags
[i
].val
;
3391 TRACE(" Unknown (%08x)", info_mod
);
3396 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
3397 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
3399 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3403 if (lpBuffer
== NULL
)
3404 *lpdwBufferLength
= 0;
3405 res
= HTTP_HttpQueryInfoW( request
, dwInfoLevel
,
3406 lpBuffer
, lpdwBufferLength
, lpdwIndex
);
3410 WININET_Release( &request
->hdr
);
3412 TRACE("%u <--\n", res
);
3413 if(res
!= ERROR_SUCCESS
)
3415 return res
== ERROR_SUCCESS
;
3418 /***********************************************************************
3419 * HttpQueryInfoA (WININET.@)
3421 * Queries for information about an HTTP request
3428 BOOL WINAPI
HttpQueryInfoA(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3429 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3435 if((dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) ||
3436 (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
))
3438 return HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, lpBuffer
,
3439 lpdwBufferLength
, lpdwIndex
);
3445 len
= (*lpdwBufferLength
)*sizeof(WCHAR
);
3446 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3448 alloclen
= MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, NULL
, 0 ) * sizeof(WCHAR
);
3454 bufferW
= HeapAlloc( GetProcessHeap(), 0, alloclen
);
3455 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3456 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3457 MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, bufferW
, alloclen
/ sizeof(WCHAR
) );
3464 result
= HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, bufferW
,
3468 len
= WideCharToMultiByte( CP_ACP
,0, bufferW
, len
/ sizeof(WCHAR
) + 1,
3469 lpBuffer
, *lpdwBufferLength
, NULL
, NULL
);
3470 *lpdwBufferLength
= len
- 1;
3472 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer
));
3475 /* since the strings being returned from HttpQueryInfoW should be
3476 * only ASCII characters, it is reasonable to assume that all of
3477 * the Unicode characters can be reduced to a single byte */
3478 *lpdwBufferLength
= len
/ sizeof(WCHAR
);
3480 HeapFree(GetProcessHeap(), 0, bufferW
);
3485 /***********************************************************************
3486 * HTTP_GetRedirectURL (internal)
3488 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*request
, LPCWSTR lpszUrl
)
3490 static WCHAR szHttp
[] = {'h','t','t','p',0};
3491 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3492 http_session_t
*session
= request
->session
;
3493 URL_COMPONENTSW urlComponents
;
3494 DWORD url_length
= 0;
3496 LPWSTR combined_url
;
3498 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3499 urlComponents
.lpszScheme
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) ? szHttps
: szHttp
;
3500 urlComponents
.dwSchemeLength
= 0;
3501 urlComponents
.lpszHostName
= session
->hostName
;
3502 urlComponents
.dwHostNameLength
= 0;
3503 urlComponents
.nPort
= session
->hostPort
;
3504 urlComponents
.lpszUserName
= session
->userName
;
3505 urlComponents
.dwUserNameLength
= 0;
3506 urlComponents
.lpszPassword
= NULL
;
3507 urlComponents
.dwPasswordLength
= 0;
3508 urlComponents
.lpszUrlPath
= request
->path
;
3509 urlComponents
.dwUrlPathLength
= 0;
3510 urlComponents
.lpszExtraInfo
= NULL
;
3511 urlComponents
.dwExtraInfoLength
= 0;
3513 if (!InternetCreateUrlW(&urlComponents
, 0, NULL
, &url_length
) &&
3514 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3517 orig_url
= HeapAlloc(GetProcessHeap(), 0, url_length
);
3519 /* convert from bytes to characters */
3520 url_length
= url_length
/ sizeof(WCHAR
) - 1;
3521 if (!InternetCreateUrlW(&urlComponents
, 0, orig_url
, &url_length
))
3523 HeapFree(GetProcessHeap(), 0, orig_url
);
3528 if (!InternetCombineUrlW(orig_url
, lpszUrl
, NULL
, &url_length
, ICU_ENCODE_SPACES_ONLY
) &&
3529 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3531 HeapFree(GetProcessHeap(), 0, orig_url
);
3534 combined_url
= HeapAlloc(GetProcessHeap(), 0, url_length
* sizeof(WCHAR
));
3536 if (!InternetCombineUrlW(orig_url
, lpszUrl
, combined_url
, &url_length
, ICU_ENCODE_SPACES_ONLY
))
3538 HeapFree(GetProcessHeap(), 0, orig_url
);
3539 HeapFree(GetProcessHeap(), 0, combined_url
);
3542 HeapFree(GetProcessHeap(), 0, orig_url
);
3543 return combined_url
;
3547 /***********************************************************************
3548 * HTTP_HandleRedirect (internal)
3550 static DWORD
HTTP_HandleRedirect(http_request_t
*request
, LPCWSTR lpszUrl
)
3552 http_session_t
*session
= request
->session
;
3553 appinfo_t
*hIC
= session
->appInfo
;
3554 BOOL using_proxy
= hIC
->proxy
&& hIC
->proxy
[0];
3555 WCHAR path
[INTERNET_MAX_URL_LENGTH
];
3560 /* if it's an absolute path, keep the same session info */
3561 lstrcpynW(path
, lpszUrl
, INTERNET_MAX_URL_LENGTH
);
3565 URL_COMPONENTSW urlComponents
;
3566 WCHAR protocol
[32], hostName
[MAXHOSTNAME
], userName
[1024];
3567 static WCHAR szHttp
[] = {'h','t','t','p',0};
3568 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3574 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3575 urlComponents
.lpszScheme
= protocol
;
3576 urlComponents
.dwSchemeLength
= 32;
3577 urlComponents
.lpszHostName
= hostName
;
3578 urlComponents
.dwHostNameLength
= MAXHOSTNAME
;
3579 urlComponents
.lpszUserName
= userName
;
3580 urlComponents
.dwUserNameLength
= 1024;
3581 urlComponents
.lpszPassword
= NULL
;
3582 urlComponents
.dwPasswordLength
= 0;
3583 urlComponents
.lpszUrlPath
= path
;
3584 urlComponents
.dwUrlPathLength
= 2048;
3585 urlComponents
.lpszExtraInfo
= NULL
;
3586 urlComponents
.dwExtraInfoLength
= 0;
3587 if(!InternetCrackUrlW(lpszUrl
, strlenW(lpszUrl
), 0, &urlComponents
))
3588 return INTERNET_GetLastError();
3590 if (!strncmpW(szHttp
, urlComponents
.lpszScheme
, strlenW(szHttp
)) &&
3591 (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3593 TRACE("redirect from secure page to non-secure page\n");
3594 /* FIXME: warn about from secure redirect to non-secure page */
3595 request
->hdr
.dwFlags
&= ~INTERNET_FLAG_SECURE
;
3597 if (!strncmpW(szHttps
, urlComponents
.lpszScheme
, strlenW(szHttps
)) &&
3598 !(request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3600 TRACE("redirect from non-secure page to secure page\n");
3601 /* FIXME: notify about redirect to secure page */
3602 request
->hdr
.dwFlags
|= INTERNET_FLAG_SECURE
;
3605 if (urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3607 if (lstrlenW(protocol
)>4) /*https*/
3608 urlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
3610 urlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
3615 * This upsets redirects to binary files on sourceforge.net
3616 * and gives an html page instead of the target file
3617 * Examination of the HTTP request sent by native wininet.dll
3618 * reveals that it doesn't send a referrer in that case.
3619 * Maybe there's a flag that enables this, or maybe a referrer
3620 * shouldn't be added in case of a redirect.
3623 /* consider the current host as the referrer */
3624 if (session
->lpszServerName
&& *session
->lpszServerName
)
3625 HTTP_ProcessHeader(request
, HTTP_REFERER
, session
->lpszServerName
,
3626 HTTP_ADDHDR_FLAG_REQ
|HTTP_ADDREQ_FLAG_REPLACE
|
3627 HTTP_ADDHDR_FLAG_ADD_IF_NEW
);
3630 HeapFree(GetProcessHeap(), 0, session
->hostName
);
3631 if (urlComponents
.nPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
3632 urlComponents
.nPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3635 static const WCHAR fmt
[] = {'%','s',':','%','u',0};
3636 len
= lstrlenW(hostName
);
3637 len
+= 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3638 session
->hostName
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
3639 sprintfW(session
->hostName
, fmt
, hostName
, urlComponents
.nPort
);
3642 session
->hostName
= heap_strdupW(hostName
);
3644 HTTP_ProcessHeader(request
, hostW
, session
->hostName
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDREQ_FLAG_REPLACE
| HTTP_ADDHDR_FLAG_REQ
);
3646 HeapFree(GetProcessHeap(), 0, session
->userName
);
3647 session
->userName
= NULL
;
3649 session
->userName
= heap_strdupW(userName
);
3653 if (strcmpiW(session
->serverName
, hostName
) || session
->serverPort
!= urlComponents
.nPort
)
3657 HeapFree(GetProcessHeap(), 0, session
->serverName
);
3658 session
->serverName
= heap_strdupW(hostName
);
3659 session
->serverPort
= urlComponents
.nPort
;
3661 NETCON_close(&request
->netConnection
);
3662 if ((res
= HTTP_ResolveName(request
)) != ERROR_SUCCESS
)
3665 res
= NETCON_init(&request
->netConnection
, request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
);
3666 if (res
!= ERROR_SUCCESS
)
3669 reset_data_stream(request
);
3673 TRACE("Redirect through proxy\n");
3676 HeapFree(GetProcessHeap(), 0, request
->path
);
3683 rc
= UrlEscapeW(path
, NULL
, &needed
, URL_ESCAPE_SPACES_ONLY
);
3684 if (rc
!= E_POINTER
)
3685 needed
= strlenW(path
)+1;
3686 request
->path
= HeapAlloc(GetProcessHeap(), 0, needed
*sizeof(WCHAR
));
3687 rc
= UrlEscapeW(path
, request
->path
, &needed
,
3688 URL_ESCAPE_SPACES_ONLY
);
3691 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path
),rc
);
3692 strcpyW(request
->path
,path
);
3696 /* Remove custom content-type/length headers on redirects. */
3697 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Type
, 0, TRUE
);
3699 HTTP_DeleteCustomHeader(request
, index
);
3700 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Length
, 0, TRUE
);
3702 HTTP_DeleteCustomHeader(request
, index
);
3704 return ERROR_SUCCESS
;
3707 /***********************************************************************
3708 * HTTP_build_req (internal)
3710 * concatenate all the strings in the request together
3712 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
)
3717 for( t
= list
; *t
; t
++ )
3718 len
+= strlenW( *t
);
3721 str
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
3724 for( t
= list
; *t
; t
++ )
3730 static DWORD
HTTP_SecureProxyConnect(http_request_t
*request
)
3733 LPWSTR requestString
;
3739 static const WCHAR szConnect
[] = {'C','O','N','N','E','C','T',0};
3740 static const WCHAR szFormat
[] = {'%','s',':','%','u',0};
3741 http_session_t
*session
= request
->session
;
3745 lpszPath
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW( session
->hostName
) + 13)*sizeof(WCHAR
) );
3746 sprintfW( lpszPath
, szFormat
, session
->hostName
, session
->hostPort
);
3747 requestString
= HTTP_BuildHeaderRequestString( request
, szConnect
, lpszPath
, g_szHttp1_1
);
3748 HeapFree( GetProcessHeap(), 0, lpszPath
);
3750 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3751 NULL
, 0, NULL
, NULL
);
3752 len
--; /* the nul terminator isn't needed */
3753 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
);
3754 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3755 ascii_req
, len
, NULL
, NULL
);
3756 HeapFree( GetProcessHeap(), 0, requestString
);
3758 TRACE("full request -> %s\n", debugstr_an( ascii_req
, len
) );
3760 res
= NETCON_send( &request
->netConnection
, ascii_req
, len
, 0, &cnt
);
3761 HeapFree( GetProcessHeap(), 0, ascii_req
);
3762 if (res
!= ERROR_SUCCESS
)
3765 responseLen
= HTTP_GetResponseHeaders( request
, TRUE
);
3767 return ERROR_HTTP_INVALID_HEADER
;
3769 return ERROR_SUCCESS
;
3772 static void HTTP_InsertCookies(http_request_t
*request
)
3774 static const WCHAR szUrlForm
[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3775 LPWSTR lpszCookies
, lpszUrl
= NULL
;
3776 DWORD nCookieSize
, size
;
3777 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
3779 size
= (strlenW(Host
->lpszValue
) + strlenW(szUrlForm
) + strlenW(request
->path
)) * sizeof(WCHAR
);
3780 if (!(lpszUrl
= HeapAlloc(GetProcessHeap(), 0, size
))) return;
3781 sprintfW( lpszUrl
, szUrlForm
, Host
->lpszValue
, request
->path
);
3783 if (InternetGetCookieW(lpszUrl
, NULL
, NULL
, &nCookieSize
))
3786 static const WCHAR szCookie
[] = {'C','o','o','k','i','e',':',' ',0};
3788 size
= sizeof(szCookie
) + nCookieSize
* sizeof(WCHAR
) + sizeof(szCrLf
);
3789 if ((lpszCookies
= HeapAlloc(GetProcessHeap(), 0, size
)))
3791 cnt
+= sprintfW(lpszCookies
, szCookie
);
3792 InternetGetCookieW(lpszUrl
, NULL
, lpszCookies
+ cnt
, &nCookieSize
);
3793 strcatW(lpszCookies
, szCrLf
);
3795 HTTP_HttpAddRequestHeadersW(request
, lpszCookies
, strlenW(lpszCookies
), HTTP_ADDREQ_FLAG_REPLACE
);
3796 HeapFree(GetProcessHeap(), 0, lpszCookies
);
3799 HeapFree(GetProcessHeap(), 0, lpszUrl
);
3802 static WORD
HTTP_ParseDay(LPCWSTR day
)
3804 static const WCHAR days
[7][4] = {{ 's','u','n',0 },
3812 for (i
= 0; i
< sizeof(days
)/sizeof(*days
); i
++)
3813 if (!strcmpiW(day
, days
[i
]))
3820 static WORD
HTTP_ParseMonth(LPCWSTR month
)
3822 static const WCHAR jan
[] = { 'j','a','n',0 };
3823 static const WCHAR feb
[] = { 'f','e','b',0 };
3824 static const WCHAR mar
[] = { 'm','a','r',0 };
3825 static const WCHAR apr
[] = { 'a','p','r',0 };
3826 static const WCHAR may
[] = { 'm','a','y',0 };
3827 static const WCHAR jun
[] = { 'j','u','n',0 };
3828 static const WCHAR jul
[] = { 'j','u','l',0 };
3829 static const WCHAR aug
[] = { 'a','u','g',0 };
3830 static const WCHAR sep
[] = { 's','e','p',0 };
3831 static const WCHAR oct
[] = { 'o','c','t',0 };
3832 static const WCHAR nov
[] = { 'n','o','v',0 };
3833 static const WCHAR dec
[] = { 'd','e','c',0 };
3835 if (!strcmpiW(month
, jan
)) return 1;
3836 if (!strcmpiW(month
, feb
)) return 2;
3837 if (!strcmpiW(month
, mar
)) return 3;
3838 if (!strcmpiW(month
, apr
)) return 4;
3839 if (!strcmpiW(month
, may
)) return 5;
3840 if (!strcmpiW(month
, jun
)) return 6;
3841 if (!strcmpiW(month
, jul
)) return 7;
3842 if (!strcmpiW(month
, aug
)) return 8;
3843 if (!strcmpiW(month
, sep
)) return 9;
3844 if (!strcmpiW(month
, oct
)) return 10;
3845 if (!strcmpiW(month
, nov
)) return 11;
3846 if (!strcmpiW(month
, dec
)) return 12;
3851 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
3852 * optionally preceded by whitespace.
3853 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
3854 * st, and sets *str to the first character after the time format.
3856 static BOOL
HTTP_ParseTime(SYSTEMTIME
*st
, LPCWSTR
*str
)
3862 while (isspaceW(*ptr
))
3865 num
= strtoulW(ptr
, &nextPtr
, 10);
3866 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
3868 ERR("unexpected time format %s\n", debugstr_w(ptr
));
3873 ERR("unexpected hour in time format %s\n", debugstr_w(ptr
));
3877 st
->wHour
= (WORD
)num
;
3878 num
= strtoulW(ptr
, &nextPtr
, 10);
3879 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
3881 ERR("unexpected time format %s\n", debugstr_w(ptr
));
3886 ERR("unexpected minute in time format %s\n", debugstr_w(ptr
));
3890 st
->wMinute
= (WORD
)num
;
3891 num
= strtoulW(ptr
, &nextPtr
, 10);
3892 if (!nextPtr
|| nextPtr
<= ptr
)
3894 ERR("unexpected time format %s\n", debugstr_w(ptr
));
3899 ERR("unexpected second in time format %s\n", debugstr_w(ptr
));
3904 st
->wSecond
= (WORD
)num
;
3908 static BOOL
HTTP_ParseDateAsAsctime(LPCWSTR value
, FILETIME
*ft
)
3910 static const WCHAR gmt
[]= { 'G','M','T',0 };
3911 WCHAR day
[4], *dayPtr
, month
[4], *monthPtr
, *nextPtr
;
3913 SYSTEMTIME st
= { 0 };
3916 for (ptr
= value
, dayPtr
= day
; *ptr
&& !isspaceW(*ptr
) &&
3917 dayPtr
- day
< sizeof(day
) / sizeof(day
[0]) - 1; ptr
++, dayPtr
++)
3920 st
.wDayOfWeek
= HTTP_ParseDay(day
);
3921 if (st
.wDayOfWeek
>= 7)
3923 ERR("unexpected weekday %s\n", debugstr_w(day
));
3927 while (isspaceW(*ptr
))
3930 for (monthPtr
= month
; !isspace(*ptr
) &&
3931 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
3935 st
.wMonth
= HTTP_ParseMonth(month
);
3936 if (!st
.wMonth
|| st
.wMonth
> 12)
3938 ERR("unexpected month %s\n", debugstr_w(month
));
3942 while (isspaceW(*ptr
))
3945 num
= strtoulW(ptr
, &nextPtr
, 10);
3946 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
3948 ERR("unexpected day %s\n", debugstr_w(ptr
));
3952 st
.wDay
= (WORD
)num
;
3954 while (isspaceW(*ptr
))
3957 if (!HTTP_ParseTime(&st
, &ptr
))
3960 while (isspaceW(*ptr
))
3963 num
= strtoulW(ptr
, &nextPtr
, 10);
3964 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
3966 ERR("unexpected year %s\n", debugstr_w(ptr
));
3970 st
.wYear
= (WORD
)num
;
3972 while (isspaceW(*ptr
))
3975 /* asctime() doesn't report a timezone, but some web servers do, so accept
3976 * with or without GMT.
3978 if (*ptr
&& strcmpW(ptr
, gmt
))
3980 ERR("unexpected timezone %s\n", debugstr_w(ptr
));
3983 return SystemTimeToFileTime(&st
, ft
);
3986 static BOOL
HTTP_ParseRfc1123Date(LPCWSTR value
, FILETIME
*ft
)
3988 static const WCHAR gmt
[]= { 'G','M','T',0 };
3989 WCHAR
*nextPtr
, day
[4], month
[4], *monthPtr
;
3992 SYSTEMTIME st
= { 0 };
3994 ptr
= strchrW(value
, ',');
3997 if (ptr
- value
!= 3)
3999 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4002 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4004 st
.wDayOfWeek
= HTTP_ParseDay(day
);
4005 if (st
.wDayOfWeek
> 6)
4007 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4012 while (isspaceW(*ptr
))
4015 num
= strtoulW(ptr
, &nextPtr
, 10);
4016 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4018 ERR("unexpected day %s\n", debugstr_w(value
));
4022 st
.wDay
= (WORD
)num
;
4024 while (isspaceW(*ptr
))
4027 for (monthPtr
= month
; !isspace(*ptr
) &&
4028 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4032 st
.wMonth
= HTTP_ParseMonth(month
);
4033 if (!st
.wMonth
|| st
.wMonth
> 12)
4035 ERR("unexpected month %s\n", debugstr_w(month
));
4039 while (isspaceW(*ptr
))
4042 num
= strtoulW(ptr
, &nextPtr
, 10);
4043 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4045 ERR("unexpected year %s\n", debugstr_w(value
));
4049 st
.wYear
= (WORD
)num
;
4051 if (!HTTP_ParseTime(&st
, &ptr
))
4054 while (isspaceW(*ptr
))
4057 if (strcmpW(ptr
, gmt
))
4059 ERR("unexpected time zone %s\n", debugstr_w(ptr
));
4062 return SystemTimeToFileTime(&st
, ft
);
4065 /* FIXME: only accepts dates in RFC 1123 format and asctime() format,
4066 * which may not be the only formats actually seen in the wild.
4067 * http://www.hackcraft.net/web/datetime/ suggests at least RFC 850 dates
4068 * should be accepted as well.
4070 static BOOL
HTTP_ParseDate(LPCWSTR value
, FILETIME
*ft
)
4074 if (strchrW(value
, ','))
4075 ret
= HTTP_ParseRfc1123Date(value
, ft
);
4078 ret
= HTTP_ParseDateAsAsctime(value
, ft
);
4080 ERR("unexpected date format %s\n", debugstr_w(value
));
4085 static void HTTP_ProcessExpires(http_request_t
*request
)
4087 BOOL expirationFound
= FALSE
;
4090 /* Look for a Cache-Control header with a max-age directive, as it takes
4091 * precedence over the Expires header.
4093 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szCache_Control
, 0, FALSE
);
4094 if (headerIndex
!= -1)
4096 LPHTTPHEADERW ccHeader
= &request
->custHeaders
[headerIndex
];
4099 for (ptr
= ccHeader
->lpszValue
; ptr
&& *ptr
; )
4101 LPWSTR comma
= strchrW(ptr
, ','), end
, equal
;
4106 end
= ptr
+ strlenW(ptr
);
4107 for (equal
= end
- 1; equal
> ptr
&& *equal
!= '='; equal
--)
4111 static const WCHAR max_age
[] = {
4112 'm','a','x','-','a','g','e',0 };
4114 if (!strncmpiW(ptr
, max_age
, equal
- ptr
- 1))
4119 age
= strtoulW(equal
+ 1, &nextPtr
, 10);
4120 if (nextPtr
> equal
+ 1)
4124 NtQuerySystemTime( &ft
);
4125 /* Age is in seconds, FILETIME resolution is in
4126 * 100 nanosecond intervals.
4128 ft
.QuadPart
+= age
* (ULONGLONG
)1000000;
4129 request
->expires
.dwLowDateTime
= ft
.u
.LowPart
;
4130 request
->expires
.dwHighDateTime
= ft
.u
.HighPart
;
4131 expirationFound
= TRUE
;
4138 while (isspaceW(*ptr
))
4145 if (!expirationFound
)
4147 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szExpires
, 0, FALSE
);
4148 if (headerIndex
!= -1)
4150 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4153 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4155 expirationFound
= TRUE
;
4156 request
->expires
= ft
;
4160 if (!expirationFound
)
4164 /* With no known age, default to 10 minutes until expiration. */
4165 NtQuerySystemTime( &t
);
4166 t
.QuadPart
+= 10 * 60 * (ULONGLONG
)10000000;
4167 request
->expires
.dwLowDateTime
= t
.u
.LowPart
;
4168 request
->expires
.dwHighDateTime
= t
.u
.HighPart
;
4172 static void HTTP_ProcessLastModified(http_request_t
*request
)
4176 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szLast_Modified
, 0, FALSE
);
4177 if (headerIndex
!= -1)
4179 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4182 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4183 request
->last_modified
= ft
;
4187 static void HTTP_CacheRequest(http_request_t
*request
)
4189 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
4190 WCHAR cacheFileName
[MAX_PATH
+1];
4193 b
= HTTP_GetRequestURL(request
, url
);
4195 WARN("Could not get URL\n");
4199 b
= CreateUrlCacheEntryW(url
, request
->contentLength
, NULL
, cacheFileName
, 0);
4201 HeapFree(GetProcessHeap(), 0, request
->cacheFile
);
4202 CloseHandle(request
->hCacheFile
);
4204 request
->cacheFile
= heap_strdupW(cacheFileName
);
4205 request
->hCacheFile
= CreateFileW(request
->cacheFile
, GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
4206 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
4207 if(request
->hCacheFile
== INVALID_HANDLE_VALUE
) {
4208 WARN("Could not create file: %u\n", GetLastError());
4209 request
->hCacheFile
= NULL
;
4212 WARN("Could not create cache entry: %08x\n", GetLastError());
4216 /***********************************************************************
4217 * HTTP_HttpSendRequestW (internal)
4219 * Sends the specified request to the HTTP server
4222 * ERROR_SUCCESS on success
4223 * win32 error code on failure
4226 static DWORD
HTTP_HttpSendRequestW(http_request_t
*request
, LPCWSTR lpszHeaders
,
4227 DWORD dwHeaderLength
, LPVOID lpOptional
, DWORD dwOptionalLength
,
4228 DWORD dwContentLength
, BOOL bEndRequest
)
4231 BOOL redirected
= FALSE
;
4232 LPWSTR requestString
= NULL
;
4235 INTERNET_ASYNC_RESULT iar
;
4236 static const WCHAR szPost
[] = { 'P','O','S','T',0 };
4237 static const WCHAR szContentLength
[] =
4238 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4239 WCHAR contentLengthStr
[sizeof szContentLength
/2 /* includes \r\n */ + 20 /* int */ ];
4242 TRACE("--> %p\n", request
);
4244 assert(request
->hdr
.htype
== WH_HHTTPREQ
);
4246 /* if the verb is NULL default to GET */
4248 request
->verb
= heap_strdupW(szGET
);
4250 if (dwContentLength
|| strcmpW(request
->verb
, szGET
))
4252 sprintfW(contentLengthStr
, szContentLength
, dwContentLength
);
4253 HTTP_HttpAddRequestHeadersW(request
, contentLengthStr
, -1L, HTTP_ADDREQ_FLAG_REPLACE
);
4254 request
->bytesToWrite
= dwContentLength
;
4256 if (request
->session
->appInfo
->agent
)
4258 WCHAR
*agent_header
;
4259 static const WCHAR user_agent
[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4262 len
= strlenW(request
->session
->appInfo
->agent
) + strlenW(user_agent
);
4263 agent_header
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
4264 sprintfW(agent_header
, user_agent
, request
->session
->appInfo
->agent
);
4266 HTTP_HttpAddRequestHeadersW(request
, agent_header
, strlenW(agent_header
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4267 HeapFree(GetProcessHeap(), 0, agent_header
);
4269 if (request
->hdr
.dwFlags
& INTERNET_FLAG_PRAGMA_NOCACHE
)
4271 static const WCHAR pragma_nocache
[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4272 HTTP_HttpAddRequestHeadersW(request
, pragma_nocache
, strlenW(pragma_nocache
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4274 if ((request
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
) && !strcmpW(request
->verb
, szPost
))
4276 static const WCHAR cache_control
[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4277 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4278 HTTP_HttpAddRequestHeadersW(request
, cache_control
, strlenW(cache_control
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4284 BOOL reusing_connection
;
4289 /* like native, just in case the caller forgot to call InternetReadFile
4290 * for all the data */
4291 HTTP_DrainContent(request
);
4293 request
->contentLength
= ~0u;
4294 request
->bytesToWrite
= 0;
4297 if (TRACE_ON(wininet
))
4299 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4300 TRACE("Going to url %s %s\n", debugstr_w(Host
->lpszValue
), debugstr_w(request
->path
));
4303 HTTP_FixURL(request
);
4304 if (request
->hdr
.dwFlags
& INTERNET_FLAG_KEEP_CONNECTION
)
4306 HTTP_ProcessHeader(request
, szConnection
, szKeepAlive
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
4308 HTTP_InsertAuthorization(request
, request
->authInfo
, szAuthorization
);
4309 HTTP_InsertAuthorization(request
, request
->proxyAuthInfo
, szProxy_Authorization
);
4311 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
))
4312 HTTP_InsertCookies(request
);
4314 /* add the headers the caller supplied */
4315 if( lpszHeaders
&& dwHeaderLength
)
4317 HTTP_HttpAddRequestHeadersW(request
, lpszHeaders
, dwHeaderLength
,
4318 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REPLACE
);
4321 if (request
->session
->appInfo
->proxy
&& request
->session
->appInfo
->proxy
[0])
4323 WCHAR
*url
= HTTP_BuildProxyRequestUrl(request
);
4324 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, url
, request
->version
);
4325 HeapFree(GetProcessHeap(), 0, url
);
4328 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
4331 TRACE("Request header -> %s\n", debugstr_w(requestString
) );
4333 /* Send the request and store the results */
4334 if(NETCON_connected(&request
->netConnection
))
4335 reusing_connection
= TRUE
;
4337 reusing_connection
= FALSE
;
4339 if ((res
= HTTP_OpenConnection(request
)) != ERROR_SUCCESS
)
4342 /* send the request as ASCII, tack on the optional data */
4343 if (!lpOptional
|| redirected
)
4344 dwOptionalLength
= 0;
4345 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4346 NULL
, 0, NULL
, NULL
);
4347 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
+ dwOptionalLength
);
4348 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4349 ascii_req
, len
, NULL
, NULL
);
4351 memcpy( &ascii_req
[len
-1], lpOptional
, dwOptionalLength
);
4352 len
= (len
+ dwOptionalLength
- 1);
4354 TRACE("full request -> %s\n", debugstr_a(ascii_req
) );
4356 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4357 INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
4359 res
= NETCON_send(&request
->netConnection
, ascii_req
, len
, 0, &cnt
);
4360 HeapFree( GetProcessHeap(), 0, ascii_req
);
4362 request
->bytesWritten
= dwOptionalLength
;
4364 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4365 INTERNET_STATUS_REQUEST_SENT
,
4366 &len
, sizeof(DWORD
));
4373 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4374 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
4376 if (res
!= ERROR_SUCCESS
)
4379 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
4380 /* FIXME: We should know that connection is closed before sending
4381 * headers. Otherwise wrong callbacks are executed */
4382 if(!responseLen
&& reusing_connection
) {
4383 TRACE("Connection closed by server, reconnecting\n");
4388 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4389 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
,
4392 HTTP_ProcessCookies(request
);
4393 HTTP_ProcessExpires(request
);
4394 HTTP_ProcessLastModified(request
);
4396 res
= set_content_length(request
);
4397 if(res
!= ERROR_SUCCESS
)
4399 if(!request
->contentLength
)
4400 HTTP_FinishedReading(request
);
4402 dwBufferSize
= sizeof(dwStatusCode
);
4403 if (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
,
4404 &dwStatusCode
,&dwBufferSize
,NULL
) != ERROR_SUCCESS
)
4407 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
) && responseLen
)
4409 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
4410 dwBufferSize
=sizeof(szNewLocation
);
4411 if ((dwStatusCode
== HTTP_STATUS_REDIRECT
||
4412 dwStatusCode
== HTTP_STATUS_MOVED
||
4413 dwStatusCode
== HTTP_STATUS_REDIRECT_METHOD
) &&
4414 HTTP_HttpQueryInfoW(request
,HTTP_QUERY_LOCATION
,szNewLocation
,&dwBufferSize
,NULL
) == ERROR_SUCCESS
)
4416 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
))
4418 HeapFree(GetProcessHeap(), 0, request
->verb
);
4419 request
->verb
= heap_strdupW(szGET
);
4421 HTTP_DrainContent(request
);
4422 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
4424 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
4425 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
4426 res
= HTTP_HandleRedirect(request
, new_url
);
4427 if (res
== ERROR_SUCCESS
)
4429 HeapFree(GetProcessHeap(), 0, requestString
);
4432 HeapFree( GetProcessHeap(), 0, new_url
);
4437 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTH
) && res
== ERROR_SUCCESS
)
4439 WCHAR szAuthValue
[2048];
4441 if (dwStatusCode
== HTTP_STATUS_DENIED
)
4443 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4445 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_WWW_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4447 if (HTTP_DoAuthorization(request
, szAuthValue
,
4449 request
->session
->userName
,
4450 request
->session
->password
,
4453 HeapFree(GetProcessHeap(), 0, requestString
);
4460 TRACE("Cleaning wrong authorization data\n");
4461 destroy_authinfo(request
->authInfo
);
4462 request
->authInfo
= NULL
;
4465 if (dwStatusCode
== HTTP_STATUS_PROXY_AUTH_REQ
)
4468 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_PROXY_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4470 if (HTTP_DoAuthorization(request
, szAuthValue
,
4471 &request
->proxyAuthInfo
,
4472 request
->session
->appInfo
->proxyUsername
,
4473 request
->session
->appInfo
->proxyPassword
,
4482 TRACE("Cleaning wrong proxy authorization data\n");
4483 destroy_authinfo(request
->proxyAuthInfo
);
4484 request
->proxyAuthInfo
= NULL
;
4490 res
= ERROR_SUCCESS
;
4494 if(res
== ERROR_SUCCESS
)
4495 HTTP_CacheRequest(request
);
4499 HeapFree(GetProcessHeap(), 0, requestString
);
4501 /* TODO: send notification for P3P header */
4503 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4505 if (res
== ERROR_SUCCESS
&& request
->bytesWritten
== request
->bytesToWrite
)
4506 HTTP_ReceiveRequestData(request
, TRUE
);
4509 iar
.dwResult
= (res
==ERROR_SUCCESS
? (DWORD_PTR
)request
->hdr
.hInternet
: 0);
4512 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4513 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
4514 sizeof(INTERNET_ASYNC_RESULT
));
4522 /***********************************************************************
4524 * Helper functions for the HttpSendRequest(Ex) functions
4527 static void AsyncHttpSendRequestProc(WORKREQUEST
*workRequest
)
4529 struct WORKREQ_HTTPSENDREQUESTW
const *req
= &workRequest
->u
.HttpSendRequestW
;
4530 http_request_t
*request
= (http_request_t
*) workRequest
->hdr
;
4532 TRACE("%p\n", request
);
4534 HTTP_HttpSendRequestW(request
, req
->lpszHeader
,
4535 req
->dwHeaderLength
, req
->lpOptional
, req
->dwOptionalLength
,
4536 req
->dwContentLength
, req
->bEndRequest
);
4538 HeapFree(GetProcessHeap(), 0, req
->lpszHeader
);
4542 static DWORD
HTTP_HttpEndRequestW(http_request_t
*request
, DWORD dwFlags
, DWORD_PTR dwContext
)
4546 DWORD res
= ERROR_SUCCESS
;
4548 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4549 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
4551 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
4553 res
= ERROR_HTTP_HEADER_NOT_FOUND
;
4555 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4556 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
, sizeof(DWORD
));
4558 /* process cookies here. Is this right? */
4559 HTTP_ProcessCookies(request
);
4560 HTTP_ProcessExpires(request
);
4561 HTTP_ProcessLastModified(request
);
4563 if ((res
= set_content_length( request
)) == ERROR_SUCCESS
) {
4564 if(!request
->contentLength
)
4565 HTTP_FinishedReading(request
);
4568 if (res
== ERROR_SUCCESS
&& !(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
))
4570 DWORD dwCode
,dwCodeLength
= sizeof(DWORD
);
4571 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
, &dwCode
, &dwCodeLength
, NULL
) == ERROR_SUCCESS
4572 && (dwCode
== 302 || dwCode
== 301 || dwCode
== 303))
4574 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
4575 dwBufferSize
=sizeof(szNewLocation
);
4576 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_LOCATION
, szNewLocation
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
4578 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
))
4580 HeapFree(GetProcessHeap(), 0, request
->verb
);
4581 request
->verb
= heap_strdupW(szGET
);
4583 HTTP_DrainContent(request
);
4584 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
4586 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
4587 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
4588 res
= HTTP_HandleRedirect(request
, new_url
);
4589 if (res
== ERROR_SUCCESS
)
4590 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, TRUE
);
4591 HeapFree( GetProcessHeap(), 0, new_url
);
4597 if (res
== ERROR_SUCCESS
) {
4598 HTTP_ReceiveRequestData(request
, TRUE
);
4600 INTERNET_ASYNC_RESULT iar
= {0, res
};
4602 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4603 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
4604 sizeof(INTERNET_ASYNC_RESULT
));
4610 /***********************************************************************
4611 * HttpEndRequestA (WININET.@)
4613 * Ends an HTTP request that was started by HttpSendRequestEx
4616 * TRUE if successful
4620 BOOL WINAPI
HttpEndRequestA(HINTERNET hRequest
,
4621 LPINTERNET_BUFFERSA lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
4623 TRACE("(%p, %p, %08x, %08lx)\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
4627 SetLastError(ERROR_INVALID_PARAMETER
);
4631 return HttpEndRequestW(hRequest
, NULL
, dwFlags
, dwContext
);
4634 static void AsyncHttpEndRequestProc(WORKREQUEST
*work
)
4636 struct WORKREQ_HTTPENDREQUESTW
const *req
= &work
->u
.HttpEndRequestW
;
4637 http_request_t
*request
= (http_request_t
*)work
->hdr
;
4639 TRACE("%p\n", request
);
4641 HTTP_HttpEndRequestW(request
, req
->dwFlags
, req
->dwContext
);
4644 /***********************************************************************
4645 * HttpEndRequestW (WININET.@)
4647 * Ends an HTTP request that was started by HttpSendRequestEx
4650 * TRUE if successful
4654 BOOL WINAPI
HttpEndRequestW(HINTERNET hRequest
,
4655 LPINTERNET_BUFFERSW lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
4657 http_request_t
*request
;
4664 SetLastError(ERROR_INVALID_PARAMETER
);
4668 request
= (http_request_t
*) get_handle_object( hRequest
);
4670 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
4672 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
4674 WININET_Release( &request
->hdr
);
4677 request
->hdr
.dwFlags
|= dwFlags
;
4679 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4682 struct WORKREQ_HTTPENDREQUESTW
*work_endrequest
;
4684 work
.asyncproc
= AsyncHttpEndRequestProc
;
4685 work
.hdr
= WININET_AddRef( &request
->hdr
);
4687 work_endrequest
= &work
.u
.HttpEndRequestW
;
4688 work_endrequest
->dwFlags
= dwFlags
;
4689 work_endrequest
->dwContext
= dwContext
;
4691 INTERNET_AsyncCall(&work
);
4692 res
= ERROR_IO_PENDING
;
4695 res
= HTTP_HttpEndRequestW(request
, dwFlags
, dwContext
);
4697 WININET_Release( &request
->hdr
);
4698 TRACE("%u <--\n", res
);
4699 if(res
!= ERROR_SUCCESS
)
4701 return res
== ERROR_SUCCESS
;
4704 /***********************************************************************
4705 * HttpSendRequestExA (WININET.@)
4707 * Sends the specified request to the HTTP server and allows chunked
4712 * Failure: FALSE, call GetLastError() for more information.
4714 BOOL WINAPI
HttpSendRequestExA(HINTERNET hRequest
,
4715 LPINTERNET_BUFFERSA lpBuffersIn
,
4716 LPINTERNET_BUFFERSA lpBuffersOut
,
4717 DWORD dwFlags
, DWORD_PTR dwContext
)
4719 INTERNET_BUFFERSW BuffersInW
;
4722 LPWSTR header
= NULL
;
4724 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
4725 lpBuffersOut
, dwFlags
, dwContext
);
4729 BuffersInW
.dwStructSize
= sizeof(LPINTERNET_BUFFERSW
);
4730 if (lpBuffersIn
->lpcszHeader
)
4732 headerlen
= MultiByteToWideChar(CP_ACP
,0,lpBuffersIn
->lpcszHeader
,
4733 lpBuffersIn
->dwHeadersLength
,0,0);
4734 header
= HeapAlloc(GetProcessHeap(),0,headerlen
*sizeof(WCHAR
));
4735 if (!(BuffersInW
.lpcszHeader
= header
))
4737 SetLastError(ERROR_OUTOFMEMORY
);
4740 BuffersInW
.dwHeadersLength
= MultiByteToWideChar(CP_ACP
, 0,
4741 lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
4745 BuffersInW
.lpcszHeader
= NULL
;
4746 BuffersInW
.dwHeadersTotal
= lpBuffersIn
->dwHeadersTotal
;
4747 BuffersInW
.lpvBuffer
= lpBuffersIn
->lpvBuffer
;
4748 BuffersInW
.dwBufferLength
= lpBuffersIn
->dwBufferLength
;
4749 BuffersInW
.dwBufferTotal
= lpBuffersIn
->dwBufferTotal
;
4750 BuffersInW
.Next
= NULL
;
4753 rc
= HttpSendRequestExW(hRequest
, lpBuffersIn
? &BuffersInW
: NULL
, NULL
, dwFlags
, dwContext
);
4755 HeapFree(GetProcessHeap(),0,header
);
4760 /***********************************************************************
4761 * HttpSendRequestExW (WININET.@)
4763 * Sends the specified request to the HTTP server and allows chunked
4768 * Failure: FALSE, call GetLastError() for more information.
4770 BOOL WINAPI
HttpSendRequestExW(HINTERNET hRequest
,
4771 LPINTERNET_BUFFERSW lpBuffersIn
,
4772 LPINTERNET_BUFFERSW lpBuffersOut
,
4773 DWORD dwFlags
, DWORD_PTR dwContext
)
4775 http_request_t
*request
;
4776 http_session_t
*session
;
4780 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
4781 lpBuffersOut
, dwFlags
, dwContext
);
4783 request
= (http_request_t
*) get_handle_object( hRequest
);
4785 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
4787 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4791 session
= request
->session
;
4792 assert(session
->hdr
.htype
== WH_HHTTPSESSION
);
4793 hIC
= session
->appInfo
;
4794 assert(hIC
->hdr
.htype
== WH_HINIT
);
4796 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4798 WORKREQUEST workRequest
;
4799 struct WORKREQ_HTTPSENDREQUESTW
*req
;
4801 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
4802 workRequest
.hdr
= WININET_AddRef( &request
->hdr
);
4803 req
= &workRequest
.u
.HttpSendRequestW
;
4808 if (lpBuffersIn
->lpcszHeader
)
4810 if (lpBuffersIn
->dwHeadersLength
== ~0u)
4811 size
= (strlenW( lpBuffersIn
->lpcszHeader
) + 1) * sizeof(WCHAR
);
4813 size
= lpBuffersIn
->dwHeadersLength
* sizeof(WCHAR
);
4815 req
->lpszHeader
= HeapAlloc( GetProcessHeap(), 0, size
);
4816 memcpy( req
->lpszHeader
, lpBuffersIn
->lpcszHeader
, size
);
4818 else req
->lpszHeader
= NULL
;
4820 req
->dwHeaderLength
= size
/ sizeof(WCHAR
);
4821 req
->lpOptional
= lpBuffersIn
->lpvBuffer
;
4822 req
->dwOptionalLength
= lpBuffersIn
->dwBufferLength
;
4823 req
->dwContentLength
= lpBuffersIn
->dwBufferTotal
;
4827 req
->lpszHeader
= NULL
;
4828 req
->dwHeaderLength
= 0;
4829 req
->lpOptional
= NULL
;
4830 req
->dwOptionalLength
= 0;
4831 req
->dwContentLength
= 0;
4834 req
->bEndRequest
= FALSE
;
4836 INTERNET_AsyncCall(&workRequest
);
4838 * This is from windows.
4840 res
= ERROR_IO_PENDING
;
4845 res
= HTTP_HttpSendRequestW(request
, lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
4846 lpBuffersIn
->lpvBuffer
, lpBuffersIn
->dwBufferLength
,
4847 lpBuffersIn
->dwBufferTotal
, FALSE
);
4849 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, FALSE
);
4854 WININET_Release( &request
->hdr
);
4858 return res
== ERROR_SUCCESS
;
4861 /***********************************************************************
4862 * HttpSendRequestW (WININET.@)
4864 * Sends the specified request to the HTTP server
4871 BOOL WINAPI
HttpSendRequestW(HINTERNET hHttpRequest
, LPCWSTR lpszHeaders
,
4872 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
4874 http_request_t
*request
;
4875 http_session_t
*session
= NULL
;
4876 appinfo_t
*hIC
= NULL
;
4877 DWORD res
= ERROR_SUCCESS
;
4879 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest
,
4880 debugstr_wn(lpszHeaders
, dwHeaderLength
), dwHeaderLength
, lpOptional
, dwOptionalLength
);
4882 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
4883 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
4885 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4889 session
= request
->session
;
4890 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
4892 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4896 hIC
= session
->appInfo
;
4897 if (NULL
== hIC
|| hIC
->hdr
.htype
!= WH_HINIT
)
4899 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4903 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4905 WORKREQUEST workRequest
;
4906 struct WORKREQ_HTTPSENDREQUESTW
*req
;
4908 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
4909 workRequest
.hdr
= WININET_AddRef( &request
->hdr
);
4910 req
= &workRequest
.u
.HttpSendRequestW
;
4915 if (dwHeaderLength
== ~0u) size
= (strlenW(lpszHeaders
) + 1) * sizeof(WCHAR
);
4916 else size
= dwHeaderLength
* sizeof(WCHAR
);
4918 req
->lpszHeader
= HeapAlloc(GetProcessHeap(), 0, size
);
4919 memcpy(req
->lpszHeader
, lpszHeaders
, size
);
4922 req
->lpszHeader
= 0;
4923 req
->dwHeaderLength
= dwHeaderLength
;
4924 req
->lpOptional
= lpOptional
;
4925 req
->dwOptionalLength
= dwOptionalLength
;
4926 req
->dwContentLength
= dwOptionalLength
;
4927 req
->bEndRequest
= TRUE
;
4929 INTERNET_AsyncCall(&workRequest
);
4931 * This is from windows.
4933 res
= ERROR_IO_PENDING
;
4937 res
= HTTP_HttpSendRequestW(request
, lpszHeaders
,
4938 dwHeaderLength
, lpOptional
, dwOptionalLength
,
4939 dwOptionalLength
, TRUE
);
4943 WININET_Release( &request
->hdr
);
4946 return res
== ERROR_SUCCESS
;
4949 /***********************************************************************
4950 * HttpSendRequestA (WININET.@)
4952 * Sends the specified request to the HTTP server
4959 BOOL WINAPI
HttpSendRequestA(HINTERNET hHttpRequest
, LPCSTR lpszHeaders
,
4960 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
4963 LPWSTR szHeaders
=NULL
;
4964 DWORD nLen
=dwHeaderLength
;
4965 if(lpszHeaders
!=NULL
)
4967 nLen
=MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,NULL
,0);
4968 szHeaders
=HeapAlloc(GetProcessHeap(),0,nLen
*sizeof(WCHAR
));
4969 MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,szHeaders
,nLen
);
4971 result
=HttpSendRequestW(hHttpRequest
, szHeaders
, nLen
, lpOptional
, dwOptionalLength
);
4972 HeapFree(GetProcessHeap(),0,szHeaders
);
4976 /***********************************************************************
4977 * HTTPSESSION_Destroy (internal)
4979 * Deallocate session handle
4982 static void HTTPSESSION_Destroy(object_header_t
*hdr
)
4984 http_session_t
*session
= (http_session_t
*) hdr
;
4986 TRACE("%p\n", session
);
4988 WININET_Release(&session
->appInfo
->hdr
);
4990 HeapFree(GetProcessHeap(), 0, session
->hostName
);
4991 HeapFree(GetProcessHeap(), 0, session
->serverName
);
4992 HeapFree(GetProcessHeap(), 0, session
->password
);
4993 HeapFree(GetProcessHeap(), 0, session
->userName
);
4996 static DWORD
HTTPSESSION_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
4999 case INTERNET_OPTION_HANDLE_TYPE
:
5000 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5002 if (*size
< sizeof(ULONG
))
5003 return ERROR_INSUFFICIENT_BUFFER
;
5005 *size
= sizeof(DWORD
);
5006 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_CONNECT_HTTP
;
5007 return ERROR_SUCCESS
;
5010 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
5013 static DWORD
HTTPSESSION_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
5015 http_session_t
*ses
= (http_session_t
*)hdr
;
5018 case INTERNET_OPTION_USERNAME
:
5020 HeapFree(GetProcessHeap(), 0, ses
->userName
);
5021 if (!(ses
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5022 return ERROR_SUCCESS
;
5024 case INTERNET_OPTION_PASSWORD
:
5026 HeapFree(GetProcessHeap(), 0, ses
->password
);
5027 if (!(ses
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5028 return ERROR_SUCCESS
;
5033 return ERROR_INTERNET_INVALID_OPTION
;
5036 static const object_vtbl_t HTTPSESSIONVtbl
= {
5037 HTTPSESSION_Destroy
,
5039 HTTPSESSION_QueryOption
,
5040 HTTPSESSION_SetOption
,
5049 /***********************************************************************
5050 * HTTP_Connect (internal)
5052 * Create http session handle
5055 * HINTERNET a session handle on success
5059 DWORD
HTTP_Connect(appinfo_t
*hIC
, LPCWSTR lpszServerName
,
5060 INTERNET_PORT serverPort
, LPCWSTR lpszUserName
,
5061 LPCWSTR lpszPassword
, DWORD dwFlags
, DWORD_PTR dwContext
,
5062 DWORD dwInternalFlags
, HINTERNET
*ret
)
5064 http_session_t
*session
= NULL
;
5068 if (!lpszServerName
|| !lpszServerName
[0])
5069 return ERROR_INVALID_PARAMETER
;
5071 assert( hIC
->hdr
.htype
== WH_HINIT
);
5073 session
= alloc_object(&hIC
->hdr
, &HTTPSESSIONVtbl
, sizeof(http_session_t
));
5075 return ERROR_OUTOFMEMORY
;
5078 * According to my tests. The name is not resolved until a request is sent
5081 session
->hdr
.htype
= WH_HHTTPSESSION
;
5082 session
->hdr
.dwFlags
= dwFlags
;
5083 session
->hdr
.dwContext
= dwContext
;
5084 session
->hdr
.dwInternalFlags
|= dwInternalFlags
;
5086 WININET_AddRef( &hIC
->hdr
);
5087 session
->appInfo
= hIC
;
5088 list_add_head( &hIC
->hdr
.children
, &session
->hdr
.entry
);
5090 if(hIC
->proxy
&& hIC
->accessType
== INTERNET_OPEN_TYPE_PROXY
) {
5091 if(hIC
->proxyBypass
)
5092 FIXME("Proxy bypass is ignored.\n");
5094 session
->serverName
= heap_strdupW(lpszServerName
);
5095 session
->hostName
= heap_strdupW(lpszServerName
);
5096 if (lpszUserName
&& lpszUserName
[0])
5097 session
->userName
= heap_strdupW(lpszUserName
);
5098 if (lpszPassword
&& lpszPassword
[0])
5099 session
->password
= heap_strdupW(lpszPassword
);
5100 session
->serverPort
= serverPort
;
5101 session
->hostPort
= serverPort
;
5103 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5104 if (!(session
->hdr
.dwInternalFlags
& INET_OPENURL
))
5106 INTERNET_SendCallback(&hIC
->hdr
, dwContext
,
5107 INTERNET_STATUS_HANDLE_CREATED
, &session
->hdr
.hInternet
,
5112 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5116 TRACE("%p --> %p\n", hIC
, session
);
5118 *ret
= session
->hdr
.hInternet
;
5119 return ERROR_SUCCESS
;
5123 /***********************************************************************
5124 * HTTP_OpenConnection (internal)
5126 * Connect to a web server
5133 static DWORD
HTTP_OpenConnection(http_request_t
*request
)
5135 http_session_t
*session
;
5136 appinfo_t
*hIC
= NULL
;
5137 char szaddr
[INET6_ADDRSTRLEN
];
5139 DWORD res
= ERROR_SUCCESS
;
5144 if (request
->hdr
.htype
!= WH_HHTTPREQ
)
5146 res
= ERROR_INVALID_PARAMETER
;
5150 if (NETCON_connected(&request
->netConnection
))
5152 if ((res
= HTTP_ResolveName(request
)) != ERROR_SUCCESS
) goto lend
;
5154 session
= request
->session
;
5156 hIC
= session
->appInfo
;
5157 switch (session
->socketAddress
.ss_family
)
5160 addr
= &((struct sockaddr_in
*)&session
->socketAddress
)->sin_addr
;
5163 addr
= &((struct sockaddr_in6
*)&session
->socketAddress
)->sin6_addr
;
5166 WARN("unsupported family %d\n", session
->socketAddress
.ss_family
);
5167 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
5169 inet_ntop(session
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
5170 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
5171 INTERNET_STATUS_CONNECTING_TO_SERVER
,
5175 res
= NETCON_create(&request
->netConnection
, session
->socketAddress
.ss_family
, SOCK_STREAM
, 0);
5176 if (res
!= ERROR_SUCCESS
)
5178 WARN("Socket creation failed: %u\n", res
);
5182 res
= NETCON_connect(&request
->netConnection
, (struct sockaddr
*)&session
->socketAddress
,
5184 if(res
!= ERROR_SUCCESS
)
5187 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
5188 INTERNET_STATUS_CONNECTED_TO_SERVER
,
5189 szaddr
, strlen(szaddr
)+1);
5191 if (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
5193 /* Note: we differ from Microsoft's WinINet here. they seem to have
5194 * a bug that causes no status callbacks to be sent when starting
5195 * a tunnel to a proxy server using the CONNECT verb. i believe our
5196 * behaviour to be more correct and to not cause any incompatibilities
5197 * because using a secure connection through a proxy server is a rare
5198 * case that would be hard for anyone to depend on */
5199 if (hIC
->proxy
&& (res
= HTTP_SecureProxyConnect(request
)) != ERROR_SUCCESS
) {
5200 HTTPREQ_CloseConnection(&request
->hdr
);
5204 res
= NETCON_secure_connect(&request
->netConnection
, session
->hostName
);
5205 if(res
!= ERROR_SUCCESS
)
5207 WARN("Couldn't connect securely to host\n");
5209 if((request
->hdr
.ErrorMask
&INTERNET_ERROR_MASK_COMBINED_SEC_CERT
) && (
5210 res
== ERROR_INTERNET_SEC_CERT_DATE_INVALID
5211 || res
== ERROR_INTERNET_INVALID_CA
5212 || res
== ERROR_INTERNET_SEC_CERT_NO_REV
5213 || res
== ERROR_INTERNET_SEC_CERT_REV_FAILED
5214 || res
== ERROR_INTERNET_SEC_CERT_REVOKED
5215 || res
== ERROR_INTERNET_SEC_INVALID_CERT
5216 || res
== ERROR_INTERNET_SEC_CERT_CN_INVALID
))
5217 res
= ERROR_INTERNET_SEC_CERT_ERRORS
;
5219 HTTPREQ_CloseConnection(&request
->hdr
);
5226 reset_data_stream(request
);
5228 TRACE("%d <--\n", res
);
5233 /***********************************************************************
5234 * HTTP_clear_response_headers (internal)
5236 * clear out any old response headers
5238 static void HTTP_clear_response_headers( http_request_t
*request
)
5242 for( i
=0; i
<request
->nCustHeaders
; i
++)
5244 if( !request
->custHeaders
[i
].lpszField
)
5246 if( !request
->custHeaders
[i
].lpszValue
)
5248 if ( request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
5250 HTTP_DeleteCustomHeader( request
, i
);
5255 /***********************************************************************
5256 * HTTP_GetResponseHeaders (internal)
5258 * Read server response
5265 static INT
HTTP_GetResponseHeaders(http_request_t
*request
, BOOL clear
)
5268 WCHAR buffer
[MAX_REPLY_LEN
];
5269 DWORD buflen
= MAX_REPLY_LEN
;
5270 BOOL bSuccess
= FALSE
;
5272 char bufferA
[MAX_REPLY_LEN
];
5273 LPWSTR status_code
= NULL
, status_text
= NULL
;
5274 DWORD cchMaxRawHeaders
= 1024;
5275 LPWSTR lpszRawHeaders
= NULL
;
5277 DWORD cchRawHeaders
= 0;
5278 BOOL codeHundred
= FALSE
;
5282 if (!NETCON_connected(&request
->netConnection
))
5286 static const WCHAR szHundred
[] = {'1','0','0',0};
5288 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5290 buflen
= MAX_REPLY_LEN
;
5291 if (!read_line(request
, bufferA
, &buflen
))
5294 /* clear old response headers (eg. from a redirect response) */
5296 HTTP_clear_response_headers( request
);
5301 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5302 /* check is this a status code line? */
5303 if (!strncmpW(buffer
, g_szHttp1_0
, 4))
5305 /* split the version from the status code */
5306 status_code
= strchrW( buffer
, ' ' );
5311 /* split the status code from the status text */
5312 status_text
= strchrW( status_code
, ' ' );
5317 TRACE("version [%s] status code [%s] status text [%s]\n",
5318 debugstr_w(buffer
), debugstr_w(status_code
), debugstr_w(status_text
) );
5320 codeHundred
= (!strcmpW(status_code
, szHundred
));
5322 else if (!codeHundred
)
5324 WARN("No status line at head of response (%s)\n", debugstr_w(buffer
));
5326 HeapFree(GetProcessHeap(), 0, request
->version
);
5327 HeapFree(GetProcessHeap(), 0, request
->statusText
);
5329 request
->version
= heap_strdupW(g_szHttp1_0
);
5330 request
->statusText
= heap_strdupW(szOK
);
5332 HeapFree(GetProcessHeap(), 0, request
->rawHeaders
);
5333 request
->rawHeaders
= heap_strdupW(szDefaultHeader
);
5338 } while (codeHundred
);
5340 /* Add status code */
5341 HTTP_ProcessHeader(request
, szStatus
, status_code
,
5342 HTTP_ADDHDR_FLAG_REPLACE
);
5344 HeapFree(GetProcessHeap(),0,request
->version
);
5345 HeapFree(GetProcessHeap(),0,request
->statusText
);
5347 request
->version
= heap_strdupW(buffer
);
5348 request
->statusText
= heap_strdupW(status_text
);
5350 /* Restore the spaces */
5351 *(status_code
-1) = ' ';
5352 *(status_text
-1) = ' ';
5354 /* regenerate raw headers */
5355 lpszRawHeaders
= HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5356 if (!lpszRawHeaders
) goto lend
;
5358 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5359 cchMaxRawHeaders
*= 2;
5360 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5361 if (temp
== NULL
) goto lend
;
5362 lpszRawHeaders
= temp
;
5363 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5364 cchRawHeaders
+= (buflen
-1);
5365 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5366 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5367 lpszRawHeaders
[cchRawHeaders
] = '\0';
5369 /* Parse each response line */
5372 buflen
= MAX_REPLY_LEN
;
5373 if (read_line(request
, bufferA
, &buflen
))
5375 LPWSTR
* pFieldAndValue
;
5377 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA
));
5379 if (!bufferA
[0]) break;
5380 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5382 pFieldAndValue
= HTTP_InterpretHttpHeader(buffer
);
5385 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5386 cchMaxRawHeaders
*= 2;
5387 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5388 if (temp
== NULL
) goto lend
;
5389 lpszRawHeaders
= temp
;
5390 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5391 cchRawHeaders
+= (buflen
-1);
5392 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5393 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5394 lpszRawHeaders
[cchRawHeaders
] = '\0';
5396 HTTP_ProcessHeader(request
, pFieldAndValue
[0], pFieldAndValue
[1],
5397 HTTP_ADDREQ_FLAG_ADD
);
5399 HTTP_FreeTokens(pFieldAndValue
);
5410 /* make sure the response header is terminated with an empty line. Some apps really
5411 truly care about that empty line being there for some reason. Just add it to the
5413 if (cchRawHeaders
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5415 cchMaxRawHeaders
= cchRawHeaders
+ strlenW(szCrLf
);
5416 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5417 if (temp
== NULL
) goto lend
;
5418 lpszRawHeaders
= temp
;
5421 memcpy(&lpszRawHeaders
[cchRawHeaders
], szCrLf
, sizeof(szCrLf
));
5423 HeapFree(GetProcessHeap(), 0, request
->rawHeaders
);
5424 request
->rawHeaders
= lpszRawHeaders
;
5425 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders
));
5435 HeapFree(GetProcessHeap(), 0, lpszRawHeaders
);
5440 /***********************************************************************
5441 * HTTP_InterpretHttpHeader (internal)
5443 * Parse server response
5447 * Pointer to array of field, value, NULL on success.
5450 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
)
5452 LPWSTR
* pTokenPair
;
5456 pTokenPair
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*pTokenPair
)*3);
5458 pszColon
= strchrW(buffer
, ':');
5459 /* must have two tokens */
5462 HTTP_FreeTokens(pTokenPair
);
5464 TRACE("No ':' in line: %s\n", debugstr_w(buffer
));
5468 pTokenPair
[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon
- buffer
+ 1) * sizeof(WCHAR
));
5471 HTTP_FreeTokens(pTokenPair
);
5474 memcpy(pTokenPair
[0], buffer
, (pszColon
- buffer
) * sizeof(WCHAR
));
5475 pTokenPair
[0][pszColon
- buffer
] = '\0';
5479 len
= strlenW(pszColon
);
5480 pTokenPair
[1] = HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
5483 HTTP_FreeTokens(pTokenPair
);
5486 memcpy(pTokenPair
[1], pszColon
, (len
+ 1) * sizeof(WCHAR
));
5488 strip_spaces(pTokenPair
[0]);
5489 strip_spaces(pTokenPair
[1]);
5491 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair
[0]), debugstr_w(pTokenPair
[1]));
5495 /***********************************************************************
5496 * HTTP_ProcessHeader (internal)
5498 * Stuff header into header tables according to <dwModifier>
5502 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5504 static DWORD
HTTP_ProcessHeader(http_request_t
*request
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
)
5506 LPHTTPHEADERW lphttpHdr
= NULL
;
5508 BOOL request_only
= dwModifier
& HTTP_ADDHDR_FLAG_REQ
;
5509 DWORD res
= ERROR_HTTP_INVALID_HEADER
;
5511 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field
), debugstr_w(value
), dwModifier
);
5513 /* REPLACE wins out over ADD */
5514 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5515 dwModifier
&= ~HTTP_ADDHDR_FLAG_ADD
;
5517 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD
)
5520 index
= HTTP_GetCustomHeaderIndex(request
, field
, 0, request_only
);
5524 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD_IF_NEW
)
5525 return ERROR_HTTP_INVALID_HEADER
;
5526 lphttpHdr
= &request
->custHeaders
[index
];
5532 hdr
.lpszField
= (LPWSTR
)field
;
5533 hdr
.lpszValue
= (LPWSTR
)value
;
5534 hdr
.wFlags
= hdr
.wCount
= 0;
5536 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5537 hdr
.wFlags
|= HDR_ISREQUEST
;
5539 return HTTP_InsertCustomHeader(request
, &hdr
);
5541 /* no value to delete */
5542 else return ERROR_SUCCESS
;
5544 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5545 lphttpHdr
->wFlags
|= HDR_ISREQUEST
;
5547 lphttpHdr
->wFlags
&= ~HDR_ISREQUEST
;
5549 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5551 HTTP_DeleteCustomHeader( request
, index
);
5557 hdr
.lpszField
= (LPWSTR
)field
;
5558 hdr
.lpszValue
= (LPWSTR
)value
;
5559 hdr
.wFlags
= hdr
.wCount
= 0;
5561 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5562 hdr
.wFlags
|= HDR_ISREQUEST
;
5564 return HTTP_InsertCustomHeader(request
, &hdr
);
5567 return ERROR_SUCCESS
;
5569 else if (dwModifier
& COALESCEFLAGS
)
5574 INT origlen
= strlenW(lphttpHdr
->lpszValue
);
5575 INT valuelen
= strlenW(value
);
5577 if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
)
5580 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
5582 else if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON
)
5585 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
5588 len
= origlen
+ valuelen
+ ((ch
> 0) ? 2 : 0);
5590 lpsztmp
= HeapReAlloc(GetProcessHeap(), 0, lphttpHdr
->lpszValue
, (len
+1)*sizeof(WCHAR
));
5593 lphttpHdr
->lpszValue
= lpsztmp
;
5594 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5597 lphttpHdr
->lpszValue
[origlen
] = ch
;
5599 lphttpHdr
->lpszValue
[origlen
] = ' ';
5603 memcpy(&lphttpHdr
->lpszValue
[origlen
], value
, valuelen
*sizeof(WCHAR
));
5604 lphttpHdr
->lpszValue
[len
] = '\0';
5605 res
= ERROR_SUCCESS
;
5609 WARN("HeapReAlloc (%d bytes) failed\n",len
+1);
5610 res
= ERROR_OUTOFMEMORY
;
5613 TRACE("<-- %d\n", res
);
5618 /***********************************************************************
5619 * HTTP_FinishedReading (internal)
5621 * Called when all content from server has been read by client.
5624 static BOOL
HTTP_FinishedReading(http_request_t
*request
)
5626 BOOL keepalive
= HTTP_KeepAlive(request
);
5633 HTTPREQ_CloseConnection(&request
->hdr
);
5636 /* FIXME: store data in the URL cache here */
5642 /***********************************************************************
5643 * HTTP_GetCustomHeaderIndex (internal)
5645 * Return index of custom header from header array
5648 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*request
, LPCWSTR lpszField
,
5649 int requested_index
, BOOL request_only
)
5653 TRACE("%s, %d, %d\n", debugstr_w(lpszField
), requested_index
, request_only
);
5655 for (index
= 0; index
< request
->nCustHeaders
; index
++)
5657 if (strcmpiW(request
->custHeaders
[index
].lpszField
, lpszField
))
5660 if (request_only
&& !(request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
5663 if (!request_only
&& (request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
5666 if (requested_index
== 0)
5671 if (index
>= request
->nCustHeaders
)
5674 TRACE("Return: %d\n", index
);
5679 /***********************************************************************
5680 * HTTP_InsertCustomHeader (internal)
5682 * Insert header into array
5685 static DWORD
HTTP_InsertCustomHeader(http_request_t
*request
, LPHTTPHEADERW lpHdr
)
5688 LPHTTPHEADERW lph
= NULL
;
5690 TRACE("--> %s: %s\n", debugstr_w(lpHdr
->lpszField
), debugstr_w(lpHdr
->lpszValue
));
5691 count
= request
->nCustHeaders
+ 1;
5693 lph
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, request
->custHeaders
, sizeof(HTTPHEADERW
) * count
);
5695 lph
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HTTPHEADERW
) * count
);
5698 return ERROR_OUTOFMEMORY
;
5700 request
->custHeaders
= lph
;
5701 request
->custHeaders
[count
-1].lpszField
= heap_strdupW(lpHdr
->lpszField
);
5702 request
->custHeaders
[count
-1].lpszValue
= heap_strdupW(lpHdr
->lpszValue
);
5703 request
->custHeaders
[count
-1].wFlags
= lpHdr
->wFlags
;
5704 request
->custHeaders
[count
-1].wCount
= lpHdr
->wCount
;
5705 request
->nCustHeaders
++;
5707 return ERROR_SUCCESS
;
5711 /***********************************************************************
5712 * HTTP_DeleteCustomHeader (internal)
5714 * Delete header from array
5715 * If this function is called, the indexs may change.
5717 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*request
, DWORD index
)
5719 if( request
->nCustHeaders
<= 0 )
5721 if( index
>= request
->nCustHeaders
)
5723 request
->nCustHeaders
--;
5725 HeapFree(GetProcessHeap(), 0, request
->custHeaders
[index
].lpszField
);
5726 HeapFree(GetProcessHeap(), 0, request
->custHeaders
[index
].lpszValue
);
5728 memmove( &request
->custHeaders
[index
], &request
->custHeaders
[index
+1],
5729 (request
->nCustHeaders
- index
)* sizeof(HTTPHEADERW
) );
5730 memset( &request
->custHeaders
[request
->nCustHeaders
], 0, sizeof(HTTPHEADERW
) );
5736 /***********************************************************************
5737 * HTTP_VerifyValidHeader (internal)
5739 * Verify the given header is not invalid for the given http request
5742 static BOOL
HTTP_VerifyValidHeader(http_request_t
*request
, LPCWSTR field
)
5744 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5745 if (!strcmpW(request
->version
, g_szHttp1_0
) && !strcmpiW(field
, szAccept_Encoding
))
5746 return ERROR_HTTP_INVALID_HEADER
;
5748 return ERROR_SUCCESS
;
5751 /***********************************************************************
5752 * IsHostInProxyBypassList (@)
5757 BOOL WINAPI
IsHostInProxyBypassList(DWORD flags
, LPCSTR szHost
, DWORD length
)
5759 FIXME("STUB: flags=%d host=%s length=%d\n",flags
,szHost
,length
);
5763 /***********************************************************************
5764 * InternetShowSecurityInfoByURLA (@)
5766 BOOL WINAPI
InternetShowSecurityInfoByURLA(LPCSTR url
, HWND window
)
5768 FIXME("stub: %s %p\n", url
, window
);
5772 /***********************************************************************
5773 * InternetShowSecurityInfoByURLW (@)
5775 BOOL WINAPI
InternetShowSecurityInfoByURLW(LPCWSTR url
, HWND window
)
5777 FIXME("stub: %s %p\n", debugstr_w(url
), window
);