add pop3 activity to activity manager, r/sr=standard8, ui-review = clarkbw, bug 535437
authorDavid Bienvenu <bienvenu@nventure.com>
Tue, 23 Feb 2010 17:09:05 -0800
changeset 4999 0503e49634b91bb0c3a57d9170ffb28592078972
parent 4998 6bb7d4b34902c21fa9f4a591f4f23f0eb1d4a381
child 5000 7dadc66f4b150284d04b01cc5f815e840da97e4e
push idunknown
push userunknown
push dateunknown
bugs535437
add pop3 activity to activity manager, r/sr=standard8, ui-review = clarkbw, bug 535437
mail/components/activity/modules/activityModules.js
mail/components/activity/modules/pop3Download.js
mail/locales/en-US/chrome/messenger/activity.properties
mailnews/local/public/nsIPop3Service.idl
mailnews/local/public/nsIPop3Sink.idl
mailnews/local/src/nsParseMailbox.cpp
mailnews/local/src/nsPop3Protocol.cpp
mailnews/local/src/nsPop3Service.cpp
mailnews/local/src/nsPop3Service.h
mailnews/local/src/nsPop3Sink.cpp
mailnews/local/src/nsPop3Sink.h
--- a/mail/components/activity/modules/activityModules.js
+++ b/mail/components/activity/modules/activityModules.js
@@ -46,8 +46,10 @@ sendLaterModule.init();
 Components.utils.import("resource://app/modules/activity/moveCopy.js");
 moveCopyModule.init();
 Components.utils.import("resource://app/modules/activity/glodaIndexer.js");
 glodaIndexerActivity.init();
 Components.utils.import("resource://app/modules/activity/autosync.js");
 autosyncModule.init();
 Components.utils.import("resource://app/modules/activity/alertHook.js");
 alertHook.init();
+Components.utils.import("resource://app/modules/activity/pop3Download.js");
+pop3DownloadModule.init();
new file mode 100644
--- /dev/null
+++ b/mail/components/activity/modules/pop3Download.js
@@ -0,0 +1,168 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Thunderbird Activity Manager.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Messaging.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   David Bienvenu <bienvenu@mozillamessaging.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const EXPORTED_SYMBOLS = ['pop3DownloadModule'];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+const nsActProcess = Components.Constructor("@mozilla.org/activity-process;1",
+                                            "nsIActivityProcess", "init");
+const nsActEvent = Components.Constructor("@mozilla.org/activity-event;1",
+                                          "nsIActivityEvent", "init");
+const nsActWarning = Components.Constructor("@mozilla.org/activity-warning;1",
+                                            "nsIActivityWarning", "init");
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/PluralForm.jsm");
+Components.utils.import("resource://app/modules/gloda/log4moz.js");
+
+// This module provides a link between the pop3 service code and the activity
+// manager.
+let pop3DownloadModule =
+{
+  // hash table of most recent download items per folder
+  _mostRecentActivityForFolder: {},
+  // hash table of prev download items per folder, so we can
+  // coalesce consecutive no new message events.
+  _prevActivityForFolder: {},
+
+  get log() {
+    delete this.log;
+    return this.log = Log4Moz.getConfiguredLogger("pop3DownloadsModule");
+  },
+
+  get activityMgr() {
+    delete this.activityMgr;
+    return this.activityMgr = Cc["@mozilla.org/activity-manager;1"]
+                                .getService(Ci.nsIActivityManager);
+  },
+
+  get bundle() {
+    delete this.bundle;
+    let bundleSvc = Cc["@mozilla.org/intl/stringbundle;1"]
+                      .getService(Ci.nsIStringBundleService);
+
+    return this.bundle = bundleSvc
+      .createBundle("chrome://messenger/locale/activity.properties");
+  },
+  
+  getString: function(stringName) {
+    try {
+      return this.bundle.GetStringFromName(stringName)
+    } catch (e) {
+      this.log.error("error trying to get a string called: " + stringName);
+      throw(e);
+    }
+  },
+
+  onDownloadStarted : function(aFolder) {
+    this.log.info("in onDownloadStarted");
+
+    let displayText = this.bundle
+                          .formatStringFromName("pop3EventStartDisplayText",
+                                               [aFolder.prettiestName], 1);
+    // remember the prev activity for this folder, if any.
+    this._prevActivityForFolder[aFolder.URI] =
+      this._mostRecentActivityForFolder[aFolder.URI];
+    let statusText = aFolder.server.prettyName;
+
+    // create an activity event
+    let event = new nsActEvent(displayText,
+                               aFolder,
+                               statusText,
+                               Date.now(),  // start time
+                               Date.now()); // completion time
+
+    event.iconClass = "syncMail";
+
+    let downloadItem = {};
+    downloadItem.eventID = this.activityMgr.addActivity(event);
+    this._mostRecentActivityForFolder[aFolder.URI] = downloadItem;
+  },
+
+  onDownloadProgress : function(aFolder, aNumMsgsDownloaded, aTotalMsgs) {
+    this.log.info("in onDownloadProgress");
+  },
+
+  onDownloadCompleted : function(aFolder, aNumMsgsDownloaded) {
+    this.log.info("in onDownloadCompleted");
+
+    this.activityMgr.removeActivity(this._mostRecentActivityForFolder[aFolder.URI].eventID);
+
+    let displayText;
+    if (aNumMsgsDownloaded > 0)
+    {
+      displayText = PluralForm.get(aNumMsgsDownloaded, this.getString("pop3EventStatusText"));
+      displayText = displayText.replace("#1", aNumMsgsDownloaded);
+    }
+    else
+      displayText = this.getString("pop3EventStatusTextNoMsgs");
+
+    let statusText = aFolder.server.prettyName;
+
+    // create an activity event
+    let event = new nsActEvent(displayText,
+                               aFolder,
+                               statusText,
+                               Date.now(),  // start time
+                               Date.now()); // completion time
+
+    event.iconClass = "syncMail";
+
+    let downloadItem = {numMsgsDownloaded: aNumMsgsDownloaded};
+    this._mostRecentActivityForFolder[aFolder.URI] = downloadItem;
+    downloadItem.eventID = this.activityMgr.addActivity(event);
+    if (!aNumMsgsDownloaded) {
+      // if we didn't download any messages this time, and the prev event
+      // for this folder also didn't download any messages, remove the
+      // prev event from the activity manager.
+      let prevItem = this._prevActivityForFolder[aFolder.URI];
+      if (prevItem != undefined && !prevItem.numMsgsDownloaded)
+        this.activityMgr.removeActivity(prevItem.eventID);
+    }
+  },
+  init: function() {
+    // XXX when do we need to remove ourselves?
+    pop3Service = Cc["@mozilla.org/messenger/popservice;1"]
+                     .getService(Ci.nsIPop3Service);
+    pop3Service.addListener(this);
+  }
+};
+
--- a/mail/locales/en-US/chrome/messenger/activity.properties
+++ b/mail/locales/en-US/chrome/messenger/activity.properties
@@ -39,16 +39,24 @@ autosyncProcessDisplayText=Bringing fold
 # LOCALIZATION NOTE (autosyncEventDisplayText): %S will be replaced by the account name
 autosyncEventDisplayText=%S is up to date
 # LOCALIZATION NOTE (autosyncEventStatusText): %S will be replaced by total number of downloaded messages
 autosyncEventStatusText=Total number of messages downloaded: %S
 autosyncEventStatusTextNoMsgs=No messages downloaded
 # LOCALIZATION NOTE (autosyncContextDisplayText): %S will be replaced by the account name
 autosyncContextDisplayText=Synchronizing: %S
 
