Partial merge of condrv_restructure branch r65657.
[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 BackgroundCopyFile_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 BackgroundCopyFile_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 BackgroundCopyFile_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 BackgroundCopyFile_GetRemoteName(
81 IBackgroundCopyFile* iface,
82 LPWSTR *pVal)
83 {
84 BackgroundCopyFileImpl *This = impl_from_IBackgroundCopyFile(iface);
85
86 TRACE("(%p)->(%p)\n", This, pVal);
87
88 return return_strval(This->info.RemoteName, pVal);
89 }
90
91 static HRESULT WINAPI BackgroundCopyFile_GetLocalName(
92 IBackgroundCopyFile* iface,
93 LPWSTR *pVal)
94 {
95 BackgroundCopyFileImpl *This = impl_from_IBackgroundCopyFile(iface);
96
97 TRACE("(%p)->(%p)\n", This, pVal);
98
99 return return_strval(This->info.LocalName, pVal);
100 }
101
102 static HRESULT WINAPI BackgroundCopyFile_GetProgress(
103 IBackgroundCopyFile* iface,
104 BG_FILE_PROGRESS *pVal)
105 {
106 BackgroundCopyFileImpl *This = impl_from_IBackgroundCopyFile(iface);
107
108 TRACE("(%p)->(%p)\n", This, pVal);
109
110 EnterCriticalSection(&This->owner->cs);
111 pVal->BytesTotal = This->fileProgress.BytesTotal;
112 pVal->BytesTransferred = This->fileProgress.BytesTransferred;
113 pVal->Completed = This->fileProgress.Completed;
114 LeaveCriticalSection(&This->owner->cs);
115
116 return S_OK;
117 }
118
119 static const IBackgroundCopyFileVtbl BackgroundCopyFileVtbl =
120 {
121 BackgroundCopyFile_QueryInterface,
122 BackgroundCopyFile_AddRef,
123 BackgroundCopyFile_Release,
124 BackgroundCopyFile_GetRemoteName,
125 BackgroundCopyFile_GetLocalName,
126 BackgroundCopyFile_GetProgress
127 };
128
129 HRESULT BackgroundCopyFileConstructor(BackgroundCopyJobImpl *owner,
130 LPCWSTR remoteName, LPCWSTR localName,
131 BackgroundCopyFileImpl **file)
132 {
133 BackgroundCopyFileImpl *This;
134 int n;
135
136 TRACE("(%s, %s, %p)\n", debugstr_w(remoteName), debugstr_w(localName), file);
137
138 This = HeapAlloc(GetProcessHeap(), 0, sizeof *This);
139 if (!This)
140 return E_OUTOFMEMORY;
141
142 n = (lstrlenW(remoteName) + 1) * sizeof(WCHAR);
143 This->info.RemoteName = HeapAlloc(GetProcessHeap(), 0, n);
144 if (!This->info.RemoteName)
145 {
146 HeapFree(GetProcessHeap(), 0, This);
147 return E_OUTOFMEMORY;
148 }
149 memcpy(This->info.RemoteName, remoteName, n);
150
151 n = (lstrlenW(localName) + 1) * sizeof(WCHAR);
152 This->info.LocalName = HeapAlloc(GetProcessHeap(), 0, n);
153 if (!This->info.LocalName)
154 {
155 HeapFree(GetProcessHeap(), 0, This->info.RemoteName);
156 HeapFree(GetProcessHeap(), 0, This);
157 return E_OUTOFMEMORY;
158 }
159 memcpy(This->info.LocalName, localName, n);
160
161 This->IBackgroundCopyFile_iface.lpVtbl = &BackgroundCopyFileVtbl;
162 This->ref = 1;
163
164 This->fileProgress.BytesTotal = BG_SIZE_UNKNOWN;
165 This->fileProgress.BytesTransferred = 0;
166 This->fileProgress.Completed = FALSE;
167 This->owner = owner;
168 IBackgroundCopyJob2_AddRef(&owner->IBackgroundCopyJob2_iface);
169
170 *file = This;
171 return S_OK;
172 }
173
174 static DWORD CALLBACK copyProgressCallback(LARGE_INTEGER totalSize,
175 LARGE_INTEGER totalTransferred,
176 LARGE_INTEGER streamSize,
177 LARGE_INTEGER streamTransferred,
178 DWORD streamNum,
179 DWORD reason,
180 HANDLE srcFile,
181 HANDLE dstFile,
182 LPVOID obj)
183 {
184 BackgroundCopyFileImpl *file = obj;
185 BackgroundCopyJobImpl *job = file->owner;
186 ULONG64 diff;
187
188 EnterCriticalSection(&job->cs);
189 diff = (file->fileProgress.BytesTotal == BG_SIZE_UNKNOWN
190 ? totalTransferred.QuadPart
191 : totalTransferred.QuadPart - file->fileProgress.BytesTransferred);
192 file->fileProgress.BytesTotal = totalSize.QuadPart;
193 file->fileProgress.BytesTransferred = totalTransferred.QuadPart;
194 job->jobProgress.BytesTransferred += diff;
195 LeaveCriticalSection(&job->cs);
196
197 return (job->state == BG_JOB_STATE_TRANSFERRING
198 ? PROGRESS_CONTINUE
199 : PROGRESS_CANCEL);
200 }
201
202 typedef struct
203 {
204 IBindStatusCallback IBindStatusCallback_iface;
205 BackgroundCopyFileImpl *file;
206 LONG ref;
207 } DLBindStatusCallback;
208
209 static inline DLBindStatusCallback *impl_from_IBindStatusCallback(IBindStatusCallback *iface)
210 {
211 return CONTAINING_RECORD(iface, DLBindStatusCallback, IBindStatusCallback_iface);
212 }
213
214 static HRESULT WINAPI DLBindStatusCallback_QueryInterface(
215 IBindStatusCallback *iface,
216 REFIID riid,
217 void **ppvObject)
218 {
219 DLBindStatusCallback *This = impl_from_IBindStatusCallback(iface);
220
221 if (IsEqualGUID(riid, &IID_IUnknown)
222 || IsEqualGUID(riid, &IID_IBindStatusCallback))
223 {
224 *ppvObject = &This->IBindStatusCallback_iface;
225 IBindStatusCallback_AddRef(iface);
226 return S_OK;
227 }
228
229 *ppvObject = NULL;
230 return E_NOINTERFACE;
231 }
232
233 static ULONG WINAPI DLBindStatusCallback_AddRef(IBindStatusCallback *iface)
234 {
235 DLBindStatusCallback *This = impl_from_IBindStatusCallback(iface);
236 return InterlockedIncrement(&This->ref);
237 }
238
239 static ULONG WINAPI DLBindStatusCallback_Release(IBindStatusCallback *iface)
240 {
241 DLBindStatusCallback *This = impl_from_IBindStatusCallback(iface);
242 ULONG ref = InterlockedDecrement(&This->ref);
243
244 if (ref == 0)
245 {
246 IBackgroundCopyFile_Release(&This->file->IBackgroundCopyFile_iface);
247 HeapFree(GetProcessHeap(), 0, This);
248 }
249
250 return ref;
251 }
252
253 static HRESULT WINAPI DLBindStatusCallback_GetBindInfo(
254 IBindStatusCallback *iface,
255 DWORD *grfBINDF,
256 BINDINFO *pbindinfo)
257 {
258 return E_NOTIMPL;
259 }
260
261 static HRESULT WINAPI DLBindStatusCallback_GetPriority(
262 IBindStatusCallback *iface,
263 LONG *pnPriority)
264 {
265 return E_NOTIMPL;
266 }
267
268 static HRESULT WINAPI DLBindStatusCallback_OnDataAvailable(
269 IBindStatusCallback *iface,
270 DWORD grfBSCF,
271 DWORD dwSize,
272 FORMATETC *pformatetc,
273 STGMEDIUM *pstgmed)
274 {
275 return E_NOTIMPL;
276 }
277
278 static HRESULT WINAPI DLBindStatusCallback_OnLowResource(
279 IBindStatusCallback *iface,
280 DWORD reserved)
281 {
282 return E_NOTIMPL;
283 }
284
285 static HRESULT WINAPI DLBindStatusCallback_OnObjectAvailable(
286 IBindStatusCallback *iface,
287 REFIID riid,
288 IUnknown *punk)
289 {
290 return E_NOTIMPL;
291 }
292
293 static HRESULT WINAPI DLBindStatusCallback_OnProgress(
294 IBindStatusCallback *iface,
295 ULONG progress,
296 ULONG progressMax,
297 ULONG statusCode,
298 LPCWSTR statusText)
299 {
300 DLBindStatusCallback *This = impl_from_IBindStatusCallback(iface);
301 BackgroundCopyFileImpl *file = This->file;
302 BackgroundCopyJobImpl *job = file->owner;
303 ULONG64 diff;
304
305 EnterCriticalSection(&job->cs);
306 diff = (file->fileProgress.BytesTotal == BG_SIZE_UNKNOWN
307 ? progress
308 : progress - file->fileProgress.BytesTransferred);
309 file->fileProgress.BytesTotal = progressMax ? progressMax : BG_SIZE_UNKNOWN;
310 file->fileProgress.BytesTransferred = progress;
311 job->jobProgress.BytesTransferred += diff;
312 LeaveCriticalSection(&job->cs);
313
314 return S_OK;
315 }
316
317 static HRESULT WINAPI DLBindStatusCallback_OnStartBinding(
318 IBindStatusCallback *iface,
319 DWORD dwReserved,
320 IBinding *pib)
321 {
322 return E_NOTIMPL;
323 }
324
325 static HRESULT WINAPI DLBindStatusCallback_OnStopBinding(
326 IBindStatusCallback *iface,
327 HRESULT hresult,
328 LPCWSTR szError)
329 {
330 return E_NOTIMPL;
331 }
332
333 static const IBindStatusCallbackVtbl DLBindStatusCallback_Vtbl =
334 {
335 DLBindStatusCallback_QueryInterface,
336 DLBindStatusCallback_AddRef,
337 DLBindStatusCallback_Release,
338 DLBindStatusCallback_OnStartBinding,
339 DLBindStatusCallback_GetPriority,
340 DLBindStatusCallback_OnLowResource,
341 DLBindStatusCallback_OnProgress,
342 DLBindStatusCallback_OnStopBinding,
343 DLBindStatusCallback_GetBindInfo,
344 DLBindStatusCallback_OnDataAvailable,
345 DLBindStatusCallback_OnObjectAvailable
346 };
347
348 static DLBindStatusCallback *DLBindStatusCallbackConstructor(
349 BackgroundCopyFileImpl *file)
350 {
351 DLBindStatusCallback *This = HeapAlloc(GetProcessHeap(), 0, sizeof *This);
352 if (!This)
353 return NULL;
354
355 This->IBindStatusCallback_iface.lpVtbl = &DLBindStatusCallback_Vtbl;
356 IBackgroundCopyFile_AddRef(&file->IBackgroundCopyFile_iface);
357 This->file = file;
358 This->ref = 1;
359 return This;
360 }
361
362 BOOL processFile(BackgroundCopyFileImpl *file, BackgroundCopyJobImpl *job)
363 {
364 static const WCHAR prefix[] = {'B','I','T', 0};
365 DLBindStatusCallback *callbackObj;
366 WCHAR tmpDir[MAX_PATH];
367 WCHAR tmpName[MAX_PATH];
368 HRESULT hr;
369
370 if (!GetTempPathW(MAX_PATH, tmpDir))
371 {
372 ERR("Couldn't create temp file name: %d\n", GetLastError());
373 /* Guessing on what state this should give us */
374 transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSIENT_ERROR);
375 return FALSE;
376 }
377
378 if (!GetTempFileNameW(tmpDir, prefix, 0, tmpName))
379 {
380 ERR("Couldn't create temp file: %d\n", GetLastError());
381 /* Guessing on what state this should give us */
382 transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSIENT_ERROR);
383 return FALSE;
384 }
385
386 callbackObj = DLBindStatusCallbackConstructor(file);
387 if (!callbackObj)
388 {
389 ERR("Out of memory\n");
390 transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSIENT_ERROR);
391 return FALSE;
392 }
393
394 EnterCriticalSection(&job->cs);
395 file->fileProgress.BytesTotal = BG_SIZE_UNKNOWN;
396 file->fileProgress.BytesTransferred = 0;
397 file->fileProgress.Completed = FALSE;
398 LeaveCriticalSection(&job->cs);
399
400 TRACE("Transferring: %s -> %s -> %s\n",
401 debugstr_w(file->info.RemoteName),
402 debugstr_w(tmpName),
403 debugstr_w(file->info.LocalName));
404
405 transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSFERRING);
406
407 DeleteUrlCacheEntryW(file->info.RemoteName);
408 hr = URLDownloadToFileW(NULL, file->info.RemoteName, tmpName, 0,
409 &callbackObj->IBindStatusCallback_iface);
410 IBindStatusCallback_Release(&callbackObj->IBindStatusCallback_iface);
411 if (hr == INET_E_DOWNLOAD_FAILURE)
412 {
413 TRACE("URLDownload failed, trying local file copy\n");
414 if (!CopyFileExW(file->info.RemoteName, tmpName, copyProgressCallback,
415 file, NULL, 0))
416 {
417 ERR("Local file copy failed: error %d\n", GetLastError());
418 transitionJobState(job, BG_JOB_STATE_TRANSFERRING, BG_JOB_STATE_ERROR);
419 return FALSE;
420 }
421 }
422 else if (FAILED(hr))
423 {
424 ERR("URLDownload failed: eh 0x%08x\n", hr);
425 transitionJobState(job, BG_JOB_STATE_TRANSFERRING, BG_JOB_STATE_ERROR);
426 return FALSE;
427 }
428
429 if (transitionJobState(job, BG_JOB_STATE_TRANSFERRING, BG_JOB_STATE_QUEUED))
430 {
431 lstrcpyW(file->tempFileName, tmpName);
432
433 EnterCriticalSection(&job->cs);
434 file->fileProgress.Completed = TRUE;
435 job->jobProgress.FilesTransferred++;
436 LeaveCriticalSection(&job->cs);
437
438 return TRUE;
439 }
440 else
441 {
442 DeleteFileW(tmpName);
443 return FALSE;
444 }
445 }