diff --git a/mozilla/mailnews/base/util/nsMsgProtocol.cpp b/mozilla/mailnews/base/util/nsMsgProtocol.cpp index eba047319a8..39a693efb52 100644 --- a/mozilla/mailnews/base/util/nsMsgProtocol.cpp +++ b/mozilla/mailnews/base/util/nsMsgProtocol.cpp @@ -35,15 +35,23 @@ #include "nsIDNSService.h" #include "nsIMsgWindow.h" #include "nsIMsgStatusFeedback.h" +#include "nsIPipe.h" #include "nsIPrompt.h" static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID); static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID); -NS_IMPL_ISUPPORTS4(nsMsgProtocol, - nsIStreamListener, - nsIStreamObserver, - nsIChannel, - nsIRequest) +static NS_DEFINE_CID(kFileTransportServiceCID, NS_FILETRANSPORTSERVICE_CID); + +NS_IMPL_THREADSAFE_ADDREF(nsMsgProtocol) +NS_IMPL_THREADSAFE_RELEASE(nsMsgProtocol) + +NS_INTERFACE_MAP_BEGIN(nsMsgProtocol) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel) + NS_INTERFACE_MAP_ENTRY(nsIStreamListener) + NS_INTERFACE_MAP_ENTRY(nsIStreamObserver) + NS_INTERFACE_MAP_ENTRY(nsIChannel) + NS_INTERFACE_MAP_ENTRY(nsIRequest) +NS_INTERFACE_MAP_END_THREADSAFE nsMsgProtocol::nsMsgProtocol(nsIURI * aURL) { @@ -138,27 +146,25 @@ nsresult nsMsgProtocol::OpenFileSocket(nsIURI * aURL, const nsFileSpec * aFileSp char * urlSpec = PR_smprintf("file://%s", (const char *) filePath); // dougt - there should be an easier way! - nsCOMPtr aIURI; - if (NS_FAILED(rv = NS_NewURI(getter_AddRefs(aIURI), urlSpec))) - return(PR_FALSE); - if (!aIURI) return(PR_FALSE); + nsCOMPtr uri; + if (NS_FAILED(rv = NS_NewURI(getter_AddRefs(uri), urlSpec))) + return rv; - nsCOMPtr fileURL = do_QueryInterface(aIURI); - if (!fileURL) return(PR_FALSE); + nsCOMPtr fileURL = do_QueryInterface(uri); + if (!fileURL) return NS_ERROR_FAILURE; - nsCOMPtr file; - rv = fileURL->GetFile(getter_AddRefs(file)); - if (NS_FAILED(rv)) return(PR_FALSE); + nsCOMPtr file; + rv = fileURL->GetFile(getter_AddRefs(file)); + if (NS_FAILED(rv)) return rv; // dougt - NS_DEFINE_CID(kFileTransportServiceCID, NS_FILETRANSPORTSERVICE_CID); - NS_WITH_SERVICE(nsIFileTransportService, fts, kFileTransportServiceCID, &rv); - if (NS_FAILED(rv)) return PR_FALSE; + NS_WITH_SERVICE(nsIFileTransportService, fts, kFileTransportServiceCID, &rv); + if (NS_FAILED(rv)) return rv; - rv = fts->CreateTransport(file, PR_RDWR | PR_CREATE_FILE, - 0664, getter_AddRefs(m_transport)); + rv = fts->CreateTransport(file, PR_RDWR | PR_CREATE_FILE, + 0664, getter_AddRefs(m_transport)); PR_FREEIF(urlSpec); - m_socketIsOpen = PR_FALSE; + m_socketIsOpen = PR_FALSE; } return rv; @@ -240,7 +246,7 @@ NS_IMETHODIMP nsMsgProtocol::OnStartRequest(nsIRequest *request, nsISupports *ct m_loadGroup->AddRequest(NS_STATIC_CAST(nsIRequest *, this), nsnull /* context isupports */); } - // if we are set up as a channel, we should notify our channel listener that we are starting... + // if we are set up as a channel, we should notify our channel listener that we are starting... // so pass in ourself as the channel and not the underlying socket or file channel the protocol // happens to be using if (!mSuppressListenerNotifications && m_channelListener) @@ -285,20 +291,20 @@ NS_IMETHODIMP nsMsgProtocol::OnStopRequest(nsIRequest *request, nsISupports *ctx switch (aStatus) { case NS_ERROR_UNKNOWN_HOST: - // todo, put this into a string bundle - alertMsg.AssignWithConversion("Failed to connect to the server."); - break; - case NS_ERROR_CONNECTION_REFUSED: - // todo, put this into a string bundle - alertMsg.AssignWithConversion("Connection refused to the server."); - break; - case NS_ERROR_NET_TIMEOUT: - // todo, put this into a string bundle - alertMsg.AssignWithConversion("Connection to the server timed out."); - break; - default: - alertMsg.AppendInt(aStatus, 16); - break; + // todo, put this into a string bundle + alertMsg.AssignWithConversion("Failed to connect to the server."); + break; + case NS_ERROR_CONNECTION_REFUSED: + // todo, put this into a string bundle + alertMsg.AssignWithConversion("Connection refused to the server."); + break; + case NS_ERROR_NET_TIMEOUT: + // todo, put this into a string bundle + alertMsg.AssignWithConversion("Connection to the server timed out."); + break; + default: + alertMsg.AppendInt(aStatus, 16); + break; } rv = msgPrompt->Alert(nsnull, alertMsg.GetUnicode()); @@ -406,7 +412,7 @@ NS_IMETHODIMP nsMsgProtocol::SetURI(nsIURI* aURI) NS_IMETHODIMP nsMsgProtocol::Open(nsIInputStream **_retval) { - NS_NOTREACHED("Open"); + NS_NOTREACHED("Open"); return NS_ERROR_NOT_IMPLEMENTED; } @@ -477,23 +483,22 @@ NS_IMETHODIMP nsMsgProtocol::GetContentLength(PRInt32 * aContentLength) NS_IMETHODIMP nsMsgProtocol::GetSecurityInfo(nsISupports * *aSecurityInfo) { - *aSecurityInfo = nsnull; - return NS_ERROR_NOT_IMPLEMENTED; + *aSecurityInfo = nsnull; + return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsMsgProtocol::GetName(PRUnichar * *aName) { - return NS_ERROR_NOT_IMPLEMENTED; + return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsMsgProtocol::SetContentLength(PRInt32 aContentLength) { - NS_NOTREACHED("SetContentLength"); - return NS_ERROR_NOT_IMPLEMENTED; + return NS_ERROR_NOT_IMPLEMENTED; } - + NS_IMETHODIMP nsMsgProtocol::GetOwner(nsISupports * *aPrincipal) { *aPrincipal = mOwner; @@ -509,8 +514,8 @@ NS_IMETHODIMP nsMsgProtocol::SetOwner(nsISupports * aPrincipal) NS_IMETHODIMP nsMsgProtocol::GetLoadGroup(nsILoadGroup * *aLoadGroup) { - *aLoadGroup = m_loadGroup; - NS_IF_ADDREF(*aLoadGroup); + *aLoadGroup = m_loadGroup; + NS_IF_ADDREF(*aLoadGroup); return NS_OK; } @@ -562,23 +567,22 @@ NS_IMETHODIMP nsMsgProtocol::GetStatus(nsresult *status) NS_IMETHODIMP nsMsgProtocol::Cancel(nsresult status) { NS_ASSERTION(m_request,"no channel"); - if (!m_request) { + if (!m_request) return NS_ERROR_FAILURE; - } return m_request->Cancel(status); } NS_IMETHODIMP nsMsgProtocol::Suspend() { - NS_NOTREACHED("Suspend"); - return NS_ERROR_NOT_IMPLEMENTED; + NS_NOTREACHED("Suspend"); + return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsMsgProtocol::Resume() { - NS_NOTREACHED("Resume"); - return NS_ERROR_NOT_IMPLEMENTED; + NS_NOTREACHED("Resume"); + return NS_ERROR_NOT_IMPLEMENTED; } nsresult nsMsgProtocol::PostMessage(nsIURI* url, nsIFileSpec *fileSpec) @@ -690,3 +694,413 @@ nsresult nsMsgProtocol::PostMessage(nsIURI* url, nsIFileSpec *fileSpec) } return NS_OK; } + +///////////////////////////////////////////////////////////////////// +// nsMsgAsyncWriteProtocol subclass and related helper classes +///////////////////////////////////////////////////////////////////// + +class nsMsgProtocolStreamProvider : public nsIStreamProvider +{ +public: + NS_DECL_ISUPPORTS + + nsMsgProtocolStreamProvider() { NS_INIT_REFCNT(); } + virtual ~nsMsgProtocolStreamProvider() {} + + void Init(nsMsgAsyncWriteProtocol *aProtInstance, nsIInputStream *aInputStream) { mMsgProtocol = aProtInstance; mInStream = aInputStream;} + + // + // nsIStreamObserver implementation ... + // + NS_IMETHODIMP OnStartRequest(nsIRequest *chan, nsISupports *ctxt) { return NS_OK; } + NS_IMETHODIMP OnStopRequest(nsIRequest *chan, nsISupports *ctxt, nsresult status, const PRUnichar *statusText) { return NS_OK; } + + // + // nsIStreamProvider implementation ... + // + NS_IMETHODIMP OnDataWritable(nsIRequest *aChannel, nsISupports *aContext, + nsIOutputStream *aOutStream, + PRUint32 aOffset, PRUint32 aCount) + { + NS_ASSERTION(mInStream, "not initialized"); + + nsresult rv; + PRUint32 avail; + + // Write whatever is available in the pipe. If the pipe is empty, then + // return NS_BASE_STREAM_WOULD_BLOCK; we will resume the write when there + // is more data. + + rv = mInStream->Available(&avail); + if (NS_FAILED(rv)) return rv; + + if (avail == 0) + { + mMsgProtocol->mSuspendedWrite = PR_TRUE; + return NS_BASE_STREAM_WOULD_BLOCK; + } + + PRUint32 bytesWritten; + rv = aOutStream->WriteFrom(mInStream, PR_MIN(avail, aCount), &bytesWritten); + // if were full at the time, the input stream may be backed up and we need to read any remains from the last ODA call + // before we'll get more ODA calls + if (mMsgProtocol->mSuspendedRead) + mMsgProtocol->UnblockPostReader(); + return rv; + } + + +protected: + nsMsgAsyncWriteProtocol * mMsgProtocol; + nsCOMPtr mInStream; +}; + +NS_IMPL_THREADSAFE_ISUPPORTS2(nsMsgProtocolStreamProvider, + nsIStreamProvider, + nsIStreamObserver) + +class nsMsgFilePostHelper : public nsIStreamListener +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSISTREAMOBSERVER + NS_DECL_NSISTREAMLISTENER + + nsMsgFilePostHelper() { NS_INIT_REFCNT(); mSuspendedPostFileRead = PR_FALSE;} + nsresult Init(nsIOutputStream * aOutStream, nsMsgAsyncWriteProtocol * aProtInstance, nsIFile *aFileToPost); + virtual ~nsMsgFilePostHelper() {} + nsCOMPtr mPostFileRequest; + PRBool mSuspendedPostFileRead; +protected: + nsCOMPtr mOutStream; + nsMsgAsyncWriteProtocol * mProtInstance; +}; + +NS_IMPL_THREADSAFE_ADDREF(nsMsgFilePostHelper) +NS_IMPL_THREADSAFE_RELEASE(nsMsgFilePostHelper) + +NS_INTERFACE_MAP_BEGIN(nsMsgFilePostHelper) + NS_INTERFACE_MAP_ENTRY(nsIStreamListener) + NS_INTERFACE_MAP_ENTRY(nsIStreamObserver) +NS_INTERFACE_MAP_END_THREADSAFE + +nsresult nsMsgFilePostHelper::Init(nsIOutputStream * aOutStream, nsMsgAsyncWriteProtocol * aProtInstance, nsIFile *aFileToPost) +{ + nsresult rv = NS_OK; + mOutStream = aOutStream; + mProtInstance = aProtInstance; // mscott work out ref counting issue + + + NS_WITH_SERVICE(nsIFileTransportService, fts, kFileTransportServiceCID, &rv); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr transport; + rv = fts->CreateTransport(aFileToPost, PR_RDONLY, 0664, getter_AddRefs(transport)); + if (transport) + { + rv = transport->AsyncRead(this, nsnull, 0, -1, 0, getter_AddRefs(mPostFileRequest)); + } + return rv; +} + +NS_IMETHODIMP nsMsgFilePostHelper::OnStartRequest(nsIRequest * aChannel, nsISupports *ctxt) +{ + return NS_OK; +} + +NS_IMETHODIMP nsMsgFilePostHelper::OnStopRequest(nsIRequest * aChannel, nsISupports *ctxt, nsresult aStatus, const PRUnichar* aMsg) +{ + if (!mSuspendedPostFileRead) + mProtInstance->PostDataFinished(); + + mSuspendedPostFileRead = PR_FALSE; + mProtInstance->mFilePostHelper = nsnull; + return NS_OK; +} + +NS_IMETHODIMP nsMsgFilePostHelper::OnDataAvailable(nsIRequest * /* aChannel */, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count) +{ + if (mSuspendedPostFileRead) + { + mProtInstance->UpdateSuspendedReadBytes(count, mProtInstance->mInsertPeriodRequired); + return NS_OK; + } + + mProtInstance->ProcessIncomingPostData(inStr, count); + + if (mProtInstance->mSuspendedWrite) + { + // if we got here then we had suspended the write 'cause we didn't have anymore + // data to write (i.e. the pipe went empty). So resume the channel to kick + // things off again. + mProtInstance->mSuspendedWrite = PR_FALSE; + mProtInstance->m_WriteRequest->Resume(); + } + + return NS_OK; +} + +NS_IMPL_ADDREF_INHERITED(nsMsgAsyncWriteProtocol, nsMsgProtocol) +NS_IMPL_RELEASE_INHERITED(nsMsgAsyncWriteProtocol, nsMsgProtocol) + +NS_INTERFACE_MAP_BEGIN(nsMsgAsyncWriteProtocol) +NS_INTERFACE_MAP_END_INHERITING(nsMsgProtocol) + +nsMsgAsyncWriteProtocol::nsMsgAsyncWriteProtocol(nsIURI * aURL) : nsMsgProtocol(aURL) +{ + mSuspendedWrite = PR_FALSE; + mSuspendedReadBytes = 0; + mSuspendedRead = PR_FALSE; + mInsertPeriodRequired = PR_FALSE; + mSuspendedReadBytesPostPeriod = 0; + mFilePostHelper = nsnull; +} + +nsMsgAsyncWriteProtocol::~nsMsgAsyncWriteProtocol() +{} + +nsresult nsMsgAsyncWriteProtocol::PostMessage(nsIURI* url, nsIFileSpec *fileSpec) +{ + // convert the file spec into a nsIFile.... + nsFileSpec * spec = new nsFileSpec(); + fileSpec->GetFileSpec(spec); + + nsCOMPtr file; + NS_FileSpecToIFile(spec, getter_AddRefs(file)); + + nsCOMPtr listener; + NS_NEWXPCOM(listener, nsMsgFilePostHelper); + if (!listener) return NS_ERROR_OUT_OF_MEMORY; + + // be sure to initialize some state before posting + mSuspendedReadBytes = 0; + mSuspendedRead = PR_FALSE; + mInsertPeriodRequired = PR_FALSE; + mSuspendedReadBytesPostPeriod = 0; + + mFilePostHelper = NS_STATIC_CAST(nsMsgFilePostHelper*,NS_STATIC_CAST(nsIStreamListener*, listener)); + + NS_STATIC_CAST(nsMsgFilePostHelper*,NS_STATIC_CAST(nsIStreamListener*, listener))->Init(m_outputStream, this, file); + + return NS_OK; +} + +nsresult nsMsgAsyncWriteProtocol::SuspendPostFileRead() +{ +#ifdef DEBUG_mscott + printf("suspending post read during send\n"); +#endif + if (mFilePostHelper) + { + // uhoh we need to pause reading in the file until we get unblocked... + mFilePostHelper->mPostFileRequest->Suspend(); + mFilePostHelper->mSuspendedPostFileRead = PR_TRUE; + } + + return NS_OK; +} + +nsresult nsMsgAsyncWriteProtocol::ResumePostFileRead() +{ +#ifdef DEBUG_mscott + printf("resuming post read during send\n"); +#endif + + if (mFilePostHelper) + { + if (mFilePostHelper->mSuspendedPostFileRead) + { + mFilePostHelper->mPostFileRequest->Resume(); + mFilePostHelper->mSuspendedPostFileRead = PR_FALSE; + } + } + else // we must be done with the download so send the '.' + { + PostDataFinished(); + } + + return NS_OK; +} + +nsresult nsMsgAsyncWriteProtocol::UpdateSuspendedReadBytes(PRUint32 aNewBytes, PRBool aAddToPostPeriodByteCount) +{ + // depending on our current state, we'll either add aNewBytes to mSuspendedReadBytes + // or mSuspendedReadBytesAfterPeriod. + + mSuspendedRead = PR_TRUE; + if (aAddToPostPeriodByteCount) + mSuspendedReadBytesPostPeriod += aNewBytes; + else + mSuspendedReadBytes += aNewBytes; + + return NS_OK; +} + +nsresult nsMsgAsyncWriteProtocol::PostDataFinished() +{ + SendData(nsnull, CRLF "." CRLF); + mPostDataStream = nsnull; + return NS_OK; +} + +nsresult nsMsgAsyncWriteProtocol::ProcessIncomingPostData(nsIInputStream *inStr, PRUint32 count) +{ + // We need to quote any '.' that occur at the beginning of a line. + // but I don't want to waste time reading out the data into a buffer and searching + // let's try to leverage nsIBufferedInputStream and see if we can "peek" into the + // current contents for this particular case. + + nsCOMPtr bufferInputStr = do_QueryInterface(inStr); + NS_ASSERTION(bufferInputStr, "i made a wrong assumption about the type of stream we are getting"); + NS_ASSERTION(mSuspendedReadBytes == 0, "oops, I missed something"); + + if (!mPostDataStream) mPostDataStream = inStr; + + if (bufferInputStr) + { + PRUint32 amountWritten; + + while (count > 0) + { + PRBool found = PR_FALSE; + PRUint32 offset = 0; + bufferInputStr->Search("\012.", PR_TRUE, &found, &offset); // LF. + + if (!found || offset > count) + { + // push this data into the output stream + m_outputStream->WriteFrom(inStr, count, &amountWritten); + // store any remains which need read out at a later date + if (count > amountWritten) // stream will block + { + UpdateSuspendedReadBytes(count - amountWritten, PR_FALSE); + SuspendPostFileRead(); + } + break; + } + else + { + // count points to the LF in a LF followed by a '.' + // go ahead and write up to offset.. + m_outputStream->WriteFrom(inStr, offset + 1, &amountWritten); + count -= amountWritten; + if (offset+1 > amountWritten) + { + UpdateSuspendedReadBytes(offset+1 - amountWritten, PR_FALSE); + mInsertPeriodRequired = PR_TRUE; + UpdateSuspendedReadBytes(count, mInsertPeriodRequired); + SuspendPostFileRead(); + break; + } + + // write out the extra '.' + m_outputStream->Write(".", 1, &amountWritten); + if (amountWritten != 1) + { + mInsertPeriodRequired = PR_TRUE; + // once we do write out the '.', if we are now blocked we need to remember the remaining count that comes + // after the '.' so we can perform processing on that once we become unblocked. + UpdateSuspendedReadBytes(count, mInsertPeriodRequired); + SuspendPostFileRead(); + } + + } + } // while count > 0 + } + + return NS_OK; +} +nsresult nsMsgAsyncWriteProtocol::UnblockPostReader() +{ + PRUint32 amountWritten = 0; + if (mSuspendedRead) + { + // (1) attempt to write out any remaining read bytes we need in order to unblock the reader + if (mSuspendedReadBytes > 0 && mPostDataStream) + { + m_outputStream->WriteFrom(mPostDataStream, mSuspendedReadBytes, &amountWritten); + if (mSuspendedReadBytes > amountWritten) + mSuspendedReadBytes -= amountWritten; + else + mSuspendedReadBytes = 0; + } + + // (2) if we are now unblocked, and we need to insert a '.' then do so now... + if (mInsertPeriodRequired && mSuspendedReadBytes == 0) + { + amountWritten = 0; + m_outputStream->Write(".", 1, &amountWritten); + if (amountWritten == 1) // if we succeeded then clear pending '.' flag + mInsertPeriodRequired = PR_FALSE; + } + + // (3) if we inserted a '.' and we still have bytes after the '.' which need processed before the stream is unblocked + // then fake an ODA call to handle this now... + if (!mInsertPeriodRequired && mSuspendedReadBytesPostPeriod > 0) + { + // these bytes actually need processed for extra '.''s..... + PRUint32 postbytes = mSuspendedReadBytesPostPeriod; + mSuspendedReadBytesPostPeriod = 0; + ProcessIncomingPostData(mPostDataStream, postbytes); + } + + // (4) determine if we are out of the suspended read state... + if (mSuspendedReadBytes == 0 && !mInsertPeriodRequired && mSuspendedReadBytesPostPeriod == 0) + { + mSuspendedRead = PR_FALSE; + ResumePostFileRead(); + } + + } // if we are in the suspended read state + + return NS_OK; +} + +nsresult nsMsgAsyncWriteProtocol::SetupTransportState() +{ + nsresult rv = NS_OK; + + if (!m_outputStream && m_transport) + { + // first create a pipe which we'll use to write the data we want to send + // into. + rv = NS_NewPipe(getter_AddRefs(mInStream), getter_AddRefs(m_outputStream), + 1024, // segmentSize + 1024*8, // maxSize + PR_TRUE, + PR_TRUE); + + nsCOMPtr provider; + NS_NEWXPCOM(provider, nsMsgProtocolStreamProvider); + if (!provider) return NS_ERROR_OUT_OF_MEMORY; + + NS_STATIC_CAST(nsMsgProtocolStreamProvider*, + NS_STATIC_CAST(nsIStreamProvider*, provider))->Init(this, mInStream); + + rv = m_transport->AsyncWrite(provider, nsnull, 0, 0, 0, getter_AddRefs(m_WriteRequest)); + if (NS_FAILED(rv)) return rv; + } // if m_transport + + return rv; +} + +PRInt32 nsMsgAsyncWriteProtocol::SendData(nsIURI * aURL, const char * dataBuffer, PRBool aSuppressLogging) +{ + PRUint32 len = nsCRT::strlen(dataBuffer); + PRUint32 cnt; + nsresult rv = m_outputStream->Write(dataBuffer, len, &cnt); + if (NS_SUCCEEDED(rv) && len==cnt) + { + if (mSuspendedWrite) + { + // if we got here then we had suspended the write 'cause we didn't have anymore + // data to write (i.e. the pipe went empty). So resume the channel to kick + // things off again. + mSuspendedWrite = PR_FALSE; + m_WriteRequest->Resume(); + } + return NS_OK; + } + else + return NS_ERROR_FAILURE; +} diff --git a/mozilla/mailnews/base/util/nsMsgProtocol.h b/mozilla/mailnews/base/util/nsMsgProtocol.h index 8a1ccb616df..c9278469743 100644 --- a/mozilla/mailnews/base/util/nsMsgProtocol.h +++ b/mozilla/mailnews/base/util/nsMsgProtocol.h @@ -32,10 +32,12 @@ #include "nsCOMPtr.h" #include "nsIFileSpec.h" #include "nsIInterfaceRequestor.h" +#include "nsIStreamProvider.h" #include "nsIProgressEventSink.h" #include "nsITransport.h" class nsIPrompt; class nsIMsgMailNewsUrl; +class nsMsgFilePostHelper; // This is a helper class used to encapsulate code shared between all of the // mailnews protocol objects (imap, news, pop, smtp, etc.) In particular, @@ -112,12 +114,13 @@ protected: virtual nsresult InitFromURI(nsIURI *aUrl); + // Ouput stream for writing commands to the socket + nsCOMPtr m_outputStream; // this will be obtained from the transport interface + // Ouput stream for writing commands to the socket nsCOMPtr m_transport; nsCOMPtr m_request; - nsCOMPtr m_outputStream; // this will be obtained from the transport interface - PRBool m_socketIsOpen; // mscott: we should look into keeping this state in the nsSocketTransport... // I'm using it to make sure I open the socket the first time a URL is loaded into the connection PRUint32 m_flags; // used to store flag information @@ -147,4 +150,58 @@ protected: PRBool mSuppressListenerNotifications; }; + +// This is is a subclass of nsMsgProtocol extends the parent class with AsyncWrite support. Protocols like smtp +// and news want to leverage aysnc write. We don't want everyone who inherits from nsMsgProtocol to have to +// pick up the extra overhead. +class NS_MSG_BASE nsMsgAsyncWriteProtocol : public nsMsgProtocol +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + nsMsgAsyncWriteProtocol(nsIURI * aURL); + virtual ~nsMsgAsyncWriteProtocol(); + + // temporary over ride... + virtual nsresult PostMessage(nsIURI* url, nsIFileSpec *fileSpec); + + // over ride the following methods from the base class + virtual nsresult SetupTransportState(); + virtual PRInt32 SendData(nsIURI * aURL, const char * dataBuffer, PRBool aSuppressLogging = PR_FALSE); + + // if we suspended the asynch write while waiting for more data to write then this will be TRUE + PRBool mSuspendedWrite; + nsCOMPtr m_WriteRequest; + + // because we are reading the post data in asychronously, it's possible that we aren't sending it + // out fast enough and the reading gets blocked. The following set of state variables are used to + // track this. + PRBool mSuspendedRead; + PRBool mInsertPeriodRequired; // do we need to insert a '.' as part of the unblocking process + + nsresult ProcessIncomingPostData(nsIInputStream *inStr, PRUint32 count); + nsresult UnblockPostReader(); + nsresult UpdateSuspendedReadBytes(PRUint32 aNewBytes, PRBool aAddToPostPeriodByteCount); + nsresult PostDataFinished(); // this is so we'll send out a closing '.' and release any state related to the post + + + // these two routines are used to pause and resume our loading of the file containing the contents + // we are trying to post. We call these routines when we aren't sending the bits out fast enough + // to keep up with the file read. + nsresult SuspendPostFileRead(); + nsresult ResumePostFileRead(); + nsresult UpdateSuspendedReadBytes(PRUint32 aNewBytes); + nsMsgFilePostHelper * mFilePostHelper; // needs to be a weak reference +protected: + // the streams for the pipe used to queue up data for the async write calls to the server. + // we actually re-use the same mOutStream variable in our parent class for the output + // stream to the socket channel. So no need for a new variable here. + nsCOMPtr mInStream; + nsCOMPtr mPostDataStream; + PRUint32 mSuspendedReadBytes; // remaining # of bytes we need to read before + // the input stream becomes unblocked + PRUint32 mSuspendedReadBytesPostPeriod; // # of bytes which need processed after we insert a '.' before + // the input stream becomes unblocked. +}; + #endif /* nsMsgProtocol_h__ */