/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Pierre Phaneuf */ #include "nsCOMPtr.h" #include "nsMsgCompose.h" #include "nsIScriptGlobalObject.h" #include "nsIDOMNode.h" #include "nsIDOMNodeList.h" #include "nsIDOMHTMLInputElement.h" #include "nsIDOMDocument.h" #include "nsMsgI18N.h" #include "nsMsgCompCID.h" #include "nsMsgSend.h" #include "nsIMessenger.h" //temporary! #include "nsIMessage.h" //temporary! #include "nsMsgQuote.h" #include "nsIPref.h" #include "nsIDocumentEncoder.h" // for editor output flags #include "nsXPIDLString.h" #include "nsIMsgHeaderParser.h" #include "nsMsgCompUtils.h" #include "nsMsgComposeStringBundle.h" #include "nsSpecialSystemDirectory.h" #include "nsMsgSend.h" #include "nsMsgCreate.h" #include "nsMailHeaders.h" #include "nsMsgPrompts.h" #include "nsMimeTypes.h" #include "nsICharsetConverterManager.h" #include "nsTextFormatter.h" #include "nsIEditor.h" #include "nsIHTMLEditor.h" #include "nsIDOMSelection.h" #include "nsIDOMNode.h" #include "nsEscape.h" #include "plstr.h" #include "nsIDocShell.h" #include "nsIRDFService.h" #include "nsRDFCID.h" #include "nsAbBaseCID.h" #include "nsIAddrDatabase.h" #include "nsIAddrBookSession.h" // Defines.... static NS_DEFINE_CID(kMsgQuoteCID, NS_MSGQUOTE_CID); static NS_DEFINE_CID(kPrefCID, NS_PREF_CID); static NS_DEFINE_CID(kHeaderParserCID, NS_MSGHEADERPARSER_CID); static NS_DEFINE_CID(kAddrBookSessionCID, NS_ADDRBOOKSESSION_CID); static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); static NS_DEFINE_CID(kAddressBookDBCID, NS_ADDRDATABASE_CID); static NS_DEFINE_CID(kMsgRecipientArrayCID, NS_MSGRECIPIENTARRAY_CID); static PRInt32 GetReplyOnTop() { PRInt32 reply_on_top = 1; nsresult rv; NS_WITH_SERVICE(nsIPref, prefs, kPrefCID, &rv); if (NS_SUCCEEDED(rv)) prefs->GetIntPref("mailnews.reply_on_top", &reply_on_top); return reply_on_top; } static nsresult RemoveDuplicateAddresses(const char * addresses, const char * anothersAddresses, PRBool removeAliasesToMe, char** newAddress) { nsresult rv; nsCOMPtr parser = do_GetService(kHeaderParserCID);; if (parser) rv= parser->RemoveDuplicateAddresses(msgCompHeaderInternalCharset(), addresses, anothersAddresses, removeAliasesToMe, newAddress); else rv = NS_ERROR_FAILURE; return rv; } nsMsgCompose::nsMsgCompose() { NS_INIT_REFCNT(); mEntityConversionDone = PR_FALSE; mQuotingToFollow = PR_FALSE; mWhatHolder = 1; mQuoteURI = ""; mDocumentListener = nsnull; mMsgSend = nsnull; m_sendListener = nsnull; m_window = nsnull; m_webShell = nsnull; m_webShellWin = nsnull; m_editor = nsnull; mQuoteStreamListener=nsnull; m_compFields = new nsMsgCompFields; NS_IF_ADDREF(m_compFields); mType = nsIMsgCompType::New; // Get the default charset from pref, use this as a mail charset. char * default_mail_charset = nsMsgI18NGetDefaultMailCharset(); if (default_mail_charset) { m_compFields->SetCharacterSet(default_mail_charset); PR_Free(default_mail_charset); } m_composeHTML = PR_FALSE; } nsMsgCompose::~nsMsgCompose() { if (mDocumentListener) { mDocumentListener->SetComposeObj(nsnull); NS_RELEASE(mDocumentListener); } NS_IF_RELEASE(m_sendListener); NS_IF_RELEASE(m_compFields); NS_IF_RELEASE(mQuoteStreamListener); } /* the following macro actually implement addref, release and query interface for our component. */ NS_IMPL_ISUPPORTS(nsMsgCompose, NS_GET_IID(nsMsgCompose)); // // Once we are here, convert the data which we know to be UTF-8 to UTF-16 // for insertion into the editor // static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID); nsresult TranslateLineEndings(nsString &aString) { PRUnichar *transBuf = nsnull; // First, do sanity checking...if aString doesn't have // any CR's, then there is no reason to call this rest // of this if (aString.FindChar(CR) < 0) return NS_OK; transBuf = aString.ToNewUnicode(); if (transBuf) { DoLineEndingConJobUnicode(transBuf, aString.Length()); aString.SetString(transBuf); PR_FREEIF(transBuf); } return NS_OK; } nsresult GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset) { NS_ASSERTION((aChild && aParent), "bad args"); nsresult result = NS_ERROR_NULL_POINTER; if (aChild && aParent) { nsCOMPtr childNodes; result = aParent->GetChildNodes(getter_AddRefs(childNodes)); if ((NS_SUCCEEDED(result)) && (childNodes)) { PRInt32 i=0; for ( ; NS_SUCCEEDED(result); i++) { nsCOMPtr childNode; result = childNodes->Item(i, getter_AddRefs(childNode)); if ((NS_SUCCEEDED(result)) && (childNode)) { if (childNode.get()==aChild) { aOffset = i; break; } } else if (!childNode) result = NS_ERROR_NULL_POINTER; } } else if (!childNodes) result = NS_ERROR_NULL_POINTER; } return result; } nsresult GetNodeLocation(nsIDOMNode *inChild, nsCOMPtr *outParent, PRInt32 *outOffset) { NS_ASSERTION((inChild && outParent && outOffset), "bad args"); nsresult result = NS_ERROR_NULL_POINTER; if (inChild && outParent && outOffset) { result = inChild->GetParentNode(getter_AddRefs(*outParent)); if ( (NS_SUCCEEDED(result)) && (*outParent) ) { result = GetChildOffset(inChild, *outParent, *outOffset); } } return result; } nsresult nsMsgCompose::ConvertAndLoadComposeWindow(nsIEditorShell *aEditorShell, nsString aPrefix, nsString aBuf, nsString aSignature, PRBool aQuoted, PRBool aHTMLEditor) { // First, get the nsIEditor interface for future use nsCOMPtr editor; nsIDOMNode *nodeInserted = nsnull; aEditorShell->GetEditor(getter_AddRefs(editor)); TranslateLineEndings(aPrefix); TranslateLineEndings(aBuf); TranslateLineEndings(aSignature); if (editor) editor->EnableUndo(PR_FALSE); // Ok - now we need to figure out the charset of the aBuf we are going to send // into the editor shell. There are I18N calls to sniff the data and then we need // to call the new routine in the editor that will allow us to send in the charset // // Now, insert it into the editor... if ( (aQuoted) ) { if (!aPrefix.IsEmpty()) { if (aHTMLEditor) aEditorShell->InsertSource(aPrefix.GetUnicode()); else aEditorShell->InsertText(aPrefix.GetUnicode()); } if (!aBuf.IsEmpty()) { aEditorShell->InsertAsQuotation(aBuf.GetUnicode(), &nodeInserted); } if (!aSignature.IsEmpty()) { if (aHTMLEditor) aEditorShell->InsertSource(aSignature.GetUnicode()); else aEditorShell->InsertText(aSignature.GetUnicode()); } } else { if (aHTMLEditor) { if (!aBuf.IsEmpty()) aEditorShell->InsertSource(aBuf.GetUnicode()); if (!aSignature.IsEmpty()) aEditorShell->InsertSource(aSignature.GetUnicode()); } else { if (!aBuf.IsEmpty()) aEditorShell->InsertText(aBuf.GetUnicode()); if (!aSignature.IsEmpty()) aEditorShell->InsertText(aSignature.GetUnicode()); } } if (editor) { switch (GetReplyOnTop()) { // This should set the cursor after the body but before the sig case 0 : { nsCOMPtr htmlEditor = do_QueryInterface(editor); if (!htmlEditor) { editor->BeginningOfDocument(); break; } nsCOMPtr selection = nsnull; nsCOMPtr parent = nsnull; PRInt32 offset; nsresult rv; // get parent and offset of mailcite rv = GetNodeLocation(nodeInserted, &parent, &offset); if ( NS_FAILED(rv) || (!parent) ) { editor->BeginningOfDocument(); break; } // get selection editor->GetSelection(getter_AddRefs(selection)); if (!selection) { editor->BeginningOfDocument(); break; } // place selection after mailcite selection->Collapse(parent, offset+1); // insert a break at current selection htmlEditor->InsertBreak(); // i'm not sure if you need to move the selection back to before the // break. expirement. selection->Collapse(parent, offset+1); break; } case 2 : { editor->SelectAll(); break; } // This should set the cursor to the top! default : editor->BeginningOfDocument(); break; } } NotifyStateListeners(nsMsgCompose::eComposeFieldsReady); if (editor) editor->EnableUndo(PR_TRUE); return NS_OK; } nsresult nsMsgCompose::SetQuotingToFollow(PRBool aVal) { mQuotingToFollow = aVal; return NS_OK; } PRBool nsMsgCompose::QuotingToFollow(void) { return mQuotingToFollow; } nsresult nsMsgCompose::Initialize(nsIDOMWindow *aWindow, const PRUnichar *originalMsgURI, MSG_ComposeType type, MSG_ComposeFormat format, nsIMsgCompFields *compFields, nsIMsgIdentity *identity) { nsresult rv = NS_OK; m_identity = identity; if (aWindow) { m_window = aWindow; nsCOMPtr globalObj(do_QueryInterface(aWindow)); if (!globalObj) return NS_ERROR_FAILURE; nsCOMPtr docShell; globalObj->GetDocShell(getter_AddRefs(docShell)); nsCOMPtr webShell(do_QueryInterface(docShell)); if (!webShell) return NS_ERROR_NOT_INITIALIZED; m_webShell = webShell; nsCOMPtr webShellContainer; m_webShell->GetContainer(*getter_AddRefs(webShellContainer)); if (!webShellContainer) return NS_ERROR_NOT_INITIALIZED; nsCOMPtr webShellWin = do_QueryInterface(webShellContainer, &rv); m_webShellWin = webShellWin; } switch (format) { case nsIMsgCompFormat::HTML : m_composeHTML = PR_TRUE; break; case nsIMsgCompFormat::PlainText : m_composeHTML = PR_FALSE; break; default : /* ask the identity which compose to use */ if (m_identity) m_identity->GetComposeHtml(&m_composeHTML); break; } CreateMessage(originalMsgURI, type, format, compFields); return rv; } nsresult nsMsgCompose::SetDocumentCharset(const PRUnichar *charset) { // Set charset, this will be used for the MIME charset labeling. m_compFields->SetCharacterSet(nsCAutoString(charset)); return NS_OK; } nsresult nsMsgCompose::RegisterStateListener(nsIMsgComposeStateListener *stateListener) { nsresult rv = NS_OK; if (!stateListener) return NS_ERROR_NULL_POINTER; if (!mStateListeners) { rv = NS_NewISupportsArray(getter_AddRefs(mStateListeners)); if (NS_FAILED(rv)) return rv; } nsCOMPtr iSupports = do_QueryInterface(stateListener, &rv); if (NS_FAILED(rv)) return rv; // note that this return value is really a PRBool, so be sure to use // NS_SUCCEEDED or NS_FAILED to check it. return mStateListeners->AppendElement(iSupports); } nsresult nsMsgCompose::UnregisterStateListener(nsIMsgComposeStateListener *stateListener) { if (!stateListener) return NS_ERROR_NULL_POINTER; nsresult rv = NS_OK; // otherwise, see if it exists in our list if (!mStateListeners) return (nsresult)PR_FALSE; // yeah, this sucks, but I'm emulating the behaviour of // nsISupportsArray::RemoveElement() nsCOMPtr iSupports = do_QueryInterface(stateListener, &rv); if (NS_FAILED(rv)) return rv; // note that this return value is really a PRBool, so be sure to use // NS_SUCCEEDED or NS_FAILED to check it. return mStateListeners->RemoveElement(iSupports); } nsresult nsMsgCompose::_SendMsg(MSG_DeliverMode deliverMode, nsIMsgIdentity *identity, const PRUnichar *callback) { nsresult rv = NS_OK; if (m_compFields && identity) { // Pref values are supposed to be stored as UTF-8, so no conversion nsXPIDLCString email; nsXPIDLString fullName; nsXPIDLCString replyTo; nsXPIDLString organization; identity->GetEmail(getter_Copies(email)); identity->GetFullName(getter_Copies(fullName)); identity->GetReplyTo(getter_Copies(replyTo)); identity->GetOrganization(getter_Copies(organization)); char * sender = nsnull; nsCOMPtr parser = do_GetService(kHeaderParserCID); if (parser) { // convert to UTF8 before passing to MakeFullAddress nsAutoString fullNameStr(fullName); char *fullNameUTF8 = fullNameStr.ToNewUTF8String(); parser->MakeFullAddress(nsnull, fullNameUTF8, email, &sender); nsCRT::free(fullNameUTF8); } if (!sender) m_compFields->SetFrom(email); else m_compFields->SetFrom(sender); PR_FREEIF(sender); //Set the reply-to only if the user have not specified one in the message const char * reply = m_compFields->GetReplyTo(); if (reply == nsnull || *reply == 0) m_compFields->SetReplyTo(replyTo); m_compFields->SetOrganization(organization); #if defined(DEBUG_ducarroz) || defined(DEBUG_seth_) printf("----------------------------\n"); printf("-- Sending Mail Message --\n"); printf("----------------------------\n"); printf("from: %s\n", m_compFields->GetFrom()); printf("To: %s Cc: %s Bcc: %s\n", m_compFields->GetTo(), m_compFields->GetCc(), m_compFields->GetBcc()); printf("Newsgroups: %s\n", m_compFields->GetNewsgroups()); printf("Subject: %s \nMsg: %s\n", m_compFields->GetSubject(), m_compFields->GetBody()); printf("Attachments: %s\n",m_compFields->GetAttachments()); printf("----------------------------\n"); #endif //DEBUG nsMsgComposeAndSend *tMsgComp = new nsMsgComposeAndSend(); if (!tMsgComp) return NS_ERROR_OUT_OF_MEMORY; mMsgSend = do_QueryInterface( tMsgComp ); if (mMsgSend) { PRBool newBody = PR_FALSE; char *bodyString = (char *)m_compFields->GetBody(); PRInt32 bodyLength; char *attachment1_type = TEXT_HTML; // we better be "text/html" at this point mMsgSend->SetWebShell(m_webShell); if (!mEntityConversionDone) { // Convert body to mail charset char *outCString; nsString aCharset = m_compFields->GetCharacterSet(); if ( aCharset != "") { // Apply entity conversion then convert to a mail charset. char charset[65]; rv = nsMsgI18NSaveAsCharset(attachment1_type, aCharset.ToCString(charset, 65), nsString(bodyString).GetUnicode(), &outCString); if (NS_SUCCEEDED(rv)) { bodyString = outCString; newBody = PR_TRUE; mEntityConversionDone = PR_TRUE; } } } bodyLength = PL_strlen(bodyString); // Create the listener for the send operation... m_sendListener = new nsMsgComposeSendListener(); if (!m_sendListener) return NS_ERROR_FAILURE; NS_ADDREF(m_sendListener); // set this object for use on completion... m_sendListener->SetComposeObj(this); m_sendListener->SetDeliverMode(deliverMode); nsIMsgSendListener **tArray = m_sendListener->CreateListenerArray(); if (!tArray) { #ifdef DEBUG printf("Error creating listener array.\n"); #endif return NS_ERROR_FAILURE; } // If we are composing HTML, then this should be sent as // multipart/related which means we pass the editor into the // backend...if not, just pass nsnull // nsIEditorShell *tEditor = nsnull; if (m_composeHTML) { tEditor = m_editor; } else tEditor = nsnull; rv = mMsgSend->CreateAndSendMessage( tEditor, identity, m_compFields, PR_FALSE, // PRBool digest_p, PR_FALSE, // PRBool dont_deliver_p, (nsMsgDeliverMode)deliverMode, // nsMsgDeliverMode mode, nsnull, // nsIMessage *msgToReplace, m_composeHTML?TEXT_HTML:TEXT_PLAIN, // const char *attachment1_type, bodyString, // const char *attachment1_body, bodyLength, // PRUint32 attachment1_body_length, nsnull, // const struct nsMsgAttachmentData *attachments, nsnull, // const struct nsMsgAttachedFile *preloaded_attachments, nsnull, // nsMsgSendPart *relatedPart, tArray); // listener array // Cleanup converted body... if (newBody) PR_FREEIF(bodyString); PR_Free(tArray); } else rv = NS_ERROR_FAILURE; } else rv = NS_ERROR_NOT_INITIALIZED; if (NS_SUCCEEDED(rv)) { /*TODO, don't close the window but just hide it, we will close it later when we receive a call back from the BE if (nsnull != mScriptContext) { const char* url = ""; PRBool isUndefined = PR_FALSE; nsString rVal; mScriptContext->EvaluateString(mScript, url, 0, rVal, &isUndefined); CloseWindow(); } else // If we don't have a JS callback, then close the window by default! */ // rhp: // We shouldn't close the window if we are just saving a draft or a template // so do this check if ( (deliverMode != nsMsgSaveAsDraft) && (deliverMode != nsMsgSaveAsTemplate) ) ShowWindow(PR_FALSE); } return rv; } nsresult nsMsgCompose::SendMsg(MSG_DeliverMode deliverMode, nsIMsgIdentity *identity, const PRUnichar *callback) { nsresult rv = NS_OK; if (m_editor && m_compFields && !m_composeHTML) { const char contentType[] = "text/plain"; nsAutoString msgBody; PRUnichar *bodyText = NULL; nsAutoString format(contentType); PRUint32 flags = nsIDocumentEncoder::OutputFormatted; nsresult rv2; NS_WITH_SERVICE(nsIPref, prefs, kPrefCID, &rv2); if (NS_SUCCEEDED(rv2)) { PRBool sendflowed; rv2=prefs->GetBoolPref("mailnews.send_plaintext_flowed", &sendflowed); if(!(NS_SUCCEEDED(rv2) && !sendflowed)) // Unless explicitly forbidden... flags |= nsIDocumentEncoder::OutputFormatFlowed; } if (!mEntityConversionDone) { rv = m_editor->GetContentsAs(format.GetUnicode(), flags, &bodyText); if (NS_SUCCEEDED(rv) && NULL != bodyText) { msgBody = bodyText; nsAllocator::Free(bodyText); // Convert body to mail charset not to utf-8 (because we don't manipulate body text) char *outCString = NULL; rv = nsMsgI18NSaveAsCharset(contentType, m_compFields->GetCharacterSet(), msgBody.GetUnicode(), &outCString); if (NS_SUCCEEDED(rv) && NULL != outCString) { mEntityConversionDone = PR_TRUE; m_compFields->SetBody(outCString); PR_Free(outCString); } else m_compFields->SetBody(nsCAutoString(msgBody)); } } } rv = _SendMsg(deliverMode, identity, callback); if (NS_FAILED(rv)) { ShowWindow(PR_TRUE); if (rv != NS_ERROR_BUT_DONT_SHOW_ALERT) nsMsgDisplayMessageByID(rv); } return rv; } nsresult nsMsgCompose::SendMsgEx(MSG_DeliverMode deliverMode, nsIMsgIdentity *identity, const PRUnichar *addrTo, const PRUnichar *addrCc, const PRUnichar *addrBcc, const PRUnichar *newsgroup, const PRUnichar *subject, const PRUnichar *body, const PRUnichar *callback) { nsresult rv = NS_OK; if (m_compFields && identity) { nsAutoString aCharset(msgCompHeaderInternalCharset()); char *outCString; // Convert fields to UTF-8 if (NS_SUCCEEDED(ConvertFromUnicode(aCharset, addrTo, &outCString))) { m_compFields->SetTo(outCString); PR_Free(outCString); } else m_compFields->SetTo(nsCAutoString(addrTo)); if (NS_SUCCEEDED(ConvertFromUnicode(aCharset, addrCc, &outCString))) { m_compFields->SetCc(outCString); PR_Free(outCString); } else m_compFields->SetCc(nsCAutoString(addrCc)); if (NS_SUCCEEDED(ConvertFromUnicode(aCharset, addrBcc, &outCString))) { m_compFields->SetBcc(outCString); PR_Free(outCString); } else m_compFields->SetBcc(nsCAutoString(addrBcc)); if (NS_SUCCEEDED(ConvertFromUnicode(aCharset, newsgroup, &outCString))) { m_compFields->SetNewsgroups(outCString); PR_Free(outCString); } else m_compFields->SetNewsgroups(nsCAutoString(newsgroup)); if (NS_SUCCEEDED(ConvertFromUnicode(aCharset, subject, &outCString))) { m_compFields->SetSubject(outCString); PR_Free(outCString); } else m_compFields->SetSubject(nsCAutoString(subject)); // Convert body to mail charset not to utf-8 (because we don't manipulate body text) aCharset.SetString(m_compFields->GetCharacterSet()); if (NS_SUCCEEDED(ConvertFromUnicode(aCharset, body, &outCString))) { m_compFields->SetBody(outCString); PR_Free(outCString); } else m_compFields->SetBody(nsCAutoString(body)); rv = _SendMsg(deliverMode, identity, callback); } else rv = NS_ERROR_NOT_INITIALIZED; if (NS_FAILED(rv)) { ShowWindow(PR_TRUE); if (rv != NS_ERROR_BUT_DONT_SHOW_ALERT) nsMsgDisplayMessageByID(rv); } return rv; } nsresult nsMsgCompose::CloseWindow() { if (m_webShellWin) { m_editor = nsnull; /* m_editor will be destroyed during the Close Window. Set it to null to */ /* be sure we wont use it anymore. */ nsIWebShellWindow *saveWin = m_webShellWin; m_webShellWin = nsnull; saveWin->Close(); } return NS_OK; } nsresult nsMsgCompose::ShowWindow(PRBool show) { if (m_webShellWin) { m_webShellWin->Show(show); } return NS_OK; } nsresult nsMsgCompose::GetEditor(nsIEditorShell * *aEditor) { *aEditor = m_editor; return NS_OK; } nsresult nsMsgCompose::SetEditor(nsIEditorShell * aEditor) { // First, store the editor shell. m_editor = aEditor; // // Now this routine will create a listener for state changes // in the editor and set us as the compose object of interest. // mDocumentListener = new nsMsgDocumentStateListener(); if (!mDocumentListener) return NS_ERROR_OUT_OF_MEMORY; mDocumentListener->SetComposeObj(this); NS_ADDREF(mDocumentListener); // Make sure we setup to listen for editor state changes... m_editor->RegisterDocumentStateListener(mDocumentListener); // Now, lets init the editor here! // Just get a blank editor started... m_editor->LoadUrl(nsAutoString("about:blank").GetUnicode()); return NS_OK; } nsresult nsMsgCompose::GetDomWindow(nsIDOMWindow * *aDomWindow) { *aDomWindow = m_window; return NS_OK; } nsresult nsMsgCompose::GetCompFields(nsIMsgCompFields * *aCompFields) { *aCompFields = (nsIMsgCompFields*)m_compFields; NS_IF_ADDREF(*aCompFields); return NS_OK; } nsresult nsMsgCompose::GetComposeHTML(PRBool *aComposeHTML) { *aComposeHTML = m_composeHTML; return NS_OK; } nsresult nsMsgCompose::GetWrapLength(PRInt32 *aWrapLength) { nsresult rv; NS_WITH_SERVICE(nsIPref, prefs, kPrefCID, &rv); if (NS_FAILED(rv)) return rv; return prefs->GetIntPref("mailnews.wraplength", aWrapLength); } nsresult nsMsgCompose::CreateMessage(const PRUnichar * originalMsgURI, MSG_ComposeType type, MSG_ComposeFormat format, nsIMsgCompFields * compFields) { nsresult rv = NS_OK; mType = type; if (compFields) { if (m_compFields) rv = m_compFields->Copy(compFields); return rv; } /* In case of forwarding multiple messages, originalMsgURI will contains several URI separated by a comma. */ /* we need to extract only the first URI*/ nsAutoString firstURI(originalMsgURI); PRInt32 offset = firstURI.FindChar(','); if (offset >= 0) firstURI.Truncate(offset); nsCOMPtr message = getter_AddRefs(GetIMessageFromURI(firstURI.GetUnicode())); if ((NS_SUCCEEDED(rv)) && message) { nsXPIDLCString subject; nsAutoString subjectStr(""); nsAutoString aCharset(""); nsAutoString decodedString; nsAutoString encodedCharset; // we don't use this char *aCString = nsnull; rv = message->GetCharSet(&aCharset); if (NS_FAILED(rv)) return rv; rv = message->GetSubject(getter_Copies(subject)); if (NS_FAILED(rv)) return rv; switch (type) { default: break; case nsIMsgCompType::Reply : case nsIMsgCompType::ReplyAll: { mQuotingToFollow = PR_TRUE; // get an original charset, used for a label, UTF-8 is used for the internal processing if (!aCharset.Equals("")) m_compFields->SetCharacterSet(nsCAutoString(aCharset)); subjectStr += "Re: "; subjectStr += subject; if (NS_SUCCEEDED(rv = nsMsgI18NDecodeMimePartIIStr(subjectStr, encodedCharset, decodedString))) m_compFields->SetSubject(decodedString.GetUnicode()); else m_compFields->SetSubject(subjectStr.GetUnicode()); nsXPIDLCString author; rv = message->GetAuthor(getter_Copies(author)); if (NS_FAILED(rv)) return rv; m_compFields->SetTo(author); nsString authorStr(author); if (NS_SUCCEEDED(rv = nsMsgI18NDecodeMimePartIIStr(authorStr, encodedCharset, decodedString))) if (NS_SUCCEEDED(rv = ConvertFromUnicode(msgCompHeaderInternalCharset(), decodedString, &aCString))) { m_compFields->SetTo(aCString); PR_Free(aCString); } if (type == nsIMsgCompType::ReplyAll) { nsXPIDLCString recipients; rv = message->GetRecipients(getter_Copies(recipients)); if (NS_FAILED(rv)) return rv; nsAutoString recipStr(recipients); CleanUpRecipients(recipStr); nsXPIDLCString ccList; rv = message->GetCcList(getter_Copies(ccList)); if (NS_FAILED(rv)) return rv; nsAutoString ccListStr(ccList); CleanUpRecipients(ccListStr); if (recipStr.Length() > 0 && ccListStr.Length() > 0) recipStr += ", "; recipStr += ccListStr; m_compFields->SetCc(nsCAutoString(recipStr)); if (NS_SUCCEEDED(rv = nsMsgI18NDecodeMimePartIIStr(recipStr, encodedCharset, decodedString))) if (NS_SUCCEEDED(rv = ConvertFromUnicode(msgCompHeaderInternalCharset(), decodedString, &aCString))) { char * resultStr = nsnull; nsCString addressToBeRemoved = m_compFields->GetTo(); if (m_identity) { nsXPIDLCString email; m_identity->GetEmail(getter_Copies(email)); addressToBeRemoved += ", "; addressToBeRemoved += NS_CONST_CAST(char*, (const char *)email); } rv= RemoveDuplicateAddresses(aCString, (char *)addressToBeRemoved, PR_TRUE, &resultStr); if (NS_SUCCEEDED(rv)) { PR_Free(aCString); aCString = resultStr; } m_compFields->SetCc(aCString); PR_Free(aCString); } } // Setup quoting callbacks for later... mWhatHolder = 1; mQuoteURI = originalMsgURI; break; } case nsIMsgCompType::ForwardAsAttachment: { if (!aCharset.Equals("")) m_compFields->SetCharacterSet(nsCAutoString(aCharset)); subjectStr += "[Fwd: "; subjectStr += subject; subjectStr += "]"; if (NS_SUCCEEDED(rv = nsMsgI18NDecodeMimePartIIStr(subjectStr, encodedCharset, decodedString))) m_compFields->SetSubject(decodedString.GetUnicode()); else m_compFields->SetSubject(subjectStr.GetUnicode()); // Setup quoting callbacks for later... mQuotingToFollow = PR_FALSE; //We don't need to quote the original message. m_compFields->SetAttachments(originalMsgURI); break; } } } return rv; } //////////////////////////////////////////////////////////////////////////////////// // THIS IS THE CLASS THAT IS THE STREAM CONSUMER OF THE HTML OUPUT // FROM LIBMIME. THIS IS FOR QUOTING //////////////////////////////////////////////////////////////////////////////////// QuotingOutputStreamListener::~QuotingOutputStreamListener() { if (mComposeObj) NS_RELEASE(mComposeObj); } QuotingOutputStreamListener::QuotingOutputStreamListener(const PRUnichar * originalMsgURI, PRBool quoteHeaders, nsIMsgIdentity *identity) { mComposeObj = nsnull; mQuoteHeaders = quoteHeaders; mIdentity = identity; // For the built message body... mMsgBody = ""; mCitePrefix = ""; mSignature = ""; nsCOMPtr originalMsg = getter_AddRefs(GetIMessageFromURI(originalMsgURI)); if (originalMsg && !quoteHeaders) { nsresult rv; nsAutoString author; rv = originalMsg->GetMime2DecodedAuthor(&author); if (NS_SUCCEEDED(rv)) { char * authorName = nsnull; nsCOMPtr parser = do_GetService(kHeaderParserCID); if (parser) { nsAutoString aCharset(msgCompHeaderInternalCharset()); char * utf8Author = nsnull; rv = ConvertFromUnicode(aCharset, author, &utf8Author); if (NS_SUCCEEDED(rv) && utf8Author) { rv = parser->ExtractHeaderAddressName(nsCAutoString(aCharset), utf8Author, &authorName); if (NS_SUCCEEDED(rv)) rv = ConvertToUnicode(aCharset, authorName, author); } if (!utf8Author || NS_FAILED(rv)) { rv = parser->ExtractHeaderAddressName(nsnull, nsCAutoString(author), &authorName); if (NS_SUCCEEDED(rv)) author = authorName; } if (NS_SUCCEEDED(rv)) { if (GetReplyOnTop() == 1) mCitePrefix.Append("

