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