Mozilla/mozilla/mailnews/compose/src/nsMsgSendLater.cpp
2007-08-25 01:24:23 +00:00

1255 lines
34 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla 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/MPL/
*
* 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 the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsMsgSendLater.h"
#include "nsCOMPtr.h"
#include "nsMsgCopy.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIEnumerator.h"
#include "nsIFile.h"
#include "nsISmtpService.h"
#include "nsIMsgMailNewsUrl.h"
#include "nsIMsgIncomingServer.h"
#include "nsICopyMessageListener.h"
#include "nsIMsgMessageService.h"
#include "nsIMsgMailSession.h"
#include "nsIMsgAccountManager.h"
#include "nsMsgBaseCID.h"
#include "nsMsgCompCID.h"
#include "nsMsgCompUtils.h"
#include "nsMsgUtils.h"
#include "nsMsgFolderFlags.h"
#include "nsISupportsArray.h"
#include "nsMailHeaders.h"
#include "nsMsgPrompts.h"
#include "nsIMsgSendListener.h"
#include "nsIMsgSendLaterListener.h"
#include "nsMsgCopy.h"
#include "nsIPrompt.h"
#include "nsIURI.h"
#include "nsISmtpUrl.h"
#include "nsIChannel.h"
#include "nsNetUtil.h"
#include "prlog.h"
#include "nsMsgSimulateError.h"
#include "nsIMimeConverter.h"
#include "nsMsgMimeCID.h"
#include "nsComposeStrings.h"
NS_IMPL_ISUPPORTS2(nsMsgSendLater, nsIMsgSendLater, nsIStreamListener)
nsMsgSendLater::nsMsgSendLater()
{
mTempFile = nsnull;
mOutFile = nsnull;
mTotalSentSuccessfully = 0;
mTotalSendCount = 0;
mMessageFolder = nsnull;
mMessage = nsnull;
mLeftoverBuffer = nsnull;
mListenerArray = nsnull;
mListenerArrayCount = 0;
m_to = nsnull;
m_bcc = nsnull;
m_fcc = nsnull;
m_newsgroups = nsnull;
m_newshost = nsnull;
m_headers = nsnull;
m_flags = 0;
m_headersFP = 0;
m_inhead = PR_TRUE;
m_headersPosition = 0;
m_bytesRead = 0;
m_position = 0;
m_flagsPosition = 0;
m_headersSize = 0;
mIdentityKey = nsnull;
mAccountKey = nsnull;
NS_NewISupportsArray(getter_AddRefs(mMessagesToSend));
}
nsMsgSendLater::~nsMsgSendLater()
{
mTempFile = nsnull;
PR_Free(m_to);
PR_Free(m_fcc);
PR_Free(m_bcc);
PR_Free(m_newsgroups);
PR_Free(m_newshost);
PR_Free(m_headers);
PR_Free(mLeftoverBuffer);
PR_Free(mIdentityKey);
PR_Free(mAccountKey);
}
// Stream is done...drive on!
NS_IMETHODIMP
nsMsgSendLater::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
{
nsresult rv;
// First, this shouldn't happen, but if
// it does, flush the buffer and move on.
if (mLeftoverBuffer)
{
DeliverQueuedLine(mLeftoverBuffer, PL_strlen(mLeftoverBuffer));
}
if (mOutFile)
mOutFile->Close();
// See if we succeeded on reading the message from the message store?
//
SET_SIMULATED_ERROR(SIMULATED_SEND_ERROR_13, status, NS_ERROR_FAILURE);
if (NS_SUCCEEDED(status))
{
// Message is done...send it!
rv = CompleteMailFileSend();
#ifdef NS_DEBUG
printf("nsMsgSendLater: Success on getting message...\n");
#endif
// If the send operation failed..try the next one...
if (NS_FAILED(rv))
{
rv = StartNextMailFileSend();
if (NS_FAILED(rv))
NotifyListenersOnStopSending(rv, nsnull, mTotalSendCount, mTotalSentSuccessfully);
}
}
else
{
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
if(!channel) return NS_ERROR_FAILURE;
// extract the prompt object to use for the alert from the url....
nsCOMPtr<nsIURI> uri;
nsCOMPtr<nsIPrompt> promptObject;
if (channel)
{
channel->GetURI(getter_AddRefs(uri));
nsCOMPtr<nsISmtpUrl> smtpUrl (do_QueryInterface(uri));
if (smtpUrl)
smtpUrl->GetPrompt(getter_AddRefs(promptObject));
}
nsMsgDisplayMessageByID(promptObject, NS_ERROR_QUEUED_DELIVERY_FAILED);
// Getting the data failed, but we will still keep trying to send the rest...
rv = StartNextMailFileSend();
if (NS_FAILED(rv))
NotifyListenersOnStopSending(rv, nsnull, mTotalSendCount, mTotalSentSuccessfully);
}
return rv;
}
char *
FindEOL(char *inBuf, char *buf_end)
{
char *buf = inBuf;
char *findLoc = nsnull;
while (buf <= buf_end)
if (*buf == 0)
return buf;
else if ( (*buf == '\n') || (*buf == '\r') )
{
findLoc = buf;
break;
}
else
++buf;
if (!findLoc)
return nsnull;
else if ((findLoc + 1) > buf_end)
return buf;
if ( (*findLoc == '\n' && *(findLoc+1) == '\r') ||
(*findLoc == '\r' && *(findLoc+1) == '\n'))
findLoc++; // possibly a pair.
return findLoc;
}
nsresult
nsMsgSendLater::RebufferLeftovers(char *startBuf, PRUint32 aLen)
{
PR_FREEIF(mLeftoverBuffer);
mLeftoverBuffer = (char *)PR_Malloc(aLen + 1);
if (!mLeftoverBuffer)
return NS_ERROR_OUT_OF_MEMORY;
memcpy(mLeftoverBuffer, startBuf, aLen);
mLeftoverBuffer[aLen] = '\0';
return NS_OK;
}
nsresult
nsMsgSendLater::BuildNewBuffer(const char* aBuf, PRUint32 aCount, PRUint32 *totalBufSize)
{
// Only build a buffer when there are leftovers...
if (!mLeftoverBuffer)
return NS_ERROR_FAILURE;
PRInt32 leftoverSize = PL_strlen(mLeftoverBuffer);
mLeftoverBuffer = (char *)PR_Realloc(mLeftoverBuffer, aCount + leftoverSize);
if (!mLeftoverBuffer)
return NS_ERROR_FAILURE;
memcpy(mLeftoverBuffer + leftoverSize, aBuf, aCount);
*totalBufSize = aCount + leftoverSize;
return NS_OK;
}
// Got data?
NS_IMETHODIMP
nsMsgSendLater::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
{
// This is a little bit tricky since we have to chop random
// buffers into lines and deliver the lines...plus keeping the
// leftovers for next time...some fun, eh?
//
nsresult rv = NS_OK;
char *startBuf;
char *endBuf;
char *lineEnd;
char *newbuf = nsnull;
PRUint32 size;
PRUint32 aCount = count;
char *aBuf = (char *)PR_Malloc(aCount + 1);
inStr->Read(aBuf, count, &aCount);
// First, create a new work buffer that will
if (NS_FAILED(BuildNewBuffer(aBuf, aCount, &size))) // no leftovers...
{
startBuf = (char *)aBuf;
endBuf = (char *)(aBuf + aCount - 1);
}
else // yum, leftovers...new buffer created...sitting in mLeftoverBuffer
{
newbuf = mLeftoverBuffer;
startBuf = newbuf;
endBuf = startBuf + size - 1;
mLeftoverBuffer = nsnull; // null out this
}
while (startBuf <= endBuf)
{
lineEnd = FindEOL(startBuf, endBuf);
if (!lineEnd)
{
rv = RebufferLeftovers(startBuf, (endBuf - startBuf) + 1);
break;
}
rv = DeliverQueuedLine(startBuf, (lineEnd - startBuf) + 1);
if (NS_FAILED(rv))
break;
startBuf = lineEnd+1;
}
PR_Free(newbuf);
PR_Free(aBuf);
return rv;
}
NS_IMETHODIMP
nsMsgSendLater::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
{
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////////
// This is the listener class for the send operation. We have to create this class
// to listen for message send completion and eventually notify the caller
////////////////////////////////////////////////////////////////////////////////////
NS_IMPL_ISUPPORTS2(SendOperationListener, nsIMsgSendListener,
nsIMsgCopyServiceListener)
SendOperationListener::SendOperationListener(void)
{
mSendLater = nsnull;
}
SendOperationListener::~SendOperationListener(void)
{
}
nsresult
SendOperationListener::SetSendLaterObject(nsMsgSendLater *obj)
{
mSendLater = obj;
return NS_OK;
}
nsresult
SendOperationListener::OnGetDraftFolderURI(const char *aFolderURI)
{
return NS_OK;
}
nsresult
SendOperationListener::OnStartSending(const char *aMsgID, PRUint32 aMsgSize)
{
#ifdef NS_DEBUG
printf("SendOperationListener::OnStartSending()\n");
#endif
return NS_OK;
}
nsresult
SendOperationListener::OnProgress(const char *aMsgID, PRUint32 aProgress, PRUint32 aProgressMax)
{
#ifdef NS_DEBUG
printf("SendOperationListener::OnProgress()\n");
#endif
return NS_OK;
}
nsresult
SendOperationListener::OnStatus(const char *aMsgID, const PRUnichar *aMsg)
{
#ifdef NS_DEBUG
printf("SendOperationListener::OnStatus()\n");
#endif
return NS_OK;
}
nsresult
SendOperationListener::OnSendNotPerformed(const char *aMsgID, nsresult aStatus)
{
return NS_OK;
}
nsresult
SendOperationListener::OnStopSending(const char *aMsgID, nsresult aStatus, const PRUnichar *aMsg,
nsIFile *returnFile)
{
nsresult rv = NS_OK;
if (mSendLater)
{
if (NS_SUCCEEDED(aStatus))
{
#ifdef NS_DEBUG
printf("nsMsgSendLater: Success on the message send operation!\n");
#endif
PRBool deleteMsgs = PR_TRUE;
//
// Now delete the message from the outbox folder.
//
nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (pPrefBranch)
pPrefBranch->GetBoolPref("mail.really_delete_draft", &deleteMsgs);
mSendLater->SetOrigMsgDisposition();
if (deleteMsgs)
{
mSendLater->DeleteCurrentMessage();
}
++(mSendLater->mTotalSentSuccessfully);
}
else if (mSendLater)
{
mSendLater->NotifyListenersOnStopSending(aStatus, nsnull,
mSendLater->mTotalSendCount,
mSendLater->mTotalSentSuccessfully);
NS_RELEASE(mSendLater);
}
}
return rv;
}
// nsIMsgCopyServiceListener
nsresult
SendOperationListener::OnStartCopy(void)
{
return NS_OK;
}
nsresult
SendOperationListener::OnProgress(PRUint32 aProgress, PRUint32 aProgressMax)
{
return NS_OK;
}
nsresult
SendOperationListener::SetMessageKey(PRUint32 aKey)
{
NS_NOTREACHED("SendOperationListener::SetMessageKey()");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
SendOperationListener::GetMessageId(nsACString& messageId)
{
NS_NOTREACHED("SendOperationListener::GetMessageId()\n");
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
SendOperationListener::OnStopCopy(nsresult aStatus)
{
if (mSendLater)
{
// Regardless of the success of the copy we will still keep trying
// to send the rest...
nsresult rv;
rv = mSendLater->StartNextMailFileSend();
if (NS_FAILED(rv))
mSendLater->NotifyListenersOnStopSending(rv, nsnull,
mSendLater->mTotalSendCount,
mSendLater->mTotalSentSuccessfully);
NS_RELEASE(mSendLater);
}
return NS_OK;
}
nsresult
nsMsgSendLater::CompleteMailFileSend()
{
// get the identity from the key
// if no key, or we fail to find the identity
// use the default identity on the default account
nsCOMPtr<nsIMsgIdentity> identity;
nsresult rv = GetIdentityFromKey(mIdentityKey, getter_AddRefs(identity));
NS_ENSURE_SUCCESS(rv,rv);
// If for some reason the tmp file didn't get created, we've failed here
PRBool created;
mTempFile->Exists(&created);
if (!created)
return NS_ERROR_FAILURE;
// Get the recipients...
nsCString recips;
nsCString ccList;
if (NS_FAILED(mMessage->GetRecipients(getter_Copies(recips))))
return NS_ERROR_UNEXPECTED;
else
mMessage->GetCcList(getter_Copies(ccList));
nsCOMPtr<nsIMsgCompFields> compFields = do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr<nsIMsgSend> pMsgSend = do_CreateInstance(NS_MSGSEND_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr<nsIMimeConverter> mimeConverter = do_GetService(NS_MIME_CONVERTER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
// Since we have already parsed all of the headers, we are simply going to
// set the composition fields and move on.
//
nsCString author;
mMessage->GetAuthor(getter_Copies(author));
nsMsgCompFields * fields = (nsMsgCompFields *)compFields.get();
nsCString decodedString;
// decoded string is null if the input is not MIME encoded
mimeConverter->DecodeMimeHeader(author.get(), getter_Copies(decodedString));
fields->SetFrom(decodedString.IsEmpty() ? author.get() : decodedString.get());
if (m_to)
{
mimeConverter->DecodeMimeHeader(m_to, getter_Copies(decodedString));
fields->SetTo(decodedString.IsEmpty() ? m_to : decodedString.get());
}
if (m_bcc)
{
mimeConverter->DecodeMimeHeader(m_bcc, getter_Copies(decodedString));
fields->SetBcc(decodedString.IsEmpty() ? m_bcc : decodedString.get());
}
if (m_fcc)
{
mimeConverter->DecodeMimeHeader(m_fcc, getter_Copies(decodedString));
fields->SetFcc(decodedString.IsEmpty() ? m_fcc : decodedString.get());
}
if (m_newsgroups)
fields->SetNewsgroups(m_newsgroups);
#if 0
// needs cleanup. SetNewspostUrl()?
if (m_newshost)
fields->SetNewshost(m_newshost);
#endif
// Create the listener for the send operation...
SendOperationListener * sendListener = new SendOperationListener();
if (!sendListener)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(sendListener);
// set this object for use on completion...
sendListener->SetSendLaterObject(this);
nsCOMPtr <nsIMsgStatusFeedback> statusFeedback;
if (m_window)
m_window->GetStatusFeedback(getter_AddRefs(statusFeedback));
NS_ADDREF(this); //TODO: We should remove this!!!
rv = pMsgSend->SendMessageFile(identity,
mAccountKey,
compFields, // nsIMsgCompFields *fields,
mTempFile, // nsIFile *sendFile,
PR_TRUE, // PRBool deleteSendFileOnCompletion,
PR_FALSE, // PRBool digest_p,
nsIMsgSend::nsMsgSendUnsent, // nsMsgDeliverMode mode,
nsnull, // nsIMsgDBHdr *msgToReplace,
sendListener,
statusFeedback,
nsnull);
NS_IF_RELEASE(sendListener);
return rv;
}
nsresult
nsMsgSendLater::StartNextMailFileSend()
{
nsresult rv = NS_OK;
nsCString messageURI;
if ( (!mEnumerator) || (mEnumerator->IsDone() == NS_OK) )
{
// Call any listeners on this operation and then exit cleanly
#ifdef NS_DEBUG
printf("nsMsgSendLater: Finished \"Send Later\" operation.\n");
#endif
mMessagesToSend->Clear(); // clear out our array
NotifyListenersOnStopSending(NS_OK, nsnull, mTotalSendCount, mTotalSentSuccessfully);
return NS_OK;
}
nsCOMPtr<nsISupports> currentItem;
mEnumerator->CurrentItem(getter_AddRefs(currentItem));
// advance to the next item for the next pass.
mEnumerator->Next();
mMessage = do_QueryInterface(currentItem);
if(!mMessage)
return NS_ERROR_NOT_AVAILABLE;
nsCOMPtr<nsIMsgDBHdr> myRDFNode ;
myRDFNode = do_QueryInterface(mMessage, &rv);
if(NS_FAILED(rv) || (!myRDFNode))
return NS_ERROR_NOT_AVAILABLE;
mMessageFolder->GetUriForMsg(mMessage, messageURI);
rv = nsMsgCreateTempFile("nsqmail.tmp", getter_AddRefs(mTempFile));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr <nsIMsgMessageService> messageService;
rv = GetMessageServiceFromURI(messageURI, getter_AddRefs(messageService));
if (NS_FAILED(rv) && !messageService)
return NS_ERROR_FACTORY_NOT_LOADED;
++mTotalSendCount;
// Setup what we need to parse the data stream correctly
m_inhead = PR_TRUE;
m_headersFP = 0;
m_headersPosition = 0;
m_bytesRead = 0;
m_position = 0;
m_flagsPosition = 0;
m_headersSize = 0;
PR_FREEIF(mLeftoverBuffer);
//
// Now, get our stream listener interface and plug it into the DisplayMessage
// operation
//
NS_ADDREF(this);
nsCOMPtr<nsIStreamListener> convertedListener = do_QueryInterface(this);
if (convertedListener)
{
// Now, just plug the two together and get the hell out of the way!
rv = messageService->DisplayMessage(messageURI.get(), convertedListener, nsnull, nsnull, nsnull, nsnull);
}
else
rv = NS_ERROR_FAILURE;
Release();
if (NS_FAILED(rv))
return rv;
return NS_OK;
}
NS_IMETHODIMP
nsMsgSendLater::GetUnsentMessagesFolder(nsIMsgIdentity *aIdentity, nsIMsgFolder **folder)
{
nsCString uri;
GetFolderURIFromUserPrefs(nsIMsgSend::nsMsgQueueForLater, aIdentity, uri);
nsresult rv = LocateMessageFolder(aIdentity, nsIMsgSend::nsMsgQueueForLater, uri.get(), folder);
return rv;
}
//
// To really finalize this capability, we need to have the ability to get
// the message from the mail store in a stream for processing. The flow
// would be something like this:
//
// foreach (message in Outbox folder)
// get stream of Nth message
// if (not done with headers)
// Tack on to current buffer of headers
// when done with headers
// BuildHeaders()
// Write Headers to Temp File
// after done with headers
// write rest of message body to temp file
//
// when done with the message
// do send operation
//
// when send is complete
// Copy from Outbox to FCC folder
// Delete from Outbox folder
//
//
NS_IMETHODIMP
nsMsgSendLater::SendUnsentMessages(nsIMsgIdentity *identity)
{
nsresult rv = GetUnsentMessagesFolder(identity, getter_AddRefs(mMessageFolder));
NS_ENSURE_SUCCESS(rv,rv);
// ### fix me - if we need to reparse the folder, this will be asynchronous
nsCOMPtr<nsISimpleEnumerator> enumerator;
nsresult ret = mMessageFolder->GetMessages(m_window, getter_AddRefs(enumerator));
if (NS_FAILED(ret) || (!enumerator))
{
return NS_ERROR_FAILURE;
}
// copy all the elements in the enumerator into our isupports array....
nsCOMPtr<nsISupports> currentItem;
PRBool hasMoreElements = PR_FALSE;
while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreElements)) && hasMoreElements)
{
rv = enumerator->GetNext(getter_AddRefs(currentItem));
if (NS_SUCCEEDED(rv) && currentItem)
mMessagesToSend->AppendElement(currentItem);
}
// now get an enumerator for our array
mMessagesToSend->Enumerate(getter_AddRefs(mEnumerator));
return StartNextMailFileSend();
}
nsresult nsMsgSendLater::SetOrigMsgDisposition()
{
// We're finished sending a queued message. We need to look at mMessage
// and see if we need to set replied/forwarded
// flags for the original message that this message might be a reply to
// or forward of.
nsCString originalMsgURIs;
nsCString queuedDisposition;
mMessage->GetStringProperty(ORIG_URI_PROPERTY, getter_Copies(originalMsgURIs));
mMessage->GetStringProperty(QUEUED_DISPOSITION_PROPERTY, getter_Copies(queuedDisposition));
if (!queuedDisposition.IsEmpty())
{
nsCStringArray uriArray;
uriArray.ParseString(originalMsgURIs.get(), ",");
for (PRInt32 i = 0; i < uriArray.Count(); i++)
{
nsCOMPtr <nsIMsgDBHdr> msgHdr;
nsresult rv = GetMsgDBHdrFromURI(uriArray[i]->get(), getter_AddRefs(msgHdr));
NS_ENSURE_SUCCESS(rv,rv);
if (msgHdr)
{
// get the folder for the message resource
nsCOMPtr<nsIMsgFolder> msgFolder;
msgHdr->GetFolder(getter_AddRefs(msgFolder));
if (msgFolder)
{
nsMsgDispositionState dispositionSetting = nsIMsgFolder::nsMsgDispositionState_Replied;
if (queuedDisposition.Equals("forwarded"))
dispositionSetting = nsIMsgFolder::nsMsgDispositionState_Forwarded;
msgFolder->AddMessageDispositionState(msgHdr, dispositionSetting);
}
}
}
}
return NS_OK;
}
nsresult
nsMsgSendLater::DeleteCurrentMessage()
{
// Get the composition fields interface
nsCOMPtr<nsISupportsArray> msgArray = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID);
if (!msgArray)
return NS_ERROR_FACTORY_NOT_LOADED;
nsCOMPtr<nsISupports> msgSupport = do_QueryInterface(mMessage);
msgArray->InsertElementAt(msgSupport, 0);
nsresult res = mMessageFolder->DeleteMessages(msgArray, nsnull, PR_TRUE, PR_FALSE, nsnull, PR_FALSE /*allowUndo*/);
if (NS_FAILED(res))
return NS_ERROR_FAILURE;
return NS_OK;
}
//
// This function parses the headers, and also deletes from the header block
// any headers which should not be delivered in mail, regardless of whether
// they were present in the queue file. Such headers include: BCC, FCC,
// Sender, X-Mozilla-Status, X-Mozilla-News-Host, and Content-Length.
// (Content-Length is for the disk file only, and must not be allowed to
// escape onto the network, since it depends on the local linebreak
// representation. Arguably, we could allow Lines to escape, but it's not
// required by NNTP.)
//
#define UNHEX(C) \
((C >= '0' && C <= '9') ? C - '0' : \
((C >= 'A' && C <= 'F') ? C - 'A' + 10 : \
((C >= 'a' && C <= 'f') ? C - 'a' + 10 : 0)))
nsresult
nsMsgSendLater::BuildHeaders()
{
char *buf = m_headers;
char *buf_end = buf + m_headersFP;
PR_FREEIF(m_to);
PR_FREEIF(m_bcc);
PR_FREEIF(m_newsgroups);
PR_FREEIF(m_newshost);
PR_FREEIF(m_fcc);
PR_FREEIF(mIdentityKey);
PR_FREEIF(mAccountKey);
m_flags = 0;
while (buf < buf_end)
{
PRBool prune_p = PR_FALSE;
PRBool do_flags_p = PR_FALSE;
char *colon = PL_strchr(buf, ':');
char *end;
char *value = 0;
char **header = 0;
char *header_start = buf;
if (! colon)
break;
end = colon;
while (end > buf && (*end == ' ' || *end == '\t'))
end--;
switch (buf [0])
{
case 'B': case 'b':
if (!PL_strncasecmp ("BCC", buf, end - buf))
{
header = &m_bcc;
prune_p = PR_TRUE;
}
break;
case 'C': case 'c':
if (!PL_strncasecmp ("CC", buf, end - buf))
header = &m_to;
else if (!PL_strncasecmp (HEADER_CONTENT_LENGTH, buf, end - buf))
prune_p = PR_TRUE;
break;
case 'F': case 'f':
if (!PL_strncasecmp ("FCC", buf, end - buf))
{
header = &m_fcc;
prune_p = PR_TRUE;
}
break;
case 'L': case 'l':
if (!PL_strncasecmp ("Lines", buf, end - buf))
prune_p = PR_TRUE;
break;
case 'N': case 'n':
if (!PL_strncasecmp ("Newsgroups", buf, end - buf))
header = &m_newsgroups;
break;
case 'S': case 's':
if (!PL_strncasecmp ("Sender", buf, end - buf))
prune_p = PR_TRUE;
break;
case 'T': case 't':
if (!PL_strncasecmp ("To", buf, end - buf))
header = &m_to;
break;
case 'X': case 'x':
{
PRInt32 headLen = PL_strlen(HEADER_X_MOZILLA_STATUS2);
if (headLen == end - buf &&
!PL_strncasecmp(HEADER_X_MOZILLA_STATUS2, buf, end - buf))
prune_p = PR_TRUE;
else if (PL_strlen(HEADER_X_MOZILLA_STATUS) == end - buf &&
!PL_strncasecmp(HEADER_X_MOZILLA_STATUS, buf, end - buf))
prune_p = do_flags_p = PR_TRUE;
else if (!PL_strncasecmp(HEADER_X_MOZILLA_DRAFT_INFO, buf, end - buf))
prune_p = PR_TRUE;
else if (!PL_strncasecmp(HEADER_X_MOZILLA_KEYWORDS, buf, end - buf))
prune_p = PR_TRUE;
else if (!PL_strncasecmp(HEADER_X_MOZILLA_NEWSHOST, buf, end - buf))
{
prune_p = PR_TRUE;
header = &m_newshost;
}
else if (!PL_strncasecmp(HEADER_X_MOZILLA_IDENTITY_KEY, buf, end - buf))
{
prune_p = PR_TRUE;
header = &mIdentityKey;
}
else if (!PL_strncasecmp(HEADER_X_MOZILLA_ACCOUNT_KEY, buf, end - buf))
{
prune_p = PR_TRUE;
header = &mAccountKey;
}
break;
}
}
buf = colon + 1;
while (*buf == ' ' || *buf == '\t')
buf++;
value = buf;
SEARCH_NEWLINE:
while (*buf != 0 && *buf != '\r' && *buf != '\n')
buf++;
if (buf+1 >= buf_end)
;
// If "\r\n " or "\r\n\t" is next, that doesn't terminate the header.
else if (buf+2 < buf_end &&
(buf[0] == '\r' && buf[1] == '\n') &&
(buf[2] == ' ' || buf[2] == '\t'))
{
buf += 3;
goto SEARCH_NEWLINE;
}
// If "\r " or "\r\t" or "\n " or "\n\t" is next, that doesn't terminate
// the header either.
else if ((buf[0] == '\r' || buf[0] == '\n') &&
(buf[1] == ' ' || buf[1] == '\t'))
{
buf += 2;
goto SEARCH_NEWLINE;
}
if (header)
{
int L = buf - value;
if (*header)
{
char *newh = (char*) PR_Realloc ((*header),
PL_strlen(*header) + L + 10);
if (!newh) return NS_ERROR_OUT_OF_MEMORY;
*header = newh;
newh = (*header) + PL_strlen (*header);
*newh++ = ',';
*newh++ = ' ';
memcpy(newh, value, L);
newh [L] = 0;
}
else
{
*header = (char *) PR_Malloc(L+1);
if (!*header) return NS_ERROR_OUT_OF_MEMORY;
memcpy((*header), value, L);
(*header)[L] = 0;
}
}
else if (do_flags_p)
{
int i;
char *s = value;
PR_ASSERT(*s != ' ' && *s != '\t');
m_flags = 0;
for (i=0 ; i<4 ; i++) {
m_flags = (m_flags << 4) | UNHEX(*s);
s++;
}
}
if (*buf == '\r' || *buf == '\n')
{
if (*buf == '\r' && buf[1] == '\n')
buf++;
buf++;
}
if (prune_p)
{
char *to = header_start;
char *from = buf;
while (from < buf_end)
*to++ = *from++;
buf = header_start;
buf_end = to;
m_headersFP = buf_end - m_headers;
}
}
m_headers[m_headersFP++] = '\r';
m_headers[m_headersFP++] = '\n';
// Now we have parsed out all of the headers we need and we
// can proceed.
return NS_OK;
}
int
DoGrowBuffer(PRInt32 desired_size, PRInt32 element_size, PRInt32 quantum,
char **buffer, PRInt32 *size)
{
if (*size <= desired_size)
{
char *new_buf;
PRInt32 increment = desired_size - *size;
if (increment < quantum) // always grow by a minimum of N bytes
increment = quantum;
new_buf = (*buffer
? (char *) PR_Realloc (*buffer, (*size + increment)
* (element_size / sizeof(char)))
: (char *) PR_Malloc ((*size + increment)
* (element_size / sizeof(char))));
if (! new_buf)
return NS_ERROR_OUT_OF_MEMORY;
*buffer = new_buf;
*size += increment;
}
return 0;
}
#define do_grow_headers(desired_size) \
(((desired_size) >= m_headersSize) ? \
DoGrowBuffer ((desired_size), sizeof(char), 1024, \
&m_headers, &m_headersSize) \
: 0)
nsresult
nsMsgSendLater::DeliverQueuedLine(char *line, PRInt32 length)
{
PRInt32 flength = length;
m_bytesRead += length;
// convert existing newline to CRLF
// Don't need this because the calling routine is taking care of it.
// if (length > 0 && (line[length-1] == '\r' ||
// (line[length-1] == '\n' && (length < 2 || line[length-2] != '\r'))))
// {
// line[length-1] = '\r';
// line[length++] = '\n';
// }
//
//
// We are going to check if we are looking at a "From - " line. If so,
// then just eat it and return NS_OK
//
if (!PL_strncasecmp(line, "From - ", 7))
return NS_OK;
if (m_inhead)
{
if (m_headersPosition == 0)
{
// This line is the first line in a header block.
// Remember its position.
m_headersPosition = m_position;
// Also, since we're now processing the headers, clear out the
// slots which we will parse data into, so that the values that
// were used the last time around do not persist.
// We must do that here, and not in the previous clause of this
// `else' (the "I've just seen a `From ' line clause") because
// that clause happens before delivery of the previous message is
// complete, whereas this clause happens after the previous msg
// has been delivered. If we did this up there, then only the
// last message in the folder would ever be able to be both
// mailed and posted (or fcc'ed.)
PR_FREEIF(m_to);
PR_FREEIF(m_bcc);
PR_FREEIF(m_newsgroups);
PR_FREEIF(m_newshost);
PR_FREEIF(m_fcc);
PR_FREEIF(mIdentityKey);
}
if (line[0] == '\r' || line[0] == '\n' || line[0] == 0)
{
// End of headers. Now parse them; open the temp file;
// and write the appropriate subset of the headers out.
m_inhead = PR_FALSE;
nsresult rv = NS_NewLocalFileOutputStream(getter_AddRefs(mOutFile), mTempFile, -1, 00600);
if (NS_FAILED(rv))
return NS_MSG_ERROR_WRITING_FILE;
nsresult status = BuildHeaders();
if (NS_FAILED(status))
return status;
PRUint32 n;
rv = mOutFile->Write(m_headers, m_headersFP, &n);
if (NS_FAILED(rv) || n != m_headersFP)
return NS_MSG_ERROR_WRITING_FILE;
}
else
{
// Otherwise, this line belongs to a header. So append it to the
// header data.
if (!PL_strncasecmp (line, HEADER_X_MOZILLA_STATUS, PL_strlen(HEADER_X_MOZILLA_STATUS)))
// Notice the position of the flags.
m_flagsPosition = m_position;
else if (m_headersFP == 0)
m_flagsPosition = 0;
nsresult status = do_grow_headers (length + m_headersFP + 10);
if (NS_FAILED(status))
return status;
memcpy(m_headers + m_headersFP, line, length);
m_headersFP += length;
}
}
else
{
// This is a body line. Write it to the file.
PR_ASSERT(mOutFile);
if (mOutFile)
{
PRUint32 wrote;
nsresult rv = mOutFile->Write(line, length, &wrote);
if (NS_FAILED(rv) || wrote < (PRUint32) length)
return NS_MSG_ERROR_WRITING_FILE;
}
}
m_position += flength;
return NS_OK;
}
NS_IMETHODIMP
nsMsgSendLater::SetMsgWindow(nsIMsgWindow *aMsgWindow)
{
m_window = aMsgWindow;
return NS_OK;
}
NS_IMETHODIMP
nsMsgSendLater::GetMsgWindow(nsIMsgWindow **aMsgWindow)
{
NS_ENSURE_ARG(aMsgWindow);
*aMsgWindow = m_window;
NS_IF_ADDREF(*aMsgWindow);
return NS_OK;
}
NS_IMETHODIMP
nsMsgSendLater::AddListener(nsIMsgSendLaterListener *aListener)
{
if ( (mListenerArrayCount > 0) || mListenerArray )
{
++mListenerArrayCount;
mListenerArray = (nsIMsgSendLaterListener **)
PR_Realloc(*mListenerArray, sizeof(nsIMsgSendLaterListener *) * mListenerArrayCount);
if (!mListenerArray)
return NS_ERROR_OUT_OF_MEMORY;
else
{
mListenerArray[mListenerArrayCount - 1] = aListener;
return NS_OK;
}
}
else
{
mListenerArrayCount = 1;
mListenerArray = (nsIMsgSendLaterListener **) PR_Malloc(sizeof(nsIMsgSendLaterListener *) * mListenerArrayCount);
if (!mListenerArray)
return NS_ERROR_OUT_OF_MEMORY;
memset(mListenerArray, 0, (sizeof(nsIMsgSendLaterListener *) * mListenerArrayCount));
mListenerArray[0] = aListener;
NS_ADDREF(mListenerArray[0]);
return NS_OK;
}
}
NS_IMETHODIMP
nsMsgSendLater::RemoveListener(nsIMsgSendLaterListener *aListener)
{
PRInt32 i;
for (i=0; i<mListenerArrayCount; i++)
if (mListenerArray[i] == aListener)
{
NS_RELEASE(mListenerArray[i]);
mListenerArray[i] = nsnull;
return NS_OK;
}
return NS_ERROR_INVALID_ARG;
}
nsresult
nsMsgSendLater::NotifyListenersOnStartSending(PRUint32 aTotalMessageCount)
{
PRInt32 i;
for (i=0; i<mListenerArrayCount; i++)
if (mListenerArray[i] != nsnull)
mListenerArray[i]->OnStartSending(aTotalMessageCount);
return NS_OK;
}
nsresult
nsMsgSendLater::NotifyListenersOnProgress(PRUint32 aCurrentMessage, PRUint32 aTotalMessage)
{
PRInt32 i;
for (i=0; i<mListenerArrayCount; i++)
if (mListenerArray[i] != nsnull)
mListenerArray[i]->OnProgress(aCurrentMessage, aTotalMessage);
return NS_OK;
}
nsresult
nsMsgSendLater::NotifyListenersOnStatus(const PRUnichar *aMsg)
{
PRInt32 i;
for (i=0; i<mListenerArrayCount; i++)
if (mListenerArray[i] != nsnull)
mListenerArray[i]->OnStatus(aMsg);
return NS_OK;
}
nsresult
nsMsgSendLater::NotifyListenersOnStopSending(nsresult aStatus, const PRUnichar *aMsg,
PRUint32 aTotalTried, PRUint32 aSuccessful)
{
PRInt32 i;
for (i=0; i<mListenerArrayCount; i++)
if (mListenerArray[i] != nsnull)
mListenerArray[i]->OnStopSending(aStatus, aMsg, aTotalTried, aSuccessful);
return NS_OK;
}
// XXX todo
// maybe this should just live in the account manager?
nsresult
nsMsgSendLater::GetIdentityFromKey(const char *aKey, nsIMsgIdentity **aIdentity)
{
NS_ENSURE_ARG_POINTER(aIdentity);
nsresult rv;
nsCOMPtr<nsIMsgAccountManager> accountManager =
do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv,rv);
if (aKey)
{
nsCOMPtr<nsISupportsArray> identities;
if (NS_SUCCEEDED(accountManager->GetAllIdentities(getter_AddRefs(identities))))
{
nsCOMPtr<nsIMsgIdentity> lookupIdentity;
PRUint32 count = 0;
identities->Count(&count);
for (PRUint32 i = 0; i < count; i++)
{
rv = identities->QueryElementAt(i, NS_GET_IID(nsIMsgIdentity),
getter_AddRefs(lookupIdentity));
if (NS_FAILED(rv))
continue;
nsCString key;
lookupIdentity->GetKey(key);
if (key.Equals(aKey))
{
NS_IF_ADDREF(*aIdentity = lookupIdentity);
return NS_OK;
}
}
}
}
// if no aKey, or we failed to find the identity from the key
// use the identity from the default account.
nsCOMPtr<nsIMsgAccount> defaultAccount;
rv = accountManager->GetDefaultAccount(getter_AddRefs(defaultAccount));
NS_ENSURE_SUCCESS(rv,rv);
rv = defaultAccount->GetDefaultIdentity(aIdentity);
NS_ENSURE_SUCCESS(rv,rv);
return rv;
}