Source code of Windows XP (NT5)
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.

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