* Bring back rbuild build to be used until bug 6372 is fixed.
[reactos.git] / 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 <stdarg.h>
22
23 #define COBJMACROS
24
25 #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
33 #include "qmgr.h"
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(qmgr);
37
38 static void BackgroundCopyFileDestructor(BackgroundCopyFileImpl *This)
39 {
40 IBackgroundCopyJob_Release((IBackgroundCopyJob *) This->owner);
41 HeapFree(GetProcessHeap(), 0, This->info.LocalName);
42 HeapFree(GetProcessHeap(), 0, This->info.RemoteName);
43 HeapFree(GetProcessHeap(), 0, This);
44 }
45
46 static ULONG WINAPI BITS_IBackgroundCopyFile_AddRef(IBackgroundCopyFile* iface)
47 {
48 BackgroundCopyFileImpl *This = (BackgroundCopyFileImpl *) iface;
49 return InterlockedIncrement(&This->ref);
50 }
51
52 static 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
72 static 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
84 /* Get the remote name of a background copy file */
85 static HRESULT WINAPI BITS_IBackgroundCopyFile_GetRemoteName(
86 IBackgroundCopyFile* iface,
87 LPWSTR *pVal)
88 {
89 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;
98 }
99
100 static HRESULT WINAPI BITS_IBackgroundCopyFile_GetLocalName(
101 IBackgroundCopyFile* iface,
102 LPWSTR *pVal)
103 {
104 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;
113 }
114
115 static HRESULT WINAPI BITS_IBackgroundCopyFile_GetProgress(
116 IBackgroundCopyFile* iface,
117 BG_FILE_PROGRESS *pVal)
118 {
119 BackgroundCopyFileImpl *This = (BackgroundCopyFileImpl *) iface;
120
121 EnterCriticalSection(&This->owner->cs);
122 pVal->BytesTotal = This->fileProgress.BytesTotal;
123 pVal->BytesTransferred = This->fileProgress.BytesTransferred;
124 pVal->Completed = This->fileProgress.Completed;
125 LeaveCriticalSection(&This->owner->cs);
126
127 return S_OK;
128 }
129
130 static 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
140 HRESULT BackgroundCopyFileConstructor(BackgroundCopyJobImpl *owner,
141 LPCWSTR remoteName, LPCWSTR localName,
142 LPVOID *ppObj)
143 {
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
176 This->fileProgress.BytesTotal = BG_SIZE_UNKNOWN;
177 This->fileProgress.BytesTransferred = 0;
178 This->fileProgress.Completed = FALSE;
179 This->owner = owner;
180 IBackgroundCopyJob_AddRef((IBackgroundCopyJob *) owner);
181
182 *ppObj = &This->lpVtbl;
183 return S_OK;
184 }
185
186 static 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 {
196 BackgroundCopyFileImpl *file = obj;
197 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
214 typedef struct
215 {
216 const IBindStatusCallbackVtbl *lpVtbl;
217 BackgroundCopyFileImpl *file;
218 LONG ref;
219 } DLBindStatusCallback;
220
221 static ULONG WINAPI DLBindStatusCallback_AddRef(IBindStatusCallback *iface)
222 {
223 DLBindStatusCallback *This = (DLBindStatusCallback *) iface;
224 return InterlockedIncrement(&This->ref);
225 }
226
227 static ULONG WINAPI DLBindStatusCallback_Release(IBindStatusCallback *iface)
228 {
229 DLBindStatusCallback *This = (DLBindStatusCallback *) iface;
230 ULONG ref = InterlockedDecrement(&This->ref);
231
232 if (ref == 0)
233 {
234 IBackgroundCopyFile_Release((IBackgroundCopyFile *) This->file);
235 HeapFree(GetProcessHeap(), 0, This);
236 }
237
238 return ref;
239 }
240
241 static HRESULT WINAPI DLBindStatusCallback_QueryInterface(
242 IBindStatusCallback *iface,
243 REFIID riid,
244 void **ppvObject)
245 {
246 DLBindStatusCallback *This = (DLBindStatusCallback *) iface;
247
248 if (IsEqualGUID(riid, &IID_IUnknown)
249 || IsEqualGUID(riid, &IID_IBindStatusCallback))
250 {
251 *ppvObject = &This->lpVtbl;
252 DLBindStatusCallback_AddRef(iface);
253 return S_OK;
254 }
255
256 *ppvObject = NULL;
257 return E_NOINTERFACE;
258 }
259
260 static HRESULT WINAPI DLBindStatusCallback_GetBindInfo(
261 IBindStatusCallback *iface,
262 DWORD *grfBINDF,
263 BINDINFO *pbindinfo)
264 {
265 return E_NOTIMPL;
266 }
267
268 static HRESULT WINAPI DLBindStatusCallback_GetPriority(
269 IBindStatusCallback *iface,
270 LONG *pnPriority)
271 {
272 return E_NOTIMPL;
273 }
274
275 static HRESULT WINAPI DLBindStatusCallback_OnDataAvailable(
276 IBindStatusCallback *iface,
277 DWORD grfBSCF,
278 DWORD dwSize,
279 FORMATETC *pformatetc,
280 STGMEDIUM *pstgmed)
281 {
282 return E_NOTIMPL;
283 }
284
285 static HRESULT WINAPI DLBindStatusCallback_OnLowResource(
286 IBindStatusCallback *iface,
287 DWORD reserved)
288 {
289 return E_NOTIMPL;
290 }
291
292 static HRESULT WINAPI DLBindStatusCallback_OnObjectAvailable(
293 IBindStatusCallback *iface,
294 REFIID riid,
295 IUnknown *punk)
296 {
297 return E_NOTIMPL;
298 }
299
300 static HRESULT WINAPI DLBindStatusCallback_OnProgress(
301 IBindStatusCallback *iface,
302 ULONG progress,
303 ULONG progressMax,
304 ULONG statusCode,
305 LPCWSTR statusText)
306 {
307 DLBindStatusCallback *This = (DLBindStatusCallback *) iface;
308 BackgroundCopyFileImpl *file = This->file;
309 BackgroundCopyJobImpl *job = file->owner;
310 ULONG64 diff;
311
312 EnterCriticalSection(&job->cs);
313 diff = (file->fileProgress.BytesTotal == BG_SIZE_UNKNOWN
314 ? progress
315 : progress - file->fileProgress.BytesTransferred);
316 file->fileProgress.BytesTotal = progressMax ? progressMax : BG_SIZE_UNKNOWN;
317 file->fileProgress.BytesTransferred = progress;
318 job->jobProgress.BytesTransferred += diff;
319 LeaveCriticalSection(&job->cs);
320
321 return S_OK;
322 }
323
324 static HRESULT WINAPI DLBindStatusCallback_OnStartBinding(
325 IBindStatusCallback *iface,
326 DWORD dwReserved,
327 IBinding *pib)
328 {
329 return E_NOTIMPL;
330 }
331
332 static HRESULT WINAPI DLBindStatusCallback_OnStopBinding(
333 IBindStatusCallback *iface,
334 HRESULT hresult,
335 LPCWSTR szError)
336 {
337 return E_NOTIMPL;
338 }
339
340 static const IBindStatusCallbackVtbl DLBindStatusCallback_Vtbl =
341 {
342 DLBindStatusCallback_QueryInterface,
343 DLBindStatusCallback_AddRef,
344 DLBindStatusCallback_Release,
345 DLBindStatusCallback_OnStartBinding,
346 DLBindStatusCallback_GetPriority,
347 DLBindStatusCallback_OnLowResource,
348 DLBindStatusCallback_OnProgress,
349 DLBindStatusCallback_OnStopBinding,
350 DLBindStatusCallback_GetBindInfo,
351 DLBindStatusCallback_OnDataAvailable,
352 DLBindStatusCallback_OnObjectAvailable
353 };
354
355 static DLBindStatusCallback *DLBindStatusCallbackConstructor(
356 BackgroundCopyFileImpl *file)
357 {
358 DLBindStatusCallback *This = HeapAlloc(GetProcessHeap(), 0, sizeof *This);
359 if (!This)
360 return NULL;
361
362 This->lpVtbl = &DLBindStatusCallback_Vtbl;
363 IBackgroundCopyFile_AddRef((IBackgroundCopyFile *) file);
364 This->file = file;
365 This->ref = 1;
366 return This;
367 }
368
369 BOOL processFile(BackgroundCopyFileImpl *file, BackgroundCopyJobImpl *job)
370 {
371 static const WCHAR prefix[] = {'B','I','T', 0};
372 IBindStatusCallback *callbackObj;
373 WCHAR tmpDir[MAX_PATH];
374 WCHAR tmpName[MAX_PATH];
375 HRESULT hr;
376
377 if (!GetTempPathW(MAX_PATH, tmpDir))
378 {
379 ERR("Couldn't create temp file name: %d\n", GetLastError());
380 /* Guessing on what state this should give us */
381 transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSIENT_ERROR);
382 return FALSE;
383 }
384
385 if (!GetTempFileNameW(tmpDir, prefix, 0, tmpName))
386 {
387 ERR("Couldn't create temp file: %d\n", GetLastError());
388 /* Guessing on what state this should give us */
389 transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSIENT_ERROR);
390 return FALSE;
391 }
392
393 callbackObj = (IBindStatusCallback *) DLBindStatusCallbackConstructor(file);
394 if (!callbackObj)
395 {
396 ERR("Out of memory\n");
397 transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSIENT_ERROR);
398 return FALSE;
399 }
400
401 EnterCriticalSection(&job->cs);
402 file->fileProgress.BytesTotal = BG_SIZE_UNKNOWN;
403 file->fileProgress.BytesTransferred = 0;
404 file->fileProgress.Completed = FALSE;
405 LeaveCriticalSection(&job->cs);
406
407 TRACE("Transferring: %s -> %s -> %s\n",
408 debugstr_w(file->info.RemoteName),
409 debugstr_w(tmpName),
410 debugstr_w(file->info.LocalName));
411
412 transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSFERRING);
413
414 DeleteUrlCacheEntryW(file->info.RemoteName);
415 hr = URLDownloadToFileW(NULL, file->info.RemoteName, tmpName, 0, callbackObj);
416 IBindStatusCallback_Release(callbackObj);
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 }