/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_tools.hxx"
#include <sal/types.h>
#include <rtl/memory.h>
#include <tools/cachestr.hxx>
#include <tools/debug.hxx>
#include <tools/inetmsg.hxx>
#include <tools/inetstrm.hxx>

#include <ctype.h> // toupper

inline sal_Bool SAL_CALL ascii_isWhitespace( sal_Unicode ch )
{
    return ((ch <= 0x20) && ch);
}

#define CONSTASCII_STRINGPARAM(a) (a), RTL_TEXTENCODING_ASCII_US

/*=======================================================================
 *
 * INetMessageEncodeQPStream Interface.
 * (Quoted-Printable Encoding)
 *
 *=====================================================================*/
class INetMessageEncodeQPStream_Impl : public INetMessageIStream
{
    SvStream               *pMsgStrm;

    sal_uIntPtr                   nMsgBufSiz;
    sal_Char               *pMsgBuffer;
    sal_Char               *pMsgRead;
    sal_Char               *pMsgWrite;

    sal_uIntPtr                   nTokBufSiz;
    sal_Char               *pTokBuffer;
    sal_Char               *pTokRead;
    sal_Char               *pTokWrite;

    INetMessageStreamState  eState;
    sal_Bool                    bDone;

    virtual int GetMsgLine (sal_Char *pData, sal_uIntPtr nSize);

public:
    INetMessageEncodeQPStream_Impl (sal_uIntPtr nMsgBufferSize = 1024);
    virtual ~INetMessageEncodeQPStream_Impl (void);
};

/*=====================================================================
 *
 * INetMessageDecodeQPStream Interface.
 * (Quoted-Printable Decoding)
 *
 *====================================================================*/
class INetMessageDecodeQPStream_Impl : public INetMessageOStream
{
    INetMessageStreamState  eState;
    SvMemoryStream         *pMsgBuffer;

    sal_uIntPtr                   nTokBufLen;
    sal_Char                pTokBuffer[4];

    virtual int PutMsgLine (const sal_Char *pData, sal_uIntPtr nSize);

public:
    INetMessageDecodeQPStream_Impl (void);
    virtual ~INetMessageDecodeQPStream_Impl (void);
};

/*======================================================================
 *
 * INetMessageEncode64Stream Interface.
 * (Base64 Encoding)
 *
 *====================================================================*/
class INetMessageEncode64Stream_Impl : public INetMessageIStream
{
    SvStream  *pMsgStrm;

    sal_uIntPtr      nMsgBufSiz;
    sal_uInt8 *pMsgBuffer;
    sal_uInt8 *pMsgRead;
    sal_uInt8 *pMsgWrite;

    sal_uIntPtr      nTokBufSiz;
    sal_Char  *pTokBuffer;
    sal_Char  *pTokRead;
    sal_Char  *pTokWrite;

    sal_Bool       bDone;

    virtual int GetMsgLine (sal_Char *pData, sal_uIntPtr nSize);

public:
    INetMessageEncode64Stream_Impl (sal_uIntPtr nMsgBufferSize = 2048);
    virtual ~INetMessageEncode64Stream_Impl (void);
};

/*======================================================================
 *
 * INetMessageDecode64Stream Interface.
 * (Base64 Decoding)
 *
 *====================================================================*/
class INetMessageDecode64Stream_Impl : public INetMessageOStream
{
    INetMessageStreamState  eState;

    sal_uIntPtr                   nMsgBufSiz;
    sal_Char               *pMsgBuffer;
    sal_Char               *pMsgRead;
    sal_Char               *pMsgWrite;

    virtual int PutMsgLine (const sal_Char *pData, sal_uIntPtr nSize);

public:
    INetMessageDecode64Stream_Impl (sal_uIntPtr nMsgBufferSize = 128);
    virtual ~INetMessageDecode64Stream_Impl (void);
};

/*=========================================================================
 *
 * INetIStream Implementation.
 *
 *=======================================================================*/
/*
 * INetIStream.
 */
INetIStream::INetIStream ()
{
}

/*
 * ~INetIStream.
 */
INetIStream::~INetIStream (void)
{
}

/*
 * Read.
 */
int INetIStream::Read (sal_Char *pData, sal_uIntPtr nSize)
{
    return GetData (pData, nSize);
}

/*
 * Decode64.
 */
void INetIStream::Decode64 (SvStream& rIn, SvStream& rOut)
{
    INetMessage aMsg;
    aMsg.SetDocumentLB(new SvAsyncLockBytes(&rOut, sal_False));

    INetMessageDecode64Stream_Impl aStream (8192);
    aStream.SetTargetMessage (&aMsg);

    sal_Char* pBuf = new sal_Char[8192];

    int nRead = 0;
    while ((nRead = rIn.Read (pBuf, 8192)) > 0)
        aStream.Write( pBuf, nRead );
    aStream.Write ("\r\n", 2);

    delete[] pBuf;
}

/*
 * Encode64.
 */
void INetIStream::Encode64 (SvStream& rIn, SvStream& rOut)
{
    INetMessage aMsg;
    aMsg.SetDocumentLB (
        new SvLockBytes (&rIn, sal_False));

    INetMessageEncode64Stream_Impl aStream (8192);
    aStream.SetSourceMessage (&aMsg);

    sal_Char* pBuf = new sal_Char[8192];

    int nRead = 0;
    while ((nRead = aStream.Read (pBuf, 8192)) > 0)
        rOut.Write( pBuf, nRead );

    delete[] pBuf;
}

/*=========================================================================
 *
 * INetOStream Implementation.
 *
 *=======================================================================*/
/*
 * INetOStream.
 */
INetOStream::INetOStream ()
{
}

/*
 * ~INetOStream.
 */
INetOStream::~INetOStream (void)
{
}

/*
 * Write.
 */
int INetOStream::Write (const sal_Char *pData, sal_uIntPtr nSize)
{
    return PutData (pData, nSize);
}

/*=========================================================================
 *
 * INetMessageIStream Implementation.
 *
 *=======================================================================*/
/*
 * INetMessageIStream.
 */
INetMessageIStream::INetMessageIStream (sal_uIntPtr nBufferSize)
    : pSourceMsg       (NULL),
      bHeaderGenerated (sal_False),
      nBufSiz          (nBufferSize),
      pMsgStrm         (NULL),
      pMsgBuffer       (new SvMemoryStream)
{
    pMsgBuffer->SetStreamCharSet (RTL_TEXTENCODING_ASCII_US);
    pBuffer = new sal_Char[nBufSiz];
    pRead = pWrite = pBuffer;
}

/*
 * ~INetMessageIStream.
 */
INetMessageIStream::~INetMessageIStream (void)
{
    delete [] pBuffer;
    delete pMsgBuffer;
    delete pMsgStrm;
}