+# LOCALIZATION NOTE (pop3ProcessDisplayText): %S will be replaced by the account name
+pop3EventStartDisplayText=checking %S for new messages
+# LOCALIZATION NOTE (pop3EventDisplayText): %S will be replaced by the account name
+pop3EventDisplayText=%S is up to date
+# LOCALIZATION NOTE (pop3EventStatusText): #1 will be replaced by total number of downloaded messages
+pop3EventStatusText=#1 message downloaded;#1 messages downloaded
+pop3EventStatusTextNoMsgs=No messages to download
+
 # Message actions that show up in activity manager
 # LOCALIZATION NOTE (deletedMessages2): #1 number of messages, #2 folder name
 deletedMessages2=Deleted #1 message from #2;Deleted #1 messages from #2
 # LOCALIZATION NOTE (movedMessages): #1 number of messages, #2 and #3: folder names
 movedMessages=Moved #1 message from #2 to #3;Moved #1 messages from #2 to #3
 # LOCALIZATION NOTE (copiedMessages): #1 number of messages, #2 and #3: folder names
 copiedMessages=Copied #1 message from #2 to #3;Copied #1 messages from #2 to #3
 # LOCALIZATION NOTE (fromServerToServer): #1 source server, #2 destination server
--- a/mailnews/local/public/nsIPop3Service.idl
+++ b/mailnews/local/public/nsIPop3Service.idl
@@ -40,21 +40,51 @@
 #include "nsIPop3IncomingServer.idl"
 #include "nsIMsgFolder.idl"
 
 interface nsIURI;
 interface nsIStreamListener;
 interface nsIMsgWindow;
 interface nsIMsgFolder;
 
+[scriptable, uuid(7302fd8e-946f-4ae3-9468-0fb3a7706c51)]
+interface nsIPop3ServiceListener : nsISupports {
+  /**
+   * Notification that a pop3 download has started.
+   *
+   * @param aFolder folder in which the download is started.
+   */
+  void onDownloadStarted(in nsIMsgFolder aFolder);
+
+  /**
+   * Notification about download progress.
+   *
+   * @param aFolder folder in which the download is happening.
+   * @param aNumDownloaded number of the messages that have been downloaded.
+   * @param aTotalToDownload total number of messages to download.
+   */
+  void onDownloadProgress(in nsIMsgFolder aFolder,
+                          in unsigned long aNumDownloaded,
+                          in unsigned long aTotalToDownload);
+
+  /**
+   * Notification that a download has completed.
+   *
+   * @param aFolder folder to which the download has completed.
+   * @param aNumberOfMessages number of the messages that were downloaded.
+   */
+  void onDownloadCompleted(in nsIMsgFolder aFolder,
+                           in unsigned long aNumberOfMessages);
+};
+
 /*
  * The Pop3 Service is an interface designed to make building and running
  * pop3 urls easier.
  */
