[THEMES]
[reactos.git] / reactos / dll / win32 / qmgr / file.c
1 /*
2 * Queue Manager (BITS) File
3 *
4 * Copyright 2007, 2008 Google (Roy Shea, Dan Hipschman)
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "qmgr.h"
22
23 #include <urlmon.h>
24 #include <wininet.h>
25
26 static inline BackgroundCopyFileImpl *impl_from_IBackgroundCopyFile(IBackgroundCopyFile *iface)
27 {
28 return CONTAINING_RECORD(iface, BackgroundCopyFileImpl, IBackgroundCopyFile_iface);
29 }
30
31 static HRESULT WINAPI BITS_IBackgroundCopyFile_QueryInterface(
32 IBackgroundCopyFile* iface,
33 REFIID riid,
34 void **obj)
35 {
36 BackgroundCopyFileImpl *This = impl_from_IBackgroundCopyFile(iface);
37
38 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
39
40 if (IsEqualGUID(riid, &IID_IUnknown)
41 || IsEqualGUID(riid, &IID_IBackgroundCopyFile))
42 {
43 *obj = iface;
44 IBackgroundCopyFile_AddRef(iface);
45 return S_OK;
46 }
47
48 *obj = NULL;
49 return E_NOINTERFACE;
50 }
51
52 static ULONG WINAPI BITS_IBackgroundCopyFile_AddRef(IBackgroundCopyFile* iface)
53 {
54 BackgroundCopyFileImpl *This = impl_from_IBackgroundCopyFile(iface);
55 ULONG ref = InterlockedIncrement(&This->ref);
56 TRACE("(%p)->(%d)\n", This, ref);
57 return ref;
58 }
59
60 static ULONG WINAPI BITS_IBackgroundCopyFile_Release(
61 IBackgroundCopyFile* iface)
62 {
63 BackgroundCopyFileImpl *This = impl_from_IBackgroundCopyFile(iface);
64 ULONG ref = InterlockedDecrement(&This->ref);
65
66 TRACE("(%p)->(%d)\n", This, ref);
67
68 if (ref == 0)
69 {
70 IBackgroundCopyJob2_Release(&This->owner->IBackgroundCopyJob2_iface);
71 HeapFree(GetProcessHeap(), 0, This->info.LocalName);
72 HeapFree(GetProcessHeap(), 0, This->info.RemoteName);
73 HeapFree(GetProcessHeap(), 0, This);
74 }
75
76 return ref;
77 }
78
79 /* Get the remote name of a background copy file */
80 static HRESULT WINAPI BITS_IBackgroundCopyFile_GetRemoteName(
81 IBackgroundCopyFile* iface,
82 LPWSTR *pVal)
83 {
84 BackgroundCopyFileImpl *This = impl_from_IBackgroundCopyFile(iface);
85 int n = (lstrlenW(This->info.RemoteName) + 1) * sizeof(WCHAR);
86
87 *pVal = CoTaskMemAlloc(n);
88 if (!*pVal)
89 return E_OUTOFMEMORY;
90
91 memcpy(*pVal, This->info.RemoteName, n);
92 return S_OK;
93 }
94
95 static HRESULT WINAPI BITS_IBackgroundCopyFile_GetLocalName(
96 IBackgroundCopyFile* iface,
97 LPWSTR *pVal)
98 {
99 BackgroundCopyFileImpl *This = impl_from_IBackgroundCopyFile(iface);
100 int n = (lstrlenW(This->info.LocalName) + 1) * sizeof(WCHAR);
101
102 *pVal = CoTaskMemAlloc(n);
103 if (!*pVal)
104 return E_OUTOFMEMORY;
105
106 memcpy(*pVal, This->info.LocalName, n);
107 return S_OK;
108 }
109
110 static HRESULT WINAPI BITS_IBackgroundCopyFile_GetProgress(
111 IBackgroundCopyFile* iface,
112 BG_FILE_PROGRESS *pVal)
113 {
114 BackgroundCopyFileImpl *This = impl_from_IBackgroundCopyFile(iface);
115
116 EnterCriticalSection(&This->owner->cs);
117 pVal->BytesTotal = This->fileProgress.BytesTotal;
118 pVal->BytesTransferred = This->fileProgress.BytesTransferred;
119 pVal->Completed = This->fileProgress.Completed;
120 LeaveCriticalSection(&This->owner->cs);
121
122 return S_OK;
123 }
124
125 static const IBackgroundCopyFileVtbl BITS_IBackgroundCopyFile_Vtbl =
126 {
127 BITS_IBackgroundCopyFile_QueryInterface,
128 BITS_IBackgroundCopyFile_AddRef,
129 BITS_IBackgroundCopyFile_Release,
130 BITS_IBackgroundCopyFile_GetRemoteName,
131 BITS_IBackgroundCopyFile_GetLocalName,
132 BITS_IBackgroundCopyFile_GetProgress
133 };
134
135 HRESULT BackgroundCopyFileConstructor(BackgroundCopyJobImpl *owner,
136 LPCWSTR remoteName, LPCWSTR localName,
137 BackgroundCopyFileImpl **file)
138 {
139 BackgroundCopyFileImpl *This;
140 int n;
141
142 TRACE("(%s, %s, %p)\n", debugstr_w(remoteName), debugstr_w(localName), file);
143
144 This = HeapAlloc(GetProcessHeap(), 0, sizeof *This);
145 if (!This)
146 return E_OUTOFMEMORY;
147
148 n = (lstrlenW(remoteName) + 1) * sizeof(WCHAR);
149 This->info.RemoteName = HeapAlloc(GetProcessHeap(), 0, n);
150 if (!This->info.RemoteName)
151 {
152 HeapFree(GetProcessHeap(), 0, This);
153 return E_OUTOFMEMORY;
154 }
155 memcpy(This->info.RemoteName, remoteName, n);
156
157 n = (lstrlenW(localName) + 1) * sizeof(WCHAR);
158 This->info.LocalName = HeapAlloc(GetProcessHeap(), 0, n);
159 if (!This->info.LocalName)
160 {
161 HeapFree(GetProcessHeap(), 0, This->info.RemoteName);
162 HeapFree(GetProcessHeap(), 0, This);
163 return E_OUTOFMEMORY;
164 }
165 memcpy(This->info.LocalName, localName, n);
166
167 This->IBackgroundCopyFile_iface.lpVtbl = &BITS_IBackgroundCopyFile_Vtbl;
168 This->ref = 1;
169
170 This->fileProgress.BytesTotal = BG_SIZE_UNKNOWN;
171 This->fileProgress.BytesTransferred = 0;
172 This->fileProgress.Completed = FALSE;
173 This->owner = owner;
174 IBackgroundCopyJob2_AddRef(&owner->IBackgroundCopyJob2_iface);
175
176 *file = This;
177 return S_OK;
178 }
179
180 static DWORD CALLBACK copyProgressCallback(LARGE_INTEGER totalSize,
181 LARGE_INTEGER totalTransferred,
182 LARGE_INTEGER streamSize,
183 LARGE_INTEGER streamTransferred,
184 DWORD streamNum,
185 DWORD reason,
186 HANDLE srcFile,
187 HANDLE dstFile,
188 LPVOID obj)
189 {
190 BackgroundCopyFileImpl *file = obj;
191 BackgroundCopyJobImpl *job = file->owner;
192 ULONG64 diff;
193
194 EnterCriticalSection(&job->cs);
195 diff = (file->fileProgress.BytesTotal == BG_SIZE_UNKNOWN
196 ? totalTransferred.QuadPart
197 : totalTransferred.QuadPart - file->fileProgress.BytesTransferred);
198 file->fileProgress.BytesTotal = totalSize.QuadPart;
199 file->fileProgress.BytesTransferred = totalTransferred.QuadPart;
200 job->jobProgress.BytesTransferred += diff;
201 LeaveCriticalSection(&job->cs);
202
203 return (job->state == BG_JOB_STATE_TRANSFERRING
204 ? PROGRESS_CONTINUE
205 : PROGRESS_CANCEL);
206 }
207
208 typedef struct
209 {
210 IBindStatusCallback IBindStatusCallback_iface;
211 BackgroundCopyFileImpl *file;
212 LONG ref;
213 } DLBindStatusCallback;
214
215 static inline DLBindStatusCallback *impl_from_IBindStatusCallback(IBindStatusCallback *iface)
216 {
217 return CONTAINING_RECORD(iface, DLBindStatusCallback, IBindStatusCallback_iface);
218 }
219
220 static ULONG WINAPI DLBindStatusCallback_AddRef(IBindStatusCallback *iface)
221 {
222 DLBindStatusCallback *This = impl_from_IBindStatusCallback(iface);
223 return InterlockedIncrement(&This->ref);
224 }
225
226 static ULONG WINAPI DLBindStatusCallback_Release(IBindStatusCallback *iface)
227 {
228 DLBindStatusCallback *This = impl_from_IBindStatusCallback(iface);
229 ULONG ref = InterlockedDecrement(&This->ref);
230
231 if (ref == 0)
232 {
233 IBackgroundCopyFile_Release(&This->file->IBackgroundCopyFile_iface);
234 HeapFree(GetProcessHeap(), 0, This);
235 }
236
237 return ref;
238 }
239
240 static HRESULT WINAPI DLBindStatusCallback_QueryInterface(
241 IBindStatusCallback *iface,
242 REFIID riid,
243 void **ppvObject)
244 {
245 DLBindStatusCallback *This = impl_from_IBindStatusCallback(iface);
246
247 if (IsEqualGUID(riid, &IID_IUnknown)
248 || IsEqualGUID(riid, &IID_IBindStatusCallback))
249 {
250 *ppvObject = &This->IBindStatusCallback_iface;
251 DLBindStatusCallback_AddRef(iface);
252 return S_OK;
253 }
254
255 *ppvObject = NULL;
256 return E_NOINTERFACE;
257 }
258
259 static HRESULT WINAPI DLBindStatusCallback_GetBindInfo(
260 IBindStatusCallback *iface,
261 DWORD *grfBINDF,
262 BINDINFO *pbindinfo)
263 {
264 return E_NOTIMPL;
265 }
266
267 static HRESULT WINAPI DLBindStatusCallback_GetPriority(
268 IBindStatusCallback *iface,
269 LONG *pnPriority)
270 {
271 return E_NOTIMPL;
272 }
273
274 static HRESULT WINAPI DLBindStatusCallback_OnDataAvailable(
275 IBindStatusCallback *iface,
276 DWORD grfBSCF,
277 DWORD dwSize,
278 FORMATETC *pformatetc,
279 STGMEDIUM *pstgmed)
280 {
281 return E_NOTIMPL;
282 }
283
284 static HRESULT WINAPI DLBindStatusCallback_OnLowResource(
285 IBindStatusCallback *iface,
286 DWORD reserved)
287 {
288 return E_NOTIMPL;
289 }
290
291 static HRESULT WINAPI DLBindStatusCallback_OnObjectAvailable(
292 IBindStatusCallback *iface,
293 REFIID riid,
294 IUnknown *punk)
295 {
296 return E_NOTIMPL;
297 }
298
299 static HRESULT WINAPI DLBindStatusCallback_OnProgress(
300 IBindStatusCallback *iface,
301 ULONG progress,
302 ULONG progressMax,
303 ULONG statusCode,
304 LPCWSTR statusText)
305 {
306 DLBindStatusCallback *This = impl_from_IBindStatusCallback(iface);
307 BackgroundCopyFileImpl *file = This->file;
308 BackgroundCopyJobImpl *job = file->owner;
309 ULONG64 diff;
310
311 EnterCriticalSection(&job->cs);
312 diff = (file->fileProgress.BytesTotal == BG_SIZE_UNKNOWN
313 ? progress
314 : progress - file->fileProgress.BytesTransferred);
315 file->fileProgress.BytesTotal = progressMax ? progressMax : BG_SIZE_UNKNOWN;
316 file->fileProgress.BytesTransferred = progress;
317 job->jobProgress.BytesTransferred += diff;
318 LeaveCriticalSection(&job->cs);
319
320 return S_OK;
321 }
322
323 static HRESULT WINAPI DLBindStatusCallback_OnStartBinding(
324 IBindStatusCallback *iface,
325 DWORD dwReserved,
326 IBinding *pib)
327 {
328 return E_NOTIMPL;
329 }
330
331 static HRESULT WINAPI DLBindStatusCallback_OnStopBinding(
332 IBindStatusCallback *iface,
333 HRESULT hresult,
334 LPCWSTR szError)
335 {
336 return E_NOTIMPL;
337 }
338
339 static const IBindStatusCallbackVtbl DLBindStatusCallback_Vtbl =
340 {
341 DLBindStatusCallback_QueryInterface,
342 DLBindStatusCallback_AddRef,
343 DLBindStatusCallback_Release,
344 DLBindStatusCallback_OnStartBinding,
345 DLBindStatusCallback_GetPriority,
346 DLBindStatusCallback_OnLowResource,
347 DLBindStatusCallback_OnProgress,
348 DLBindStatusCallback_OnStopBinding,
349 DLBindStatusCallback_GetBindInfo,
350 DLBindStatusCallback_OnDataAvailable,
351 DLBindStatusCallback_OnObjectAvailable
352 };
353
354 static DLBindStatusCallback *DLBindStatusCallbackConstructor(
355 BackgroundCopyFileImpl *file)
356 {
357 DLBindStatusCallback *This = HeapAlloc(GetProcessHeap(), 0, sizeof *This);
358 if (!This)
359 return NULL;
360
361 This->IBindStatusCallback_iface.lpVtbl = &DLBindStatusCallback_Vtbl;
362 IBackgroundCopyFile_AddRef(&file->IBackgroundCopyFile_iface);
363 This->file = file;
364 This->ref = 1;
365 return This;
366 }
367
368 BOOL processFile(BackgroundCopyFileImpl *file, BackgroundCopyJobImpl *job)
369 {
370 static const WCHAR prefix[] = {'B','I','T', 0};
371 DLBindStatusCallback *callbackObj;
372 WCHAR tmpDir[MAX_PATH];
373 WCHAR tmpName[MAX_PATH];
374 HRESULT hr;
375
376 if (!GetTempPathW(MAX_PATH, tmpDir))
377 {
378 ERR("Couldn't create temp file name: %d\n", GetLastError());
379 /* Guessing on what state this should give us */
380 transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSIENT_ERROR);
381 return FALSE;
382 }
383
384 if (!GetTempFileNameW(tmpDir, prefix, 0, tmpName))
385 {
386 ERR("Couldn't create temp file: %d\n", GetLastError());
387 /* Guessing on what state this should give us */
388 transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSIENT_ERROR);
389 return FALSE;
390 }
391
392 callbackObj = DLBindStatusCallbackConstructor(file);
393 if (!callbackObj)
394 {
395 ERR("Out of memory\n");
396 transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSIENT_ERROR);
397 return FALSE;
398 }
399
400 EnterCriticalSection(&job->cs);
401 file->fileProgress.BytesTotal = BG_SIZE_UNKNOWN;
402 file->fileProgress.BytesTransferred = 0;
403 file->fileProgress.Completed = FALSE;
404 LeaveCriticalSection(&job->cs);
405
406 TRACE("Transferring: %s -> %s -> %s\n",
407 debugstr_w(file->info.RemoteName),
408 debugstr_w(tmpName),
409 debugstr_w(file->info.LocalName));
410
411 transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSFERRING);
412
413 DeleteUrlCacheEntryW(file->info.RemoteName);
414 hr = URLDownloadToFileW(NULL, file->info.RemoteName, tmpName, 0,
415 &callbackObj->IBindStatusCallback_iface);
416 IBindStatusCallback_Release(&callbackObj->IBindStatusCallback_iface);
417 if (hr == INET_E_DOWNLOAD_FAILURE)
418 {
419 TRACE("URLDownload failed, trying local file copy\n");
420 if (!CopyFileExW(file->info.RemoteName, tmpName, copyProgressCallback,
421 file, NULL, 0))
422 {
423 ERR("Local file copy failed: error %d\n", GetLastError());
424 transitionJobState(job, BG_JOB_STATE_TRANSFERRING, BG_JOB_STATE_ERROR);
425 return FALSE;
426 }
427 }
428 else if (FAILED(hr))
429 {
430 ERR("URLDownload failed: eh 0x%08x\n", hr);
431 transitionJobState(job, BG_JOB_STATE_TRANSFERRING, BG_JOB_STATE_ERROR);
432 return FALSE;
433 }
434
435 if (transitionJobState(job, BG_JOB_STATE_TRANSFERRING, BG_JOB_STATE_QUEUED))
436 {
437 lstrcpyW(file->tempFileName, tmpName);
438
439 EnterCriticalSection(&job->cs);
440 file->fileProgress.Completed = TRUE;
441 job->jobProgress.FilesTransferred++;
442 LeaveCriticalSection(&job->cs);
443
444 return TRUE;
445 }
446 else
447 {
448 DeleteFileW(tmpName);
449 return FALSE;
450 }
451 }