[CMAKE]
[reactos.git] / dll / win32 / urlmon / protocol.c
1 /*
2 * Copyright 2007 Misha Koshelev
3 * Copyright 2009 Jacek Caban for CodeWeavers
4 *
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.
9 *
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.
14 *
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
18 */
19
20 #include "urlmon_main.h"
21
22 #include "wine/debug.h"
23
24 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
25
26 /* Flags are needed for, among other things, return HRESULTs from the Read function
27 * to conform to native. For example, Read returns:
28 *
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)
40 *
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.
48 */
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
55
56 static inline HRESULT report_progress(Protocol *protocol, ULONG status_code, LPCWSTR status_text)
57 {
58 return IInternetProtocolSink_ReportProgress(protocol->protocol_sink, status_code, status_text);
59 }
60
61 static inline HRESULT report_result(Protocol *protocol, HRESULT hres)
62 {
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);
66 }
67
68 return hres;
69 }
70
71 static void report_data(Protocol *protocol)
72 {
73 DWORD bscf;
74
75 if((protocol->flags & FLAG_LAST_DATA_REPORTED) || !protocol->protocol_sink)
76 return;
77
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;
83 }
84
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;
88 }
89
90 IInternetProtocolSink_ReportData(protocol->protocol_sink, bscf,
91 protocol->current_position+protocol->available_bytes,
92 protocol->content_length);
93 }
94
95 static void all_data_read(Protocol *protocol)
96 {
97 protocol->flags |= FLAG_ALL_DATA_READ;
98
99 report_data(protocol);
100 report_result(protocol, S_OK);
101 }
102
103 static void request_complete(Protocol *protocol, INTERNET_ASYNC_RESULT *ar)
104 {
105 PROTOCOLDATA data;
106
107 TRACE("(%p)->(%p)\n", protocol, ar);
108
109 if(!ar->dwResult) {
110 WARN("request failed: %d\n", ar->dwError);
111 return;
112 }
113
114 protocol->flags |= FLAG_REQUEST_COMPLETE;
115
116 if(!protocol->request) {
117 TRACE("setting request handle %p\n", (HINTERNET)ar->dwResult);
118 protocol->request = (HINTERNET)ar->dwResult;
119 }
120
121 /* PROTOCOLDATA same as native */
122 memset(&data, 0, sizeof(data));
123 data.dwState = 0xf1000000;
124 if(protocol->flags & FLAG_FIRST_CONTINUE_COMPLETE)
125 data.pData = (LPVOID)BINDSTATUS_ENDDOWNLOADCOMPONENTS;
126 else
127 data.pData = (LPVOID)BINDSTATUS_DOWNLOADINGDATA;
128
129 if (protocol->bindf & BINDF_FROMURLMON)
130 IInternetProtocolSink_Switch(protocol->protocol_sink, &data);
131 else
132 protocol_continue(protocol, &data);
133 }
134
135 static void WINAPI internet_status_callback(HINTERNET internet, DWORD_PTR context,
136 DWORD internet_status, LPVOID status_info, DWORD status_info_len)
137 {
138 Protocol *protocol = (Protocol*)context;
139
140 switch(internet_status) {
141 case INTERNET_STATUS_RESOLVING_NAME:
142 TRACE("%p INTERNET_STATUS_RESOLVING_NAME\n", protocol);
143 report_progress(protocol, BINDSTATUS_FINDINGRESOURCE, (LPWSTR)status_info);
144 break;
145
146 case INTERNET_STATUS_CONNECTING_TO_SERVER:
147 TRACE("%p INTERNET_STATUS_CONNECTING_TO_SERVER\n", protocol);
148 report_progress(protocol, BINDSTATUS_CONNECTING, (LPWSTR)status_info);
149 break;
150
151 case INTERNET_STATUS_SENDING_REQUEST:
152 TRACE("%p INTERNET_STATUS_SENDING_REQUEST\n", protocol);
153 report_progress(protocol, BINDSTATUS_SENDINGREQUEST, (LPWSTR)status_info);
154 break;
155
156 case INTERNET_STATUS_REDIRECT:
157 TRACE("%p INTERNET_STATUS_REDIRECT\n", protocol);
158 report_progress(protocol, BINDSTATUS_REDIRECTING, (LPWSTR)status_info);
159 break;
160
161 case INTERNET_STATUS_REQUEST_COMPLETE:
162 request_complete(protocol, status_info);
163 break;
164
165 case INTERNET_STATUS_HANDLE_CREATED:
166 TRACE("%p INTERNET_STATUS_HANDLE_CREATED\n", protocol);
167 IInternetProtocol_AddRef(protocol->protocol);
168 break;
169
170 case INTERNET_STATUS_HANDLE_CLOSING:
171 TRACE("%p INTERNET_STATUS_HANDLE_CLOSING\n", protocol);
172
173 if(*(HINTERNET *)status_info == protocol->request) {
174 protocol->request = NULL;
175 if(protocol->protocol_sink) {
176 IInternetProtocolSink_Release(protocol->protocol_sink);
177 protocol->protocol_sink = NULL;
178 }
179
180 if(protocol->bind_info.cbSize) {
181 ReleaseBindInfo(&protocol->bind_info);
182 memset(&protocol->bind_info, 0, sizeof(protocol->bind_info));
183 }
184 }else if(*(HINTERNET *)status_info == protocol->connection) {
185 protocol->connection = NULL;
186 }
187
188 IInternetProtocol_Release(protocol->protocol);
189 break;
190
191 default:
192 WARN("Unhandled Internet status callback %d\n", internet_status);
193 }
194 }
195
196 static HRESULT write_post_stream(Protocol *protocol)
197 {
198 BYTE buf[0x20000];
199 DWORD written;
200 ULONG size;
201 BOOL res;
202 HRESULT hres;
203
204 protocol->flags &= ~FLAG_REQUEST_COMPLETE;
205
206 while(1) {
207 size = 0;
208 hres = IStream_Read(protocol->post_stream, buf, sizeof(buf), &size);
209 if(FAILED(hres) || !size)
210 break;
211 res = InternetWriteFile(protocol->request, buf, size, &written);
212 if(!res) {
213 FIXME("InternetWriteFile failed: %u\n", GetLastError());
214 hres = E_FAIL;
215 break;
216 }
217 }
218
219 if(SUCCEEDED(hres)) {
220 IStream_Release(protocol->post_stream);
221 protocol->post_stream = NULL;
222
223 hres = protocol->vtbl->end_request(protocol);
224 }
225
226 if(FAILED(hres))
227 return report_result(protocol, hres);
228
229 return S_OK;
230 }
231
232 static HINTERNET create_internet_session(IInternetBindInfo *bind_info)
233 {
234 LPWSTR global_user_agent = NULL;
235 LPOLESTR user_agent = NULL;
236 ULONG size = 0;
237 HINTERNET ret;
238 HRESULT hres;
239
240 hres = IInternetBindInfo_GetBindString(bind_info, BINDSTRING_USER_AGENT, &user_agent, 1, &size);
241 if(hres != S_OK || !size)
242 global_user_agent = get_useragent();
243
244 ret = InternetOpenW(user_agent ? user_agent : global_user_agent, 0, NULL, NULL, INTERNET_FLAG_ASYNC);
245 heap_free(global_user_agent);
246 CoTaskMemFree(user_agent);
247 if(!ret) {
248 WARN("InternetOpen failed: %d\n", GetLastError());
249 return NULL;
250 }
251
252 InternetSetStatusCallbackW(ret, internet_status_callback);
253 return ret;
254 }
255
256 static HINTERNET internet_session;
257
258 HINTERNET get_internet_session(IInternetBindInfo *bind_info)
259 {
260 HINTERNET new_session;
261
262 if(internet_session)
263 return internet_session;
264
265 if(!bind_info)
266 return NULL;
267
268 new_session = create_internet_session(bind_info);
269 if(new_session && InterlockedCompareExchangePointer((void**)&internet_session, new_session, NULL))
270 InternetCloseHandle(new_session);
271
272 return internet_session;
273 }
274
275 HRESULT protocol_start(Protocol *protocol, IInternetProtocol *prot, IUri *uri,
276 IInternetProtocolSink *protocol_sink, IInternetBindInfo *bind_info)
277 {
278 DWORD request_flags;
279 HRESULT hres;
280
281 protocol->protocol = prot;
282
283 IInternetProtocolSink_AddRef(protocol_sink);
284 protocol->protocol_sink = protocol_sink;
285
286 memset(&protocol->bind_info, 0, sizeof(protocol->bind_info));
287 protocol->bind_info.cbSize = sizeof(BINDINFO);
288 hres = IInternetBindInfo_GetBindInfo(bind_info, &protocol->bindf, &protocol->bind_info);
289 if(hres != S_OK) {
290 WARN("GetBindInfo failed: %08x\n", hres);
291 return report_result(protocol, hres);
292 }
293
294 if(!(protocol->bindf & BINDF_FROMURLMON))
295 report_progress(protocol, BINDSTATUS_DIRECTBIND, NULL);
296
297 if(!get_internet_session(bind_info))
298 return report_result(protocol, INET_E_NO_SESSION);
299
300 request_flags = INTERNET_FLAG_KEEP_CONNECTION;
301 if(protocol->bindf & BINDF_NOWRITECACHE)
302 request_flags |= INTERNET_FLAG_NO_CACHE_WRITE;
303 if(protocol->bindf & BINDF_NEEDFILE)
304 request_flags |= INTERNET_FLAG_NEED_FILE;
305
306 hres = protocol->vtbl->open_request(protocol, uri, request_flags, internet_session, bind_info);
307 if(FAILED(hres)) {
308 protocol_close_connection(protocol);
309 return report_result(protocol, hres);
310 }
311
312 return S_OK;
313 }
314
315 HRESULT protocol_continue(Protocol *protocol, PROTOCOLDATA *data)
316 {
317 HRESULT hres;
318
319 if (!data) {
320 WARN("Expected pProtocolData to be non-NULL\n");
321 return S_OK;
322 }
323
324 if(!protocol->request) {
325 WARN("Expected request to be non-NULL\n");
326 return S_OK;
327 }
328
329 if(!protocol->protocol_sink) {
330 WARN("Expected IInternetProtocolSink pointer to be non-NULL\n");
331 return S_OK;
332 }
333
334 if(protocol->post_stream)
335 return write_post_stream(protocol);
336
337 if(data->pData == (LPVOID)BINDSTATUS_DOWNLOADINGDATA) {
338 hres = protocol->vtbl->start_downloading(protocol);
339 if(FAILED(hres)) {
340 protocol_close_connection(protocol);
341 report_result(protocol, hres);
342 return S_OK;
343 }
344
345 if(protocol->bindf & BINDF_NEEDFILE) {
346 WCHAR cache_file[MAX_PATH];
347 DWORD buflen = sizeof(cache_file);
348
349 if(InternetQueryOptionW(protocol->request, INTERNET_OPTION_DATAFILE_NAME,
350 cache_file, &buflen)) {
351 report_progress(protocol, BINDSTATUS_CACHEFILENAMEAVAILABLE, cache_file);
352 }else {
353 FIXME("Could not get cache file\n");
354 }
355 }
356
357 protocol->flags |= FLAG_FIRST_CONTINUE_COMPLETE;
358 }
359
360 if(data->pData >= (LPVOID)BINDSTATUS_DOWNLOADINGDATA) {
361 BOOL res;
362
363 /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
364 * read, so clear the flag _before_ calling so it does not incorrectly get cleared
365 * after the status callback is called */
366 protocol->flags &= ~FLAG_REQUEST_COMPLETE;
367 res = InternetQueryDataAvailable(protocol->request, &protocol->available_bytes, 0, 0);
368 if(res) {
369 protocol->flags |= FLAG_REQUEST_COMPLETE;
370 report_data(protocol);
371 }else if(GetLastError() != ERROR_IO_PENDING) {
372 protocol->flags |= FLAG_REQUEST_COMPLETE;
373 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
374 report_result(protocol, INET_E_DATA_NOT_AVAILABLE);
375 }
376 }
377
378 return S_OK;
379 }
380
381 HRESULT protocol_read(Protocol *protocol, void *buf, ULONG size, ULONG *read_ret)
382 {
383 ULONG read = 0;
384 BOOL res;
385 HRESULT hres = S_FALSE;
386
387 if(protocol->flags & FLAG_ALL_DATA_READ) {
388 *read_ret = 0;
389 return S_FALSE;
390 }
391
392 if(!(protocol->flags & FLAG_REQUEST_COMPLETE)) {
393 *read_ret = 0;
394 return E_PENDING;
395 }
396
397 while(read < size) {
398 if(protocol->available_bytes) {
399 ULONG len;
400
401 res = InternetReadFile(protocol->request, ((BYTE *)buf)+read,
402 protocol->available_bytes > size-read ? size-read : protocol->available_bytes, &len);
403 if(!res) {
404 WARN("InternetReadFile failed: %d\n", GetLastError());
405 hres = INET_E_DOWNLOAD_FAILURE;
406 report_result(protocol, hres);
407 break;
408 }
409
410 if(!len) {
411 all_data_read(protocol);
412 break;
413 }
414
415 read += len;
416 protocol->current_position += len;
417 protocol->available_bytes -= len;
418 }else {
419 /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
420 * read, so clear the flag _before_ calling so it does not incorrectly get cleared
421 * after the status callback is called */
422 protocol->flags &= ~FLAG_REQUEST_COMPLETE;
423 res = InternetQueryDataAvailable(protocol->request, &protocol->available_bytes, 0, 0);
424 if(!res) {
425 if (GetLastError() == ERROR_IO_PENDING) {
426 hres = E_PENDING;
427 }else {
428 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
429 hres = INET_E_DATA_NOT_AVAILABLE;
430 report_result(protocol, hres);
431 }
432 break;
433 }
434
435 if(!protocol->available_bytes) {
436 all_data_read(protocol);
437 break;
438 }
439 }
440 }
441
442 *read_ret = read;
443
444 if (hres != E_PENDING)
445 protocol->flags |= FLAG_REQUEST_COMPLETE;
446 if(FAILED(hres))
447 return hres;
448
449 return read ? S_OK : S_FALSE;
450 }
451
452 HRESULT protocol_lock_request(Protocol *protocol)
453 {
454 if (!InternetLockRequestFile(protocol->request, &protocol->lock))
455 WARN("InternetLockRequest failed: %d\n", GetLastError());
456
457 return S_OK;
458 }
459
460 HRESULT protocol_unlock_request(Protocol *protocol)
461 {
462 if(!protocol->lock)
463 return S_OK;
464
465 if(!InternetUnlockRequestFile(protocol->lock))
466 WARN("InternetUnlockRequest failed: %d\n", GetLastError());
467 protocol->lock = 0;
468
469 return S_OK;
470 }
471
472 HRESULT protocol_abort(Protocol *protocol, HRESULT reason)
473 {
474 if(!protocol->protocol_sink)
475 return S_OK;
476
477 if(protocol->flags & FLAG_RESULT_REPORTED)
478 return INET_E_RESULT_DISPATCHED;
479
480 report_result(protocol, reason);
481 return S_OK;
482 }
483
484 void protocol_close_connection(Protocol *protocol)
485 {
486 protocol->vtbl->close_connection(protocol);
487
488 if(protocol->request)
489 InternetCloseHandle(protocol->request);
490
491 if(protocol->connection)
492 InternetCloseHandle(protocol->connection);
493
494 if(protocol->post_stream) {
495 IStream_Release(protocol->post_stream);
496 protocol->post_stream = NULL;
497 }
498
499 protocol->flags = 0;
500 }