2 * Copyright 2007 Misha Koshelev
3 * Copyright 2009 Jacek Caban for CodeWeavers
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include "urlmon_main.h"
22 #include "wine/debug.h"
24 WINE_DEFAULT_DEBUG_CHANNEL(urlmon
);
26 /* Flags are needed for, among other things, return HRESULTs from the Read function
27 * to conform to native. For example, Read returns:
29 * 1. E_PENDING if called before the request has completed,
31 * 2. S_FALSE after all data has been read and S_OK has been reported,
32 * (flags = FLAG_REQUEST_COMPLETE | FLAG_ALL_DATA_READ | FLAG_RESULT_REPORTED)
33 * 3. INET_E_DATA_NOT_AVAILABLE if InternetQueryDataAvailable fails. The first time
34 * this occurs, INET_E_DATA_NOT_AVAILABLE will also be reported to the sink,
35 * (flags = FLAG_REQUEST_COMPLETE)
36 * but upon subsequent calls to Read no reporting will take place, yet
37 * InternetQueryDataAvailable will still be called, and, on failure,
38 * INET_E_DATA_NOT_AVAILABLE will still be returned.
39 * (flags = FLAG_REQUEST_COMPLETE | FLAG_RESULT_REPORTED)
41 * FLAG_FIRST_DATA_REPORTED and FLAG_LAST_DATA_REPORTED are needed for proper
42 * ReportData reporting. For example, if OnResponse returns S_OK, Continue will
43 * report BSCF_FIRSTDATANOTIFICATION, and when all data has been read Read will
44 * report BSCF_INTERMEDIATEDATANOTIFICATION|BSCF_LASTDATANOTIFICATION. However,
45 * if OnResponse does not return S_OK, Continue will not report data, and Read
46 * will report BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION when all
49 #define FLAG_REQUEST_COMPLETE 0x0001
50 #define FLAG_FIRST_CONTINUE_COMPLETE 0x0002
51 #define FLAG_FIRST_DATA_REPORTED 0x0004
52 #define FLAG_ALL_DATA_READ 0x0008
53 #define FLAG_LAST_DATA_REPORTED 0x0010
54 #define FLAG_RESULT_REPORTED 0x0020
56 static inline HRESULT
report_progress(Protocol
*protocol
, ULONG status_code
, LPCWSTR status_text
)
58 return IInternetProtocolSink_ReportProgress(protocol
->protocol_sink
, status_code
, status_text
);
61 static inline HRESULT
report_result(Protocol
*protocol
, HRESULT hres
)
63 if (!(protocol
->flags
& FLAG_RESULT_REPORTED
) && protocol
->protocol_sink
) {
64 protocol
->flags
|= FLAG_RESULT_REPORTED
;
65 IInternetProtocolSink_ReportResult(protocol
->protocol_sink
, hres
, 0, NULL
);
71 static void report_data(Protocol
*protocol
)
75 if((protocol
->flags
& FLAG_LAST_DATA_REPORTED
) || !protocol
->protocol_sink
)
78 if(protocol
->flags
& FLAG_FIRST_DATA_REPORTED
) {
79 bscf
= BSCF_INTERMEDIATEDATANOTIFICATION
;
81 protocol
->flags
|= FLAG_FIRST_DATA_REPORTED
;
82 bscf
= BSCF_FIRSTDATANOTIFICATION
;
85 if(protocol
->flags
& FLAG_ALL_DATA_READ
&& !(protocol
->flags
& FLAG_LAST_DATA_REPORTED
)) {
86 protocol
->flags
|= FLAG_LAST_DATA_REPORTED
;
87 bscf
|= BSCF_LASTDATANOTIFICATION
;
90 IInternetProtocolSink_ReportData(protocol
->protocol_sink
, bscf
,
91 protocol
->current_position
+protocol
->available_bytes
,
92 protocol
->content_length
);
95 static void all_data_read(Protocol
*protocol
)
97 protocol
->flags
|= FLAG_ALL_DATA_READ
;
99 report_data(protocol
);
100 report_result(protocol
, S_OK
);
103 static void request_complete(Protocol
*protocol
, INTERNET_ASYNC_RESULT
*ar
)
108 WARN("request failed: %d\n", ar
->dwError
);
112 protocol
->flags
|= FLAG_REQUEST_COMPLETE
;
114 if(!protocol
->request
) {
115 TRACE("setting request handle %p\n", (HINTERNET
)ar
->dwResult
);
116 protocol
->request
= (HINTERNET
)ar
->dwResult
;
119 /* PROTOCOLDATA same as native */
120 memset(&data
, 0, sizeof(data
));
121 data
.dwState
= 0xf1000000;
122 if(protocol
->flags
& FLAG_FIRST_CONTINUE_COMPLETE
)
123 data
.pData
= (LPVOID
)BINDSTATUS_ENDDOWNLOADCOMPONENTS
;
125 data
.pData
= (LPVOID
)BINDSTATUS_DOWNLOADINGDATA
;
127 if (protocol
->bindf
& BINDF_FROMURLMON
)
128 IInternetProtocolSink_Switch(protocol
->protocol_sink
, &data
);
130 protocol_continue(protocol
, &data
);
133 static void WINAPI
internet_status_callback(HINTERNET internet
, DWORD_PTR context
,
134 DWORD internet_status
, LPVOID status_info
, DWORD status_info_len
)
136 Protocol
*protocol
= (Protocol
*)context
;
138 switch(internet_status
) {
139 case INTERNET_STATUS_RESOLVING_NAME
:
140 TRACE("%p INTERNET_STATUS_RESOLVING_NAME\n", protocol
);
141 report_progress(protocol
, BINDSTATUS_FINDINGRESOURCE
, (LPWSTR
)status_info
);
144 case INTERNET_STATUS_CONNECTING_TO_SERVER
:
145 TRACE("%p INTERNET_STATUS_CONNECTING_TO_SERVER\n", protocol
);
146 report_progress(protocol
, BINDSTATUS_CONNECTING
, (LPWSTR
)status_info
);
149 case INTERNET_STATUS_SENDING_REQUEST
:
150 TRACE("%p INTERNET_STATUS_SENDING_REQUEST\n", protocol
);
151 report_progress(protocol
, BINDSTATUS_SENDINGREQUEST
, (LPWSTR
)status_info
);
154 case INTERNET_STATUS_REQUEST_COMPLETE
:
155 request_complete(protocol
, status_info
);
158 case INTERNET_STATUS_HANDLE_CREATED
:
159 TRACE("%p INTERNET_STATUS_HANDLE_CREATED\n", protocol
);
160 IInternetProtocol_AddRef(protocol
->protocol
);
163 case INTERNET_STATUS_HANDLE_CLOSING
:
164 TRACE("%p INTERNET_STATUS_HANDLE_CLOSING\n", protocol
);
166 if(*(HINTERNET
*)status_info
== protocol
->request
) {
167 protocol
->request
= NULL
;
168 if(protocol
->protocol_sink
) {
169 IInternetProtocolSink_Release(protocol
->protocol_sink
);
170 protocol
->protocol_sink
= NULL
;
173 if(protocol
->bind_info
.cbSize
) {
174 ReleaseBindInfo(&protocol
->bind_info
);
175 memset(&protocol
->bind_info
, 0, sizeof(protocol
->bind_info
));
177 }else if(*(HINTERNET
*)status_info
== protocol
->connection
) {
178 protocol
->connection
= NULL
;
181 IInternetProtocol_Release(protocol
->protocol
);
185 WARN("Unhandled Internet status callback %d\n", internet_status
);
189 HRESULT
protocol_start(Protocol
*protocol
, IInternetProtocol
*prot
, LPCWSTR url
,
190 IInternetProtocolSink
*protocol_sink
, IInternetBindInfo
*bind_info
)
192 LPOLESTR user_agent
= NULL
;
197 protocol
->protocol
= prot
;
199 IInternetProtocolSink_AddRef(protocol_sink
);
200 protocol
->protocol_sink
= protocol_sink
;
202 memset(&protocol
->bind_info
, 0, sizeof(protocol
->bind_info
));
203 protocol
->bind_info
.cbSize
= sizeof(BINDINFO
);
204 hres
= IInternetBindInfo_GetBindInfo(bind_info
, &protocol
->bindf
, &protocol
->bind_info
);
206 WARN("GetBindInfo failed: %08x\n", hres
);
207 return report_result(protocol
, hres
);
210 if(!(protocol
->bindf
& BINDF_FROMURLMON
))
211 report_progress(protocol
, BINDSTATUS_DIRECTBIND
, NULL
);
213 hres
= IInternetBindInfo_GetBindString(bind_info
, BINDSTRING_USER_AGENT
, &user_agent
, 1, &size
);
214 if (hres
!= S_OK
|| !size
) {
217 LPSTR user_agenta
= NULL
;
220 if ((hres
= ObtainUserAgentString(0, &null_char
, &len
)) != E_OUTOFMEMORY
) {
221 WARN("ObtainUserAgentString failed: %08x\n", hres
);
222 }else if (!(user_agenta
= heap_alloc(len
*sizeof(CHAR
)))) {
223 WARN("Out of memory\n");
224 }else if ((hres
= ObtainUserAgentString(0, user_agenta
, &len
)) != S_OK
) {
225 WARN("ObtainUserAgentString failed: %08x\n", hres
);
227 if(!(user_agent
= CoTaskMemAlloc((len
)*sizeof(WCHAR
))))
228 WARN("Out of memory\n");
230 MultiByteToWideChar(CP_ACP
, 0, user_agenta
, -1, user_agent
, len
);
232 heap_free(user_agenta
);
235 protocol
->internet
= InternetOpenW(user_agent
, 0, NULL
, NULL
, INTERNET_FLAG_ASYNC
);
236 CoTaskMemFree(user_agent
);
237 if(!protocol
->internet
) {
238 WARN("InternetOpen failed: %d\n", GetLastError());
239 return report_result(protocol
, INET_E_NO_SESSION
);
242 /* Native does not check for success of next call, so we won't either */
243 InternetSetStatusCallbackW(protocol
->internet
, internet_status_callback
);
245 request_flags
= INTERNET_FLAG_KEEP_CONNECTION
;
246 if(protocol
->bindf
& BINDF_NOWRITECACHE
)
247 request_flags
|= INTERNET_FLAG_NO_CACHE_WRITE
;
248 if(protocol
->bindf
& BINDF_NEEDFILE
)
249 request_flags
|= INTERNET_FLAG_NEED_FILE
;
251 hres
= protocol
->vtbl
->open_request(protocol
, url
, request_flags
, bind_info
);
253 protocol_close_connection(protocol
);
254 return report_result(protocol
, hres
);
260 HRESULT
protocol_continue(Protocol
*protocol
, PROTOCOLDATA
*data
)
265 WARN("Expected pProtocolData to be non-NULL\n");
269 if(!protocol
->request
) {
270 WARN("Expected request to be non-NULL\n");
274 if(!protocol
->protocol_sink
) {
275 WARN("Expected IInternetProtocolSink pointer to be non-NULL\n");
279 if(data
->pData
== (LPVOID
)BINDSTATUS_DOWNLOADINGDATA
) {
280 hres
= protocol
->vtbl
->start_downloading(protocol
);
282 protocol_close_connection(protocol
);
283 report_result(protocol
, hres
);
287 if(protocol
->bindf
& BINDF_NEEDFILE
) {
288 WCHAR cache_file
[MAX_PATH
];
289 DWORD buflen
= sizeof(cache_file
);
291 if(InternetQueryOptionW(protocol
->request
, INTERNET_OPTION_DATAFILE_NAME
,
292 cache_file
, &buflen
)) {
293 report_progress(protocol
, BINDSTATUS_CACHEFILENAMEAVAILABLE
, cache_file
);
295 FIXME("Could not get cache file\n");
299 protocol
->flags
|= FLAG_FIRST_CONTINUE_COMPLETE
;
302 if(data
->pData
>= (LPVOID
)BINDSTATUS_DOWNLOADINGDATA
) {
305 /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
306 * read, so clear the flag _before_ calling so it does not incorrectly get cleared
307 * after the status callback is called */
308 protocol
->flags
&= ~FLAG_REQUEST_COMPLETE
;
309 res
= InternetQueryDataAvailable(protocol
->request
, &protocol
->available_bytes
, 0, 0);
311 protocol
->flags
|= FLAG_REQUEST_COMPLETE
;
312 report_data(protocol
);
313 }else if(GetLastError() != ERROR_IO_PENDING
) {
314 protocol
->flags
|= FLAG_REQUEST_COMPLETE
;
315 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
316 report_result(protocol
, INET_E_DATA_NOT_AVAILABLE
);
323 HRESULT
protocol_read(Protocol
*protocol
, void *buf
, ULONG size
, ULONG
*read_ret
)
327 HRESULT hres
= S_FALSE
;
329 if(!(protocol
->flags
& FLAG_REQUEST_COMPLETE
)) {
334 if(protocol
->flags
& FLAG_ALL_DATA_READ
) {
340 if(protocol
->available_bytes
) {
343 res
= InternetReadFile(protocol
->request
, ((BYTE
*)buf
)+read
,
344 protocol
->available_bytes
> size
-read
? size
-read
: protocol
->available_bytes
, &len
);
346 WARN("InternetReadFile failed: %d\n", GetLastError());
347 hres
= INET_E_DOWNLOAD_FAILURE
;
348 report_result(protocol
, hres
);
353 all_data_read(protocol
);
358 protocol
->current_position
+= len
;
359 protocol
->available_bytes
-= len
;
361 /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
362 * read, so clear the flag _before_ calling so it does not incorrectly get cleared
363 * after the status callback is called */
364 protocol
->flags
&= ~FLAG_REQUEST_COMPLETE
;
365 res
= InternetQueryDataAvailable(protocol
->request
, &protocol
->available_bytes
, 0, 0);
367 if (GetLastError() == ERROR_IO_PENDING
) {
370 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
371 hres
= INET_E_DATA_NOT_AVAILABLE
;
372 report_result(protocol
, hres
);
377 if(!protocol
->available_bytes
) {
378 all_data_read(protocol
);
386 if (hres
!= E_PENDING
)
387 protocol
->flags
|= FLAG_REQUEST_COMPLETE
;
391 return read
? S_OK
: S_FALSE
;
394 HRESULT
protocol_lock_request(Protocol
*protocol
)
396 if (!InternetLockRequestFile(protocol
->request
, &protocol
->lock
))
397 WARN("InternetLockRequest failed: %d\n", GetLastError());
402 HRESULT
protocol_unlock_request(Protocol
*protocol
)
407 if(!InternetUnlockRequestFile(protocol
->lock
))
408 WARN("InternetUnlockRequest failed: %d\n", GetLastError());
414 void protocol_close_connection(Protocol
*protocol
)
416 protocol
->vtbl
->close_connection(protocol
);
418 if(protocol
->request
)
419 InternetCloseHandle(protocol
->request
);
421 if(protocol
->connection
)
422 InternetCloseHandle(protocol
->connection
);
424 if(protocol
->internet
) {
425 InternetCloseHandle(protocol
->internet
);
426 protocol
->internet
= 0;