| /* |
| * Queue Manager (BITS) File |
| * |
| * Copyright 2007, 2008 Google (Roy Shea, Dan Hipschman) |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include <stdarg.h> |
| |
| #define COBJMACROS |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winuser.h" |
| #include "winreg.h" |
| #include "ole2.h" |
| #include "urlmon.h" |
| #include "wininet.h" |
| |
| #include "qmgr.h" |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(qmgr); |
| |
| static void BackgroundCopyFileDestructor(BackgroundCopyFileImpl *This) |
| { |
| IBackgroundCopyJob_Release((IBackgroundCopyJob *) This->owner); |
| HeapFree(GetProcessHeap(), 0, This->info.LocalName); |
| HeapFree(GetProcessHeap(), 0, This->info.RemoteName); |
| HeapFree(GetProcessHeap(), 0, This); |
| } |
| |
| static ULONG WINAPI BITS_IBackgroundCopyFile_AddRef(IBackgroundCopyFile* iface) |
| { |
| BackgroundCopyFileImpl *This = (BackgroundCopyFileImpl *) iface; |
| return InterlockedIncrement(&This->ref); |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyFile_QueryInterface( |
| IBackgroundCopyFile* iface, |
| REFIID riid, |
| void **ppvObject) |
| { |
| BackgroundCopyFileImpl *This = (BackgroundCopyFileImpl *) iface; |
| |
| if (IsEqualGUID(riid, &IID_IUnknown) |
| || IsEqualGUID(riid, &IID_IBackgroundCopyFile)) |
| { |
| *ppvObject = &This->lpVtbl; |
| BITS_IBackgroundCopyFile_AddRef(iface); |
| return S_OK; |
| } |
| |
| *ppvObject = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| |
| static ULONG WINAPI BITS_IBackgroundCopyFile_Release( |
| IBackgroundCopyFile* iface) |
| { |
| BackgroundCopyFileImpl *This = (BackgroundCopyFileImpl *) iface; |
| ULONG ref = InterlockedDecrement(&This->ref); |
| |
| if (ref == 0) |
| BackgroundCopyFileDestructor(This); |
| |
| return ref; |
| } |
| |
| /* Get the remote name of a background copy file */ |
| static HRESULT WINAPI BITS_IBackgroundCopyFile_GetRemoteName( |
| IBackgroundCopyFile* iface, |
| LPWSTR *pVal) |
| { |
| BackgroundCopyFileImpl *This = (BackgroundCopyFileImpl *) iface; |
| int n = (lstrlenW(This->info.RemoteName) + 1) * sizeof(WCHAR); |
| |
| *pVal = CoTaskMemAlloc(n); |
| if (!*pVal) |
| return E_OUTOFMEMORY; |
| |
| memcpy(*pVal, This->info.RemoteName, n); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyFile_GetLocalName( |
| IBackgroundCopyFile* iface, |
| LPWSTR *pVal) |
| { |
| BackgroundCopyFileImpl *This = (BackgroundCopyFileImpl *) iface; |
| int n = (lstrlenW(This->info.LocalName) + 1) * sizeof(WCHAR); |
| |
| *pVal = CoTaskMemAlloc(n); |
| if (!*pVal) |
| return E_OUTOFMEMORY; |
| |
| memcpy(*pVal, This->info.LocalName, n); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyFile_GetProgress( |
| IBackgroundCopyFile* iface, |
| BG_FILE_PROGRESS *pVal) |
| { |
| BackgroundCopyFileImpl *This = (BackgroundCopyFileImpl *) iface; |
| |
| EnterCriticalSection(&This->owner->cs); |
| pVal->BytesTotal = This->fileProgress.BytesTotal; |
| pVal->BytesTransferred = This->fileProgress.BytesTransferred; |
| pVal->Completed = This->fileProgress.Completed; |
| LeaveCriticalSection(&This->owner->cs); |
| |
| return S_OK; |
| } |
| |
| static const IBackgroundCopyFileVtbl BITS_IBackgroundCopyFile_Vtbl = |
| { |
| BITS_IBackgroundCopyFile_QueryInterface, |
| BITS_IBackgroundCopyFile_AddRef, |
| BITS_IBackgroundCopyFile_Release, |
| BITS_IBackgroundCopyFile_GetRemoteName, |
| BITS_IBackgroundCopyFile_GetLocalName, |
| BITS_IBackgroundCopyFile_GetProgress |
| }; |
| |
| HRESULT BackgroundCopyFileConstructor(BackgroundCopyJobImpl *owner, |
| LPCWSTR remoteName, LPCWSTR localName, |
| LPVOID *ppObj) |
| { |
| BackgroundCopyFileImpl *This; |
| int n; |
| |
| TRACE("(%s,%s,%p)\n", debugstr_w(remoteName), |
| debugstr_w(localName), ppObj); |
| |
| This = HeapAlloc(GetProcessHeap(), 0, sizeof *This); |
| if (!This) |
| return E_OUTOFMEMORY; |
| |
| n = (lstrlenW(remoteName) + 1) * sizeof(WCHAR); |
| This->info.RemoteName = HeapAlloc(GetProcessHeap(), 0, n); |
| if (!This->info.RemoteName) |
| { |
| HeapFree(GetProcessHeap(), 0, This); |
| return E_OUTOFMEMORY; |
| } |
| memcpy(This->info.RemoteName, remoteName, n); |
| |
| n = (lstrlenW(localName) + 1) * sizeof(WCHAR); |
| This->info.LocalName = HeapAlloc(GetProcessHeap(), 0, n); |
| if (!This->info.LocalName) |
| { |
| HeapFree(GetProcessHeap(), 0, This->info.RemoteName); |
| HeapFree(GetProcessHeap(), 0, This); |
| return E_OUTOFMEMORY; |
| } |
| memcpy(This->info.LocalName, localName, n); |
| |
| This->lpVtbl = &BITS_IBackgroundCopyFile_Vtbl; |
| This->ref = 1; |
| |
| This->fileProgress.BytesTotal = BG_SIZE_UNKNOWN; |
| This->fileProgress.BytesTransferred = 0; |
| This->fileProgress.Completed = FALSE; |
| This->owner = owner; |
| IBackgroundCopyJob_AddRef((IBackgroundCopyJob *) owner); |
| |
| *ppObj = &This->lpVtbl; |
| return S_OK; |
| } |
| |
| static DWORD CALLBACK copyProgressCallback(LARGE_INTEGER totalSize, |
| LARGE_INTEGER totalTransferred, |
| LARGE_INTEGER streamSize, |
| LARGE_INTEGER streamTransferred, |
| DWORD streamNum, |
| DWORD reason, |
| HANDLE srcFile, |
| HANDLE dstFile, |
| LPVOID obj) |
| { |
| BackgroundCopyFileImpl *file = obj; |
| BackgroundCopyJobImpl *job = file->owner; |
| ULONG64 diff; |
| |
| EnterCriticalSection(&job->cs); |
| diff = (file->fileProgress.BytesTotal == BG_SIZE_UNKNOWN |
| ? totalTransferred.QuadPart |
| : totalTransferred.QuadPart - file->fileProgress.BytesTransferred); |
| file->fileProgress.BytesTotal = totalSize.QuadPart; |
| file->fileProgress.BytesTransferred = totalTransferred.QuadPart; |
| job->jobProgress.BytesTransferred += diff; |
| LeaveCriticalSection(&job->cs); |
| |
| return (job->state == BG_JOB_STATE_TRANSFERRING |
| ? PROGRESS_CONTINUE |
| : PROGRESS_CANCEL); |
| } |
| |
| typedef struct |
| { |
| const IBindStatusCallbackVtbl *lpVtbl; |
| BackgroundCopyFileImpl *file; |
| LONG ref; |
| } DLBindStatusCallback; |
| |
| static ULONG WINAPI DLBindStatusCallback_AddRef(IBindStatusCallback *iface) |
| { |
| DLBindStatusCallback *This = (DLBindStatusCallback *) iface; |
| return InterlockedIncrement(&This->ref); |
| } |
| |
| static ULONG WINAPI DLBindStatusCallback_Release(IBindStatusCallback *iface) |
| { |
| DLBindStatusCallback *This = (DLBindStatusCallback *) iface; |
| ULONG ref = InterlockedDecrement(&This->ref); |
| |
| if (ref == 0) |
| { |
| IBackgroundCopyFile_Release((IBackgroundCopyFile *) This->file); |
| HeapFree(GetProcessHeap(), 0, This); |
| } |
| |
| return ref; |
| } |
| |
| static HRESULT WINAPI DLBindStatusCallback_QueryInterface( |
| IBindStatusCallback *iface, |
| REFIID riid, |
| void **ppvObject) |
| { |
| DLBindStatusCallback *This = (DLBindStatusCallback *) iface; |
| |
| if (IsEqualGUID(riid, &IID_IUnknown) |
| || IsEqualGUID(riid, &IID_IBindStatusCallback)) |
| { |
| *ppvObject = &This->lpVtbl; |
| DLBindStatusCallback_AddRef(iface); |
| return S_OK; |
| } |
| |
| *ppvObject = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| static HRESULT WINAPI DLBindStatusCallback_GetBindInfo( |
| IBindStatusCallback *iface, |
| DWORD *grfBINDF, |
| BINDINFO *pbindinfo) |
| { |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI DLBindStatusCallback_GetPriority( |
| IBindStatusCallback *iface, |
| LONG *pnPriority) |
| { |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI DLBindStatusCallback_OnDataAvailable( |
| IBindStatusCallback *iface, |
| DWORD grfBSCF, |
| DWORD dwSize, |
| FORMATETC *pformatetc, |
| STGMEDIUM *pstgmed) |
| { |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI DLBindStatusCallback_OnLowResource( |
| IBindStatusCallback *iface, |
| DWORD reserved) |
| { |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI DLBindStatusCallback_OnObjectAvailable( |
| IBindStatusCallback *iface, |
| REFIID riid, |
| IUnknown *punk) |
| { |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI DLBindStatusCallback_OnProgress( |
| IBindStatusCallback *iface, |
| ULONG progress, |
| ULONG progressMax, |
| ULONG statusCode, |
| LPCWSTR statusText) |
| { |
| DLBindStatusCallback *This = (DLBindStatusCallback *) iface; |
| BackgroundCopyFileImpl *file = This->file; |
| BackgroundCopyJobImpl *job = file->owner; |
| ULONG64 diff; |
| |
| EnterCriticalSection(&job->cs); |
| diff = (file->fileProgress.BytesTotal == BG_SIZE_UNKNOWN |
| ? progress |
| : progress - file->fileProgress.BytesTransferred); |
| file->fileProgress.BytesTotal = progressMax ? progressMax : BG_SIZE_UNKNOWN; |
| file->fileProgress.BytesTransferred = progress; |
| job->jobProgress.BytesTransferred += diff; |
| LeaveCriticalSection(&job->cs); |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI DLBindStatusCallback_OnStartBinding( |
| IBindStatusCallback *iface, |
| DWORD dwReserved, |
| IBinding *pib) |
| { |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI DLBindStatusCallback_OnStopBinding( |
| IBindStatusCallback *iface, |
| HRESULT hresult, |
| LPCWSTR szError) |
| { |
| return E_NOTIMPL; |
| } |
| |
| static const IBindStatusCallbackVtbl DLBindStatusCallback_Vtbl = |
| { |
| DLBindStatusCallback_QueryInterface, |
| DLBindStatusCallback_AddRef, |
| DLBindStatusCallback_Release, |
| DLBindStatusCallback_OnStartBinding, |
| DLBindStatusCallback_GetPriority, |
| DLBindStatusCallback_OnLowResource, |
| DLBindStatusCallback_OnProgress, |
| DLBindStatusCallback_OnStopBinding, |
| DLBindStatusCallback_GetBindInfo, |
| DLBindStatusCallback_OnDataAvailable, |
| DLBindStatusCallback_OnObjectAvailable |
| }; |
| |
| static DLBindStatusCallback *DLBindStatusCallbackConstructor( |
| BackgroundCopyFileImpl *file) |
| { |
| DLBindStatusCallback *This = HeapAlloc(GetProcessHeap(), 0, sizeof *This); |
| if (!This) |
| return NULL; |
| |
| This->lpVtbl = &DLBindStatusCallback_Vtbl; |
| IBackgroundCopyFile_AddRef((IBackgroundCopyFile *) file); |
| This->file = file; |
| This->ref = 1; |
| return This; |
| } |
| |
| BOOL processFile(BackgroundCopyFileImpl *file, BackgroundCopyJobImpl *job) |
| { |
| static const WCHAR prefix[] = {'B','I','T', 0}; |
| IBindStatusCallback *callbackObj; |
| WCHAR tmpDir[MAX_PATH]; |
| WCHAR tmpName[MAX_PATH]; |
| HRESULT hr; |
| |
| if (!GetTempPathW(MAX_PATH, tmpDir)) |
| { |
| ERR("Couldn't create temp file name: %d\n", GetLastError()); |
| /* Guessing on what state this should give us */ |
| transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSIENT_ERROR); |
| return FALSE; |
| } |
| |
| if (!GetTempFileNameW(tmpDir, prefix, 0, tmpName)) |
| { |
| ERR("Couldn't create temp file: %d\n", GetLastError()); |
| /* Guessing on what state this should give us */ |
| transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSIENT_ERROR); |
| return FALSE; |
| } |
| |
| callbackObj = (IBindStatusCallback *) DLBindStatusCallbackConstructor(file); |
| if (!callbackObj) |
| { |
| ERR("Out of memory\n"); |
| transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSIENT_ERROR); |
| return FALSE; |
| } |
| |
| EnterCriticalSection(&job->cs); |
| file->fileProgress.BytesTotal = BG_SIZE_UNKNOWN; |
| file->fileProgress.BytesTransferred = 0; |
| file->fileProgress.Completed = FALSE; |
| LeaveCriticalSection(&job->cs); |
| |
| TRACE("Transferring: %s -> %s -> %s\n", |
| debugstr_w(file->info.RemoteName), |
| debugstr_w(tmpName), |
| debugstr_w(file->info.LocalName)); |
| |
| transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSFERRING); |
| |
| DeleteUrlCacheEntryW(file->info.RemoteName); |
| hr = URLDownloadToFileW(NULL, file->info.RemoteName, tmpName, 0, callbackObj); |
| IBindStatusCallback_Release(callbackObj); |
| if (hr == INET_E_DOWNLOAD_FAILURE) |
| { |
| TRACE("URLDownload failed, trying local file copy\n"); |
| if (!CopyFileExW(file->info.RemoteName, tmpName, copyProgressCallback, |
| file, NULL, 0)) |
| { |
| ERR("Local file copy failed: error %d\n", GetLastError()); |
| transitionJobState(job, BG_JOB_STATE_TRANSFERRING, BG_JOB_STATE_ERROR); |
| return FALSE; |
| } |
| } |
| else if (FAILED(hr)) |
| { |
| ERR("URLDownload failed: eh 0x%08x\n", hr); |
| transitionJobState(job, BG_JOB_STATE_TRANSFERRING, BG_JOB_STATE_ERROR); |
| return FALSE; |
| } |
| |
| if (transitionJobState(job, BG_JOB_STATE_TRANSFERRING, BG_JOB_STATE_QUEUED)) |
| { |
| lstrcpyW(file->tempFileName, tmpName); |
| |
| EnterCriticalSection(&job->cs); |
| file->fileProgress.Completed = TRUE; |
| job->jobProgress.FilesTransferred++; |
| LeaveCriticalSection(&job->cs); |
| |
| return TRUE; |
| } |
| else |
| { |
| DeleteFileW(tmpName); |
| return FALSE; |
| } |
| } |