/*
 * GetData.
 */
int INetMessageIStream::GetData (sal_Char *pData, sal_uIntPtr nSize)
{
    if (pSourceMsg == NULL) return INETSTREAM_STATUS_ERROR;

    sal_Char *pWBuf = pData;
    sal_Char *pWEnd = pData + nSize;

    while (pWBuf < pWEnd)
    {
        // Caller's buffer not yet filled.
        sal_uIntPtr n = pRead - pWrite;
        if (n > 0)
        {
            // Bytes still in buffer.
            sal_uIntPtr m = pWEnd - pWBuf;
            if (m < n) n = m;
            for (sal_uIntPtr i = 0; i < n; i++) *pWBuf++ = *pWrite++;
        }
        else
        {
            // Buffer empty. Reset to <Begin-of-Buffer>.
            pRead = pWrite = pBuffer;

            // Read next message line.
            int nRead = GetMsgLine (pBuffer, nBufSiz);
            if (nRead > 0)
            {
                // Set read pointer.
                pRead = pBuffer + nRead;
            }
            else
            {
                if (!bHeaderGenerated)
                {
                    // Header generated. Insert empty line.
                    bHeaderGenerated = sal_True;
                    *pRead++ = '\r';
                    *pRead++ = '\n';
                }
                else
                {
                    // Body generated.
                    return (pWBuf - pData);
                }
            }
        }
    }
    return (pWBuf - pData);
}

/*
 * GetMsgLine.
 */
int INetMessageIStream::GetMsgLine (sal_Char *pData, sal_uIntPtr nSize)
{
    if (pSourceMsg == NULL) return INETSTREAM_STATUS_ERROR;

    sal_Char *pWBuf = pData;
    sal_Char *pWEnd = pData + nSize;

    if (!bHeaderGenerated)
    {
        sal_uIntPtr i, n;

        if (pMsgBuffer->Tell() == 0)
        {
            // Insert formatted header into buffer.
            n = pSourceMsg->GetHeaderCount();
            for (i = 0; i < n; i++)
            {
                INetMessageHeader aHeader (pSourceMsg->GetHeaderField(i));
                if (aHeader.GetValue().Len())
                {
                    // NYI: Folding long lines.
                    *pMsgBuffer << (sal_Char*)(aHeader.GetName().GetBuffer());
                    *pMsgBuffer << ": ";
                    *pMsgBuffer << (sal_Char*)(aHeader.GetValue().GetBuffer());
                    *pMsgBuffer << "\r\n";
                }
            }

            pMsgWrite = (sal_Char *)(pMsgBuffer->GetData());
            pMsgRead  = pMsgWrite + pMsgBuffer->Tell();
        }

        n = pMsgRead - pMsgWrite;
        if (n > 0)
        {
            // Move to caller.
            if (nSize < n) n = nSize;
            for (i = 0; i < n; i++) *pWBuf++ = *pMsgWrite++;
        }
        else
        {
            // Reset buffer.
            pMsgBuffer->Seek (STREAM_SEEK_TO_BEGIN);
        }
    }
    else
    {
        if (pSourceMsg->GetDocumentLB())
        {
            if (pMsgStrm == NULL)
                pMsgStrm = new SvStream (pSourceMsg->GetDocumentLB());

            sal_uIntPtr nRead = pMsgStrm->Read (pWBuf, (pWEnd - pWBuf));
            pWBuf += nRead;
        }
    }
    return (pWBuf - pData);
}

/*=========================================================================
 *
 * INetMessageOStream Implementation.
 *
 *=======================================================================*/
/*
 * INetMessageOStream.
 */
INetMessageOStream::INetMessageOStream (void)
    : pTargetMsg    (NULL),
      bHeaderParsed (sal_False),
      eOState       (INETMSG_EOL_BEGIN),
      pMsgBuffer    (new SvMemoryStream)
{
}

/*
 * ~INetMessageOStream.
 */
INetMessageOStream::~INetMessageOStream (void)
{
    if (pMsgBuffer->Tell() > 0)
        PutMsgLine ((const sal_Char *) pMsgBuffer->GetData(), pMsgBuffer->Tell());
    delete pMsgBuffer;

    if (pTargetMsg)
    {
        SvOpenLockBytes *pLB =
            PTR_CAST (SvOpenLockBytes, pTargetMsg->GetDocumentLB());
        if (pLB)
        {
            pLB->Flush();
            pLB->Terminate();
        }
    }
}

/*
 * PutData.
 * (Simple Field Parsing (RFC822, Appendix B)).
 */
int INetMessageOStream::PutData (const sal_Char *pData, sal_uIntPtr nSize)
{
    if (pTargetMsg == NULL) return INETSTREAM_STATUS_ERROR;

    const sal_Char *pStop = (pData + nSize);

    while (!bHeaderParsed && (pData < pStop))
    {
        if (eOState == INETMSG_EOL_BEGIN)
        {
            if ((*pData == '\r') || (*pData == '\n'))
            {
                /*
                 * Empty Line. Separates header fields from message body.
                 * Skip this and any 2nd line break character (if any).
                 */
                pData++;
                if ((pData < pStop) && ((*pData == '\r') || (*pData == '\n')))
                    pData++;

                // Emit any buffered last header field.
                if (pMsgBuffer->Tell() > 0)
                {
                    *pMsgBuffer << '\0';
                    int status = PutMsgLine (
                        (const sal_Char *) pMsgBuffer->GetData(),
                        pMsgBuffer->Tell());
                    if (status != INETSTREAM_STATUS_OK) return status;
                }

                // Reset to begin.
                eOState = INETMSG_EOL_BEGIN;
                pMsgBuffer->Seek (STREAM_SEEK_TO_BEGIN);

                // Mark header parsed.
                bHeaderParsed = sal_True;
            }
            else if ((*pData == ' ') || (*pData == '\t'))
            {
                // Continuation line. Unfold multi-line field-body.
                *pMsgBuffer << ' ';
                pData++;
            }
            else
            {
                // Begin of new header field.
                if (pMsgBuffer->Tell() > 0)
                {
                    // Emit buffered header field now.
                    *pMsgBuffer << '\0';
                    int status = PutMsgLine (
                        (const sal_Char *) pMsgBuffer->GetData(),
                        pMsgBuffer->Tell());
                    if (status != INETSTREAM_STATUS_OK) return status;
                }

                // Reset to begin of buffer.
                pMsgBuffer->Seek (STREAM_SEEK_TO_BEGIN);

                // Insert current character into buffer.
                *pMsgBuffer << *pData++;
            }

            // Search for next line break character.
            if (!bHeaderParsed) eOState = INETMSG_EOL_SCR;
        }
        else if (eOState == INETMSG_EOL_FCR)
        {
            // Skip line break character.
            pData++;

            // Mark begin of line.
            eOState = INETMSG_EOL_BEGIN;
        }
        else if ((*pData == '\r') || (*pData == '\n'))
        {
            if (*pData == '\r') pData++;
            eOState = INETMSG_EOL_FCR;
        }
        else if (ascii_isWhitespace (*pData & 0x7f))
        {
            // Any <LWS> is folded into a single <SP> character.
            sal_Char c = *((const sal_Char *) pMsgBuffer->GetData() + pMsgBuffer->Tell() - 1);
            if (!ascii_isWhitespace (c & 0x7f)) *pMsgBuffer << ' ';

            // Skip over this <LWS> character.
            pData++;
        }
        else
        {
            // Any other character is inserted into line buffer.
            *pMsgBuffer << *pData++;
        }
    }

    if (bHeaderParsed && (pData < pStop))
    {
        // Put message body down-stream.
        return PutMsgLine (pData, (pStop - pData));
    }

    return INETSTREAM_STATUS_OK;
}

