add support for dealing with duplicate incoming messages, 9413 sr=mscott

git-svn-id: svn://10.0.0.236/trunk@179303 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
bienvenu%nventure.com 2005-08-30 14:39:01 +00:00
parent d4fdf966bd
commit 5539f9a6b0
7 changed files with 217 additions and 32 deletions

View File

@ -49,14 +49,14 @@ interface nsIMsgDownloadSettings;
interface nsISpamSettings;
interface nsIMsgFilterPlugin;
interface nsIUrlListener;
interface nsIMsgDBHdr;
/*
* Interface for incoming mail/news host
* this is the base interface for all mail server types (imap, pop, nntp, etc)
* often you will want to add extra interfaces that give you server-specific
* attributes and methods.
*/
[scriptable, uuid(dc2f06f9-0ee8-47ad-901e-168d14323576)]
[scriptable, uuid(e0446c10-1bb1-4282-be4b-7fdadce4717a)]
interface nsIMsgIncomingServer : nsISupports {
/**
@ -407,6 +407,16 @@ interface nsIMsgIncomingServer : nsISupports {
nsIMsgFolder getMsgFolderFromURI(in nsIMsgFolder aFolderResource, in string aURI);
readonly attribute boolean isDeferredTo;
const long keepDups = 0;
const long deleteDups = 1;
const long moveDupsToTrash = 2;
const long markDupsRead = 3;
attribute long incomingDuplicateAction;
// check if new hdr is a duplicate of a recently arrived header
boolean isNewHdrDuplicate(in nsIMsgDBHdr aNewHdr);
};
%{C++

View File

@ -67,7 +67,7 @@
#include "nsNetUtil.h"
#include "nsIWindowWatcher.h"
#include "nsIStringBundle.h"
#include "nsIMsgHdr.h"
#include "nsIRDFService.h"
#include "nsRDFCID.h"
#include "nsIInterfaceRequestor.h"
@ -91,6 +91,7 @@ nsMsgIncomingServer::nsMsgIncomingServer():
m_prefBranch(0),
m_biffState(nsIMsgFolder::nsMsgBiffState_NoMail),
m_serverBusy(PR_FALSE),
m_numMsgsDownloaded(0),
m_canHaveFilters(PR_TRUE),
m_displayStartupPage(PR_TRUE),
mPerformingBiff(PR_FALSE)
@ -1752,6 +1753,8 @@ NS_IMPL_SERVERPREF_BOOL(nsMsgIncomingServer,
NS_IMPL_SERVERPREF_INT(nsMsgIncomingServer, MaxMessageSize, "max_size")
NS_IMPL_SERVERPREF_INT(nsMsgIncomingServer, IncomingDuplicateAction, "dup_action");
NS_IMETHODIMP nsMsgIncomingServer::SetUnicharAttribute(const char *aName, const PRUnichar *val)
{
return SetUnicharValue(aName, val);
@ -2515,3 +2518,52 @@ NS_IMETHODIMP nsMsgIncomingServer::GetIsDeferredTo(PRBool *aIsDeferredTo)
*aIsDeferredTo = PR_FALSE;
return NS_OK;
}
const long kMaxDownloadTableSize = 500;
// aData is the server, from that we get the cutoff point, below which we evict
// element is the arrival index of the msg.
/* static */PRBool nsMsgIncomingServer::evictOldEntries(nsHashKey *aKey, void *element, void *aData)
{
nsMsgIncomingServer *server = (nsMsgIncomingServer *)aData;
if ((PRInt32) element < server->m_numMsgsDownloaded - kMaxDownloadTableSize/2)
return kHashEnumerateRemove;
return server->m_downloadedHdrs.Count() > kMaxDownloadTableSize/2;
}
// hash the concatenation of the message-id and subject as the hash table key,
// and store the arrival index as the value. To limit the size of the hash table,
// we just throw out ones with a lower ordinal value than the cut-off point.
NS_IMETHODIMP nsMsgIncomingServer::IsNewHdrDuplicate(nsIMsgDBHdr *aNewHdr, PRBool *aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
*aResult = PR_FALSE;
nsCAutoString strHashKey;
nsXPIDLCString messageId, subject;
aNewHdr->GetMessageId(getter_Copies(messageId));
strHashKey.Append(messageId);
aNewHdr->GetSubject(getter_Copies(subject));
// err on the side of caution and ignore messages w/o subject or messageid.
if (subject.IsEmpty() || messageId.IsEmpty())
return NS_OK;
strHashKey.Append(subject);
nsCStringKey hashKey(strHashKey);
PRInt32 hashValue = (PRInt32) m_downloadedHdrs.Get(&hashKey);
if (hashValue)
{
*aResult = PR_TRUE;
}
else
{
// we store the current size of the hash table as the hash
// value - this allows us to delete older entries.
m_downloadedHdrs.Put(&hashKey, (void *) ++m_numMsgsDownloaded);
// Check if hash table is larger than some reasonable size
// and if is it, iterate over hash table deleting messages
// with an arrival index < number of msgs downloaded - half the reasonable size.
if (m_downloadedHdrs.Count() >= kMaxDownloadTableSize)
m_downloadedHdrs.Enumerate(evictOldEntries, this);
}
return NS_OK;
}

View File

@ -50,6 +50,7 @@
#include "nsIFileSpec.h"
#include "nsISpamSettings.h"
#include "nsIMsgFilterPlugin.h"
#include "nsHashtable.h"
class nsIMsgFolderCache;
class nsIMsgProtocolInfo;
@ -106,7 +107,10 @@ protected:
nsresult getProtocolInfo(nsIMsgProtocolInfo **aResult);
nsCOMPtr <nsIFileSpec> mFilterFile;
nsCOMPtr <nsIMsgFilterList> mFilterList;
// these allow us to handle duplicate incoming messages, e.g. delete them.
nsHashtable m_downloadedHdrs;
PRInt32 m_numMsgsDownloaded;
static PRBool evictOldEntries(nsHashKey *aKey, void *element, void *aData);
private:
nsIPrefBranch *m_prefBranch;
nsCString m_password;

View File

@ -2909,6 +2909,20 @@ nsresult nsImapMailFolder::NormalEndHeaderParseStream(nsIImapProtocol*
char *headers;
PRInt32 headersSize;
nsCOMPtr <nsIMsgWindow> msgWindow;
if (aProtocol)
{
nsCOMPtr <nsIImapUrl> aImapUrl;
nsCOMPtr <nsIMsgMailNewsUrl> msgUrl;
rv = aProtocol->GetRunningImapURL(getter_AddRefs(aImapUrl));
if (NS_SUCCEEDED(rv) && aImapUrl)
{
msgUrl = do_QueryInterface(aImapUrl);
if (msgUrl)
msgUrl->GetMsgWindow(getter_AddRefs(msgWindow));
}
}
nsCOMPtr<nsIMsgIncomingServer> server;
rv = GetServer(getter_AddRefs(server));
if (NS_SUCCEEDED(rv)) // don't use NS_ENSURE_SUCCESS here; it's not a fatal error
@ -2934,26 +2948,69 @@ nsresult nsImapMailFolder::NormalEndHeaderParseStream(nsIImapProtocol*
newMsgHdr->GetFlags(&msgFlags);
if (!(msgFlags & (MSG_FLAG_READ | MSG_FLAG_IMAP_DELETED))) // only fire on unread msgs that haven't been deleted
{
PRInt32 duplicateAction = nsIMsgIncomingServer::keepDups;
if (server)
server->GetIncomingDuplicateAction(&duplicateAction);
if (duplicateAction != nsIMsgIncomingServer::keepDups)
{
PRBool isDup;
server->IsNewHdrDuplicate(newMsgHdr, &isDup);
if (isDup)
{
// we want to do something similar to applying filter hits.
// if a dup is marked read, it shouldn't trigger biff.
// Same for deleting it or moving it to trash.
switch (duplicateAction)
{
case nsIMsgIncomingServer::deleteDups:
{
PRUint32 newFlags;
newMsgHdr->OrFlags(MSG_FLAG_READ | MSG_FLAG_IMAP_DELETED, &newFlags);
nsMsgKeyArray keysToFlag;
keysToFlag.Add(m_curMsgUid);
StoreImapFlags(kImapMsgSeenFlag | kImapMsgDeletedFlag, PR_TRUE, keysToFlag.GetArray(),
keysToFlag.GetSize(), nsnull);
m_msgMovedByFilter = PR_TRUE;
}
break;
case nsIMsgIncomingServer::moveDupsToTrash:
{
nsCOMPtr <nsIMsgFolder> trash;
GetTrashFolder(getter_AddRefs(trash));
if (trash)
{
nsXPIDLCString trashUri;
trash->GetURI(getter_Copies(trashUri));
nsresult err = MoveIncorporatedMessage(newMsgHdr, mDatabase, trashUri, nsnull, msgWindow);
if (NS_SUCCEEDED(err))
m_msgMovedByFilter = PR_TRUE;
}
}
break;
case nsIMsgIncomingServer::markDupsRead:
{
PRUint32 newFlags;
nsMsgKeyArray keysToFlag;
keysToFlag.Add(m_curMsgUid);
newMsgHdr->OrFlags(MSG_FLAG_READ, &newFlags);
StoreImapFlags(kImapMsgSeenFlag, PR_TRUE, keysToFlag.GetArray(), keysToFlag.GetSize(), nsnull);
}
break;
}
PRInt32 numNewMessages;
GetNumNewMessages(PR_FALSE, &numNewMessages);
SetNumNewMessages(numNewMessages - 1);
}
}
rv = m_msgParser->GetAllHeaders(&headers, &headersSize);
if (NS_SUCCEEDED(rv) && headers)
if (NS_SUCCEEDED(rv) && headers && !m_msgMovedByFilter)
{
if (m_filterList)
{
nsCOMPtr <nsIMsgWindow> msgWindow;
if (aProtocol)
{
nsCOMPtr <nsIImapUrl> aImapUrl;
nsCOMPtr <nsIMsgMailNewsUrl> msgUrl;
rv = aProtocol->GetRunningImapURL(getter_AddRefs(aImapUrl));
if (NS_SUCCEEDED(rv) && aImapUrl)
{
msgUrl = do_QueryInterface(aImapUrl);
if (msgUrl)
msgUrl->GetMsgWindow(getter_AddRefs(msgWindow));
}
}
GetMoveCoalescer(); // not sure why we're doing this here.
m_filterList->ApplyFiltersToHdr(nsMsgFilterType::InboxRule, newMsgHdr, this, mDatabase,
headers, headersSize, this, msgWindow);
@ -3789,7 +3846,7 @@ nsresult nsImapMailFolder::MoveIncorporatedMessage(nsIMsgDBHdr *mailHdr,
destIFolder->GetParent(getter_AddRefs(parentFolder));
if (parentFolder)
destIFolder->GetCanFileMessages(&canFileMessages);
if (!parentFolder || !canFileMessages)
if (filter && (!parentFolder || !canFileMessages))
{
filter->SetEnabled(PR_FALSE);
m_filterList->SaveToDefaultFile();

View File

@ -249,7 +249,6 @@ public:
NS_IMETHOD MarkThreadRead(nsIMsgThread *thread);
NS_IMETHOD SetLabelForMessages(nsISupportsArray *aMessages, nsMsgLabelValue aLabel);
NS_IMETHOD SetJunkScoreForMessages(nsISupportsArray *aMessages, const char *aJunkScore);
NS_IMETHOD DeleteSubFolders(nsISupportsArray *folders, nsIMsgWindow *msgWindow);
NS_IMETHOD ReadFromFolderCacheElem(nsIMsgFolderCacheElement *element);
NS_IMETHOD WriteToFolderCacheElem(nsIMsgFolderCacheElement *element);

View File

@ -1561,6 +1561,65 @@ PRInt32 nsParseNewMailState::PublishMsgHeader(nsIMsgWindow *msgWindow)
m_inboxFileStream->flush();
PRUint32 msgOffset;
(void) m_newMsgHdr->GetMessageOffset(&msgOffset);
nsCOMPtr<nsIMsgIncomingServer> server;
nsresult rv = m_rootFolder->GetServer(getter_AddRefs(server));
NS_ENSURE_SUCCESS(rv, 0);
PRInt32 duplicateAction;
server->GetIncomingDuplicateAction(&duplicateAction);
if (duplicateAction != nsIMsgIncomingServer::keepDups)
{
PRBool isDup;
server->IsNewHdrDuplicate(m_newMsgHdr, &isDup);
if (isDup)
{
// we want to do something similar to applying filter hits.
// if a dup is marked read, it shouldn't trigger biff.
// Same for deleting it or moving it to trash.
switch (duplicateAction)
{
case nsIMsgIncomingServer::deleteDups:
{
m_inboxFileStream->close();
nsresult truncRet = m_inboxFileSpec.Truncate(msgOffset);
NS_ASSERTION(NS_SUCCEEDED(truncRet), "unable to truncate file");
if (NS_FAILED(truncRet))
m_rootFolder->ThrowAlertMsg("dupDeleteFolderTruncateFailed", msgWindow);
// need to re-open the inbox file stream.
m_inboxFileStream->Open(m_inboxFileSpec, (PR_RDWR | PR_CREATE_FILE));
if (m_inboxFileStream)
m_inboxFileStream->seek(m_inboxFileSpec.GetFileSize());
m_mailDB->RemoveHeaderMdbRow(m_newMsgHdr);
// tell parser we've truncated the inbox.
nsParseMailMessageState::Init(msgOffset);
}
break;
case nsIMsgIncomingServer::moveDupsToTrash:
{
nsCOMPtr <nsIMsgFolder> trash;
GetTrashFolder(getter_AddRefs(trash));
if (trash)
MoveIncorporatedMessage(m_newMsgHdr, m_mailDB, trash,
nsnull, msgWindow);
}
break;
case nsIMsgIncomingServer::markDupsRead:
MarkFilteredMessageRead(m_newMsgHdr);
break;
}
PRInt32 numNewMessages;
m_downloadFolder->GetNumNewMessages(PR_FALSE, &numNewMessages);
m_downloadFolder->SetNumNewMessages(numNewMessages - 1);
m_newMsgHdr = nsnull;
return 0;
}
}
ApplyFilters(&moved, msgWindow, msgOffset);
}
if (!moved)
@ -1854,7 +1913,7 @@ NS_IMETHODIMP nsParseNewMailState::ApplyFilterHit(nsIMsgFilter *filter, nsIMsgWi
break;
case nsMsgFilterAction::FetchBodyFromPop3Server:
{
PRUint32 flags = 0;
PRUint32 flags = 0;
nsCOMPtr <nsIMsgFolder> downloadFolder;
msgHdr->GetFolder(getter_AddRefs(downloadFolder));
nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(downloadFolder);
@ -1867,13 +1926,13 @@ NS_IMETHODIMP nsParseNewMailState::ApplyFilterHit(nsIMsgFilter *filter, nsIMsgWi
nsCOMPtr<nsISupports> iSupports = do_QueryInterface(msgHdr);
messages->AppendElement(iSupports);
localFolder->MarkMsgsOnPop3Server(messages, POP3_FETCH_BODY);
// Don't add this header to the DB, we're going to replace it
// with the full message.
// Don't add this header to the DB, we're going to replace it
// with the full message.
m_msgMovedByFilter = PR_TRUE;
msgIsNew = PR_FALSE;
// Don't do anything else in this filter, wait until we
// have the full message.
*applyMore = PR_FALSE;
// Don't do anything else in this filter, wait until we
// have the full message.
*applyMore = PR_FALSE;
}
}
break;
@ -2052,11 +2111,14 @@ nsresult nsParseNewMailState::MoveIncorporatedMessage(nsIMsgDBHdr *mailHdr,
destIFolder->GetCanFileMessages(&canFileMessages);
if (!parentFolder || !canFileMessages)
{
filter->SetEnabled(PR_FALSE);
// we need to explicitly save the filter file.
if (m_filterList)
m_filterList->SaveToDefaultFile();
destIFolder->ThrowAlertMsg("filterDisabled", msgWindow);
if (filter)
{
filter->SetEnabled(PR_FALSE);
// we need to explicitly save the filter file.
if (m_filterList)
m_filterList->SaveToDefaultFile();
destIFolder->ThrowAlertMsg("filterDisabled", msgWindow);
}
return NS_MSG_NOT_A_MAIL_FOLDER;
}

View File

@ -430,6 +430,7 @@ pref("mail.server.default.max_articles", 500);
pref("mail.server.default.notify.on", true);
pref("mail.server.default.mark_old_read", false);
pref("mail.server.default.empty_trash_on_exit", false);
pref("mail.server.default.dup_action", 0);
pref("mail.server.default.using_subscription", true);
pref("mail.server.default.dual_use_folders", true);