"); mCitePrefix.Append(author); mCitePrefix.Append(" wrote:
"); } if (authorName) PL_strfree(authorName); PR_FREEIF(utf8Author); } } } if (mCitePrefix.IsEmpty()) mCitePrefix.Append("

--- Original Message ---
"); NS_INIT_REFCNT(); } nsresult QuotingOutputStreamListener::ConvertToPlainText() { nsresult rv = NS_OK; rv += ConvertBufToPlainText(mCitePrefix); rv += ConvertBufToPlainText(mMsgBody); rv += ConvertBufToPlainText(mSignature); return rv; } NS_IMETHODIMP QuotingOutputStreamListener::OnStartRequest(nsIChannel * /* aChannel */, nsISupports * /* ctxt */) { return NS_OK; } NS_IMETHODIMP QuotingOutputStreamListener::OnStopRequest(nsIChannel *aChannel, nsISupports * /* ctxt */, nsresult status, const PRUnichar * /* errorMsg */) { nsresult rv = NS_OK; if (mComposeObj) { MSG_ComposeType type = mComposeObj->GetMessageType(); if (mHeaders && (type == nsIMsgCompType::Reply || type == nsIMsgCompType::ReplyAll)) { nsIMsgCompFields *compFields = nsnull; mComposeObj->GetCompFields(&compFields); //GetCompFields will addref, you need to release when your are done with it if (compFields) { nsAutoString aCharset(msgCompHeaderInternalCharset()); nsAutoString replyTo; nsAutoString newgroups; nsAutoString followUpTo; nsAutoString messageId; nsAutoString references; char *outCString = nsnull; PRUnichar emptyUnichar = 0; PRBool toChanged = PR_FALSE; mHeaders->ExtractHeader(HEADER_REPLY_TO, PR_FALSE, &outCString); if (outCString) { // Convert fields to UTF-8 ConvertToUnicode(aCharset, outCString, replyTo); PR_FREEIF(outCString); } mHeaders->ExtractHeader(HEADER_NEWSGROUPS, PR_FALSE, &outCString); if (outCString) { // Convert fields to UTF-8 ConvertToUnicode(aCharset, outCString, newgroups); PR_FREEIF(outCString); } mHeaders->ExtractHeader(HEADER_FOLLOWUP_TO, PR_FALSE, &outCString); if (outCString) { // Convert fields to UTF-8 ConvertToUnicode(aCharset, outCString, followUpTo); PR_FREEIF(outCString); } mHeaders->ExtractHeader(HEADER_MESSAGE_ID, PR_FALSE, &outCString); if (outCString) { // Convert fields to UTF-8 ConvertToUnicode(aCharset, outCString, messageId); PR_FREEIF(outCString); } mHeaders->ExtractHeader(HEADER_REFERENCES, PR_FALSE, &outCString); if (outCString) { // Convert fields to UTF-8 ConvertToUnicode(aCharset, outCString, references); PR_FREEIF(outCString); } if (! replyTo.IsEmpty()) { compFields->SetTo(replyTo.GetUnicode()); toChanged = PR_TRUE; } if (! newgroups.IsEmpty()) { compFields->SetNewsgroups(newgroups.GetUnicode()); if (type == nsIMsgCompType::Reply) compFields->SetTo(&emptyUnichar); } if (! followUpTo.IsEmpty()) { compFields->SetNewsgroups(followUpTo.GetUnicode()); if (type == nsIMsgCompType::Reply) compFields->SetTo(&emptyUnichar); } if (! references.IsEmpty()) references += ' '; references += messageId; compFields->SetReferences(references.GetUnicode()); if (toChanged) { //Remove duplicate addresses between TO && CC char * resultStr; nsMsgCompFields* _compFields = (nsMsgCompFields*)compFields; if (NS_SUCCEEDED(rv)) { rv= RemoveDuplicateAddresses(_compFields->GetCc(), _compFields->GetTo(), PR_FALSE, &resultStr); if (NS_SUCCEEDED(rv)) { _compFields->SetCc(resultStr); PR_Free(resultStr); } } } // Ok, if we are here, we need to see if a charset was tagged // on this channel. If there WAS, then this charset needs to // override the default charset that is set in compFields. This // is the case where you are replying to a message that has a // non US-ASCII charset. You are supposed to reply in that charset. // char *contentType = nsnull; if (NS_SUCCEEDED(aChannel->GetContentType(&contentType)) && contentType) { char *workContentType = nsCRT::strdup(contentType); if (workContentType) { char *ptr = PL_strstr(workContentType, "charset="); if (ptr) { ptr += nsCRT::strlen("charset="); if (*ptr == '"') ptr++; char *ptr2 = ptr; while (*ptr2) { if ( (*ptr2 == ' ') || (*ptr2 == ';') || (*ptr2 == '"')) { *ptr2 = '\0'; break; } ++ptr2; } compFields->SetCharacterSet(nsString(ptr).GetUnicode()); } PR_FREEIF(workContentType); } PR_FREEIF(contentType); } NS_RELEASE(compFields); } } mMsgBody += ""; // Now we have an HTML representation of the quoted message. // If we are in plain text mode, we need to convert this to plain // text before we try to insert it into the editor. If we don't, we // just get lots of HTML text in the message...not good. // PRBool composeHTML = PR_TRUE; mComposeObj->GetComposeHTML(&composeHTML); if (!composeHTML) ConvertToPlainText(); // // Ok, now we have finished quoting so we should load this into the editor // window. // PRBool compHTML = PR_FALSE; mComposeObj->GetComposeHTML(&compHTML); mComposeObj->ProcessSignature(mIdentity, &mSignature); nsIEditorShell *editor = nsnull; if (NS_SUCCEEDED(mComposeObj->GetEditor(&editor)) && editor) { mComposeObj->ConvertAndLoadComposeWindow(editor, mCitePrefix, mMsgBody, mSignature, PR_TRUE, compHTML); } } NS_IF_RELEASE(mComposeObj); //We are done with it, therefore release it. return rv; } NS_IMETHODIMP QuotingOutputStreamListener::OnDataAvailable(nsIChannel * /* aChannel */, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count) { nsresult rv = NS_OK; if (!inStr) return NS_ERROR_NULL_POINTER; char *newBuf = (char *)PR_Malloc(count + 1); if (!newBuf) return NS_ERROR_FAILURE; PRUint32 numWritten = 0; rv = inStr->Read(newBuf, count, &numWritten); if (rv == NS_BASE_STREAM_WOULD_BLOCK) rv = NS_OK; newBuf[numWritten] = '\0'; if (NS_SUCCEEDED(rv) && numWritten > 0) { PRUnichar *u = nsnull; nsAutoString fmt("%s"); u = nsTextFormatter::smprintf(fmt.GetUnicode(), newBuf); // this converts UTF-8 to UCS-2 if (u) { PRInt32 newLen = nsCRT::strlen(u); mMsgBody.Append(u, newLen); PR_FREEIF(u); } else mMsgBody.Append(newBuf, numWritten); } PR_FREEIF(newBuf); return rv; } nsresult QuotingOutputStreamListener::SetComposeObj(nsMsgCompose *obj) { mComposeObj = obj; return NS_OK; } nsresult QuotingOutputStreamListener::SetMimeHeaders(nsIMimeHeaders * headers) { mHeaders = headers; return NS_OK; } NS_IMPL_ISUPPORTS(QuotingOutputStreamListener, NS_GET_IID(nsIStreamListener)); //////////////////////////////////////////////////////////////////////////////////// // END OF QUOTING LISTENER //////////////////////////////////////////////////////////////////////////////////// MSG_ComposeType nsMsgCompose::GetMessageType() { return mType; } nsresult nsMsgCompose::QuoteOriginalMessage(const PRUnichar *originalMsgURI, PRInt32 what) // New template { nsresult rv; mQuotingToFollow = PR_FALSE; nsAutoString tmpURI(originalMsgURI); // Create a mime parser (nsIStreamConverter)! rv = nsComponentManager::CreateInstance(kMsgQuoteCID, NULL, NS_GET_IID(nsIMsgQuote), (void **) getter_AddRefs(mQuote)); if (NS_FAILED(rv) || !mQuote) return NS_ERROR_FAILURE; // Create the consumer output stream.. this will receive all the HTML from libmime mQuoteStreamListener = new QuotingOutputStreamListener(originalMsgURI, what != 1, m_identity); if (!mQuoteStreamListener) { #ifdef NS_DEBUG printf("Failed to create mQuoteStreamListener\n"); #endif return NS_ERROR_FAILURE; } NS_ADDREF(mQuoteStreamListener); NS_ADDREF(this); mQuoteStreamListener->SetComposeObj(this); rv = mQuote->QuoteMessage(originalMsgURI, what != 1, mQuoteStreamListener); return rv; } //CleanUpRecipient will remove un-necesary "<>" when a recipient as an address without name void nsMsgCompose::CleanUpRecipients(nsString& recipients) { // TODO... PRInt16 i; PRBool startANewRecipient = PR_TRUE; PRBool removeBracket = PR_FALSE; nsAutoString newRecipient; PRUnichar aChar; for (i = 0; i < recipients.Length(); i ++) { aChar = recipients[i]; switch (aChar) { case '<' : if (startANewRecipient) removeBracket = PR_TRUE; else newRecipient += aChar; startANewRecipient = PR_FALSE; break; case '>' : if (removeBracket) removeBracket = PR_FALSE; else newRecipient += aChar; break; case ' ' : newRecipient += aChar; break; case ',' : newRecipient += aChar; startANewRecipient = PR_TRUE; removeBracket = PR_FALSE; break; default : newRecipient += aChar; startANewRecipient = PR_FALSE; break; } } recipients = newRecipient; } //////////////////////////////////////////////////////////////////////////////////// // This is the listener class for both the send operation and the copy operation. // We have to create this class to listen for message send completion and deal with // failures in both send and copy operations //////////////////////////////////////////////////////////////////////////////////// NS_IMPL_ADDREF(nsMsgComposeSendListener) NS_IMPL_RELEASE(nsMsgComposeSendListener) nsIMsgSendListener ** nsMsgComposeSendListener::CreateListenerArray() { nsIMsgSendListener **tArray = (nsIMsgSendListener **)PR_Malloc(sizeof(nsIMsgSendListener *) * 2); if (!tArray) return nsnull; nsCRT::memset(tArray, 0, sizeof(nsIMsgSendListener *) * 2); tArray[0] = this; return tArray; } NS_IMETHODIMP nsMsgComposeSendListener::QueryInterface(const nsIID &aIID, void** aInstancePtr) { if (NULL == aInstancePtr) return NS_ERROR_NULL_POINTER; *aInstancePtr = NULL; if (aIID.Equals(NS_GET_IID(nsIMsgSendListener))) { *aInstancePtr = (nsIMsgSendListener *) this; NS_ADDREF_THIS(); return NS_OK; } if (aIID.Equals(NS_GET_IID(nsIMsgCopyServiceListener))) { *aInstancePtr = (nsIMsgCopyServiceListener *) this; NS_ADDREF_THIS(); return NS_OK; } return NS_NOINTERFACE; } nsMsgComposeSendListener::nsMsgComposeSendListener(void) { mComposeObj = nsnull; mDeliverMode = 0; NS_INIT_REFCNT(); } nsMsgComposeSendListener::~nsMsgComposeSendListener(void) { } nsresult nsMsgComposeSendListener::SetComposeObj(nsMsgCompose *obj) { mComposeObj = obj; return NS_OK; } nsresult nsMsgComposeSendListener::SetDeliverMode(MSG_DeliverMode deliverMode) { mDeliverMode = deliverMode; return NS_OK; } nsresult nsMsgComposeSendListener::OnStartSending(const char *aMsgID, PRUint32 aMsgSize) { #ifdef NS_DEBUG printf("nsMsgComposeSendListener::OnStartSending()\n"); #endif return NS_OK; } nsresult nsMsgComposeSendListener::OnProgress(const char *aMsgID, PRUint32 aProgress, PRUint32 aProgressMax) { #ifdef NS_DEBUG printf("nsMsgComposeSendListener::OnProgress()\n"); #endif return NS_OK; } nsresult nsMsgComposeSendListener::OnStatus(const char *aMsgID, const PRUnichar *aMsg) { #ifdef NS_DEBUG printf("nsMsgComposeSendListener::OnStatus()\n"); #endif return NS_OK; } nsresult nsMsgComposeSendListener::OnStopSending(const char *aMsgID, nsresult aStatus, const PRUnichar *aMsg, nsIFileSpec *returnFileSpec) { nsresult rv = NS_OK; if (mComposeObj) { if (NS_SUCCEEDED(aStatus)) { #ifdef NS_DEBUG printf("nsMsgComposeSendListener: Success on the message send operation!\n"); #endif nsIMsgCompFields *compFields = nsnull; mComposeObj->GetCompFields(&compFields); //GetCompFields will addref, you need to release when your are done with it // Close the window ONLY if we are not going to do a save operation PRUnichar *fieldsFCC = nsnull; if (NS_SUCCEEDED(compFields->GetFcc(&fieldsFCC))) { if (fieldsFCC && *fieldsFCC) { if (nsCRT::strcasecmp(fieldsFCC, "nocopy://") == 0) mComposeObj->CloseWindow(); } } else mComposeObj->CloseWindow(); // if we fail on the simple GetFcc call, close the window to be safe and avoid // windows hanging around to prevent the app from exiting. NS_IF_RELEASE(compFields); } else { #ifdef NS_DEBUG printf("nsMsgComposeSendListener: the message send operation failed!\n"); #endif mComposeObj->ShowWindow(PR_TRUE); } } return rv; } nsresult nsMsgComposeSendListener::OnStartCopy() { #ifdef NS_DEBUG printf("nsMsgComposeSendListener::OnStartCopy()\n"); #endif return NS_OK; } nsresult nsMsgComposeSendListener::OnProgress(PRUint32 aProgress, PRUint32 aProgressMax) { #ifdef NS_DEBUG printf("nsMsgComposeSendListener::OnProgress() - COPY\n"); #endif return NS_OK; } nsresult nsMsgComposeSendListener::OnStopCopy(nsresult aStatus) { nsresult rv = NS_OK; if (mComposeObj) { // Ok, if we are here, we are done with the send/copy operation so // we have to do something with the window....SHOW if failed, Close // if succeeded if (NS_SUCCEEDED(aStatus)) { #ifdef NS_DEBUG printf("nsMsgComposeSendListener: Success on the message copy operation!\n"); #endif // We should only close the window if we are done. Things like templates // and drafts aren't done so their windows should stay open if ( (mDeliverMode != nsMsgSaveAsDraft) && (mDeliverMode != nsMsgSaveAsTemplate) ) mComposeObj->CloseWindow(); } else { #ifdef NS_DEBUG printf("nsMsgComposeSendListener: the message copy operation failed!\n"); #endif mComposeObj->ShowWindow(PR_TRUE); } } return rv; } nsresult nsMsgComposeSendListener::SetMessageKey(PRUint32 aMessageKey) { return NS_OK; } nsresult nsMsgComposeSendListener::GetMessageId(nsCString* aMessageId) { return NS_OK; } //////////////////////////////////////////////////////////////////////////////////// // This is a class that will allow us to listen to state changes in the Ender // compose window. This is important since we must wait until we are told Ender // is ready before we do various quoting operations //////////////////////////////////////////////////////////////////////////////////// NS_IMPL_ISUPPORTS(nsMsgDocumentStateListener, NS_GET_IID(nsIDocumentStateListener)); nsMsgDocumentStateListener::nsMsgDocumentStateListener(void) { NS_INIT_REFCNT(); mComposeObj = nsnull; } nsMsgDocumentStateListener::~nsMsgDocumentStateListener(void) { } void nsMsgDocumentStateListener::SetComposeObj(nsMsgCompose *obj) { mComposeObj = obj; } nsresult nsMsgDocumentStateListener::NotifyDocumentCreated(void) { // Ok, now the document has been loaded, so we are ready to setup // the compose window and let the user run hog wild! // Now, do the appropriate startup operation...signature only // or quoted message and signature... if ( mComposeObj->QuotingToFollow() ) return mComposeObj->BuildQuotedMessageAndSignature(); else return mComposeObj->BuildBodyMessageAndSignature(); } nsresult nsMsgDocumentStateListener::NotifyDocumentWillBeDestroyed(void) { if (mComposeObj) mComposeObj->m_editor = nsnull; /* m_editor will be destroyed. Set it to null to */ /* be sure we wont use it anymore. */ return NS_OK; } nsresult nsMsgDocumentStateListener::NotifyDocumentStateChanged(PRBool nowDirty) { return NS_OK; } nsresult nsMsgCompose::ConvertHTMLToText(nsFileSpec& aSigFile, nsString &aSigData) { nsresult rv; nsAutoString origBuf; rv = LoadDataFromFile(aSigFile, origBuf); if (NS_FAILED(rv)) return rv; ConvertBufToPlainText(origBuf); aSigData = origBuf; return NS_OK; } nsresult nsMsgCompose::ConvertTextToHTML(nsFileSpec& aSigFile, nsString &aSigData) { nsresult rv; nsAutoString origBuf; rv = LoadDataFromFile(aSigFile, origBuf); if (NS_FAILED(rv)) return rv; aSigData.Append(origBuf); return NS_OK; } nsresult nsMsgCompose::LoadDataFromFile(nsFileSpec& fSpec, nsString &sigData) { PRInt32 readSize; char *readBuf; nsInputFileStream tempFile(fSpec); if (!tempFile.is_open()) return NS_MSG_ERROR_WRITING_FILE; readSize = fSpec.GetFileSize(); readBuf = (char *)PR_Malloc(readSize + 1); if (!readBuf) return NS_ERROR_OUT_OF_MEMORY; nsCRT::memset(readBuf, 0, readSize + 1); readSize = tempFile.read(readBuf, readSize); tempFile.close(); sigData = readBuf; PR_FREEIF(readBuf); return NS_OK; } nsresult nsMsgCompose::BuildQuotedMessageAndSignature(void) { // // This should never happen...if it does, just bail out... // if (!m_editor) return NS_ERROR_FAILURE; // We will fire off the quote operation and wait for it to // finish before we actually do anything with Ender... return QuoteOriginalMessage(mQuoteURI.GetUnicode(), mWhatHolder); } // // This will process the signature file for the user. This method // will always append the results to the mMsgBody member variable. // nsresult nsMsgCompose::ProcessSignature(nsIMsgIdentity *identity, nsString *aMsgBody) { nsresult rv; // Now, we can get sort of fancy. This is the time we need to check // for all sorts of user defined stuff, like signatures and editor // types and the like! // // user_pref(".....signature_file", "y:\\sig.html"); // user_pref(".....use_signature_file", true); // // Note: We will have intelligent signature behavior in that we // look at the signature file first...if the extension is .htm or // .html, we assume its HTML, otherwise, we assume it is plain text // nsAutoString urlStr; nsCOMPtr sigFileSpec; PRBool useSigFile = PR_FALSE; PRBool htmlSig = PR_FALSE; nsAutoString sigData = ""; if (identity) { rv = identity->GetAttachSignature(&useSigFile); if (NS_SUCCEEDED(rv) && useSigFile) { identity->GetSignature(getter_AddRefs(sigFileSpec)); } } // Now, if they didn't even want to use a signature, we should // just return nicely. // if ((!useSigFile) || (!sigFileSpec)) return NS_OK; nsFileSpec testSpec; sigFileSpec->GetFileSpec(&testSpec); // If this file doesn't really exist, just bail! if (!testSpec.Exists()) return NS_OK; // Once we get here, we need to figure out if we have the correct file // type for the editor. // nsFileURL sigFilePath(testSpec); char *fileExt = nsMsgGetExtensionFromFileURL(nsAutoString(sigFilePath)); if ( (fileExt) && (*fileExt) ) { htmlSig = ( (!PL_strcasecmp(fileExt, "HTM")) || (!PL_strcasecmp(fileExt, "HTML")) ); PR_FREEIF(fileExt); } // is this a text sig with an HTML editor? if ( (m_composeHTML) && (!htmlSig) ) ConvertTextToHTML(testSpec, sigData); // is this a HTML sig with a text window? else if ( (!m_composeHTML) && (htmlSig) ) ConvertHTMLToText(testSpec, sigData); else // We have a match... LoadDataFromFile(testSpec, sigData); // Get the data! // Now that sigData holds data...if any, append it to the body in a nice // looking manner // char *htmlBreak = "
"; char *dashes = "--"; if (sigData != "") { if (m_composeHTML) { aMsgBody->Append(htmlBreak); if (!htmlSig) { aMsgBody->Append("
");
        aMsgBody->Append(CRLF);
      }
    }
    else
      aMsgBody->Append(CRLF);
    
    aMsgBody->Append(dashes);

    if ( (!m_composeHTML) || ((m_composeHTML) && (!htmlSig)) )
      aMsgBody->Append(CRLF);
    else if (m_composeHTML)
      aMsgBody->Append(htmlBreak);
    
    aMsgBody->Append(sigData);

    if ( (m_composeHTML) && (!htmlSig) )
      aMsgBody->Append("
"); } return NS_OK; } nsresult nsMsgCompose::BuildBodyMessageAndSignature() { PRUnichar *bod = nsnull; nsresult rv; // // This should never happen...if it does, just bail out... // if (!m_editor) return NS_ERROR_FAILURE; // // Now, we have the body so we can just blast it into the // composition editor window. // m_compFields->GetBody(&bod); nsAutoString tSignature = ""; /* Some time we want to add a signature and sometime we wont. Let's figure that now...*/ PRBool addSignature; switch (mType) { case nsIMsgCompType::New : case nsIMsgCompType::Reply : /* should not append! but just in case */ case nsIMsgCompType::ReplyAll : /* should not append! but just in case */ case nsIMsgCompType::ForwardAsAttachment : /* should not append! but just in case */ case nsIMsgCompType::ForwardInline : case nsIMsgCompType::NewsPost : addSignature = PR_TRUE; break; case nsIMsgCompType::Draft : case nsIMsgCompType::Template : addSignature = PR_FALSE; break; case nsIMsgCompType::MailToUrl : addSignature = !(bod && *bod != 0); break; default : addSignature = PR_FALSE; break; } if (addSignature) ProcessSignature(m_identity, &tSignature); if (m_editor) { if (bod) rv = ConvertAndLoadComposeWindow(m_editor, "", bod, tSignature, PR_FALSE, m_composeHTML); else rv = ConvertAndLoadComposeWindow(m_editor, "", "", tSignature, PR_FALSE, m_composeHTML); } PR_FREEIF(bod); return rv; } nsresult nsMsgCompose::NotifyStateListeners(TStateListenerNotification aNotificationType) { if (!mStateListeners) return NS_OK; // maybe there just aren't any. PRUint32 numListeners; nsresult rv = mStateListeners->Count(&numListeners); if (NS_FAILED(rv)) return rv; PRUint32 i; switch (aNotificationType) { case eComposeFieldsReady: for (i = 0; i < numListeners;i++) { nsCOMPtr iSupports = getter_AddRefs(mStateListeners->ElementAt(i)); nsCOMPtr thisListener = do_QueryInterface(iSupports); if (thisListener) { rv = thisListener->NotifyComposeFieldsReady(); if (NS_FAILED(rv)) break; } } break; default: NS_NOTREACHED("Unknown notification"); } return rv; } nsresult nsMsgCompose::AttachmentPrettyName(const PRUnichar* url, PRUnichar** _retval) { nsCAutoString unescapeULR = url; nsUnescape(unescapeULR); if (unescapeULR.IsEmpty()) { *_retval = nsCRT::strdup(url); return NS_OK; } if (PL_strncasestr(unescapeULR, "file:", 5)) { nsFileURL fileUrl(url); nsFileSpec fileSpec(fileUrl); char * leafName = fileSpec.GetLeafName(); if (leafName && *leafName) { nsAutoString tempStr; nsresult rv = ConvertToUnicode(msgCompFileSystemCharset(), leafName, tempStr); if (NS_FAILED(rv)) tempStr = leafName; *_retval = tempStr.ToNewUnicode(); nsCRT::free(leafName); return NS_OK; } } if (PL_strncasestr(unescapeULR, "http:", 5)) { nsAutoString tempStr = &unescapeULR.mBuffer[7]; *_retval = tempStr.ToNewUnicode(); return NS_OK; } *_retval = nsCRT::strdup(url); return NS_OK; } static nsresult OpenAddressBook(const char * dbName, nsIAddrDatabase** aDatabase, nsIAbDirectory** aDirectory) { if (!aDatabase || !aDirectory) return NS_ERROR_NULL_POINTER; nsresult rv = NS_OK; nsFileSpec* dbPath = nsnull; NS_WITH_SERVICE(nsIAddrBookSession, abSession, kAddrBookSessionCID, &rv); if(NS_SUCCEEDED(rv)) abSession->GetUserProfileDirectory(&dbPath); if (dbPath) { (*dbPath) += dbName; NS_WITH_SERVICE(nsIAddrDatabase, addrDBFactory, kAddressBookDBCID, &rv); if (NS_SUCCEEDED(rv) && addrDBFactory) rv = addrDBFactory->Open(dbPath, PR_TRUE, aDatabase, PR_TRUE); } NS_WITH_SERVICE(nsIRDFService, rdfService, kRDFServiceCID, &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr resource; nsCAutoString path("abdirectory://"); path += dbName; rv = rdfService->GetResource(path, getter_AddRefs(resource)); if (NS_FAILED(rv)) return rv; // query interface rv = resource->QueryInterface(nsIAbDirectory::GetIID(), (void **)aDirectory); return rv; } nsresult nsMsgCompose::GetNoHtmlRecipients(const PRUnichar *recipients, PRUnichar **_retval) { nsresult rv = NS_OK; *_retval = nsnull; PRInt32 j; PRInt32 i; PRInt32 nbrRecipients; nsXPIDLString emailAddr; nsAutoString recipientStr; if (recipients != nsnull) recipientStr = recipients; else { recipientStr += m_compFields->GetTo(); recipientStr += ','; recipientStr += m_compFields->GetCc(); recipientStr += ','; recipientStr += m_compFields->GetBcc(); } /*ducarroz: for now, I've hardcoded the addressbook DBs we are looking in it, will do much better later! */ PRInt32 nbrOfAddrbook = 2; const char addrbookName[2][20] = {"abook.mab", "history.mab"}; nsCOMPtr array; nsCOMPtr noHTMLArray; rv = nsComponentManager::CreateInstance(kMsgRecipientArrayCID, NULL, NS_GET_IID(nsIMsgRecipientArray), (void **) getter_AddRefs(noHTMLArray)); if (NS_FAILED(rv)) return rv; rv = m_compFields->SplitRecipients(recipientStr.GetUnicode(), PR_TRUE, getter_AddRefs(array)); if (NS_SUCCEEDED(rv)) { nsCOMPtr abDataBase; nsCOMPtr abDirectory; nsCOMPtr existingCard; for (j = 0; j < nbrOfAddrbook; j++) { array->GetCount(&nbrRecipients); if (nbrRecipients == 0) break; rv = OpenAddressBook(addrbookName[j], getter_AddRefs(abDataBase), getter_AddRefs(abDirectory)); if (NS_FAILED(rv)) continue; for (i = 0; i < nbrRecipients; i ++) { rv = array->StringAt(i, getter_Copies(emailAddr)); if (NS_FAILED(rv)) continue; nsCAutoString emailStr(emailAddr); rv = abDataBase->GetCardForEmailAddress(abDirectory, emailStr, getter_AddRefs(existingCard)); if (NS_SUCCEEDED(rv) && existingCard) { PRBool bPlainText; rv = existingCard->GetSendPlainText(&bPlainText); if (NS_SUCCEEDED(rv)) { PRBool aBool; if (bPlainText) { //this guy doesn't want/support HTML message, move it in the noHTML array. noHTMLArray->AppendString(emailAddr, &aBool); } array->RemoveStringAt(i, &aBool); if (aBool) { nbrRecipients --; i --; } } } } if (abDataBase) abDataBase->Close(PR_FALSE); } } //now, build the result recipientStr = ""; noHTMLArray->GetCount(&nbrRecipients); for (i = 0; i < nbrRecipients; i ++) { if (! recipientStr.IsEmpty()) recipientStr += ','; noHTMLArray->StringAt(i, getter_Copies(emailAddr)); recipientStr += emailAddr; } //Remaining recipients which do not have an entry in the AB are considered as non HTML compliant array->GetCount(&nbrRecipients); for (i = 0; i < nbrRecipients; i ++) { if (! recipientStr.IsEmpty()) recipientStr += ','; array->StringAt(i, getter_Copies(emailAddr)); recipientStr += emailAddr; } *_retval = recipientStr.ToNewUnicode(); return NS_OK; } nsresult nsMsgCompose::GetNoHtmlNewsgroups(const PRUnichar *newsgroups, PRUnichar **_retval) { //FIX ME: write me nsresult rv = NS_ERROR_NOT_IMPLEMENTED; *_retval = nsnull; return rv; }