diff --git a/dlls/qmgr/file.c b/dlls/qmgr/file.c index 1ed34d3273a..f00fa3fd07c 100644 --- a/dlls/qmgr/file.c +++ b/dlls/qmgr/file.c @@ -424,13 +424,53 @@ static HRESULT WINAPI http_negotiate_BeginningTransaction( 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); - FIXME("(%p)->(%d %s %s %p)\n", callback, code, debugstr_w(resp_headers), debugstr_w(req_headers), + BackgroundCopyJobImpl *job = callback->file->owner; + + TRACE("(%p)->(%d %s %s %p)\n", callback, code, debugstr_w(resp_headers), debugstr_w(req_headers), add_reqheaders); - return E_NOTIMPL; + + 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 = @@ -523,6 +563,12 @@ BOOL processFile(BackgroundCopyFileImpl *file, BackgroundCopyJobImpl *job) 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)) { diff --git a/dlls/qmgr/job.c b/dlls/qmgr/job.c index 912c23ece32..d3420417a35 100644 --- a/dlls/qmgr/job.c +++ b/dlls/qmgr/job.c @@ -27,6 +27,167 @@ WINE_DEFAULT_DEBUG_CHANNEL(qmgr); +struct copy_error +{ + IBackgroundCopyError IBackgroundCopyError_iface; + LONG refs; + BG_ERROR_CONTEXT context; + HRESULT code; + IBackgroundCopyFile2 *file; +}; + +static inline struct copy_error *impl_from_IBackgroundCopyError(IBackgroundCopyError *iface) +{ + return CONTAINING_RECORD(iface, struct copy_error, IBackgroundCopyError_iface); +} + +static HRESULT WINAPI copy_error_QueryInterface( + IBackgroundCopyError *iface, + REFIID riid, + void **obj) +{ + struct copy_error *error = impl_from_IBackgroundCopyError(iface); + + TRACE("(%p)->(%s %p)\n", error, debugstr_guid(riid), obj); + + if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IBackgroundCopyError)) + { + *obj = &error->IBackgroundCopyError_iface; + } + else + { + *obj = NULL; + WARN("interface %s not supported\n", debugstr_guid(riid)); + return E_NOINTERFACE; + } + + IBackgroundCopyError_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI copy_error_AddRef( + IBackgroundCopyError *iface) +{ + struct copy_error *error = impl_from_IBackgroundCopyError(iface); + LONG refs = InterlockedIncrement(&error->refs); + TRACE("(%p)->(%d)\n", error, refs); + return refs; +} + +static ULONG WINAPI copy_error_Release( + IBackgroundCopyError *iface) +{ + struct copy_error *error = impl_from_IBackgroundCopyError(iface); + LONG refs = InterlockedDecrement(&error->refs); + + TRACE("(%p)->(%d)\n", error, refs); + + if (!refs) + { + if (error->file) IBackgroundCopyFile2_Release(error->file); + HeapFree(GetProcessHeap(), 0, error); + } + return refs; +} + +static HRESULT WINAPI copy_error_GetError( + IBackgroundCopyError *iface, + BG_ERROR_CONTEXT *pContext, + HRESULT *pCode) +{ + struct copy_error *error = impl_from_IBackgroundCopyError(iface); + + TRACE("(%p)->(%p %p)\n", error, pContext, pCode); + + *pContext = error->context; + *pCode = error->code; + + TRACE("returning context %u error code 0x%08x\n", error->context, error->code); + return S_OK; +} + +static HRESULT WINAPI copy_error_GetFile( + IBackgroundCopyError *iface, + IBackgroundCopyFile **pVal) +{ + struct copy_error *error = impl_from_IBackgroundCopyError(iface); + + TRACE("(%p)->(%p)\n", error, pVal); + + if (error->file) + { + IBackgroundCopyFile2_AddRef(error->file); + *pVal = (IBackgroundCopyFile *)error->file; + return S_OK; + } + *pVal = NULL; + return BG_E_FILE_NOT_AVAILABLE; +} + +static HRESULT WINAPI copy_error_GetErrorDescription( + IBackgroundCopyError *iface, + DWORD LanguageId, + LPWSTR *pErrorDescription) +{ + struct copy_error *error = impl_from_IBackgroundCopyError(iface); + FIXME("(%p)->(%p)\n", error, pErrorDescription); + return E_NOTIMPL; +} + +static HRESULT WINAPI copy_error_GetErrorContextDescription( + IBackgroundCopyError *iface, + DWORD LanguageId, + LPWSTR *pContextDescription) +{ + struct copy_error *error = impl_from_IBackgroundCopyError(iface); + FIXME("(%p)->(%p)\n", error, pContextDescription); + return E_NOTIMPL; +} + +static HRESULT WINAPI copy_error_GetProtocol( + IBackgroundCopyError *iface, + LPWSTR *pProtocol) +{ + struct copy_error *error = impl_from_IBackgroundCopyError(iface); + FIXME("(%p)->(%p)\n", error, pProtocol); + return E_NOTIMPL; +} + +static const IBackgroundCopyErrorVtbl copy_error_vtbl = +{ + copy_error_QueryInterface, + copy_error_AddRef, + copy_error_Release, + copy_error_GetError, + copy_error_GetFile, + copy_error_GetErrorDescription, + copy_error_GetErrorContextDescription, + copy_error_GetProtocol +}; + +static HRESULT create_copy_error( + BG_ERROR_CONTEXT context, + HRESULT code, + IBackgroundCopyFile2 *file, + IBackgroundCopyError **obj) +{ + struct copy_error *error; + + TRACE("context %u code %08x file %p\n", context, code, file); + + if (!(error = HeapAlloc(GetProcessHeap(), 0, sizeof(*error) ))) return E_OUTOFMEMORY; + error->IBackgroundCopyError_iface.lpVtbl = ©_error_vtbl; + error->refs = 1; + error->context = context; + error->code = code; + error->file = file; + if (error->file) IBackgroundCopyFile2_AddRef(error->file); + + *obj = &error->IBackgroundCopyError_iface; + TRACE("returning iface %p\n", *obj); + return S_OK; +} + static inline BOOL is_job_done(const BackgroundCopyJobImpl *job) { return job->state == BG_JOB_STATE_CANCELLED || job->state == BG_JOB_STATE_ACKNOWLEDGED; @@ -312,9 +473,13 @@ static HRESULT WINAPI BackgroundCopyJob_GetError( IBackgroundCopyJob3 *iface, IBackgroundCopyError **ppError) { - BackgroundCopyJobImpl *This = impl_from_IBackgroundCopyJob3(iface); - FIXME("(%p)->(%p): stub\n", This, ppError); - return E_NOTIMPL; + BackgroundCopyJobImpl *job = impl_from_IBackgroundCopyJob3(iface); + + TRACE("(%p)->(%p)\n", job, ppError); + + if (!job->error.context) return BG_E_ERROR_INFORMATION_UNAVAILABLE; + + return create_copy_error(job->error.context, job->error.code, job->error.file, ppError); } static HRESULT WINAPI BackgroundCopyJob_GetOwner( @@ -788,6 +953,10 @@ HRESULT BackgroundCopyJobConstructor(LPCWSTR displayName, BG_JOB_TYPE type, GUID This->callback = NULL; This->callback2 = FALSE; + This->error.context = 0; + This->error.code = 0; + This->error.file = NULL; + *job = This; TRACE("created job %s:%p\n", debugstr_guid(&This->jobId), This); diff --git a/dlls/qmgr/qmgr.h b/dlls/qmgr/qmgr.h index 87145fda74a..80a3e97ac38 100644 --- a/dlls/qmgr/qmgr.h +++ b/dlls/qmgr/qmgr.h @@ -51,6 +51,12 @@ typedef struct /* Protects file list, and progress */ CRITICAL_SECTION cs; struct list entryFromQmgr; + struct + { + BG_ERROR_CONTEXT context; + HRESULT code; + IBackgroundCopyFile2 *file; + } error; } BackgroundCopyJobImpl; /* Background copy file vtbl and related data */