fix some message move/copying issues with maildir pluggable store, r=neil, sr=standard8, bug 738651
authorDavid Bienvenu <bienvenu@nventure.com>
Thu, 19 Apr 2012 07:39:42 -0700
changeset 11670 26c2c8008156966302f82799f2d74ade4cafe3de
parent 11669 2fe9dc371bfda34127f2e767b688c52a53345e7c
child 11671 7fe3e7bebed6e27b889afd070975bfd75e51e55f
push idunknown
push userunknown
push dateunknown
reviewersneil, standard8, bug
bugs738651
fix some message move/copying issues with maildir pluggable store, r=neil, sr=standard8, bug 738651
mailnews/base/public/nsIMsgPluggableStore.idl
mailnews/local/src/nsLocalMailFolder.cpp
mailnews/local/src/nsMsgBrkMBoxStore.cpp
mailnews/local/src/nsMsgMaildirStore.cpp
--- a/mailnews/base/public/nsIMsgPluggableStore.idl
+++ b/mailnews/base/public/nsIMsgPluggableStore.idl
@@ -45,18 +45,19 @@ interface nsIMsgDBHdr;
 interface nsIMsgWindow;
 interface nsISimpleEnumerator;
 interface nsIOutputStream;
 interface nsIInputStream;
 interface nsIArray;
 interface nsIUrlListener;
 interface nsIMsgIncomingServer;
 interface nsIMsgDatabase;
+interface nsITransaction;
 
-[scriptable, uuid(370f7a56-db1b-4cf9-b541-148572390416)]
+[scriptable, uuid(7c1f7385-ecec-45c5-b194-0a63af6690fd)]
 
 /**
  * Pluggable message store interface. Each incoming server can have a different
  * message store.
  * All methods are synchronous unless otherwise specified.
  */
 interface nsIMsgPluggableStore : nsISupports {
   /**
@@ -236,31 +237,34 @@ interface nsIMsgPluggableStore : nsISupp
   /**
    * This allows the store to handle a msg move/copy if it wants. This lets
    * it optimize move/copies within the same store. E.g., for maildir, a
    * msg move mostly entails moving the file containing the message, and
    * updating the db. If the store does not want to implement this, the core
    * code will use getMsgInputStream on the source message,
    * getNewMsgOutputStream for the dest message, and stream the input to
    * the output. This operation can be asynchronous.
-   * If the store does the copy, it must add the appropriate undo action,
+   * If the store does the copy, it must return the appropriate undo action,
    * which can be store dependent. And it must send the appropriate
    * nsIMsgFolderNotificationService notifications.
    *
    * @param isMove true if this is a move, false if it is a copy.
    * @param aHdrArray array of nsIMsgDBHdr's, all in the same folder
    * @param aDstFolder folder to move/copy the messages to.
    * @param aListener listener to notify of copy status.
+   * @param[out,optional] aUndoAction transaction to provide undo, if
+   * the store does the copy itself.
    * @return true if messages were copied, false if the core code should
    *         do the copy.
    */
   boolean copyMessages(in boolean isMove,
                        in nsIArray aHdrArray,
                        in nsIMsgFolder aDstFolder,
-                       in nsIMsgCopyServiceListener aListener);
+                       in nsIMsgCopyServiceListener aListener,
+                       out nsITransaction aUndoAction);
 
   /**
    * Does this store require compaction? For example, maildir doesn't require
    * compaction at all. Berkeley mailbox does. A sqlite store probably doesn't.
    * This is a static property of the store. It doesn't mean that any particular
    * folder has space that can be reclaimed via compaction. Right now, the core
    * code keeps track of the size of messages deleted, which it can use in
    * conjunction with this store attribute.
--- a/mailnews/local/src/nsLocalMailFolder.cpp
+++ b/mailnews/local/src/nsLocalMailFolder.cpp
@@ -1538,19 +1538,31 @@ nsMsgLocalMailFolder::CopyMessages(nsIMs
                            totalMsgSize))
     return NS_OK;
 
   NS_ENSURE_SUCCESS(rv, rv);
   bool storeDidCopy = false;
   nsCOMPtr<nsIMsgPluggableStore> msgStore;
   rv = GetMsgStore(getter_AddRefs(msgStore));
   NS_ENSURE_SUCCESS(rv, rv);
-  rv = msgStore->CopyMessages(isMove, messages, this, listener, &storeDidCopy);
+  nsCOMPtr<nsITransaction> undoTxn;
+  rv = msgStore->CopyMessages(isMove, messages, this, listener,
+                              getter_AddRefs(undoTxn), &storeDidCopy);
   if (storeDidCopy)
+  {
+    NS_ASSERTION(undoTxn, "if store does copy, it needs to add undo action");
+    if (msgWindow && undoTxn)
+    {
+      nsCOMPtr<nsITransactionManager> txnMgr;
+      msgWindow->GetTransactionManager(getter_AddRefs(txnMgr));
+      if (txnMgr)
+        txnMgr->DoTransaction(undoTxn);
+    }
     return rv;
+  }
   // If the store doesn't do the copy, we'll stream the source messages into
   // the target folder, using getMsgInputStream and getNewMsgOutputStream.
 
   // don't update the counts in the dest folder until it is all over
   EnableNotifications(allMessageCountNotifications, false, false /*dbBatching*/);  //dest folder doesn't need db batching
 
   // sort the message array by key
   PRUint32 numMsgs = 0;