/*
 * PutMsgLine.
 */
int INetMessageOStream::PutMsgLine (const sal_Char *pData, sal_uIntPtr nSize)
{
    // Check for message container.
    if (pTargetMsg == NULL) return INETSTREAM_STATUS_ERROR;

    // Check for header or body.
    if (!IsHeaderParsed())
    {
        ByteString aField (pData);
        sal_uInt16 nPos = aField.Search (':');
        if (nPos != STRING_NOTFOUND)
        {
            ByteString aName (
                aField.Copy (0, nPos));
            ByteString aValue (
                aField.Copy (nPos + 1, aField.Len() - nPos + 1));
            aValue.EraseLeadingChars (' ');

            pTargetMsg->SetHeaderField (
                INetMessageHeader (aName, aValue));
        }
    }
    else
    {
        SvOpenLockBytes *pLB =
            PTR_CAST(SvOpenLockBytes, pTargetMsg->GetDocumentLB());
        if (pLB == NULL)
            return INETSTREAM_STATUS_WOULDBLOCK;

        sal_Size nDocSiz = pTargetMsg->GetDocumentSize();
        sal_Size nWrite  = 0;

        pLB->FillAppend ((sal_Char *)pData, nSize, &nWrite);
        pTargetMsg->SetDocumentSize (nDocSiz + nWrite);

        if (nWrite < nSize) return INETSTREAM_STATUS_ERROR;
    }
    return INETSTREAM_STATUS_OK;
}

/*=========================================================================
 *
 * INetMessageIOStream Implementation.
 *
 *=======================================================================*/
/*
 * INetMessageIOStream.
 */
INetMessageIOStream::INetMessageIOStream (sal_uIntPtr nBufferSize)
    : INetMessageIStream (nBufferSize),
      INetMessageOStream ()
{
}

/*
 * ~INetMessageIOStream.
 */
INetMessageIOStream::~INetMessageIOStream (void)
{
}

/*=======================================================================
 *
 * INetMessageEncodeQPStream_Impl Implementation.
 * (Quoted-Printable Encoding)
 *
 *=====================================================================*/
static const sal_Char hex2pr[16] = {
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
    'A', 'B', 'C', 'D', 'E', 'F'
};

static const sal_Char ebcdic[] = {
    '!', '"', '#', '$', '@', '[', '\\', ']', '^', '`', '{', '|', '}', '~'
};

/*
 * INetMessageEncodeQPStream_Impl.
 */
INetMessageEncodeQPStream_Impl::INetMessageEncodeQPStream_Impl (
    sal_uIntPtr nMsgBufferSize)
    : INetMessageIStream (),
      pMsgStrm   (NULL),
      nMsgBufSiz (nMsgBufferSize),
      nTokBufSiz (80),
      eState     (INETMSG_EOL_SCR),
      bDone      (sal_False)
{
    GenerateHeader (sal_False);

    pMsgBuffer = new sal_Char[nMsgBufSiz];
    pMsgRead = pMsgWrite = pMsgBuffer;

    pTokBuffer = new sal_Char[nTokBufSiz];
    pTokRead = pTokWrite = pTokBuffer;
}

/*
 * ~INetMessageEncodeQPStream_Impl.
 */
INetMessageEncodeQPStream_Impl::~INetMessageEncodeQPStream_Impl (void)
{
    delete pMsgStrm;
    delete [] pMsgBuffer;
    delete [] pTokBuffer;
}

/*
 * GetMsgLine.
 */
