d3d8/tests: Add more tests for UnlockRect().
[wine.git] / dlls / urlmon / protocol.c
blob1437152c2299aede28be2091382cbee30e7966fd
1 /*
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 static inline HRESULT report_progress(Protocol *protocol, ULONG status_code, LPCWSTR status_text)
28 return IInternetProtocolSink_ReportProgress(protocol->protocol_sink, status_code, status_text);
31 static inline HRESULT report_result(Protocol *protocol, HRESULT hres)
33 if (!(protocol->flags & FLAG_RESULT_REPORTED) && protocol->protocol_sink) {
34 protocol->flags |= FLAG_RESULT_REPORTED;
35 IInternetProtocolSink_ReportResult(protocol->protocol_sink, hres, 0, NULL);
38 return hres;
41 static void report_data(Protocol *protocol)
43 DWORD bscf;
45 if((protocol->flags & FLAG_LAST_DATA_REPORTED) || !protocol->protocol_sink)
46 return;
48 if(protocol->flags & FLAG_FIRST_DATA_REPORTED) {
49 bscf = BSCF_INTERMEDIATEDATANOTIFICATION;
50 }else {
51 protocol->flags |= FLAG_FIRST_DATA_REPORTED;
52 bscf = BSCF_FIRSTDATANOTIFICATION;
55 if(protocol->flags & FLAG_ALL_DATA_READ && !(protocol->flags & FLAG_LAST_DATA_REPORTED)) {
56 protocol->flags |= FLAG_LAST_DATA_REPORTED;
57 bscf |= BSCF_LASTDATANOTIFICATION;
60 IInternetProtocolSink_ReportData(protocol->protocol_sink, bscf,
61 protocol->current_position+protocol->available_bytes,
62 protocol->content_length);
65 static void all_data_read(Protocol *protocol)
67 protocol->flags |= FLAG_ALL_DATA_READ;
69 report_data(protocol);
70 report_result(protocol, S_OK);
73 static HRESULT start_downloading(Protocol *protocol)
75 HRESULT hres;
77 hres = protocol->vtbl->start_downloading(protocol);
78 if(FAILED(hres)) {
79 protocol_close_connection(protocol);
80 report_result(protocol, hres);
81 return hres;
84 if(protocol->bindf & BINDF_NEEDFILE) {
85 WCHAR cache_file[MAX_PATH];
86 DWORD buflen = sizeof(cache_file);
88 if(InternetQueryOptionW(protocol->request, INTERNET_OPTION_DATAFILE_NAME, cache_file, &buflen)) {
89 report_progress(protocol, BINDSTATUS_CACHEFILENAMEAVAILABLE, cache_file);
90 }else {
91 FIXME("Could not get cache file\n");
95 protocol->flags |= FLAG_FIRST_CONTINUE_COMPLETE;
96 return S_OK;
99 HRESULT protocol_syncbinding(Protocol *protocol)
101 BOOL res;
102 HRESULT hres;
104 protocol->flags |= FLAG_SYNC_READ;
106 hres = start_downloading(protocol);
107 if(FAILED(hres))
108 return hres;
110 res = InternetQueryDataAvailable(protocol->request, &protocol->query_available, 0, 0);
111 if(res)
112 protocol->available_bytes = protocol->query_available;
113 else
114 WARN("InternetQueryDataAvailable failed: %u\n", GetLastError());
116 protocol->flags |= FLAG_FIRST_DATA_REPORTED|FLAG_LAST_DATA_REPORTED;
117 IInternetProtocolSink_ReportData(protocol->protocol_sink, BSCF_LASTDATANOTIFICATION|BSCF_DATAFULLYAVAILABLE,
118 protocol->available_bytes, protocol->content_length);
119 return S_OK;
122 static void request_complete(Protocol *protocol, INTERNET_ASYNC_RESULT *ar)
124 PROTOCOLDATA data;
126 TRACE("(%p)->(%p)\n", protocol, ar);
128 /* PROTOCOLDATA same as native */
129 memset(&data, 0, sizeof(data));
130 data.dwState = 0xf1000000;
132 if(ar->dwResult) {
133 protocol->flags |= FLAG_REQUEST_COMPLETE;
135 if(!protocol->request) {
136 TRACE("setting request handle %p\n", (HINTERNET)ar->dwResult);
137 protocol->request = (HINTERNET)ar->dwResult;
140 if(protocol->flags & FLAG_FIRST_CONTINUE_COMPLETE)
141 data.pData = UlongToPtr(BINDSTATUS_ENDDOWNLOADCOMPONENTS);
142 else
143 data.pData = UlongToPtr(BINDSTATUS_DOWNLOADINGDATA);
145 }else {
146 protocol->flags |= FLAG_ERROR;
147 data.pData = UlongToPtr(ar->dwError);
150 if (protocol->bindf & BINDF_FROMURLMON)
151 IInternetProtocolSink_Switch(protocol->protocol_sink, &data);
152 else
153 protocol_continue(protocol, &data);
156 static void WINAPI internet_status_callback(HINTERNET internet, DWORD_PTR context,
157 DWORD internet_status, LPVOID status_info, DWORD status_info_len)
159 Protocol *protocol = (Protocol*)context;
161 switch(internet_status) {
162 case INTERNET_STATUS_RESOLVING_NAME:
163 TRACE("%p INTERNET_STATUS_RESOLVING_NAME\n", protocol);
164 report_progress(protocol, BINDSTATUS_FINDINGRESOURCE, (LPWSTR)status_info);
165 break;
167 case INTERNET_STATUS_CONNECTING_TO_SERVER: {
168 WCHAR *info;
170 TRACE("%p INTERNET_STATUS_CONNECTING_TO_SERVER %s\n", protocol, (const char*)status_info);
172 info = heap_strdupAtoW(status_info);
173 if(!info)
174 return;
176 report_progress(protocol, BINDSTATUS_CONNECTING, info);
177 heap_free(info);
178 break;
181 case INTERNET_STATUS_SENDING_REQUEST:
182 TRACE("%p INTERNET_STATUS_SENDING_REQUEST\n", protocol);
183 report_progress(protocol, BINDSTATUS_SENDINGREQUEST, (LPWSTR)status_info);
184 break;
186 case INTERNET_STATUS_REDIRECT:
187 TRACE("%p INTERNET_STATUS_REDIRECT\n", protocol);
188 report_progress(protocol, BINDSTATUS_REDIRECTING, (LPWSTR)status_info);
189 break;
191 case INTERNET_STATUS_REQUEST_COMPLETE:
192 request_complete(protocol, status_info);
193 break;
195 case INTERNET_STATUS_HANDLE_CREATED:
196 TRACE("%p INTERNET_STATUS_HANDLE_CREATED\n", protocol);
197 IInternetProtocol_AddRef(protocol->protocol);
198 break;
200 case INTERNET_STATUS_HANDLE_CLOSING:
201 TRACE("%p INTERNET_STATUS_HANDLE_CLOSING\n", protocol);
203 if(*(HINTERNET *)status_info == protocol->request) {
204 protocol->request = NULL;
205 if(protocol->protocol_sink) {
206 IInternetProtocolSink_Release(protocol->protocol_sink);
207 protocol->protocol_sink = NULL;
210 if(protocol->bind_info.cbSize) {
211 ReleaseBindInfo(&protocol->bind_info);
212 memset(&protocol->bind_info, 0, sizeof(protocol->bind_info));
214 }else if(*(HINTERNET *)status_info == protocol->connection) {
215 protocol->connection = NULL;
218 IInternetProtocol_Release(protocol->protocol);
219 break;
221 default:
222 WARN("Unhandled Internet status callback %d\n", internet_status);
226 static HRESULT write_post_stream(Protocol *protocol)
228 BYTE buf[0x20000];
229 DWORD written;
230 ULONG size;
231 BOOL res;
232 HRESULT hres;
234 protocol->flags &= ~FLAG_REQUEST_COMPLETE;
236 while(1) {
237 size = 0;
238 hres = IStream_Read(protocol->post_stream, buf, sizeof(buf), &size);
239 if(FAILED(hres) || !size)
240 break;
241 res = InternetWriteFile(protocol->request, buf, size, &written);
242 if(!res) {
243 FIXME("InternetWriteFile failed: %u\n", GetLastError());
244 hres = E_FAIL;
245 break;
249 if(SUCCEEDED(hres)) {
250 IStream_Release(protocol->post_stream);
251 protocol->post_stream = NULL;
253 hres = protocol->vtbl->end_request(protocol);
256 if(FAILED(hres))
257 return report_result(protocol, hres);
259 return S_OK;
262 static HINTERNET create_internet_session(IInternetBindInfo *bind_info)
264 LPWSTR global_user_agent = NULL;
265 LPOLESTR user_agent = NULL;
266 ULONG size = 0;
267 HINTERNET ret;
268 HRESULT hres;
270 hres = IInternetBindInfo_GetBindString(bind_info, BINDSTRING_USER_AGENT, &user_agent, 1, &size);
271 if(hres != S_OK || !size)
272 global_user_agent = get_useragent();
274 ret = InternetOpenW(user_agent ? user_agent : global_user_agent, 0, NULL, NULL, INTERNET_FLAG_ASYNC);
275 heap_free(global_user_agent);
276 CoTaskMemFree(user_agent);
277 if(!ret) {
278 WARN("InternetOpen failed: %d\n", GetLastError());
279 return NULL;
282 InternetSetStatusCallbackW(ret, internet_status_callback);
283 return ret;
286 static HINTERNET internet_session;
288 HINTERNET get_internet_session(IInternetBindInfo *bind_info)
290 HINTERNET new_session;
292 if(internet_session)
293 return internet_session;
295 if(!bind_info)
296 return NULL;
298 new_session = create_internet_session(bind_info);
299 if(new_session && InterlockedCompareExchangePointer((void**)&internet_session, new_session, NULL))
300 InternetCloseHandle(new_session);
302 return internet_session;
305 void update_user_agent(WCHAR *user_agent)
307 if(internet_session)
308 InternetSetOptionW(internet_session, INTERNET_OPTION_USER_AGENT, user_agent, strlenW(user_agent));
311 HRESULT protocol_start(Protocol *protocol, IInternetProtocol *prot, IUri *uri,
312 IInternetProtocolSink *protocol_sink, IInternetBindInfo *bind_info)
314 DWORD request_flags;
315 HRESULT hres;
317 protocol->protocol = prot;
319 IInternetProtocolSink_AddRef(protocol_sink);
320 protocol->protocol_sink = protocol_sink;
322 memset(&protocol->bind_info, 0, sizeof(protocol->bind_info));
323 protocol->bind_info.cbSize = sizeof(BINDINFO);
324 hres = IInternetBindInfo_GetBindInfo(bind_info, &protocol->bindf, &protocol->bind_info);
325 if(hres != S_OK) {
326 WARN("GetBindInfo failed: %08x\n", hres);
327 return report_result(protocol, hres);
330 if(!(protocol->bindf & BINDF_FROMURLMON))
331 report_progress(protocol, BINDSTATUS_DIRECTBIND, NULL);
333 if(!get_internet_session(bind_info))
334 return report_result(protocol, INET_E_NO_SESSION);
336 request_flags = INTERNET_FLAG_KEEP_CONNECTION;
337 if(protocol->bindf & BINDF_NOWRITECACHE)
338 request_flags |= INTERNET_FLAG_NO_CACHE_WRITE;
339 if(protocol->bindf & BINDF_NEEDFILE)
340 request_flags |= INTERNET_FLAG_NEED_FILE;
342 hres = protocol->vtbl->open_request(protocol, uri, request_flags, internet_session, bind_info);
343 if(FAILED(hres)) {
344 protocol_close_connection(protocol);
345 return report_result(protocol, hres);
348 return S_OK;
351 HRESULT protocol_continue(Protocol *protocol, PROTOCOLDATA *data)
353 BOOL is_start;
354 HRESULT hres;
356 is_start = !data || data->pData == UlongToPtr(BINDSTATUS_DOWNLOADINGDATA);
358 if(!protocol->request) {
359 WARN("Expected request to be non-NULL\n");
360 return S_OK;
363 if(!protocol->protocol_sink) {
364 WARN("Expected IInternetProtocolSink pointer to be non-NULL\n");
365 return S_OK;
368 if(protocol->flags & FLAG_ERROR) {
369 protocol->flags &= ~FLAG_ERROR;
370 protocol->vtbl->on_error(protocol, PtrToUlong(data->pData));
371 return S_OK;
374 if(protocol->post_stream)
375 return write_post_stream(protocol);
377 if(is_start) {
378 hres = start_downloading(protocol);
379 if(FAILED(hres))
380 return S_OK;
383 if(!data || data->pData >= UlongToPtr(BINDSTATUS_DOWNLOADINGDATA)) {
384 if(!protocol->available_bytes) {
385 if(protocol->query_available) {
386 protocol->available_bytes = protocol->query_available;
387 }else {
388 BOOL res;
390 /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
391 * read, so clear the flag _before_ calling so it does not incorrectly get cleared
392 * after the status callback is called */
393 protocol->flags &= ~FLAG_REQUEST_COMPLETE;
394 res = InternetQueryDataAvailable(protocol->request, &protocol->query_available, 0, 0);
395 if(res) {
396 TRACE("available %u bytes\n", protocol->query_available);
397 if(!protocol->query_available) {
398 if(is_start) {
399 TRACE("empty file\n");
400 all_data_read(protocol);
401 }else {
402 WARN("unexpected end of file?\n");
403 report_result(protocol, INET_E_DOWNLOAD_FAILURE);
405 return S_OK;
407 protocol->available_bytes = protocol->query_available;
408 }else if(GetLastError() != ERROR_IO_PENDING) {
409 protocol->flags |= FLAG_REQUEST_COMPLETE;
410 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
411 report_result(protocol, INET_E_DATA_NOT_AVAILABLE);
412 return S_OK;
416 protocol->flags |= FLAG_REQUEST_COMPLETE;
419 report_data(protocol);
422 return S_OK;
425 HRESULT protocol_read(Protocol *protocol, void *buf, ULONG size, ULONG *read_ret)
427 ULONG read = 0;
428 BOOL res;
429 HRESULT hres = S_FALSE;
431 if(protocol->flags & FLAG_ALL_DATA_READ) {
432 *read_ret = 0;
433 return S_FALSE;
436 if(!(protocol->flags & FLAG_SYNC_READ) && (!(protocol->flags & FLAG_REQUEST_COMPLETE) || !protocol->available_bytes)) {
437 *read_ret = 0;
438 return E_PENDING;
441 while(read < size && protocol->available_bytes) {
442 ULONG len;
444 res = InternetReadFile(protocol->request, ((BYTE *)buf)+read,
445 protocol->available_bytes > size-read ? size-read : protocol->available_bytes, &len);
446 if(!res) {
447 WARN("InternetReadFile failed: %d\n", GetLastError());
448 hres = INET_E_DOWNLOAD_FAILURE;
449 report_result(protocol, hres);
450 break;
453 if(!len) {
454 all_data_read(protocol);
455 break;
458 read += len;
459 protocol->current_position += len;
460 protocol->available_bytes -= len;
462 TRACE("current_position %d, available_bytes %d\n", protocol->current_position, protocol->available_bytes);
464 if(!protocol->available_bytes) {
465 /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
466 * read, so clear the flag _before_ calling so it does not incorrectly get cleared
467 * after the status callback is called */
468 protocol->flags &= ~FLAG_REQUEST_COMPLETE;
469 res = InternetQueryDataAvailable(protocol->request, &protocol->query_available, 0, 0);
470 if(!res) {
471 if (GetLastError() == ERROR_IO_PENDING) {
472 hres = E_PENDING;
473 }else {
474 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
475 hres = INET_E_DATA_NOT_AVAILABLE;
476 report_result(protocol, hres);
478 break;
481 if(!protocol->query_available) {
482 all_data_read(protocol);
483 break;
486 protocol->available_bytes = protocol->query_available;
490 *read_ret = read;
492 if (hres != E_PENDING)
493 protocol->flags |= FLAG_REQUEST_COMPLETE;
494 if(FAILED(hres))
495 return hres;
497 return read ? S_OK : S_FALSE;
500 HRESULT protocol_lock_request(Protocol *protocol)
502 if (!InternetLockRequestFile(protocol->request, &protocol->lock))
503 WARN("InternetLockRequest failed: %d\n", GetLastError());
505 return S_OK;
508 HRESULT protocol_unlock_request(Protocol *protocol)
510 if(!protocol->lock)
511 return S_OK;
513 if(!InternetUnlockRequestFile(protocol->lock))
514 WARN("InternetUnlockRequest failed: %d\n", GetLastError());
515 protocol->lock = 0;
517 return S_OK;
520 HRESULT protocol_abort(Protocol *protocol, HRESULT reason)
522 if(!protocol->protocol_sink)
523 return S_OK;
525 /* NOTE: IE10 returns S_OK here */
526 if(protocol->flags & FLAG_RESULT_REPORTED)
527 return INET_E_RESULT_DISPATCHED;
529 report_result(protocol, reason);
530 return S_OK;
533 void protocol_close_connection(Protocol *protocol)
535 protocol->vtbl->close_connection(protocol);
537 if(protocol->request)
538 InternetCloseHandle(protocol->request);
540 if(protocol->connection)
541 InternetCloseHandle(protocol->connection);
543 if(protocol->post_stream) {
544 IStream_Release(protocol->post_stream);
545 protocol->post_stream = NULL;
548 protocol->flags = 0;