fix 166111 patch by ch.ey@gmx.net, r=bienvenu, sr=mscott fix handling of downloading of pop3 messages with non-standard line endings

This commit is contained in:
bienvenu%nventure.com 2005-02-04 15:45:56 +00:00
Родитель c2b5d9f9c1
Коммит 235a74f646
5 изменённых файлов: 93 добавлений и 114 удалений

Просмотреть файл

@ -107,7 +107,6 @@ nsMsgLineBuffer::nsMsgLineBuffer(nsMsgLineBufferHandler *handler, PRBool convert
m_handler = handler; m_handler = handler;
m_convertNewlinesP = convertNewlinesP; m_convertNewlinesP = convertNewlinesP;
m_lookingForCRLF = PR_TRUE; m_lookingForCRLF = PR_TRUE;
m_ignoreCRLFs = PR_FALSE;
} }
nsMsgLineBuffer::~nsMsgLineBuffer() nsMsgLineBuffer::~nsMsgLineBuffer()
@ -140,43 +139,40 @@ PRInt32 nsMsgLineBuffer::BufferInput(const char *net_buffer, PRInt32 net_buffer_
const char *net_buffer_end = net_buffer + net_buffer_size; const char *net_buffer_end = net_buffer + net_buffer_size;
const char *newline = 0; const char *newline = 0;
const char *s; const char *s;
if (!m_ignoreCRLFs) for (s = net_buffer; s < net_buffer_end; s++)
{ {
for (s = net_buffer; s < net_buffer_end; s++) if (m_lookingForCRLF) {
{ /* Move forward in the buffer until the first newline.
if (m_lookingForCRLF) { Stop when we see CRLF, CR, or LF, or the end of the buffer.
/* Move forward in the buffer until the first newline. *But*, if we see a lone CR at the *very end* of the buffer,
Stop when we see CRLF, CR, or LF, or the end of the buffer. treat this as if we had reached the end of the buffer without
*But*, if we see a lone CR at the *very end* of the buffer, seeing a line terminator. This is to catch the case of the
treat this as if we had reached the end of the buffer without buffers splitting a CRLF pair, as in "FOO\r\nBAR\r" "\nBAZ\r\n".
seeing a line terminator. This is to catch the case of the */
buffers splitting a CRLF pair, as in "FOO\r\nBAR\r" "\nBAZ\r\n". if (*s == nsCRT::CR || *s == nsCRT::LF) {
*/ newline = s;
if (*s == nsCRT::CR || *s == nsCRT::LF) { if (newline[0] == nsCRT::CR) {
newline = s; if (s == net_buffer_end - 1) {
if (newline[0] == nsCRT::CR) { /* CR at end - wait for the next character. */
if (s == net_buffer_end - 1) { newline = 0;
/* CR at end - wait for the next character. */ break;
newline = 0; }
break; else if (newline[1] == nsCRT::LF) {
} /* CRLF seen; swallow both. */
else if (newline[1] == nsCRT::LF) { newline++;
/* CRLF seen; swallow both. */
newline++;
}
} }
newline++;
break;
} }
newline++;
break;
} }
else { }
/* if not looking for a CRLF, stop at CR or LF. (for example, when parsing the newsrc file). this fixes #9896, where we'd lose the last line of anything we'd parse that used CR as the line break. */ else {
if (*s == nsCRT::CR || *s == nsCRT::LF) { /* if not looking for a CRLF, stop at CR or LF. (for example, when parsing the newsrc file). this fixes #9896, where we'd lose the last line of anything we'd parse that used CR as the line break. */
newline = s; if (*s == nsCRT::CR || *s == nsCRT::LF) {
newline++; newline = s;
break; newline++;
} break;
} }
} }
} }
@ -332,7 +328,7 @@ void nsMsgLineStreamBuffer::ClearBuffer()
// Note to people wishing to modify this function: Be *VERY CAREFUL* this is a critical function used by all of // Note to people wishing to modify this function: Be *VERY CAREFUL* this is a critical function used by all of
// our mail protocols including imap, nntp, and pop. If you screw it up, you could break a lot of stuff..... // our mail protocols including imap, nntp, and pop. If you screw it up, you could break a lot of stuff.....
char * nsMsgLineStreamBuffer::ReadNextLine(nsIInputStream * aInputStream, PRUint32 &aNumBytesInLine, PRBool &aPauseForMoreData, nsresult *prv) char * nsMsgLineStreamBuffer::ReadNextLine(nsIInputStream * aInputStream, PRUint32 &aNumBytesInLine, PRBool &aPauseForMoreData, nsresult *prv, PRBool addLineTerminator)
{ {
// try to extract a line from m_inputBuffer. If we don't have an entire line, // try to extract a line from m_inputBuffer. If we don't have an entire line,
// then read more bytes out from the stream. If the stream is empty then wait // then read more bytes out from the stream. If the stream is empty then wait
@ -436,7 +432,7 @@ char * nsMsgLineStreamBuffer::ReadNextLine(nsIInputStream * aInputStream, PRUint
aNumBytesInLine--; aNumBytesInLine--;
// PR_CALLOC zeros out the allocated line // PR_CALLOC zeros out the allocated line
char* newLine = (char*) PR_CALLOC(aNumBytesInLine+1); char* newLine = (char*) PR_CALLOC(aNumBytesInLine + (addLineTerminator ? MSG_LINEBREAK_LEN : 0) + 1);
if (!newLine) if (!newLine)
{ {
aNumBytesInLine = 0; aNumBytesInLine = 0;
@ -445,6 +441,11 @@ char * nsMsgLineStreamBuffer::ReadNextLine(nsIInputStream * aInputStream, PRUint
} }
memcpy(newLine, startOfLine, aNumBytesInLine); // copy the string into the new line buffer memcpy(newLine, startOfLine, aNumBytesInLine); // copy the string into the new line buffer
if (addLineTerminator)
{
memcpy(newLine + aNumBytesInLine, MSG_LINEBREAK, MSG_LINEBREAK_LEN);
aNumBytesInLine += MSG_LINEBREAK_LEN;
}
if (m_eatCRLFs) if (m_eatCRLFs)
endOfLine += 1; // advance past LF or CR if we haven't already done so... endOfLine += 1; // advance past LF or CR if we haven't already done so...

Просмотреть файл

@ -90,7 +90,6 @@ protected:
nsMsgLineBufferHandler *m_handler; nsMsgLineBufferHandler *m_handler;
PRBool m_convertNewlinesP; PRBool m_convertNewlinesP;
PRBool m_lookingForCRLF; PRBool m_lookingForCRLF;
PRBool m_ignoreCRLFs;
}; };
// I'm adding this utility class here for lack of a better place. This utility class is similar to nsMsgLineBuffer // I'm adding this utility class here for lack of a better place. This utility class is similar to nsMsgLineBuffer
@ -122,7 +121,7 @@ public:
// aEndOfLinetoken -- delimiter used to denote the end of a line. // aEndOfLinetoken -- delimiter used to denote the end of a line.
// aNumBytesInLine -- The number of bytes in the line returned // aNumBytesInLine -- The number of bytes in the line returned
// aPauseForMoreData -- There is not enough data in the stream to make a line at this time... // aPauseForMoreData -- There is not enough data in the stream to make a line at this time...
char * ReadNextLine(nsIInputStream * aInputStream, PRUint32 &anumBytesInLine, PRBool &aPauseForMoreData, nsresult *rv = nsnull); char * ReadNextLine(nsIInputStream * aInputStream, PRUint32 &anumBytesInLine, PRBool &aPauseForMoreData, nsresult *rv = nsnull, PRBool addLineTerminator = PR_FALSE);
nsresult GrowBuffer(PRInt32 desiredSize); nsresult GrowBuffer(PRInt32 desiredSize);
void ClearBuffer(); void ClearBuffer();
PRBool NextLineAvailable(); PRBool NextLineAvailable();

Просмотреть файл

@ -509,7 +509,6 @@ NS_INTERFACE_MAP_END_INHERITING(nsMsgProtocol)
nsPop3Protocol::nsPop3Protocol(nsIURI* aURL) nsPop3Protocol::nsPop3Protocol(nsIURI* aURL)
: nsMsgProtocol(aURL), : nsMsgProtocol(aURL),
nsMsgLineBuffer(NULL, PR_FALSE),
m_bytesInMsgReceived(0), m_bytesInMsgReceived(0),
m_totalFolderSize(0), m_totalFolderSize(0),
m_totalDownloadSize(0), m_totalDownloadSize(0),
@ -520,8 +519,6 @@ nsPop3Protocol::nsPop3Protocol(nsIURI* aURL)
m_responseTimer(nsnull), m_responseTimer(nsnull),
m_responseTimeout(45) m_responseTimeout(45)
{ {
SetLookingForCRLF(MSG_LINEBREAK_LEN == 2);
m_ignoreCRLFs = PR_TRUE;
} }
nsresult nsPop3Protocol::Initialize(nsIURI * aURL) nsresult nsPop3Protocol::Initialize(nsIURI * aURL)
@ -3020,21 +3017,22 @@ nsPop3Protocol::RetrResponse(nsIInputStream* inputStream,
{ {
if (m_pop3ConData->msg_closure) if (m_pop3ConData->msg_closure)
{ {
m_ignoreCRLFs = PR_TRUE; rv = HandleLine(line, buffer_size);
PRInt32 res = BufferInput(line, buffer_size); if (NS_FAILED(rv))
if (res < 0) return(Error(POP3_MESSAGE_WRITE_ERROR)); return (Error(POP3_MESSAGE_WRITE_ERROR));
m_ignoreCRLFs = PR_FALSE;
res = BufferInput(MSG_LINEBREAK, MSG_LINEBREAK_LEN);
if (res < 0) return(Error(POP3_MESSAGE_WRITE_ERROR));
// not really sure we always had CRLF in input since we
// also treat a single LF as line ending!
m_pop3ConData->parsed_bytes += (buffer_size+2); // including CRLF m_pop3ConData->parsed_bytes += (buffer_size+2); // including CRLF
} }
// now read in the next line // now read in the next line
PR_Free(line); PR_Free(line);
line = m_lineStreamBuffer->ReadNextLine(inputStream, buffer_size, line = m_lineStreamBuffer->ReadNextLine(inputStream, buffer_size,
pauseForMoreData); pauseForMoreData, &rv, PR_TRUE);
PR_LOG(POP3LOGMODULE, PR_LOG_ALWAYS,("RECV: %s", line)); PR_LOG(POP3LOGMODULE, PR_LOG_ALWAYS,("RECV: %s", line));
// not really sure we always had CRLF in input since we
// also treat a single LF as line ending!
status += (buffer_size+2); // including CRLF status += (buffer_size+2); // including CRLF
} while (line); } while (line);
} }
@ -3068,8 +3066,8 @@ nsPop3Protocol::RetrResponse(nsIInputStream* inputStream,
// (Note: This is only a temp hack until the underlying XPCOM is // (Note: This is only a temp hack until the underlying XPCOM is
// fixed to return errors) // fixed to return errors)
if(NS_FAILED(rv)) if (NS_FAILED(rv))
return(Error(POP3_MESSAGE_WRITE_ERROR)); return (Error(POP3_MESSAGE_WRITE_ERROR));
m_pop3ConData->msg_closure = 0; m_pop3ConData->msg_closure = 0;
} }
@ -3195,50 +3193,41 @@ nsPop3Protocol::TopResponse(nsIInputStream* inputStream, PRUint32 length)
return RetrResponse(inputStream, length); return RetrResponse(inputStream, length);
} }
/* line is handed over as null-terminated string with MSG_LINEBREAK */
PRInt32 nsresult
nsPop3Protocol::HandleLine(char *line, PRUint32 line_length) nsPop3Protocol::HandleLine(char *line, PRUint32 line_length)
{ {
nsresult rv; nsresult rv = NS_OK;
NS_ASSERTION(m_pop3ConData->msg_closure, "m_pop3ConData->msg_closure is null in nsPop3Protocol::HandleLine()"); NS_ASSERTION(m_pop3ConData->msg_closure, "m_pop3ConData->msg_closure is null in nsPop3Protocol::HandleLine()");
if (!m_pop3ConData->msg_closure) if (!m_pop3ConData->msg_closure)
return -1; return NS_ERROR_NULL_POINTER;
if (!m_senderInfo.IsEmpty() && !m_pop3ConData->seenFromHeader) if (!m_senderInfo.IsEmpty() && !m_pop3ConData->seenFromHeader)
{ {
if (line_length > 6 && !PL_strncasecmp("From: ", line, 6)) if (line_length > 6 && !PL_strncasecmp("From: ", line, 6))
{ {
/* Zzzzz PL_strstr only works with NULL terminated string. Since,
* the last character of a line is either a carriage return
* or a linefeed. Temporary setting the last character of the
* line to NULL and later setting it back should be the right
* thing to do.
*/
char ch = line[line_length-1];
line[line_length-1] = 0;
m_pop3ConData->seenFromHeader = PR_TRUE; m_pop3ConData->seenFromHeader = PR_TRUE;
if (PL_strstr(line, m_senderInfo.get()) == NULL) if (PL_strstr(line, m_senderInfo.get()) == NULL)
m_nsIPop3Sink->SetSenderAuthedFlag(m_pop3ConData->msg_closure, m_nsIPop3Sink->SetSenderAuthedFlag(m_pop3ConData->msg_closure,
PR_FALSE); PR_FALSE);
line[line_length-1] = ch;
} }
} }
// line contains only dot and linebreak -> message end // line contains only a single dot and linebreak -> message end
if (line[0] == '.' && line_length == 1 + MSG_LINEBREAK_LEN) if (line_length == 1 + MSG_LINEBREAK_LEN && line[0] == '.')
{ {
m_pop3ConData->assumed_end = PR_TRUE; /* in case byte count from server is */ m_pop3ConData->assumed_end = PR_TRUE; /* in case byte count from server is */
/* wrong, mark we may have had the end */ /* wrong, mark we may have had the end */
if (!m_pop3ConData->dot_fix || m_pop3ConData->truncating_cur_msg || if (!m_pop3ConData->dot_fix || m_pop3ConData->truncating_cur_msg ||
(m_pop3ConData->parsed_bytes >= (m_pop3ConData->pop3_size -3))) (m_pop3ConData->parsed_bytes >= (m_pop3ConData->pop3_size -3)))
{ {
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv); nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
nsCOMPtr<nsIMsgWindow> msgWindow; nsCOMPtr<nsIMsgWindow> msgWindow;
if (NS_SUCCEEDED(rv)) if (NS_SUCCEEDED(rv))
rv = mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow)); rv = mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
rv = m_nsIPop3Sink->IncorporateComplete(msgWindow, rv = m_nsIPop3Sink->IncorporateComplete(msgWindow,
m_pop3ConData->truncating_cur_msg ? m_pop3ConData->cur_msg_size : 0); m_pop3ConData->truncating_cur_msg ? m_pop3ConData->cur_msg_size : 0);
// The following was added to prevent the loss of Data when we try // The following was added to prevent the loss of Data when we try
// and write to somewhere we dont have write access error to (See // and write to somewhere we dont have write access error to (See
@ -3246,35 +3235,25 @@ nsPop3Protocol::HandleLine(char *line, PRUint32 line_length)
// (Note: This is only a temp hack until the underlying XPCOM is // (Note: This is only a temp hack until the underlying XPCOM is
// fixed to return errors) // fixed to return errors)
if(NS_FAILED(rv)) if (NS_FAILED(rv))
return(Error((rv == NS_MSG_ERROR_COPYING_FROM_TMP_DOWNLOAD) return (Error((rv == NS_MSG_ERROR_COPYING_FROM_TMP_DOWNLOAD)
? POP3_TMP_DOWNLOAD_FAILED ? POP3_TMP_DOWNLOAD_FAILED
: POP3_MESSAGE_WRITE_ERROR)); : POP3_MESSAGE_WRITE_ERROR));
m_pop3ConData->msg_closure = 0; m_pop3ConData->msg_closure = nsnull;
return 0; return rv;
} }
} }
/*When examining a multi-line response, the client checks /* Check if the line begins with the termination octet. If so
to see if the line begins with the termination octet. If so and if and if another termination octet follows, we step over the
octets other than CRLF follow, the first octet of the line (the first occurence of it. */
termination octet) is stripped away.*/ else if (line_length > 1 && line[0] == '.' && line[1] == '.') {
else if (line_length > 1 && line[0] == '.' && line[1] == '.' ) line++;
{ line_length--;
PRUint32 i=0;
while ( i < line_length -1 ){
line[i] = line[i+1];
i++;
}
line[i] = '\0';
line_length -= 1;
} }
rv = m_nsIPop3Sink->IncorporateWrite(line, line_length);
if(NS_FAILED(rv)) return m_nsIPop3Sink->IncorporateWrite(line, line_length);
return(Error(POP3_MESSAGE_WRITE_ERROR));
return 0;
} }
PRInt32 nsPop3Protocol::SendDele() PRInt32 nsPop3Protocol::SendDele()

