Windows2003-3790/inetcore/outlookexpress/mailnews/store/findfold.cpp
2020-09-30 16:53:55 +02:00

1529 lines
42 KiB
C++

//--------------------------------------------------------------------------
// FindFold.cpp
//--------------------------------------------------------------------------
#include "pch.hxx"
#include "finder.h"
#include "findfold.h"
#include "storutil.h"
#include "msgfldr.h"
#include "shlwapip.h"
#include "storecb.h"
//--------------------------------------------------------------------------
// ENUMFINDFOLDERS
//--------------------------------------------------------------------------
typedef struct tagENUMFINDFOLDERS {
LPFOLDERENTRY prgFolder;
DWORD cFolders;
DWORD cAllocated;
DWORD cMax;
} ENUMFINDFOLDERS, *LPENUMFINDFOLDERS;
//--------------------------------------------------------------------------
// CLEAR_MESSAGE_FIELDS(_pMessage)
//--------------------------------------------------------------------------
#define CLEAR_MESSAGE_FIELDS(_Message) \
_Message.pszUidl = NULL; \
_Message.pszServer = NULL; \
_Message.faStream = 0; \
_Message.Offsets.cbSize = 0; \
_Message.Offsets.pBlobData = NULL
//--------------------------------------------------------------------------
// EnumerateFindFolders
//--------------------------------------------------------------------------
HRESULT EnumerateFindFolders(LPFOLDERINFO pFolder, BOOL fSubFolders,
DWORD cIndent, DWORD_PTR dwCookie)
{
// Locals
HRESULT hr=S_OK;
FOLDERID idDeleted;
FOLDERID idServer;
LPENUMFINDFOLDERS pEnum=(LPENUMFINDFOLDERS)dwCookie;
LPFOLDERENTRY pEntry;
IMessageFolder *pFolderObject=NULL;
// Trace
TraceCall("EnumerateFindFolders");
// If not a server
if (ISFLAGSET(pFolder->dwFlags, FOLDER_SERVER) || FOLDERID_ROOT == pFolder->idFolder)
goto exit;
// Room For One More
if (pEnum->cFolders + 1 > pEnum->cAllocated)
{
// Realloc
IF_FAILEXIT(hr = HrRealloc((LPVOID *)&pEnum->prgFolder, sizeof(FOLDERENTRY) * (pEnum->cAllocated + 5)));
// Set cAllocated
pEnum->cAllocated += 5;
}
// Readability
pEntry = &pEnum->prgFolder[pEnum->cFolders];
// Open the Folder
if (SUCCEEDED(g_pStore->OpenFolder(pFolder->idFolder, NULL, OPEN_FOLDER_NOCREATE, &pFolderObject)))
{
// Get the Database
if (SUCCEEDED(pFolderObject->GetDatabase(&pEntry->pDB)))
{
// No Folder
pEntry->pFolder = NULL;
// fInDeleted
if (S_OK == IsParentDeletedItems(pFolder->idFolder, &idDeleted, &idServer))
{
// We are in the deleted items folder
pEntry->fInDeleted = TRUE;
}
// Otherwise, not in deleted items
else
{
// Nope
pEntry->fInDeleted = FALSE;
}
// Count Record
IF_FAILEXIT(hr = pEntry->pDB->GetRecordCount(IINDEX_PRIMARY, &pEntry->cRecords));
// Save the Folder id
pEntry->idFolder = pFolder->idFolder;
// Save the Folder Type
pEntry->tyFolder = pFolder->tyFolder;
// Increment Max
pEnum->cMax += pEntry->cRecords;
// Copy folder Name
IF_NULLEXIT(pEntry->pszName = PszDupA(pFolder->pszName));
// Increment Folder Count
pEnum->cFolders++;
}
}
exit:
// Cleanup
SafeRelease(pFolderObject);
// Done
return(hr);
}
//--------------------------------------------------------------------------
// CFindFolder::CFindFolder
//--------------------------------------------------------------------------
CFindFolder::CFindFolder(void)
{
m_cRef = 1;
m_pCriteria = NULL;
m_pSearch = NULL;
m_pStore = NULL;
m_cFolders = 0;
m_cAllocated = 0;
m_cMax = 0;
m_cCur = 0;
m_fCancel = FALSE;
m_prgFolder = NULL;
m_pCallback = NULL;
m_idRoot = FOLDERID_INVALID;
m_idFolder = FOLDERID_INVALID;
m_pMessage = NULL;
}
//--------------------------------------------------------------------------
// CFindFolder::~CFindFolder
//--------------------------------------------------------------------------
CFindFolder::~CFindFolder(void)
{
// Locals
LPACTIVEFINDFOLDER pCurrent;
LPACTIVEFINDFOLDER pPrevious=NULL;
// Thread Safety
EnterCriticalSection(&g_csFindFolder);
// Walk Through the global list of Active Search Folders
for (pCurrent=g_pHeadFindFolder; pCurrent!=NULL; pCurrent=pCurrent->pNext)
{
// Is this it
if (m_idFolder == pCurrent->idFolder)
{
// If there was a Previous
if (pPrevious)
pPrevious->pNext = pCurrent->pNext;
// Otherwise, reset the header
else
g_pHeadFindFolder = pCurrent->pNext;
// Free pCurrent
g_pMalloc->Free(pCurrent);
// Done
break;
}
// Save Previous
pPrevious = pCurrent;
}
// Thread Safety
LeaveCriticalSection(&g_csFindFolder);
// Release Database
SafeRelease(m_pSearch);
// Delete this folder
if (FOLDERID_INVALID != m_idFolder && m_pStore)
{
// Delete this folder
m_pStore->DeleteFolder(m_idFolder, DELETE_FOLDER_NOTRASHCAN, (IStoreCallback *)this);
}
// Release the Store
SafeRelease(m_pStore);
// Release the Callback
SafeRelease(m_pCallback);
// Free the Folder Array
for (ULONG i=0; i<m_cFolders; i++)
{
// Free the Folder Name
SafeMemFree(m_prgFolder[i].pszName);
// Remove Notify
m_prgFolder[i].pDB->UnregisterNotify((IDatabaseNotify *)this);
// Release the Folder Object
SafeRelease(m_prgFolder[i].pDB);
// Release the Folder Object
SafeRelease(m_prgFolder[i].pFolder);
}
// Release my mime message
SafeRelease(m_pMessage);
// Free the Array
SafeMemFree(m_prgFolder);
// Free Find Info
if (m_pCriteria)
{
FreeFindInfo(m_pCriteria);
SafeMemFree(m_pCriteria);
}
}
//--------------------------------------------------------------------------
// CFindFolder::AddRef
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CFindFolder::AddRef(void)
{
TraceCall("CFindFolder::AddRef");
return InterlockedIncrement(&m_cRef);
}
//--------------------------------------------------------------------------
// CFindFolder::Release
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CFindFolder::Release(void)
{
TraceCall("CFindFolder::Release");
LONG cRef = InterlockedDecrement(&m_cRef);
if (0 == cRef)
delete this;
return (ULONG)cRef;
}
//--------------------------------------------------------------------------
// CFindFolder::QueryInterface
//--------------------------------------------------------------------------
STDMETHODIMP CFindFolder::QueryInterface(REFIID riid, LPVOID *ppv)
{
// Locals
HRESULT hr=S_OK;
// Stack
TraceCall("CFindFolder::QueryInterface");
// Invalid Arg
Assert(ppv);
// 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_IDatabaseNotify == riid)
*ppv = (IDatabaseNotify *)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);
}
//--------------------------------------------------------------------------
// CFindFolder::QueryService
//--------------------------------------------------------------------------
STDMETHODIMP CFindFolder::QueryService(REFGUID guidService, REFIID riid,
LPVOID *ppvObject)
{
// Trace
TraceCall("CFindFolder::QueryService");
// Just a Query Interface
return(QueryInterface(riid, ppvObject));
}
//--------------------------------------------------------------------------
// CFindFolder::Initialize
//--------------------------------------------------------------------------
HRESULT CFindFolder::Initialize(IMessageStore *pStore, IMessageServer *pServer,
OPENFOLDERFLAGS dwFlags, FOLDERID idFolder)
{
// Locals
HRESULT hr=S_OK;
FOLDERINFO Folder={0};
FOLDERUSERDATA UserData={0};
TABLEINDEX Index;
LPACTIVEFINDFOLDER pActiveFind;
// Trace
TraceCall("CFindFolder::Initialize");
// I don't need a server
Assert(NULL == pServer);
// Invalid Arg
if (NULL == pStore)
return TraceResult(E_INVALIDARG);
// Should be NULL
Assert(NULL == m_pCriteria);
// Save the Folder Id
m_idRoot = idFolder;
// Save the Store
m_pStore = pStore;
m_pStore->AddRef();
// Fill Up My folder Info
Folder.pszName = "Search Folder";
Folder.tyFolder = FOLDER_LOCAL;
Folder.tySpecial = FOLDER_NOTSPECIAL;
Folder.dwFlags = FOLDER_HIDDEN | FOLDER_FINDRESULTS;
Folder.idParent = FOLDERID_LOCAL_STORE;
// Create a Folder
IF_FAILEXIT(hr = m_pStore->CreateFolder(CREATE_FOLDER_UNIQUIFYNAME, &Folder, (IStoreCallback *)this));
// Save the Id
m_idFolder = Folder.idFolder;
// Create a CMessageFolder Object
IF_NULLEXIT(m_pSearch = new CMessageFolder);
// Initialize
IF_FAILEXIT(hr = m_pSearch->Initialize((IMessageStore *)pStore, NULL, NOFLAGS, m_idFolder));
// Fill the IINDEX_FINDER Information
ZeroMemory(&Index, sizeof(TABLEINDEX));
Index.cKeys = 2;
Index.rgKey[0].iColumn = MSGCOL_FINDFOLDER;
Index.rgKey[1].iColumn = MSGCOL_FINDSOURCE;
// Set Index
IF_FAILEXIT(hr = m_pSearch->ModifyIndex(IINDEX_FINDER, NULL, &Index));
// Allocate ACTIVEFINDFOLDER
IF_NULLEXIT(pActiveFind = (LPACTIVEFINDFOLDER)ZeroAllocate(sizeof(ACTIVEFINDFOLDER)));
// Set idFolder
pActiveFind->idFolder = m_idFolder;
// Set this
pActiveFind->pFolder = this;
// Thread Safety
EnterCriticalSection(&g_csFindFolder);
// Set Next
pActiveFind->pNext = g_pHeadFindFolder;
// Set Head
g_pHeadFindFolder = pActiveFind;
// Thread Safety
LeaveCriticalSection(&g_csFindFolder);
exit:
// Done
return(hr);
}
//--------------------------------------------------------------------------
// CFindFolder::GetMessageFolderId
//--------------------------------------------------------------------------
HRESULT CFindFolder::GetMessageFolderId(MESSAGEID idMessage, LPFOLDERID pidFolder)
{
// Locals
HRESULT hr=S_OK;
MESSAGEINFO Message={0};
// Trace
TraceCall("CFindFolder::GetMessageFolderId");
// Invalid Args
if (NULL == m_pSearch || NULL == pidFolder)
return TraceResult(E_INVALIDARG);
// Initialize Message
IF_FAILEXIT(hr = GetMessageInfo(m_pSearch, idMessage, &Message));
// Get the Folder Entry
*pidFolder = m_prgFolder[Message.iFindFolder].idFolder;
exit:
// Done
m_pSearch->FreeRecord(&Message);
// Done
return(hr);
}
//--------------------------------------------------------------------------
// CFindFolder::GetMessageFolderType
//--------------------------------------------------------------------------
HRESULT CFindFolder::GetMessageFolderType(MESSAGEID idMessage,
FOLDERTYPE *ptyFolder)
{
// Locals
HRESULT hr=S_OK;
MESSAGEINFO Message={0};
// Trace
TraceCall("CFindFolder::GetMessageFolderType");
// Invalid Args
if (NULL == m_pSearch || NULL == ptyFolder)
return TraceResult(E_INVALIDARG);
// Initialize Message
IF_FAILEXIT(hr = GetMessageInfo(m_pSearch, idMessage, &Message));
// Get the Folder Entry
*ptyFolder = m_prgFolder[Message.iFindFolder].tyFolder;
exit:
// Done
m_pSearch->FreeRecord(&Message);
// Done
return(hr);
}
//--------------------------------------------------------------------------
// CFindFolder::StartFind
//--------------------------------------------------------------------------
HRESULT CFindFolder::StartFind(LPFINDINFO pCriteria, IStoreCallback *pCallback)
{
// Locals
HRESULT hr=S_OK;
RECURSEFLAGS dwFlags=RECURSE_ONLYSUBSCRIBED;
ENUMFINDFOLDERS EnumFolders={0};
// Trace
TraceCall("CFindFolder::StartFind");
// Invalid Arg
if (NULL == pCriteria || NULL == pCallback)
return TraceResult(E_INVALIDARG);
// Should be NULL
Assert(NULL == m_pCriteria && m_pStore);
// Allocate m_pCriteria
IF_NULLEXIT(m_pCriteria = (FINDINFO *)g_pMalloc->Alloc(sizeof(FINDINFO)));
// Copy the Find Info
IF_FAILEXIT(hr = CopyFindInfo(pCriteria, m_pCriteria));
// Hold Onto the Callback
m_pCallback = pCallback;
m_pCallback->AddRef();
// Setup Flags
if (FOLDERID_ROOT != m_idRoot)
FLAGSET(dwFlags, RECURSE_INCLUDECURRENT);
// SubFolder
if (m_pCriteria->fSubFolders)
FLAGSET(dwFlags, RECURSE_SUBFOLDERS);
// Build my Folder Table
IF_FAILEXIT(hr = RecurseFolderHierarchy(m_idRoot, dwFlags, 0, (DWORD_PTR)&EnumFolders, (PFNRECURSECALLBACK)EnumerateFindFolders));
// Take Stuff Back
m_prgFolder = EnumFolders.prgFolder;
m_cFolders = EnumFolders.cFolders;
m_cAllocated = EnumFolders.cAllocated;
m_cMax = EnumFolders.cMax;
// Start the find...
IF_FAILEXIT(hr = _StartFind());
exit:
// Done
return(hr);
}
//--------------------------------------------------------------------------
// CFindFolder::_StartFind
//--------------------------------------------------------------------------
HRESULT CFindFolder::_StartFind(void)
{
// Locals
HRESULT hr=S_OK;
DWORD i;
// Trace
TraceCall("CFindFolder::_StartFind");
// Callback
if (m_pCallback)
m_pCallback->OnBegin(SOT_SEARCHING, NULL, (IOperationCancel *)this);
// Loop through the Folders
for (i=0; i<m_cFolders; i++)
{
// Query the Folder
IF_FAILEXIT(hr = _SearchFolder(i));
}
exit:
// Callback
if (m_pCallback)
m_pCallback->OnComplete(SOT_SEARCHING, hr, NULL, NULL);
// Done
return(hr);
}
//--------------------------------------------------------------------------
// CFindFolder::_SearchFolder
//--------------------------------------------------------------------------
HRESULT CFindFolder::_SearchFolder(DWORD iFolder)
{
// Locals
HRESULT hr=S_OK;
DWORD iRow=0;
DWORD cRows=0;
HROWSET hRowset=NULL;
LPSTR pszName;
HLOCK hNotify=NULL;
MESSAGEINFO rgMessage[100];
BOOL fFree=FALSE;
LPFOLDERENTRY pEntry;
BOOL fMatch;
IDatabase *pDB;
DWORD cMatch=0;
// Trace
TraceCall("CFindFolder::_SearchFolder");
// Get pEntry
pEntry = &m_prgFolder[iFolder];
// Get the Folder Name
pszName = pEntry->pszName;
// Get the Folder Object
pDB = pEntry->pDB;
// Create a Rowset for this Folder
IF_FAILEXIT(hr = pDB->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset));
// Progress
if (m_fCancel || (m_pCallback && S_FALSE == m_pCallback->OnProgress(SOT_SEARCHING, m_cCur, m_cMax, pszName)))
goto exit;
// Queue Notifications
IF_FAILEXIT(hr = m_pSearch->LockNotify(NOFLAGS, &hNotify));
// Walk the Rowset
while (S_OK == pDB->QueryRowset(hRowset, 100, (LPVOID *)rgMessage, &cRows))
{
// Need to Free
fFree = TRUE;
// Walk through the Rows
for (iRow=0; iRow<cRows; iRow++)
{
// Does Row Match Criteria
IF_FAILEXIT(hr = _OnInsert(iFolder, &rgMessage[iRow], &fMatch));
// Count Matched
if (fMatch)
cMatch++;
// Incrment m_cCur
m_cCur++;
// Adjust the max
if (m_cCur > m_cMax)
m_cMax = m_cCur;
// Do Progress Stuff
if ((m_cCur % 50) == 0 && m_cCur > 0)
{
// Progress
if (m_fCancel || (m_pCallback && S_FALSE == m_pCallback->OnProgress(SOT_SEARCHING, m_cCur, m_cMax, NULL)))
{
// Register for a notifications on the stuff that we've searched
pDB->RegisterNotify(IINDEX_PRIMARY, REGISTER_NOTIFY_NOADDREF, iFolder, (IDatabaseNotify *)this);
// Done..
goto exit;
}
}
// Do Progress Stuff
if ((cMatch % 50) == 0 && cMatch > 0)
{
// Unlock the Notificaton Queue
m_pSearch->UnlockNotify(&hNotify);
// Lock It Again
m_pSearch->LockNotify(NOFLAGS, &hNotify);
}
// Free It
pDB->FreeRecord(&rgMessage[iRow]);
}
// No Need to Free
fFree = FALSE;
}
// Register for a notificatoin on this folder
pDB->RegisterNotify(IINDEX_PRIMARY, REGISTER_NOTIFY_NOADDREF, iFolder, (IDatabaseNotify *)this);
exit:
// Unlock the Notificaton Queue
m_pSearch->UnlockNotify(&hNotify);
// Free ?
if (fFree)
{
// Loop through remaining unfreed rows
for (; iRow<cRows; iRow++)
{
// Free the Row
pDB->FreeRecord(&rgMessage[iRow]);
}
}
// Cleanup
pDB->CloseRowset(&hRowset);
// Done
return(hr);
}
//--------------------------------------------------------------------------
// CFindFolder::_OnInsert
//--------------------------------------------------------------------------
HRESULT CFindFolder::_OnInsert(DWORD iFolder, LPMESSAGEINFO pInfo,
BOOL *pfMatch, LPMESSAGEID pidNew /* =NULL */)
{
// Locals
HRESULT hr=S_OK;
MESSAGEINFO Message;
// Trace
TraceCall("CFindFolder::_OnInsert");
// Invalid Argts
Assert(iFolder < m_cFolders && pInfo);
// Init
if (pfMatch)
*pfMatch = FALSE;
// Doesn't match my criteria ?
if (S_FALSE == _IsMatch(iFolder, pInfo))
goto exit;
// Init
if (pfMatch)
*pfMatch = TRUE;
// Copy the Message Info
CopyMemory(&Message, pInfo, sizeof(MESSAGEINFO));
// Store the Folder Name
Message.pszFolder = m_prgFolder[iFolder].pszName;
// Set the Source Id
Message.idFindSource = Message.idMessage;
// Set the Tag
Message.iFindFolder = iFolder;
// Generate a New Message Id
IF_FAILEXIT(hr = m_pSearch->GenerateId((LPDWORD)&Message.idMessage));
// Remove some stuff to make it smaller
CLEAR_MESSAGE_FIELDS(Message);
// Insert the Record
IF_FAILEXIT(hr = m_pSearch->InsertRecord(&Message));
// Return the Id
if (pidNew)
*pidNew = Message.idMessage;
exit:
// Done
return(hr);
}
//--------------------------------------------------------------------------
// CFindFolder::_OnDelete
//--------------------------------------------------------------------------
HRESULT CFindFolder::_OnDelete(DWORD iFolder, LPMESSAGEINFO pInfo)
{
// Locals
HRESULT hr=S_OK;
MESSAGEINFO Message={0};
// Trace
TraceCall("CFindFolder::_OnDelete");
// Invalid Argts
Assert(iFolder < m_cFolders && pInfo);
// Setup the Search key
Message.iFindFolder = iFolder;
Message.idFindSource = pInfo->idMessage;
// Find It
IF_FAILEXIT(hr = m_pSearch->FindRecord(IINDEX_FINDER, COLUMNS_ALL, &Message, NULL));
// Not Found
if (DB_S_NOTFOUND == hr)
{
hr = TraceResult(DB_E_NOTFOUND);
goto exit;
}
// Delete this Record
IF_FAILEXIT(hr = m_pSearch->DeleteRecord(&Message));
exit:
// Cleanup
m_pSearch->FreeRecord(&Message);
// Done
return(hr);
}
//--------------------------------------------------------------------------
// CFindFolder::_OnUpdate
//--------------------------------------------------------------------------
HRESULT CFindFolder::_OnUpdate(DWORD iFolder, LPMESSAGEINFO pInfo1,
LPMESSAGEINFO pInfo2)
{
// Locals
HRESULT hr=S_OK;
MESSAGEINFO Message;
MESSAGEINFO Current={0};
// Trace
TraceCall("CFindFolder::_OnUpdate");
// Invalid Argts
Assert(iFolder < m_cFolders && pInfo1 && pInfo2);
// Doesn't match my criteria ?
if (S_FALSE == _IsMatch(iFolder, pInfo1))
{
// If the Original Record was not in the find folder, then see if record 2 should be added
_OnInsert(iFolder, pInfo2, NULL);
}
// If pInfo2 should not be displayed, then delete pInfo1
else if (S_FALSE == _IsMatch(iFolder, pInfo2))
{
// Delete pInfo1
_OnDelete(iFolder, pInfo1);
}
// Otherwise, update pInfo1
else
{
// Setup the Search key
Current.iFindFolder = iFolder;
Current.idFindSource = pInfo1->idMessage;
// Find It
IF_FAILEXIT(hr = m_pSearch->FindRecord(IINDEX_FINDER, COLUMNS_ALL, &Current, NULL));
// Not Found
if (DB_S_NOTFOUND == hr)
{
hr = TraceResult(DB_E_NOTFOUND);
goto exit;
}
// Copy the Message Info
CopyMemory(&Message, pInfo2, sizeof(MESSAGEINFO));
// Fixup the Version
Message.bVersion = Current.bVersion;
// Store the Folder Name
Message.pszFolder = m_prgFolder[iFolder].pszName;
// Set the Source Id
Message.idFindSource = Current.idFindSource;
// Set the Tag
Message.iFindFolder = iFolder;
// Set the id
Message.idMessage = Current.idMessage;
// Remove some stuff to make it smaller
Message.pszUidl = NULL;
Message.pszServer = NULL;
Message.faStream = 0;
// Insert the Record
IF_FAILEXIT(hr = m_pSearch->UpdateRecord(&Message));
}
exit:
// Cleanup
m_pSearch->FreeRecord(&Current);
// Done
return(hr);
}
//--------------------------------------------------------------------------
// CFindFolder::_IsMatch
//--------------------------------------------------------------------------
HRESULT CFindFolder::_IsMatch(DWORD iFolder, LPMESSAGEINFO pInfo)
{
// Trace
TraceCall("CFindFolder::_ProcessMessageInfo");
// Has Attachment
if (ISFLAGSET(m_pCriteria->mask, FIM_ATTACHMENT))
{
// No Attachment
if (FALSE == ISFLAGSET(pInfo->dwFlags, ARF_HASATTACH))
return(S_FALSE);
}
// Is Flagged
if (ISFLAGSET(m_pCriteria->mask, FIM_FLAGGED))
{
// No Attachment
if (FALSE == ISFLAGSET(pInfo->dwFlags, ARF_FLAGGED))
return(S_FALSE);
}
// Was Forwarded
if (ISFLAGSET(m_pCriteria->mask, FIM_FORWARDED))
{
// No Attachment
if (FALSE == ISFLAGSET(pInfo->dwFlags, ARF_FORWARDED))
return(S_FALSE);
}
// Was Replied to
if (ISFLAGSET(m_pCriteria->mask, FIM_REPLIED))
{
// No Attachment
if (FALSE == ISFLAGSET(pInfo->dwFlags, ARF_REPLIED))
return(S_FALSE);
}
// From
if (ISFLAGSET(m_pCriteria->mask, FIM_FROM))
{
// No pszFrom
if (NULL == m_pCriteria->pszFrom)
return(S_FALSE);
// Check pszEmail From
if (NULL == pInfo->pszDisplayFrom || NULL == StrStrIA(pInfo->pszDisplayFrom, m_pCriteria->pszFrom))
{
// Try Email
if (NULL == pInfo->pszEmailFrom || NULL == StrStrIA(pInfo->pszEmailFrom, m_pCriteria->pszFrom))
return(S_FALSE);
}
}
// Subject
if (ISFLAGSET(m_pCriteria->mask, FIM_SUBJECT))
{
// Check Subject
if (NULL == m_pCriteria->pszSubject || NULL == pInfo->pszSubject || NULL == StrStrIA(pInfo->pszSubject, m_pCriteria->pszSubject))
return(S_FALSE);
}
// Recipient
if (ISFLAGSET(m_pCriteria->mask, FIM_TO))
{
// No pszFrom
if (NULL == m_pCriteria->pszTo)
return(S_FALSE);
// Check pszEmail From
if (NULL == pInfo->pszDisplayTo || NULL == StrStrIA(pInfo->pszDisplayTo, m_pCriteria->pszTo))
{
// Try Email
if (NULL == pInfo->pszEmailTo || NULL == StrStrIA(pInfo->pszEmailTo, m_pCriteria->pszTo))
return(S_FALSE);
}
}
// DateFrom <= pInfo <= DateTo
if (ISFLAGSET(m_pCriteria->mask, FIM_DATEFROM))
{
// Locals
FILETIME ftLocal;
// Convert to local file time
FileTimeToLocalFileTime(&pInfo->ftReceived, &ftLocal);
// Compare Received
if (CompareFileTime(&ftLocal, &m_pCriteria->ftDateFrom) < 0)
return(S_FALSE);
}
// DateFrom <= pInfo <= DateTo
if (ISFLAGSET(m_pCriteria->mask, FIM_DATETO))
{
// Locals
FILETIME ftLocal;
// Convert to local file time
FileTimeToLocalFileTime(&pInfo->ftReceived, &ftLocal);
// Compare Received
if (CompareFileTime(&ftLocal, &m_pCriteria->ftDateTo) > 0)
return(S_FALSE);
}
// Body Text
if (ISFLAGSET(m_pCriteria->mask, FIM_BODYTEXT))
{
// Locals
BOOL fMatch=FALSE;
IStream *pStream;
// No Body TExt
if (NULL == m_pCriteria->pszBody)
return(S_FALSE);
// Open the mime message
if (SUCCEEDED(LighweightOpenMessage(m_prgFolder[iFolder].pDB, pInfo, &m_pMessage)))
{
// Try to Get the Plain Text Stream
if (FAILED(m_pMessage->GetTextBody(TXT_PLAIN, IET_DECODED, &pStream, NULL)))
{
// Try to get the HTML stream
if (FAILED(m_pMessage->GetTextBody(TXT_HTML, IET_DECODED, &pStream, NULL)))
pStream = NULL;
}
// Do we have a strema
if (pStream)
{
// Search the Stream
fMatch = StreamSubStringMatch(pStream, m_pCriteria->pszBody);
// Release the Stream
pStream->Release();
}
}
// No Match
if (FALSE == fMatch)
return(S_FALSE);
}
// Its a match
return(S_OK);
}
//--------------------------------------------------------------------------
// CFindFolder::SaveMessage
//--------------------------------------------------------------------------
STDMETHODIMP CFindFolder::SaveMessage(LPMESSAGEID pidMessage,
SAVEMESSAGEFLAGS dwOptions, MESSAGEFLAGS dwFlags,
IStream *pStream, IMimeMessage *pMessage, IStoreCallback *pCallback)
{
// Locals
HRESULT hr=S_OK;
HLOCK hLock=NULL;
MESSAGEID idSaved;
MESSAGEINFO Saved={0};
MESSAGEINFO Message={0};
LPFOLDERENTRY pEntry=NULL;
BOOL fRegNotify=FALSE;
IMessageFolder *pFolder=NULL;
// Trace
TraceCall("CFindFolder::SaveMessage");
// Invalid Args
if (NULL == pidMessage || NULL == pMessage || !ISFLAGSET(dwOptions, SAVE_MESSAGE_GENID))
{
Assert(FALSE);
return TraceResult(E_INVALIDARG);
}
// Set the messageId
Message.idMessage = *pidMessage;
// Find It
IF_FAILEXIT(hr = m_pSearch->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL));
// Not Found
if (DB_S_NOTFOUND == hr)
{
AssertSz(FALSE, "This can't happen because you can't save new messages into a search folder.");
hr = TraceResult(DB_E_NOTFOUND);
goto exit;
}
// Get the Folder Entry
pEntry = &m_prgFolder[Message.iFindFolder];
// Open the folder
IF_FAILEXIT(hr = g_pStore->OpenFolder(pEntry->idFolder, NULL, NOFLAGS, &pFolder));
// Lock
IF_FAILEXIT(hr = pEntry->pDB->Lock(&hLock));
// Remove my notification
pEntry->pDB->UnregisterNotify((IDatabaseNotify *)this);
// Re-Register for notifications
fRegNotify = TRUE;
// Set idFindSource
idSaved = Message.idFindSource;
// Open the Message
IF_FAILEXIT(hr = pFolder->SaveMessage(&idSaved, dwOptions, dwFlags, pStream, pMessage, pCallback));
// Get the new message info
IF_FAILEXIT(hr = GetMessageInfo(pFolder, idSaved, &Saved));
// Insert This Dude
IF_FAILEXIT(hr = _OnInsert(Message.iFindFolder, &Saved, NULL, pidMessage));
exit:
// Cleanup
if (pEntry)
{
// fRegNotify
if (fRegNotify)
{
// Re-register for notifications
pEntry->pDB->RegisterNotify(IINDEX_PRIMARY, REGISTER_NOTIFY_NOADDREF, Message.iFindFolder, (IDatabaseNotify *)this);
}
// Unlock the Folder
pEntry->pDB->Unlock(&hLock);
}
// Free Message
m_pSearch->FreeRecord(&Message);
// Free
if (pFolder)
pFolder->FreeRecord(&Saved);
// Release the Folder
SafeRelease(pFolder);
// Done
return(hr);
}
//--------------------------------------------------------------------------
// CFindFolder::OpenMessage
//--------------------------------------------------------------------------
STDMETHODIMP CFindFolder::OpenMessage(MESSAGEID idMessage,
OPENMESSAGEFLAGS dwFlags, IMimeMessage **ppMessage,
IStoreCallback *pCallback)
{
// Locals
HRESULT hr=S_OK;
MESSAGEINFO Message={0};
LPFOLDERENTRY pEntry;
// Trace
TraceCall("CFindFolder::OpenMessage");
// Set the messageId
Message.idMessage = idMessage;
// Find It
IF_FAILEXIT(hr = m_pSearch->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL));
// Not Found
if (DB_S_NOTFOUND == hr)
{
hr = TraceResult(DB_E_NOTFOUND);
goto exit;
}
// Get entry
pEntry = &m_prgFolder[Message.iFindFolder];
// Do we have a folder open yet ?
if (NULL == pEntry->pFolder)
{
// Get the Real Folder
IF_FAILEXIT(hr = g_pStore->OpenFolder(pEntry->idFolder, NULL, NOFLAGS, &pEntry->pFolder));
}
// Open the Message
IF_FAILEXIT(hr = pEntry->pFolder->OpenMessage(Message.idFindSource, dwFlags, ppMessage, pCallback));
exit:
// Cleanup
m_pSearch->FreeRecord(&Message);
// Done
return(hr);
}
//--------------------------------------------------------------------------
// CFindFolder::SetMessageFlags
//--------------------------------------------------------------------------
STDMETHODIMP CFindFolder::SetMessageFlags(LPMESSAGEIDLIST pList,
LPADJUSTFLAGS pFlags, LPRESULTLIST pResults,
IStoreCallback *pCallback)
{
// Locals
HRESULT hr=S_OK;
HWND hwndParent;
DWORD i;
LPMESSAGEIDLIST prgList=NULL;
IMessageFolder *pFolder=NULL;
// Trace
TraceCall("CFindFolder::SetMessageFlags");
// Invalid Args
Assert(NULL == pList || pList->cMsgs > 0);
Assert(pCallback);
// Invalid Args
if (NULL == pCallback)
return TraceResult(E_INVALIDARG);
// Get the Parent Window
IF_FAILEXIT(hr = pCallback->GetParentWindow(0, &hwndParent));
// Collate into folders
IF_FAILEXIT(hr = _CollateIdList(pList, &prgList, NULL));
// Walk through the folders...
for (i=0; i<m_cFolders; i++)
{
// Call Into the Folder unless cMsgs == 0
if (prgList[i].cMsgs > 0)
{
// Get the Real Folder
IF_FAILEXIT(hr = g_pStore->OpenFolder(m_prgFolder[i].idFolder, NULL, NOFLAGS, &pFolder));
// Blocking...
IF_FAILEXIT(hr = SetMessageFlagsProgress(hwndParent, pFolder, pFlags, &prgList[i]));
// Cleanup
SafeRelease(pFolder);
}
}
exit:
// Cleanup
SafeRelease(pFolder);
_FreeIdListArray(&prgList);
// Done
return(hr);
}
//--------------------------------------------------------------------------
// CFindFolder::CopyMessages
//--------------------------------------------------------------------------
STDMETHODIMP CFindFolder::CopyMessages(IMessageFolder *pDest,
COPYMESSAGEFLAGS dwFlags, LPMESSAGEIDLIST pList, LPADJUSTFLAGS pFlags,
LPRESULTLIST pResults, IStoreCallback *pCallback)
{
// Locals
HRESULT hr=S_OK;
HWND hwndParent;
DWORD i;
LPMESSAGEIDLIST prgList=NULL;
IMessageFolder *pFolder=NULL;
// Trace
TraceCall("CFindFolder::CopyMessages");
// Better have a callback
Assert(pCallback);
// Invalid Args
if (NULL == pCallback)
return TraceResult(E_INVALIDARG);
// Get the Parent Window
IF_FAILEXIT(hr = pCallback->GetParentWindow(0, &hwndParent));
// Collate into folders
IF_FAILEXIT(hr = _CollateIdList(pList, &prgList, NULL));
// Walk through the folders...
for (i=0; i<m_cFolders; i++)
{
// Anything to do?
if (prgList[i].cMsgs > 0)
{
// Get the Real Folder
IF_FAILEXIT(hr = g_pStore->OpenFolder(m_prgFolder[i].idFolder, NULL, NOFLAGS, &pFolder));
// Call Justins
IF_FAILEXIT(hr = CopyMessagesProgress(hwndParent, pFolder, pDest, dwFlags, &prgList[i], pFlags));
// Cleanup
SafeRelease(pFolder);
}
}
exit:
// Cleanup
SafeRelease(pFolder);
_FreeIdListArray(&prgList);
// Done
return(hr);
}
//--------------------------------------------------------------------------
// CFindFolder::DeleteMessages
//--------------------------------------------------------------------------
STDMETHODIMP CFindFolder::DeleteMessages(DELETEMESSAGEFLAGS dwFlags,
LPMESSAGEIDLIST pList, LPRESULTLIST pResults,
IStoreCallback *pCallback)
{
// Locals
HRESULT hr=S_OK;
DWORD i;
BOOL fSomeInDeleted;
HWND hwndParent;
LPMESSAGEIDLIST prgList=NULL;
IMessageFolder *pFolder=NULL;
// Trace
TraceCall("CFindFolder::DeleteMessages");
// Invalid Args
Assert(NULL == pList || pList->cMsgs > 0);
Assert(pCallback);
// Invalid Args
if (NULL == pCallback)
return TraceResult(E_INVALIDARG);
// Collate into folders
IF_FAILEXIT(hr = _CollateIdList(pList, &prgList, &fSomeInDeleted));
// Prompt...
if (fSomeInDeleted && FALSE == ISFLAGSET(dwFlags, 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(idsWarnSomePermDelete), NULL, MB_YESNO | MB_DEFBUTTON2 | MB_ICONEXCLAMATION ))
goto exit;
}
// Get the Parent Window
IF_FAILEXIT(hr = pCallback->GetParentWindow(0, &hwndParent));
// Walk through the folders...
for (i=0; i<m_cFolders; i++)
{
// Call Into the Folder unless cMsgs == 0
if (prgList[i].cMsgs > 0)
{
// Get the Real Folder
IF_FAILEXIT(hr = g_pStore->OpenFolder(m_prgFolder[i].idFolder, NULL, NOFLAGS, &pFolder));
// Call Into the Folder
IF_FAILEXIT(hr = DeleteMessagesProgress(hwndParent, pFolder, dwFlags | DELETE_MESSAGE_NOPROMPT, &prgList[i]));
// Cleanup
SafeRelease(pFolder);
}
}
exit:
// Cleanup
SafeRelease(pFolder);
_FreeIdListArray(&prgList);
// Done
return(hr);
}
//--------------------------------------------------------------------------
// CFindFolder::_CollateIdList
//--------------------------------------------------------------------------
HRESULT CFindFolder::_CollateIdList(LPMESSAGEIDLIST pList,
LPMESSAGEIDLIST *pprgCollated, BOOL *pfSomeInDeleted)
{
// Locals
HRESULT hr=S_OK;
HROWSET hRowset=NULL;
LPMESSAGEIDLIST pListDst;
DWORD i;
MESSAGEINFO Message={0};
// Trace
TraceCall("CFindFolder::_CollateIdList");
// Initialize
if (pfSomeInDeleted)
*pfSomeInDeleted = FALSE;
// Allocate pprgCollated
IF_NULLEXIT(*pprgCollated = (LPMESSAGEIDLIST)ZeroAllocate(sizeof(MESSAGEIDLIST) * m_cFolders));
// Need a Rowset
if (NULL == pList)
{
// Create a Rowset
IF_FAILEXIT(hr = m_pSearch->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
Message.idMessage = pList->prgidMsg[i];
// Look for this record
IF_FAILEXIT(hr = m_pSearch->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL));
}
// Otherwise, enumerate next
else
{
// Get the next
IF_FAILEXIT(hr = m_pSearch->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)
{
// Validate
Assert(Message.iFindFolder < m_cFolders);
// Return pfSomeInDeleted
if (pfSomeInDeleted && m_prgFolder[Message.iFindFolder].fInDeleted)
*pfSomeInDeleted = TRUE;
// Locate the Correct
pListDst = &((*pprgCollated)[Message.iFindFolder]);
// Need to Grow this puppy
if (pListDst->cMsgs + 1 >= pListDst->cAllocated)
{
// Realloc the Array
IF_FAILEXIT(hr = HrRealloc((LPVOID *)&pListDst->prgidMsg, sizeof(MESSAGEID) * (pListDst->cAllocated + 256)));
// Increment
pListDst->cAllocated += 256;
}
// Store the Id
pListDst->prgidMsg[pListDst->cMsgs++] = Message.idFindSource;
// Free
m_pSearch->FreeRecord(&Message);
}
}
exit:
// Cleanup
m_pSearch->FreeRecord(&Message);
m_pSearch->CloseRowset(&hRowset);
// Failure
if (FAILED(hr))
_FreeIdListArray(pprgCollated);
// Done
return(hr);
}
//--------------------------------------------------------------------------
// CFindFolder::_FreeIdListArray
//--------------------------------------------------------------------------
HRESULT CFindFolder::_FreeIdListArray(LPMESSAGEIDLIST *pprgList)
{
// Locals
DWORD i;
// Trace
TraceCall("CFindFolder::_FreeIdListArray");
// Nothing to Free
if (NULL == *pprgList)
return(S_OK);
// Loop
for (i=0; i<m_cFolders; i++)
{
// Free prgidMsg
SafeMemFree((*pprgList)[i].prgidMsg);
}
// Free the Array
SafeMemFree((*pprgList));
// Done
return(S_OK);
}
//--------------------------------------------------------------------------
// CFindFolder::OnTransaction
//--------------------------------------------------------------------------
STDMETHODIMP CFindFolder::OnTransaction(HTRANSACTION hTransaction, DWORD_PTR dwCookie,
IDatabase *pDB)
{
// Locals
HRESULT hr;
HLOCK hNotify=NULL;
MESSAGEINFO Message1={0};
MESSAGEINFO Message2={0};
ORDINALLIST Ordinals;
INDEXORDINAL iIndex;
TRANSACTIONTYPE tyTransaction;
// Trace
TraceCall("CFindFolder::OnRecordNotify");
// Lock Notifications
m_pSearch->LockNotify(NOFLAGS, &hNotify);
// While we have a Transaction...
while (hTransaction)
{
// Get Transaction
IF_FAILEXIT(hr = pDB->GetTransaction(&hTransaction, &tyTransaction, &Message1, &Message2, &iIndex, &Ordinals));
// Insert
if (TRANSACTION_INSERT == tyTransaction)
{
// Ccall OnInsert
_OnInsert((DWORD) dwCookie, &Message1, NULL);
}
// Delete
else if (TRANSACTION_DELETE == tyTransaction)
{
// Ccall OnDelete
_OnDelete((DWORD) dwCookie, &Message1);
}
// Update
else if (TRANSACTION_UPDATE == tyTransaction)
{
// Ccall OnInsert
_OnUpdate((DWORD) dwCookie, &Message1, &Message2);
}
}
exit:
// Cleanup
pDB->FreeRecord(&Message1);
pDB->FreeRecord(&Message2);
// Lock Notifications
m_pSearch->UnlockNotify(&hNotify);
// Done
return(S_OK);
}
HRESULT CFindFolder::ConnectionAddRef()
{
if (m_pSearch)
m_pSearch->ConnectionAddRef();
return S_OK;
}
HRESULT CFindFolder::ConnectionRelease()
{
if (m_pSearch)
m_pSearch->ConnectionAddRef();
return S_OK;
}