Page MenuHome GnuPG

nsPipeFilterListener.cpp
No OneTemporary

nsPipeFilterListener.cpp

/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "MPL"); you may not use this file
* except in compliance with the MPL. You may obtain a copy of
* the MPL at http://www.mozilla.org/MPL/
*
* Software distributed under the MPL is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the MPL for the specific language governing
* rights and limitations under the MPL.
*
* The Original Code is protoZilla.
*
* The Initial Developer of the Original Code is Ramalingam Saravanan.
* Portions created by Ramalingam Saravanan <svn@xmlterm.org> are
* Copyright (C) 2000 Ramalingam Saravanan. All Rights Reserved.
*
* Contributor(s):
* Patrick Brunschwig <patrick@mozilla-enigmail.org>
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License (the "GPL"), in which case
* the provisions of the GPL are applicable instead of
* those above. If you wish to allow use of your version of this
* file only under the terms of the GPL and not to allow
* others to use your version of this file under the MPL, indicate
* your decision by deleting the provisions above and replace them
* with the notice and other provisions required by the GPL.
* If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
// Logging of debug output
// The following define statement should occur before any include statements
#define FORCE_PR_LOG /* Allow logging even in release build */
#include "ipc.h"
#ifndef _IPC_FORCE_INTERNAL_API
#include "nsStringAPI.h"
#else
#include "nsString.h"
#endif
#include "prlog.h"
#include "nsCOMPtr.h"
#include "nsAutoLock.h"
#include "nsIInputStream.h"
#include "nsIThread.h"
#include "nsIHttpChannel.h"
#include "nsNetUtil.h"
#include "nsMimeTypes.h"
#include "nsPipeFilterListener.h"
#ifdef PR_LOGGING
PRLogModuleInfo* gPipeFilterListenerLog = NULL;
#endif
#define ERROR_LOG(args) PR_LOG(gPipeFilterListenerLog,PR_LOG_ERROR,args)
#define WARNING_LOG(args) PR_LOG(gPipeFilterListenerLog,PR_LOG_WARNING,args)
#define DEBUG_LOG(args) PR_LOG(gPipeFilterListenerLog,PR_LOG_DEBUG,args)
#define NS_PIPE_CONSOLE_BUFFER_SIZE (1024)
static const PRUint32 kCharMax = NS_PIPE_CONSOLE_BUFFER_SIZE;
#define MK_MIME_ERROR_WRITING_FILE -1
///////////////////////////////////////////////////////////////////////////////
// nsPipeFilterListener implementation
// nsISupports implementation
NS_IMPL_THREADSAFE_ISUPPORTS4(nsPipeFilterListener,
nsIPipeFilterListener,
nsIRequestObserver,
nsIStreamListener,
nsIInputStream)
// nsPipeFilterListener implementation
nsPipeFilterListener::nsPipeFilterListener()
: mInitialized(PR_FALSE),
mRequestStarted(PR_FALSE),
mRequestEnded(PR_FALSE),
mTailRequestStarted(PR_FALSE),
mStartDelimiter(""),
mEndDelimiter(""),
mStartLine(""),
mEndLine(""),
mKeepDelimiters(PR_FALSE),
mMimeMultipart(PR_FALSE),
mAutoMimeBoundary(PR_FALSE),
mFirstMatch(PR_TRUE),
mLastMatch(PR_FALSE),
mSavePartMatch(PR_FALSE),
mOldPartMatch(""),
mPartMatch(""),
mLinebreak(0),
mStreamBuf(nsnull),
mStreamOffset(0),
mStreamLength(0),
mListener(nsnull),
mTailListener(nsnull),
mContext(nsnull)
{
NS_INIT_ISUPPORTS();
#ifdef PR_LOGGING
if (gPipeFilterListenerLog == nsnull) {
gPipeFilterListenerLog = PR_NewLogModule("nsPipeFilterListener");
}
#endif
#ifdef FORCE_PR_LOG
nsresult rv;
nsCOMPtr<nsIThread> myThread;
rv = IPC_GET_THREAD(myThread);
DEBUG_LOG(("nsPipeFilterListener:: <<<<<<<<< CTOR(%p): myThread=%p\n",
this, myThread.get()));
#endif
}
nsPipeFilterListener::~nsPipeFilterListener()
{
nsresult rv;
#ifdef FORCE_PR_LOG
nsCOMPtr<nsIThread> myThread;
rv = IPC_GET_THREAD(myThread);
DEBUG_LOG(("nsPipeFilterListener:: >>>>>>>>> DTOR(%p): myThread=%p\n",
this, myThread.get()));
#endif
// Release owning refs
mListener = nsnull;
mTailListener = nsnull;
mContext = nsnull;
}
///////////////////////////////////////////////////////////////////////////////
// nsIPipeFilterListener methods
///////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsPipeFilterListener::Init(nsIStreamListener* listener, nsISupports* ctxt,
const char *startDelimiter,
const char *endDelimiter,
PRUint32 skipCount,
PRBool keepDelimiters,
PRBool mimeMultipart,
nsIStreamListener* tailListener)
{
DEBUG_LOG(("nsPipeFilterListener::Init: (%p)\n", this));
mListener = listener;
mTailListener = tailListener;
mContext = ctxt;
mStartDelimiter = startDelimiter;
mEndDelimiter = endDelimiter;
mMimeMultipart = mimeMultipart;
if (mMimeMultipart && mStartDelimiter.IsEmpty()) {
mAutoMimeBoundary = PR_TRUE;
mStartDelimiter = "--";
mEndDelimiter = "--";
}
mStart.skipCount = mStartDelimiter.IsEmpty() ? 0 : skipCount;
mStart.matchedLine = PR_FALSE;
mStart.matchedCR = PR_FALSE;
mStart.matchOffset = 0;
mStart.matchCount = mStartDelimiter.IsEmpty() ? 1 : 0;
mEnd.skipCount = 0;
mEnd.matchedLine = PR_FALSE;
mEnd.matchedCR = PR_FALSE;
mEnd.matchOffset = 0;
mEnd.matchCount = 0;
mKeepDelimiters = keepDelimiters;
mInitialized = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
nsPipeFilterListener::TransmitData(const char* buf, PRUint32 count,
nsIStreamListener* listener,
nsIRequest* aRequest, nsISupports* aContext)
{
nsresult rv;
DEBUG_LOG(("nsPipeFilterListener::TransmitData: (%p) %d\n", this, count));
if (!listener)
return NS_OK;
mStreamBuf = buf;
mStreamOffset = 0;
mStreamLength = count;
rv = listener->OnDataAvailable(aRequest,
mContext ? mContext.get() : aContext,
(nsIInputStream*)(this),
0, count);
if (NS_FAILED(rv)) {
DEBUG_LOG(("nsPipeFilterListener::TransmitData: (%p) rv=%p\n", this, rv));
return rv;
}
Close();
return NS_OK;
}
NS_IMETHODIMP
nsPipeFilterListener::GetStartDelimiter(nsACString &aStartDelimiter)
{
aStartDelimiter = mStartDelimiter;
DEBUG_LOG(("nsPipeFilterListener::GetStartDelimiter: %s\n", mStartDelimiter.get()));
return NS_OK;
}
NS_IMETHODIMP
nsPipeFilterListener::GetEndDelimiter(nsACString &aEndDelimiter)
{
aEndDelimiter = mEndDelimiter;
DEBUG_LOG(("nsPipeFilterListener::GetEndDelimiter: %s\n", mEndDelimiter.get()));
return NS_OK;
}
NS_IMETHODIMP
nsPipeFilterListener::GetStartLine(nsACString &aStartLine)
{
aStartLine = mStartLine;
DEBUG_LOG(("nsPipeFilterListener::GetStartLine: %s\n", mStartLine.get()));
return NS_OK;
}
NS_IMETHODIMP
nsPipeFilterListener::GetEndLine(nsACString &aEndLine)
{
aEndLine = mEndLine;
DEBUG_LOG(("nsPipeFilterListener::GetEndLine: %s\n", mEndLine.get()));
return NS_OK;
}
const char* const nsPipeFilterListener::LineBreaks[] = {
"\r",
"\r\n",
"\n"
};
NS_IMETHODIMP
nsPipeFilterListener::Write(const char* buf, PRUint32 count,
nsIRequest* aRequest, nsISupports* aContext)
{
nsresult rv;
DEBUG_LOG(("nsPipeFilterListener::Write: (%p) %d\n", this, count));
if (count <= 0)
return NS_OK;
PRInt32 consumed;
if (mStart.matchCount <= mStart.skipCount) {
consumed = MatchDelimiter(buf, count, mStart, mStartDelimiter, mStartLine);
if (consumed < 0)
return NS_ERROR_FAILURE;
buf += consumed;
count -= consumed;
}
if (!mRequestStarted && (mStart.matchCount > mStart.skipCount)) {
mRequestStarted = PR_TRUE;
DEBUG_LOG(("nsPipeFilterListener::Write: RequestStarted\n", count));
if (mListener) {
rv = mListener->OnStartRequest(aRequest,
mContext ? mContext.get() : aContext);
NS_ENSURE_SUCCESS(rv, rv);
if (mKeepDelimiters && !mStartLine.IsEmpty()) {
rv = TransmitData(mStartLine.get(), mStartLine.Length(),
mListener, aRequest, aContext);
NS_ENSURE_SUCCESS(rv, rv);
}
}
}
DEBUG_LOG(("nsPipeFilterListener::Write: after start, count %d\n", count));
if (count <= 0)
return NS_OK;
if (mEndDelimiter.IsEmpty()) {
return TransmitData(buf, count, mListener, aRequest, aContext);
}
if (mEnd.matchCount > mEnd.skipCount) {
// End delimiter match complete
if (mTailListener) {
DEBUG_LOG(("nsPipeFilterListener::Write: TAIL count %d\n", count));
rv = TransmitData(buf, count, mTailListener, aRequest, aContext);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
mLastMatch = PR_TRUE;
mSavePartMatch = PR_TRUE;
PRUint32 savedPartMatchLen = mPartMatch.Length();
consumed = MatchDelimiter(buf, count, mEnd, mEndDelimiter, mEndLine);
if (consumed < 0)
return NS_ERROR_FAILURE;
if (!mSavePartMatch && savedPartMatchLen &&
(mOldPartMatch.Length() >= savedPartMatchLen)) {
rv = TransmitData(mOldPartMatch.get(), savedPartMatchLen,
mListener, aRequest, aContext);
NS_ENSURE_SUCCESS(rv, rv);
mOldPartMatch = "";
}
PRInt32 transCount = consumed - mPartMatch.Length() - mEndLine.Length();
if (transCount > 0) {
rv = TransmitData(buf, transCount, mListener, aRequest, aContext);
NS_ENSURE_SUCCESS(rv, rv);
}
if (mTailListener && (mEnd.matchCount > mEnd.skipCount)) {
// End delimiter match complete
mTailRequestStarted = PR_TRUE;
rv = mTailListener->OnStartRequest(aRequest,
mContext ? mContext.get() : aContext);
NS_ENSURE_SUCCESS(rv, rv);
buf += consumed;
count -= consumed;
if (count > 0) {
DEBUG_LOG(("nsPipeFilterListener::Write: TAIL START count %d\n", count));
rv = TransmitData(buf, count, mTailListener, aRequest, aContext);
NS_ENSURE_SUCCESS(rv, rv);
}
}
return NS_OK;
}
// Return number of bytes consumed (>= 0) or -1 on error
PRInt32
nsPipeFilterListener::MatchDelimiter(const char* buf, PRUint32 bufLen,
LineMatchStatus& delim,
nsCString& delimStr,
nsCString& delimLine)
{
//DEBUG_LOG(("nsPipeFilterListener::MatchDelimiter: bufLen=%d\n", bufLen));
PRUint32 count = bufLen;
while ((count > 0) && (delim.matchCount <= delim.skipCount)) {
if (delim.matchOffset < delimStr.Length()) {
PRUint32 consumed = MatchString(buf, count, delimStr.get(),
delimStr.Length(),
delim.matchOffset);
//DEBUG_LOG(("nsPipeFilterListener::MatchDelimiter: consumed=%d\n", consumed));
if (!consumed) {
ERROR_LOG(("nsPipeFilterListener::MatchDelimiter: consumed=%d\n", consumed));
return -1;
}
buf += consumed;
count -= consumed;
if (delim.matchOffset >= delimStr.Length()) {
DEBUG_LOG(("nsPipeFilterListener::MatchDelimiter: delimStr='%s'\n", delimStr.get()));
if (mLastMatch) {
delimLine = mPartMatch;
mPartMatch = "";
} else {
delimLine = delimStr;
}
mLinebreak = 0;
}
if (!count)
return bufLen;
if (delim.matchOffset < delimStr.Length()) {
ERROR_LOG(("nsPipeFilterListener::MatchDelimiter: count=%d, delim.matchOffset=%d, delimStr='%s'\n", count, delim.matchOffset, delimStr.get() ));
return -1;
}
}
// Match to end of line
while (count > 0) {
char ch = buf[0];
if (delim.matchedCR) {
// Already matched a CR
if (ch == '\n') {
// Consume LF following CR
delimLine.Append(ch);
buf++;
count--;
}
delim.matchedLine = PR_TRUE;
break;
}
delimLine.Append(ch);
buf++;
count--;
if (ch == '\n') {
delim.matchedLine = PR_TRUE;
break;
}
if (ch == '\r') {
delim.matchedCR = PR_TRUE;
}
}
if (delim.matchedLine) {
delim.matchCount++;
delim.matchOffset = 0;
delim.matchedCR = PR_FALSE;
delim.matchedLine = PR_FALSE;
DEBUG_LOG(("nsPipeFilterListener::MatchDelimiter: delimLine(%d)='%s'\n", delimLine.Length(), delimLine.get()));
DEBUG_LOG(("nsPipeFilterListener::MatchDelimiter: matchCount=%d\n", delim.matchCount));
if (mAutoMimeBoundary) {
// Eliminate all trailing whitespace (including linebreaks) for delimiter
mAutoMimeBoundary = PR_FALSE;
mStartDelimiter = mStartLine;
mStartDelimiter.Trim(" \t\r\n", PR_FALSE, PR_TRUE);
mEndDelimiter = mStartDelimiter;
DEBUG_LOG(("nsPipeFilterListener::MatchDelimiter: Mime Boundary='%s'\n", mStartDelimiter.get()));
}
}
}
return bufLen - count;
}
// Matches a string against characters in a buffer, taking into account
// a previous substring match up to strOffset. strOffset is updated to reflect
// the new match state, and the count of consumed bytes in buf is returned.
// mLinebreak = 1, if CR encountered previously
// = 2, if CRLF encountered previously
// = 3, if LF encountered previously,
// = 0, otherwise
// (New matches only start at the beginning of a line)
PRUint32
nsPipeFilterListener::MatchString(const char* buf, PRUint32 count,
const char* str, PRUint32 length,
PRUint32& strOffset)
{
//DEBUG_LOG(("nsPipeFilterListener::MatchString: strOffset=%d, length=%d\n", strOffset, length));
if (strOffset >= length) {
// Complete match
return 0;
}
PRUint32 consumed = count;
PRUint32 j;
char ch;
for (j=0; j < count; j++) {
ch = buf[j];
if ( (ch == str[strOffset]) &&
((strOffset > 0) || (mLinebreak > 0) || (mFirstMatch && (j == 0)) )) {
// Extend match
strOffset++;
if (mLastMatch)
mPartMatch += ch;
if (strOffset >= length) {
// Complete match
consumed = j+1;
break;
}
} else {
// Match again from beginning of string
strOffset = 0;
if ((mLinebreak == 1) && (ch == '\n')) {
mLinebreak = 2;
if (mLastMatch)
mPartMatch += '\n';
} else {
if (mLastMatch && mSavePartMatch) {
mOldPartMatch = mPartMatch;
mSavePartMatch = PR_FALSE;
}
if (ch == '\r') {
mLinebreak = 1;
if (mLastMatch)
mPartMatch = '\r';
} else if (ch == '\n') {
mLinebreak = 3;
if (mLastMatch)
mPartMatch = '\n';
} else if (mLinebreak > 0) {
mLinebreak = 0;
if (mLastMatch)
mPartMatch = "";
}
}
}
}
mFirstMatch = PR_FALSE;
return consumed;
}
///////////////////////////////////////////////////////////////////////////////
// nsIRequestObserver methods
///////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsPipeFilterListener::OnStartRequest(nsIRequest *aRequest,
nsISupports *aContext)
{
DEBUG_LOG(("nsPipeFilterListener::OnStartRequest: (%p)\n", this));
if (!mInitialized)
return NS_ERROR_NOT_INITIALIZED;
return NS_OK;
}
NS_IMETHODIMP
nsPipeFilterListener::EndRequest(nsIRequest* aRequest, nsISupports* aContext)
{
nsresult rv;
DEBUG_LOG(("nsPipeFilterListener::EndRequest:(%p)\n", this));
mRequestEnded = PR_TRUE;
if (mListener) {
if (!mRequestStarted) {
mRequestStarted = PR_TRUE;
rv = mListener->OnStartRequest(aRequest,
mContext ? mContext.get() : aContext);
NS_ENSURE_SUCCESS(rv, rv);
if (mKeepDelimiters && !mStartLine.IsEmpty()) {
rv = TransmitData(mStartLine.get(), mStartLine.Length(),
mListener, aRequest, aContext);
NS_ENSURE_SUCCESS(rv, rv);
}
}
if (!mPartMatch.IsEmpty()) {
// Transmit any partially matched line
DEBUG_LOG(("nsPipeFilterListener::EndRequest: PARTIALLY MATCHED LINE '%s'\n", mPartMatch.get()));
rv = TransmitData(mPartMatch.get(), mPartMatch.Length(),
mListener, aRequest, aContext);
NS_ENSURE_SUCCESS(rv, rv);
mPartMatch = "";
}
if (mKeepDelimiters && !mEndLine.IsEmpty()) {
rv = TransmitData(mEndLine.get(), mEndLine.Length(),
mListener, aRequest, aContext);
NS_ENSURE_SUCCESS(rv, rv);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsPipeFilterListener::OnStopRequest(nsIRequest* aRequest,
nsISupports* aContext,
nsresult aStatus)
{
nsresult rv = NS_OK;
DEBUG_LOG(("nsPipeFilterListener::OnStopRequest: (%p)\n", this));
// Ensure that OnStopRequest call chain does not break by failing softly
if (!mEndDelimiter.IsEmpty() && mEndLine.IsEmpty()) {
// Failed to match end delimiter
aStatus = NS_BINDING_ABORTED;
}
if (!mRequestEnded) {
rv = EndRequest(aRequest, aContext);
if (NS_FAILED(rv))
aStatus = NS_BINDING_ABORTED;
}
if (mTailListener) {
if (!mTailRequestStarted) {
mTailRequestStarted = PR_TRUE;
rv = mTailListener->OnStartRequest(aRequest,
mContext ? mContext.get() : aContext);
if (NS_FAILED(rv))
aStatus = NS_BINDING_ABORTED;
}
rv = mTailListener->OnStopRequest(aRequest,
mContext ? mContext.get() : aContext,
aStatus);
if (NS_FAILED(rv))
aStatus = NS_BINDING_ABORTED;
}
if (mListener) {
rv = mListener->OnStopRequest(aRequest,
mContext ? mContext.get() : aContext,
aStatus);
if (NS_FAILED(rv))
aStatus = NS_BINDING_ABORTED;
}
// Release owning refs
mListener = nsnull;
mTailListener = nsnull;
mContext = nsnull;
return (aStatus == NS_BINDING_ABORTED) ? NS_ERROR_FAILURE : NS_OK;
}
///////////////////////////////////////////////////////////////////////////////
// nsIStreamListener method
///////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsPipeFilterListener::OnDataAvailable(nsIRequest* aRequest,
nsISupports* aContext,
nsIInputStream *aInputStream,
PRUint32 aSourceOffset,
PRUint32 aLength)
{
nsresult rv = NS_OK;
DEBUG_LOG(("nsPipeFilterListener::OnDataAvailable: (%p) %d\n", this, aLength));
char buf[kCharMax];
PRUint32 readCount, readMax;
while (aLength > 0) {
readMax = (aLength < kCharMax) ? aLength : kCharMax;
rv = aInputStream->Read((char *) buf, readMax, &readCount);
if (NS_FAILED(rv)){
ERROR_LOG(("nsPipeFilterListener::OnDataAvailable: Error in reading from input stream, %x\n", rv));
return rv;
}
if (readCount == 0) {
DEBUG_LOG(("nsPipeFilterListener::OnDataAvailable: (%p) readCount=%d\n", this, readCount));
}
if (readCount <= 0)
break;
aLength -= readCount;
aSourceOffset += readCount;
rv = Write(buf, readCount, aRequest, aContext);
if (NS_FAILED(rv)) {
DEBUG_LOG(("nsPipeFilterListener::OnDataAvailable: (%p) rv=%p\n", this, rv));
return rv;
}
}
return NS_OK;
}
///////////////////////////////////////////////////////////////////////////////
// nsIInputStream methods
///////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsPipeFilterListener::Available(PRUint32* _retval)
{
if (!_retval)
return NS_ERROR_NULL_POINTER;
*_retval = (mStreamLength > mStreamOffset) ?
mStreamLength - mStreamOffset : 0;
DEBUG_LOG(("nsPipeFilterListener::Available: (%p) %d\n", this, *_retval));
return NS_OK;
}
NS_IMETHODIMP
nsPipeFilterListener::Read(char* buf, PRUint32 count,
PRUint32 *readCount)
{
DEBUG_LOG(("nsPipeFilterListener::Read: (%p) %d\n", this, count));
if (!buf || !readCount)
return NS_ERROR_NULL_POINTER;
PRInt32 avail = (mStreamLength > mStreamOffset) ?
mStreamLength - mStreamOffset : 0;
*readCount = ((PRUint32) avail > count) ? count : avail;
if (*readCount) {
memcpy(buf, mStreamBuf+mStreamOffset, *readCount);
mStreamOffset += *readCount;
}
if (mStreamOffset >= mStreamLength) {
Close();
}
return NS_OK;
}
NS_IMETHODIMP
nsPipeFilterListener::ReadSegments(nsWriteSegmentFun writer,
void * aClosure, PRUint32 count,
PRUint32 *readCount)
{
nsresult rv;
DEBUG_LOG(("nsPipeFilterListener::ReadSegments: %d\n", count));
if (!readCount)
return NS_ERROR_NULL_POINTER;
PRUint32 avail, readyCount, writeCount;
*readCount = 0;
while ((count > 0) && (mStreamOffset < mStreamLength)) {
avail = mStreamLength - mStreamOffset;
readyCount = ((PRUint32) avail > count) ? count : avail;
rv = writer((nsIInputStream*)(this),
aClosure, mStreamBuf+mStreamOffset,
mStreamOffset, readyCount, &writeCount);
NS_ENSURE_SUCCESS(rv, rv);
if (!writeCount)
return NS_ERROR_FAILURE;
DEBUG_LOG(("nsPipeFilterListener::ReadSegments: writer %d\n", writeCount));
*readCount += writeCount;
mStreamOffset += writeCount;
count -= writeCount;
}
if (mStreamOffset >= mStreamLength) {
Close();
}
return NS_OK;
}
NS_IMETHODIMP
nsPipeFilterListener::IsNonBlocking(PRBool *aNonBlocking)
{
DEBUG_LOG(("nsPipeFilterListener::IsNonBlocking: \n"));
*aNonBlocking = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
nsPipeFilterListener::Close()
{
DEBUG_LOG(("nsPipeFilterListener::Close: (%p)\n", this));
mStreamBuf = nsnull;
mStreamOffset = 0;
mStreamLength = 0;
return NS_OK;
}

File Metadata

Mime Type
text/x-c
Expires
Sat, May 10, 8:39 AM (1 d, 15 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
4f/29/80c48718ec9f5d58463411569ff5

Event Timeline