blob: 40a9c280074b052eb62693cb39473e1511ce8f2d [file] [log] [blame]
Roy Shea128654b2008-02-26 17:50:43 -08001/*
2 * Queue Manager (BITS) File
3 *
Dan Hipschman2f2b3302008-03-13 15:57:19 -07004 * Copyright 2007, 2008 Google (Roy Shea, Dan Hipschman)
Roy Shea128654b2008-02-26 17:50:43 -08005 *
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
Dan Hipschman2f2b3302008-03-13 15:57:19 -070021#include <stdarg.h>
22
Dan Hipschman9617b322008-03-13 15:57:54 -070023#define COBJMACROS
24
Dan Hipschman2f2b3302008-03-13 15:57:19 -070025#include "windef.h"
26#include "winbase.h"
27#include "winuser.h"
28#include "winreg.h"
29#include "ole2.h"
30#include "urlmon.h"
31#include "wininet.h"
32
Roy Shea128654b2008-02-26 17:50:43 -080033#include "qmgr.h"
34#include "wine/debug.h"
35
36WINE_DEFAULT_DEBUG_CHANNEL(qmgr);
37
38static void BackgroundCopyFileDestructor(BackgroundCopyFileImpl *This)
39{
Dan Hipschmanfd499df2008-03-12 11:56:13 -070040 IBackgroundCopyJob_Release((IBackgroundCopyJob *) This->owner);
Roy Shea128654b2008-02-26 17:50:43 -080041 HeapFree(GetProcessHeap(), 0, This->info.LocalName);
42 HeapFree(GetProcessHeap(), 0, This->info.RemoteName);
43 HeapFree(GetProcessHeap(), 0, This);
44}
45
46static ULONG WINAPI BITS_IBackgroundCopyFile_AddRef(IBackgroundCopyFile* iface)
47{
48 BackgroundCopyFileImpl *This = (BackgroundCopyFileImpl *) iface;
49 return InterlockedIncrement(&This->ref);
50}
51
52static HRESULT WINAPI BITS_IBackgroundCopyFile_QueryInterface(
53 IBackgroundCopyFile* iface,
54 REFIID riid,
55 void **ppvObject)
56{
57 BackgroundCopyFileImpl *This = (BackgroundCopyFileImpl *) iface;
58
59 if (IsEqualGUID(riid, &IID_IUnknown)
60 || IsEqualGUID(riid, &IID_IBackgroundCopyFile))
61 {
62 *ppvObject = &This->lpVtbl;
63 BITS_IBackgroundCopyFile_AddRef(iface);
64 return S_OK;
65 }
66
67 *ppvObject = NULL;
68 return E_NOINTERFACE;
69}
70
71
72static ULONG WINAPI BITS_IBackgroundCopyFile_Release(
73 IBackgroundCopyFile* iface)
74{
75 BackgroundCopyFileImpl *This = (BackgroundCopyFileImpl *) iface;
76 ULONG ref = InterlockedDecrement(&This->ref);
77
78 if (ref == 0)
79 BackgroundCopyFileDestructor(This);
80
81 return ref;
82}
83
Roy Shea4866d1b2008-02-28 18:59:26 -080084/* Get the remote name of a background copy file */
Roy Shea128654b2008-02-26 17:50:43 -080085static HRESULT WINAPI BITS_IBackgroundCopyFile_GetRemoteName(
86 IBackgroundCopyFile* iface,
87 LPWSTR *pVal)
88{
Roy Shea4866d1b2008-02-28 18:59:26 -080089 BackgroundCopyFileImpl *This = (BackgroundCopyFileImpl *) iface;
90 int n = (lstrlenW(This->info.RemoteName) + 1) * sizeof(WCHAR);
91
92 *pVal = CoTaskMemAlloc(n);
93 if (!*pVal)
94 return E_OUTOFMEMORY;
95
96 memcpy(*pVal, This->info.RemoteName, n);
97 return S_OK;
Roy Shea128654b2008-02-26 17:50:43 -080098}
99
100static HRESULT WINAPI BITS_IBackgroundCopyFile_GetLocalName(
101 IBackgroundCopyFile* iface,
102 LPWSTR *pVal)
103{
Roy Shea4866d1b2008-02-28 18:59:26 -0800104 BackgroundCopyFileImpl *This = (BackgroundCopyFileImpl *) iface;
105 int n = (lstrlenW(This->info.LocalName) + 1) * sizeof(WCHAR);
106
107 *pVal = CoTaskMemAlloc(n);
108 if (!*pVal)
109 return E_OUTOFMEMORY;
110
111 memcpy(*pVal, This->info.LocalName, n);
112 return S_OK;
Roy Shea128654b2008-02-26 17:50:43 -0800113}
114
115static HRESULT WINAPI BITS_IBackgroundCopyFile_GetProgress(
116 IBackgroundCopyFile* iface,
117 BG_FILE_PROGRESS *pVal)
118{
Roy Sheae6cbde12008-02-28 19:00:12 -0800119 BackgroundCopyFileImpl *This = (BackgroundCopyFileImpl *) iface;
120
Dan Hipschmanfd499df2008-03-12 11:56:13 -0700121 EnterCriticalSection(&This->owner->cs);
Roy Sheae6cbde12008-02-28 19:00:12 -0800122 pVal->BytesTotal = This->fileProgress.BytesTotal;
123 pVal->BytesTransferred = This->fileProgress.BytesTransferred;
124 pVal->Completed = This->fileProgress.Completed;
Dan Hipschmanfd499df2008-03-12 11:56:13 -0700125 LeaveCriticalSection(&This->owner->cs);
Roy Sheae6cbde12008-02-28 19:00:12 -0800126
127 return S_OK;
Roy Shea128654b2008-02-26 17:50:43 -0800128}
129
130static const IBackgroundCopyFileVtbl BITS_IBackgroundCopyFile_Vtbl =
131{
132 BITS_IBackgroundCopyFile_QueryInterface,
133 BITS_IBackgroundCopyFile_AddRef,
134 BITS_IBackgroundCopyFile_Release,
135 BITS_IBackgroundCopyFile_GetRemoteName,
136 BITS_IBackgroundCopyFile_GetLocalName,
137 BITS_IBackgroundCopyFile_GetProgress
138};
139
Dan Hipschmanfd499df2008-03-12 11:56:13 -0700140HRESULT BackgroundCopyFileConstructor(BackgroundCopyJobImpl *owner,
141 LPCWSTR remoteName, LPCWSTR localName,
142 LPVOID *ppObj)
Roy Shea128654b2008-02-26 17:50:43 -0800143{
144 BackgroundCopyFileImpl *This;
145 int n;
146
147 TRACE("(%s,%s,%p)\n", debugstr_w(remoteName),
148 debugstr_w(localName), ppObj);
149
150 This = HeapAlloc(GetProcessHeap(), 0, sizeof *This);
151 if (!This)
152 return E_OUTOFMEMORY;
153
154 n = (lstrlenW(remoteName) + 1) * sizeof(WCHAR);
155 This->info.RemoteName = HeapAlloc(GetProcessHeap(), 0, n);
156 if (!This->info.RemoteName)
157 {
158 HeapFree(GetProcessHeap(), 0, This);
159 return E_OUTOFMEMORY;
160 }
161 memcpy(This->info.RemoteName, remoteName, n);
162
163 n = (lstrlenW(localName) + 1) * sizeof(WCHAR);
164 This->info.LocalName = HeapAlloc(GetProcessHeap(), 0, n);
165 if (!This->info.LocalName)
166 {
167 HeapFree(GetProcessHeap(), 0, This->info.RemoteName);
168 HeapFree(GetProcessHeap(), 0, This);
169 return E_OUTOFMEMORY;
170 }
171 memcpy(This->info.LocalName, localName, n);
172
173 This->lpVtbl = &BITS_IBackgroundCopyFile_Vtbl;
174 This->ref = 1;
175
Roy Sheae6cbde12008-02-28 19:00:12 -0800176 This->fileProgress.BytesTotal = BG_SIZE_UNKNOWN;
177 This->fileProgress.BytesTransferred = 0;
178 This->fileProgress.Completed = FALSE;
Dan Hipschmanfd499df2008-03-12 11:56:13 -0700179 This->owner = owner;
180 IBackgroundCopyJob_AddRef((IBackgroundCopyJob *) owner);
Roy Sheae6cbde12008-02-28 19:00:12 -0800181
Roy Shea128654b2008-02-26 17:50:43 -0800182 *ppObj = &This->lpVtbl;
183 return S_OK;
184}
Dan Hipschmanb329a2e2008-03-13 15:54:35 -0700185
186static DWORD CALLBACK copyProgressCallback(LARGE_INTEGER totalSize,
187 LARGE_INTEGER totalTransferred,
188 LARGE_INTEGER streamSize,
189 LARGE_INTEGER streamTransferred,
190 DWORD streamNum,
191 DWORD reason,
192 HANDLE srcFile,
193 HANDLE dstFile,
194 LPVOID obj)
195{
Michael Stefaniuc0fed8b92009-01-22 09:51:39 +0100196 BackgroundCopyFileImpl *file = obj;
Dan Hipschmanb329a2e2008-03-13 15:54:35 -0700197 BackgroundCopyJobImpl *job = file->owner;
198 ULONG64 diff;
199
200 EnterCriticalSection(&job->cs);
201 diff = (file->fileProgress.BytesTotal == BG_SIZE_UNKNOWN
202 ? totalTransferred.QuadPart
203 : totalTransferred.QuadPart - file->fileProgress.BytesTransferred);
204 file->fileProgress.BytesTotal = totalSize.QuadPart;
205 file->fileProgress.BytesTransferred = totalTransferred.QuadPart;
206 job->jobProgress.BytesTransferred += diff;
207 LeaveCriticalSection(&job->cs);
208
209 return (job->state == BG_JOB_STATE_TRANSFERRING
210 ? PROGRESS_CONTINUE
211 : PROGRESS_CANCEL);
212}
213
Dan Hipschman9617b322008-03-13 15:57:54 -0700214typedef struct
215{
Michael Stefaniuc21c1cbf2010-12-04 22:16:22 +0100216 IBindStatusCallback IBindStatusCallback_iface;
Dan Hipschman9617b322008-03-13 15:57:54 -0700217 BackgroundCopyFileImpl *file;
218 LONG ref;
219} DLBindStatusCallback;
220
Michael Stefaniuc21c1cbf2010-12-04 22:16:22 +0100221static inline DLBindStatusCallback *impl_from_IBindStatusCallback(IBindStatusCallback *iface)
222{
223 return CONTAINING_RECORD(iface, DLBindStatusCallback, IBindStatusCallback_iface);
224}
225
Dan Hipschman9617b322008-03-13 15:57:54 -0700226static ULONG WINAPI DLBindStatusCallback_AddRef(IBindStatusCallback *iface)
227{
Michael Stefaniuc21c1cbf2010-12-04 22:16:22 +0100228 DLBindStatusCallback *This = impl_from_IBindStatusCallback(iface);
Dan Hipschman9617b322008-03-13 15:57:54 -0700229 return InterlockedIncrement(&This->ref);
230}
231
232static ULONG WINAPI DLBindStatusCallback_Release(IBindStatusCallback *iface)
233{
Michael Stefaniuc21c1cbf2010-12-04 22:16:22 +0100234 DLBindStatusCallback *This = impl_from_IBindStatusCallback(iface);
Dan Hipschman9617b322008-03-13 15:57:54 -0700235 ULONG ref = InterlockedDecrement(&This->ref);
236
237 if (ref == 0)
238 {
239 IBackgroundCopyFile_Release((IBackgroundCopyFile *) This->file);
240 HeapFree(GetProcessHeap(), 0, This);
241 }
242
243 return ref;
244}
245
246static HRESULT WINAPI DLBindStatusCallback_QueryInterface(
247 IBindStatusCallback *iface,
248 REFIID riid,
249 void **ppvObject)
250{
Michael Stefaniuc21c1cbf2010-12-04 22:16:22 +0100251 DLBindStatusCallback *This = impl_from_IBindStatusCallback(iface);
Dan Hipschman9617b322008-03-13 15:57:54 -0700252
253 if (IsEqualGUID(riid, &IID_IUnknown)
254 || IsEqualGUID(riid, &IID_IBindStatusCallback))
255 {
Michael Stefaniuc21c1cbf2010-12-04 22:16:22 +0100256 *ppvObject = &This->IBindStatusCallback_iface;
Dan Hipschman9617b322008-03-13 15:57:54 -0700257 DLBindStatusCallback_AddRef(iface);
258 return S_OK;
259 }
260
261 *ppvObject = NULL;
262 return E_NOINTERFACE;
263}
264
265static HRESULT WINAPI DLBindStatusCallback_GetBindInfo(
266 IBindStatusCallback *iface,
267 DWORD *grfBINDF,
268 BINDINFO *pbindinfo)
269{
270 return E_NOTIMPL;
271}
272
273static HRESULT WINAPI DLBindStatusCallback_GetPriority(
274 IBindStatusCallback *iface,
275 LONG *pnPriority)
276{
277 return E_NOTIMPL;
278}
279
280static HRESULT WINAPI DLBindStatusCallback_OnDataAvailable(
281 IBindStatusCallback *iface,
282 DWORD grfBSCF,
283 DWORD dwSize,
284 FORMATETC *pformatetc,
285 STGMEDIUM *pstgmed)
286{
287 return E_NOTIMPL;
288}
289
290static HRESULT WINAPI DLBindStatusCallback_OnLowResource(
291 IBindStatusCallback *iface,
292 DWORD reserved)
293{
294 return E_NOTIMPL;
295}
296
297static HRESULT WINAPI DLBindStatusCallback_OnObjectAvailable(
298 IBindStatusCallback *iface,
299 REFIID riid,
300 IUnknown *punk)
301{
302 return E_NOTIMPL;
303}
304
305static HRESULT WINAPI DLBindStatusCallback_OnProgress(
306 IBindStatusCallback *iface,
307 ULONG progress,
308 ULONG progressMax,
309 ULONG statusCode,
310 LPCWSTR statusText)
311{
Michael Stefaniuc21c1cbf2010-12-04 22:16:22 +0100312 DLBindStatusCallback *This = impl_from_IBindStatusCallback(iface);
Dan Hipschman9617b322008-03-13 15:57:54 -0700313 BackgroundCopyFileImpl *file = This->file;
314 BackgroundCopyJobImpl *job = file->owner;
315 ULONG64 diff;
316
317 EnterCriticalSection(&job->cs);
318 diff = (file->fileProgress.BytesTotal == BG_SIZE_UNKNOWN
319 ? progress
320 : progress - file->fileProgress.BytesTransferred);
321 file->fileProgress.BytesTotal = progressMax ? progressMax : BG_SIZE_UNKNOWN;
322 file->fileProgress.BytesTransferred = progress;
323 job->jobProgress.BytesTransferred += diff;
324 LeaveCriticalSection(&job->cs);
325
326 return S_OK;
327}
328
329static HRESULT WINAPI DLBindStatusCallback_OnStartBinding(
330 IBindStatusCallback *iface,
331 DWORD dwReserved,
332 IBinding *pib)
333{
334 return E_NOTIMPL;
335}
336
337static HRESULT WINAPI DLBindStatusCallback_OnStopBinding(
338 IBindStatusCallback *iface,
339 HRESULT hresult,
340 LPCWSTR szError)
341{
342 return E_NOTIMPL;
343}
344
345static const IBindStatusCallbackVtbl DLBindStatusCallback_Vtbl =
346{
347 DLBindStatusCallback_QueryInterface,
348 DLBindStatusCallback_AddRef,
349 DLBindStatusCallback_Release,
350 DLBindStatusCallback_OnStartBinding,
351 DLBindStatusCallback_GetPriority,
352 DLBindStatusCallback_OnLowResource,
353 DLBindStatusCallback_OnProgress,
354 DLBindStatusCallback_OnStopBinding,
355 DLBindStatusCallback_GetBindInfo,
356 DLBindStatusCallback_OnDataAvailable,
357 DLBindStatusCallback_OnObjectAvailable
358};
359
360static DLBindStatusCallback *DLBindStatusCallbackConstructor(
361 BackgroundCopyFileImpl *file)
362{
Andrew Talbot801c9692008-05-10 16:25:29 +0100363 DLBindStatusCallback *This = HeapAlloc(GetProcessHeap(), 0, sizeof *This);
Dan Hipschman9617b322008-03-13 15:57:54 -0700364 if (!This)
365 return NULL;
366
Michael Stefaniuc21c1cbf2010-12-04 22:16:22 +0100367 This->IBindStatusCallback_iface.lpVtbl = &DLBindStatusCallback_Vtbl;
Dan Hipschman9617b322008-03-13 15:57:54 -0700368 IBackgroundCopyFile_AddRef((IBackgroundCopyFile *) file);
369 This->file = file;
370 This->ref = 1;
371 return This;
372}
373
Dan Hipschmanb329a2e2008-03-13 15:54:35 -0700374BOOL processFile(BackgroundCopyFileImpl *file, BackgroundCopyJobImpl *job)
375{
Andrew Talbote9d1d5d2008-06-19 22:51:55 +0100376 static const WCHAR prefix[] = {'B','I','T', 0};
Michael Stefaniuc21c1cbf2010-12-04 22:16:22 +0100377 DLBindStatusCallback *callbackObj;
Dan Hipschmanb329a2e2008-03-13 15:54:35 -0700378 WCHAR tmpDir[MAX_PATH];
379 WCHAR tmpName[MAX_PATH];
Dan Hipschman2f2b3302008-03-13 15:57:19 -0700380 HRESULT hr;
Dan Hipschmanb329a2e2008-03-13 15:54:35 -0700381
382 if (!GetTempPathW(MAX_PATH, tmpDir))
383 {
384 ERR("Couldn't create temp file name: %d\n", GetLastError());
385 /* Guessing on what state this should give us */
386 transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSIENT_ERROR);
387 return FALSE;
388 }
389
390 if (!GetTempFileNameW(tmpDir, prefix, 0, tmpName))
391 {
392 ERR("Couldn't create temp file: %d\n", GetLastError());
393 /* Guessing on what state this should give us */
394 transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSIENT_ERROR);
395 return FALSE;
396 }
397
Michael Stefaniuc21c1cbf2010-12-04 22:16:22 +0100398 callbackObj = DLBindStatusCallbackConstructor(file);
Dan Hipschman9617b322008-03-13 15:57:54 -0700399 if (!callbackObj)
400 {
401 ERR("Out of memory\n");
402 transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSIENT_ERROR);
403 return FALSE;
404 }
405
Dan Hipschmanb329a2e2008-03-13 15:54:35 -0700406 EnterCriticalSection(&job->cs);
407 file->fileProgress.BytesTotal = BG_SIZE_UNKNOWN;
408 file->fileProgress.BytesTransferred = 0;
409 file->fileProgress.Completed = FALSE;
410 LeaveCriticalSection(&job->cs);
411
412 TRACE("Transferring: %s -> %s -> %s\n",
413 debugstr_w(file->info.RemoteName),
414 debugstr_w(tmpName),
415 debugstr_w(file->info.LocalName));
416
417 transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSFERRING);
Dan Hipschman2f2b3302008-03-13 15:57:19 -0700418
419 DeleteUrlCacheEntryW(file->info.RemoteName);
Michael Stefaniuc21c1cbf2010-12-04 22:16:22 +0100420 hr = URLDownloadToFileW(NULL, file->info.RemoteName, tmpName, 0,
421 &callbackObj->IBindStatusCallback_iface);
422 IBindStatusCallback_Release(&callbackObj->IBindStatusCallback_iface);
Dan Hipschman9617b322008-03-13 15:57:54 -0700423 if (hr == INET_E_DOWNLOAD_FAILURE)
Dan Hipschman2f2b3302008-03-13 15:57:19 -0700424 {
425 TRACE("URLDownload failed, trying local file copy\n");
426 if (!CopyFileExW(file->info.RemoteName, tmpName, copyProgressCallback,
427 file, NULL, 0))
428 {
429 ERR("Local file copy failed: error %d\n", GetLastError());
430 transitionJobState(job, BG_JOB_STATE_TRANSFERRING, BG_JOB_STATE_ERROR);
431 return FALSE;
432 }
433 }
Michael Stefaniucdd843b82008-10-08 01:33:25 +0200434 else if (FAILED(hr))
Dan Hipschman2f2b3302008-03-13 15:57:19 -0700435 {
436 ERR("URLDownload failed: eh 0x%08x\n", hr);
Dan Hipschmanb329a2e2008-03-13 15:54:35 -0700437 transitionJobState(job, BG_JOB_STATE_TRANSFERRING, BG_JOB_STATE_ERROR);
438 return FALSE;
439 }
440
441 if (transitionJobState(job, BG_JOB_STATE_TRANSFERRING, BG_JOB_STATE_QUEUED))
442 {
443 lstrcpyW(file->tempFileName, tmpName);
444
445 EnterCriticalSection(&job->cs);
446 file->fileProgress.Completed = TRUE;
447 job->jobProgress.FilesTransferred++;
448 LeaveCriticalSection(&job->cs);
449
450 return TRUE;
451 }
452 else
453 {
454 DeleteFileW(tmpName);
455 return FALSE;
456 }
457}