kernel32/tests: Init pointers in common way, add some win_skip() while skipping.
[wine/hramrach.git] / dlls / urlmon / protocol.c
blob3f3cbe505bc8b139cc37cf0e5971202d35d2f97c
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 /* 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,
30 * (flags = 0)
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
47 * data has been read.
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);
68 return hres;
71 static void report_data(Protocol *protocol)
73 DWORD bscf;
75 if((protocol->flags & FLAG_LAST_DATA_REPORTED) || !protocol->protocol_sink)
76 return;
78 if(protocol->flags & FLAG_FIRST_DATA_REPORTED) {
79 bscf = BSCF_INTERMEDIATEDATANOTIFICATION;
80 }else {
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)
105 PROTOCOLDATA data;
107 if(!ar->dwResult) {
108 WARN("request failed: %d\n", ar->dwError);
109 return;
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;
124 else
125 data.pData = (LPVOID)BINDSTATUS_DOWNLOADINGDATA;
127 if (protocol->bindf & BINDF_FROMURLMON)
128 IInternetProtocolSink_Switch(protocol->protocol_sink, &data);
129 else
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);
142 break;
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);
147 break;
149 case INTERNET_STATUS_SENDING_REQUEST:
150 TRACE("%p INTERNET_STATUS_SENDING_REQUEST\n", protocol);
151 report_progress(protocol, BINDSTATUS_SENDINGREQUEST, (LPWSTR)status_info);
152 break;
154 case INTERNET_STATUS_REDIRECT:
155 TRACE("%p INTERNET_STATUS_REDIRECT\n", protocol);
156 report_progress(protocol, BINDSTATUS_REDIRECTING, (LPWSTR)status_info);
157 break;
159 case INTERNET_STATUS_REQUEST_COMPLETE:
160 request_complete(protocol, status_info);
161 break;
163 case INTERNET_STATUS_HANDLE_CREATED:
164 TRACE("%p INTERNET_STATUS_HANDLE_CREATED\n", protocol);
165 IInternetProtocol_AddRef(protocol->protocol);
166 break;
168 case INTERNET_STATUS_HANDLE_CLOSING:
169 TRACE("%p INTERNET_STATUS_HANDLE_CLOSING\n", protocol);
171 if(*(HINTERNET *)status_info == protocol->request) {
172 protocol->request = NULL;
173 if(protocol->protocol_sink) {
174 IInternetProtocolSink_Release(protocol->protocol_sink);
175 protocol->protocol_sink = NULL;
178 if(protocol->bind_info.cbSize) {
179 ReleaseBindInfo(&protocol->bind_info);
180 memset(&protocol->bind_info, 0, sizeof(protocol->bind_info));
182 }else if(*(HINTERNET *)status_info == protocol->connection) {
183 protocol->connection = NULL;
186 IInternetProtocol_Release(protocol->protocol);
187 break;
189 default:
190 WARN("Unhandled Internet status callback %d\n", internet_status);
194 static HINTERNET create_internet_session(IInternetBindInfo *bind_info)
196 LPWSTR global_user_agent = NULL;
197 LPOLESTR user_agent = NULL;
198 ULONG size = 0;
199 HINTERNET ret;
200 HRESULT hres;
202 hres = IInternetBindInfo_GetBindString(bind_info, BINDSTRING_USER_AGENT, &user_agent, 1, &size);
203 if(hres != S_OK || !size)
204 global_user_agent = get_useragent();
206 ret = InternetOpenW(user_agent ? user_agent : global_user_agent, 0, NULL, NULL, INTERNET_FLAG_ASYNC);
207 heap_free(global_user_agent);
208 CoTaskMemFree(user_agent);
209 if(!ret) {
210 WARN("InternetOpen failed: %d\n", GetLastError());
211 return NULL;
214 InternetSetStatusCallbackW(ret, internet_status_callback);
215 return ret;
218 static HINTERNET internet_session;
220 HINTERNET get_internet_session(IInternetBindInfo *bind_info)
222 HINTERNET new_session;
224 if(internet_session)
225 return internet_session;
227 if(!bind_info)
228 return NULL;
230 new_session = create_internet_session(bind_info);
231 if(new_session && InterlockedCompareExchangePointer((void**)&internet_session, new_session, NULL))
232 InternetCloseHandle(new_session);
234 return internet_session;
237 HRESULT protocol_start(Protocol *protocol, IInternetProtocol *prot, LPCWSTR url,
238 IInternetProtocolSink *protocol_sink, IInternetBindInfo *bind_info)
240 DWORD request_flags;
241 HRESULT hres;
243 protocol->protocol = prot;
245 IInternetProtocolSink_AddRef(protocol_sink);
246 protocol->protocol_sink = protocol_sink;
248 memset(&protocol->bind_info, 0, sizeof(protocol->bind_info));
249 protocol->bind_info.cbSize = sizeof(BINDINFO);
250 hres = IInternetBindInfo_GetBindInfo(bind_info, &protocol->bindf, &protocol->bind_info);
251 if(hres != S_OK) {
252 WARN("GetBindInfo failed: %08x\n", hres);
253 return report_result(protocol, hres);
256 if(!(protocol->bindf & BINDF_FROMURLMON))
257 report_progress(protocol, BINDSTATUS_DIRECTBIND, NULL);
259 if(!get_internet_session(bind_info))
260 return report_result(protocol, INET_E_NO_SESSION);
262 request_flags = INTERNET_FLAG_KEEP_CONNECTION;
263 if(protocol->bindf & BINDF_NOWRITECACHE)
264 request_flags |= INTERNET_FLAG_NO_CACHE_WRITE;
265 if(protocol->bindf & BINDF_NEEDFILE)
266 request_flags |= INTERNET_FLAG_NEED_FILE;
268 hres = protocol->vtbl->open_request(protocol, url, request_flags, internet_session, bind_info);
269 if(FAILED(hres)) {
270 protocol_close_connection(protocol);
271 return report_result(protocol, hres);
274 return S_OK;
277 HRESULT protocol_continue(Protocol *protocol, PROTOCOLDATA *data)
279 HRESULT hres;
281 if (!data) {
282 WARN("Expected pProtocolData to be non-NULL\n");
283 return S_OK;
286 if(!protocol->request) {
287 WARN("Expected request to be non-NULL\n");
288 return S_OK;
291 if(!protocol->protocol_sink) {
292 WARN("Expected IInternetProtocolSink pointer to be non-NULL\n");
293 return S_OK;
296 if(data->pData == (LPVOID)BINDSTATUS_DOWNLOADINGDATA) {
297 hres = protocol->vtbl->start_downloading(protocol);
298 if(FAILED(hres)) {
299 protocol_close_connection(protocol);
300 report_result(protocol, hres);
301 return S_OK;
304 if(protocol->bindf & BINDF_NEEDFILE) {
305 WCHAR cache_file[MAX_PATH];
306 DWORD buflen = sizeof(cache_file);
308 if(InternetQueryOptionW(protocol->request, INTERNET_OPTION_DATAFILE_NAME,
309 cache_file, &buflen)) {
310 report_progress(protocol, BINDSTATUS_CACHEFILENAMEAVAILABLE, cache_file);
311 }else {
312 FIXME("Could not get cache file\n");
316 protocol->flags |= FLAG_FIRST_CONTINUE_COMPLETE;
319 if(data->pData >= (LPVOID)BINDSTATUS_DOWNLOADINGDATA) {
320 BOOL res;
322 /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
323 * read, so clear the flag _before_ calling so it does not incorrectly get cleared
324 * after the status callback is called */
325 protocol->flags &= ~FLAG_REQUEST_COMPLETE;
326 res = InternetQueryDataAvailable(protocol->request, &protocol->available_bytes, 0, 0);
327 if(res) {
328 protocol->flags |= FLAG_REQUEST_COMPLETE;
329 report_data(protocol);
330 }else if(GetLastError() != ERROR_IO_PENDING) {
331 protocol->flags |= FLAG_REQUEST_COMPLETE;
332 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
333 report_result(protocol, INET_E_DATA_NOT_AVAILABLE);
337 return S_OK;
340 HRESULT protocol_read(Protocol *protocol, void *buf, ULONG size, ULONG *read_ret)
342 ULONG read = 0;
343 BOOL res;
344 HRESULT hres = S_FALSE;
346 if(protocol->flags & FLAG_ALL_DATA_READ) {
347 *read_ret = 0;
348 return S_FALSE;
351 if(!(protocol->flags & FLAG_REQUEST_COMPLETE)) {
352 *read_ret = 0;
353 return E_PENDING;
356 while(read < size) {
357 if(protocol->available_bytes) {
358 ULONG len;
360 res = InternetReadFile(protocol->request, ((BYTE *)buf)+read,
361 protocol->available_bytes > size-read ? size-read : protocol->available_bytes, &len);
362 if(!res) {
363 WARN("InternetReadFile failed: %d\n", GetLastError());
364 hres = INET_E_DOWNLOAD_FAILURE;
365 report_result(protocol, hres);
366 break;
369 if(!len) {
370 all_data_read(protocol);
371 break;
374 read += len;
375 protocol->current_position += len;
376 protocol->available_bytes -= len;
377 }else {
378 /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
379 * read, so clear the flag _before_ calling so it does not incorrectly get cleared
380 * after the status callback is called */
381 protocol->flags &= ~FLAG_REQUEST_COMPLETE;
382 res = InternetQueryDataAvailable(protocol->request, &protocol->available_bytes, 0, 0);
383 if(!res) {
384 if (GetLastError() == ERROR_IO_PENDING) {
385 hres = E_PENDING;
386 }else {
387 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
388 hres = INET_E_DATA_NOT_AVAILABLE;
389 report_result(protocol, hres);
391 break;
394 if(!protocol->available_bytes) {
395 all_data_read(protocol);
396 break;
401 *read_ret = read;
403 if (hres != E_PENDING)
404 protocol->flags |= FLAG_REQUEST_COMPLETE;
405 if(FAILED(hres))
406 return hres;
408 return read ? S_OK : S_FALSE;
411 HRESULT protocol_lock_request(Protocol *protocol)
413 if (!InternetLockRequestFile(protocol->request, &protocol->lock))
414 WARN("InternetLockRequest failed: %d\n", GetLastError());
416 return S_OK;
419 HRESULT protocol_unlock_request(Protocol *protocol)
421 if(!protocol->lock)
422 return S_OK;
424 if(!InternetUnlockRequestFile(protocol->lock))
425 WARN("InternetUnlockRequest failed: %d\n", GetLastError());
426 protocol->lock = 0;
428 return S_OK;
431 void protocol_close_connection(Protocol *protocol)
433 protocol->vtbl->close_connection(protocol);
435 if(protocol->request)
436 InternetCloseHandle(protocol->request);
438 if(protocol->connection)
439 InternetCloseHandle(protocol->connection);
441 protocol->flags = 0;