-[scriptable, uuid(b6594f64-4f9d-4655-9e96-ed927823fb73)]
+[scriptable, uuid(96d3cc14-a842-4cdf-98f8-a4cc695f8b3b)]
 interface nsIPop3Service : nsISupports {
   /*
    * All of these functions build pop3 urls and run them. If you want
    * a handle on the running task, pass in a valid nsIURI ptr. You can later
    * interrupt this action by asking the netlib service manager to interrupt
    * the url you are given back. Remember to release aURL when you are
    * done with it. Pass nsnull in for aURL if you
    * don't care about the returned URL.
@@ -80,9 +110,52 @@ interface nsIPop3Service : nsISupports {
    * @param  aUrlListener - gets called back with success or failure.
    * @param aMsgWindow    - nsIMsgWindow to use for notification callbacks.
    * @return - the url that we run.
    *
    */
   nsIURI verifyLogon(in nsIMsgIncomingServer aServer,
                      in nsIUrlListener aUrlListener,
                      in nsIMsgWindow aMsgWindow);
+
+  /**
+   * Add a listener for pop3 events like message download. This is
+   * used by the activity manager.
+   *
+   * @param aListener listener that gets notified of pop3 events.
+   */
+  void addListener(in nsIPop3ServiceListener aListener);
+
+  /**
+   * Remove a listener for pop3 events like message download.
+   *
+   * @param aListener listener to remove.
+   */
+  void removeListener(in nsIPop3ServiceListener aListener);
+
+  /**
+   * Send the notification that a pop3 download has started.
+   * This is called from the nsIPop3Sink code.
+   *
+   * @param aFolder folder in which the download is started.
+   */
+  void notifyDownloadStarted(in nsIMsgFolder aFolder);
+
+  /**
+   * Send notification about download progress.
+   *
+   * @param aFolder folder in which the download is happening.
+   * @param aNumDownloaded number of the messages that have been downloaded.
+   * @param aTotalToDownload total number of messages to download.
+   */
+  void notifyDownloadProgress(in nsIMsgFolder aFolder,
+                              in unsigned long aNumDownloaded,
+                              in unsigned long aTotalToDownload);
+  /**
+   * Send the notification that a download has completed.
+   * This is called from the nsIPop3Sink code.
+   *
+   * @param aFolder folder to which the download has completed.
+   * @param aNumberOfMessages number of the messages that were downloaded.
+   */
+  void notifyDownloadCompleted(in nsIMsgFolder aFolder,
+                               in unsigned long aNumberOfMessages);
 };
--- a/mailnews/local/public/nsIPop3Sink.idl
+++ b/mailnews/local/public/nsIPop3Sink.idl
@@ -36,17 +36,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 #include "nsIPop3IncomingServer.idl"
 #include "nsIMsgFolder.idl"
 
 interface nsIURI;
 
-[scriptable, uuid(29F76AC4-51FF-4081-A268-B43CACDF4BFF)]
+[scriptable, uuid(ceabfc6b-f139-4c25-890f-efb7c3069d3f)]
 interface nsIPop3Sink : nsISupports {
 
   attribute boolean userAuthenticated;
   attribute string mailAccountURL;
   attribute boolean buildMessageUri;
   attribute string messageUri;
   attribute string baseMessageUri;
 
@@ -64,16 +64,22 @@ interface nsIPop3Sink : nsISupports {
   [noscript] void IncorporateWrite(in string block,
                                    in long length);
 
   [noscript] void IncorporateComplete(in nsIMsgWindow aMsgWindow, in PRInt32 aSize);
   [noscript] void IncorporateAbort(in boolean uidlDownload);
 
   void BiffGetNewMail();
 
+  /**
+   * Tell the pop3sink how many messages we're going to download.
+   *
+   * @param aNumMessages how many messages we're going to download.
+   */
+  void setMsgsToDownload(in unsigned long aNumMessages);
+
   void SetBiffStateAndUpdateFE(in unsigned long biffState, in long numNewMessages, in boolean notify);
 
   [noscript] void SetSenderAuthedFlag(in voidPtr closure, in boolean authed);
 
   attribute nsIPop3IncomingServer popServer;
   attribute nsIMsgFolder folder;
-
 };
--- a/mailnews/local/src/nsParseMailbox.cpp
+++ b/mailnews/local/src/nsParseMailbox.cpp
@@ -550,16 +550,18 @@ nsParseMailMessageState::nsParseMailMess
   // "x-spam-score" property of nsMsgHdr to the value of the header.
   m_customDBHeaderValues = nsnull;
   nsCString customDBHeaders;
   nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
   if (pPrefBranch)
   {
      pPrefBranch->GetCharPref("mailnews.customDBHeaders",  getter_Copies(customDBHeaders));
      ToLowerCase(customDBHeaders);
+     if (customDBHeaders.Find("content-base") == -1)
+      customDBHeaders.Insert(NS_LITERAL_CSTRING("content-base "), 0);
      ParseString(customDBHeaders, ' ', m_customDBHeaders);
      if (m_customDBHeaders.Length())
      {
        m_customDBHeaderValues = new struct message_header [m_customDBHeaders.Length()];
        if (!m_customDBHeaderValues)
          m_customDBHeaders.Clear();
      }
   }
--- a/mailnews/local/src/nsPop3Protocol.cpp
+++ b/mailnews/local/src/nsPop3Protocol.cpp
@@ -2691,16 +2691,19 @@ PRInt32 nsPop3Protocol::GetMsg()
         {
           // Not enough disk space!
 #ifdef DEBUG
           printf("Not enough disk space! Raising error! \n");
 #endif
           return (Error(MK_POP3_OUT_OF_DISK_SPACE));
         }
       }
