Bug 452879 - "Junk message with from address matching to are whitelisted" [r+sr=Standard8]
authorKent James <kent@caspia.com>
Thu, 02 Apr 2009 09:49:40 +0100
changeset 2325 71bdea990aaeda4f0de7e3f98331726086a7af71
parent 2324 fe72cee167526517548e7484de8c5a8f611063e7
child 2326 8978e2862ac01cc7c8c47c54a383d8108cc39f2b
push idunknown
push userunknown
push dateunknown
bugs452879
Bug 452879 - "Junk message with from address matching to are whitelisted" [r+sr=Standard8]
mailnews/base/src/nsSpamSettings.cpp
mailnews/base/src/nsSpamSettings.h
mailnews/base/test/unit/test_junkWhitelisting.js
mailnews/mailnews.js
--- a/mailnews/base/src/nsSpamSettings.cpp
+++ b/mailnews/base/src/nsSpamSettings.cpp
@@ -61,16 +61,18 @@
 #include "nsMailDirServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsIMsgHeaderParser.h"
 #include "nsAbBaseCID.h"
 #include "nsIAbManager.h"
+#include "nsIMsgAccountManager.h"
+#include "nsMsgBaseCID.h"
 
 nsSpamSettings::nsSpamSettings()
 {
   mLevel = 0;
   mMoveOnSpam = PR_FALSE;
   mMoveTargetMode = nsISpamSettings::MOVE_TARGET_MODE_ACCOUNT;
   mPurge = PR_FALSE;
   mPurgeInterval = 14; // 14 days
@@ -365,16 +367,57 @@ NS_IMETHODIMP nsSpamSettings::Initialize
                                    getter_AddRefs(directory));
       NS_ENSURE_SUCCESS(rv, rv);
 
       if (directory)
         mWhiteListDirArray.AppendObject(directory);
     }
   }
 
