Back to index

lightning-sunbird  0.9+nobinonly
nsMsgCompose.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  *   Jean-Francois Ducarroz <ducarroz@netscape.com>
00024  *   Ben Bucksch <mozilla@bucksch.org>
00025  *   HÃ¥kan Waara <hwaara@chello.se>
00026  *   Pierre Phaneuf <pp@ludusdesign.com>
00027  *   Masayuki Nakano <masayuki@d-toybox.com>
00028  *
00029  * Alternatively, the contents of this file may be used under the terms of
00030  * either of the GNU General Public License Version 2 or later (the "GPL"),
00031  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00032  * in which case the provisions of the GPL or the LGPL are applicable instead
00033  * of those above. If you wish to allow use of your version of this file only
00034  * under the terms of either the GPL or the LGPL, and not to allow others to
00035  * use your version of this file under the terms of the MPL, indicate your
00036  * decision by deleting the provisions above and replace them with the notice
00037  * and other provisions required by the GPL or the LGPL. If you do not delete
00038  * the provisions above, a recipient may use your version of this file under
00039  * the terms of any one of the MPL, the GPL or the LGPL.
00040  *
00041  * ***** END LICENSE BLOCK ***** */
00042 
00043 #include "nsMsgCompose.h"
00044 
00045 #include "nsIScriptGlobalObject.h"
00046 #include "nsIScriptContext.h"
00047 #include "nsIDOMNode.h"
00048 #include "nsIDOMNodeList.h"
00049 #include "nsIDOMHTMLInputElement.h"
00050 #include "nsIDOMHTMLImageElement.h"
00051 #include "nsIDOMHTMLLinkElement.h"
00052 #include "nsIDOMHTMLAnchorElement.h"
00053 #include "nsISelection.h"
00054 #include "nsISelectionController.h"
00055 #include "nsIDOMNamedNodeMap.h"
00056 #include "nsMsgI18N.h"
00057 #include "nsICharsetConverterManager.h"
00058 #include "nsMsgCompCID.h"
00059 #include "nsMsgQuote.h"
00060 #include "nsIPrefService.h"
00061 #include "nsIPrefBranch.h"
00062 #include "nsIDocumentEncoder.h"    // for editor output flags
00063 #include "nsXPIDLString.h"
00064 #include "nsIMsgHeaderParser.h"
00065 #include "nsMsgCompUtils.h"
00066 #include "nsIMsgStringService.h"
00067 #include "nsMsgComposeStringBundle.h"
00068 #include "nsSpecialSystemDirectory.h"
00069 #include "nsMsgSend.h"
00070 #include "nsMailHeaders.h"
00071 #include "nsMsgPrompts.h"
00072 #include "nsMimeTypes.h"
00073 #include "nsICharsetConverterManager.h"
00074 #include "nsTextFormatter.h"
00075 #include "nsIPlaintextEditor.h"
00076 #include "nsIHTMLEditor.h"
00077 #include "nsIEditorMailSupport.h"
00078 #include "nsEscape.h"
00079 #include "plstr.h"
00080 #include "nsIDocShell.h"
00081 #include "nsIRDFService.h"
00082 #include "nsRDFCID.h"
00083 #include "nsAbBaseCID.h"
00084 #include "nsIAddrDatabase.h"
00085 #include "nsIAddrBookSession.h"
00086 #include "nsIAddressBook.h"
00087 #include "nsCExternalHandlerService.h"
00088 #include "nsIMIMEService.h"
00089 #include "nsIDocShellTreeItem.h"
00090 #include "nsIDocShellTreeOwner.h"
00091 #include "nsIWindowMediator.h"
00092 #include "nsISupportsArray.h"
00093 #include "nsCOMArray.h"
00094 #include "nsIIOService.h"
00095 #include "nsIURL.h"
00096 #include "nsIMsgMailSession.h"
00097 #include "nsMsgBaseCID.h"
00098 #include "nsIPrompt.h"
00099 #include "nsMsgMimeCID.h"
00100 #include "nsCOMPtr.h"
00101 #include "nsDateTimeFormatCID.h"
00102 #include "nsIDateTimeFormat.h"
00103 #include "nsILocaleService.h"
00104 #include "nsILocale.h"
00105 #include "nsMsgComposeService.h"
00106 #include "nsIMsgComposeProgressParams.h"
00107 #include "nsMsgUtils.h"
00108 #include "nsIMsgImapMailFolder.h"
00109 #include "nsImapCore.h"
00110 #include "nsReadableUtils.h"
00111 #include "nsUnicharUtils.h"
00112 #include "nsNetUtil.h"
00113 #include "nsMsgSimulateError.h"
00114 #include "nsIAddrDatabase.h"
00115 #include "nsILocalFile.h"
00116 #include "nsIContentViewer.h"
00117 #include "nsIMarkupDocumentViewer.h"
00118 #include "nsIMsgMdnGenerator.h"
00119 #include "plbase64.h"
00120 #include "nsIUTF8ConverterService.h"
00121 #include "nsUConvCID.h"
00122 #include "nsIUnicodeNormalizer.h"
00123 #include "nsIMsgAccountManager.h"
00124 #include "nsIMsgProgress.h"
00125 #include "nsMsgFolderFlags.h"
00126 
00127 // Defines....
00128 static NS_DEFINE_CID(kDateTimeFormatCID, NS_DATETIMEFORMAT_CID);
00129 
00130 static void GetReplyHeaderInfo(PRInt32* reply_header_type, 
00131                                nsString& reply_header_locale,
00132                                nsXPIDLString& reply_header_authorwrote,
00133                                nsXPIDLString& reply_header_ondate,
00134                                nsString& reply_header_separator,
00135                                nsString& reply_header_colon,
00136                                nsXPIDLString& reply_header_originalmessage)
00137 {
00138   nsresult  rv;
00139   nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
00140 
00141   *reply_header_type = 1;
00142   if(NS_SUCCEEDED(rv)) {
00143     prefBranch->GetIntPref("mailnews.reply_header_type", reply_header_type);
00144 
00145     NS_GetUnicharPreferenceWithDefault(prefBranch, "mailnews.reply_header_locale", EmptyString(), reply_header_locale);
00146     NS_GetLocalizedUnicharPreferenceWithDefault(prefBranch, "mailnews.reply_header_authorwrote", NS_LITERAL_STRING("%s wrote"), reply_header_authorwrote);
00147     NS_GetLocalizedUnicharPreferenceWithDefault(prefBranch, "mailnews.reply_header_ondate", NS_LITERAL_STRING("On %s"), reply_header_ondate);
00148     NS_GetUnicharPreferenceWithDefault(prefBranch, "mailnews.reply_header_separator", NS_LITERAL_STRING(", "), reply_header_separator);
00149     NS_GetUnicharPreferenceWithDefault(prefBranch, "mailnews.reply_header_colon", NS_LITERAL_STRING(":"), reply_header_colon);
00150     NS_GetLocalizedUnicharPreferenceWithDefault(prefBranch, "mailnews.reply_header_originalmessage", NS_LITERAL_STRING("--- Original Message ---"), reply_header_originalmessage);
00151   }
00152 }
00153 
00154 static nsresult RemoveDuplicateAddresses(const char * addresses, const char * anothersAddresses, PRBool removeAliasesToMe, char** newAddress)
00155 {
00156   nsresult rv;
00157 
00158   nsCOMPtr<nsIMsgHeaderParser> parser (do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID));
00159   if (parser)
00160     rv= parser->RemoveDuplicateAddresses("UTF-8", addresses, anothersAddresses, removeAliasesToMe, newAddress);
00161   else
00162     rv = NS_ERROR_FAILURE;
00163 
00164   return rv;
00165 }
00166 
00167 static void TranslateLineEnding(nsString& data)
00168 {
00169   PRUnichar* rPtr;   //Read pointer
00170   PRUnichar* wPtr;   //Write pointer
00171   PRUnichar* sPtr;   //Start data pointer
00172   PRUnichar* ePtr;   //End data pointer
00173 
00174   rPtr = wPtr = sPtr = data.BeginWriting();
00175   ePtr = rPtr + data.Length();
00176 
00177   while (rPtr < ePtr)
00178   {
00179     if (*rPtr == 0x0D)
00180       if (rPtr + 1 < ePtr && *(rPtr + 1) == 0x0A)
00181       {
00182         *wPtr = 0x0A;
00183         rPtr ++;
00184       }
00185       else
00186         *wPtr = 0x0A;
00187     else
00188       *wPtr = *rPtr;
00189 
00190     rPtr ++;
00191     wPtr ++;
00192   }
00193 
00194   data.SetLength(wPtr - sPtr);
00195 }
00196 
00197 static void GetTopmostMsgWindowCharacterSet(nsXPIDLCString& charset, PRBool* charsetOverride)
00198 {
00199   // HACK: if we are replying to a message and that message used a charset over ride
00200   // (as specified in the top most window (assuming the reply originated from that window)
00201   // then use that over ride charset instead of the charset specified in the message
00202   nsCOMPtr <nsIMsgMailSession> mailSession (do_GetService(NS_MSGMAILSESSION_CONTRACTID));          
00203   if (mailSession)
00204   {
00205     nsCOMPtr<nsIMsgWindow>    msgWindow;
00206     mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow));
00207     if (msgWindow)
00208     {
00209       msgWindow->GetMailCharacterSet(getter_Copies(charset));
00210       msgWindow->GetCharsetOverride(charsetOverride);
00211     }
00212   }
00213 }
00214 
00215 nsMsgCompose::nsMsgCompose()
00216 {
00217 #if defined(DEBUG_ducarroz)
00218   printf("CREATE nsMsgCompose: %x\n", this);
00219 #endif
00220 
00221   mQuotingToFollow = PR_FALSE;
00222   mInsertingQuotedContent = PR_FALSE;
00223   mWhatHolder = 1;
00224   m_window = nsnull;
00225   m_editor = nsnull;
00226   mQuoteStreamListener=nsnull;
00227   mCharsetOverride = PR_FALSE;
00228   mDeleteDraft = PR_FALSE;
00229   m_compFields = nsnull;    //m_compFields will be set during nsMsgCompose::Initialize
00230   mType = nsIMsgCompType::New;
00231 
00232   // For TagConvertible
00233   // Read and cache pref
00234   mConvertStructs = PR_FALSE;
00235   nsCOMPtr<nsIPrefBranch> prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID));
00236   if (prefBranch)
00237     prefBranch->GetBoolPref("converter.html2txt.structs", &mConvertStructs);
00238 
00239   m_composeHTML = PR_FALSE;
00240   mRecycledWindow = PR_TRUE;
00241 }
00242 
00243 
00244 nsMsgCompose::~nsMsgCompose()
00245 {
00246 #if defined(DEBUG_ducarroz)
00247   printf("DISPOSE nsMsgCompose: %x\n", this);
00248 #endif
00249 
00250   NS_IF_RELEASE(m_compFields);
00251   NS_IF_RELEASE(mQuoteStreamListener);
00252 }
00253 
00254 /* the following macro actually implement addref, release and query interface for our component. */
00255 NS_IMPL_ISUPPORTS2(nsMsgCompose, nsIMsgCompose, nsISupportsWeakReference)
00256 
00257 //
00258 // Once we are here, convert the data which we know to be UTF-8 to UTF-16
00259 // for insertion into the editor
00260 //
00261 nsresult 
00262 GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset)
00263 {
00264   NS_ASSERTION((aChild && aParent), "bad args");
00265   nsresult result = NS_ERROR_NULL_POINTER;
00266   if (aChild && aParent)
00267   {
00268     nsCOMPtr<nsIDOMNodeList> childNodes;
00269     result = aParent->GetChildNodes(getter_AddRefs(childNodes));
00270     if ((NS_SUCCEEDED(result)) && (childNodes))
00271     {
00272       PRInt32 i=0;
00273       for ( ; NS_SUCCEEDED(result); i++)
00274       {
00275         nsCOMPtr<nsIDOMNode> childNode;
00276         result = childNodes->Item(i, getter_AddRefs(childNode));
00277         if ((NS_SUCCEEDED(result)) && (childNode))
00278         {
00279           if (childNode.get()==aChild)
00280           {
00281             aOffset = i;
00282             break;
00283           }
00284         }
00285         else if (!childNode)
00286           result = NS_ERROR_NULL_POINTER;
00287       }
00288     }
00289     else if (!childNodes)
00290       result = NS_ERROR_NULL_POINTER;
00291   }
00292   return result;
00293 }
00294 
00295 nsresult 
00296 GetNodeLocation(nsIDOMNode *inChild, nsCOMPtr<nsIDOMNode> *outParent, PRInt32 *outOffset)
00297 {
00298   NS_ASSERTION((outParent && outOffset), "bad args");
00299   nsresult result = NS_ERROR_NULL_POINTER;
00300   if (inChild && outParent && outOffset)
00301   {
00302     result = inChild->GetParentNode(getter_AddRefs(*outParent));
00303     if ( (NS_SUCCEEDED(result)) && (*outParent) )
00304     {
00305       result = GetChildOffset(inChild, *outParent, *outOffset);
00306     }
00307   }
00308 
00309   return result;
00310 }
00311 
00312 PRBool nsMsgCompose::IsEmbeddedObjectSafe(const char * originalScheme,
00313                                           const char * originalHost,
00314                                           const char * originalPath,
00315                                           nsIDOMNode * object)
00316 {
00317   nsresult rv;
00318 
00319   nsCOMPtr<nsIDOMHTMLImageElement> image;
00320   nsCOMPtr<nsIDOMHTMLLinkElement> link;
00321   nsCOMPtr<nsIDOMHTMLAnchorElement> anchor;
00322   nsAutoString objURL;
00323 
00324   if (!object || !originalScheme || !originalPath) //having a null host is ok...
00325     return PR_FALSE;
00326 
00327   if ((image = do_QueryInterface(object)))
00328   {
00329     if (NS_FAILED(image->GetSrc(objURL)))
00330       return PR_FALSE;
00331   }
00332   else if ((link = do_QueryInterface(object)))
00333   {
00334     if (NS_FAILED(link->GetHref(objURL)))
00335       return PR_FALSE;
00336   }
00337   else if ((anchor = do_QueryInterface(object)))
00338   {
00339     if (NS_FAILED(anchor->GetHref(objURL)))
00340       return PR_FALSE;
00341   }
00342   else
00343     return PR_FALSE;
00344 
00345   if (!objURL.IsEmpty())
00346   {
00347     nsCOMPtr<nsIURI> uri;
00348     rv = NS_NewURI(getter_AddRefs(uri), objURL);
00349     if (NS_SUCCEEDED(rv) && uri)
00350     {
00351       nsCAutoString scheme;
00352       rv = uri->GetScheme(scheme);
00353       if (NS_SUCCEEDED(rv) && (nsCRT::strcasecmp(scheme.get(), originalScheme) == 0))
00354       {
00355         nsCAutoString host;
00356         rv = uri->GetAsciiHost(host);
00357         // mailbox url don't have a host therefore don't be too strict.
00358         if (NS_SUCCEEDED(rv) && (host.IsEmpty() || originalHost || (nsCRT::strcasecmp(host.get(), originalHost) == 0)))
00359         {
00360           nsCAutoString path;
00361           rv = uri->GetPath(path);
00362           if (NS_SUCCEEDED(rv))
00363           {
00364             const char * query = strrchr(path.get(), '?');
00365             if (query && nsCRT::strncasecmp(path.get(), originalPath, query - path.get()) == 0)
00366               return PR_TRUE; //This object is a part of the original message, we can send it safely.
00367           }
00368         }
00369       }
00370     }
00371   }
00372 
00373   return PR_FALSE;
00374 }
00375 
00376 /* Reset the uri's of embedded objects because we've saved the draft message, and the 
00377    original message doesn't exist anymore.
00378  */
00379 nsresult nsMsgCompose::ResetUrisForEmbeddedObjects()
00380 {
00381   nsCOMPtr<nsISupportsArray> aNodeList;
00382   PRUint32 numNodes;
00383   PRUint32 i;
00384 
00385   nsCOMPtr<nsIEditorMailSupport> mailEditor (do_QueryInterface(m_editor));
00386   if (!mailEditor)
00387     return NS_ERROR_FAILURE;
00388 
00389   nsresult rv = mailEditor->GetEmbeddedObjects(getter_AddRefs(aNodeList));
00390   if ((NS_FAILED(rv) || (!aNodeList)))
00391     return NS_ERROR_FAILURE;
00392 
00393   if (NS_FAILED(aNodeList->Count(&numNodes)))
00394     return NS_ERROR_FAILURE;
00395 
00396   nsCOMPtr<nsIDOMNode> node;
00397   nsXPIDLCString curDraftIdURL;
00398 
00399   rv = m_compFields->GetDraftId(getter_Copies(curDraftIdURL));
00400   NS_ASSERTION((NS_SUCCEEDED(rv) && (curDraftIdURL)), "RemoveCurrentDraftMessage can't get draft id");
00401 
00402   // Skip if no draft id (probably a new draft msg).
00403   if (NS_SUCCEEDED(rv) && mMsgSend && !curDraftIdURL.IsEmpty())
00404   {
00405     // we don't currently handle imap urls
00406     if (StringBeginsWith(curDraftIdURL, NS_LITERAL_CSTRING("imap-message")))
00407       return NS_OK;
00408 
00409     nsCOMPtr <nsIMsgDBHdr> msgDBHdr;
00410     rv = GetMsgDBHdrFromURI(curDraftIdURL, getter_AddRefs(msgDBHdr));
00411     NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveCurrentDraftMessage can't get msg header DB interface pointer.");
00412     if (NS_SUCCEEDED(rv) && msgDBHdr)
00413     {
00414       nsMsgKey oldDraftKey;
00415 
00416       // build up the old and new ?number= parts. This code assumes it is 
00417       // called *before* RemoveCurrentDraftMessage, so that curDraftIdURL
00418       // is the previous draft.
00419       // This code currently only works for local mail folders.
00420       // For imap folders, the url looks like <folder>%3E<UID>?part=...
00421       // We could handle the imap case as well, but it turns out 
00422       // not to be so important because the old message is still on
00423       // the imap server. If it turns out to be a problem, we can
00424       // deal with imap urls as well.
00425       msgDBHdr->GetMessageKey(&oldDraftKey);
00426       nsAutoString oldNumberPart(NS_LITERAL_STRING("?number="));
00427       oldNumberPart.AppendInt(oldDraftKey);
00428       nsAutoString newNumberPart;
00429       nsMsgKey newMsgKey;
00430       mMsgSend->GetMessageKey(&newMsgKey);
00431       newNumberPart.AppendInt(newMsgKey);
00432 
00433       nsCOMPtr<nsIDOMElement> domElement;
00434       for (i = 0; i < numNodes; i ++)
00435       {
00436         domElement = do_QueryElementAt(aNodeList, i);
00437         if (!domElement)
00438           continue;
00439 
00440         nsCOMPtr<nsIDOMHTMLImageElement> image = do_QueryInterface(domElement);
00441         if (!image)
00442           continue;
00443         // do we care about anything besides images?
00444         nsAutoString objURL;
00445         image->GetSrc(objURL);
00446         // the objURL is the full path to the mailbox, 
00447         // e.g., mailbox:///C/Documents%20Settings.../Local%20Folders/Drafts?number=
00448         // Find the ?number= part of the uri, and replace the
00449         // old number with the new msg key.
00450 
00451         PRInt32 numberIndex = objURL.Find(oldNumberPart);
00452         if (numberIndex != kNotFound)
00453         {
00454           objURL.Replace(numberIndex + 8, oldNumberPart.Length() - 8, newNumberPart);
00455           image->SetSrc(objURL);
00456         }
00457       }
00458     }
00459   }
00460 
00461   return NS_OK;
00462 }
00463 
00464 
00465 /* The purpose of this function is to mark any embedded object that wasn't a RFC822 part
00466    of the original message as moz-do-not-send.
00467    That will prevent us to attach data not specified by the user or not present in the
00468    original message.
00469 */
00470 nsresult nsMsgCompose::TagEmbeddedObjects(nsIEditorMailSupport *aEditor)
00471 {
00472   nsresult rv = NS_OK;
00473   nsCOMPtr<nsISupportsArray> aNodeList;
00474   PRUint32 count;
00475   PRUint32 i;
00476 
00477   if (!aEditor)
00478     return NS_ERROR_FAILURE;
00479 
00480   rv = aEditor->GetEmbeddedObjects(getter_AddRefs(aNodeList));
00481   if ((NS_FAILED(rv) || (!aNodeList)))
00482     return NS_ERROR_FAILURE;
00483 
00484   if (NS_FAILED(aNodeList->Count(&count)))
00485     return NS_ERROR_FAILURE;
00486 
00487   nsCOMPtr<nsIDOMNode> node;
00488 
00489   nsCOMPtr<nsIURI> originalUrl;
00490   nsXPIDLCString originalScheme;
00491   nsXPIDLCString originalHost;
00492   nsXPIDLCString originalPath;
00493 
00494   // first, convert the rdf original msg uri into a url that represents the message...
00495   nsCOMPtr <nsIMsgMessageService> msgService;
00496   rv = GetMessageServiceFromURI(mOriginalMsgURI.get(), getter_AddRefs(msgService));
00497   if (NS_SUCCEEDED(rv))
00498   {
00499     rv = msgService->GetUrlForUri(mOriginalMsgURI.get(), getter_AddRefs(originalUrl), nsnull);
00500     if (NS_SUCCEEDED(rv) && originalUrl)
00501     {
00502       originalUrl->GetScheme(originalScheme);
00503       originalUrl->GetAsciiHost(originalHost);
00504       originalUrl->GetPath(originalPath);
00505     }
00506   }
00507 
00508   // Then compare the url of each embedded objects with the original message.
00509   // If they a not coming from the original message, they should not be sent
00510   // with the message.
00511   nsCOMPtr<nsIDOMElement> domElement;
00512   for (i = 0; i < count; i ++)
00513   {
00514     node = do_QueryElementAt(aNodeList, i);
00515     if (!node)
00516       continue;
00517     if (IsEmbeddedObjectSafe(originalScheme.get(), originalHost.get(),
00518                              originalPath.get(), node))
00519       continue; //Don't need to tag this object, it safe to send it.
00520 
00521     //The source of this object should not be sent with the message 
00522     domElement = do_QueryInterface(node);
00523     if (domElement)
00524       domElement->SetAttribute(NS_LITERAL_STRING("moz-do-not-send"), NS_LITERAL_STRING("true"));
00525   }
00526 
00527   return NS_OK;
00528 }
00529 
00530 NS_IMETHODIMP
00531 nsMsgCompose::GetInsertingQuotedContent(PRBool * aInsertingQuotedText)
00532 {
00533   NS_ENSURE_ARG_POINTER(aInsertingQuotedText);
00534   *aInsertingQuotedText = mInsertingQuotedContent;
00535   return NS_OK;
00536 }
00537 
00538 NS_IMETHODIMP 
00539 nsMsgCompose::SetInsertingQuotedContent(PRBool aInsertingQuotedText)
00540 {
00541   mInsertingQuotedContent = aInsertingQuotedText;
00542   return NS_OK;
00543 }
00544 
00545 NS_IMETHODIMP
00546 nsMsgCompose::ConvertAndLoadComposeWindow(nsString& aPrefix,
00547                                           nsString& aBuf,
00548                                           nsString& aSignature,
00549                                           PRBool aQuoted,
00550                                           PRBool aHTMLEditor)
00551 {
00552   NS_ASSERTION(m_editor, "ConvertAndLoadComposeWindow but no editor\n");
00553   if (!m_editor)
00554     return NS_ERROR_FAILURE;
00555 
00556   // First, get the nsIEditor interface for future use
00557   nsCOMPtr<nsIDOMNode> nodeInserted;
00558 
00559   TranslateLineEnding(aPrefix);
00560   TranslateLineEnding(aBuf);
00561   TranslateLineEnding(aSignature);
00562 
00563   // We're going to be inserting stuff, and MsgComposeCommands
00564   // may have set the editor to readonly in the recycled case.
00565   // So set it back to writable.
00566   // Note!  enableEditableFields in gComposeRecyclingListener::onReopen
00567   // will redundantly set this flag to writable, but it gets there
00568   // too late.
00569   PRUint32 flags = 0;
00570   m_editor->GetFlags(&flags);
00571   flags &= ~nsIPlaintextEditor::eEditorReadonlyMask;
00572   m_editor->SetFlags(flags);
00573 
00574   m_editor->EnableUndo(PR_FALSE);
00575 
00576   // Ok - now we need to figure out the charset of the aBuf we are going to send
00577   // into the editor shell. There are I18N calls to sniff the data and then we need
00578   // to call the new routine in the editor that will allow us to send in the charset
00579   //
00580 
00581   // Now, insert it into the editor...
00582   nsCOMPtr<nsIHTMLEditor> htmlEditor (do_QueryInterface(m_editor));
00583   nsCOMPtr<nsIPlaintextEditor> textEditor (do_QueryInterface(m_editor));
00584   nsCOMPtr<nsIEditorMailSupport> mailEditor (do_QueryInterface(m_editor));
00585   m_editor->BeginTransaction();
00586   PRInt32 reply_on_top = 0;
00587   PRBool sig_bottom = PR_TRUE;
00588   m_identity->GetReplyOnTop(&reply_on_top);
00589   m_identity->GetSigBottom(&sig_bottom);
00590   PRBool sigOnTop = (reply_on_top == 1 && !sig_bottom);
00591   if (aQuoted)
00592   {
00593     mInsertingQuotedContent = PR_TRUE;
00594     if (!aPrefix.IsEmpty())
00595     {
00596       if (!aHTMLEditor)
00597         aPrefix.AppendLiteral("\n");
00598       textEditor->InsertText(aPrefix);
00599       m_editor->EndOfDocument();
00600     }
00601 
00602     if (!aBuf.IsEmpty() && mailEditor)
00603     {
00604       // XXX see bug #206793
00605       nsIDocShell *docshell = nsnull;
00606       nsCOMPtr<nsIScriptGlobalObject> globalObj = do_QueryInterface(m_window);
00607       if (globalObj && (docshell = globalObj->GetDocShell()))
00608         docshell->SetAppType(nsIDocShell::APP_TYPE_EDITOR);
00609 
00610       if (aHTMLEditor && !mCiteReference.IsEmpty())
00611         mailEditor->InsertAsCitedQuotation(aBuf,
00612                                            mCiteReference,
00613                                            PR_TRUE,
00614                                            getter_AddRefs(nodeInserted));
00615       else
00616         mailEditor->InsertAsQuotation(aBuf,
00617                                       getter_AddRefs(nodeInserted));
00618 
00619       m_editor->EndOfDocument();
00620     }
00621 
00622     mInsertingQuotedContent = PR_FALSE;
00623 
00624     (void)TagEmbeddedObjects(mailEditor);
00625 
00626     if (!aSignature.IsEmpty() )
00627     {
00628       //we cannot add it on top earlier, because TagEmbeddedObjects will mark all images in the signature as "moz-do-not-send"
00629       if( sigOnTop )
00630         m_editor->BeginningOfDocument();
00631 
00632       if (aHTMLEditor && htmlEditor)
00633         htmlEditor->InsertHTML(aSignature);
00634       else if (textEditor)
00635         textEditor->InsertText(aSignature);
00636 
00637       if( sigOnTop )
00638         m_editor->EndOfDocument();
00639     }
00640   }
00641   else
00642   {
00643     if (aHTMLEditor && htmlEditor)
00644     {
00645       mInsertingQuotedContent = PR_TRUE;
00646       htmlEditor->RebuildDocumentFromSource(aBuf);
00647       mInsertingQuotedContent = PR_FALSE;
00648 
00649       m_editor->EndOfDocument();
00650 
00651       // when forwarding a message as inline, tag any embedded objects
00652       // which refer to local images or files so we know not to include 
00653       // send them
00654       if (mType == nsIMsgCompType::ForwardInline)
00655         (void)TagEmbeddedObjects(mailEditor);
00656 
00657       if (!aSignature.IsEmpty())
00658         htmlEditor->InsertHTML(aSignature);
00659     }
00660     else if (textEditor)
00661     {
00662       if (!aBuf.IsEmpty())
00663       {
00664         if (mailEditor)
00665           mailEditor->InsertTextWithQuotations(aBuf);
00666         else
00667           textEditor->InsertText(aBuf);
00668         m_editor->EndOfDocument();
00669       }
00670 
00671       if (!aSignature.IsEmpty())
00672         textEditor->InsertText(aSignature);
00673     }
00674   }
00675   m_editor->EndTransaction();
00676 
00677   if (m_editor)
00678   {
00679     if (aBuf.IsEmpty())
00680       m_editor->BeginningOfDocument();
00681     else
00682     {
00683       switch (reply_on_top)
00684         {
00685           // This should set the cursor after the body but before the sig
00686           case 0  :
00687           {
00688             if (!textEditor)
00689             {
00690               m_editor->BeginningOfDocument();
00691               break;
00692             }
00693 
00694             nsCOMPtr<nsISelection> selection = nsnull; 
00695             nsCOMPtr<nsIDOMNode>      parent = nsnull; 
00696             PRInt32                   offset;
00697             nsresult                  rv;
00698 
00699             // get parent and offset of mailcite
00700             rv = GetNodeLocation(nodeInserted, address_of(parent), &offset);
00701             if (NS_FAILED(rv) || (!parent))
00702             {
00703               m_editor->BeginningOfDocument();
00704               break;
00705             }
00706 
00707             // get selection
00708             m_editor->GetSelection(getter_AddRefs(selection));
00709             if (!selection)
00710             {
00711               m_editor->BeginningOfDocument();
00712               break;
00713             }
00714 
00715             // place selection after mailcite
00716             selection->Collapse(parent, offset+1);
00717 
00718             // insert a break at current selection
00719             textEditor->InsertLineBreak();
00720 
00721             // i'm not sure if you need to move the selection back to before the
00722             // break. expirement.
00723             selection->Collapse(parent, offset+1);
00724 
00725             break;
00726           }
00727 
00728         case 2  : 
00729         {
00730           m_editor->SelectAll();
00731           break;
00732         }
00733 
00734         // This should set the cursor to the top!
00735         default : m_editor->BeginningOfDocument();    break;
00736       }
00737     }
00738 
00739     nsCOMPtr<nsISelectionController> selCon;
00740     m_editor->GetSelectionController(getter_AddRefs(selCon));
00741 
00742     if (selCon)
00743       selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_ANCHOR_REGION, PR_TRUE);
00744   }
00745 
00746   if (m_editor)
00747     m_editor->EnableUndo(PR_TRUE);
00748   SetBodyModified(PR_FALSE);
00749 
00750 #ifdef MSGCOMP_TRACE_PERFORMANCE
00751   nsCOMPtr<nsIMsgComposeService> composeService (do_GetService(NS_MSGCOMPOSESERVICE_CONTRACTID));
00752   composeService->TimeStamp("Finished inserting data into the editor. The window is finally ready!", PR_FALSE);
00753 #endif
00754   return NS_OK;
00755 }
00756 
00757 nsresult 
00758 nsMsgCompose::SetQuotingToFollow(PRBool aVal)
00759 {
00760   mQuotingToFollow = aVal;
00761   return NS_OK;
00762 }
00763 
00764 NS_IMETHODIMP
00765 nsMsgCompose::GetQuotingToFollow(PRBool* quotingToFollow)
00766 {
00767   NS_ENSURE_ARG(quotingToFollow);
00768   *quotingToFollow = mQuotingToFollow;
00769   return NS_OK;
00770 }
00771 
00772 NS_IMETHODIMP
00773 nsMsgCompose::Initialize(nsIDOMWindowInternal *aWindow, nsIMsgComposeParams *params)
00774 {
00775   NS_ENSURE_ARG_POINTER(params);
00776   nsresult rv;
00777 
00778   params->GetIdentity(getter_AddRefs(m_identity));
00779 
00780   if (aWindow)
00781   {
00782     m_window = aWindow;
00783     nsCOMPtr<nsIScriptGlobalObject> globalObj(do_QueryInterface(aWindow));
00784     if (!globalObj)
00785       return NS_ERROR_FAILURE;
00786 
00787     nsCOMPtr<nsIDocShellTreeItem>  treeItem =
00788       do_QueryInterface(globalObj->GetDocShell());
00789     nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
00790     rv = treeItem->GetTreeOwner(getter_AddRefs(treeOwner));
00791     if (NS_FAILED(rv)) return rv;
00792 
00793     m_baseWindow = do_QueryInterface(treeOwner);
00794 
00795     globalObj->GetDocShell()->SetAppType(nsIDocShell::APP_TYPE_EDITOR);
00796   }
00797 
00798   MSG_ComposeFormat format;
00799   params->GetFormat(&format);
00800 
00801   MSG_ComposeType type;
00802   params->GetType(&type);
00803 
00804   nsXPIDLCString originalMsgURI;
00805   params->GetOriginalMsgURI(getter_Copies(originalMsgURI));
00806   params->GetOrigMsgHdr(getter_AddRefs(mOrigMsgHdr));
00807 
00808   nsCOMPtr<nsIMsgCompFields> composeFields;
00809   params->GetComposeFields(getter_AddRefs(composeFields));
00810 
00811   nsCOMPtr<nsIMsgComposeService> composeService = do_GetService(NS_MSGCOMPOSESERVICE_CONTRACTID, &rv);
00812   NS_ENSURE_SUCCESS(rv,rv);
00813   
00814   rv = composeService->DetermineComposeHTML(m_identity, format, &m_composeHTML);
00815   NS_ENSURE_SUCCESS(rv,rv);
00816 
00817   // Set return receipt flag and type, and if we should attach a vCard
00818   if (m_identity && composeFields)
00819   {
00820     PRBool requestReturnReceipt = PR_FALSE;
00821     rv = m_identity->GetRequestReturnReceipt(&requestReturnReceipt);
00822     NS_ENSURE_SUCCESS(rv, rv);
00823     rv = composeFields->SetReturnReceipt(requestReturnReceipt);
00824     NS_ENSURE_SUCCESS(rv, rv);
00825 
00826     PRInt32 receiptType = nsIMsgMdnGenerator::eDntType;
00827     rv = m_identity->GetReceiptHeaderType(&receiptType);
00828     NS_ENSURE_SUCCESS(rv, rv);
00829     rv = composeFields->SetReceiptHeaderType(receiptType);
00830     NS_ENSURE_SUCCESS(rv, rv);
00831 
00832     PRBool attachVCard;
00833     rv = m_identity->GetAttachVCard(&attachVCard);
00834     NS_ENSURE_SUCCESS(rv, rv);
00835     rv = composeFields->SetAttachVCard(attachVCard);
00836     NS_ENSURE_SUCCESS(rv, rv);
00837   }
00838 
00839   params->GetSendListener(getter_AddRefs(mExternalSendListener));
00840   nsXPIDLCString smtpPassword;
00841   params->GetSmtpPassword(getter_Copies(smtpPassword));
00842   mSmtpPassword = (const char *)smtpPassword;
00843 
00844   if (aWindow)
00845   {
00846     // register the compose object with the compose service
00847     rv = composeService->RegisterComposeWindow(aWindow, this);
00848     NS_ENSURE_SUCCESS(rv, rv);
00849   }
00850   return CreateMessage(originalMsgURI, type, composeFields);
00851 }
00852 
00853 nsresult nsMsgCompose::SetDocumentCharset(const char *charset) 
00854 {
00855   // Set charset, this will be used for the MIME charset labeling.
00856   m_compFields->SetCharacterSet(charset);
00857 
00858   // notify the change to editor
00859   m_editor->SetDocumentCharacterSet(nsDependentCString(charset));
00860 
00861   return NS_OK;
00862 }
00863 
00864 nsresult nsMsgCompose::RegisterStateListener(nsIMsgComposeStateListener *stateListener)
00865 {
00866   nsresult rv = NS_OK;
00867 
00868   if (!stateListener)
00869     return NS_ERROR_NULL_POINTER;
00870 
00871   if (!mStateListeners)
00872   {
00873     rv = NS_NewISupportsArray(getter_AddRefs(mStateListeners));
00874     if (NS_FAILED(rv)) return rv;
00875   }
00876   nsCOMPtr<nsISupports> iSupports = do_QueryInterface(stateListener, &rv);
00877   if (NS_FAILED(rv)) return rv;
00878 
00879   // note that this return value is really a PRBool, so be sure to use
00880   // NS_SUCCEEDED or NS_FAILED to check it.
00881   return mStateListeners->AppendElement(iSupports);
00882 }
00883 
00884 nsresult nsMsgCompose::UnregisterStateListener(nsIMsgComposeStateListener *stateListener)
00885 {
00886   if (!stateListener)
00887     return NS_ERROR_NULL_POINTER;
00888 
00889   nsresult rv = NS_OK;
00890 
00891   // otherwise, see if it exists in our list
00892   if (!mStateListeners)
00893     return (nsresult)PR_FALSE;      // yeah, this sucks, but I'm emulating the behaviour of
00894                                     // nsISupportsArray::RemoveElement()
00895 
00896   nsCOMPtr<nsISupports> iSupports = do_QueryInterface(stateListener, &rv);
00897   if (NS_FAILED(rv)) return rv;
00898 
00899   // note that this return value is really a PRBool, so be sure to use
00900   // NS_SUCCEEDED or NS_FAILED to check it.
00901   return mStateListeners->RemoveElement(iSupports);
00902 }
00903 
00904 nsresult nsMsgCompose::_SendMsg(MSG_DeliverMode deliverMode, nsIMsgIdentity *identity, const char *accountKey, PRBool entityConversionDone)
00905 {
00906   nsresult rv = NS_OK;
00907 
00908   // clear saved message id if sending, so we don't send out the same message-id.
00909   if (deliverMode == nsIMsgCompDeliverMode::Now || deliverMode == nsIMsgCompDeliverMode::Later)
00910     m_compFields->SetMessageId("");
00911 
00912   if (m_compFields && identity) 
00913   {
00914     // Pref values are supposed to be stored as UTF-8, so no conversion
00915     nsXPIDLCString email;
00916     nsXPIDLString fullName;
00917     nsXPIDLString organization;
00918 
00919     identity->GetEmail(getter_Copies(email));
00920     identity->GetFullName(getter_Copies(fullName));
00921     identity->GetOrganization(getter_Copies(organization));
00922 
00923     char * sender = nsnull;
00924     nsCOMPtr<nsIMsgHeaderParser> parser (do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID));
00925     if (parser) {
00926       // convert to UTF8 before passing to MakeFullAddress
00927       parser->MakeFullAddress(nsnull, NS_ConvertUCS2toUTF8(fullName).get(), email, &sender);
00928     }
00929 
00930   if (!sender)
00931     m_compFields->SetFrom(email);
00932   else
00933     m_compFields->SetFrom(sender);
00934   PR_FREEIF(sender);
00935 
00936     m_compFields->SetOrganization(organization);
00937 
00938 #if defined(DEBUG_ducarroz) || defined(DEBUG_seth_)
00939   {
00940     printf("----------------------------\n");
00941     printf("--  Sending Mail Message  --\n");
00942     printf("----------------------------\n");
00943     printf("from: %s\n", m_compFields->GetFrom());
00944     printf("To: %s  Cc: %s  Bcc: %s\n", m_compFields->GetTo(), m_compFields->GetCc(), m_compFields->GetBcc());
00945     printf("Newsgroups: %s\n", m_compFields->GetNewsgroups());
00946     printf("Subject: %s  \nMsg: %s\n", m_compFields->GetSubject(), m_compFields->GetBody());
00947     nsCOMPtr<nsISupportsArray> attachmentsArray;
00948     m_compFields->GetAttachmentsArray(getter_AddRefs(attachmentsArray));
00949     if (attachmentsArray)
00950     {
00951       PRUint32 i;
00952       PRUint32 attachmentCount = 0;
00953       attachmentsArray->Count(&attachmentCount);
00954 
00955       nsCOMPtr<nsIMsgAttachment> element;
00956       for (i = 0; i < attachmentCount; i ++)
00957       {
00958         attachmentsArray->QueryElementAt(i, NS_GET_IID(nsIMsgAttachment), getter_AddRefs(element));
00959         if (element)
00960         {
00961           nsAutoString name;
00962           nsXPIDLCString url;
00963           element->GetName(name);
00964           element->GetUrl(getter_Copies(url));
00965           printf("Attachment %d: %s - %s\n",i + 1, NS_ConvertUTF16toUTF8(name).get(), url.get());
00966         }
00967       }
00968     }
00969     printf("----------------------------\n");
00970   }
00971 #endif //DEBUG
00972 
00973     mMsgSend = do_CreateInstance(NS_MSGSEND_CONTRACTID);
00974     if (mMsgSend)
00975     {
00976       PRBool      newBody = PR_FALSE;
00977       char        *bodyString = (char *)m_compFields->GetBody();
00978       PRInt32     bodyLength;
00979       const char  attachment1_type[] = TEXT_HTML;  // we better be "text/html" at this point
00980 
00981       if (!entityConversionDone)
00982       {
00983         // Convert body to mail charset
00984         char      *outCString;
00985 
00986         if (  bodyString && *bodyString )
00987         {
00988           // Apply entity conversion then convert to a mail charset. 
00989           PRBool isAsciiOnly;
00990           rv = nsMsgI18NSaveAsCharset(attachment1_type, m_compFields->GetCharacterSet(), 
00991                                       NS_ConvertUTF8toUTF16(bodyString).get(), &outCString,
00992                                       nsnull, &isAsciiOnly);
00993           if (NS_SUCCEEDED(rv)) 
00994           {
00995             if (m_compFields->GetForceMsgEncoding())
00996               isAsciiOnly = PR_FALSE;
00997 
00998             m_compFields->SetBodyIsAsciiOnly(isAsciiOnly);
00999             bodyString = outCString;
01000             newBody = PR_TRUE;
01001           }
01002         }
01003       }
01004 
01005       bodyLength = PL_strlen(bodyString);
01006 
01007       // Create the listener for the send operation...
01008       nsCOMPtr<nsIMsgComposeSendListener> composeSendListener = do_CreateInstance(NS_MSGCOMPOSESENDLISTENER_CONTRACTID);
01009       if (!composeSendListener)
01010         return NS_ERROR_OUT_OF_MEMORY;
01011 
01012       // right now, AutoSaveAsDraft is identical to SaveAsDraft as
01013       // far as the msg send code is concerned. This way, we don't have
01014       // to add an nsMsgDeliverMode for autosaveasdraft, and add cases for
01015       // it in the msg send code.
01016       if (deliverMode == nsIMsgCompDeliverMode::AutoSaveAsDraft)
01017         deliverMode = nsIMsgCompDeliverMode::SaveAsDraft;
01018 
01019       composeSendListener->SetMsgCompose(this);
01020       composeSendListener->SetDeliverMode(deliverMode);
01021 
01022       if (mProgress)
01023       {
01024         nsCOMPtr<nsIWebProgressListener> progressListener = do_QueryInterface(composeSendListener);
01025         mProgress->RegisterListener(progressListener);
01026       }
01027 
01028       // If we are composing HTML, then this should be sent as
01029       // multipart/related which means we pass the editor into the
01030       // backend...if not, just pass nsnull
01031       //
01032       nsCOMPtr<nsIMsgSendListener> sendListener = do_QueryInterface(composeSendListener);
01033       rv = mMsgSend->CreateAndSendMessage(
01034                     m_composeHTML ? m_editor.get() : nsnull,
01035                     identity,
01036                     accountKey,
01037                     m_compFields, 
01038                     PR_FALSE,                           // PRBool                            digest_p,
01039                     PR_FALSE,                           // PRBool                            dont_deliver_p,
01040                     (nsMsgDeliverMode)deliverMode,      // nsMsgDeliverMode                  mode,
01041                     nsnull,                             // nsIMsgDBHdr                       *msgToReplace, 
01042                     m_composeHTML?TEXT_HTML:TEXT_PLAIN, // const char                        *attachment1_type,
01043                     bodyString,                         // const char                        *attachment1_body,
01044                     bodyLength,                         // PRUint32                          attachment1_body_length,
01045                     nsnull,                             // const struct nsMsgAttachmentData  *attachments,
01046                     nsnull,                             // const struct nsMsgAttachedFile    *preloaded_attachments,
01047                     nsnull,                             // nsMsgSendPart                     *relatedPart,
01048                     m_window,                           // nsIDOMWindowInternal              *parentWindow;
01049                     mProgress,                          // nsIMsgProgress                    *progress,
01050                     sendListener,                       // listener
01051                     mSmtpPassword.get(),
01052                     mOriginalMsgURI,
01053                     mType);
01054 
01055       // Cleanup converted body...
01056       if (newBody)
01057         PR_FREEIF(bodyString);
01058     }
01059     else
01060         rv = NS_ERROR_FAILURE;
01061   }
01062   else
01063     rv = NS_ERROR_NOT_INITIALIZED;
01064 
01065   if (NS_FAILED(rv))
01066     NotifyStateListeners(eComposeProcessDone,rv);
01067 
01068   return rv;
01069 }
01070 
01071 NS_IMETHODIMP nsMsgCompose::SendMsg(MSG_DeliverMode deliverMode, nsIMsgIdentity *identity, const char *accountKey, nsIMsgWindow *aMsgWindow, nsIMsgProgress *progress)
01072 {
01073   nsresult rv = NS_OK;
01074   PRBool entityConversionDone = PR_FALSE;
01075   nsCOMPtr<nsIPrompt> prompt;
01076 
01077   // i'm assuming the compose window is still up at this point...
01078   if (!prompt && m_window)
01079      m_window->GetPrompter(getter_AddRefs(prompt));
01080 
01081   if (m_compFields && !m_composeHTML)
01082   {
01083     // The plain text compose window was used
01084     const char contentType[] = "text/plain";
01085     nsString msgBody;
01086     PRUint32 flags = nsIDocumentEncoder::OutputFormatted;
01087     if (m_editor)
01088     {
01089       // Reset message body previously stored in the compose fields
01090       // There is 2 nsIMsgCompFields::SetBody() functions using a pointer as argument,
01091       // therefore a casting is required.
01092       m_compFields->SetBody((const char *)nsnull);
01093 
01094       const char *charset = m_compFields->GetCharacterSet();
01095       if(UseFormatFlowed(charset))
01096           flags |= nsIDocumentEncoder::OutputFormatFlowed;
01097 
01098       rv = m_editor->OutputToString(NS_LITERAL_STRING("text/plain"), flags, msgBody);
01099     }
01100     else
01101     {
01102       m_compFields->GetBody(msgBody);
01103     }
01104     if (NS_SUCCEEDED(rv) && !msgBody.IsEmpty())
01105     {
01106       // Convert body to mail charset
01107       nsXPIDLCString outCString; 
01108       nsXPIDLCString fallbackCharset;
01109       PRBool isAsciiOnly;
01110       // check if the body text is covered by the current charset.
01111       rv = nsMsgI18NSaveAsCharset(contentType, m_compFields->GetCharacterSet(), 
01112                                   msgBody.get(), getter_Copies(outCString),
01113                                   getter_Copies(fallbackCharset), &isAsciiOnly);
01114       SET_SIMULATED_ERROR(SIMULATED_SEND_ERROR_14, rv, NS_ERROR_UENC_NOMAPPING);
01115       if (m_compFields->GetForceMsgEncoding())
01116         isAsciiOnly = PR_FALSE;
01117       if (NS_SUCCEEDED(rv) && !outCString.IsEmpty())
01118       {
01119         // body contains characters outside the repertoire of the current 
01120         // charset. ask whether to convert to UTF-8 or go back to reset
01121         // charset with a wider repertoire. (bug 233361) (if not mapi blind send)
01122         if (NS_ERROR_UENC_NOMAPPING == rv && m_editor) {
01123           PRBool needToCheckCharset;
01124           m_compFields->GetNeedToCheckCharset(&needToCheckCharset);
01125           if (needToCheckCharset) {
01126             PRInt32 answer = nsMsgAskAboutUncoveredCharacters(prompt);
01127             switch (answer) {
01128               case 0 : // convert to UTF-8
01129                 CopyUTF16toUTF8(msgBody.get(), outCString);
01130                 m_compFields->SetCharacterSet("UTF-8");
01131                 break; 
01132               case 1 : // return to the editor 
01133                 return NS_ERROR_MSG_MULTILINGUAL_SEND;
01134               case 2 : // send anyway
01135                 break;
01136             }
01137           }
01138         }
01139         // re-label to the fallback charset
01140         else if (fallbackCharset)
01141           m_compFields->SetCharacterSet(fallbackCharset.get());
01142         m_compFields->SetBodyIsAsciiOnly(isAsciiOnly);
01143         m_compFields->SetBody(outCString.get());
01144         entityConversionDone = PR_TRUE;
01145       }
01146       else
01147         m_compFields->SetBody(NS_LossyConvertUTF16toASCII(msgBody).get());
01148     }
01149   }
01150 
01151   // Let's open the progress dialog
01152   if (progress)
01153   {
01154     mProgress = progress;
01155 
01156     if (deliverMode != nsIMsgCompDeliverMode::AutoSaveAsDraft)
01157     {
01158       nsAutoString msgSubject;
01159       m_compFields->GetSubject(msgSubject);
01160 
01161       PRBool showProgress = PR_FALSE;
01162       nsCOMPtr<nsIPrefBranch> prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID));
01163       if (prefBranch)
01164       {
01165         prefBranch->GetBoolPref("mailnews.show_send_progress", &showProgress);
01166         if (showProgress)
01167         {
01168           nsCOMPtr<nsIMsgComposeProgressParams> params = do_CreateInstance(NS_MSGCOMPOSEPROGRESSPARAMS_CONTRACTID, &rv);
01169           if (NS_FAILED(rv) || !params)
01170             return NS_ERROR_FAILURE;
01171 
01172           params->SetSubject(msgSubject.get());
01173           params->SetDeliveryMode(deliverMode);
01174 
01175           mProgress->OpenProgressDialog(m_window, aMsgWindow, "chrome://messenger/content/messengercompose/sendProgress.xul", params);
01176           mProgress->GetPrompter(getter_AddRefs(prompt));
01177         }
01178       }
01179     }
01180 
01181     mProgress->OnStateChange(nsnull, nsnull, nsIWebProgressListener::STATE_START, NS_OK);
01182   }
01183 
01184   PRBool attachVCard = PR_FALSE;
01185   if (m_compFields)
01186       m_compFields->GetAttachVCard(&attachVCard);
01187 
01188   if (attachVCard && identity && (deliverMode == nsIMsgCompDeliverMode::Now || deliverMode == nsIMsgCompDeliverMode::Later))
01189   {
01190       nsXPIDLCString escapedVCard;
01191       // make sure, if there is no card, this returns an empty string, or NS_ERROR_FAILURE
01192       rv = identity->GetEscapedVCard(getter_Copies(escapedVCard));
01193 
01194       if (NS_SUCCEEDED(rv) && !escapedVCard.IsEmpty()) 
01195       {
01196           nsCString vCardUrl;
01197           vCardUrl = "data:text/x-vcard;charset=utf-8;base64,";
01198           char *unescapedData = PL_strdup(escapedVCard);
01199           if (!unescapedData)
01200               return NS_ERROR_OUT_OF_MEMORY;
01201           nsUnescape(unescapedData);
01202           char *result = PL_Base64Encode(unescapedData, 0, nsnull);
01203           vCardUrl += result;
01204           PR_Free(result);
01205           PR_Free(unescapedData);
01206 
01207           nsCOMPtr<nsIMsgAttachment> attachment = do_CreateInstance(NS_MSGATTACHMENT_CONTRACTID, &rv);
01208           if (NS_SUCCEEDED(rv) && attachment)
01209           {
01210               // [comment from 4.x]
01211               // Send the vCard out with a filename which distinguishes this user. e.g. jsmith.vcf
01212               // The main reason to do this is for interop with Eudora, which saves off 
01213               // the attachments separately from the message body
01214               nsXPIDLCString userid;
01215               (void)identity->GetEmail(getter_Copies(userid));
01216               PRInt32 index = userid.FindChar('@');
01217               if (index != kNotFound)
01218                   userid.Truncate(index);
01219 
01220               if (userid.IsEmpty()) 
01221                   attachment->SetName(NS_LITERAL_STRING("vcard.vcf"));
01222               else
01223               {
01224                   // Replace any dot with underscore to stop vCards
01225                   // generating false positives with some heuristic scanners
01226                   userid.ReplaceChar('.', '_');
01227                   userid.AppendLiteral(".vcf");
01228                   attachment->SetName(NS_ConvertASCIItoUCS2(userid));
01229               }
01230 
01231               attachment->SetUrl(vCardUrl.get());
01232               m_compFields->AddAttachment(attachment);
01233           }
01234       }
01235   }
01236 
01237   rv = _SendMsg(deliverMode, identity, accountKey, entityConversionDone);
01238   if (NS_FAILED(rv))
01239   {
01240     nsCOMPtr<nsIMsgSendReport> sendReport;
01241     if (mMsgSend)
01242       mMsgSend->GetSendReport(getter_AddRefs(sendReport));
01243     if (sendReport)
01244     {
01245       nsresult theError;
01246       sendReport->DisplayReport(prompt, PR_TRUE, PR_TRUE, &theError);
01247     }
01248     else
01249     {
01250       /* If we come here it's because we got an error before we could intialize a
01251          send report! Let's try our best...
01252       */
01253       switch (deliverMode)
01254       {
01255         case nsIMsgCompDeliverMode::Later:
01256           nsMsgDisplayMessageByID(prompt, NS_MSG_UNABLE_TO_SEND_LATER);
01257           break;
01258         case nsIMsgCompDeliverMode::AutoSaveAsDraft:
01259         case nsIMsgCompDeliverMode::SaveAsDraft:
01260           nsMsgDisplayMessageByID(prompt, NS_MSG_UNABLE_TO_SAVE_DRAFT);
01261           break;
01262         case nsIMsgCompDeliverMode::SaveAsTemplate:
01263           nsMsgDisplayMessageByID(prompt, NS_MSG_UNABLE_TO_SAVE_TEMPLATE);
01264           break;
01265 
01266         default:
01267           nsMsgDisplayMessageByID(prompt, NS_ERROR_SEND_FAILED);
01268           break;
01269       }
01270     }
01271 
01272     if (progress)
01273       progress->CloseProgressDialog(PR_TRUE);
01274   }
01275 
01276   return rv;
01277 }
01278 
01279 // XXX when do we break this ref to the listener?
01280 NS_IMETHODIMP nsMsgCompose::SetRecyclingListener(nsIMsgComposeRecyclingListener *aRecyclingListener)
01281 {
01282   mRecyclingListener = aRecyclingListener;
01283   return NS_OK;
01284 }
01285 
01286 NS_IMETHODIMP nsMsgCompose::GetRecyclingListener(nsIMsgComposeRecyclingListener **aRecyclingListener)
01287 {
01288   NS_ENSURE_ARG_POINTER(aRecyclingListener);
01289   *aRecyclingListener = mRecyclingListener;
01290   NS_IF_ADDREF(*aRecyclingListener);
01291   return NS_OK;
01292 }
01293 
01294 /* attribute boolean recycledWindow; */
01295 NS_IMETHODIMP nsMsgCompose::GetRecycledWindow(PRBool *aRecycledWindow)
01296 {
01297   NS_ENSURE_ARG_POINTER(aRecycledWindow);
01298   *aRecycledWindow = mRecycledWindow;
01299   return NS_OK;
01300 }
01301 NS_IMETHODIMP nsMsgCompose::SetRecycledWindow(PRBool aRecycledWindow)
01302 {
01303   mRecycledWindow = aRecycledWindow;
01304   return NS_OK;
01305 }
01306 
01307 /* attribute boolean deleteDraft */
01308 NS_IMETHODIMP nsMsgCompose::GetDeleteDraft(PRBool *aDeleteDraft)
01309 {
01310   NS_ENSURE_ARG_POINTER(aDeleteDraft);
01311   *aDeleteDraft = mDeleteDraft;
01312   return NS_OK;
01313 }
01314 
01315 NS_IMETHODIMP nsMsgCompose::SetDeleteDraft(PRBool aDeleteDraft)
01316 {
01317   mDeleteDraft = aDeleteDraft;
01318   return NS_OK;
01319 }
01320 
01321 #if !defined(XP_MAC)
01322 PRBool nsMsgCompose::IsLastWindow()
01323 {
01324   nsresult rv;
01325   PRBool more;
01326   nsCOMPtr<nsIWindowMediator> windowMediator =
01327               do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
01328   if (NS_SUCCEEDED(rv))
01329   {
01330     nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
01331     rv = windowMediator->GetEnumerator(nsnull,
01332                getter_AddRefs(windowEnumerator));
01333     if (NS_SUCCEEDED(rv))
01334     {
01335       nsCOMPtr<nsISupports> isupports;
01336 
01337       if (NS_SUCCEEDED(windowEnumerator->GetNext(getter_AddRefs(isupports))))
01338         if (NS_SUCCEEDED(windowEnumerator->HasMoreElements(&more)))
01339           return !more;
01340     }
01341   }
01342   return PR_TRUE;
01343 }
01344 #endif /* XP_MAC */
01345 
01346 NS_IMETHODIMP nsMsgCompose::CloseWindow(PRBool recycleIt)
01347 {
01348   nsresult rv;
01349 
01350   nsCOMPtr<nsIMsgComposeService> composeService = do_GetService(NS_MSGCOMPOSESERVICE_CONTRACTID, &rv);
01351   NS_ENSURE_SUCCESS(rv,rv);
01352   
01353   // unregister the compose object with the compose service
01354   rv = composeService->UnregisterComposeWindow(m_window);
01355   NS_ENSURE_SUCCESS(rv, rv);
01356 
01357 #if !defined(XP_MAC)
01358   recycleIt = recycleIt && !IsLastWindow();
01359 #endif /* XP_MAC */
01360   if (recycleIt)
01361   {
01362     rv = composeService->CacheWindow(m_window, m_composeHTML, mRecyclingListener);
01363     if (NS_SUCCEEDED(rv))
01364     {
01365       nsCOMPtr<nsIHTMLEditor> htmlEditor (do_QueryInterface(m_editor));
01366       NS_ASSERTION(htmlEditor, "no editor");
01367       if (htmlEditor)
01368       {
01369         // XXX clear undo txn manager?
01370 
01371         rv = m_editor->EnableUndo(PR_FALSE);
01372         NS_ENSURE_SUCCESS(rv,rv);
01373 
01374         rv = htmlEditor->RebuildDocumentFromSource(EmptyString());
01375         NS_ENSURE_SUCCESS(rv,rv);
01376 
01377         rv = m_editor->EnableUndo(PR_TRUE);
01378         NS_ENSURE_SUCCESS(rv,rv);
01379 
01380         SetBodyModified(PR_FALSE);
01381       }
01382       if (mRecyclingListener)
01383       {
01384         mRecyclingListener->OnClose();
01385 
01394         nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(m_window));
01395         if (sgo)
01396         {
01397           nsIScriptContext *scriptContext = sgo->GetContext();
01398           if (scriptContext)
01399             scriptContext->GC();
01400         }
01401       }
01402       return NS_OK;
01403     }
01404   }
01405 
01406   //We are going away for real, we need to do some clean up first
01407   if (m_baseWindow)
01408   {
01409     if (m_editor)
01410     {
01411         /* The editor will be destroyed during yje close window.
01412          * Set it to null to be sure we wont uses it anymore
01413          */
01414       m_editor = nsnull;
01415     }
01416     nsIBaseWindow * window = m_baseWindow;
01417     m_baseWindow = nsnull;
01418     rv = window->Destroy();
01419   }
01420 
01421   return rv;
01422 }
01423 
01424 nsresult nsMsgCompose::Abort()
01425 {
01426   if (mMsgSend)
01427     mMsgSend->Abort();
01428 
01429   if (mProgress)
01430     mProgress->CloseProgressDialog(PR_TRUE);
01431 
01432   return NS_OK;
01433 }
01434 
01435 nsresult nsMsgCompose::GetEditor(nsIEditor * *aEditor)
01436 { 
01437   NS_IF_ADDREF(*aEditor = m_editor);
01438   return NS_OK;
01439 } 
01440 
01441 nsresult nsMsgCompose::ClearEditor()
01442 {
01443   m_editor = nsnull;
01444   return NS_OK;
01445 }
01446 
01447 // This used to be called BEFORE editor was created 
01448 //  (it did the loadUrl that triggered editor creation)
01449 // It is called from JS after editor creation
01450 //  (loadUrl is done in JS)
01451 NS_IMETHODIMP nsMsgCompose::InitEditor(nsIEditor* aEditor, nsIDOMWindow* aContentWindow)
01452 {
01453   NS_ENSURE_ARG_POINTER(aEditor);
01454   NS_ENSURE_ARG_POINTER(aContentWindow);
01455 
01456   m_editor = aEditor;
01457 
01458   // Set the charset
01459   const nsDependentCString msgCharSet(m_compFields->GetCharacterSet());
01460   m_editor->SetDocumentCharacterSet(msgCharSet);
01461 
01462   nsCOMPtr<nsIScriptGlobalObject> globalObj = do_QueryInterface(aContentWindow);
01463 
01464   nsIDocShell *docShell = globalObj->GetDocShell();
01465   NS_ENSURE_TRUE(docShell, NS_ERROR_UNEXPECTED);
01466 
01467   nsCOMPtr<nsIContentViewer> childCV;
01468   NS_ENSURE_SUCCESS(docShell->GetContentViewer(getter_AddRefs(childCV)), NS_ERROR_FAILURE);
01469   if (childCV)
01470   {
01471     nsCOMPtr<nsIMarkupDocumentViewer> markupCV = do_QueryInterface(childCV);
01472     if (markupCV) {
01473       NS_ENSURE_SUCCESS(markupCV->SetDefaultCharacterSet(msgCharSet), NS_ERROR_FAILURE);
01474       NS_ENSURE_SUCCESS(markupCV->SetForceCharacterSet(msgCharSet), NS_ERROR_FAILURE);
01475     }
01476   }
01477 
01478   // This is what used to be done in mDocumentListener, 
01479   //   nsMsgDocumentStateListener::NotifyDocumentCreated()
01480   PRBool quotingToFollow = PR_FALSE;
01481   GetQuotingToFollow(&quotingToFollow);
01482   if (quotingToFollow)
01483     return BuildQuotedMessageAndSignature();
01484   else
01485   {
01486     NotifyStateListeners(eComposeFieldsReady, NS_OK);
01487     nsresult rv = BuildBodyMessageAndSignature();
01488     NotifyStateListeners(eComposeBodyReady, NS_OK);
01489     return rv;
01490   }
01491 } 
01492 
01493 nsresult nsMsgCompose::GetBodyModified(PRBool * modified)
01494 {
01495   nsresult rv;
01496 
01497   if (! modified)
01498     return NS_ERROR_NULL_POINTER;
01499 
01500   *modified = PR_TRUE;
01501 
01502   if (m_editor)
01503   {
01504     rv = m_editor->GetDocumentModified(modified);
01505     if (NS_FAILED(rv))
01506       *modified = PR_TRUE;
01507   }
01508 
01509   return NS_OK;   
01510 }
01511 
01512 nsresult nsMsgCompose::SetBodyModified(PRBool modified)
01513 {
01514   nsresult  rv = NS_OK;
01515 
01516   if (m_editor)
01517   {
01518     if (modified)
01519     {
01520       PRInt32  modCount = 0;
01521       m_editor->GetModificationCount(&modCount);
01522       if (modCount == 0)
01523         m_editor->IncrementModificationCount(1);
01524     }
01525     else
01526       m_editor->ResetModificationCount();
01527   }
01528 
01529   return rv;  
01530 }
01531 
01532 NS_IMETHODIMP 
01533 nsMsgCompose::GetDomWindow(nsIDOMWindowInternal * *aDomWindow)
01534 {
01535   NS_IF_ADDREF(*aDomWindow = m_window);
01536   return NS_OK;
01537 }
01538 
01539 nsresult nsMsgCompose::GetCompFields(nsIMsgCompFields * *aCompFields)
01540 {
01541   *aCompFields = (nsIMsgCompFields*)m_compFields;
01542   NS_IF_ADDREF(*aCompFields);
01543   return NS_OK;
01544 }
01545 
01546 NS_IMETHODIMP nsMsgCompose::GetComposeHTML(PRBool *aComposeHTML)
01547 {
01548   *aComposeHTML = m_composeHTML;
01549   return NS_OK;
01550 }
01551 
01552 nsresult nsMsgCompose::GetWrapLength(PRInt32 *aWrapLength)
01553 {
01554   nsresult rv;
01555   nsCOMPtr<nsIPrefBranch> prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
01556   if (NS_FAILED(rv)) return rv;
01557 
01558   return prefBranch->GetIntPref("mailnews.wraplength", aWrapLength);
01559 }
01560 
01561 nsresult nsMsgCompose::CreateMessage(const char * originalMsgURI,
01562                                      MSG_ComposeType type,
01563                                      nsIMsgCompFields * compFields)
01564 {
01565   nsresult rv = NS_OK;
01566 
01567   mType = type;
01568   mDraftDisposition = nsIMsgFolder::nsMsgDispositionState_None;
01569 
01570   mDeleteDraft = (type == nsIMsgCompType::Draft);
01571   nsCAutoString msgUri(originalMsgURI);
01572   // check if we're dealing with an opened .eml file msg
01573   PRBool fileUrl = StringBeginsWith(msgUri, NS_LITERAL_CSTRING("file:"));
01574   if (fileUrl)
01575   {
01576     // strip out ?type=application/x-message-display because it confuses libmime
01577     PRInt32 typeIndex = msgUri.Find("?type=application/x-message-display");
01578     if (typeIndex != kNotFound)
01579     {
01580       msgUri.Cut(typeIndex, sizeof("?type=application/x-message-display") - 1);
01581       // we also need to replace the next '&' with '?'
01582       if (msgUri.CharAt(typeIndex) == '&')
01583         msgUri.SetCharAt('?', typeIndex);
01584       originalMsgURI = msgUri.get();
01585     }
01586   }
01587   else // check if we're dealing with a displayed message/rfc822 attachment
01588   {
01589     PRInt32 typeIndex = msgUri.Find("&type=application/x-message-display");
01590     if (typeIndex != kNotFound)
01591     {
01592       msgUri.Cut(typeIndex, sizeof("&type=application/x-message-display") - 1);
01593       // nsURLFetcher will check for "realtype=message/rfc822" and will set the
01594       // content type to message/rfc822 in the forwarded message.
01595       msgUri.Append("&realtype=message/rfc822");
01596       originalMsgURI = msgUri.get();
01597     }
01598   }
01599   if (compFields)
01600   {
01601     NS_IF_RELEASE(m_compFields);
01602     m_compFields = NS_REINTERPRET_CAST(nsMsgCompFields*, compFields);
01603     NS_ADDREF(m_compFields);
01604   }
01605   else
01606   {
01607     NS_NEWXPCOM(m_compFields, nsMsgCompFields);
01608     if (m_compFields)
01609       NS_ADDREF(m_compFields);
01610     else
01611       return NS_ERROR_OUT_OF_MEMORY;
01612   }
01613 
01614   if (m_identity)
01615   {
01616       nsXPIDLCString::const_iterator start, end;
01617 
01618       /* Setup reply-to field */
01619       nsXPIDLCString replyTo;
01620       m_identity->GetReplyTo(getter_Copies(replyTo));
01621       if (replyTo && *(const char *)replyTo)
01622       {
01623         nsXPIDLCString replyToStr;
01624         replyToStr.Assign(m_compFields->GetReplyTo());
01625 
01626         replyToStr.BeginReading(start);
01627         replyToStr.EndReading(end);
01628 
01629         if (FindInReadable(replyTo, start, end) == PR_FALSE) {
01630           if (replyToStr.Length() > 0)
01631             replyToStr.Append(',');
01632           replyToStr.Append(replyTo);
01633         }
01634         m_compFields->SetReplyTo(replyToStr.get());
01635       }
01636 
01637       /* Setup bcc field */
01638       PRBool doBcc;
01639       m_identity->GetDoBcc(&doBcc);
01640       if (doBcc) 
01641       {
01642         nsXPIDLCString bccStr;
01643         bccStr.Assign(m_compFields->GetBcc());
01644 
01645           bccStr.BeginReading(start);
01646           bccStr.EndReading(end);
01647 
01648           nsXPIDLCString bccList;
01649           m_identity->GetDoBccList(getter_Copies(bccList));
01650           if (FindInReadable(bccList, start, end) == PR_FALSE) {
01651             if (bccStr.Length() > 0)
01652               bccStr.Append(',');
01653             bccStr.Append(bccList);
01654           }
01655 
01656         m_compFields->SetBcc(bccStr.get());
01657       }
01658   }
01659 
01660   if (mType == nsIMsgCompType::Draft)
01661   {
01662     nsXPIDLCString curDraftIdURL;
01663 
01664     rv = m_compFields->GetDraftId(getter_Copies(curDraftIdURL));
01665     NS_ASSERTION((NS_SUCCEEDED(rv) && (curDraftIdURL)), "RemoveCurrentDraftMessage can't get draft id");
01666 
01667     // Skip if no draft id (probably a new draft msg).
01668     if (NS_SUCCEEDED(rv) && curDraftIdURL.get() && strlen(curDraftIdURL.get()))
01669     { 
01670       nsCOMPtr <nsIMsgDBHdr> msgDBHdr;
01671       rv = GetMsgDBHdrFromURI(curDraftIdURL, getter_AddRefs(msgDBHdr));
01672       NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveCurrentDraftMessage can't get msg header DB interface pointer.");
01673       if (msgDBHdr)
01674       {
01675         nsXPIDLCString queuedDisposition;
01676         msgDBHdr->GetStringProperty(QUEUED_DISPOSITION_PROPERTY, getter_Copies(queuedDisposition));
01677         nsXPIDLCString originalMsgURIs;
01678         msgDBHdr->GetStringProperty(ORIG_URI_PROPERTY, getter_Copies(originalMsgURIs));
01679         mOriginalMsgURI = originalMsgURIs;
01680         if (!queuedDisposition.IsEmpty())
01681         {
01682           if (queuedDisposition.Equals("replied"))
01683              mDraftDisposition = nsIMsgFolder::nsMsgDispositionState_Replied;
01684           else if (queuedDisposition.Equals("forward"))
01685              mDraftDisposition = nsIMsgFolder::nsMsgDispositionState_Forwarded;
01686         }
01687       }
01688     }
01689   }
01690 
01691   // If we don't have an original message URI, nothing else to do...
01692   if (!originalMsgURI || *originalMsgURI == 0)
01693     return NS_OK;
01694 
01695   // store the original message URI so we can extract it after we send the message to properly
01696   // mark any disposition flags like replied or forwarded on the message.
01697   mOriginalMsgURI = originalMsgURI;
01698 
01699   // If we are forwarding inline, mime did already setup the compose fields therefore we should stop now
01700   if (type == nsIMsgCompType::ForwardInline )
01701   {
01702     // use send_default_charset if reply_in_default_charset is on.
01703     nsCOMPtr<nsIPrefBranch> prefs (do_GetService(NS_PREFSERVICE_CONTRACTID));
01704     if (prefs)
01705     {
01706       PRBool replyInDefault = PR_FALSE;
01707       prefs->GetBoolPref("mailnews.reply_in_default_charset",
01708                          &replyInDefault);
01709       if (replyInDefault)
01710       {
01711         nsXPIDLString str;
01712         nsXPIDLCString charset;
01713         NS_GetLocalizedUnicharPreferenceWithDefault(prefs, "mailnews.send_default_charset",
01714                                                     EmptyString(), str);
01715         if (!str.IsEmpty())
01716         {
01717           LossyCopyUTF16toASCII(str, charset);
01718           m_compFields->SetCharacterSet(charset);
01719         }
01720       }
01721     }
01722     return rv;
01723   }
01724 
01725   char *uriList = PL_strdup(originalMsgURI);
01726   if (!uriList)
01727     return NS_ERROR_OUT_OF_MEMORY;
01728 
01729   nsCOMPtr<nsIMimeConverter> mimeConverter = do_GetService(NS_MIME_CONVERTER_CONTRACTID);
01730 
01731   nsXPIDLCString charset;
01732   // use a charset of the original message
01733   nsXPIDLCString mailCharset;
01734   PRBool charsetOverride = PR_FALSE;
01735   GetTopmostMsgWindowCharacterSet(mailCharset, &mCharsetOverride);
01736   if (!mailCharset.IsEmpty())
01737   {
01738     charset = mailCharset;
01739     charsetOverride = mCharsetOverride;
01740   }
01741 #ifdef DEBUG_jungshik
01742   printf ("charset=%s\n", charset.get());
01743   printf ("charsetOverride=%d\n", charsetOverride);
01744 #endif
01745 
01746   // although the charset in which to _send_ the message might change,
01747   // the original message will be parsed for quoting using the charset it is
01748   // now displayed with
01749   mQuoteCharset = charset;
01750 
01751   PRBool isFirstPass = PR_TRUE;
01752   char *uri = uriList;
01753   char *nextUri;
01754   do 
01755   {
01756     nextUri = strstr(uri, "://");
01757     if (nextUri)
01758     {
01759       // look for next ://, and then back up to previous ','
01760       nextUri = strstr(nextUri + 1, "://");
01761       if (nextUri)
01762       {
01763         *nextUri = '\0';
01764         char *saveNextUri = nextUri;
01765         nextUri = strrchr(uri, ',');
01766         if (nextUri)
01767           *nextUri = '\0';
01768         *saveNextUri = ':';
01769       }
01770     }
01771 
01772     nsCOMPtr <nsIMsgDBHdr> msgHdr;
01773     if (mOrigMsgHdr)
01774       msgHdr = mOrigMsgHdr;
01775     else
01776     {
01777       rv = GetMsgDBHdrFromURI(uri, getter_AddRefs(msgHdr));
01778       NS_ENSURE_SUCCESS(rv,rv);
01779     }
01780     if (msgHdr)
01781     {
01782       nsXPIDLString subject;
01783       nsXPIDLCString decodedCString;
01784 
01785       if (!charsetOverride && charset.IsEmpty())
01786       {
01787         rv = msgHdr->GetCharset(getter_Copies(charset));
01788         if (NS_FAILED(rv)) return rv;
01789       }
01790 
01791       // save the charset of a message being replied to because
01792       // we need to use it when decoding RFC-2047-encoded author name
01793       // with |charsetOverride == PR_TRUE|
01794       nsCAutoString originCharset(charset); 
01795 
01796       // use send_default_charset if reply_in_default_charset is on.
01797       nsCOMPtr<nsIPrefBranch> prefs (do_GetService(NS_PREFSERVICE_CONTRACTID));
01798       if (prefs)
01799       {
01800         PRBool replyInDefault = PR_FALSE;
01801         prefs->GetBoolPref("mailnews.reply_in_default_charset",
01802                            &replyInDefault);
01803         if (replyInDefault) {
01804           nsXPIDLString str;
01805           NS_GetLocalizedUnicharPreferenceWithDefault(prefs, "mailnews.send_default_charset",
01806                                                       EmptyString(), str);
01807           if (!str.IsEmpty())
01808             LossyCopyUTF16toASCII(str, charset);
01809         }
01810       }
01811 
01812       // No matter what, we should block x-windows-949 (our internal name)
01813       // from being used for outgoing emails (bug 234958)
01814       if (charset.Equals("x-windows-949",
01815             nsCaseInsensitiveCStringComparator()))
01816         charset = "EUC-KR";
01817 
01818       // get an original charset, used for a label, UTF-8 is used for the internal processing
01819       if (isFirstPass && !charset.IsEmpty())
01820         m_compFields->SetCharacterSet(charset);
01821 
01822       nsXPIDLCString subjectCStr;
01823       (void) msgHdr->GetSubject(getter_Copies(subjectCStr));
01824       rv = mimeConverter->DecodeMimeHeader(subjectCStr,
01825                 getter_Copies(decodedCString),
01826                 originCharset.get(), charsetOverride);
01827       if (NS_FAILED(rv)) return rv;
01828 
01829       CopyUTF8toUTF16(decodedCString, subject);
01830 
01831       // Check if (was: is present in the subject
01832       nsAString::const_iterator wasStart, wasEnd;
01833       subject.BeginReading(wasStart);
01834       subject.EndReading(wasEnd);
01835       PRBool wasFound = RFindInReadable(NS_LITERAL_STRING(" (was:"), wasStart, wasEnd);
01836       PRBool strip = PR_TRUE;
01837 
01838       if (wasFound) {
01839         // Check the number of references, to check if was: should be stripped
01840         // First, assume that it should be stripped; the variable will be set to
01841         // false later if stripping should not happen.
01842         PRUint16 numRef;
01843         msgHdr->GetNumReferences(&numRef);
01844         if (numRef) {
01845           // If there are references, look for the first message in the thread
01846           // firstly, get the database via the folder
01847           nsCOMPtr<nsIMsgFolder> folder;
01848           msgHdr->GetFolder(getter_AddRefs(folder));
01849           if (folder) {
01850             nsCOMPtr<nsIMsgDatabase> db;
01851             folder->GetMsgDatabase(nsnull, getter_AddRefs(db));
01852 
01853             if (db) {
01854               nsCAutoString reference;
01855               msgHdr->GetStringReference(0, reference);
01856 
01857               nsCOMPtr<nsIMsgDBHdr> refHdr;
01858               db->GetMsgHdrForMessageID(reference.get(), getter_AddRefs(refHdr));
01859 
01860               if (refHdr) {
01861                 nsXPIDLCString refSubject;
01862                 rv = refHdr->GetSubject(getter_Copies(refSubject));
01863                 if (NS_SUCCEEDED(rv)) {
01864                   nsACString::const_iterator start, end;
01865                   refSubject.BeginReading(start);
01866                   refSubject.EndReading(end);
01867                   if (FindInReadable(NS_LITERAL_CSTRING(" (was:"), start, end))
01868                     strip = PR_FALSE;
01869                 }
01870               }
01871             }
01872           }
01873         }
01874         else
01875           strip = PR_FALSE;
01876       }
01877 
01878       if (strip && wasFound) {
01879         // Strip off the "(was: old subject)" part
01880         nsAString::const_iterator start;
01881         subject.BeginReading(start);
01882         subject.Assign(Substring(start, wasStart));
01883       }
01884 
01885       switch (type)
01886       {
01887         default: break;
01888         case nsIMsgCompType::Reply :
01889         case nsIMsgCompType::ReplyAll:
01890         case nsIMsgCompType::ReplyToGroup:
01891         case nsIMsgCompType::ReplyToSender:
01892         case nsIMsgCompType::ReplyToSenderAndGroup:
01893           {
01894             if (!isFirstPass)       // safeguard, just in case...
01895             {
01896               PR_Free(uriList);
01897               return rv;
01898             }
01899             mQuotingToFollow = PR_TRUE;
01900 
01901             subject.Insert(NS_LITERAL_STRING("Re: "), 0);
01902             m_compFields->SetSubject(subject);
01903 
01904             nsXPIDLCString author, authorEmailAddress;
01905             msgHdr->GetAuthor(getter_Copies(author));
01906             nsCOMPtr<nsIMsgHeaderParser> parser (do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID));
01907             if (parser) {
01908               // convert to UTF8 before passing to MakeFullAddress
01909               rv = parser->ExtractHeaderAddressMailboxes(nsnull, author.get(), 
01910                                                             getter_Copies(authorEmailAddress));
01911             }
01912 
01913             PRBool replyToSelfCheckAll = PR_FALSE;
01914             nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
01915             if (NS_SUCCEEDED(rv)) 
01916               prefBranch->GetBoolPref("mailnews.reply_to_self_check_all_ident", &replyToSelfCheckAll);
01917 
01918             nsCOMPtr<nsIMsgAccountManager> accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
01919             NS_ENSURE_SUCCESS(rv,rv);
01920 
01921             nsCOMPtr<nsISupportsArray> identities;
01922             nsXPIDLCString accountKey;
01923             msgHdr->GetAccountKey(getter_Copies(accountKey));
01924             if(replyToSelfCheckAll)
01925             {
01926               // check all avaliable identities if the pref was set
01927               accountManager->GetAllIdentities(getter_AddRefs(identities));
01928             }
01929             else if (!accountKey.IsEmpty())
01930             {
01931                // check headers to see which account the message came in from (only works for pop3)
01932               nsCOMPtr<nsIMsgAccount> account;
01933               accountManager->GetAccount(accountKey, getter_AddRefs(account));
01934 
01935               if(account)
01936                 account->GetIdentities(getter_AddRefs(identities));
01937             } 
01938             else 
01939             {
01940               // check identities only for the server of the folder that the message is in 
01941               nsCOMPtr <nsIMsgFolder> msgFolder;
01942               rv = msgHdr->GetFolder(getter_AddRefs(msgFolder));
01943 
01944               if (NS_SUCCEEDED(rv) && msgFolder){ 
01945                 nsCOMPtr<nsIMsgIncomingServer> nsIMsgIncomingServer;
01946                 rv = msgFolder->GetServer(getter_AddRefs(nsIMsgIncomingServer));
01947 
01948                 if(NS_SUCCEEDED(rv) && nsIMsgIncomingServer) 
01949                   accountManager->GetIdentitiesForServer(nsIMsgIncomingServer, getter_AddRefs(identities));
01950               }
01951             }
01952 
01953             PRBool isReplyToOwnMsg = PR_FALSE;
01954             if(identities)
01955             {
01956               // go through the identities to see if any of them is the author of the email
01957               nsCOMPtr<nsIMsgIdentity> lookupIdentity;
01958 
01959               PRUint32 count = 0;
01960               identities->Count(&count);
01961 
01962               for (PRUint32 i = 0; i < count; i++) 
01963               {
01964                 rv = identities->QueryElementAt(i, NS_GET_IID(nsIMsgIdentity),
01965                                           getter_AddRefs(lookupIdentity));
01966                 if (NS_FAILED(rv))
01967                   continue;
01968 
01969                 nsXPIDLCString curIdentityEmail;
01970                 lookupIdentity->GetEmail(getter_Copies(curIdentityEmail));
01971                 if (curIdentityEmail.Equals(authorEmailAddress))
01972                 {
01973                   isReplyToOwnMsg = PR_TRUE;
01974                   break;
01975                 }
01976               }
01977             }
01978 
01979             nsXPIDLCString toField;
01980             if (isReplyToOwnMsg)
01981               msgHdr->GetRecipients(getter_Copies(toField));
01982             else
01983               toField.Assign(author);
01984 
01985             rv = mimeConverter->DecodeMimeHeader(toField,
01986                 getter_Copies(decodedCString),
01987                 originCharset.get(), charsetOverride);
01988             if (NS_SUCCEEDED(rv) && decodedCString)
01989               m_compFields->SetTo(decodedCString);
01990             else
01991               m_compFields->SetTo(toField);
01992 
01993             // Setup quoting callbacks for later...
01994             mWhatHolder = 1;
01995             break;
01996           }
01997         case nsIMsgCompType::ForwardAsAttachment:
01998           {
01999             PRUint32 flags;
02000 
02001             msgHdr->GetFlags(&flags);
02002             if (flags & MSG_FLAG_HAS_RE)
02003               subject.Insert(NS_LITERAL_STRING("Re: "), 0);
02004 
02005             // Setup quoting callbacks for later...
02006             mQuotingToFollow = PR_FALSE;  //We don't need to quote the original message.
02007             nsCOMPtr<nsIMsgAttachment> attachment = do_CreateInstance(NS_MSGATTACHMENT_CONTRACTID, &rv);
02008             if (NS_SUCCEEDED(rv) && attachment)
02009             {
02010               attachment->SetName(subject + NS_LITERAL_STRING(".eml"));
02011               attachment->SetUrl(uri);
02012               m_compFields->AddAttachment(attachment);
02013             }
02014 
02015             if (isFirstPass)
02016             {
02017               subject.Insert(NS_LITERAL_STRING("[Fwd: ").get(), 0);
02018               subject.Append(NS_LITERAL_STRING("]").get());
02019               m_compFields->SetSubject(subject); 
02020             }
02021             break;
02022           }
02023       }
02024     }
02025     isFirstPass = PR_FALSE;
02026     uri = nextUri + 1;
02027   }
02028   while (nextUri);
02029   PR_Free(uriList);
02030   return rv;
02031 }
02032 
02033 NS_IMETHODIMP nsMsgCompose::GetProgress(nsIMsgProgress **_retval)
02034 {
02035   NS_ENSURE_ARG_POINTER(_retval);
02036   *_retval = mProgress;
02037   NS_IF_ADDREF(*_retval);
02038   return NS_OK;
02039 }
02040 
02041 NS_IMETHODIMP nsMsgCompose::GetMessageSend(nsIMsgSend **_retval)
02042 {
02043   NS_ENSURE_ARG_POINTER(_retval);
02044   *_retval = mMsgSend;
02045   NS_IF_ADDREF(*_retval);
02046   return NS_OK;
02047 }
02048 
02049 NS_IMETHODIMP nsMsgCompose::GetExternalSendListener(nsIMsgSendListener **_retval)
02050 {
02051   NS_ENSURE_ARG_POINTER(_retval);
02052   *_retval = mExternalSendListener;
02053   NS_IF_ADDREF(*_retval);
02054   return NS_OK;
02055 }
02056 
02057 NS_IMETHODIMP nsMsgCompose::SetCiteReference(nsString citeReference)
02058 {
02059   mCiteReference = citeReference;
02060   return NS_OK;
02061 }
02062 
02063 NS_IMETHODIMP nsMsgCompose::SetSavedFolderURI(const char *folderURI)
02064 {
02065   m_folderName = folderURI;
02066   return NS_OK;
02067 }
02068 
02069 NS_IMETHODIMP nsMsgCompose::GetSavedFolderURI(char ** folderURI)
02070 {
02071   NS_ENSURE_ARG_POINTER(folderURI);
02072   *folderURI = ToNewCString(m_folderName);
02073   return (*folderURI) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
02074 }
02075 
02076 NS_IMETHODIMP nsMsgCompose::GetOriginalMsgURI(char ** originalMsgURI)
02077 {
02078   NS_ENSURE_ARG_POINTER(originalMsgURI);
02079   *originalMsgURI = ToNewCString(mOriginalMsgURI);
02080   return (*originalMsgURI) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
02081 }
02082 
02084 // THIS IS THE CLASS THAT IS THE STREAM CONSUMER OF THE HTML OUPUT
02085 // FROM LIBMIME. THIS IS FOR QUOTING
02087 QuotingOutputStreamListener::~QuotingOutputStreamListener() 
02088 {
02089   if (mUnicodeConversionBuffer)
02090     nsMemory::Free(mUnicodeConversionBuffer);
02091 }
02092 
02093 QuotingOutputStreamListener::QuotingOutputStreamListener(const char * originalMsgURI,
02094                                                          nsIMsgDBHdr *originalMsgHdr,
02095                                                          PRBool quoteHeaders,
02096                                                          PRBool headersOnly,
02097                                                          nsIMsgIdentity *identity,
02098                                                          const char *charset,
02099                                                          PRBool charetOverride, 
02100                                                          PRBool quoteOriginal)
02101 { 
02102   nsresult rv;
02103   mQuoteHeaders = quoteHeaders;
02104   mHeadersOnly = headersOnly;
02105   mIdentity = identity;
02106   mUnicodeBufferCharacterLength = 0;
02107   mUnicodeConversionBuffer = nsnull;
02108   mQuoteOriginal = quoteOriginal;
02109 
02110   if (! mHeadersOnly)
02111   {
02112     nsXPIDLString replyHeaderOriginalmessage;
02113     // For the built message body...
02114     if (originalMsgHdr && !quoteHeaders)
02115     {
02116       // Setup the cite information....
02117       nsXPIDLCString myGetter;
02118       if (NS_SUCCEEDED(originalMsgHdr->GetMessageId(getter_Copies(myGetter))))
02119       {
02120         if (!myGetter.IsEmpty())
02121         {
02122           nsCAutoString buf;
02123           mCiteReference.AssignLiteral("mid:");
02124           AppendASCIItoUTF16(NS_EscapeURL(myGetter, esc_FileBaseName | esc_Forced, buf),
02125                              mCiteReference);
02126         }
02127       }
02128 
02129       PRInt32 reply_on_top = 0;
02130       mIdentity->GetReplyOnTop(&reply_on_top);
02131       if (reply_on_top == 1)
02132         mCitePrefix.AppendLiteral("\n\n");
02133 
02134 
02135       PRBool header, headerDate;
02136       PRInt32 replyHeaderType;
02137       nsAutoString replyHeaderLocale;
02138       nsXPIDLString replyHeaderAuthorwrote;
02139       nsXPIDLString replyHeaderOndate;
02140       nsAutoString replyHeaderSeparator;
02141       nsAutoString replyHeaderColon;
02142 
02143       // Get header type, locale and strings from pref.
02144       GetReplyHeaderInfo(&replyHeaderType, 
02145                          replyHeaderLocale,
02146                          replyHeaderAuthorwrote,
02147                          replyHeaderOndate,
02148                          replyHeaderSeparator,
02149                          replyHeaderColon,
02150                          replyHeaderOriginalmessage);
02151 
02152       switch (replyHeaderType)
02153       {
02154         case 0: // No reply header at all
02155           header=PR_FALSE;
02156           headerDate=PR_FALSE;
02157           break;
02158 
02159         case 2: // Insert both the original author and date in the reply header (date followed by author)
02160         case 3: // Insert both the original author and date in the reply header (author followed by date)
02161           header=PR_TRUE;
02162           headerDate=PR_TRUE;
02163           break;
02164 
02165         case 4: // XXX implement user specified header
02166         case 1: // Default is to only view the author. We will reconsider this decision when bug 75377 is fixed.
02167         default:
02168           header=PR_TRUE;
02169           headerDate=PR_FALSE;
02170           break;
02171       }
02172 
02173       nsAutoString citePrefixDate;
02174       nsAutoString citePrefixAuthor;
02175 
02176       if (header)
02177       {
02178         if (headerDate)
02179         {
02180           nsCOMPtr<nsIDateTimeFormat> dateFormatter = do_CreateInstance(kDateTimeFormatCID, &rv);
02181 
02182           if (NS_SUCCEEDED(rv)) 
02183           {  
02184             PRTime originalMsgDate;
02185             rv = originalMsgHdr->GetDate(&originalMsgDate); 
02186 
02187             if (NS_SUCCEEDED(rv)) 
02188             {
02189               nsAutoString formattedDateString;
02190               nsCOMPtr<nsILocale> locale;
02191               nsCOMPtr<nsILocaleService> localeService(do_GetService(NS_LOCALESERVICE_CONTRACTID));
02192 
02193               // Format date using "mailnews.reply_header_locale", if empty then use application default locale.
02194               if (!replyHeaderLocale.IsEmpty())
02195                 rv = localeService->NewLocale(replyHeaderLocale, getter_AddRefs(locale));
02196 
02197               if (NS_SUCCEEDED(rv))
02198               {
02199                 rv = dateFormatter->FormatPRTime(locale,
02200                                                  kDateFormatShort,
02201                                                  kTimeFormatNoSeconds,
02202                                                  originalMsgDate,
02203                                                  formattedDateString);
02204 
02205                 if (NS_SUCCEEDED(rv)) 
02206                 {
02207                   // take care "On %s"
02208                   PRUnichar *formatedString = nsnull;
02209                   formatedString = nsTextFormatter::smprintf(replyHeaderOndate.get(), 
02210                                                              NS_ConvertUCS2toUTF8(formattedDateString.get()).get());
02211                   if (formatedString) 
02212                   {
02213                     citePrefixDate.Assign(formatedString);
02214                     nsTextFormatter::smprintf_free(formatedString);
02215                   }
02216                 }
02217               }
02218             }
02219           }
02220         }
02221 
02222 
02223       nsXPIDLCString author;
02224       rv = originalMsgHdr->GetAuthor(getter_Copies(author));
02225 
02226       if (NS_SUCCEEDED(rv))
02227       {
02228         nsXPIDLCString decodedString;
02229         mMimeConverter = do_GetService(NS_MIME_CONVERTER_CONTRACTID);
02230         // Decode header, the result string is null if the input is non MIME encoded ASCII.
02231         if (mMimeConverter)
02232           mMimeConverter->DecodeMimeHeader(author.get(), getter_Copies(decodedString), charset, charetOverride);
02233 
02234         nsCOMPtr<nsIMsgHeaderParser> parser (do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID));
02235 
02236         if (parser)
02237         {
02238           nsXPIDLCString authorName;
02239           rv = parser->ExtractHeaderAddressName("UTF-8", decodedString ? decodedString.get() : author.get(),
02240                                                 getter_Copies(authorName));
02241           // take care "%s wrote"
02242           PRUnichar *formatedString = nsnull;
02243           if (NS_SUCCEEDED(rv) && authorName)
02244             formatedString = nsTextFormatter::smprintf(replyHeaderAuthorwrote.get(), authorName.get());
02245           else
02246             formatedString = nsTextFormatter::smprintf(replyHeaderAuthorwrote.get(), author.get());
02247           if (formatedString) 
02248           {
02249             citePrefixAuthor.Assign(formatedString);
02250             nsTextFormatter::smprintf_free(formatedString);
02251           }
02252         }
02253 
02254 
02255         }
02256         if (replyHeaderType == 2)
02257         {
02258           mCitePrefix.Append(citePrefixDate);
02259           mCitePrefix.Append(replyHeaderSeparator);
02260           mCitePrefix.Append(citePrefixAuthor);
02261         }
02262         else if (replyHeaderType == 3) 
02263         {
02264           mCitePrefix.Append(citePrefixAuthor);
02265           mCitePrefix.Append(replyHeaderSeparator);
02266           mCitePrefix.Append(citePrefixDate);
02267         }
02268         else
02269           mCitePrefix.Append(citePrefixAuthor);
02270         mCitePrefix.Append(replyHeaderColon);
02271       }
02272     }
02273 
02274     if (mCitePrefix.IsEmpty())
02275     {
02276       if (replyHeaderOriginalmessage.IsEmpty())
02277       {
02278         // This is not likely to happen but load the string if it's not done already.
02279         PRInt32 replyHeaderType;
02280         nsAutoString replyHeaderLocale;
02281         nsXPIDLString replyHeaderAuthorwrote;
02282         nsXPIDLString replyHeaderOndate;
02283         nsAutoString replyHeaderSeparator;
02284         nsAutoString replyHeaderColon;
02285         GetReplyHeaderInfo(&replyHeaderType, 
02286                            replyHeaderLocale,
02287                            replyHeaderAuthorwrote,
02288                            replyHeaderOndate,
02289                            replyHeaderSeparator,
02290                            replyHeaderColon,
02291                            replyHeaderOriginalmessage);
02292       }
02293       mCitePrefix.AppendLiteral("\n\n");
02294       mCitePrefix.Append(replyHeaderOriginalmessage);
02295       mCitePrefix.AppendLiteral("\n");
02296     }
02297   }
02298 }
02299 
02305 nsresult
02306 QuotingOutputStreamListener::ConvertToPlainText(PRBool formatflowed /* = PR_FALSE */)
02307 {
02308   nsresult rv = ConvertBufToPlainText(mMsgBody, formatflowed);
02309   if (NS_FAILED(rv))
02310     return rv;
02311   return ConvertBufToPlainText(mSignature, formatflowed);
02312 }
02313 
02314 NS_IMETHODIMP QuotingOutputStreamListener::OnStartRequest(nsIRequest *request, nsISupports * /* ctxt */)
02315 {
02316   return NS_OK;
02317 }
02318 
02319 NS_IMETHODIMP QuotingOutputStreamListener::OnStopRequest(nsIRequest *request, nsISupports * /* ctxt */, nsresult status)
02320 {
02321   nsresult rv = NS_OK;
02322   nsAutoString aCharset;
02323 
02324   nsCOMPtr<nsIMsgCompose> compose = do_QueryReferent(mWeakComposeObj);
02325   if (compose) 
02326   {
02327     MSG_ComposeType type;
02328     compose->GetType(&type);
02329 
02330     // Assign cite information if available...
02331     if (!mCiteReference.IsEmpty())
02332       compose->SetCiteReference(mCiteReference);
02333 
02334     if (mHeaders && (type == nsIMsgCompType::Reply || type == nsIMsgCompType::ReplyAll || type == nsIMsgCompType::ReplyToSender ||
02335                      type == nsIMsgCompType::ReplyToGroup || type == nsIMsgCompType::ReplyToSenderAndGroup) && mQuoteOriginal)
02336     {
02337       nsCOMPtr<nsIMsgCompFields> compFields;
02338       compose->GetCompFields(getter_AddRefs(compFields));
02339       if (compFields)
02340       {
02341         aCharset.AssignLiteral("UTF-8");
02342         nsAutoString recipient;
02343         nsAutoString cc;
02344         nsAutoString bcc;
02345         nsAutoString replyTo;
02346         nsAutoString mailReplyTo;
02347         nsAutoString mailFollowupTo;
02348         nsAutoString newgroups;
02349         nsAutoString followUpTo;
02350         nsAutoString messageId;
02351         nsAutoString references;
02352         nsXPIDLCString outCString;
02353         PRBool needToRemoveDup = PR_FALSE;
02354         if (!mMimeConverter)
02355         {
02356           mMimeConverter = do_GetService(NS_MIME_CONVERTER_CONTRACTID, &rv);
02357           NS_ENSURE_SUCCESS(rv, rv);
02358         }
02359         nsXPIDLCString charset;
02360         compFields->GetCharacterSet(getter_Copies(charset));
02361 
02362         if (type == nsIMsgCompType::ReplyAll)
02363         {
02364           mHeaders->ExtractHeader(HEADER_TO, PR_TRUE, getter_Copies(outCString));
02365           if (outCString)
02366           {
02367             mMimeConverter->DecodeMimeHeader(outCString, recipient, charset);
02368           }
02369 
02370           mHeaders->ExtractHeader(HEADER_CC, PR_TRUE, getter_Copies(outCString));
02371           if (outCString)
02372           {
02373             mMimeConverter->DecodeMimeHeader(outCString, cc, charset);
02374           }
02375 
02376           // preserve BCC for the reply-to-self case
02377           mHeaders->ExtractHeader(HEADER_BCC, PR_TRUE, getter_Copies(outCString));
02378           if (outCString)
02379           {
02380             mMimeConverter->DecodeMimeHeader(outCString, bcc, charset);
02381             if (bcc.Length() > 0)
02382               compFields->SetBcc(bcc);
02383           }
02384 
02385           mHeaders->ExtractHeader(HEADER_MAIL_FOLLOWUP_TO, PR_TRUE, getter_Copies(outCString));
02386           if (outCString)
02387           {
02388             mMimeConverter->DecodeMimeHeader(outCString, mailFollowupTo, charset);
02389           }
02390 
02391           if (! mailFollowupTo.IsEmpty())
02392           { // handle Mail-Followup-To (http://cr.yp.to/proto/replyto.html)
02393             compFields->SetTo(mailFollowupTo);
02394           }
02395           else
02396           { // default behaviour for messages without Mail-Followup-To
02397             if (recipient.Length() > 0 && cc.Length() > 0)
02398               recipient.AppendLiteral(", ");
02399             recipient += cc;
02400             compFields->SetCc(recipient);
02401           }
02402 
02403           needToRemoveDup = PR_TRUE;
02404         }
02405               
02406         mHeaders->ExtractHeader(HEADER_REPLY_TO, PR_FALSE, getter_Copies(outCString));
02407         if (outCString)
02408         {
02409           mMimeConverter->DecodeMimeHeader(outCString, replyTo, charset);
02410         }
02411 
02412         mHeaders->ExtractHeader(HEADER_MAIL_REPLY_TO, PR_TRUE, getter_Copies(outCString));
02413         if (outCString)
02414         {
02415           mMimeConverter->DecodeMimeHeader(outCString, mailReplyTo, charset);
02416         }
02417 
02418         mHeaders->ExtractHeader(HEADER_NEWSGROUPS, PR_FALSE, getter_Copies(outCString));
02419         if (outCString)
02420         {
02421           mMimeConverter->DecodeMimeHeader(outCString, newgroups, charset);
02422         }
02423 
02424         mHeaders->ExtractHeader(HEADER_FOLLOWUP_TO, PR_FALSE, getter_Copies(outCString));
02425         if (outCString)
02426         {
02427           mMimeConverter->DecodeMimeHeader(outCString, followUpTo, charset);
02428         }
02429 
02430         mHeaders->ExtractHeader(HEADER_MESSAGE_ID, PR_FALSE, getter_Copies(outCString));
02431         if (outCString)
02432         {
02433           mMimeConverter->DecodeMimeHeader(outCString, messageId, charset);
02434         }
02435 
02436         mHeaders->ExtractHeader(HEADER_REFERENCES, PR_FALSE, getter_Copies(outCString));
02437         if (outCString)
02438         {
02439           mMimeConverter->DecodeMimeHeader(outCString, references, charset);
02440         }
02441 
02442         if (! ((type == nsIMsgCompType::ReplyAll) && ! mailFollowupTo.IsEmpty()))
02443         {
02444           if (! mailReplyTo.IsEmpty())
02445           { // handle Mail-Reply-To (http://cr.yp.to/proto/replyto.html)
02446             compFields->SetTo(mailReplyTo);
02447             needToRemoveDup = PR_TRUE;
02448           }
02449           else if (! replyTo.IsEmpty())
02450           { // default behaviour for messages without Mail-Reply-To
02451             compFields->SetTo(replyTo);
02452             needToRemoveDup = PR_TRUE;
02453           }
02454         }
02455 
02456         if (! newgroups.IsEmpty())
02457         {
02458           if ((type != nsIMsgCompType::Reply) && (type != nsIMsgCompType::ReplyToSender))
02459             compFields->SetNewsgroups(newgroups);
02460           if (type == nsIMsgCompType::ReplyToGroup)
02461             compFields->SetTo(EmptyString());
02462         }
02463 
02464         if (! followUpTo.IsEmpty())
02465         {
02466           // Handle "followup-to: poster" magic keyword here
02467           if (followUpTo.EqualsLiteral("poster"))
02468           {
02469             nsCOMPtr<nsIDOMWindowInternal> composeWindow;
02470             nsCOMPtr<nsIPrompt> prompt;
02471             compose->GetDomWindow(getter_AddRefs(composeWindow));
02472             if (composeWindow)
02473               composeWindow->GetPrompter(getter_AddRefs(prompt));
02474             nsMsgDisplayMessageByID(prompt, NS_MSG_FOLLOWUPTO_ALERT);
02475 
02476             // If reply-to is empty, use the from header to fetch
02477             // the original sender's email
02478             if (!replyTo.IsEmpty())
02479               compFields->SetTo(replyTo);
02480             else
02481             {
02482               mHeaders->ExtractHeader(HEADER_FROM, PR_FALSE, getter_Copies(outCString));
02483               if (outCString)
02484               {
02485                 nsAutoString from;
02486                 mMimeConverter->DecodeMimeHeader(outCString, from, charset);
02487                 compFields->SetTo(from);
02488               }
02489             }
02490 
02491             // Clear the newsgroup: header field, because followup-to: poster
02492             // only follows up to the original sender
02493             if (! newgroups.IsEmpty())
02494               compFields->SetNewsgroups(EmptyString());
02495           }
02496           else // Process "followup-to: newsgroup-content" here
02497           {
02498             if (type != nsIMsgCompType::ReplyToSender)
02499               compFields->SetNewsgroups(followUpTo);
02500             if (type == nsIMsgCompType::Reply)
02501               compFields->SetTo(EmptyString());
02502           }
02503         }
02504 
02505         if (! references.IsEmpty())
02506           references.Append(PRUnichar(' '));
02507         references += messageId;
02508         compFields->SetReferences(NS_LossyConvertUCS2toASCII(references).get());
02509 
02510         if (needToRemoveDup)
02511         {
02512           //Remove duplicate addresses between TO && CC
02513           char * resultStr;
02514           nsMsgCompFields* _compFields = (nsMsgCompFields*)compFields.get();  // XXX what is this?
02515           if (NS_SUCCEEDED(rv))
02516           {
02517             nsCString addressToBeRemoved(_compFields->GetTo());
02518             if (mIdentity)
02519             {
02520               nsXPIDLCString email;
02521               mIdentity->GetEmail(getter_Copies(email));
02522               addressToBeRemoved += ", ";
02523               addressToBeRemoved += email;
02524             }
02525 
02526             rv= RemoveDuplicateAddresses(_compFields->GetCc(), addressToBeRemoved.get(), PR_TRUE, &resultStr);
02527             if (NS_SUCCEEDED(rv))
02528             {
02529               _compFields->SetCc(resultStr);
02530               PR_Free(resultStr);
02531             }
02532           }
02533         }    
02534       }
02535     }
02536 
02537 #ifdef MSGCOMP_TRACE_PERFORMANCE
02538     nsCOMPtr<nsIMsgComposeService> composeService (do_GetService(NS_MSGCOMPOSESERVICE_CONTRACTID));
02539     composeService->TimeStamp("Done with MIME. Now we're updating the UI elements", PR_FALSE);
02540 #endif
02541 
02542     if (mQuoteOriginal)
02543       compose->NotifyStateListeners(eComposeFieldsReady, NS_OK);
02544 
02545 #ifdef MSGCOMP_TRACE_PERFORMANCE
02546     composeService->TimeStamp("Addressing widget, window title and focus are now set, time to insert the body", PR_FALSE);
02547 #endif
02548 
02549     if (! mHeadersOnly)
02550       mMsgBody.AppendLiteral("</html>");
02551 
02552     // Now we have an HTML representation of the quoted message.
02553     // If we are in plain text mode, we need to convert this to plain
02554     // text before we try to insert it into the editor. If we don't, we
02555     // just get lots of HTML text in the message...not good.
02556     //
02557     // XXX not m_composeHTML? /BenB
02558     PRBool composeHTML = PR_TRUE;
02559     compose->GetComposeHTML(&composeHTML);
02560     if (!composeHTML)
02561     {
02562       // Downsampling. The charset should only consist of ascii.
02563       char *target_charset = ToNewCString(aCharset);
02564       PRBool formatflowed = UseFormatFlowed(target_charset);
02565       ConvertToPlainText(formatflowed);
02566       Recycle(target_charset);
02567     }
02568 
02569     compose->ProcessSignature(mIdentity, PR_TRUE, &mSignature);
02570 
02571     nsCOMPtr<nsIEditor> editor;
02572     if (NS_SUCCEEDED(compose->GetEditor(getter_AddRefs(editor))) && editor)
02573     {
02574       if (mQuoteOriginal)
02575         compose->ConvertAndLoadComposeWindow(mCitePrefix,
02576                                              mMsgBody, mSignature,
02577                                              PR_TRUE, composeHTML);
02578       else
02579         InsertToCompose(editor, composeHTML);
02580     }
02581 
02582     if (mQuoteOriginal)
02583       compose->NotifyStateListeners(eComposeBodyReady, NS_OK);
02584   }
02585   return rv;
02586 }
02587 
02588 NS_IMETHODIMP QuotingOutputStreamListener::OnDataAvailable(nsIRequest *request,
02589                               nsISupports *ctxt, nsIInputStream *inStr, 
02590                               PRUint32 sourceOffset, PRUint32 count)
02591 {
02592   nsresult rv = NS_OK;
02593   NS_ENSURE_ARG(inStr);
02594 
02595   if (mHeadersOnly)
02596     return rv;
02597 
02598   char *newBuf = (char *)PR_Malloc(count + 1);
02599   if (!newBuf)
02600     return NS_ERROR_FAILURE;
02601 
02602   PRUint32 numWritten = 0; 
02603   rv = inStr->Read(newBuf, count, &numWritten);
02604   if (rv == NS_BASE_STREAM_WOULD_BLOCK)
02605     rv = NS_OK;
02606   newBuf[numWritten] = '\0';
02607   if (NS_SUCCEEDED(rv) && numWritten > 0)
02608   {
02609     // Create unicode decoder.
02610     if (!mUnicodeDecoder)
02611     {
02612       nsCOMPtr<nsICharsetConverterManager> ccm = 
02613                do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv); 
02614       if (NS_SUCCEEDED(rv))
02615       {
02616         rv = ccm->GetUnicodeDecoderRaw("UTF-8",
02617                                        getter_AddRefs(mUnicodeDecoder));
02618       }
02619     }
02620 
02621     if (NS_SUCCEEDED(rv))
02622     {
02623       PRInt32 unicharLength;
02624       PRInt32 inputLength = (PRInt32) numWritten;
02625       rv = mUnicodeDecoder->GetMaxLength(newBuf, numWritten, &unicharLength);
02626       if (NS_SUCCEEDED(rv))
02627       {
02628         // Use this local buffer if possible.
02629         const PRInt32 kLocalBufSize = 4096;
02630         PRUnichar localBuf[kLocalBufSize];
02631         PRUnichar *unichars = localBuf;
02632 
02633         if (unicharLength > kLocalBufSize)
02634         {
02635           // Otherwise, use the buffer of the class.
02636           if (!mUnicodeConversionBuffer ||
02637               unicharLength > mUnicodeBufferCharacterLength)
02638           {
02639             if (mUnicodeConversionBuffer)
02640               nsMemory::Free(mUnicodeConversionBuffer);
02641             mUnicodeConversionBuffer = (PRUnichar *) nsMemory::Alloc(unicharLength * sizeof(PRUnichar));
02642             if (!mUnicodeConversionBuffer)
02643             {
02644               mUnicodeBufferCharacterLength = 0;
02645               PR_Free(newBuf);
02646               return NS_ERROR_OUT_OF_MEMORY;
02647             }
02648             mUnicodeBufferCharacterLength = unicharLength;
02649           }
02650           unichars = mUnicodeConversionBuffer;
02651         }
02652 
02653         PRInt32 consumedInputLength = 0;
02654         PRInt32 originalInputLength = inputLength;
02655         char *inputBuffer = newBuf;
02656         PRInt32 convertedOutputLength = 0;
02657         PRInt32 outputBufferLength = unicharLength;
02658         PRUnichar *originalOutputBuffer = unichars;
02659         do 
02660         {
02661           rv = mUnicodeDecoder->Convert(inputBuffer, &inputLength, unichars, &unicharLength);
02662           if (NS_SUCCEEDED(rv)) 
02663           {
02664             convertedOutputLength += unicharLength;
02665             break;
02666           }
02667 
02668           // if we failed, we consume one byte, replace it with a question mark
02669           // and try the conversion again.
02670           unichars += unicharLength;
02671           *unichars = (PRUnichar)'?';
02672           unichars++;
02673           unicharLength++;
02674 
02675           mUnicodeDecoder->Reset();
02676 
02677           inputBuffer += ++inputLength;
02678           consumedInputLength += inputLength;
02679           inputLength = originalInputLength - consumedInputLength;  // update input length to convert
02680           convertedOutputLength += unicharLength;
02681           unicharLength = outputBufferLength - unicharLength;       // update output length
02682 
02683         } while (NS_FAILED(rv) &&
02684                  (originalInputLength > consumedInputLength) && 
02685                  (outputBufferLength > convertedOutputLength));
02686 
02687         if (convertedOutputLength > 0)
02688           mMsgBody.Append(originalOutputBuffer, convertedOutputLength);
02689       }
02690     }
02691   }
02692 
02693   PR_FREEIF(newBuf);
02694   return rv;
02695 }
02696 
02697 nsresult
02698 QuotingOutputStreamListener::SetComposeObj(nsIMsgCompose *obj)
02699 {
02700   mWeakComposeObj = do_GetWeakReference(obj);
02701   return NS_OK;
02702 }
02703 
02704 nsresult
02705 QuotingOutputStreamListener::SetMimeHeaders(nsIMimeHeaders * headers)
02706 {
02707   mHeaders = headers;
02708   return NS_OK;
02709 }
02710 
02711 NS_IMETHODIMP
02712 QuotingOutputStreamListener::InsertToCompose(nsIEditor *aEditor,
02713                                              PRBool aHTMLEditor)
02714 {
02715   // First, get the nsIEditor interface for future use
02716   nsCOMPtr<nsIDOMNode> nodeInserted;
02717 
02718   TranslateLineEnding(mMsgBody);
02719 
02720   // Now, insert it into the editor...
02721   if (aEditor)
02722     aEditor->EnableUndo(PR_TRUE);
02723 
02724   nsCOMPtr<nsIMsgCompose> compose = do_QueryReferent(mWeakComposeObj);
02725   if (!mMsgBody.IsEmpty() && compose)
02726   {
02727     compose->SetInsertingQuotedContent(PR_TRUE);
02728     if (!mCitePrefix.IsEmpty())
02729     {
02730       if (!aHTMLEditor)
02731         mCitePrefix.AppendLiteral("\n");
02732       nsCOMPtr<nsIPlaintextEditor> textEditor (do_QueryInterface(aEditor));
02733       if (textEditor)
02734         textEditor->InsertText(mCitePrefix);
02735     }
02736 
02737     nsCOMPtr<nsIEditorMailSupport> mailEditor (do_QueryInterface(aEditor));
02738     if (mailEditor)
02739     {
02740       // XXX see bug #206793
02741       nsCOMPtr<nsIMsgCompose> compose = do_QueryReferent(mWeakComposeObj);
02742       nsCOMPtr<nsIDOMWindowInternal> domWindow;
02743       if (compose)
02744         compose->GetDomWindow(getter_AddRefs(domWindow));
02745       nsIDocShell *docshell = nsnull;
02746       nsCOMPtr<nsIScriptGlobalObject> globalObj = do_QueryInterface(domWindow);
02747       if (globalObj)
02748         docshell = globalObj->GetDocShell();
02749       if (docshell)
02750         docshell->SetAppType(nsIDocShell::APP_TYPE_EDITOR);
02751       
02752       if (aHTMLEditor)
02753         mailEditor->InsertAsCitedQuotation(mMsgBody, EmptyString(), PR_TRUE,
02754                                            getter_AddRefs(nodeInserted));
02755       else
02756         mailEditor->InsertAsQuotation(mMsgBody, getter_AddRefs(nodeInserted));
02757     }
02758     compose->SetInsertingQuotedContent(PR_FALSE);
02759   }
02760 
02761   if (aEditor)
02762   {
02763     nsCOMPtr<nsIPlaintextEditor> textEditor = do_QueryInterface(aEditor);
02764     if (textEditor)
02765     {
02766       nsCOMPtr<nsISelection> selection;
02767       nsCOMPtr<nsIDOMNode>   parent;
02768       PRInt32                offset;
02769       nsresult               rv;
02770 
02771       // get parent and offset of mailcite
02772       rv = GetNodeLocation(nodeInserted, address_of(parent), &offset);
02773       NS_ENSURE_SUCCESS(rv, rv);
02774 
02775       // get selection
02776       aEditor->GetSelection(getter_AddRefs(selection));
02777       if (selection)
02778       {
02779         // place selection after mailcite
02780         selection->Collapse(parent, offset+1);
02781         // insert a break at current selection
02782         textEditor->InsertLineBreak();
02783         selection->Collapse(parent, offset+1);
02784       }
02785       nsCOMPtr<nsISelectionController> selCon;
02786       aEditor->GetSelectionController(getter_AddRefs(selCon));
02787 
02788       if (selCon)
02789         selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_ANCHOR_REGION, PR_TRUE);
02790     }
02791   }
02792 
02793   return NS_OK;
02794 }
02795 
02796 NS_IMPL_ISUPPORTS2(QuotingOutputStreamListener, nsIMsgQuotingOutputStreamListener, nsIStreamListener)
02798 // END OF QUOTING LISTENER
02800 
02801 /* attribute MSG_ComposeType type; */
02802 NS_IMETHODIMP nsMsgCompose::SetType(MSG_ComposeType aType)
02803 {
02804 
02805   mType = aType;
02806   return NS_OK;
02807 }
02808 
02809 NS_IMETHODIMP nsMsgCompose::GetType(MSG_ComposeType *aType)
02810 {
02811   NS_ENSURE_ARG_POINTER(aType);
02812 
02813   *aType = mType;
02814   return NS_OK;
02815 }
02816 
02817 NS_IMETHODIMP
02818 nsMsgCompose::QuoteMessage(const char *msgURI)
02819 {
02820   nsresult    rv;
02821 
02822   mQuotingToFollow = PR_FALSE;
02823 
02824   // Create a mime parser (nsIStreamConverter)!
02825   mQuote = do_CreateInstance(NS_MSGQUOTE_CONTRACTID, &rv);
02826   NS_ENSURE_SUCCESS(rv, rv);
02827 
02828   nsCOMPtr <nsIMsgDBHdr> msgHdr;
02829   rv = GetMsgDBHdrFromURI(msgURI, getter_AddRefs(msgHdr));
02830 
02831   // Create the consumer output stream.. this will receive all the HTML from libmime
02832   mQuoteStreamListener =
02833     new QuotingOutputStreamListener(msgURI, msgHdr, PR_FALSE, PR_FALSE, m_identity,
02834                                     m_compFields->GetCharacterSet(), mCharsetOverride, PR_FALSE);
02835 
02836   if (!mQuoteStreamListener)
02837   {
02838 #ifdef NS_DEBUG
02839     printf("Failed to create mQuoteStreamListener\n");
02840 #endif
02841     return NS_ERROR_FAILURE;
02842   }
02843   NS_ADDREF(mQuoteStreamListener);
02844 
02845   mQuoteStreamListener->SetComposeObj(this);
02846 
02847   rv = mQuote->QuoteMessage(msgURI, PR_FALSE, mQuoteStreamListener, 
02848                             mCharsetOverride ? m_compFields->GetCharacterSet() : "", PR_FALSE);
02849   return rv;
02850 }
02851 
02852 nsresult
02853 nsMsgCompose::QuoteOriginalMessage(const char *originalMsgURI, PRInt32 what) // New template
02854 {
02855   nsresult    rv;
02856 
02857   mQuotingToFollow = PR_FALSE;
02858 
02859   // Create a mime parser (nsIStreamConverter)!
02860   mQuote = do_CreateInstance(NS_MSGQUOTE_CONTRACTID, &rv);
02861   if (NS_FAILED(rv) || !mQuote)
02862     return NS_ERROR_FAILURE;
02863 
02864   PRBool bAutoQuote = PR_TRUE;
02865   m_identity->GetAutoQuote(&bAutoQuote);
02866 
02867   nsCOMPtr <nsIMsgDBHdr> originalMsgHdr = mOrigMsgHdr;
02868   if (!originalMsgHdr)
02869   {
02870     rv = GetMsgDBHdrFromURI(originalMsgURI, getter_AddRefs(originalMsgHdr));
02871     NS_ENSURE_SUCCESS(rv, rv);
02872   }
02873 
02874   // Create the consumer output stream.. this will receive all the HTML from libmime
02875   mQuoteStreamListener =
02876     new QuotingOutputStreamListener(originalMsgURI, originalMsgHdr, what != 1, !bAutoQuote, m_identity,
02877                                     mQuoteCharset.get(), mCharsetOverride, PR_TRUE);
02878 
02879   if (!mQuoteStreamListener)
02880   {
02881 #ifdef NS_DEBUG
02882     printf("Failed to create mQuoteStreamListener\n");
02883 #endif
02884     return NS_ERROR_FAILURE;
02885   }
02886   NS_ADDREF(mQuoteStreamListener);
02887 
02888   mQuoteStreamListener->SetComposeObj(this);
02889 
02890   rv = mQuote->QuoteMessage(originalMsgURI, what != 1, mQuoteStreamListener, 
02891                             mCharsetOverride ? mQuoteCharset.get() : "", !bAutoQuote);
02892   return rv;
02893 }
02894 
02895 //CleanUpRecipient will remove un-necesary "<>" when a recipient as an address without name
02896 void nsMsgCompose::CleanUpRecipients(nsString& recipients)
02897 {
02898   PRUint16 i;
02899   PRBool startANewRecipient = PR_TRUE;
02900   PRBool removeBracket = PR_FALSE;
02901   nsAutoString newRecipient;
02902   PRUnichar aChar;
02903 
02904   for (i = 0; i < recipients.Length(); i ++)
02905   {
02906     aChar = recipients[i];
02907     switch (aChar)
02908     {
02909       case '<'  :
02910         if (startANewRecipient)
02911           removeBracket = PR_TRUE;
02912         else
02913           newRecipient += aChar;
02914         startANewRecipient = PR_FALSE;
02915         break;
02916 
02917       case '>'  :
02918         if (removeBracket)
02919           removeBracket = PR_FALSE;
02920         else
02921           newRecipient += aChar;
02922         break;
02923 
02924       case ' '  :
02925         newRecipient += aChar;
02926         break;
02927 
02928       case ','  :
02929         newRecipient += aChar;
02930         startANewRecipient = PR_TRUE;
02931         removeBracket = PR_FALSE;
02932         break;
02933 
02934       default   :
02935         newRecipient += aChar;
02936         startANewRecipient = PR_FALSE;
02937         break;
02938     } 
02939   }
02940   recipients = newRecipient;
02941 }
02942 
02943 NS_IMETHODIMP nsMsgCompose::RememberQueuedDisposition()
02944 {
02945   // need to find the msg hdr in the saved folder and then set a property on 
02946   // the header that we then look at when we actually send the message.
02947   if (mType == nsIMsgCompType::Reply || 
02948     mType == nsIMsgCompType::ReplyAll ||
02949     mType == nsIMsgCompType::ReplyToGroup ||
02950     mType == nsIMsgCompType::ReplyToSender ||
02951     mType == nsIMsgCompType::ReplyToSenderAndGroup ||
02952     mType == nsIMsgCompType::ForwardAsAttachment ||              
02953     mType == nsIMsgCompType::ForwardInline)
02954   {
02955     if (!mOriginalMsgURI.IsEmpty())
02956     {
02957       nsMsgKey msgKey;
02958       if (mMsgSend)
02959       {
02960         mMsgSend->GetMessageKey(&msgKey);
02961         const char *dispositionSetting = "replied";
02962         if (mType == nsIMsgCompType::ForwardAsAttachment ||              
02963           mType == nsIMsgCompType::ForwardInline)
02964           dispositionSetting = "forwarded";
02965         nsCAutoString msgUri(m_folderName);
02966         msgUri.Insert("-message", 7); // "mailbox: -> "mailbox-message:"
02967         msgUri.Append('#');
02968         msgUri.AppendInt(msgKey);
02969         nsCOMPtr <nsIMsgDBHdr> msgHdr;
02970         nsresult rv = GetMsgDBHdrFromURI(msgUri.get(), getter_AddRefs(msgHdr));
02971         NS_ENSURE_SUCCESS(rv, rv);
02972         msgHdr->SetStringProperty(ORIG_URI_PROPERTY, mOriginalMsgURI.get());
02973         msgHdr->SetStringProperty(QUEUED_DISPOSITION_PROPERTY, dispositionSetting);
02974       }
02975     }
02976   }
02977   return NS_OK;
02978 }
02979 
02980 nsresult nsMsgCompose::ProcessReplyFlags()
02981 {
02982   nsresult rv;
02983   // check to see if we were doing a reply or a forward, if we were, set the answered field flag on the message folder
02984   // for this URI.
02985   if (mType == nsIMsgCompType::Reply || 
02986       mType == nsIMsgCompType::ReplyAll ||
02987       mType == nsIMsgCompType::ReplyToGroup ||
02988       mType == nsIMsgCompType::ReplyToSender ||
02989       mType == nsIMsgCompType::ReplyToSenderAndGroup ||
02990       mType == nsIMsgCompType::ForwardAsAttachment ||              
02991       mType == nsIMsgCompType::ForwardInline ||
02992       mDraftDisposition != nsIMsgFolder::nsMsgDispositionState_None)
02993   {
02994     if (!mOriginalMsgURI.IsEmpty())
02995     {
02996       char *uriList = PL_strdup(mOriginalMsgURI.get());
02997       if (!uriList)
02998         return NS_ERROR_OUT_OF_MEMORY;
02999       char *newStr = uriList;
03000       char *uri;
03001       while (nsnull != (uri = nsCRT::strtok(newStr, ",", &newStr)))
03002       {
03003         nsCOMPtr <nsIMsgDBHdr> msgHdr;
03004         rv = GetMsgDBHdrFromURI(uri, getter_AddRefs(msgHdr));
03005         NS_ENSURE_SUCCESS(rv,rv);
03006         if (msgHdr)
03007         {
03008           // get the folder for the message resource
03009           nsCOMPtr<nsIMsgFolder> msgFolder;
03010           msgHdr->GetFolder(getter_AddRefs(msgFolder));
03011           if (msgFolder)
03012           {
03013             // assume reply. If a draft with disposition, use that, otherwise,
03014             // check if it's a forward.
03015             nsMsgDispositionState dispositionSetting = nsIMsgFolder::nsMsgDispositionState_Replied;
03016             if (mDraftDisposition != nsIMsgFolder::nsMsgDispositionState_None)
03017               dispositionSetting = mDraftDisposition;
03018             else if (mType == nsIMsgCompType::ForwardAsAttachment ||              
03019                 mType == nsIMsgCompType::ForwardInline)
03020               dispositionSetting = nsIMsgFolder::nsMsgDispositionState_Forwarded;
03021 
03022             msgFolder->AddMessageDispositionState(msgHdr, dispositionSetting);
03023             if (mType != nsIMsgCompType::ForwardAsAttachment)
03024               break;         // just safeguard
03025           }
03026         }
03027       }
03028       PR_Free(uriList);
03029     }
03030   }
03031 
03032   return NS_OK;
03033 }
03034 
03036 // This is the listener class for both the send operation and the copy operation. 
03037 // We have to create this class to listen for message send completion and deal with
03038 // failures in both send and copy operations
03040 NS_IMPL_ADDREF(nsMsgComposeSendListener)
03041 NS_IMPL_RELEASE(nsMsgComposeSendListener)
03042 
03043 /*
03044 NS_IMPL_QUERY_INTERFACE4(nsMsgComposeSendListener,
03045                          nsIMsgComposeSendListener,
03046                          nsIMsgSendListener,
03047                          nsIMsgCopyServiceListener,
03048                          nsIWebProgressListener)
03049 */
03050 NS_INTERFACE_MAP_BEGIN(nsMsgComposeSendListener)
03051   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMsgComposeSendListener)
03052   NS_INTERFACE_MAP_ENTRY(nsIMsgComposeSendListener)
03053   NS_INTERFACE_MAP_ENTRY(nsIMsgSendListener)
03054   NS_INTERFACE_MAP_ENTRY(nsIMsgCopyServiceListener)
03055   NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
03056 NS_INTERFACE_MAP_END
03057 
03058 
03059 nsMsgComposeSendListener::nsMsgComposeSendListener(void) 
03060 { 
03061 #if defined(DEBUG_ducarroz)
03062   printf("CREATE nsMsgComposeSendListener: %x\n", this);
03063 #endif
03064   mDeliverMode = 0;
03065 }
03066 
03067 nsMsgComposeSendListener::~nsMsgComposeSendListener(void) 
03068 {
03069 #if defined(DEBUG_ducarroz)
03070   printf("DISPOSE nsMsgComposeSendListener: %x\n", this);
03071 #endif
03072 }
03073 
03074 NS_IMETHODIMP nsMsgComposeSendListener::SetMsgCompose(nsIMsgCompose *obj)
03075 {
03076   mWeakComposeObj = do_GetWeakReference(obj);
03077   return NS_OK;
03078 }
03079 
03080 NS_IMETHODIMP nsMsgComposeSendListener::SetDeliverMode(MSG_DeliverMode deliverMode)
03081 {
03082   mDeliverMode = deliverMode;
03083   return NS_OK;
03084 }
03085 
03086 nsresult
03087 nsMsgComposeSendListener::OnStartSending(const char *aMsgID, PRUint32 aMsgSize)
03088 {
03089 #ifdef NS_DEBUG
03090   printf("nsMsgComposeSendListener::OnStartSending()\n");
03091 #endif
03092 
03093   nsCOMPtr<nsIMsgCompose>compose = do_QueryReferent(mWeakComposeObj);
03094   if (compose)
03095   {
03096     nsCOMPtr<nsIMsgSendListener> externalListener;
03097     compose->GetExternalSendListener(getter_AddRefs(externalListener));
03098     if (externalListener)
03099       externalListener->OnStartSending(aMsgID, aMsgSize);
03100   }
03101 
03102   return NS_OK;
03103 }
03104 
03105 nsresult
03106 nsMsgComposeSendListener::OnProgress(const char *aMsgID, PRUint32 aProgress, PRUint32 aProgressMax)
03107 {
03108 #ifdef NS_DEBUG
03109   printf("nsMsgComposeSendListener::OnProgress()\n");
03110 #endif
03111 
03112   nsCOMPtr<nsIMsgCompose>compose = do_QueryReferent(mWeakComposeObj);
03113   if (compose)
03114   {
03115     nsCOMPtr<nsIMsgSendListener> externalListener;
03116     compose->GetExternalSendListener(getter_AddRefs(externalListener));
03117     if (externalListener)
03118       externalListener->OnProgress(aMsgID, aProgress, aProgressMax);
03119   }
03120 
03121   return NS_OK;
03122 }
03123 
03124 nsresult
03125 nsMsgComposeSendListener::OnStatus(const char *aMsgID, const PRUnichar *aMsg)
03126 {
03127 #ifdef NS_DEBUG
03128   printf("nsMsgComposeSendListener::OnStatus()\n");
03129 #endif
03130 
03131   nsCOMPtr<nsIMsgCompose>compose = do_QueryReferent(mWeakComposeObj);
03132   if (compose)
03133   {
03134     nsCOMPtr<nsIMsgSendListener> externalListener;
03135     compose->GetExternalSendListener(getter_AddRefs(externalListener));
03136     if (externalListener)
03137       externalListener->OnStatus(aMsgID, aMsg);
03138   }
03139 
03140   return NS_OK;
03141 }
03142 
03143 nsresult nsMsgComposeSendListener::OnSendNotPerformed(const char *aMsgID, nsresult aStatus)
03144 {
03145  // since OnSendNotPerformed is called in the case where the user aborts the operation
03146  // by closing the compose window, we need not do the stuff required 
03147  // for closing the windows. However we would need to do the other operations as below.
03148 
03149   nsresult rv = NS_OK;
03150 
03151   nsCOMPtr<nsIMsgCompose>compose = do_QueryReferent(mWeakComposeObj);
03152   if (compose)
03153   {
03154     compose->NotifyStateListeners(eComposeProcessDone,aStatus);
03155 
03156     nsCOMPtr<nsIMsgSendListener> externalListener;
03157     compose->GetExternalSendListener(getter_AddRefs(externalListener));
03158     if (externalListener)
03159       externalListener->OnSendNotPerformed(aMsgID, aStatus) ;
03160   }
03161 
03162   return rv ;
03163 }
03164 
03165 
03166 nsresult nsMsgComposeSendListener::OnStopSending(const char *aMsgID, nsresult aStatus, const PRUnichar *aMsg, 
03167                                      nsIFileSpec *returnFileSpec)
03168 {
03169   nsresult rv = NS_OK;
03170 
03171   nsCOMPtr<nsIMsgCompose>compose = do_QueryReferent(mWeakComposeObj);
03172   if (compose)
03173   {
03174     nsCOMPtr<nsIMsgProgress> progress;
03175     compose->GetProgress(getter_AddRefs(progress));
03176 
03177     if (NS_SUCCEEDED(aStatus))
03178     {
03179 #ifdef NS_DEBUG
03180       printf("nsMsgComposeSendListener: Success on the message send operation!\n");
03181 #endif
03182       nsCOMPtr<nsIMsgCompFields> compFields;
03183       compose->GetCompFields(getter_AddRefs(compFields));
03184 
03185       // only process the reply flags if we successfully sent the message
03186       compose->ProcessReplyFlags();
03187 
03188       // Close the window ONLY if we are not going to do a save operation
03189       nsAutoString fieldsFCC;
03190       if (NS_SUCCEEDED(compFields->GetFcc(fieldsFCC)))
03191       {
03192         if (!fieldsFCC.IsEmpty())
03193         {
03194           if (fieldsFCC.LowerCaseEqualsLiteral("nocopy://"))
03195           {
03196             compose->NotifyStateListeners(eComposeProcessDone, NS_OK);
03197             if (progress)
03198             {
03199               progress->UnregisterListener(this);
03200               progress->CloseProgressDialog(PR_FALSE);
03201             }
03202             compose->CloseWindow(PR_TRUE);
03203           }
03204         }
03205       }
03206       else
03207       {
03208         compose->NotifyStateListeners(eComposeProcessDone, NS_OK);
03209         if (progress)
03210         {
03211           progress->UnregisterListener(this);
03212           progress->CloseProgressDialog(PR_FALSE);
03213         }
03214         compose->CloseWindow(PR_TRUE);  // if we fail on the simple GetFcc call, close the window to be safe and avoid
03215                                         // windows hanging around to prevent the app from exiting.
03216       }
03217 
03218       // Remove the current draft msg when sending draft is done.
03219       PRBool deleteDraft;
03220       compose->GetDeleteDraft(&deleteDraft);
03221       if (deleteDraft)
03222         RemoveCurrentDraftMessage(compose, PR_FALSE);
03223     }
03224     else
03225     {
03226 #ifdef NS_DEBUG
03227       printf("nsMsgComposeSendListener: the message send operation failed!\n");
03228 #endif
03229       compose->NotifyStateListeners(eComposeProcessDone,aStatus);
03230       if (progress)
03231       {
03232         progress->CloseProgressDialog(PR_TRUE);
03233         progress->UnregisterListener(this);
03234       }
03235     }
03236 
03237     nsCOMPtr<nsIMsgSendListener> externalListener;
03238     compose->GetExternalSendListener(getter_AddRefs(externalListener));
03239     if (externalListener)
03240       externalListener->OnStopSending(aMsgID, aStatus, aMsg, returnFileSpec);
03241   }
03242 
03243   return rv;
03244 }
03245 
03246 nsresult
03247 nsMsgComposeSendListener::OnGetDraftFolderURI(const char *aFolderURI)
03248 {
03249   nsCOMPtr<nsIMsgCompose>compose = do_QueryReferent(mWeakComposeObj);
03250   if (compose)
03251   {
03252     compose->SetSavedFolderURI(aFolderURI);
03253 
03254     nsCOMPtr<nsIMsgSendListener> externalListener;
03255     compose->GetExternalSendListener(getter_AddRefs(externalListener));
03256     if (externalListener)
03257       externalListener->OnGetDraftFolderURI(aFolderURI);
03258   }
03259 
03260   return NS_OK;
03261 }
03262 
03263 
03264 nsresult
03265 nsMsgComposeSendListener::OnStartCopy()
03266 {
03267 #ifdef NS_DEBUG
03268   printf("nsMsgComposeSendListener::OnStartCopy()\n");
03269 #endif
03270 
03271   return NS_OK;
03272 }
03273 
03274 nsresult
03275 nsMsgComposeSendListener::OnProgress(PRUint32 aProgress, PRUint32 aProgressMax)
03276 {
03277 #ifdef NS_DEBUG
03278   printf("nsMsgComposeSendListener::OnProgress() - COPY\n");
03279 #endif
03280   return NS_OK;
03281 }
03282 
03283 nsresult
03284 nsMsgComposeSendListener::OnStopCopy(nsresult aStatus)
03285 {
03286   nsresult rv = NS_OK;
03287 
03288   nsCOMPtr<nsIMsgCompose>compose = do_QueryReferent(mWeakComposeObj);
03289   if (compose)
03290   {
03291     if (mDeliverMode == nsIMsgSend::nsMsgQueueForLater || 
03292           mDeliverMode == nsIMsgSend::nsMsgSaveAsDraft)
03293       compose->RememberQueuedDisposition();
03294 
03295     // Ok, if we are here, we are done with the send/copy operation so
03296     // we have to do something with the window....SHOW if failed, Close
03297     // if succeeded
03298 
03299     nsCOMPtr<nsIMsgProgress> progress;
03300     compose->GetProgress(getter_AddRefs(progress));
03301     if (progress)
03302     {
03303     //Unregister ourself from msg compose progress
03304       progress->UnregisterListener(this);
03305       progress->CloseProgressDialog(NS_FAILED(aStatus));
03306     }
03307 
03308     compose->NotifyStateListeners(eComposeProcessDone,aStatus);
03309 
03310     if (NS_SUCCEEDED(aStatus))
03311     {
03312 #ifdef NS_DEBUG
03313       printf("nsMsgComposeSendListener: Success on the message copy operation!\n");
03314 #endif
03315       // We should only close the window if we are done. Things like templates
03316       // and drafts aren't done so their windows should stay open
03317       if (mDeliverMode == nsIMsgSend::nsMsgSaveAsDraft ||
03318           mDeliverMode == nsIMsgSend::nsMsgSaveAsTemplate)
03319       {
03320         compose->NotifyStateListeners(eSaveInFolderDone,aStatus);
03321         // Remove the current draft msg when saving as draft/template is done.
03322         compose->SetDeleteDraft(PR_TRUE);
03323         RemoveCurrentDraftMessage(compose, PR_TRUE);
03324       }
03325       else
03326       {
03327         // Remove (possible) draft if we're in send later mode
03328         if(mDeliverMode == nsIMsgSend::nsMsgQueueForLater) {
03329           compose->SetDeleteDraft(PR_TRUE);
03330           RemoveCurrentDraftMessage(compose, PR_TRUE);
03331         }
03332         compose->CloseWindow(PR_TRUE);
03333       }
03334     }
03335 #ifdef NS_DEBUG
03336     else
03337       printf("nsMsgComposeSendListener: the message copy operation failed!\n");
03338 #endif
03339   }
03340 
03341   return rv;
03342 }
03343 
03344 nsresult
03345 nsMsgComposeSendListener::GetMsgFolder(nsIMsgCompose *compObj, nsIMsgFolder **msgFolder)
03346 {
03347   nsresult    rv;
03348   nsCOMPtr<nsIMsgFolder> aMsgFolder;
03349   nsXPIDLCString folderUri;
03350 
03351   rv = compObj->GetSavedFolderURI(getter_Copies(folderUri));
03352   NS_ENSURE_SUCCESS(rv, rv);
03353 
03354   nsCOMPtr<nsIRDFService> rdfService (do_GetService("@mozilla.org/rdf/rdf-service;1", &rv));
03355   NS_ENSURE_SUCCESS(rv, rv); 
03356 
03357   nsCOMPtr <nsIRDFResource> resource;
03358   rv = rdfService->GetResource(folderUri, getter_AddRefs(resource));
03359   NS_ENSURE_SUCCESS(rv, rv); 
03360 
03361   aMsgFolder = do_QueryInterface(resource, &rv);
03362   NS_ENSURE_SUCCESS(rv, rv);
03363   *msgFolder = aMsgFolder;
03364   NS_IF_ADDREF(*msgFolder);
03365   return rv;
03366 }
03367 
03368 nsresult
03369 nsMsgComposeSendListener::RemoveCurrentDraftMessage(nsIMsgCompose *compObj, PRBool calledByCopy)
03370 {
03371   nsresult    rv;
03372   nsCOMPtr <nsIMsgCompFields> compFields = nsnull;
03373 
03374   rv = compObj->GetCompFields(getter_AddRefs(compFields));
03375   NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveCurrentDraftMessage can't get compose fields");
03376   if (NS_FAILED(rv) || !compFields)
03377     return rv;
03378 
03379   nsXPIDLCString curDraftIdURL;
03380   nsMsgKey newUid = 0;
03381   nsXPIDLCString newDraftIdURL;
03382   nsCOMPtr<nsIMsgFolder> msgFolder;
03383 
03384   rv = compFields->GetDraftId(getter_Copies(curDraftIdURL));
03385   NS_ASSERTION((NS_SUCCEEDED(rv) && (curDraftIdURL)), "RemoveCurrentDraftMessage can't get draft id");
03386 
03387   // Skip if no draft id (probably a new draft msg).
03388   if (NS_SUCCEEDED(rv) && curDraftIdURL.get() && strlen(curDraftIdURL.get()))
03389   { 
03390     nsCOMPtr <nsIMsgDBHdr> msgDBHdr;
03391     rv = GetMsgDBHdrFromURI(curDraftIdURL, getter_AddRefs(msgDBHdr));
03392     NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveCurrentDraftMessage can't get msg header DB interface pointer.");
03393     if (NS_SUCCEEDED(rv) && msgDBHdr)
03394     { // get the folder for the message resource
03395       msgDBHdr->GetFolder(getter_AddRefs(msgFolder));
03396       NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveCurrentDraftMessage can't get msg folder interface pointer.");
03397       if (NS_SUCCEEDED(rv) && msgFolder)
03398       {
03399         PRUint32 folderFlags;
03400         msgFolder->GetFlags(&folderFlags);
03401         // only do this if it's a drafts folder.
03402         if (folderFlags & MSG_FOLDER_FLAG_DRAFTS)
03403         {  // build the msg arrary
03404           nsCOMPtr<nsISupportsArray> messageArray;
03405           rv = NS_NewISupportsArray(getter_AddRefs(messageArray));
03406           NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveCurrentDraftMessage can't allocate support array.");
03407 
03408           //nsCOMPtr<nsISupports> msgSupport = do_QueryInterface(msgDBHdr, &rv);
03409           //NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveCurrentDraftMessage can't get msg header interface pointer.");
03410           if (NS_SUCCEEDED(rv) && messageArray)
03411           {   // ready to delete the msg
03412             rv = messageArray->AppendElement(msgDBHdr);
03413             NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveCurrentDraftMessage can't append msg header to array.");
03414             if (NS_SUCCEEDED(rv))
03415             rv = msgFolder->DeleteMessages(messageArray, nsnull, PR_TRUE, PR_FALSE, nsnull, PR_FALSE /*allowUndo*/);
03416             NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveCurrentDraftMessage can't delete message.");
03417           }
03418         }
03419       }
03420     }
03421     else
03422     {
03423       // If we get here we have the case where the draft folder 
03424                   // is on the server and
03425       // it's not currently open (in thread pane), so draft 
03426                   // msgs are saved to the server
03427       // but they're not in our local DB. In this case, 
03428       // GetMsgDBHdrFromURI() will never
03429       // find the msg. If the draft folder is a local one 
03430       // then we'll not get here because
03431       // the draft msgs are saved to the local folder and 
03432       // are in local DB. Make sure the
03433       // msg folder is imap.  Even if we get here due to 
03434       // DB errors (worst case), we should
03435       // still try to delete msg on the server because 
03436       // that's where the master copy of the
03437       // msgs are stored, if draft folder is on the server.  
03438       // For local case, since DB is bad
03439       // we can't do anything with it anyway so it'll be 
03440       // noop in this case.
03441       rv = GetMsgFolder(compObj, getter_AddRefs(msgFolder));
03442       if (NS_SUCCEEDED(rv) && msgFolder)
03443       {
03444         nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(msgFolder);
03445         NS_ASSERTION(imapFolder, "The draft folder MUST be an imap folder in order to mark the msg delete!");
03446         if (NS_SUCCEEDED(rv) && imapFolder)
03447         {
03448           const char * str = PL_strstr(curDraftIdURL.get(), "#");
03449           NS_ASSERTION(str, "Failed to get current draft id url");
03450           if (str)
03451           {
03452             nsMsgKeyArray messageID;
03453             nsCAutoString srcStr(str+1);
03454             PRInt32 num=0, err;
03455             num = srcStr.ToInteger(&err);
03456             if (num != PRInt32(nsMsgKey_None))
03457             {
03458               messageID.Add(num);
03459               rv = imapFolder->StoreImapFlags(kImapMsgDeletedFlag, PR_TRUE, messageID.GetArray(), 
03460                                                 messageID.GetSize(), nsnull);
03461             }
03462           }
03463         }
03464       }
03465     }
03466   }
03467 
03468   // Now get the new uid so that next save will remove the right msg
03469   // regardless whether or not the exiting msg can be deleted.
03470   if (calledByCopy)
03471   {
03472     nsCOMPtr<nsIMsgFolder> savedToFolder;
03473     nsCOMPtr<nsIMsgSend> msgSend;
03474     rv = compObj->GetMessageSend(getter_AddRefs(msgSend));
03475     NS_ASSERTION(msgSend, "RemoveCurrentDraftMessage msgSend is null.");
03476     if (NS_FAILED(rv) || !msgSend)
03477       return rv;
03478 
03479     rv = msgSend->GetMessageKey(&newUid);
03480     NS_ENSURE_SUCCESS(rv, rv);
03481 
03482     // Make sure we have a folder interface pointer
03483     rv = GetMsgFolder(compObj, getter_AddRefs(savedToFolder));
03484 
03485     // Reset draft (uid) url with the new uid.
03486     if (savedToFolder && newUid != nsMsgKey_None)
03487     {
03488       PRUint32 folderFlags;
03489       savedToFolder->GetFlags(&folderFlags);
03490       if (folderFlags & MSG_FOLDER_FLAG_DRAFTS)
03491       {
03492         rv = savedToFolder->GenerateMessageURI(newUid, getter_Copies(newDraftIdURL));
03493         NS_ENSURE_SUCCESS(rv, rv);
03494         compFields->SetDraftId(newDraftIdURL.get());
03495       }
03496     }
03497   }
03498   return rv;
03499 }
03500 
03501 nsresult
03502 nsMsgComposeSendListener::SetMessageKey(PRUint32 aMessageKey)
03503 {
03504   return NS_OK;
03505 }
03506 
03507 nsresult
03508 nsMsgComposeSendListener::GetMessageId(nsCString* aMessageId)
03509 {
03510   return NS_OK;
03511 }
03512 
03513 /* void onStateChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in unsigned long aStateFlags, in nsresult aStatus); */
03514 NS_IMETHODIMP nsMsgComposeSendListener::OnStateChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, PRUint32 aStateFlags, nsresult aStatus)
03515 {
03516   if (aStateFlags == nsIWebProgressListener::STATE_STOP)
03517   {
03518     nsCOMPtr<nsIMsgCompose>compose = do_QueryReferent(mWeakComposeObj);
03519     if (compose)
03520     {
03521       nsCOMPtr<nsIMsgProgress> progress;
03522       compose->GetProgress(getter_AddRefs(progress));
03523 
03524       //Time to stop any pending operation...
03525       if (progress)
03526       {
03527         //Unregister ourself from msg compose progress
03528         progress->UnregisterListener(this);
03529 
03530         PRBool  bCanceled = PR_FALSE;
03531         progress->GetProcessCanceledByUser(&bCanceled);
03532         if (bCanceled)
03533         {
03534           nsXPIDLString msg; 
03535           nsCOMPtr<nsIMsgStringService> strBundle = do_GetService(NS_MSG_COMPOSESTRINGSERVICE_CONTRACTID);
03536           strBundle->GetStringByID(NS_MSG_CANCELLING, getter_Copies(msg));
03537           progress->OnStatusChange(nsnull, nsnull, 0, msg);
03538         }
03539       }
03540 
03541       nsCOMPtr<nsIMsgSend> msgSend;
03542       compose->GetMessageSend(getter_AddRefs(msgSend));
03543       if (msgSend)
03544           msgSend->Abort();
03545     }
03546   }
03547   return NS_OK;
03548 }
03549 
03550 /* void onProgressChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long aCurSelfProgress, in long aMaxSelfProgress, in long aCurTotalProgress, in long aMaxTotalProgress); */
03551 NS_IMETHODIMP nsMsgComposeSendListener::OnProgressChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, PRInt32 aCurSelfProgress, PRInt32 aMaxSelfProgress, PRInt32 aCurTotalProgress, PRInt32 aMaxTotalProgress)
03552 {
03553   /* Ignore this call */
03554   return NS_OK;
03555 }
03556 
03557 /* void onLocationChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsIURI location); */
03558 NS_IMETHODIMP nsMsgComposeSendListener::OnLocationChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, nsIURI *location)
03559 {
03560   /* Ignore this call */
03561   return NS_OK;
03562 }
03563 
03564 /* void onStatusChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsresult aStatus, in wstring aMessage); */
03565 NS_IMETHODIMP nsMsgComposeSendListener::OnStatusChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, nsresult aStatus, const PRUnichar *aMessage)
03566 {
03567   /* Ignore this call */
03568   return NS_OK;
03569 }
03570 
03571 /* void onSecurityChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in unsigned long state); */
03572 NS_IMETHODIMP nsMsgComposeSendListener::OnSecurityChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, PRUint32 state)
03573 {
03574   /* Ignore this call */
03575   return NS_OK;
03576 }
03577 
03578 nsresult
03579 nsMsgCompose::ConvertHTMLToText(nsFileSpec& aSigFile, nsString &aSigData)
03580 {
03581   nsresult    rv;
03582   nsAutoString    origBuf;
03583 
03584   rv = LoadDataFromFile(aSigFile, origBuf);
03585   if (NS_FAILED(rv))
03586     return rv;
03587 
03588   ConvertBufToPlainText(origBuf,PR_FALSE);
03589   aSigData = origBuf;
03590   return NS_OK;
03591 }
03592 
03593 nsresult
03594 nsMsgCompose::ConvertTextToHTML(nsFileSpec& aSigFile, nsString &aSigData)
03595 {
03596   nsresult    rv;
03597   nsAutoString    origBuf;
03598 
03599   rv = LoadDataFromFile(aSigFile, origBuf);
03600   if (NS_FAILED(rv))
03601     return rv;
03602 
03603   // Ok, once we are here, we need to escape the data to make sure that
03604   // we don't do HTML stuff with plain text sigs.
03605   //
03606   PRUnichar *escaped = nsEscapeHTML2(origBuf.get());
03607   if (escaped) 
03608   {
03609     aSigData.Append(escaped);
03610     nsCRT::free(escaped);
03611   }
03612   else
03613   {
03614     aSigData.Append(origBuf);
03615   }
03616 
03617   return NS_OK;
03618 }
03619 
03620 nsresult
03621 nsMsgCompose::LoadDataFromFile(nsFileSpec& fSpec, nsString &sigData,
03622                                PRBool aAllowUTF8, PRBool aAllowUTF16)
03623 {
03624   PRInt32       readSize;
03625   PRInt32       nGot;
03626   char          *readBuf;
03627   char          *ptr;
03628 
03629   if (fSpec.IsDirectory()) {
03630     NS_ASSERTION(0,"file is a directory");
03631     return NS_MSG_ERROR_READING_FILE;  
03632   }
03633 
03634   nsInputFileStream tempFile(fSpec);
03635   if (!tempFile.is_open())
03636     return NS_MSG_ERROR_READING_FILE;  
03637 
03638   readSize = fSpec.GetFileSize();
03639   ptr = readBuf = (char *)PR_Malloc(readSize + 1);  if (!readBuf)
03640     return NS_ERROR_OUT_OF_MEMORY;
03641   memset(readBuf, 0, readSize + 1);
03642 
03643   while (readSize) {
03644     nGot = tempFile.read(ptr, readSize);
03645     if (nGot > 0) {
03646       readSize -= nGot;
03647       ptr += nGot;
03648     }
03649     else {
03650       readSize = 0;
03651     }
03652   }
03653   tempFile.close();
03654 
03655   nsCAutoString sigEncoding(nsMsgI18NParseMetaCharset(&fSpec));
03656   PRBool removeSigCharset = !sigEncoding.IsEmpty() && m_composeHTML;
03657 
03658   if (sigEncoding.IsEmpty()) {
03659     if (aAllowUTF8 && IsUTF8(nsDependentCString(readBuf))) {
03660       sigEncoding.Assign("UTF-8");
03661     }
03662     else if (sigEncoding.IsEmpty() && aAllowUTF16 &&
03663              fSpec.GetFileSize() % 2 == 0 && fSpec.GetFileSize() >= 2 &&
03664              ((readBuf[0] == char(0xFE) && readBuf[1] == char(0xFF)) ||
03665               (readBuf[0] == char(0xFF) && readBuf[1] == char(0xFE)))) {
03666       sigEncoding.Assign("UTF-16");
03667     }
03668     else {
03669       //default to platform encoding for plain text files w/o meta charset
03670       nsCAutoString textFileCharset;
03671       nsMsgI18NTextFileCharset(textFileCharset);
03672       sigEncoding.Assign(textFileCharset);
03673     }
03674   }
03675 
03676   nsCAutoString readStr(readBuf, fSpec.GetFileSize());
03677   PR_FREEIF(readBuf);
03678 
03679   if (NS_FAILED(ConvertToUnicode(sigEncoding.get(), readStr, sigData)))
03680     CopyASCIItoUTF16(readStr, sigData);
03681 
03682   //remove sig meta charset to allow user charset override during composition
03683   if (removeSigCharset)
03684   {
03685     nsAutoString metaCharset(NS_LITERAL_STRING("charset="));
03686     AppendASCIItoUTF16(sigEncoding, metaCharset);
03687     nsAString::const_iterator realstart, start, end;
03688     sigData.BeginReading(start);
03689     sigData.EndReading(end);
03690     realstart = start;
03691     if (FindInReadable(metaCharset, start, end,
03692                        nsCaseInsensitiveStringComparator()))
03693       sigData.Cut(Distance(realstart, start), Distance(start, end));
03694   }
03695 
03696   return NS_OK;
03697 }
03698 
03699 nsresult
03700 nsMsgCompose::BuildQuotedMessageAndSignature(void)
03701 {
03702   // 
03703   // This should never happen...if it does, just bail out...
03704   //
03705   NS_ASSERTION(m_editor, "BuildQuotedMessageAndSignature but no editor!\n");
03706   if (!m_editor)
03707     return NS_ERROR_FAILURE;
03708 
03709   // We will fire off the quote operation and wait for it to
03710   // finish before we actually do anything with Ender...
03711   return QuoteOriginalMessage(mOriginalMsgURI.get(), mWhatHolder);
03712 }
03713 
03714 //
03715 // This will process the signature file for the user. This method
03716 // will always append the results to the mMsgBody member variable.
03717 //
03718 nsresult
03719 nsMsgCompose::ProcessSignature(nsIMsgIdentity *identity, PRBool aQuoted, nsString *aMsgBody)
03720 {
03721   nsresult    rv = NS_OK;
03722 
03723   // Now, we can get sort of fancy. This is the time we need to check
03724   // for all sorts of user defined stuff, like signatures and editor
03725   // types and the like!
03726   //
03727   //    user_pref(".....sig_file", "y:\\sig.html");
03728   //    user_pref(".....attach_signature", true);
03729   //
03730   // Note: We will have intelligent signature behavior in that we
03731   // look at the signature file first...if the extension is .htm or 
03732   // .html, we assume its HTML, otherwise, we assume it is plain text
03733   //
03734   // ...and that's not all! What we will also do now is look and see if
03735   // the file is an image file. If it is an image file, then we should 
03736   // insert the correct HTML into the composer to have it work, but if we
03737   // are doing plain text compose, we should insert some sort of message
03738   // saying "Image Signature Omitted" or something.
03739   //
03740   nsCAutoString sigNativePath;
03741   PRBool        useSigFile = PR_FALSE;
03742   PRBool        htmlSig = PR_FALSE;
03743   PRBool        imageSig = PR_FALSE;
03744   nsAutoString  sigData;
03745   nsAutoString sigOutput;
03746   PRInt32      reply_on_top = 0;
03747   PRBool       sig_bottom = PR_TRUE;
03748 
03749   if (identity)
03750   {
03751     identity->GetReplyOnTop(&reply_on_top);
03752     identity->GetSigBottom(&sig_bottom);
03753     rv = identity->GetAttachSignature(&useSigFile);
03754     if (NS_SUCCEEDED(rv) && useSigFile) 
03755     {
03756       useSigFile = PR_FALSE;  // by default, assume no signature file!
03757 
03758       nsCOMPtr<nsILocalFile> sigFile;
03759       rv = identity->GetSignature(getter_AddRefs(sigFile));
03760       if (NS_SUCCEEDED(rv) && sigFile) {
03761         rv = sigFile->GetNativePath(sigNativePath);
03762         if (NS_SUCCEEDED(rv) && !sigNativePath.IsEmpty())
03763           useSigFile = PR_TRUE; // ok, there's a signature file
03764 
03765         // Now, most importantly, we need to figure out what the content type is for
03766         // this signature...if we can't, we assume text
03767         nsCAutoString sigContentType;
03768         nsresult rv2; // don't want to clobber the other rv
03769         nsCOMPtr<nsIMIMEService> mimeFinder (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv2));
03770         if (NS_SUCCEEDED(rv2)) {
03771           rv2 = mimeFinder->GetTypeFromFile(sigFile, sigContentType);
03772           if (NS_SUCCEEDED(rv2)) {
03773             if (StringBeginsWith(sigContentType, NS_LITERAL_CSTRING("image/"), nsCaseInsensitiveCStringComparator()))
03774               imageSig = PR_TRUE;
03775             else if (sigContentType.Equals(TEXT_HTML, nsCaseInsensitiveCStringComparator()))
03776               htmlSig = PR_TRUE;
03777           }
03778         }
03779       }
03780     }
03781   }
03782 
03783   // Now, if they didn't even want to use a signature, we should
03784   // just return nicely.
03785   //
03786   if (!useSigFile || NS_FAILED(rv))
03787     return NS_OK;
03788 
03789   nsFileSpec    testSpec(sigNativePath.get());
03790 
03791   // If this file doesn't really exist, just bail!
03792   if (!testSpec.Exists())
03793     return NS_OK;
03794 
03795   static const char      htmlBreak[] = "<BR>";
03796   static const char      dashes[] = "-- ";
03797   static const char      htmlsigopen[] = "<div class=\"moz-signature\">";
03798   static const char      htmlsigclose[] = "</div>";    /* XXX: Due to a bug in
03799                              4.x' HTML editor, it will not be able to
03800                              break this HTML sig, if quoted (for the user to
03801                              interleave a comment). */
03802   static const char      _preopen[] = "<pre class=\"moz-signature\" cols=%d>";
03803   char*                  preopen;
03804   static const char      preclose[] = "</pre>";
03805 
03806   PRInt32 wrapLength = 72; // setup default value in case GetWrapLength failed
03807   GetWrapLength(&wrapLength);
03808   preopen = PR_smprintf(_preopen, wrapLength);
03809   if (!preopen)
03810     return NS_ERROR_OUT_OF_MEMORY;
03811 
03812   if (imageSig)
03813   {
03814     // We have an image signature. If we're using the in HTML composer, we
03815     // should put in the appropriate HTML for inclusion, otherwise, do nothing.
03816     if (m_composeHTML)
03817     {
03818       sigOutput.AppendLiteral(htmlBreak);
03819       sigOutput.AppendLiteral(htmlsigopen);
03820       if (reply_on_top != 1 || sig_bottom || !aQuoted)
03821         sigOutput.AppendLiteral(dashes);
03822       sigOutput.AppendLiteral(htmlBreak);
03823       sigOutput.AppendLiteral("<img src=\"file:///");
03824            /* XXX pp This gives me 4 slashes on Unix, that's at least one to
03825               much. Better construct the URL with some service. */
03826       sigOutput.AppendWithConversion(testSpec);
03827       sigOutput.AppendLiteral("\" border=0>");
03828       sigOutput.AppendLiteral(htmlsigclose);
03829     }
03830   }
03831   else
03832   {
03833     // is this a text sig with an HTML editor?
03834     if ( (m_composeHTML) && (!htmlSig) )
03835       ConvertTextToHTML(testSpec, sigData);
03836     // is this a HTML sig with a text window?
03837     else if ( (!m_composeHTML) && (htmlSig) )
03838       ConvertHTMLToText(testSpec, sigData);
03839     else // We have a match...
03840       LoadDataFromFile(testSpec, sigData);  // Get the data!
03841   }
03842 
03843   // Now that sigData holds data...if any, append it to the body in a nice
03844   // looking manner
03845   if (!sigData.IsEmpty())
03846   {
03847     if (m_composeHTML)
03848     {
03849       sigOutput.AppendLiteral(htmlBreak);
03850       if (htmlSig)
03851         sigOutput.AppendLiteral(htmlsigopen);
03852       else
03853         sigOutput.AppendASCII(preopen);
03854     }
03855     else
03856       sigOutput.AppendLiteral(CRLF);
03857 
03858     if ((reply_on_top != 1 || sig_bottom || !aQuoted) &&
03859         sigData.Find("\r-- \r", PR_TRUE) < 0 &&
03860         sigData.Find("\n-- \n", PR_TRUE) < 0 &&
03861         sigData.Find("\n-- \r", PR_TRUE) < 0)
03862     {
03863       nsDependentSubstring firstFourChars(sigData, 0, 4);
03864 
03865       if (!(firstFourChars.EqualsLiteral("-- \n") ||
03866             firstFourChars.EqualsLiteral("-- \r")))
03867       {
03868         sigOutput.AppendLiteral(dashes);
03869 
03870         if (!m_composeHTML || !htmlSig)
03871           sigOutput.AppendLiteral(CRLF);
03872         else if (m_composeHTML)
03873           sigOutput.AppendLiteral(htmlBreak);
03874       }
03875     }
03876 
03877     sigOutput.Append(sigData);
03878 
03879     if (m_composeHTML)
03880     {
03881       if (htmlSig)
03882         sigOutput.AppendLiteral(htmlsigclose);
03883       else
03884         sigOutput.AppendLiteral(preclose);
03885     }
03886   }
03887 
03888   aMsgBody->Append(sigOutput);
03889   PR_Free(preopen);
03890   return NS_OK;
03891 }
03892 
03893 nsresult
03894 nsMsgCompose::BuildBodyMessageAndSignature()
03895 {
03896   nsresult    rv = NS_OK;
03897 
03898   // 
03899   // This should never happen...if it does, just bail out...
03900   //
03901   if (!m_editor)
03902     return NS_ERROR_FAILURE;
03903 
03904   // 
03905   // Now, we have the body so we can just blast it into the
03906   // composition editor window.
03907   //
03908   nsAutoString   body;
03909   m_compFields->GetBody(body);
03910 
03911   /* Some time we want to add a signature and sometime we wont. Let's figure that now...*/
03912   PRBool addSignature;
03913   switch (mType)
03914   {
03915     case nsIMsgCompType::New :
03916     case nsIMsgCompType::MailToUrl :    /* same as New */
03917     case nsIMsgCompType::Reply :        /* should not happen! but just in case */
03918     case nsIMsgCompType::ReplyAll :       /* should not happen! but just in case */
03919     case nsIMsgCompType::ForwardAsAttachment :  /* should not happen! but just in case */
03920     case nsIMsgCompType::ForwardInline :
03921     case nsIMsgCompType::NewsPost :
03922     case nsIMsgCompType::ReplyToGroup :
03923     case nsIMsgCompType::ReplyToSender : 
03924     case nsIMsgCompType::ReplyToSenderAndGroup :
03925       addSignature = PR_TRUE;
03926       break;
03927 
03928     case nsIMsgCompType::Draft :
03929     case nsIMsgCompType::Template :
03930       addSignature = PR_FALSE;
03931       break;
03932 
03933     default :
03934       addSignature = PR_FALSE;
03935       break;
03936   }
03937 
03938   nsAutoString tSignature;
03939 
03940   if (addSignature)
03941     ProcessSignature(m_identity, PR_FALSE, &tSignature);
03942 
03943   // if type is new, but we have body, this is probably a mapi send, so we need to 
03944   // replace '\n' with <br> so that the line breaks won't be lost by html.
03945   // if mailtourl, do the same.
03946   if (m_composeHTML && (mType == nsIMsgCompType::New || mType == nsIMsgCompType::MailToUrl))
03947     body.ReplaceSubstring(NS_LITERAL_STRING("\n").get(), NS_LITERAL_STRING("<br>").get());
03948 
03949   nsString empty;
03950   rv = ConvertAndLoadComposeWindow(empty, body, tSignature,
03951                                    PR_FALSE, m_composeHTML);
03952 
03953   return rv;
03954 }
03955 
03956 nsresult nsMsgCompose::NotifyStateListeners(TStateListenerNotification aNotificationType, nsresult aResult)
03957 {
03958 
03959   if (aNotificationType == eSaveInFolderDone)
03960     ResetUrisForEmbeddedObjects();
03961 
03962   if (!mStateListeners)
03963     return NS_OK;    // maybe there just aren't any.
03964 
03965   PRUint32 numListeners;
03966   nsresult rv = mStateListeners->Count(&numListeners);
03967   if (NS_FAILED(rv)) return rv;
03968 
03969   PRUint32 i;
03970   for (i = 0; i < numListeners;i++)
03971   {
03972     nsCOMPtr<nsIMsgComposeStateListener> thisListener =
03973       do_QueryElementAt(mStateListeners, i);
03974     if (thisListener)
03975     {
03976       switch (aNotificationType)
03977       {
03978         case eComposeFieldsReady:
03979           thisListener->NotifyComposeFieldsReady();
03980           break;
03981 
03982         case eComposeProcessDone:
03983           thisListener->ComposeProcessDone(aResult);
03984           break;
03985 
03986         case eSaveInFolderDone:
03987           thisListener->SaveInFolderDone(m_folderName.get());
03988           break;
03989 
03990         case eComposeBodyReady:
03991           thisListener->NotifyComposeBodyReady();
03992           break;
03993 
03994         default:
03995           NS_NOTREACHED("Unknown notification");
03996           break;
03997       }
03998     }
03999   }
04000 
04001   return NS_OK;
04002 }
04003 
04004 nsresult nsMsgCompose::AttachmentPrettyName(const char* scheme, const char* charset, nsACString& _retval)
04005 {
04006   nsresult rv;
04007 
04008   nsCOMPtr<nsIUTF8ConverterService> utf8Cvt =
04009     do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID);
04010   NS_ENSURE_TRUE(utf8Cvt, NS_ERROR_UNEXPECTED);
04011 
04012   nsCAutoString utf8Scheme;
04013 
04014   if (PL_strncasestr(scheme, "file:", 5)) 
04015   {
04016     nsCOMPtr<nsIFile> file;
04017     rv = NS_GetFileFromURLSpec(nsDependentCString(scheme),
04018                                getter_AddRefs(file));
04019     NS_ENSURE_SUCCESS(rv, rv);
04020     nsAutoString leafName;
04021     rv = file->GetLeafName(leafName);
04022     NS_ENSURE_SUCCESS(rv, rv);
04023     CopyUTF16toUTF8(leafName, _retval);
04024     return rv;
04025   }
04026 
04027   // To work around a mysterious bug in VC++ 6.
04028   const char* cset = (!charset || !*charset) ? "UTF-8" : charset;
04029   rv = utf8Cvt->ConvertURISpecToUTF8(nsDependentCString(scheme), 
04030                                      cset, utf8Scheme);
04031 
04032   if (NS_SUCCEEDED(rv)) {
04033     // Some ASCII characters still need to be escaped.
04034     NS_UnescapeURL(utf8Scheme.get(), utf8Scheme.Length(),
04035                    esc_SkipControl | esc_AlwaysCopy, _retval);
04036   } else {
04037     _retval.Assign(scheme);
04038   }
04039   if (PL_strncasestr(scheme, "http:", 5)) 
04040     _retval.Cut(0, 7);
04041 
04042   return NS_OK;
04043 }
04044 
04045 static nsresult OpenAddressBook(const char * dbUri, nsIAddrDatabase** aDatabase)
04046 {
04047   NS_ENSURE_ARG_POINTER(aDatabase);
04048 
04049   nsresult rv;
04050   nsCOMPtr<nsIAddressBook> addressBook = do_GetService(NS_ADDRESSBOOK_CONTRACTID, &rv);
04051   NS_ENSURE_SUCCESS(rv,rv);
04052 
04053   rv = addressBook->GetAbDatabaseFromURI(dbUri, aDatabase);
04054   NS_ENSURE_SUCCESS(rv,rv);
04055   return rv;
04056 }
04057 
04058 nsresult nsMsgCompose::GetABDirectories(const nsACString& dirUri, nsISupportsArray* directoriesArray, PRBool searchSubDirectory)
04059 {
04060   static PRBool collectedAddressbookFound;
04061   if (dirUri.EqualsLiteral(kMDBDirectoryRoot))
04062     collectedAddressbookFound = PR_FALSE;
04063 
04064   nsresult rv = NS_OK;
04065   nsCOMPtr<nsIRDFService> rdfService (do_GetService("@mozilla.org/rdf/rdf-service;1", &rv));
04066   if (NS_FAILED(rv)) return rv;
04067 
04068   nsCOMPtr <nsIRDFResource> resource;
04069   rv = rdfService->GetResource(dirUri, getter_AddRefs(resource));
04070   if (NS_FAILED(rv)) return rv;
04071 
04072   // query interface 
04073   nsCOMPtr<nsIAbDirectory> directory(do_QueryInterface(resource, &rv));
04074   if (NS_FAILED(rv)) return rv;
04075 
04076   if (!searchSubDirectory)
04077       return rv;
04078 
04079   nsCOMPtr<nsISimpleEnumerator> subDirectories;
04080   if (NS_SUCCEEDED(directory->GetChildNodes(getter_AddRefs(subDirectories))) && subDirectories)
04081   {
04082     nsCOMPtr<nsISupports> item;
04083     PRBool hasMore;
04084     while (NS_SUCCEEDED(rv = subDirectories->HasMoreElements(&hasMore)) && hasMore)
04085     {
04086       if (NS_SUCCEEDED(subDirectories->GetNext(getter_AddRefs(item))))
04087       {
04088         directory = do_QueryInterface(item, &rv);
04089         if (NS_SUCCEEDED(rv))
04090         {
04091           PRBool bIsMailList;
04092 
04093           if (NS_SUCCEEDED(directory->GetIsMailList(&bIsMailList)) && bIsMailList)
04094             continue;
04095 
04096           nsCOMPtr<nsIRDFResource> source(do_QueryInterface(directory));
04097 
04098           nsXPIDLCString uri;
04099           // rv = directory->GetDirUri(getter_Copies(uri));
04100           rv = source->GetValue(getter_Copies(uri));
04101           NS_ENSURE_SUCCESS(rv, rv);
04102 
04103           PRInt32 pos;
04104           if (nsCRT::strcmp((const char *)uri, kPersonalAddressbookUri) == 0)
04105             pos = 0;
04106           else
04107           {
04108             PRUint32 count = 0;
04109             directoriesArray->Count(&count);
04110 
04111             if (PL_strcmp((const char *)uri, kCollectedAddressbookUri) == 0)
04112             {
04113               collectedAddressbookFound = PR_TRUE;
04114               pos = count;
04115             }
04116             else
04117             {
04118               if (collectedAddressbookFound && count > 1)
04119                 pos = count - 1;
04120               else
04121                 pos = count;
04122             }
04123           }
04124 
04125           directoriesArray->InsertElementAt(directory, pos);
04126           rv = GetABDirectories(uri, directoriesArray, PR_TRUE);
04127         }
04128       }
04129     }
04130   }
04131   return rv;
04132 }
04133 
04134 nsresult nsMsgCompose::BuildMailListArray(nsIAddrDatabase* database, nsIAbDirectory* parentDir, nsISupportsArray* array)
04135 {
04136   nsresult rv;
04137 
04138   nsCOMPtr<nsIAbDirectory> directory;
04139   nsCOMPtr<nsISimpleEnumerator> subDirectories;
04140 
04141   if (NS_SUCCEEDED(parentDir->GetChildNodes(getter_AddRefs(subDirectories))) && subDirectories)
04142   {
04143     nsCOMPtr<nsISupports> item;
04144     PRBool hasMore;
04145     while (NS_SUCCEEDED(rv = subDirectories->HasMoreElements(&hasMore)) && hasMore)
04146     {
04147       if (NS_SUCCEEDED(subDirectories->GetNext(getter_AddRefs(item))))
04148       {
04149         directory = do_QueryInterface(item, &rv);
04150         if (NS_SUCCEEDED(rv))
04151         {
04152           PRBool bIsMailList;
04153 
04154           if (NS_SUCCEEDED(directory->GetIsMailList(&bIsMailList)) && bIsMailList)
04155           {
04156             nsXPIDLString listName;
04157             nsXPIDLString listDescription;
04158 
04159             directory->GetDirName(getter_Copies(listName));
04160             directory->GetDescription(getter_Copies(listDescription));
04161 
04162             nsMsgMailList* mailList = new nsMsgMailList(nsAutoString((const PRUnichar*)listName),
04163                   nsAutoString((const PRUnichar*)listDescription), directory);
04164             if (!mailList)
04165               return NS_ERROR_OUT_OF_MEMORY;
04166             NS_ADDREF(mailList);
04167 
04168             rv = array->AppendElement(mailList);
04169             if (NS_FAILED(rv))
04170               return rv;
04171 
04172             NS_RELEASE(mailList);
04173           }
04174         }
04175       }
04176     }
04177   }
04178   return rv;
04179 }
04180 
04181 
04182 nsresult nsMsgCompose::GetMailListAddresses(nsString& name, nsISupportsArray* mailListArray, nsISupportsArray** addressesArray)
04183 {
04184   nsresult rv;
04185   nsCOMPtr<nsIEnumerator> enumerator;
04186 
04187   rv = mailListArray->Enumerate(getter_AddRefs(enumerator));
04188   if (NS_SUCCEEDED(rv))
04189   {
04190     for (rv = enumerator->First(); NS_SUCCEEDED(rv); rv = enumerator->Next())
04191     {
04192       nsMsgMailList* mailList;
04193       rv = enumerator->CurrentItem((nsISupports**)&mailList);
04194       if (NS_SUCCEEDED(rv) && mailList)
04195       {
04196         if (name.Equals(mailList->mFullName, nsCaseInsensitiveStringComparator()))
04197         {
04198           if (!mailList->mDirectory)
04199             return NS_ERROR_FAILURE;
04200 
04201           mailList->mDirectory->GetAddressLists(addressesArray);
04202           NS_RELEASE(mailList);
04203           return NS_OK;
04204         }
04205         NS_RELEASE(mailList);
04206       }
04207     }
04208   }
04209 
04210   return NS_ERROR_FAILURE;
04211 }
04212 
04213 
04214 // 3 = To, Cc, Bcc
04215 #define MAX_OF_RECIPIENT_ARRAY    3
04216 
04217 NS_IMETHODIMP nsMsgCompose::CheckAndPopulateRecipients(PRBool populateMailList, PRBool returnNonHTMLRecipients, PRUnichar **nonHTMLRecipients, PRUint32 *_retval)
04218 {
04219   if (returnNonHTMLRecipients && !nonHTMLRecipients || !_retval)
04220     return NS_ERROR_INVALID_ARG;
04221 
04222   nsresult rv = NS_OK;
04223   PRInt32 i;
04224   PRInt32 j;
04225   PRInt32 k;
04226 
04227   if (nonHTMLRecipients)
04228     *nonHTMLRecipients = nsnull;
04229   if (_retval)
04230     *_retval = nsIAbPreferMailFormat::unknown;
04231 
04232   /* First, build an array with original recipients */
04233   nsCOMArray<nsMsgRecipient> recipientsList[MAX_OF_RECIPIENT_ARRAY];
04234 
04235   nsAutoString originalRecipients[MAX_OF_RECIPIENT_ARRAY];
04236   m_compFields->GetTo(originalRecipients[0]);
04237   m_compFields->GetCc(originalRecipients[1]);
04238   m_compFields->GetBcc(originalRecipients[2]);
04239 
04240   nsCOMPtr<nsIMsgRecipientArray> addressArray;
04241   nsCOMPtr<nsIMsgRecipientArray> emailArray;
04242   for (i = 0; i < MAX_OF_RECIPIENT_ARRAY; i ++)
04243   {
04244     if (originalRecipients[i].IsEmpty())
04245       continue;
04246     rv = m_compFields->SplitRecipientsEx(originalRecipients[i].get(),
04247                                          getter_AddRefs(addressArray), getter_AddRefs(emailArray));
04248     if (NS_SUCCEEDED(rv))
04249     {
04250       PRInt32 nbrRecipients;
04251       nsXPIDLString emailAddr;
04252       nsXPIDLString addr;
04253       addressArray->GetCount(&nbrRecipients);
04254 
04255       for (j = 0; j < nbrRecipients; j ++)
04256       {
04257         rv = addressArray->StringAt(j, getter_Copies(addr));
04258         if (NS_FAILED(rv))
04259           return rv;
04260 
04261         rv = emailArray->StringAt(j, getter_Copies(emailAddr));
04262         if (NS_FAILED(rv))
04263           return rv;
04264 
04265         nsMsgRecipient* recipient = new nsMsgRecipient(nsAutoString(addr), nsAutoString(emailAddr));
04266         if (!recipient)
04267            return NS_ERROR_OUT_OF_MEMORY;
04268         NS_ADDREF(recipient);
04269         rv = recipientsList[i].AppendObject(recipient) ? NS_OK : NS_ERROR_FAILURE;
04270         NS_RELEASE(recipient);
04271         if (NS_FAILED(rv))
04272           return rv;
04273       }
04274     }
04275     else
04276       return rv;
04277   }
04278 
04279   /* Then look them up in the Addressbooks*/
04280 
04281   PRBool stillNeedToSearch = PR_TRUE;
04282   nsCOMPtr<nsIAddrDatabase> abDataBase;
04283   nsCOMPtr<nsIAbDirectory> abDirectory;   
04284   nsCOMPtr <nsIAbCard> existingCard;
04285   nsCOMPtr <nsISupportsArray> mailListAddresses;
04286   nsCOMPtr<nsIMsgHeaderParser> parser (do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID));
04287   nsCOMPtr<nsISupportsArray> mailListArray (do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv));
04288   if (NS_FAILED(rv))
04289     return rv;
04290 
04291   nsCOMPtr<nsISupportsArray> addrbookDirArray (do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv));
04292   if (NS_SUCCEEDED(rv) && addrbookDirArray)
04293   {
04294     nsString dirPath;
04295     GetABDirectories(NS_LITERAL_CSTRING(kAllDirectoryRoot), addrbookDirArray, PR_TRUE);
04296     PRInt32 nbrRecipients;
04297 
04298     PRBool dirtyABDatabase; 
04299     PRUint32 nbrAddressbook;
04300     addrbookDirArray->Count(&nbrAddressbook);
04301     for (k = 0; k < (PRInt32)nbrAddressbook && stillNeedToSearch; k ++)
04302     {
04303       nsCOMPtr<nsISupports> item;
04304       addrbookDirArray->GetElementAt(k, getter_AddRefs(item));
04305 
04306       // Avoid recursive mailing lists
04307       if (abDirectory && (item == abDirectory))
04308       {
04309         stillNeedToSearch = PR_FALSE;
04310         break;
04311       }
04312 
04313       abDirectory = do_QueryInterface(item, &rv);
04314       if (NS_FAILED(rv))
04315         return rv;
04316 
04317       nsCOMPtr<nsIRDFResource> source(do_QueryInterface(abDirectory));
04318 
04319       nsXPIDLCString uri;
04320       rv = source->GetValue(getter_Copies(uri));
04321       NS_ENSURE_SUCCESS(rv, rv);
04322 
04323       PRBool supportsMailingLists;
04324       rv = abDirectory->GetSupportsMailingLists(&supportsMailingLists);
04325       if (NS_FAILED(rv) || !supportsMailingLists)
04326         continue;
04327 
04328       dirtyABDatabase = PR_FALSE; 
04329       rv = OpenAddressBook(uri.get(), getter_AddRefs(abDataBase));
04330       if (NS_FAILED(rv) || !abDataBase)
04331         continue;
04332 
04333       /* Collect all mailing list defined in this address book */
04334       rv = BuildMailListArray(abDataBase, abDirectory, mailListArray);
04335       if (NS_FAILED(rv))
04336         return rv;
04337 
04338       stillNeedToSearch = PR_FALSE;
04339       for (i = 0; i < MAX_OF_RECIPIENT_ARRAY; i ++)
04340       {
04341         nbrRecipients = recipientsList[i].Count();
04342         if (nbrRecipients == 0)
04343           continue;
04344         for (j = 0; j < nbrRecipients; j++, nbrRecipients = recipientsList[i].Count())
04345         {
04346           nsMsgRecipient* recipient = recipientsList[i][j];
04347           if (recipient && !recipient->mProcessed)
04348           {
04349             /* First check if it's a mailing list */
04350             if (NS_SUCCEEDED(GetMailListAddresses(recipient->mAddress, mailListArray, getter_AddRefs(mailListAddresses))))
04351             {
04352               if (populateMailList)
04353               {
04354                   PRUint32 nbrAddresses = 0;
04355                   for (mailListAddresses->Count(&nbrAddresses); nbrAddresses > 0; nbrAddresses --)
04356                   {
04357                     existingCard = do_QueryElementAt(mailListAddresses,
04358                                                      nbrAddresses - 1, &rv);
04359                     if (NS_FAILED(rv))
04360                       return rv;
04361 
04362                     nsXPIDLString pDisplayName;
04363                     nsXPIDLString pEmail;
04364                     nsAutoString fullNameStr;
04365 
04366                     PRBool bIsMailList;
04367                     rv = existingCard->GetIsMailList(&bIsMailList);
04368                     if (NS_FAILED(rv))
04369                       return rv;
04370 
04371                     rv = existingCard->GetDisplayName(getter_Copies(pDisplayName));
04372                     if (NS_FAILED(rv))
04373                       return rv;
04374 
04375                     if (bIsMailList)
04376                       rv = existingCard->GetNotes(getter_Copies(pEmail));
04377                     else
04378                       rv = existingCard->GetPrimaryEmail(getter_Copies(pEmail));
04379                     if (NS_FAILED(rv))
04380                       return rv;
04381 
04382                     if (parser)
04383                     {
04384                       nsXPIDLCString fullAddress;
04385 
04386                       parser->MakeFullAddress(nsnull, NS_ConvertUCS2toUTF8(pDisplayName).get(),
04387                                               NS_ConvertUCS2toUTF8(pEmail).get(), getter_Copies(fullAddress));
04388                       if (!fullAddress.IsEmpty())
04389                       {
04390                         /* We need to convert back the result from UTF-8 to Unicode */
04391                         CopyUTF8toUTF16(fullAddress, fullNameStr);
04392                       }
04393                     }
04394                     if (fullNameStr.IsEmpty())
04395                     {
04396                       //oops, parser problem! I will try to do my best...
04397                       fullNameStr = pDisplayName;
04398                       fullNameStr.AppendLiteral(" <");
04399                       if (bIsMailList)
04400                       {
04401                         if (pEmail && pEmail[0] != 0)
04402                           fullNameStr += pEmail;
04403                         else
04404                           fullNameStr += pDisplayName;
04405                       }
04406                       else
04407                         fullNameStr += pEmail;
04408                       fullNameStr.Append(PRUnichar('>'));
04409                     }
04410 
04411                     if (fullNameStr.IsEmpty())
04412                       continue;
04413 
04414                     /* Now we need to insert the new address into the list of recipient */
04415                     nsMsgRecipient* newRecipient = new nsMsgRecipient(fullNameStr, nsAutoString(pEmail));
04416                     if (!recipient)
04417                        return  NS_ERROR_OUT_OF_MEMORY;
04418                     NS_ADDREF(newRecipient);
04419 
04420                     if (bIsMailList)
04421                     {
04422                       stillNeedToSearch = PR_TRUE;
04423                     }
04424                     else
04425                     {
04426                       newRecipient->mPreferFormat = nsIAbPreferMailFormat::unknown;
04427                       rv = existingCard->GetPreferMailFormat(&newRecipient->mPreferFormat);
04428                       if (NS_SUCCEEDED(rv))
04429                         recipient->mProcessed = PR_TRUE;
04430                     }
04431                     rv = recipientsList[i].InsertObjectAt(newRecipient,
04432                                                           j + 1) ? NS_OK : NS_ERROR_FAILURE;
04433                     NS_RELEASE(newRecipient);
04434                     if (NS_FAILED(rv))
04435                       return rv;
04436                   }
04437                   rv = recipientsList[i].RemoveObjectAt(j) ? NS_OK : NS_ERROR_FAILURE;
04438                  j --;
04439               }
04440               else
04441                 recipient->mProcessed = PR_TRUE;
04442 
04443               continue;
04444             }
04445 
04446             // Then if we have a card for this email address
04447             // Please DO NOT change the 4th param of GetCardFromAttribute() call to 
04448             // PR_TRUE (ie, case insensitive) without reading bugs #128535 and #121478.
04449             rv = abDataBase->GetCardFromAttribute(abDirectory, kPriEmailColumn, NS_LossyConvertUCS2toASCII(recipient->mEmail).get(), PR_FALSE /* case insensitive */, getter_AddRefs(existingCard));
04450             if (NS_SUCCEEDED(rv) && existingCard)
04451             {
04452               recipient->mPreferFormat = nsIAbPreferMailFormat::unknown;
04453               rv = existingCard->GetPreferMailFormat(&recipient->mPreferFormat);
04454               if (NS_SUCCEEDED(rv))
04455                 recipient->mProcessed = PR_TRUE;
04456 
04457               // bump the popularity index for this card since we are about to send e-mail to it
04458               PRUint32 popularityIndex = 0;
04459               if (NS_SUCCEEDED(existingCard->GetPopularityIndex(&popularityIndex)))
04460               {
04461                 existingCard->SetPopularityIndex(++popularityIndex);
04462                 abDataBase->EditCard(existingCard, PR_FALSE);
04463                 dirtyABDatabase = PR_TRUE;
04464               }
04465             }
04466             else
04467               stillNeedToSearch = PR_TRUE;
04468           }
04469         }
04470       }
04471 
04472         abDataBase->Close(dirtyABDatabase); // commit the database changes if we updated the popularity count.
04473     }
04474   }
04475 
04476   /* Finally return the list of non HTML recipient if requested and/or rebuilt the recipient field.
04477      Also, check for domain preference when preferFormat is unknown
04478   */
04479     nsAutoString recipientsStr;
04480     nsAutoString nonHtmlRecipientsStr;
04481     nsXPIDLString plaintextDomains;
04482     nsXPIDLString htmlDomains;
04483     nsAutoString domain;
04484 
04485     nsCOMPtr<nsIPrefBranch> prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID));
04486     if (prefBranch)
04487     {
04488       NS_GetLocalizedUnicharPreferenceWithDefault(prefBranch, "mailnews.plaintext_domains", EmptyString(),
04489                                                   plaintextDomains);
04490       NS_GetLocalizedUnicharPreferenceWithDefault(prefBranch, "mailnews.html_domains", EmptyString(),
04491                                                   htmlDomains);
04492     }
04493 
04494     PRBool atLeastOneRecipientPrefersUnknown = PR_FALSE;
04495     PRBool atLeastOneRecipientPrefersPlainText = PR_FALSE;
04496     PRBool atLeastOneRecipientPrefersHTML = PR_FALSE;
04497 
04498     for (i = 0; i < MAX_OF_RECIPIENT_ARRAY; i ++)
04499     {
04500       PRInt32 nbrRecipients = recipientsList[i].Count();
04501       if (nbrRecipients == 0)
04502         continue;
04503       recipientsStr.SetLength(0);
04504 
04505       for (j = 0; j < nbrRecipients; j ++)
04506       {
04507         nsMsgRecipient* recipient = recipientsList[i][j];
04508         if (recipient)
04509         {          
04510           /* if we don't have a prefer format for a recipient, check the domain in case we have a format defined for it */
04511           if (recipient->mPreferFormat == nsIAbPreferMailFormat::unknown &&
04512               (plaintextDomains.Length() || htmlDomains.Length()))
04513           {
04514             PRInt32 atPos = recipient->mEmail.FindChar('@');
04515             if (atPos >= 0)
04516             {
04517               recipient->mEmail.Right(domain, recipient->mEmail.Length() - atPos - 1);
04518               if (FindInReadable(domain, plaintextDomains, nsCaseInsensitiveStringComparator()))
04519                 recipient->mPreferFormat = nsIAbPreferMailFormat::plaintext;
04520               else
04521                 if (FindInReadable(domain, htmlDomains, nsCaseInsensitiveStringComparator()))
04522                   recipient->mPreferFormat = nsIAbPreferMailFormat::html;
04523             }
04524           }
04525 
04526           switch (recipient->mPreferFormat)
04527           {
04528             case nsIAbPreferMailFormat::html:
04529               atLeastOneRecipientPrefersHTML = PR_TRUE;
04530               break;
04531 
04532             case nsIAbPreferMailFormat::plaintext:
04533               atLeastOneRecipientPrefersPlainText = PR_TRUE;
04534               break;
04535 
04536             default: /* nsIAbPreferMailFormat::unknown */
04537               atLeastOneRecipientPrefersUnknown = PR_TRUE;
04538               break;
04539           }
04540 
04541           if (populateMailList)
04542           {
04543             if (! recipientsStr.IsEmpty())
04544               recipientsStr.Append(PRUnichar(','));
04545             recipientsStr.Append(recipient->mAddress);
04546           }
04547 
04548           if (returnNonHTMLRecipients && recipient->mPreferFormat != nsIAbPreferMailFormat::html)
04549           {
04550             if (! nonHtmlRecipientsStr.IsEmpty())
04551               nonHtmlRecipientsStr.Append(PRUnichar(','));
04552             nonHtmlRecipientsStr.Append(recipient->mEmail);
04553           }
04554 
04555         }
04556       }
04557 
04558       if (populateMailList)
04559       {
04560         switch (i)
04561         {
04562           case 0 : m_compFields->SetTo(recipientsStr);  break;
04563           case 1 : m_compFields->SetCc(recipientsStr);  break;
04564           case 2 : m_compFields->SetBcc(recipientsStr); break;
04565         }
04566       }
04567   }
04568 
04569   if (returnNonHTMLRecipients)
04570     *nonHTMLRecipients = ToNewUnicode(nonHtmlRecipientsStr);
04571 
04572   if (atLeastOneRecipientPrefersUnknown)
04573     *_retval = nsIAbPreferMailFormat::unknown;
04574   else if (atLeastOneRecipientPrefersHTML)
04575   {
04576     // if we have at least one recipient that prefers html
04577     // and at least one that recipients that prefers plain text
04578     // we need to return unknown, so that we can prompt the user
04579     if (atLeastOneRecipientPrefersPlainText)
04580       *_retval = nsIAbPreferMailFormat::unknown;
04581     else
04582       *_retval = nsIAbPreferMailFormat::html;
04583   }
04584   else 
04585   {
04586     NS_ASSERTION(atLeastOneRecipientPrefersPlainText, "at least one should prefer plain text");
04587     *_retval = nsIAbPreferMailFormat::plaintext;
04588   }      
04589 
04590   return rv;
04591 }
04592 
04593 /* Decides which tags trigger which convertible mode, i.e. here is the logic
04594    for BodyConvertible */
04595 // Helper function. Parameters are not checked.
04596 nsresult nsMsgCompose::TagConvertible(nsIDOMNode *node,  PRInt32 *_retval)
04597 {
04598     nsresult rv;
04599 
04600     *_retval = nsIMsgCompConvertible::No;
04601 
04602     PRUint16 nodeType;
04603     rv = node->GetNodeType(&nodeType);
04604     if (NS_FAILED(rv))
04605       return rv;
04606 
04607     nsAutoString element;
04608     rv = node->GetNodeName(element);
04609     if (NS_FAILED(rv))
04610       return rv;
04611 
04612     nsCOMPtr<nsIDOMNode> pItem;
04613     if      (
04614               nodeType == nsIDOMNode::TEXT_NODE ||
04615               element.LowerCaseEqualsLiteral("br") ||
04616               element.LowerCaseEqualsLiteral("p") ||
04617               element.LowerCaseEqualsLiteral("pre") ||
04618               element.LowerCaseEqualsLiteral("tt") ||
04619               element.LowerCaseEqualsLiteral("html") ||
04620               element.LowerCaseEqualsLiteral("head") ||
04621               element.LowerCaseEqualsLiteral("title")
04622             )
04623     {
04624       *_retval = nsIMsgCompConvertible::Plain;
04625     }
04626     else if (
04627               //element.LowerCaseEqualsLiteral("blockquote") || // see below
04628               element.LowerCaseEqualsLiteral("ul") ||
04629               element.LowerCaseEqualsLiteral("ol") ||
04630               element.LowerCaseEqualsLiteral("li") ||
04631               element.LowerCaseEqualsLiteral("dl") ||
04632               element.LowerCaseEqualsLiteral("dt") ||
04633               element.LowerCaseEqualsLiteral("dd")
04634             )
04635     {
04636       *_retval = nsIMsgCompConvertible::Yes;
04637     }
04638     else if (
04639               //element.LowerCaseEqualsLiteral("a") || // see below
04640               element.LowerCaseEqualsLiteral("h1") ||
04641               element.LowerCaseEqualsLiteral("h2") ||
04642               element.LowerCaseEqualsLiteral("h3") ||
04643               element.LowerCaseEqualsLiteral("h4") ||
04644               element.LowerCaseEqualsLiteral("h5") ||
04645               element.LowerCaseEqualsLiteral("h6") ||
04646               element.LowerCaseEqualsLiteral("hr") ||
04647               (
04648                 mConvertStructs
04649                 &&
04650                 (
04651                   element.LowerCaseEqualsLiteral("em") ||
04652                   element.LowerCaseEqualsLiteral("strong") ||
04653                   element.LowerCaseEqualsLiteral("code") ||
04654                   element.LowerCaseEqualsLiteral("b") ||
04655                   element.LowerCaseEqualsLiteral("i") ||
04656                   element.LowerCaseEqualsLiteral("u")
04657                 )
04658               )
04659             )
04660     {
04661       *_retval = nsIMsgCompConvertible::Altering;
04662     }
04663     else if (element.LowerCaseEqualsLiteral("body"))
04664     {
04665       *_retval = nsIMsgCompConvertible::Plain;
04666 
04667       nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(node);
04668       if (domElement)
04669       {
04670         PRBool hasAttribute;
04671         nsAutoString color;
04672         if (NS_SUCCEEDED(domElement->HasAttribute(NS_LITERAL_STRING("background"), &hasAttribute))
04673             && hasAttribute)  // There is a background image
04674           *_retval = nsIMsgCompConvertible::No; 
04675         else if (NS_SUCCEEDED(domElement->HasAttribute(NS_LITERAL_STRING("text"), &hasAttribute)) &&
04676                  hasAttribute &&
04677                  NS_SUCCEEDED(domElement->GetAttribute(NS_LITERAL_STRING("text"), color)) &&
04678                  !color.EqualsLiteral("#000000")) {
04679           *_retval = nsIMsgCompConvertible::Altering;
04680         }
04681         else if (NS_SUCCEEDED(domElement->HasAttribute(NS_LITERAL_STRING("bgcolor"), &hasAttribute)) &&
04682                  hasAttribute &&
04683                  NS_SUCCEEDED(domElement->GetAttribute(NS_LITERAL_STRING("bgcolor"), color)) &&
04684                  !color.LowerCaseEqualsLiteral("#ffffff")) {
04685           *_retval = nsIMsgCompConvertible::Altering;
04686         }
04687               else if (NS_SUCCEEDED(domElement->HasAttribute(NS_LITERAL_STRING("dir"), &hasAttribute))
04688             && hasAttribute)  // dir=rtl attributes should not downconvert
04689           *_retval = nsIMsgCompConvertible::No; 
04690 
04691         //ignore special color setting for link, vlink and alink at this point.
04692       }
04693 
04694     }
04695     else if (element.LowerCaseEqualsLiteral("blockquote"))
04696     {
04697       // Skip <blockquote type="cite">
04698       *_retval = nsIMsgCompConvertible::Yes;
04699 
04700       nsCOMPtr<nsIDOMNamedNodeMap> pAttributes;
04701       if (NS_SUCCEEDED(node->GetAttributes(getter_AddRefs(pAttributes)))
04702           && pAttributes)
04703       {
04704         nsAutoString typeName; typeName.AssignLiteral("type");
04705         if (NS_SUCCEEDED(pAttributes->GetNamedItem(typeName,
04706                                                    getter_AddRefs(pItem)))
04707             && pItem)
04708         {
04709           nsAutoString typeValue;
04710           if (NS_SUCCEEDED(pItem->GetNodeValue(typeValue)))
04711           {
04712             typeValue.StripChars("\"");
04713             if (typeValue.LowerCaseEqualsLiteral("cite"))
04714               *_retval = nsIMsgCompConvertible::Plain;
04715           }
04716         }
04717       }
04718     }
04719     else if (
04720               element.LowerCaseEqualsLiteral("div") ||
04721               element.LowerCaseEqualsLiteral("span") ||
04722               element.LowerCaseEqualsLiteral("a")
04723             )
04724     {
04725       /* Do some special checks for these tags. They are inside this |else if|
04726          for performance reasons */
04727       nsCOMPtr<nsIDOMNamedNodeMap> pAttributes;
04728 
04729       /* First, test, if the <a>, <div> or <span> is inserted by our
04730          [TXT|HTML]->HTML converter */
04731       /* This is for an edge case: A Mozilla user replies to plaintext per HTML
04732          and the recipient of that HTML msg, also a Mozilla user, replies to
04733          that again. Then we'll have to recognize the stuff inserted by our
04734          TXT->HTML converter. */
04735       if (NS_SUCCEEDED(node->GetAttributes(getter_AddRefs(pAttributes)))
04736           && pAttributes)
04737       {
04738         nsAutoString className;
04739         className.AssignLiteral("class");
04740         if (NS_SUCCEEDED(pAttributes->GetNamedItem(className,
04741                                                    getter_AddRefs(pItem)))
04742             && pItem)
04743         {
04744           nsAutoString classValue;
04745           if (NS_SUCCEEDED(pItem->GetNodeValue(classValue))
04746               && (classValue.EqualsIgnoreCase("moz-txt", 7) ||
04747                   classValue.EqualsIgnoreCase("\"moz-txt", 8)))
04748           {
04749             *_retval = nsIMsgCompConvertible::Plain;
04750             return rv;  // Inconsistent :-(
04751           }
04752         }
04753       }
04754 
04755       // Maybe, it's an <a> element inserted by another recognizer (e.g. 4.x')
04756       if (element.LowerCaseEqualsLiteral("a"))
04757       {
04758         /* Ignore anchor tag, if the URI is the same as the text
04759            (as inserted by recognizers) */
04760         *_retval = nsIMsgCompConvertible::Altering;
04761 
04762         if (NS_SUCCEEDED(node->GetAttributes(getter_AddRefs(pAttributes)))
04763             && pAttributes)
04764         {
04765           nsAutoString hrefName; hrefName.AssignLiteral("href");
04766           if (NS_SUCCEEDED(pAttributes->GetNamedItem(hrefName,
04767                                                      getter_AddRefs(pItem)))
04768               && pItem)
04769           {
04770             nsAutoString hrefValue;
04771             PRBool hasChild;
04772             if (NS_SUCCEEDED(pItem->GetNodeValue(hrefValue))
04773                 && NS_SUCCEEDED(node->HasChildNodes(&hasChild)) && hasChild)
04774             {
04775               nsCOMPtr<nsIDOMNodeList> children;
04776               if (NS_SUCCEEDED(node->GetChildNodes(getter_AddRefs(children)))
04777                   && children
04778                   && NS_SUCCEEDED(children->Item(0, getter_AddRefs(pItem)))
04779                   && pItem)
04780               {
04781                 nsAutoString textValue;
04782                 if (NS_SUCCEEDED(pItem->GetNodeValue(textValue))
04783                     && textValue == hrefValue)
04784                   *_retval = nsIMsgCompConvertible::Plain;
04785               }
04786             }
04787           }
04788         }
04789       }
04790 
04791       // Lastly, test, if it is just a "simple" <div> or <span>
04792       else if (
04793                 element.LowerCaseEqualsLiteral("div") ||
04794                 element.LowerCaseEqualsLiteral("span")
04795               )
04796       {
04797         /* skip only if no style attribute */
04798         *_retval = nsIMsgCompConvertible::Plain;
04799 
04800         if (NS_SUCCEEDED(node->GetAttributes(getter_AddRefs(pAttributes)))
04801             && pAttributes)
04802         {
04803           nsAutoString styleName;
04804           styleName.AssignLiteral("style");
04805           if (NS_SUCCEEDED(pAttributes->GetNamedItem(styleName,
04806                                                      getter_AddRefs(pItem)))
04807               && pItem)
04808           {
04809             nsAutoString styleValue;
04810             if (NS_SUCCEEDED(pItem->GetNodeValue(styleValue))
04811                 && !styleValue.IsEmpty())
04812               *_retval = nsIMsgCompConvertible::No;
04813           }
04814         }
04815       }
04816     }
04817 
04818     return rv;
04819 }
04820 
04821 nsresult nsMsgCompose::_BodyConvertible(nsIDOMNode *node, PRInt32 *_retval)
04822 {
04823     NS_ENSURE_TRUE(node && _retval, NS_ERROR_NULL_POINTER);
04824 
04825     nsresult rv;
04826     PRInt32 result;
04827 
04828     // Check this node
04829     rv = TagConvertible(node, &result);
04830     if (NS_FAILED(rv))
04831         return rv;
04832 
04833     // Walk tree recursively to check the children
04834     PRBool hasChild;
04835     if (NS_SUCCEEDED(node->HasChildNodes(&hasChild)) && hasChild)
04836     {
04837       nsCOMPtr<nsIDOMNodeList> children;
04838       if (NS_SUCCEEDED(node->GetChildNodes(getter_AddRefs(children)))
04839           && children)
04840       {
04841         PRUint32 nbrOfElements;
04842         rv = children->GetLength(&nbrOfElements);
04843         for (PRUint32 i = 0; NS_SUCCEEDED(rv) && i < nbrOfElements; i++)
04844         {
04845           nsCOMPtr<nsIDOMNode> pItem;
04846           if (NS_SUCCEEDED(children->Item(i, getter_AddRefs(pItem)))
04847               && pItem)
04848           {
04849             PRInt32 curresult;
04850             rv = _BodyConvertible(pItem, &curresult);
04851             if (NS_SUCCEEDED(rv) && curresult > result)
04852               result = curresult;
04853           }
04854         }
04855       }
04856     }
04857 
04858     *_retval = result;
04859     return rv;
04860 }
04861 
04862 nsresult nsMsgCompose::BodyConvertible(PRInt32 *_retval)
04863 {
04864     NS_ENSURE_TRUE(_retval, NS_ERROR_NULL_POINTER);
04865 
04866     nsresult rv;
04867 
04868     if (!m_editor)
04869       return NS_ERROR_FAILURE;
04870 
04871     nsCOMPtr<nsIDOMElement> rootElement;
04872     rv = m_editor->GetRootElement(getter_AddRefs(rootElement));
04873     if (NS_FAILED(rv) || nsnull == rootElement)
04874       return rv;
04875 
04876     nsCOMPtr<nsIDOMNode> node = do_QueryInterface(rootElement);
04877     if (nsnull == node)
04878       return NS_ERROR_FAILURE;
04879 
04880     return _BodyConvertible(node, _retval);
04881 }
04882 
04883 nsresult nsMsgCompose::SetSignature(nsIMsgIdentity *identity)
04884 {
04885   nsresult rv;
04886 
04887   if (! m_editor)
04888     return NS_ERROR_FAILURE;
04889 
04890   nsCOMPtr<nsIDOMElement> rootElement;
04891   rv = m_editor->GetRootElement(getter_AddRefs(rootElement));
04892   if (NS_FAILED(rv) || nsnull == rootElement)
04893     return rv;
04894 
04895   //First look for the current signature, if we have one
04896   nsCOMPtr<nsIDOMNode> lastNode;
04897   nsCOMPtr<nsIDOMNode> node;
04898   nsCOMPtr<nsIDOMNode> tempNode;
04899   nsAutoString tagLocalName;
04900 
04901   rv = rootElement->GetLastChild(getter_AddRefs(lastNode));
04902   if (NS_SUCCEEDED(rv) && nsnull != lastNode)
04903   {
04904     node = lastNode;
04905     if (m_composeHTML)
04906     {
04907       /* In html, the signature is inside an element with
04908          class="moz-signature", it's must be the last node */
04909       nsCOMPtr<nsIDOMElement> element = do_QueryInterface(node);
04910       if (element)
04911       {
04912         nsAutoString attributeName;
04913         nsAutoString attributeValue;
04914         attributeName.AssignLiteral("class");
04915 
04916         rv = element->GetAttribute(attributeName, attributeValue);
04917         if (NS_SUCCEEDED(rv))
04918         {
04919           if (attributeValue.Find("moz-signature", PR_TRUE) != kNotFound)
04920           {
04921             //Now, I am sure I get the right node!
04922             m_editor->BeginTransaction();
04923             node->GetPreviousSibling(getter_AddRefs(tempNode));
04924             rv = m_editor->DeleteNode(node);
04925             if (NS_FAILED(rv))
04926             {
04927               m_editor->EndTransaction();
04928               return rv;
04929             }
04930 
04931             //Also, remove the <br> right before the signature.
04932             if (tempNode)
04933             {
04934               tempNode->GetLocalName(tagLocalName);
04935               if (tagLocalName.EqualsLiteral("BR"))
04936                 m_editor->DeleteNode(tempNode);
04937             }
04938             m_editor->EndTransaction();
04939           }
04940         }
04941       }
04942     }
04943     else
04944     {
04945       //In plain text, we have to walk back the dom look for the pattern <br>-- <br>
04946       PRUint16 nodeType;
04947       PRInt32 searchState = 0; //0=nothing, 1=br 2='-- '+br, 3=br+'-- '+br
04948 
04949       do
04950       {
04951         node->GetNodeType(&nodeType);
04952         node->GetLocalName(tagLocalName);
04953         switch (searchState)
04954         {
04955           case 0: 
04956             if (nodeType == nsIDOMNode::ELEMENT_NODE && tagLocalName.EqualsLiteral("BR"))
04957               searchState = 1;
04958             break;
04959 
04960           case 1:
04961             searchState = 0;
04962             if (nodeType == nsIDOMNode::TEXT_NODE)
04963             {
04964               nsString nodeValue;
04965               node->GetNodeValue(nodeValue);
04966               if (nodeValue.EqualsLiteral("-- "))
04967                 searchState = 2;
04968             }
04969             else
04970               if (nodeType == nsIDOMNode::ELEMENT_NODE && tagLocalName.EqualsLiteral("BR"))
04971               {
04972                 searchState = 1;
04973                 break;
04974               }
04975             break;
04976 
04977           case 2:
04978             if (nodeType == nsIDOMNode::ELEMENT_NODE && tagLocalName.EqualsLiteral("BR"))
04979               searchState = 3;
04980             else
04981               searchState = 0;               
04982             break;
04983         }
04984 
04985         tempNode = node;
04986       } while (searchState != 3 && NS_SUCCEEDED(tempNode->GetPreviousSibling(getter_AddRefs(node))) && node);
04987 
04988       if (searchState == 3)
04989       {
04990         //Now, I am sure I get the right node!
04991         m_editor->BeginTransaction();
04992 
04993         tempNode = lastNode;
04994         lastNode = node;
04995         do
04996         {
04997           node = tempNode;
04998           node->GetPreviousSibling(getter_AddRefs(tempNode));
04999           rv = m_editor->DeleteNode(node);
05000           if (NS_FAILED(rv))
05001           {
05002             m_editor->EndTransaction();
05003             return rv;
05004           }
05005 
05006         } while (node != lastNode && tempNode);
05007         m_editor->EndTransaction();
05008       }
05009     }
05010   }
05011 
05012   //Then add the new one if needed
05013   nsAutoString aSignature;
05014 
05015   // No delimiter needed if not a compose window
05016   PRBool noDelimiter;
05017   switch (mType)
05018   {
05019     case nsIMsgCompType::New :
05020     case nsIMsgCompType::NewsPost :
05021     case nsIMsgCompType::MailToUrl :
05022     case nsIMsgCompType::ForwardAsAttachment :
05023       noDelimiter = PR_FALSE;
05024       break;
05025     default :
05026       noDelimiter = PR_TRUE;
05027       break;
05028   }
05029 
05030   ProcessSignature(identity, noDelimiter, &aSignature);
05031 
05032   if (!aSignature.IsEmpty())
05033   {
05034     TranslateLineEnding(aSignature);
05035 
05036     m_editor->BeginTransaction();
05037     PRInt32 reply_on_top = 0;
05038     PRBool sig_bottom = PR_TRUE;
05039     identity->GetReplyOnTop(&reply_on_top);
05040     identity->GetSigBottom(&sig_bottom);
05041     PRBool sigOnTop = (reply_on_top == 1 && !sig_bottom);
05042     if (sigOnTop && noDelimiter)
05043       m_editor->BeginningOfDocument();
05044     else
05045       m_editor->EndOfDocument();
05046     if (m_composeHTML)
05047     {
05048       nsCOMPtr<nsIHTMLEditor> htmlEditor (do_QueryInterface(m_editor));
05049       rv = htmlEditor->InsertHTML(aSignature);
05050     }
05051     else
05052     {
05053       nsCOMPtr<nsIPlaintextEditor> textEditor (do_QueryInterface(m_editor));
05054       rv = textEditor->InsertText(aSignature);
05055     }
05056     if (sigOnTop && noDelimiter)
05057       m_editor->EndOfDocument();
05058     m_editor->EndTransaction();
05059   }
05060 
05061   return rv;
05062 }
05063 
05064 NS_IMETHODIMP nsMsgCompose::CheckCharsetConversion(nsIMsgIdentity *identity, char **fallbackCharset, PRBool *_retval)
05065 {
05066   NS_ENSURE_ARG_POINTER(identity);
05067   NS_ENSURE_ARG_POINTER(_retval);
05068 
05069   nsresult rv = m_compFields->CheckCharsetConversion(fallbackCharset, _retval);
05070   NS_ENSURE_SUCCESS(rv, rv);
05071 
05072   if (*_retval) 
05073   {
05074     nsXPIDLString fullName;
05075     nsXPIDLString organization;
05076     nsAutoString identityStrings;
05077 
05078     rv = identity->GetFullName(getter_Copies(fullName));
05079     NS_ENSURE_SUCCESS(rv, rv);
05080     if (fullName)
05081       identityStrings.Append(fullName.get());
05082 
05083     rv = identity->GetOrganization(getter_Copies(organization));
05084     NS_ENSURE_SUCCESS(rv, rv);
05085     if (organization)
05086       identityStrings.Append(organization.get());
05087 
05088     if (!identityStrings.IsEmpty())
05089     {
05090       // use fallback charset if that's already set
05091       const char *charset = (fallbackCharset && *fallbackCharset) ? *fallbackCharset : m_compFields->GetCharacterSet();
05092       *_retval = nsMsgI18Ncheck_data_in_charset_range(charset, identityStrings.get(),
05093                                                       fallbackCharset);
05094     }
05095   }
05096 
05097   return NS_OK;
05098 }
05099 
05100 NS_IMPL_ADDREF(nsMsgRecipient)
05101 NS_IMPL_RELEASE(nsMsgRecipient)
05102 
05103 NS_INTERFACE_MAP_BEGIN(nsMsgRecipient)
05104    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISupports)
05105 NS_INTERFACE_MAP_END
05106 
05107 nsMsgRecipient::nsMsgRecipient() :
05108   mPreferFormat(nsIAbPreferMailFormat::unknown),
05109   mProcessed(PR_FALSE)
05110 {
05111 }
05112 
05113 nsMsgRecipient::nsMsgRecipient(nsString fullAddress, nsString email, PRUint32 preferFormat, PRBool processed) :
05114   mAddress(fullAddress),
05115   mEmail(email),
05116   mPreferFormat(preferFormat),
05117   mProcessed(processed)
05118 {
05119 }
05120 
05121 nsMsgRecipient::~nsMsgRecipient()
05122 {
05123 }
05124 
05125 NS_IMPL_ADDREF(nsMsgMailList)
05126 NS_IMPL_RELEASE(nsMsgMailList)
05127 
05128 NS_INTERFACE_MAP_BEGIN(nsMsgMailList)
05129    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISupports)
05130 NS_INTERFACE_MAP_END
05131 
05132 
05133 nsMsgMailList::nsMsgMailList()
05134 {
05135 }
05136 
05137 nsMsgMailList::nsMsgMailList(nsString listName, nsString listDescription, nsIAbDirectory* directory) :
05138   mDirectory(directory)
05139 {
05140   nsCOMPtr<nsIMsgHeaderParser> parser (do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID));
05141 
05142   if (parser)
05143   {
05144     nsXPIDLCString utf8Email;
05145     if (listDescription.IsEmpty())
05146       CopyUTF16toUTF8(listName, utf8Email);
05147     else
05148       CopyUTF16toUTF8(listDescription, utf8Email);
05149 
05150     nsXPIDLCString fullAddress;
05151     parser->MakeFullAddress(nsnull, NS_ConvertUTF16toUTF8(listName).get(),
05152                             utf8Email, getter_Copies(fullAddress));
05153     if (!fullAddress.IsEmpty())
05154     {
05155       /* We need to convert back the result from UTF-8 to Unicode */
05156       CopyUTF8toUTF16(fullAddress, mFullName);
05157     }
05158   }
05159 
05160   if (mFullName.IsEmpty())
05161   {
05162       //oops, parser problem! I will try to do my best...
05163       mFullName = listName;
05164       mFullName.AppendLiteral(" <");
05165       if (listDescription.IsEmpty())
05166         mFullName += listName;
05167       else
05168         mFullName += listDescription;
05169       mFullName.Append(PRUnichar('>'));
05170   }
05171 
05172   mDirectory = directory;
05173 }
05174 
05175 nsMsgMailList::~nsMsgMailList()
05176 {
05177 }