Back to index

lightning-sunbird  0.9+nobinonly
nsMsgSendLater.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 #include "nsMsgSendLater.h"
00039 #include "nsCOMPtr.h"
00040 #include "nsMsgCopy.h"
00041 #include "nsIPrefService.h"
00042 #include "nsIPrefBranch.h"
00043 #include "nsIEnumerator.h"
00044 #include "nsIFileSpec.h"
00045 #include "nsISmtpService.h"
00046 #include "nsIMsgMailNewsUrl.h"
00047 #include "nsMsgDeliveryListener.h"
00048 #include "nsIMsgIncomingServer.h"
00049 #include "nsICopyMessageListener.h"
00050 #include "nsIMsgMessageService.h"
00051 #include "nsIMsgMailSession.h"
00052 #include "nsIMsgAccountManager.h"
00053 #include "nsMsgBaseCID.h"
00054 #include "nsMsgCompCID.h"
00055 #include "nsMsgCompUtils.h"
00056 #include "nsMsgUtils.h"
00057 #include "nsMsgFolderFlags.h"
00058 #include "nsISupportsArray.h"
00059 #include "nsMailHeaders.h"
00060 #include "nsMsgPrompts.h"
00061 #include "nsIMsgSendListener.h"
00062 #include "nsIMsgSendLaterListener.h"
00063 #include "nsMsgCopy.h"
00064 #include "nsMsgComposeStringBundle.h"
00065 #include "nsXPIDLString.h"
00066 #include "nsIPrompt.h"
00067 #include "nsIURI.h"
00068 #include "nsISmtpUrl.h"
00069 #include "nsIChannel.h"
00070 #include "prlog.h"
00071 #include "nsMsgSimulateError.h"
00072 #include "nsIMimeConverter.h"
00073 #include "nsMsgMimeCID.h"
00074 
00075 static NS_DEFINE_CID(kISupportsArrayCID, NS_SUPPORTSARRAY_CID);
00076 
00077 NS_IMPL_ISUPPORTS2(nsMsgSendLater, nsIMsgSendLater, nsIStreamListener)
00078 
00079 nsMsgSendLater::nsMsgSendLater()
00080 {
00081   mTempIFileSpec = nsnull;
00082   mTempFileSpec = nsnull;
00083   mOutFile = nsnull;
00084   mTotalSentSuccessfully = 0;
00085   mTotalSendCount = 0;
00086   mMessageFolder = nsnull;
00087   mMessage = nsnull;
00088   mLeftoverBuffer = nsnull;
00089 
00090   mListenerArray = nsnull;
00091   mListenerArrayCount = 0;
00092 
00093   m_to = nsnull;
00094   m_bcc = nsnull;
00095   m_fcc = nsnull;
00096   m_newsgroups = nsnull;
00097   m_newshost = nsnull;
00098   m_headers = nsnull;
00099   m_flags = 0;
00100   m_headersFP = 0;
00101   m_inhead = PR_TRUE;
00102   m_headersPosition = 0;
00103 
00104   m_bytesRead = 0;
00105   m_position = 0;
00106   m_flagsPosition = 0;
00107   m_headersSize = 0;
00108 
00109   mIdentityKey = nsnull;
00110   mAccountKey = nsnull;
00111 
00112   mRequestReturnReceipt = PR_FALSE;
00113 
00114   NS_NewISupportsArray(getter_AddRefs(mMessagesToSend));
00115 }
00116 
00117 nsMsgSendLater::~nsMsgSendLater()
00118 {
00119   NS_IF_RELEASE(mTempIFileSpec);
00120   PR_Free(m_to);
00121   PR_Free(m_fcc);
00122   PR_Free(m_bcc);
00123   PR_Free(m_newsgroups);
00124   PR_Free(m_newshost);
00125   PR_Free(m_headers);
00126   PR_Free(mLeftoverBuffer);
00127   PR_Free(mIdentityKey);
00128   PR_Free(mAccountKey);
00129 }
00130 
00131 // Stream is done...drive on!
00132 NS_IMETHODIMP
00133 nsMsgSendLater::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
00134 {
00135   nsresult    rv;
00136 
00137   // First, this shouldn't happen, but if
00138   // it does, flush the buffer and move on.
00139   if (mLeftoverBuffer)
00140   {
00141     DeliverQueuedLine(mLeftoverBuffer, PL_strlen(mLeftoverBuffer));
00142   }
00143 
00144   if (mOutFile)
00145     mOutFile->close();
00146 
00147   // See if we succeeded on reading the message from the message store?
00148   //
00149   SET_SIMULATED_ERROR(SIMULATED_SEND_ERROR_13, status, NS_ERROR_FAILURE);
00150   if (NS_SUCCEEDED(status))
00151   {
00152     // Message is done...send it!
00153     rv = CompleteMailFileSend();
00154 
00155 #ifdef NS_DEBUG
00156     printf("nsMsgSendLater: Success on getting message...\n");
00157 #endif
00158     
00159     // If the send operation failed..try the next one...
00160     if (NS_FAILED(rv))
00161     {
00162       rv = StartNextMailFileSend();
00163       if (NS_FAILED(rv))
00164         NotifyListenersOnStopSending(rv, nsnull, mTotalSendCount, mTotalSentSuccessfully);
00165     }
00166   }
00167   else
00168   {
00169     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
00170     if(!channel) return NS_ERROR_FAILURE;
00171 
00172     // extract the prompt object to use for the alert from the url....
00173     nsCOMPtr<nsIURI> uri; 
00174     nsCOMPtr<nsIPrompt> promptObject;
00175     if (channel)
00176     {
00177       channel->GetURI(getter_AddRefs(uri));
00178       nsCOMPtr<nsISmtpUrl> smtpUrl (do_QueryInterface(uri));
00179       if (smtpUrl)
00180         smtpUrl->GetPrompt(getter_AddRefs(promptObject));
00181     } 
00182     nsMsgDisplayMessageByID(promptObject, NS_ERROR_QUEUED_DELIVERY_FAILED);
00183     
00184     // Getting the data failed, but we will still keep trying to send the rest...
00185     rv = StartNextMailFileSend();
00186     if (NS_FAILED(rv))
00187       NotifyListenersOnStopSending(rv, nsnull, mTotalSendCount, mTotalSentSuccessfully);
00188   }
00189 
00190   return rv;
00191 }
00192 
00193 char *
00194 FindEOL(char *inBuf, char *buf_end)
00195 {
00196   char *buf = inBuf;
00197   char *findLoc = nsnull;
00198 
00199   while (buf <= buf_end)
00200     if (*buf == 0) 
00201       return buf;
00202     else if ( (*buf == nsCRT::LF) || (*buf == nsCRT::CR) )
00203     {
00204       findLoc = buf;
00205       break;
00206     }
00207     else
00208       ++buf;
00209 
00210   if (!findLoc)
00211     return nsnull;
00212   else if ((findLoc + 1) > buf_end)
00213     return buf;
00214 
00215   if ( (*findLoc == nsCRT::LF && *(findLoc+1) == nsCRT::CR) || 
00216        (*findLoc == nsCRT::CR && *(findLoc+1) == nsCRT::LF))
00217     findLoc++; // possibly a pair.       
00218   return findLoc;
00219 }
00220 
00221 nsresult
00222 nsMsgSendLater::RebufferLeftovers(char *startBuf, PRUint32 aLen)
00223 {
00224   PR_FREEIF(mLeftoverBuffer);
00225   mLeftoverBuffer = (char *)PR_Malloc(aLen + 1);
00226   if (!mLeftoverBuffer)
00227     return NS_ERROR_OUT_OF_MEMORY;
00228 
00229   memcpy(mLeftoverBuffer, startBuf, aLen);
00230   mLeftoverBuffer[aLen] = '\0';
00231   return NS_OK;
00232 }
00233 
00234 nsresult
00235 nsMsgSendLater::BuildNewBuffer(const char* aBuf, PRUint32 aCount, PRUint32 *totalBufSize)
00236 {
00237   // Only build a buffer when there are leftovers...
00238   if (!mLeftoverBuffer)
00239     return NS_ERROR_FAILURE;
00240 
00241   PRInt32 leftoverSize = PL_strlen(mLeftoverBuffer);
00242   mLeftoverBuffer = (char *)PR_Realloc(mLeftoverBuffer, aCount + leftoverSize);
00243   if (!mLeftoverBuffer)
00244     return NS_ERROR_FAILURE;
00245 
00246   memcpy(mLeftoverBuffer + leftoverSize, aBuf, aCount);
00247   *totalBufSize = aCount + leftoverSize;
00248   return NS_OK;
00249 }
00250 
00251 // Got data?
00252 NS_IMETHODIMP
00253 nsMsgSendLater::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
00254 {
00255   // This is a little bit tricky since we have to chop random 
00256   // buffers into lines and deliver the lines...plus keeping the
00257   // leftovers for next time...some fun, eh?
00258   //
00259   nsresult    rv = NS_OK;
00260   char        *startBuf; 
00261   char        *endBuf;
00262   char        *lineEnd;
00263   char        *newbuf = nsnull;
00264   PRUint32    size;  
00265 
00266   PRUint32    aCount = count;
00267   char        *aBuf = (char *)PR_Malloc(aCount + 1);
00268 
00269   inStr->Read(aBuf, count, &aCount);
00270 
00271   // First, create a new work buffer that will 
00272   if (NS_FAILED(BuildNewBuffer(aBuf, aCount, &size))) // no leftovers...
00273   {
00274     startBuf = (char *)aBuf;
00275     endBuf = (char *)(aBuf + aCount - 1);
00276   }
00277   else  // yum, leftovers...new buffer created...sitting in mLeftoverBuffer
00278   {
00279     newbuf = mLeftoverBuffer;
00280     startBuf = newbuf; 
00281     endBuf = startBuf + size - 1;
00282     mLeftoverBuffer = nsnull; // null out this 
00283   }
00284 
00285   while (startBuf <= endBuf)
00286   {
00287     lineEnd = FindEOL(startBuf, endBuf);
00288     if (!lineEnd)
00289     {
00290       rv = RebufferLeftovers(startBuf, (endBuf - startBuf) + 1);           
00291       break;
00292     }
00293 
00294     rv = DeliverQueuedLine(startBuf, (lineEnd - startBuf) + 1);
00295     if (NS_FAILED(rv))
00296       break;
00297 
00298     startBuf = lineEnd+1;
00299   }
00300 
00301   PR_Free(newbuf);
00302   PR_Free(aBuf);
00303   return rv;
00304 }
00305 
00306 NS_IMETHODIMP
00307 nsMsgSendLater::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
00308 {
00309   return NS_OK;
00310 }
00311 
00313 // This is the listener class for the send operation. We have to create this class 
00314 // to listen for message send completion and eventually notify the caller
00316 NS_IMPL_ISUPPORTS2(SendOperationListener, nsIMsgSendListener,
00317                    nsIMsgCopyServiceListener)
00318 
00319 SendOperationListener::SendOperationListener(void) 
00320 { 
00321   mSendLater = nsnull;
00322 }
00323 
00324 SendOperationListener::~SendOperationListener(void) 
00325 {
00326 }
00327 
00328 nsresult
00329 SendOperationListener::SetSendLaterObject(nsMsgSendLater *obj)
00330 {
00331   mSendLater = obj;
00332   return NS_OK;
00333 }
00334 
00335 nsresult
00336 SendOperationListener::OnGetDraftFolderURI(const char *aFolderURI)
00337 {
00338   return NS_OK;
00339 }
00340   
00341 nsresult
00342 SendOperationListener::OnStartSending(const char *aMsgID, PRUint32 aMsgSize)
00343 {
00344 #ifdef NS_DEBUG
00345   printf("SendOperationListener::OnStartSending()\n");
00346 #endif
00347   return NS_OK;
00348 }
00349   
00350 nsresult
00351 SendOperationListener::OnProgress(const char *aMsgID, PRUint32 aProgress, PRUint32 aProgressMax)
00352 {
00353 #ifdef NS_DEBUG
00354   printf("SendOperationListener::OnProgress()\n");
00355 #endif
00356   return NS_OK;
00357 }
00358 
00359 nsresult
00360 SendOperationListener::OnStatus(const char *aMsgID, const PRUnichar *aMsg)
00361 {
00362 #ifdef NS_DEBUG
00363   printf("SendOperationListener::OnStatus()\n");
00364 #endif
00365 
00366   return NS_OK;
00367 }
00368 
00369 nsresult
00370 SendOperationListener::OnSendNotPerformed(const char *aMsgID, nsresult aStatus)
00371 {
00372   return NS_OK;
00373 }
00374   
00375 nsresult
00376 SendOperationListener::OnStopSending(const char *aMsgID, nsresult aStatus, const PRUnichar *aMsg, 
00377                                      nsIFileSpec *returnFileSpec)
00378 {
00379   nsresult rv = NS_OK;
00380 
00381   if (mSendLater)
00382   {
00383     if (NS_SUCCEEDED(aStatus))
00384     {
00385 #ifdef NS_DEBUG
00386       printf("nsMsgSendLater: Success on the message send operation!\n");
00387 #endif
00388 
00389       PRBool    deleteMsgs = PR_TRUE;
00390 
00391       //
00392       // Now delete the message from the outbox folder.
00393       //
00394       nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
00395       if (pPrefBranch)
00396         pPrefBranch->GetBoolPref("mail.really_delete_draft", &deleteMsgs);
00397 
00398       mSendLater->SetOrigMsgDisposition();
00399       if (deleteMsgs)
00400       {
00401         mSendLater->DeleteCurrentMessage();
00402       }
00403 
00404       ++(mSendLater->mTotalSentSuccessfully);
00405     }
00406     else if (mSendLater) 
00407     {
00408       mSendLater->NotifyListenersOnStopSending(aStatus, nsnull,
00409                                                mSendLater->mTotalSendCount, 
00410                                                mSendLater->mTotalSentSuccessfully);
00411       NS_RELEASE(mSendLater);
00412     }
00413   }
00414 
00415   return rv;
00416 }
00417 
00418 // nsIMsgCopyServiceListener
00419 
00420 nsresult
00421 SendOperationListener::OnStartCopy(void)
00422 {
00423   return NS_OK;
00424 }
00425 
00426 nsresult
00427 SendOperationListener::OnProgress(PRUint32 aProgress, PRUint32 aProgressMax)
00428 {
00429   return NS_OK;
00430 }
00431 
00432 nsresult
00433 SendOperationListener::SetMessageKey(PRUint32 aKey)
00434 {
00435   NS_NOTREACHED("SendOperationListener::SetMessageKey()");
00436   return NS_ERROR_NOT_IMPLEMENTED;
00437 }
00438 
00439 nsresult
00440 SendOperationListener::GetMessageId(nsCString * aMessageId)
00441 {
00442   NS_NOTREACHED("SendOperationListener::GetMessageId()\n");
00443   return NS_ERROR_NOT_IMPLEMENTED;
00444 }
00445 
00446 nsresult
00447 SendOperationListener::OnStopCopy(nsresult aStatus)
00448 {
00449   if (mSendLater) 
00450   {
00451     // Regardless of the success of the copy we will still keep trying
00452     // to send the rest...
00453     nsresult rv;
00454     rv = mSendLater->StartNextMailFileSend();
00455     if (NS_FAILED(rv))
00456       mSendLater->NotifyListenersOnStopSending(rv, nsnull,
00457                                                mSendLater->mTotalSendCount, 
00458                                                mSendLater->mTotalSentSuccessfully);
00459     NS_RELEASE(mSendLater);
00460   }
00461 
00462   return NS_OK;
00463 }
00464 
00465 nsresult
00466 nsMsgSendLater::CompleteMailFileSend()
00467 {
00468   // get the identity from the key
00469   // if no key, or we fail to find the identity
00470   // use the default identity on the default account
00471   nsCOMPtr<nsIMsgIdentity> identity;
00472   nsresult rv = GetIdentityFromKey(mIdentityKey, getter_AddRefs(identity));
00473   NS_ENSURE_SUCCESS(rv,rv);
00474 
00475   // If for some reason the tmp file didn't get created, we've failed here
00476   PRBool created;
00477   mTempIFileSpec->Exists(&created);
00478   if (!created)
00479     return NS_ERROR_FAILURE;
00480 
00481   // Get the recipients...
00482   nsXPIDLCString recips;
00483   nsXPIDLCString ccList;
00484   if (NS_FAILED(mMessage->GetRecipients(getter_Copies(recips))))
00485     return NS_ERROR_UNEXPECTED;
00486   else
00487     mMessage->GetCcList(getter_Copies(ccList));
00488 
00489   nsCOMPtr<nsIMsgCompFields> compFields = do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv);
00490   NS_ENSURE_SUCCESS(rv,rv);
00491 
00492   nsCOMPtr<nsIMsgSend> pMsgSend = do_CreateInstance(NS_MSGSEND_CONTRACTID, &rv);
00493   NS_ENSURE_SUCCESS(rv,rv);
00494 
00495   nsCOMPtr<nsIMimeConverter> mimeConverter = do_GetService(NS_MIME_CONVERTER_CONTRACTID, &rv);
00496   NS_ENSURE_SUCCESS(rv, rv);
00497 
00498   // Since we have already parsed all of the headers, we are simply going to
00499   // set the composition fields and move on.
00500   //
00501   nsXPIDLCString author;
00502   mMessage->GetAuthor(getter_Copies(author));
00503 
00504   nsMsgCompFields * fields = (nsMsgCompFields *)compFields.get();
00505 
00506   nsXPIDLCString decodedString;
00507   // decoded string is null if the input is not MIME encoded
00508   mimeConverter->DecodeMimeHeader(author.get(), getter_Copies(decodedString));
00509 
00510   fields->SetFrom(decodedString.IsEmpty() ? author.get() : decodedString.get());
00511 
00512   if (m_to)
00513   {
00514     mimeConverter->DecodeMimeHeader(m_to, getter_Copies(decodedString));
00515     fields->SetTo(decodedString.IsEmpty() ? m_to : decodedString.get());
00516   }
00517 
00518   if (m_bcc)
00519   {
00520     mimeConverter->DecodeMimeHeader(m_bcc, getter_Copies(decodedString));
00521     fields->SetBcc(decodedString.IsEmpty() ? m_bcc : decodedString.get());
00522   }
00523 
00524   if (m_fcc)
00525   {
00526     mimeConverter->DecodeMimeHeader(m_fcc, getter_Copies(decodedString));
00527     fields->SetFcc(decodedString.IsEmpty() ? m_fcc : decodedString.get());
00528   }
00529 
00530   if (m_newsgroups)
00531     fields->SetNewsgroups(m_newsgroups);
00532 
00533 #if 0
00534   // needs cleanup.  SetNewspostUrl()?
00535   if (m_newshost)
00536     fields->SetNewshost(m_newshost);
00537 #endif
00538 
00539   if (mRequestReturnReceipt)
00540     fields->SetReturnReceipt(PR_TRUE);
00541 
00542   // Create the listener for the send operation...
00543   SendOperationListener * sendListener = new SendOperationListener();
00544   if (!sendListener)
00545     return NS_ERROR_OUT_OF_MEMORY;
00546   
00547   NS_ADDREF(sendListener);
00548   // set this object for use on completion...
00549   sendListener->SetSendLaterObject(this);
00550 
00551   nsCOMPtr <nsIMsgStatusFeedback> statusFeedback;
00552   if (m_window)
00553     m_window->GetStatusFeedback(getter_AddRefs(statusFeedback));
00554   NS_ADDREF(this);  //TODO: We should remove this!!!
00555   rv = pMsgSend->SendMessageFile(identity,
00556                                  mAccountKey,
00557                                  compFields, // nsIMsgCompFields *fields,
00558                                  mTempIFileSpec, // nsIFileSpec *sendFileSpec,
00559                                  PR_TRUE, // PRBool deleteSendFileOnCompletion,
00560                                  PR_FALSE, // PRBool digest_p,
00561                                  nsIMsgSend::nsMsgSendUnsent, // nsMsgDeliverMode mode,
00562                                  nsnull, // nsIMsgDBHdr *msgToReplace, 
00563                                  sendListener,
00564                                  statusFeedback,
00565                                  nsnull); 
00566   NS_IF_RELEASE(sendListener);
00567   return rv;
00568 }
00569 
00570 nsresult
00571 nsMsgSendLater::StartNextMailFileSend()
00572 {
00573   nsFileSpec    fileSpec;
00574   nsresult      rv = NS_OK;
00575   nsXPIDLCString  messageURI;
00576 
00577   if ( (!mEnumerator) || (mEnumerator->IsDone() == NS_OK) )
00578   {
00579     // Call any listeners on this operation and then exit cleanly
00580 #ifdef NS_DEBUG
00581     printf("nsMsgSendLater: Finished \"Send Later\" operation.\n");
00582 #endif
00583 
00584     mMessagesToSend->Clear(); // clear out our array
00585     NotifyListenersOnStopSending(NS_OK, nsnull, mTotalSendCount, mTotalSentSuccessfully);
00586     return NS_OK;
00587   }
00588 
00589   nsCOMPtr<nsISupports>   currentItem;
00590   mEnumerator->CurrentItem(getter_AddRefs(currentItem));
00591   // advance to the next item for the next pass.
00592   mEnumerator->Next();
00593 
00594   mMessage = do_QueryInterface(currentItem); 
00595   if(!mMessage)
00596     return NS_ERROR_NOT_AVAILABLE;
00597 
00598   nsCOMPtr<nsIMsgDBHdr>  myRDFNode ;
00599   myRDFNode = do_QueryInterface(mMessage, &rv);
00600   if(NS_FAILED(rv) || (!myRDFNode))
00601     return NS_ERROR_NOT_AVAILABLE;
00602 
00603   mMessageFolder->GetUriForMsg(mMessage, getter_Copies(messageURI));
00604 
00605 #ifdef NS_DEBUG
00606   nsXPIDLCString      subject;
00607   mMessage->GetSubject(getter_Copies(subject));
00608   printf("Sending message: [%s]\n", (const char*)subject);
00609 #endif
00610 
00611   mTempFileSpec = nsMsgCreateTempFileSpec("nsqmail.tmp"); 
00612   if (!mTempFileSpec)
00613     return NS_ERROR_FAILURE;
00614 
00615   NS_NewFileSpecWithSpec(*mTempFileSpec, &mTempIFileSpec);
00616   if (!mTempIFileSpec)
00617     return NS_ERROR_FAILURE;
00618 
00619   nsCOMPtr <nsIMsgMessageService> messageService;
00620        rv = GetMessageServiceFromURI(messageURI, getter_AddRefs(messageService));
00621   if (NS_FAILED(rv) && !messageService)
00622     return NS_ERROR_FACTORY_NOT_LOADED;
00623 
00624   ++mTotalSendCount;
00625 
00626   // Setup what we need to parse the data stream correctly
00627   m_inhead = PR_TRUE;
00628   m_headersFP = 0;
00629   m_headersPosition = 0;
00630   m_bytesRead = 0;
00631   m_position = 0;
00632   m_flagsPosition = 0;
00633   m_headersSize = 0;
00634   PR_FREEIF(mLeftoverBuffer);
00635 
00636   //
00637   // Now, get our stream listener interface and plug it into the DisplayMessage
00638   // operation
00639   //
00640   NS_ADDREF(this);
00641 
00642   nsCOMPtr<nsIStreamListener> convertedListener = do_QueryInterface(this);
00643   if (convertedListener)
00644   {
00645     // Now, just plug the two together and get the hell out of the way!
00646     rv = messageService->DisplayMessage(messageURI, convertedListener, nsnull, nsnull, nsnull, nsnull);
00647   }
00648   else
00649     rv = NS_ERROR_FAILURE;
00650 
00651   Release();
00652 
00653   if (NS_FAILED(rv))
00654     return rv;
00655 
00656   return NS_OK;
00657 }
00658 
00659 NS_IMETHODIMP 
00660 nsMsgSendLater::GetUnsentMessagesFolder(nsIMsgIdentity *aIdentity, nsIMsgFolder **folder)
00661 {
00662   char *uri = GetFolderURIFromUserPrefs(nsIMsgSend::nsMsgQueueForLater, aIdentity);
00663 
00664   if (!uri)
00665     return NS_ERROR_OUT_OF_MEMORY;
00666 
00667   nsresult rv = LocateMessageFolder(aIdentity, nsIMsgSend::nsMsgQueueForLater, uri, folder);
00668   PR_Free(uri);
00669   return rv;
00670 }
00671 
00672 //
00673 // To really finalize this capability, we need to have the ability to get
00674 // the message from the mail store in a stream for processing. The flow 
00675 // would be something like this:
00676 //
00677 //      foreach (message in Outbox folder)
00678 //         get stream of Nth message
00679 //         if (not done with headers)
00680 //            Tack on to current buffer of headers
00681 //         when done with headers
00682 //            BuildHeaders()
00683 //            Write Headers to Temp File
00684 //         after done with headers
00685 //            write rest of message body to temp file
00686 //
00687 //          when done with the message
00688 //            do send operation
00689 //
00690 //          when send is complete
00691 //            Copy from Outbox to FCC folder
00692 //            Delete from Outbox folder
00693 //
00694 //
00695 NS_IMETHODIMP 
00696 nsMsgSendLater::SendUnsentMessages(nsIMsgIdentity *identity)
00697 {
00698   nsresult rv = GetUnsentMessagesFolder(identity, getter_AddRefs(mMessageFolder));
00699   NS_ENSURE_SUCCESS(rv,rv);
00700 
00701   // ### fix me - if we need to reparse the folder, this will be asynchronous
00702   nsCOMPtr<nsISimpleEnumerator> enumerator;
00703   nsresult ret = mMessageFolder->GetMessages(m_window, getter_AddRefs(enumerator));
00704   if (NS_FAILED(ret) || (!enumerator))
00705   {
00706     return NS_ERROR_FAILURE;
00707   }
00708 
00709   // copy all the elements in the enumerator into our isupports array....
00710 
00711   nsCOMPtr<nsISupports>   currentItem;
00712   PRBool hasMoreElements = PR_FALSE;
00713   while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreElements)) && hasMoreElements)
00714   {
00715     rv = enumerator->GetNext(getter_AddRefs(currentItem));
00716     if (NS_SUCCEEDED(rv) && currentItem)
00717       mMessagesToSend->AppendElement(currentItem);
00718   }
00719 
00720   // now get an enumerator for our array
00721   mMessagesToSend->Enumerate(getter_AddRefs(mEnumerator));
00722 
00723   return StartNextMailFileSend();
00724 }
00725 
00726 nsresult nsMsgSendLater::SetOrigMsgDisposition()
00727 {
00728   // We're finished sending a queued message. We need to look at mMessage 
00729   // and see if we need to set replied/forwarded
00730   // flags for the original message that this message might be a reply to
00731   // or forward of.
00732   nsXPIDLCString originalMsgURIs;
00733   nsXPIDLCString queuedDisposition;
00734   mMessage->GetStringProperty(ORIG_URI_PROPERTY, getter_Copies(originalMsgURIs));
00735   mMessage->GetStringProperty(QUEUED_DISPOSITION_PROPERTY, getter_Copies(queuedDisposition));
00736   if (!queuedDisposition.IsEmpty())
00737   {
00738     char *uriList = PL_strdup(originalMsgURIs.get());
00739     if (!uriList)
00740       return NS_ERROR_OUT_OF_MEMORY;
00741     char *newStr = uriList;
00742     char *uri;
00743     while (nsnull != (uri = nsCRT::strtok(newStr, ",", &newStr)))
00744     {
00745       nsCOMPtr <nsIMsgDBHdr> msgHdr;
00746       nsresult rv = GetMsgDBHdrFromURI(uri, getter_AddRefs(msgHdr));
00747       NS_ENSURE_SUCCESS(rv,rv);
00748       if (msgHdr)
00749       {
00750         // get the folder for the message resource
00751         nsCOMPtr<nsIMsgFolder> msgFolder;
00752         msgHdr->GetFolder(getter_AddRefs(msgFolder));
00753         if (msgFolder)
00754         {
00755           nsMsgDispositionState dispositionSetting = nsIMsgFolder::nsMsgDispositionState_Replied;
00756           if (queuedDisposition.Equals("forwarded"))
00757             dispositionSetting = nsIMsgFolder::nsMsgDispositionState_Forwarded;
00758           
00759           msgFolder->AddMessageDispositionState(msgHdr, dispositionSetting);
00760         }
00761       }
00762     }
00763     PR_Free(uriList);
00764   }
00765   return NS_OK;
00766 }
00767 
00768 nsresult
00769 nsMsgSendLater::DeleteCurrentMessage()
00770 {
00771   // Get the composition fields interface
00772   nsCOMPtr<nsISupportsArray> msgArray = do_CreateInstance(kISupportsArrayCID);
00773   if (!msgArray)
00774     return NS_ERROR_FACTORY_NOT_LOADED;
00775 
00776   nsCOMPtr<nsISupports> msgSupport = do_QueryInterface(mMessage);
00777   msgArray->InsertElementAt(msgSupport, 0);
00778   nsresult res = mMessageFolder->DeleteMessages(msgArray, nsnull, PR_TRUE, PR_FALSE, nsnull, PR_FALSE /*allowUndo*/);
00779   if (NS_FAILED(res))
00780     return NS_ERROR_FAILURE;
00781 
00782   return NS_OK;
00783 }
00784 
00785 //
00786 // This function parses the headers, and also deletes from the header block
00787 // any headers which should not be delivered in mail, regardless of whether
00788 // they were present in the queue file.  Such headers include: BCC, FCC,
00789 // Sender, X-Mozilla-Status, X-Mozilla-News-Host, and Content-Length.
00790 // (Content-Length is for the disk file only, and must not be allowed to
00791 // escape onto the network, since it depends on the local linebreak
00792 // representation.  Arguably, we could allow Lines to escape, but it's not
00793 // required by NNTP.)
00794 //
00795 #define UNHEX(C) \
00796   ((C >= '0' && C <= '9') ? C - '0' : \
00797   ((C >= 'A' && C <= 'F') ? C - 'A' + 10 : \
00798         ((C >= 'a' && C <= 'f') ? C - 'a' + 10 : 0)))
00799 nsresult
00800 nsMsgSendLater::BuildHeaders()
00801 {
00802   char *buf = m_headers;
00803   char *buf_end = buf + m_headersFP;
00804 
00805   PR_FREEIF(m_to);
00806   PR_FREEIF(m_bcc);
00807   PR_FREEIF(m_newsgroups);
00808   PR_FREEIF(m_newshost);
00809   PR_FREEIF(m_fcc);
00810   PR_FREEIF(mIdentityKey);
00811   PR_FREEIF(mAccountKey);
00812   m_flags = 0;
00813 
00814   while (buf < buf_end)
00815   {
00816     PRBool prune_p = PR_FALSE;
00817     PRBool  do_flags_p = PR_FALSE;
00818     PRBool  do_return_receipt_p = PR_FALSE;
00819     char *colon = PL_strchr(buf, ':');
00820     char *end;
00821     char *value = 0;
00822     char **header = 0;
00823     char *header_start = buf;
00824 
00825     if (! colon)
00826       break;
00827 
00828     end = colon;
00829     while (end > buf && (*end == ' ' || *end == '\t'))
00830       end--;
00831 
00832     switch (buf [0])
00833     {
00834     case 'B': case 'b':
00835       if (!PL_strncasecmp ("BCC", buf, end - buf))
00836       {
00837         header = &m_bcc;
00838         prune_p = PR_TRUE;
00839       }
00840       break;
00841     case 'C': case 'c':
00842       if (!PL_strncasecmp ("CC", buf, end - buf))
00843       header = &m_to;
00844       else if (!PL_strncasecmp (HEADER_CONTENT_LENGTH, buf, end - buf))
00845       prune_p = PR_TRUE;
00846       break;
00847     case 'F': case 'f':
00848       if (!PL_strncasecmp ("FCC", buf, end - buf))
00849       {
00850         header = &m_fcc;
00851         prune_p = PR_TRUE;
00852       }
00853       break;
00854     case 'L': case 'l':
00855       if (!PL_strncasecmp ("Lines", buf, end - buf))
00856       prune_p = PR_TRUE;
00857       break;
00858     case 'N': case 'n':
00859       if (!PL_strncasecmp ("Newsgroups", buf, end - buf))
00860         header = &m_newsgroups;
00861       break;
00862     case 'S': case 's':
00863       if (!PL_strncasecmp ("Sender", buf, end - buf))
00864       prune_p = PR_TRUE;
00865       break;
00866     case 'T': case 't':
00867       if (!PL_strncasecmp ("To", buf, end - buf))
00868       header = &m_to;
00869       break;
00870     case 'X': case 'x':
00871       {
00872         PRInt32 headLen = PL_strlen(HEADER_X_MOZILLA_STATUS2);
00873         if (headLen == end - buf &&
00874           !PL_strncasecmp(HEADER_X_MOZILLA_STATUS2, buf, end - buf))
00875           prune_p = PR_TRUE;
00876         else if (PL_strlen(HEADER_X_MOZILLA_STATUS) == end - buf &&
00877           !PL_strncasecmp(HEADER_X_MOZILLA_STATUS, buf, end - buf))
00878           prune_p = do_flags_p = PR_TRUE;
00879         else if (!PL_strncasecmp(HEADER_X_MOZILLA_DRAFT_INFO, buf, end - buf))
00880           prune_p = do_return_receipt_p = PR_TRUE;
00881         else if (!PL_strncasecmp(HEADER_X_MOZILLA_KEYWORDS, buf, end - buf))
00882           prune_p = PR_TRUE;
00883         else if (!PL_strncasecmp(HEADER_X_MOZILLA_NEWSHOST, buf, end - buf))
00884         {
00885           prune_p = PR_TRUE;
00886           header = &m_newshost;
00887         }
00888         else if (!PL_strncasecmp(HEADER_X_MOZILLA_IDENTITY_KEY, buf, end - buf))
00889         {
00890           prune_p = PR_TRUE;
00891           header = &mIdentityKey;
00892         }
00893         else if (!PL_strncasecmp(HEADER_X_MOZILLA_ACCOUNT_KEY, buf, end - buf))
00894         {
00895           prune_p = PR_TRUE;
00896           header = &mAccountKey;
00897         }
00898         break;
00899       }
00900     }
00901 
00902     buf = colon + 1;
00903     while (*buf == ' ' || *buf == '\t')
00904     buf++;
00905 
00906     value = buf;
00907 
00908 SEARCH_NEWLINE:
00909     while (*buf != 0 && *buf != nsCRT::CR && *buf != nsCRT::LF)
00910       buf++;
00911 
00912     if (buf+1 >= buf_end)
00913       ;
00914     // If "\r\n " or "\r\n\t" is next, that doesn't terminate the header.
00915     else if (buf+2 < buf_end &&
00916          (buf[0] == nsCRT::CR  && buf[1] == nsCRT::LF) &&
00917          (buf[2] == ' ' || buf[2] == '\t'))
00918     {
00919       buf += 3;
00920       goto SEARCH_NEWLINE;
00921     }
00922     // If "\r " or "\r\t" or "\n " or "\n\t" is next, that doesn't terminate
00923     // the header either. 
00924     else if ((buf[0] == nsCRT::CR  || buf[0] == nsCRT::LF) &&
00925          (buf[1] == ' ' || buf[1] == '\t'))
00926     {
00927       buf += 2;
00928       goto SEARCH_NEWLINE;
00929     }
00930 
00931     if (header)
00932     {
00933       int L = buf - value;
00934       if (*header)
00935       {
00936         char *newh = (char*) PR_Realloc ((*header),
00937                          PL_strlen(*header) + L + 10);
00938         if (!newh) return NS_ERROR_OUT_OF_MEMORY;
00939         *header = newh;
00940         newh = (*header) + PL_strlen (*header);
00941         *newh++ = ',';
00942         *newh++ = ' ';
00943         memcpy(newh, value, L);
00944         newh [L] = 0;
00945       }
00946       else
00947       {
00948         *header = (char *) PR_Malloc(L+1);
00949         if (!*header) return NS_ERROR_OUT_OF_MEMORY;
00950         memcpy((*header), value, L);
00951         (*header)[L] = 0;
00952       }
00953     }
00954     else if (do_flags_p)
00955     {
00956       int i;
00957       char *s = value;
00958       PR_ASSERT(*s != ' ' && *s != '\t');
00959       m_flags = 0;
00960       for (i=0 ; i<4 ; i++) {
00961       m_flags = (m_flags << 4) | UNHEX(*s);
00962       s++;
00963       }
00964     }
00965     else if (do_return_receipt_p)
00966     {
00967       int L = buf - value;
00968       char *draftInfo = (char*) PR_Malloc(L+1);
00969       char *receipt = NULL;
00970       if (!draftInfo) return NS_ERROR_OUT_OF_MEMORY;
00971       memcpy(draftInfo, value, L);
00972       *(draftInfo+L)=0;
00973       receipt = PL_strstr(draftInfo, "receipt=");
00974       if (receipt) 
00975       {
00976         char *s = receipt+8;
00977         int requestForReturnReceipt = 0;
00978         PR_sscanf(s, "%d", &requestForReturnReceipt);
00979         
00980         if ((requestForReturnReceipt == 2 || requestForReturnReceipt == 3))
00981           mRequestReturnReceipt = PR_TRUE;
00982       }
00983       PR_Free(draftInfo);
00984     }
00985 
00986     if (*buf == nsCRT::CR || *buf == nsCRT::LF)
00987     {
00988       if (*buf == nsCRT::CR && buf[1] == nsCRT::LF)
00989       buf++;
00990       buf++;
00991     }
00992 
00993     if (prune_p)
00994     {
00995       char *to = header_start;
00996       char *from = buf;
00997       while (from < buf_end)
00998       *to++ = *from++;
00999       buf = header_start;
01000       buf_end = to;
01001       m_headersFP = buf_end - m_headers;
01002     }
01003   }
01004 
01005   m_headers[m_headersFP++] = nsCRT::CR;
01006   m_headers[m_headersFP++] = nsCRT::LF;
01007 
01008   // Now we have parsed out all of the headers we need and we 
01009   // can proceed.
01010   return NS_OK;
01011 }
01012 
01013 int
01014 DoGrowBuffer(PRInt32 desired_size, PRInt32 element_size, PRInt32 quantum,
01015             char **buffer, PRInt32 *size)
01016 {
01017   if (*size <= desired_size)
01018   {
01019     char *new_buf;
01020     PRInt32 increment = desired_size - *size;
01021     if (increment < quantum) // always grow by a minimum of N bytes 
01022       increment = quantum;
01023     
01024     new_buf = (*buffer
01025                 ? (char *) PR_Realloc (*buffer, (*size + increment)
01026                 * (element_size / sizeof(char)))
01027                 : (char *) PR_Malloc ((*size + increment)
01028                 * (element_size / sizeof(char))));
01029     if (! new_buf)
01030       return NS_ERROR_OUT_OF_MEMORY;
01031     *buffer = new_buf;
01032     *size += increment;
01033   }
01034   return 0;
01035 }
01036 
01037 #define do_grow_headers(desired_size) \
01038   (((desired_size) >= m_headersSize) ? \
01039    DoGrowBuffer ((desired_size), sizeof(char), 1024, \
01040            &m_headers, &m_headersSize) \
01041    : 0)
01042 
01043 nsresult
01044 nsMsgSendLater::DeliverQueuedLine(char *line, PRInt32 length)
01045 {
01046   PRInt32 flength = length;
01047   
01048   m_bytesRead += length;
01049   
01050 // convert existing newline to CRLF 
01051 // Don't need this because the calling routine is taking care of it.
01052 //  if (length > 0 && (line[length-1] == nsCRT::CR || 
01053 //     (line[length-1] == nsCRT::LF && (length < 2 || line[length-2] != nsCRT::CR))))
01054 //  {
01055 //    line[length-1] = nsCRT::CR;
01056 //    line[length++] = nsCRT::LF;
01057 //  }
01058 //
01059   //
01060   // We are going to check if we are looking at a "From - " line. If so, 
01061   // then just eat it and return NS_OK
01062   //
01063   if (!PL_strncasecmp(line, "From - ", 7))
01064     return NS_OK;
01065 
01066   if (m_inhead)
01067   {
01068     if (m_headersPosition == 0)
01069     {
01070       // This line is the first line in a header block.
01071       // Remember its position.
01072       m_headersPosition = m_position;
01073       
01074       // Also, since we're now processing the headers, clear out the
01075       // slots which we will parse data into, so that the values that
01076       // were used the last time around do not persist.
01077       
01078       // We must do that here, and not in the previous clause of this
01079       // `else' (the "I've just seen a `From ' line clause") because
01080       // that clause happens before delivery of the previous message is
01081       // complete, whereas this clause happens after the previous msg
01082       // has been delivered.  If we did this up there, then only the
01083       // last message in the folder would ever be able to be both
01084       // mailed and posted (or fcc'ed.)
01085       PR_FREEIF(m_to);
01086       PR_FREEIF(m_bcc);
01087       PR_FREEIF(m_newsgroups);
01088       PR_FREEIF(m_newshost);
01089       PR_FREEIF(m_fcc);
01090       PR_FREEIF(mIdentityKey);
01091     }
01092     
01093     if (line[0] == nsCRT::CR || line[0] == nsCRT::LF || line[0] == 0)
01094     {
01095       // End of headers.  Now parse them; open the temp file;
01096       // and write the appropriate subset of the headers out. 
01097       m_inhead = PR_FALSE;
01098 
01099       mOutFile = new nsOutputFileStream(*mTempFileSpec, PR_WRONLY | PR_CREATE_FILE, 00600);
01100       if ( (!mOutFile) || (!mOutFile->is_open()) )
01101         return NS_MSG_ERROR_WRITING_FILE;
01102 
01103       nsresult status = BuildHeaders();
01104       if (NS_FAILED(status))
01105         return status;
01106 
01107       if (mOutFile->write(m_headers, m_headersFP) != m_headersFP)
01108         return NS_MSG_ERROR_WRITING_FILE;
01109     }
01110     else
01111     {
01112       // Otherwise, this line belongs to a header.  So append it to the
01113       // header data.
01114       
01115       if (!PL_strncasecmp (line, HEADER_X_MOZILLA_STATUS, PL_strlen(HEADER_X_MOZILLA_STATUS)))
01116         // Notice the position of the flags.
01117         m_flagsPosition = m_position;
01118       else if (m_headersFP == 0)
01119         m_flagsPosition = 0;
01120       
01121       nsresult status = do_grow_headers (length + m_headersFP + 10);
01122       if (NS_FAILED(status)) 
01123         return status;
01124       
01125       memcpy(m_headers + m_headersFP, line, length);
01126       m_headersFP += length;
01127     }
01128   }
01129   else
01130   {
01131     // This is a body line.  Write it to the file.
01132     PR_ASSERT(mOutFile);
01133     if (mOutFile)
01134     {
01135       PRInt32 wrote = mOutFile->write(line, length);
01136       if (wrote < (PRInt32) length) 
01137         return NS_MSG_ERROR_WRITING_FILE;
01138     }
01139   }
01140   
01141   m_position += flength;
01142   return NS_OK;
01143 }
01144 
01145 NS_IMETHODIMP
01146 nsMsgSendLater::SetMsgWindow(nsIMsgWindow *aMsgWindow)
01147 {
01148   m_window = aMsgWindow;
01149   return NS_OK;
01150 }
01151 NS_IMETHODIMP
01152 nsMsgSendLater::GetMsgWindow(nsIMsgWindow **aMsgWindow)
01153 {
01154   NS_ENSURE_ARG(aMsgWindow);
01155   *aMsgWindow = m_window;
01156   NS_IF_ADDREF(*aMsgWindow);
01157   return NS_OK;
01158 }
01159 
01160 NS_IMETHODIMP
01161 nsMsgSendLater::AddListener(nsIMsgSendLaterListener *aListener)
01162 {
01163   if ( (mListenerArrayCount > 0) || mListenerArray )
01164   {
01165     ++mListenerArrayCount;
01166     mListenerArray = (nsIMsgSendLaterListener **) 
01167                   PR_Realloc(*mListenerArray, sizeof(nsIMsgSendLaterListener *) * mListenerArrayCount);
01168     if (!mListenerArray)
01169       return NS_ERROR_OUT_OF_MEMORY;
01170     else
01171     {
01172       mListenerArray[mListenerArrayCount - 1] = aListener;
01173       return NS_OK;
01174     }
01175   }
01176   else
01177   {
01178     mListenerArrayCount = 1;
01179     mListenerArray = (nsIMsgSendLaterListener **) PR_Malloc(sizeof(nsIMsgSendLaterListener *) * mListenerArrayCount);
01180     if (!mListenerArray)
01181       return NS_ERROR_OUT_OF_MEMORY;
01182 
01183     memset(mListenerArray, 0, (sizeof(nsIMsgSendLaterListener *) * mListenerArrayCount));
01184   
01185     mListenerArray[0] = aListener;
01186     NS_ADDREF(mListenerArray[0]);
01187     return NS_OK;
01188   }
01189 }
01190 
01191 NS_IMETHODIMP
01192 nsMsgSendLater::RemoveListener(nsIMsgSendLaterListener *aListener)
01193 {
01194   PRInt32 i;
01195   for (i=0; i<mListenerArrayCount; i++)
01196     if (mListenerArray[i] == aListener)
01197     {
01198       NS_RELEASE(mListenerArray[i]);
01199       mListenerArray[i] = nsnull;
01200       return NS_OK;
01201     }
01202 
01203   return NS_ERROR_INVALID_ARG;
01204 }
01205 
01206 
01207 nsresult
01208 nsMsgSendLater::NotifyListenersOnStartSending(PRUint32 aTotalMessageCount)
01209 {
01210   PRInt32 i;
01211   for (i=0; i<mListenerArrayCount; i++)
01212     if (mListenerArray[i] != nsnull)
01213       mListenerArray[i]->OnStartSending(aTotalMessageCount);
01214 
01215   return NS_OK;
01216 }
01217 
01218 nsresult
01219 nsMsgSendLater::NotifyListenersOnProgress(PRUint32 aCurrentMessage, PRUint32 aTotalMessage)
01220 {
01221   PRInt32 i;
01222   for (i=0; i<mListenerArrayCount; i++)
01223     if (mListenerArray[i] != nsnull)
01224       mListenerArray[i]->OnProgress(aCurrentMessage, aTotalMessage);
01225 
01226   return NS_OK;
01227 }
01228 
01229 nsresult
01230 nsMsgSendLater::NotifyListenersOnStatus(const PRUnichar *aMsg)
01231 {
01232   PRInt32 i;
01233   for (i=0; i<mListenerArrayCount; i++)
01234     if (mListenerArray[i] != nsnull)
01235       mListenerArray[i]->OnStatus(aMsg);
01236 
01237   return NS_OK;
01238 }
01239 
01240 nsresult
01241 nsMsgSendLater::NotifyListenersOnStopSending(nsresult aStatus, const PRUnichar *aMsg, 
01242                                              PRUint32 aTotalTried, PRUint32 aSuccessful)
01243 {
01244   PRInt32 i;
01245   for (i=0; i<mListenerArrayCount; i++)
01246     if (mListenerArray[i] != nsnull)
01247       mListenerArray[i]->OnStopSending(aStatus, aMsg, aTotalTried, aSuccessful);
01248 
01249   return NS_OK;
01250 }
01251 
01252 // XXX todo
01253 // maybe this should just live in the account manager?
01254 nsresult
01255 nsMsgSendLater::GetIdentityFromKey(const char *aKey, nsIMsgIdentity  **aIdentity)
01256 {
01257   NS_ENSURE_ARG_POINTER(aIdentity);
01258 
01259   nsresult rv;
01260   nsCOMPtr<nsIMsgAccountManager> accountManager = 
01261     do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
01262   NS_ENSURE_SUCCESS(rv,rv);
01263  
01264   if (aKey)
01265   {
01266     nsCOMPtr<nsISupportsArray> identities;
01267     if (NS_SUCCEEDED(accountManager->GetAllIdentities(getter_AddRefs(identities))))
01268     {
01269       nsCOMPtr<nsIMsgIdentity> lookupIdentity;
01270       PRUint32          count = 0;
01271 
01272       identities->Count(&count);
01273       for (PRUint32 i = 0; i < count; i++)
01274       {
01275         rv = identities->QueryElementAt(i, NS_GET_IID(nsIMsgIdentity),
01276                                   getter_AddRefs(lookupIdentity));
01277         if (NS_FAILED(rv))
01278           continue;
01279 
01280         nsXPIDLCString key;
01281         lookupIdentity->GetKey(getter_Copies(key));
01282         if (key.Equals(aKey))
01283         {
01284           NS_IF_ADDREF(*aIdentity = lookupIdentity);
01285           return NS_OK;
01286         }
01287       }
01288     }
01289   }
01290 
01291   // if no aKey, or we failed to find the identity from the key
01292   // use the identity from the default account.
01293   nsCOMPtr<nsIMsgAccount> defaultAccount;
01294   rv = accountManager->GetDefaultAccount(getter_AddRefs(defaultAccount));
01295   NS_ENSURE_SUCCESS(rv,rv);
01296   
01297   rv = defaultAccount->GetDefaultIdentity(aIdentity);
01298   NS_ENSURE_SUCCESS(rv,rv);
01299   return rv;
01300 }
01301