Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5712 lines
139 KiB

  1. /************************************************************************
  2. Copyright (c) 2000 - 2000 Microsoft Corporation
  3. Module Name :
  4. cjob.cpp
  5. Abstract :
  6. Main code file for handling jobs and files.
  7. Author :
  8. Revision History :
  9. ***********************************************************************/
  10. #include "stdafx.h"
  11. #include <malloc.h>
  12. #include <numeric>
  13. #include <functional>
  14. #include <algorithm>
  15. #include <sddl.h>
  16. #include "cjob.tmh"
  17. // infinite retry wait time
  18. //
  19. #define INFINITE_RETRY_DELAY UINT64(-1)
  20. //
  21. // This is the number of seconds to keep trying to cancel an upload session in progress.
  22. //
  23. #define UPLOAD_CANCEL_TIMEOUT (24 * 60 * 60)
  24. #define DEFAULT_JOB_TIMEOUT_TIME (90 * 24 * 60 * 60)
  25. #define PROGRESS_SERIALIZE_INTERVAL (15 * NanoSec100PerSec)
  26. // largest reply blob that can be returned via GetReplyData
  27. //
  28. #define MAX_EASY_REPLY_DATA (1024 * 1024)
  29. #define MAX_LOGGED_UNSUCCESSFUL_FILES 50
  30. //------------------------------------------------------------------------
  31. CJob::CJob()
  32. :
  33. m_ExternalInterface( new CJobExternal),
  34. m_state( BG_JOB_STATE_SUSPENDED ),
  35. m_NotifyPointer( NULL ),
  36. m_sd( NULL ),
  37. m_CurrentFile( 0 ),
  38. m_OldExternalJobInterface( NULL ),
  39. m_OldExternalGroupInterface( NULL ),
  40. m_FilesVerified( false )
  41. {
  42. //
  43. // constructor has succeeded; allow CJobExternal to manage our lifetime.
  44. //
  45. GetExternalInterface()->SetInterfaceClass(this);
  46. }
  47. CJob::CJob(
  48. LPCWSTR DisplayName,
  49. BG_JOB_TYPE Type,
  50. REFGUID JobId,
  51. SidHandle NotifySid
  52. ) :
  53. m_ExternalInterface( new CJobExternal),
  54. m_id( JobId ),
  55. m_name( DisplayName ),
  56. m_type( Type ),
  57. m_priority( BG_JOB_PRIORITY_NORMAL ),
  58. m_state( BG_JOB_STATE_SUSPENDED ),
  59. m_retries( 0 ),
  60. m_NotifySid( NotifySid ),
  61. m_NotifyPointer( NULL ),
  62. m_sd( NULL ),
  63. m_CurrentFile( 0 ),
  64. m_MinimumRetryDelay( g_GlobalInfo->m_DefaultMinimumRetryDelay ),
  65. m_NoProgressTimeout( g_GlobalInfo->m_DefaultNoProgressTimeout ),
  66. m_OldExternalJobInterface( NULL ),
  67. m_OldExternalGroupInterface( NULL ),
  68. m_TransferCompletionTime( UINT64ToFILETIME( 0 )),
  69. m_SerializeTime( UINT64ToFILETIME( 0 )),
  70. m_fVolumeLocked( false ),
  71. m_NotifyFlags( BG_NOTIFY_JOB_TRANSFERRED | BG_NOTIFY_JOB_ERROR ),
  72. m_fGroupNotifySid( FALSE ),
  73. m_FilesVerified( false )
  74. {
  75. LogInfo( "new job %p : ID is %!guid!, external %p", this, &m_id, m_ExternalInterface );
  76. GetSystemTimeAsFileTime( &m_CreationTime );
  77. m_ModificationTime = m_CreationTime;
  78. m_LastAccessTime = m_CreationTime;
  79. // we don't support group SIDs yet.
  80. // THROW_HRESULT( IsGroupSid( m_NotifySid, &m_fGroupNotifySid ))
  81. m_sd = new CJobSecurityDescriptor( NotifySid );
  82. //
  83. // constructor has succeeded; allow CJobExternal to manage our lifetime.
  84. //
  85. GetExternalInterface()->SetInterfaceClass(this);
  86. }
  87. CJob::~CJob()
  88. {
  89. //
  90. // This should be redundant, but let's be safe.
  91. //
  92. g_Manager->m_TaskScheduler.CancelWorkItem( static_cast<CJobModificationItem *> (this) );
  93. CancelWorkitems();
  94. delete m_sd;
  95. for (CFileList::iterator iter = m_files.begin(); iter != m_files.end(); ++iter)
  96. {
  97. delete (*iter);
  98. }
  99. m_files.clear();
  100. if (g_LastServiceControl != SERVICE_CONTROL_SHUTDOWN)
  101. {
  102. SafeRelease( m_NotifyPointer );
  103. }
  104. }
  105. void CJob::UnlinkFromExternalInterfaces()
  106. {
  107. //
  108. // These objects np longer control the CJob's lifetime...
  109. //
  110. if (m_ExternalInterface)
  111. {
  112. m_ExternalInterface->SetInterfaceClass( NULL );
  113. }
  114. if (m_OldExternalJobInterface)
  115. {
  116. m_OldExternalJobInterface->SetInterfaceClass( NULL );
  117. }
  118. if (m_OldExternalGroupInterface)
  119. {
  120. m_OldExternalGroupInterface->SetInterfaceClass( NULL );
  121. }
  122. //
  123. // ...and the CJob no longer holds a reference to them.
  124. //
  125. SafeRelease( m_ExternalInterface );
  126. SafeRelease( m_OldExternalJobInterface );
  127. SafeRelease( m_OldExternalGroupInterface );
  128. }
  129. void
  130. CJob::HandleAddFile()
  131. {
  132. if ( m_state == BG_JOB_STATE_TRANSFERRED )
  133. {
  134. SetState( BG_JOB_STATE_QUEUED );
  135. m_TransferCompletionTime = UINT64ToFILETIME( 0 );
  136. g_Manager->m_TaskScheduler.CancelWorkItem( (CJobRetryItem *) this );
  137. g_Manager->m_TaskScheduler.CancelWorkItem( (CJobCallbackItem *) this );
  138. g_Manager->m_TaskScheduler.CancelWorkItem( (CJobNoProgressItem *) this );
  139. }
  140. UpdateModificationTime();
  141. // restart the downloader if its running.
  142. g_Manager->RetaskJob( this );
  143. }
  144. //
  145. // Returns E_INVALIDARG if one of the filesets has
  146. // - local name is blank
  147. // - local name contains invalid characters
  148. // - remote name is blank
  149. // - remote name has invalid format
  150. //
  151. // Returns CO_E_NOT_SUPPORTED if
  152. // - remote URL contains unsupported protocol
  153. //
  154. HRESULT
  155. CJob::AddFileSet(
  156. IN ULONG cFileCount,
  157. IN BG_FILE_INFO *pFileSet
  158. )
  159. {
  160. ULONG FirstNewIndex = m_files.size();
  161. try
  162. {
  163. ULONG i;
  164. g_Manager->ExtendMetadata( ( METADATA_FOR_FILE * cFileCount ) + METADATA_PADDING );
  165. for (i=0; i < cFileCount; ++i)
  166. {
  167. THROW_HRESULT( AddFile( pFileSet[i].RemoteName,
  168. pFileSet[i].LocalName,
  169. false
  170. ));
  171. }
  172. HandleAddFile();
  173. return S_OK;
  174. }
  175. catch ( ComError exception )
  176. {
  177. if (m_type == BG_JOB_TYPE_DOWNLOAD)
  178. {
  179. try
  180. {
  181. // Delete temp files for new file objects.
  182. //
  183. RemoveTemporaryFiles( FirstNewIndex );
  184. }
  185. catch ( ComError err )
  186. {
  187. LogError("exception 0x%x at line %d", err.m_error, err.m_line );
  188. // carry on
  189. }
  190. }
  191. // Delete new files objects.
  192. // This assumes that new files are added at the back of the sequence.
  193. //
  194. m_files.Delete( m_files.begin() + FirstNewIndex, m_files.end() );
  195. g_Manager->ShrinkMetadata();
  196. return exception.Error();
  197. }
  198. }
  199. HRESULT
  200. CJob::AddFile(
  201. IN LPCWSTR RemoteName,
  202. IN LPCWSTR LocalName,
  203. IN bool SingleAdd
  204. )
  205. {
  206. HRESULT hr = S_OK;
  207. CFile * file = NULL;
  208. //
  209. // This check must be completed outside the try..except; otherwise
  210. // the attempt to add a 2nd file would delete the generated reply file
  211. // for the 1st file.
  212. //
  213. if (m_type != BG_JOB_TYPE_DOWNLOAD && m_files.size() > 0)
  214. {
  215. return BG_E_TOO_MANY_FILES;
  216. }
  217. try
  218. {
  219. if ( !RemoteName || !LocalName )
  220. THROW_HRESULT( E_INVALIDARG );
  221. LogInfo("job %p addfile( %S, %S )", this, RemoteName, LocalName );
  222. if ( ( _GetState() == BG_JOB_STATE_CANCELLED ) ||
  223. ( _GetState() == BG_JOB_STATE_ACKNOWLEDGED ) )
  224. throw ComError( BG_E_INVALID_STATE );
  225. if ( SingleAdd )
  226. g_Manager->ExtendMetadata( METADATA_FOR_FILE + METADATA_PADDING );
  227. //
  228. // Impersonate the user while checking file access.
  229. //
  230. CNestedImpersonation imp;
  231. imp.SwitchToLogonToken();
  232. file = new CFile( this, m_type, RemoteName, LocalName );
  233. // WARNING: if you change this, also update the cleanup logic in AddFileSet.
  234. //
  235. m_files.push_back( file );
  236. //
  237. // Try to create the default reply file. Ignore error, because the app
  238. // may be planning to set the reply file somewhere else.
  239. //
  240. if (m_type == BG_JOB_TYPE_UPLOAD_REPLY)
  241. {
  242. ((CUploadJob *) this)->GenerateReplyFile( false );
  243. }
  244. m_FilesVerified = false;
  245. }
  246. catch ( ComError exception )
  247. {
  248. delete file;
  249. file = NULL;
  250. if (m_type == BG_JOB_TYPE_UPLOAD_REPLY)
  251. {
  252. ((CUploadJob *) this)->DeleteGeneratedReplyFile();
  253. ((CUploadJob *) this)->ClearOwnFileNameBit();
  254. }
  255. if ( SingleAdd )
  256. g_Manager->ShrinkMetadata();
  257. hr = exception.Error();
  258. }
  259. if ( SUCCEEDED(hr) && SingleAdd )
  260. {
  261. HandleAddFile();
  262. }
  263. return hr;
  264. }
  265. HRESULT
  266. CJob::SetDisplayName(
  267. LPCWSTR Val
  268. )
  269. {
  270. return SetLimitedString( m_name, Val, MAX_DISPLAYNAME );
  271. }
  272. HRESULT
  273. CJob::GetDisplayName(
  274. LPWSTR * pVal
  275. ) const
  276. {
  277. *pVal = MidlCopyString( m_name );
  278. return (*pVal) ? S_OK : E_OUTOFMEMORY;
  279. }
  280. HRESULT
  281. CJob::SetDescription(
  282. LPCWSTR Val
  283. )
  284. {
  285. return SetLimitedString( m_description, Val, MAX_DESCRIPTION );
  286. }
  287. HRESULT
  288. CJob::GetDescription(
  289. LPWSTR *pVal
  290. ) const
  291. {
  292. *pVal = MidlCopyString( m_description );
  293. return (*pVal) ? S_OK : E_OUTOFMEMORY;
  294. }
  295. HRESULT
  296. CJob::SetNotifyCmdLine(
  297. LPCWSTR Program,
  298. LPCWSTR Parameters
  299. )
  300. {
  301. StringHandle OldProgram;
  302. StringHandle OldParameters;
  303. if (Program == NULL && Parameters != NULL)
  304. {
  305. return E_INVALIDARG;
  306. }
  307. try
  308. {
  309. OldProgram = m_NotifyProgram;
  310. OldParameters = m_NotifyParameters;
  311. THROW_HRESULT( SetLimitedString( m_NotifyProgram, Program, MAX_NOTIFY_PROGRAM ));
  312. THROW_HRESULT( SetLimitedString( m_NotifyParameters, Parameters, MAX_NOTIFY_PARAMETERS ));
  313. return S_OK;
  314. }
  315. catch ( ComError err )
  316. {
  317. m_NotifyProgram = OldProgram;
  318. m_NotifyParameters = OldParameters;
  319. return err.Error();
  320. }
  321. }
  322. HRESULT
  323. CJob::GetNotifyCmdLine(
  324. LPWSTR *pProgram,
  325. LPWSTR *pParameters
  326. ) const
  327. {
  328. *pProgram = NULL;
  329. *pParameters = NULL;
  330. if (m_NotifyProgram.Size() > 0)
  331. {
  332. *pProgram = MidlCopyString( m_NotifyProgram );
  333. if (!*pProgram)
  334. {
  335. return E_OUTOFMEMORY;
  336. }
  337. }
  338. if (m_NotifyParameters.Size() > 0)
  339. {
  340. *pParameters = MidlCopyString( m_NotifyParameters );
  341. if (!*pParameters)
  342. {
  343. CoTaskMemFree( *pProgram );
  344. *pProgram = NULL;
  345. return E_OUTOFMEMORY;
  346. }
  347. }
  348. return S_OK;
  349. }
  350. HRESULT
  351. CJob::SetProxySettings(
  352. BG_JOB_PROXY_USAGE ProxyUsage,
  353. LPCWSTR ProxyList,
  354. LPCWSTR ProxyBypassList
  355. )
  356. {
  357. HRESULT hr = S_OK;
  358. if ( ProxyUsage != BG_JOB_PROXY_USAGE_PRECONFIG &&
  359. ProxyUsage != BG_JOB_PROXY_USAGE_NO_PROXY &&
  360. ProxyUsage != BG_JOB_PROXY_USAGE_OVERRIDE )
  361. {
  362. return E_INVALIDARG;
  363. }
  364. if ( BG_JOB_PROXY_USAGE_PRECONFIG == ProxyUsage ||
  365. BG_JOB_PROXY_USAGE_NO_PROXY == ProxyUsage )
  366. {
  367. if ( NULL != ProxyList ||
  368. NULL != ProxyBypassList )
  369. return E_INVALIDARG;
  370. }
  371. else
  372. {
  373. // BG_PROXY_USAGE_OVERRIDE == ProxyUsage
  374. if ( NULL == ProxyList )
  375. return E_INVALIDARG;
  376. }
  377. try
  378. {
  379. //
  380. // Allocate space for the new proxy settings.
  381. //
  382. CAutoString ProxyListTemp(NULL);
  383. CAutoString ProxyBypassListTemp(NULL);
  384. g_Manager->ExtendMetadata();
  385. if ( ProxyList )
  386. {
  387. if ( wcslen( ProxyList ) > MAX_PROXYLIST )
  388. throw ComError( BG_E_PROXY_LIST_TOO_LARGE );
  389. ProxyListTemp = CAutoString( CopyString( ProxyList ));
  390. }
  391. if ( ProxyBypassList )
  392. {
  393. if ( wcslen( ProxyBypassList ) > MAX_PROXYBYPASSLIST )
  394. throw ComError( BG_E_PROXY_BYPASS_LIST_TOO_LARGE );
  395. ProxyBypassListTemp = CAutoString( CopyString( ProxyBypassList ));
  396. }
  397. //
  398. // Interrupt the job if it is downloading, and resume at the end of this fn.
  399. // This keeps the downloader from referring to the memory that we are deleting,
  400. // and ensures that the most current job settings are used during the download.
  401. //
  402. CRescheduleDownload r( this );
  403. //
  404. // Swap the old proxy settings for the new ones.
  405. //
  406. delete[] m_ProxySettings.ProxyList;
  407. delete[] m_ProxySettings.ProxyBypassList;
  408. m_ProxySettings.ProxyUsage = ProxyUsage;
  409. m_ProxySettings.ProxyList = ProxyListTemp.release();
  410. m_ProxySettings.ProxyBypassList = ProxyBypassListTemp.release();
  411. UpdateModificationTime();
  412. return S_OK;
  413. }
  414. catch( ComError error )
  415. {
  416. g_Manager->ShrinkMetadata();
  417. return error.Error();
  418. }
  419. }
  420. HRESULT
  421. CJob::GetProxySettings(
  422. BG_JOB_PROXY_USAGE *pProxyUsage,
  423. LPWSTR *pProxyList,
  424. LPWSTR *pProxyBypassList
  425. ) const
  426. {
  427. HRESULT Hr = S_OK;
  428. *pProxyUsage = m_ProxySettings.ProxyUsage;
  429. *pProxyList = NULL;
  430. *pProxyBypassList = NULL;
  431. try
  432. {
  433. if ( m_ProxySettings.ProxyList )
  434. {
  435. *pProxyList = MidlCopyString( m_ProxySettings.ProxyList );
  436. if (!*pProxyList)
  437. throw ComError( E_OUTOFMEMORY );
  438. }
  439. if ( m_ProxySettings.ProxyBypassList )
  440. {
  441. *pProxyBypassList = MidlCopyString( m_ProxySettings.ProxyBypassList );
  442. if (!*pProxyBypassList)
  443. throw ComError( E_OUTOFMEMORY );
  444. }
  445. }
  446. catch( ComError exception )
  447. {
  448. Hr = exception.Error();
  449. CoTaskMemFree( *pProxyList );
  450. CoTaskMemFree( *pProxyBypassList );
  451. *pProxyList = *pProxyBypassList = NULL;
  452. }
  453. return Hr;
  454. }
  455. void
  456. CJob::GetTimes(
  457. BG_JOB_TIMES * s
  458. ) const
  459. {
  460. s->CreationTime = m_CreationTime;
  461. s->ModificationTime = m_ModificationTime;
  462. s->TransferCompletionTime = m_TransferCompletionTime;
  463. }
  464. void
  465. CJob::GetProgress(
  466. BG_JOB_PROGRESS * s
  467. ) const
  468. {
  469. s->BytesTransferred = 0;
  470. s->BytesTotal = 0;
  471. CFileList::const_iterator iter;
  472. for (iter = m_files.begin(); iter != m_files.end(); ++iter)
  473. {
  474. BG_FILE_PROGRESS s2;
  475. (*iter)->GetProgress( &s2 );
  476. s->BytesTransferred += s2.BytesTransferred;
  477. if (s2.BytesTotal != BG_SIZE_UNKNOWN &&
  478. s->BytesTotal != BG_SIZE_UNKNOWN )
  479. {
  480. s->BytesTotal += s2.BytesTotal;
  481. }
  482. else
  483. {
  484. s->BytesTotal = BG_SIZE_UNKNOWN;
  485. }
  486. }
  487. s->FilesTransferred = (ULONG) m_CurrentFile;
  488. s->FilesTotal = m_files.size();
  489. }
  490. HRESULT
  491. CJob::GetOwner(
  492. LPWSTR * pVal
  493. ) const
  494. {
  495. wchar_t * buf;
  496. wchar_t * str;
  497. if (!ConvertSidToStringSid( m_NotifySid.get(), &str))
  498. {
  499. return HRESULT_FROM_WIN32( GetLastError());
  500. }
  501. *pVal = MidlCopyString( str );
  502. LocalFree( str );
  503. return (*pVal) ? S_OK : E_OUTOFMEMORY;
  504. }
  505. HRESULT
  506. CJob::SetPriority(
  507. BG_JOB_PRIORITY Val
  508. )
  509. {
  510. if (Val > BG_JOB_PRIORITY_LOW ||
  511. Val < BG_JOB_PRIORITY_FOREGROUND)
  512. {
  513. return E_NOTIMPL;
  514. }
  515. if (Val == m_priority)
  516. {
  517. return S_OK;
  518. }
  519. m_priority = Val;
  520. g_Manager->RetaskJob( this );
  521. UpdateModificationTime();
  522. return S_OK;
  523. }
  524. HRESULT
  525. CJob::SetNotifyFlags(
  526. ULONG Val
  527. )
  528. {
  529. // Note, this flag will have no affect on a callback already in progress.
  530. if ( Val & ~(BG_NOTIFY_JOB_TRANSFERRED | BG_NOTIFY_JOB_ERROR | BG_NOTIFY_DISABLE | BG_NOTIFY_JOB_MODIFICATION ) )
  531. {
  532. return E_NOTIMPL;
  533. }
  534. m_NotifyFlags = Val;
  535. UpdateModificationTime();
  536. return S_OK;
  537. }
  538. HRESULT
  539. CJob::SetNotifyInterface(
  540. IUnknown * Val
  541. )
  542. {
  543. // Note, this flag may not have any affect on a callback already in progress.
  544. IBackgroundCopyCallback *pICB = NULL;
  545. if ( Val )
  546. {
  547. try
  548. {
  549. CNestedImpersonation imp;
  550. imp.SwitchToLogonToken();
  551. THROW_HRESULT( SetStaticCloaking( Val ) );
  552. THROW_HRESULT( Val->QueryInterface( __uuidof(IBackgroundCopyCallback),
  553. (void **) &pICB ) );
  554. // All callbacks should happen in the context of the
  555. // person who set the interface pointer.
  556. HRESULT Hr = SetStaticCloaking( pICB );
  557. if ( FAILED( Hr ) )
  558. {
  559. SafeRelease( pICB );
  560. throw ComError( Hr );
  561. }
  562. }
  563. catch( ComError Error )
  564. {
  565. return Error.Error();
  566. }
  567. }
  568. // Release the old pointer if it exists
  569. SafeRelease( m_NotifyPointer );
  570. m_NotifyPointer = pICB;
  571. return S_OK;
  572. }
  573. HRESULT
  574. CJob::GetNotifyInterface(
  575. IUnknown ** ppVal
  576. ) const
  577. {
  578. try
  579. {
  580. CNestedImpersonation imp;
  581. if (m_NotifyPointer)
  582. {
  583. m_NotifyPointer->AddRef();
  584. }
  585. *ppVal = m_NotifyPointer;
  586. return S_OK;
  587. }
  588. catch ( ComError err )
  589. {
  590. *ppVal = NULL;
  591. return err.Error();
  592. }
  593. }
  594. // CJob::TestNotifyInterface()
  595. //
  596. // See if a notification interface is provide, if so, test it to see if it is
  597. // valid. If so, then return TRUE, else return FALSE.
  598. BOOL
  599. CJob::TestNotifyInterface()
  600. {
  601. BOOL fValidNotifyInterface = TRUE;
  602. try
  603. {
  604. CNestedImpersonation imp;
  605. IUnknown *pPrevIntf = NULL;
  606. // Ok, see if there was a previously registered interface, and if
  607. // there is, see if it's still valid.
  608. if (m_NotifyPointer)
  609. {
  610. m_NotifyPointer->AddRef();
  611. if ( (FAILED(m_NotifyPointer->QueryInterface(IID_IUnknown,(void**)&pPrevIntf)))
  612. ||(pPrevIntf == NULL) )
  613. {
  614. fValidNotifyInterface = FALSE;
  615. }
  616. else
  617. {
  618. fValidNotifyInterface = TRUE;
  619. pPrevIntf->Release();
  620. }
  621. m_NotifyPointer->Release();
  622. }
  623. else
  624. {
  625. fValidNotifyInterface = FALSE;
  626. }
  627. }
  628. catch( ComError err )
  629. {
  630. fValidNotifyInterface = FALSE;
  631. }
  632. return fValidNotifyInterface;
  633. }
  634. HRESULT
  635. CJob::GetMinimumRetryDelay(
  636. ULONG * pVal
  637. ) const
  638. {
  639. *pVal = m_MinimumRetryDelay;
  640. return S_OK;
  641. }
  642. HRESULT
  643. CJob::SetMinimumRetryDelay(
  644. ULONG Val
  645. )
  646. {
  647. m_MinimumRetryDelay = Val;
  648. g_Manager->m_TaskScheduler.RescheduleDelayedTask(
  649. (CJobRetryItem *)this,
  650. (UINT64)m_MinimumRetryDelay * (UINT64) NanoSec100PerSec);
  651. UpdateModificationTime();
  652. return S_OK;
  653. }
  654. HRESULT
  655. CJob::GetNoProgressTimeout(
  656. ULONG * pVal
  657. ) const
  658. {
  659. *pVal = m_NoProgressTimeout;
  660. return S_OK;
  661. }
  662. HRESULT
  663. CJob::SetNoProgressTimeout(
  664. ULONG Val
  665. )
  666. {
  667. m_NoProgressTimeout = Val;
  668. g_Manager->m_TaskScheduler.RescheduleDelayedTask(
  669. (CJobNoProgressItem *)this,
  670. (UINT64)m_NoProgressTimeout * (UINT64) NanoSec100PerSec);
  671. UpdateModificationTime();
  672. return S_OK;
  673. }
  674. HRESULT
  675. CJob::GetErrorCount(
  676. ULONG * pVal
  677. ) const
  678. {
  679. *pVal = m_retries;
  680. return S_OK;
  681. }
  682. HRESULT
  683. CJob::SetCredentials(
  684. BG_AUTH_CREDENTIALS * Credentials
  685. )
  686. {
  687. try
  688. {
  689. THROW_HRESULT( ValidateCredentials( Credentials ));
  690. CNestedImpersonation imp;
  691. imp.SwitchToLogonToken();
  692. //
  693. // Interrupt the job if it is downloading, and resume at the end of this fn.
  694. // This keeps the downloader from referring to the memory that we are deleting,
  695. // and ensures that the most current job settings are used during the download.
  696. //
  697. CRescheduleDownload r( this );
  698. g_Manager->ExtendMetadata( m_Credentials.GetSizeEstimate( Credentials ));
  699. THROW_HRESULT( m_Credentials.Update( Credentials ));
  700. g_Manager->Serialize();
  701. return S_OK;
  702. }
  703. catch ( ComError err )
  704. {
  705. g_Manager->ShrinkMetadata();
  706. return err.Error();
  707. }
  708. }
  709. HRESULT
  710. CJob::RemoveCredentials(
  711. BG_AUTH_TARGET Target,
  712. BG_AUTH_SCHEME Scheme
  713. )
  714. {
  715. try
  716. {
  717. CNestedImpersonation imp;
  718. imp.SwitchToLogonToken();
  719. //
  720. // Interrupt the job if it is downloading, and resume at the end of this fn.
  721. // This keeps the downloader from referring to the memory that we are deleting,
  722. // and ensures that the most current job settings are used during the download.
  723. //
  724. CRescheduleDownload r( this );
  725. HRESULT hr = m_Credentials.Remove( Target, Scheme );
  726. THROW_HRESULT( hr );
  727. g_Manager->Serialize();
  728. return hr; // may be S_FALSE if the credential was never in the collection
  729. }
  730. catch ( ComError err )
  731. {
  732. return err.Error();
  733. }
  734. }
  735. HRESULT
  736. CJob::SetReplyFileName(
  737. LPCWSTR Val
  738. )
  739. {
  740. return E_NOTIMPL;
  741. }
  742. HRESULT
  743. CJob::GetReplyFileName(
  744. LPWSTR * pVal
  745. ) const
  746. {
  747. return E_NOTIMPL;
  748. }
  749. HRESULT
  750. CJob::GetReplyProgress(
  751. BG_JOB_REPLY_PROGRESS *pProgress
  752. ) const
  753. {
  754. return E_NOTIMPL;
  755. }
  756. HRESULT
  757. CJob::GetReplyData(
  758. byte **ppBuffer,
  759. UINT64 *pLength
  760. ) const
  761. {
  762. return E_NOTIMPL;
  763. }
  764. HRESULT
  765. CJob::IsVisible()
  766. {
  767. HRESULT hr;
  768. hr = CheckClientAccess( BG_JOB_READ );
  769. if (hr == S_OK)
  770. {
  771. return S_OK;
  772. }
  773. if (hr == E_ACCESSDENIED)
  774. {
  775. return S_FALSE;
  776. }
  777. return hr;
  778. }
  779. bool
  780. CJob::IsOwner(
  781. SidHandle sid
  782. )
  783. {
  784. return (sid == m_NotifySid);
  785. }
  786. void CJob::SetState( BG_JOB_STATE state )
  787. {
  788. if (m_state == state)
  789. {
  790. return;
  791. }
  792. LogInfo("job %p state %d -> %d", this, m_state, state);
  793. #if DBG
  794. CheckStateTransition( m_state, state );
  795. #endif
  796. m_state = state;
  797. bool ShouldClearError = false;
  798. switch( state )
  799. {
  800. case BG_JOB_STATE_QUEUED:
  801. case BG_JOB_STATE_CONNECTING:
  802. ShouldClearError = false;
  803. break;
  804. case BG_JOB_STATE_TRANSFERRING:
  805. case BG_JOB_STATE_SUSPENDED:
  806. ShouldClearError = true;
  807. break;
  808. case BG_JOB_STATE_ERROR:
  809. case BG_JOB_STATE_TRANSIENT_ERROR:
  810. ShouldClearError = false;
  811. break;
  812. case BG_JOB_STATE_TRANSFERRED:
  813. case BG_JOB_STATE_ACKNOWLEDGED:
  814. case BG_JOB_STATE_CANCELLED:
  815. ShouldClearError = true;
  816. break;
  817. default:
  818. ASSERT(0);
  819. break;
  820. }
  821. if (ShouldClearError)
  822. m_error.ClearError();
  823. if (state != BG_JOB_STATE_TRANSIENT_ERROR)
  824. {
  825. g_Manager->m_TaskScheduler.CancelWorkItem( (CJobRetryItem *) this );
  826. }
  827. UpdateModificationTime( false );
  828. }
  829. #if DBG
  830. bool
  831. CJob::CheckStateTransition(
  832. BG_JOB_STATE Old,
  833. BG_JOB_STATE New
  834. )
  835. {
  836. bool ok = true;
  837. switch (Old)
  838. {
  839. case BG_JOB_STATE_QUEUED:
  840. case BG_JOB_STATE_CONNECTING:
  841. case BG_JOB_STATE_SUSPENDED:
  842. case BG_JOB_STATE_ERROR:
  843. case BG_JOB_STATE_TRANSIENT_ERROR:
  844. case BG_JOB_STATE_TRANSFERRED:
  845. {
  846. break;
  847. }
  848. case BG_JOB_STATE_TRANSFERRING:
  849. {
  850. if (New == BG_JOB_STATE_CONNECTING)
  851. {
  852. ok = false;
  853. }
  854. break;
  855. }
  856. case BG_JOB_STATE_ACKNOWLEDGED:
  857. case BG_JOB_STATE_CANCELLED:
  858. default:
  859. {
  860. ok = false;
  861. break;
  862. }
  863. }
  864. if (!ok)
  865. {
  866. ASSERT( "invalid state transition" );
  867. #if DBG
  868. DbgPrint("old state is %d, new state is %d", Old, New );
  869. #endif
  870. }
  871. #if DBG
  872. //
  873. // Check for the stress failure where a download job with all files transferred goes into ERROR state.
  874. //
  875. if (New == BG_JOB_STATE_TRANSIENT_ERROR ||
  876. New == BG_JOB_STATE_ERROR)
  877. {
  878. if (m_type == BG_JOB_TYPE_DOWNLOAD && GetCurrentFile() == NULL)
  879. {
  880. DbgPrint("assert failure: BITS: transferred job %p going into error state. assign the failure to jroberts\n", this);
  881. DbgBreakPoint();
  882. }
  883. }
  884. #endif
  885. return ok;
  886. }
  887. #endif
  888. GENERIC_MAPPING CJob::s_AccessMapping =
  889. {
  890. STANDARD_RIGHTS_READ,
  891. STANDARD_RIGHTS_WRITE,
  892. STANDARD_RIGHTS_EXECUTE,
  893. STANDARD_RIGHTS_ALL
  894. };
  895. HRESULT
  896. CJob::CheckClientAccess(
  897. IN DWORD RequestedAccess
  898. ) const
  899. /*
  900. Checks the current thread's access to this group. The token must allow impersonation.
  901. RequestedAccess lists the standard access bits that the client needs.
  902. */
  903. {
  904. HRESULT hr = S_OK;
  905. BOOL fSuccess = FALSE;
  906. DWORD AllowedAccess = 0;
  907. HANDLE hToken = 0;
  908. //
  909. // Convert generic bits into specific bits.
  910. //
  911. MapGenericMask( &RequestedAccess, &s_AccessMapping );
  912. try
  913. {
  914. if ( ( RequestedAccess & ~BG_JOB_READ ) &&
  915. ( ( m_state == BG_JOB_STATE_CANCELLED ) || ( m_state == BG_JOB_STATE_ACKNOWLEDGED ) ) )
  916. {
  917. LogError("Denying non-read access since job/file is cancelled or acknowledged");
  918. throw ComError(BG_E_INVALID_STATE);
  919. }
  920. CNestedImpersonation imp;
  921. if (imp.CopySid() == g_GlobalInfo->m_AnonymousSid)
  922. {
  923. throw ComError( E_ACCESSDENIED );
  924. }
  925. hr = IsRemoteUser();
  926. if (FAILED(hr) )
  927. throw ComError( hr );
  928. if ( S_OK == hr )
  929. throw ComError( BG_E_REMOTE_NOT_SUPPORTED );
  930. if (IsTokenRestricted( imp.QueryToken()))
  931. {
  932. throw ComError( E_ACCESSDENIED );
  933. }
  934. THROW_HRESULT(
  935. m_sd->CheckTokenAccess( imp.QueryToken(),
  936. RequestedAccess,
  937. &AllowedAccess,
  938. &fSuccess
  939. ));
  940. if (!fSuccess || AllowedAccess != RequestedAccess)
  941. {
  942. LogWarning( "denied access %s 0x%x", fSuccess ? "TRUE" : "FALSE", AllowedAccess );
  943. throw ComError( E_ACCESSDENIED );
  944. }
  945. hr = S_OK;
  946. }
  947. catch (ComError exception)
  948. {
  949. hr = exception.Error();
  950. }
  951. if (hToken)
  952. {
  953. CloseHandle( hToken );
  954. }
  955. return hr;
  956. }
  957. bool
  958. CJob::IsCallbackEnabled(
  959. DWORD bit
  960. )
  961. {
  962. //
  963. // Only one bit, please.
  964. //
  965. ASSERT( 0 == (bit & (bit-1)) );
  966. if ((m_NotifyFlags & bit) == 0 ||
  967. (m_NotifyFlags & BG_NOTIFY_DISABLE))
  968. {
  969. return false;
  970. }
  971. if (m_OldExternalGroupInterface)
  972. {
  973. IBackgroundCopyCallback1 * pif = m_OldExternalGroupInterface->GetNotificationPointer();
  974. if (pif == NULL)
  975. {
  976. return false;
  977. }
  978. pif->Release();
  979. }
  980. else
  981. {
  982. if (m_NotifyPointer == NULL && m_NotifyProgram.Size() == 0)
  983. {
  984. return false;
  985. }
  986. }
  987. return true;
  988. }
  989. void
  990. CJob::ScheduleCompletionCallback(
  991. DWORD Seconds
  992. )
  993. {
  994. //
  995. // See whether any notification regime has been established.
  996. // The callback procedure will check this again, in case something has changed
  997. // between queuing the workitem and dispatching it.
  998. //
  999. if (!IsCallbackEnabled( BG_NOTIFY_JOB_TRANSFERRED ))
  1000. {
  1001. LogInfo("completion callback is not enabled");
  1002. return;
  1003. }
  1004. if (g_Manager->m_TaskScheduler.IsWorkItemInScheduler( static_cast<CJobCallbackItem *>(this) ))
  1005. {
  1006. LogInfo("callback is already scheduled");
  1007. return;
  1008. }
  1009. g_Manager->ScheduleDelayedTask( (CJobCallbackItem *) this, Seconds );
  1010. }
  1011. void
  1012. CJob::ScheduleErrorCallback(
  1013. DWORD Seconds
  1014. )
  1015. {
  1016. //
  1017. // See whether any notification regime has been established.
  1018. // The callback procedure will check this again, in case something has changed
  1019. // between queuing the workitem and dispatching it.
  1020. //
  1021. if (!IsCallbackEnabled( BG_NOTIFY_JOB_ERROR ))
  1022. {
  1023. LogInfo("error callback is not enabled");
  1024. return;
  1025. }
  1026. if (g_Manager->m_TaskScheduler.IsWorkItemInScheduler( static_cast<CJobCallbackItem *>(this) ))
  1027. {
  1028. LogInfo("callback is already scheduled");
  1029. return;
  1030. }
  1031. g_Manager->ScheduleDelayedTask( (CJobCallbackItem *) this, Seconds );
  1032. }
  1033. void
  1034. CJob::JobTransferred()
  1035. {
  1036. // the file list is done
  1037. SetState( BG_JOB_STATE_TRANSFERRED );
  1038. g_Manager->m_TaskScheduler.CancelWorkItem( static_cast<CJobNoProgressItem *>( this ));
  1039. SetCompletionTime();
  1040. ScheduleCompletionCallback();
  1041. }
  1042. void
  1043. CJob::Transfer()
  1044. {
  1045. HRESULT hr;
  1046. auto_HANDLE<NULL> AutoToken;
  1047. if( LogLevelEnabled( LogFlagInfo ) )
  1048. {
  1049. LogDl( "current job: %!guid!", &m_id );
  1050. }
  1051. //
  1052. // Get a copy of the user's token.
  1053. //
  1054. HANDLE hToken = NULL;
  1055. hr = g_Manager->CloneUserToken( GetOwnerSid(), ANY_SESSION, &hToken );
  1056. if (FAILED(hr))
  1057. {
  1058. if (hr == HRESULT_FROM_WIN32( ERROR_NOT_LOGGED_ON ))
  1059. {
  1060. LogDl( "job owner is not logged on");
  1061. // move the group off the main list.
  1062. g_Manager->MoveJobOffline( this );
  1063. RecalcTransientError();
  1064. }
  1065. else
  1066. {
  1067. LogError( "download : unable to get token %!winerr!", hr);
  1068. QMErrInfo err( QM_FILE_TRANSIENT_ERROR, SOURCE_QMGR_QUEUE, ERROR_STYLE_HRESULT, hr, "CloneUserToken" );
  1069. SetTransientError( err, m_CurrentFile, false, false );
  1070. }
  1071. g_Manager->m_TaskScheduler.CompleteWorkItem();
  1072. return;
  1073. }
  1074. AutoToken = hToken;
  1075. //
  1076. // Download the current file.
  1077. //
  1078. QMErrInfo ErrInfo;
  1079. long tries = 0;
  1080. bool bThrottle = ShouldThrottle();
  1081. LogDl( "Throttling %s", bThrottle ? "enabled" : "disabled" );
  1082. if (bThrottle)
  1083. {
  1084. // ignore errors
  1085. //
  1086. (void) SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_IDLE );
  1087. }
  1088. if (m_state != BG_JOB_STATE_TRANSFERRING)
  1089. {
  1090. SetState( BG_JOB_STATE_CONNECTING );
  1091. ScheduleModificationCallback();
  1092. }
  1093. if (!VerifyFileSizes( hToken ))
  1094. {
  1095. goto restore_thread;
  1096. }
  1097. ASSERT( GetCurrentFile() ); // if no more files, it shouldn't be the current job
  1098. retry:
  1099. ErrInfo.Clear();
  1100. if (!GetCurrentFile()->Transfer( hToken,
  1101. m_priority,
  1102. m_ProxySettings,
  1103. &m_Credentials,
  1104. ErrInfo ))
  1105. {
  1106. goto restore_thread;
  1107. }
  1108. //
  1109. // Interpret the download result.
  1110. //
  1111. switch (ErrInfo.result)
  1112. {
  1113. case QM_FILE_TRANSIENT_ERROR: FileTransientError( &ErrInfo ); break;
  1114. case QM_FILE_DONE: FileComplete(); break;
  1115. case QM_FILE_FATAL_ERROR: FileFatalError( &ErrInfo ); break;
  1116. case QM_FILE_ABORTED: break;
  1117. default: ASSERT( 0 && "unhandled download result" ); break;
  1118. case QM_SERVER_FILE_CHANGED:
  1119. {
  1120. FileChangedOnServer();
  1121. if (++tries < 3)
  1122. {
  1123. goto retry;
  1124. }
  1125. ErrInfo.result = QM_FILE_FATAL_ERROR;
  1126. ErrInfo.Set( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, BG_E_INVALID_SERVER_RESPONSE );
  1127. FileFatalError( &ErrInfo );
  1128. break;
  1129. }
  1130. }
  1131. restore_thread:
  1132. if (bThrottle)
  1133. {
  1134. while (!SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_NORMAL ))
  1135. {
  1136. Sleep(100);
  1137. }
  1138. }
  1139. }
  1140. void
  1141. CJob::FileComplete()
  1142. {
  1143. if ( GetOldExternalJobInterface() )
  1144. {
  1145. // Need to rename the files as they are completed for Mars.
  1146. HRESULT Hr = GetCurrentFile()->MoveTempFile();
  1147. if (FAILED(Hr))
  1148. {
  1149. QMErrInfo ErrorInfo;
  1150. ErrorInfo.Set( SOURCE_QMGR_FILE, ERROR_STYLE_HRESULT, Hr, "Unable to rename file" );
  1151. FileFatalError( &ErrorInfo );
  1152. return;
  1153. }
  1154. }
  1155. ++m_CurrentFile;
  1156. if (m_CurrentFile == m_files.size())
  1157. {
  1158. JobTransferred();
  1159. g_Manager->Serialize();
  1160. }
  1161. else
  1162. {
  1163. // more files to download
  1164. ScheduleModificationCallback();
  1165. //
  1166. // To avoid hammering the disk,
  1167. // don't serialize every interim progress notification.
  1168. //
  1169. FILETIME time;
  1170. GetSystemTimeAsFileTime( &time );
  1171. if (FILETIMEToUINT64(time) - FILETIMEToUINT64(m_SerializeTime) > PROGRESS_SERIALIZE_INTERVAL )
  1172. {
  1173. UpdateModificationTime();
  1174. }
  1175. else
  1176. SetModificationTime( &time );
  1177. }
  1178. }
  1179. bool CJob::VerifyFileSizes(
  1180. HANDLE hToken
  1181. )
  1182. {
  1183. if ( m_FilesVerified )
  1184. return true;
  1185. if ( AreRemoteSizesKnown() )
  1186. {
  1187. m_FilesVerified = true;
  1188. return true;
  1189. }
  1190. try
  1191. {
  1192. // retrieve file infomation on the file list.
  1193. // Ignore any errors.
  1194. LogDl("Need to retrieve file sizes before download can start");
  1195. auto_ptr<CUnknownFileSizeList> pFileSizeList = auto_ptr<CUnknownFileSizeList>( GetUnknownFileSizeList() );
  1196. QMErrInfo ErrInfo;
  1197. //
  1198. // Release the global lock while the download is in progress.
  1199. //
  1200. g_Manager->m_TaskScheduler.UnlockWriter();
  1201. LogDl( "UpdateRemoteSizes starting..." );
  1202. g_Manager->UpdateRemoteSizes( pFileSizeList.get(),
  1203. hToken,
  1204. &ErrInfo,
  1205. &m_ProxySettings,
  1206. &m_Credentials
  1207. );
  1208. LogDl( "UpdateRemoteSizes complete." );
  1209. ErrInfo.Log();
  1210. ASSERT( ErrInfo.result != QM_IN_PROGRESS );
  1211. bool fSuccessful = (ErrInfo.result != QM_FILE_ABORTED);
  1212. //
  1213. // Take the writer lock, since the caller expects it to be taken
  1214. // upon return.
  1215. //
  1216. while (g_Manager->m_TaskScheduler.LockWriter() )
  1217. {
  1218. g_Manager->m_TaskScheduler.AcknowledgeWorkItemCancel();
  1219. fSuccessful = false;
  1220. }
  1221. return fSuccessful;
  1222. }
  1223. catch (ComError err)
  1224. {
  1225. LogWarning("caught exception %u", err.Error() );
  1226. return false;
  1227. }
  1228. }
  1229. bool CJob::IsRunning()
  1230. {
  1231. if (m_state == BG_JOB_STATE_TRANSFERRING ||
  1232. m_state == BG_JOB_STATE_CONNECTING)
  1233. {
  1234. return true;
  1235. }
  1236. return false;
  1237. }
  1238. bool CJob::IsRunnable()
  1239. {
  1240. if (m_fVolumeLocked)
  1241. {
  1242. return false;
  1243. }
  1244. if (m_state == BG_JOB_STATE_TRANSFERRING ||
  1245. m_state == BG_JOB_STATE_CONNECTING ||
  1246. m_state == BG_JOB_STATE_QUEUED )
  1247. {
  1248. return true;
  1249. }
  1250. return false;
  1251. }
  1252. void
  1253. CJob::SetTransientError(
  1254. QMErrInfo & ErrInfo,
  1255. LONG FileIndex,
  1256. bool fRetryLater,
  1257. bool fUpdateTime
  1258. )
  1259. {
  1260. LogWarning( "job %p transient failure, interrupt count = %d", this, m_retries );
  1261. if (_GetState() == BG_JOB_STATE_TRANSFERRING)
  1262. {
  1263. ++m_retries;
  1264. }
  1265. //
  1266. // This is written to work withc both upload and download jobs.
  1267. // Be sure that an upload job never transits from CANCELLING state back to QUEUED
  1268. // or TRANSIENT_ERROR.
  1269. //
  1270. if (m_state != BG_JOB_STATE_CANCELLED &&
  1271. m_state != BG_JOB_STATE_ACKNOWLEDGED)
  1272. {
  1273. SetState( BG_JOB_STATE_TRANSIENT_ERROR );
  1274. }
  1275. else
  1276. {
  1277. ASSERT( m_type != BG_JOB_TYPE_DOWNLOAD );
  1278. }
  1279. RecordError( &ErrInfo, FileIndex );
  1280. if ( m_NoProgressTimeout != INFINITE &&
  1281. !g_Manager->m_TaskScheduler.IsWorkItemInScheduler((CJobNoProgressItem *) this))
  1282. {
  1283. g_Manager->ScheduleDelayedTask( (CJobNoProgressItem *) this, m_NoProgressTimeout );
  1284. }
  1285. if (fRetryLater && !g_Manager->m_TaskScheduler.IsWorkItemInScheduler(static_cast<CJobRetryItem *>( this )))
  1286. {
  1287. g_Manager->ScheduleDelayedTask( static_cast<CJobRetryItem *>( this ), m_MinimumRetryDelay );
  1288. }
  1289. if (fUpdateTime)
  1290. {
  1291. UpdateModificationTime();
  1292. }
  1293. }
  1294. bool
  1295. CJob::RecordError(
  1296. QMErrInfo * ErrInfo,
  1297. LONG FileIndex
  1298. )
  1299. {
  1300. m_error.Set( this, FileIndex, ErrInfo );
  1301. return true;
  1302. }
  1303. CFile *
  1304. CJob::_GetFileIndex( LONG index ) const
  1305. {
  1306. if (index < 0)
  1307. {
  1308. LogError("invalid file index %d", index );
  1309. return NULL;
  1310. }
  1311. if (index >= m_files.size())
  1312. {
  1313. return NULL;
  1314. }
  1315. return m_files[ index ];
  1316. }
  1317. void
  1318. CJob::FileTransientError(
  1319. QMErrInfo * ErrInfo
  1320. )
  1321. {
  1322. SetTransientError( *ErrInfo, m_CurrentFile, true, true );
  1323. }
  1324. void
  1325. CJob::FileFatalError(
  1326. QMErrInfo * ErrInfo
  1327. )
  1328. /*
  1329. Puts the job into ERROR state, cancelling any existing callback and no-progress timer
  1330. and scheduling a new error callback.
  1331. If ErrInfo is non-NULL, then the existing error info is overwritten with the new data.
  1332. Otherwise the existing info is retained. This is useful for the no-progress timeout.
  1333. */
  1334. {
  1335. if ( BG_JOB_STATE_TRANSFERRING == m_state )
  1336. {
  1337. ++m_retries;
  1338. }
  1339. g_Manager->m_TaskScheduler.CancelWorkItem( static_cast<CJobNoProgressItem *>(this) );
  1340. g_Manager->m_TaskScheduler.CancelWorkItem( static_cast<CJobCallbackItem *>(this) );
  1341. SetState( BG_JOB_STATE_ERROR );
  1342. if ( ErrInfo )
  1343. {
  1344. RecordError( ErrInfo, m_CurrentFile );
  1345. }
  1346. ScheduleErrorCallback();
  1347. g_Manager->Serialize();
  1348. }
  1349. void CJob::OnRetryJob()
  1350. {
  1351. if (g_Manager->m_TaskScheduler.LockWriter() )
  1352. {
  1353. g_Manager->m_TaskScheduler.AcknowledgeWorkItemCancel();
  1354. return;
  1355. }
  1356. g_Manager->m_TaskScheduler.CompleteWorkItem();
  1357. ASSERT( m_state == BG_JOB_STATE_TRANSIENT_ERROR );
  1358. SetState( BG_JOB_STATE_QUEUED );
  1359. g_Manager->AppendOnline( this );
  1360. UpdateModificationTime();
  1361. g_Manager->ScheduleAnotherGroup();
  1362. g_Manager->m_TaskScheduler.UnlockWriter();
  1363. }
  1364. void CJob::RetryNow()
  1365. {
  1366. RecalcTransientError();
  1367. UpdateModificationTime( false );
  1368. //
  1369. // Normally UpdateModificationTime() would do these things for us,
  1370. // but we chose not to serialize.
  1371. //
  1372. if (g_Manager->m_TaskScheduler.IsWorkItemInScheduler( (CJobInactivityTimeout *) this))
  1373. {
  1374. g_Manager->m_TaskScheduler.CancelWorkItem( (CJobInactivityTimeout *) this );
  1375. g_Manager->m_TaskScheduler.InsertDelayedWorkItem( (CJobInactivityTimeout *) this, g_GlobalInfo->m_JobInactivityTimeout );
  1376. }
  1377. }
  1378. void CJob::OnNoProgress()
  1379. {
  1380. LogInfo("job %p no-progress timeout", this);
  1381. if (g_Manager->m_TaskScheduler.LockWriter() )
  1382. {
  1383. g_Manager->m_TaskScheduler.AcknowledgeWorkItemCancel();
  1384. return;
  1385. }
  1386. //
  1387. // Make sure the downloader thread isn't using the job.
  1388. // Otherwise MoveActiveJobToListEnd may get confused.
  1389. //
  1390. switch (m_state)
  1391. {
  1392. case BG_JOB_STATE_TRANSFERRING:
  1393. {
  1394. // The job is making progress, after all.
  1395. //
  1396. g_Manager->m_TaskScheduler.CompleteWorkItem();
  1397. g_Manager->m_TaskScheduler.UnlockWriter();
  1398. return;
  1399. }
  1400. case BG_JOB_STATE_CONNECTING:
  1401. {
  1402. g_Manager->InterruptDownload();
  1403. break;
  1404. }
  1405. }
  1406. g_Manager->m_TaskScheduler.CompleteWorkItem();
  1407. FileFatalError( NULL );
  1408. g_Manager->ScheduleAnotherGroup();
  1409. g_Manager->m_TaskScheduler.UnlockWriter();
  1410. }
  1411. void CJob::UpdateProgress(
  1412. UINT64 BytesTransferred,
  1413. UINT64 BytesTotal
  1414. )
  1415. {
  1416. //
  1417. // Upload jobs may see their progress reset to zero if the server changes;
  1418. // better to leave the job in CONNECTING state in that situation.
  1419. // The test is harmless for download jobs.
  1420. //
  1421. if (BytesTransferred > 0)
  1422. {
  1423. SetState( BG_JOB_STATE_TRANSFERRING );
  1424. }
  1425. g_Manager->m_TaskScheduler.CancelWorkItem( (CJobNoProgressItem *) this );
  1426. ScheduleModificationCallback();
  1427. //
  1428. // To avoid hammering the disk,
  1429. // don't serialize every interim progress notification.
  1430. //
  1431. FILETIME time;
  1432. GetSystemTimeAsFileTime( &time );
  1433. if (FILETIMEToUINT64(time) - FILETIMEToUINT64(m_SerializeTime) > PROGRESS_SERIALIZE_INTERVAL )
  1434. {
  1435. UpdateModificationTime();
  1436. }
  1437. }
  1438. void CJob::OnInactivityTimeout()
  1439. {
  1440. if (g_Manager->m_TaskScheduler.LockWriter() )
  1441. {
  1442. g_Manager->m_TaskScheduler.AcknowledgeWorkItemCancel();
  1443. return;
  1444. }
  1445. g_Manager->m_TaskScheduler.CompleteWorkItem();
  1446. try
  1447. {
  1448. //
  1449. // Temp files will be deleted using the local system account, because we cannot guarantee
  1450. // that any other account is available. Although not ideal, it should not provide much of a
  1451. // security breach because the file names were generated by BITS, and the directory was
  1452. // accessible to the job owner when the files were created.
  1453. //
  1454. THROW_HRESULT( RemoveTemporaryFiles() );
  1455. SetState( BG_JOB_STATE_CANCELLED );
  1456. RemoveFromManager();
  1457. }
  1458. catch ( ComError err )
  1459. {
  1460. }
  1461. g_Manager->m_TaskScheduler.UnlockWriter();
  1462. }
  1463. BOOL IsInterfacePointerDead(
  1464. IUnknown * punk,
  1465. HRESULT hr
  1466. )
  1467. {
  1468. if (hr == MAKE_HRESULT( SEVERITY_ERROR, FACILITY_WIN32, RPC_S_SERVER_UNAVAILABLE ))
  1469. {
  1470. return TRUE;
  1471. }
  1472. return FALSE;
  1473. }
  1474. void CJob::OnMakeCallback()
  1475. /*++
  1476. Description:
  1477. Used to notify the client app of job completion or a non-recoverable error.
  1478. Impersonates the user, CoCreates a notification object, and calls the method.
  1479. If the call fails, the fn posts a delayed task to retry.
  1480. At entry:
  1481. m_method: the method to call
  1482. m_notifysid: the user to impersonate
  1483. m_error: (if m_method is CM_ERROR) the error that halted the job
  1484. (if m_method is CM_COMPLETE) zero
  1485. m_RetryTime: sleep time before retrying after a failed notification attempt
  1486. At exit:
  1487. --*/
  1488. {
  1489. //
  1490. // check for cancel, and take a reference so the job cannot be deleted
  1491. // while this precedure is using it.
  1492. //
  1493. if (g_Manager->m_TaskScheduler.LockReader())
  1494. {
  1495. g_Manager->m_TaskScheduler.AcknowledgeWorkItemCancel();
  1496. return;
  1497. }
  1498. bool OldInterface = (m_OldExternalGroupInterface != NULL);
  1499. GetExternalInterface()->AddRef();
  1500. g_Manager->m_TaskScheduler.UnlockReader();
  1501. //
  1502. // Need to have this item out of the queue before the call,
  1503. // otherwise an incoming CompleteJob() call may block trying to remove it
  1504. // from the task scheduler queue.
  1505. // Also prevents CancelWorkItem calls from interfering with our mutex access.
  1506. //
  1507. g_Manager->m_TaskScheduler.CompleteWorkItem();
  1508. if (OldInterface)
  1509. {
  1510. if (FAILED(OldInterfaceCallback()))
  1511. {
  1512. RescheduleCallback();
  1513. }
  1514. }
  1515. else
  1516. {
  1517. if (FAILED(InterfaceCallback()) &&
  1518. FAILED(CmdLineCallback()))
  1519. {
  1520. RescheduleCallback();
  1521. }
  1522. }
  1523. GetExternalInterface()->Release();
  1524. }
  1525. HRESULT
  1526. CJob::RescheduleCallback()
  1527. {
  1528. if (g_Manager->m_TaskScheduler.LockWriter() )
  1529. {
  1530. LogInfo( "callback was cancelled" );
  1531. g_Manager->m_TaskScheduler.AcknowledgeWorkItemCancel();
  1532. return S_FALSE;
  1533. }
  1534. switch (m_state)
  1535. {
  1536. case BG_JOB_STATE_TRANSFERRED:
  1537. {
  1538. ScheduleCompletionCallback( m_MinimumRetryDelay );
  1539. break;
  1540. }
  1541. case BG_JOB_STATE_ERROR:
  1542. {
  1543. ScheduleErrorCallback( m_MinimumRetryDelay );
  1544. break;
  1545. }
  1546. default:
  1547. {
  1548. LogInfo("callback failed; job state is %d so no retry is planned", m_state );
  1549. }
  1550. }
  1551. g_Manager->m_TaskScheduler.UnlockWriter();
  1552. return S_OK;
  1553. }
  1554. void
  1555. CJob::OnModificationCallback()
  1556. {
  1557. if (g_Manager->m_TaskScheduler.LockWriter() )
  1558. {
  1559. LogInfo( "Modification call cancelled, ack cancel" );
  1560. g_Manager->m_TaskScheduler.AcknowledgeWorkItemCancel();
  1561. return;
  1562. }
  1563. if (!IsCallbackEnabled( BG_NOTIFY_JOB_MODIFICATION ))
  1564. {
  1565. LogInfo( "Modification call cancelled via flag/interface change" );
  1566. m_ModificationsPending = 0;
  1567. g_Manager->m_TaskScheduler.CancelWorkItem(
  1568. g_Manager->m_TaskScheduler.GetCurrentWorkItem());
  1569. GetExternalInterface()->Release();
  1570. g_Manager->m_TaskScheduler.UnlockWriter();
  1571. return;
  1572. }
  1573. IBackgroundCopyCallback *pICB = m_NotifyPointer;
  1574. pICB->AddRef();
  1575. g_Manager->m_TaskScheduler.UnlockWriter();
  1576. HRESULT Hr = pICB->JobModification( GetExternalInterface(), 0 );
  1577. LogInfo( "JobModification call complete, result %!winerr!", Hr );
  1578. SafeRelease( pICB );
  1579. if (g_Manager->m_TaskScheduler.LockWriter() )
  1580. {
  1581. LogInfo( "Modification work item canceled before lock reaquire" );
  1582. g_Manager->m_TaskScheduler.AcknowledgeWorkItemCancel();
  1583. return;
  1584. }
  1585. g_Manager->m_TaskScheduler.CompleteWorkItem();
  1586. m_ModificationsPending--;
  1587. if ( FAILED(Hr) && IsInterfacePointerDead( m_NotifyPointer, Hr ) )
  1588. {
  1589. LogInfo( "Modification interface pointer is dead, no more modifications" );
  1590. m_ModificationsPending = 0;
  1591. }
  1592. if ( m_ModificationsPending )
  1593. {
  1594. LogInfo( "%u more modification callbacks pending, reinsert work item", m_ModificationsPending );
  1595. g_Manager->m_TaskScheduler.InsertWorkItem( static_cast<CJobModificationItem*>(this) );
  1596. }
  1597. else
  1598. {
  1599. LogInfo( "no more modification callbacks pending, release interface ref" );
  1600. GetExternalInterface()->Release();
  1601. }
  1602. g_Manager->m_TaskScheduler.UnlockWriter();
  1603. }
  1604. void
  1605. CJob::ScheduleModificationCallback()
  1606. {
  1607. // Requires writer lock
  1608. //
  1609. // The old interface doesn't support this.
  1610. //
  1611. if (m_OldExternalGroupInterface)
  1612. {
  1613. return;
  1614. }
  1615. if (!IsCallbackEnabled( BG_NOTIFY_JOB_MODIFICATION ))
  1616. {
  1617. return;
  1618. }
  1619. if ( !m_ModificationsPending )
  1620. {
  1621. LogInfo( "New modification callback, adding work item for job %p", this );
  1622. GetExternalInterface()->AddRef();
  1623. g_Manager->m_TaskScheduler.InsertWorkItem( static_cast<CJobModificationItem*>(this) );
  1624. }
  1625. m_ModificationsPending++;
  1626. min( m_ModificationsPending, 0x7FFFFFFE );
  1627. LogInfo( "Added modification callback, new count of %u for job %p", m_ModificationsPending, this );
  1628. }
  1629. HRESULT
  1630. CJob::InterfaceCallback()
  1631. {
  1632. bool bLocked = true;
  1633. HRESULT hr;
  1634. IBackgroundCopyCallback * pICB = 0;
  1635. IBackgroundCopyError * pJobErrorExternal = 0;
  1636. try
  1637. {
  1638. CallbackMethod method;
  1639. IBackgroundCopyJob * pJobExternal = 0;
  1640. {
  1641. HoldReaderLock lock ( g_Manager->m_TaskScheduler );
  1642. pJobExternal = GetExternalInterface();
  1643. //
  1644. // It is possible that the job state changed after the callback was queued.
  1645. // Make the callback based on the current job state.
  1646. //
  1647. if (!m_NotifyPointer)
  1648. {
  1649. LogInfo( "Notification pointer for job %p is NULL, skipping callback", this );
  1650. return E_FAIL;
  1651. }
  1652. switch (m_state)
  1653. {
  1654. case BG_JOB_STATE_TRANSFERRED:
  1655. {
  1656. if (!IsCallbackEnabled( BG_NOTIFY_JOB_TRANSFERRED ))
  1657. {
  1658. LogInfo("error callback is not enabled");
  1659. return S_OK;
  1660. }
  1661. method = CM_COMPLETE;
  1662. break;
  1663. }
  1664. case BG_JOB_STATE_ERROR:
  1665. {
  1666. ASSERT( m_error.IsErrorSet() );
  1667. if (!IsCallbackEnabled( BG_NOTIFY_JOB_ERROR ))
  1668. {
  1669. LogInfo("error callback is not enabled");
  1670. return S_OK;
  1671. }
  1672. method = CM_ERROR;
  1673. pJobErrorExternal = new CJobErrorExternal( &m_error );
  1674. break;
  1675. }
  1676. default:
  1677. {
  1678. LogInfo("callback has become irrelevant, job state is %d", m_state );
  1679. return S_OK;
  1680. }
  1681. }
  1682. pICB = m_NotifyPointer;
  1683. pICB->AddRef();
  1684. }
  1685. //
  1686. // Free from the mutex, make the call.
  1687. //
  1688. switch (method)
  1689. {
  1690. case CM_COMPLETE:
  1691. LogInfo( "callback : job %p completion", this );
  1692. hr = pICB->JobTransferred( pJobExternal );
  1693. break;
  1694. case CM_ERROR:
  1695. LogInfo( "callback : job %p error", this );
  1696. hr = pICB->JobError( pJobExternal, pJobErrorExternal );
  1697. break;
  1698. default:
  1699. LogError( "job %p: invalid callback type 0x%x", this, method );
  1700. hr = S_OK;
  1701. break;
  1702. }
  1703. LogInfo("callback completed with 0x%x", hr);
  1704. //
  1705. // Clear the notification pointer if it is unusable.
  1706. //
  1707. if (FAILED(hr))
  1708. {
  1709. HoldWriterLock lock ( g_Manager->m_TaskScheduler );
  1710. if (m_NotifyPointer && IsInterfacePointerDead( m_NotifyPointer, hr ))
  1711. {
  1712. m_NotifyPointer->Release();
  1713. m_NotifyPointer = NULL;
  1714. }
  1715. throw ComError( hr );
  1716. }
  1717. hr = S_OK;
  1718. }
  1719. catch ( ComError exception )
  1720. {
  1721. LogWarning( "exception %x while dispatching callback", exception.Error() );
  1722. hr = exception.Error();
  1723. }
  1724. SafeRelease( pJobErrorExternal );
  1725. SafeRelease( pICB );
  1726. return hr;
  1727. }
  1728. HRESULT
  1729. CJob::CmdLineCallback()
  1730. {
  1731. ASSERT( GetOldExternalGroupInterface() == 0 );
  1732. HRESULT hr;
  1733. CUser * user = 0;
  1734. try
  1735. {
  1736. StringHandle Program;
  1737. StringHandle Parameters;
  1738. {
  1739. HoldReaderLock lock ( g_Manager->m_TaskScheduler );
  1740. switch (m_state)
  1741. {
  1742. case BG_JOB_STATE_TRANSFERRED:
  1743. {
  1744. if (!IsCallbackEnabled( BG_NOTIFY_JOB_TRANSFERRED ))
  1745. {
  1746. LogInfo("error callback is not enabled");
  1747. return S_OK;
  1748. }
  1749. break;
  1750. }
  1751. case BG_JOB_STATE_ERROR:
  1752. {
  1753. ASSERT( m_error.IsErrorSet() );
  1754. if (!IsCallbackEnabled( BG_NOTIFY_JOB_ERROR ))
  1755. {
  1756. LogInfo("error callback is not enabled");
  1757. return S_OK;
  1758. }
  1759. break;
  1760. }
  1761. default:
  1762. {
  1763. LogInfo("callback has become irrelevant, job state is %d", m_state );
  1764. return S_OK;
  1765. }
  1766. }
  1767. Program = m_NotifyProgram;
  1768. Parameters = m_NotifyParameters;
  1769. }
  1770. //
  1771. // Free from the mutex, launch the application.
  1772. //
  1773. user = g_Manager->m_Users.FindUser( GetOwnerSid(), ANY_SESSION );
  1774. if (!user)
  1775. {
  1776. throw ComError( HRESULT_FROM_WIN32( ERROR_NOT_LOGGED_ON ));
  1777. }
  1778. THROW_HRESULT( user->LaunchProcess( Program, Parameters ) );
  1779. hr = S_OK;
  1780. }
  1781. catch ( ComError err )
  1782. {
  1783. LogWarning( "exception %x while launching callback process", err.Error() );
  1784. hr = err.Error();
  1785. }
  1786. if (user)
  1787. {
  1788. user->DecrementRefCount();
  1789. }
  1790. return hr;
  1791. }
  1792. HRESULT
  1793. CJob::OldInterfaceCallback()
  1794. {
  1795. HRESULT Hr = S_OK;
  1796. IBackgroundCopyCallback1 *pICB = NULL;
  1797. IBackgroundCopyGroup *pGroup = NULL;
  1798. IBackgroundCopyJob1 *pJob = NULL;
  1799. try
  1800. {
  1801. CallbackMethod method;
  1802. DWORD dwCurrentFile = 0;
  1803. DWORD dwRetries = 0;
  1804. DWORD dwWin32Result = 0;
  1805. DWORD dwTransportResult = 0;
  1806. {
  1807. CLockedJobReadPointer LockedJob(this);
  1808. pGroup = GetOldExternalGroupInterface();
  1809. ASSERT( pGroup );
  1810. pGroup->AddRef();
  1811. //
  1812. // It is possible that the job state changed after the callback was queued.
  1813. // Make the callback based on the current job state.
  1814. //
  1815. pICB = GetOldExternalGroupInterface()->GetNotificationPointer();
  1816. if (!pICB)
  1817. {
  1818. return S_FALSE;
  1819. }
  1820. switch (m_state)
  1821. {
  1822. case BG_JOB_STATE_TRANSFERRED:
  1823. {
  1824. if (!IsCallbackEnabled( BG_NOTIFY_JOB_TRANSFERRED ))
  1825. {
  1826. LogInfo("error callback is not enabled");
  1827. return S_OK;
  1828. }
  1829. method = CM_COMPLETE;
  1830. break;
  1831. }
  1832. case BG_JOB_STATE_ERROR:
  1833. {
  1834. ASSERT( m_error.IsErrorSet() );
  1835. if (!IsCallbackEnabled( BG_NOTIFY_JOB_ERROR ))
  1836. {
  1837. LogInfo("error callback is not enabled");
  1838. return S_OK;
  1839. }
  1840. method = CM_ERROR;
  1841. pJob = GetOldExternalJobInterface();
  1842. pJob->AddRef();
  1843. dwCurrentFile = m_error.GetFileIndex();
  1844. m_error.GetOldInterfaceErrors( &dwWin32Result, &dwTransportResult );
  1845. THROW_HRESULT( GetErrorCount(&dwRetries) );
  1846. break;
  1847. }
  1848. default:
  1849. {
  1850. LogInfo("callback has become irrelevant, job state is %d", m_state );
  1851. return S_OK;
  1852. }
  1853. }
  1854. }
  1855. // Outside of lock, do the callback
  1856. switch( method )
  1857. {
  1858. case CM_ERROR:
  1859. THROW_HRESULT( pICB->OnStatus(pGroup, pJob, dwCurrentFile,
  1860. QM_STATUS_GROUP_ERROR | QM_STATUS_GROUP_SUSPENDED,
  1861. dwRetries,
  1862. dwWin32Result,
  1863. dwTransportResult) );
  1864. break;
  1865. case CM_COMPLETE:
  1866. THROW_HRESULT( pICB->OnStatus(pGroup, NULL, -1, QM_STATUS_GROUP_COMPLETE, 0, 0, 0));
  1867. GetOldExternalGroupInterface()->SetNotificationPointer( __uuidof(IBackgroundCopyCallback1),
  1868. NULL );
  1869. break;
  1870. default:
  1871. ASSERT(0);
  1872. throw ComError( E_FAIL );
  1873. }
  1874. Hr = S_OK;
  1875. }
  1876. catch ( ComError exception )
  1877. {
  1878. LogWarning( "exception %x while dispatching callback", exception.Error() );
  1879. Hr = exception.Error();
  1880. }
  1881. SafeRelease( pICB );
  1882. SafeRelease( pGroup );
  1883. SafeRelease( pJob );
  1884. return Hr;
  1885. }
  1886. //
  1887. // Pause all activity on the job. The service will take no action until one of
  1888. // Resume(), Cancel(), Complete() is called.
  1889. //
  1890. // if already suspended, just returns S_OK.
  1891. //
  1892. HRESULT
  1893. CJob::Suspend()
  1894. {
  1895. return g_Manager->SuspendJob( this );
  1896. }
  1897. //
  1898. // Enable downloading for this job.
  1899. //
  1900. // if already running, just returns S_OK.
  1901. //
  1902. HRESULT
  1903. CJob::Resume()
  1904. {
  1905. if (IsEmpty())
  1906. {
  1907. return BG_E_EMPTY;
  1908. }
  1909. switch (m_state)
  1910. {
  1911. case BG_JOB_STATE_SUSPENDED:
  1912. {
  1913. CFile * file = GetCurrentFile();
  1914. if (!file)
  1915. {
  1916. // job was already transferred when it was suspended
  1917. JobTransferred();
  1918. return S_OK;
  1919. }
  1920. }
  1921. // fall through here
  1922. case BG_JOB_STATE_TRANSIENT_ERROR:
  1923. case BG_JOB_STATE_ERROR:
  1924. RecalcTransientError( true );
  1925. if (IsRunnable())
  1926. {
  1927. g_Manager->AppendOnline( this );
  1928. }
  1929. g_Manager->ScheduleAnotherGroup();
  1930. UpdateModificationTime();
  1931. return S_OK;
  1932. case BG_JOB_STATE_CONNECTING:
  1933. case BG_JOB_STATE_TRANSFERRING:
  1934. case BG_JOB_STATE_QUEUED:
  1935. case BG_JOB_STATE_TRANSFERRED: // no-op
  1936. {
  1937. return S_OK;
  1938. }
  1939. case BG_JOB_STATE_CANCELLED:
  1940. case BG_JOB_STATE_ACKNOWLEDGED:
  1941. {
  1942. return BG_E_INVALID_STATE;
  1943. }
  1944. default:
  1945. {
  1946. ASSERT( 0 );
  1947. return S_OK;
  1948. }
  1949. }
  1950. ASSERT( 0 );
  1951. return S_OK;
  1952. }
  1953. //
  1954. // Permanently stop the job. The service will delete the job metadata and downloaded files.
  1955. //
  1956. HRESULT
  1957. CJob::Cancel()
  1958. {
  1959. HRESULT Hr = S_OK;
  1960. switch (m_state)
  1961. {
  1962. case BG_JOB_STATE_CONNECTING:
  1963. case BG_JOB_STATE_TRANSFERRING:
  1964. {
  1965. g_Manager->InterruptDownload();
  1966. // OK to fall through here
  1967. }
  1968. case BG_JOB_STATE_SUSPENDED:
  1969. case BG_JOB_STATE_ERROR:
  1970. case BG_JOB_STATE_QUEUED:
  1971. case BG_JOB_STATE_TRANSIENT_ERROR:
  1972. case BG_JOB_STATE_TRANSFERRED:
  1973. {
  1974. try
  1975. {
  1976. //
  1977. // For file deletion to work, we need IMPERSONATE level impersonation,
  1978. // not the COM default of IDENTIFY.
  1979. //
  1980. CNestedImpersonation imp;
  1981. imp.SwitchToLogonToken();
  1982. RETURN_HRESULT( NonOwnerModificationCheck( imp.CopySid(), CHG_CANCEL, PROP_NONE ));
  1983. // abandon temporary files
  1984. RETURN_HRESULT( Hr = RemoveTemporaryFiles() );
  1985. SetState( BG_JOB_STATE_CANCELLED );
  1986. RemoveFromManager();
  1987. return Hr;
  1988. }
  1989. catch ( ComError err )
  1990. {
  1991. return err.Error();
  1992. }
  1993. }
  1994. case BG_JOB_STATE_CANCELLED:
  1995. case BG_JOB_STATE_ACKNOWLEDGED:
  1996. {
  1997. return BG_E_INVALID_STATE;
  1998. }
  1999. default:
  2000. {
  2001. ASSERT( 0 );
  2002. return Hr;
  2003. }
  2004. }
  2005. ASSERT( 0 );
  2006. return Hr;
  2007. }
  2008. //
  2009. // Acknowledges receipt of the job-complete notification. The service will delete
  2010. // the job metadata and leave the downloaded files.
  2011. //
  2012. HRESULT
  2013. CJob::Complete( )
  2014. {
  2015. HRESULT hr;
  2016. switch (m_state)
  2017. {
  2018. case BG_JOB_STATE_CONNECTING:
  2019. case BG_JOB_STATE_TRANSFERRING:
  2020. case BG_JOB_STATE_QUEUED:
  2021. case BG_JOB_STATE_TRANSIENT_ERROR:
  2022. Suspend();
  2023. // OK to fall through here
  2024. case BG_JOB_STATE_SUSPENDED:
  2025. case BG_JOB_STATE_ERROR:
  2026. case BG_JOB_STATE_TRANSFERRED:
  2027. hr = S_OK;
  2028. // move downloaded files to final destination(skip for Mars)
  2029. if ( !GetOldExternalJobInterface() )
  2030. {
  2031. RETURN_HRESULT( hr = CommitTemporaryFiles() );
  2032. }
  2033. // hr may be S_OK, or BG_S_PARTIAL_COMPLETE.
  2034. SetState( BG_JOB_STATE_ACKNOWLEDGED );
  2035. RemoveFromManager();
  2036. return hr;
  2037. case BG_JOB_STATE_CANCELLED:
  2038. case BG_JOB_STATE_ACKNOWLEDGED:
  2039. {
  2040. return BG_E_INVALID_STATE;
  2041. }
  2042. default:
  2043. {
  2044. ASSERT( 0 );
  2045. return BG_E_INVALID_STATE;
  2046. }
  2047. }
  2048. ASSERT(0);
  2049. return BG_E_INVALID_STATE;
  2050. }
  2051. HRESULT
  2052. CJob::CommitTemporaryFiles()
  2053. {
  2054. HRESULT Hr = S_OK;
  2055. try
  2056. {
  2057. bool fPartial = false;
  2058. CNestedImpersonation imp( GetOwnerSid() );
  2059. CFileList::iterator iter;
  2060. LogInfo("commit job %p", this );
  2061. //
  2062. // First loop, rename completed temp files.
  2063. //
  2064. SIZE_T FilesMoved = 0;
  2065. for (iter = m_files.begin(); iter != m_files.end(); ++iter, FilesMoved++)
  2066. {
  2067. if (false == (*iter)->IsCompleted())
  2068. {
  2069. if ((*iter)->ReceivedAllData())
  2070. {
  2071. //
  2072. // Retain the first error encountered.
  2073. //
  2074. HRESULT LastResult = (*iter)->MoveTempFile();
  2075. if (FAILED(LastResult))
  2076. {
  2077. LogError( "commit: failed 0x%x", LastResult );
  2078. if (Hr == S_OK)
  2079. {
  2080. Hr = LastResult;
  2081. }
  2082. }
  2083. }
  2084. else
  2085. {
  2086. fPartial = true;
  2087. LogInfo("commit: skipping partial file '%S'", (const WCHAR*)(*iter)->GetLocalName());
  2088. }
  2089. }
  2090. else
  2091. {
  2092. LogInfo("commit: skipping previously completed file '%S'", (const WCHAR*)(*iter)->GetLocalName());
  2093. }
  2094. }
  2095. if (SUCCEEDED(Hr))
  2096. {
  2097. Hr = RemoveTemporaryFiles();
  2098. //
  2099. // Return S_OK if all files are returned, otherwise BG_S_PARTIAL_COMPLETE.
  2100. //
  2101. if (fPartial)
  2102. {
  2103. Hr = BG_S_PARTIAL_COMPLETE;
  2104. }
  2105. }
  2106. }
  2107. catch ( ComError exception )
  2108. {
  2109. Hr = exception.Error();
  2110. LogError( "commit: exception 0x%x", Hr );
  2111. }
  2112. //
  2113. // If commitment failed, the job will not be deleted.
  2114. // Update its modification time, and schedule the modification callback.
  2115. //
  2116. if (FAILED(Hr))
  2117. {
  2118. UpdateModificationTime();
  2119. }
  2120. return Hr;
  2121. }
  2122. HRESULT
  2123. CJob::RemoveTemporaryFiles(
  2124. DWORD StartingIndex
  2125. )
  2126. /*
  2127. This fn deletes the temporary local files of the job. It attempts to keep track of failures
  2128. and log an event, but failures in this part will not cause the fn to abort.
  2129. */
  2130. {
  2131. CFileList * FailedFiles = NULL;
  2132. //
  2133. // Delete each temporary file. Record failures in FailedFiles, if possible.
  2134. //
  2135. for (CFileList::iterator iter = m_files.begin() + StartingIndex; iter != m_files.end(); ++iter)
  2136. {
  2137. if (false == (*iter)->IsCompleted())
  2138. {
  2139. HRESULT hr = (*iter)->DeleteTempFile();
  2140. if (FAILED(hr) && IsReportableFileDeletionError(hr, (*iter)->GetTemporaryName()))
  2141. {
  2142. try
  2143. {
  2144. if (!FailedFiles)
  2145. {
  2146. FailedFiles = new CFileList;
  2147. }
  2148. FailedFiles->push_back( *iter );
  2149. }
  2150. catch ( ComError err )
  2151. {
  2152. // ignore failures
  2153. }
  2154. }
  2155. }
  2156. }
  2157. //
  2158. // If some files were not deleted, log an event with their names.
  2159. //
  2160. if (FailedFiles && FailedFiles->size() > 0)
  2161. {
  2162. LogUnsuccessfulFileDeletion( *FailedFiles );
  2163. delete FailedFiles;
  2164. return BG_S_UNABLE_TO_DELETE_FILES;
  2165. }
  2166. ASSERT( FailedFiles == NULL );
  2167. return S_OK;
  2168. }
  2169. void
  2170. CJob::LogUnsuccessfulFileDeletion(
  2171. CFileList & files
  2172. )
  2173. /*
  2174. This function generates an event log entry when BITS is not able to delete temporary files.
  2175. */
  2176. {
  2177. int FileCount = 0;
  2178. size_t CharCount = 0;
  2179. const WCHAR Template[] = L" %s\n";
  2180. //
  2181. // Record the total length of the files we are going to log.
  2182. //
  2183. CFileList::iterator iter = files.begin();
  2184. while (iter != files.end() && (++FileCount < MAX_LOGGED_UNSUCCESSFUL_FILES))
  2185. {
  2186. CharCount += ((*iter)->GetTemporaryName().Size() + RTL_NUMBER_OF(Template)); //
  2187. ++iter;
  2188. }
  2189. LogInfo("%d temp files were not deleted", FileCount);
  2190. WCHAR * FileNames = new WCHAR[ CharCount+1 ];
  2191. WCHAR * Start = FileNames;
  2192. WCHAR * End;
  2193. //
  2194. // Construct the string of all files.
  2195. //
  2196. HRESULT hr;
  2197. FileCount = 0;
  2198. iter = files.begin();
  2199. while (iter != files.end() && (++FileCount < MAX_LOGGED_UNSUCCESSFUL_FILES))
  2200. {
  2201. hr = StringCchPrintfEx(
  2202. Start,
  2203. CharCount,
  2204. &End,
  2205. &CharCount,
  2206. 0, // no special flags
  2207. Template,
  2208. LPCWSTR( (*iter)->GetTemporaryName() )
  2209. );
  2210. Start = End;
  2211. if (FAILED(hr))
  2212. {
  2213. LogWarning("printf on file %d failed with %x", FileCount, hr);
  2214. break;
  2215. }
  2216. ++iter;
  2217. }
  2218. //
  2219. // Log the event.
  2220. //
  2221. bool fMoreFiles = (iter != files.end());
  2222. g_EventLogger->ReportFileDeletionFailure( m_id, m_name, FileNames, fMoreFiles );
  2223. delete [] FileNames;
  2224. }
  2225. void
  2226. CJob::SetCompletionTime( const FILETIME *pftCompletionTime )
  2227. {
  2228. FILETIME ftCurrentTime;
  2229. if ( !pftCompletionTime )
  2230. {
  2231. GetSystemTimeAsFileTime( &ftCurrentTime );
  2232. pftCompletionTime = &ftCurrentTime;
  2233. }
  2234. m_TransferCompletionTime = *pftCompletionTime;
  2235. SetModificationTime( pftCompletionTime );
  2236. }
  2237. void
  2238. CJob::SetModificationTime( const FILETIME *pftModificationTime )
  2239. {
  2240. FILETIME ftCurrentTime;
  2241. if ( !pftModificationTime )
  2242. {
  2243. GetSystemTimeAsFileTime( &ftCurrentTime );
  2244. pftModificationTime = &ftCurrentTime;
  2245. }
  2246. m_ModificationTime = *pftModificationTime;
  2247. }
  2248. void
  2249. CJob::SetLastAccessTime( const FILETIME *pftModificationTime )
  2250. {
  2251. FILETIME ftCurrentTime;
  2252. if ( !pftModificationTime )
  2253. {
  2254. GetSystemTimeAsFileTime( &ftCurrentTime );
  2255. pftModificationTime = &ftCurrentTime;
  2256. }
  2257. m_LastAccessTime = *pftModificationTime;
  2258. }
  2259. void
  2260. CJob::OnDiskChange(
  2261. const WCHAR *CanonicalVolume,
  2262. DWORD VolumeSerialNumber )
  2263. {
  2264. switch(m_state)
  2265. {
  2266. case BG_JOB_STATE_QUEUED:
  2267. case BG_JOB_STATE_CONNECTING:
  2268. case BG_JOB_STATE_TRANSFERRING:
  2269. break;
  2270. case BG_JOB_STATE_SUSPENDED:
  2271. case BG_JOB_STATE_ERROR:
  2272. return;
  2273. case BG_JOB_STATE_TRANSIENT_ERROR:
  2274. break;
  2275. case BG_JOB_STATE_TRANSFERRED:
  2276. case BG_JOB_STATE_ACKNOWLEDGED:
  2277. case BG_JOB_STATE_CANCELLED:
  2278. return;
  2279. default:
  2280. ASSERTMSG("Invalid job state", 0);
  2281. }
  2282. for (CFileList::iterator iter = m_files.begin(); iter != m_files.end(); ++iter)
  2283. {
  2284. if (!(*iter)->OnDiskChange( CanonicalVolume, VolumeSerialNumber ))
  2285. {
  2286. // If one file fails, the whole job fails.
  2287. return;
  2288. }
  2289. }
  2290. }
  2291. void
  2292. CJob::OnDismount(
  2293. const WCHAR *CanonicalVolume )
  2294. {
  2295. switch(m_state)
  2296. {
  2297. case BG_JOB_STATE_QUEUED:
  2298. case BG_JOB_STATE_CONNECTING:
  2299. case BG_JOB_STATE_TRANSFERRING:
  2300. break;
  2301. case BG_JOB_STATE_SUSPENDED:
  2302. case BG_JOB_STATE_ERROR:
  2303. return;
  2304. case BG_JOB_STATE_TRANSIENT_ERROR:
  2305. break;
  2306. case BG_JOB_STATE_TRANSFERRED:
  2307. case BG_JOB_STATE_ACKNOWLEDGED:
  2308. case BG_JOB_STATE_CANCELLED:
  2309. return;
  2310. default:
  2311. ASSERTMSG("Invalid job state", 0);
  2312. }
  2313. for (CFileList::iterator iter = m_files.begin(); iter != m_files.end(); ++iter)
  2314. {
  2315. if (!(*iter)->OnDismount( CanonicalVolume ))
  2316. {
  2317. // If one file fails, the whole job fails.
  2318. return;
  2319. }
  2320. }
  2321. }
  2322. bool CJob::IsTransferringToDrive( const WCHAR *CanonicalVolume )
  2323. {
  2324. CFile *CurrentFile = GetCurrentFile();
  2325. if ( !CurrentFile )
  2326. return false;
  2327. if ( CurrentFile->IsCanonicalVolume( CanonicalVolume ) )
  2328. return true;
  2329. else
  2330. return false;
  2331. }
  2332. bool
  2333. CJob::OnDeviceLock(
  2334. const WCHAR * CanonicalVolume
  2335. )
  2336. {
  2337. LogInfo( "job %p", this );
  2338. //
  2339. // Currently this same code works for all jobs, upload and download.
  2340. // If this code changes in a way that breaks upload jobs in CANCELLING state,
  2341. // then you will need to write CUploadJob::OnDeviceLock and OnDeviceUnlock.
  2342. //
  2343. if ( IsTransferringToDrive( CanonicalVolume ) )
  2344. {
  2345. if (IsRunning() )
  2346. {
  2347. g_Manager->InterruptDownload();
  2348. }
  2349. m_fVolumeLocked = true;
  2350. RecalcTransientError();
  2351. return true;
  2352. }
  2353. return false;
  2354. }
  2355. bool
  2356. CJob::OnDeviceUnlock(
  2357. const WCHAR * CanonicalVolume
  2358. )
  2359. {
  2360. LogInfo( "job %p", this );
  2361. //
  2362. // Currently this same code works for all jobs, upload and download.
  2363. // If this code changes in a way that breaks upload jobs in CANCELLING state,
  2364. // then you will need to write CUploadJob::OnDeviceLock and OnDeviceUnlock.
  2365. //
  2366. if ( IsTransferringToDrive( CanonicalVolume ) )
  2367. {
  2368. m_fVolumeLocked = false;
  2369. RecalcTransientError();
  2370. return true;
  2371. }
  2372. return false;
  2373. }
  2374. void CJob::OnNetworkDisconnect()
  2375. {
  2376. RecalcTransientError();
  2377. }
  2378. void CJob::OnNetworkConnect()
  2379. {
  2380. RecalcTransientError();
  2381. }
  2382. void CJob::RecalcTransientError( bool ForResume )
  2383. {
  2384. if ( !ForResume )
  2385. {
  2386. if ( BG_JOB_STATE_ERROR == m_state ||
  2387. BG_JOB_STATE_TRANSFERRED == m_state ||
  2388. BG_JOB_STATE_SUSPENDED == m_state )
  2389. return;
  2390. }
  2391. BG_JOB_STATE OldState = m_state;
  2392. CJobError OldError = m_error;
  2393. //
  2394. // This is written to work withc both upload and download jobs.
  2395. // Be sure that an upload job never transits from CANCELLING state back to QUEUED
  2396. // or TRANSIENT_ERROR.
  2397. //
  2398. if (m_state != BG_JOB_STATE_CANCELLED &&
  2399. m_state != BG_JOB_STATE_ACKNOWLEDGED)
  2400. {
  2401. SetState( BG_JOB_STATE_QUEUED );
  2402. }
  2403. else
  2404. {
  2405. ASSERT( m_type != BG_JOB_TYPE_DOWNLOAD );
  2406. }
  2407. g_Manager->m_TaskScheduler.CancelWorkItem( static_cast<CJobRetryItem *>( this ));
  2408. if (m_fVolumeLocked)
  2409. {
  2410. QMErrInfo err( QM_FILE_TRANSIENT_ERROR, SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, BG_E_DESTINATION_LOCKED, NULL );
  2411. SetTransientError( err, m_CurrentFile, false, false );
  2412. }
  2413. else if (g_Manager->m_NetworkMonitor.GetAddressCount() == 0)
  2414. {
  2415. QMErrInfo err( QM_FILE_TRANSIENT_ERROR, SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, BG_E_NETWORK_DISCONNECTED, NULL );
  2416. SetTransientError( err, m_CurrentFile, false, false );
  2417. }
  2418. if (m_state != OldState ||
  2419. !(m_error == OldError))
  2420. {
  2421. ScheduleModificationCallback();
  2422. }
  2423. //
  2424. // SetTransientError has a similar test, but RecalcTransientError always sets the job to QUEUED state.
  2425. // Thus, the SetTransientError test will never succeed when called from here.
  2426. //
  2427. if (OldState == BG_JOB_STATE_TRANSFERRING &&
  2428. m_state == BG_JOB_STATE_TRANSIENT_ERROR)
  2429. {
  2430. ++m_retries;
  2431. }
  2432. }
  2433. HRESULT
  2434. CJob::TakeOwnership()
  2435. {
  2436. HRESULT Hr = S_OK;
  2437. CJobSecurityDescriptor *newsd = NULL;
  2438. try
  2439. {
  2440. SidHandle sid = GetThreadClientSid();
  2441. // If we are being called by the current
  2442. // owner, then we have nothing to do.
  2443. if ( sid == m_NotifySid )
  2444. return S_OK;
  2445. if ( IsRunning() )
  2446. {
  2447. g_Manager->InterruptDownload();
  2448. }
  2449. // revalidate access to all the local files
  2450. for (CFileList::iterator iter = m_files.begin(); iter != m_files.end(); ++iter)
  2451. {
  2452. Hr = (*iter)->ValidateAccessForUser( sid,
  2453. (m_type == BG_JOB_TYPE_DOWNLOAD) ? true : false );
  2454. if (FAILED(Hr))
  2455. {
  2456. g_Manager->ScheduleAnotherGroup();
  2457. return Hr;
  2458. }
  2459. }
  2460. // actually reassign ownership
  2461. SidHandle OldSid = m_NotifySid;
  2462. g_Manager->ExtendMetadata();
  2463. newsd = new CJobSecurityDescriptor( sid );
  2464. // replace the old notify sid and SECURITY_DESCRIPTOR
  2465. delete m_sd;
  2466. m_sd = newsd;
  2467. m_NotifySid = sid;
  2468. //
  2469. // Clear explicit credentials, because they are too important to leave open to the new owner.
  2470. // Also clear the notification command line, because it will be run in the context of the new owner.
  2471. // This could be the basis for a Trojan horse. The COM notification is not affected because it always
  2472. // makes calls as the original owner.
  2473. //
  2474. m_Credentials.Clear();
  2475. m_NotifyProgram = NULL;
  2476. m_NotifyParameters = NULL;
  2477. //
  2478. // Move the job to the online list if necessary.
  2479. //
  2480. g_Manager->ResetOnlineStatus( this, sid );
  2481. //
  2482. // Serialize and notify the client app of changes.
  2483. //
  2484. UpdateModificationTime();
  2485. //
  2486. // The old qmgr interface automatically changes the owner during each call to GetGroupInternal.
  2487. // Don't record those ownership changes.
  2488. //
  2489. if (NULL == GetOldExternalGroupInterface())
  2490. {
  2491. g_EventLogger->ReportJobOwnershipChange( m_id, m_name, OldSid, sid );
  2492. }
  2493. g_Manager->ScheduleAnotherGroup();
  2494. return Hr;
  2495. }
  2496. catch( ComError Error )
  2497. {
  2498. Hr = Error.Error();
  2499. delete newsd;
  2500. g_Manager->ScheduleAnotherGroup();
  2501. g_Manager->ShrinkMetadata();
  2502. return Hr;
  2503. }
  2504. }
  2505. CUnknownFileSizeList*
  2506. CJob::GetUnknownFileSizeList()
  2507. {
  2508. auto_ptr<CUnknownFileSizeList> pList( new CUnknownFileSizeList );
  2509. if (m_type == BG_JOB_TYPE_DOWNLOAD)
  2510. {
  2511. for(CFileList::iterator iter = m_files.begin(); iter != m_files.end(); iter++ )
  2512. {
  2513. if ( (*iter)->_GetBytesTotal() == -1 )
  2514. {
  2515. if (!pList->Add( (*iter), (*iter)->GetRemoteName() ) )
  2516. {
  2517. throw ComError( E_OUTOFMEMORY );
  2518. }
  2519. }
  2520. }
  2521. }
  2522. return pList.release();
  2523. }
  2524. void
  2525. CJob::UpdateModificationTime(
  2526. bool fNotify
  2527. )
  2528. {
  2529. FILETIME ftCurrentTime;
  2530. GetSystemTimeAsFileTime( &ftCurrentTime );
  2531. SetModificationTime( &ftCurrentTime );
  2532. UpdateLastAccessTime( );
  2533. if (fNotify)
  2534. {
  2535. if (g_Manager->m_TaskScheduler.IsWorkItemInScheduler( (CJobInactivityTimeout *) this))
  2536. {
  2537. g_Manager->m_TaskScheduler.CancelWorkItem( (CJobInactivityTimeout *) this );
  2538. g_Manager->m_TaskScheduler.InsertDelayedWorkItem( (CJobInactivityTimeout *) this, g_GlobalInfo->m_JobInactivityTimeout );
  2539. }
  2540. ScheduleModificationCallback();
  2541. g_Manager->Serialize();
  2542. }
  2543. }
  2544. void
  2545. CJob::UpdateLastAccessTime(
  2546. )
  2547. {
  2548. FILETIME ftCurrentTime;
  2549. GetSystemTimeAsFileTime( &ftCurrentTime );
  2550. SetLastAccessTime( &ftCurrentTime );
  2551. }
  2552. void CJob::CancelWorkitems()
  2553. {
  2554. ASSERT( g_Manager );
  2555. //
  2556. // While the job-modification item is pending, it keeps a separate ref to the job.
  2557. // The other work items share a single ref.
  2558. //
  2559. // g_Manager->m_TaskScheduler.CancelWorkItem( static_cast<CJobModificationItem *> (this) );
  2560. g_Manager->m_TaskScheduler.CancelWorkItem( static_cast<CJobInactivityTimeout *> (this) );
  2561. g_Manager->m_TaskScheduler.CancelWorkItem( static_cast<CJobNoProgressItem *> (this) );
  2562. g_Manager->m_TaskScheduler.CancelWorkItem( static_cast<CJobCallbackItem *> (this) );
  2563. g_Manager->m_TaskScheduler.CancelWorkItem( static_cast<CJobRetryItem *> (this) );
  2564. }
  2565. void
  2566. CJob::RemoveFromManager()
  2567. {
  2568. //
  2569. // The job is dead, except perhaps for a few references held by app threads.
  2570. // Ensure that no more action is taken on this job.
  2571. //
  2572. CancelWorkitems();
  2573. //
  2574. // If the job is not already removed from the job list, remove it
  2575. // and remove the refcount for the job's membership in the list.
  2576. //
  2577. if (g_Manager->RemoveJob( this ))
  2578. {
  2579. g_Manager->ScheduleAnotherGroup();
  2580. g_Manager->Serialize();
  2581. NotifyInternalDelete();
  2582. }
  2583. }
  2584. HRESULT
  2585. CJob::SetLimitedString(
  2586. StringHandle & destination,
  2587. const LPCWSTR Val,
  2588. SIZE_T limit
  2589. )
  2590. {
  2591. try
  2592. {
  2593. StringHandle name = Val;
  2594. // If the string is too long then reject it...
  2595. if (name.Size() > limit)
  2596. {
  2597. return BG_E_STRING_TOO_LONG;
  2598. }
  2599. UpdateString( destination, name );
  2600. return S_OK;
  2601. }
  2602. catch ( ComError err )
  2603. {
  2604. return err.Error();
  2605. }
  2606. }
  2607. HRESULT
  2608. CJob::UpdateString(
  2609. StringHandle & destination,
  2610. const StringHandle & Val
  2611. )
  2612. {
  2613. try
  2614. {
  2615. if ( destination.Size() < Val.Size() )
  2616. g_Manager->ExtendMetadata( sizeof(wchar_t) * (Val.Size() - destination.Size()) );
  2617. destination = Val;
  2618. UpdateModificationTime();
  2619. return S_OK;
  2620. }
  2621. catch ( ComError err )
  2622. {
  2623. g_Manager->ShrinkMetadata();
  2624. return err.Error();
  2625. }
  2626. }
  2627. HRESULT
  2628. CJob::ExcludeFilesFromBackup(
  2629. IN IVssCreateWriterMetadata *pMetadata
  2630. )
  2631. {
  2632. // defined in cmanager.cpp next to other backup writer code
  2633. //
  2634. extern void AddExcludeFile(
  2635. IN IVssCreateWriterMetadata *pMetadata,
  2636. LPCWSTR FileName
  2637. );
  2638. for (CFileList::iterator iter = m_files.begin(); iter != m_files.end(); ++iter)
  2639. {
  2640. try
  2641. {
  2642. if (false == (*iter)->IsCompleted())
  2643. {
  2644. AddExcludeFile( pMetadata, (*iter)->GetTemporaryName());
  2645. }
  2646. }
  2647. catch ( ComError err )
  2648. {
  2649. return err.m_error;
  2650. }
  2651. }
  2652. return S_OK;
  2653. }
  2654. //------------------------------------------------------------------------
  2655. // Change the GUID when an incompatible Serialize change is made.
  2656. GUID UploadJobGuid_v1_5 = { /* 42a25c7d-96b2-483e-a225-e2f102b6a30b */
  2657. 0x42a25c7d,
  2658. 0x96b2,
  2659. 0x483e,
  2660. {0xa2, 0x25, 0xe2, 0xf1, 0x02, 0xb6, 0xa3, 0x0b}
  2661. };
  2662. GUID DownloadJobGuid_v1_5 = { /* 0b73f1e8-2f4c-44de-8533-98092b34a18b */
  2663. 0x0b73f1e8,
  2664. 0x2f4c,
  2665. 0x44de,
  2666. {0x85, 0x33, 0x98, 0x09, 0x2b, 0x34, 0xa1, 0x8b}
  2667. };
  2668. GUID DownloadJobGuid_v1_2 = { /* 85e5c459-ef86-4fcd-8ea0-5b4f00d27e35 */
  2669. 0x85e5c459,
  2670. 0xef86,
  2671. 0x4fcd,
  2672. {0x8e, 0xa0, 0x5b, 0x4f, 0x00, 0xd2, 0x7e, 0x35}
  2673. };
  2674. GUID DownloadJobGuid_v1_0 = { /* 5770fca4-cf9f-4513-8737-972b4ea1265d */
  2675. 0x5770fca4,
  2676. 0xcf9f,
  2677. 0x4513,
  2678. {0x87, 0x37, 0x97, 0x2b, 0x4e, 0xa1, 0x26, 0x5d}
  2679. };
  2680. /* static */
  2681. CJob *
  2682. CJob::UnserializeJob(
  2683. HANDLE hFile
  2684. )
  2685. {
  2686. #define JOB_DOWNLOAD_V1_5 0
  2687. #define JOB_UPLOAD_V1_5 1
  2688. #define JOB_DOWNLOAD_V1 2
  2689. #define JOB_DOWNLOAD_V1_2 3
  2690. const GUID * JobGuids[] =
  2691. {
  2692. &DownloadJobGuid_v1_5,
  2693. &UploadJobGuid_v1_5,
  2694. &DownloadJobGuid_v1_0,
  2695. &DownloadJobGuid_v1_2,
  2696. NULL
  2697. };
  2698. CJob * job = NULL;
  2699. try
  2700. {
  2701. int Type = SafeReadGuidChoice( hFile, JobGuids );
  2702. switch (Type)
  2703. {
  2704. case JOB_DOWNLOAD_V1: job = new CJob; break;
  2705. case JOB_DOWNLOAD_V1_2: job = new CJob; break;
  2706. case JOB_DOWNLOAD_V1_5: job = new CJob; break;
  2707. case JOB_UPLOAD_V1_5: job = new CUploadJob; break;
  2708. default: THROW_HRESULT( E_FAIL );
  2709. }
  2710. // rewind to the front of the GUID
  2711. //
  2712. SetFilePointer( hFile, -1 * LONG(sizeof(GUID)), NULL, FILE_CURRENT );
  2713. job->Unserialize( hFile, Type );
  2714. }
  2715. catch( ComError err )
  2716. {
  2717. if (job)
  2718. {
  2719. job->UnlinkFromExternalInterfaces();
  2720. delete job;
  2721. }
  2722. throw;
  2723. }
  2724. return job;
  2725. }
  2726. HRESULT
  2727. CJob::Serialize(
  2728. HANDLE hFile
  2729. )
  2730. {
  2731. //
  2732. // If this function changes, be sure that the metadata extension
  2733. // constants are adequate.
  2734. //
  2735. SafeWriteBlockBegin( hFile, DownloadJobGuid_v1_5 );
  2736. long Was_m_refs = 0;
  2737. SafeWriteFile( hFile, Was_m_refs );
  2738. SafeWriteFile( hFile, m_priority );
  2739. SafeWriteFile( hFile, IsRunning() ? BG_JOB_STATE_QUEUED : m_state );
  2740. SafeWriteFile( hFile, m_type );
  2741. SafeWriteFile( hFile, m_id );
  2742. SafeWriteStringHandle( hFile, m_name );
  2743. SafeWriteStringHandle( hFile, m_description );
  2744. SafeWriteStringHandle( hFile, m_NotifyProgram );
  2745. SafeWriteStringHandle( hFile, m_NotifyParameters );
  2746. SafeWriteSid( hFile, m_NotifySid );
  2747. SafeWriteFile( hFile, m_NotifyFlags );
  2748. SafeWriteFile( hFile, m_fGroupNotifySid );
  2749. SafeWriteFile( hFile, m_CurrentFile );
  2750. m_sd->Serialize( hFile );
  2751. m_files.Serialize( hFile );
  2752. m_error.Serialize( hFile );
  2753. SafeWriteFile( hFile, m_retries );
  2754. SafeWriteFile( hFile, m_MinimumRetryDelay );
  2755. SafeWriteFile( hFile, m_NoProgressTimeout );
  2756. SafeWriteFile( hFile, m_CreationTime );
  2757. SafeWriteFile( hFile, m_LastAccessTime );
  2758. SafeWriteFile( hFile, m_ModificationTime );
  2759. SafeWriteFile( hFile, m_TransferCompletionTime );
  2760. if ( GetOldExternalGroupInterface() )
  2761. {
  2762. SafeWriteFile( hFile, (bool)true );
  2763. GetOldExternalGroupInterface()->Serialize( hFile );
  2764. }
  2765. else
  2766. {
  2767. SafeWriteFile( hFile, (bool)false );
  2768. }
  2769. SafeWriteFile( hFile, m_method );
  2770. ((CJobInactivityTimeout *) this)->Serialize( hFile );
  2771. ((CJobNoProgressItem *) this)->Serialize( hFile );
  2772. ((CJobCallbackItem *) this)->Serialize( hFile );
  2773. ((CJobRetryItem *) this)->Serialize( hFile );
  2774. SafeWriteFile( hFile, m_ProxySettings.ProxyUsage );
  2775. SafeWriteFile( hFile, m_ProxySettings.ProxyList );
  2776. SafeWriteFile( hFile, m_ProxySettings.ProxyBypassList );
  2777. m_Credentials.Serialize( hFile );
  2778. SafeWriteBlockEnd( hFile, DownloadJobGuid_v1_5 );
  2779. GetSystemTimeAsFileTime( &m_SerializeTime );
  2780. return S_OK;
  2781. }
  2782. void
  2783. CJob::Unserialize(
  2784. HANDLE hFile,
  2785. int Type
  2786. )
  2787. {
  2788. try
  2789. {
  2790. switch (Type)
  2791. {
  2792. case JOB_DOWNLOAD_V1:
  2793. LogInfo("unserializing v1.0 job %p", this);
  2794. SafeReadBlockBegin( hFile, DownloadJobGuid_v1_0 );
  2795. break;
  2796. case JOB_DOWNLOAD_V1_2:
  2797. LogInfo("unserializing v1.2 job %p", this);
  2798. SafeReadBlockBegin( hFile, DownloadJobGuid_v1_2 );
  2799. break;
  2800. case JOB_UPLOAD_V1_5:
  2801. case JOB_DOWNLOAD_V1_5:
  2802. LogInfo("unserializing v1.5 job %p", this);
  2803. SafeReadBlockBegin( hFile, DownloadJobGuid_v1_5 );
  2804. break;
  2805. default:
  2806. ASSERT( 0 );
  2807. }
  2808. long Was_m_refs = 0;
  2809. SafeReadFile( hFile, &Was_m_refs );
  2810. SafeReadFile( hFile, &m_priority );
  2811. SafeReadFile( hFile, &m_state );
  2812. SafeReadFile( hFile, &m_type );
  2813. SafeReadFile( hFile, &m_id );
  2814. m_name = SafeReadStringHandle( hFile );
  2815. m_description = SafeReadStringHandle( hFile );
  2816. switch (Type)
  2817. {
  2818. case JOB_DOWNLOAD_V1:
  2819. break;
  2820. case JOB_DOWNLOAD_V1_2:
  2821. // The 1.2 version internally allowed for a single notification command line,
  2822. // even though this was not exposed by the 1.0 interface. Ignore it.
  2823. //
  2824. {
  2825. StringHandle Unused = SafeReadStringHandle( hFile );
  2826. break;
  2827. }
  2828. case JOB_DOWNLOAD_V1_5:
  2829. case JOB_UPLOAD_V1_5:
  2830. m_NotifyProgram = SafeReadStringHandle( hFile );
  2831. m_NotifyParameters = SafeReadStringHandle( hFile );
  2832. break;
  2833. default:
  2834. ASSERT( 0 );
  2835. }
  2836. SafeReadSid( hFile, m_NotifySid );
  2837. SafeReadFile( hFile, &m_NotifyFlags );
  2838. SafeReadFile( hFile, &m_fGroupNotifySid );
  2839. SafeReadFile( hFile, &m_CurrentFile );
  2840. m_sd = CJobSecurityDescriptor::Unserialize( hFile );
  2841. m_files.Unserialize( hFile, this );
  2842. m_error.Unserialize( hFile, this );
  2843. SafeReadFile( hFile, &m_retries );
  2844. SafeReadFile( hFile, &m_MinimumRetryDelay );
  2845. SafeReadFile( hFile, &m_NoProgressTimeout );
  2846. SafeReadFile( hFile, &m_CreationTime );
  2847. SafeReadFile( hFile, &m_LastAccessTime );
  2848. SafeReadFile( hFile, &m_ModificationTime );
  2849. SafeReadFile( hFile, &m_TransferCompletionTime );
  2850. bool bHasOldExternalGroupInterface = false;
  2851. SafeReadFile( hFile, &bHasOldExternalGroupInterface );
  2852. if (bHasOldExternalGroupInterface)
  2853. {
  2854. COldGroupInterface *OldGroup = COldGroupInterface::UnSerialize( hFile, this );
  2855. SetOldExternalGroupInterface( OldGroup );
  2856. }
  2857. SafeReadFile( hFile, &m_method );
  2858. ((CJobInactivityTimeout *) this)->Unserialize( hFile );
  2859. ((CJobNoProgressItem *) this)->Unserialize( hFile );
  2860. ((CJobCallbackItem *) this)->Unserialize( hFile );
  2861. ((CJobRetryItem *) this)->Unserialize( hFile );
  2862. SafeReadFile( hFile, &m_ProxySettings.ProxyUsage );
  2863. SafeReadFile( hFile, &m_ProxySettings.ProxyList );
  2864. SafeReadFile( hFile, &m_ProxySettings.ProxyBypassList );
  2865. if (Type != JOB_DOWNLOAD_V1)
  2866. {
  2867. m_Credentials.Unserialize( hFile );
  2868. }
  2869. m_fVolumeLocked = false;
  2870. switch (Type)
  2871. {
  2872. case JOB_DOWNLOAD_V1: SafeReadBlockEnd( hFile, DownloadJobGuid_v1_0 ); break;
  2873. case JOB_DOWNLOAD_V1_2: SafeReadBlockEnd( hFile, DownloadJobGuid_v1_2 ); break;
  2874. case JOB_UPLOAD_V1_5: SafeReadBlockEnd( hFile, DownloadJobGuid_v1_5 ); break;
  2875. case JOB_DOWNLOAD_V1_5: SafeReadBlockEnd( hFile, DownloadJobGuid_v1_5 ); break;
  2876. default: THROW_HRESULT( E_INVALIDARG ); break;
  2877. }
  2878. }
  2879. catch( ComError Error )
  2880. {
  2881. LogError("invalid job data, exception 0x%x at line %d", Error.m_error, Error.m_line);
  2882. throw;
  2883. }
  2884. }
  2885. CUploadJob::CUploadJob(
  2886. LPCWSTR DisplayName,
  2887. BG_JOB_TYPE Type,
  2888. REFGUID JobId,
  2889. SidHandle NotifySid
  2890. )
  2891. : CJob( DisplayName, Type, JobId, NotifySid ),
  2892. m_ReplyFile( 0 )
  2893. {
  2894. }
  2895. CUploadJob::~CUploadJob()
  2896. {
  2897. delete m_ReplyFile;
  2898. }
  2899. HRESULT
  2900. CUploadJob::Serialize(
  2901. HANDLE hFile
  2902. )
  2903. {
  2904. LogInfo("serializing upload job %p", this);
  2905. SafeWriteBlockBegin( hFile, UploadJobGuid_v1_5 );
  2906. CJob::Serialize( hFile );
  2907. // additional data not in a download job
  2908. //
  2909. m_UploadData.Serialize( hFile );
  2910. SafeWriteFile( hFile, m_fOwnReplyFileName );
  2911. SafeWriteStringHandle( hFile, m_ReplyFileName );
  2912. if (m_ReplyFile)
  2913. {
  2914. SafeWriteFile( hFile, true );
  2915. m_ReplyFile->Serialize( hFile );
  2916. }
  2917. else
  2918. {
  2919. SafeWriteFile( hFile, false );
  2920. }
  2921. SafeWriteBlockEnd( hFile, UploadJobGuid_v1_5 );
  2922. return S_OK;
  2923. }
  2924. void
  2925. CUploadJob::Unserialize(
  2926. HANDLE hFile,
  2927. int Type
  2928. )
  2929. {
  2930. ASSERT( Type == JOB_UPLOAD_V1_5 );
  2931. LogInfo("unserializing upload job %p", this);
  2932. SafeReadBlockBegin( hFile, UploadJobGuid_v1_5 );
  2933. CJob::Unserialize( hFile, Type );
  2934. // additional data not in a download job
  2935. //
  2936. m_UploadData.Unserialize( hFile );
  2937. SafeReadFile( hFile, &m_fOwnReplyFileName );
  2938. m_ReplyFileName = SafeReadStringHandle( hFile );
  2939. bool fReplyFile;
  2940. SafeReadFile( hFile, &fReplyFile );
  2941. if (fReplyFile)
  2942. {
  2943. m_ReplyFile = CFile::Unserialize( hFile, this );
  2944. }
  2945. SafeReadBlockEnd( hFile, UploadJobGuid_v1_5 );
  2946. if (m_state == BG_JOB_STATE_CANCELLED ||
  2947. m_state == BG_JOB_STATE_ACKNOWLEDGED)
  2948. {
  2949. if (g_Manager->m_TaskScheduler.IsWorkItemInScheduler(static_cast<CJobRetryItem *>(this)))
  2950. {
  2951. m_UploadData.fSchedulable = false;
  2952. }
  2953. }
  2954. }
  2955. CFile *
  2956. CUploadJob::_GetFileIndex( LONG index ) const
  2957. {
  2958. if (index == REPLY_FILE_INDEX)
  2959. {
  2960. return m_ReplyFile;
  2961. }
  2962. if (index == NO_FILE_INDEX)
  2963. {
  2964. return NULL;
  2965. }
  2966. if (index < 0)
  2967. {
  2968. LogError("invalid index %d", index);
  2969. return NULL;
  2970. }
  2971. if (index >= m_files.size())
  2972. {
  2973. return NULL;
  2974. }
  2975. return m_files[ index ];
  2976. }
  2977. UPLOAD_DATA::UPLOAD_DATA()
  2978. {
  2979. State = UPLOAD_STATE_CREATE_SESSION;
  2980. fSchedulable = true;
  2981. Protocol = GUID_NULL;
  2982. HostId = NULL;
  2983. HostIdFallbackTimeout = 0xFFFFFFFF;
  2984. memset( &HostIdNoProgressStartTime, 0, sizeof(HostIdNoProgressStartTime) );
  2985. }
  2986. UPLOAD_DATA::~UPLOAD_DATA()
  2987. {
  2988. }
  2989. void
  2990. UPLOAD_DATA::SetUploadState(
  2991. UPLOAD_STATE NewState
  2992. )
  2993. {
  2994. if (State != NewState)
  2995. {
  2996. LogInfo( "upload state: %d -> %d", State, NewState );
  2997. State = NewState;
  2998. }
  2999. }
  3000. void
  3001. UPLOAD_DATA::Serialize(
  3002. HANDLE hFile
  3003. )
  3004. {
  3005. SafeWriteFile( hFile, State );
  3006. SafeWriteFile( hFile, Protocol );
  3007. SafeWriteStringHandle( hFile, SessionId );
  3008. SafeWriteStringHandle( hFile, ReplyUrl );
  3009. SafeWriteStringHandle( hFile, HostId );
  3010. SafeWriteFile( hFile, HostIdFallbackTimeout );
  3011. SafeWriteFile( hFile, HostIdNoProgressStartTime );
  3012. }
  3013. void
  3014. UPLOAD_DATA::Unserialize(
  3015. HANDLE hFile
  3016. )
  3017. {
  3018. SafeReadFile( hFile, &State );
  3019. SafeReadFile( hFile, &Protocol );
  3020. SessionId = SafeReadStringHandle( hFile );
  3021. ReplyUrl = SafeReadStringHandle( hFile );
  3022. HostId = SafeReadStringHandle( hFile );
  3023. SafeReadFile( hFile, &HostIdFallbackTimeout );
  3024. SafeReadFile( hFile, &HostIdNoProgressStartTime );
  3025. fSchedulable = true;
  3026. }
  3027. HRESULT
  3028. CUploadJob::GetReplyData(
  3029. byte **ppBuffer,
  3030. UINT64 *pLength
  3031. ) const
  3032. {
  3033. if (!ppBuffer || !pLength)
  3034. {
  3035. return E_INVALIDARG;
  3036. }
  3037. *pLength = 0;
  3038. *ppBuffer = 0;
  3039. if (m_type != BG_JOB_TYPE_UPLOAD_REPLY)
  3040. {
  3041. return E_NOTIMPL;
  3042. }
  3043. if (m_state != BG_JOB_STATE_TRANSFERRED &&
  3044. m_state != BG_JOB_STATE_ACKNOWLEDGED)
  3045. {
  3046. return BG_E_INVALID_STATE;
  3047. }
  3048. byte * buffer = 0;
  3049. try
  3050. {
  3051. CNestedImpersonation imp;
  3052. imp.SwitchToLogonToken();
  3053. //
  3054. // Open the file.
  3055. //
  3056. auto_HANDLE<INVALID_HANDLE_VALUE> hFile;
  3057. hFile = CreateFile( (m_state == BG_JOB_STATE_ACKNOWLEDGED)
  3058. ? m_ReplyFile->GetLocalName()
  3059. : m_ReplyFile->GetTemporaryName(),
  3060. GENERIC_READ,
  3061. 0, // no file sharing
  3062. NULL, // gneeric security descriptor
  3063. OPEN_EXISTING,
  3064. 0,
  3065. NULL // no template file
  3066. );
  3067. if (hFile.get() == INVALID_HANDLE_VALUE)
  3068. {
  3069. ThrowLastError();
  3070. }
  3071. //
  3072. // Allocate a buffer.
  3073. //
  3074. LARGE_INTEGER size;
  3075. if (!GetFileSizeEx( hFile.get(), &size ))
  3076. {
  3077. ThrowLastError();
  3078. }
  3079. if ( size.QuadPart > MAX_EASY_REPLY_DATA )
  3080. {
  3081. *pLength = size.QuadPart;
  3082. THROW_HRESULT( BG_E_TOO_LARGE );
  3083. }
  3084. buffer = (byte *) CoTaskMemAlloc( size.QuadPart );
  3085. if (!buffer)
  3086. {
  3087. THROW_HRESULT( E_OUTOFMEMORY );
  3088. }
  3089. //
  3090. // Read the file data.
  3091. //
  3092. DWORD BytesRead;
  3093. if (!ReadFile( hFile.get(),
  3094. buffer,
  3095. size.QuadPart,
  3096. &BytesRead,
  3097. 0 )) // no OVERLAPPED
  3098. {
  3099. ThrowLastError();
  3100. }
  3101. if (BytesRead != size.QuadPart)
  3102. {
  3103. throw ComError( E_FAIL );
  3104. }
  3105. //
  3106. // store it in the user pointers.
  3107. //
  3108. *pLength = BytesRead;
  3109. *ppBuffer = buffer;
  3110. return S_OK;
  3111. }
  3112. catch ( ComError err )
  3113. {
  3114. if (buffer)
  3115. {
  3116. CoTaskMemFree( buffer );
  3117. }
  3118. return err.Error();
  3119. }
  3120. }
  3121. HRESULT
  3122. CUploadJob::SetReplyFileName(
  3123. LPCWSTR Val
  3124. )
  3125. {
  3126. if (m_type != BG_JOB_TYPE_UPLOAD_REPLY)
  3127. {
  3128. return E_NOTIMPL;
  3129. }
  3130. if (m_ReplyFile)
  3131. {
  3132. return BG_E_INVALID_STATE;
  3133. }
  3134. try
  3135. {
  3136. StringHandle name = Val;
  3137. //
  3138. // Impersonate the user while checking file access.
  3139. //
  3140. CNestedImpersonation imp;
  3141. imp.SwitchToLogonToken();
  3142. if (Val)
  3143. {
  3144. RETURN_HRESULT( CFile::VerifyLocalFileName( Val, BG_JOB_TYPE_DOWNLOAD ));
  3145. }
  3146. //
  3147. // Four cases:
  3148. //
  3149. // 1. new name NULL, old name NULL:
  3150. // no change
  3151. //
  3152. // 2. new name NULL, old name non-NULL:
  3153. // overwrite the file name, set ownership correctly. No need to
  3154. // delete the old file because it wasn't created yet.
  3155. //
  3156. // 3. new name non-NULL, old name NULL:
  3157. // overwrite the file name, set ownership correctly. Delete the
  3158. // temporary old file name.
  3159. //
  3160. // 4. new name non-NULL, old name non-NULL:
  3161. // overwrite the file name. no file to delete.
  3162. //
  3163. if (name.Size() > 0)
  3164. {
  3165. THROW_HRESULT( BITSCheckFileWritability( name ));
  3166. DeleteGeneratedReplyFile();
  3167. THROW_HRESULT( UpdateString( m_ReplyFileName, name));
  3168. m_fOwnReplyFileName = false;
  3169. }
  3170. else
  3171. {
  3172. THROW_HRESULT( UpdateString( m_ReplyFileName, name));
  3173. (void) GenerateReplyFile( false );
  3174. }
  3175. g_Manager->Serialize();
  3176. return S_OK;
  3177. }
  3178. catch ( ComError err )
  3179. {
  3180. return err.Error();
  3181. }
  3182. }
  3183. HRESULT
  3184. CUploadJob::GetReplyFileName(
  3185. LPWSTR * pVal
  3186. ) const
  3187. {
  3188. if (m_ReplyFileName.Size() == 0)
  3189. {
  3190. *pVal = NULL;
  3191. return S_OK;
  3192. }
  3193. *pVal = MidlCopyString( m_ReplyFileName );
  3194. return (*pVal) ? S_OK : E_OUTOFMEMORY;
  3195. }
  3196. HRESULT
  3197. CUploadJob::GetReplyProgress(
  3198. BG_JOB_REPLY_PROGRESS *pProgress
  3199. ) const
  3200. {
  3201. if (m_type != BG_JOB_TYPE_UPLOAD_REPLY)
  3202. {
  3203. return E_NOTIMPL;
  3204. }
  3205. if (m_ReplyFile)
  3206. {
  3207. pProgress->BytesTotal = m_ReplyFile->_GetBytesTotal();
  3208. pProgress->BytesTransferred = m_ReplyFile->_GetBytesTransferred();
  3209. }
  3210. else
  3211. {
  3212. pProgress->BytesTotal = BG_SIZE_UNKNOWN;
  3213. pProgress->BytesTransferred = 0;
  3214. }
  3215. return S_OK;
  3216. }
  3217. HRESULT
  3218. CUploadJob::Resume()
  3219. {
  3220. if (m_type == BG_JOB_TYPE_UPLOAD_REPLY)
  3221. {
  3222. RETURN_HRESULT( GenerateReplyFile(true ) );
  3223. }
  3224. return CJob::Resume();
  3225. }
  3226. HRESULT
  3227. CUploadJob::GenerateReplyFile(
  3228. bool fSerialize
  3229. )
  3230. {
  3231. if (0 != wcscmp( m_ReplyFileName, L"" ))
  3232. {
  3233. return S_OK;
  3234. }
  3235. //
  3236. // Gotta create a reply file name.
  3237. //
  3238. try
  3239. {
  3240. if (IsEmpty())
  3241. {
  3242. return BG_E_EMPTY;
  3243. }
  3244. g_Manager->ExtendMetadata();
  3245. //
  3246. // Impersonate the user while checking file access.
  3247. //
  3248. CNestedImpersonation imp;
  3249. imp.SwitchToLogonToken();
  3250. StringHandle Ignore;
  3251. StringHandle Directory = BITSCrackFileName( GetUploadFile()->GetLocalName(), Ignore );
  3252. m_ReplyFileName = BITSCreateTempFile( Directory );
  3253. m_fOwnReplyFileName = true;
  3254. if (fSerialize)
  3255. {
  3256. g_Manager->Serialize();
  3257. }
  3258. return S_OK;
  3259. }
  3260. catch ( ComError err )
  3261. {
  3262. g_Manager->ShrinkMetadata();
  3263. return err.Error();
  3264. }
  3265. }
  3266. HRESULT
  3267. CUploadJob::DeleteGeneratedReplyFile()
  3268. {
  3269. if (m_fOwnReplyFileName && wcslen( m_ReplyFileName ) > 0)
  3270. {
  3271. if (!DeleteFile( m_ReplyFileName ))
  3272. {
  3273. HRESULT hr = HRESULT_FROM_WIN32( GetLastError());
  3274. LogWarning("unable to delete generated reply file '%S', %!winerr!", LPCWSTR(m_ReplyFileName), hr);
  3275. if (IsReportableFileDeletionError( hr, m_ReplyFileName ))
  3276. {
  3277. g_EventLogger->ReportFileDeletionFailure( m_id, m_name, LPCWSTR(m_ReplyFileName), false );
  3278. }
  3279. return hr;
  3280. }
  3281. }
  3282. return S_OK;
  3283. }
  3284. void
  3285. CUploadJob::SetReplyFile(
  3286. CFile * file
  3287. )
  3288. {
  3289. try
  3290. {
  3291. g_Manager->ExtendMetadata( file->GetSizeEstimate() );
  3292. m_ReplyFile = file;
  3293. g_Manager->Serialize();
  3294. }
  3295. catch ( ComError err )
  3296. {
  3297. g_Manager->ShrinkMetadata();
  3298. throw;
  3299. }
  3300. }
  3301. void CUploadJob::Transfer()
  3302. {
  3303. HRESULT hr;
  3304. HANDLE hToken = NULL;
  3305. if( LogLevelEnabled( LogFlagInfo ) )
  3306. {
  3307. LogDl( "current job: %!guid!", &m_id );
  3308. }
  3309. //
  3310. // Get a copy of the user's token.
  3311. //
  3312. hr = g_Manager->CloneUserToken( GetOwnerSid(), ANY_SESSION, &hToken );
  3313. if (FAILED(hr))
  3314. {
  3315. if (hr == HRESULT_FROM_WIN32( ERROR_NOT_LOGGED_ON ))
  3316. {
  3317. LogDl( "job owner is not logged on");
  3318. // move the group off the main list.
  3319. g_Manager->MoveJobOffline( this );
  3320. RecalcTransientError();
  3321. }
  3322. else
  3323. {
  3324. LogError( "download : unable to get token %!winerr!", hr);
  3325. QMErrInfo err( QM_FILE_TRANSIENT_ERROR, SOURCE_QMGR_QUEUE, ERROR_STYLE_HRESULT, hr, "CloneUserToken" );
  3326. SetTransientError( err, m_CurrentFile, false, false );
  3327. }
  3328. g_Manager->m_TaskScheduler.CompleteWorkItem();
  3329. g_Manager->ScheduleAnotherGroup();
  3330. return;
  3331. }
  3332. //
  3333. // Switch states and begin uploading.
  3334. //
  3335. bool bThrottle = ShouldThrottle();
  3336. LogDl( "Throttling %s", bThrottle ? "enabled" : "disabled" );
  3337. if (bThrottle)
  3338. {
  3339. // ignore errors
  3340. //
  3341. (void) SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_IDLE );
  3342. }
  3343. if (m_state == BG_JOB_STATE_QUEUED)
  3344. {
  3345. SetState( BG_JOB_STATE_CONNECTING );
  3346. ScheduleModificationCallback();
  3347. }
  3348. QMErrInfo ErrInfo;
  3349. long tries = 0;
  3350. retry:
  3351. ErrInfo.Clear();
  3352. LogDl("Upload Starting, tries = %d", tries);
  3353. ASSERT( g_Manager->m_TaskScheduler.IsWriter() );
  3354. g_Manager->m_pPD->Upload( this,
  3355. GetUploadFile(),
  3356. hToken,
  3357. ErrInfo
  3358. );
  3359. ASSERT( g_Manager->m_TaskScheduler.IsWriter() );
  3360. LogDl( "Upload Ended." );
  3361. ErrInfo.Log();
  3362. ASSERT( ErrInfo.result != QM_IN_PROGRESS );
  3363. if (ErrInfo.result == QM_SERVER_FILE_CHANGED && ++tries < 3)
  3364. {
  3365. goto retry;
  3366. }
  3367. //
  3368. // Update state based on the upload result.
  3369. //
  3370. g_Manager->m_TaskScheduler.CompleteWorkItem();
  3371. switch (ErrInfo.result)
  3372. {
  3373. case QM_FILE_TRANSIENT_ERROR: FileTransientError( &ErrInfo ); break;
  3374. case QM_FILE_DONE: FileComplete(); break;
  3375. case QM_FILE_FATAL_ERROR: FileFatalError( &ErrInfo ); break;
  3376. case QM_FILE_ABORTED: break;
  3377. case QM_SERVER_FILE_CHANGED: g_Manager->AppendOnline( this ); break;
  3378. default: ASSERT( 0 && "unhandled download result" ); break;
  3379. }
  3380. CloseHandle( hToken );
  3381. if (bThrottle)
  3382. {
  3383. while (!SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_NORMAL ))
  3384. {
  3385. Sleep(100);
  3386. }
  3387. }
  3388. }
  3389. HRESULT
  3390. CUploadJob::Complete()
  3391. {
  3392. HRESULT hr;
  3393. switch (m_state)
  3394. {
  3395. case BG_JOB_STATE_TRANSFERRED:
  3396. {
  3397. try
  3398. {
  3399. hr = S_OK;
  3400. //
  3401. // In order to manipulate files, we need to have IMPERSONATE level access rather
  3402. // than the COM default of IDENTIFY access. The files will be committed as the called
  3403. // rather than the job owner (assuming they are different).
  3404. //
  3405. CNestedImpersonation imp;
  3406. imp.SwitchToLogonToken();
  3407. RETURN_HRESULT( hr = CommitReplyFile() );
  3408. // hr may be S_OK, or BG_S_PARTIAL_COMPLETE.
  3409. SetState( BG_JOB_STATE_ACKNOWLEDGED );
  3410. RemoveFromManager();
  3411. return hr;
  3412. }
  3413. catch ( ComError err )
  3414. {
  3415. return err.Error();
  3416. }
  3417. }
  3418. default:
  3419. {
  3420. return BG_E_INVALID_STATE;
  3421. }
  3422. }
  3423. ASSERT(0);
  3424. return BG_E_INVALID_STATE;
  3425. }
  3426. HRESULT
  3427. CUploadJob::Cancel()
  3428. {
  3429. HRESULT Hr = S_OK;
  3430. switch (m_state)
  3431. {
  3432. case BG_JOB_STATE_CONNECTING:
  3433. case BG_JOB_STATE_TRANSFERRING:
  3434. {
  3435. g_Manager->InterruptDownload();
  3436. // OK to fall through here
  3437. }
  3438. case BG_JOB_STATE_SUSPENDED:
  3439. case BG_JOB_STATE_ERROR:
  3440. case BG_JOB_STATE_QUEUED:
  3441. case BG_JOB_STATE_TRANSIENT_ERROR:
  3442. case BG_JOB_STATE_TRANSFERRED:
  3443. {
  3444. try
  3445. {
  3446. //
  3447. // In order to manipulate files, we need to have IMPERSONATE level access rather
  3448. // than the COM default of IDENTIFY access. The files will be committed as the called
  3449. // rather than the job owner (assuming they are different).
  3450. //
  3451. CNestedImpersonation imp;
  3452. imp.SwitchToLogonToken();
  3453. RETURN_HRESULT( NonOwnerModificationCheck( imp.CopySid(), CHG_CANCEL, PROP_NONE ));
  3454. RETURN_HRESULT( Hr = RemoveReplyFile() );
  3455. // Hr may be BG_S_UNABLE_TO_REMOVE_FILES
  3456. SetState( BG_JOB_STATE_CANCELLED );
  3457. //
  3458. // If the close-session exchange has not happened yet,
  3459. // begin a cancel-session exchange.
  3460. //
  3461. if (SessionInProgress())
  3462. {
  3463. LogInfo("job %p: upload session in state %d, cancelling", this, m_UploadData.State );
  3464. g_Manager->m_TaskScheduler.CancelWorkItem( (CJobCallbackItem *) this );
  3465. SetNoProgressTimeout( UPLOAD_CANCEL_TIMEOUT );
  3466. m_UploadData.SetUploadState( UPLOAD_STATE_CANCEL_SESSION );
  3467. g_Manager->ScheduleAnotherGroup();
  3468. g_Manager->Serialize();
  3469. }
  3470. else
  3471. {
  3472. RemoveFromManager();
  3473. }
  3474. return Hr;
  3475. }
  3476. catch ( ComError err )
  3477. {
  3478. return err.Error();
  3479. }
  3480. }
  3481. case BG_JOB_STATE_CANCELLED:
  3482. case BG_JOB_STATE_ACKNOWLEDGED:
  3483. {
  3484. return BG_E_INVALID_STATE;
  3485. }
  3486. default:
  3487. {
  3488. ASSERT( 0 );
  3489. return Hr;
  3490. }
  3491. }
  3492. ASSERT( 0 );
  3493. return Hr;
  3494. }
  3495. void
  3496. CUploadJob::FileComplete()
  3497. {
  3498. //
  3499. // The downloader successfully completed one of three things:
  3500. //
  3501. // 1. job type is UPLOAD. The file was uploaded and the session closed.
  3502. // 2. job type is UPLOAD_REPLY. The file was uploaded, reply downloaded, and session closed.
  3503. // 3. either job type; an early Cancel required the job to cancel the session.
  3504. //
  3505. switch (m_state)
  3506. {
  3507. case BG_JOB_STATE_CANCELLED:
  3508. case BG_JOB_STATE_ACKNOWLEDGED:
  3509. {
  3510. ASSERT (m_UploadData.State == UPLOAD_STATE_CLOSED || m_UploadData.State == UPLOAD_STATE_CANCELLED);
  3511. RemoveFromManager();
  3512. break;
  3513. }
  3514. default:
  3515. {
  3516. ++m_CurrentFile;
  3517. JobTransferred();
  3518. g_Manager->Serialize();
  3519. }
  3520. }
  3521. }
  3522. void
  3523. CUploadJob::UpdateProgress(
  3524. UINT64 BytesTransferred,
  3525. UINT64 BytesTotal
  3526. )
  3527. {
  3528. memset( &GetUploadData().HostIdNoProgressStartTime, 0,
  3529. sizeof( GetUploadData().HostIdNoProgressStartTime ) );
  3530. CJob::UpdateProgress( BytesTransferred, BytesTotal );
  3531. }
  3532. bool
  3533. CUploadJob::CheckHostIdFallbackTimeout()
  3534. {
  3535. if ( GetUploadData().HostIdFallbackTimeout != 0xFFFFFFFF )
  3536. {
  3537. UINT64 HostIdNoProgressStartTime = FILETIMEToUINT64( GetUploadData().HostIdNoProgressStartTime );
  3538. if ( HostIdNoProgressStartTime )
  3539. {
  3540. UINT64 TimeoutTime = HostIdNoProgressStartTime +
  3541. GetUploadData().HostIdFallbackTimeout * NanoSec100PerSec;
  3542. if ( TimeoutTime < HostIdNoProgressStartTime )
  3543. return true; //wraparound
  3544. FILETIME CurrentTimeAsFileTime;
  3545. GetSystemTimeAsFileTime( &CurrentTimeAsFileTime );
  3546. UINT64 CurrentTime = FILETIMEToUINT64( CurrentTimeAsFileTime );
  3547. if ( CurrentTime > TimeoutTime )
  3548. return true;
  3549. }
  3550. }
  3551. return false;
  3552. }
  3553. void
  3554. CUploadJob::FileFatalError(
  3555. QMErrInfo * ErrInfo
  3556. )
  3557. {
  3558. switch (m_state)
  3559. {
  3560. case BG_JOB_STATE_CANCELLED:
  3561. case BG_JOB_STATE_ACKNOWLEDGED:
  3562. {
  3563. //
  3564. // Our attempt to cancel or close the session has failed. The job is finished.
  3565. //
  3566. ASSERT (m_UploadData.State == UPLOAD_STATE_CLOSE_SESSION || m_UploadData.State == UPLOAD_STATE_CANCEL_SESSION);
  3567. RemoveFromManager();
  3568. break;
  3569. }
  3570. default:
  3571. {
  3572. if ( CheckHostIdFallbackTimeout() )
  3573. {
  3574. LogError( "Reverting back to main URL since the timeout has expired" );
  3575. FileTransientError( ErrInfo );
  3576. return;
  3577. }
  3578. // If ErrInfo is NULL, use the current error.
  3579. if ( BG_JOB_STATE_TRANSFERRING == m_state )
  3580. {
  3581. ++m_retries;
  3582. }
  3583. g_Manager->m_TaskScheduler.CancelWorkItem( static_cast<CJobNoProgressItem *>(this) );
  3584. g_Manager->m_TaskScheduler.CancelWorkItem( static_cast<CJobCallbackItem *>(this) );
  3585. SetState( BG_JOB_STATE_ERROR );
  3586. if ( ErrInfo )
  3587. {
  3588. if (m_UploadData.State == UPLOAD_STATE_GET_REPLY)
  3589. {
  3590. RecordError( ErrInfo, REPLY_FILE_INDEX );
  3591. }
  3592. else
  3593. {
  3594. RecordError( ErrInfo, m_CurrentFile );
  3595. }
  3596. }
  3597. ScheduleErrorCallback();
  3598. g_Manager->Serialize();
  3599. }
  3600. }
  3601. }
  3602. void
  3603. CUploadJob::FileTransientError(
  3604. QMErrInfo * ErrInfo
  3605. )
  3606. {
  3607. switch (m_state)
  3608. {
  3609. case BG_JOB_STATE_CANCELLED:
  3610. case BG_JOB_STATE_ACKNOWLEDGED:
  3611. {
  3612. //
  3613. // Cancelling or completing a session is not subject to the host-ID back-off policy, because
  3614. // we do not intend to create a new session if the current one is gone.
  3615. //
  3616. SetTransientError( *ErrInfo, NO_FILE_INDEX, true, true );
  3617. m_UploadData.fSchedulable = false;
  3618. break;
  3619. }
  3620. default:
  3621. {
  3622. if (m_UploadData.State == UPLOAD_STATE_GET_REPLY)
  3623. {
  3624. //
  3625. // Once the reply is generated, we do not want to back off to a different server.
  3626. //
  3627. SetTransientError( *ErrInfo, REPLY_FILE_INDEX, true, true );
  3628. }
  3629. else
  3630. {
  3631. //
  3632. // Establishing a session or uploading a file is subject to the host-ID back-off policy:
  3633. // If the HostId server has been unavailable for <HostIdFallbackTimeout> seconds,
  3634. // then clear the HostId field and retry the upload with the original server name.
  3635. //
  3636. bool ShouldRevertToOriginalURL = CheckHostIdFallbackTimeout();
  3637. if ( ShouldRevertToOriginalURL )
  3638. {
  3639. LogError( "Reverting back to main URL since the timeout has expired" );
  3640. GetUploadData().HostId = StringHandle();
  3641. GetUploadData().HostIdFallbackTimeout = 0xFFFFFFFF;
  3642. SetState( BG_JOB_STATE_QUEUED );
  3643. }
  3644. else
  3645. {
  3646. //
  3647. // Note the time of first failure, if we haven't already.
  3648. //
  3649. if ( GetUploadData().HostIdFallbackTimeout != 0xFFFFFFFF &&
  3650. !FILETIMEToUINT64( GetUploadData().HostIdNoProgressStartTime ) )
  3651. {
  3652. GetSystemTimeAsFileTime( &GetUploadData().HostIdNoProgressStartTime );
  3653. }
  3654. SetTransientError( *ErrInfo, m_CurrentFile, true, true );
  3655. }
  3656. }
  3657. }
  3658. }
  3659. }
  3660. void CUploadJob::OnRetryJob()
  3661. {
  3662. if (g_Manager->m_TaskScheduler.LockWriter() )
  3663. {
  3664. g_Manager->m_TaskScheduler.AcknowledgeWorkItemCancel();
  3665. return;
  3666. }
  3667. if (m_state == BG_JOB_STATE_TRANSIENT_ERROR)
  3668. {
  3669. SetState( BG_JOB_STATE_QUEUED );
  3670. g_Manager->AppendOnline( this );
  3671. UpdateModificationTime();
  3672. }
  3673. else if (m_state == BG_JOB_STATE_CANCELLED ||
  3674. m_state == BG_JOB_STATE_ACKNOWLEDGED)
  3675. {
  3676. m_UploadData.fSchedulable = true;
  3677. g_Manager->AppendOnline( this );
  3678. }
  3679. g_Manager->m_TaskScheduler.CompleteWorkItem();
  3680. g_Manager->ScheduleAnotherGroup();
  3681. g_Manager->m_TaskScheduler.UnlockWriter();
  3682. }
  3683. void CUploadJob::OnInactivityTimeout()
  3684. {
  3685. if (g_Manager->m_TaskScheduler.LockWriter() )
  3686. {
  3687. g_Manager->m_TaskScheduler.AcknowledgeWorkItemCancel();
  3688. return;
  3689. }
  3690. g_Manager->m_TaskScheduler.CompleteWorkItem();
  3691. RemoveFromManager();
  3692. g_Manager->m_TaskScheduler.UnlockWriter();
  3693. }
  3694. bool CUploadJob::IsTransferringToDrive( const WCHAR *CanonicalVolume )
  3695. {
  3696. CFile *file = GetCurrentFile();
  3697. if ( !file )
  3698. {
  3699. file = m_ReplyFile;
  3700. if (!file)
  3701. {
  3702. return false;
  3703. }
  3704. }
  3705. if ( file->IsCanonicalVolume( CanonicalVolume ) )
  3706. return true;
  3707. else
  3708. return false;
  3709. }
  3710. bool CUploadJob::IsRunnable()
  3711. {
  3712. if (m_fVolumeLocked)
  3713. {
  3714. return false;
  3715. }
  3716. switch (m_state)
  3717. {
  3718. case BG_JOB_STATE_SUSPENDED:
  3719. case BG_JOB_STATE_ERROR:
  3720. case BG_JOB_STATE_TRANSIENT_ERROR:
  3721. return false;
  3722. default:
  3723. if (m_UploadData.fSchedulable &&
  3724. m_UploadData.State != UPLOAD_STATE_CLOSED &&
  3725. m_UploadData.State != UPLOAD_STATE_CANCELLED )
  3726. {
  3727. return true;
  3728. }
  3729. return false;
  3730. }
  3731. }
  3732. HRESULT
  3733. CUploadJob::RemoveReplyFile()
  3734. {
  3735. //
  3736. // Delete the reply file, if it was created by BITS.
  3737. // Delete the temporary reply file
  3738. //
  3739. HRESULT Hr;
  3740. HRESULT FinalHr = S_OK;
  3741. if (FAILED( DeleteGeneratedReplyFile() ))
  3742. {
  3743. FinalHr = BG_S_UNABLE_TO_DELETE_FILES;
  3744. }
  3745. m_fOwnReplyFileName = false;
  3746. if (m_ReplyFile)
  3747. {
  3748. Hr = m_ReplyFile->DeleteTempFile();
  3749. if (FAILED(Hr))
  3750. {
  3751. LPCWSTR FileName = m_ReplyFile->GetTemporaryName();
  3752. LogWarning("unable to delete temporary reply file '%S', %!winerr!", FileName, Hr);
  3753. if (IsReportableFileDeletionError( Hr, FileName ))
  3754. {
  3755. g_EventLogger->ReportFileDeletionFailure( m_id, m_name, FileName, false );
  3756. }
  3757. FinalHr = BG_S_UNABLE_TO_DELETE_FILES;
  3758. }
  3759. }
  3760. return FinalHr;
  3761. }
  3762. HRESULT
  3763. CUploadJob::CommitReplyFile()
  3764. {
  3765. //
  3766. // Commit the reply file if it is complete.
  3767. // Otherwise, clean it up.
  3768. //
  3769. if (m_ReplyFile && m_ReplyFile->ReceivedAllData())
  3770. {
  3771. RETURN_HRESULT( m_ReplyFile->MoveTempFile() );
  3772. }
  3773. else
  3774. {
  3775. LogInfo("commit reply: skipping partial file '%S'",
  3776. m_ReplyFile ? (const WCHAR*) m_ReplyFile->GetLocalName() : L"(null)");
  3777. RemoveReplyFile();
  3778. return BG_S_PARTIAL_COMPLETE;
  3779. }
  3780. return S_OK;
  3781. }
  3782. HRESULT CJob::NonOwnerModificationCheck(
  3783. SidHandle CurrentSid,
  3784. enum ChangeType Change,
  3785. enum PropertyType Property
  3786. )
  3787. {
  3788. try
  3789. {
  3790. if (CurrentSid == m_NotifySid)
  3791. {
  3792. return S_OK;
  3793. }
  3794. switch (Change)
  3795. {
  3796. case CHG_CANCEL:
  3797. {
  3798. g_EventLogger->ReportJobCancellation( m_id, m_name, m_NotifySid, CurrentSid );
  3799. break;
  3800. }
  3801. }
  3802. return S_OK;
  3803. }
  3804. catch ( ComError err )
  3805. {
  3806. return err.Error();
  3807. }
  3808. }
  3809. HRESULT
  3810. CUploadJob::ExcludeFilesFromBackup(
  3811. IN IVssCreateWriterMetadata *pMetadata
  3812. )
  3813. {
  3814. // defined in cmanager.cpp next to other backup writer code
  3815. //
  3816. extern void AddExcludeFile(
  3817. IN IVssCreateWriterMetadata *pMetadata,
  3818. LPCWSTR FileName
  3819. );
  3820. if (m_ReplyFile && false == m_ReplyFile->IsCompleted())
  3821. {
  3822. try
  3823. {
  3824. AddExcludeFile( pMetadata, m_ReplyFile->GetTemporaryName());
  3825. }
  3826. catch ( ComError err )
  3827. {
  3828. return err.m_error;
  3829. }
  3830. }
  3831. return S_OK;
  3832. }
  3833. //------------------------------------------------------------------------
  3834. GUID FileListStorageGuid = { /* 7756da36-516f-435a-acac-44a248fff34d */
  3835. 0x7756da36,
  3836. 0x516f,
  3837. 0x435a,
  3838. {0xac, 0xac, 0x44, 0xa2, 0x48, 0xff, 0xf3, 0x4d}
  3839. };
  3840. HRESULT
  3841. CJob::CFileList::Serialize(
  3842. HANDLE hFile
  3843. )
  3844. {
  3845. //
  3846. // If this function changes, be sure that the metadata extension
  3847. // constants are adequate.
  3848. //
  3849. iterator iter;
  3850. SafeWriteBlockBegin( hFile, FileListStorageGuid );
  3851. DWORD count = size();
  3852. SafeWriteFile( hFile, count );
  3853. for (iter=begin(); iter != end(); ++iter)
  3854. {
  3855. (*iter)->Serialize( hFile );
  3856. }
  3857. SafeWriteBlockEnd( hFile, FileListStorageGuid );
  3858. return S_OK;
  3859. }
  3860. void
  3861. CJob::CFileList::Unserialize(
  3862. HANDLE hFile,
  3863. CJob* Job
  3864. )
  3865. {
  3866. DWORD i, count;
  3867. SafeReadBlockBegin( hFile, FileListStorageGuid );
  3868. SafeReadFile( hFile, &count );
  3869. for (i=0; i < count; ++i)
  3870. {
  3871. CFile * file = CFile::Unserialize( hFile, Job );
  3872. push_back( file );
  3873. }
  3874. SafeReadBlockEnd( hFile, FileListStorageGuid );
  3875. }
  3876. void
  3877. CJob::CFileList::Delete(
  3878. CFileList::iterator Initial,
  3879. CFileList::iterator Terminal
  3880. )
  3881. {
  3882. //
  3883. // delete the CFile objects
  3884. //
  3885. iterator iter = Initial;
  3886. while (iter != Terminal)
  3887. {
  3888. CFile * file = (*iter);
  3889. ++iter;
  3890. delete file;
  3891. }
  3892. //
  3893. // erase them from the dictionary
  3894. //
  3895. erase( Initial, Terminal );
  3896. }
  3897. //------------------------------------------------------------------------
  3898. HRESULT CLockedJobWritePointer::ValidateAccess()
  3899. {
  3900. HRESULT hr = CLockedWritePointer<CJob, BG_JOB_WRITE>::ValidateAccess();
  3901. if (SUCCEEDED(hr))
  3902. {
  3903. m_Pointer->UpdateLastAccessTime();
  3904. }
  3905. return hr;
  3906. }
  3907. HRESULT CLockedJobReadPointer::ValidateAccess()
  3908. {
  3909. HRESULT hr = CLockedReadPointer<CJob, BG_JOB_READ>::ValidateAccess();
  3910. if (SUCCEEDED(hr))
  3911. {
  3912. ((CJob *) m_Pointer)->UpdateLastAccessTime();
  3913. }
  3914. return hr;
  3915. }
  3916. CJobExternal::CJobExternal()
  3917. : m_ServiceInstance( g_ServiceInstance ),
  3918. pJob( NULL ),
  3919. m_refs(1)
  3920. {
  3921. }
  3922. CJobExternal::~CJobExternal()
  3923. {
  3924. //
  3925. // Delete the underlying job object, unless it was already deleted when the service stopped.
  3926. //
  3927. if (g_ServiceInstance != m_ServiceInstance ||
  3928. (g_ServiceState != MANAGER_ACTIVE && g_ServiceState != MANAGER_STARTING))
  3929. {
  3930. return;
  3931. }
  3932. delete pJob;
  3933. }
  3934. STDMETHODIMP
  3935. CJobExternal::QueryInterface(
  3936. REFIID iid,
  3937. void** ppvObject
  3938. )
  3939. {
  3940. BEGIN_EXTERNAL_FUNC
  3941. HRESULT Hr = S_OK;
  3942. *ppvObject = NULL;
  3943. if (iid == __uuidof(IUnknown)
  3944. || iid == __uuidof(IBackgroundCopyJob)
  3945. #if !defined( BITS_V12 )
  3946. || iid == __uuidof(IBackgroundCopyJob2)
  3947. #endif
  3948. )
  3949. {
  3950. *ppvObject = (IBackgroundCopyJob2 *) this;
  3951. ((IUnknown *)(*ppvObject))->AddRef();
  3952. }
  3953. else
  3954. {
  3955. Hr = E_NOINTERFACE;
  3956. }
  3957. LogRef( "job %p, iid %!guid!, Hr %x", pJob, &iid, Hr );
  3958. return Hr;
  3959. END_EXTERNAL_FUNC
  3960. }
  3961. ULONG
  3962. CJobExternal::AddRef()
  3963. {
  3964. BEGIN_EXTERNAL_FUNC
  3965. ULONG newrefs = InterlockedIncrement(&m_refs);
  3966. LogRef( "job %p, refs = %d", pJob, newrefs );
  3967. return newrefs;
  3968. END_EXTERNAL_FUNC
  3969. }
  3970. ULONG
  3971. CJobExternal::Release()
  3972. {
  3973. BEGIN_EXTERNAL_FUNC
  3974. ULONG newrefs = InterlockedDecrement(&m_refs);
  3975. LogRef( "job %p, refs = %d", pJob, newrefs );
  3976. if (newrefs == 0)
  3977. {
  3978. delete this;
  3979. }
  3980. return newrefs;
  3981. END_EXTERNAL_FUNC
  3982. }
  3983. STDMETHODIMP
  3984. CJobExternal::AddFileSetInternal(
  3985. /* [in] */ ULONG cFileCount,
  3986. /* [size_is][in] */ BG_FILE_INFO *pFileSet
  3987. )
  3988. {
  3989. CLockedJobWritePointer LockedJob(pJob);
  3990. LogPublicApiBegin( "cFileCount %u, pFileSet %p", cFileCount, pFileSet );
  3991. HRESULT Hr = LockedJob.ValidateAccess();
  3992. if (SUCCEEDED(Hr))
  3993. {
  3994. Hr = LockedJob->AddFileSet( cFileCount, pFileSet );
  3995. }
  3996. LogPublicApiEnd( "cFileCount %u, pFileSet %p", cFileCount, pFileSet );
  3997. return Hr;
  3998. }
  3999. STDMETHODIMP
  4000. CJobExternal::AddFileInternal(
  4001. /* [in] */ LPCWSTR RemoteUrl,
  4002. /* [in] */ LPCWSTR LocalName
  4003. )
  4004. {
  4005. CLockedJobWritePointer LockedJob(pJob);
  4006. LogPublicApiBegin( "RemoteUrl %S, LocalName %S", RemoteUrl, LocalName );
  4007. HRESULT Hr = LockedJob.ValidateAccess();
  4008. if (SUCCEEDED(Hr))
  4009. {
  4010. Hr = LockedJob->AddFile( RemoteUrl, LocalName, true );
  4011. }
  4012. LogPublicApiEnd( "RemoteUrl %S, LocalName %S", RemoteUrl, LocalName );
  4013. return Hr;
  4014. }
  4015. STDMETHODIMP
  4016. CJobExternal::EnumFilesInternal(
  4017. /* [out] */ IEnumBackgroundCopyFiles **ppEnum
  4018. )
  4019. {
  4020. CLockedJobReadPointer LockedJob(pJob);
  4021. LogPublicApiBegin( "ppEnum %p", ppEnum );
  4022. HRESULT Hr = S_OK;
  4023. CEnumFiles *pEnum = NULL;
  4024. try
  4025. {
  4026. *ppEnum = NULL;
  4027. THROW_HRESULT( LockedJob.ValidateAccess());
  4028. pEnum = new CEnumFiles;
  4029. for (CJob::CFileList::const_iterator iter = LockedJob->m_files.begin();
  4030. iter != LockedJob->m_files.end(); ++iter)
  4031. {
  4032. CFileExternal * file = (*iter)->CreateExternalInterface();
  4033. pEnum->Add( file );
  4034. file->Release();
  4035. }
  4036. *ppEnum = pEnum;
  4037. Hr = S_OK;
  4038. }
  4039. catch ( ComError exception )
  4040. {
  4041. Hr = exception.Error();
  4042. SafeRelease( pEnum );
  4043. }
  4044. LogPublicApiEnd( "ppEnum %p(%p)", ppEnum, *ppEnum );
  4045. return Hr;
  4046. }
  4047. STDMETHODIMP
  4048. CJobExternal::SuspendInternal(
  4049. void
  4050. )
  4051. {
  4052. CLockedJobWritePointer LockedJob(pJob);
  4053. LogPublicApiBegin( " " );
  4054. HRESULT Hr = LockedJob.ValidateAccess();
  4055. if (SUCCEEDED(Hr))
  4056. {
  4057. Hr = LockedJob->Suspend();
  4058. }
  4059. LogPublicApiEnd( " " );
  4060. return Hr;
  4061. }
  4062. STDMETHODIMP
  4063. CJobExternal::ResumeInternal(
  4064. void
  4065. )
  4066. {
  4067. CLockedJobWritePointer LockedJob(pJob);
  4068. LogPublicApiBegin( " " );
  4069. HRESULT Hr = LockedJob.ValidateAccess();
  4070. if (SUCCEEDED(Hr))
  4071. {
  4072. Hr = LockedJob->Resume();
  4073. }
  4074. LogPublicApiEnd( " " );
  4075. return Hr;
  4076. }
  4077. STDMETHODIMP
  4078. CJobExternal::CancelInternal(
  4079. void
  4080. )
  4081. {
  4082. CLockedJobWritePointer LockedJob(pJob);
  4083. LogPublicApiBegin( " " );
  4084. HRESULT Hr = LockedJob.ValidateAccess();
  4085. if (SUCCEEDED(Hr))
  4086. {
  4087. Hr = LockedJob->Cancel();
  4088. }
  4089. LogPublicApiEnd( " " );
  4090. return Hr;
  4091. }
  4092. STDMETHODIMP
  4093. CJobExternal::CompleteInternal(
  4094. void
  4095. )
  4096. {
  4097. CLockedJobWritePointer LockedJob(pJob);
  4098. LogPublicApiBegin( " " );
  4099. HRESULT Hr = LockedJob.ValidateAccess();
  4100. if (SUCCEEDED(Hr))
  4101. {
  4102. Hr = LockedJob->Complete();
  4103. }
  4104. LogPublicApiEnd( " " );
  4105. return Hr;
  4106. }
  4107. STDMETHODIMP
  4108. CJobExternal::GetIdInternal(
  4109. /* [out] */ GUID *pVal
  4110. )
  4111. {
  4112. CLockedJobReadPointer LockedJob(pJob);
  4113. LogPublicApiBegin( "GetId pVal %p", pVal );
  4114. HRESULT Hr = LockedJob.ValidateAccess();
  4115. if (SUCCEEDED(Hr))
  4116. {
  4117. *pVal = LockedJob->GetId();
  4118. }
  4119. LogPublicApiEnd( "pVal %p(%!guid!)", pVal, pVal );
  4120. return Hr;
  4121. }
  4122. STDMETHODIMP
  4123. CJobExternal::GetTypeInternal(
  4124. /* [out] */ BG_JOB_TYPE *pVal
  4125. )
  4126. {
  4127. CLockedJobReadPointer LockedJob(pJob);
  4128. LogPublicApiBegin( "pVal %p", pVal );
  4129. HRESULT Hr = LockedJob.ValidateAccess();
  4130. if (SUCCEEDED(Hr))
  4131. {
  4132. *pVal = LockedJob->GetType();
  4133. }
  4134. LogPublicApiEnd( "pVal %p(%u)", pVal, *pVal );
  4135. return Hr;
  4136. }
  4137. STDMETHODIMP
  4138. CJobExternal::GetProgressInternal(
  4139. /* [out] */ BG_JOB_PROGRESS *pVal
  4140. )
  4141. {
  4142. CLockedJobReadPointer LockedJob(pJob);
  4143. LogPublicApiBegin( "pVal %p", pVal );
  4144. HRESULT Hr = LockedJob.ValidateAccess();
  4145. if (SUCCEEDED(Hr))
  4146. {
  4147. LockedJob->GetProgress( pVal );
  4148. }
  4149. LogPublicApiEnd( "pVal %p", pVal );
  4150. return Hr;
  4151. }
  4152. STDMETHODIMP
  4153. CJobExternal::GetTimesInternal(
  4154. /* [out] */ BG_JOB_TIMES *pVal
  4155. )
  4156. {
  4157. CLockedJobReadPointer LockedJob(pJob);
  4158. LogPublicApiBegin( "pVal %p", pVal );
  4159. HRESULT Hr = LockedJob.ValidateAccess();
  4160. if (SUCCEEDED(Hr))
  4161. {
  4162. LockedJob->GetTimes( pVal );
  4163. }
  4164. LogPublicApiEnd( "pVal %p", pVal );
  4165. return Hr;
  4166. }
  4167. STDMETHODIMP
  4168. CJobExternal::GetStateInternal(
  4169. /* [out] */ BG_JOB_STATE *pVal
  4170. )
  4171. {
  4172. CLockedJobReadPointer LockedJob(pJob);
  4173. LogPublicApiBegin( "pVal %p", pVal );
  4174. HRESULT Hr = LockedJob.ValidateAccess();
  4175. if (SUCCEEDED(Hr))
  4176. {
  4177. *pVal = LockedJob->_GetState();
  4178. }
  4179. LogPublicApiEnd( "state %d", *pVal );
  4180. return Hr;
  4181. }
  4182. STDMETHODIMP
  4183. CJobExternal::GetErrorInternal(
  4184. /* [out] */ IBackgroundCopyError **ppError
  4185. )
  4186. {
  4187. CLockedJobReadPointer LockedJob(pJob);
  4188. LogPublicApiBegin( "ppError %p", ppError );
  4189. *ppError = NULL;
  4190. HRESULT Hr = LockedJob.ValidateAccess();
  4191. if (SUCCEEDED(Hr))
  4192. {
  4193. const CJobError *Error = LockedJob->GetError();
  4194. if ( !Error )
  4195. {
  4196. Hr = BG_E_ERROR_INFORMATION_UNAVAILABLE;
  4197. }
  4198. else
  4199. {
  4200. try
  4201. {
  4202. *ppError = new CJobErrorExternal( Error );
  4203. Hr = S_OK;
  4204. }
  4205. catch ( ComError err )
  4206. {
  4207. Hr = err.Error();
  4208. }
  4209. }
  4210. }
  4211. LogPublicApiEnd( "pError %p", *ppError );
  4212. return Hr;
  4213. }
  4214. STDMETHODIMP
  4215. CJobExternal::SetDisplayNameInternal(
  4216. /* [in] */ LPCWSTR Val
  4217. )
  4218. {
  4219. CLockedJobWritePointer LockedJob(pJob);
  4220. LogPublicApiBegin( "Val %S", Val );
  4221. HRESULT Hr = LockedJob.ValidateAccess();
  4222. if (SUCCEEDED(Hr))
  4223. {
  4224. Hr = LockedJob->SetDisplayName( Val );
  4225. }
  4226. LogPublicApiEnd( "Val %S", Val );
  4227. return Hr;
  4228. }
  4229. STDMETHODIMP
  4230. CJobExternal::GetDisplayNameInternal(
  4231. /* [out] */ LPWSTR *pVal
  4232. )
  4233. {
  4234. CLockedJobReadPointer LockedJob(pJob);
  4235. LogPublicApiBegin( "pVal %p", pVal );
  4236. HRESULT Hr = LockedJob.ValidateAccess();
  4237. if (SUCCEEDED(Hr))
  4238. {
  4239. Hr = LockedJob->GetDisplayName( pVal );
  4240. }
  4241. LogPublicApiEnd( "pVal %p(%S)", pVal, SUCCEEDED(Hr) ? *pVal : L"?" );
  4242. return Hr;
  4243. }
  4244. STDMETHODIMP
  4245. CJobExternal::SetDescriptionInternal(
  4246. /* [in] */ LPCWSTR Val
  4247. )
  4248. {
  4249. CLockedJobWritePointer LockedJob(pJob);
  4250. LogPublicApiBegin( "Val %S", Val );
  4251. HRESULT Hr = LockedJob.ValidateAccess();
  4252. if (SUCCEEDED(Hr))
  4253. {
  4254. Hr = LockedJob->SetDescription( Val );
  4255. }
  4256. LogPublicApiEnd( "Val %S", Val );
  4257. return Hr;
  4258. }
  4259. STDMETHODIMP
  4260. CJobExternal::GetDescriptionInternal(
  4261. /* [out] */ LPWSTR *pVal
  4262. )
  4263. {
  4264. CLockedJobReadPointer LockedJob(pJob);
  4265. LogPublicApiBegin("pVal %p", pVal );
  4266. HRESULT Hr = LockedJob.ValidateAccess();
  4267. if (SUCCEEDED(Hr))
  4268. {
  4269. Hr = LockedJob->GetDescription( pVal );
  4270. }
  4271. LogPublicApiEnd("pVal %p(%S)", pVal, SUCCEEDED(Hr) ? *pVal : L"?" );
  4272. return Hr;
  4273. }
  4274. STDMETHODIMP
  4275. CJobExternal::SetPriorityInternal(
  4276. /* [in] */ BG_JOB_PRIORITY Val
  4277. )
  4278. {
  4279. CLockedJobWritePointer LockedJob(pJob);
  4280. LogPublicApiBegin("Val %u", Val);
  4281. HRESULT Hr = LockedJob.ValidateAccess();
  4282. if (SUCCEEDED(Hr))
  4283. {
  4284. Hr = LockedJob->SetPriority( Val );
  4285. }
  4286. LogPublicApiEnd("Val %u", Val );
  4287. return Hr;
  4288. }
  4289. STDMETHODIMP
  4290. CJobExternal::GetPriorityInternal(
  4291. /* [out] */ BG_JOB_PRIORITY *pVal
  4292. )
  4293. {
  4294. CLockedJobReadPointer LockedJob(pJob);
  4295. LogPublicApiBegin( "pVal %p", pVal );
  4296. HRESULT Hr = LockedJob.ValidateAccess();
  4297. if (SUCCEEDED(Hr))
  4298. {
  4299. *pVal = LockedJob->_GetPriority();
  4300. }
  4301. LogPublicApiEnd( "pVal %p(%u)", pVal, *pVal );
  4302. return Hr;
  4303. }
  4304. STDMETHODIMP
  4305. CJobExternal::GetOwnerInternal(
  4306. /* [out] */ LPWSTR *pVal
  4307. )
  4308. {
  4309. CLockedJobReadPointer LockedJob(pJob);
  4310. LogPublicApiBegin( "pVal %p", pVal );
  4311. HRESULT Hr = LockedJob.ValidateAccess();
  4312. if (SUCCEEDED(Hr))
  4313. {
  4314. Hr = LockedJob->GetOwner( pVal );
  4315. }
  4316. LogPublicApiEnd( "pVal %p(%S)", pVal, SUCCEEDED(Hr) ? *pVal : L"?" );
  4317. return Hr;
  4318. }
  4319. STDMETHODIMP
  4320. CJobExternal::SetNotifyFlagsInternal(
  4321. /* [in] */ ULONG Val
  4322. )
  4323. {
  4324. CLockedJobWritePointer LockedJob(pJob);
  4325. LogPublicApiBegin( "Val %u", Val );
  4326. HRESULT Hr = LockedJob.ValidateAccess();
  4327. if (SUCCEEDED(Hr))
  4328. {
  4329. Hr = LockedJob->SetNotifyFlags( Val );
  4330. }
  4331. LogPublicApiEnd( "Val %u", Val );
  4332. return Hr;
  4333. }
  4334. STDMETHODIMP
  4335. CJobExternal::GetNotifyFlagsInternal(
  4336. /* [out] */ ULONG *pVal
  4337. )
  4338. {
  4339. CLockedJobReadPointer LockedJob(pJob);
  4340. LogPublicApiBegin( "pVal %p", pVal );
  4341. HRESULT Hr = LockedJob.ValidateAccess();
  4342. if (SUCCEEDED(Hr))
  4343. {
  4344. *pVal = LockedJob->GetNotifyFlags();
  4345. }
  4346. LogPublicApiEnd( "pVal %p(%u)", pVal, *pVal );
  4347. return Hr;
  4348. }
  4349. STDMETHODIMP
  4350. CJobExternal::SetNotifyInterfaceInternal(
  4351. /* [in] */ IUnknown * Val
  4352. )
  4353. {
  4354. CLockedJobWritePointer LockedJob(pJob);
  4355. LogPublicApiBegin( "Val %p", Val );
  4356. HRESULT Hr = LockedJob.ValidateAccess();
  4357. if (SUCCEEDED(Hr))
  4358. {
  4359. BOOL fValidNotifyInterface = pJob->TestNotifyInterface();
  4360. Hr = pJob->SetNotifyInterface( Val );
  4361. // If there was no previous notification interface (or it's
  4362. // no longer valid) and the job is already in the Transferred
  4363. // state or fatal error state then go ahead and do the callback:
  4364. if ((SUCCEEDED(Hr))&&(Val)&&(!fValidNotifyInterface))
  4365. {
  4366. if (pJob->_GetState() == BG_JOB_STATE_TRANSFERRED)
  4367. {
  4368. pJob->ScheduleCompletionCallback();
  4369. }
  4370. else if (pJob->_GetState() == BG_JOB_STATE_ERROR)
  4371. {
  4372. pJob->ScheduleErrorCallback();
  4373. }
  4374. }
  4375. }
  4376. LogPublicApiEnd( "Val %p", Val );
  4377. return Hr;
  4378. }
  4379. STDMETHODIMP
  4380. CJobExternal::GetNotifyInterfaceInternal(
  4381. /* [out] */ IUnknown ** pVal
  4382. )
  4383. {
  4384. CLockedJobReadPointer LockedJob(pJob);
  4385. LogPublicApiBegin( "pVal %p", pVal );
  4386. HRESULT Hr = LockedJob.ValidateAccess();
  4387. if (SUCCEEDED(Hr))
  4388. {
  4389. Hr = LockedJob->GetNotifyInterface( pVal );
  4390. }
  4391. LogPublicApiEnd( "pVal %p", pVal );
  4392. return Hr;
  4393. }
  4394. STDMETHODIMP
  4395. CJobExternal::SetMinimumRetryDelayInternal(
  4396. /* [in] */ ULONG Seconds
  4397. )
  4398. {
  4399. CLockedJobWritePointer LockedJob(pJob);
  4400. LogPublicApiBegin( "Seconds %u", Seconds );
  4401. HRESULT Hr = LockedJob.ValidateAccess();
  4402. if (SUCCEEDED(Hr))
  4403. {
  4404. Hr = LockedJob->SetMinimumRetryDelay( Seconds );
  4405. }
  4406. LogPublicApiEnd( "Seconds %u", Seconds );
  4407. return Hr;
  4408. }
  4409. STDMETHODIMP
  4410. CJobExternal::GetMinimumRetryDelayInternal(
  4411. /* [out] */ ULONG *Seconds
  4412. )
  4413. {
  4414. CLockedJobReadPointer LockedJob(pJob);
  4415. LogPublicApiBegin( "Seconds %p", Seconds );
  4416. HRESULT Hr = LockedJob.ValidateAccess();
  4417. if (SUCCEEDED(Hr))
  4418. {
  4419. Hr = LockedJob->GetMinimumRetryDelay( Seconds );
  4420. }
  4421. LogPublicApiEnd( "Seconds %p(%u)", Seconds, *Seconds );
  4422. return Hr;
  4423. }
  4424. STDMETHODIMP
  4425. CJobExternal::SetNoProgressTimeoutInternal(
  4426. /* [in] */ ULONG Seconds
  4427. )
  4428. {
  4429. CLockedJobWritePointer LockedJob(pJob);
  4430. LogPublicApiBegin( "Seconds %u", Seconds );
  4431. HRESULT Hr = LockedJob.ValidateAccess();
  4432. if (SUCCEEDED(Hr))
  4433. {
  4434. Hr = LockedJob->SetNoProgressTimeout( Seconds );
  4435. }
  4436. LogPublicApiEnd( "Seconds %u", Seconds );
  4437. return Hr;
  4438. }
  4439. STDMETHODIMP
  4440. CJobExternal::GetNoProgressTimeoutInternal(
  4441. /* [out] */ ULONG *Seconds
  4442. )
  4443. {
  4444. CLockedJobReadPointer LockedJob(pJob);
  4445. LogPublicApiBegin( "Seconds %p", Seconds );
  4446. HRESULT Hr = LockedJob.ValidateAccess();
  4447. if (SUCCEEDED(Hr))
  4448. {
  4449. Hr = LockedJob->GetNoProgressTimeout( Seconds );
  4450. }
  4451. LogPublicApiEnd( "Seconds %p(%u)", Seconds, *Seconds );
  4452. return Hr;
  4453. }
  4454. STDMETHODIMP
  4455. CJobExternal::GetErrorCountInternal(
  4456. /* [out] */ ULONG * Retries
  4457. )
  4458. {
  4459. CLockedJobReadPointer LockedJob(pJob);
  4460. LogPublicApiBegin( "retries %p", Retries );
  4461. HRESULT Hr = LockedJob.ValidateAccess();
  4462. if (SUCCEEDED(Hr))
  4463. {
  4464. Hr = LockedJob->GetErrorCount( Retries );
  4465. }
  4466. LogPublicApiEnd( "retries %p(%u)", Retries, *Retries );
  4467. return Hr;
  4468. }
  4469. STDMETHODIMP
  4470. CJobExternal::SetProxySettingsInternal(
  4471. BG_JOB_PROXY_USAGE ProxyUsage,
  4472. LPCWSTR ProxyList,
  4473. LPCWSTR ProxyBypassList
  4474. )
  4475. {
  4476. CLockedJobWritePointer LockedJob(pJob);
  4477. LogPublicApiBegin( "ProxyUsage %u, ProxyList %S, ProxyBypassList %S",
  4478. ProxyUsage,
  4479. ProxyList ? ProxyList : L"NULL",
  4480. ProxyBypassList ? ProxyBypassList : L"NULL" );
  4481. HRESULT Hr = LockedJob.ValidateAccess();
  4482. if (SUCCEEDED(Hr))
  4483. {
  4484. Hr = LockedJob->SetProxySettings( ProxyUsage, ProxyList, ProxyBypassList );
  4485. }
  4486. LogPublicApiEnd( "ProxyUsage %u, ProxyList %S, ProxyBypassList %S",
  4487. ProxyUsage,
  4488. ProxyList ? ProxyList : L"NULL",
  4489. ProxyBypassList ? ProxyBypassList : L"NULL" );
  4490. return Hr;
  4491. }
  4492. STDMETHODIMP
  4493. CJobExternal::GetProxySettingsInternal(
  4494. BG_JOB_PROXY_USAGE *pProxyUsage,
  4495. LPWSTR *pProxyList,
  4496. LPWSTR *pProxyBypassList
  4497. )
  4498. {
  4499. CLockedJobReadPointer LockedJob(pJob);
  4500. LogPublicApiBegin( "pProxyUsage %p, pProxyList %p, pProxyBypassList %p",
  4501. pProxyUsage, pProxyList, pProxyBypassList );
  4502. HRESULT Hr = LockedJob.ValidateAccess();
  4503. if (SUCCEEDED(Hr))
  4504. {
  4505. Hr = LockedJob->GetProxySettings( pProxyUsage, pProxyList, pProxyBypassList );
  4506. }
  4507. LogPublicApiEnd( "pProxyUsage %p, pProxyList %p, pProxyBypassList %p",
  4508. pProxyUsage, pProxyList, pProxyBypassList );
  4509. return Hr;
  4510. }
  4511. STDMETHODIMP
  4512. CJobExternal::TakeOwnershipInternal()
  4513. {
  4514. LogPublicApiBegin( " " );
  4515. CLockedJobWritePointer LockedJob(pJob);
  4516. HRESULT Hr = LockedJob.ValidateAccess();
  4517. if (SUCCEEDED(Hr))
  4518. {
  4519. Hr = LockedJob->TakeOwnership();
  4520. }
  4521. LogPublicApiEnd( " " );
  4522. return Hr;
  4523. }
  4524. HRESULT STDMETHODCALLTYPE
  4525. CJobExternal::SetNotifyCmdLineInternal(
  4526. LPCWSTR Program,
  4527. LPCWSTR Parameters
  4528. )
  4529. {
  4530. CLockedJobWritePointer LockedJob(pJob);
  4531. LogPublicApiBegin( "program '%S' parms '%S'",
  4532. (Program)?Program : L"?",
  4533. (Parameters)?Parameters : L"?" );
  4534. HRESULT Hr = LockedJob.ValidateAccess();
  4535. if (SUCCEEDED(Hr))
  4536. {
  4537. Hr = LockedJob->SetNotifyCmdLine( Program, Parameters );
  4538. }
  4539. LogPublicApiEnd( " " );
  4540. return Hr;
  4541. }
  4542. HRESULT STDMETHODCALLTYPE
  4543. CJobExternal::GetNotifyCmdLineInternal(
  4544. LPWSTR *pProgram,
  4545. LPWSTR *pParameters
  4546. )
  4547. {
  4548. CLockedJobReadPointer LockedJob(pJob);
  4549. LogPublicApiBegin(" ");
  4550. HRESULT Hr = LockedJob.ValidateAccess();
  4551. if (SUCCEEDED(Hr))
  4552. {
  4553. Hr = LockedJob->GetNotifyCmdLine( pProgram, pParameters );
  4554. }
  4555. LogPublicApiEnd( "program %p(%S), parms %p(%S)",
  4556. pProgram,
  4557. (SUCCEEDED(Hr)&&(*pProgram)) ? *pProgram : L"?",
  4558. pParameters,
  4559. (SUCCEEDED(Hr)&&(*pParameters)) ? *pParameters : L"?"
  4560. );
  4561. return Hr;
  4562. }
  4563. HRESULT STDMETHODCALLTYPE
  4564. CJobExternal::GetReplyProgressInternal(
  4565. BG_JOB_REPLY_PROGRESS *pProgress
  4566. )
  4567. {
  4568. LogPublicApiBegin( " " );
  4569. CLockedJobReadPointer LockedJob(pJob);
  4570. HRESULT Hr = LockedJob.ValidateAccess();
  4571. if (SUCCEEDED(Hr))
  4572. {
  4573. Hr = LockedJob->GetReplyProgress( pProgress );
  4574. }
  4575. LogPublicApiEnd( "%I64d of %I64d transferred", pProgress->BytesTransferred, pProgress->BytesTotal );
  4576. return Hr;
  4577. }
  4578. HRESULT STDMETHODCALLTYPE
  4579. CJobExternal::GetReplyDataInternal(
  4580. byte **ppBuffer,
  4581. UINT64 *pLength
  4582. )
  4583. {
  4584. LogPublicApiBegin( " " );
  4585. CLockedJobReadPointer LockedJob(pJob);
  4586. HRESULT Hr = LockedJob.ValidateAccess();
  4587. if (SUCCEEDED(Hr))
  4588. {
  4589. Hr = LockedJob->GetReplyData( ppBuffer, pLength );
  4590. }
  4591. LogPublicApiEnd( " length %I64d", (pLength) ? *pLength : 0 );
  4592. return Hr;
  4593. }
  4594. HRESULT STDMETHODCALLTYPE
  4595. CJobExternal::SetReplyFileNameInternal(
  4596. LPCWSTR Val
  4597. )
  4598. {
  4599. LogPublicApiBegin( "file '%S'", Val ? Val : L"(null)");
  4600. CLockedJobWritePointer LockedJob(pJob);
  4601. HRESULT Hr = LockedJob.ValidateAccess();
  4602. if (SUCCEEDED(Hr))
  4603. {
  4604. Hr = LockedJob->SetReplyFileName( Val );
  4605. }
  4606. LogPublicApiEnd( " " );
  4607. return Hr;
  4608. }
  4609. HRESULT STDMETHODCALLTYPE
  4610. CJobExternal::GetReplyFileNameInternal(
  4611. LPWSTR *pReplyFileName
  4612. )
  4613. {
  4614. LogPublicApiBegin( " " );
  4615. //
  4616. // This can modify the job, if the reply file name is not yet created.
  4617. //
  4618. CLockedJobReadPointer LockedJob(pJob);
  4619. HRESULT Hr = LockedJob.ValidateAccess();
  4620. if (SUCCEEDED(Hr))
  4621. {
  4622. Hr = LockedJob->GetReplyFileName( pReplyFileName );
  4623. }
  4624. LogPublicApiEnd( "file '%S'", *pReplyFileName ? *pReplyFileName : L"(null)" );
  4625. return Hr;
  4626. }
  4627. HRESULT STDMETHODCALLTYPE
  4628. CJobExternal::SetCredentialsInternal(
  4629. BG_AUTH_CREDENTIALS * Credentials
  4630. )
  4631. {
  4632. LogPublicApiBegin( "cred %p, target %d, scheme %d", Credentials, Credentials->Target, Credentials->Scheme );
  4633. CLockedJobWritePointer LockedJob(pJob);
  4634. HRESULT Hr = LockedJob.ValidateAccess();
  4635. if (SUCCEEDED(Hr))
  4636. {
  4637. Hr = LockedJob->SetCredentials( Credentials );
  4638. }
  4639. LogPublicApiEnd( " " );
  4640. return Hr;
  4641. }
  4642. HRESULT STDMETHODCALLTYPE
  4643. CJobExternal::RemoveCredentialsInternal(
  4644. BG_AUTH_TARGET Target,
  4645. BG_AUTH_SCHEME Scheme
  4646. )
  4647. {
  4648. LogPublicApiBegin( "target %d, scheme %d", Target, Scheme );
  4649. CLockedJobWritePointer LockedJob(pJob);
  4650. HRESULT Hr = LockedJob.ValidateAccess();
  4651. if (SUCCEEDED(Hr))
  4652. {
  4653. Hr = LockedJob->RemoveCredentials( Target, Scheme );
  4654. }
  4655. LogPublicApiEnd( " " );
  4656. return Hr;
  4657. }