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