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

990 lines
26 KiB

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Copyright (C) 1993-1998 Microsoft Corporation. All Rights Reserved.
  3. //
  4. // MODULE: watchtsk.cpp
  5. //
  6. // PURPOSE: Implements the spooler task that is responsible for checking
  7. // for watched messages.
  8. //
  9. #include "pch.hxx"
  10. #include "watchtsk.h"
  11. #include "storutil.h"
  12. #include "storsync.h"
  13. ASSERTDATA
  14. /////////////////////////////////////////////////////////////////////////////
  15. // State Machine dispatch table
  16. //
  17. static const PFNWSTATEFUNC g_rgpfnState[WTS_MAX] =
  18. {
  19. NULL,
  20. NULL,
  21. &CWatchTask::_Watch_Init,
  22. &CWatchTask::_Watch_NextFolder,
  23. NULL,
  24. &CWatchTask::_Watch_Done
  25. };
  26. /////////////////////////////////////////////////////////////////////////////
  27. // Local Data
  28. //
  29. static const TCHAR c_szWatchWndClass[] = "Outlook Express Watch Spooler Task Window";
  30. static const TCHAR c_szThis[] = "this";
  31. //
  32. // FUNCTION: CWatchTask::CWatchTask()
  33. //
  34. // PURPOSE: Initializes the member variables of the object.
  35. //
  36. CWatchTask::CWatchTask()
  37. {
  38. m_cRef = 1;
  39. m_fInited = FALSE;
  40. m_dwFlags = 0;
  41. *m_szAccount = 0;
  42. *m_szAccountId = 0;
  43. m_idAccount = 0;
  44. m_eidCur = 0;
  45. m_pBindCtx = NULL;
  46. m_pUI = NULL;
  47. m_pAccount = NULL;
  48. m_pServer = NULL;
  49. m_pCancel = NULL;
  50. m_idFolderCheck = FOLDERID_INVALID;
  51. m_rgidFolders = 0;
  52. m_cFolders = 0;
  53. m_hwnd = 0;
  54. m_hTimeout = 0;
  55. m_state = WTS_IDLE;
  56. m_fCancel = FALSE;
  57. m_cCurFolder = 0;
  58. m_cFailed = 0;
  59. m_tyOperation = SOT_INVALID;
  60. };
  61. //
  62. // FUNCTION: CWatchTask::~CWatchTask()
  63. //
  64. // PURPOSE: Frees any resources allocated during the life of the class.
  65. //
  66. CWatchTask::~CWatchTask()
  67. {
  68. SafeRelease(m_pBindCtx);
  69. SafeRelease(m_pAccount);
  70. SafeRelease(m_pServer);
  71. SafeRelease(m_pCancel);
  72. SafeMemFree(m_rgidFolders);
  73. // Don't RIP
  74. if (m_hwnd)
  75. DestroyWindow(m_hwnd);
  76. };
  77. /////////////////////////////////////////////////////////////////////////////
  78. // IUnknown
  79. //
  80. HRESULT CWatchTask::QueryInterface(REFIID riid, LPVOID *ppvObj)
  81. {
  82. *ppvObj = NULL;
  83. if (IsEqualIID(riid, IID_IUnknown))
  84. *ppvObj = (LPVOID) (IUnknown *) (ISpoolerTask *) this;
  85. else if (IsEqualIID(riid, IID_ISpoolerTask))
  86. *ppvObj = (LPVOID) (ISpoolerTask *) this;
  87. else if (IsEqualIID(riid, IID_IStoreCallback))
  88. *ppvObj = (LPVOID) (IStoreCallback *) this;
  89. else if (IsEqualIID(riid, IID_ITimeoutCallback))
  90. *ppvObj = (LPVOID) (ITimeoutCallback *) this;
  91. if (NULL == *ppvObj)
  92. return (E_NOINTERFACE);
  93. AddRef();
  94. return S_OK;
  95. }
  96. ULONG CWatchTask::AddRef(void)
  97. {
  98. return InterlockedIncrement((LONG *) &m_cRef);
  99. }
  100. ULONG CWatchTask::Release(void)
  101. {
  102. InterlockedDecrement((LONG *) &m_cRef);
  103. if (0 == m_cRef)
  104. {
  105. delete this;
  106. return (0);
  107. }
  108. return (m_cRef);
  109. }
  110. //
  111. // FUNCTION: CWatchTask::Init()
  112. //
  113. // PURPOSE: Called by the spooler engine to tell us what type of task to
  114. // execute and to provide us with a pointer to our bind context.
  115. //
  116. // PARAMETERS:
  117. // <in> dwFlags - Flags to tell us what types of things to do
  118. // <in> pBindCtx - Pointer to the bind context interface we are to use
  119. //
  120. // RETURN VALUE:
  121. // E_INVALIDARG
  122. // SP_E_ALREADYINITIALIZED
  123. // S_OK
  124. // E_OUTOFMEMORY
  125. //
  126. HRESULT CWatchTask::Init(DWORD dwFlags, ISpoolerBindContext *pBindCtx)
  127. {
  128. HRESULT hr = S_OK;
  129. // Validate the args
  130. if (NULL == pBindCtx)
  131. return (E_INVALIDARG);
  132. // Check to see if we've already been initialized
  133. if (m_fInited)
  134. {
  135. hr = SP_E_ALREADYINITIALIZED;
  136. goto exit;
  137. }
  138. // Copy the flags for later
  139. m_dwFlags = dwFlags;
  140. // Copy the bind context pointer
  141. m_pBindCtx = pBindCtx;
  142. m_pBindCtx->AddRef();
  143. // Register the window class
  144. WNDCLASSEX wc;
  145. wc.cbSize = sizeof(WNDCLASSEX);
  146. if (!GetClassInfoEx(g_hInst, c_szWatchWndClass, &wc))
  147. {
  148. wc.style = 0;
  149. wc.lpfnWndProc = _TaskWndProc;
  150. wc.cbClsExtra = 0;
  151. wc.cbWndExtra = 0;
  152. wc.hInstance = g_hInst;
  153. wc.hCursor = NULL;
  154. wc.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
  155. wc.lpszMenuName = NULL;
  156. wc.lpszClassName = c_szWatchWndClass;
  157. wc.hIcon = NULL;
  158. wc.hIconSm = NULL;
  159. RegisterClassEx(&wc);
  160. }
  161. m_fInited = TRUE;
  162. exit:
  163. return (hr);
  164. }
  165. //
  166. // FUNCTION: CWatchTask::BuildEvents()
  167. //
  168. // PURPOSE: This method is called by the spooler engine telling us to create
  169. // and event list for the account specified.
  170. //
  171. // PARAMETERS:
  172. // <in> pAccount - Account object to build the event list for
  173. //
  174. // RETURN VALUE:
  175. // SP_E_UNINITALIZED
  176. // E_INVALIDARG
  177. // S_OK
  178. //
  179. HRESULT CWatchTask::BuildEvents(ISpoolerUI *pSpoolerUI, IImnAccount *pAccount,
  180. FOLDERID idFolder)
  181. {
  182. HRESULT hr = S_OK;
  183. DWORD dwPoll;
  184. DWORD dw;
  185. // Validate the args
  186. if (pSpoolerUI == NULL || pAccount == NULL)
  187. return (E_INVALIDARG);
  188. // Make sure we've been initialized
  189. if (!m_fInited)
  190. return (SP_E_UNINITIALIZED);
  191. // Figure out which account this is
  192. if (FAILED(hr = pAccount->GetPropSz(AP_ACCOUNT_ID, m_szAccountId, ARRAYSIZE(m_szAccountId))))
  193. goto exit;
  194. // We only do this for accounts that have polling turned on
  195. if (0 == (m_dwFlags & DELIVER_NOSKIP))
  196. {
  197. if (FAILED(hr = pAccount->GetPropDw(AP_NNTP_POLL, &dw)) || dw == 0)
  198. goto exit;
  199. }
  200. if (FAILED(hr = pAccount->GetPropSz(AP_ACCOUNT_NAME, m_szAccount, ARRAYSIZE(m_szAccount))))
  201. goto exit;
  202. // Get the folder ID for this account from the store
  203. if (FAILED(hr = g_pStore->FindServerId(m_szAccountId, &m_idAccount)))
  204. goto exit;
  205. // Hold on to the UI object
  206. m_pUI = pSpoolerUI;
  207. m_pUI->AddRef();
  208. // Also hold on to the account
  209. m_pAccount = pAccount;
  210. m_pAccount->AddRef();
  211. // Also hold on to the folder ID
  212. m_idFolderCheck = idFolder;
  213. // Check to see if any folders that are part of this account have watched
  214. // messages within them.
  215. if (_ChildFoldersHaveWatched(m_idAccount))
  216. {
  217. TCHAR szRes[CCHMAX_STRINGRES], szBuf[CCHMAX_STRINGRES];
  218. EVENTID eid;
  219. // Create the string for the event description
  220. AthLoadString(idsCheckWatchedMessgesServer, szRes, ARRAYSIZE(szRes));
  221. wnsprintf(szBuf, ARRAYSIZE(szBuf), szRes, m_szAccount);
  222. // Insert the event into the spooler
  223. hr = m_pBindCtx->RegisterEvent(szBuf, this, 0, pAccount, &eid);
  224. }
  225. else
  226. {
  227. // Do this so stuff at the end get's released correctly
  228. hr = E_FAIL;
  229. }
  230. exit:
  231. // If we failed, we should clean up all the info we accumulated along
  232. // the way so we don't accidentially think we're initalized later.
  233. if (FAILED(hr))
  234. {
  235. SafeRelease(m_pUI);
  236. SafeRelease(m_pAccount);
  237. SafeMemFree(m_rgidFolders);
  238. *m_szAccountId = 0;
  239. *m_szAccount = 0;
  240. m_idAccount = FOLDERID_INVALID;
  241. }
  242. return (hr);
  243. }
  244. //
  245. // FUNCTION: CWatchTask::Execute()
  246. //
  247. // PURPOSE: Called by the spooler to when it's our turn to run.
  248. //
  249. HRESULT CWatchTask::Execute(EVENTID eid, DWORD_PTR dwTwinkie)
  250. {
  251. TCHAR szRes[CCHMAX_STRINGRES], szBuf[CCHMAX_STRINGRES];
  252. // Double check that we're idle.
  253. Assert(m_state == WTS_IDLE && m_eidCur == NULL);
  254. // Make sure we're initialized
  255. if (FALSE == m_fInited || NULL == m_pUI)
  256. return (SP_E_UNINITIALIZED);
  257. // Copy the event ID
  258. m_eidCur = eid;
  259. // Create our internal window now
  260. if (!m_hwnd)
  261. {
  262. m_hwnd = CreateWindow(c_szWatchWndClass, NULL, WS_POPUP, 10, 10, 10, 10,
  263. GetDesktopWindow(), NULL, g_hInst, this);
  264. }
  265. // Set up the UI to show progress for us
  266. m_pUI->SetProgressRange(1);
  267. AthLoadString(idsInetMailConnectingHost, szRes, ARRAYSIZE(szRes));
  268. wnsprintf(szBuf, ARRAYSIZE(szBuf), szRes, m_szAccount);
  269. m_pUI->SetGeneralProgress(szBuf);
  270. m_pUI->SetAnimation(idanDownloadNews, TRUE);
  271. // Start the state machine
  272. m_state = WTS_INIT;
  273. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  274. return (S_OK);
  275. }
  276. //
  277. // FUNCTION: CWatchTask::CancelEvent()
  278. //
  279. // PURPOSE: Called by the spooler when it needs to free us before
  280. // executing our events. This gives us an opporutnity to free
  281. // our cookie.
  282. //
  283. HRESULT CWatchTask::CancelEvent(EVENTID eid, DWORD_PTR dwTwinkie)
  284. {
  285. // We have no cookie now, so there's nothing to do.
  286. return (S_OK);
  287. }
  288. //
  289. // FUNCTION: CWatchTask::ShowProperties()
  290. //
  291. // PURPOSE: Not Implemented
  292. //
  293. HRESULT CWatchTask::ShowProperties(HWND hwndParent, EVENTID eid, DWORD_PTR dwTwinkie)
  294. {
  295. return (E_NOTIMPL);
  296. }
  297. //
  298. // FUNCTION: CWatchTask::GetExtendedDetails()
  299. //
  300. // PURPOSE: Called by the spooler to get more information about an error
  301. // that has occured.
  302. //
  303. HRESULT CWatchTask::GetExtendedDetails(EVENTID eid, DWORD_PTR dwTwinkie, LPSTR *ppszDetails)
  304. {
  305. return (E_NOTIMPL);
  306. }
  307. //
  308. // FUNCTION: CWatchTask::Cancel()
  309. //
  310. // PURPOSE: Called by the spooler when the user presses the <Cancel> button
  311. // on the spooler dialog.
  312. //
  313. HRESULT CWatchTask::Cancel(void)
  314. {
  315. // This happens if the user cancel's out of the Connect dialog
  316. if (m_state == WTS_IDLE)
  317. return (S_OK);
  318. // Drop the server connection
  319. if (m_pServer)
  320. m_pServer->Close(MSGSVRF_DROP_CONNECTION);
  321. m_fCancel = TRUE;
  322. // Clean up
  323. m_state = WTS_END;
  324. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  325. return (S_OK);
  326. }
  327. //
  328. // FUNCTION: CWatchTask::IsDialogMessage()
  329. //
  330. // PURPOSE: Gives the task an opportunity to see window messages.
  331. //
  332. HRESULT CWatchTask::IsDialogMessage(LPMSG pMsg)
  333. {
  334. return (S_FALSE);
  335. }
  336. //
  337. // FUNCTION: CWatchTask::OnFlagsChanged()
  338. //
  339. // PURPOSE: Called by the spooler to notify us when current stae flags
  340. // have changed (such as visible, background etc)
  341. //
  342. HRESULT CWatchTask::OnFlagsChanged(DWORD dwFlags)
  343. {
  344. m_dwFlags = dwFlags;
  345. return (S_OK);
  346. }
  347. //
  348. // FUNCTION: CWatchTask::OnBegin()
  349. //
  350. // PURPOSE: Called by the server object when it begins some operation we
  351. // requested.
  352. //
  353. HRESULT CWatchTask::OnBegin(STOREOPERATIONTYPE tyOperation, STOREOPERATIONINFO *pOpInfo,
  354. IOperationCancel *pCancel)
  355. {
  356. // Hold on to the operation type
  357. Assert(m_tyOperation == SOT_INVALID);
  358. m_tyOperation = tyOperation;
  359. if (tyOperation == SOT_GET_WATCH_INFO)
  360. m_cMsgs = 0;
  361. // Keep the pointer to the cancel object too
  362. if (pCancel)
  363. {
  364. m_pCancel = pCancel;
  365. m_pCancel->AddRef();
  366. }
  367. return (S_OK);
  368. }
  369. //
  370. // FUNCTION: CWatchTask::OnProgress()
  371. //
  372. // PURPOSE: Called by the server to give us progress on the current operation
  373. //
  374. HRESULT CWatchTask::OnProgress(STOREOPERATIONTYPE tyOperation, DWORD dwCurrent,
  375. DWORD dwMax, LPCSTR pszStatus)
  376. {
  377. // Close any timeout dialog that might be present
  378. CallbackCloseTimeout(&m_hTimeout);
  379. if (tyOperation == SOT_GET_WATCH_INFO)
  380. {
  381. m_cMsgs = dwMax;
  382. }
  383. return (S_OK);
  384. }
  385. //
  386. // FUNCTION: CWatchTask::OnTimeout()
  387. //
  388. // PURPOSE: Get's called when we timeout waiting for a server response. If
  389. // the user has the spooler window visible, we show the timeout
  390. // dialog. If not, we eat it and fail.
  391. //
  392. HRESULT CWatchTask::OnTimeout(LPINETSERVER pServer, LPDWORD pdwTimeout, IXPTYPE ixpServerType)
  393. {
  394. if (!!(m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND)))
  395. return (E_FAIL);
  396. // Display the dialog
  397. return (CallbackOnTimeout(pServer, ixpServerType, *pdwTimeout, (ITimeoutCallback *) this, &m_hTimeout));
  398. }
  399. //
  400. // FUNCTION: CWatchTask::CanConnect()
  401. //
  402. // PURPOSE: Get's called when we need to dial the phone to connect to the
  403. // server. If we have our UI visible, we go ahead and show the UI,
  404. // otherwise we eat it.
  405. //
  406. HRESULT CWatchTask::CanConnect(LPCSTR pszAccountId, DWORD dwFlags)
  407. {
  408. HWND hwnd;
  409. BOOL fPrompt = TRUE;
  410. if (m_pUI)
  411. m_pUI->GetWindow(&hwnd);
  412. else
  413. hwnd = NULL;
  414. // Call into general CanConnect Utility
  415. if ((m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND)) || (dwFlags & CC_FLAG_DONTPROMPT))
  416. fPrompt = FALSE;
  417. return CallbackCanConnect(pszAccountId, hwnd, fPrompt);
  418. }
  419. //
  420. // FUNCTION: CWatchTask::OnLogonPrompt()
  421. //
  422. // PURPOSE: Get's called when we need to prompt the user for their password
  423. // to connect to the server.
  424. //
  425. HRESULT CWatchTask::OnLogonPrompt(LPINETSERVER pServer, IXPTYPE ixpServerType)
  426. {
  427. HWND hwnd;
  428. // Close any timeout dialog, if present
  429. CallbackCloseTimeout(&m_hTimeout);
  430. if (!!(m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND)))
  431. return(S_FALSE);
  432. if (m_pUI)
  433. m_pUI->GetWindow(&hwnd);
  434. else
  435. hwnd = NULL;
  436. // Call into general OnLogonPrompt Utility
  437. return CallbackOnLogonPrompt(hwnd, pServer, ixpServerType);
  438. }
  439. //
  440. // FUNCTION: CWatchTask::OnComplete()
  441. //
  442. // PURPOSE: Called by the server when it completes a requested task.
  443. //
  444. HRESULT CWatchTask::OnComplete(STOREOPERATIONTYPE tyOperation, HRESULT hrComplete,
  445. LPSTOREOPERATIONINFO pOpInfo, LPSTOREERROR pErrorInfo)
  446. {
  447. LPCSTR pszError;
  448. // Close any timeout dialog, if present
  449. CallbackCloseTimeout(&m_hTimeout);
  450. if (m_tyOperation != tyOperation)
  451. return (S_OK);
  452. if (SOT_GET_WATCH_INFO == tyOperation)
  453. {
  454. if (FAILED(hrComplete))
  455. {
  456. // If an error detail was returned, insert that
  457. pszError = pErrorInfo->pszDetails;
  458. if (pszError == NULL || *pszError == 0)
  459. pszError = pErrorInfo->pszProblem;
  460. if (pszError != NULL && *pszError != 0)
  461. m_pUI->InsertError(m_eidCur, pszError);
  462. // Increment the failure count
  463. m_cFailed++;
  464. }
  465. m_pBindCtx->Notify(DELIVERY_NOTIFY_COMPLETE, 0);
  466. m_state = WTS_NEXTFOLDER;
  467. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  468. }
  469. // If the user canceled something just give up.
  470. if (IXP_E_USER_CANCEL == hrComplete)
  471. {
  472. Cancel();
  473. }
  474. // Reset some state information
  475. SafeRelease(m_pCancel);
  476. m_tyOperation = SOT_INVALID;
  477. return (S_OK);
  478. }
  479. //
  480. // FUNCTION: CWatchTask::OnPrompt()
  481. //
  482. // PURPOSE: Called by the server when it needs to do some funky SSL thing.
  483. //
  484. HRESULT CWatchTask::OnPrompt(HRESULT hrError, LPCTSTR pszText, LPCTSTR pszCaption, UINT uType, INT *piUserResponse)
  485. {
  486. HWND hwnd;
  487. // Close any timeout dialog, if present
  488. CallbackCloseTimeout(&m_hTimeout);
  489. // Raid 55082 - SPOOLER: SPA/SSL auth to NNTP does not display cert warning and fails.
  490. #if 0
  491. if (!!(m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND)))
  492. return(E_FAIL);
  493. #endif
  494. if (m_pUI)
  495. m_pUI->GetWindow(&hwnd);
  496. else
  497. hwnd = NULL;
  498. // Call into my swanky utility
  499. return CallbackOnPrompt(hwnd, hrError, pszText, pszCaption, uType, piUserResponse);
  500. }
  501. //
  502. // FUNCTION: CWatchTask::OnPrompt()
  503. //
  504. // PURPOSE: Called by the timeout dialog when the user responds.
  505. //
  506. HRESULT CWatchTask::OnTimeoutResponse(TIMEOUTRESPONSE eResponse)
  507. {
  508. // Call into general timeout response utility
  509. return CallbackOnTimeoutResponse(eResponse, m_pCancel, &m_hTimeout);
  510. }
  511. //
  512. // FUNCTION: CWatchTask::GetParentWindow()
  513. //
  514. // PURPOSE: Called by the server object when it needs to display some sort
  515. // of UI. If we're running in the background, we fail the call.
  516. //
  517. HRESULT CWatchTask::GetParentWindow(DWORD dwReserved, HWND *phwndParent)
  518. {
  519. HRESULT hr;
  520. if (!!(m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND)))
  521. return(E_FAIL);
  522. if (m_pUI)
  523. {
  524. hr = m_pUI->GetWindow(phwndParent);
  525. }
  526. else
  527. {
  528. *phwndParent = NULL;
  529. hr = E_FAIL;
  530. }
  531. return(hr);
  532. }
  533. //
  534. // FUNCTION: CWatchTask::_TaskWndProc()
  535. //
  536. // PURPOSE: Hidden window that processes messages for this task.
  537. //
  538. LRESULT CALLBACK CWatchTask::_TaskWndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
  539. LPARAM lParam)
  540. {
  541. CWatchTask *pThis = (CWatchTask *) GetProp(hwnd, c_szThis);
  542. switch (uMsg)
  543. {
  544. case WM_CREATE:
  545. {
  546. LPCREATESTRUCT pcs = (LPCREATESTRUCT) lParam;
  547. pThis = (CWatchTask *) pcs->lpCreateParams;
  548. SetProp(hwnd, c_szThis, (LPVOID) pThis);
  549. return (0);
  550. }
  551. case NTM_NEXTSTATE:
  552. if (pThis)
  553. {
  554. pThis->AddRef();
  555. pThis->_NextState();
  556. pThis->Release();
  557. }
  558. return (0);
  559. case WM_DESTROY:
  560. RemoveProp(hwnd, c_szThis);
  561. break;
  562. }
  563. return (DefWindowProc(hwnd, uMsg, wParam, lParam));
  564. }
  565. //
  566. // FUNCTION: CWatchTask::_NextState()
  567. //
  568. // PURPOSE: Executes the function for the current state
  569. //
  570. void CWatchTask::_NextState(void)
  571. {
  572. if (NULL != g_rgpfnState[m_state])
  573. (this->*(g_rgpfnState[m_state]))();
  574. }
  575. //
  576. // FUNCTION: CWatchTask::_Watch_Init()
  577. //
  578. // PURPOSE: When we need to start doing our thing. This function creates
  579. // and initializes any objects we need to do our job and starts
  580. // looking at the first group.
  581. //
  582. HRESULT CWatchTask::_Watch_Init(void)
  583. {
  584. FOLDERINFO fi;
  585. HRESULT hr;
  586. // Get information about the server we're checking
  587. if (SUCCEEDED(hr = g_pStore->GetFolderInfo(m_idAccount, &fi)))
  588. {
  589. // With that information, create the server object
  590. hr = CreateMessageServerType(fi.tyFolder, &m_pServer);
  591. g_pStore->FreeRecord(&fi);
  592. if (SUCCEEDED(hr))
  593. {
  594. // Initialize the server object
  595. if (SUCCEEDED(m_pServer->Initialize(g_pLocalStore, m_idAccount,
  596. NULL, FOLDERID_INVALID)))
  597. {
  598. // At this point we have all the information we need. Initialize
  599. // the progress UI.
  600. TCHAR szRes[CCHMAX_STRINGRES], szBuf[CCHMAX_STRINGRES];
  601. AthLoadString(idsCheckingWatchedProgress, szRes, ARRAYSIZE(szRes));
  602. wnsprintf(szBuf, ARRAYSIZE(szBuf), szRes, m_szAccount);
  603. m_pUI->SetGeneralProgress(szBuf);
  604. m_pUI->SetProgressRange((WORD) m_cFolders);
  605. m_pBindCtx->Notify(DELIVERY_NOTIFY_CHECKING_NEWS, 0);
  606. // Go ahead and start with the first folder.
  607. m_cCurFolder = -1;
  608. m_cFailed = 0;
  609. m_state = WTS_NEXTFOLDER;
  610. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  611. return (S_OK);
  612. }
  613. }
  614. }
  615. // If we got here, we didn't succeed in initializing the required stuff.
  616. // We need to log the error and bail.
  617. m_pUI->InsertError(m_eidCur, MAKEINTRESOURCE(idsErrFailedWatchInit));
  618. m_cFailed = m_cFolders;
  619. m_state = WTS_END;
  620. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  621. SafeRelease(m_pServer);
  622. return (E_OUTOFMEMORY);
  623. }
  624. //
  625. // FUNCTION: CWatchTask::_Watch_NextFolder()
  626. //
  627. // PURPOSE: Requests the watched information from the server object for the
  628. // next folder in our list of folders to check.
  629. //
  630. HRESULT CWatchTask::_Watch_NextFolder(void)
  631. {
  632. HRESULT hr = E_FAIL;
  633. FOLDERINFO fi;
  634. TCHAR szRes[CCHMAX_STRINGRES], szBuf[CCHMAX_STRINGRES];
  635. // Loop until we succeed
  636. while (TRUE)
  637. {
  638. m_cCurFolder++;
  639. // Check to see if we've reached the end
  640. if (m_cCurFolder >= m_cFolders)
  641. {
  642. m_state = WTS_END;
  643. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  644. return (S_OK);
  645. }
  646. // Update the progress UI. If we fail to get the folder name, it's not
  647. // fatal, just keep truckin.
  648. if (SUCCEEDED(g_pStore->GetFolderInfo(m_rgidFolders[m_cCurFolder], &fi)))
  649. {
  650. AthLoadString(idsCheckingWatchedFolderProg, szRes, ARRAYSIZE(szRes));
  651. wnsprintf(szBuf, ARRAYSIZE(szBuf), szRes, fi.pszName);
  652. m_pUI->SetSpecificProgress(szBuf);
  653. g_pStore->FreeRecord(&fi);
  654. }
  655. // Open the store folder for this next folder
  656. hr = E_FAIL;
  657. IMessageFolder *pFolder;
  658. hr = g_pStore->OpenFolder(m_rgidFolders[m_cCurFolder], NULL, OPEN_FOLDER_NOCREATE, &pFolder);
  659. if (SUCCEEDED(hr))
  660. {
  661. IServiceProvider *pSP;
  662. if (SUCCEEDED(pFolder->QueryInterface(IID_IServiceProvider, (LPVOID *) &pSP)))
  663. {
  664. IMessageFolder *pFolderReal;
  665. if (SUCCEEDED(hr = pSP->QueryService(SID_LocalMessageFolder, IID_IMessageFolder, (LPVOID *) &pFolderReal)))
  666. {
  667. m_pServer->ResetFolder(pFolderReal, m_rgidFolders[m_cCurFolder]);
  668. // Request the information
  669. hr = m_pServer->GetWatchedInfo(m_rgidFolders[m_cCurFolder], (IStoreCallback *) this);
  670. if (E_PENDING == hr)
  671. {
  672. m_state = WTS_RESP;
  673. }
  674. pFolderReal->Release();
  675. }
  676. pSP->Release();
  677. }
  678. pFolder->Release();
  679. }
  680. if (E_PENDING == hr)
  681. return (S_OK);
  682. }
  683. if (FAILED(hr))
  684. {
  685. // If we got here, something failed
  686. m_cFailed++;
  687. }
  688. m_pUI->IncrementProgress(1);
  689. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  690. return (E_FAIL);
  691. }
  692. //
  693. // FUNCTION: CWatchTask::_Watch_Done()
  694. //
  695. // PURPOSE: Called when we're done getting all our watched stuff. This
  696. // function primarily is used to clean stuff up.
  697. //
  698. HRESULT CWatchTask::_Watch_Done(void)
  699. {
  700. // Tell the spooler we're done
  701. Assert(m_pBindCtx);
  702. m_pBindCtx->Notify(DELIVERY_NOTIFY_COMPLETE, 0);
  703. // Tell the spooler if we failed or not
  704. if (m_fCancel)
  705. {
  706. m_pBindCtx->EventDone(m_eidCur, EVENT_CANCELED);
  707. m_fCancel = FALSE;
  708. }
  709. else if (m_cFailed == m_cFolders)
  710. m_pBindCtx->EventDone(m_eidCur, EVENT_FAILED);
  711. else if (m_cFailed == 0)
  712. m_pBindCtx->EventDone(m_eidCur, EVENT_SUCCEEDED);
  713. else
  714. m_pBindCtx->EventDone(m_eidCur, EVENT_WARNINGS);
  715. if (m_pServer)
  716. m_pServer->Close(MSGSVRF_DROP_CONNECTION | MSGSVRF_HANDS_OFF_SERVER);
  717. SafeRelease(m_pServer);
  718. SafeMemFree(m_rgidFolders);
  719. SafeRelease(m_pAccount);
  720. SafeRelease(m_pUI);
  721. m_cFolders = 0;
  722. m_cFailed = 0;
  723. m_eidCur = 0;
  724. m_state = WTS_IDLE;
  725. return (S_OK);
  726. }
  727. //
  728. // FUNCTION: CWatchTask::_ChildFoldersHaveWatched()
  729. //
  730. // PURPOSE: Checks to see if any of the folders which are a child of the
  731. // given folder have any messages that are being watched.
  732. //
  733. BOOL CWatchTask::_ChildFoldersHaveWatched(FOLDERID id)
  734. {
  735. HRESULT hr = S_OK;
  736. FOLDERID *rgidFolderList = 0;
  737. DWORD dwAllocated;
  738. DWORD dwUsed;
  739. DWORD i;
  740. // If the user want's us to check all folders, get a list of 'em
  741. if (m_idFolderCheck == FOLDERID_INVALID)
  742. {
  743. // Get a list of all the folders which are a child of this folder
  744. hr = FlattenHierarchy(g_pStore, id, FALSE, TRUE, &rgidFolderList, &dwAllocated,
  745. &dwUsed);
  746. if (FAILED(hr))
  747. goto exit;
  748. }
  749. else
  750. {
  751. if (!MemAlloc((LPVOID *) &rgidFolderList, sizeof(FOLDERID) * 1))
  752. return (FALSE);
  753. *rgidFolderList = m_idFolderCheck;
  754. dwUsed = 1;
  755. }
  756. // Check to see if we got any folders back
  757. m_cFolders = 0;
  758. if (dwUsed)
  759. {
  760. // Allocate a new array that will in the end only contain the folders we
  761. // care about.
  762. if (!MemAlloc((LPVOID *) &m_rgidFolders, sizeof(FOLDERID) * dwUsed))
  763. {
  764. hr = E_OUTOFMEMORY;
  765. goto exit;
  766. }
  767. // Initialize the stored list
  768. ZeroMemory(m_rgidFolders, sizeof(FOLDERID) * dwUsed);
  769. m_cFolders = 0;
  770. // Now loop through the array
  771. for (i = 0; i < dwUsed; i++)
  772. {
  773. if (_FolderContainsWatched(rgidFolderList[i]))
  774. {
  775. m_rgidFolders[m_cFolders] = rgidFolderList[i];
  776. m_cFolders++;
  777. }
  778. }
  779. }
  780. exit:
  781. SafeMemFree(rgidFolderList);
  782. if (FAILED(hr))
  783. {
  784. SafeMemFree(m_rgidFolders);
  785. m_cFolders = 0;
  786. }
  787. return (m_cFolders != 0);
  788. }
  789. //
  790. // FUNCTION: CWatchTask::_FolderContainsWatched()
  791. //
  792. // PURPOSE: Checks to see if the specified folder has any messages which are
  793. // being watched.
  794. //
  795. BOOL CWatchTask::_FolderContainsWatched(FOLDERID id)
  796. {
  797. FOLDERINFO rFolderInfo = {0};
  798. BOOL fReturn = FALSE;
  799. // Get the folder info struct
  800. if (SUCCEEDED(g_pStore->GetFolderInfo(id, &rFolderInfo)))
  801. {
  802. fReturn = rFolderInfo.cWatched;
  803. g_pStore->FreeRecord(&rFolderInfo);
  804. }
  805. return (fReturn);
  806. }