1595 lines
51 KiB
C++
1595 lines
51 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* 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 .
|
|
*/
|
|
|
|
#include <comphelper/string.hxx>
|
|
#include <sal/types.h>
|
|
#include <rtl/strbuf.hxx>
|
|
#include <tools/inetmsg.hxx>
|
|
#include <tools/inetstrm.hxx>
|
|
|
|
#include <ctype.h> // toupper
|
|
|
|
inline bool SAL_CALL ascii_isWhitespace( sal_Unicode ch )
|
|
{
|
|
return ((ch <= 0x20) && ch);
|
|
}
|
|
|
|
/** 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;
|
|
bool bDone;
|
|
|
|
virtual int GetMsgLine(sal_Char* pData, sal_uIntPtr nSize);
|
|
|
|
public:
|
|
INetMessageEncodeQPStream_Impl(sal_uIntPtr nMsgBufferSize = 1024);
|
|
virtual ~INetMessageEncodeQPStream_Impl(void);
|
|
};
|
|
|
|
/** 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);
|
|
};
|
|
|
|
/** 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;
|
|
|
|
bool bDone;
|
|
|
|
virtual int GetMsgLine(sal_Char* pData, sal_uIntPtr nSize);
|
|
|
|
public:
|
|
INetMessageEncode64Stream_Impl(sal_uIntPtr nMsgBufferSize = 2048);
|
|
virtual ~INetMessageEncode64Stream_Impl(void);
|
|
};
|
|
|
|
/** 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
|
|
|
|
INetIStream::INetIStream()
|
|
{
|
|
}
|
|
|
|
INetIStream::~INetIStream(void)
|
|
{
|
|
}
|
|
|
|
int INetIStream::Read(sal_Char* pData, sal_uIntPtr nSize)
|
|
{
|
|
return GetData(pData, nSize);
|
|
}
|
|
|
|
// INetOStream
|
|
|
|
INetOStream::INetOStream()
|
|
{
|
|
}
|
|
|
|
INetOStream::~INetOStream(void)
|
|
{
|
|
}
|
|
|
|
int INetOStream::Write(const sal_Char* pData, sal_uIntPtr nSize)
|
|
{
|
|
return PutData(pData, nSize);
|
|
}
|
|
|
|
// INetMessageIStream
|
|
|
|
INetMessageIStream::INetMessageIStream(sal_uIntPtr nBufferSize)
|
|
: pSourceMsg (NULL),
|
|
bHeaderGenerated (false),
|
|
nBufSiz (nBufferSize),
|
|
pMsgStrm (NULL),
|
|
pMsgBuffer (new SvMemoryStream)
|
|
{
|
|
pMsgBuffer->SetStreamCharSet(RTL_TEXTENCODING_ASCII_US);
|
|
pBuffer = new sal_Char[nBufSiz];
|
|
pRead = pWrite = pBuffer;
|
|
}
|
|
|
|
INetMessageIStream::~INetMessageIStream(void)
|
|
{
|
|
delete [] pBuffer;
|
|
delete pMsgBuffer;
|
|
delete pMsgStrm;
|
|
}
|
|
|
|
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 = true;
|
|
*pRead++ = '\r';
|
|
*pRead++ = '\n';
|
|
}
|
|
else
|
|
{
|
|
// Body generated.
|
|
return (pWBuf - pData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (pWBuf - pData);
|
|
}
|
|
|
|
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().getLength())
|
|
{
|
|
// NYI: Folding long lines.
|
|
*pMsgBuffer << aHeader.GetName().getStr();
|
|
*pMsgBuffer << ": ";
|
|
*pMsgBuffer << aHeader.GetValue().getStr();
|
|
*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
|
|
|
|
INetMessageOStream::INetMessageOStream(void)
|
|
: pTargetMsg (NULL),
|
|
bHeaderParsed (false),
|
|
eOState (INETMSG_EOL_BEGIN),
|
|
pMsgBuffer (new SvMemoryStream)
|
|
{
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 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 = 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;
|
|
}
|
|
|
|
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())
|
|
{
|
|
OString aField(pData);
|
|
sal_Int32 nPos = aField.indexOf(':');
|
|
if (nPos != -1)
|
|
{
|
|
OString aName( aField.copy(0, nPos));
|
|
OString aValue( aField.copy(nPos + 1, aField.getLength() - nPos + 1));
|
|
aValue = comphelper::string::stripStart(aValue, ' ');
|
|
|
|
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
|
|
|
|
INetMessageIOStream::INetMessageIOStream(sal_uIntPtr nBufferSize)
|
|
: INetMessageIStream (nBufferSize),
|
|
INetMessageOStream ()
|
|
{
|
|
}
|
|
|
|
INetMessageIOStream::~INetMessageIOStream(void)
|
|
{
|
|
}
|
|
|
|
// INetMessageEncodeQPStream_Impl
|
|
|
|
static const sal_Char hex2pr[16] = {
|
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
|
'A', 'B', 'C', 'D', 'E', 'F'
|
|
};
|
|
|
|
INetMessageEncodeQPStream_Impl::INetMessageEncodeQPStream_Impl( sal_uIntPtr nMsgBufferSize)
|
|
: INetMessageIStream (),
|
|
pMsgStrm (NULL),
|
|
nMsgBufSiz (nMsgBufferSize),
|
|
nTokBufSiz (80),
|
|
eState (INETMSG_EOL_SCR),
|
|
bDone (false)
|
|
{
|
|
GenerateHeader (false);
|
|
|
|
pMsgBuffer = new sal_Char[nMsgBufSiz];
|
|
pMsgRead = pMsgWrite = pMsgBuffer;
|
|
|
|
pTokBuffer = new sal_Char[nTokBufSiz];
|
|
pTokRead = pTokWrite = pTokBuffer;
|
|
}
|
|
|
|
INetMessageEncodeQPStream_Impl::~INetMessageEncodeQPStream_Impl(void)
|
|
{
|
|
delete pMsgStrm;
|
|
delete [] pMsgBuffer;
|
|
delete [] pTokBuffer;
|
|
}
|
|
|
|
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 = 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
|
|
|
|
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(void)
|
|
: INetMessageOStream(),
|
|
eState (INETMSG_EOL_BEGIN),
|
|
pMsgBuffer (new SvMemoryStream),
|
|
nTokBufLen (0)
|
|
{
|
|
ParseHeader(false);
|
|
}
|
|
|
|
INetMessageDecodeQPStream_Impl::~INetMessageDecodeQPStream_Impl(void)
|
|
{
|
|
delete pMsgBuffer;
|
|
}
|
|
|
|
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
|
|
|
|
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(
|
|
sal_uIntPtr nMsgBufferSize)
|
|
: INetMessageIStream(),
|
|
pMsgStrm (NULL),
|
|
nMsgBufSiz (nMsgBufferSize),
|
|
nTokBufSiz (80),
|
|
bDone (false)
|
|
{
|
|
GenerateHeader(false);
|
|
|
|
pMsgBuffer = new sal_uInt8[nMsgBufSiz];
|
|
pMsgRead = pMsgWrite = pMsgBuffer;
|
|
|
|
pTokBuffer = new sal_Char[nTokBufSiz];
|
|
pTokRead = pTokWrite = pTokBuffer;
|
|
}
|
|
|
|
INetMessageEncode64Stream_Impl::~INetMessageEncode64Stream_Impl(void)
|
|
{
|
|
delete pMsgStrm;
|
|
delete [] pMsgBuffer;
|
|
delete [] pTokBuffer;
|
|
}
|
|
|
|
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 = 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 = false;
|
|
|
|
// Return.
|
|
return (pWBuf - pData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // while (pWBuf < (pData + nSize))
|
|
return (pWBuf - pData);
|
|
}
|
|
|
|
// INetMessageDecode64Stream_Impl
|
|
|
|
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(
|
|
sal_uIntPtr nMsgBufferSize)
|
|
: INetMessageOStream(),
|
|
eState (INETMSG_EOL_SCR),
|
|
nMsgBufSiz (nMsgBufferSize)
|
|
{
|
|
ParseHeader(false);
|
|
|
|
pMsgBuffer = new sal_Char[nMsgBufSiz];
|
|
pMsgRead = pMsgWrite = pMsgBuffer;
|
|
}
|
|
|
|
INetMessageDecode64Stream_Impl::~INetMessageDecode64Stream_Impl(void)
|
|
{
|
|
delete [] pMsgBuffer;
|
|
}
|
|
|
|
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
|
|
|
|
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(void)
|
|
{
|
|
delete pChildStrm;
|
|
delete pEncodeStrm;
|
|
delete pDecodeStrm;
|
|
delete pMsgBuffer;
|
|
}
|
|
|
|
INetMessageEncoding
|
|
INetMIMEMessageStream::GetMsgEncoding(const OUString& rContentType)
|
|
{
|
|
if (rContentType.startsWithIgnoreAsciiCase("message") ||
|
|
rContentType.startsWithIgnoreAsciiCase("multipart"))
|
|
{
|
|
return INETMSG_ENCODING_7BIT;
|
|
}
|
|
if (rContentType.startsWithIgnoreAsciiCase("text"))
|
|
{
|
|
if (rContentType.startsWithIgnoreAsciiCase("text/plain"))
|
|
{
|
|
if (comphelper::string::getTokenCount(rContentType, '=') > 1)
|
|
{
|
|
OUString aCharset(rContentType.getToken(1, '='));
|
|
aCharset = comphelper::string::stripStart(aCharset, ' ');
|
|
aCharset = comphelper::string::stripStart(aCharset, '"');
|
|
|
|
if (aCharset.startsWithIgnoreAsciiCase("us-ascii"))
|
|
return INETMSG_ENCODING_7BIT;
|
|
else
|
|
return INETMSG_ENCODING_QUOTED;
|
|
}
|
|
else
|
|
return INETMSG_ENCODING_7BIT;
|
|
}
|
|
else
|
|
return INETMSG_ENCODING_QUOTED;
|
|
}
|
|
|
|
return INETMSG_ENCODING_BASE64;
|
|
}
|
|
|
|
/// 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())
|
|
{
|
|
OUString aPCT(pMsg->GetParent()->GetContentType());
|
|
if (aPCT.startsWithIgnoreAsciiCase("message/rfc822"))
|
|
pMsg->SetMIMEVersion("1.0");
|
|
else
|
|
pMsg->SetMIMEVersion(OUString());
|
|
}
|
|
else
|
|
{
|
|
pMsg->SetMIMEVersion("1.0");
|
|
}
|
|
|
|
// Check ContentType.
|
|
OUString aContentType(pMsg->GetContentType());
|
|
if (!aContentType.isEmpty())
|
|
{
|
|
// Determine default Content-Type.
|
|
OUString aDefaultType = pMsg->GetDefaultContentType();
|
|
|
|
if (aDefaultType.equalsIgnoreAsciiCase(aContentType))
|
|
{
|
|
// No need to specify default.
|
|
pMsg->SetContentType(OUString());
|
|
}
|
|
}
|
|
|
|
// Check Encoding.
|
|
OUString aEncoding(pMsg->GetContentTransferEncoding());
|
|
if (!aEncoding.isEmpty())
|
|
{
|
|
// Use given Encoding.
|
|
if (aEncoding.startsWithIgnoreAsciiCase("base64"))
|
|
{
|
|
eEncoding = INETMSG_ENCODING_BASE64;
|
|
}
|
|
else if (aEncoding.startsWithIgnoreAsciiCase("quoted-printable"))
|
|
{
|
|
eEncoding = INETMSG_ENCODING_QUOTED;
|
|
}
|
|
else
|
|
{
|
|
eEncoding = INETMSG_ENCODING_7BIT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Use default Encoding for (given|default) Content-Type.
|
|
if (aContentType.isEmpty())
|
|
{
|
|
// Determine default Content-Type.
|
|
aContentType = pMsg->GetDefaultContentType();
|
|
}
|
|
eEncoding = GetMsgEncoding(aContentType);
|
|
}
|
|
|
|
// Set Content-Transfer-Encoding header.
|
|
if (eEncoding == INETMSG_ENCODING_BASE64)
|
|
{
|
|
// Base64.
|
|
pMsg->SetContentTransferEncoding("base64");
|
|
}
|
|
else if (eEncoding == INETMSG_ENCODING_QUOTED)
|
|
{
|
|
// Quoted-Printable.
|
|
pMsg->SetContentTransferEncoding("quoted-printable");
|
|
}
|
|
else
|
|
{
|
|
// No need to specify default.
|
|
pMsg->SetContentTransferEncoding(OUString());
|
|
}
|
|
|
|
// 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.
|
|
OStringBuffer aDelim("--");
|
|
aDelim.append(pMsg->GetMultipartBoundary());
|
|
aDelim.append("\r\n");
|
|
|
|
memcpy(pData, aDelim.getStr(),
|
|
aDelim.getLength());
|
|
return aDelim.getLength();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No more parts. Mark we're done.
|
|
eState = INETMSG_EOL_DONE;
|
|
nChildIndex = 0;
|
|
|
|
if (pMsg->IsMultipart())
|
|
{
|
|
// Insert close delimiter.
|
|
OStringBuffer aDelim("--");
|
|
aDelim.append(pMsg->GetMultipartBoundary());
|
|
aDelim.append("--\r\n");
|
|
|
|
memcpy(pData, aDelim.getStr(),
|
|
aDelim.getLength());
|
|
return aDelim.getLength();
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
// Check whether message body needs to be encoded.
|
|
if (eEncoding == INETMSG_ENCODING_7BIT)
|
|
{
|
|
// No Encoding.
|
|
return INetMessageIOStream::GetMsgLine(pData, nSize);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 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.
|
|
INetMIMEMessage* pNewMessage = new INetMIMEMessage;
|
|
pNewMessage->SetDocumentLB( new SvAsyncLockBytes(new SvMemoryStream(), false));
|
|
pMsg->AttachChild( *pNewMessage, 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().getLength() == 0)
|
|
{
|
|
// Determine boundary.
|
|
OString aType(OUStringToOString(
|
|
pMsg->GetContentType(), RTL_TEXTENCODING_ASCII_US));
|
|
OString aLowerType(aType.toAsciiLowerCase());
|
|
|
|
sal_Int32 nPos = aLowerType.indexOf("boundary=");
|
|
OString aBoundary(aType.copy(nPos + 9));
|
|
|
|
aBoundary = comphelper::string::strip(aBoundary, ' ');
|
|
aBoundary = comphelper::string::strip(aBoundary, '"');
|
|
|
|
// Save boundary.
|
|
pMsg->SetMultipartBoundary(aBoundary);
|
|
}
|
|
|
|
OString aPlainDelim(pMsg->GetMultipartBoundary());
|
|
OString aDelim = OStringBuffer("--").
|
|
append(aPlainDelim).
|
|
makeStringAndClear();
|
|
OString aPlainClose = OStringBuffer(
|
|
aPlainDelim).
|
|
append("--").
|
|
makeStringAndClear();
|
|
OString aClose = OStringBuffer(
|
|
aDelim).
|
|
append("--").
|
|
makeStringAndClear();
|
|
|
|
if (pMsgBuffer == NULL) pMsgBuffer = new SvMemoryStream;
|
|
pMsgBuffer->Write(pData, nSize);
|
|
sal_uIntPtr nBufSize = pMsgBuffer->Tell();
|
|
|
|
const sal_Char* pChar;
|
|
const sal_Char* pOldPos;
|
|
int status;
|
|
for( pOldPos = pChar = (const sal_Char*) pMsgBuffer->GetData(); nBufSize--;
|
|
pChar++ )
|
|
{
|
|
if( *pChar == '\r' || *pChar == '\n' )
|
|
{
|
|
if( aDelim.compareTo(pOldPos, aDelim.getLength())
|
|
!= -1 &&
|
|
aClose.compareTo(pOldPos, aClose.getLength())
|
|
!= -1 &&
|
|
aPlainDelim.compareTo(pOldPos, aPlainDelim.getLength())
|
|
!= -1 &&
|
|
aPlainClose.compareTo(pOldPos, aPlainClose.getLength())
|
|
!= -1 )
|
|
{
|
|
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 {
|
|
SAL_WARN( "tools.stream", "Boundary not found." );
|
|
}
|
|
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.getLength())
|
|
!= -1 &&
|
|
aPlainClose.compareTo(pOldPos, aClose.getLength())
|
|
!= -1 )
|
|
{
|
|
// Encapsulated message.
|
|
INetMIMEMessage* pNewMessage =
|
|
new INetMIMEMessage;
|
|
pNewMessage->SetDocumentLB(
|
|
new SvAsyncLockBytes(
|
|
new SvMemoryStream(), false));
|
|
|
|
pMsg->AttachChild( *pNewMessage, 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().isEmpty())
|
|
{
|
|
pMsg->SetContentType(pMsg->GetDefaultContentType());
|
|
}
|
|
|
|
if (eEncoding == INETMSG_ENCODING_BINARY)
|
|
{
|
|
OUString aEncoding(pMsg->GetContentTransferEncoding());
|
|
if (aEncoding.startsWithIgnoreAsciiCase("base64"))
|
|
{
|
|
eEncoding = INETMSG_ENCODING_BASE64;
|
|
}
|
|
else if (aEncoding.startsWithIgnoreAsciiCase("quoted-printable"))
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|