Files
Mozilla/mozilla/mailnews/compose/src/nsMsgCompose.cpp
ducarroz%netscape.com aab064666b Fix breakage with plain text message composition due to wrong url loaded in Ender
git-svn-id: svn://10.0.0.236/trunk@42598 18797224-902f-48f8-a5cc-f745e15eee43
1999-08-07 00:30:01 +00:00

1590 lines
44 KiB
C++

/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsMsgCompose.h"
#include "nsMsgCompPrefs.h"
#include "nsIScriptGlobalObject.h"
#include "nsIDOMNode.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsIDOMDocument.h"
#include "nsMsgI18N.h"
#include "nsMsgSend.h"
#include "nsIMessenger.h" //temporary!
#include "nsIMessage.h" //temporary!
#include "nsMsgQuote.h"
#include "nsIPref.h"
#include "nsIEditor.h" // for output flags
#include "nsXPIDLString.h"
#include "nsIParser.h"
#include "nsParserCIID.h"
#include "nsHTMLToTXTSinkStream.h"
#include "CNavDTD.h"
#include "nsMsgCompUtils.h"
#include "nsMsgComposeStringBundle.h"
// XXX temporary so we can use the current identity hack -alecf
#include "nsIMsgMailSession.h"
#include "nsMsgBaseCID.h"
static NS_DEFINE_CID(kMsgMailSessionCID, NS_MSGMAILSESSION_CID);
#if defined(XP_UNIX) || defined(XP_BEOS)
#define TEMP_PATH_DIR "/tmp/"
#elif defined(XP_PC)
#define TEMP_PATH_DIR "c:\\temp\\"
#elif defined(XP_MAC)
#define TEMP_PATH_DIR ""
#else
#error TEMP_PATH_DIR_NOT_DEFINED
#endif
#define TEMP_MESSAGE_IN "tempMessage.eml"
#define TEMP_MESSAGE_OUT "tempMessage.html"
#define TEMP_MESSAGE_OUT_TEXT "tempMessage.txt"
// Defines....
static NS_DEFINE_CID(kMsgQuoteCID, NS_MSGQUOTE_CID);
static NS_DEFINE_CID(kPrefCID, NS_PREF_CID);
// RICHIE
// This is a temp hack and will go away soon!
//
PRBool
nsMsgCompose::UsingOldQuotingHack(const char *compString)
{
nsresult rv;
NS_WITH_SERVICE(nsIPref, prefs, kPrefCID, &rv);
if (NS_SUCCEEDED(rv) && prefs)
rv = prefs->GetBoolPref("mail.old_quoting", &mUseOldQuotingHack);
if (compString)
{
if (PL_strncasecmp(compString, "mailbox_message:", 16) != 0)
{
mUseOldQuotingHack = PR_TRUE;
}
}
return mUseOldQuotingHack;
}
nsMsgCompose::nsMsgCompose()
{
mTempComposeFileSpec = nsnull;
mQuotingToFollow = PR_FALSE;
mSigFileSpec = nsnull;
mUseOldQuotingHack = PR_FALSE; // RICHIE - hack for old quoting
mWhatHolder = 1; // RICHIE - hack for old quoting
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);
// 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;
// temporary - m_composeHTML from the "current" identity
// eventually we should know this when we open the compose window
// -alecf
nsresult rv;
NS_WITH_SERVICE(nsIMsgMailSession, mailSession, kMsgMailSessionCID, &rv);
if (NS_FAILED(rv)) return;
nsCOMPtr<nsIMsgIdentity> identity;
rv = mailSession->GetCurrentIdentity(getter_AddRefs(identity));
if (NS_FAILED(rv)) return;
rv = identity->GetComposeHtml(&m_composeHTML);
NS_INIT_REFCNT();
}
nsMsgCompose::~nsMsgCompose()
{
if (m_editor)
m_editor->UnregisterDocumentStateListener(mDocumentListener);
NS_IF_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, nsCOMTypeInfo<nsMsgCompose>::GetIID());
nsresult
nsMsgCompose::SetQuotingToFollow(PRBool aVal)
{
mQuotingToFollow = aVal;
return NS_OK;
}
PRBool
nsMsgCompose::QuotingToFollow(void)
{
return mQuotingToFollow;
}
nsresult
nsMsgCompose::LoadAsQuote(nsString aTextToLoad)
{
// Now Load the body...
if (m_editor)
{
if (aTextToLoad.Length())
{
m_editor->InsertAsQuotation(aTextToLoad.GetUnicode());
}
}
return NS_OK;
}
nsresult nsMsgCompose::Initialize(nsIDOMWindow *aWindow, const PRUnichar *originalMsgURI,
MSG_ComposeType type, MSG_ComposeFormat format, nsIMsgCompFields *compFields, nsISupports *object)
{
nsresult rv = NS_OK;
if (aWindow)
{
m_window = aWindow;
nsCOMPtr<nsIScriptGlobalObject> globalObj(do_QueryInterface(aWindow));
if (!globalObj)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIWebShell> webShell;
globalObj->GetWebShell(getter_AddRefs(webShell));
if (!webShell)
return NS_ERROR_NOT_INITIALIZED;
m_webShell = webShell;
nsCOMPtr<nsIWebShellContainer> webShellContainer;
m_webShell->GetContainer(*getter_AddRefs(webShellContainer));
if (!webShellContainer)
return NS_ERROR_NOT_INITIALIZED;
nsCOMPtr<nsIWebShellWindow> webShellWin = do_QueryInterface(webShellContainer, &rv);
m_webShellWin = webShellWin;
}
switch (format)
{
case MSGCOMP_FORMAT_HTML : m_composeHTML = PR_TRUE; break;
case MSGCOMP_FORMAT_PlainText : m_composeHTML = PR_FALSE; break;
default : /* m_composeHTML initialized in ctor */ break;
}
CreateMessage(originalMsgURI, type, format, compFields, object); //object is temporary
return rv;
}
nsresult nsMsgCompose::LoadFields()
{
nsresult rv = NS_OK;
// Not sure if this call is really necessary anymore...
return rv;
}
nsresult nsMsgCompose::SetDocumentCharset(const PRUnichar *charset)
{
// Set charset, this will be used for the MIME charset labeling.
m_compFields->SetCharacterSet(nsAutoCString(charset));
return NS_OK;
}
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;
nsXPIDLCString replyTo;
nsXPIDLCString organization;
identity->GetEmail(getter_Copies(email));
identity->GetReplyTo(getter_Copies(replyTo));
identity->GetOrganization(getter_Copies(organization));
m_compFields->SetFrom(NS_CONST_CAST(char*, (const char *)email));
//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(NS_CONST_CAST(char*, (const char *)replyTo));
m_compFields->SetOrganization(NS_CONST_CAST(char*, (const char *)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
nsIMsgSend *tMsgComp = new nsMsgComposeAndSend();
if (!tMsgComp)
return NS_ERROR_OUT_OF_MEMORY;
mMsgSend = do_QueryInterface( tMsgComp );
if (mMsgSend)
{
const char *bodyString = m_compFields->GetBody();
PRInt32 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
delete 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)
{
nsAutoString msgBody;
PRUnichar *bodyText = NULL;
nsString format;
PRUint32 flags = 0;
if (m_composeHTML)
format = "text/html";
else
{
flags = nsIEditor::EditorOutputFormatted;
format = "text/plain";
}
m_editor->GetContentsAs(format.GetUnicode(), flags, &bodyText);
msgBody = bodyText;
delete [] bodyText;
// Convert body to mail charset not to utf-8 (because we don't manipulate body text)
char *outCString;
nsString aCharset = m_compFields->GetCharacterSet();
if (NS_SUCCEEDED(ConvertFromUnicode(aCharset, msgBody, &outCString)))
{
m_compFields->SetBody(outCString);
PR_Free(outCString);
}
else
m_compFields->SetBody(nsAutoCString(msgBody));
}
rv = _SendMsg(deliverMode, identity, callback);
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)
{
nsString aString;
nsString 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(nsAutoCString(addrTo));
if (NS_SUCCEEDED(ConvertFromUnicode(aCharset, addrCc, &outCString)))
{
m_compFields->SetCc(outCString);
PR_Free(outCString);
}
else
m_compFields->SetCc(nsAutoCString(addrCc));
if (NS_SUCCEEDED(ConvertFromUnicode(aCharset, addrBcc, &outCString)))
{
m_compFields->SetBcc(outCString);
PR_Free(outCString);
}
else
m_compFields->SetBcc(nsAutoCString(addrBcc));
if (NS_SUCCEEDED(ConvertFromUnicode(aCharset, newsgroup, &outCString)))
{
m_compFields->SetNewsgroups(outCString);
PR_Free(outCString);
}
else
m_compFields->SetNewsgroups(nsAutoCString(newsgroup));
if (NS_SUCCEEDED(ConvertFromUnicode(aCharset, subject, &outCString)))
{
m_compFields->SetSubject(outCString);
PR_Free(outCString);
}
else
m_compFields->SetSubject(nsAutoCString(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(nsAutoCString(body));
rv = _SendMsg(deliverMode, identity, callback);
}
else
rv = NS_ERROR_NOT_INITIALIZED;
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. */
m_webShellWin->Close();
m_webShellWin = nsnull;
}
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);
//
// Have to check to see if there is a body stored in the
// comp fields...
//
PRBool bodyInCompFields = PR_FALSE;
if (m_compFields)
{
PRUnichar *bod;
m_compFields->GetBody(&bod);
if ((bod) && (*bod))
bodyInCompFields = PR_TRUE;
}
// Now, do the appropriate startup operation...signature only
// or quoted message and signature...
if ( QuotingToFollow() )
return BuildQuotedMessageAndSignature();
else if (bodyInCompFields)
return BuildBodyMessage();
else
return ProcessSignature(nsnull);
}
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)
{
nsMsgCompPrefs prefs;
*aWrapLength = prefs.GetWrapColumn();
return NS_OK;
}
nsresult nsMsgCompose::CreateMessage(const PRUnichar * originalMsgURI, MSG_ComposeType type, MSG_ComposeFormat format,
nsIMsgCompFields * compFields, nsISupports * object)
{
nsresult rv = NS_OK;
if (compFields)
{
if (m_compFields)
m_compFields->Copy(compFields);
return rv;
}
/* At this point, we have a list of URI of original message to reply to or forward but as the BE isn't ready yet,
we still need to use the old patch... gather the information from the object and the temp file use to display the selected message*/
if (object)
{
nsCOMPtr<nsIMessage> message;
rv = object->QueryInterface(nsIMessage::GetIID(), getter_AddRefs(message));
if ((NS_SUCCEEDED(rv)) && message)
{
nsString aString = "";
nsString bString = "";
nsString aCharset = "";
nsString decodedString;
nsString encodedCharset; // we don't use this
char *aCString;
message->GetCharSet(&aCharset);
message->GetSubject(&aString);
switch (type)
{
default: break;
case MSGCOMP_TYPE_Reply :
case MSGCOMP_TYPE_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(nsAutoCString(aCharset));
bString += "Re: ";
bString += aString;
if (NS_SUCCEEDED(rv = nsMsgI18NDecodeMimePartIIStr(bString, encodedCharset, decodedString)))
m_compFields->SetSubject(decodedString.GetUnicode());
else
m_compFields->SetSubject(bString.GetUnicode());
message->GetAuthor(&aString);
m_compFields->SetTo(nsAutoCString(aString));
if (NS_SUCCEEDED(rv = nsMsgI18NDecodeMimePartIIStr(aString, encodedCharset, decodedString)))
if (NS_SUCCEEDED(rv = ConvertFromUnicode(msgCompHeaderInternalCharset(), decodedString, &aCString)))
{
m_compFields->SetTo(aCString);
PR_Free(aCString);
}
if (type == MSGCOMP_TYPE_ReplyAll)
{
nsString cString, dString;
message->GetRecipients(&cString);
CleanUpRecipients(cString);
message->GetCCList(&dString);
CleanUpRecipients(dString);
if (cString.Length() > 0 && dString.Length() > 0)
cString = cString + ", ";
cString = cString + dString;
m_compFields->SetCc(nsAutoCString(cString));
if (NS_SUCCEEDED(rv = nsMsgI18NDecodeMimePartIIStr(cString, encodedCharset, decodedString)))
if (NS_SUCCEEDED(rv = ConvertFromUnicode(msgCompHeaderInternalCharset(), decodedString, &aCString)))
{
m_compFields->SetCc(aCString);
PR_Free(aCString);
}
}
// Setup quoting callbacks for later...
mWhatHolder = 1;
mQuoteURI = originalMsgURI;
break;
}
case MSGCOMP_TYPE_ForwardAsAttachment:
case MSGCOMP_TYPE_ForwardInline:
{
mQuotingToFollow = PR_TRUE;
if (!aCharset.Equals(""))
m_compFields->SetCharacterSet(nsAutoCString(aCharset));
bString += "[Fwd: ";
bString += aString;
bString += "]";
if (NS_SUCCEEDED(rv = nsMsgI18NDecodeMimePartIIStr(bString, encodedCharset, decodedString)))
m_compFields->SetSubject(decodedString.GetUnicode());
else
m_compFields->SetSubject(bString.GetUnicode());
// Setup quoting callbacks for later...
mQuoteURI = originalMsgURI;
if (type == MSGCOMP_TYPE_ForwardAsAttachment)
{
mWhatHolder = 0;
}
else
{
mWhatHolder = 2;
}
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(void)
{
mComposeObj = nsnull;
mMsgBody = "<br><BLOCKQUOTE TYPE=CITE><html><br>--- Original Message ---<br><br>";
mMsgBody = "";
NS_INIT_REFCNT();
}
static nsresult
ConvertBufToPlainText(nsString &aConBuf, const char *charSet)
{
nsresult rv;
nsString convertedText;
nsIParser *parser;
static NS_DEFINE_IID(kCParserIID, NS_IPARSER_IID);
static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID);
rv = nsComponentManager::CreateInstance(kCParserCID, nsnull,
kCParserIID, (void **)&parser);
if (NS_SUCCEEDED(rv) && parser)
{
nsHTMLToTXTSinkStream *sink = nsnull;
rv = NS_New_HTMLToTXT_SinkStream((nsIHTMLContentSink **)&sink, &convertedText, PR_TRUE);
if (sink && NS_SUCCEEDED(rv))
{
sink->DoFragment(PR_TRUE);
parser->SetContentSink(sink);
// Set the charset...
//
if (charSet)
{
nsAutoString cSet(charSet);
parser->SetDocumentCharset(cSet, kCharsetFromMetaTag);
}
nsIDTD* dtd = nsnull;
rv = NS_NewNavHTMLDTD(&dtd);
if (NS_SUCCEEDED(rv))
{
parser->RegisterDTD(dtd);
rv = parser->Parse(aConBuf, 0, "text/html", PR_FALSE, PR_TRUE);
}
NS_IF_RELEASE(dtd);
NS_IF_RELEASE(sink);
}
NS_RELEASE(parser);
//
// Now assign the results if we worked!
//
if (NS_SUCCEEDED(rv))
aConBuf = convertedText;
}
return rv;
}
nsresult
QuotingOutputStreamListener::ConvertToPlainText()
{
return ConvertBufToPlainText(mMsgBody, "UTF-8");
}
NS_IMETHODIMP QuotingOutputStreamListener::OnStartRequest(nsIChannel * /* aChannel */, nsISupports * /* ctxt */)
{
return NS_OK;
}
NS_IMETHODIMP QuotingOutputStreamListener::OnStopRequest(nsIChannel * /* aChannel */, nsISupports * /* ctxt */, nsresult status, const PRUnichar * /* errorMsg */)
{
if (mComposeObj)
{
mMsgBody += "</html></BLOCKQUOTE>";
// 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->mTempComposeFileSpec = nsMsgCreateTempFileSpec(compHTML ? "nscomp.html" : "nscomp.txt");
if (!mComposeObj->mTempComposeFileSpec)
return NS_MSG_ERROR_WRITING_FILE;
nsOutputFileStream tempFile(*(mComposeObj->mTempComposeFileSpec));
if (!tempFile.is_open())
return NS_MSG_ERROR_WRITING_FILE;
tempFile.write(nsAutoCString(mMsgBody), mMsgBody.Length());
mComposeObj->ProcessSignature(&tempFile);
tempFile.close();
// Now load the URL...
nsString urlStr = mComposeObj->mTempComposeFileSpec->GetNativePathCString();
nsIEditorShell *editor;
mComposeObj->GetEditor(&editor);
if (editor)
editor->LoadUrl(urlStr.GetUnicode());
else
return NS_ERROR_FAILURE;
}
return NS_OK;
}
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);
newBuf[count] = '\0';
if (NS_SUCCEEDED(rv))
{
mMsgBody += newBuf;
}
PR_FREEIF(newBuf);
return rv;
}
nsresult
QuotingOutputStreamListener::SetComposeObj(nsMsgCompose *obj)
{
mComposeObj = obj;
return NS_OK;
}
NS_IMPL_ISUPPORTS(QuotingOutputStreamListener, nsCOMTypeInfo<nsIStreamListener>::GetIID());
////////////////////////////////////////////////////////////////////////////////////
// END OF QUOTING LISTENER
////////////////////////////////////////////////////////////////////////////////////
nsresult
nsMsgCompose::QuoteOriginalMessage(const PRUnichar *originalMsgURI, PRInt32 what) // New template
{
nsresult rv;
mQuotingToFollow = PR_FALSE;
nsString tmpURI(originalMsgURI);
char *compString = tmpURI.ToNewCString();
// RICHIE - this will get removed when we
UsingOldQuotingHack(compString);
PR_FREEIF(compString);
if (mUseOldQuotingHack)
{
printf("nsMsgCompose: using old quoting function!");
HackToGetBody(what);
return NS_OK;
}
// Create a mime parser (nsIStreamConverter)!
rv = nsComponentManager::CreateInstance(kMsgQuoteCID,
NULL, nsCOMTypeInfo<nsIMsgQuote>::GetIID(),
(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();
if (!mQuoteStreamListener)
{
printf("Failed to create mQuoteStreamListener\n");
return NS_ERROR_FAILURE;
}
NS_ADDREF(mQuoteStreamListener);
NS_ADDREF(this);
mQuoteStreamListener->SetComposeObj(this);
return mQuote->QuoteMessage(originalMsgURI, mQuoteStreamListener);
}
void nsMsgCompose::HackToGetBody(PRInt32 what)
{
char *buffer = (char *) PR_CALLOC(16384);
if (buffer)
{
nsString fileName(TEMP_PATH_DIR);
fileName += TEMP_MESSAGE_IN;
nsFileSpec fileSpec(fileName);
nsInputFileStream fileStream(fileSpec);
nsString msgBody = (what == 2 && !m_composeHTML) ? "--------Original Message--------\r\n" : "";
// skip RFC822 header
while (!fileStream.eof() && !fileStream.failed() &&
fileStream.is_open())
{
fileStream.readline(buffer, 1024);
if (*buffer == 0)
break;
}
// copy message body
while (!fileStream.eof() && !fileStream.failed() &&
fileStream.is_open())
{
fileStream.readline(buffer, 1024);
if (what == 1 && ! m_composeHTML)
msgBody += "> ";
msgBody += buffer;
msgBody += MSG_LINEBREAK;
}
if (m_composeHTML)
{
nsString lowerMsgBody (msgBody);
lowerMsgBody.ToLowerCase();
PRInt32 startBodyOffset;
PRInt32 endBodyOffset = -1;
PRInt32 offset;
startBodyOffset = lowerMsgBody.Find("<html>");
if (startBodyOffset != -1) //it's an HTML body
{
//Does it have a <body> tag?
offset = lowerMsgBody.Find("<body");
if (offset != -1)
{
offset = lowerMsgBody.FindChar('>', PR_FALSE,offset);
if (offset != -1)
{
startBodyOffset = offset + 1;
endBodyOffset = lowerMsgBody.RFind("</body>");
}
}
if (endBodyOffset == -1)
endBodyOffset = lowerMsgBody.RFind("</html>");
}
if (startBodyOffset == -1)
startBodyOffset = 0;
if (endBodyOffset == -1)
endBodyOffset = lowerMsgBody.Length();
msgBody.Insert(MSG_LINEBREAK, endBodyOffset);
if (startBodyOffset == 0)
{
msgBody.Insert("</html>", endBodyOffset);
msgBody.Insert(MSG_LINEBREAK, endBodyOffset);
}
msgBody.Insert("</blockquote>", endBodyOffset);
msgBody.Insert(MSG_LINEBREAK, endBodyOffset);
msgBody.Insert(MSG_LINEBREAK, startBodyOffset);
msgBody.Insert("<blockquote TYPE=CITE>", startBodyOffset);
msgBody.Insert(MSG_LINEBREAK, startBodyOffset);
if (startBodyOffset == 0)
{
msgBody.Insert("<html>", startBodyOffset);
msgBody.Insert(MSG_LINEBREAK, startBodyOffset);
msgBody.Insert("<!doctype html public \"-//w3c//dtd html 4.0 transitional//en\">", startBodyOffset);
}
}
else
{
//ducarroz: today, we are not converting HTML to plain text if needed!
}
// m_compFields->SetBody(msgBody.ToNewCString());
// SetBody() strdup()'s cmsgBody.
m_compFields->SetBody(nsAutoCString(msgBody));
PR_Free(buffer);
ProcessSignature(nsnull);
}
}
//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(nsCOMTypeInfo<nsIMsgSendListener>::GetIID()))
{
*aInstancePtr = (nsIMsgSendListener *) this;
NS_ADDREF_THIS();
return NS_OK;
}
if (aIID.Equals(nsCOMTypeInfo<nsIMsgCopyServiceListener>::GetIID()))
{
*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)))
{
nsString2 compString("nocopy://");
if (fieldsFCC && *fieldsFCC)
{
if (compString == fieldsFCC)
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(nsString2* 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, nsCOMTypeInfo<nsIDocumentStateListener>::GetIID());
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 let
// the user run hog wild with editing...but first, lets cleanup
// any temp files
//
if (mComposeObj->mSigFileSpec)
{
mComposeObj->mSigFileSpec->Delete(PR_FALSE);
delete mComposeObj->mSigFileSpec;
mComposeObj->mSigFileSpec = nsnull;
}
if (mComposeObj->mTempComposeFileSpec)
{
mComposeObj->mTempComposeFileSpec->Delete(PR_FALSE);
delete mComposeObj->mTempComposeFileSpec;
mComposeObj->mTempComposeFileSpec = nsnull;
}
// RICHIE - hack! This is only if we are using the old
// quoting hack
if (mComposeObj->mUseOldQuotingHack)
{
PRUnichar *bod;
nsIMsgCompFields *compFields;
mComposeObj->GetCompFields(&compFields);
compFields->GetBody(&bod);
mComposeObj->LoadAsQuote(nsString(bod));
PR_FREEIF(bod);
}
// RICHIE - hack! This is only if we are using the old
// quoting hack
return NS_OK;
}
nsresult
nsMsgDocumentStateListener::NotifyDocumentWillBeDestroyed(void)
{
return NS_OK;
}
nsresult
nsMsgDocumentStateListener::NotifyDocumentStateChanged(PRBool nowDirty)
{
return NS_OK;
}
nsresult
nsMsgCompose::ConvertHTMLToText(char *aSigFile, nsString &aSigData)
{
nsresult rv;
nsString origBuf;
rv = LoadDataFromFile(aSigFile, origBuf);
if (NS_FAILED(rv))
return rv;
ConvertBufToPlainText(origBuf, nsnull);
aSigData = origBuf;
return NS_OK;
}
nsresult
nsMsgCompose::ConvertTextToHTML(char *aSigFile, nsString &aSigData)
{
nsresult rv;
nsString origBuf;
rv = LoadDataFromFile(aSigFile, origBuf);
if (NS_FAILED(rv))
return rv;
aSigData = "<pre>";
aSigData.Append(origBuf);
aSigData.Append("</pre>");
return NS_OK;
}
nsresult
nsMsgCompose::LoadDataFromFile(char *sigFilePath, nsString &sigData)
{
nsFileSpec fSpec(sigFilePath);
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);
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. If aAppendFile
// is passed in, then this will be treated as an operation following
// a quoting operation and the specified signature will be appended
// to the file passed in. If this argument is null, then this is a clean
// compose window and we should just create whatever we need to create
// as a temp file and do a LoadURL operation.
//
nsresult
nsMsgCompose::ProcessSignature(nsOutputFileStream *aAppendFileStream)
{
nsresult rv;
//
// This should never happen...if it does, just bail out...
//
if (!m_editor)
return NS_ERROR_FAILURE;
// 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("mail.signature_file", "y:\\sig.html");
// user_pref("mail.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
//
nsString urlStr;
char *sigFilePath = nsnull;
PRBool useSigFile = PR_FALSE;
PRBool htmlSig = PR_FALSE;
nsString sigData = "";
NS_WITH_SERVICE(nsIPref, prefs, kPrefCID, &rv);
if (NS_SUCCEEDED(rv) && prefs)
{
rv = prefs->GetBoolPref("mail.use_signature_file", &useSigFile);
if (NS_SUCCEEDED(rv) && useSigFile)
{
rv = prefs->CopyCharPref("mail.signature_file", &sigFilePath);
}
}
// Setup the urlStr to load to be the signature if the user wants it
// that way...
//
if ((useSigFile) && (sigFilePath))
{
nsFileSpec testSpec(sigFilePath);
if (!testSpec.Exists())
{
PR_FREEIF(sigFilePath);
sigFilePath = nsnull;
if (m_composeHTML)
urlStr = "chrome://messengercompose/content/defaultHtmlBody.html";
else
urlStr = "chrome://messengercompose/content/defaultTextBody.txt";
}
else
{
// Once we get here, we need to figure out if we have the correct file
// type for the editor.
//
char *fileExt = nsMsgGetExtensionFromFileURL(nsString(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(sigFilePath, sigData);
// is this a HTML sig with a text window?
else if ( (!m_composeHTML) && (htmlSig) )
ConvertHTMLToText(sigFilePath, sigData);
else // We have a match...
{
if (!aAppendFileStream) // Just load this URL
urlStr = nsMsgPlatformFileToURL(sigFilePath);
else
LoadDataFromFile(sigFilePath, sigData); // Get the data!
}
}
}
else
{
if (m_composeHTML)
urlStr = "chrome://messengercompose/content/defaultHtmlBody.html";
else
urlStr = "chrome://messengercompose/content/defaultTextBody.txt";
}
PR_FREEIF(sigFilePath);
// This is the "load signature alone" operation!
char *htmlBreak = "<BR>";
char *dashes = "--";
if (!aAppendFileStream)
{
if ( (sigData == "") && (urlStr != "") ) // just load URL
{
m_editor->LoadUrl(urlStr.GetUnicode());
}
else // Have to write data to a temp file then load the URL...
{
mSigFileSpec = nsMsgCreateTempFileSpec(m_composeHTML ? "sig.html" : "sig.txt");
if (!mSigFileSpec)
return NS_ERROR_FAILURE;
nsOutputFileStream tempFile(*mSigFileSpec);
if (!tempFile.is_open())
return NS_MSG_ERROR_WRITING_FILE;
if (m_composeHTML)
tempFile.write(htmlBreak, PL_strlen(htmlBreak));
else
{
tempFile.write(MSG_LINEBREAK, MSG_LINEBREAK_LEN);
tempFile.write(dashes, PL_strlen(dashes));
}
if (m_composeHTML)
tempFile.write(htmlBreak, PL_strlen(htmlBreak));
else
tempFile.write(MSG_LINEBREAK, MSG_LINEBREAK_LEN);
tempFile.write(nsAutoCString(sigData), sigData.Length());
tempFile.close();
urlStr = mSigFileSpec->GetNativePathCString();
m_editor->LoadUrl(urlStr.GetUnicode());
}
}
else // This is where we are appending to the current file...
{
if (sigData != "")
{
if (!aAppendFileStream->is_open())
return NS_MSG_ERROR_WRITING_FILE;
if (m_composeHTML)
aAppendFileStream->write(htmlBreak, PL_strlen(htmlBreak));
else
aAppendFileStream->write(MSG_LINEBREAK, MSG_LINEBREAK_LEN);
aAppendFileStream->write(dashes, PL_strlen(dashes));
if (m_composeHTML)
aAppendFileStream->write(htmlBreak, PL_strlen(htmlBreak));
else
aAppendFileStream->write(MSG_LINEBREAK, MSG_LINEBREAK_LEN);
aAppendFileStream->write(nsAutoCString(sigData), sigData.Length());
}
// else // We are punting on putting a default signature on a quoted message!
}
return NS_OK;
}
nsresult
nsMsgCompose::BuildBodyMessage()
{
PRUnichar *bod;
//
// This should never happen...if it does, just bail out...
//
if (!m_editor)
return NS_ERROR_FAILURE;
// Since we have a body in the comp fields, we need to create a
// body of this old body...
//
mTempComposeFileSpec = nsMsgCreateTempFileSpec(m_composeHTML ? "nscomp.html" : "nscomp.txt");
if (!mTempComposeFileSpec)
return NS_MSG_ERROR_WRITING_FILE;
nsOutputFileStream tempFile(*(mTempComposeFileSpec));
if (!tempFile.is_open())
return NS_MSG_ERROR_WRITING_FILE;
m_compFields->GetBody(&bod);
nsString tempBody(bod);
tempFile.write(nsAutoCString(tempBody), tempBody.Length());
tempFile.close();
//
// Now load the URL...
//
nsString urlStr = nsMsgPlatformFileToURL(mTempComposeFileSpec->GetNativePathCString());
m_editor->LoadUrl(urlStr.GetUnicode());
return NS_OK;
}