+  // the next two preferences affect whether we try to whitelist our own
+  // address or domain. Spammers send emails with spoofed from address matching
+  // either the email address of the recipient, or the recipient's domain,
+  // hoping to get whitelisted.
+  //
+  // The terms to describe this get wrapped up in chains of negatives. A full
+  // definition of the boolean inhibitWhiteListingIdentityUser is "Suppress address
+  // book whitelisting if the sender matches an identity's email address"
+
+  rv = aServer->GetBoolValue("inhibitWhiteListingIdentityUser",
+                             &mInhibitWhiteListingIdentityUser);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aServer->GetBoolValue("inhibitWhiteListingIdentityDomain",
+                             &mInhibitWhiteListingIdentityDomain);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // collect lists of identity users if needed
+  if (mInhibitWhiteListingIdentityDomain || mInhibitWhiteListingIdentityUser)
+  {
+    nsCOMPtr<nsIMsgAccountManager>
+      accountManager(do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv));
+    NS_ENSURE_SUCCESS(rv, rv);
+    nsCOMPtr<nsISupportsArray> identities;
+    rv = accountManager->GetIdentitiesForServer(aServer, getter_AddRefs(identities));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRUint32 count = 0;
+    identities->Count(&count);
+    mEmails.Clear();
+    mEmails.SetCapacity(count);
+    for (PRUint32 index = 0; index < count; ++index)
+    {
+      nsCOMPtr<nsIMsgIdentity> identity(do_QueryElementAt(identities, index));
+      if (!identity)
+        continue;
+      nsCAutoString email;
+      rv = identity->GetEmail(email);
+      mEmails.AppendElement(email);
+    }
+  }
+
   return UpdateJunkFolderState();
 }
 
 nsresult nsSpamSettings::UpdateJunkFolderState()
 {
   nsresult rv;
 
   // if the spam folder uri changed on us, we need to unset the junk flag
@@ -758,26 +801,65 @@ NS_IMETHODIMP nsSpamSettings::CheckWhite
 
   nsCAutoString authorEmailAddress;
   rv = headerParser->ExtractHeaderAddressMailboxes(author, authorEmailAddress);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (authorEmailAddress.IsEmpty())
     return NS_OK;
 
-  if (!mTrustedMailDomains.IsEmpty())
+  // should we skip whitelisting for the identity email?
+  if (mInhibitWhiteListingIdentityUser)
+  {
+    for (PRUint32 i = 0; i < mEmails.Length(); ++i)
+    {
+#ifdef MOZILLA_INTERNAL_API
+      if (mEmails[i].Equals(authorEmailAddress, nsCaseInsensitiveCStringComparator()))
+        return NS_OK;
+#else
+      if (mEmails[i].Equals(authorEmailAddress, CaseInsensitiveCompare))
+        return NS_OK;
+#endif
+    }
+  }
+
+  if (!mTrustedMailDomains.IsEmpty() || mInhibitWhiteListingIdentityDomain)
   {
     nsCAutoString domain;
     PRInt32 atPos = authorEmailAddress.FindChar('@');
     if (atPos >= 0)
       domain = Substring(authorEmailAddress, atPos + 1);
-    if (!domain.IsEmpty() && MsgHostDomainIsTrusted(domain, mTrustedMailDomains))
+    if (!domain.IsEmpty())
     {
-      *aResult = PR_TRUE;
-      return NS_OK;
+      if (!mTrustedMailDomains.IsEmpty() &&
+          MsgHostDomainIsTrusted(domain, mTrustedMailDomains))
+      {
+        *aResult = PR_TRUE;
+        return NS_OK;
+      }
+
+      if (mInhibitWhiteListingIdentityDomain)
+      {
+        for (PRUint32 i = 0; i < mEmails.Length(); ++i)
+        {
+          nsCAutoString identityDomain;
+          PRInt32 atPos = mEmails[i].FindChar('@');
+          if (atPos >= 0)
+          {
+            identityDomain = Substring(mEmails[i], atPos + 1);
+#ifdef MOZILLA_INTERNAL_API
+            if (identityDomain.Equals(domain, nsCaseInsensitiveCStringComparator()))
+              return NS_OK; // don't whitelist
+#else
+            if (identityDomain.Equals(domain, CaseInsensitiveCompare))
+              return NS_OK;
+#endif
+          }
+        }
+      }
     }
   }
 
   if (mWhiteListDirArray.Count())
   {
     nsCOMPtr<nsIAbCard> cardForAddress;
     for (PRInt32 index = 0;
          index < mWhiteListDirArray.Count() && !cardForAddress;
--- a/mailnews/base/src/nsSpamSettings.h
+++ b/mailnews/base/src/nsSpamSettings.h
@@ -45,16 +45,17 @@
 #include "nsISpamSettings.h"
 #include "nsString.h"
 #include "nsIOutputStream.h"
 #include "nsIMsgIncomingServer.h"
 #include "nsIUrlListener.h"
 #include "nsIDateTimeFormat.h"
 #include "nsCOMArray.h"
 #include "nsIAbDirectory.h"
+#include "nsTArray.h"
 
 class nsSpamSettings : public nsISpamSettings, public nsIUrlListener
 {
 public:
   nsSpamSettings();
   virtual ~nsSpamSettings();
 
   NS_DECL_ISUPPORTS
@@ -84,15 +85,21 @@ private:
   PRInt32  mServerFilterTrustFlags;
 
   nsCOMPtr<nsIDateTimeFormat> mDateFormatter;
 
   // array of address directories to use in junk whitelisting
   nsCOMArray<nsIAbDirectory> mWhiteListDirArray;
   // mail domains to use in junk whitelisting
   nsCString mTrustedMailDomains;
+  // should we inhibit whitelisting address of identity?
+  PRBool mInhibitWhiteListingIdentityUser;
+  // should we inhibit whitelisting domain of identity?
+  PRBool mInhibitWhiteListingIdentityDomain;
+  // email addresses associated with this server
+  nsTArray<nsCString> mEmails;
 
   // helper routine used by Initialize which unsets the junk flag on the previous junk folder
   // for this account, and sets it on the new junk folder.
   nsresult UpdateJunkFolderState();
 };
 
 #endif /* nsSpamSettings_h__ */
--- a/mailnews/base/test/unit/test_junkWhitelisting.js
+++ b/mailnews/base/test/unit/test_junkWhitelisting.js
@@ -81,17 +81,17 @@ function run_test()
   testAB.copyTo(gProfileDir, kPABData.fileName);
 
   var copyListener = 
   {
     OnStartCopy: function() {},
     OnProgress: function(aProgress, aProgressMax) {},
     SetMessageKey: function(aKey) { hdrs.push(gLocalInboxFolder.GetMessageHeader(aKey));},
     SetMessageId: function(aMessageId) {},
-    OnStopCopy: function(aStatus)
+    OnStopCopy: function _OnStopCopy(aStatus)
     {
       var fileName = Files.shift();
       if (fileName)
       { 
         var file = do_get_file(fileName);
         copyService.CopyFileMessage(file, gLocalInboxFolder, null, false, 0,
                                     "", copyListener, null);
       }
@@ -165,11 +165,59 @@ function continueTest()
   spamSettings.initialize(server);
   do_check_false(spamSettings.checkWhiteList(hdrs[kDomainExample]));
 
   // add back the Personal Address Book
   server.setCharValue("whiteListAbURI", kPABData.URI);
   spamSettings.initialize(server);
   do_check_true(spamSettings.checkWhiteList(hdrs[kDomainTest]));
 
+  /*
+   * tests of whitelist suppression by identity
+   */
+
+  // setup
+  let accountManager = Cc["@mozilla.org/messenger/account-manager;1"]
+                         .getService(Ci.nsIMsgAccountManager);
+  let account = accountManager.FindAccountForServer(server);
+  let identity = accountManager.createIdentity();
+  // start with an email that does not match
+  identity.email = "iAmNotTheSender@test.invalid";
+  account.addIdentity(identity);
+
+  // suppress whitelisting for sender
+  server.setBoolValue("inhibitWhiteListingIdentityUser", true);
+  spamSettings.initialize(server);
+  // (email does not match yet though)
+  do_check_true(spamSettings.checkWhiteList(hdrs[kDomainTest]));
+
+  // add a matching email (mixing case)
+  identity.email = "PrimaryEMAIL1@test.INVALID";
+  spamSettings.initialize(server);
+  do_check_false(spamSettings.checkWhiteList(hdrs[kDomainTest]));
+
+  // stop suppressing identity users
+  server.setBoolValue("inhibitWhiteListingIdentityUser", false);
+  spamSettings.initialize(server);
+  do_check_true(spamSettings.checkWhiteList(hdrs[kDomainTest]));
+
+  // add a fully non-matching domain to the identity
+  identity.email = "PrimaryEmail1@wrong.domain";
+
+  // suppress whitelist by matching domain
+  server.setBoolValue("inhibitWhiteListingIdentityDomain", true);
+  spamSettings.initialize(server);
+  // but domain still does not match
+  do_check_true(spamSettings.checkWhiteList(hdrs[kDomainTest]));
+
+  // add a matching email to the identity, in the domain (mixing case)
+  identity.email = "iAmNotTheSender@TEST.invalid";
+  spamSettings.initialize(server);
+  do_check_false(spamSettings.checkWhiteList(hdrs[kDomainTest]));
+
+  // stop suppressing whitelist by domain
+  server.setBoolValue("inhibitWhiteListingIdentityDomain", false);
+  spamSettings.initialize(server);
+  do_check_true(spamSettings.checkWhiteList(hdrs[kDomainTest]));
+
   do_test_finished();
 }
 
--- a/mailnews/mailnews.js
+++ b/mailnews/mailnews.js
@@ -515,16 +515,20 @@ pref("mail.server.default.spamActionTarg
 pref("mail.server.default.spamActionTargetFolder", "");
 pref("mail.server.default.useWhiteList", true);
 pref("mail.server.default.whiteListAbURI", "moz-abmdbdirectory://abook.mab"); // the Personal addressbook.
 pref("mail.server.default.useServerFilter", false);
 pref("mail.server.default.serverFilterName", "SpamAssassin");
 pref("mail.server.default.serverFilterTrustFlags", 1); // 1 == trust positives, 2 == trust negatives, 3 == trust both
 pref("mail.server.default.purgeSpam", false);
 pref("mail.server.default.purgeSpamInterval", 14); // 14 days
+// should we inhibit whitelisting of the email addresses for a server's identities?
+pref("mail.server.default.inhibitWhiteListingIdentityUser", false);
+// should we inhibit whitelisting of the domain for a server's identities?
+pref("mail.server.default.inhibitWhiteListingIdentityDomain", false);
 
 // to activate auto-sync feature (preemptive message download for imap) by default
 pref("mail.server.default.autosync_offline_stores",true);
 pref("mail.server.default.offline_download",true);
 
 // the probablilty threshold over which messages are classified as junk
 // this number is divided by 100 before it is used. The classifier can be fine tuned
 // by changing this pref. Typical values are .99, .95, .90, .5, etc.