//-------------------------------------------------------------------------- // 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 #include //-------------------------------------------------------------------------- // 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); }