--- a/mailnews/local/src/nsMsgBrkMBoxStore.cpp
+++ b/mailnews/local/src/nsMsgBrkMBoxStore.cpp
@@ -839,21 +839,23 @@ NS_IMETHODIMP nsMsgBrkMBoxStore::DeleteM
 {
   return ChangeFlags(aHdrArray, nsMsgMessageFlags::Expunged, true);
 }
 
 NS_IMETHODIMP
 nsMsgBrkMBoxStore::CopyMessages(bool isMove, nsIArray *aHdrArray,
                                nsIMsgFolder *aDstFolder,
                                nsIMsgCopyServiceListener *aListener,
+                               nsITransaction **aUndoAction,
                                bool *aCopyDone)
 {
   NS_ENSURE_ARG_POINTER(aHdrArray);
   NS_ENSURE_ARG_POINTER(aDstFolder);
   NS_ENSURE_ARG_POINTER(aCopyDone);
+  *aUndoAction = nsnull;
   *aCopyDone = false;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsMsgBrkMBoxStore::GetSupportsCompaction(bool *aSupportsCompaction)
 {
   NS_ENSURE_ARG_POINTER(aSupportsCompaction);
--- a/mailnews/local/src/nsMsgMaildirStore.cpp
+++ b/mailnews/local/src/nsMsgMaildirStore.cpp
@@ -790,17 +790,17 @@ nsMsgMaildirStore::MoveNewlyDownloadedMe
   {
     NS_ERROR("FinishNewMessage - no storeToken in msg hdr!!\n");
     return NS_ERROR_FAILURE;
   }
 
   // path to the downloaded message
   nsCOMPtr<nsIFile> fromPath;
   folderPath->Clone(getter_AddRefs(fromPath));
-  fromPath->Append(NS_LITERAL_STRING("tmp"));
+  fromPath->Append(NS_LITERAL_STRING("cur"));
   fromPath->AppendNative(fileName);
 
   // let's check if the tmp file exists
   bool exists;
   fromPath->Exists(&exists);
   if (!exists)
   {
     NS_ERROR("FinishNewMessage - oops! file does not exist!");
@@ -907,21 +907,23 @@ NS_IMETHODIMP nsMsgMaildirStore::DeleteM
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsMsgMaildirStore::CopyMessages(bool aIsMove, nsIArray *aHdrArray,
                                nsIMsgFolder *aDstFolder,
                                nsIMsgCopyServiceListener *aListener,
+                               nsITransaction **aUndoAction,
                                bool *aCopyDone)
 {
   NS_ENSURE_ARG_POINTER(aHdrArray);
   NS_ENSURE_ARG_POINTER(aDstFolder);
   NS_ENSURE_ARG_POINTER(aCopyDone);
+  NS_ENSURE_ARG_POINTER(aUndoAction);
   PRUint32 messageCount;
   nsresult rv = aHdrArray->GetLength(&messageCount);
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIMsgFolder> srcFolder;
   nsCOMPtr<nsILocalFile> destFolderPath;
   nsCOMPtr<nsIMsgDatabase> destDB;
   nsCOMPtr<nsIMsgDatabase> srcDB;
   aDstFolder->GetMsgDatabase(getter_AddRefs(destDB));
@@ -929,17 +931,18 @@ nsMsgMaildirStore::CopyMessages(bool aIs
   destFolderPath->Append(NS_LITERAL_STRING("cur"));
 
   nsCOMPtr<nsIMsgDBHdr> msgHdr = do_QueryElementAt(aHdrArray, 0, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = msgHdr->GetFolder(getter_AddRefs(srcFolder));
   NS_ENSURE_SUCCESS(rv, rv);
   srcFolder->GetMsgDatabase(getter_AddRefs(srcDB));
   nsRefPtr<nsLocalMoveCopyMsgTxn> msgTxn = new nsLocalMoveCopyMsgTxn;
-  if (msgTxn && NS_SUCCEEDED(msgTxn->Init(srcFolder, aDstFolder, aIsMove)))
+  NS_ENSURE_TRUE(msgTxn, NS_ERROR_OUT_OF_MEMORY);
+  if (NS_SUCCEEDED(msgTxn->Init(srcFolder, aDstFolder, aIsMove)))
   {
     if (aIsMove)
       msgTxn->SetTransactionType(nsIMessenger::eMoveMsg);
     else
       msgTxn->SetTransactionType(nsIMessenger::eCopyMsg);
   }
 
   nsCOMPtr<nsIMutableArray> dstHdrs(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
@@ -985,42 +988,45 @@ nsMsgMaildirStore::CopyMessages(bool aIs
     if (aIsMove)
       path->MoveToNative(destFolderPath, fileName);
     else
       path->CopyToNative(destFolderPath, fileName);
 
     nsCOMPtr<nsIMsgDBHdr> destHdr;
     if (destDB)
     {
-      rv = destDB->CopyHdrFromExistingHdr(srcKey, msgHdr, true, getter_AddRefs(destHdr));
+      rv = destDB->CopyHdrFromExistingHdr(nsMsgKey_None, msgHdr, true, getter_AddRefs(destHdr));
       NS_ENSURE_SUCCESS(rv, rv);
       destHdr->SetStringProperty("storeToken", fileName.get());
       dstHdrs->AppendElement(destHdr, false);
+      nsMsgKey dstKey;
+      destHdr->GetMessageKey(&dstKey);
+      msgTxn->AddDstKey(dstKey);
     }
   }
   nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
   if (notifier)
     notifier->NotifyMsgsMoveCopyCompleted(aIsMove, aHdrArray, aDstFolder,
                                           dstHdrs);
   if (aIsMove)
   {
     for (PRUint32 i = 0; i < messageCount; ++i)
     {
       nsCOMPtr<nsIMsgDBHdr> msgDBHdr(do_QueryElementAt(aHdrArray, i, &rv));
       rv = srcDB->DeleteHeader(msgDBHdr, nsnull, false, true);
     }
   }
   *aCopyDone = true;
-  nsCOMPtr<nsISupports> srcSupports(srcFolder);
+  nsCOMPtr<nsISupports> srcSupports(do_QueryInterface(srcFolder));
   nsCOMPtr<nsIMsgLocalMailFolder> localDest(do_QueryInterface(aDstFolder));
   if (localDest)
     localDest->OnCopyCompleted(srcSupports, true);
   if (aListener)
     aListener->OnStopCopy(NS_OK);
-
+  msgTxn.forget(aUndoAction);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsMsgMaildirStore::GetSupportsCompaction(bool *aSupportsCompaction)
 {
   NS_ENSURE_ARG_POINTER(aSupportsCompaction);
   *aSupportsCompaction = false;