Sweden-Number/dlls/qmgr/file.c

590 lines
18 KiB
C

/*
* 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>
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "winreg.h"
#include "wininet.h"
#define COBJMACROS
#include "urlmon.h"
#include "qmgr.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(qmgr);
static inline BackgroundCopyFileImpl *impl_from_IBackgroundCopyFile2(
IBackgroundCopyFile2 *iface)
{
return CONTAINING_RECORD(iface, BackgroundCopyFileImpl, IBackgroundCopyFile2_iface);
}
static HRESULT WINAPI BackgroundCopyFile_QueryInterface(
IBackgroundCopyFile2 *iface,
REFIID riid,
void **obj)
{
BackgroundCopyFileImpl *file = impl_from_IBackgroundCopyFile2(iface);
TRACE("(%p)->(%s %p)\n", file, debugstr_guid(riid), obj);
if (IsEqualGUID(riid, &IID_IUnknown) ||
IsEqualGUID(riid, &IID_IBackgroundCopyFile) ||
IsEqualGUID(riid, &IID_IBackgroundCopyFile2))
{
*obj = iface;
}
else
{
*obj = NULL;
return E_NOINTERFACE;
}
IBackgroundCopyFile2_AddRef(iface);
return S_OK;
}
static ULONG WINAPI BackgroundCopyFile_AddRef(
IBackgroundCopyFile2 *iface)
{
BackgroundCopyFileImpl *file = impl_from_IBackgroundCopyFile2(iface);
ULONG ref = InterlockedIncrement(&file->ref);
TRACE("(%p)->(%d)\n", file, ref);
return ref;
}
static ULONG WINAPI BackgroundCopyFile_Release(
IBackgroundCopyFile2 *iface)
{
BackgroundCopyFileImpl *file = impl_from_IBackgroundCopyFile2(iface);
ULONG ref = InterlockedDecrement(&file->ref);
TRACE("(%p)->(%d)\n", file, ref);
if (ref == 0)
{
IBackgroundCopyJob3_Release(&file->owner->IBackgroundCopyJob3_iface);
HeapFree(GetProcessHeap(), 0, file->info.LocalName);
HeapFree(GetProcessHeap(), 0, file->info.RemoteName);
HeapFree(GetProcessHeap(), 0, file);
}
return ref;
}
/* Get the remote name of a background copy file */
static HRESULT WINAPI BackgroundCopyFile_GetRemoteName(
IBackgroundCopyFile2 *iface,
LPWSTR *pVal)
{
BackgroundCopyFileImpl *file = impl_from_IBackgroundCopyFile2(iface);
TRACE("(%p)->(%p)\n", file, pVal);
return return_strval(file->info.RemoteName, pVal);
}
static HRESULT WINAPI BackgroundCopyFile_GetLocalName(
IBackgroundCopyFile2 *iface,
LPWSTR *pVal)
{
BackgroundCopyFileImpl *file = impl_from_IBackgroundCopyFile2(iface);
TRACE("(%p)->(%p)\n", file, pVal);
return return_strval(file->info.LocalName, pVal);
}
static HRESULT WINAPI BackgroundCopyFile_GetProgress(
IBackgroundCopyFile2 *iface,
BG_FILE_PROGRESS *pVal)
{
BackgroundCopyFileImpl *file = impl_from_IBackgroundCopyFile2(iface);
TRACE("(%p)->(%p)\n", file, pVal);
EnterCriticalSection(&file->owner->cs);
pVal->BytesTotal = file->fileProgress.BytesTotal;
pVal->BytesTransferred = file->fileProgress.BytesTransferred;
pVal->Completed = file->fileProgress.Completed;
LeaveCriticalSection(&file->owner->cs);
return S_OK;
}
static HRESULT WINAPI BackgroundCopyFile_GetFileRanges(
IBackgroundCopyFile2 *iface,
DWORD *RangeCount,
BG_FILE_RANGE **Ranges)
{
BackgroundCopyFileImpl *file = impl_from_IBackgroundCopyFile2(iface);
FIXME("(%p)->(%p %p)\n", file, RangeCount, Ranges);
return E_NOTIMPL;
}
static HRESULT WINAPI BackgroundCopyFile_SetRemoteName(
IBackgroundCopyFile2 *iface,
LPCWSTR Val)
{
BackgroundCopyFileImpl *file = impl_from_IBackgroundCopyFile2(iface);
FIXME("(%p)->(%s)\n", file, debugstr_w(Val));
return E_NOTIMPL;
}
static const IBackgroundCopyFile2Vtbl BackgroundCopyFile2Vtbl =
{
BackgroundCopyFile_QueryInterface,
BackgroundCopyFile_AddRef,
BackgroundCopyFile_Release,
BackgroundCopyFile_GetRemoteName,
BackgroundCopyFile_GetLocalName,
BackgroundCopyFile_GetProgress,
BackgroundCopyFile_GetFileRanges,
BackgroundCopyFile_SetRemoteName
};
HRESULT BackgroundCopyFileConstructor(BackgroundCopyJobImpl *owner,
LPCWSTR remoteName, LPCWSTR localName,
BackgroundCopyFileImpl **file)
{
BackgroundCopyFileImpl *This;
int n;
TRACE("(%s, %s, %p)\n", debugstr_w(remoteName), debugstr_w(localName), file);
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->IBackgroundCopyFile2_iface.lpVtbl = &BackgroundCopyFile2Vtbl;
This->ref = 1;
This->fileProgress.BytesTotal = BG_SIZE_UNKNOWN;
This->fileProgress.BytesTransferred = 0;
This->fileProgress.Completed = FALSE;
This->owner = owner;
IBackgroundCopyJob3_AddRef(&owner->IBackgroundCopyJob3_iface);
*file = This;
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
{
IBindStatusCallback IBindStatusCallback_iface;
IHttpNegotiate IHttpNegotiate_iface;
BackgroundCopyFileImpl *file;
LONG ref;
} DLBindStatusCallback;
static inline DLBindStatusCallback *impl_from_IBindStatusCallback(IBindStatusCallback *iface)
{
return CONTAINING_RECORD(iface, DLBindStatusCallback, IBindStatusCallback_iface);
}
static HRESULT WINAPI DLBindStatusCallback_QueryInterface(
IBindStatusCallback *iface,
REFIID riid,
void **ppvObject)
{
DLBindStatusCallback *This = impl_from_IBindStatusCallback(iface);
TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IBindStatusCallback))
{
*ppvObject = &This->IBindStatusCallback_iface;
}
else if (IsEqualGUID(riid, &IID_IHttpNegotiate))
{
*ppvObject = &This->IHttpNegotiate_iface;
}
else
{
*ppvObject = NULL;
return E_NOINTERFACE;
}
IBindStatusCallback_AddRef(iface);
return S_OK;
}
static ULONG WINAPI DLBindStatusCallback_AddRef(IBindStatusCallback *iface)
{
DLBindStatusCallback *This = impl_from_IBindStatusCallback(iface);
return InterlockedIncrement(&This->ref);
}
static ULONG WINAPI DLBindStatusCallback_Release(IBindStatusCallback *iface)
{
DLBindStatusCallback *This = impl_from_IBindStatusCallback(iface);
ULONG ref = InterlockedDecrement(&This->ref);
if (ref == 0)
{
IBackgroundCopyFile2_Release(&This->file->IBackgroundCopyFile2_iface);
HeapFree(GetProcessHeap(), 0, This);
}
return ref;
}
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 = impl_from_IBindStatusCallback(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 inline DLBindStatusCallback *impl_from_IHttpNegotiate(IHttpNegotiate *iface)
{
return CONTAINING_RECORD(iface, DLBindStatusCallback, IHttpNegotiate_iface);
}
static HRESULT WINAPI http_negotiate_QueryInterface(
IHttpNegotiate *iface, REFIID riid, void **ppv)
{
DLBindStatusCallback *callback = impl_from_IHttpNegotiate(iface);
return IBindStatusCallback_QueryInterface(&callback->IBindStatusCallback_iface, riid, ppv);
}
static ULONG WINAPI http_negotiate_AddRef(
IHttpNegotiate *iface)
{
DLBindStatusCallback *callback = impl_from_IHttpNegotiate(iface);
return IBindStatusCallback_AddRef(&callback->IBindStatusCallback_iface);
}
static ULONG WINAPI http_negotiate_Release(
IHttpNegotiate *iface)
{
DLBindStatusCallback *callback = impl_from_IHttpNegotiate(iface);
return IBindStatusCallback_Release(&callback->IBindStatusCallback_iface);
}
static HRESULT WINAPI http_negotiate_BeginningTransaction(
IHttpNegotiate *iface, LPCWSTR url, LPCWSTR headers, DWORD reserved, LPWSTR *add_headers)
{
DLBindStatusCallback *callback = impl_from_IHttpNegotiate(iface);
FIXME("(%p)->(%s %s %u %p)\n", callback, debugstr_w(url), debugstr_w(headers), reserved, add_headers);
return E_NOTIMPL;
}
static HRESULT error_from_http_response(DWORD code)
{
switch (code)
{
case 200: return S_OK;
case 400: return BG_E_HTTP_ERROR_400;
case 401: return BG_E_HTTP_ERROR_401;
case 404: return BG_E_HTTP_ERROR_404;
case 407: return BG_E_HTTP_ERROR_407;
case 414: return BG_E_HTTP_ERROR_414;
case 501: return BG_E_HTTP_ERROR_501;
case 503: return BG_E_HTTP_ERROR_503;
case 504: return BG_E_HTTP_ERROR_504;
case 505: return BG_E_HTTP_ERROR_505;
default:
FIXME("unhandled response code %u\n", code);
return S_OK;
}
}
static HRESULT WINAPI http_negotiate_OnResponse(
IHttpNegotiate *iface, DWORD code, LPCWSTR resp_headers, LPCWSTR req_headers, LPWSTR *add_reqheaders)
{
DLBindStatusCallback *callback = impl_from_IHttpNegotiate(iface);
BackgroundCopyJobImpl *job = callback->file->owner;
TRACE("(%p)->(%d %s %s %p)\n", callback, code, debugstr_w(resp_headers), debugstr_w(req_headers),
add_reqheaders);
if ((job->error.code = error_from_http_response(code)))
{
job->error.context = BG_ERROR_CONTEXT_REMOTE_FILE;
if (job->error.file) IBackgroundCopyFile2_Release(job->error.file);
job->error.file = &callback->file->IBackgroundCopyFile2_iface;
IBackgroundCopyFile2_AddRef(job->error.file);
}
else
{
job->error.context = 0;
if (job->error.file)
{
IBackgroundCopyFile2_Release(job->error.file);
job->error.file = NULL;
}
}
*add_reqheaders = NULL;
return S_OK;
}
static const IHttpNegotiateVtbl http_negotiate_vtbl =
{
http_negotiate_QueryInterface,
http_negotiate_AddRef,
http_negotiate_Release,
http_negotiate_BeginningTransaction,
http_negotiate_OnResponse
};
static DLBindStatusCallback *DLBindStatusCallbackConstructor(
BackgroundCopyFileImpl *file)
{
DLBindStatusCallback *This = HeapAlloc(GetProcessHeap(), 0, sizeof *This);
if (!This)
return NULL;
This->IBindStatusCallback_iface.lpVtbl = &DLBindStatusCallback_Vtbl;
This->IHttpNegotiate_iface.lpVtbl = &http_negotiate_vtbl;
IBackgroundCopyFile2_AddRef(&file->IBackgroundCopyFile2_iface);
This->file = file;
This->ref = 1;
return This;
}
BOOL processFile(BackgroundCopyFileImpl *file, BackgroundCopyJobImpl *job)
{
static const WCHAR prefix[] = {'B','I','T', 0};
DLBindStatusCallback *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 = 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_iface);
IBindStatusCallback_Release(&callbackObj->IBindStatusCallback_iface);
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;
}
else if (job->error.code)
{
ERR("transfer error: 0x%08x\n", job->error.code);
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;
}
}