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