int INetMessageEncodeQPStream_Impl::GetMsgLine (sal_Char *pData, sal_uIntPtr nSize)
{
    INetMessage *pMsg = GetSourceMessage ();
    if (pMsg == NULL) return INETSTREAM_STATUS_ERROR;

    if (pMsg->GetDocumentLB() == NULL) return 0;
    if (pMsgStrm == NULL) pMsgStrm = new SvStream (pMsg->GetDocumentLB());

    sal_Char *pWBuf = pData;
    while (pWBuf < (pData + nSize))
    {
        // Caller's buffer not yet filled.
        if ((pMsgRead - pMsgWrite) > 0)
        {
            // Bytes still in message buffer.
            if ((eState != INETMSG_EOL_BEGIN) &&
                ((pTokRead - pTokBuffer) < 72))
            {
                // Token buffer not yet filled.
                if (eState == INETMSG_EOL_FCR)
                {
                    eState = INETMSG_EOL_BEGIN;
                    if (*pMsgWrite != '\n')
                    {
                        // Convert orphant <CR> into <CR><LF> sequence.
                        *pTokRead++ = '\n';
                    }
                    *pTokRead++ = *pMsgWrite++;
                }
                else if ((*pMsgWrite == ' ') || (*pMsgWrite == '\t'))
                {
                    eState = INETMSG_EOL_FSP;
                    *pTokRead++ = *pMsgWrite++;
                }
                else if (*pMsgWrite == '\r')
                {
                    // Found <CR>.
                    if (eState == INETMSG_EOL_FSP)
                    {
                        // Encode last (trailing space) character.
                        sal_uInt8 c = (sal_uInt8)(*(--pTokRead));
                        *pTokRead++ = '=';
                        *pTokRead++ = hex2pr[((c & 0xf0) >> 4)];
                        *pTokRead++ = hex2pr[((c & 0x0f)     )];
                    }
                    eState = INETMSG_EOL_FCR;
                    *pTokRead++ = *pMsgWrite++;
                }
                else if (*pMsgWrite == '\n')
                {
                    // Found <LF> only.
                    if (eState == INETMSG_EOL_FSP)
                    {
                        // Encode last (trailing space) character.
                        sal_uInt8 c = (sal_uInt8)(*(--pTokRead));
                        *pTokRead++ = '=';
                        *pTokRead++ = hex2pr[((c & 0xf0) >> 4)];
                        *pTokRead++ = hex2pr[((c & 0x0f)     )];
                    }
                    eState = INETMSG_EOL_BEGIN;

                    // Convert orphant <LF> into <CR><LF> sequence.
                    *pTokRead++ = '\r';
                    *pTokRead++ = *pMsgWrite++;
                }
                else if (*pMsgWrite == '=')
                {
                    // Escape character itself MUST be encoded, of course.
                    sal_uInt8 c = (sal_uInt8)(*pMsgWrite++);
                    *pTokRead++ = '=';
                    *pTokRead++ = hex2pr[((c & 0xf0) >> 4)];
                    *pTokRead++ = hex2pr[((c & 0x0f)     )];

                    eState = INETMSG_EOL_SCR;
                }
                else if (((sal_uInt8)(*pMsgWrite) > 0x20) &&
                         ((sal_uInt8)(*pMsgWrite) < 0x7f)    )
                {
                    /*
                     * Some printable ASCII character.
                     * (Encode EBCDIC special characters (NYI)).
                     */
                    *pTokRead++ = *pMsgWrite++;
                    eState = INETMSG_EOL_SCR;
                }
                else
                {
                    // Encode any other character.
                    sal_uInt8 c = (sal_uInt8)(*pMsgWrite++);
                    *pTokRead++ = '=';
                    *pTokRead++ = hex2pr[((c & 0xf0) >> 4)];
                    *pTokRead++ = hex2pr[((c & 0x0f)     )];

                    eState = INETMSG_EOL_SCR;
                }
            }
            else
            {
                // Check for maximum line length.
                if (eState != INETMSG_EOL_BEGIN)
                {
                    // Insert soft line break.
                    *pTokRead++ = '=';
                    *pTokRead++ = '\r';
                    *pTokRead++ = '\n';

                    eState = INETMSG_EOL_BEGIN;
                }

                // Copy to caller's buffer.
                if ((pTokRead - pTokWrite) > 0)
                {
                    // Bytes still in token buffer.
                    *pWBuf++ = *pTokWrite++;
                }
                else
                {
                    // Token buffer empty. Reset to <Begin-of-Buffer>.
                    pTokRead = pTokWrite = pTokBuffer;
                    eState = INETMSG_EOL_SCR;
                }
            }
        }
        else
        {
            // Message buffer empty. Reset to <Begin-of-Buffer>.
            pMsgRead = pMsgWrite = pMsgBuffer;

            // Read next message block.
            sal_uIntPtr nRead = pMsgStrm->Read (pMsgBuffer, nMsgBufSiz);
            if (nRead > 0)
            {
                // Set read pointer.
                pMsgRead = (pMsgBuffer + nRead);
            }
            else
            {
                // Nothing more ro read.
                if (!bDone)
                {
                    // Append final <CR><LF> and mark we're done.
                    *pTokRead++ = '\r';
                    *pTokRead++ = '\n';

                    bDone = sal_True;
                }
                else
                {
                    // Already done all encoding.
                    if ((pTokRead - pTokWrite) > 0)
                    {
                        // Bytes still in token buffer.
                        *pWBuf++ = *pTokWrite++;
                    }
                    else
                    {
                        // Token buffer empty. Reset to <Begin-of-Buffer>.
                        pTokRead = pTokWrite = pTokBuffer;

                        // Return.
                        return (pWBuf - pData);
                    }
                }
            }
        }
    }
    return (pWBuf - pData);
}

/*=====================================================================
 *
 * INetMessageDecodeQPStream_Impl Implementation.
 * (Quoted-Printable Decoding)
 *
 *====================================================================*/
static const sal_uInt8 pr2hex[128] = {
    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,

    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,

    0x10, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,

    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10
};

/*
 * INetMessageDecodeQPStream_Impl.
 */
INetMessageDecodeQPStream_Impl::INetMessageDecodeQPStream_Impl (void)
    : INetMessageOStream (),
      eState     (INETMSG_EOL_BEGIN),
      pMsgBuffer (new SvMemoryStream),
      nTokBufLen (0)
{
    ParseHeader (sal_False);
}

/*
 * ~INetMessageDecodeQPStream_Impl.
 */
INetMessageDecodeQPStream_Impl::~INetMessageDecodeQPStream_Impl (void)
{
    delete pMsgBuffer;
}

/*
 * PutMsgLine.
 */
int INetMessageDecodeQPStream_Impl::PutMsgLine (
    const sal_Char *pData, sal_uIntPtr nSize)
{
    INetMessage *pMsg = GetTargetMessage();
    if (pMsg == NULL) return INETSTREAM_STATUS_ERROR;

    SvOpenLockBytes * pLB = PTR_CAST(SvOpenLockBytes, pMsg->GetDocumentLB());
    if (pLB == NULL) return INETSTREAM_STATUS_WOULDBLOCK;

    const sal_Char *pStop = pData + nSize;
    while (pData < pStop)
    {
        if (eState == INETMSG_EOL_FESC)
        {
            *(pTokBuffer + nTokBufLen++) = static_cast< char >(toupper(*pData));
            pData++;
            if (nTokBufLen == 2)
            {
                if ((*pTokBuffer == '\r') || (*pTokBuffer == '\n'))
                {
                    // Soft line break (=<CR><LF>). Emit buffer now.
                    eState = INETMSG_EOL_BEGIN;
                }
                else
                {
                    // Decode token.
                    *pMsgBuffer << sal_uInt8 (
                        (pr2hex[(int)(pTokBuffer[0] & 0x7f)] << 4) |
                        (pr2hex[(int)(pTokBuffer[1] & 0x7f)] & 15)   );

                    // Search for next <CR>.
                    eState = INETMSG_EOL_SCR;
                }

                // Reset token buffer.
                nTokBufLen = 0;
            }
        }
        else if (*pData == '=')
        {
            // Found escape character.
            pData++;
            eState = INETMSG_EOL_FESC;
        }
        else if (eState == INETMSG_EOL_FCR)
        {
            *pMsgBuffer << *pData++;
            eState = INETMSG_EOL_BEGIN;
        }
        else if (*pData == '\r')
        {
            *pMsgBuffer << *pData++;
            eState = INETMSG_EOL_FCR;
        }
        else
        {
            *pMsgBuffer << *pData++;
        }

        if (eState == INETMSG_EOL_BEGIN)
        {
            sal_Size nRead = pMsgBuffer->Tell();
            if (nRead > 0)
            {
                // Emit buffer.
                sal_Size nDocSiz = pMsg->GetDocumentSize();
                sal_Size nWrite  = 0;

                pLB->FillAppend (
                    (sal_Char *)(pMsgBuffer->GetData()), nRead, &nWrite);
                pMsg->SetDocumentSize (nDocSiz + nWrite);

                if (nWrite < nRead) return INETSTREAM_STATUS_ERROR;

                pMsgBuffer->Seek (STREAM_SEEK_TO_BEGIN);
            }
            eState = INETMSG_EOL_SCR;
        }
    }
    return INETSTREAM_STATUS_OK;
}