Просмотреть файл

@ -281,7 +281,7 @@ typedef struct _Pop3ConData {
#define POP3_AUTH_FAILURE 0x00000008 /* extended code said authentication failed */ #define POP3_AUTH_FAILURE 0x00000008 /* extended code said authentication failed */
class nsPop3Protocol : public nsMsgProtocol, public nsMsgLineBuffer, public nsIPop3Protocol class nsPop3Protocol : public nsMsgProtocol, public nsIPop3Protocol
{ {
public: public:
nsPop3Protocol(nsIURI* aURL); nsPop3Protocol(nsIURI* aURL);
@ -301,8 +301,6 @@ public:
NS_IMETHOD OnTransportStatus(nsITransport *transport, nsresult status, PRUint32 progress, PRUint32 progressMax); NS_IMETHOD OnTransportStatus(nsITransport *transport, nsresult status, PRUint32 progress, PRUint32 progressMax);
NS_IMETHOD OnStopRequest(nsIRequest *request, nsISupports * aContext, nsresult aStatus); NS_IMETHOD OnStopRequest(nsIRequest *request, nsISupports * aContext, nsresult aStatus);
NS_IMETHOD Cancel(nsresult status); NS_IMETHOD Cancel(nsresult status);
// for nsMsgLineBuffer
virtual PRInt32 HandleLine(char *line, PRUint32 line_length);
static void MarkMsgInHashTable(PLHashTable *hashTable, const Pop3UidlEntry *uidl, static void MarkMsgInHashTable(PLHashTable *hashTable, const Pop3UidlEntry *uidl,
PRBool *changed); PRBool *changed);
@ -365,6 +363,8 @@ private:
void SetResponseTimer(); void SetResponseTimer();
void CancelResponseTimer(); void CancelResponseTimer();
nsresult HandleLine(char *line, PRUint32 line_length);
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
// Begin Pop3 protocol state handlers // Begin Pop3 protocol state handlers
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////

Просмотреть файл

@ -2422,14 +2422,14 @@ PRInt32 nsNNTPProtocol::BeginArticle()
PRInt32 nsNNTPProtocol::DisplayArticle(nsIInputStream * inputStream, PRUint32 length) PRInt32 nsNNTPProtocol::DisplayArticle(nsIInputStream * inputStream, PRUint32 length)
{ {
PRUint32 status = 0; PRUint32 line_length = 0;
PRBool pauseForMoreData = PR_FALSE; PRBool pauseForMoreData = PR_FALSE;
if (m_channelListener) if (m_channelListener)
{ {
nsresult rv = NS_OK;
char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData); char *line = m_lineStreamBuffer->ReadNextLine(inputStream, line_length, pauseForMoreData, &rv, PR_TRUE);
if(pauseForMoreData) if (pauseForMoreData)
{ {
PRUint32 inlength = 0; PRUint32 inlength = 0;
mDisplayInputStream->Available(&inlength); mDisplayInputStream->Available(&inlength);
@ -2437,13 +2437,14 @@ PRInt32 nsNNTPProtocol::DisplayArticle(nsIInputStream * inputStream, PRUint32 le
m_channelListener->OnDataAvailable(this, m_channelContext, mDisplayInputStream, 0, inlength); m_channelListener->OnDataAvailable(this, m_channelContext, mDisplayInputStream, 0, inlength);
SetFlag(NNTP_PAUSE_FOR_READ); SetFlag(NNTP_PAUSE_FOR_READ);
PR_Free(line); PR_Free(line);
return status; return line_length;
} }
if (m_newsFolder) if (m_newsFolder)
m_newsFolder->NotifyDownloadedLine(line, m_key); m_newsFolder->NotifyDownloadedLine(line, m_key);
if (line[0] == '.' && line[1] == 0) // line only contains a single dot -> message end
if (line_length == 1 + MSG_LINEBREAK_LEN && line[0] == '.')
{ {
m_nextState = NEWS_DONE; m_nextState = NEWS_DONE;
@ -2454,23 +2455,22 @@ PRInt32 nsNNTPProtocol::DisplayArticle(nsIInputStream * inputStream, PRUint32 le
if (inlength > 0) // broadcast our batched up ODA changes if (inlength > 0) // broadcast our batched up ODA changes
m_channelListener->OnDataAvailable(this, m_channelContext, mDisplayInputStream, 0, inlength); m_channelListener->OnDataAvailable(this, m_channelContext, mDisplayInputStream, 0, inlength);
PR_Free(line); PR_Free(line);
return status; return line_length;
} }
else // we aren't finished with the message yet else // we aren't finished with the message yet
{ {
PRUint32 count = 0; PRUint32 count = 0;
// skip over the quoted '.' // skip over the quoted '.'
if (line[0] == '.') if (line_length > 1 && line[0] == '.' && line[1] == '.')
mDisplayOutputStream->Write(line+1, PL_strlen(line)-1, &count); mDisplayOutputStream->Write(line+1, line_length-1, &count);
else else
mDisplayOutputStream->Write(line, PL_strlen(line), &count); mDisplayOutputStream->Write(line, line_length, &count);
mDisplayOutputStream->Write(MSG_LINEBREAK, PL_strlen(MSG_LINEBREAK), &count);
} }
PR_Free(line); PR_Free(line);
} }
return 0; return 0;
} }