Create a branch for working on csrss and co.
[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 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 = (LPVOID)BINDSTATUS_ENDDOWNLOADCOMPONENTS;
93 else
94 data.pData = (LPVOID)BINDSTATUS_DOWNLOADINGDATA;
95
96 }else {
97 protocol->flags |= FLAG_ERROR;
98 data.pData = (LPVOID)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 HRESULT hres;
290
291 if (!data) {
292 WARN("Expected pProtocolData to be non-NULL\n");
293 return S_OK;
294 }
295
296 if(!protocol->request) {
297 WARN("Expected request to be non-NULL\n");
298 return S_OK;
299 }
300
301 if(!protocol->protocol_sink) {
302 WARN("Expected IInternetProtocolSink pointer to be non-NULL\n");
303 return S_OK;
304 }
305
306 if(protocol->flags & FLAG_ERROR) {
307 protocol->flags &= ~FLAG_ERROR;
308 protocol->vtbl->on_error(protocol, (DWORD)data->pData);
309 return S_OK;
310 }
311
312 if(protocol->post_stream)
313 return write_post_stream(protocol);
314
315 if(data->pData == (LPVOID)BINDSTATUS_DOWNLOADINGDATA) {
316 hres = protocol->vtbl->start_downloading(protocol);
317 if(FAILED(hres)) {
318 protocol_close_connection(protocol);
319 report_result(protocol, hres);
320 return S_OK;
321 }
322
323 if(protocol->bindf & BINDF_NEEDFILE) {
324 WCHAR cache_file[MAX_PATH];
325 DWORD buflen = sizeof(cache_file);
326
327 if(InternetQueryOptionW(protocol->request, INTERNET_OPTION_DATAFILE_NAME,
328 cache_file, &buflen)) {
329 report_progress(protocol, BINDSTATUS_CACHEFILENAMEAVAILABLE, cache_file);
330 }else {
331 FIXME("Could not get cache file\n");
332 }
333 }
334
335 protocol->flags |= FLAG_FIRST_CONTINUE_COMPLETE;
336 }
337
338 if(data->pData >= (LPVOID)BINDSTATUS_DOWNLOADINGDATA && !protocol->available_bytes) {
339 BOOL res;
340
341 /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
342 * read, so clear the flag _before_ calling so it does not incorrectly get cleared
343 * after the status callback is called */
344 protocol->flags &= ~FLAG_REQUEST_COMPLETE;
345 res = InternetQueryDataAvailable(protocol->request, &protocol->available_bytes, 0, 0);
346 if(res) {
347 protocol->flags |= FLAG_REQUEST_COMPLETE;
348 report_data(protocol);
349 }else if(GetLastError() != ERROR_IO_PENDING) {
350 protocol->flags |= FLAG_REQUEST_COMPLETE;
351 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
352 report_result(protocol, INET_E_DATA_NOT_AVAILABLE);
353 }
354 }
355
356 return S_OK;
357 }
358
359 HRESULT protocol_read(Protocol *protocol, void *buf, ULONG size, ULONG *read_ret)
360 {
361 ULONG read = 0;
362 BOOL res;
363 HRESULT hres = S_FALSE;
364
365 if(protocol->flags & FLAG_ALL_DATA_READ) {
366 *read_ret = 0;
367 return S_FALSE;
368 }
369
370 if(!(protocol->flags & FLAG_REQUEST_COMPLETE) || !protocol->available_bytes) {
371 *read_ret = 0;
372 return E_PENDING;
373 }
374
375 while(read < size && protocol->available_bytes) {
376 ULONG len;
377
378 res = InternetReadFile(protocol->request, ((BYTE *)buf)+read,
379 protocol->available_bytes > size-read ? size-read : protocol->available_bytes, &len);
380 if(!res) {
381 WARN("InternetReadFile failed: %d\n", GetLastError());
382 hres = INET_E_DOWNLOAD_FAILURE;
383 report_result(protocol, hres);
384 break;
385 }
386
387 if(!len) {
388 all_data_read(protocol);
389 break;
390 }
391
392 read += len;
393 protocol->current_position += len;
394 protocol->available_bytes -= len;
395
396 if(!protocol->available_bytes) {
397 /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
398 * read, so clear the flag _before_ calling so it does not incorrectly get cleared
399 * after the status callback is called */
400 protocol->flags &= ~FLAG_REQUEST_COMPLETE;
401 res = InternetQueryDataAvailable(protocol->request, &protocol->available_bytes, 0, 0);
402 if(!res) {
403 if (GetLastError() == ERROR_IO_PENDING) {
404 hres = E_PENDING;
405 }else {
406 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
407 hres = INET_E_DATA_NOT_AVAILABLE;
408 report_result(protocol, hres);
409 }
410 break;
411 }
412
413 if(!protocol->available_bytes) {
414 all_data_read(protocol);
415 break;
416 }
417 }
418 }
419
420 *read_ret = read;
421
422 if (hres != E_PENDING)
423 protocol->flags |= FLAG_REQUEST_COMPLETE;
424 if(FAILED(hres))
425 return hres;
426
427 return read ? S_OK : S_FALSE;
428 }
429
430 HRESULT protocol_lock_request(Protocol *protocol)
431 {
432 if (!InternetLockRequestFile(protocol->request, &protocol->lock))
433 WARN("InternetLockRequest failed: %d\n", GetLastError());
434
435 return S_OK;
436 }
437
438 HRESULT protocol_unlock_request(Protocol *protocol)
439 {
440 if(!protocol->lock)
441 return S_OK;
442
443 if(!InternetUnlockRequestFile(protocol->lock))
444 WARN("InternetUnlockRequest failed: %d\n", GetLastError());
445 protocol->lock = 0;
446
447 return S_OK;
448 }
449
450 HRESULT protocol_abort(Protocol *protocol, HRESULT reason)
451 {
452 if(!protocol->protocol_sink)
453 return S_OK;
454
455 if(protocol->flags & FLAG_RESULT_REPORTED)
456 return INET_E_RESULT_DISPATCHED;
457
458 report_result(protocol, reason);
459 return S_OK;
460 }
461
462 void protocol_close_connection(Protocol *protocol)
463 {
464 protocol->vtbl->close_connection(protocol);
465
466 if(protocol->request)
467 InternetCloseHandle(protocol->request);
468
469 if(protocol->connection)
470 InternetCloseHandle(protocol->connection);
471
472 if(protocol->post_stream) {
473 IStream_Release(protocol->post_stream);
474 protocol->post_stream = NULL;
475 }
476
477 protocol->flags = 0;
478 }