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.

2114 lines
60 KiB

  1. //--------------------------------------------------------------------------
  2. // MsgFldr.cpp
  3. //--------------------------------------------------------------------------
  4. #include "pch.hxx"
  5. #include "store.h"
  6. #include "instance.h"
  7. #include "msgfldr.h"
  8. #include "secutil.h"
  9. #include "storutil.h"
  10. #include "shared.h"
  11. #include "flagconv.h"
  12. #include "qstrcmpi.h"
  13. #include "xpcomm.h"
  14. #include "msgtable.h"
  15. #include "shlwapip.h"
  16. #include <oerules.h>
  17. #include <ruleutil.h>
  18. //--------------------------------------------------------------------------
  19. // Watch/Ignore Index Filter
  20. //--------------------------------------------------------------------------
  21. static const char c_szWatchIgnoreFilter[] = "((MSGCOL_FLAGS & ARF_WATCH) != 0 || (MSGCOL_FLAGS & ARF_IGNORE) != 0)";
  22. //--------------------------------------------------------------------------
  23. // GETWATCHIGNOREPARENT
  24. //--------------------------------------------------------------------------
  25. typedef struct tagGETWATCHIGNOREPARENT {
  26. IDatabase *pDatabase;
  27. HRESULT hrResult;
  28. MESSAGEINFO Parent;
  29. } GETWATCHIGNOREPARENT, *LPGETWATCHIGNOREPARENT;
  30. //--------------------------------------------------------------------------
  31. // EnumRefsGetWatchIgnoreParent
  32. //--------------------------------------------------------------------------
  33. HRESULT EnumRefsGetWatchIgnoreParent(LPCSTR pszMessageId, DWORD_PTR dwCookie,
  34. BOOL *pfDone)
  35. {
  36. // Locals
  37. LPGETWATCHIGNOREPARENT pGetParent = (LPGETWATCHIGNOREPARENT)dwCookie;
  38. // Trace
  39. TraceCall("EnumRefsGetWatchIgnoreParent");
  40. // Set MessageId
  41. pGetParent->Parent.pszMessageId = (LPSTR)pszMessageId;
  42. // Find pszMessageId in the IINDEX_WATCHIGNORE Index
  43. pGetParent->hrResult = pGetParent->pDatabase->FindRecord(IINDEX_WATCHIGNORE, 1, &pGetParent->Parent, NULL);
  44. // Done
  45. if (DB_S_FOUND == pGetParent->hrResult)
  46. {
  47. // We are done
  48. *pfDone = TRUE;
  49. }
  50. // Done
  51. return(S_OK);
  52. }
  53. //--------------------------------------------------------------------------
  54. // CreateMsgDbExtension
  55. //--------------------------------------------------------------------------
  56. HRESULT CreateMsgDbExtension(IUnknown *pUnkOuter, IUnknown **ppUnknown)
  57. {
  58. // Trace
  59. TraceCall("CreateMsgDbExtension");
  60. // Invalid Args
  61. Assert(ppUnknown);
  62. // Initialize
  63. *ppUnknown = NULL;
  64. // Create me
  65. CMessageFolder *pNew = new CMessageFolder();
  66. if (NULL == pNew)
  67. return TraceResult(E_OUTOFMEMORY);
  68. // Cast to unknown
  69. *ppUnknown = SAFECAST(pNew, IDatabaseExtension *);
  70. // Done
  71. return(S_OK);
  72. }
  73. //--------------------------------------------------------------------------
  74. // CMessageFolder::CMessageFolder
  75. //--------------------------------------------------------------------------
  76. CMessageFolder::CMessageFolder(void)
  77. {
  78. TraceCall("CMessageFolder::CMessageFolder");
  79. #ifndef _WIN64
  80. Assert(1560 == sizeof(FOLDERUSERDATA));
  81. #endif // WIN64
  82. m_cRef = 1;
  83. m_pStore = NULL;
  84. m_pDB = NULL;
  85. m_tyFolder = FOLDER_INVALID;
  86. m_tySpecial = FOLDER_NOTSPECIAL;
  87. m_idFolder = FOLDERID_INVALID;
  88. m_dwState = 0;
  89. ZeroMemory(&m_OnLock, sizeof(ONLOCKINFO));
  90. }
  91. //--------------------------------------------------------------------------
  92. // CMessageFolder::~CMessageFolder
  93. //--------------------------------------------------------------------------
  94. CMessageFolder::~CMessageFolder(void)
  95. {
  96. // Trace
  97. TraceCall("CMessageFolder::~CMessageFolder");
  98. // Release the Store
  99. SafeRelease(m_pStore);
  100. // Release the Database Table
  101. if (ISFLAGSET(m_dwState, FOLDER_STATE_RELEASEDB) && m_pDB)
  102. {
  103. m_pDB->Release();
  104. m_pDB = NULL;
  105. }
  106. }
  107. //--------------------------------------------------------------------------
  108. // CMessageFolder::QueryInterface
  109. //--------------------------------------------------------------------------
  110. STDMETHODIMP CMessageFolder::QueryInterface(REFIID riid, LPVOID *ppv)
  111. {
  112. // Locals
  113. HRESULT hr=S_OK;
  114. // Stack
  115. TraceCall("CMessageFolder::QueryInterface");
  116. // Find IID
  117. if (IID_IUnknown == riid)
  118. *ppv = (IUnknown *)(IMessageFolder *)this;
  119. else if (IID_IMessageFolder == riid)
  120. *ppv = (IMessageFolder *)this;
  121. else if (IID_IDatabase == riid)
  122. *ppv = (IDatabase *)this;
  123. else if (IID_IDatabaseExtension == riid)
  124. *ppv = (IDatabaseExtension *)this;
  125. else if (IID_IServiceProvider == riid)
  126. *ppv = (IServiceProvider *)this;
  127. else
  128. {
  129. *ppv = NULL;
  130. hr = E_NOINTERFACE;
  131. goto exit;
  132. }
  133. // AddRef It
  134. ((IUnknown *)*ppv)->AddRef();
  135. exit:
  136. // Done
  137. return(hr);
  138. }
  139. //--------------------------------------------------------------------------
  140. // CMessageFolder::AddRef
  141. //--------------------------------------------------------------------------
  142. STDMETHODIMP_(ULONG) CMessageFolder::AddRef(void)
  143. {
  144. TraceCall("CMessageFolder::AddRef");
  145. return InterlockedIncrement(&m_cRef);
  146. }
  147. //--------------------------------------------------------------------------
  148. // CMessageFolder::Release
  149. //--------------------------------------------------------------------------
  150. STDMETHODIMP_(ULONG) CMessageFolder::Release(void)
  151. {
  152. TraceCall("CMessageFolder::Release");
  153. LONG cRef = InterlockedDecrement(&m_cRef);
  154. if (0 == cRef)
  155. delete this;
  156. return (ULONG)cRef;
  157. }
  158. //--------------------------------------------------------------------------
  159. // CMessageFolder::QueryService
  160. //--------------------------------------------------------------------------
  161. STDMETHODIMP CMessageFolder::QueryService(REFGUID guidService, REFIID riid,
  162. LPVOID *ppvObject)
  163. {
  164. // Trace
  165. TraceCall("CMessageFolder::QueryService");
  166. // Just a Query Interface
  167. return(QueryInterface(riid, ppvObject));
  168. }
  169. //--------------------------------------------------------------------------
  170. // CMessageFolder::Initialize
  171. //--------------------------------------------------------------------------
  172. STDMETHODIMP CMessageFolder::Initialize(IMessageStore *pStore, IMessageServer *pServer,
  173. OPENFOLDERFLAGS dwFlags, FOLDERID idFolder)
  174. {
  175. // Locals
  176. HRESULT hr=S_OK;
  177. CHAR szDirectory[MAX_PATH];
  178. CHAR szFilePath[MAX_PATH + MAX_PATH];
  179. FOLDERINFO Folder={0};
  180. FOLDERUSERDATA UserData={0};
  181. // Trace
  182. TraceCall("CMessageFolder::Initialize");
  183. // Invalid Args
  184. if (NULL == pStore)
  185. return TraceResult(E_INVALIDARG);
  186. // Save the FolderId
  187. m_idFolder = idFolder;
  188. // Save pStore (This must happen before m_pDB->Open happens)
  189. m_pStore = pStore;
  190. m_pStore->AddRef();
  191. // Find the Folder Information
  192. IF_FAILEXIT(hr = pStore->GetFolderInfo(idFolder, &Folder));
  193. // Make Folder File Path
  194. IF_FAILEXIT(hr = pStore->GetDirectory(szDirectory, ARRAYSIZE(szDirectory)));
  195. // No Folder File Yet ?
  196. if (FIsEmptyA(Folder.pszFile))
  197. {
  198. // Don't Create
  199. if (ISFLAGSET(dwFlags, OPEN_FOLDER_NOCREATE))
  200. {
  201. hr = STORE_E_FILENOEXIST;
  202. goto exit;
  203. }
  204. // Build Friendly Name
  205. IF_FAILEXIT(hr = BuildFriendlyFolderFileName(szDirectory, &Folder, szFilePath, ARRAYSIZE(szFilePath), NULL, NULL));
  206. // Get the new pszFile...
  207. Folder.pszFile = PathFindFileName(szFilePath);
  208. // Update the Record
  209. IF_FAILEXIT(hr = pStore->UpdateRecord(&Folder));
  210. }
  211. // Otherwise, build the filepath
  212. else
  213. {
  214. // Make File Path
  215. IF_FAILEXIT(hr = MakeFilePath(szDirectory, Folder.pszFile, c_szEmpty, szFilePath, ARRAYSIZE(szFilePath)));
  216. }
  217. // If the file doesn't exist...
  218. if (FALSE == PathFileExists(szFilePath))
  219. {
  220. // Reset the Folder Counts...
  221. Folder.cMessages = 0;
  222. Folder.dwClientHigh = 0;
  223. Folder.dwClientLow = 0;
  224. Folder.cUnread = 0;
  225. Folder.cWatched = 0;
  226. Folder.cWatchedUnread = 0;
  227. Folder.dwServerHigh = 0;
  228. Folder.dwServerLow = 0;
  229. Folder.dwServerCount = 0;
  230. Folder.dwStatusMsgDelta = 0;
  231. Folder.dwStatusUnreadDelta = 0;
  232. Folder.dwNotDownloaded = 0;
  233. Folder.dwClientWatchedHigh = 0;
  234. Folder.Requested.cbSize = 0;
  235. Folder.Requested.pBlobData = NULL;
  236. Folder.Read.cbSize = 0;
  237. Folder.Read.pBlobData = NULL;
  238. // Update the Record
  239. IF_FAILEXIT(hr = pStore->UpdateRecord(&Folder));
  240. // No Create ?
  241. if (ISFLAGSET(dwFlags, OPEN_FOLDER_NOCREATE))
  242. {
  243. hr = STORE_E_FILENOEXIST;
  244. goto exit;
  245. }
  246. }
  247. // Save Special Folder Type
  248. m_tySpecial = Folder.tySpecial;
  249. // Save the Folder Type
  250. m_tyFolder = Folder.tyFolder;
  251. // Create a Database Table
  252. IF_FAILEXIT(hr = g_pDBSession->OpenDatabase(szFilePath, OPEN_DATABASE_NOADDREFEXT, &g_MessageTableSchema, (IDatabaseExtension *)this, &m_pDB));
  253. // Release m_pDB
  254. FLAGSET(m_dwState, FOLDER_STATE_RELEASEDB);
  255. // Get the User Data
  256. IF_FAILEXIT(hr = m_pDB->GetUserData(&UserData, sizeof(FOLDERUSERDATA)));
  257. // May not have been initialized yet ?
  258. if (FALSE == UserData.fInitialized)
  259. {
  260. // Locals
  261. FOLDERINFO Server;
  262. // Get the Server Info
  263. IF_FAILEXIT(hr = GetFolderServer(Folder.idParent, &Server));
  264. // Its Initialized
  265. UserData.fInitialized = TRUE;
  266. // Configure the Folder UserData
  267. UserData.tyFolder = Folder.tyFolder;
  268. // Is Special Folder ?
  269. UserData.tySpecial = Folder.tySpecial;
  270. // Copy the Account Id
  271. StrCpyN(UserData.szAcctId, Server.pszAccountId, ARRAYSIZE(UserData.szAcctId));
  272. // Free
  273. pStore->FreeRecord(&Server);
  274. // Store the Folder Name
  275. StrCpyN(UserData.szFolder, Folder.pszName, ARRAYSIZE(UserData.szFolder));
  276. // Set Folder Id
  277. UserData.idFolder = Folder.idFolder;
  278. // Sort Ascending
  279. UserData.fAscending = FALSE;
  280. // No Threading
  281. UserData.fThreaded = FALSE;
  282. // Base Filter
  283. UserData.ridFilter = (RULEID) IntToPtr(DwGetOption(OPT_VIEW_GLOBAL));
  284. if ((RULEID_INVALID == UserData.ridFilter) || ((RULEID_VIEW_DOWNLOADED == UserData.ridFilter) && (FOLDER_LOCAL == m_tyFolder)))
  285. UserData.ridFilter = RULEID_VIEW_ALL;
  286. // Hide Deleted Messages
  287. UserData.fShowDeleted = FALSE;
  288. // Hide Deleted Messages
  289. UserData.fShowReplies = FALSE;
  290. // Set Sort Order
  291. UserData.idSort = COLUMN_RECEIVED;
  292. // New thread model
  293. UserData.fNoIndexes = TRUE;
  294. // Set the User Data
  295. IF_FAILEXIT(hr = m_pDB->SetUserData(&UserData, sizeof(FOLDERUSERDATA)));
  296. }
  297. // Otherwise, fixup cWatchedUnread ?
  298. else
  299. {
  300. // No Indexes
  301. if (FALSE == UserData.fNoIndexes)
  302. {
  303. // Index Ordinals
  304. const INDEXORDINAL IINDEX_VIEW = 1;
  305. const INDEXORDINAL IINDEX_MESSAGEID = 3;
  306. const INDEXORDINAL IINDEX_SUBJECT = 4;
  307. const INDEXORDINAL IINDEX_THREADS = 5;
  308. // Delete the indexes that I don't user anymore
  309. m_pDB->DeleteIndex(IINDEX_VIEW);
  310. m_pDB->DeleteIndex(IINDEX_MESSAGEID);
  311. m_pDB->DeleteIndex(IINDEX_SUBJECT);
  312. m_pDB->DeleteIndex(IINDEX_THREADS);
  313. // Reset fNoIndexes
  314. UserData.fNoIndexes = TRUE;
  315. // Set the User Data
  316. IF_FAILEXIT(hr = m_pDB->SetUserData(&UserData, sizeof(FOLDERUSERDATA)));
  317. }
  318. }
  319. // Initialize Watch/Ignore Index
  320. _InitializeWatchIgnoreIndex();
  321. exit:
  322. // Cleanup
  323. pStore->FreeRecord(&Folder);
  324. // Done
  325. return(hr);
  326. }
  327. //--------------------------------------------------------------------------
  328. // CMessageFolder::IsWatched
  329. //--------------------------------------------------------------------------
  330. STDMETHODIMP CMessageFolder::IsWatched(LPCSTR pszReferences,
  331. LPCSTR pszSubject)
  332. {
  333. // Locals
  334. MESSAGEFLAGS dwFlags;
  335. // Trace
  336. TraceCall("CMessageFolder::IsWatched");
  337. // Get Flags
  338. if (DB_S_FOUND == _GetWatchIgnoreParentFlags(pszReferences, pszSubject, &dwFlags))
  339. {
  340. // Watched
  341. if (ISFLAGSET(dwFlags, ARF_WATCH))
  342. return(S_OK);
  343. }
  344. // Not Watched
  345. return(S_FALSE);
  346. }
  347. //--------------------------------------------------------------------------
  348. // CMessageFolder::_GetWatchIgnoreParentFlags
  349. //--------------------------------------------------------------------------
  350. HRESULT CMessageFolder::_GetWatchIgnoreParentFlags(LPCSTR pszReferences,
  351. LPCSTR pszSubject, MESSAGEFLAGS *pdwFlags)
  352. {
  353. // Locals
  354. GETWATCHIGNOREPARENT GetParent;
  355. // Trace
  356. TraceCall("CMessageFolder::_GetWatchIgnoreParentFlags");
  357. // Init hrResult...
  358. GetParent.pDatabase = m_pDB;
  359. GetParent.hrResult = DB_S_NOTFOUND;
  360. // EnumerateReferences
  361. if (SUCCEEDED(EnumerateRefs(pszReferences, (DWORD_PTR)&GetParent, EnumRefsGetWatchIgnoreParent)))
  362. {
  363. // If Found
  364. if (DB_S_FOUND == GetParent.hrResult)
  365. {
  366. // Return the Flags
  367. *pdwFlags = GetParent.Parent.dwFlags;
  368. // Free It
  369. m_pDB->FreeRecord(&GetParent.Parent);
  370. }
  371. }
  372. // Not Watched
  373. return(GetParent.hrResult);
  374. }
  375. //--------------------------------------------------------------------------
  376. // CMessageFolder::_InitializeWatchIgnoreIndex
  377. //--------------------------------------------------------------------------
  378. HRESULT CMessageFolder::_InitializeWatchIgnoreIndex(void)
  379. {
  380. // Locals
  381. HRESULT hr=S_OK;
  382. BOOL fRebuild=FALSE;
  383. LPSTR pszFilter=NULL;
  384. TABLEINDEX Index;
  385. // Trace
  386. TraceCall("CMessageFolder::_InitializeWatchIgnoreIndex");
  387. // Reset fRebuild
  388. fRebuild = FALSE;
  389. // Create the Watch Ignore Index
  390. if (FAILED(m_pDB->GetIndexInfo(IINDEX_WATCHIGNORE, &pszFilter, &Index)))
  391. fRebuild = TRUE;
  392. // Filter Change ?
  393. else if (NULL == pszFilter || lstrcmpi(pszFilter, c_szWatchIgnoreFilter) != 0)
  394. fRebuild = TRUE;
  395. // Otherwise, the index is different
  396. else if (S_FALSE == CompareTableIndexes(&Index, &g_WatchIgnoreIndex))
  397. fRebuild = TRUE;
  398. // Rebuild It ?
  399. if (fRebuild)
  400. {
  401. // Create the Index
  402. IF_FAILEXIT(hr = m_pDB->ModifyIndex(IINDEX_WATCHIGNORE, c_szWatchIgnoreFilter, &g_WatchIgnoreIndex));
  403. }
  404. exit:
  405. // Cleanup
  406. SafeMemFree(pszFilter);
  407. // Done
  408. return(hr);
  409. }
  410. //--------------------------------------------------------------------------
  411. // CMessageFolder::GetFolderId
  412. //--------------------------------------------------------------------------
  413. STDMETHODIMP CMessageFolder::GetFolderId(LPFOLDERID pidFolder)
  414. {
  415. // Trace
  416. TraceCall("CMessageFolder::GetFolderId");
  417. // Invalid Args
  418. if (NULL == pidFolder)
  419. return TraceResult(E_INVALIDARG);
  420. // Return the FolderId
  421. *pidFolder = m_idFolder;
  422. // Done
  423. return(S_OK);
  424. }
  425. //--------------------------------------------------------------------------
  426. // CMessageFolder::GetMessageFolderId
  427. //--------------------------------------------------------------------------
  428. STDMETHODIMP CMessageFolder::GetMessageFolderId(MESSAGEID idMessage, LPFOLDERID pidFolder)
  429. {
  430. // Trace
  431. TraceCall("CMessageFolder::GetFolderId");
  432. // Invalid Args
  433. if (NULL == pidFolder)
  434. return TraceResult(E_INVALIDARG);
  435. // Return the FolderId
  436. *pidFolder = m_idFolder;
  437. // Done
  438. return(S_OK);
  439. }
  440. //--------------------------------------------------------------------------
  441. // CMessageFolder::OpenMessage
  442. //--------------------------------------------------------------------------
  443. STDMETHODIMP CMessageFolder::OpenMessage(MESSAGEID idMessage,
  444. OPENMESSAGEFLAGS dwFlags, IMimeMessage **ppMessage,
  445. IStoreCallback *pCallback)
  446. {
  447. // Locals
  448. HRESULT hr=S_OK;
  449. IMimeMessage *pMessage=NULL;
  450. MESSAGEINFO Message={0};
  451. PROPVARIANT Variant;
  452. IStream *pStream=NULL;
  453. // Trace
  454. TraceCall("CMessageFolder::OpenMessage");
  455. // Invalid Args
  456. if (NULL == ppMessage)
  457. return TraceResult(E_INVALIDARG);
  458. // Initiailize
  459. *ppMessage = NULL;
  460. // Initialize Message with the Id
  461. Message.idMessage = idMessage;
  462. // Find the Row
  463. IF_FAILEXIT(hr = m_pDB->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL));
  464. // Does we have it ?
  465. if (DB_S_NOTFOUND == hr)
  466. {
  467. hr = TraceResult(DB_E_NOTFOUND);
  468. goto exit;
  469. }
  470. // Has Expired?
  471. if (Message.dwFlags & ARF_ARTICLE_EXPIRED)
  472. {
  473. hr = STORE_E_EXPIRED;
  474. goto exit;
  475. }
  476. // No Body ?
  477. if (0 == Message.faStream)
  478. {
  479. hr = STORE_E_NOBODY;
  480. goto exit;
  481. }
  482. // Create a Message
  483. IF_FAILEXIT(hr = MimeOleCreateMessage(NULL, &pMessage));
  484. // Open the Stream from the Store
  485. IF_FAILEXIT(hr = m_pDB->OpenStream(ACCESS_READ, Message.faStream, &pStream));
  486. // If there is an offset table
  487. if (Message.Offsets.cbSize > 0)
  488. {
  489. // Create a ByteStream Object
  490. CByteStream cByteStm(Message.Offsets.pBlobData, Message.Offsets.cbSize);
  491. // Load the Offset Table Into the message
  492. pMessage->LoadOffsetTable(&cByteStm);
  493. // Take the bytes back out of the bytestream object (so that it doesn't try to free it)
  494. cByteStm.AcquireBytes(&Message.Offsets.cbSize, &Message.Offsets.pBlobData, ACQ_DISPLACE);
  495. }
  496. // Load the pMessage
  497. IF_FAILEXIT(hr = pMessage->Load(pStream));
  498. // Undo security enhancements if the caller wants us to
  499. if (!ISFLAGSET(dwFlags, OPEN_MESSAGE_SECURE))
  500. {
  501. // Handle Message Security
  502. IF_FAILEXIT(hr = HandleSecurity(NULL, pMessage));
  503. }
  504. // All Props are VT_LPSTR
  505. Variant.vt = VT_LPSTR;
  506. // MUD_SERVER
  507. if (Message.pszServer)
  508. {
  509. Variant.pszVal = Message.pszServer;
  510. pMessage->SetProp(PIDTOSTR(PID_ATT_SERVER), 0, &Variant);
  511. }
  512. // PID_ATT_ACCOUNTID
  513. if (Message.pszAcctId)
  514. {
  515. Variant.pszVal = Message.pszAcctId;
  516. pMessage->SetProp(PIDTOSTR(PID_ATT_ACCOUNTID), 0, &Variant);
  517. }
  518. // PID_ATT_ACCOUNTID
  519. if (Message.pszAcctName)
  520. {
  521. Variant.pszVal = Message.pszAcctName;
  522. pMessage->SetProp(STR_ATT_ACCOUNTNAME, 0, &Variant);
  523. }
  524. // Otherwise, if there is an account id... lets get the account name
  525. else if (Message.pszAcctId)
  526. {
  527. // Locals
  528. IImnAccount *pAccount=NULL;
  529. CHAR szName[CCHMAX_ACCOUNT_NAME];
  530. // Find an Account
  531. if (g_pAcctMan && SUCCEEDED(g_pAcctMan->FindAccount(AP_ACCOUNT_ID, Message.pszAcctId, &pAccount)))
  532. {
  533. // Get the Account name
  534. if (SUCCEEDED(pAccount->GetPropSz(AP_ACCOUNT_NAME, szName, ARRAYSIZE(szName))))
  535. {
  536. Variant.pszVal = szName;
  537. pMessage->SetProp(STR_ATT_ACCOUNTNAME, 0, &Variant);
  538. }
  539. // Release
  540. pAccount->Release();
  541. }
  542. }
  543. // PID_ATT_UIDL
  544. if (Message.pszUidl)
  545. {
  546. Variant.pszVal = Message.pszUidl;
  547. pMessage->SetProp(PIDTOSTR(PID_ATT_UIDL), 0, &Variant);
  548. }
  549. // PID_ATT_FORWARDTO
  550. if (Message.pszForwardTo)
  551. {
  552. Variant.pszVal = Message.pszForwardTo;
  553. pMessage->SetProp(PIDTOSTR(PID_ATT_FORWARDTO), 0, &Variant);
  554. }
  555. // PID_HDR_XUNSENT
  556. if (ISFLAGSET(Message.dwFlags, ARF_UNSENT))
  557. {
  558. Variant.pszVal = "1";
  559. pMessage->SetProp(PIDTOSTR(PID_HDR_XUNSENT), 0, &Variant);
  560. }
  561. // Fixup Character Set
  562. IF_FAILEXIT(hr = _FixupMessageCharset(pMessage, (CODEPAGEID)Message.wLanguage));
  563. // Clear Dirty
  564. MimeOleClearDirtyTree(pMessage);
  565. // Return pMessage
  566. *ppMessage = pMessage;
  567. pMessage = NULL;
  568. exit:
  569. // Free Records
  570. m_pDB->FreeRecord(&Message);
  571. // Release
  572. SafeRelease(pMessage);
  573. SafeRelease(pStream);
  574. // Done
  575. return(hr);
  576. }
  577. //--------------------------------------------------------------------------
  578. // CMessageFolder::SaveMessage
  579. //--------------------------------------------------------------------------
  580. STDMETHODIMP CMessageFolder::SaveMessage(LPMESSAGEID pidMessage,
  581. SAVEMESSAGEFLAGS dwOptions, MESSAGEFLAGS dwFlags,
  582. IStream *pStream, IMimeMessage *pMessage, IStoreCallback *pCallback)
  583. {
  584. // Locals
  585. HRESULT hr=S_OK;
  586. IStream *pSource=NULL;
  587. CByteStream cStream;
  588. MESSAGEINFO Message={0};
  589. // Trace
  590. TraceCall("CMessageFolder::SaveMessage");
  591. // Invalid Args
  592. if (NULL == pMessage)
  593. return TraceResult(E_INVALIDARG);
  594. if (NULL == pidMessage && !ISFLAGSET(dwOptions, SAVE_MESSAGE_GENID))
  595. return TraceResult(E_INVALIDARG);
  596. // Get Message from the Message
  597. IF_FAILEXIT(hr = _GetMsgInfoFromMessage(pMessage, &Message));
  598. // Validate or Generate a message id
  599. if (ISFLAGSET(dwOptions, SAVE_MESSAGE_GENID))
  600. {
  601. // Generate Unique Message Id
  602. IF_FAILEXIT(hr = m_pDB->GenerateId((LPDWORD)&Message.idMessage));
  603. // Return It ?
  604. if (pidMessage)
  605. *pidMessage = Message.idMessage;
  606. }
  607. // Otherwise, just use idMessage
  608. else
  609. Message.idMessage = *pidMessage;
  610. // Set the Message Flags
  611. Message.dwFlags |= dwFlags;
  612. // Do I need to store the message stream...
  613. if (NULL == pStream)
  614. {
  615. // Get the Message Stream From the Message
  616. IF_FAILEXIT(hr = pMessage->GetMessageSource(&pSource, COMMIT_ONLYIFDIRTY));
  617. }
  618. // Otherwise, set pSource
  619. else
  620. {
  621. pSource = pStream;
  622. pSource->AddRef();
  623. }
  624. // Store the Message onto this record
  625. IF_FAILEXIT(hr = _SetMessageStream(&Message, FALSE, pSource));
  626. // Create the offset table
  627. if (SUCCEEDED(pMessage->SaveOffsetTable(&cStream, 0)))
  628. {
  629. // pulls the Bytes out of cByteStm
  630. cStream.AcquireBytes(&Message.Offsets.cbSize, &Message.Offsets.pBlobData, ACQ_DISPLACE);
  631. }
  632. // Store the Record
  633. if (FAILED(hr = m_pDB->InsertRecord(&Message)))
  634. {
  635. // Trace That
  636. TraceResult(hr);
  637. // A failure here means that the stream's refCount has been incremented, but the message does not reference the stream
  638. SideAssert(SUCCEEDED(m_pDB->DeleteStream(Message.faStream)));
  639. // Done
  640. goto exit;
  641. }
  642. exit:
  643. // Free Allocate Message Properties
  644. _FreeMsgInfoData(&Message);
  645. // Release Message Source IStream
  646. SafeRelease(pSource);
  647. // Done
  648. return(hr);
  649. }
  650. //--------------------------------------------------------------------------
  651. // CMessageFolder::SetMessageStream
  652. //--------------------------------------------------------------------------
  653. STDMETHODIMP CMessageFolder::SetMessageStream(MESSAGEID idMessage,
  654. IStream *pStream)
  655. {
  656. // Locals
  657. HRESULT hr=S_OK;
  658. MESSAGEINFO Message={0};
  659. // Trace
  660. TraceCall("CMessageFolder::SetMessageStream");
  661. // Invalid Args
  662. if (NULL == pStream)
  663. return TraceResult(E_INVALIDARG);
  664. // Set the MsgId
  665. Message.idMessage = idMessage;
  666. // Find the Record
  667. IF_FAILEXIT(hr = m_pDB->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL));
  668. // Store the Stream
  669. IF_FAILEXIT(hr = _SetMessageStream(&Message, TRUE, pStream));
  670. exit:
  671. // Free the Record
  672. m_pDB->FreeRecord(&Message);
  673. // Done
  674. return(hr);
  675. }
  676. //--------------------------------------------------------------------------
  677. // CMessageFolder::SetMessageFlags
  678. //--------------------------------------------------------------------------
  679. STDMETHODIMP CMessageFolder::SetMessageFlags(LPMESSAGEIDLIST pList,
  680. LPADJUSTFLAGS pFlags, LPRESULTLIST pResults,
  681. IStoreCallback *pCallback)
  682. {
  683. // Locals
  684. HRESULT hr=S_OK;
  685. DWORD i=0;
  686. DWORD cWatchedUnread=0;
  687. DWORD cWatched=0;
  688. MESSAGEINFO Message={0};
  689. HROWSET hRowset=NULL;
  690. HLOCK hLock=NULL;
  691. MESSAGEFLAGS dwFlags;
  692. DWORD cTotal;
  693. // Trace
  694. TraceCall("CMessageFolder::SetMessageFlags");
  695. // Invalid Args
  696. if (NULL == pFlags)
  697. return TraceResult(E_INVALIDARG);
  698. // Lock Notifications
  699. IF_FAILEXIT(hr = m_pDB->Lock(&hLock));
  700. // Need a Rowset
  701. if (NULL == pList)
  702. {
  703. // Create a Rowset
  704. IF_FAILEXIT(hr = m_pDB->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset));
  705. // Get Count
  706. IF_FAILEXIT(hr = m_pDB->GetRecordCount(IINDEX_PRIMARY, &cTotal));
  707. }
  708. // Otherwise, set cTotal
  709. else
  710. cTotal = pList->cMsgs;
  711. // User Wants Results ?
  712. if (pResults)
  713. {
  714. // Zero Init
  715. ZeroMemory(pResults, sizeof(RESULTLIST));
  716. // Return Results
  717. IF_NULLEXIT(pResults->prgResult = (LPRESULTINFO)ZeroAllocate(cTotal * sizeof(RESULTINFO)));
  718. // Set cAllocated
  719. pResults->cAllocated = pResults->cMsgs = cTotal;
  720. }
  721. // Loop through the messageIds
  722. for (i=0;;i++)
  723. {
  724. // Done
  725. if (pList)
  726. {
  727. // Done
  728. if (i >= pList->cMsgs)
  729. break;
  730. // Set the MessageId
  731. Message.idMessage = pList->prgidMsg[i];
  732. // Look for this record
  733. IF_FAILEXIT(hr = m_pDB->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL));
  734. }
  735. // Otherwise, enumerate next
  736. else
  737. {
  738. // Get the next
  739. IF_FAILEXIT(hr = m_pDB->QueryRowset(hRowset, 1, (LPVOID *)&Message, NULL));
  740. // Done
  741. if (S_FALSE == hr)
  742. {
  743. hr = S_OK;
  744. break;
  745. }
  746. // Found
  747. hr = DB_S_FOUND;
  748. }
  749. // Was It Found
  750. if (DB_S_FOUND == hr)
  751. {
  752. // Save Flags
  753. dwFlags = Message.dwFlags;
  754. // Remove Flags
  755. FLAGCLEAR(dwFlags, pFlags->dwRemove);
  756. // Add Flags
  757. FLAGSET(dwFlags, pFlags->dwAdd);
  758. // if there is a body for this msg, then the download flag can't be on
  759. if (ISFLAGSET(dwFlags, ARF_DOWNLOAD) && ISFLAGSET(dwFlags, ARF_HASBODY))
  760. FLAGCLEAR(dwFlags, ARF_DOWNLOAD);
  761. // Update All...or no change
  762. if (Message.dwFlags != dwFlags)
  763. {
  764. // Reset the Flags
  765. Message.dwFlags = dwFlags;
  766. // Update the Record
  767. IF_FAILEXIT(hr = m_pDB->UpdateRecord(&Message));
  768. }
  769. // Count Watched Unread
  770. if (ISFLAGSET(Message.dwFlags, ARF_WATCH))
  771. {
  772. // Count Watched
  773. cWatched++;
  774. // Is unread
  775. if (!ISFLAGSET(Message.dwFlags, ARF_READ))
  776. cWatchedUnread++;
  777. }
  778. // Return Result
  779. if (pResults)
  780. {
  781. // hrResult
  782. pResults->prgResult[i].hrResult = S_OK;
  783. // Message Id
  784. pResults->prgResult[i].idMessage = Message.idMessage;
  785. // Store Falgs
  786. pResults->prgResult[i].dwFlags = Message.dwFlags;
  787. // Increment Success
  788. pResults->cValid++;
  789. }
  790. // Free
  791. m_pDB->FreeRecord(&Message);
  792. }
  793. // Otherwise, if pResults
  794. else if (pResults)
  795. {
  796. // Set hr
  797. pResults->prgResult[i].hrResult = hr;
  798. // Increment Success
  799. pResults->cValid++;
  800. }
  801. }
  802. exit:
  803. // Reset Folder Counts ?
  804. if (NULL == pList && ISFLAGSET(pFlags->dwAdd, ARF_READ))
  805. {
  806. // Reset Folder Counts
  807. ResetFolderCounts(i, 0, cWatchedUnread, cWatched);
  808. }
  809. // Unlock the Database
  810. m_pDB->Unlock(&hLock);
  811. // Cleanup
  812. m_pDB->FreeRecord(&Message);
  813. m_pDB->CloseRowset(&hRowset);
  814. // Done
  815. return(hr);
  816. }
  817. //--------------------------------------------------------------------------
  818. // CMesageFolder::ResetFolderCounts
  819. //--------------------------------------------------------------------------
  820. HRESULT CMessageFolder::ResetFolderCounts(DWORD cMessages, DWORD cUnread,
  821. DWORD cWatchedUnread, DWORD cWatched)
  822. {
  823. // Locals
  824. HRESULT hr=S_OK;
  825. FOLDERINFO Folder={0};
  826. // Trace
  827. TraceCall("CMesageFolder::ResetFolderCounts");
  828. // Get Folder Info
  829. IF_FAILEXIT(hr = m_pStore->GetFolderInfo(m_idFolder, &Folder));
  830. Folder.cMessages = cMessages;
  831. Folder.cUnread = cUnread;
  832. Folder.cWatchedUnread = cWatchedUnread;
  833. Folder.cWatched = cWatched;
  834. Folder.dwStatusMsgDelta = 0;
  835. Folder.dwStatusUnreadDelta = 0;
  836. // Update the Record
  837. IF_FAILEXIT(hr = m_pStore->UpdateRecord(&Folder));
  838. exit:
  839. // Cleanup
  840. m_pStore->FreeRecord(&Folder);
  841. // Done
  842. return(hr);
  843. }
  844. //--------------------------------------------------------------------------
  845. // CMessageFolder::CopyMessages
  846. //--------------------------------------------------------------------------
  847. STDMETHODIMP CMessageFolder::CopyMessages(IMessageFolder *pDest,
  848. COPYMESSAGEFLAGS dwOptions, LPMESSAGEIDLIST pList,
  849. LPADJUSTFLAGS pFlags, LPRESULTLIST pResults,
  850. IStoreCallback *pCallback)
  851. {
  852. // Locals
  853. HRESULT hr=S_OK;
  854. HROWSET hRowset=NULL;
  855. MESSAGEINFO InfoSrc={0};
  856. MESSAGEINFO InfoDst;
  857. DWORD i;
  858. FOLDERID idDstFolder=FOLDERID_INVALID;
  859. HLOCK hSrcLock=NULL;
  860. HLOCK hDstLock=NULL;
  861. // Trace
  862. TraceCall("CMessageFolder::CopyMessages");
  863. // Invalid Args
  864. if (NULL == pDest)
  865. return TraceResult(E_INVALIDARG);
  866. // Get Destination Folder Id
  867. IF_FAILEXIT(hr = pDest->GetFolderId(&idDstFolder));
  868. // Same ?
  869. if (ISFLAGSET(dwOptions, COPY_MESSAGE_MOVE) && m_idFolder == idDstFolder)
  870. return(S_OK);
  871. // Lock current folder
  872. IF_FAILEXIT(hr = Lock(&hSrcLock));
  873. // Lock the Dest
  874. IF_FAILEXIT(hr = pDest->Lock(&hDstLock));
  875. // Need a Rowset
  876. if (NULL == pList)
  877. {
  878. // Create a Rowset
  879. IF_FAILEXIT(hr = m_pDB->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset));
  880. }
  881. // Loop through the messageIds
  882. for (i=0;;i++)
  883. {
  884. // Done
  885. if (pList)
  886. {
  887. // Done
  888. if (i >= pList->cMsgs)
  889. break;
  890. // Set the MessageId
  891. InfoSrc.idMessage = pList->prgidMsg[i];
  892. // Look for this record
  893. IF_FAILEXIT(hr = m_pDB->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &InfoSrc, NULL));
  894. }
  895. // Otherwise, enumerate next
  896. else
  897. {
  898. // Get the next
  899. IF_FAILEXIT(hr = m_pDB->QueryRowset(hRowset, 1, (LPVOID *)&InfoSrc, NULL));
  900. // Done
  901. if (S_FALSE == hr)
  902. {
  903. hr = S_OK;
  904. break;
  905. }
  906. // Found
  907. hr = DB_S_FOUND;
  908. }
  909. // Was It Found
  910. if (DB_S_FOUND == hr)
  911. {
  912. // Initialize the InfoDst
  913. CopyMemory(&InfoDst, &InfoSrc, sizeof(MESSAGEINFO));
  914. // Kill some fields
  915. InfoDst.idMessage = 0;
  916. // Don't Copy the UIDL...
  917. if (FALSE == ISFLAGSET(dwOptions, COPY_MESSAGE_MOVE))
  918. {
  919. // Clear It Out
  920. InfoDst.pszUidl = NULL;
  921. }
  922. // Clear a flag
  923. FLAGCLEAR(InfoDst.dwFlags, ARF_ENDANGERED);
  924. // Copy Source Stream
  925. if (InfoSrc.faStream)
  926. {
  927. // Copy the Stream
  928. IF_FAILEXIT(hr = m_pDB->CopyStream(pDest, InfoSrc.faStream, &InfoDst.faStream));
  929. }
  930. // Adjust Flags
  931. if (pFlags)
  932. {
  933. // Remove the Flags
  934. FLAGCLEAR(InfoDst.dwFlags, pFlags->dwRemove);
  935. // Flags to Add
  936. FLAGSET(InfoDst.dwFlags, pFlags->dwAdd);
  937. }
  938. // Generate a Message Id
  939. IF_FAILEXIT(hr = pDest->GenerateId((LPDWORD)&InfoDst.idMessage));
  940. // Insert the Record
  941. IF_FAILEXIT(hr = pDest->InsertRecord(&InfoDst));
  942. // Cleanup
  943. m_pDB->FreeRecord(&InfoSrc);
  944. }
  945. }
  946. // Delete the Original Array of messages ?
  947. if (ISFLAGSET(dwOptions, COPY_MESSAGE_MOVE))
  948. {
  949. // DeleteMessages
  950. IF_FAILEXIT(hr = DeleteMessages(DELETE_MESSAGE_NOUIDLUPDATE | DELETE_MESSAGE_NOTRASHCAN | DELETE_MESSAGE_NOPROMPT, pList, pResults, pCallback));
  951. }
  952. exit:
  953. // Unlock
  954. Unlock(&hSrcLock);
  955. pDest->Unlock(&hDstLock);
  956. // Cleanup
  957. m_pDB->CloseRowset(&hRowset);
  958. m_pDB->FreeRecord(&InfoSrc);
  959. // Done
  960. return(hr);
  961. }
  962. //--------------------------------------------------------------------------
  963. // CMessageFolder::DeleteMessages
  964. //--------------------------------------------------------------------------
  965. STDMETHODIMP CMessageFolder::DeleteMessages(DELETEMESSAGEFLAGS dwOptions,
  966. LPMESSAGEIDLIST pList, LPRESULTLIST pResults,
  967. IStoreCallback *pCallback)
  968. {
  969. // Locals
  970. HRESULT hr=S_OK;
  971. HRESULT hrCancel;
  972. HROWSET hRowset=NULL;
  973. MESSAGEINFO Message={0};
  974. DWORD cTotal;
  975. DWORD cCurrent=0;
  976. DWORD i;
  977. FOLDERID idServer;
  978. FOLDERID idDeletedItems;
  979. HLOCK hLock=NULL;
  980. HWND hwndParent;
  981. BOOL fOnBegin=FALSE;
  982. IDatabase *pUidlDB=NULL;
  983. IMessageFolder *pDeleted=NULL;
  984. // Trace
  985. TraceCall("CMessageFolder::DeleteMessages");
  986. // I can't Undelete
  987. AssertSz(0 == (dwOptions & DELETE_MESSAGE_UNDELETE), "This flag only makes sense for IMAP!");
  988. // Am I in the Trash Can ?
  989. if (!ISFLAGSET(dwOptions, DELETE_MESSAGE_NOTRASHCAN))
  990. {
  991. // Not in the deleted items folder
  992. if (S_FALSE == IsParentDeletedItems(m_idFolder, &idDeletedItems, &idServer))
  993. {
  994. // Get the Deleted Items Folder
  995. IF_FAILEXIT(hr = m_pStore->OpenSpecialFolder(idServer, NULL, FOLDER_DELETED, &pDeleted));
  996. // Simply move messages to the deleted items
  997. IF_FAILEXIT(hr = CopyMessages(pDeleted, COPY_MESSAGE_MOVE, pList, NULL, pResults, pCallback));
  998. // Done
  999. goto exit;
  1000. }
  1001. // Otherwise, do deleted items
  1002. else
  1003. {
  1004. // Prompt...
  1005. if (FALSE == ISFLAGSET(dwOptions, DELETE_MESSAGE_NOPROMPT))
  1006. {
  1007. // Get a Parent Hwnd
  1008. Assert(pCallback);
  1009. // Get Parent Window
  1010. if (FAILED(pCallback->GetParentWindow(0, &hwndParent)))
  1011. hwndParent = NULL;
  1012. // Prompt...
  1013. if (IDNO == AthMessageBoxW(hwndParent, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsWarnPermDelete), NULL, MB_YESNO | MB_DEFBUTTON2 | MB_ICONEXCLAMATION ))
  1014. goto exit;
  1015. }
  1016. }
  1017. }
  1018. else if (!ISFLAGSET(dwOptions, DELETE_MESSAGE_NOPROMPT))
  1019. {
  1020. // Get a Parent Hwnd
  1021. Assert(pCallback);
  1022. // Get Parent Window
  1023. if (FAILED(pCallback->GetParentWindow(0, &hwndParent)))
  1024. hwndParent = NULL;
  1025. // Prompt...
  1026. if (IDNO == AthMessageBoxW(hwndParent, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsWarnPermDelete), NULL, MB_YESNO | MB_DEFBUTTON2 | MB_ICONEXCLAMATION ))
  1027. goto exit;
  1028. }
  1029. // If deleting messages from local folder, update the uidl cache.
  1030. if (FOLDER_LOCAL == m_tyFolder && !ISFLAGSET(dwOptions, DELETE_MESSAGE_NOUIDLUPDATE))
  1031. {
  1032. // Open the UIDL Cache
  1033. IF_FAILEXIT(hr = OpenUidlCache(&pUidlDB));
  1034. }
  1035. // No Cancel
  1036. FLAGCLEAR(m_dwState, FOLDER_STATE_CANCEL);
  1037. // Lock Notifications
  1038. IF_FAILEXIT(hr = m_pDB->Lock(&hLock));
  1039. // Need a Rowset
  1040. if (NULL == pList)
  1041. {
  1042. // Create a Rowset
  1043. IF_FAILEXIT(hr = m_pDB->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset));
  1044. // Get Count
  1045. IF_FAILEXIT(hr = m_pDB->GetRecordCount(IINDEX_PRIMARY, &cTotal));
  1046. }
  1047. // Otherwise, set cTotal
  1048. else
  1049. cTotal = pList->cMsgs;
  1050. // User Wants Results ?
  1051. if (pResults)
  1052. {
  1053. // Zero Init
  1054. ZeroMemory(pResults, sizeof(RESULTLIST));
  1055. // Return Results
  1056. IF_NULLEXIT(pResults->prgResult = (LPRESULTINFO)ZeroAllocate(cTotal * sizeof(RESULTINFO)));
  1057. // Set cAllocated
  1058. pResults->cAllocated = pResults->cMsgs = cTotal;
  1059. }
  1060. // Loop through the messageIds
  1061. for (i=0;;i++)
  1062. {
  1063. // Done
  1064. if (pList)
  1065. {
  1066. // Done
  1067. if (i >= pList->cMsgs)
  1068. break;
  1069. // Set the MessageId
  1070. Message.idMessage = pList->prgidMsg[i];
  1071. // Look for this record
  1072. IF_FAILEXIT(hr = m_pDB->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL));
  1073. }
  1074. // Otherwise, enumerate next
  1075. else
  1076. {
  1077. // Get the next
  1078. IF_FAILEXIT(hr = m_pDB->QueryRowset(hRowset, 1, (LPVOID *)&Message, NULL));
  1079. // Done
  1080. if (S_FALSE == hr)
  1081. {
  1082. hr = S_OK;
  1083. break;
  1084. }
  1085. // Found
  1086. hr = DB_S_FOUND;
  1087. }
  1088. // Was It Found
  1089. if (DB_S_FOUND == hr)
  1090. {
  1091. // Delete the message
  1092. IF_FAILEXIT(hr = DeleteMessageFromStore(&Message, m_pDB, pUidlDB));
  1093. // Free
  1094. m_pDB->FreeRecord(&Message);
  1095. // Return Result
  1096. if (pResults)
  1097. {
  1098. // hrResult
  1099. pResults->prgResult[i].hrResult = S_OK;
  1100. // Message Id
  1101. pResults->prgResult[i].idMessage = Message.idMessage;
  1102. // Store Falgs
  1103. pResults->prgResult[i].dwFlags = Message.dwFlags;
  1104. // Increment Success
  1105. pResults->cValid++;
  1106. }
  1107. }
  1108. // Otherwise, if pResults
  1109. else if (pResults)
  1110. {
  1111. // Set hr
  1112. pResults->prgResult[i].hrResult = hr;
  1113. // Increment Success
  1114. pResults->cValid++;
  1115. }
  1116. // Increment Progress
  1117. cCurrent++;
  1118. // Update Progress
  1119. if (pCallback)
  1120. {
  1121. // Do some progress
  1122. hrCancel = pCallback->OnProgress(SOT_DELETING_MESSAGES, cCurrent, cTotal, NULL);
  1123. if (FAILED(hrCancel) && E_NOTIMPL != hrCancel)
  1124. break;
  1125. // Cancelled ?
  1126. if (ISFLAGSET(m_dwState, FOLDER_STATE_CANCEL))
  1127. break;
  1128. }
  1129. }
  1130. exit:
  1131. // Deleted All ?
  1132. if (NULL == pList)
  1133. {
  1134. // Get Count
  1135. if (SUCCEEDED(m_pDB->GetRecordCount(IINDEX_PRIMARY, &cTotal)) && 0 == cTotal)
  1136. {
  1137. // Reset The Counts
  1138. ResetFolderCounts(0, 0, 0, 0);
  1139. }
  1140. }
  1141. // Lock Notifications
  1142. m_pDB->Unlock(&hLock);
  1143. // Cleanup
  1144. SafeRelease(pDeleted);
  1145. SafeRelease(pUidlDB);
  1146. m_pDB->CloseRowset(&hRowset);
  1147. m_pDB->FreeRecord(&Message);
  1148. // Done
  1149. return(hr);
  1150. }
  1151. // --------------------------------------------------------------------------------
  1152. // CMessageFolder::_FixupMessageCharset
  1153. // --------------------------------------------------------------------------------
  1154. HRESULT CMessageFolder::_FixupMessageCharset(IMimeMessage *pMessage,
  1155. CODEPAGEID cpCurrent)
  1156. {
  1157. // Locals
  1158. HRESULT hr=S_OK;
  1159. HCHARSET hCharset;
  1160. INETCSETINFO CsetInfo;
  1161. DWORD dwCodePage=0;
  1162. DWORD dwFlags;
  1163. // Trace
  1164. TraceCall("CMessageFolder::_FixupMessageCharset");
  1165. // Invalid Args
  1166. Assert(pMessage);
  1167. // See if we need to apply charset re-mapping
  1168. if (cpCurrent == 0)
  1169. {
  1170. HCHARSET hChar = NULL;
  1171. // Get Flags
  1172. IF_FAILEXIT(hr = pMessage->GetFlags(&dwFlags));
  1173. if(DwGetOption(OPT_INCOMDEFENCODE))
  1174. {
  1175. if (SUCCEEDED(HGetDefaultCharset(&hChar)))
  1176. pMessage->SetCharset(hChar, CSET_APPLY_ALL);
  1177. else
  1178. cpCurrent = GetACP();
  1179. }
  1180. // for tagged message or news only
  1181. else if (ISFLAGSET(dwFlags, IMF_CSETTAGGED))
  1182. {
  1183. // Get the Character SEt
  1184. IF_FAILEXIT(hr= pMessage->GetCharset(&hCharset));
  1185. // Remap the Character Set
  1186. if (hCharset && CheckIntlCharsetMap(hCharset, &dwCodePage))
  1187. cpCurrent = dwCodePage;
  1188. }
  1189. // Check AutoSelect
  1190. else if(CheckAutoSelect((UINT *) &dwCodePage))
  1191. cpCurrent = dwCodePage;
  1192. // The message is not tagged, use the default character set
  1193. else if (SUCCEEDED(HGetDefaultCharset(&hChar)))
  1194. {
  1195. // Change the Character set of the message to default
  1196. pMessage->SetCharset(hChar, CSET_APPLY_ALL);
  1197. }
  1198. }
  1199. // If cpCurrent is set, call SetCharset to change charset
  1200. if (cpCurrent)
  1201. {
  1202. // Get the character set fromt he codepage
  1203. hCharset = GetMimeCharsetFromCodePage(cpCurrent);
  1204. // Modify the Character set of the message
  1205. if (hCharset)
  1206. {
  1207. // SetCharset
  1208. IF_FAILEXIT(hr = pMessage->SetCharset(hCharset, CSET_APPLY_ALL));
  1209. }
  1210. }
  1211. exit:
  1212. // Done
  1213. return(hr);
  1214. }
  1215. // --------------------------------------------------------------------------------
  1216. // CMessageFolder::_GetMsgInfoFromMessage
  1217. // --------------------------------------------------------------------------------
  1218. HRESULT CMessageFolder::_GetMsgInfoFromMessage(IMimeMessage *pMessage,
  1219. LPMESSAGEINFO pInfo)
  1220. {
  1221. // Locals
  1222. HRESULT hr=S_OK;
  1223. DWORD dwImf;
  1224. IMSGPRIORITY priority;
  1225. PROPVARIANT Variant;
  1226. SYSTEMTIME st;
  1227. FILETIME ftCurrent;
  1228. IMimePropertySet *pPropertySet=NULL;
  1229. // Trace
  1230. TraceCall("CMessageFolder::_GetMsgInfoFromMessage");
  1231. // Invalid Args
  1232. Assert(pMessage && pInfo);
  1233. // Get the Root Property Set from the Message
  1234. IF_FAILEXIT(hr = pMessage->BindToObject(HBODY_ROOT, IID_IMimePropertySet, (LPVOID *)&pPropertySet));
  1235. // File pInfo from pPropertySet
  1236. IF_FAILEXIT(hr = _GetMsgInfoFromPropertySet(pPropertySet, pInfo));
  1237. // Get Message Flags
  1238. if (SUCCEEDED(pMessage->GetFlags(&dwImf)))
  1239. pInfo->dwFlags = ConvertIMFFlagsToARF(dwImf);
  1240. // Get the Message Size
  1241. pMessage->GetMessageSize(&pInfo->cbMessage, 0);
  1242. exit:
  1243. // Cleanup
  1244. SafeRelease(pPropertySet);
  1245. // Done
  1246. return(hr);
  1247. }
  1248. //--------------------------------------------------------------------------
  1249. // CMessageFolder:_GetMsgInfoFromPropertySet
  1250. //--------------------------------------------------------------------------
  1251. HRESULT CMessageFolder::_GetMsgInfoFromPropertySet(IMimePropertySet *pPropertySet,
  1252. LPMESSAGEINFO pInfo)
  1253. {
  1254. // Locals
  1255. HRESULT hr=S_OK;
  1256. IMSGPRIORITY priority;
  1257. PROPVARIANT Variant;
  1258. FILETIME ftCurrent;
  1259. IMimeAddressTable *pAdrTable=NULL;
  1260. ADDRESSPROPS rAddress;
  1261. // Trace
  1262. TraceCall("CMessageFolder::_GetMsgInfoFromPropertySet");
  1263. // Invalid Args
  1264. Assert(pPropertySet && pInfo);
  1265. // Default Sent and Received Times...
  1266. GetSystemTimeAsFileTime(&ftCurrent);
  1267. // Set Variant tyStore
  1268. Variant.vt = VT_UI4;
  1269. // Priority
  1270. if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_PRIORITY), 0, &Variant)))
  1271. {
  1272. // Set Priority
  1273. pInfo->wPriority = (WORD)Variant.ulVal;
  1274. }
  1275. // Partial Numbers...
  1276. if (pPropertySet->IsContentType(STR_CNT_MESSAGE, STR_SUB_PARTIAL) == S_OK)
  1277. {
  1278. // Locals
  1279. WORD cParts=0, iPart=0;
  1280. // Get Total
  1281. if (SUCCEEDED(pPropertySet->GetProp(STR_PAR_TOTAL, NOFLAGS, &Variant)))
  1282. cParts = (WORD)Variant.ulVal;
  1283. // Get Number
  1284. if (SUCCEEDED(pPropertySet->GetProp(STR_PAR_NUMBER, NOFLAGS, &Variant)))
  1285. iPart = (WORD)Variant.ulVal;
  1286. // Set Parts
  1287. pInfo->dwPartial = MAKELONG(cParts, iPart);
  1288. }
  1289. // Otherwise, check for user property
  1290. else if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_COMBINED), NOFLAGS, &Variant)))
  1291. {
  1292. // Set the Partial Id
  1293. pInfo->dwPartial = Variant.ulVal;
  1294. }
  1295. // Getting some file times
  1296. Variant.vt = VT_FILETIME;
  1297. // Get Received Time...
  1298. if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_RECVTIME), 0, &Variant)))
  1299. pInfo->ftReceived = Variant.filetime;
  1300. else
  1301. pInfo->ftReceived = ftCurrent;
  1302. // Get Sent Time...
  1303. if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_SENTTIME), 0, &Variant)))
  1304. pInfo->ftSent = Variant.filetime;
  1305. else
  1306. pInfo->ftSent = ftCurrent;
  1307. // Get Address Table
  1308. IF_FAILEXIT(hr = pPropertySet->BindToObject(IID_IMimeAddressTable, (LPVOID *)&pAdrTable));
  1309. // Display From
  1310. pAdrTable->GetFormat(IAT_FROM, AFT_DISPLAY_FRIENDLY, &pInfo->pszDisplayFrom);
  1311. // Email From
  1312. rAddress.dwProps = IAP_EMAIL;
  1313. if (SUCCEEDED(pAdrTable->GetSender(&rAddress)))
  1314. {
  1315. pInfo->pszEmailFrom = rAddress.pszEmail;
  1316. }
  1317. // Display to
  1318. pAdrTable->GetFormat(IAT_TO, AFT_DISPLAY_FRIENDLY, &pInfo->pszDisplayTo);
  1319. // Email To
  1320. pAdrTable->GetFormat(IAT_TO, AFT_DISPLAY_EMAIL, &pInfo->pszEmailTo);
  1321. // String Properties
  1322. Variant.vt = VT_LPSTR;
  1323. // pszDisplayFrom as newsgroups
  1324. if (NULL == pInfo->pszDisplayTo && SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_HDR_NEWSGROUPS), NOFLAGS, &Variant)))
  1325. pInfo->pszDisplayTo = Variant.pszVal;
  1326. // pszMessageId
  1327. if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_HDR_MESSAGEID), NOFLAGS, &Variant)))
  1328. pInfo->pszMessageId = Variant.pszVal;
  1329. // pszMSOESRec
  1330. if (SUCCEEDED(pPropertySet->GetProp(STR_HDR_XMSOESREC, NOFLAGS, &Variant)))
  1331. pInfo->pszMSOESRec = Variant.pszVal;
  1332. // pszXref
  1333. if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_HDR_XREF), NOFLAGS, &Variant)))
  1334. pInfo->pszXref = Variant.pszVal;
  1335. // pszReferences
  1336. if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(STR_HDR_REFS), NOFLAGS, &Variant)))
  1337. pInfo->pszReferences = Variant.pszVal;
  1338. // pszSubject
  1339. if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, &Variant)))
  1340. pInfo->pszSubject = Variant.pszVal;
  1341. // pszNormalSubj
  1342. if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_NORMSUBJ), NOFLAGS, &Variant)))
  1343. pInfo->pszNormalSubj = Variant.pszVal;
  1344. // pszAcctId
  1345. if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_ACCOUNTID), NOFLAGS, &Variant)))
  1346. pInfo->pszAcctId = Variant.pszVal;
  1347. // pszAcctName
  1348. if (SUCCEEDED(pPropertySet->GetProp(STR_ATT_ACCOUNTNAME, NOFLAGS, &Variant)))
  1349. pInfo->pszAcctName = Variant.pszVal;
  1350. // pszServer
  1351. if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_SERVER), NOFLAGS, &Variant)))
  1352. pInfo->pszServer = Variant.pszVal;
  1353. // pszUidl
  1354. if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_UIDL), NOFLAGS, &Variant)))
  1355. pInfo->pszUidl = Variant.pszVal;
  1356. // pszPartialId
  1357. if (pInfo->dwPartial != 0 && SUCCEEDED(pPropertySet->GetProp(STR_PAR_ID, NOFLAGS, &Variant)))
  1358. pInfo->pszPartialId = Variant.pszVal;
  1359. // ForwardTo
  1360. if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_FORWARDTO), NOFLAGS, &Variant)))
  1361. pInfo->pszForwardTo = Variant.pszVal;
  1362. exit:
  1363. // Cleanup
  1364. SafeRelease(pAdrTable);
  1365. // Done
  1366. return(hr);
  1367. }
  1368. //--------------------------------------------------------------------------
  1369. // CMessageFolder::_FreeMsgInfoData
  1370. //--------------------------------------------------------------------------
  1371. HRESULT CMessageFolder::_FreeMsgInfoData(LPMESSAGEINFO pInfo)
  1372. {
  1373. // Trace
  1374. TraceCall("CMessageFolder::_FreeMsgInfoData");
  1375. // Invalid Args
  1376. Assert(pInfo && NULL == pInfo->pAllocated);
  1377. // Free all the items
  1378. g_pMalloc->Free(pInfo->pszMessageId);
  1379. g_pMalloc->Free(pInfo->pszSubject);
  1380. g_pMalloc->Free(pInfo->pszNormalSubj);
  1381. g_pMalloc->Free(pInfo->pszFromHeader);
  1382. g_pMalloc->Free(pInfo->pszReferences);
  1383. g_pMalloc->Free(pInfo->pszXref);
  1384. g_pMalloc->Free(pInfo->pszServer);
  1385. g_pMalloc->Free(pInfo->pszDisplayFrom);
  1386. g_pMalloc->Free(pInfo->pszEmailFrom);
  1387. g_pMalloc->Free(pInfo->pszDisplayTo);
  1388. g_pMalloc->Free(pInfo->pszEmailTo);
  1389. g_pMalloc->Free(pInfo->pszUidl);
  1390. g_pMalloc->Free(pInfo->pszPartialId);
  1391. g_pMalloc->Free(pInfo->pszForwardTo);
  1392. g_pMalloc->Free(pInfo->pszAcctId);
  1393. g_pMalloc->Free(pInfo->pszAcctName);
  1394. g_pMalloc->Free(pInfo->Offsets.pBlobData);
  1395. g_pMalloc->Free(pInfo->pszMSOESRec);
  1396. // Zero It
  1397. ZeroMemory(pInfo, sizeof(MESSAGEINFO));
  1398. // Done
  1399. return(S_OK);
  1400. }
  1401. //--------------------------------------------------------------------------
  1402. // CMessageFolder::_SetMessageStream
  1403. //--------------------------------------------------------------------------
  1404. HRESULT CMessageFolder::_SetMessageStream(LPMESSAGEINFO pInfo,
  1405. BOOL fUpdateRecord, IStream *pStmSrc)
  1406. {
  1407. // Locals
  1408. HRESULT hr=S_OK;
  1409. FILEADDRESS faStream=0;
  1410. FILEADDRESS faOldStream=0;
  1411. IStream *pStmDst=NULL;
  1412. IDatabaseStream *pDBStream=NULL;
  1413. // Trace
  1414. TraceCall("CMessageFolder::_SetMessageStream");
  1415. // Invalid Args
  1416. Assert(pInfo && pStmSrc);
  1417. // Raid 38276: message moves after being downloaded (don't reset the size if its already set)
  1418. if (0 == pInfo->cbMessage)
  1419. {
  1420. // Get the size of the stream
  1421. IF_FAILEXIT(hr = HrGetStreamSize(pStmSrc, &pInfo->cbMessage));
  1422. }
  1423. // Rewind the source stream
  1424. IF_FAILEXIT(hr = HrRewindStream(pStmSrc));
  1425. // Determine if this is an ObjectDB Stream
  1426. if (SUCCEEDED(pStmSrc->QueryInterface(IID_IDatabaseStream, (LPVOID *)&pDBStream)) && S_OK == pDBStream->CompareDatabase(m_pDB))
  1427. {
  1428. // Get the Stream Id
  1429. pDBStream->GetFileAddress(&faStream);
  1430. }
  1431. // Otherwise, create a stream
  1432. else
  1433. {
  1434. // Create a stream
  1435. IF_FAILEXIT(hr = m_pDB->CreateStream(&faStream));
  1436. // Open the Stream
  1437. IF_FAILEXIT(hr = m_pDB->OpenStream(ACCESS_WRITE, faStream, &pStmDst));
  1438. // Copy the Stream
  1439. IF_FAILEXIT(hr = HrCopyStream(pStmSrc, pStmDst, NULL));
  1440. // Commit
  1441. IF_FAILEXIT(hr = pStmDst->Commit(STGC_DEFAULT));
  1442. }
  1443. // Save the Address of the Old Message Stream Attached to this message
  1444. faOldStream = pInfo->faStream;
  1445. // Update the Message Information
  1446. pInfo->faStream = faStream;
  1447. // Get the time in which the article was downloaded
  1448. GetSystemTimeAsFileTime(&pInfo->ftDownloaded);
  1449. // Has a Body
  1450. FLAGSET(pInfo->dwFlags, ARF_HASBODY);
  1451. // Update the Record ?
  1452. if (fUpdateRecord)
  1453. {
  1454. // Save the new Record
  1455. IF_FAILEXIT(hr = m_pDB->UpdateRecord(pInfo));
  1456. }
  1457. // Don't Free faStream
  1458. faStream = 0;
  1459. exit:
  1460. // If pInfo already has a message sstream,
  1461. if (faOldStream)
  1462. {
  1463. // Free this stream
  1464. SideAssert(SUCCEEDED(m_pDB->DeleteStream(faOldStream)));
  1465. }
  1466. // Failure
  1467. if (faStream)
  1468. {
  1469. // Free this stream
  1470. SideAssert(SUCCEEDED(m_pDB->DeleteStream(faStream)));
  1471. }
  1472. // Cleanup
  1473. SafeRelease(pDBStream);
  1474. SafeRelease(pStmDst);
  1475. // Done
  1476. return hr;
  1477. }
  1478. //--------------------------------------------------------------------------
  1479. // CMessageFolder::Initialize
  1480. //--------------------------------------------------------------------------
  1481. STDMETHODIMP CMessageFolder::Initialize(IDatabase *pDB)
  1482. {
  1483. // Trace
  1484. TraceCall("CMessageFolder::Initialize");
  1485. // Assume the Database from here ?
  1486. if (NULL == m_pDB)
  1487. {
  1488. // Save Database
  1489. m_pDB = pDB;
  1490. }
  1491. // Only if there is a global store...
  1492. if (NULL == m_pStore && g_pStore)
  1493. {
  1494. // Locals
  1495. FOLDERINFO Folder;
  1496. FOLDERUSERDATA UserData;
  1497. // Get the user data
  1498. m_pDB->GetUserData(&UserData, sizeof(FOLDERUSERDATA));
  1499. // Get Folder Info
  1500. if (UserData.fInitialized && SUCCEEDED(g_pStore->GetFolderInfo(UserData.idFolder, &Folder)))
  1501. {
  1502. // AddRef g_pStore
  1503. m_pStore = g_pStore;
  1504. // AddRef it
  1505. m_pStore->AddRef();
  1506. // Save My Folder Id
  1507. m_idFolder = Folder.idFolder;
  1508. // Save m_tyFolder
  1509. m_tyFolder = Folder.tyFolder;
  1510. // Save m_tySpecial
  1511. m_tySpecial = Folder.tySpecial;
  1512. // Free Folder
  1513. g_pStore->FreeRecord(&Folder);
  1514. }
  1515. }
  1516. // Done
  1517. return(S_OK);
  1518. }
  1519. //--------------------------------------------------------------------------
  1520. // CMessageFolder::OnLock
  1521. //--------------------------------------------------------------------------
  1522. STDMETHODIMP CMessageFolder::OnLock(void)
  1523. {
  1524. // Trace
  1525. TraceCall("CMessageFolder::OnLock");
  1526. // Validate
  1527. Assert(0 == m_OnLock.cLocked ? (0 == m_OnLock.lMsgs && 0 == m_OnLock.lUnread && 0 == m_OnLock.lWatchedUnread) : TRUE);
  1528. // Increment cLock
  1529. m_OnLock.cLocked++;
  1530. // Done
  1531. return(S_OK);
  1532. }
  1533. //--------------------------------------------------------------------------
  1534. // CMessageFolder::OnUnlock
  1535. //--------------------------------------------------------------------------
  1536. STDMETHODIMP CMessageFolder::OnUnlock(void)
  1537. {
  1538. // Trace
  1539. TraceCall("CMessageFolder::OnUnlock");
  1540. // Increment cLock
  1541. m_OnLock.cLocked--;
  1542. // If zero, then lets flush counts...
  1543. if (0 == m_OnLock.cLocked)
  1544. {
  1545. // Do we have a folder ?
  1546. if (FOLDERID_INVALID != m_idFolder && m_pStore)
  1547. {
  1548. // Update Folder Counts
  1549. m_pStore->UpdateFolderCounts(m_idFolder, m_OnLock.lMsgs, m_OnLock.lUnread, m_OnLock.lWatchedUnread, m_OnLock.lWatched);
  1550. }
  1551. // Zero OnLock
  1552. ZeroMemory(&m_OnLock, sizeof(ONLOCKINFO));
  1553. }
  1554. // Done
  1555. return(S_OK);
  1556. }
  1557. //--------------------------------------------------------------------------
  1558. // CMessageFolder::OnInsertRecord
  1559. //--------------------------------------------------------------------------
  1560. STDMETHODIMP CMessageFolder::OnRecordInsert(OPERATIONSTATE tyState,
  1561. LPORDINALLIST pOrdinals, LPVOID pRecord)
  1562. {
  1563. // Locals
  1564. HRESULT hr;
  1565. MESSAGEFLAGS dwFlags;
  1566. LPMESSAGEINFO pMessage=(LPMESSAGEINFO)pRecord;
  1567. // Trace
  1568. TraceCall("CMessageFolder::OnInsertRecord");
  1569. // Validate
  1570. Assert(pRecord && m_OnLock.cLocked > 0);
  1571. // Before
  1572. if (OPERATION_BEFORE == tyState)
  1573. {
  1574. // If not Watched and Not ignored...
  1575. if (!ISFLAGSET(pMessage->dwFlags, ARF_WATCH) && !ISFLAGSET(pMessage->dwFlags, ARF_IGNORE))
  1576. {
  1577. // Get Flags
  1578. if (DB_S_FOUND == _GetWatchIgnoreParentFlags(pMessage->pszReferences, pMessage->pszNormalSubj, &dwFlags))
  1579. {
  1580. // Set Watched
  1581. if (ISFLAGSET(dwFlags, ARF_WATCH))
  1582. FLAGSET(pMessage->dwFlags, ARF_WATCH);
  1583. else if (ISFLAGSET(dwFlags, ARF_IGNORE))
  1584. FLAGSET(pMessage->dwFlags, ARF_IGNORE);
  1585. }
  1586. }
  1587. }
  1588. // After
  1589. else if (OPERATION_AFTER == tyState)
  1590. {
  1591. // One more message...
  1592. m_OnLock.lMsgs++;
  1593. // Watched
  1594. if (ISFLAGSET(pMessage->dwFlags, ARF_WATCH))
  1595. m_OnLock.lWatched++;
  1596. // On more unread...
  1597. if (FALSE == ISFLAGSET(pMessage->dwFlags, ARF_READ))
  1598. {
  1599. // Total Unread
  1600. m_OnLock.lUnread++;
  1601. // Watched ?
  1602. if (ISFLAGSET(pMessage->dwFlags, ARF_WATCH))
  1603. m_OnLock.lWatchedUnread++;
  1604. }
  1605. }
  1606. // Done
  1607. return(S_OK);
  1608. }
  1609. //--------------------------------------------------------------------------
  1610. // CMessageFolder::OnUpdateRecord
  1611. //--------------------------------------------------------------------------
  1612. STDMETHODIMP CMessageFolder::OnRecordUpdate(OPERATIONSTATE tyState,
  1613. LPORDINALLIST pOrdinals, LPVOID pRecordOld, LPVOID pRecordNew)
  1614. {
  1615. // Locals
  1616. HRESULT hr=S_OK;
  1617. LONG lUnread=0;
  1618. ROWORDINAL iOrdinal1;
  1619. ROWORDINAL iOrdinal2;
  1620. LPMESSAGEINFO pMsgOld = (LPMESSAGEINFO)pRecordOld;
  1621. LPMESSAGEINFO pMsgNew = (LPMESSAGEINFO)pRecordNew;
  1622. // Trace
  1623. TraceCall("CMessageFolder::OnRecordUpdate");
  1624. // Validate
  1625. Assert(pRecordOld && pRecordNew && m_OnLock.cLocked > 0);
  1626. // After
  1627. if (OPERATION_AFTER == tyState)
  1628. {
  1629. // One less Unread Message
  1630. if (!ISFLAGSET(pMsgOld->dwFlags, ARF_READ) && ISFLAGSET(pMsgNew->dwFlags, ARF_READ))
  1631. lUnread = -1;
  1632. // Otherwise...new unread
  1633. else if (ISFLAGSET(pMsgOld->dwFlags, ARF_READ) && !ISFLAGSET(pMsgNew->dwFlags, ARF_READ))
  1634. lUnread = 1;
  1635. // Update m_OnLock
  1636. m_OnLock.lUnread += lUnread;
  1637. // Old was Watched new is not watched
  1638. if (ISFLAGSET(pMsgOld->dwFlags, ARF_WATCH) && !ISFLAGSET(pMsgNew->dwFlags, ARF_WATCH))
  1639. {
  1640. // Total Watched
  1641. m_OnLock.lWatched--;
  1642. // Unread
  1643. if (!ISFLAGSET(pMsgOld->dwFlags, ARF_READ))
  1644. m_OnLock.lWatchedUnread--;
  1645. }
  1646. // Otherwise, Old was not watched and new message is watched
  1647. else if (!ISFLAGSET(pMsgOld->dwFlags, ARF_WATCH) && ISFLAGSET(pMsgNew->dwFlags, ARF_WATCH))
  1648. {
  1649. // Total Watched
  1650. m_OnLock.lWatched++;
  1651. // Unread
  1652. if (!ISFLAGSET(pMsgNew->dwFlags, ARF_READ))
  1653. m_OnLock.lWatchedUnread++;
  1654. }
  1655. // Otherwise, old was watched, new is watched, then just adjust the unread count
  1656. else if (ISFLAGSET(pMsgOld->dwFlags, ARF_WATCH) && ISFLAGSET(pMsgNew->dwFlags, ARF_WATCH))
  1657. m_OnLock.lWatchedUnread += lUnread;
  1658. }
  1659. // Done
  1660. return(hr);
  1661. }
  1662. //--------------------------------------------------------------------------
  1663. // CMessageFolder::OnDeleteRecord
  1664. //--------------------------------------------------------------------------
  1665. STDMETHODIMP CMessageFolder::OnRecordDelete(OPERATIONSTATE tyState,
  1666. LPORDINALLIST pOrdinals, LPVOID pRecord)
  1667. {
  1668. // Locals
  1669. LPMESSAGEINFO pMessage=(LPMESSAGEINFO)pRecord;
  1670. // Trace
  1671. TraceCall("CMessageFolder::OnDeleteRecord");
  1672. // Validate
  1673. Assert(pRecord && m_OnLock.cLocked > 0);
  1674. // After
  1675. if (OPERATION_AFTER == tyState)
  1676. {
  1677. // One less message
  1678. m_OnLock.lMsgs--;
  1679. // Watched
  1680. if (ISFLAGSET(pMessage->dwFlags, ARF_WATCH))
  1681. m_OnLock.lWatched--;
  1682. // Read State Change
  1683. if (FALSE == ISFLAGSET(pMessage->dwFlags, ARF_READ))
  1684. {
  1685. // Total Unread
  1686. m_OnLock.lUnread--;
  1687. // Watched
  1688. if (ISFLAGSET(pMessage->dwFlags, ARF_WATCH))
  1689. m_OnLock.lWatchedUnread--;
  1690. }
  1691. }
  1692. // Done
  1693. return(S_OK);
  1694. }
  1695. //--------------------------------------------------------------------------
  1696. // CMessageFolder::OnExecuteMethod
  1697. //--------------------------------------------------------------------------
  1698. STDMETHODIMP CMessageFolder::OnExecuteMethod(METHODID idMethod, LPVOID pBinding,
  1699. LPDWORD pdwResult)
  1700. {
  1701. // Locals
  1702. FILETIME ftCurrent;
  1703. LPMESSAGEINFO pMessage=(LPMESSAGEINFO)pBinding;
  1704. // Validate
  1705. Assert(METHODID_MESSAGEAGEINDAYS == idMethod);
  1706. // Get system time as filetime
  1707. GetSystemTimeAsFileTime(&ftCurrent);
  1708. // Convert st to seconds since Jan 1, 1996
  1709. *pdwResult = (UlDateDiff(&pMessage->ftSent, &ftCurrent) / SECONDS_INA_DAY);
  1710. // Done
  1711. return(S_OK);
  1712. }