Bug 481676 - "Move bayes listeners to nsMsgDBFolder" [r=neil sr=bienvenu]
authorKent James <kent@caspia.com>
Wed, 11 Mar 2009 19:53:12 +0000
changeset 2184 996cdbab71a91ad267a26fd9f92d10ff0cd9a90d
parent 2183 a4305dcef0180bc0a6768f5ead0428211a0aedf9
child 2185 92b814189264999c947ed7fa9e332da62b86efe4
push idunknown
push userunknown
push dateunknown
reviewersneil, bienvenu
bugs481676
Bug 481676 - "Move bayes listeners to nsMsgDBFolder" [r=neil sr=bienvenu]
mailnews/base/search/public/nsIMsgFilterPlugin.idl
mailnews/base/util/nsMsgDBFolder.cpp
mailnews/base/util/nsMsgDBFolder.h
mailnews/extensions/bayesian-spam-filter/src/nsBayesianFilter.cpp
mailnews/extensions/bayesian-spam-filter/test/unit/test_bug228675.js
mailnews/extensions/bayesian-spam-filter/test/unit/test_junkAsTraits.js
mailnews/extensions/bayesian-spam-filter/test/unit/test_traits.js
mailnews/imap/src/nsImapMailFolder.cpp
mailnews/imap/src/nsImapMailFolder.h
mailnews/local/src/nsLocalMailFolder.cpp
mailnews/local/src/nsLocalMailFolder.h
--- a/mailnews/base/search/public/nsIMsgFilterPlugin.idl
+++ b/mailnews/base/search/public/nsIMsgFilterPlugin.idl
@@ -70,38 +70,44 @@ interface nsIMsgFilterPlugin : nsISuppor
 /*
  * These interfaces typically implement a Bayesian classifier of messages.
  *
  * Two sets of interfaces may be used: the older junk-only interfaces, and
  * the newer trait-oriented interfaces that treat junk classification as
  * one of a set of classifications to accomplish.
  */
 
-/**
- * Inform a listener of a message's classification aClassification, which
- * can be one of 3 values:  UNCLASSIFIED, GOOD, or JUNK.
- * Pass the indicator aJunkPercent, where 0 is not junk,
- * 100 is junk, and intermediate values represent varying uncertainty.
- */
 [scriptable, uuid(b15a0f9c-df07-4af0-9ba8-80dca68ac35d)]
 interface nsIJunkMailClassificationListener : nsISupports
 {
-    void onMessageClassified(in string aMsgURI,
-      in nsMsgJunkStatus aClassification,
-      in PRUint32 aJunkPercent);
-
+  /**
+   * Inform a listener of a message's classification as junk. At the end
+   * of a batch of classifications, signify end of batch by calling with
+   * null aMsgURI (other parameters are don't care)
+   *
+   * @param aMsgURI          URI of the message that was classified.
+   * @param aClassification  classification of message as UNCLASSIFIED, GOOD,
+   *                         or JUNK.
+   * @param aJunkPercent     indicator of degree of uncertainty, with 100 being
+   *                         probably junk, and 0 probably good
+   */
+  void onMessageClassified(in string aMsgURI,
+                           in nsMsgJunkStatus aClassification,
+                           in PRUint32 aJunkPercent);
 };
 
 [scriptable, uuid(AF247D07-72F0-482d-9EAB-5A786407AA4C)]
 interface nsIMsgTraitClassificationListener : nsISupports
 {
   /**
    * Inform a listener of a message's match to traits. The list
    * of traits being matched is in aTraits. Corresponding
-   * indicator of match (percent) is in aPercents.
+   * indicator of match (percent) is in aPercents. At the end
+   * of a batch of classifications, signify end of batch by calling with
+   * null aMsgURI (other parameters are don't care)
    *
    * @param aMsgURI      URI of the message that was classified
    * @param aTraitCount  length of aTraits and aPercents arrays
    * @param aTraits      array of matched trait ids
    * @param aPercents    array of percent match (0 is unmatched, 100 is fully
    *                     matched) of the trait with the corresponding array
    *                     index in aTraits
    */
--- a/mailnews/base/util/nsMsgDBFolder.cpp
+++ b/mailnews/base/util/nsMsgDBFolder.cpp
@@ -143,19 +143,21 @@ PRUnichar *nsMsgDBFolder::kLocalizedTemp
 PRUnichar *nsMsgDBFolder::kLocalizedUnsentName;
 PRUnichar *nsMsgDBFolder::kLocalizedJunkName;
 PRUnichar *nsMsgDBFolder::kLocalizedArchivesName;
 
 PRUnichar *nsMsgDBFolder::kLocalizedBrandShortName;
 
 nsrefcnt nsMsgDBFolder::mInstanceCount=0;
 
-NS_IMPL_ISUPPORTS_INHERITED4(nsMsgDBFolder, nsRDFResource, 
+NS_IMPL_ISUPPORTS_INHERITED6(nsMsgDBFolder, nsRDFResource, 
                              nsISupportsWeakReference, nsIMsgFolder,
-                             nsIDBChangeListener, nsIUrlListener)
+                             nsIDBChangeListener, nsIUrlListener,
+                             nsIJunkMailClassificationListener,
+                             nsIMsgTraitClassificationListener)
 
 const nsStaticAtom nsMsgDBFolder::folder_atoms[] = {
   { "FolderLoaded", &nsMsgDBFolder::mFolderLoadedAtom },
   { "DeleteOrMoveMsgCompleted", &nsMsgDBFolder::mDeleteOrMoveMsgCompletedAtom },
   { "DeleteOrMoveMsgFailed", &nsMsgDBFolder::mDeleteOrMoveMsgFailedAtom },
   { "JunkStatusChanged", &nsMsgDBFolder::mJunkStatusChangedAtom },
   { "BiffState", &nsMsgDBFolder::kBiffStateAtom },
   { "NewMailReceived", &nsMsgDBFolder::kNewMailReceivedAtom },
@@ -2018,27 +2020,163 @@ nsMsgDBFolder::GetInheritedStringPropert
     if (parent)
       return parent->GetInheritedStringProperty(aPropertyName, aPropertyValue);
   }
 
   aPropertyValue.Assign(value);
   return NS_OK;
 }
 