/*======================================================================
 *
 * INetMessageEncode64Stream_Impl Implementation.
 * (Base64 Encoding)
 *
 *====================================================================*/
static const sal_Char six2pr[64] = {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
};

/*
 * INetMessageEncode64Stream_Impl.
 */
INetMessageEncode64Stream_Impl::INetMessageEncode64Stream_Impl (
    sal_uIntPtr nMsgBufferSize)
    : INetMessageIStream (),
      pMsgStrm   (NULL),
      nMsgBufSiz (nMsgBufferSize),
      nTokBufSiz (80),
      bDone      (sal_False)
{
    GenerateHeader (sal_False);

    pMsgBuffer = new sal_uInt8[nMsgBufSiz];
    pMsgRead = pMsgWrite = pMsgBuffer;

    pTokBuffer = new sal_Char[nTokBufSiz];
    pTokRead = pTokWrite = pTokBuffer;
}

/*
 * ~INetMessageEncode64Stream_Impl.
 */
INetMessageEncode64Stream_Impl::~INetMessageEncode64Stream_Impl (void)
{
    delete pMsgStrm;
    delete [] pMsgBuffer;
    delete [] pTokBuffer;
}

/*
 * GetMsgLine.
 */
int INetMessageEncode64Stream_Impl::GetMsgLine (sal_Char *pData, sal_uIntPtr nSize)
{
    INetMessage *pMsg = GetSourceMessage ();
    if (pMsg == NULL) return INETSTREAM_STATUS_ERROR;

    if (pMsg->GetDocumentLB() == NULL) return 0;
    if (pMsgStrm == NULL) pMsgStrm = new SvStream (pMsg->GetDocumentLB());

    sal_Char *pWBuf = pData;
    while (pWBuf < (pData + nSize))
    {
        // Caller's buffer not yet filled.
        if ((pMsgRead - pMsgWrite) > 0)
        {
            // Bytes still in message buffer.
            if ((pTokRead - pTokBuffer) < 72)
            {
                // Token buffer not yet filled.
                switch ((pTokRead - pTokBuffer) % 4)
                {
                    case 0:
                        *pTokRead++ = six2pr[(int)(*pMsgWrite >> 2)];
                        break;

                    case 1:
                        *pTokRead++ = six2pr[
                            (int)(((*pMsgWrite << 4) & 060) |
                                  (((*(pMsgWrite + 1)) >> 4) & 017))];
                        pMsgWrite++;
                        break;

                    case 2:
                        *pTokRead++ = six2pr[
                            (int)(((*pMsgWrite << 2) & 074) |
                                  (((*(pMsgWrite + 1)) >> 6) & 003))];
                        pMsgWrite++;
                        break;

                    default: // == case 3
                        *pTokRead++ = six2pr[(int)(*pMsgWrite & 077)];
                        pMsgWrite++;
                        break;
                }
            }
            else if ((pTokRead - pTokBuffer) == 72)
            {
                // Maximum line length. Append <CR><LF>.
                *pTokRead++ = '\r';
                *pTokRead++ = '\n';
            }
            else
            {
                if ((pTokRead - pTokWrite) > 0)
                {
                    // Bytes still in token buffer.
                    *pWBuf++ = *pTokWrite++;
                }
                else
                {
                    // Token buffer empty. Reset to <Begin-of-Buffer>.
                    pTokRead = pTokWrite = pTokBuffer;
                }
            }
        }
        else
        {
            // Message buffer empty. Reset to <Begin-of-Buffer>.
            pMsgRead = pMsgWrite = pMsgBuffer;

            // Read next message block.
            sal_uIntPtr nRead = pMsgStrm->Read (pMsgBuffer, nMsgBufSiz);
            if (nRead > 0)
            {
                // Set read pointer.
                pMsgRead = (pMsgBuffer + nRead);
            }
            else
            {
                // Nothing more to read.
                if (!bDone)
                {
                    // Append pad character(s) and final <CR><LF>.
                    switch ((pTokRead - pTokBuffer) % 4)
                    {
                        case 2:
                            *pTokRead++ = '=';
                            // Fall through for 2nd pad character.

                        case 3:
                            *pTokRead++ = '=';
                            break;

                        default:
                            break;
                    }
                    *pTokRead++ = '\r';
                    *pTokRead++ = '\n';

                    // Mark we're done.
                    bDone = sal_True;
                }
                else
                {
                    // Already done all encoding.
                    if ((pTokRead - pTokWrite) > 0)
                    {
                        // Bytes still in token buffer.
                        *pWBuf++ = *pTokWrite++;
                    }
                    else
                    {
                        // Token buffer empty. Reset to <Begin-of-Buffer>.
                        pTokRead = pTokWrite = pTokBuffer;

                        // Reset done flag, if everything has been done.
                        // if (pWBuf == pData) bDone = sal_False;

                        // Return.
                        return (pWBuf - pData);
                    }
                }
            }
        }
    } // while (pWBuf < (pData + nSize))
    return (pWBuf - pData);
}

/*======================================================================
 *
 * INetMessageDecode64Stream_Impl Implementation.
 * (Base64 Decoding)
 *
 *====================================================================*/
static const sal_uInt8 pr2six[256] = {
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,

    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x3E, 0x40, 0x40, 0x40, 0x3F,
    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
    0x3C, 0x3D, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,

    0x40, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
    0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
    0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
    0x17, 0x18, 0x19, 0x40, 0x40, 0x40, 0x40, 0x40,

    0x40, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
    0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
    0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
    0x31, 0x32, 0x33, 0x40, 0x40, 0x40, 0x40, 0x40,

    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,

    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,

    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,

    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
};

/*
 * INetMessageDecode64Stream_Impl.
 */
INetMessageDecode64Stream_Impl::INetMessageDecode64Stream_Impl (
    sal_uIntPtr nMsgBufferSize)
    : INetMessageOStream (),
      eState     (INETMSG_EOL_SCR),
      nMsgBufSiz (nMsgBufferSize)
{
    ParseHeader (sal_False);

    pMsgBuffer = new sal_Char[nMsgBufSiz];
    pMsgRead = pMsgWrite = pMsgBuffer;
}

/*
 * ~INetMessageDecode64Stream_Impl.
 */
