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
2114 lines
60 KiB
//--------------------------------------------------------------------------
|
|
// MsgFldr.cpp
|
|
//--------------------------------------------------------------------------
|
|
#include "pch.hxx"
|
|
#include "store.h"
|
|
#include "instance.h"
|
|
#include "msgfldr.h"
|
|
#include "secutil.h"
|
|
#include "storutil.h"
|
|
#include "shared.h"
|
|
#include "flagconv.h"
|
|
#include "qstrcmpi.h"
|
|
#include "xpcomm.h"
|
|
#include "msgtable.h"
|
|
#include "shlwapip.h"
|
|
#include <oerules.h>
|
|
#include <ruleutil.h>
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Watch/Ignore Index Filter
|
|
//--------------------------------------------------------------------------
|
|
static const char c_szWatchIgnoreFilter[] = "((MSGCOL_FLAGS & ARF_WATCH) != 0 || (MSGCOL_FLAGS & ARF_IGNORE) != 0)";
|
|
|
|
//--------------------------------------------------------------------------
|
|
// GETWATCHIGNOREPARENT
|
|
//--------------------------------------------------------------------------
|
|
typedef struct tagGETWATCHIGNOREPARENT {
|
|
IDatabase *pDatabase;
|
|
HRESULT hrResult;
|
|
MESSAGEINFO Parent;
|
|
} GETWATCHIGNOREPARENT, *LPGETWATCHIGNOREPARENT;
|
|
|
|
//--------------------------------------------------------------------------
|
|
// EnumRefsGetWatchIgnoreParent
|
|
//--------------------------------------------------------------------------
|
|
HRESULT EnumRefsGetWatchIgnoreParent(LPCSTR pszMessageId, DWORD_PTR dwCookie,
|
|
BOOL *pfDone)
|
|
{
|
|
// Locals
|
|
LPGETWATCHIGNOREPARENT pGetParent = (LPGETWATCHIGNOREPARENT)dwCookie;
|
|
|
|
// Trace
|
|
TraceCall("EnumRefsGetWatchIgnoreParent");
|
|
|
|
// Set MessageId
|
|
pGetParent->Parent.pszMessageId = (LPSTR)pszMessageId;
|
|
|
|
// Find pszMessageId in the IINDEX_WATCHIGNORE Index
|
|
pGetParent->hrResult = pGetParent->pDatabase->FindRecord(IINDEX_WATCHIGNORE, 1, &pGetParent->Parent, NULL);
|
|
|
|
// Done
|
|
if (DB_S_FOUND == pGetParent->hrResult)
|
|
{
|
|
// We are done
|
|
*pfDone = TRUE;
|
|
}
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CreateMsgDbExtension
|
|
//--------------------------------------------------------------------------
|
|
HRESULT CreateMsgDbExtension(IUnknown *pUnkOuter, IUnknown **ppUnknown)
|
|
{
|
|
// Trace
|
|
TraceCall("CreateMsgDbExtension");
|
|
|
|
// Invalid Args
|
|
Assert(ppUnknown);
|
|
|
|
// Initialize
|
|
*ppUnknown = NULL;
|
|
|
|
// Create me
|
|
CMessageFolder *pNew = new CMessageFolder();
|
|
if (NULL == pNew)
|
|
return TraceResult(E_OUTOFMEMORY);
|
|
|
|
// Cast to unknown
|
|
*ppUnknown = SAFECAST(pNew, IDatabaseExtension *);
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::CMessageFolder
|
|
//--------------------------------------------------------------------------
|
|
CMessageFolder::CMessageFolder(void)
|
|
{
|
|
TraceCall("CMessageFolder::CMessageFolder");
|
|
#ifndef _WIN64
|
|
Assert(1560 == sizeof(FOLDERUSERDATA));
|
|
#endif // WIN64
|
|
m_cRef = 1;
|
|
m_pStore = NULL;
|
|
m_pDB = NULL;
|
|
m_tyFolder = FOLDER_INVALID;
|
|
m_tySpecial = FOLDER_NOTSPECIAL;
|
|
m_idFolder = FOLDERID_INVALID;
|
|
m_dwState = 0;
|
|
ZeroMemory(&m_OnLock, sizeof(ONLOCKINFO));
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::~CMessageFolder
|
|
//--------------------------------------------------------------------------
|
|
CMessageFolder::~CMessageFolder(void)
|
|
{
|
|
// Trace
|
|
TraceCall("CMessageFolder::~CMessageFolder");
|
|
|
|
// Release the Store
|
|
SafeRelease(m_pStore);
|
|
|
|
// Release the Database Table
|
|
if (ISFLAGSET(m_dwState, FOLDER_STATE_RELEASEDB) && m_pDB)
|
|
{
|
|
m_pDB->Release();
|
|
m_pDB = NULL;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::QueryInterface
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::QueryInterface(REFIID riid, LPVOID *ppv)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
|
|
// Stack
|
|
TraceCall("CMessageFolder::QueryInterface");
|
|
|
|
// Find IID
|
|
if (IID_IUnknown == riid)
|
|
*ppv = (IUnknown *)(IMessageFolder *)this;
|
|
else if (IID_IMessageFolder == riid)
|
|
*ppv = (IMessageFolder *)this;
|
|
else if (IID_IDatabase == riid)
|
|
*ppv = (IDatabase *)this;
|
|
else if (IID_IDatabaseExtension == riid)
|
|
*ppv = (IDatabaseExtension *)this;
|
|
else if (IID_IServiceProvider == riid)
|
|
*ppv = (IServiceProvider *)this;
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
hr = E_NOINTERFACE;
|
|
goto exit;
|
|
}
|
|
|
|
// AddRef It
|
|
((IUnknown *)*ppv)->AddRef();
|
|
|
|
exit:
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::AddRef
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP_(ULONG) CMessageFolder::AddRef(void)
|
|
{
|
|
TraceCall("CMessageFolder::AddRef");
|
|
return InterlockedIncrement(&m_cRef);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::Release
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP_(ULONG) CMessageFolder::Release(void)
|
|
{
|
|
TraceCall("CMessageFolder::Release");
|
|
LONG cRef = InterlockedDecrement(&m_cRef);
|
|
if (0 == cRef)
|
|
delete this;
|
|
return (ULONG)cRef;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::QueryService
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::QueryService(REFGUID guidService, REFIID riid,
|
|
LPVOID *ppvObject)
|
|
{
|
|
// Trace
|
|
TraceCall("CMessageFolder::QueryService");
|
|
|
|
// Just a Query Interface
|
|
return(QueryInterface(riid, ppvObject));
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::Initialize
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::Initialize(IMessageStore *pStore, IMessageServer *pServer,
|
|
OPENFOLDERFLAGS dwFlags, FOLDERID idFolder)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
CHAR szDirectory[MAX_PATH];
|
|
CHAR szFilePath[MAX_PATH + MAX_PATH];
|
|
FOLDERINFO Folder={0};
|
|
FOLDERUSERDATA UserData={0};
|
|
|
|
// Trace
|
|
TraceCall("CMessageFolder::Initialize");
|
|
|
|
// Invalid Args
|
|
if (NULL == pStore)
|
|
return TraceResult(E_INVALIDARG);
|
|
|
|
// Save the FolderId
|
|
m_idFolder = idFolder;
|
|
|
|
// Save pStore (This must happen before m_pDB->Open happens)
|
|
m_pStore = pStore;
|
|
m_pStore->AddRef();
|
|
|
|
// Find the Folder Information
|
|
IF_FAILEXIT(hr = pStore->GetFolderInfo(idFolder, &Folder));
|
|
|
|
// Make Folder File Path
|
|
IF_FAILEXIT(hr = pStore->GetDirectory(szDirectory, ARRAYSIZE(szDirectory)));
|
|
|
|
// No Folder File Yet ?
|
|
if (FIsEmptyA(Folder.pszFile))
|
|
{
|
|
// Don't Create
|
|
if (ISFLAGSET(dwFlags, OPEN_FOLDER_NOCREATE))
|
|
{
|
|
hr = STORE_E_FILENOEXIST;
|
|
goto exit;
|
|
}
|
|
|
|
// Build Friendly Name
|
|
IF_FAILEXIT(hr = BuildFriendlyFolderFileName(szDirectory, &Folder, szFilePath, ARRAYSIZE(szFilePath), NULL, NULL));
|
|
|
|
// Get the new pszFile...
|
|
Folder.pszFile = PathFindFileName(szFilePath);
|
|
|
|
// Update the Record
|
|
IF_FAILEXIT(hr = pStore->UpdateRecord(&Folder));
|
|
}
|
|
|
|
// Otherwise, build the filepath
|
|
else
|
|
{
|
|
// Make File Path
|
|
IF_FAILEXIT(hr = MakeFilePath(szDirectory, Folder.pszFile, c_szEmpty, szFilePath, ARRAYSIZE(szFilePath)));
|
|
}
|
|
|
|
// If the file doesn't exist...
|
|
if (FALSE == PathFileExists(szFilePath))
|
|
{
|
|
// Reset the Folder Counts...
|
|
Folder.cMessages = 0;
|
|
Folder.dwClientHigh = 0;
|
|
Folder.dwClientLow = 0;
|
|
Folder.cUnread = 0;
|
|
Folder.cWatched = 0;
|
|
Folder.cWatchedUnread = 0;
|
|
Folder.dwServerHigh = 0;
|
|
Folder.dwServerLow = 0;
|
|
Folder.dwServerCount = 0;
|
|
Folder.dwStatusMsgDelta = 0;
|
|
Folder.dwStatusUnreadDelta = 0;
|
|
Folder.dwNotDownloaded = 0;
|
|
Folder.dwClientWatchedHigh = 0;
|
|
Folder.Requested.cbSize = 0;
|
|
Folder.Requested.pBlobData = NULL;
|
|
Folder.Read.cbSize = 0;
|
|
Folder.Read.pBlobData = NULL;
|
|
|
|
// Update the Record
|
|
IF_FAILEXIT(hr = pStore->UpdateRecord(&Folder));
|
|
|
|
// No Create ?
|
|
if (ISFLAGSET(dwFlags, OPEN_FOLDER_NOCREATE))
|
|
{
|
|
hr = STORE_E_FILENOEXIST;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
// Save Special Folder Type
|
|
m_tySpecial = Folder.tySpecial;
|
|
|
|
// Save the Folder Type
|
|
m_tyFolder = Folder.tyFolder;
|
|
|
|
// Create a Database Table
|
|
IF_FAILEXIT(hr = g_pDBSession->OpenDatabase(szFilePath, OPEN_DATABASE_NOADDREFEXT, &g_MessageTableSchema, (IDatabaseExtension *)this, &m_pDB));
|
|
|
|
// Release m_pDB
|
|
FLAGSET(m_dwState, FOLDER_STATE_RELEASEDB);
|
|
|
|
// Get the User Data
|
|
IF_FAILEXIT(hr = m_pDB->GetUserData(&UserData, sizeof(FOLDERUSERDATA)));
|
|
|
|
// May not have been initialized yet ?
|
|
if (FALSE == UserData.fInitialized)
|
|
{
|
|
// Locals
|
|
FOLDERINFO Server;
|
|
|
|
// Get the Server Info
|
|
IF_FAILEXIT(hr = GetFolderServer(Folder.idParent, &Server));
|
|
|
|
// Its Initialized
|
|
UserData.fInitialized = TRUE;
|
|
|
|
// Configure the Folder UserData
|
|
UserData.tyFolder = Folder.tyFolder;
|
|
|
|
// Is Special Folder ?
|
|
UserData.tySpecial = Folder.tySpecial;
|
|
|
|
// Copy the Account Id
|
|
StrCpyN(UserData.szAcctId, Server.pszAccountId, ARRAYSIZE(UserData.szAcctId));
|
|
|
|
// Free
|
|
pStore->FreeRecord(&Server);
|
|
|
|
// Store the Folder Name
|
|
StrCpyN(UserData.szFolder, Folder.pszName, ARRAYSIZE(UserData.szFolder));
|
|
|
|
// Set Folder Id
|
|
UserData.idFolder = Folder.idFolder;
|
|
|
|
// Sort Ascending
|
|
UserData.fAscending = FALSE;
|
|
|
|
// No Threading
|
|
UserData.fThreaded = FALSE;
|
|
|
|
// Base Filter
|
|
UserData.ridFilter = (RULEID) IntToPtr(DwGetOption(OPT_VIEW_GLOBAL));
|
|
if ((RULEID_INVALID == UserData.ridFilter) || ((RULEID_VIEW_DOWNLOADED == UserData.ridFilter) && (FOLDER_LOCAL == m_tyFolder)))
|
|
UserData.ridFilter = RULEID_VIEW_ALL;
|
|
|
|
// Hide Deleted Messages
|
|
UserData.fShowDeleted = FALSE;
|
|
|
|
// Hide Deleted Messages
|
|
UserData.fShowReplies = FALSE;
|
|
|
|
// Set Sort Order
|
|
UserData.idSort = COLUMN_RECEIVED;
|
|
|
|
// New thread model
|
|
UserData.fNoIndexes = TRUE;
|
|
|
|
// Set the User Data
|
|
IF_FAILEXIT(hr = m_pDB->SetUserData(&UserData, sizeof(FOLDERUSERDATA)));
|
|
}
|
|
|
|
// Otherwise, fixup cWatchedUnread ?
|
|
else
|
|
{
|
|
// No Indexes
|
|
if (FALSE == UserData.fNoIndexes)
|
|
{
|
|
// Index Ordinals
|
|
const INDEXORDINAL IINDEX_VIEW = 1;
|
|
const INDEXORDINAL IINDEX_MESSAGEID = 3;
|
|
const INDEXORDINAL IINDEX_SUBJECT = 4;
|
|
const INDEXORDINAL IINDEX_THREADS = 5;
|
|
|
|
// Delete the indexes that I don't user anymore
|
|
m_pDB->DeleteIndex(IINDEX_VIEW);
|
|
m_pDB->DeleteIndex(IINDEX_MESSAGEID);
|
|
m_pDB->DeleteIndex(IINDEX_SUBJECT);
|
|
m_pDB->DeleteIndex(IINDEX_THREADS);
|
|
|
|
// Reset fNoIndexes
|
|
UserData.fNoIndexes = TRUE;
|
|
|
|
// Set the User Data
|
|
IF_FAILEXIT(hr = m_pDB->SetUserData(&UserData, sizeof(FOLDERUSERDATA)));
|
|
}
|
|
}
|
|
|
|
// Initialize Watch/Ignore Index
|
|
_InitializeWatchIgnoreIndex();
|
|
|
|
exit:
|
|
// Cleanup
|
|
pStore->FreeRecord(&Folder);
|
|
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::IsWatched
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::IsWatched(LPCSTR pszReferences,
|
|
LPCSTR pszSubject)
|
|
{
|
|
// Locals
|
|
MESSAGEFLAGS dwFlags;
|
|
|
|
// Trace
|
|
TraceCall("CMessageFolder::IsWatched");
|
|
|
|
// Get Flags
|
|
if (DB_S_FOUND == _GetWatchIgnoreParentFlags(pszReferences, pszSubject, &dwFlags))
|
|
{
|
|
// Watched
|
|
if (ISFLAGSET(dwFlags, ARF_WATCH))
|
|
return(S_OK);
|
|
}
|
|
|
|
// Not Watched
|
|
return(S_FALSE);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::_GetWatchIgnoreParentFlags
|
|
//--------------------------------------------------------------------------
|
|
HRESULT CMessageFolder::_GetWatchIgnoreParentFlags(LPCSTR pszReferences,
|
|
LPCSTR pszSubject, MESSAGEFLAGS *pdwFlags)
|
|
{
|
|
// Locals
|
|
GETWATCHIGNOREPARENT GetParent;
|
|
|
|
// Trace
|
|
TraceCall("CMessageFolder::_GetWatchIgnoreParentFlags");
|
|
|
|
// Init hrResult...
|
|
GetParent.pDatabase = m_pDB;
|
|
GetParent.hrResult = DB_S_NOTFOUND;
|
|
|
|
// EnumerateReferences
|
|
if (SUCCEEDED(EnumerateRefs(pszReferences, (DWORD_PTR)&GetParent, EnumRefsGetWatchIgnoreParent)))
|
|
{
|
|
// If Found
|
|
if (DB_S_FOUND == GetParent.hrResult)
|
|
{
|
|
// Return the Flags
|
|
*pdwFlags = GetParent.Parent.dwFlags;
|
|
|
|
// Free It
|
|
m_pDB->FreeRecord(&GetParent.Parent);
|
|
}
|
|
}
|
|
|
|
// Not Watched
|
|
return(GetParent.hrResult);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::_InitializeWatchIgnoreIndex
|
|
//--------------------------------------------------------------------------
|
|
HRESULT CMessageFolder::_InitializeWatchIgnoreIndex(void)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
BOOL fRebuild=FALSE;
|
|
LPSTR pszFilter=NULL;
|
|
TABLEINDEX Index;
|
|
|
|
// Trace
|
|
TraceCall("CMessageFolder::_InitializeWatchIgnoreIndex");
|
|
|
|
// Reset fRebuild
|
|
fRebuild = FALSE;
|
|
|
|
// Create the Watch Ignore Index
|
|
if (FAILED(m_pDB->GetIndexInfo(IINDEX_WATCHIGNORE, &pszFilter, &Index)))
|
|
fRebuild = TRUE;
|
|
|
|
// Filter Change ?
|
|
else if (NULL == pszFilter || lstrcmpi(pszFilter, c_szWatchIgnoreFilter) != 0)
|
|
fRebuild = TRUE;
|
|
|
|
// Otherwise, the index is different
|
|
else if (S_FALSE == CompareTableIndexes(&Index, &g_WatchIgnoreIndex))
|
|
fRebuild = TRUE;
|
|
|
|
// Rebuild It ?
|
|
if (fRebuild)
|
|
{
|
|
// Create the Index
|
|
IF_FAILEXIT(hr = m_pDB->ModifyIndex(IINDEX_WATCHIGNORE, c_szWatchIgnoreFilter, &g_WatchIgnoreIndex));
|
|
}
|
|
|
|
exit:
|
|
// Cleanup
|
|
SafeMemFree(pszFilter);
|
|
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::GetFolderId
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::GetFolderId(LPFOLDERID pidFolder)
|
|
{
|
|
// Trace
|
|
TraceCall("CMessageFolder::GetFolderId");
|
|
|
|
// Invalid Args
|
|
if (NULL == pidFolder)
|
|
return TraceResult(E_INVALIDARG);
|
|
|
|
// Return the FolderId
|
|
*pidFolder = m_idFolder;
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::GetMessageFolderId
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::GetMessageFolderId(MESSAGEID idMessage, LPFOLDERID pidFolder)
|
|
{
|
|
// Trace
|
|
TraceCall("CMessageFolder::GetFolderId");
|
|
|
|
// Invalid Args
|
|
if (NULL == pidFolder)
|
|
return TraceResult(E_INVALIDARG);
|
|
|
|
// Return the FolderId
|
|
*pidFolder = m_idFolder;
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::OpenMessage
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::OpenMessage(MESSAGEID idMessage,
|
|
OPENMESSAGEFLAGS dwFlags, IMimeMessage **ppMessage,
|
|
IStoreCallback *pCallback)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
IMimeMessage *pMessage=NULL;
|
|
MESSAGEINFO Message={0};
|
|
PROPVARIANT Variant;
|
|
IStream *pStream=NULL;
|
|
|
|
// Trace
|
|
TraceCall("CMessageFolder::OpenMessage");
|
|
|
|
// Invalid Args
|
|
if (NULL == ppMessage)
|
|
return TraceResult(E_INVALIDARG);
|
|
|
|
// Initiailize
|
|
*ppMessage = NULL;
|
|
|
|
// Initialize Message with the Id
|
|
Message.idMessage = idMessage;
|
|
|
|
// Find the Row
|
|
IF_FAILEXIT(hr = m_pDB->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL));
|
|
|
|
// Does we have it ?
|
|
if (DB_S_NOTFOUND == hr)
|
|
{
|
|
hr = TraceResult(DB_E_NOTFOUND);
|
|
goto exit;
|
|
}
|
|
|
|
// Has Expired?
|
|
if (Message.dwFlags & ARF_ARTICLE_EXPIRED)
|
|
{
|
|
hr = STORE_E_EXPIRED;
|
|
goto exit;
|
|
}
|
|
|
|
// No Body ?
|
|
if (0 == Message.faStream)
|
|
{
|
|
hr = STORE_E_NOBODY;
|
|
goto exit;
|
|
}
|
|
|
|
// Create a Message
|
|
IF_FAILEXIT(hr = MimeOleCreateMessage(NULL, &pMessage));
|
|
|
|
// Open the Stream from the Store
|
|
IF_FAILEXIT(hr = m_pDB->OpenStream(ACCESS_READ, Message.faStream, &pStream));
|
|
|
|
// If there is an offset table
|
|
if (Message.Offsets.cbSize > 0)
|
|
{
|
|
// Create a ByteStream Object
|
|
CByteStream cByteStm(Message.Offsets.pBlobData, Message.Offsets.cbSize);
|
|
|
|
// Load the Offset Table Into the message
|
|
pMessage->LoadOffsetTable(&cByteStm);
|
|
|
|
// Take the bytes back out of the bytestream object (so that it doesn't try to free it)
|
|
cByteStm.AcquireBytes(&Message.Offsets.cbSize, &Message.Offsets.pBlobData, ACQ_DISPLACE);
|
|
}
|
|
|
|
// Load the pMessage
|
|
IF_FAILEXIT(hr = pMessage->Load(pStream));
|
|
|
|
// Undo security enhancements if the caller wants us to
|
|
if (!ISFLAGSET(dwFlags, OPEN_MESSAGE_SECURE))
|
|
{
|
|
// Handle Message Security
|
|
IF_FAILEXIT(hr = HandleSecurity(NULL, pMessage));
|
|
}
|
|
|
|
// All Props are VT_LPSTR
|
|
Variant.vt = VT_LPSTR;
|
|
|
|
// MUD_SERVER
|
|
if (Message.pszServer)
|
|
{
|
|
Variant.pszVal = Message.pszServer;
|
|
pMessage->SetProp(PIDTOSTR(PID_ATT_SERVER), 0, &Variant);
|
|
}
|
|
|
|
// PID_ATT_ACCOUNTID
|
|
if (Message.pszAcctId)
|
|
{
|
|
Variant.pszVal = Message.pszAcctId;
|
|
pMessage->SetProp(PIDTOSTR(PID_ATT_ACCOUNTID), 0, &Variant);
|
|
}
|
|
|
|
// PID_ATT_ACCOUNTID
|
|
if (Message.pszAcctName)
|
|
{
|
|
Variant.pszVal = Message.pszAcctName;
|
|
pMessage->SetProp(STR_ATT_ACCOUNTNAME, 0, &Variant);
|
|
}
|
|
|
|
// Otherwise, if there is an account id... lets get the account name
|
|
else if (Message.pszAcctId)
|
|
{
|
|
// Locals
|
|
IImnAccount *pAccount=NULL;
|
|
CHAR szName[CCHMAX_ACCOUNT_NAME];
|
|
|
|
// Find an Account
|
|
if (g_pAcctMan && SUCCEEDED(g_pAcctMan->FindAccount(AP_ACCOUNT_ID, Message.pszAcctId, &pAccount)))
|
|
{
|
|
// Get the Account name
|
|
if (SUCCEEDED(pAccount->GetPropSz(AP_ACCOUNT_NAME, szName, ARRAYSIZE(szName))))
|
|
{
|
|
Variant.pszVal = szName;
|
|
pMessage->SetProp(STR_ATT_ACCOUNTNAME, 0, &Variant);
|
|
}
|
|
|
|
// Release
|
|
pAccount->Release();
|
|
}
|
|
}
|
|
|
|
// PID_ATT_UIDL
|
|
if (Message.pszUidl)
|
|
{
|
|
Variant.pszVal = Message.pszUidl;
|
|
pMessage->SetProp(PIDTOSTR(PID_ATT_UIDL), 0, &Variant);
|
|
}
|
|
|
|
// PID_ATT_FORWARDTO
|
|
if (Message.pszForwardTo)
|
|
{
|
|
Variant.pszVal = Message.pszForwardTo;
|
|
pMessage->SetProp(PIDTOSTR(PID_ATT_FORWARDTO), 0, &Variant);
|
|
}
|
|
|
|
// PID_HDR_XUNSENT
|
|
if (ISFLAGSET(Message.dwFlags, ARF_UNSENT))
|
|
{
|
|
Variant.pszVal = "1";
|
|
pMessage->SetProp(PIDTOSTR(PID_HDR_XUNSENT), 0, &Variant);
|
|
}
|
|
|
|
// Fixup Character Set
|
|
IF_FAILEXIT(hr = _FixupMessageCharset(pMessage, (CODEPAGEID)Message.wLanguage));
|
|
|
|
// Clear Dirty
|
|
MimeOleClearDirtyTree(pMessage);
|
|
|
|
// Return pMessage
|
|
*ppMessage = pMessage;
|
|
pMessage = NULL;
|
|
|
|
exit:
|
|
// Free Records
|
|
m_pDB->FreeRecord(&Message);
|
|
|
|
// Release
|
|
SafeRelease(pMessage);
|
|
SafeRelease(pStream);
|
|
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::SaveMessage
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::SaveMessage(LPMESSAGEID pidMessage,
|
|
SAVEMESSAGEFLAGS dwOptions, MESSAGEFLAGS dwFlags,
|
|
IStream *pStream, IMimeMessage *pMessage, IStoreCallback *pCallback)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
IStream *pSource=NULL;
|
|
CByteStream cStream;
|
|
MESSAGEINFO Message={0};
|
|
|
|
// Trace
|
|
TraceCall("CMessageFolder::SaveMessage");
|
|
|
|
// Invalid Args
|
|
if (NULL == pMessage)
|
|
return TraceResult(E_INVALIDARG);
|
|
|
|
if (NULL == pidMessage && !ISFLAGSET(dwOptions, SAVE_MESSAGE_GENID))
|
|
return TraceResult(E_INVALIDARG);
|
|
|
|
// Get Message from the Message
|
|
IF_FAILEXIT(hr = _GetMsgInfoFromMessage(pMessage, &Message));
|
|
|
|
// Validate or Generate a message id
|
|
if (ISFLAGSET(dwOptions, SAVE_MESSAGE_GENID))
|
|
{
|
|
// Generate Unique Message Id
|
|
IF_FAILEXIT(hr = m_pDB->GenerateId((LPDWORD)&Message.idMessage));
|
|
|
|
// Return It ?
|
|
if (pidMessage)
|
|
*pidMessage = Message.idMessage;
|
|
}
|
|
|
|
// Otherwise, just use idMessage
|
|
else
|
|
Message.idMessage = *pidMessage;
|
|
|
|
// Set the Message Flags
|
|
Message.dwFlags |= dwFlags;
|
|
|
|
// Do I need to store the message stream...
|
|
if (NULL == pStream)
|
|
{
|
|
// Get the Message Stream From the Message
|
|
IF_FAILEXIT(hr = pMessage->GetMessageSource(&pSource, COMMIT_ONLYIFDIRTY));
|
|
}
|
|
|
|
// Otherwise, set pSource
|
|
else
|
|
{
|
|
pSource = pStream;
|
|
pSource->AddRef();
|
|
}
|
|
|
|
// Store the Message onto this record
|
|
IF_FAILEXIT(hr = _SetMessageStream(&Message, FALSE, pSource));
|
|
|
|
// Create the offset table
|
|
if (SUCCEEDED(pMessage->SaveOffsetTable(&cStream, 0)))
|
|
{
|
|
// pulls the Bytes out of cByteStm
|
|
cStream.AcquireBytes(&Message.Offsets.cbSize, &Message.Offsets.pBlobData, ACQ_DISPLACE);
|
|
}
|
|
|
|
// Store the Record
|
|
if (FAILED(hr = m_pDB->InsertRecord(&Message)))
|
|
{
|
|
// Trace That
|
|
TraceResult(hr);
|
|
|
|
// A failure here means that the stream's refCount has been incremented, but the message does not reference the stream
|
|
SideAssert(SUCCEEDED(m_pDB->DeleteStream(Message.faStream)));
|
|
|
|
// Done
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
// Free Allocate Message Properties
|
|
_FreeMsgInfoData(&Message);
|
|
|
|
// Release Message Source IStream
|
|
SafeRelease(pSource);
|
|
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::SetMessageStream
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::SetMessageStream(MESSAGEID idMessage,
|
|
IStream *pStream)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
MESSAGEINFO Message={0};
|
|
|
|
// Trace
|
|
TraceCall("CMessageFolder::SetMessageStream");
|
|
|
|
// Invalid Args
|
|
if (NULL == pStream)
|
|
return TraceResult(E_INVALIDARG);
|
|
|
|
// Set the MsgId
|
|
Message.idMessage = idMessage;
|
|
|
|
// Find the Record
|
|
IF_FAILEXIT(hr = m_pDB->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL));
|
|
|
|
// Store the Stream
|
|
IF_FAILEXIT(hr = _SetMessageStream(&Message, TRUE, pStream));
|
|
|
|
exit:
|
|
// Free the Record
|
|
m_pDB->FreeRecord(&Message);
|
|
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::SetMessageFlags
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::SetMessageFlags(LPMESSAGEIDLIST pList,
|
|
LPADJUSTFLAGS pFlags, LPRESULTLIST pResults,
|
|
IStoreCallback *pCallback)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
DWORD i=0;
|
|
DWORD cWatchedUnread=0;
|
|
DWORD cWatched=0;
|
|
MESSAGEINFO Message={0};
|
|
HROWSET hRowset=NULL;
|
|
HLOCK hLock=NULL;
|
|
MESSAGEFLAGS dwFlags;
|
|
DWORD cTotal;
|
|
|
|
// Trace
|
|
TraceCall("CMessageFolder::SetMessageFlags");
|
|
|
|
// Invalid Args
|
|
if (NULL == pFlags)
|
|
return TraceResult(E_INVALIDARG);
|
|
|
|
// Lock Notifications
|
|
IF_FAILEXIT(hr = m_pDB->Lock(&hLock));
|
|
|
|
// Need a Rowset
|
|
if (NULL == pList)
|
|
{
|
|
// Create a Rowset
|
|
IF_FAILEXIT(hr = m_pDB->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset));
|
|
|
|
// Get Count
|
|
IF_FAILEXIT(hr = m_pDB->GetRecordCount(IINDEX_PRIMARY, &cTotal));
|
|
}
|
|
|
|
// Otherwise, set cTotal
|
|
else
|
|
cTotal = pList->cMsgs;
|
|
|
|
// User Wants Results ?
|
|
if (pResults)
|
|
{
|
|
// Zero Init
|
|
ZeroMemory(pResults, sizeof(RESULTLIST));
|
|
|
|
// Return Results
|
|
IF_NULLEXIT(pResults->prgResult = (LPRESULTINFO)ZeroAllocate(cTotal * sizeof(RESULTINFO)));
|
|
|
|
// Set cAllocated
|
|
pResults->cAllocated = pResults->cMsgs = cTotal;
|
|
}
|
|
|
|
// Loop through the messageIds
|
|
for (i=0;;i++)
|
|
{
|
|
// Done
|
|
if (pList)
|
|
{
|
|
// Done
|
|
if (i >= pList->cMsgs)
|
|
break;
|
|
|
|
// Set the MessageId
|
|
Message.idMessage = pList->prgidMsg[i];
|
|
|
|
// Look for this record
|
|
IF_FAILEXIT(hr = m_pDB->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL));
|
|
}
|
|
|
|
// Otherwise, enumerate next
|
|
else
|
|
{
|
|
// Get the next
|
|
IF_FAILEXIT(hr = m_pDB->QueryRowset(hRowset, 1, (LPVOID *)&Message, NULL));
|
|
|
|
// Done
|
|
if (S_FALSE == hr)
|
|
{
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
|
|
// Found
|
|
hr = DB_S_FOUND;
|
|
}
|
|
|
|
// Was It Found
|
|
if (DB_S_FOUND == hr)
|
|
{
|
|
// Save Flags
|
|
dwFlags = Message.dwFlags;
|
|
|
|
// Remove Flags
|
|
FLAGCLEAR(dwFlags, pFlags->dwRemove);
|
|
|
|
// Add Flags
|
|
FLAGSET(dwFlags, pFlags->dwAdd);
|
|
|
|
// if there is a body for this msg, then the download flag can't be on
|
|
if (ISFLAGSET(dwFlags, ARF_DOWNLOAD) && ISFLAGSET(dwFlags, ARF_HASBODY))
|
|
FLAGCLEAR(dwFlags, ARF_DOWNLOAD);
|
|
|
|
// Update All...or no change
|
|
if (Message.dwFlags != dwFlags)
|
|
{
|
|
// Reset the Flags
|
|
Message.dwFlags = dwFlags;
|
|
|
|
// Update the Record
|
|
IF_FAILEXIT(hr = m_pDB->UpdateRecord(&Message));
|
|
}
|
|
|
|
// Count Watched Unread
|
|
if (ISFLAGSET(Message.dwFlags, ARF_WATCH))
|
|
{
|
|
// Count Watched
|
|
cWatched++;
|
|
|
|
// Is unread
|
|
if (!ISFLAGSET(Message.dwFlags, ARF_READ))
|
|
cWatchedUnread++;
|
|
}
|
|
|
|
// Return Result
|
|
if (pResults)
|
|
{
|
|
// hrResult
|
|
pResults->prgResult[i].hrResult = S_OK;
|
|
|
|
// Message Id
|
|
pResults->prgResult[i].idMessage = Message.idMessage;
|
|
|
|
// Store Falgs
|
|
pResults->prgResult[i].dwFlags = Message.dwFlags;
|
|
|
|
// Increment Success
|
|
pResults->cValid++;
|
|
}
|
|
|
|
// Free
|
|
m_pDB->FreeRecord(&Message);
|
|
}
|
|
|
|
// Otherwise, if pResults
|
|
else if (pResults)
|
|
{
|
|
// Set hr
|
|
pResults->prgResult[i].hrResult = hr;
|
|
|
|
// Increment Success
|
|
pResults->cValid++;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
// Reset Folder Counts ?
|
|
if (NULL == pList && ISFLAGSET(pFlags->dwAdd, ARF_READ))
|
|
{
|
|
// Reset Folder Counts
|
|
ResetFolderCounts(i, 0, cWatchedUnread, cWatched);
|
|
}
|
|
|
|
// Unlock the Database
|
|
m_pDB->Unlock(&hLock);
|
|
|
|
// Cleanup
|
|
m_pDB->FreeRecord(&Message);
|
|
m_pDB->CloseRowset(&hRowset);
|
|
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMesageFolder::ResetFolderCounts
|
|
//--------------------------------------------------------------------------
|
|
HRESULT CMessageFolder::ResetFolderCounts(DWORD cMessages, DWORD cUnread,
|
|
DWORD cWatchedUnread, DWORD cWatched)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
FOLDERINFO Folder={0};
|
|
|
|
// Trace
|
|
TraceCall("CMesageFolder::ResetFolderCounts");
|
|
|
|
// Get Folder Info
|
|
IF_FAILEXIT(hr = m_pStore->GetFolderInfo(m_idFolder, &Folder));
|
|
|
|
Folder.cMessages = cMessages;
|
|
Folder.cUnread = cUnread;
|
|
Folder.cWatchedUnread = cWatchedUnread;
|
|
Folder.cWatched = cWatched;
|
|
Folder.dwStatusMsgDelta = 0;
|
|
Folder.dwStatusUnreadDelta = 0;
|
|
|
|
// Update the Record
|
|
IF_FAILEXIT(hr = m_pStore->UpdateRecord(&Folder));
|
|
|
|
exit:
|
|
// Cleanup
|
|
m_pStore->FreeRecord(&Folder);
|
|
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::CopyMessages
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::CopyMessages(IMessageFolder *pDest,
|
|
COPYMESSAGEFLAGS dwOptions, LPMESSAGEIDLIST pList,
|
|
LPADJUSTFLAGS pFlags, LPRESULTLIST pResults,
|
|
IStoreCallback *pCallback)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
HROWSET hRowset=NULL;
|
|
MESSAGEINFO InfoSrc={0};
|
|
MESSAGEINFO InfoDst;
|
|
DWORD i;
|
|
FOLDERID idDstFolder=FOLDERID_INVALID;
|
|
HLOCK hSrcLock=NULL;
|
|
HLOCK hDstLock=NULL;
|
|
|
|
// Trace
|
|
TraceCall("CMessageFolder::CopyMessages");
|
|
|
|
// Invalid Args
|
|
if (NULL == pDest)
|
|
return TraceResult(E_INVALIDARG);
|
|
|
|
// Get Destination Folder Id
|
|
IF_FAILEXIT(hr = pDest->GetFolderId(&idDstFolder));
|
|
|
|
// Same ?
|
|
if (ISFLAGSET(dwOptions, COPY_MESSAGE_MOVE) && m_idFolder == idDstFolder)
|
|
return(S_OK);
|
|
|
|
// Lock current folder
|
|
IF_FAILEXIT(hr = Lock(&hSrcLock));
|
|
|
|
// Lock the Dest
|
|
IF_FAILEXIT(hr = pDest->Lock(&hDstLock));
|
|
|
|
// Need a Rowset
|
|
if (NULL == pList)
|
|
{
|
|
// Create a Rowset
|
|
IF_FAILEXIT(hr = m_pDB->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset));
|
|
}
|
|
|
|
// Loop through the messageIds
|
|
for (i=0;;i++)
|
|
{
|
|
// Done
|
|
if (pList)
|
|
{
|
|
// Done
|
|
if (i >= pList->cMsgs)
|
|
break;
|
|
|
|
// Set the MessageId
|
|
InfoSrc.idMessage = pList->prgidMsg[i];
|
|
|
|
// Look for this record
|
|
IF_FAILEXIT(hr = m_pDB->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &InfoSrc, NULL));
|
|
}
|
|
|
|
// Otherwise, enumerate next
|
|
else
|
|
{
|
|
// Get the next
|
|
IF_FAILEXIT(hr = m_pDB->QueryRowset(hRowset, 1, (LPVOID *)&InfoSrc, NULL));
|
|
|
|
// Done
|
|
if (S_FALSE == hr)
|
|
{
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
|
|
// Found
|
|
hr = DB_S_FOUND;
|
|
}
|
|
|
|
// Was It Found
|
|
if (DB_S_FOUND == hr)
|
|
{
|
|
// Initialize the InfoDst
|
|
CopyMemory(&InfoDst, &InfoSrc, sizeof(MESSAGEINFO));
|
|
|
|
// Kill some fields
|
|
InfoDst.idMessage = 0;
|
|
|
|
// Don't Copy the UIDL...
|
|
if (FALSE == ISFLAGSET(dwOptions, COPY_MESSAGE_MOVE))
|
|
{
|
|
// Clear It Out
|
|
InfoDst.pszUidl = NULL;
|
|
}
|
|
|
|
// Clear a flag
|
|
FLAGCLEAR(InfoDst.dwFlags, ARF_ENDANGERED);
|
|
|
|
// Copy Source Stream
|
|
if (InfoSrc.faStream)
|
|
{
|
|
// Copy the Stream
|
|
IF_FAILEXIT(hr = m_pDB->CopyStream(pDest, InfoSrc.faStream, &InfoDst.faStream));
|
|
}
|
|
|
|
// Adjust Flags
|
|
if (pFlags)
|
|
{
|
|
// Remove the Flags
|
|
FLAGCLEAR(InfoDst.dwFlags, pFlags->dwRemove);
|
|
|
|
// Flags to Add
|
|
FLAGSET(InfoDst.dwFlags, pFlags->dwAdd);
|
|
}
|
|
|
|
// Generate a Message Id
|
|
IF_FAILEXIT(hr = pDest->GenerateId((LPDWORD)&InfoDst.idMessage));
|
|
|
|
// Insert the Record
|
|
IF_FAILEXIT(hr = pDest->InsertRecord(&InfoDst));
|
|
|
|
// Cleanup
|
|
m_pDB->FreeRecord(&InfoSrc);
|
|
}
|
|
}
|
|
|
|
// Delete the Original Array of messages ?
|
|
if (ISFLAGSET(dwOptions, COPY_MESSAGE_MOVE))
|
|
{
|
|
// DeleteMessages
|
|
IF_FAILEXIT(hr = DeleteMessages(DELETE_MESSAGE_NOUIDLUPDATE | DELETE_MESSAGE_NOTRASHCAN | DELETE_MESSAGE_NOPROMPT, pList, pResults, pCallback));
|
|
}
|
|
|
|
exit:
|
|
// Unlock
|
|
Unlock(&hSrcLock);
|
|
pDest->Unlock(&hDstLock);
|
|
|
|
// Cleanup
|
|
m_pDB->CloseRowset(&hRowset);
|
|
m_pDB->FreeRecord(&InfoSrc);
|
|
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::DeleteMessages
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::DeleteMessages(DELETEMESSAGEFLAGS dwOptions,
|
|
LPMESSAGEIDLIST pList, LPRESULTLIST pResults,
|
|
IStoreCallback *pCallback)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
HRESULT hrCancel;
|
|
HROWSET hRowset=NULL;
|
|
MESSAGEINFO Message={0};
|
|
DWORD cTotal;
|
|
DWORD cCurrent=0;
|
|
DWORD i;
|
|
FOLDERID idServer;
|
|
FOLDERID idDeletedItems;
|
|
HLOCK hLock=NULL;
|
|
HWND hwndParent;
|
|
BOOL fOnBegin=FALSE;
|
|
IDatabase *pUidlDB=NULL;
|
|
IMessageFolder *pDeleted=NULL;
|
|
|
|
// Trace
|
|
TraceCall("CMessageFolder::DeleteMessages");
|
|
|
|
// I can't Undelete
|
|
AssertSz(0 == (dwOptions & DELETE_MESSAGE_UNDELETE), "This flag only makes sense for IMAP!");
|
|
|
|
// Am I in the Trash Can ?
|
|
if (!ISFLAGSET(dwOptions, DELETE_MESSAGE_NOTRASHCAN))
|
|
{
|
|
// Not in the deleted items folder
|
|
if (S_FALSE == IsParentDeletedItems(m_idFolder, &idDeletedItems, &idServer))
|
|
{
|
|
// Get the Deleted Items Folder
|
|
IF_FAILEXIT(hr = m_pStore->OpenSpecialFolder(idServer, NULL, FOLDER_DELETED, &pDeleted));
|
|
|
|
// Simply move messages to the deleted items
|
|
IF_FAILEXIT(hr = CopyMessages(pDeleted, COPY_MESSAGE_MOVE, pList, NULL, pResults, pCallback));
|
|
|
|
// Done
|
|
goto exit;
|
|
}
|
|
|
|
// Otherwise, do deleted items
|
|
else
|
|
{
|
|
// Prompt...
|
|
if (FALSE == ISFLAGSET(dwOptions, DELETE_MESSAGE_NOPROMPT))
|
|
{
|
|
// Get a Parent Hwnd
|
|
Assert(pCallback);
|
|
|
|
// Get Parent Window
|
|
if (FAILED(pCallback->GetParentWindow(0, &hwndParent)))
|
|
hwndParent = NULL;
|
|
|
|
// Prompt...
|
|
if (IDNO == AthMessageBoxW(hwndParent, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsWarnPermDelete), NULL, MB_YESNO | MB_DEFBUTTON2 | MB_ICONEXCLAMATION ))
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
else if (!ISFLAGSET(dwOptions, DELETE_MESSAGE_NOPROMPT))
|
|
{
|
|
// Get a Parent Hwnd
|
|
Assert(pCallback);
|
|
|
|
// Get Parent Window
|
|
if (FAILED(pCallback->GetParentWindow(0, &hwndParent)))
|
|
hwndParent = NULL;
|
|
|
|
// Prompt...
|
|
if (IDNO == AthMessageBoxW(hwndParent, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsWarnPermDelete), NULL, MB_YESNO | MB_DEFBUTTON2 | MB_ICONEXCLAMATION ))
|
|
goto exit;
|
|
}
|
|
|
|
// If deleting messages from local folder, update the uidl cache.
|
|
if (FOLDER_LOCAL == m_tyFolder && !ISFLAGSET(dwOptions, DELETE_MESSAGE_NOUIDLUPDATE))
|
|
{
|
|
// Open the UIDL Cache
|
|
IF_FAILEXIT(hr = OpenUidlCache(&pUidlDB));
|
|
}
|
|
|
|
// No Cancel
|
|
FLAGCLEAR(m_dwState, FOLDER_STATE_CANCEL);
|
|
|
|
// Lock Notifications
|
|
IF_FAILEXIT(hr = m_pDB->Lock(&hLock));
|
|
|
|
// Need a Rowset
|
|
if (NULL == pList)
|
|
{
|
|
// Create a Rowset
|
|
IF_FAILEXIT(hr = m_pDB->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset));
|
|
|
|
// Get Count
|
|
IF_FAILEXIT(hr = m_pDB->GetRecordCount(IINDEX_PRIMARY, &cTotal));
|
|
}
|
|
|
|
// Otherwise, set cTotal
|
|
else
|
|
cTotal = pList->cMsgs;
|
|
|
|
// User Wants Results ?
|
|
if (pResults)
|
|
{
|
|
// Zero Init
|
|
ZeroMemory(pResults, sizeof(RESULTLIST));
|
|
|
|
// Return Results
|
|
IF_NULLEXIT(pResults->prgResult = (LPRESULTINFO)ZeroAllocate(cTotal * sizeof(RESULTINFO)));
|
|
|
|
// Set cAllocated
|
|
pResults->cAllocated = pResults->cMsgs = cTotal;
|
|
}
|
|
|
|
// Loop through the messageIds
|
|
for (i=0;;i++)
|
|
{
|
|
// Done
|
|
if (pList)
|
|
{
|
|
// Done
|
|
if (i >= pList->cMsgs)
|
|
break;
|
|
|
|
// Set the MessageId
|
|
Message.idMessage = pList->prgidMsg[i];
|
|
|
|
// Look for this record
|
|
IF_FAILEXIT(hr = m_pDB->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL));
|
|
}
|
|
|
|
// Otherwise, enumerate next
|
|
else
|
|
{
|
|
// Get the next
|
|
IF_FAILEXIT(hr = m_pDB->QueryRowset(hRowset, 1, (LPVOID *)&Message, NULL));
|
|
|
|
// Done
|
|
if (S_FALSE == hr)
|
|
{
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
|
|
// Found
|
|
hr = DB_S_FOUND;
|
|
}
|
|
|
|
// Was It Found
|
|
if (DB_S_FOUND == hr)
|
|
{
|
|
// Delete the message
|
|
IF_FAILEXIT(hr = DeleteMessageFromStore(&Message, m_pDB, pUidlDB));
|
|
|
|
// Free
|
|
m_pDB->FreeRecord(&Message);
|
|
|
|
// Return Result
|
|
if (pResults)
|
|
{
|
|
// hrResult
|
|
pResults->prgResult[i].hrResult = S_OK;
|
|
|
|
// Message Id
|
|
pResults->prgResult[i].idMessage = Message.idMessage;
|
|
|
|
// Store Falgs
|
|
pResults->prgResult[i].dwFlags = Message.dwFlags;
|
|
|
|
// Increment Success
|
|
pResults->cValid++;
|
|
}
|
|
}
|
|
|
|
// Otherwise, if pResults
|
|
else if (pResults)
|
|
{
|
|
// Set hr
|
|
pResults->prgResult[i].hrResult = hr;
|
|
|
|
// Increment Success
|
|
pResults->cValid++;
|
|
}
|
|
|
|
// Increment Progress
|
|
cCurrent++;
|
|
|
|
// Update Progress
|
|
if (pCallback)
|
|
{
|
|
// Do some progress
|
|
hrCancel = pCallback->OnProgress(SOT_DELETING_MESSAGES, cCurrent, cTotal, NULL);
|
|
if (FAILED(hrCancel) && E_NOTIMPL != hrCancel)
|
|
break;
|
|
|
|
// Cancelled ?
|
|
if (ISFLAGSET(m_dwState, FOLDER_STATE_CANCEL))
|
|
break;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
// Deleted All ?
|
|
if (NULL == pList)
|
|
{
|
|
// Get Count
|
|
if (SUCCEEDED(m_pDB->GetRecordCount(IINDEX_PRIMARY, &cTotal)) && 0 == cTotal)
|
|
{
|
|
// Reset The Counts
|
|
ResetFolderCounts(0, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
// Lock Notifications
|
|
m_pDB->Unlock(&hLock);
|
|
|
|
// Cleanup
|
|
SafeRelease(pDeleted);
|
|
SafeRelease(pUidlDB);
|
|
m_pDB->CloseRowset(&hRowset);
|
|
m_pDB->FreeRecord(&Message);
|
|
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// CMessageFolder::_FixupMessageCharset
|
|
// --------------------------------------------------------------------------------
|
|
HRESULT CMessageFolder::_FixupMessageCharset(IMimeMessage *pMessage,
|
|
CODEPAGEID cpCurrent)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
HCHARSET hCharset;
|
|
INETCSETINFO CsetInfo;
|
|
DWORD dwCodePage=0;
|
|
DWORD dwFlags;
|
|
|
|
// Trace
|
|
TraceCall("CMessageFolder::_FixupMessageCharset");
|
|
|
|
// Invalid Args
|
|
Assert(pMessage);
|
|
|
|
// See if we need to apply charset re-mapping
|
|
if (cpCurrent == 0)
|
|
{
|
|
HCHARSET hChar = NULL;
|
|
|
|
// Get Flags
|
|
IF_FAILEXIT(hr = pMessage->GetFlags(&dwFlags));
|
|
|
|
if(DwGetOption(OPT_INCOMDEFENCODE))
|
|
{
|
|
if (SUCCEEDED(HGetDefaultCharset(&hChar)))
|
|
pMessage->SetCharset(hChar, CSET_APPLY_ALL);
|
|
else
|
|
cpCurrent = GetACP();
|
|
}
|
|
// for tagged message or news only
|
|
else if (ISFLAGSET(dwFlags, IMF_CSETTAGGED))
|
|
{
|
|
// Get the Character SEt
|
|
IF_FAILEXIT(hr= pMessage->GetCharset(&hCharset));
|
|
|
|
// Remap the Character Set
|
|
if (hCharset && CheckIntlCharsetMap(hCharset, &dwCodePage))
|
|
cpCurrent = dwCodePage;
|
|
}
|
|
// Check AutoSelect
|
|
else if(CheckAutoSelect((UINT *) &dwCodePage))
|
|
cpCurrent = dwCodePage;
|
|
|
|
// The message is not tagged, use the default character set
|
|
else if (SUCCEEDED(HGetDefaultCharset(&hChar)))
|
|
{
|
|
// Change the Character set of the message to default
|
|
pMessage->SetCharset(hChar, CSET_APPLY_ALL);
|
|
}
|
|
}
|
|
|
|
// If cpCurrent is set, call SetCharset to change charset
|
|
if (cpCurrent)
|
|
{
|
|
// Get the character set fromt he codepage
|
|
hCharset = GetMimeCharsetFromCodePage(cpCurrent);
|
|
|
|
// Modify the Character set of the message
|
|
if (hCharset)
|
|
{
|
|
// SetCharset
|
|
IF_FAILEXIT(hr = pMessage->SetCharset(hCharset, CSET_APPLY_ALL));
|
|
}
|
|
}
|
|
|
|
exit:
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// CMessageFolder::_GetMsgInfoFromMessage
|
|
// --------------------------------------------------------------------------------
|
|
HRESULT CMessageFolder::_GetMsgInfoFromMessage(IMimeMessage *pMessage,
|
|
LPMESSAGEINFO pInfo)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
DWORD dwImf;
|
|
IMSGPRIORITY priority;
|
|
PROPVARIANT Variant;
|
|
SYSTEMTIME st;
|
|
FILETIME ftCurrent;
|
|
IMimePropertySet *pPropertySet=NULL;
|
|
|
|
// Trace
|
|
TraceCall("CMessageFolder::_GetMsgInfoFromMessage");
|
|
|
|
// Invalid Args
|
|
Assert(pMessage && pInfo);
|
|
|
|
// Get the Root Property Set from the Message
|
|
IF_FAILEXIT(hr = pMessage->BindToObject(HBODY_ROOT, IID_IMimePropertySet, (LPVOID *)&pPropertySet));
|
|
|
|
// File pInfo from pPropertySet
|
|
IF_FAILEXIT(hr = _GetMsgInfoFromPropertySet(pPropertySet, pInfo));
|
|
|
|
// Get Message Flags
|
|
if (SUCCEEDED(pMessage->GetFlags(&dwImf)))
|
|
pInfo->dwFlags = ConvertIMFFlagsToARF(dwImf);
|
|
|
|
// Get the Message Size
|
|
pMessage->GetMessageSize(&pInfo->cbMessage, 0);
|
|
|
|
exit:
|
|
// Cleanup
|
|
SafeRelease(pPropertySet);
|
|
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder:_GetMsgInfoFromPropertySet
|
|
//--------------------------------------------------------------------------
|
|
HRESULT CMessageFolder::_GetMsgInfoFromPropertySet(IMimePropertySet *pPropertySet,
|
|
LPMESSAGEINFO pInfo)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
IMSGPRIORITY priority;
|
|
PROPVARIANT Variant;
|
|
FILETIME ftCurrent;
|
|
IMimeAddressTable *pAdrTable=NULL;
|
|
ADDRESSPROPS rAddress;
|
|
|
|
// Trace
|
|
TraceCall("CMessageFolder::_GetMsgInfoFromPropertySet");
|
|
|
|
// Invalid Args
|
|
Assert(pPropertySet && pInfo);
|
|
|
|
// Default Sent and Received Times...
|
|
GetSystemTimeAsFileTime(&ftCurrent);
|
|
|
|
// Set Variant tyStore
|
|
Variant.vt = VT_UI4;
|
|
|
|
// Priority
|
|
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_PRIORITY), 0, &Variant)))
|
|
{
|
|
// Set Priority
|
|
pInfo->wPriority = (WORD)Variant.ulVal;
|
|
}
|
|
|
|
// Partial Numbers...
|
|
if (pPropertySet->IsContentType(STR_CNT_MESSAGE, STR_SUB_PARTIAL) == S_OK)
|
|
{
|
|
// Locals
|
|
WORD cParts=0, iPart=0;
|
|
|
|
// Get Total
|
|
if (SUCCEEDED(pPropertySet->GetProp(STR_PAR_TOTAL, NOFLAGS, &Variant)))
|
|
cParts = (WORD)Variant.ulVal;
|
|
|
|
// Get Number
|
|
if (SUCCEEDED(pPropertySet->GetProp(STR_PAR_NUMBER, NOFLAGS, &Variant)))
|
|
iPart = (WORD)Variant.ulVal;
|
|
|
|
// Set Parts
|
|
pInfo->dwPartial = MAKELONG(cParts, iPart);
|
|
}
|
|
|
|
// Otherwise, check for user property
|
|
else if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_COMBINED), NOFLAGS, &Variant)))
|
|
{
|
|
// Set the Partial Id
|
|
pInfo->dwPartial = Variant.ulVal;
|
|
}
|
|
|
|
// Getting some file times
|
|
Variant.vt = VT_FILETIME;
|
|
|
|
// Get Received Time...
|
|
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_RECVTIME), 0, &Variant)))
|
|
pInfo->ftReceived = Variant.filetime;
|
|
else
|
|
pInfo->ftReceived = ftCurrent;
|
|
|
|
// Get Sent Time...
|
|
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_SENTTIME), 0, &Variant)))
|
|
pInfo->ftSent = Variant.filetime;
|
|
else
|
|
pInfo->ftSent = ftCurrent;
|
|
|
|
// Get Address Table
|
|
IF_FAILEXIT(hr = pPropertySet->BindToObject(IID_IMimeAddressTable, (LPVOID *)&pAdrTable));
|
|
|
|
// Display From
|
|
pAdrTable->GetFormat(IAT_FROM, AFT_DISPLAY_FRIENDLY, &pInfo->pszDisplayFrom);
|
|
|
|
// Email From
|
|
rAddress.dwProps = IAP_EMAIL;
|
|
if (SUCCEEDED(pAdrTable->GetSender(&rAddress)))
|
|
{
|
|
pInfo->pszEmailFrom = rAddress.pszEmail;
|
|
}
|
|
|
|
// Display to
|
|
pAdrTable->GetFormat(IAT_TO, AFT_DISPLAY_FRIENDLY, &pInfo->pszDisplayTo);
|
|
|
|
// Email To
|
|
pAdrTable->GetFormat(IAT_TO, AFT_DISPLAY_EMAIL, &pInfo->pszEmailTo);
|
|
|
|
// String Properties
|
|
Variant.vt = VT_LPSTR;
|
|
|
|
// pszDisplayFrom as newsgroups
|
|
if (NULL == pInfo->pszDisplayTo && SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_HDR_NEWSGROUPS), NOFLAGS, &Variant)))
|
|
pInfo->pszDisplayTo = Variant.pszVal;
|
|
|
|
// pszMessageId
|
|
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_HDR_MESSAGEID), NOFLAGS, &Variant)))
|
|
pInfo->pszMessageId = Variant.pszVal;
|
|
|
|
// pszMSOESRec
|
|
if (SUCCEEDED(pPropertySet->GetProp(STR_HDR_XMSOESREC, NOFLAGS, &Variant)))
|
|
pInfo->pszMSOESRec = Variant.pszVal;
|
|
|
|
// pszXref
|
|
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_HDR_XREF), NOFLAGS, &Variant)))
|
|
pInfo->pszXref = Variant.pszVal;
|
|
|
|
// pszReferences
|
|
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(STR_HDR_REFS), NOFLAGS, &Variant)))
|
|
pInfo->pszReferences = Variant.pszVal;
|
|
|
|
// pszSubject
|
|
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, &Variant)))
|
|
pInfo->pszSubject = Variant.pszVal;
|
|
|
|
// pszNormalSubj
|
|
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_NORMSUBJ), NOFLAGS, &Variant)))
|
|
pInfo->pszNormalSubj = Variant.pszVal;
|
|
|
|
// pszAcctId
|
|
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_ACCOUNTID), NOFLAGS, &Variant)))
|
|
pInfo->pszAcctId = Variant.pszVal;
|
|
|
|
// pszAcctName
|
|
if (SUCCEEDED(pPropertySet->GetProp(STR_ATT_ACCOUNTNAME, NOFLAGS, &Variant)))
|
|
pInfo->pszAcctName = Variant.pszVal;
|
|
|
|
// pszServer
|
|
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_SERVER), NOFLAGS, &Variant)))
|
|
pInfo->pszServer = Variant.pszVal;
|
|
|
|
// pszUidl
|
|
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_UIDL), NOFLAGS, &Variant)))
|
|
pInfo->pszUidl = Variant.pszVal;
|
|
|
|
// pszPartialId
|
|
if (pInfo->dwPartial != 0 && SUCCEEDED(pPropertySet->GetProp(STR_PAR_ID, NOFLAGS, &Variant)))
|
|
pInfo->pszPartialId = Variant.pszVal;
|
|
|
|
// ForwardTo
|
|
if (SUCCEEDED(pPropertySet->GetProp(PIDTOSTR(PID_ATT_FORWARDTO), NOFLAGS, &Variant)))
|
|
pInfo->pszForwardTo = Variant.pszVal;
|
|
|
|
exit:
|
|
// Cleanup
|
|
SafeRelease(pAdrTable);
|
|
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::_FreeMsgInfoData
|
|
//--------------------------------------------------------------------------
|
|
HRESULT CMessageFolder::_FreeMsgInfoData(LPMESSAGEINFO pInfo)
|
|
{
|
|
// Trace
|
|
TraceCall("CMessageFolder::_FreeMsgInfoData");
|
|
|
|
// Invalid Args
|
|
Assert(pInfo && NULL == pInfo->pAllocated);
|
|
|
|
// Free all the items
|
|
g_pMalloc->Free(pInfo->pszMessageId);
|
|
g_pMalloc->Free(pInfo->pszSubject);
|
|
g_pMalloc->Free(pInfo->pszNormalSubj);
|
|
g_pMalloc->Free(pInfo->pszFromHeader);
|
|
g_pMalloc->Free(pInfo->pszReferences);
|
|
g_pMalloc->Free(pInfo->pszXref);
|
|
g_pMalloc->Free(pInfo->pszServer);
|
|
g_pMalloc->Free(pInfo->pszDisplayFrom);
|
|
g_pMalloc->Free(pInfo->pszEmailFrom);
|
|
g_pMalloc->Free(pInfo->pszDisplayTo);
|
|
g_pMalloc->Free(pInfo->pszEmailTo);
|
|
g_pMalloc->Free(pInfo->pszUidl);
|
|
g_pMalloc->Free(pInfo->pszPartialId);
|
|
g_pMalloc->Free(pInfo->pszForwardTo);
|
|
g_pMalloc->Free(pInfo->pszAcctId);
|
|
g_pMalloc->Free(pInfo->pszAcctName);
|
|
g_pMalloc->Free(pInfo->Offsets.pBlobData);
|
|
g_pMalloc->Free(pInfo->pszMSOESRec);
|
|
|
|
// Zero It
|
|
ZeroMemory(pInfo, sizeof(MESSAGEINFO));
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::_SetMessageStream
|
|
//--------------------------------------------------------------------------
|
|
HRESULT CMessageFolder::_SetMessageStream(LPMESSAGEINFO pInfo,
|
|
BOOL fUpdateRecord, IStream *pStmSrc)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
FILEADDRESS faStream=0;
|
|
FILEADDRESS faOldStream=0;
|
|
IStream *pStmDst=NULL;
|
|
IDatabaseStream *pDBStream=NULL;
|
|
|
|
// Trace
|
|
TraceCall("CMessageFolder::_SetMessageStream");
|
|
|
|
// Invalid Args
|
|
Assert(pInfo && pStmSrc);
|
|
|
|
// Raid 38276: message moves after being downloaded (don't reset the size if its already set)
|
|
if (0 == pInfo->cbMessage)
|
|
{
|
|
// Get the size of the stream
|
|
IF_FAILEXIT(hr = HrGetStreamSize(pStmSrc, &pInfo->cbMessage));
|
|
}
|
|
|
|
// Rewind the source stream
|
|
IF_FAILEXIT(hr = HrRewindStream(pStmSrc));
|
|
|
|
// Determine if this is an ObjectDB Stream
|
|
if (SUCCEEDED(pStmSrc->QueryInterface(IID_IDatabaseStream, (LPVOID *)&pDBStream)) && S_OK == pDBStream->CompareDatabase(m_pDB))
|
|
{
|
|
// Get the Stream Id
|
|
pDBStream->GetFileAddress(&faStream);
|
|
}
|
|
|
|
// Otherwise, create a stream
|
|
else
|
|
{
|
|
// Create a stream
|
|
IF_FAILEXIT(hr = m_pDB->CreateStream(&faStream));
|
|
|
|
// Open the Stream
|
|
IF_FAILEXIT(hr = m_pDB->OpenStream(ACCESS_WRITE, faStream, &pStmDst));
|
|
|
|
// Copy the Stream
|
|
IF_FAILEXIT(hr = HrCopyStream(pStmSrc, pStmDst, NULL));
|
|
|
|
// Commit
|
|
IF_FAILEXIT(hr = pStmDst->Commit(STGC_DEFAULT));
|
|
}
|
|
|
|
// Save the Address of the Old Message Stream Attached to this message
|
|
faOldStream = pInfo->faStream;
|
|
|
|
// Update the Message Information
|
|
pInfo->faStream = faStream;
|
|
|
|
// Get the time in which the article was downloaded
|
|
GetSystemTimeAsFileTime(&pInfo->ftDownloaded);
|
|
|
|
// Has a Body
|
|
FLAGSET(pInfo->dwFlags, ARF_HASBODY);
|
|
|
|
// Update the Record ?
|
|
if (fUpdateRecord)
|
|
{
|
|
// Save the new Record
|
|
IF_FAILEXIT(hr = m_pDB->UpdateRecord(pInfo));
|
|
}
|
|
|
|
// Don't Free faStream
|
|
faStream = 0;
|
|
|
|
exit:
|
|
// If pInfo already has a message sstream,
|
|
if (faOldStream)
|
|
{
|
|
// Free this stream
|
|
SideAssert(SUCCEEDED(m_pDB->DeleteStream(faOldStream)));
|
|
}
|
|
|
|
// Failure
|
|
if (faStream)
|
|
{
|
|
// Free this stream
|
|
SideAssert(SUCCEEDED(m_pDB->DeleteStream(faStream)));
|
|
}
|
|
|
|
// Cleanup
|
|
SafeRelease(pDBStream);
|
|
SafeRelease(pStmDst);
|
|
|
|
// Done
|
|
return hr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::Initialize
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::Initialize(IDatabase *pDB)
|
|
{
|
|
// Trace
|
|
TraceCall("CMessageFolder::Initialize");
|
|
|
|
// Assume the Database from here ?
|
|
if (NULL == m_pDB)
|
|
{
|
|
// Save Database
|
|
m_pDB = pDB;
|
|
}
|
|
|
|
// Only if there is a global store...
|
|
if (NULL == m_pStore && g_pStore)
|
|
{
|
|
// Locals
|
|
FOLDERINFO Folder;
|
|
FOLDERUSERDATA UserData;
|
|
|
|
// Get the user data
|
|
m_pDB->GetUserData(&UserData, sizeof(FOLDERUSERDATA));
|
|
|
|
// Get Folder Info
|
|
if (UserData.fInitialized && SUCCEEDED(g_pStore->GetFolderInfo(UserData.idFolder, &Folder)))
|
|
{
|
|
// AddRef g_pStore
|
|
m_pStore = g_pStore;
|
|
|
|
// AddRef it
|
|
m_pStore->AddRef();
|
|
|
|
// Save My Folder Id
|
|
m_idFolder = Folder.idFolder;
|
|
|
|
// Save m_tyFolder
|
|
m_tyFolder = Folder.tyFolder;
|
|
|
|
// Save m_tySpecial
|
|
m_tySpecial = Folder.tySpecial;
|
|
|
|
// Free Folder
|
|
g_pStore->FreeRecord(&Folder);
|
|
}
|
|
}
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::OnLock
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::OnLock(void)
|
|
{
|
|
// Trace
|
|
TraceCall("CMessageFolder::OnLock");
|
|
|
|
// Validate
|
|
Assert(0 == m_OnLock.cLocked ? (0 == m_OnLock.lMsgs && 0 == m_OnLock.lUnread && 0 == m_OnLock.lWatchedUnread) : TRUE);
|
|
|
|
// Increment cLock
|
|
m_OnLock.cLocked++;
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::OnUnlock
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::OnUnlock(void)
|
|
{
|
|
// Trace
|
|
TraceCall("CMessageFolder::OnUnlock");
|
|
|
|
// Increment cLock
|
|
m_OnLock.cLocked--;
|
|
|
|
// If zero, then lets flush counts...
|
|
if (0 == m_OnLock.cLocked)
|
|
{
|
|
// Do we have a folder ?
|
|
if (FOLDERID_INVALID != m_idFolder && m_pStore)
|
|
{
|
|
// Update Folder Counts
|
|
m_pStore->UpdateFolderCounts(m_idFolder, m_OnLock.lMsgs, m_OnLock.lUnread, m_OnLock.lWatchedUnread, m_OnLock.lWatched);
|
|
}
|
|
|
|
// Zero OnLock
|
|
ZeroMemory(&m_OnLock, sizeof(ONLOCKINFO));
|
|
}
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::OnInsertRecord
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::OnRecordInsert(OPERATIONSTATE tyState,
|
|
LPORDINALLIST pOrdinals, LPVOID pRecord)
|
|
{
|
|
// Locals
|
|
HRESULT hr;
|
|
MESSAGEFLAGS dwFlags;
|
|
LPMESSAGEINFO pMessage=(LPMESSAGEINFO)pRecord;
|
|
|
|
// Trace
|
|
TraceCall("CMessageFolder::OnInsertRecord");
|
|
|
|
// Validate
|
|
Assert(pRecord && m_OnLock.cLocked > 0);
|
|
|
|
// Before
|
|
if (OPERATION_BEFORE == tyState)
|
|
{
|
|
// If not Watched and Not ignored...
|
|
if (!ISFLAGSET(pMessage->dwFlags, ARF_WATCH) && !ISFLAGSET(pMessage->dwFlags, ARF_IGNORE))
|
|
{
|
|
// Get Flags
|
|
if (DB_S_FOUND == _GetWatchIgnoreParentFlags(pMessage->pszReferences, pMessage->pszNormalSubj, &dwFlags))
|
|
{
|
|
// Set Watched
|
|
if (ISFLAGSET(dwFlags, ARF_WATCH))
|
|
FLAGSET(pMessage->dwFlags, ARF_WATCH);
|
|
else if (ISFLAGSET(dwFlags, ARF_IGNORE))
|
|
FLAGSET(pMessage->dwFlags, ARF_IGNORE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// After
|
|
else if (OPERATION_AFTER == tyState)
|
|
{
|
|
// One more message...
|
|
m_OnLock.lMsgs++;
|
|
|
|
// Watched
|
|
if (ISFLAGSET(pMessage->dwFlags, ARF_WATCH))
|
|
m_OnLock.lWatched++;
|
|
|
|
// On more unread...
|
|
if (FALSE == ISFLAGSET(pMessage->dwFlags, ARF_READ))
|
|
{
|
|
// Total Unread
|
|
m_OnLock.lUnread++;
|
|
|
|
// Watched ?
|
|
if (ISFLAGSET(pMessage->dwFlags, ARF_WATCH))
|
|
m_OnLock.lWatchedUnread++;
|
|
}
|
|
}
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::OnUpdateRecord
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::OnRecordUpdate(OPERATIONSTATE tyState,
|
|
LPORDINALLIST pOrdinals, LPVOID pRecordOld, LPVOID pRecordNew)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
LONG lUnread=0;
|
|
ROWORDINAL iOrdinal1;
|
|
ROWORDINAL iOrdinal2;
|
|
LPMESSAGEINFO pMsgOld = (LPMESSAGEINFO)pRecordOld;
|
|
LPMESSAGEINFO pMsgNew = (LPMESSAGEINFO)pRecordNew;
|
|
|
|
// Trace
|
|
TraceCall("CMessageFolder::OnRecordUpdate");
|
|
|
|
// Validate
|
|
Assert(pRecordOld && pRecordNew && m_OnLock.cLocked > 0);
|
|
|
|
// After
|
|
if (OPERATION_AFTER == tyState)
|
|
{
|
|
// One less Unread Message
|
|
if (!ISFLAGSET(pMsgOld->dwFlags, ARF_READ) && ISFLAGSET(pMsgNew->dwFlags, ARF_READ))
|
|
lUnread = -1;
|
|
|
|
// Otherwise...new unread
|
|
else if (ISFLAGSET(pMsgOld->dwFlags, ARF_READ) && !ISFLAGSET(pMsgNew->dwFlags, ARF_READ))
|
|
lUnread = 1;
|
|
|
|
// Update m_OnLock
|
|
m_OnLock.lUnread += lUnread;
|
|
|
|
// Old was Watched new is not watched
|
|
if (ISFLAGSET(pMsgOld->dwFlags, ARF_WATCH) && !ISFLAGSET(pMsgNew->dwFlags, ARF_WATCH))
|
|
{
|
|
// Total Watched
|
|
m_OnLock.lWatched--;
|
|
|
|
// Unread
|
|
if (!ISFLAGSET(pMsgOld->dwFlags, ARF_READ))
|
|
m_OnLock.lWatchedUnread--;
|
|
}
|
|
|
|
// Otherwise, Old was not watched and new message is watched
|
|
else if (!ISFLAGSET(pMsgOld->dwFlags, ARF_WATCH) && ISFLAGSET(pMsgNew->dwFlags, ARF_WATCH))
|
|
{
|
|
// Total Watched
|
|
m_OnLock.lWatched++;
|
|
|
|
// Unread
|
|
if (!ISFLAGSET(pMsgNew->dwFlags, ARF_READ))
|
|
m_OnLock.lWatchedUnread++;
|
|
}
|
|
|
|
// Otherwise, old was watched, new is watched, then just adjust the unread count
|
|
else if (ISFLAGSET(pMsgOld->dwFlags, ARF_WATCH) && ISFLAGSET(pMsgNew->dwFlags, ARF_WATCH))
|
|
m_OnLock.lWatchedUnread += lUnread;
|
|
}
|
|
|
|
// Done
|
|
return(hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::OnDeleteRecord
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::OnRecordDelete(OPERATIONSTATE tyState,
|
|
LPORDINALLIST pOrdinals, LPVOID pRecord)
|
|
{
|
|
// Locals
|
|
LPMESSAGEINFO pMessage=(LPMESSAGEINFO)pRecord;
|
|
|
|
// Trace
|
|
TraceCall("CMessageFolder::OnDeleteRecord");
|
|
|
|
// Validate
|
|
Assert(pRecord && m_OnLock.cLocked > 0);
|
|
|
|
// After
|
|
if (OPERATION_AFTER == tyState)
|
|
{
|
|
// One less message
|
|
m_OnLock.lMsgs--;
|
|
|
|
// Watched
|
|
if (ISFLAGSET(pMessage->dwFlags, ARF_WATCH))
|
|
m_OnLock.lWatched--;
|
|
|
|
// Read State Change
|
|
if (FALSE == ISFLAGSET(pMessage->dwFlags, ARF_READ))
|
|
{
|
|
// Total Unread
|
|
m_OnLock.lUnread--;
|
|
|
|
// Watched
|
|
if (ISFLAGSET(pMessage->dwFlags, ARF_WATCH))
|
|
m_OnLock.lWatchedUnread--;
|
|
}
|
|
}
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CMessageFolder::OnExecuteMethod
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMessageFolder::OnExecuteMethod(METHODID idMethod, LPVOID pBinding,
|
|
LPDWORD pdwResult)
|
|
{
|
|
// Locals
|
|
FILETIME ftCurrent;
|
|
LPMESSAGEINFO pMessage=(LPMESSAGEINFO)pBinding;
|
|
|
|
// Validate
|
|
Assert(METHODID_MESSAGEAGEINDAYS == idMethod);
|
|
|
|
// Get system time as filetime
|
|
GetSystemTimeAsFileTime(&ftCurrent);
|
|
|
|
// Convert st to seconds since Jan 1, 1996
|
|
*pdwResult = (UlDateDiff(&pMessage->ftSent, &ftCurrent) / SECONDS_INA_DAY);
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|