1999-03-20 01:55:08 +03:00
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
1999-11-06 06:43:54 +03:00
* The contents of this file are subject to the Netscape 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/NPL/
1999-03-20 01:55:08 +03:00
*
1999-11-06 06:43:54 +03:00
* 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 .
1999-03-20 01:55:08 +03:00
*
1999-11-06 06:43:54 +03:00
* The Original Code is mozilla . org code .
*
* The Initial Developer of the Original Code is Netscape
1999-03-20 01:55:08 +03:00
* Communications Corporation . Portions created by Netscape are
1999-11-06 06:43:54 +03:00
* Copyright ( C ) 1999 Netscape Communications Corporation . All
* Rights Reserved .
*
* Contributor ( s ) :
1999-03-20 01:55:08 +03:00
*/
# include "msgCore.h"
1999-09-08 07:08:27 +04:00
# include "prlog.h"
1999-03-20 01:55:08 +03:00
# include "nsMsgLineBuffer.h"
1999-04-17 02:05:33 +04:00
# include "nsIInputStream.h" // used by nsMsgLineStreamBuffer
1999-10-11 02:32:54 +04:00
MOZ_DECL_CTOR_COUNTER ( nsByteArray ) ;
1999-03-20 01:55:08 +03:00
nsByteArray : : nsByteArray ( )
{
1999-10-11 02:32:54 +04:00
MOZ_COUNT_CTOR ( nsByteArray ) ;
1999-03-20 01:55:08 +03:00
m_buffer = NULL ;
m_bufferSize = 0 ;
m_bufferPos = 0 ;
}
nsByteArray : : ~ nsByteArray ( )
{
1999-10-11 02:32:54 +04:00
MOZ_COUNT_DTOR ( nsByteArray ) ;
1999-03-20 01:55:08 +03:00
PR_FREEIF ( m_buffer ) ;
}
nsresult nsByteArray : : GrowBuffer ( PRUint32 desired_size , PRUint32 quantum )
{
if ( m_bufferSize < desired_size )
{
char * new_buf ;
PRUint32 increment = desired_size - m_bufferSize ;
if ( increment < quantum ) /* always grow by a minimum of N bytes */
increment = quantum ;
new_buf = ( m_buffer
? ( char * ) PR_REALLOC ( m_buffer , ( m_bufferSize + increment ) )
: ( char * ) PR_MALLOC ( m_bufferSize + increment ) ) ;
if ( ! new_buf )
return NS_ERROR_OUT_OF_MEMORY ;
m_buffer = new_buf ;
m_bufferSize + = increment ;
}
return 0 ;
}
nsresult nsByteArray : : AppendString ( const char * string )
{
PRUint32 strLength = ( string ) ? PL_strlen ( string ) : 0 ;
return AppendBuffer ( string , strLength ) ;
}
nsresult nsByteArray : : AppendBuffer ( const char * buffer , PRUint32 length )
{
nsresult ret = NS_OK ;
if ( m_bufferPos + length > m_bufferSize )
ret = GrowBuffer ( m_bufferPos + length , 1024 ) ;
if ( ret = = NS_OK )
{
memcpy ( m_buffer + m_bufferPos , buffer , length ) ;
m_bufferPos + = length ;
}
return ret ;
}
1999-10-11 02:32:54 +04:00
MOZ_DECL_CTOR_COUNTER ( nsMsgLineBuffer ) ;
1999-03-20 01:55:08 +03:00
nsMsgLineBuffer : : nsMsgLineBuffer ( nsMsgLineBufferHandler * handler , PRBool convertNewlinesP )
{
1999-10-11 02:32:54 +04:00
MOZ_COUNT_CTOR ( nsMsgLineBuffer ) ;
1999-03-20 01:55:08 +03:00
m_handler = handler ;
m_convertNewlinesP = convertNewlinesP ;
1999-07-24 22:15:19 +04:00
m_lookingForCRLF = PR_TRUE ;
1999-03-20 01:55:08 +03:00
}
nsMsgLineBuffer : : ~ nsMsgLineBuffer ( )
{
1999-10-11 02:32:54 +04:00
MOZ_COUNT_DTOR ( nsMsgLineBuffer ) ;
1999-03-20 01:55:08 +03:00
}
1999-07-24 22:15:19 +04:00
void
nsMsgLineBuffer : : SetLookingForCRLF ( PRBool b )
{
m_lookingForCRLF = b ;
}
1999-03-20 01:55:08 +03:00
PRInt32 nsMsgLineBuffer : : BufferInput ( const char * net_buffer , PRInt32 net_buffer_size )
{
int status = 0 ;
if ( m_bufferPos > 0 & & m_buffer & & m_buffer [ m_bufferPos - 1 ] = = CR & &
net_buffer_size > 0 & & net_buffer [ 0 ] ! = LF ) {
/* The last buffer ended with a CR. The new buffer does not start
with a LF . This old buffer should be shipped out and discarded . */
PR_ASSERT ( m_bufferSize > m_bufferPos ) ;
if ( m_bufferSize < = m_bufferPos ) return - 1 ;
status = ConvertAndSendBuffer ( ) ;
if ( status < 0 )
return status ;
m_bufferPos = 0 ;
}
while ( net_buffer_size > 0 )
{
const char * net_buffer_end = net_buffer + net_buffer_size ;
const char * newline = 0 ;
const char * s ;
for ( s = net_buffer ; s < net_buffer_end ; s + + )
{
1999-07-24 22:15:19 +04:00
if ( m_lookingForCRLF ) {
1999-03-20 01:55:08 +03:00
/* Move forward in the buffer until the first newline.
Stop when we see CRLF , CR , or LF , or the end of the buffer .
* But * , if we see a lone CR at the * very end * of the buffer ,
treat this as if we had reached the end of the buffer without
seeing a line terminator . This is to catch the case of the
buffers splitting a CRLF pair , as in " FOO \r \n BAR \r " " \n BAZ \r \n " .
1999-07-24 22:15:19 +04:00
*/
if ( * s = = CR | | * s = = LF ) {
newline = s ;
if ( newline [ 0 ] = = CR ) {
if ( s = = net_buffer_end - 1 ) {
/* CR at end - wait for the next character. */
newline = 0 ;
break ;
}
else if ( newline [ 1 ] = = LF ) {
/* CRLF seen; swallow both. */
newline + + ;
}
}
newline + + ;
break ;
1999-03-20 01:55:08 +03:00
}
1999-07-24 22:15:19 +04:00
}
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. */
if ( * s = = CR | | * s = = LF ) {
newline = s ;
newline + + ;
break ;
}
}
1999-03-20 01:55:08 +03:00
}
/* Ensure room in the net_buffer and append some or all of the current
chunk of data to it . */
{
const char * end = ( newline ? newline : net_buffer_end ) ;
PRUint32 desired_size = ( end - net_buffer ) + m_bufferPos + 1 ;
if ( desired_size > = m_bufferSize )
{
status = GrowBuffer ( desired_size , 1024 ) ;
if ( status < 0 )
return status ;
}
memcpy ( m_buffer + m_bufferPos , net_buffer , ( end - net_buffer ) ) ;
m_bufferPos + = ( end - net_buffer ) ;
}
/* Now m_buffer contains either a complete line, or as complete
a line as we have read so far .
If we have a line , process it , and then remove it from ` m_buffer ' .
Then go around the loop again , until we drain the incoming data .
*/
if ( ! newline )
return 0 ;
status = ConvertAndSendBuffer ( ) ;
if ( status < 0 ) return status ;
net_buffer_size - = ( newline - net_buffer ) ;
net_buffer = newline ;
m_bufferPos = 0 ;
}
# ifdef DEBUG_bienvenu
printf ( " returning from buffer input m_bufferPos = %ld \n " , m_bufferPos ) ;
# endif
return 0 ;
}
PRInt32 nsMsgLineBuffer : : HandleLine ( char * line , PRUint32 line_length )
{
1999-09-08 07:08:27 +04:00
NS_ASSERTION ( PR_FALSE , " must override this method if you don't provide a handler " ) ;
1999-03-20 01:55:08 +03:00
return 0 ;
}
PRInt32 nsMsgLineBuffer : : ConvertAndSendBuffer ( )
{
/* Convert the line terminator to the native form.
*/
char * buf = m_buffer ;
PRInt32 length = m_bufferPos ;
char * newline ;
PR_ASSERT ( buf & & length > 0 ) ;
if ( ! buf | | length < = 0 )
return - 1 ;
newline = buf + length ;
PR_ASSERT ( newline [ - 1 ] = = CR | | newline [ - 1 ] = = LF ) ;
if ( newline [ - 1 ] ! = CR & & newline [ - 1 ] ! = LF )
return - 1 ;
if ( ! m_convertNewlinesP )
{
}
1999-04-27 07:06:34 +04:00
# if (MSG_LINEBREAK_LEN == 1)
1999-03-20 01:55:08 +03:00
else if ( ( newline - buf ) > = 2 & &
newline [ - 2 ] = = CR & &
newline [ - 1 ] = = LF )
{
/* CRLF -> CR or LF */
1999-04-27 07:06:34 +04:00
buf [ length - 2 ] = MSG_LINEBREAK [ 0 ] ;
1999-03-20 01:55:08 +03:00
length - - ;
}
else if ( newline > buf + 1 & &
1999-04-27 07:06:34 +04:00
newline [ - 1 ] ! = MSG_LINEBREAK [ 0 ] )
1999-03-20 01:55:08 +03:00
{
/* CR -> LF or LF -> CR */
1999-04-27 07:06:34 +04:00
buf [ length - 1 ] = MSG_LINEBREAK [ 0 ] ;
1999-03-20 01:55:08 +03:00
}
# else
else if ( ( ( newline - buf ) > = 2 & & newline [ - 2 ] ! = CR ) | |
( ( newline - buf ) > = 1 & & newline [ - 1 ] ! = LF ) )
{
/* LF -> CRLF or CR -> CRLF */
length + + ;
1999-04-27 07:06:34 +04:00
buf [ length - 2 ] = MSG_LINEBREAK [ 0 ] ;
buf [ length - 1 ] = MSG_LINEBREAK [ 1 ] ;
1999-03-20 01:55:08 +03:00
}
# endif
return ( m_handler ) ? m_handler - > HandleLine ( buf , length ) : HandleLine ( buf , length ) ;
}
// If there's still some data (non CRLF terminated) flush it out
PRInt32 nsMsgLineBuffer : : FlushLastLine ( )
{
char * buf = m_buffer + m_bufferPos ;
PRInt32 length = m_bufferPos - 1 ;
if ( length > 0 )
return ( m_handler ) ? m_handler - > HandleLine ( buf , length ) : HandleLine ( buf , length ) ;
else
return 0 ;
}
1999-04-17 02:05:33 +04:00
///////////////////////////////////////////////////////////////////////////////////////////////////
// This is a utility class used to efficiently extract lines from an input stream by buffering
// read but unprocessed stream data in a buffer.
///////////////////////////////////////////////////////////////////////////////////////////////////
1999-04-25 23:51:08 +04:00
nsMsgLineStreamBuffer : : nsMsgLineStreamBuffer ( PRUint32 aBufferSize , const char * aEndOfLineToken ,
PRBool aAllocateNewLines , PRBool aEatCRLFs )
: m_eatCRLFs ( aEatCRLFs ) , m_allocateNewLines ( aAllocateNewLines ) ,
m_endOfLineToken ( aEndOfLineToken )
1999-04-17 02:05:33 +04:00
{
NS_PRECONDITION ( aBufferSize > 0 , " invalid buffer size!!! " ) ;
m_dataBuffer = nsnull ;
1999-12-20 17:58:05 +03:00
m_startPos = 0 ;
m_numBytesInBuffer = 0 ;
1999-04-17 02:05:33 +04:00
// used to buffer incoming data by ReadNextLineFromInput
if ( aBufferSize > 0 )
{
m_dataBuffer = ( char * ) PR_CALLOC ( sizeof ( char ) * aBufferSize ) ;
}
m_dataBufferSize = aBufferSize ;
}
nsMsgLineStreamBuffer : : ~ nsMsgLineStreamBuffer ( )
{
PR_FREEIF ( m_dataBuffer ) ; // release our buffer...
}
// the design for this method has an inherit bug: if the length of the line is greater than the size of m_dataBufferSize,
// then we'll never find the next line because we can't hold the whole line in memory.
// aInputStream - the input stream we want to read a line from
// aPauseForMoreData is returned as PR_TRUE if the stream does not yet contain a line and we must wait for more
// data to come into the stream.
// 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.....
1999-04-25 23:51:08 +04:00
char * nsMsgLineStreamBuffer : : ReadNextLine ( nsIInputStream * aInputStream , PRUint32 & aNumBytesInLine , PRBool & aPauseForMoreData )
1999-04-17 02:05:33 +04:00
{
// 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
// on the monitor for more data to come in.
1999-12-20 17:58:05 +03:00
NS_PRECONDITION ( m_dataBuffer & & m_dataBufferSize > 0 , " invalid input arguments for read next line from input " ) ;
1999-04-17 02:05:33 +04:00
1999-04-25 23:51:08 +04:00
// initialize out values
1999-04-17 02:05:33 +04:00
aPauseForMoreData = PR_FALSE ;
1999-04-25 23:51:08 +04:00
aNumBytesInLine = 0 ;
1999-04-17 02:05:33 +04:00
char * endOfLine = nsnull ;
1999-12-20 17:58:05 +03:00
char * startOfLine = m_dataBuffer + m_startPos ;
1999-03-20 01:55:08 +03:00
1999-12-20 17:58:05 +03:00
if ( m_numBytesInBuffer > 0 ) // any data in our internal buffer?
endOfLine = PL_strstr ( startOfLine , m_endOfLineToken ) ; // see if we already have a line ending...
1999-04-17 02:05:33 +04:00
// it's possible that we got here before the first time we receive data from the server
// so aInputStream will be nsnull...
if ( ! endOfLine & & aInputStream ) // get some more data from the server
{
PRUint32 numBytesInStream = 0 ;
PRUint32 numBytesCopied = 0 ;
1999-09-10 02:05:05 +04:00
aInputStream - > Available ( & numBytesInStream ) ;
1999-04-17 02:05:33 +04:00
// if the number of bytes we want to read from the stream, is greater than the number
// of bytes left in our buffer, then we need to shift the start pos and its contents
// down to the beginning of m_dataBuffer...
1999-12-20 17:58:05 +03:00
PRUint32 numFreeBytesInBuffer = m_dataBufferSize - m_startPos - m_numBytesInBuffer ;
1999-10-21 01:01:58 +04:00
if ( numBytesInStream > = numFreeBytesInBuffer )
1999-04-17 02:05:33 +04:00
{
1999-12-20 17:58:05 +03:00
if ( m_numBytesInBuffer & & m_startPos )
{
nsCRT : : memmove ( m_dataBuffer , startOfLine , m_numBytesInBuffer ) ;
m_dataBuffer [ m_numBytesInBuffer ] = ' \0 ' ; // make sure the end
// of the buffer is
// terminated
m_startPos = 0 ;
startOfLine = m_dataBuffer ;
numFreeBytesInBuffer = m_dataBufferSize - m_numBytesInBuffer ;
2000-01-28 04:22:33 +03:00
// printf("moving data in read line around because buffer filling up\n");
1999-12-20 17:58:05 +03:00
}
NS_ASSERTION ( m_startPos = = 0 , " m_startPos should be 0 ..... \n " ) ;
1999-04-17 02:05:33 +04:00
}
1999-04-22 05:53:19 +04:00
PRUint32 numBytesToCopy = PR_MIN ( numFreeBytesInBuffer - 1 /* leave one for a null terminator */ , numBytesInStream ) ;
1999-04-17 02:05:33 +04:00
// read the data into the end of our data buffer
if ( numBytesToCopy > 0 )
{
1999-12-20 17:58:05 +03:00
aInputStream - > Read ( startOfLine + m_numBytesInBuffer ,
numBytesToCopy , & numBytesCopied ) ;
m_numBytesInBuffer + = numBytesCopied ;
m_dataBuffer [ m_startPos + m_numBytesInBuffer ] = ' \0 ' ;
1999-04-17 02:05:33 +04:00
}
1999-12-20 17:58:05 +03:00
else if ( ! m_numBytesInBuffer )
{
aPauseForMoreData = PR_TRUE ;
return nsnull ;
}
1999-04-17 02:05:33 +04:00
// okay, now that we've tried to read in more data from the stream, look for another end of line
// character
1999-12-20 17:58:05 +03:00
endOfLine = PL_strstr ( startOfLine , m_endOfLineToken ) ;
1999-04-25 23:51:08 +04:00
1999-04-17 02:05:33 +04:00
}
// okay, now check again for endOfLine.
if ( endOfLine )
{
1999-04-21 03:44:34 +04:00
if ( ! m_eatCRLFs )
1999-04-25 23:51:08 +04:00
endOfLine + = PL_strlen ( m_endOfLineToken ) ; // count for CRLF
1999-04-21 03:44:34 +04:00
1999-12-20 17:58:05 +03:00
aNumBytesInLine = endOfLine - startOfLine ;
1999-04-17 02:05:33 +04:00
// PR_CALLOC zeros out the allocated line
1999-12-20 17:58:05 +03:00
char * newLine = ( char * ) PR_CALLOC ( aNumBytesInLine + 1 ) ;
1999-04-17 02:05:33 +04:00
if ( ! newLine )
1999-12-20 17:58:05 +03:00
{
aNumBytesInLine = 0 ;
aPauseForMoreData = PR_TRUE ;
1999-04-17 02:05:33 +04:00
return nsnull ;
1999-12-20 17:58:05 +03:00
}
1999-04-17 02:05:33 +04:00
1999-12-20 17:58:05 +03:00
nsCRT : : memcpy ( newLine , startOfLine , aNumBytesInLine ) ; // copy the string into the new line buffer
1999-04-17 02:05:33 +04:00
1999-04-21 03:44:34 +04:00
if ( m_eatCRLFs )
1999-04-25 23:51:08 +04:00
endOfLine + = PL_strlen ( m_endOfLineToken ) ; // advance past CRLF if we haven't already done so...
1999-04-21 03:44:34 +04:00
1999-04-17 02:05:33 +04:00
// now we need to update the data buffer to go past the line we just read out.
1999-12-20 17:58:05 +03:00
m_numBytesInBuffer - = ( endOfLine - startOfLine ) ;
if ( m_numBytesInBuffer )
m_startPos = endOfLine - m_dataBuffer ;
else
m_startPos = 0 ;
1999-04-17 02:05:33 +04:00
return newLine ;
}
aPauseForMoreData = PR_TRUE ;
return nsnull ; // if we somehow got here. we don't have another line in the buffer yet...need to wait for more data...
}