INetMessageDecode64Stream_Impl::~INetMessageDecode64Stream_Impl (void)
{
    delete [] pMsgBuffer;
}

/*
 * PutMsgLine.
 */
int INetMessageDecode64Stream_Impl::PutMsgLine (
    const sal_Char *pData, sal_uIntPtr nSize)
{
    INetMessage *pMsg = GetTargetMessage ();
    if (pMsg == NULL) return INETSTREAM_STATUS_ERROR;

    SvOpenLockBytes * pLB = PTR_CAST(SvOpenLockBytes, pMsg->GetDocumentLB());
    if (pLB == NULL) return INETSTREAM_STATUS_WOULDBLOCK;

    const sal_Char *pStop = (pData + nSize);
    while (pData < pStop)
    {
        if (pr2six[(int)(*pData)] > 63)
        {
            /*
             * Character not in base64 alphabet.
             * Check for <End-of-Stream> or Junk.
             */
            if (*pData == '=')
            {
                // Final pad character -> Done.
                sal_Size nDocSiz = pMsg->GetDocumentSize();
                sal_Size nRead   = pMsgWrite - pMsgBuffer;
                sal_Size nWrite  = 0;

                pLB->FillAppend (pMsgBuffer, nRead, &nWrite);
                pMsg->SetDocumentSize (nDocSiz + nWrite);

                if (nWrite < nRead)
                    return INETSTREAM_STATUS_ERROR;
                else
                    return INETSTREAM_STATUS_LOADED;
            }
            else if (eState == INETMSG_EOL_FCR)
            {
                // Skip any line break character.
                if ((*pData == '\r') || (*pData == '\n')) pData++;

                // Store decoded message buffer contents.
                sal_Size nDocSiz = pMsg->GetDocumentSize();
                sal_Size nRead   = pMsgWrite - pMsgBuffer;
                sal_Size nWrite  = 0;

                pLB->FillAppend (pMsgBuffer, nRead, &nWrite);
                pMsg->SetDocumentSize (nDocSiz + nWrite);

                if (nWrite < nRead) return INETSTREAM_STATUS_ERROR;

                // Reset to <Begin-of-Buffer>.
                pMsgWrite = pMsgBuffer;
                eState = INETMSG_EOL_SCR;
            }
            else if ((*pData == '\r') || (*pData == '\n'))
            {
                // Skip any line break character.
                pData++;
                eState = INETMSG_EOL_FCR;
            }
            else
            {
                // Skip any junk character (may be transmission error).
                pData++;
            }
        }
        else
        {
            // Decode any other character into message buffer.
            switch ((pMsgRead - pMsgBuffer) % 4)
            {
                case 0:
                    *pMsgWrite    = (pr2six[(int)(*pData++)] << 2);
                    pMsgRead++;
                    break;

                case 1:
                    *pMsgWrite++ |= (pr2six[(int)(*pData  )] >> 4);
                    *pMsgWrite    = (pr2six[(int)(*pData++)] << 4);
                    pMsgRead++;
                    break;

                case 2:
                    *pMsgWrite++ |= (pr2six[(int)(*pData  )] >> 2);
                    *pMsgWrite    = (pr2six[(int)(*pData++)] << 6);
                    pMsgRead++;
                    break;

                default: // == case 3
                    *pMsgWrite++ |= (pr2six[(int)(*pData++)]);
                    pMsgRead = pMsgBuffer;
                    break;
            } // switch ((pMsgRead - pMsgBuffer) % 4)
        }
    } // while (pData < pStop)
    return INETSTREAM_STATUS_OK;
}

/*=========================================================================
 *
 * INetMIMEMessageStream Implementation.
 *
 *=======================================================================*/
/*
 * INetMIMEMessageStream.
 */
INetMIMEMessageStream::INetMIMEMessageStream (sal_uIntPtr nBufferSize)
    : INetMessageIOStream (nBufferSize),
      eState      (INETMSG_EOL_BEGIN),
      nChildIndex (0),
      pChildStrm  (NULL),
      eEncoding   (INETMSG_ENCODING_BINARY),
      pEncodeStrm (NULL),
      pDecodeStrm (NULL),
      pMsgBuffer  (NULL)
{
}

/*
 * ~INetMIMEMessageStream.
 */
INetMIMEMessageStream::~INetMIMEMessageStream (void)
{
    delete pChildStrm;
    delete pEncodeStrm;
    delete pDecodeStrm;
    delete pMsgBuffer;
}

/*
 * GetMsgEncoding.
 */
INetMessageEncoding
INetMIMEMessageStream::GetMsgEncoding (const String& rContentType)
{
    if ((rContentType.CompareIgnoreCaseToAscii ("message"  , 7) == 0) ||
        (rContentType.CompareIgnoreCaseToAscii ("multipart", 9) == 0)    )
        return INETMSG_ENCODING_7BIT;

    if (rContentType.CompareIgnoreCaseToAscii ("text", 4) == 0)
    {
        if (rContentType.CompareIgnoreCaseToAscii ("text/plain", 10) == 0)
        {
            if (rContentType.GetTokenCount ('=') > 1)
            {
                String aCharset (rContentType.GetToken (1, '='));
                aCharset.EraseLeadingChars (' ');
                aCharset.EraseLeadingChars ('"');

                if (aCharset.CompareIgnoreCaseToAscii ("us-ascii", 8) == 0)
                    return INETMSG_ENCODING_7BIT;
                else
                    return INETMSG_ENCODING_QUOTED;
            }
            else
                return INETMSG_ENCODING_7BIT;
        }
        else
            return INETMSG_ENCODING_QUOTED;
    }

    return INETMSG_ENCODING_BASE64;
}

/*
 * GetMsgLine.
 * (Message Generator).
 */