-// sub-classes need to override
 nsresult
 nsMsgDBFolder::SpamFilterClassifyMessage(const char *aURI, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin)
 {
-  return aJunkMailPlugin->ClassifyMessage(aURI, aMsgWindow, nsnull);
+  nsresult rv;
+  nsCOMPtr<nsIMsgTraitService> traitService(do_GetService("@mozilla.org/msg-trait-service;1", &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint32 count;
+  PRUint32 *proIndices;
+  PRUint32 *antiIndices;
+  rv = traitService->GetEnabledIndices(&count, &proIndices, &antiIndices);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aJunkMailPlugin->ClassifyTraitsInMessage(aURI, count, proIndices, antiIndices, this, aMsgWindow, this);
+  NS_Free(proIndices);
+  NS_Free(antiIndices);
+  return rv;
 }
 
 nsresult
 nsMsgDBFolder::SpamFilterClassifyMessages(const char **aURIArray, PRUint32 aURICount, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin)
 {
-  return aJunkMailPlugin->ClassifyMessages(aURICount, aURIArray, aMsgWindow, nsnull);
+
+  nsresult rv;
+  nsCOMPtr<nsIMsgTraitService> traitService(do_GetService("@mozilla.org/msg-trait-service;1", &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint32 count;
+  PRUint32 *proIndices;
+  PRUint32 *antiIndices;
+  rv = traitService->GetEnabledIndices(&count, &proIndices, &antiIndices);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aJunkMailPlugin->ClassifyTraitsInMessages(aURICount, aURIArray, count,
+      proIndices, antiIndices, this, aMsgWindow, this);
+  NS_Free(proIndices);
+  NS_Free(antiIndices);
+  return rv;
+}
+
+NS_IMETHODIMP
+nsMsgDBFolder::OnMessageClassified(const char *aMsgURI,
+                                   nsMsgJunkStatus aClassification,
+                                   PRUint32 aJunkPercent)
+{
+  if (!aMsgURI)
+    return NS_OK;; // ignore end-of-batch signal
+
+  nsCOMPtr<nsIMsgIncomingServer> server;
+  nsresult rv = GetServer(getter_AddRefs(server));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsISpamSettings> spamSettings;
+  rv = server->GetSpamSettings(getter_AddRefs(spamSettings));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr <nsIMsgDBHdr> msgHdr;
+  rv = GetMsgDBHdrFromURI(aMsgURI, getter_AddRefs(msgHdr));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsMsgKey msgKey;
+  rv = msgHdr->GetMessageKey(&msgKey);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // check if this message needs junk classification
+  PRUint32 processingFlags;
+  GetProcessingFlags(msgKey, &processingFlags);
+
+  if (processingFlags & nsMsgProcessingFlags::ClassifyJunk)
+  {
+    AndProcessingFlags(msgKey, ~nsMsgProcessingFlags::ClassifyJunk);
+
+    nsCAutoString msgJunkScore;
+    msgJunkScore.AppendInt(aClassification == nsIJunkMailPlugin::JUNK ?
+          nsIJunkMailPlugin::IS_SPAM_SCORE:
+          nsIJunkMailPlugin::IS_HAM_SCORE);
+    mDatabase->SetStringProperty(msgKey, "junkscore", msgJunkScore.get());
+    mDatabase->SetStringProperty(msgKey, "junkscoreorigin", "plugin");
+
+    nsCAutoString strPercent;
+    strPercent.AppendInt(aJunkPercent);
+    mDatabase->SetStringProperty(msgKey, "junkpercent", strPercent.get());
+
+    if (aClassification == nsIJunkMailPlugin::JUNK)
+    {
+      // IMAP has its own way of marking read.
+      if (!(mFlags & nsMsgFolderFlags::ImapBox))
+      {
+        PRBool markAsReadOnSpam;
+        (void)spamSettings->GetMarkAsReadOnSpam(&markAsReadOnSpam);
+        if (markAsReadOnSpam)
+        {
+          rv = mDatabase->MarkRead(msgKey, true, this);
+          if (!NS_SUCCEEDED(rv))
+            NS_WARNING("failed marking spam message as read");
+        }
+      }
+      // mail folders will log junk hits with move info. Perhaps we should
+      // add a log here for non-mail folders as well, that don't override
+      // onMessageClassified
+      //rv = spamSettings->LogJunkHit(msgHdr, PR_FALSE);
+      //NS_ENSURE_SUCCESS(rv,rv);
+    }
+  }
+}
+
+NS_IMETHODIMP
+nsMsgDBFolder::OnMessageTraitsClassified(const char *aMsgURI,
+                                         PRUint32 aTraitCount,
+                                         PRUint32 *aTraits,
+                                         PRUint32 *aPercents)
+{
+  if (!aMsgURI) // This signifies end of batch
+    return NS_OK; // We are not handling batching
+  
+  nsresult rv;
+  nsCOMPtr <nsIMsgDBHdr> msgHdr;
+  rv = GetMsgDBHdrFromURI(aMsgURI, getter_AddRefs(msgHdr));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsMsgKey msgKey;
+  rv = msgHdr->GetMessageKey(&msgKey);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint32 processingFlags;
+  GetProcessingFlags(msgKey, &processingFlags);
+  if (!(processingFlags & nsMsgProcessingFlags::ClassifyTraits))
+    return NS_OK;
+
+  AndProcessingFlags(msgKey, ~nsMsgProcessingFlags::ClassifyTraits);
+
+  nsCOMPtr<nsIMsgTraitService> traitService;
+  traitService = do_GetService("@mozilla.org/msg-trait-service;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  for (PRUint32 i = 0; i < aTraitCount; i++)
+  {
+    if (aTraits[i] == nsIJunkMailPlugin::JUNK_TRAIT)
+      continue; // junk is processed by the junk listener
+    nsCAutoString traitId;
+    rv = traitService->GetId(aTraits[i], traitId);
+    traitId.Insert(NS_LITERAL_CSTRING("bayespercent/"), 0);
+    nsCAutoString strPercent;
+    strPercent.AppendInt(aPercents[i]);
+    mDatabase->SetStringPropertyByHdr(msgHdr, traitId.get(), strPercent.get());
+  }
+  return NS_OK;
 }
 
 /**
  * Call the filter plugins (XXX currently just one)
  */
 NS_IMETHODIMP
 nsMsgDBFolder::CallFilterPlugins(nsIMsgWindow *aMsgWindow, PRBool *aFiltersRun)
 {
--- a/mailnews/base/util/nsMsgDBFolder.h
+++ b/mailnews/base/util/nsMsgDBFolder.h
@@ -55,42 +55,46 @@
 #include "nsIMsgHdr.h"
 #include "nsIOutputStream.h"
 #include "nsITransport.h"
 #include "nsIStringBundle.h"
 #include "nsTObserverArray.h"
 #include "nsCOMArray.h"
 #include "nsMsgKeySet.h"
 #include "nsMsgMessageFlags.h"
+#include "nsIMsgFilterPlugin.h"
 class nsIMsgFolderCacheElement;
-class nsIJunkMailPlugin;
 class nsICollation;
 class nsMsgKeySetU;
 
  /* 
   * nsMsgDBFolder
   * class derived from nsMsgFolder for those folders that use an nsIMsgDatabase
   */ 
 
 #undef IMETHOD_VISIBILITY
 #define IMETHOD_VISIBILITY NS_VISIBILITY_DEFAULT
 
 class NS_MSG_BASE nsMsgDBFolder: public nsRDFResource,
                                  public nsSupportsWeakReference,
                                  public nsIMsgFolder,
                                  public nsIDBChangeListener,
-                                 public nsIUrlListener
+                                 public nsIUrlListener,
+                                 public nsIJunkMailClassificationListener,
+                                 public nsIMsgTraitClassificationListener
 {
 public: 
   nsMsgDBFolder(void);
   virtual ~nsMsgDBFolder(void);
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIMSGFOLDER
   NS_DECL_NSIDBCHANGELISTENER
   NS_DECL_NSIURLLISTENER
+  NS_DECL_NSIJUNKMAILCLASSIFICATIONLISTENER
+  NS_DECL_NSIMSGTRAITCLASSIFICATIONLISTENER
   
   NS_IMETHOD WriteToFolderCacheElem(nsIMsgFolderCacheElement *element);
   NS_IMETHOD ReadFromFolderCacheElem(nsIMsgFolderCacheElement *element);
 
   // nsRDFResource overrides
   NS_IMETHOD Init(const char* aURI);
 
   // These functions are used for tricking the front end into thinking that we have more 
--- a/mailnews/extensions/bayesian-spam-filter/src/nsBayesianFilter.cpp
+++ b/mailnews/extensions/bayesian-spam-filter/src/nsBayesianFilter.cpp
@@ -1146,16 +1146,21 @@ public:
     {
 
       if (++mCurMessageToClassify < mNumMessagesToClassify && mMessageURIs[mCurMessageToClassify]) {
         PR_LOG(BayesianFilterLogModule, PR_LOG_WARNING, ("classifyNextMessage(%s)", mMessageURIs[mCurMessageToClassify]));
         mFilter->tokenizeMessage(mMessageURIs[mCurMessageToClassify], mMsgWindow, this);
       }
       else
       {
+        // call all listeners with null parameters to signify end of batch
+        if (mJunkListener)
+          mJunkListener->OnMessageClassified(nsnull, nsnull, nsnull);
+        if (mTraitListener)
+          mTraitListener->OnMessageTraitsClassified(nsnull, nsnull, nsnull, nsnull);
         mTokenListener = nsnull; // this breaks the circular ref that keeps this object alive
                                  // so we will be destroyed as a result.
       }
     }
 
 private:
     nsBayesianFilter* mFilter;
     nsCOMPtr<nsISupports> mSupports;
--- a/mailnews/extensions/bayesian-spam-filter/test/unit/test_bug228675.js
+++ b/mailnews/extensions/bayesian-spam-filter/test/unit/test_bug228675.js
@@ -72,16 +72,18 @@ function run_test()
   nsIJunkMailPlugin.setMessageClassification(getSpec(email),
     kUnclassified, classification, null, doTestingListener);
 }
 
 var doTestingListener = 
 {
   onMessageClassified: function(aMsgURI, aClassification, aJunkPercent)
   {
+    if (!aMsgURI)
+      return; // ignore end-of-batch signal
     var email = emails.shift();
     var classification = classifications.shift();
     if (email)
     { nsIJunkMailPlugin.setMessageClassification(getSpec(email),
           kUnclassified, classification, null, doTestingListener);
       return;
     }
     
--- a/mailnews/extensions/bayesian-spam-filter/test/unit/test_junkAsTraits.js
+++ b/mailnews/extensions/bayesian-spam-filter/test/unit/test_junkAsTraits.js
@@ -311,16 +311,18 @@ function run_test()
   startCommand();
 }
 
 var junkListener =
 {
 // nsIJunkMailClassificationListener implementation
   onMessageClassified: function(aMsgURI, aClassification, aJunkPercent)
   {
+    if (!aMsgURI)
+      return; // ignore end-of-batch signal
     //print("Message URI is " + aMsgURI);
     //print("Junk percent is " + aJunkPercent);
     //print("Classification is " + aClassification);
     var command = gTest.command;
     var junkPercent = gTest.junkPercent;
     // file returned correctly
     do_check_eq(getSpec(gTest.fileName), aMsgURI);
 
@@ -359,16 +361,18 @@ var junkListener =
   }
 };
 
 var traitListener =
 {
   //nsIMsgTraitClassificationListener implementation
   onMessageTraitsClassified: function(aMsgURI, {}, aTraits, aPercents)
   {
+    if (!aMsgURI)
+      return; //ignore end-of-batch signal
     //print("(Trait Listener)Message URI is " + aMsgURI);
     //print("(Trait Listener)Junk percent is " + aPercents);
     var command = gTest.command;
     var junkPercent = gTest.junkPercent;
     //print("command, junkPercent is " + command + " , " + junkPercent);
 
     do_check_eq(getSpec(gTest.fileName), aMsgURI);
 
--- a/mailnews/extensions/bayesian-spam-filter/test/unit/test_traits.js
+++ b/mailnews/extensions/bayesian-spam-filter/test/unit/test_traits.js
@@ -141,16 +141,18 @@ function run_test()
 }
 
 var listener =
 {
   //nsIMsgTraitClassificationListener implementation
   onMessageTraitsClassified: function(aMsgURI, {}, aTraits, aPercents)
   {
     //print("Message URI is " + aMsgURI);
+    if (!aMsgURI)
+      return; //ignore end-of-batch signal
 
     switch (gTest.command)
     {
       case kClass:
         do_check_eq(gTest.files[gTest.currentIndex], aMsgURI);
         var currentPercents = gTest.percents[gTest.currentIndex];
         for (var i = 0; i < currentPercents.length; i++)
         {
--- a/mailnews/imap/src/nsImapMailFolder.cpp
+++ b/mailnews/imap/src/nsImapMailFolder.cpp
@@ -119,17 +119,16 @@
 #include "nsIExternalProtocolService.h"
 #include "nsCExternalHandlerService.h"
 #include "prprf.h"
 #include "nsIMutableArray.h"
 #include "nsArrayUtils.h"
 #include "nsArrayEnumerator.h"
 #include "nsAutoSyncManager.h"
 #include "nsIMsgFilterCustomAction.h"
-#include "nsIMsgTraitService.h"
 #include "nsMsgReadStateTxn.h"
 
 
 static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
 static NS_DEFINE_CID(kParseMailMsgStateCID, NS_PARSEMAILMSGSTATE_CID);
 static NS_DEFINE_CID(kCImapHostSessionList, NS_IIMAPHOSTSESSIONLIST_CID);
 
 nsIAtom* nsImapMailFolder::mImapHdrDownloadedAtom=nsnull;
@@ -240,17 +239,16 @@ nsImapMailFolder::nsImapMailFolder() :
   m_numServerUnseenMessages = 0;
   m_numServerTotalMessages = 0;
   m_nextUID = nsMsgKey_None;
   m_hierarchyDelimiter = kOnlineHierarchySeparatorUnknown;
   m_folderACL = nsnull;
   m_aclFlags = 0;
   m_supportedUserFlags = 0;
   m_namespace = nsnull;
-  m_numFilterClassifyRequests = 0;
   m_pendingPlaybackReq = nsnull;
 }
 
 nsImapMailFolder::~nsImapMailFolder()
 {
   MOZ_COUNT_DTOR(nsImapMailFolder);
   if (m_appendMsgMonitor)
       PR_DestroyMonitor(m_appendMsgMonitor);
@@ -269,18 +267,16 @@ NS_IMPL_ADDREF_INHERITED(nsImapMailFolde
 NS_IMPL_RELEASE_INHERITED(nsImapMailFolder, nsMsgDBFolder)
 NS_IMPL_QUERY_HEAD(nsImapMailFolder)
     NS_IMPL_QUERY_BODY(nsIMsgImapMailFolder)
     NS_IMPL_QUERY_BODY(nsICopyMessageListener)
     NS_IMPL_QUERY_BODY(nsIImapMailFolderSink)
     NS_IMPL_QUERY_BODY(nsIImapMessageSink)
     NS_IMPL_QUERY_BODY(nsIUrlListener)
     NS_IMPL_QUERY_BODY(nsIMsgFilterHitNotify)
-    NS_IMPL_QUERY_BODY(nsIJunkMailClassificationListener)
-    NS_IMPL_QUERY_BODY(nsIMsgTraitClassificationListener)
 NS_IMPL_QUERY_TAIL_INHERITING(nsMsgDBFolder)
 
 nsresult nsImapMailFolder::AddDirectorySeparator(nsILocalFile *path)
 {
   if (mURI.Equals(kImapRootURI))
   {
     // don't concat the full separator with .sbd
   }
@@ -7916,58 +7912,16 @@ nsresult nsImapMailFolder::GetMoveCoales
   {
     m_moveCoalescer = new nsImapMoveCoalescer(this, nsnull /* msgWindow */);
     NS_ENSURE_TRUE (m_moveCoalescer, NS_ERROR_OUT_OF_MEMORY);
     m_moveCoalescer->AddRef();
   }
   return NS_OK;
 }
 
-nsresult
-nsImapMailFolder::SpamFilterClassifyMessage(const char *aURI, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin)
-{
-  nsresult rv;
-  nsCOMPtr<nsIMsgTraitService> traitService(do_GetService("@mozilla.org/msg-trait-service;1", &rv));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRUint32 count;
-  PRUint32 *proIndices;
-  PRUint32 *antiIndices;
-  rv = traitService->GetEnabledIndices(&count, &proIndices, &antiIndices);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  ++m_numFilterClassifyRequests;
-  rv = aJunkMailPlugin->ClassifyTraitsInMessage(aURI, count, proIndices,
-                          antiIndices, this, aMsgWindow, this);
-  NS_Free(proIndices);
-  NS_Free(antiIndices);
-  return rv;
-}
-
-nsresult
-nsImapMailFolder::SpamFilterClassifyMessages(const char **aURIArray, PRUint32 aURICount, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin)
-{
-  nsresult rv;
-  nsCOMPtr<nsIMsgTraitService> traitService(do_GetService("@mozilla.org/msg-trait-service;1", &rv));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRUint32 count;
-  PRUint32 *proIndices;
-  PRUint32 *antiIndices;
-  rv = traitService->GetEnabledIndices(&count, &proIndices, &antiIndices);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  m_numFilterClassifyRequests += aURICount;
-  rv = aJunkMailPlugin->ClassifyTraitsInMessages(aURICount, aURIArray, count,
-                          proIndices, antiIndices, this, aMsgWindow, this);
-  NS_Free(proIndices);
-  NS_Free(antiIndices);
-  return rv;
-}
-
 NS_IMETHODIMP
 nsImapMailFolder::StoreCustomKeywords(nsIMsgWindow *aMsgWindow, const nsACString& aFlagsToAdd,
                                       const nsACString& aFlagsToSubtract, nsMsgKey *aKeysToStore, PRUint32 aNumKeys, nsIURI **_retval)
 {
   nsresult rv;
   if (WeAreOffline())
   {
     GetDatabase();
@@ -8054,119 +8008,114 @@ NS_IMETHODIMP
 nsImapMailFolder::OnMessageClassified(const char * aMsgURI,
   nsMsgJunkStatus aClassification,
   PRUint32 aJunkPercent)
 {
   nsCOMPtr <nsIMsgIncomingServer> server;
   nsresult rv = GetServer(getter_AddRefs(server));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsISpamSettings> spamSettings;
-  rv = server->GetSpamSettings(getter_AddRefs(spamSettings));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr <nsIMsgDBHdr> msgHdr;
-  rv = GetMsgDBHdrFromURI(aMsgURI, getter_AddRefs(msgHdr));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsMsgKey msgKey;
-  rv = msgHdr->GetMessageKey(&msgKey);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // check if this message needs junk classification
-
-  PRUint32 processingFlags;
-  GetProcessingFlags(msgKey, &processingFlags);
-
-  if (processingFlags & nsMsgProcessingFlags::ClassifyJunk)
-  {
-    AndProcessingFlags(msgKey, ~nsMsgProcessingFlags::ClassifyJunk);
-    nsCString spamFolderURI;
-
-    nsCAutoString msgJunkScore;
-    msgJunkScore.AppendInt(aClassification == nsIJunkMailPlugin::JUNK ?
-          nsIJunkMailPlugin::IS_SPAM_SCORE:
-          nsIJunkMailPlugin::IS_HAM_SCORE);
-    mDatabase->SetStringProperty(msgKey, "junkscore", msgJunkScore.get());
-    mDatabase->SetStringProperty(msgKey, "junkscoreorigin", "plugin");
-
-    nsCAutoString msgJunkPercent;
-    msgJunkPercent.AppendInt(aJunkPercent);
-    mDatabase->SetStringProperty(msgKey, "junkpercent", msgJunkPercent.get());
-
-    GetMoveCoalescer();
-    if (m_moveCoalescer)
-    {
-      nsTArray<nsMsgKey> *keysToClassify = m_moveCoalescer->GetKeyBucket((aClassification == nsIJunkMailPlugin::JUNK) ? 0 : 1);
-      NS_ASSERTION(keysToClassify, "error getting key bucket");
-      if (keysToClassify)
-        keysToClassify->AppendElement(msgKey);
-    }
-    if (aClassification == nsIJunkMailPlugin::JUNK)
-    {
-      PRBool markAsReadOnSpam;
-      (void)spamSettings->GetMarkAsReadOnSpam(&markAsReadOnSpam);
-      if (markAsReadOnSpam)
+  if (aMsgURI) // not end of batch
+  {
+    nsCOMPtr <nsIMsgDBHdr> msgHdr;
+    rv = GetMsgDBHdrFromURI(aMsgURI, getter_AddRefs(msgHdr));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsMsgKey msgKey;
+    rv = msgHdr->GetMessageKey(&msgKey);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // check if this message needs junk classification
+
+    PRUint32 processingFlags;
+    GetProcessingFlags(msgKey, &processingFlags);
+
+    if (processingFlags & nsMsgProcessingFlags::ClassifyJunk)
+    {
+      nsMsgDBFolder::OnMessageClassified(aMsgURI, aClassification, aJunkPercent);
+
+      GetMoveCoalescer();
+      if (m_moveCoalescer)
+      {
+        nsTArray<nsMsgKey> *keysToClassify = m_moveCoalescer->GetKeyBucket((aClassification == nsIJunkMailPlugin::JUNK) ? 0 : 1);
+        NS_ASSERTION(keysToClassify, "error getting key bucket");
+        if (keysToClassify)
+          keysToClassify->AppendElement(msgKey);
+      }
+      if (aClassification == nsIJunkMailPlugin::JUNK)
       {
-        if (!m_junkMessagesToMarkAsRead)
-          m_junkMessagesToMarkAsRead = do_CreateInstance(NS_ARRAY_CONTRACTID);
-        m_junkMessagesToMarkAsRead->AppendElement(msgHdr, PR_FALSE);
-      }
-
-      PRBool willMoveMessage = PR_FALSE;
-
-      // don't do the move when we are opening up
-      // the junk mail folder or the trash folder
-      // or when manually classifying messages in those folders
-      if (!(mFlags & nsMsgFolderFlags::Junk || mFlags & nsMsgFolderFlags::Trash))
-      {
-        PRBool moveOnSpam;
-        (void)spamSettings->GetMoveOnSpam(&moveOnSpam);
-        if (moveOnSpam)
+        nsCOMPtr<nsISpamSettings> spamSettings;
+        rv = server->GetSpamSettings(getter_AddRefs(spamSettings));
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        PRBool markAsReadOnSpam;
+        (void)spamSettings->GetMarkAsReadOnSpam(&markAsReadOnSpam);
+        if (markAsReadOnSpam)
         {
-          rv = spamSettings->GetSpamFolderURI(getter_Copies(spamFolderURI));
-          NS_ENSURE_SUCCESS(rv,rv);
-
-          if (!spamFolderURI.IsEmpty())
+          if (!m_junkMessagesToMarkAsRead)
+          {
+            m_junkMessagesToMarkAsRead = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+            NS_ENSURE_SUCCESS(rv, rv);
+          }
+          m_junkMessagesToMarkAsRead->AppendElement(msgHdr, PR_FALSE);
+        }
+
+        PRBool willMoveMessage = PR_FALSE;
+
+        // don't do the move when we are opening up
+        // the junk mail folder or the trash folder
+        // or when manually classifying messages in those folders
+        if (!(mFlags & nsMsgFolderFlags::Junk || mFlags & nsMsgFolderFlags::Trash))
+        {
+          PRBool moveOnSpam;
+          (void)spamSettings->GetMoveOnSpam(&moveOnSpam);
+          if (moveOnSpam)
           {
-            nsCOMPtr<nsIMsgFolder> folder;
-            rv = GetExistingFolder(spamFolderURI, getter_AddRefs(folder));
-            if (NS_SUCCEEDED(rv) && folder)
+            nsCString spamFolderURI;
+            rv = spamSettings->GetSpamFolderURI(getter_Copies(spamFolderURI));
+            NS_ENSURE_SUCCESS(rv,rv);
+
+            if (!spamFolderURI.IsEmpty())
             {
-              rv = folder->SetFlag(nsMsgFolderFlags::Junk);
-              NS_ENSURE_SUCCESS(rv,rv);
-              if (NS_SUCCEEDED(GetMoveCoalescer()))
+              nsCOMPtr<nsIMsgFolder> folder;
+              rv = GetExistingFolder(spamFolderURI, getter_AddRefs(folder));
+              if (NS_SUCCEEDED(rv) && folder)
               {
-                m_moveCoalescer->AddMove(folder, msgKey);
-                willMoveMessage = PR_TRUE;
+                rv = folder->SetFlag(nsMsgFolderFlags::Junk);
+                NS_ENSURE_SUCCESS(rv,rv);
+                if (NS_SUCCEEDED(GetMoveCoalescer()))
+                {
+                  m_moveCoalescer->AddMove(folder, msgKey);
+                  willMoveMessage = PR_TRUE;
+                }
               }
-            }
-            else
-            {
-              // XXX TODO
-              // JUNK MAIL RELATED
-              // the listener should do
-              // rv = folder->SetFlag(nsMsgFolderFlags::Junk);
-              // NS_ENSURE_SUCCESS(rv,rv);
-              // if (NS_SUCCEEDED(GetMoveCoalescer())) {
-              //   m_moveCoalescer->AddMove(folder, msgKey);
-              //   willMoveMessage = PR_TRUE;
-              // }
-              rv = GetOrCreateFolder(spamFolderURI, nsnull /* aListener */);
-              NS_ASSERTION(NS_SUCCEEDED(rv), "GetOrCreateFolder failed");
+              else
+              {
+                // XXX TODO
+                // JUNK MAIL RELATED
+                // the listener should do
+                // rv = folder->SetFlag(nsMsgFolderFlags::Junk);
+                // NS_ENSURE_SUCCESS(rv,rv);
+                // if (NS_SUCCEEDED(GetMoveCoalescer())) {
+                //   m_moveCoalescer->AddMove(folder, msgKey);
+                //   willMoveMessage = PR_TRUE;
+                // }
+                rv = GetOrCreateFolder(spamFolderURI, nsnull /* aListener */);
+                NS_ASSERTION(NS_SUCCEEDED(rv), "GetOrCreateFolder failed");
+              }
             }
           }
         }
+        rv = spamSettings->LogJunkHit(msgHdr, willMoveMessage);
+        NS_ENSURE_SUCCESS(rv,rv);
       }
-      rv = spamSettings->LogJunkHit(msgHdr, willMoveMessage);
-      NS_ENSURE_SUCCESS(rv,rv);
-    }
-  }
-
-  if (--m_numFilterClassifyRequests == 0)
+    }
+  }
+
+  else // end of batch
   {
     if (m_junkMessagesToMarkAsRead)
     {
       PRUint32 count;
       m_junkMessagesToMarkAsRead->GetLength(&count);
       if (count > 0)
       {
         rv = MarkMessagesRead(m_junkMessagesToMarkAsRead, true);
@@ -8180,56 +8129,17 @@ nsImapMailFolder::OnMessageClassified(co
     if ((!pendingMoves || !ShowPreviewText()) && m_performingBiff)
     {
       // we don't need to adjust the num new messages in this folder because
       // the playback moves code already did that.
       (void) PerformBiffNotifications();
       server->SetPerformingBiff(PR_FALSE);
       m_performingBiff = PR_FALSE;
     }
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsImapMailFolder::OnMessageTraitsClassified(
-    const char *aMsgURI, PRUint32 aTraitCount,
-    PRUint32 *aTraits, PRUint32 *aPercents)
-{
-  nsresult rv;
-  nsCOMPtr <nsIMsgDBHdr> msgHdr;
-  rv = GetMsgDBHdrFromURI(aMsgURI, getter_AddRefs(msgHdr));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsMsgKey msgKey;
-  rv = msgHdr->GetMessageKey(&msgKey);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRUint32 processingFlags;
-  GetProcessingFlags(msgKey, &processingFlags);
-  if (!(processingFlags & nsMsgProcessingFlags::ClassifyTraits))
-    return NS_OK;
-
-  AndProcessingFlags(msgKey, ~nsMsgProcessingFlags::ClassifyTraits);
-
-  nsCOMPtr<nsIMsgTraitService> traitService;
-  traitService = do_GetService("@mozilla.org/msg-trait-service;1", &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  for (PRUint32 i = 0; i < aTraitCount; i++)
-  {
-    if (aTraits[i] == nsIJunkMailPlugin::JUNK_TRAIT)
-      continue; // junk is processed by the junk listener
-
-    nsCAutoString traitId;
-    rv = traitService->GetId(aTraits[i], traitId);
-    traitId.Insert(NS_LITERAL_CSTRING("bayespercent/"), 0);
-    nsCAutoString strPercent;
-    strPercent.AppendInt(aPercents[i]);
-    mDatabase->SetStringPropertyByHdr(msgHdr, traitId.get(), strPercent.get());
+    // We aren't calling the parent method, since it ignores batching anyway
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsImapMailFolder::GetShouldDownloadAllHeaders(PRBool *aResult)
 {
   NS_ENSURE_ARG_POINTER(aResult);
--- a/mailnews/imap/src/nsImapMailFolder.h
+++ b/mailnews/imap/src/nsImapMailFolder.h
@@ -48,17 +48,16 @@
 #include "nsAutoPtr.h"
 #include "nsIImapIncomingServer.h" // we need this for its IID
 #include "nsIMsgParseMailMsgState.h"
 #include "nsITransactionManager.h"
 #include "nsMsgTxn.h"
 #include "nsIMsgMessageService.h"
 #include "nsIMsgFilterHitNotify.h"
 #include "nsIMsgFilterList.h"
-#include "nsIMsgFilterPlugin.h"
 #include "prmon.h"
 #include "nsIMsgImapMailFolder.h"
 #include "nsIMsgLocalMailFolder.h"
 #include "nsIImapMailFolderSink.h"
 #include "nsIImapServerSink.h"
 #include "nsIMsgFilterPlugin.h"
 #include "nsIEventTarget.h"
 #include "nsIThread.h"
@@ -215,19 +214,17 @@ struct nsPlaybackRequest
   nsIMsgWindow *MsgWindow;
 };
 
 class nsImapMailFolder :  public nsMsgDBFolder,
                           public nsIMsgImapMailFolder,
                           public nsIImapMailFolderSink,
                           public nsIImapMessageSink,
                           public nsICopyMessageListener,
-                          public nsIMsgFilterHitNotify,
-                          public nsIJunkMailClassificationListener,
-                          public nsIMsgTraitClassificationListener
+                          public nsIMsgFilterHitNotify
 {
  static const PRUint32 PLAYBACK_TIMER_INTERVAL_IN_MS = 500; 
 public:
   nsImapMailFolder();
   virtual ~nsImapMailFolder();
 
   NS_DECL_ISUPPORTS_INHERITED
 
@@ -324,30 +321,27 @@ public:
   NS_DECL_NSIREQUESTOBSERVER
 
   // nsIUrlListener methods
   NS_IMETHOD OnStartRunningUrl(nsIURI * aUrl);
   NS_IMETHOD OnStopRunningUrl(nsIURI * aUrl, nsresult aExitCode);
 
   NS_DECL_NSIMSGFILTERHITNOTIFY
   NS_DECL_NSIJUNKMAILCLASSIFICATIONLISTENER
-  NS_DECL_NSIMSGTRAITCLASSIFICATIONLISTENER
 
   NS_IMETHOD IsCommandEnabled(const nsACString& command, PRBool *result);
   NS_IMETHOD SetFilterList(nsIMsgFilterList *aMsgFilterList);
   NS_IMETHOD GetCustomIdentity(nsIMsgIdentity **aIdentity);
 
   nsresult AddSubfolderWithPath(nsAString& name, nsILocalFile *dbPath, nsIMsgFolder **child, PRBool brandNew = PR_FALSE);
   nsresult MoveIncorporatedMessage(nsIMsgDBHdr *mailHdr,
                                   nsIMsgDatabase *sourceDB,
                                   const nsACString& destFolder,
                                   nsIMsgFilter *filter,
                                   nsIMsgWindow *msgWindow);
-  virtual nsresult SpamFilterClassifyMessage(const char *aURI, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin);
-  virtual nsresult SpamFilterClassifyMessages(const char **aURIArray, PRUint32 aURICount, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin);
 
   static nsresult  AllocateUidStringFromKeys(nsMsgKey *keys, PRUint32 numKeys, nsCString &msgIds);
   static nsresult  BuildIdsAndKeyArray(nsIArray* messages, nsCString& msgIds, nsTArray<nsMsgKey>& keyArray);
 
   // these might end up as an nsIImapMailFolder attribute.
   nsresult SetSupportedUserFlags(PRUint32 userFlags);
   nsresult GetSupportedUserFlags(PRUint32 *userFlags);
 
@@ -448,17 +442,16 @@ protected:
 
   PRBool m_initialized;
   PRBool m_haveDiscoveredAllFolders;
   PRBool m_haveReadNameFromDB;
   nsCOMPtr<nsIMsgParseMailMsgState> m_msgParser;
   nsCOMPtr<nsIMsgFilterList> m_filterList;
   nsCOMPtr<nsIMsgFilterPlugin> m_filterPlugin;  // XXX should be a list
   // used with filter plugins to know when we've finished classifying and can playback moves
-  PRInt32 m_numFilterClassifyRequests;
   PRBool m_msgMovedByFilter;
   nsImapMoveCoalescer *m_moveCoalescer; // strictly owned by the nsImapMailFolder
   nsCOMPtr<nsIMutableArray> m_junkMessagesToMarkAsRead;
   nsMsgKey m_curMsgUid;
   PRUint32 m_uidValidity;
   // used for condstore support;
   PRUint64 m_highestModSeq;
 
--- a/mailnews/local/src/nsLocalMailFolder.cpp
+++ b/mailnews/local/src/nsLocalMailFolder.cpp
@@ -151,30 +151,28 @@ nsLocalFolderScanState::~nsLocalFolderSc
 ///////////////////////////////////////////////////////////////////////////////
 // nsMsgLocalMailFolder interface
 ///////////////////////////////////////////////////////////////////////////////
 
 nsMsgLocalMailFolder::nsMsgLocalMailFolder(void)
   : mCopyState(nsnull), mHaveReadNameFromDB(PR_FALSE),
     mInitialized(PR_FALSE),
     mCheckForNewMessagesAfterParsing(PR_FALSE), m_parsingFolder(PR_FALSE),
-    mNumFilterClassifyRequests(0), mDownloadState(DOWNLOAD_STATE_NONE)
+    mDownloadState(DOWNLOAD_STATE_NONE)
 {
 }
 
 nsMsgLocalMailFolder::~nsMsgLocalMailFolder(void)
 {
 }
 
-NS_IMPL_ISUPPORTS_INHERITED4(nsMsgLocalMailFolder,
+NS_IMPL_ISUPPORTS_INHERITED2(nsMsgLocalMailFolder,
                              nsMsgDBFolder,
                              nsICopyMessageListener,
-                             nsIMsgLocalMailFolder,
-                             nsIJunkMailClassificationListener,
-                             nsIMsgTraitClassificationListener)
+                             nsIMsgLocalMailFolder)
 
 ////////////////////////////////////////////////////////////////////////////////
 
 static PRBool
 nsStringEndsWith(nsString& name, const char *ending)
 {
   PRInt32 len = name.Length();
   if (len == 0) return PR_FALSE;
@@ -3468,238 +3466,134 @@ nsMsgLocalMailFolder::NotifyCompactCompl
 }
 
 NS_IMETHODIMP nsMsgLocalMailFolder::Shutdown(PRBool shutdownChildren)
 {
   mInitialized = PR_FALSE;
   return nsMsgDBFolder::Shutdown(shutdownChildren);
 }
 
-nsresult
-nsMsgLocalMailFolder::SpamFilterClassifyMessage(const char *aURI, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin)
-{
-  nsresult rv;
-  nsCOMPtr<nsIMsgTraitService> traitService(do_GetService("@mozilla.org/msg-trait-service;1", &rv));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRUint32 count;
-  PRUint32 *proIndices;
-  PRUint32 *antiIndices;
-  rv = traitService->GetEnabledIndices(&count, &proIndices, &antiIndices);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  ++mNumFilterClassifyRequests;
-  rv = aJunkMailPlugin->ClassifyTraitsInMessage(aURI, count, proIndices, antiIndices, this, aMsgWindow, this);
-  NS_Free(proIndices);
-  NS_Free(antiIndices);
-  return rv;
-}
-
-nsresult
-nsMsgLocalMailFolder::SpamFilterClassifyMessages(const char **aURIArray, PRUint32 aURICount, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin)
-{
-  NS_ASSERTION(!mNumFilterClassifyRequests, "shouldn't call this when already classifying messages");
-  mNumFilterClassifyRequests = aURICount;
-  nsresult rv;
-  nsCOMPtr<nsIMsgTraitService> traitService(do_GetService("@mozilla.org/msg-trait-service;1", &rv));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRUint32 count;
-  PRUint32 *proIndices;
-  PRUint32 *antiIndices;
-  rv = traitService->GetEnabledIndices(&count, &proIndices, &antiIndices);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = aJunkMailPlugin->ClassifyTraitsInMessages(aURICount, aURIArray, count,
-      proIndices, antiIndices, this, aMsgWindow, this);
-  NS_Free(proIndices);
-  NS_Free(antiIndices);
-  return rv;
-}
-
 NS_IMETHODIMP
 nsMsgLocalMailFolder::OnMessageClassified(const char *aMsgURI,
   nsMsgJunkStatus aClassification,
   PRUint32 aJunkPercent)
 
 {
-  if (mNumFilterClassifyRequests > 0)
-    --mNumFilterClassifyRequests;
-
   nsCOMPtr<nsIMsgIncomingServer> server;
   nsresult rv = GetServer(getter_AddRefs(server));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr <nsIMsgDBHdr> msgHdr;
-  rv = GetMsgDBHdrFromURI(aMsgURI, getter_AddRefs(msgHdr));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsMsgKey msgKey;
-  rv = msgHdr->GetMessageKey(&msgKey);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   nsCOMPtr<nsISpamSettings> spamSettings;
   rv = server->GetSpamSettings(getter_AddRefs(spamSettings));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // check if this message needs junk classification
-  PRUint32 processingFlags;
-  GetProcessingFlags(msgKey, &processingFlags);
-
-  if (processingFlags & nsMsgProcessingFlags::ClassifyJunk)
+  nsCString spamFolderURI;
+  rv = spamSettings->GetSpamFolderURI(getter_Copies(spamFolderURI));
+  NS_ENSURE_SUCCESS(rv,rv);
+
+  if (aMsgURI) // not end of batch
   {
-    AndProcessingFlags(msgKey, ~nsMsgProcessingFlags::ClassifyJunk);
-
-    nsCAutoString msgJunkScore;
-    msgJunkScore.AppendInt(aClassification == nsIJunkMailPlugin::JUNK ?
-          nsIJunkMailPlugin::IS_SPAM_SCORE:
-          nsIJunkMailPlugin::IS_HAM_SCORE);
-    mDatabase->SetStringProperty(msgKey, "junkscore", msgJunkScore.get());
-    mDatabase->SetStringProperty(msgKey, "junkscoreorigin", "plugin");
-
-    nsCAutoString strPercent;
-    strPercent.AppendInt(aJunkPercent);
-    mDatabase->SetStringProperty(msgKey, "junkpercent", strPercent.get());
-
-    PRBool moveOnSpam = PR_FALSE;
-
-    if (aClassification == nsIJunkMailPlugin::JUNK)
+    nsCOMPtr <nsIMsgDBHdr> msgHdr;
+    rv = GetMsgDBHdrFromURI(aMsgURI, getter_AddRefs(msgHdr));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsMsgKey msgKey;
+    rv = msgHdr->GetMessageKey(&msgKey);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // check if this message needs junk classification
+    PRUint32 processingFlags;
+    GetProcessingFlags(msgKey, &processingFlags);
+
+    if (processingFlags & nsMsgProcessingFlags::ClassifyJunk)
     {
-      PRBool markAsReadOnSpam;
-      (void)spamSettings->GetMarkAsReadOnSpam(&markAsReadOnSpam);
-      if (markAsReadOnSpam)
+      nsMsgDBFolder::OnMessageClassified(aMsgURI, aClassification, aJunkPercent);
+
+      if (aClassification == nsIJunkMailPlugin::JUNK)
       {
-        rv = mDatabase->MarkRead(msgKey, true, this);
-          if (!NS_SUCCEEDED(rv))
-            NS_WARNING("failed marking spam message as read");
-      }
-
-      PRBool willMoveMessage = PR_FALSE;
-
-      // don't do the move when we are opening up
-      // the junk mail folder or the trash folder
-      // or when manually classifying messages in those folders
-      if (!(mFlags & nsMsgFolderFlags::Junk || mFlags & nsMsgFolderFlags::Trash))
-      {
-        rv = spamSettings->GetMoveOnSpam(&moveOnSpam);
-        NS_ENSURE_SUCCESS(rv,rv);
-        if (moveOnSpam)
+        PRBool willMoveMessage = PR_FALSE;
+
+        // don't do the move when we are opening up
+        // the junk mail folder or the trash folder
+        // or when manually classifying messages in those folders
+        if (!(mFlags & nsMsgFolderFlags::Junk || mFlags & nsMsgFolderFlags::Trash))
         {
-          nsCString uriStr;
-          rv = spamSettings->GetSpamFolderURI(getter_Copies(uriStr));
+          PRBool moveOnSpam = PR_FALSE;
+          rv = spamSettings->GetMoveOnSpam(&moveOnSpam);
           NS_ENSURE_SUCCESS(rv,rv);
-          mSpamFolderURI = uriStr;
-
-          nsCOMPtr<nsIMsgFolder> folder;
-          rv = GetExistingFolder(mSpamFolderURI, getter_AddRefs(folder));
-          if (NS_SUCCEEDED(rv) && folder)
+          if (moveOnSpam)
           {
-            rv = folder->SetFlag(nsMsgFolderFlags::Junk);
-            NS_ENSURE_SUCCESS(rv,rv);
-            mSpamKeysToMove.AppendElement(msgKey);
-            willMoveMessage = PR_TRUE;
-          }
-          else
-          {
-            // XXX TODO
-            // JUNK MAIL RELATED
-            // the listener should do
-            // rv = folder->SetFlag(nsMsgFolderFlags::Junk);
-            // NS_ENSURE_SUCCESS(rv,rv);
-            // mSpamKeysToMove.AppendElement(msgKey);
-            // willMoveMessage = PR_TRUE;
-            rv = GetOrCreateFolder(mSpamFolderURI, nsnull /* aListener */);
-            NS_ASSERTION(NS_SUCCEEDED(rv), "GetOrCreateFolder failed");
+            nsCOMPtr<nsIMsgFolder> folder;
+            rv = GetExistingFolder(spamFolderURI, getter_AddRefs(folder));
+            if (NS_SUCCEEDED(rv) && folder)
+            {
+              rv = folder->SetFlag(nsMsgFolderFlags::Junk);
+              NS_ENSURE_SUCCESS(rv,rv);
+              mSpamKeysToMove.AppendElement(msgKey);
+              willMoveMessage = PR_TRUE;
+            }
+            else
+            {
+              // XXX TODO
+              // JUNK MAIL RELATED
+              // the listener should do
+              // rv = folder->SetFlag(nsMsgFolderFlags::Junk);
+              // NS_ENSURE_SUCCESS(rv,rv);
+              // mSpamKeysToMove.AppendElement(msgKey);
+              // willMoveMessage = PR_TRUE;
+              rv = GetOrCreateFolder(spamFolderURI, nsnull /* aListener */);
+              NS_ASSERTION(NS_SUCCEEDED(rv), "GetOrCreateFolder failed");
+            }
           }
         }
+        rv = spamSettings->LogJunkHit(msgHdr, willMoveMessage);
+        NS_ENSURE_SUCCESS(rv,rv);
       }
-      rv = spamSettings->LogJunkHit(msgHdr, willMoveMessage);
-      NS_ENSURE_SUCCESS(rv,rv);
     }
   }
 
-  if (mNumFilterClassifyRequests == 0)
+  else // end of batch
   {
-    if (!mSpamKeysToMove.IsEmpty())
+    if (!mSpamKeysToMove.IsEmpty() && !spamFolderURI.IsEmpty())
     {
-      if (!mSpamFolderURI.IsEmpty())
-      {
-        nsCOMPtr<nsIMsgFolder> folder;
-        rv = GetExistingFolder(mSpamFolderURI, getter_AddRefs(folder));
-        if (NS_SUCCEEDED(rv) && folder) {
-          nsCOMPtr<nsIMutableArray> messages(do_CreateInstance(NS_ARRAY_CONTRACTID));
-          for (PRUint32 keyIndex = 0; keyIndex < mSpamKeysToMove.Length(); keyIndex++)
-          {
-            nsCOMPtr<nsIMsgDBHdr> mailHdr = nsnull;
-            rv = GetMessageHeader(mSpamKeysToMove.ElementAt(keyIndex), getter_AddRefs(mailHdr));
-            if (NS_SUCCEEDED(rv) && mailHdr)
-              messages->AppendElement(mailHdr, PR_FALSE);
-          }
-
-          nsCOMPtr<nsIMsgCopyService> copySvc = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
-          NS_ENSURE_SUCCESS(rv,rv);
-
-          rv = copySvc->CopyMessages(this, messages, folder, PR_TRUE,
-            /*nsIMsgCopyServiceListener* listener*/ nsnull, nsnull, PR_FALSE /*allowUndo*/);
-          NS_ASSERTION(NS_SUCCEEDED(rv), "CopyMessages failed");
-          if (NS_FAILED(rv))
-          {
-            nsCAutoString logMsg("failed to copy junk messages to junk folder rv = ");
-            logMsg.AppendInt(rv, 16);
-            spamSettings->LogJunkString(logMsg.get());
-          }
+      nsCOMPtr<nsIMsgFolder> folder;
+      rv = GetExistingFolder(spamFolderURI, getter_AddRefs(folder));
+      if (NS_SUCCEEDED(rv) && folder) {
+        nsCOMPtr<nsIMutableArray> messages(do_CreateInstance(NS_ARRAY_CONTRACTID));
+        for (PRUint32 keyIndex = 0; keyIndex < mSpamKeysToMove.Length(); keyIndex++)
+        {
+          nsCOMPtr<nsIMsgDBHdr> mailHdr;
+          rv = GetMessageHeader(mSpamKeysToMove.ElementAt(keyIndex), getter_AddRefs(mailHdr));
+          if (NS_SUCCEEDED(rv) && mailHdr)
+            messages->AppendElement(mailHdr, PR_FALSE);
+        }
+
+        nsCOMPtr<nsIMsgCopyService> copySvc = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
+        NS_ENSURE_SUCCESS(rv,rv);
+
+        rv = copySvc->CopyMessages(this, messages, folder, PR_TRUE,
+          /*nsIMsgCopyServiceListener* listener*/ nsnull, nsnull, PR_FALSE /*allowUndo*/);
+        NS_ASSERTION(NS_SUCCEEDED(rv), "CopyMessages failed");
+        if (NS_FAILED(rv))
+        {
+          nsCAutoString logMsg("failed to copy junk messages to junk folder rv = ");
+          logMsg.AppendInt(rv, 16);
+          spamSettings->LogJunkString(logMsg.get());
         }
       }
     }
     PRInt32 numNewMessages;
     GetNumNewMessages(PR_FALSE, &numNewMessages);
     SetNumNewMessages(numNewMessages - mSpamKeysToMove.Length());
     mSpamKeysToMove.Clear();
     // check if this is the inbox first...
     if (mFlags & nsMsgFolderFlags::Inbox)
       PerformBiffNotifications();
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsMsgLocalMailFolder::OnMessageTraitsClassified(
-    const char *aMsgURI, PRUint32 aTraitCount,
-    PRUint32 *aTraits, PRUint32 *aPercents)
-{
-  nsresult rv;
-  nsCOMPtr <nsIMsgDBHdr> msgHdr;
-  rv = GetMsgDBHdrFromURI(aMsgURI, getter_AddRefs(msgHdr));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsMsgKey msgKey;
-  rv = msgHdr->GetMessageKey(&msgKey);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRUint32 processingFlags;
-  GetProcessingFlags(msgKey, &processingFlags);
-  if (!(processingFlags & nsMsgProcessingFlags::ClassifyTraits))
-    return NS_OK;
-
-  AndProcessingFlags(msgKey, ~nsMsgProcessingFlags::ClassifyTraits);
-
-  nsCOMPtr<nsIMsgTraitService> traitService;
-  traitService = do_GetService("@mozilla.org/msg-trait-service;1", &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  for (PRUint32 i = 0; i < aTraitCount; i++)
-  {
-    if (aTraits[i] == nsIJunkMailPlugin::JUNK_TRAIT)
-      continue; // junk is processed by the junk listener
-    nsCAutoString traitId;
-    rv = traitService->GetId(aTraits[i], traitId);
-    traitId.Insert(NS_LITERAL_CSTRING("bayespercent/"), 0);
-    nsCAutoString strPercent;
-    strPercent.AppendInt(aPercents[i]);
-    mDatabase->SetStringPropertyByHdr(msgHdr, traitId.get(), strPercent.get());
+    // Note we are not calling the parent class's method, since it
+    // currently ignores end-of-batch anyway
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsMsgLocalMailFolder::GetFolderScanState(nsLocalFolderScanState *aState)
 {
   NS_ENSURE_ARG_POINTER(aState);
--- a/mailnews/local/src/nsLocalMailFolder.h
+++ b/mailnews/local/src/nsLocalMailFolder.h
@@ -47,17 +47,16 @@
 #include "nsICopyMessageListener.h"
 #include "nsIFileStreams.h"
 #include "nsIPop3IncomingServer.h"  // need this for an interface ID
 #include "nsMsgTxn.h"
 #include "nsIMsgMessageService.h"
 #include "nsIMsgParseMailMsgState.h"
 #include "nsITransactionManager.h"
 #include "nsIMsgLocalMailFolder.h"
-#include "nsIMsgFilterPlugin.h"
 #include "nsISeekableStream.h"
 #include "nsIMutableArray.h"
 #include "nsLocalUndoTxn.h"
 
 #define COPY_BUFFER_SIZE 16384
 
 class nsParseMailMessageState;
 
@@ -112,27 +111,24 @@ struct nsLocalFolderScanState
   nsCOMPtr<nsILineInputStream> m_fileLineStream;
   nsCString m_header;
   nsCString m_accountKey;
   const char *m_uidl; // memory is owned by m_header
 };
 
 class nsMsgLocalMailFolder : public nsMsgDBFolder,
                              public nsIMsgLocalMailFolder,
-                             public nsICopyMessageListener,
-                             public nsIJunkMailClassificationListener,
-                             public nsIMsgTraitClassificationListener
+                             public nsICopyMessageListener
 {
 public:
   nsMsgLocalMailFolder(void);
   virtual ~nsMsgLocalMailFolder(void);
   NS_DECL_NSICOPYMESSAGELISTENER
   NS_DECL_NSIMSGLOCALMAILFOLDER
   NS_DECL_NSIJUNKMAILCLASSIFICATIONLISTENER
-  NS_DECL_NSIMSGTRAITCLASSIFICATIONLISTENER
   NS_DECL_ISUPPORTS_INHERITED
   // nsIRDFResource methods:
   NS_IMETHOD Init(const char *aURI);
 
   // nsIUrlListener methods
   NS_IMETHOD OnStartRunningUrl(nsIURI * aUrl);
   NS_IMETHOD OnStopRunningUrl(nsIURI * aUrl, nsresult aExitCode);
 
@@ -237,32 +233,28 @@ protected:
                                        nsIMsgWindow *aMsgWindow,
                                        nsIMsgFolder *dstFolder,
                                        PRBool isMove);
   virtual void GetIncomingServerType(nsCString& serverType);
   nsresult InitCopyState(nsISupports* aSupport, nsIArray* messages,
                          PRBool isMove, nsIMsgCopyServiceListener* listener, nsIMsgWindow *msgWindow, PRBool isMoveFolder, PRBool allowUndo);
   void CopyPropertiesToMsgHdr(nsIMsgDBHdr *destHdr, nsIMsgDBHdr *srcHdr);
   virtual nsresult CreateBaseMessageURI(const nsACString& aURI);
-  virtual nsresult SpamFilterClassifyMessage(const char *aURI, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin);
-  virtual nsresult SpamFilterClassifyMessages(const char **aURIArray, PRUint32 aURICount, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin);
   nsresult ChangeKeywordForMessages(nsIArray *aMessages, const nsACString& aKeyword, PRBool add);
   PRBool GetDeleteFromServerOnMove();
 
 protected:
   nsLocalMailCopyState *mCopyState; //We only allow one of these at a time
   nsCString mType;
   PRPackedBool mHaveReadNameFromDB;
   PRPackedBool mInitialized;
   PRPackedBool mCheckForNewMessagesAfterParsing;
   PRPackedBool m_parsingFolder;
   nsCOMPtr<nsIUrlListener> mReparseListener;
-  PRInt32 mNumFilterClassifyRequests;
   nsTArray<nsMsgKey> mSpamKeysToMove;
-  nsCString mSpamFolderURI;
   nsresult setSubfolderFlag(const nsAString& aFolderName, PRUint32 flags);
 
   // state variables for DownloadMessagesForOffline
 
   // Do we notify the owning window of Delete's before or after
   // Adding the new msg?
 #define DOWNLOAD_NOTIFY_FIRST 1
 #define DOWNLOAD_NOTIFY_LAST  2