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.

1666 lines
45 KiB

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Copyright (C) 1993-1996 Microsoft Corporation. All Rights Reserved.
  3. //
  4. // MODULE: ontask.cpp
  5. //
  6. // PURPOSE: Implements the offline news task.
  7. //
  8. #include "pch.hxx"
  9. #include "resource.h"
  10. #include "ontask.h"
  11. #include "thormsgs.h"
  12. #include "xputil.h"
  13. #include "mimeutil.h"
  14. #include <stdio.h>
  15. #include "strconst.h"
  16. #include <newsstor.h>
  17. #include "ourguid.h"
  18. #include "taskutil.h"
  19. ASSERTDATA
  20. const static char c_szThis[] = "this";
  21. const PFNONSTATEFUNC COfflineTask::m_rgpfnState[ONTS_MAX] =
  22. {
  23. NULL,
  24. NULL,
  25. &COfflineTask::Download_Init,
  26. NULL,
  27. &COfflineTask::Download_AllMsgs,
  28. &COfflineTask::Download_NewMsgs,
  29. &COfflineTask::Download_MarkedMsgs,
  30. &COfflineTask::Download_Done,
  31. };
  32. const PFNARTICLEFUNC COfflineTask::m_rgpfnArticle[ARTICLE_MAX] =
  33. {
  34. &COfflineTask::Article_GetNext,
  35. NULL,
  36. &COfflineTask::Article_Done
  37. };
  38. #define GROUP_DOWNLOAD_FLAGS(flag) (((flag) & FOLDER_DOWNLOADHEADERS) || \
  39. ((flag) & FOLDER_DOWNLOADNEW) || \
  40. ((flag) & FOLDER_DOWNLOADALL))
  41. #define CMSGIDALLOC 512
  42. //
  43. // FUNCTION: COfflineTask::COfflineTask()
  44. //
  45. // PURPOSE: Initializes the member variables of the object.
  46. //
  47. COfflineTask::COfflineTask()
  48. {
  49. m_cRef = 1;
  50. m_fInited = FALSE;
  51. m_dwFlags = 0;
  52. m_state = ONTS_IDLE;
  53. m_eidCur = 0;
  54. m_pInfo = NULL;
  55. m_szAccount[0] = 0;
  56. m_cEvents = 0;
  57. m_fDownloadErrors = FALSE;
  58. m_fFailed = FALSE;
  59. m_fNewHeaders = FALSE;
  60. m_fCancel = FALSE;
  61. m_pBindCtx = NULL;
  62. m_pUI = NULL;
  63. m_pFolder = NULL;
  64. m_hwnd = 0;
  65. m_dwLast = 0;
  66. m_dwPrev = 0;
  67. m_cDownloaded = 0;
  68. m_dwPrevHigh = 0;
  69. m_dwNewInboxMsgs = 0;
  70. m_pList = NULL;
  71. m_pCancel = NULL;
  72. m_hTimeout = NULL;
  73. m_tyOperation = SOT_INVALID;
  74. }
  75. //
  76. // FUNCTION: COfflineTask::~COfflineTask()
  77. //
  78. // PURPOSE: Frees any resources allocated during the life of the class.
  79. //
  80. COfflineTask::~COfflineTask()
  81. {
  82. DestroyWindow(m_hwnd);
  83. SafeMemFree(m_pInfo);
  84. SafeMemFree(m_pList);
  85. SafeRelease(m_pBindCtx);
  86. SafeRelease(m_pUI);
  87. CallbackCloseTimeout(&m_hTimeout);
  88. SafeRelease(m_pCancel);
  89. if (m_pFolder)
  90. {
  91. m_pFolder->Close();
  92. SideAssert(0 == m_pFolder->Release());
  93. }
  94. }
  95. HRESULT COfflineTask::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
  96. {
  97. if (NULL == *ppvObj)
  98. return (E_INVALIDARG);
  99. *ppvObj = NULL;
  100. if (IsEqualIID(riid, IID_IUnknown))
  101. *ppvObj = (LPVOID)(ISpoolerTask *) this;
  102. else if (IsEqualIID(riid, IID_ISpoolerTask))
  103. *ppvObj = (LPVOID)(ISpoolerTask *) this;
  104. if (NULL == *ppvObj)
  105. return (E_NOINTERFACE);
  106. AddRef();
  107. return (S_OK);
  108. }
  109. ULONG COfflineTask::AddRef(void)
  110. {
  111. ULONG cRefT;
  112. cRefT = ++m_cRef;
  113. return (cRefT);
  114. }
  115. ULONG COfflineTask::Release(void)
  116. {
  117. ULONG cRefT;
  118. cRefT = --m_cRef;
  119. if (0 == cRefT)
  120. delete this;
  121. return (cRefT);
  122. }
  123. static const char c_szOfflineTask[] = "Offline Task";
  124. //
  125. // FUNCTION: COfflineTask::Init()
  126. //
  127. // PURPOSE: Called by the spooler engine to tell us what type of task to
  128. // execute and to provide us with a pointer to our bind context.
  129. //
  130. // PARAMETERS:
  131. // <in> dwFlags - Flags to tell us what types of things to do
  132. // <in> pBindCtx - Pointer to the bind context interface we are to use
  133. //
  134. // RETURN VALUE:
  135. // E_INVALIDARG
  136. // SP_E_ALREADYINITIALIZED
  137. // S_OK
  138. // E_OUTOFMEMORY
  139. //
  140. HRESULT COfflineTask::Init(DWORD dwFlags, ISpoolerBindContext *pBindCtx)
  141. {
  142. // Validate the arguments
  143. Assert(pBindCtx != NULL);
  144. // Check to see if we've been initialzed already
  145. Assert(!m_fInited);
  146. // Copy the flags
  147. m_dwFlags = dwFlags;
  148. // Copy the bind context pointer
  149. m_pBindCtx = pBindCtx;
  150. m_pBindCtx->AddRef();
  151. // Create the window
  152. WNDCLASSEX wc;
  153. wc.cbSize = sizeof(WNDCLASSEX);
  154. if (!GetClassInfoEx(g_hInst, c_szOfflineTask, &wc))
  155. {
  156. wc.style = 0;
  157. wc.lpfnWndProc = TaskWndProc;
  158. wc.cbClsExtra = 0;
  159. wc.cbWndExtra = 0;
  160. wc.hInstance = g_hInst;
  161. wc.hCursor = NULL;
  162. wc.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
  163. wc.lpszMenuName = NULL;
  164. wc.lpszClassName = c_szOfflineTask;
  165. wc.hIcon = NULL;
  166. wc.hIconSm = NULL;
  167. RegisterClassEx(&wc);
  168. }
  169. m_hwnd = CreateWindow(c_szOfflineTask, NULL, WS_POPUP, 10, 10, 10, 10,
  170. GetDesktopWindow(), NULL, g_hInst, this);
  171. if (!m_hwnd)
  172. return(E_OUTOFMEMORY);
  173. m_fInited = TRUE;
  174. return(S_OK);
  175. }
  176. //
  177. // FUNCTION: COfflineTask::BuildEvents()
  178. //
  179. // PURPOSE: This method is called by the spooler engine telling us to create
  180. // and event list for the account specified.
  181. //
  182. // PARAMETERS:
  183. // <in> pAccount - Account object to build the event list for
  184. //
  185. // RETURN VALUE:
  186. // SP_E_UNINITALIZED
  187. // E_INVALIDARG
  188. // S_OK
  189. //
  190. HRESULT COfflineTask::BuildEvents(ISpoolerUI *pSpoolerUI, IImnAccount *pAccount, FOLDERID idFolder)
  191. {
  192. HRESULT hr;
  193. // Validate the arguments
  194. Assert(pAccount != NULL);
  195. Assert(pSpoolerUI != NULL);
  196. // Check to see if we've been initalized
  197. Assert(m_fInited);
  198. // Get the account name from the account object
  199. if (FAILED(hr = pAccount->GetPropSz(AP_ACCOUNT_NAME, m_szAccount, ARRAYSIZE(m_szAccount))))
  200. return(hr);
  201. // Get the account name from the account object
  202. if (FAILED(hr = pAccount->GetPropSz(AP_ACCOUNT_ID, m_szAccountId, ARRAYSIZE(m_szAccountId))))
  203. return(hr);
  204. if (FAILED(hr = g_pStore->FindServerId(m_szAccountId, &m_idAccount)))
  205. return(hr);
  206. // Copy the UI object
  207. m_pUI = pSpoolerUI;
  208. m_pUI->AddRef();
  209. hr = InsertGroups(pAccount, idFolder);
  210. return(hr);
  211. }
  212. //
  213. // FUNCTION: COfflineTask::InsertGroups()
  214. //
  215. // PURPOSE: Scans the specified account for groups that have an update
  216. // property or marked messages.
  217. //
  218. // PARAMETERS:
  219. // <in> szAccount - Name of the account to check
  220. // <in> pAccount - Pointer to the IImnAccount object for szAccount
  221. //
  222. // RETURN VALUE:
  223. // S_OK
  224. // E_OUTOFMEMORY
  225. //
  226. HRESULT COfflineTask::InsertGroups(IImnAccount *pAccount, FOLDERID idFolder)
  227. {
  228. FOLDERINFO info = { 0 };
  229. HRESULT hr = S_OK;
  230. DWORD dwFlags = 0;
  231. DWORD ids;
  232. TCHAR szRes[CCHMAX_STRINGRES], szBuf[CCHMAX_STRINGRES];
  233. EVENTID eid;
  234. ONEVENTINFO *pei = NULL;
  235. BOOL fIMAP = FALSE;
  236. DWORD dwServerFlags;
  237. // Figure out if this is NNTP or IMAP
  238. if (SUCCEEDED(pAccount->GetServerTypes(&dwServerFlags)) && (dwServerFlags & (SRV_IMAP | SRV_HTTPMAIL)))
  239. fIMAP = TRUE;
  240. if (FOLDERID_INVALID != idFolder)
  241. {
  242. // Fill Folder
  243. hr = g_pStore->GetFolderInfo(idFolder, &info);
  244. if (FAILED(hr))
  245. return hr;
  246. // Figure out what we're downloading
  247. ids = 0;
  248. if (m_dwFlags & DELIVER_OFFLINE_HEADERS)
  249. {
  250. dwFlags = FOLDER_DOWNLOADHEADERS;
  251. if (m_dwFlags & DELIVER_OFFLINE_MARKED)
  252. ids = idsDLHeadersAndMarked;
  253. else
  254. ids = idsDLHeaders;
  255. }
  256. else if (m_dwFlags & DELIVER_OFFLINE_NEW)
  257. {
  258. dwFlags = FOLDER_DOWNLOADNEW;
  259. if (m_dwFlags & DELIVER_OFFLINE_MARKED)
  260. ids = idsDLNewMsgsAndMarked;
  261. else
  262. ids = idsDLNewMsgs;
  263. }
  264. else if (m_dwFlags & DELIVER_OFFLINE_ALL)
  265. {
  266. dwFlags = FOLDER_DOWNLOADALL;
  267. ids = idsDLAllMsgs;
  268. }
  269. else if (m_dwFlags & DELIVER_OFFLINE_MARKED)
  270. {
  271. ids = idsDLMarkedMsgs;
  272. }
  273. // Create the event description
  274. Assert(ids);
  275. AthLoadString(ids, szRes, ARRAYSIZE(szRes));
  276. wnsprintf(szBuf, ARRAYSIZE(szBuf), szRes, info.pszName);
  277. // Allocate a structure to save as our twinkie
  278. if (!MemAlloc((LPVOID *) &pei, sizeof(ONEVENTINFO)))
  279. {
  280. g_pStore->FreeRecord(&info);
  281. return(E_OUTOFMEMORY);
  282. }
  283. StrCpyN(pei->szGroup, info.pszName, ARRAYSIZE(pei->szGroup));
  284. pei->idGroup = info.idFolder;
  285. pei->dwFlags = dwFlags;
  286. pei->fMarked = m_dwFlags & DELIVER_OFFLINE_MARKED;
  287. pei->fIMAP = fIMAP;
  288. // Insert the event into the spooler
  289. hr = m_pBindCtx->RegisterEvent(szBuf, this, (DWORD_PTR) pei, pAccount, &eid);
  290. if (SUCCEEDED(hr))
  291. m_cEvents++;
  292. g_pStore->FreeRecord(&info);
  293. }
  294. else
  295. {
  296. //Either Sync All or Send & Receive
  297. Assert(m_idAccount != FOLDERID_INVALID);
  298. BOOL fInclude = FALSE;
  299. if (!(m_dwFlags & DELIVER_OFFLINE_SYNC) && !(m_dwFlags & DELIVER_NOSKIP))
  300. {
  301. DWORD dw;
  302. if (dwServerFlags & SRV_IMAP)
  303. {
  304. if (SUCCEEDED(pAccount->GetPropDw(AP_IMAP_POLL, &dw)) && dw)
  305. {
  306. fInclude = TRUE;
  307. }
  308. }
  309. else
  310. {
  311. if (dwServerFlags & SRV_HTTPMAIL)
  312. {
  313. if (SUCCEEDED(pAccount->GetPropDw(AP_HTTPMAIL_POLL, &dw)) && dw)
  314. {
  315. fInclude = TRUE;
  316. }
  317. }
  318. }
  319. }
  320. else
  321. fInclude = TRUE;
  322. if (fInclude)
  323. hr = InsertAllGroups(m_idAccount, pAccount, fIMAP);
  324. }
  325. return (hr);
  326. }
  327. HRESULT COfflineTask::InsertAllGroups(FOLDERID idParent, IImnAccount *pAccount, BOOL fIMAP)
  328. {
  329. FOLDERINFO info = { 0 };
  330. IEnumerateFolders *pEnum = NULL;
  331. HRESULT hr = S_OK;
  332. DWORD dwFlags = 0;
  333. BOOL fMarked;
  334. DWORD ids;
  335. TCHAR szRes[CCHMAX_STRINGRES], szBuf[CCHMAX_STRINGRES];
  336. EVENTID eid;
  337. ONEVENTINFO *pei = NULL;
  338. BOOL fSubscribedOnly = TRUE;
  339. if (fIMAP)
  340. fSubscribedOnly = FALSE;
  341. Assert(idParent != FOLDERID_INVALID);
  342. hr = g_pStore->EnumChildren(idParent, fSubscribedOnly, &pEnum);
  343. if (FAILED(hr))
  344. return(hr);
  345. // Walk the list of groups and add them to the queue as necessary
  346. while (S_OK == pEnum->Next(1, &info, NULL))
  347. {
  348. // If the download flags are set for this group, insert it
  349. dwFlags = info.dwFlags;
  350. HasMarkedMsgs(info.idFolder, &fMarked);
  351. if (GROUP_DOWNLOAD_FLAGS(dwFlags) || fMarked)
  352. {
  353. // Figure out what we're downloading
  354. ids = 0;
  355. if (dwFlags & FOLDER_DOWNLOADHEADERS)
  356. {
  357. if (fMarked)
  358. ids = idsDLHeadersAndMarked;
  359. else
  360. ids = idsDLHeaders;
  361. }
  362. else if (dwFlags & FOLDER_DOWNLOADNEW)
  363. {
  364. if (fMarked)
  365. ids = idsDLNewMsgsAndMarked;
  366. else
  367. ids = idsDLNewMsgs;
  368. }
  369. else if (dwFlags & FOLDER_DOWNLOADALL)
  370. {
  371. ids = idsDLAllMsgs;
  372. }
  373. else if (fMarked)
  374. {
  375. ids = idsDLMarkedMsgs;
  376. }
  377. // Create the event description
  378. Assert(ids);
  379. AthLoadString(ids, szRes, ARRAYSIZE(szRes));
  380. wnsprintf(szBuf, ARRAYSIZE(szBuf), szRes, info.pszName);
  381. // Allocate a structure to save as our twinkie
  382. if (!MemAlloc((LPVOID *) &pei, sizeof(ONEVENTINFO)))
  383. {
  384. g_pStore->FreeRecord(&info);
  385. hr = E_OUTOFMEMORY;
  386. break;
  387. }
  388. StrCpyN(pei->szGroup, info.pszName, ARRAYSIZE(pei->szGroup));
  389. pei->idGroup = info.idFolder;
  390. pei->dwFlags = dwFlags;
  391. pei->fMarked = fMarked;
  392. pei->fIMAP = fIMAP;
  393. // Insert the event into the spooler
  394. hr = m_pBindCtx->RegisterEvent(szBuf, this, (DWORD_PTR) pei, pAccount, &eid);
  395. if (FAILED(hr))
  396. {
  397. g_pStore->FreeRecord(&info);
  398. break;
  399. }
  400. m_cEvents++;
  401. }
  402. // Recurse on any children
  403. if (info.dwFlags & FOLDER_HASCHILDREN)
  404. {
  405. hr = InsertAllGroups(info.idFolder, pAccount, fIMAP);
  406. if (FAILED(hr))
  407. break;
  408. }
  409. g_pStore->FreeRecord(&info);
  410. }
  411. pEnum->Release();
  412. return hr;
  413. }
  414. //
  415. // FUNCTION: COfflineTask::Execute()
  416. //
  417. // PURPOSE: This signals our task to start executing an event.
  418. //
  419. // PARAMETERS:
  420. // <in> pSpoolerUI - Pointer of the UI object we'll display progress through
  421. // <in> eid - ID of the event to execute
  422. // <in> dwTwinkie - Our extra information we associated with the event
  423. //
  424. // RETURN VALUE:
  425. // SP_E_EXECUTING
  426. // S_OK
  427. // E_INVALIDARG
  428. // SP_E_UNINITIALIZED
  429. //
  430. HRESULT COfflineTask::Execute(EVENTID eid, DWORD_PTR dwTwinkie)
  431. {
  432. // Make sure we're already idle
  433. Assert(m_state == ONTS_IDLE)
  434. // Make sure we're initialized
  435. Assert(m_fInited);
  436. Assert(m_pInfo == NULL);
  437. // Copy the event id and event info
  438. m_eidCur = eid;
  439. m_pInfo = (ONEVENTINFO *) dwTwinkie;
  440. // Forget UI stuff if we're just going to cancel everything
  441. if (FALSE == m_fCancel)
  442. {
  443. // Update the event UI to an executing state
  444. Assert(m_pUI);
  445. m_pUI->UpdateEventState(m_eidCur, -1, NULL, MAKEINTRESOURCE(idsStateExecuting));
  446. m_pUI->SetProgressRange(1);
  447. // Set up the progress
  448. SetGeneralProgress((LPSTR)idsInetMailConnectingHost, m_szAccount);
  449. if (m_pInfo->fIMAP)
  450. m_pUI->SetAnimation(idanInbox, TRUE);
  451. else
  452. m_pUI->SetAnimation(idanDownloadNews, TRUE);
  453. }
  454. m_state = ONTS_INIT;
  455. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  456. return(S_OK);
  457. }
  458. HRESULT COfflineTask::CancelEvent(EVENTID eid, DWORD_PTR dwTwinkie)
  459. {
  460. // Make sure we're initialized
  461. Assert(m_fInited);
  462. Assert(dwTwinkie != 0);
  463. MemFree((ONEVENTINFO *)dwTwinkie);
  464. return(S_OK);
  465. }
  466. //
  467. // FUNCTION: <???>
  468. //
  469. // PURPOSE: <???>
  470. //
  471. // PARAMETERS:
  472. // <???>
  473. //
  474. // RETURN VALUE:
  475. // <???>
  476. //
  477. // COMMENTS:
  478. // <???>
  479. //
  480. HRESULT COfflineTask::ShowProperties(HWND hwndParent, EVENTID eid, DWORD_PTR dwTwinkie)
  481. {
  482. return (E_NOTIMPL);
  483. }
  484. //
  485. // FUNCTION: <???>
  486. //
  487. // PURPOSE: <???>
  488. //
  489. // PARAMETERS:
  490. // <???>
  491. //
  492. // RETURN VALUE:
  493. // <???>
  494. //
  495. // COMMENTS:
  496. // <???>
  497. //
  498. HRESULT COfflineTask::GetExtendedDetails(EVENTID eid, DWORD_PTR dwTwinkie,
  499. LPSTR *ppszDetails)
  500. {
  501. return (E_NOTIMPL);
  502. }
  503. //
  504. // FUNCTION: <???>
  505. //
  506. // PURPOSE: <???>
  507. //
  508. // PARAMETERS:
  509. // <???>
  510. //
  511. // RETURN VALUE:
  512. // <???>
  513. //
  514. // COMMENTS:
  515. // <???>
  516. //
  517. HRESULT COfflineTask::Cancel(void)
  518. {
  519. Assert(m_state != ONTS_IDLE);
  520. m_fCancel = TRUE;
  521. m_state = ONTS_END;
  522. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  523. return (S_OK);
  524. }
  525. //
  526. // FUNCTION: COfflineTask::TaskWndProc()
  527. //
  528. // PURPOSE: Hidden window that processes messages for this task.
  529. //
  530. LRESULT CALLBACK COfflineTask::TaskWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  531. {
  532. COfflineTask *pThis = (COfflineTask *) GetProp(hwnd, c_szThis);
  533. switch (uMsg)
  534. {
  535. case WM_CREATE:
  536. {
  537. LPCREATESTRUCT pcs = (LPCREATESTRUCT) lParam;
  538. pThis = (COfflineTask *) pcs->lpCreateParams;
  539. SetProp(hwnd, c_szThis, (LPVOID) pThis);
  540. return (0);
  541. }
  542. case NTM_NEXTSTATE:
  543. if (pThis)
  544. {
  545. pThis->AddRef();
  546. pThis->NextState();
  547. pThis->Release();
  548. }
  549. return (0);
  550. case NTM_NEXTARTICLESTATE:
  551. if (pThis)
  552. {
  553. pThis->AddRef();
  554. if (m_rgpfnArticle[pThis->m_as])
  555. (pThis->*(m_rgpfnArticle[pThis->m_as]))();
  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: COfflineTask::NextState()
  567. //
  568. // PURPOSE: Executes the function for the current state
  569. //
  570. void COfflineTask::NextState(void)
  571. {
  572. if (m_fCancel)
  573. m_state = ONTS_END;
  574. if (NULL != m_rgpfnState[m_state])
  575. (this->*(m_rgpfnState[m_state]))();
  576. }
  577. //
  578. // FUNCTION: COfflineTask::Download_Init()
  579. //
  580. // PURPOSE: Does the initialization needed to download headers and messages
  581. // for a particular newsgroup.
  582. //
  583. HRESULT COfflineTask::Download_Init(void)
  584. {
  585. HRESULT hr;
  586. SYNCFOLDERFLAGS flags = SYNC_FOLDER_DEFAULT;
  587. FOLDERINFO info;
  588. Assert(m_pFolder == NULL);
  589. Assert(0 == flags); // If this isn't 0, please verify correctness
  590. hr = g_pStore->OpenFolder(m_pInfo->idGroup, NULL, NOFLAGS, &m_pFolder);
  591. if (FAILED(hr))
  592. {
  593. goto Failure;
  594. }
  595. Assert(m_pFolder != NULL);
  596. hr = g_pStore->GetFolderInfo(m_pInfo->idGroup, &info);
  597. if (FAILED(hr))
  598. {
  599. goto Failure;
  600. }
  601. if (m_pInfo->fIMAP)
  602. {
  603. // Get highest Msg ID the brute-force way (IMAP doesn't set dwClientHigh)
  604. GetHighestCachedMsgID(m_pFolder, &m_dwPrevHigh);
  605. }
  606. else
  607. m_dwPrevHigh = info.dwClientHigh;
  608. g_pStore->FreeRecord(&info);
  609. // Update the UI to an executing state
  610. Assert(m_pUI);
  611. m_pUI->UpdateEventState(m_eidCur, -1, NULL, MAKEINTRESOURCE(idsStateExecuting));
  612. m_fDownloadErrors = FALSE;
  613. // Check to see if the user wants us to download new headers
  614. if (GROUP_DOWNLOAD_FLAGS(m_pInfo->dwFlags))
  615. {
  616. if (!(m_pInfo->dwFlags & FOLDER_DOWNLOADALL) || m_pInfo->fIMAP)
  617. flags = SYNC_FOLDER_NEW_HEADERS | SYNC_FOLDER_CACHED_HEADERS;
  618. else
  619. flags = SYNC_FOLDER_ALLFLAGS;
  620. // Update Progress
  621. SetGeneralProgress((LPSTR)idsLogCheckingNewMessages, m_pInfo->szGroup);
  622. }
  623. else
  624. {
  625. m_state = ONTS_ALLMSGS;
  626. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  627. return(S_OK);
  628. }
  629. // Before we download any headers, we need to make a note of what the current
  630. // server high is so we know which articles are new.
  631. hr = m_pFolder->Synchronize(flags, 0, (IStoreCallback *)this);
  632. Assert(hr != S_OK);
  633. if (hr == E_PENDING)
  634. hr = S_OK;
  635. if (m_pInfo->fIMAP)
  636. {
  637. m_pUI->SetAnimation(idanInbox, TRUE);
  638. m_pBindCtx->Notify(DELIVERY_NOTIFY_RECEIVING, 0);
  639. }
  640. else
  641. {
  642. m_pUI->SetAnimation(idanDownloadNews, TRUE);
  643. m_pBindCtx->Notify(DELIVERY_NOTIFY_RECEIVING_NEWS, 0);
  644. }
  645. Failure:
  646. if (FAILED(hr))
  647. {
  648. // $$$$BUGBUG$$$$
  649. InsertError((LPSTR)idsLogErrorSwitchGroup, m_pInfo->szGroup, m_szAccount);
  650. m_fFailed = TRUE;
  651. m_state = ONTS_END;
  652. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  653. }
  654. return (hr);
  655. }
  656. //
  657. // FUNCTION: COfflineTask::Download_AllMsgs()
  658. //
  659. // PURPOSE:
  660. //
  661. //
  662. HRESULT COfflineTask::Download_AllMsgs(void)
  663. {
  664. HRESULT hr;
  665. DWORD cMsgs, cMsgsBuf;
  666. LPMESSAGEID pMsgId;
  667. MESSAGEIDLIST list;
  668. MESSAGEINFO MsgInfo = {0};
  669. HROWSET hRowset = NULL;
  670. // Check to see if we even want to download all messages
  671. if (!(m_pInfo->dwFlags & FOLDER_DOWNLOADALL))
  672. {
  673. m_state = ONTS_NEWMSGS;
  674. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  675. return(S_OK);
  676. }
  677. // We need to determine a list of messages to download. What we're looking
  678. // to do is download all of the messages that we know about which are unread.
  679. // To do this, we need to find the intersection of the unread range list and
  680. // the known range list.
  681. // Create a Rowset
  682. hr = m_pFolder->CreateRowset(IINDEX_PRIMARY, 0, &hRowset);
  683. if (FAILED(hr))
  684. {
  685. goto Failure;
  686. }
  687. cMsgs = 0;
  688. cMsgsBuf = 0;
  689. pMsgId = NULL;
  690. // Get the first message
  691. while (S_OK == m_pFolder->QueryRowset(hRowset, 1, (void **)&MsgInfo, NULL))
  692. {
  693. if (0 == (MsgInfo.dwFlags & ARF_HASBODY) && 0 == (MsgInfo.dwFlags & ARF_IGNORE))
  694. {
  695. if (cMsgs == cMsgsBuf)
  696. {
  697. if (!MemRealloc((void **)&pMsgId, (cMsgsBuf + CMSGIDALLOC) * sizeof(MESSAGEID)))
  698. {
  699. m_pFolder->FreeRecord(&MsgInfo);
  700. hr = E_OUTOFMEMORY;
  701. break;
  702. }
  703. cMsgsBuf += CMSGIDALLOC;
  704. }
  705. pMsgId[cMsgs] = MsgInfo.idMessage;
  706. cMsgs++;
  707. }
  708. // Free the header info
  709. m_pFolder->FreeRecord(&MsgInfo);
  710. }
  711. // Release Lock
  712. m_pFolder->CloseRowset(&hRowset);
  713. // TODO: error handling
  714. Assert(!FAILED(hr));
  715. // Check to see if we found anything
  716. if (cMsgs == 0)
  717. {
  718. // Nothing to download. We should move on to the marked download
  719. // state.
  720. Assert(pMsgId == NULL);
  721. m_state = ONTS_MARKEDMSGS;
  722. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  723. return(S_OK);
  724. }
  725. // Update the general progress
  726. SetGeneralProgress((LPSTR)idsLogStartDownloadAll, m_pInfo->szGroup);
  727. list.cAllocated = 0;
  728. list.cMsgs = cMsgs;
  729. list.prgidMsg = pMsgId;
  730. // Ask for the first article
  731. hr = Article_Init(&list);
  732. if (pMsgId != NULL)
  733. MemFree(pMsgId);
  734. Failure:
  735. if (FAILED(hr))
  736. {
  737. // $$$$BUGBUG$$$$
  738. InsertError((LPSTR)idsLogErrorSwitchGroup, m_pInfo->szGroup, m_szAccount);
  739. m_fFailed = TRUE;
  740. m_state = ONTS_END;
  741. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  742. }
  743. return (hr);
  744. }
  745. //
  746. // FUNCTION: COfflineTask::Download_NewMsgs()
  747. //
  748. // PURPOSE: This function determines if there are any new messages to be
  749. // downloaded. If so, it creates a list of message numbers that
  750. // need to be downloaded.
  751. //
  752. HRESULT COfflineTask::Download_NewMsgs(void)
  753. {
  754. HRESULT hr;
  755. ROWORDINAL iRow = 0;
  756. BOOL fFound;
  757. HROWSET hRowset;
  758. DWORD cMsgs, cMsgsBuf;
  759. LPMESSAGEID pMsgId;
  760. MESSAGEIDLIST list;
  761. MESSAGEINFO Message = {0};
  762. // Check to see if there are even new messages to download
  763. // Check to see if we even want to download all messages
  764. if (!(m_pInfo->dwFlags & FOLDER_DOWNLOADNEW) || !m_fNewHeaders)
  765. {
  766. // Move the next state
  767. m_state = ONTS_MARKEDMSGS;
  768. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  769. return(S_OK);
  770. }
  771. // We've got new messages, build a range list of those message numbers.
  772. // This range list is essentially every number in the known range above
  773. // m_dwPrevHigh.
  774. hr = S_OK;
  775. cMsgs = 0;
  776. cMsgsBuf = 0;
  777. pMsgId = NULL;
  778. fFound = FALSE;
  779. // TODO: this method of figuring out if there are new msgs isn't going to work all
  780. // the time. if the previous high is removed from the store during syncing (cancelled
  781. // news post, deleted msg, expired news post, etc) and new headers are downloaded,
  782. // we won't pull down the new msgs. we need a better way of detecting new hdrs and
  783. // pulling down there bodies
  784. if (m_dwPrevHigh > 0)
  785. {
  786. Message.idMessage = (MESSAGEID)m_dwPrevHigh;
  787. // Find This Record. If this fails, we go ahead and do a full scan which is less
  788. // efficient, but OK.
  789. if (DB_S_FOUND == m_pFolder->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, &iRow))
  790. {
  791. m_pFolder->FreeRecord(&Message);
  792. }
  793. }
  794. hr = m_pFolder->CreateRowset(IINDEX_PRIMARY, 0, &hRowset);
  795. if (SUCCEEDED(hr))
  796. {
  797. if (SUCCEEDED(m_pFolder->SeekRowset(hRowset, SEEK_ROWSET_BEGIN, iRow, NULL)))
  798. {
  799. // Get the first message
  800. while (S_OK == m_pFolder->QueryRowset(hRowset, 1, (void **)&Message, NULL))
  801. {
  802. if (cMsgs == cMsgsBuf)
  803. {
  804. if (!MemRealloc((void **)&pMsgId, (cMsgsBuf + CMSGIDALLOC) * sizeof(MESSAGEID)))
  805. {
  806. m_pFolder->FreeRecord(&Message);
  807. hr = E_OUTOFMEMORY;
  808. break;
  809. }
  810. cMsgsBuf += CMSGIDALLOC;
  811. }
  812. // It's possible to have already downloaded the body if the message was
  813. // watched. It's also possible for the message to be part of an ignored
  814. // thread.
  815. if (0 == (Message.dwFlags & ARF_HASBODY) && 0 == (Message.dwFlags & ARF_IGNORE) && (Message.idMessage >= (MESSAGEID) m_dwPrevHigh))
  816. {
  817. pMsgId[cMsgs] = Message.idMessage;
  818. cMsgs++;
  819. }
  820. // Free the header info
  821. m_pFolder->FreeRecord(&Message);
  822. }
  823. }
  824. // Release Lock
  825. m_pFolder->CloseRowset(&hRowset);
  826. }
  827. // TODO: error handling
  828. Assert(!FAILED(hr));
  829. // Check to see if there was anything added
  830. if (cMsgs == 0)
  831. {
  832. // Nothing to download. We should move on to the marked download
  833. // state.
  834. m_state = ONTS_MARKEDMSGS;
  835. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  836. return(S_OK);
  837. }
  838. // Update the general progress
  839. SetGeneralProgress((LPSTR)idsLogStartDownloadAll, m_pInfo->szGroup);
  840. list.cAllocated = 0;
  841. list.cMsgs = cMsgs;
  842. list.prgidMsg = pMsgId;
  843. // Ask for the first article
  844. hr = Article_Init(&list);
  845. if (pMsgId != NULL)
  846. MemFree(pMsgId);
  847. return(hr);
  848. }
  849. //
  850. // FUNCTION: COfflineTask::Download_MarkedMsgs()
  851. //
  852. // PURPOSE:
  853. //
  854. //
  855. HRESULT COfflineTask::Download_MarkedMsgs(void)
  856. {
  857. HRESULT hr;
  858. HROWSET hRowset;
  859. DWORD cMsgs, cMsgsBuf;
  860. LPMESSAGEID pMsgId;
  861. MESSAGEIDLIST list;
  862. MESSAGEINFO MsgInfo;
  863. // Check to see if we even want to download marked messages
  864. if (!m_pInfo->fMarked)
  865. {
  866. // Move on to the next state
  867. m_state = ONTS_END;
  868. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  869. return(S_OK);
  870. }
  871. // We need to determine a list of messages to download. What we're looking
  872. // to do is download all of the messages that are marked which are unread.
  873. // To do this, we need to find the intersection of the unread range list and
  874. // the marked range list.
  875. // Create a Rowset
  876. hr = m_pFolder->CreateRowset(IINDEX_PRIMARY, 0, &hRowset);
  877. if (FAILED(hr))
  878. {
  879. goto Failure;
  880. }
  881. cMsgs = 0;
  882. cMsgsBuf = 0;
  883. pMsgId = NULL;
  884. // Get the first message
  885. while (S_OK == m_pFolder->QueryRowset(hRowset, 1, (void **)&MsgInfo, NULL))
  886. {
  887. if (((MsgInfo.dwFlags & ARF_DOWNLOAD) || (MsgInfo.dwFlags & ARF_WATCH)) && 0 == (MsgInfo.dwFlags & ARF_HASBODY))
  888. {
  889. if (cMsgs == cMsgsBuf)
  890. {
  891. if (!MemRealloc((void **)&pMsgId, (cMsgsBuf + CMSGIDALLOC) * sizeof(MESSAGEID)))
  892. {
  893. m_pFolder->FreeRecord(&MsgInfo);
  894. hr = E_OUTOFMEMORY;
  895. break;
  896. }
  897. cMsgsBuf += CMSGIDALLOC;
  898. }
  899. pMsgId[cMsgs] = MsgInfo.idMessage;
  900. cMsgs++;
  901. }
  902. // Free the header info
  903. m_pFolder->FreeRecord(&MsgInfo);
  904. }
  905. // Release Lock
  906. m_pFolder->CloseRowset(&hRowset);
  907. // TODO: error handling
  908. Assert(!FAILED(hr));
  909. // Check to see if we found anything
  910. if (cMsgs == 0)
  911. {
  912. // Nothing to download. We should move on to next state.
  913. m_state = ONTS_END;
  914. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  915. return(S_OK);
  916. }
  917. // Update the general progress
  918. SetGeneralProgress((LPSTR)idsLogStartDownloadAll, m_pInfo->szGroup);
  919. list.cAllocated = 0;
  920. list.cMsgs = cMsgs;
  921. list.prgidMsg = pMsgId;
  922. // Ask for the first article
  923. hr = Article_Init(&list);
  924. if (pMsgId != NULL)
  925. MemFree(pMsgId);
  926. Failure:
  927. if (FAILED(hr))
  928. {
  929. // $$$$BUGBUG$$$$
  930. InsertError((LPSTR)idsLogErrorSwitchGroup, m_pInfo->szGroup, m_szAccount);
  931. m_fFailed = TRUE;
  932. m_state = ONTS_END;
  933. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  934. }
  935. return (hr);
  936. }
  937. //
  938. // FUNCTION: COfflineTask::Download_Done()
  939. //
  940. // PURPOSE:
  941. //
  942. //
  943. HRESULT COfflineTask::Download_Done(void)
  944. {
  945. // Make sure we don't get freed before we can clean up
  946. AddRef();
  947. // Tell the spooler we're done
  948. Assert(m_pBindCtx);
  949. m_pBindCtx->Notify(DELIVERY_NOTIFY_COMPLETE, m_dwNewInboxMsgs);
  950. if (m_fCancel)
  951. m_pBindCtx->EventDone(m_eidCur, EVENT_CANCELED);
  952. else if (m_fFailed)
  953. m_pBindCtx->EventDone(m_eidCur, EVENT_FAILED);
  954. else if (m_fDownloadErrors)
  955. m_pBindCtx->EventDone(m_eidCur, EVENT_WARNINGS);
  956. else
  957. m_pBindCtx->EventDone(m_eidCur, EVENT_SUCCEEDED);
  958. m_cEvents--;
  959. if (m_pFolder != NULL)
  960. {
  961. m_pFolder->Close();
  962. m_pFolder->Release();
  963. m_pFolder = NULL;
  964. }
  965. m_state = ONTS_IDLE;
  966. SafeMemFree(m_pInfo);
  967. Release();
  968. return (S_OK);
  969. }
  970. //
  971. // FUNCTION: COfflineTask::InsertError()
  972. //
  973. // PURPOSE: This function is a wrapper for the ISpoolerUI::InsertError()
  974. // that takes the responsibility of loading the string resource
  975. // and constructing the error message.
  976. //
  977. void COfflineTask::InsertError(const TCHAR *pFmt, ...)
  978. {
  979. int i;
  980. va_list pArgs;
  981. LPCTSTR pszT;
  982. TCHAR szFmt[CCHMAX_STRINGRES];
  983. DWORD cbWritten;
  984. TCHAR szBuf[2 * CCHMAX_STRINGRES];
  985. // If we were passed a string resource ID, then we need to load it
  986. if (IS_INTRESOURCE(pFmt))
  987. {
  988. AthLoadString(PtrToUlong(pFmt), szFmt, ARRAYSIZE(szFmt));
  989. pszT = szFmt;
  990. }
  991. else
  992. pszT = pFmt;
  993. // Format the string
  994. va_start(pArgs, pFmt);
  995. i = wvnsprintf(szBuf, ARRAYSIZE(szBuf), pszT, pArgs);
  996. va_end(pArgs);
  997. // Send the string to the UI
  998. m_pUI->InsertError(m_eidCur, szBuf);
  999. }
  1000. //
  1001. // FUNCTION: COfflineTask::SetSpecificProgress()
  1002. //
  1003. // PURPOSE: This function is a wrapper for the ISpoolerUI::SetSpecificProgress()
  1004. // that takes the responsibility of loading the string resource
  1005. // and constructing the error message.
  1006. //
  1007. void COfflineTask::SetSpecificProgress(const TCHAR *pFmt, ...)
  1008. {
  1009. int i;
  1010. va_list pArgs;
  1011. LPCTSTR pszT;
  1012. TCHAR szFmt[CCHMAX_STRINGRES];
  1013. DWORD cbWritten;
  1014. TCHAR szBuf[2 * CCHMAX_STRINGRES];
  1015. // If we were passed a string resource ID, then we need to load it
  1016. if (IS_INTRESOURCE(pFmt))
  1017. {
  1018. AthLoadString(PtrToUlong(pFmt), szFmt, ARRAYSIZE(szFmt));
  1019. pszT = szFmt;
  1020. }
  1021. else
  1022. pszT = pFmt;
  1023. // Format the string
  1024. va_start(pArgs, pFmt);
  1025. i = wvnsprintf(szBuf, ARRAYSIZE(szBuf), pszT, pArgs);
  1026. va_end(pArgs);
  1027. // Send the string to the UI
  1028. m_pUI->SetSpecificProgress(szBuf);
  1029. }
  1030. //
  1031. // FUNCTION: COfflineTask::SetGeneralProgress()
  1032. //
  1033. // PURPOSE: This function is a wrapper for the ISpoolerUI::SetGeneralProgress()
  1034. // that takes the responsibility of loading the string resource
  1035. // and constructing the error message.
  1036. //
  1037. void COfflineTask::SetGeneralProgress(const TCHAR *pFmt, ...)
  1038. {
  1039. int i;
  1040. va_list pArgs;
  1041. LPCTSTR pszT;
  1042. TCHAR szFmt[CCHMAX_STRINGRES];
  1043. DWORD cbWritten;
  1044. TCHAR szBuf[2 * CCHMAX_STRINGRES];
  1045. // If we were passed a string resource ID, then we need to load it
  1046. if (IS_INTRESOURCE(pFmt))
  1047. {
  1048. AthLoadString(PtrToUlong(pFmt), szFmt, ARRAYSIZE(szFmt));
  1049. pszT = szFmt;
  1050. }
  1051. else
  1052. pszT = pFmt;
  1053. // Format the string
  1054. va_start(pArgs, pFmt);
  1055. i = wvnsprintf(szBuf, ARRAYSIZE(szBuf), pszT, pArgs);
  1056. va_end(pArgs);
  1057. // Send the string to the UI
  1058. m_pUI->SetGeneralProgress(szBuf);
  1059. }
  1060. //
  1061. // FUNCTION: COfflineTask::Article_Init()
  1062. //
  1063. // PURPOSE: Initializes the article download substate machine.
  1064. //
  1065. // PARAMETERS:
  1066. // <in> pRange - Range list of articles to download.
  1067. //
  1068. HRESULT COfflineTask::Article_Init(MESSAGEIDLIST *pList)
  1069. {
  1070. HRESULT hr;
  1071. Assert(pList != NULL);
  1072. Assert(pList->cMsgs > 0);
  1073. Assert(m_pList == NULL);
  1074. hr = CloneMessageIDList(pList, &m_pList);
  1075. if (FAILED(hr))
  1076. return(hr);
  1077. // Determine the first and the size
  1078. m_cDownloaded = 0;
  1079. m_cCur = 0;
  1080. m_dwNewInboxMsgs = 0;
  1081. // Set up the UI
  1082. SetSpecificProgress((LPSTR)idsIMAPDnldProgressFmt, 0, m_pList->cMsgs);
  1083. m_pUI->SetProgressRange((WORD)m_pList->cMsgs);
  1084. // Request the first one
  1085. m_as = ARTICLE_GETNEXT;
  1086. PostMessage(m_hwnd, NTM_NEXTARTICLESTATE, 0, 0);
  1087. return(S_OK);
  1088. }
  1089. //
  1090. // FUNCTION: COfflineTask::Article_GetNext()
  1091. //
  1092. // PURPOSE: Determines the next article in the range of articles to
  1093. // download and requests that article from the server.
  1094. //
  1095. HRESULT COfflineTask::Article_GetNext(void)
  1096. {
  1097. HRESULT hr;
  1098. LPMIMEMESSAGE pMsg = NULL;
  1099. if (NULL == m_pFolder)
  1100. return(S_OK);
  1101. // Find out the next article number
  1102. if (m_cCur == m_pList->cMsgs)
  1103. {
  1104. // We're done. Exit.
  1105. m_as = ARTICLE_END;
  1106. PostMessage(m_hwnd, NTM_NEXTARTICLESTATE, 0, 0);
  1107. return(S_OK);
  1108. }
  1109. m_cDownloaded++;
  1110. // (YST) Bug 97397 We should send notification message from here too, because this is
  1111. // only one availble place for HTTP (fIMAP is set for HTTP).
  1112. if(m_pInfo->fIMAP)
  1113. OnProgress(SOT_NEW_MAIL_NOTIFICATION, 1, 0, NULL);
  1114. // Update the progress UI
  1115. SetSpecificProgress((LPSTR)idsIMAPDnldProgressFmt, m_cDownloaded, m_pList->cMsgs);
  1116. m_pUI->IncrementProgress(1);
  1117. // Ask for the article
  1118. hr = m_pFolder->OpenMessage(m_pList->prgidMsg[m_cCur], 0, &pMsg, (IStoreCallback *)this);
  1119. if (pMsg != NULL)
  1120. pMsg->Release();
  1121. m_cCur++;
  1122. if (hr == E_PENDING)
  1123. {
  1124. m_as = ARTICLE_ONRESP;
  1125. }
  1126. else
  1127. {
  1128. // Whatever happened, we should move on to the next article.
  1129. m_as = ARTICLE_GETNEXT;
  1130. PostMessage(m_hwnd, NTM_NEXTARTICLESTATE, 0, 0);
  1131. }
  1132. return(S_OK);
  1133. }
  1134. //
  1135. // FUNCTION: COfflineTask::Article_Done()
  1136. //
  1137. // PURPOSE: When we've downloaded the last article, this function cleans
  1138. // up and moves us to the next state.
  1139. //
  1140. HRESULT COfflineTask::Article_Done(void)
  1141. {
  1142. // Free the range list we were working off of
  1143. MemFree(m_pList);
  1144. m_pList = NULL;
  1145. // Move to the next state. The next state is either get marked or done.
  1146. if (m_state == ONTS_MARKEDMSGS)
  1147. m_state = ONTS_END;
  1148. else
  1149. m_state = ONTS_MARKEDMSGS;
  1150. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  1151. return(S_OK);
  1152. }
  1153. STDMETHODIMP COfflineTask::IsDialogMessage(LPMSG pMsg)
  1154. {
  1155. return S_FALSE;
  1156. }
  1157. STDMETHODIMP COfflineTask::OnFlagsChanged(DWORD dwFlags)
  1158. {
  1159. m_dwFlags = dwFlags;
  1160. return (S_OK);
  1161. }
  1162. STDMETHODIMP COfflineTask::OnBegin(STOREOPERATIONTYPE tyOperation, STOREOPERATIONINFO *pOpInfo, IOperationCancel *pCancel)
  1163. {
  1164. // Hold onto this
  1165. Assert(m_tyOperation == SOT_INVALID);
  1166. if (pCancel)
  1167. {
  1168. m_pCancel = pCancel;
  1169. m_pCancel->AddRef();
  1170. }
  1171. m_tyOperation = tyOperation;
  1172. m_dwPrev = 0;
  1173. m_dwLast = 0;
  1174. // Party On
  1175. return(S_OK);
  1176. }
  1177. STDMETHODIMP COfflineTask::OnProgress(STOREOPERATIONTYPE tyOperation, DWORD dwCurrent, DWORD dwMax, LPCSTR pszStatus)
  1178. {
  1179. // Close any timeout dialog, if present
  1180. CallbackCloseTimeout(&m_hTimeout);
  1181. // NOTE: that you can get more than one type of value for tyOperation.
  1182. // Most likely, you will get SOT_CONNECTION_STATUS and then the
  1183. // operation that you might expect. See HotStore.idl and look for
  1184. // the STOREOPERATION enumeration type for more info.
  1185. switch (tyOperation)
  1186. {
  1187. case SOT_CONNECTION_STATUS:
  1188. break;
  1189. case SOT_NEW_MAIL_NOTIFICATION:
  1190. m_dwNewInboxMsgs += dwCurrent;
  1191. break;
  1192. default:
  1193. if (m_state == ONTS_INIT)
  1194. {
  1195. // Update UI
  1196. if (dwMax > m_dwLast)
  1197. {
  1198. m_dwLast = dwMax;
  1199. m_pUI->SetProgressRange((WORD)m_dwLast);
  1200. }
  1201. SetSpecificProgress((LPSTR)idsDownloadingHeaders, dwCurrent, m_dwLast);
  1202. m_pUI->IncrementProgress((WORD) (dwCurrent - m_dwPrev));
  1203. m_dwPrev = dwCurrent;
  1204. }
  1205. } // switch
  1206. // Done
  1207. return(S_OK);
  1208. }
  1209. STDMETHODIMP COfflineTask::OnTimeout(LPINETSERVER pServer, LPDWORD pdwTimeout, IXPTYPE ixpServerType)
  1210. {
  1211. if (!!(m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND)))
  1212. return(E_FAIL);
  1213. // Display a timeout dialog
  1214. return CallbackOnTimeout(pServer, ixpServerType, *pdwTimeout, (ITimeoutCallback *)this, &m_hTimeout);
  1215. }
  1216. STDMETHODIMP COfflineTask::CanConnect(LPCSTR pszAccountId, DWORD dwFlags)
  1217. {
  1218. HWND hwnd;
  1219. BOOL fPrompt = TRUE;
  1220. if (m_pUI)
  1221. m_pUI->GetWindow(&hwnd);
  1222. else
  1223. hwnd = NULL;
  1224. // Call into general CanConnect Utility
  1225. if ((m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND)) || (dwFlags & CC_FLAG_DONTPROMPT))
  1226. fPrompt = FALSE;
  1227. return CallbackCanConnect(pszAccountId, hwnd, fPrompt);
  1228. }
  1229. STDMETHODIMP COfflineTask::OnLogonPrompt(LPINETSERVER pServer, IXPTYPE ixpServerType)
  1230. {
  1231. HWND hwnd;
  1232. // Close any timeout dialog, if present
  1233. CallbackCloseTimeout(&m_hTimeout);
  1234. if (!!(m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND)) &&
  1235. !(ISFLAGSET(pServer->dwFlags, ISF_ALWAYSPROMPTFORPASSWORD) &&
  1236. '\0' == pServer->szPassword[0]))
  1237. return(S_FALSE);
  1238. if (m_pUI)
  1239. m_pUI->GetWindow(&hwnd);
  1240. else
  1241. hwnd = NULL;
  1242. // Call into general OnLogonPrompt Utility
  1243. return CallbackOnLogonPrompt(hwnd, pServer, ixpServerType);
  1244. }
  1245. STDMETHODIMP COfflineTask::OnComplete(STOREOPERATIONTYPE tyOperation, HRESULT hrComplete,
  1246. LPSTOREOPERATIONINFO pOpInfo, LPSTOREERROR pErrorInfo)
  1247. {
  1248. HRESULT hr;
  1249. DWORD dw;
  1250. BOOL fUserCancel = FALSE;
  1251. // Close any timeout dialog, if present
  1252. CallbackCloseTimeout(&m_hTimeout);
  1253. Assert(m_tyOperation != SOT_INVALID);
  1254. if (m_tyOperation != tyOperation)
  1255. return(S_OK);
  1256. switch (hrComplete)
  1257. {
  1258. case STORE_E_EXPIRED:
  1259. case IXP_E_HTTP_NOT_MODIFIED:
  1260. // Completely ignore errors due to expired/deleted messages
  1261. hrComplete = S_OK;
  1262. break;
  1263. case STORE_E_OPERATION_CANCELED:
  1264. case HR_E_USER_CANCEL_CONNECT:
  1265. case IXP_E_USER_CANCEL:
  1266. fUserCancel = TRUE;
  1267. break;
  1268. }
  1269. if (FAILED(hrComplete))
  1270. {
  1271. LPSTR pszOpDescription = NULL;
  1272. LPSTR pszSubject = NULL;
  1273. MESSAGEINFO Message;
  1274. BOOL fFreeMsgInfo = FALSE;
  1275. char szBuf[CCHMAX_STRINGRES], szFmt[CCHMAX_STRINGRES];
  1276. switch (tyOperation)
  1277. {
  1278. case SOT_GET_MESSAGE:
  1279. // we've already incremented m_cCur by the time we get this
  1280. Assert((m_cCur - 1) < m_pList->cMsgs);
  1281. Message.idMessage = m_pList->prgidMsg[m_cCur - 1];
  1282. pszOpDescription = MAKEINTRESOURCE(idsNewsTaskArticleError);
  1283. if (DB_S_FOUND == m_pFolder->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL))
  1284. {
  1285. fFreeMsgInfo = TRUE;
  1286. pszSubject = Message.pszSubject;
  1287. }
  1288. break; // case SOT_GET_MESSAGE
  1289. case SOT_SYNC_FOLDER:
  1290. LoadString(g_hLocRes, idsHeaderDownloadFailureFmt, szFmt, sizeof(szFmt));
  1291. wnsprintf(szBuf, ARRAYSIZE(szBuf), szFmt, (NULL == m_pInfo) ? c_szEmpty : m_pInfo->szGroup);
  1292. pszOpDescription = szBuf;
  1293. break;
  1294. default:
  1295. LoadString(g_hLocRes, idsMessageSyncFailureFmt, szFmt, sizeof(szFmt));
  1296. wnsprintf(szBuf, ARRAYSIZE(szBuf), szFmt, (NULL == m_pInfo) ? c_szEmpty : m_pInfo->szGroup);
  1297. pszOpDescription = szBuf;
  1298. break; // default case
  1299. } // switch
  1300. m_fDownloadErrors = TRUE;
  1301. if (NULL != pErrorInfo)
  1302. {
  1303. Assert(pErrorInfo->hrResult == hrComplete); // These two should not be different
  1304. TaskUtil_InsertTransportError(ISFLAGCLEAR(m_dwFlags, DELIVER_NOUI), m_pUI, m_eidCur,
  1305. pErrorInfo, pszOpDescription, pszSubject);
  1306. }
  1307. if (fFreeMsgInfo)
  1308. m_pFolder->FreeRecord(&Message);
  1309. }
  1310. if (fUserCancel)
  1311. {
  1312. // User has cancelled the OnLogonPrompt dialog, so abort EVERYTHING
  1313. Cancel();
  1314. }
  1315. else if (m_state == ONTS_INIT)
  1316. {
  1317. SetSpecificProgress((LPSTR)idsDownloadingHeaders, m_dwLast, m_dwLast);
  1318. m_pUI->IncrementProgress((WORD) (m_dwLast - m_dwPrev));
  1319. // Set a flag if we actually downloaded new headers
  1320. m_fNewHeaders = (m_dwLast > 0);
  1321. // Move to the next state
  1322. m_state = ONTS_ALLMSGS;
  1323. PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
  1324. }
  1325. else
  1326. {
  1327. m_as = ARTICLE_GETNEXT;
  1328. PostMessage(m_hwnd, NTM_NEXTARTICLESTATE, 0, 0);
  1329. }
  1330. // Release your cancel object
  1331. SafeRelease(m_pCancel);
  1332. m_tyOperation = SOT_INVALID;
  1333. // Done
  1334. return(S_OK);
  1335. }
  1336. STDMETHODIMP COfflineTask::OnPrompt(HRESULT hrError, LPCTSTR pszText, LPCTSTR pszCaption, UINT uType, INT *piUserResponse)
  1337. {
  1338. HWND hwnd;
  1339. // Close any timeout dialog, if present
  1340. CallbackCloseTimeout(&m_hTimeout);
  1341. // Raid 55082 - SPOOLER: SPA/SSL auth to NNTP does not display cert warning and fails.
  1342. #if 0
  1343. if (!!(m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND)))
  1344. return(E_FAIL);
  1345. #endif
  1346. if (m_pUI)
  1347. m_pUI->GetWindow(&hwnd);
  1348. else
  1349. hwnd = NULL;
  1350. // Call into my swanky utility
  1351. return CallbackOnPrompt(hwnd, hrError, pszText, pszCaption, uType, piUserResponse);
  1352. }
  1353. STDMETHODIMP COfflineTask::OnTimeoutResponse(TIMEOUTRESPONSE eResponse)
  1354. {
  1355. // Call into general timeout response utility
  1356. return CallbackOnTimeoutResponse(eResponse, m_pCancel, &m_hTimeout);
  1357. }
  1358. STDMETHODIMP COfflineTask::GetParentWindow(DWORD dwReserved, HWND *phwndParent)
  1359. {
  1360. if (!!(m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND)))
  1361. return(E_FAIL);
  1362. if (m_pUI)
  1363. {
  1364. return m_pUI->GetWindow(phwndParent);
  1365. }
  1366. else
  1367. {
  1368. *phwndParent = NULL;
  1369. return E_FAIL;
  1370. }
  1371. }