Back to index

lightning-sunbird  0.9+nobinonly
nsLocalUndoTxn.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "msgCore.h"
00040 #include "nsIMsgHdr.h"
00041 #include "nsLocalUndoTxn.h"
00042 #include "nsImapCore.h"
00043 #include "nsMsgImapCID.h"
00044 #include "nsIImapService.h"
00045 #include "nsIUrlListener.h"
00046 #include "nsIMsgLocalMailFolder.h"
00047 #include "nsIEventQueueService.h"
00048 #include "nsIMsgMailSession.h"
00049 
00050 static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
00051 
00052 nsLocalMoveCopyMsgTxn::nsLocalMoveCopyMsgTxn()  : m_srcIsImap4(PR_FALSE)
00053 {
00054 }
00055 
00056 nsLocalMoveCopyMsgTxn::~nsLocalMoveCopyMsgTxn()
00057 {
00058 }
00059 
00060 NS_IMPL_ADDREF_INHERITED(nsLocalMoveCopyMsgTxn, nsMsgTxn)
00061 NS_IMPL_RELEASE_INHERITED(nsLocalMoveCopyMsgTxn, nsMsgTxn)
00062 
00063 NS_IMETHODIMP
00064 nsLocalMoveCopyMsgTxn::QueryInterface(REFNSIID aIID, void** aInstancePtr)
00065 {
00066     if (!aInstancePtr) return NS_ERROR_NULL_POINTER;
00067 
00068     *aInstancePtr = nsnull;
00069 
00070     if (aIID.Equals(NS_GET_IID(nsLocalMoveCopyMsgTxn))) 
00071     {
00072         *aInstancePtr = NS_STATIC_CAST(nsLocalMoveCopyMsgTxn*, this);
00073     }
00074 
00075     if (*aInstancePtr)
00076     {
00077         NS_ADDREF_THIS();
00078         return NS_OK;
00079     }
00080 
00081     return nsMsgTxn::QueryInterface(aIID, aInstancePtr);
00082 }
00083 
00084 nsresult
00085 nsLocalMoveCopyMsgTxn::Init(nsIMsgFolder* srcFolder, nsIMsgFolder* dstFolder,
00086                                                  PRBool isMove)
00087 {
00088     nsresult rv;
00089     rv = SetSrcFolder(srcFolder);
00090     rv = SetDstFolder(dstFolder);
00091     m_isMove = isMove;
00092 
00093     mUndoFolderListener = nsnull;
00094 
00095     nsXPIDLCString uri;
00096     if (!srcFolder) return rv;
00097     rv = srcFolder->GetURI(getter_Copies(uri));
00098     nsCString protocolType(uri);
00099     protocolType.SetLength(protocolType.FindChar(':'));
00100     if (protocolType.LowerCaseEqualsLiteral("imap"))
00101     {
00102         m_srcIsImap4 = PR_TRUE;
00103     }
00104     return nsMsgTxn::Init();
00105 }
00106 nsresult 
00107 nsLocalMoveCopyMsgTxn::GetSrcIsImap(PRBool *isImap)
00108 {
00109   *isImap = m_srcIsImap4;
00110   return NS_OK;
00111 }
00112 nsresult
00113 nsLocalMoveCopyMsgTxn::SetSrcFolder(nsIMsgFolder* srcFolder)
00114 {
00115        nsresult rv = NS_ERROR_NULL_POINTER;
00116        if (srcFolder)
00117           m_srcFolder = do_GetWeakReference(srcFolder, &rv);
00118        return rv;
00119 }
00120 
00121 nsresult
00122 nsLocalMoveCopyMsgTxn::SetDstFolder(nsIMsgFolder* dstFolder)
00123 {
00124        nsresult rv = NS_ERROR_NULL_POINTER;
00125        if (dstFolder)
00126           m_dstFolder = do_GetWeakReference(dstFolder, &rv);
00127        return rv;
00128 }
00129 
00130 nsresult
00131 nsLocalMoveCopyMsgTxn::AddSrcKey(nsMsgKey aKey)
00132 {
00133        m_srcKeyArray.Add(aKey);
00134        return NS_OK;
00135 }
00136 
00137 nsresult
00138 nsLocalMoveCopyMsgTxn::AddSrcStatusOffset(PRUint32 aStatusOffset)
00139 {
00140        m_srcStatusOffsetArray.Add(aStatusOffset);
00141        return NS_OK;
00142 }
00143 
00144 
00145 nsresult
00146 nsLocalMoveCopyMsgTxn::AddDstKey(nsMsgKey aKey)
00147 {
00148        m_dstKeyArray.Add(aKey);
00149        return NS_OK;
00150 }
00151 
00152 nsresult
00153 nsLocalMoveCopyMsgTxn::AddDstMsgSize(PRUint32 msgSize)
00154 {
00155     m_dstSizeArray.Add(msgSize);
00156     return NS_OK;
00157 }
00158 
00159 nsresult
00160 nsLocalMoveCopyMsgTxn::UndoImapDeleteFlag(nsIMsgFolder* folder, 
00161                                           nsMsgKeyArray& keyArray,
00162                                           PRBool deleteFlag)
00163 {
00164     nsresult rv = NS_ERROR_FAILURE;
00165     if (m_srcIsImap4)
00166     {
00167         nsCOMPtr<nsIImapService> imapService = 
00168                  do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
00169         if (NS_SUCCEEDED(rv))
00170         {
00171             nsCOMPtr<nsIUrlListener> urlListener;
00172             nsCString msgIds;
00173             PRUint32 i, count = keyArray.GetSize();
00174 
00175             urlListener = do_QueryInterface(folder, &rv);
00176 
00177             for (i=0; i < count; i++)
00178             {
00179                 if (!msgIds.IsEmpty())
00180                     msgIds.Append(',');
00181                 msgIds.AppendInt((PRInt32) keyArray.GetAt(i));
00182             }
00183             nsCOMPtr<nsIEventQueue> eventQueue;
00184             nsCOMPtr<nsIEventQueueService> pEventQService = 
00185                      do_GetService(kEventQueueServiceCID, &rv);
00186             if (NS_SUCCEEDED(rv) && pEventQService)
00187             {
00188                 pEventQService->GetThreadEventQueue(NS_CURRENT_THREAD,
00189                                               getter_AddRefs(eventQueue));
00190                 if (eventQueue)
00191                 {
00192                     // This is to make sure that we are in the selected state
00193                     // when executing the imap url; we don't want to load the
00194                     // folder so use lite select to do the trick
00195                     rv = imapService->LiteSelectFolder(eventQueue, folder,
00196                                                        urlListener, nsnull);
00197                     if (!deleteFlag)
00198                         rv =imapService->AddMessageFlags(eventQueue, folder,
00199                                                         urlListener, nsnull,
00200                                                         msgIds.get(),
00201                                                         kImapMsgDeletedFlag,
00202                                                         PR_TRUE);
00203                     else
00204                         rv = imapService->SubtractMessageFlags(eventQueue,
00205                                                               folder,
00206                                                          urlListener, nsnull,
00207                                                          msgIds.get(),
00208                                                          kImapMsgDeletedFlag,
00209                                                          PR_TRUE);
00210                     if (NS_SUCCEEDED(rv) && m_msgWindow)
00211                         folder->UpdateFolder(m_msgWindow);
00212                 }
00213             }
00214         }
00215         rv = NS_OK; // always return NS_OK to indicate that the src is imap
00216     }
00217     else
00218     {
00219         rv = NS_ERROR_FAILURE;
00220     }
00221     return rv;
00222 }
00223 
00224 NS_IMETHODIMP
00225 nsLocalMoveCopyMsgTxn::UndoTransaction()
00226 {
00227     nsresult rv;
00228     nsCOMPtr<nsIMsgDatabase> dstDB;
00229     
00230     nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv);
00231     if (NS_FAILED(rv) || !dstFolder) return rv;
00232     nsCOMPtr<nsIMsgLocalMailFolder> dstlocalMailFolder = do_QueryReferent(m_dstFolder, &rv);
00233     if (NS_FAILED(rv) || !dstlocalMailFolder) return rv;
00234               dstlocalMailFolder->GetDatabaseWOReparse(getter_AddRefs(dstDB));
00235 
00236     if (!dstDB)
00237     {
00238       mUndoFolderListener = new nsLocalUndoFolderListener(this, dstFolder);
00239       if (!mUndoFolderListener)
00240         return NS_ERROR_OUT_OF_MEMORY; 
00241       NS_ADDREF(mUndoFolderListener);
00242       
00243       nsCOMPtr<nsIMsgMailSession> mailSession = 
00244         do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv); 
00245       NS_ENSURE_SUCCESS(rv,rv);
00246       
00247       rv = mailSession->AddFolderListener(mUndoFolderListener, nsIFolderListener::event);
00248       NS_ENSURE_SUCCESS(rv,rv);
00249       
00250       rv = dstFolder->GetMsgDatabase(nsnull, getter_AddRefs(dstDB));
00251       NS_ENSURE_SUCCESS(rv,rv);
00252     }
00253     else
00254     {
00255       rv = UndoTransactionInternal();
00256     }
00257     return rv;
00258 }
00259 
00260 nsresult 
00261 nsLocalMoveCopyMsgTxn::UndoTransactionInternal()
00262 {
00263     nsresult rv = NS_ERROR_FAILURE;
00264 
00265     if (mUndoFolderListener)
00266     {
00267       nsCOMPtr<nsIMsgMailSession> mailSession = 
00268         do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv); 
00269       NS_ENSURE_SUCCESS(rv,rv);
00270       
00271       rv = mailSession->RemoveFolderListener(mUndoFolderListener);
00272       NS_ENSURE_SUCCESS(rv,rv);
00273       
00274       NS_RELEASE(mUndoFolderListener);
00275       mUndoFolderListener = nsnull;
00276     }
00277 
00278     nsCOMPtr<nsIMsgDatabase> srcDB;
00279     nsCOMPtr<nsIMsgDatabase> dstDB;
00280     nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv);
00281     if (NS_FAILED(rv) || !srcFolder) return rv;
00282     
00283     nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv);
00284     if (NS_FAILED(rv) || !dstFolder) return rv;
00285     
00286     rv = srcFolder->GetMsgDatabase(nsnull, getter_AddRefs(srcDB));
00287     if(NS_FAILED(rv)) return rv;
00288 
00289     rv = dstFolder->GetMsgDatabase(nsnull, getter_AddRefs(dstDB));
00290     if (NS_FAILED(rv)) return rv;
00291 
00292     PRUint32 count = m_srcKeyArray.GetSize();
00293     PRUint32 i;
00294     nsCOMPtr<nsIMsgDBHdr> oldHdr;
00295     nsCOMPtr<nsIMsgDBHdr> newHdr;
00296 
00297     // protect against a bogus undo txn without any source keys
00298     // see bug #179856 for details
00299     NS_ASSERTION(count, "no source keys");
00300     if (!count)
00301       return NS_ERROR_UNEXPECTED;
00302 
00303     if (m_isMove)
00304     {
00305         if (m_srcIsImap4)
00306         {
00307             PRBool deleteFlag = PR_TRUE;  //message has been deleted -we are trying to undo it
00308             CheckForToggleDelete(srcFolder, m_srcKeyArray.GetAt(0), &deleteFlag); //there could have been a toggle.
00309             rv = UndoImapDeleteFlag(srcFolder, m_srcKeyArray, deleteFlag);
00310         }
00311         else
00312         {
00313             nsCOMPtr<nsISupportsArray> srcMessages;
00314             NS_NewISupportsArray(getter_AddRefs(srcMessages));
00315             nsCOMPtr <nsISupports> msgSupports;
00316             for (i=0; i<count; i++)
00317             {
00318                 rv = dstDB->GetMsgHdrForKey(m_dstKeyArray.GetAt(i), 
00319                                             getter_AddRefs(oldHdr));
00320                 NS_ASSERTION(oldHdr, "fatal ... cannot get old msg header\n");
00321                 if (NS_SUCCEEDED(rv) && oldHdr)
00322                 {
00323                     rv = srcDB->CopyHdrFromExistingHdr(m_srcKeyArray.GetAt(i),
00324                                                        oldHdr, PR_TRUE,
00325                                                        getter_AddRefs(newHdr));
00326                     NS_ASSERTION(newHdr, 
00327                                  "fatal ... cannot create new msg header\n");
00328                     if (NS_SUCCEEDED(rv) && newHdr)
00329                     {
00330                                                                               newHdr->SetStatusOffset(m_srcStatusOffsetArray.GetAt(i));
00331                        srcDB->UndoDelete(newHdr);
00332                        msgSupports = do_QueryInterface(newHdr);
00333                        srcMessages->AppendElement(msgSupports);
00334                     }
00335                 }
00336             }
00337             nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(srcFolder);
00338             if (localFolder)
00339               localFolder->MarkMsgsOnPop3Server(srcMessages, POP3_NONE /*deleteMsgs*/);
00340         }
00341         srcDB->SetSummaryValid(PR_TRUE);
00342     }
00343 
00344     dstDB->DeleteMessages(&m_dstKeyArray, nsnull);
00345     dstDB->SetSummaryValid(PR_TRUE);
00346 
00347     return rv;
00348 }
00349 
00350 NS_IMETHODIMP
00351 nsLocalMoveCopyMsgTxn::RedoTransaction()
00352 {
00353     nsresult rv = NS_ERROR_FAILURE;
00354     nsCOMPtr<nsIMsgDatabase> srcDB;
00355     nsCOMPtr<nsIMsgDatabase> dstDB;
00356 
00357     nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv);
00358     if (NS_FAILED(rv) || !srcFolder) return rv;
00359 
00360     nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv);
00361     if (NS_FAILED(rv) || !dstFolder) return rv;
00362     
00363     rv = srcFolder->GetMsgDatabase(nsnull, getter_AddRefs(srcDB));
00364     if(NS_FAILED(rv)) return rv;
00365     rv = dstFolder->GetMsgDatabase(nsnull, getter_AddRefs(dstDB));
00366     if (NS_FAILED(rv)) return rv;
00367 
00368     PRUint32 count = m_srcKeyArray.GetSize();
00369     PRUint32 i;
00370     nsCOMPtr<nsIMsgDBHdr> oldHdr;
00371     nsCOMPtr<nsIMsgDBHdr> newHdr;
00372 
00373     nsCOMPtr<nsISupportsArray> srcMessages;
00374     NS_NewISupportsArray(getter_AddRefs(srcMessages));
00375     nsCOMPtr <nsISupports> msgSupports;
00376     
00377     for (i=0; i<count; i++)
00378     {
00379         rv = srcDB->GetMsgHdrForKey(m_srcKeyArray.GetAt(i), 
00380                                     getter_AddRefs(oldHdr));
00381         NS_ASSERTION(oldHdr, "fatal ... cannot get old msg header\n");
00382 
00383         if (NS_SUCCEEDED(rv) && oldHdr)
00384         {
00385             msgSupports =do_QueryInterface(oldHdr);
00386             srcMessages->AppendElement(msgSupports);
00387             
00388             rv = dstDB->CopyHdrFromExistingHdr(m_dstKeyArray.GetAt(i),
00389                                                oldHdr, PR_TRUE,
00390                                                getter_AddRefs(newHdr));
00391             NS_ASSERTION(newHdr, "fatal ... cannot get new msg header\n");
00392             if (NS_SUCCEEDED(rv) && newHdr)
00393             {
00394                 if (m_dstSizeArray.GetSize() > i)
00395                     rv = newHdr->SetMessageSize(m_dstSizeArray.GetAt(i));
00396                 dstDB->UndoDelete(newHdr);
00397             }
00398         }
00399     }
00400     dstDB->SetSummaryValid(PR_TRUE);
00401 
00402     if (m_isMove)
00403     {
00404         if (m_srcIsImap4)
00405         {
00406             // protect against a bogus undo txn without any source keys
00407             // see bug #179856 for details
00408             NS_ASSERTION(m_srcKeyArray.GetSize(), "no source keys");
00409             if (!m_srcKeyArray.GetSize())
00410               return NS_ERROR_UNEXPECTED;
00411           
00412             PRBool deleteFlag = PR_FALSE; //message is un-deleted- we are trying to redo
00413             CheckForToggleDelete(srcFolder, m_srcKeyArray.GetAt(0), &deleteFlag); // there could have been a toggle
00414             rv = UndoImapDeleteFlag(srcFolder, m_srcKeyArray, deleteFlag);
00415         }
00416         else
00417         {
00418             nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(srcFolder);
00419             if (localFolder)
00420               localFolder->MarkMsgsOnPop3Server(srcMessages, POP3_DELETE /*deleteMsgs*/);
00421 
00422             rv = srcDB->DeleteMessages(&m_srcKeyArray, nsnull);
00423             srcDB->SetSummaryValid(PR_TRUE);
00424         }
00425     }
00426 
00427     return rv;
00428 }
00429 
00430 NS_IMPL_ISUPPORTS1(nsLocalUndoFolderListener, nsIFolderListener)
00431 
00432 nsLocalUndoFolderListener::nsLocalUndoFolderListener(nsLocalMoveCopyMsgTxn *aTxn, nsIMsgFolder *aFolder)
00433 {
00434   mTxn = aTxn;
00435   mFolder = aFolder;
00436 }
00437 
00438 nsLocalUndoFolderListener::~nsLocalUndoFolderListener()
00439 {
00440 }
00441 
00442 NS_IMETHODIMP nsLocalUndoFolderListener::OnItemAdded(nsIRDFResource *parentItem, nsISupports *item)
00443 {
00444     return NS_OK;
00445 }
00446 
00447 NS_IMETHODIMP nsLocalUndoFolderListener::OnItemRemoved(nsIRDFResource *parentItem, nsISupports *item)
00448 {
00449     return NS_OK;
00450 }
00451 
00452 NS_IMETHODIMP nsLocalUndoFolderListener::OnItemPropertyChanged(nsIRDFResource *item, nsIAtom *property, const char *oldValue, const char *newValue)
00453 {
00454     return NS_OK;
00455 }
00456 
00457 NS_IMETHODIMP nsLocalUndoFolderListener::OnItemIntPropertyChanged(nsIRDFResource *item, nsIAtom *property, PRInt32 oldValue, PRInt32 newValue)
00458 {
00459     return NS_OK;
00460 }
00461 
00462 NS_IMETHODIMP nsLocalUndoFolderListener::OnItemBoolPropertyChanged(nsIRDFResource *item, nsIAtom *property, PRBool oldValue, PRBool newValue)
00463 {
00464     return NS_OK;
00465 }
00466 
00467 NS_IMETHODIMP nsLocalUndoFolderListener::OnItemUnicharPropertyChanged(nsIRDFResource *item, nsIAtom *property, const PRUnichar *oldValue, const PRUnichar *newValue)
00468 {
00469     return NS_OK;
00470 }
00471 
00472 NS_IMETHODIMP nsLocalUndoFolderListener::OnItemPropertyFlagChanged(nsIMsgDBHdr *item, nsIAtom *property, PRUint32 oldFlag, PRUint32 newFlag)
00473 {
00474     return NS_OK;
00475 }
00476 
00477 NS_IMETHODIMP nsLocalUndoFolderListener::OnItemEvent(nsIMsgFolder *item, nsIAtom *event)
00478 {
00479   nsCOMPtr <nsIAtom> folderLoadedAtom = do_GetAtom("FolderLoaded");
00480   nsCOMPtr <nsIMsgFolder> itemFolder = do_QueryInterface(item);
00481   if (mTxn && mFolder && folderLoadedAtom == event && item == mFolder)
00482     return mTxn->UndoTransactionInternal();
00483 
00484   return NS_ERROR_FAILURE;
00485 }