int INetMIMEMessageStream::GetMsgLine (sal_Char *pData, sal_uIntPtr nSize)
{
    // Check for message container.
    INetMIMEMessage *pMsg = GetSourceMessage();
    if (pMsg == NULL) return INETSTREAM_STATUS_ERROR;

    // Check for header or body.
    if (!IsHeaderGenerated())
    {
        if (eState == INETMSG_EOL_BEGIN)
        {
            // Prepare special header fields.
            if (pMsg->GetParent())
            {
                String aPCT (pMsg->GetParent()->GetContentType());
                if (aPCT.CompareIgnoreCaseToAscii ("message/rfc822", 14) == 0)
                    pMsg->SetMIMEVersion (
                        String(CONSTASCII_STRINGPARAM("1.0")));
                else
                    pMsg->SetMIMEVersion (String());
            }
            else
            {
                pMsg->SetMIMEVersion (String(CONSTASCII_STRINGPARAM("1.0")));
            }

            // Check ContentType.
            String aContentType (pMsg->GetContentType());
            if (aContentType.Len())
            {
                // Determine default Content-Type.
                String aDefaultType;
                pMsg->GetDefaultContentType (aDefaultType);

                if (aDefaultType.CompareIgnoreCaseToAscii (
                    aContentType, aContentType.Len()) == 0)
                {
                    // No need to specify default.
                    pMsg->SetContentType (String());
                }
            }

            // Check Encoding.
            String aEncoding (pMsg->GetContentTransferEncoding());
            if (aEncoding.Len())
            {
                // Use given Encoding.
                if (aEncoding.CompareIgnoreCaseToAscii (
                    "base64", 6) == 0)
                    eEncoding = INETMSG_ENCODING_BASE64;
                else if (aEncoding.CompareIgnoreCaseToAscii (
                    "quoted-printable", 16) == 0)
                    eEncoding = INETMSG_ENCODING_QUOTED;
                else
                    eEncoding = INETMSG_ENCODING_7BIT;
            }
            else
            {
                // Use default Encoding for (given|default) Content-Type.
                if (aContentType.Len() == 0)
                {
                    // Determine default Content-Type.
                    pMsg->GetDefaultContentType (aContentType);
                }
                eEncoding = GetMsgEncoding (aContentType);
            }

            // Set Content-Transfer-Encoding header.
            if (eEncoding == INETMSG_ENCODING_BASE64)
            {
                // Base64.
                pMsg->SetContentTransferEncoding (
                    String(CONSTASCII_STRINGPARAM("base64")));
            }
            else if (eEncoding == INETMSG_ENCODING_QUOTED)
            {
                // Quoted-Printable.
                pMsg->SetContentTransferEncoding (
                    String(CONSTASCII_STRINGPARAM("quoted-printable")));
            }
            else
            {
                // No need to specify default.
                pMsg->SetContentTransferEncoding (String());
            }

            // Mark we're done.
            eState = INETMSG_EOL_DONE;
        }

        // Generate the message header.
        int nRead = INetMessageIOStream::GetMsgLine (pData, nSize);
        if (nRead <= 0)
        {
            // Reset state.
            eState = INETMSG_EOL_BEGIN;
        }
        return nRead;
    }
    else
    {
        // Generate the message body.
        if (pMsg->IsContainer())
        {
            // Encapsulated message body.
            while (eState == INETMSG_EOL_BEGIN)
            {
                if (pChildStrm == NULL)
                {
                    INetMIMEMessage *pChild = pMsg->GetChild (nChildIndex);
                    if (pChild)
                    {
                        // Increment child index.
                        nChildIndex++;

                        // Create child stream.
                        pChildStrm = new INetMIMEMessageStream;
                        pChildStrm->SetSourceMessage (pChild);

                        if (pMsg->IsMultipart())
                        {
                            // Insert multipart delimiter.
                            ByteString aDelim ("--");
                            aDelim += pMsg->GetMultipartBoundary();
                            aDelim += "\r\n";

                            rtl_copyMemory (
                                pData, aDelim.GetBuffer(), aDelim.Len());
                            return aDelim.Len();
                        }
                    }
                    else
                    {
                        // No more parts. Mark we're done.
                        eState = INETMSG_EOL_DONE;
                        nChildIndex = 0;

                        if (pMsg->IsMultipart())
                        {
                            // Insert close delimiter.
                            ByteString aDelim ("--");
                            aDelim += pMsg->GetMultipartBoundary();
                            aDelim += "--\r\n";

                            rtl_copyMemory (
                                pData, aDelim.GetBuffer(), aDelim.Len());
                            return aDelim.Len();
                        }
                    }
                }
                else
                {
                    // Read current child stream.
                    int nRead = pChildStrm->Read (pData, nSize);
                    if (nRead > 0)
                    {
                        return nRead;
                    }
                    else
                    {
                        // Cleanup exhausted child stream.
                        delete pChildStrm;
                        pChildStrm = NULL;
                    }
                }
            }
            return 0;
        }
        else
        {
            // Single part message body.
            if (pMsg->GetDocumentLB() == NULL)
            {
                // Empty message body.
                return 0;
            }
            else
            {
                // Check whether message body needs to be encoded.
                if (eEncoding == INETMSG_ENCODING_7BIT)
                {
                    // No Encoding.
                    return INetMessageIOStream::GetMsgLine (pData, nSize);
                }
                else
                {
                    // Apply appropriate Encoding.
                    while (eState == INETMSG_EOL_BEGIN)
                    {
                        if (pEncodeStrm == NULL)
                        {
                            // Create encoder stream.
                            if (eEncoding == INETMSG_ENCODING_QUOTED)
                            {
                                // Quoted-Printable Encoding.
                                pEncodeStrm
                                 = new INetMessageEncodeQPStream_Impl;
                            }
                            else
                            {
                                // Base64 Encoding.
                                pEncodeStrm
                                 = new INetMessageEncode64Stream_Impl;
                            }
                            pEncodeStrm->SetSourceMessage (pMsg);
                        }

                        // Read encoded message.
                        int nRead = pEncodeStrm->Read (pData, nSize);
                        if (nRead > 0)
                        {
                            return nRead;
                        }
                        else
                        {
                            // Cleanup exhausted encoder stream.
                            delete pEncodeStrm;
                            pEncodeStrm = NULL;

                            // Mark we're done.
                            eState = INETMSG_EOL_DONE;
                        }
                    }
                    return 0;
                }
            }
        }
    }
}

/*
 * PutMsgLine.
 * (Message Parser).
 */
