| /* |
| * Background Copy Job Interface for BITS |
| * |
| * Copyright 2007 Google (Roy Shea) |
| * |
| * 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 "qmgr.h" |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(qmgr); |
| |
| static void BackgroundCopyJobDestructor(BackgroundCopyJobImpl *This) |
| { |
| This->cs.DebugInfo->Spare[0] = 0; |
| DeleteCriticalSection(&This->cs); |
| HeapFree(GetProcessHeap(), 0, This->displayName); |
| HeapFree(GetProcessHeap(), 0, This); |
| } |
| |
| static ULONG WINAPI BITS_IBackgroundCopyJob_AddRef(IBackgroundCopyJob2 *iface) |
| { |
| BackgroundCopyJobImpl *This = (BackgroundCopyJobImpl *) iface; |
| return InterlockedIncrement(&This->ref); |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_QueryInterface( |
| IBackgroundCopyJob2 *iface, REFIID riid, LPVOID *ppvObject) |
| { |
| BackgroundCopyJobImpl *This = (BackgroundCopyJobImpl *) iface; |
| TRACE("IID: %s\n", debugstr_guid(riid)); |
| |
| if (IsEqualGUID(riid, &IID_IUnknown) |
| || IsEqualGUID(riid, &IID_IBackgroundCopyJob) |
| || IsEqualGUID(riid, &IID_IBackgroundCopyJob2)) |
| { |
| *ppvObject = &This->lpVtbl; |
| BITS_IBackgroundCopyJob_AddRef(iface); |
| return S_OK; |
| } |
| |
| *ppvObject = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI BITS_IBackgroundCopyJob_Release(IBackgroundCopyJob2 *iface) |
| { |
| BackgroundCopyJobImpl *This = (BackgroundCopyJobImpl *) iface; |
| ULONG ref = InterlockedDecrement(&This->ref); |
| |
| if (ref == 0) |
| BackgroundCopyJobDestructor(This); |
| |
| return ref; |
| } |
| |
| /*** IBackgroundCopyJob methods ***/ |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_AddFileSet( |
| IBackgroundCopyJob2 *iface, |
| ULONG cFileCount, |
| BG_FILE_INFO *pFileSet) |
| { |
| ULONG i; |
| for (i = 0; i < cFileCount; ++i) |
| { |
| HRESULT hr = IBackgroundCopyJob_AddFile(iface, pFileSet[i].RemoteName, |
| pFileSet[i].LocalName); |
| if (FAILED(hr)) |
| return hr; |
| } |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_AddFile( |
| IBackgroundCopyJob2 *iface, |
| LPCWSTR RemoteUrl, |
| LPCWSTR LocalName) |
| { |
| BackgroundCopyJobImpl *This = (BackgroundCopyJobImpl *) iface; |
| IBackgroundCopyFile *pFile; |
| BackgroundCopyFileImpl *file; |
| HRESULT res; |
| |
| /* We should return E_INVALIDARG in these cases. */ |
| FIXME("Check for valid filenames and supported protocols\n"); |
| |
| res = BackgroundCopyFileConstructor(This, RemoteUrl, LocalName, (LPVOID *) &pFile); |
| if (res != S_OK) |
| return res; |
| |
| /* Add a reference to the file to file list */ |
| IBackgroundCopyFile_AddRef(pFile); |
| file = (BackgroundCopyFileImpl *) pFile; |
| EnterCriticalSection(&This->cs); |
| list_add_head(&This->files, &file->entryFromJob); |
| This->jobProgress.BytesTotal = BG_SIZE_UNKNOWN; |
| ++This->jobProgress.FilesTotal; |
| LeaveCriticalSection(&This->cs); |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_EnumFiles( |
| IBackgroundCopyJob2 *iface, |
| IEnumBackgroundCopyFiles **ppEnum) |
| { |
| TRACE("\n"); |
| return EnumBackgroundCopyFilesConstructor((LPVOID *) ppEnum, iface); |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_Suspend( |
| IBackgroundCopyJob2 *iface) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_Resume( |
| IBackgroundCopyJob2 *iface) |
| { |
| BackgroundCopyJobImpl *This = (BackgroundCopyJobImpl *) iface; |
| HRESULT rv = S_OK; |
| |
| EnterCriticalSection(&globalMgr.cs); |
| if (This->state == BG_JOB_STATE_CANCELLED |
| || This->state == BG_JOB_STATE_ACKNOWLEDGED) |
| { |
| rv = BG_E_INVALID_STATE; |
| } |
| else if (This->jobProgress.FilesTransferred == This->jobProgress.FilesTotal) |
| { |
| rv = BG_E_EMPTY; |
| } |
| else if (This->state != BG_JOB_STATE_CONNECTING |
| && This->state != BG_JOB_STATE_TRANSFERRING) |
| { |
| This->state = BG_JOB_STATE_QUEUED; |
| SetEvent(globalMgr.jobEvent); |
| } |
| LeaveCriticalSection(&globalMgr.cs); |
| |
| return rv; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_Cancel( |
| IBackgroundCopyJob2 *iface) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_Complete( |
| IBackgroundCopyJob2 *iface) |
| { |
| BackgroundCopyJobImpl *This = (BackgroundCopyJobImpl *) iface; |
| HRESULT rv = S_OK; |
| |
| EnterCriticalSection(&This->cs); |
| |
| if (This->state == BG_JOB_STATE_CANCELLED |
| || This->state == BG_JOB_STATE_ACKNOWLEDGED) |
| { |
| rv = BG_E_INVALID_STATE; |
| } |
| else |
| { |
| BackgroundCopyFileImpl *file; |
| LIST_FOR_EACH_ENTRY(file, &This->files, BackgroundCopyFileImpl, entryFromJob) |
| { |
| if (file->fileProgress.Completed) |
| { |
| if (!MoveFileExW(file->tempFileName, file->info.LocalName, |
| (MOVEFILE_COPY_ALLOWED |
| | MOVEFILE_REPLACE_EXISTING |
| | MOVEFILE_WRITE_THROUGH))) |
| { |
| ERR("Couldn't rename file %s -> %s\n", |
| debugstr_w(file->tempFileName), |
| debugstr_w(file->info.LocalName)); |
| rv = BG_S_PARTIAL_COMPLETE; |
| } |
| } |
| else |
| rv = BG_S_PARTIAL_COMPLETE; |
| } |
| } |
| |
| This->state = BG_JOB_STATE_ACKNOWLEDGED; |
| LeaveCriticalSection(&This->cs); |
| |
| return rv; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetId( |
| IBackgroundCopyJob2 *iface, |
| GUID *pVal) |
| { |
| BackgroundCopyJobImpl *This = (BackgroundCopyJobImpl *) iface; |
| *pVal = This->jobId; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetType( |
| IBackgroundCopyJob2 *iface, |
| BG_JOB_TYPE *pVal) |
| { |
| BackgroundCopyJobImpl *This = (BackgroundCopyJobImpl *) iface; |
| |
| if (!pVal) |
| return E_INVALIDARG; |
| |
| *pVal = This->type; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetProgress( |
| IBackgroundCopyJob2 *iface, |
| BG_JOB_PROGRESS *pVal) |
| { |
| BackgroundCopyJobImpl *This = (BackgroundCopyJobImpl *) iface; |
| |
| if (!pVal) |
| return E_INVALIDARG; |
| |
| EnterCriticalSection(&This->cs); |
| pVal->BytesTotal = This->jobProgress.BytesTotal; |
| pVal->BytesTransferred = This->jobProgress.BytesTransferred; |
| pVal->FilesTotal = This->jobProgress.FilesTotal; |
| pVal->FilesTransferred = This->jobProgress.FilesTransferred; |
| LeaveCriticalSection(&This->cs); |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetTimes( |
| IBackgroundCopyJob2 *iface, |
| BG_JOB_TIMES *pVal) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetState( |
| IBackgroundCopyJob2 *iface, |
| BG_JOB_STATE *pVal) |
| { |
| BackgroundCopyJobImpl *This = (BackgroundCopyJobImpl *) iface; |
| |
| if (!pVal) |
| return E_INVALIDARG; |
| |
| /* Don't think we need a critical section for this */ |
| *pVal = This->state; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetError( |
| IBackgroundCopyJob2 *iface, |
| IBackgroundCopyError **ppError) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetOwner( |
| IBackgroundCopyJob2 *iface, |
| LPWSTR *pVal) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_SetDisplayName( |
| IBackgroundCopyJob2 *iface, |
| LPCWSTR Val) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetDisplayName( |
| IBackgroundCopyJob2 *iface, |
| LPWSTR *pVal) |
| { |
| BackgroundCopyJobImpl *This = (BackgroundCopyJobImpl *) iface; |
| int n; |
| |
| if (!pVal) |
| return E_INVALIDARG; |
| |
| n = (lstrlenW(This->displayName) + 1) * sizeof **pVal; |
| *pVal = CoTaskMemAlloc(n); |
| if (*pVal == NULL) |
| return E_OUTOFMEMORY; |
| memcpy(*pVal, This->displayName, n); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_SetDescription( |
| IBackgroundCopyJob2 *iface, |
| LPCWSTR Val) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetDescription( |
| IBackgroundCopyJob2 *iface, |
| LPWSTR *pVal) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_SetPriority( |
| IBackgroundCopyJob2 *iface, |
| BG_JOB_PRIORITY Val) |
| { |
| FIXME("(%p,0x%08x) stub\n", iface, Val); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetPriority( |
| IBackgroundCopyJob2 *iface, |
| BG_JOB_PRIORITY *pVal) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_SetNotifyFlags( |
| IBackgroundCopyJob2 *iface, |
| ULONG Val) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetNotifyFlags( |
| IBackgroundCopyJob2 *iface, |
| ULONG *pVal) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_SetNotifyInterface( |
| IBackgroundCopyJob2 *iface, |
| IUnknown *Val) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetNotifyInterface( |
| IBackgroundCopyJob2 *iface, |
| IUnknown **pVal) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_SetMinimumRetryDelay( |
| IBackgroundCopyJob2 *iface, |
| ULONG Seconds) |
| { |
| FIXME("%u\n", Seconds); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetMinimumRetryDelay( |
| IBackgroundCopyJob2 *iface, |
| ULONG *Seconds) |
| { |
| FIXME("%p\n", Seconds); |
| *Seconds = 30; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_SetNoProgressTimeout( |
| IBackgroundCopyJob2 *iface, |
| ULONG Seconds) |
| { |
| FIXME("%u\n", Seconds); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetNoProgressTimeout( |
| IBackgroundCopyJob2 *iface, |
| ULONG *Seconds) |
| { |
| FIXME("%p\n", Seconds); |
| *Seconds = 900; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetErrorCount( |
| IBackgroundCopyJob2 *iface, |
| ULONG *Errors) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_SetProxySettings( |
| IBackgroundCopyJob2 *iface, |
| BG_JOB_PROXY_USAGE ProxyUsage, |
| const WCHAR *ProxyList, |
| const WCHAR *ProxyBypassList) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetProxySettings( |
| IBackgroundCopyJob2 *iface, |
| BG_JOB_PROXY_USAGE *pProxyUsage, |
| LPWSTR *pProxyList, |
| LPWSTR *pProxyBypassList) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_TakeOwnership( |
| IBackgroundCopyJob2 *iface) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_SetNotifyCmdLine( |
| IBackgroundCopyJob2 *iface, |
| LPCWSTR prog, |
| LPCWSTR params) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetNotifyCmdLine( |
| IBackgroundCopyJob2 *iface, |
| LPWSTR *prog, |
| LPWSTR *params) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetReplyProgress( |
| IBackgroundCopyJob2 *iface, |
| BG_JOB_REPLY_PROGRESS *progress) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetReplyData( |
| IBackgroundCopyJob2 *iface, |
| byte **pBuffer, |
| UINT64 *pLength) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_SetReplyFileName( |
| IBackgroundCopyJob2 *iface, |
| LPCWSTR filename) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_GetReplyFileName( |
| IBackgroundCopyJob2 *iface, |
| LPWSTR *pFilename) |
| { |
| FIXME("Not implemented\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_SetCredentials( |
| IBackgroundCopyJob2 *iface, |
| BG_AUTH_CREDENTIALS *cred) |
| { |
| FIXME("Not implemented\n"); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI BITS_IBackgroundCopyJob_RemoveCredentials( |
| IBackgroundCopyJob2 *iface, |
| BG_AUTH_TARGET target, |
| BG_AUTH_SCHEME scheme) |
| { |
| FIXME("Not implemented\n"); |
| return S_OK; |
| } |
| |
| static const IBackgroundCopyJob2Vtbl BITS_IBackgroundCopyJob_Vtbl = |
| { |
| BITS_IBackgroundCopyJob_QueryInterface, |
| BITS_IBackgroundCopyJob_AddRef, |
| BITS_IBackgroundCopyJob_Release, |
| BITS_IBackgroundCopyJob_AddFileSet, |
| BITS_IBackgroundCopyJob_AddFile, |
| BITS_IBackgroundCopyJob_EnumFiles, |
| BITS_IBackgroundCopyJob_Suspend, |
| BITS_IBackgroundCopyJob_Resume, |
| BITS_IBackgroundCopyJob_Cancel, |
| BITS_IBackgroundCopyJob_Complete, |
| BITS_IBackgroundCopyJob_GetId, |
| BITS_IBackgroundCopyJob_GetType, |
| BITS_IBackgroundCopyJob_GetProgress, |
| BITS_IBackgroundCopyJob_GetTimes, |
| BITS_IBackgroundCopyJob_GetState, |
| BITS_IBackgroundCopyJob_GetError, |
| BITS_IBackgroundCopyJob_GetOwner, |
| BITS_IBackgroundCopyJob_SetDisplayName, |
| BITS_IBackgroundCopyJob_GetDisplayName, |
| BITS_IBackgroundCopyJob_SetDescription, |
| BITS_IBackgroundCopyJob_GetDescription, |
| BITS_IBackgroundCopyJob_SetPriority, |
| BITS_IBackgroundCopyJob_GetPriority, |
| BITS_IBackgroundCopyJob_SetNotifyFlags, |
| BITS_IBackgroundCopyJob_GetNotifyFlags, |
| BITS_IBackgroundCopyJob_SetNotifyInterface, |
| BITS_IBackgroundCopyJob_GetNotifyInterface, |
| BITS_IBackgroundCopyJob_SetMinimumRetryDelay, |
| BITS_IBackgroundCopyJob_GetMinimumRetryDelay, |
| BITS_IBackgroundCopyJob_SetNoProgressTimeout, |
| BITS_IBackgroundCopyJob_GetNoProgressTimeout, |
| BITS_IBackgroundCopyJob_GetErrorCount, |
| BITS_IBackgroundCopyJob_SetProxySettings, |
| BITS_IBackgroundCopyJob_GetProxySettings, |
| BITS_IBackgroundCopyJob_TakeOwnership, |
| BITS_IBackgroundCopyJob_SetNotifyCmdLine, |
| BITS_IBackgroundCopyJob_GetNotifyCmdLine, |
| BITS_IBackgroundCopyJob_GetReplyProgress, |
| BITS_IBackgroundCopyJob_GetReplyData, |
| BITS_IBackgroundCopyJob_SetReplyFileName, |
| BITS_IBackgroundCopyJob_GetReplyFileName, |
| BITS_IBackgroundCopyJob_SetCredentials, |
| BITS_IBackgroundCopyJob_RemoveCredentials |
| }; |
| |
| HRESULT BackgroundCopyJobConstructor(LPCWSTR displayName, BG_JOB_TYPE type, |
| GUID *pJobId, LPVOID *ppObj) |
| { |
| HRESULT hr; |
| BackgroundCopyJobImpl *This; |
| int n; |
| |
| TRACE("(%s,%d,%p)\n", debugstr_w(displayName), type, ppObj); |
| |
| This = HeapAlloc(GetProcessHeap(), 0, sizeof *This); |
| if (!This) |
| return E_OUTOFMEMORY; |
| |
| This->lpVtbl = &BITS_IBackgroundCopyJob_Vtbl; |
| InitializeCriticalSection(&This->cs); |
| This->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": BackgroundCopyJobImpl.cs"); |
| |
| This->ref = 1; |
| This->type = type; |
| |
| n = (lstrlenW(displayName) + 1) * sizeof *displayName; |
| This->displayName = HeapAlloc(GetProcessHeap(), 0, n); |
| if (!This->displayName) |
| { |
| This->cs.DebugInfo->Spare[0] = 0; |
| DeleteCriticalSection(&This->cs); |
| HeapFree(GetProcessHeap(), 0, This); |
| return E_OUTOFMEMORY; |
| } |
| memcpy(This->displayName, displayName, n); |
| |
| hr = CoCreateGuid(&This->jobId); |
| if (FAILED(hr)) |
| { |
| This->cs.DebugInfo->Spare[0] = 0; |
| DeleteCriticalSection(&This->cs); |
| HeapFree(GetProcessHeap(), 0, This->displayName); |
| HeapFree(GetProcessHeap(), 0, This); |
| return hr; |
| } |
| *pJobId = This->jobId; |
| |
| list_init(&This->files); |
| This->jobProgress.BytesTotal = 0; |
| This->jobProgress.BytesTransferred = 0; |
| This->jobProgress.FilesTotal = 0; |
| This->jobProgress.FilesTransferred = 0; |
| |
| This->state = BG_JOB_STATE_SUSPENDED; |
| |
| *ppObj = &This->lpVtbl; |
| return S_OK; |
| } |
| |
| void processJob(BackgroundCopyJobImpl *job) |
| { |
| for (;;) |
| { |
| BackgroundCopyFileImpl *file; |
| BOOL done = TRUE; |
| |
| EnterCriticalSection(&job->cs); |
| LIST_FOR_EACH_ENTRY(file, &job->files, BackgroundCopyFileImpl, entryFromJob) |
| if (!file->fileProgress.Completed) |
| { |
| done = FALSE; |
| break; |
| } |
| LeaveCriticalSection(&job->cs); |
| if (done) |
| { |
| transitionJobState(job, BG_JOB_STATE_QUEUED, BG_JOB_STATE_TRANSFERRED); |
| return; |
| } |
| |
| if (!processFile(file, job)) |
| return; |
| } |
| } |