+      // Here we know how many messages we're going to download, so let
+      // the pop3 sink know.
+      rv = m_nsIPop3Sink->SetMsgsToDownload(m_pop3ConData->really_new_messages);
     }
   }
 
   /* Look at this message, and decide whether to ignore it, get it, just get
   the TOP of it, or delete it. */
 
   // if this is a message we've seen for the first time, we won't find it in
   // m_pop3ConData-uidlinfo->hash.  By default, we retrieve messages, unless they have a status,
--- a/mailnews/local/src/nsPop3Service.cpp
+++ b/mailnews/local/src/nsPop3Service.cpp
@@ -662,8 +662,64 @@ nsPop3Service::GetDefaultServerPort(PRBo
 NS_IMETHODIMP
 nsPop3Service::GetSpecialFoldersDeletionAllowed(PRBool *specialFoldersDeletionAllowed)
 {
     NS_ENSURE_ARG_POINTER(specialFoldersDeletionAllowed);
     *specialFoldersDeletionAllowed = PR_TRUE;
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsPop3Service::NotifyDownloadStarted(nsIMsgFolder *aFolder)
+{
+  nsTObserverArray<nsCOMPtr<nsIPop3ServiceListener>>::ForwardIterator
+    iter(mListeners);
+  nsCOMPtr<nsIPop3ServiceListener> listener;
+  while (iter.HasMore()) {
+    listener = iter.GetNext();
+    listener->OnDownloadStarted(aFolder);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Service::NotifyDownloadProgress(nsIMsgFolder *aFolder,
+                                      PRUint32 aNumMessages,
+                                      PRUint32 aNumTotalMessages)
+{
+  nsTObserverArray<nsCOMPtr<nsIPop3ServiceListener>>::ForwardIterator
+    iter(mListeners);
+  nsCOMPtr<nsIPop3ServiceListener> listener;
+  while (iter.HasMore()) {
+    listener = iter.GetNext();
+    listener->OnDownloadProgress(aFolder, aNumMessages, aNumTotalMessages);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Service::NotifyDownloadCompleted(nsIMsgFolder *aFolder,
+                                       PRUint32 aNumMessages)
+{
+  nsTObserverArray<nsCOMPtr<nsIPop3ServiceListener>>::ForwardIterator
+    iter(mListeners);
+  nsCOMPtr<nsIPop3ServiceListener> listener;
+  while (iter.HasMore()) {
+    listener = iter.GetNext();
+    listener->OnDownloadCompleted(aFolder, aNumMessages);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Service::AddListener(nsIPop3ServiceListener *aListener)
+{
+  NS_ENSURE_ARG_POINTER(aListener);
+  mListeners.AppendElementUnlessExists(aListener);
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Service::RemoveListener(nsIPop3ServiceListener *aListener)
+{
+  NS_ENSURE_ARG_POINTER(aListener);
+  mListeners.RemoveElement(aListener);
+  return NS_OK;
+}
+
--- a/mailnews/local/src/nsPop3Service.h
+++ b/mailnews/local/src/nsPop3Service.h
@@ -41,16 +41,17 @@
 #include "nscore.h"
 
 #include "nsIPop3Service.h"
 #include "nsIPop3URL.h"
 #include "nsIUrlListener.h"
 #include "nsIStreamListener.h"
 #include "nsIProtocolHandler.h"
 #include "nsIMsgProtocolInfo.h"
+#include "nsTObserverArray.h"
 
 class nsIMsgMailNewsUrl;
 
 class nsPop3Service : public nsIPop3Service,
                       public nsIProtocolHandler,
                       public nsIMsgProtocolInfo
 {
 public:
@@ -72,11 +73,12 @@ protected:
                    nsIURI ** aURL);
   // convience function to make constructing of the pop3 url easier...
   nsresult BuildPop3Url(const char * urlSpec, nsIMsgFolder *inbox,
                     nsIPop3IncomingServer *, nsIUrlListener * aUrlListener,
                     nsIURI ** aUrl, nsIMsgWindow *aMsgWindow);
 
   nsresult RunPopUrl(nsIMsgIncomingServer * aServer, nsIURI * aUrlToRun);
   void AlertServerBusy(nsIMsgMailNewsUrl *url);
+  nsTObserverArray<nsCOMPtr<nsIPop3ServiceListener> > mListeners;
 };
 
 #endif /* nsPop3Service_h___ */
--- a/mailnews/local/src/nsPop3Sink.cpp
+++ b/mailnews/local/src/nsPop3Sink.cpp
@@ -16,17 +16,17 @@
  *
  * The Initial Developer of the Original Code is
  * Netscape Communications Corporation.
  * Portions created by the Initial Developer are Copyright (C) 1998
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Pierre Phaneuf <pp@ludusdesign.com>
- *   bienvenu@nventure.com
+ *   David Bienvenu <bienvenu@nventure.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of the GNU General Public License Version 2 or later (the "GPL"),
  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -64,47 +64,49 @@
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIDocShell.h"
 #include "nsIDOMWindowInternal.h"
 #include "nsEmbedCID.h"
 #include "nsMsgUtils.h"
 #include "nsMsgBaseCID.h"
 #include "nsLocalStrings.h"
 #include "nsServiceManagerUtils.h"
+#include "nsIPop3Service.h"
+#include "nsMsgLocalCID.h"
+
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(nsPop3Sink, nsIPop3Sink)
 
 nsPop3Sink::nsPop3Sink()
 {
     m_authed = PR_FALSE;
     m_downloadingToTempFile = PR_FALSE;
     m_accountUrl = nsnull;
     m_biffState = 0;
     m_numNewMessages = 0;
     m_numNewMessagesInFolder = 0;
+    m_numMsgsDownloaded = 0;
     m_senderAuthed = PR_FALSE;
     m_outputBuffer = nsnull;
     m_outputBufferSize = 0;
     m_newMailParser = nsnull;
 #ifdef DEBUG
     m_fileCounter = 0;
 #endif
     m_popServer = nsnull;
     m_outFileStream = nsnull;
-    m_folder = nsnull;
     m_buildMessageUri = PR_FALSE;
 }
 
 nsPop3Sink::~nsPop3Sink()
 {
     PR_Free(m_accountUrl);
     PR_Free(m_outputBuffer);
     NS_IF_RELEASE(m_popServer);
     ReleaseFolderLock();
-    NS_IF_RELEASE(m_folder);
     NS_IF_RELEASE(m_newMailParser);
 }
 
 nsresult
 nsPop3Sink::SetUserAuthenticated(PRBool authed)
 {
   m_authed = authed;
   m_popServer->SetAuthenticated(authed);
@@ -266,135 +268,138 @@ nsPop3Sink::CheckPartialMessages(nsIPop3
       localFolder->NotifyDelete();
   }
 }
 
 nsresult
 nsPop3Sink::BeginMailDelivery(PRBool uidlDownload, nsIMsgWindow *aMsgWindow, PRBool* aBool)
 {
 #ifdef DEBUG
-    m_fileCounter++;
+  m_fileCounter++;
 #endif
 
-    nsresult rv;
+  nsresult rv;
+
+  nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_popServer);
+  if (!server)
+    return NS_ERROR_UNEXPECTED;
 
-    nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_popServer);
-    if (!server)
-      return NS_ERROR_UNEXPECTED;
+  nsCOMPtr <nsIMsgAccountManager> acctMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+  nsCOMPtr <nsIMsgAccount> account;
+  NS_ENSURE_SUCCESS(rv, rv);
+  acctMgr->FindAccountForServer(server, getter_AddRefs(account));
+  if (account)
+    account->GetKey(m_accountKey);
+
+  PRBool isLocked;
+  nsCOMPtr <nsISupports> supports = do_QueryInterface(static_cast<nsIPop3Sink*>(this));
+  m_folder->GetLocked(&isLocked);
+  if(!isLocked)
+    m_folder->AcquireSemaphore(supports);
+  else
+    return NS_MSG_FOLDER_BUSY;
 
-    nsCOMPtr <nsIMsgAccountManager> acctMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
-    nsCOMPtr <nsIMsgAccount> account;
+  nsCOMPtr<nsILocalFile> path;
+
+  m_folder->GetFilePath(getter_AddRefs(path));
+
+  nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+  if (pPrefBranch)
+     pPrefBranch->GetBoolPref("mailnews.downloadToTempFile", &m_downloadingToTempFile);
+
+  if (m_downloadingToTempFile)
+  {
+    // need to create an nsIOFileStream from a temp file...
+    nsCOMPtr<nsIFile> tmpDownloadFile;
+    rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR,
+                                         "newmsg",
+                                         getter_AddRefs(tmpDownloadFile));
+
+    NS_ASSERTION(NS_SUCCEEDED(rv),"writing tmp pop3 download file: failed to append filename");
+    if (NS_FAILED(rv))
+      return rv;
+
+    rv = tmpDownloadFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600);  //need a unique tmp file to prevent dataloss in multiuser environment
     NS_ENSURE_SUCCESS(rv, rv);
-    acctMgr->FindAccountForServer(server, getter_AddRefs(account));
-    if (account)
-      account->GetKey(m_accountKey);
 
-    PRBool isLocked;
-    nsCOMPtr <nsISupports> supports = do_QueryInterface(static_cast<nsIPop3Sink*>(this));
-    m_folder->GetLocked(&isLocked);
-    if(!isLocked)
-      m_folder->AcquireSemaphore(supports);
-    else
-      return NS_MSG_FOLDER_BUSY;
+    m_tmpDownloadFile = do_QueryInterface(tmpDownloadFile, &rv);
+    if (NS_SUCCEEDED(rv))
+    {
+      rv = MsgGetFileStream(m_tmpDownloadFile, getter_AddRefs(m_outFileStream));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+  else
+  {
+    rv = MsgGetFileStream(path, getter_AddRefs(m_outFileStream));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  // The following (!m_outFileStream etc) was added to make sure that we don't write somewhere
+  // where for some reason or another we can't write to and lose the messages
+  // See bug 62480
+  if (!m_outFileStream)
+      return NS_ERROR_OUT_OF_MEMORY;
+
+  nsCOMPtr <nsISeekableStream> seekableOutStream = do_QueryInterface(m_outFileStream);
+  seekableOutStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
 
-    nsCOMPtr<nsILocalFile> path;
+  // create a new mail parser
+  m_newMailParser = new nsParseNewMailState;
+  NS_IF_ADDREF(m_newMailParser);
+  if (m_newMailParser == nsnull)
+    return NS_ERROR_OUT_OF_MEMORY;
 
-    m_folder->GetFilePath(getter_AddRefs(path));
+  m_folder->GetNumNewMessages(PR_FALSE, &m_numNewMessagesInFolder);
+  nsCOMPtr <nsIMsgFolder> serverFolder;
+  rv = GetServerFolder(getter_AddRefs(serverFolder));
+  if (NS_FAILED(rv)) return rv;
 
-    nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
-    if (pPrefBranch)
-       pPrefBranch->GetBoolPref("mailnews.downloadToTempFile", &m_downloadingToTempFile);
+  nsCOMPtr <nsIInputStream> inboxInputStream = do_QueryInterface(m_outFileStream);
+  rv = m_newMailParser->Init(serverFolder, m_folder, (m_downloadingToTempFile) ? m_tmpDownloadFile : path,
+                            inboxInputStream, aMsgWindow, m_downloadingToTempFile);
+  // If we failed to initialize the parser, then just don't use it!!!
+  // We can still continue without one.
 
+  if (NS_FAILED(rv))
+  {
+    NS_IF_RELEASE(m_newMailParser);
+    rv = NS_OK;
+  }
+  else
+  {
+    // Share the inbox fileStream so that moz-status-line flags can be set in the Inbox
+    m_newMailParser->SetDBFolderStream(m_outFileStream);
     if (m_downloadingToTempFile)
     {
-      // need to create an nsIOFileStream from a temp file...
-      nsCOMPtr<nsIFile> tmpDownloadFile;
-      rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR,
-                                           "newmsg",
-                                           getter_AddRefs(tmpDownloadFile));
-
-      NS_ASSERTION(NS_SUCCEEDED(rv),"writing tmp pop3 download file: failed to append filename");
-      if (NS_FAILED(rv))
-        return rv;
-
-      rv = tmpDownloadFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600);  //need a unique tmp file to prevent dataloss in multiuser environment
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      m_tmpDownloadFile = do_QueryInterface(tmpDownloadFile, &rv);
-      if (NS_SUCCEEDED(rv))
-      {
-        rv = MsgGetFileStream(m_tmpDownloadFile, getter_AddRefs(m_outFileStream));
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
-    }
-    else
-    {
-      rv = MsgGetFileStream(path, getter_AddRefs(m_outFileStream));
-      NS_ENSURE_SUCCESS(rv, rv);
+      // Tell the parser to use the offset that will be in the dest folder,
+      // not the temp folder, so that the msg hdr will start off with
+      // the correct mdb oid
+      PRInt64 fileSize;
+      path->GetFileSize(&fileSize);
+      m_newMailParser->SetEnvelopePos((PRUint32) fileSize);
     }
-    // The following (!m_outFileStream etc) was added to make sure that we don't write somewhere
-    // where for some reason or another we can't write to and lose the messages
-    // See bug 62480
-    if (!m_outFileStream)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-    nsCOMPtr <nsISeekableStream> seekableOutStream = do_QueryInterface(m_outFileStream);
-    seekableOutStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
-
-    // create a new mail parser
-    m_newMailParser = new nsParseNewMailState;
-    NS_IF_ADDREF(m_newMailParser);
-    if (m_newMailParser == nsnull)
-      return NS_ERROR_OUT_OF_MEMORY;
-
-    m_folder->GetNumNewMessages(PR_FALSE, &m_numNewMessagesInFolder);
-    nsCOMPtr <nsIMsgFolder> serverFolder;
-    rv = GetServerFolder(getter_AddRefs(serverFolder));
-    if (NS_FAILED(rv)) return rv;
-
-    nsCOMPtr <nsIInputStream> inboxInputStream = do_QueryInterface(m_outFileStream);
-    rv = m_newMailParser->Init(serverFolder, m_folder, (m_downloadingToTempFile) ? m_tmpDownloadFile : path,
-                              inboxInputStream, aMsgWindow, m_downloadingToTempFile);
-  // if we failed to initialize the parser, then just don't use it!!!
-  // we can still continue without one...
-
-    if (NS_FAILED(rv))
-    {
-      NS_IF_RELEASE(m_newMailParser);
-      rv = NS_OK;
-    }
+  }
+  if (m_newMailParser)
+  {
+    if (uidlDownload)
+      m_newMailParser->DisableFilters();
     else
-    {
-      // Share the inbox fileStream so that moz-status-line flags can be set in the Inbox
-      m_newMailParser->SetDBFolderStream(m_outFileStream);
-      if (m_downloadingToTempFile)
-      {
-        // Tell the parser to use the offset that will be in the dest folder,
-        // not the temp folder, so that the msg hdr will start off with
-        // the correct mdb oid
-        PRInt64 fileSize;
-        path->GetFileSize(&fileSize);
-        m_newMailParser->SetEnvelopePos((PRUint32) fileSize);
-      }
-    }
-    if (m_newMailParser)
-    {
-      if (uidlDownload)
-        m_newMailParser->DisableFilters();
-      else
-        FindPartialMessages(path);
-    }
+      FindPartialMessages(path);
+  }
 
 
 #ifdef DEBUG
-    printf("Begin mail message delivery.\n");
+  printf("Begin mail message delivery.\n");
 #endif
-    if (aBool)
-        *aBool = PR_TRUE;
-    return NS_OK;
+  nsCOMPtr<nsIPop3Service> pop3Service(do_GetService(NS_POP3SERVICE_CONTRACTID1, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+  pop3Service->NotifyDownloadStarted(m_folder);
+  if (aBool)
+    *aBool = PR_TRUE;
+  return NS_OK;
 }
 
 nsresult
 nsPop3Sink::EndMailDelivery(nsIPop3Protocol *protocol)
 {
   CheckPartialMessages(protocol);
 
   if (m_newMailParser)
@@ -446,17 +451,17 @@ nsPop3Sink::EndMailDelivery(nsIPop3Proto
   nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_folder);
   if (localFolder)
     (void) localFolder->RefreshSizeOnDisk();
   nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_popServer);
   if (server)
   {
     nsCOMPtr <nsIMsgFilterList> filterList;
     rv = server->GetFilterList(nsnull, getter_AddRefs(filterList));
-    NS_ENSURE_SUCCESS(rv,rv);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     if (filterList)
       (void) filterList->FlushLogIfNecessary();
   }
 
   // fix for bug #161999
   // we should update the summary totals for the folder (inbox)
   // in case it's not the open folder
@@ -496,16 +501,19 @@ nsPop3Sink::EndMailDelivery(nsIPop3Proto
           }
         }
       }
     }
   }
 #ifdef DEBUG
   printf("End mail message delivery.\n");
 #endif
+  nsCOMPtr<nsIPop3Service> pop3Service(do_GetService(NS_POP3SERVICE_CONTRACTID1, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+  pop3Service->NotifyDownloadCompleted(m_folder, m_numNewMessages);
   return NS_OK;
 }
 
 nsresult
 nsPop3Sink::ReleaseFolderLock()
 {
   nsresult result = NS_OK;
   if (!m_folder)
@@ -543,16 +551,19 @@ nsPop3Sink::AbortMailDelivery(nsIPop3Pro
   nsresult rv =
 #endif
     ReleaseFolderLock();
   NS_ASSERTION(NS_SUCCEEDED(rv),"folder lock not released successfully");
 
 #ifdef DEBUG
     printf("Abort mail message delivery.\n");
 #endif
+  nsCOMPtr<nsIPop3Service> pop3Service(do_GetService(NS_POP3SERVICE_CONTRACTID1, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+  pop3Service->NotifyDownloadCompleted(m_folder, 0);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPop3Sink::IncorporateBegin(const char* uidlString,
                              nsIURI* aURL,
                              PRUint32 flags,
                              void** closure)
@@ -599,48 +610,44 @@ nsPop3Sink::IncorporateBegin(const char*
     rv = WriteLineToMailbox("X-Mozilla-Status2: 00000000" MSG_LINEBREAK);
     if (NS_FAILED(rv)) return rv;
     // leave space for 60 bytes worth of keys/tags
     rv = WriteLineToMailbox(X_MOZILLA_KEYWORDS);
     PR_smprintf_free(statusLine);
     return NS_OK;
 }
 
-nsresult
+NS_IMETHODIMP
 nsPop3Sink::SetPopServer(nsIPop3IncomingServer *server)
 {
   NS_IF_RELEASE(m_popServer);
   m_popServer=server;
   NS_ADDREF(m_popServer);
 
   return NS_OK;
 }
 
-nsresult
-nsPop3Sink::GetPopServer(nsIPop3IncomingServer* *server)
+NS_IMETHODIMP
+nsPop3Sink::GetPopServer(nsIPop3IncomingServer **aServer)
 {
-    if (!server) return NS_ERROR_NULL_POINTER;
-    *server = m_popServer;
-    if (*server) NS_ADDREF(*server);
-    return NS_OK;
-}
-
-nsresult nsPop3Sink::GetFolder(nsIMsgFolder * *folder)
-{
-  NS_ENSURE_ARG_POINTER(folder);
-  NS_IF_ADDREF(*folder = m_folder);
+  NS_ENSURE_ARG_POINTER(aServer);
+  NS_IF_ADDREF(*aServer = m_popServer);
   return NS_OK;
 }
 
-nsresult nsPop3Sink::SetFolder(nsIMsgFolder * folder)
+NS_IMETHODIMP nsPop3Sink::GetFolder(nsIMsgFolder **aFolder)
 {
-  NS_IF_RELEASE(m_folder);
-  m_folder=folder;
-  NS_IF_ADDREF(m_folder);
+  NS_ENSURE_ARG_POINTER(aFolder);
+  NS_IF_ADDREF(*aFolder = m_folder);
+  return NS_OK;
+}
 
+NS_IMETHODIMP nsPop3Sink::SetFolder(nsIMsgFolder * aFolder)
+{
+  m_folder = aFolder;
   return NS_OK;
 }
 
 nsresult
 nsPop3Sink::GetServerFolder(nsIMsgFolder **aFolder)
 {
   if (!aFolder)
     return NS_ERROR_NULL_POINTER;
@@ -650,16 +657,22 @@ nsPop3Sink::GetServerFolder(nsIMsgFolder
     nsCOMPtr <nsIMsgIncomingServer> incomingServer = do_QueryInterface(m_popServer);
     if (incomingServer)
       return incomingServer->GetRootFolder(aFolder);
   }
   *aFolder = nsnull;
   return NS_ERROR_NULL_POINTER;
 }
 
+NS_IMETHODIMP nsPop3Sink::SetMsgsToDownload(PRUint32 aNumMessages)
+{
+  m_numNewMessages = aNumMessages;
+  return NS_OK;
+}
+
 char*
 nsPop3Sink::GetDummyEnvelope(void)
 {
   static char result[75];
   char *ct;
   time_t now = time ((time_t *) 0);
 #if defined (XP_WIN)
   if (now < 0 || now > 0x7FFFFFFF)
@@ -935,16 +948,19 @@ nsPop3Sink::IncorporateComplete(nsIMsgWi
     // if DeleteDownloadMsg requested it, select the new message
     else if (doSelect)
       (void) localFolder->SelectDownloadMsg();
   }
 
 #ifdef DEBUG
   printf("Incorporate message complete.\n");
 #endif
+  nsCOMPtr<nsIPop3Service> pop3Service(do_GetService(NS_POP3SERVICE_CONTRACTID1, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+  pop3Service->NotifyDownloadProgress(m_folder, ++m_numMsgsDownloaded, m_numNewMessages);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPop3Sink::IncorporateAbort(PRBool uidlDownload)
 {
   nsresult rv;
   rv = m_outFileStream->Close();   //need to close so that the file can be truncated.
--- a/mailnews/local/src/nsPop3Sink.h
+++ b/mailnews/local/src/nsPop3Sink.h
@@ -71,22 +71,23 @@ protected:
     nsresult HandleTempDownloadFailed(nsIMsgWindow *msgWindow);
 
     PRBool m_authed;
     PRInt64 m_msgOffset;
     char* m_accountUrl;
     PRUint32 m_biffState;
     PRInt32 m_numNewMessages;
     PRInt32 m_numNewMessagesInFolder;
+    PRInt32 m_numMsgsDownloaded;
     PRBool m_senderAuthed;
     char* m_outputBuffer;
     PRInt32 m_outputBufferSize;
     nsIPop3IncomingServer *m_popServer;
     //Currently the folder we want to update about biff info
-    nsIMsgFolder *m_folder;
+    nsCOMPtr<nsIMsgFolder> m_folder;
     nsParseNewMailState  *m_newMailParser;
 #ifdef DEBUG
     PRInt32 m_fileCounter;
 #endif
     nsCOMPtr <nsIOutputStream> m_outFileStream;
     PRBool m_buildMessageUri;
     PRBool m_downloadingToTempFile;
     nsCOMPtr <nsILocalFile> m_tmpDownloadFile;