int INetMIMEMessageStream::PutMsgLine (const sal_Char *pData, sal_uIntPtr nSize)
{
    // Check for message container.
    INetMIMEMessage *pMsg = GetTargetMessage();
    if (pMsg == NULL) return INETSTREAM_STATUS_ERROR;

    // Check for header or body.
    if (!IsHeaderParsed())
    {
        // Parse the message header.
        int nRet = INetMessageIOStream::PutMsgLine (pData, nSize);
        return nRet;
    }
    else
    {
        pMsg->SetHeaderParsed();
        // Parse the message body.
        if (pMsg->IsContainer())
        {

            // Content-Transfer-Encoding MUST be "7bit" (RFC1521).
            if (pMsg->IsMessage())
            {
                if( !pChildStrm )
                {
                    // Encapsulated message.
                    pMsg->SetChildCount( pMsg->GetChildCount() + 1);
                    INetMIMEMessage* pNewMessage = new INetMIMEMessage;
                    pNewMessage->SetDocumentLB (
                        new SvAsyncLockBytes(new SvCacheStream, sal_False));
                    pMsg->AttachChild( *pNewMessage, sal_True );

                    // Encapsulated message body. Create message parser stream.
                    pChildStrm = new INetMIMEMessageStream;
                    pChildStrm->SetTargetMessage ( pNewMessage );

                    // Initialize control variables.
                    eState = INETMSG_EOL_BEGIN;
                }
                if ( nSize > 0)
                {
                    // Bytes still in buffer. Put message down-stream.
                    int status = pChildStrm->Write( pData, nSize );
                    if (status != INETSTREAM_STATUS_OK)
                        return status;
                }

                return INetMessageIOStream::PutMsgLine (pData, nSize);
            }
            else
            {

                // Multipart message body. Initialize multipart delimiters.
                // Multipart message.
                if (pMsg->GetMultipartBoundary().Len() == 0)
                {
                    // Determine boundary.
                    ByteString aType (
                        pMsg->GetContentType(), RTL_TEXTENCODING_ASCII_US);
                    ByteString aLowerType (aType);
                    aLowerType.ToLowerAscii();

                    sal_uInt16 nPos = aLowerType.Search ("boundary=");
                    ByteString aBoundary (aType.Copy (nPos + 9));

                    aBoundary.EraseLeadingAndTrailingChars (' ');
                    aBoundary.EraseLeadingAndTrailingChars ('"');

                    // Save boundary.
                    pMsg->SetMultipartBoundary (aBoundary);
                }

                ByteString aPlainDelim (pMsg->GetMultipartBoundary());
                ByteString aDelim ("--");
                aDelim += aPlainDelim;

                ByteString aPlainClose (aPlainDelim);
                aPlainClose += "--";

                ByteString aClose (aDelim);
                aClose += "--";

                if (pMsgBuffer == NULL) pMsgBuffer = new SvMemoryStream;
                pMsgBuffer->Write (pData, nSize);
                sal_uIntPtr nBufSize = pMsgBuffer->Tell();

                const sal_Char* pChar;
                const sal_Char* pOldPos;
                for( pOldPos = pChar = (const sal_Char *) pMsgBuffer->GetData(); nBufSize--;
                     pChar++ )
                {
                    int status;
                    if( *pChar == '\r' || *pChar == '\n' )
                    {
                        if( aDelim.CompareTo (pOldPos, aDelim.Len())
                            != COMPARE_EQUAL &&
                            aClose.CompareTo (pOldPos, aClose.Len())
                            != COMPARE_EQUAL &&
                            aPlainDelim.CompareTo (pOldPos, aPlainDelim.Len())
                            != COMPARE_EQUAL &&
                            aPlainClose.CompareTo(pOldPos, aPlainClose.Len())
                            != COMPARE_EQUAL )
                        {
                            if( nBufSize &&
                                ( pChar[1] == '\r' || pChar[1] == '\n' ) )
                                nBufSize--, pChar++;
                            if( pChildStrm )
                            {
                                status = pChildStrm->Write(
                                    pOldPos, pChar - pOldPos + 1 );
                                if( status != INETSTREAM_STATUS_OK )
                                    return status;
                            }
                            else {
                                DBG_ERRORFILE( "Die Boundary nicht gefunden" );
                            }
                            status = INetMessageIOStream::PutMsgLine(
                                pOldPos, pChar - pOldPos + 1 );
                            if( status != INETSTREAM_STATUS_OK )
                                return status;
                            pOldPos = pChar + 1;
                        }
                        else
                        {
                            if( nBufSize &&
                                ( pChar[1] == '\r' || pChar[1] == '\n' ) )
                                nBufSize--, pChar++;
                            pOldPos = pChar + 1;
                            DELETEZ( pChildStrm );

                            if (aClose.CompareTo (pOldPos, aClose.Len())
                                != COMPARE_EQUAL &&
                                aPlainClose.CompareTo (pOldPos, aClose.Len())
                                != COMPARE_EQUAL )
                            {
                                // Encapsulated message.
                                pMsg->SetChildCount(pMsg->GetChildCount() + 1);
                                INetMIMEMessage* pNewMessage =
                                    new INetMIMEMessage;
                                pNewMessage->SetDocumentLB (
                                    new SvAsyncLockBytes (
                                        new SvCacheStream, sal_False));

                                pMsg->AttachChild( *pNewMessage, sal_True );

                                // Encapsulated message body. Create message parser stream.
                                pChildStrm = new INetMIMEMessageStream;
                                pChildStrm->SetTargetMessage ( pNewMessage );

                                // Initialize control variables.
                            }
                            eState = INETMSG_EOL_BEGIN;
                            status = INetMessageIOStream::PutMsgLine(
                                pOldPos, pChar - pOldPos + 1 );
                            if( status != INETSTREAM_STATUS_OK )
                                return status;
                        }
                    }
                }
                if( pOldPos < pChar )
                {
                    SvMemoryStream* pNewStream = new SvMemoryStream;
                    pNewStream->Write( pOldPos, pChar - pOldPos );
                    SvMemoryStream* pTmp = pMsgBuffer;
                    pMsgBuffer = pNewStream;
                    delete pTmp;
                }
                else
                {
                    pMsgBuffer->Seek( 0L );
                    pMsgBuffer->SetStreamSize( 0 );
                }
                return INETSTREAM_STATUS_OK;
            }
        }
        else
        {
            /*
             * Single part message.
             * Remove any ContentTransferEncoding.
             */
            if (pMsg->GetContentType().Len() == 0)
            {
                String aDefaultCT;
                pMsg->GetDefaultContentType (aDefaultCT);
                pMsg->SetContentType (aDefaultCT);
            }

            if (eEncoding == INETMSG_ENCODING_BINARY)
            {
                String aEncoding (pMsg->GetContentTransferEncoding());
                if (aEncoding.CompareIgnoreCaseToAscii (
                    "base64", 6) == COMPARE_EQUAL)
                    eEncoding = INETMSG_ENCODING_BASE64;
                else if (aEncoding.CompareIgnoreCaseToAscii (
                    "quoted-printable", 16) == COMPARE_EQUAL)
                    eEncoding = INETMSG_ENCODING_QUOTED;
                else
                    eEncoding = INETMSG_ENCODING_7BIT;
            }

            if (eEncoding == INETMSG_ENCODING_7BIT)
            {
                // No decoding necessary.
                return INetMessageIOStream::PutMsgLine (pData, nSize);
            }
            else
            {
                if (pDecodeStrm == NULL)
                {
                    if (eEncoding == INETMSG_ENCODING_QUOTED)
                        pDecodeStrm = new INetMessageDecodeQPStream_Impl;
                    else
                        pDecodeStrm = new INetMessageDecode64Stream_Impl;

                    pDecodeStrm->SetTargetMessage (pMsg);
                }
                return pDecodeStrm->Write (pData, nSize);
            }
        }
    }
}



