bdef664729
2008/04/01 12:57:26 thb 1.13.84.2: #i85898# Stripping all external header guards 2008/03/28 15:41:21 rt 1.13.84.1: #i87441# Change license header to LPGL v3.
4552 lines
183 KiB
C++
4552 lines
183 KiB
C++
/*************************************************************************
|
|
*
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* Copyright 2008 by Sun Microsystems, Inc.
|
|
*
|
|
* OpenOffice.org - a multi-platform office productivity suite
|
|
*
|
|
* $RCSfile: inetmime.cxx,v $
|
|
* $Revision: 1.14 $
|
|
*
|
|
* This file is part of OpenOffice.org.
|
|
*
|
|
* OpenOffice.org is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License version 3
|
|
* only, as published by the Free Software Foundation.
|
|
*
|
|
* OpenOffice.org is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License version 3 for more details
|
|
* (a copy is included in the LICENSE file that accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* version 3 along with OpenOffice.org. If not, see
|
|
* <http://www.openoffice.org/license.html>
|
|
* for a copy of the LGPLv3 License.
|
|
*
|
|
************************************************************************/
|
|
|
|
// MARKER(update_precomp.py): autogen include statement, do not remove
|
|
#include "precompiled_tools.hxx"
|
|
|
|
#include <cstddef>
|
|
#include <limits>
|
|
|
|
#include "rtl/tencinfo.h"
|
|
#include <tools/datetime.hxx>
|
|
#include <tools/inetmime.hxx>
|
|
|
|
namespace unnamed_tools_inetmime {} using namespace unnamed_tools_inetmime;
|
|
// unnamed namespaces don't work well yet
|
|
|
|
//============================================================================
|
|
namespace unnamed_tools_inetmime {
|
|
|
|
class Charset
|
|
{
|
|
rtl_TextEncoding m_eEncoding;
|
|
const sal_uInt32 * m_pRanges;
|
|
|
|
public:
|
|
inline Charset(rtl_TextEncoding eTheEncoding,
|
|
const sal_uInt32 * pTheRanges);
|
|
|
|
rtl_TextEncoding getEncoding() const { return m_eEncoding; }
|
|
|
|
bool contains(sal_uInt32 nChar) const;
|
|
};
|
|
|
|
inline Charset::Charset(rtl_TextEncoding eTheEncoding,
|
|
const sal_uInt32 * pTheRanges):
|
|
m_eEncoding(eTheEncoding),
|
|
m_pRanges(pTheRanges)
|
|
{
|
|
DBG_ASSERT(m_pRanges, "Charset::Charset(): Bad ranges");
|
|
}
|
|
|
|
//============================================================================
|
|
void appendISO88591(UniString & rText, sal_Char const * pBegin,
|
|
sal_Char const * pEnd);
|
|
|
|
}
|
|
|
|
//============================================================================
|
|
class INetMIMECharsetList_Impl
|
|
{
|
|
struct Node
|
|
{
|
|
Charset m_aCharset;
|
|
bool m_bDisabled;
|
|
Node * m_pNext;
|
|
|
|
inline Node(const Charset & rTheCharset, bool bTheDisabled,
|
|
Node * pTheNext);
|
|
};
|
|
|
|
Node * m_pFirst;
|
|
|
|
public:
|
|
INetMIMECharsetList_Impl(): m_pFirst(0) {}
|
|
|
|
~INetMIMECharsetList_Impl();
|
|
|
|
void prepend(const Charset & rCharset)
|
|
{ m_pFirst = new Node(rCharset, false, m_pFirst); }
|
|
|
|
void includes(sal_uInt32 nChar);
|
|
|
|
rtl_TextEncoding getPreferredEncoding(rtl_TextEncoding eDefault
|
|
= RTL_TEXTENCODING_DONTKNOW)
|
|
const;
|
|
|
|
void reset();
|
|
};
|
|
|
|
inline INetMIMECharsetList_Impl::Node::Node(const Charset & rTheCharset,
|
|
bool bTheDisabled,
|
|
Node * pTheNext):
|
|
m_aCharset(rTheCharset),
|
|
m_bDisabled(bTheDisabled),
|
|
m_pNext(pTheNext)
|
|
{}
|
|
|
|
//============================================================================
|
|
namespace unnamed_tools_inetmime {
|
|
|
|
struct Parameter
|
|
{
|
|
Parameter * m_pNext;
|
|
ByteString m_aAttribute;
|
|
ByteString m_aCharset;
|
|
ByteString m_aLanguage;
|
|
ByteString m_aValue;
|
|
sal_uInt32 m_nSection;
|
|
bool m_bExtended;
|
|
|
|
inline Parameter(Parameter * pTheNext, ByteString const & rTheAttribute,
|
|
ByteString const & rTheCharset,
|
|
ByteString const & rTheLanguage,
|
|
ByteString const & rTheValue, sal_uInt32 nTheSection,
|
|
bool bTheExtended);
|
|
};
|
|
|
|
inline Parameter::Parameter(Parameter * pTheNext,
|
|
ByteString const & rTheAttribute,
|
|
ByteString const & rTheCharset,
|
|
ByteString const & rTheLanguage,
|
|
ByteString const & rTheValue,
|
|
sal_uInt32 nTheSection, bool bTheExtended):
|
|
m_pNext(pTheNext),
|
|
m_aAttribute(rTheAttribute),
|
|
m_aCharset(rTheCharset),
|
|
m_aLanguage(rTheLanguage),
|
|
m_aValue(rTheValue),
|
|
m_nSection(nTheSection),
|
|
m_bExtended(bTheExtended)
|
|
{}
|
|
|
|
//============================================================================
|
|
struct ParameterList
|
|
{
|
|
Parameter * m_pList;
|
|
|
|
ParameterList(): m_pList(0) {}
|
|
|
|
inline ~ParameterList();
|
|
|
|
Parameter ** find(ByteString const & rAttribute, sal_uInt32 nSection,
|
|
bool & rPresent);
|
|
};
|
|
|
|
inline ParameterList::~ParameterList()
|
|
{
|
|
while (m_pList)
|
|
{
|
|
Parameter * pNext = m_pList->m_pNext;
|
|
delete m_pList;
|
|
m_pList = pNext;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
bool parseParameters(ParameterList const & rInput,
|
|
INetContentTypeParameterList * pOutput);
|
|
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// Charset
|
|
//
|
|
//============================================================================
|
|
|
|
bool Charset::contains(sal_uInt32 nChar) const
|
|
{
|
|
for (const sal_uInt32 * p = m_pRanges;;)
|
|
{
|
|
if (nChar < *p++)
|
|
return false;
|
|
if (nChar <= *p++)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// appendISO88591
|
|
//
|
|
//============================================================================
|
|
|
|
namespace unnamed_tools_inetmime {
|
|
|
|
void appendISO88591(UniString & rText, sal_Char const * pBegin,
|
|
sal_Char const * pEnd)
|
|
{
|
|
xub_StrLen nLength = static_cast< xub_StrLen >(pEnd - pBegin);
|
|
sal_Unicode * pBuffer = new sal_Unicode[nLength];
|
|
for (sal_Unicode * p = pBuffer; pBegin != pEnd;)
|
|
*p++ = sal_uChar(*pBegin++);
|
|
rText.Append(pBuffer, nLength);
|
|
delete[] pBuffer;
|
|
}
|
|
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// INetMIMECharsetList_Impl
|
|
//
|
|
//============================================================================
|
|
|
|
INetMIMECharsetList_Impl::~INetMIMECharsetList_Impl()
|
|
{
|
|
while (m_pFirst)
|
|
{
|
|
Node * pRemove = m_pFirst;
|
|
m_pFirst = m_pFirst->m_pNext;
|
|
delete pRemove;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
void INetMIMECharsetList_Impl::includes(sal_uInt32 nChar)
|
|
{
|
|
for (Node * p = m_pFirst; p; p = p->m_pNext)
|
|
if (!(p->m_bDisabled || p->m_aCharset.contains(nChar)))
|
|
p->m_bDisabled = true;
|
|
}
|
|
|
|
//============================================================================
|
|
rtl_TextEncoding
|
|
INetMIMECharsetList_Impl::getPreferredEncoding(rtl_TextEncoding eDefault)
|
|
const
|
|
{
|
|
for (Node * p = m_pFirst; p; p = p->m_pNext)
|
|
if (!p->m_bDisabled)
|
|
return p->m_aCharset.getEncoding();
|
|
return eDefault;
|
|
}
|
|
|
|
//============================================================================
|
|
void INetMIMECharsetList_Impl::reset()
|
|
{
|
|
for (Node * p = m_pFirst; p; p = p->m_pNext)
|
|
p->m_bDisabled = false;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// ParameterList
|
|
//
|
|
//============================================================================
|
|
|
|
Parameter ** ParameterList::find(ByteString const & rAttribute,
|
|
sal_uInt32 nSection, bool & rPresent)
|
|
{
|
|
Parameter ** p = &m_pList;
|
|
for (; *p; p = &(*p)->m_pNext)
|
|
{
|
|
StringCompare eCompare = rAttribute.CompareTo((*p)->m_aAttribute);
|
|
if (eCompare == COMPARE_GREATER)
|
|
break;
|
|
else if (eCompare == COMPARE_EQUAL)
|
|
if (nSection > (*p)->m_nSection)
|
|
break;
|
|
else if (nSection == (*p)->m_nSection)
|
|
{
|
|
rPresent = true;
|
|
return p;
|
|
}
|
|
}
|
|
rPresent = false;
|
|
return p;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// parseParameters
|
|
//
|
|
//============================================================================
|
|
|
|
namespace unnamed_tools_inetmime {
|
|
|
|
bool parseParameters(ParameterList const & rInput,
|
|
INetContentTypeParameterList * pOutput)
|
|
{
|
|
if (pOutput)
|
|
pOutput->Clear();
|
|
|
|
Parameter * pPrev = 0;
|
|
for (Parameter * p = rInput.m_pList; p; p = p->m_pNext)
|
|
{
|
|
if (p->m_nSection > 0
|
|
&& (!pPrev
|
|
|| pPrev->m_nSection != p->m_nSection - 1
|
|
|| pPrev->m_aAttribute != p->m_aAttribute))
|
|
return false;
|
|
pPrev = p;
|
|
}
|
|
|
|
if (pOutput)
|
|
for (Parameter * p = rInput.m_pList; p;)
|
|
{
|
|
bool bCharset = p->m_aCharset.Len() != 0;
|
|
rtl_TextEncoding eEncoding = RTL_TEXTENCODING_DONTKNOW;
|
|
if (bCharset)
|
|
eEncoding
|
|
= INetMIME::getCharsetEncoding(p->m_aCharset.GetBuffer(),
|
|
p->m_aCharset.GetBuffer()
|
|
+ rInput.m_pList->
|
|
m_aCharset.
|
|
Len());
|
|
UniString aValue;
|
|
bool bBadEncoding = false;
|
|
Parameter * pNext = p;
|
|
do
|
|
{
|
|
sal_Size nSize;
|
|
sal_Unicode * pUnicode
|
|
= INetMIME::convertToUnicode(pNext->m_aValue.GetBuffer(),
|
|
pNext->m_aValue.GetBuffer()
|
|
+ pNext->m_aValue.Len(),
|
|
bCharset && p->m_bExtended ?
|
|
eEncoding :
|
|
RTL_TEXTENCODING_UTF8,
|
|
nSize);
|
|
if (!pUnicode && !(bCharset && p->m_bExtended))
|
|
pUnicode = INetMIME::convertToUnicode(
|
|
pNext->m_aValue.GetBuffer(),
|
|
pNext->m_aValue.GetBuffer()
|
|
+ pNext->m_aValue.Len(),
|
|
RTL_TEXTENCODING_ISO_8859_1, nSize);
|
|
if (!pUnicode)
|
|
{
|
|
bBadEncoding = true;
|
|
break;
|
|
}
|
|
aValue += UniString(pUnicode, static_cast< xub_StrLen >(nSize));
|
|
delete[] pUnicode;
|
|
pNext = pNext->m_pNext;
|
|
}
|
|
while (pNext && pNext->m_nSection > 0);
|
|
if (bBadEncoding)
|
|
{
|
|
aValue.Erase();
|
|
for (pNext = p;;)
|
|
{
|
|
if (pNext->m_bExtended)
|
|
for (xub_StrLen i = 0; i < pNext->m_aValue.Len(); ++i)
|
|
aValue += sal_Unicode(
|
|
sal_Unicode(
|
|
sal_uChar(pNext->m_aValue.GetChar(i)))
|
|
| 0xF800);
|
|
else
|
|
for (xub_StrLen i = 0; i < pNext->m_aValue.Len(); ++i)
|
|
aValue
|
|
+= sal_Unicode(sal_uChar
|
|
(pNext->
|
|
m_aValue.GetChar(i)));
|
|
pNext = pNext->m_pNext;
|
|
if (!pNext || pNext->m_nSection == 0)
|
|
break;
|
|
};
|
|
}
|
|
pOutput->Insert(new INetContentTypeParameter(p->m_aAttribute,
|
|
p->m_aCharset,
|
|
p->m_aLanguage,
|
|
aValue,
|
|
!bBadEncoding),
|
|
LIST_APPEND);
|
|
p = pNext;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// INetMIME
|
|
//
|
|
//============================================================================
|
|
|
|
// static
|
|
bool INetMIME::isAtomChar(sal_uInt32 nChar)
|
|
{
|
|
static const bool aMap[128]
|
|
= { false, false, false, false, false, false, false, false,
|
|
false, false, false, false, false, false, false, false,
|
|
false, false, false, false, false, false, false, false,
|
|
false, false, false, false, false, false, false, false,
|
|
false, true, false, true, true, true, true, true, // !"#$%&'
|
|
false, false, true, true, false, true, false, true, //()*+,-./
|
|
true, true, true, true, true, true, true, true, //01234567
|
|
true, true, false, false, false, true, false, true, //89:;<=>?
|
|
false, true, true, true, true, true, true, true, //@ABCDEFG
|
|
true, true, true, true, true, true, true, true, //HIJKLMNO
|
|
true, true, true, true, true, true, true, true, //PQRSTUVW
|
|
true, true, true, false, false, false, true, true, //XYZ[\]^_
|
|
true, true, true, true, true, true, true, true, //`abcdefg
|
|
true, true, true, true, true, true, true, true, //hijklmno
|
|
true, true, true, true, true, true, true, true, //pqrstuvw
|
|
true, true, true, true, true, true, true, false //xyz{|}~
|
|
};
|
|
return isUSASCII(nChar) && aMap[nChar];
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
bool INetMIME::isTokenChar(sal_uInt32 nChar)
|
|
{
|
|
static const sal_Char aMap[128]
|
|
= { false, false, false, false, false, false, false, false,
|
|
false, false, false, false, false, false, false, false,
|
|
false, false, false, false, false, false, false, false,
|
|
false, false, false, false, false, false, false, false,
|
|
false, true, false, true, true, true, true, true, // !"#$%&'
|
|
false, false, true, true, false, true, true, false, //()*+,-./
|
|
true, true, true, true, true, true, true, true, //01234567
|
|
true, true, false, false, false, false, false, false, //89:;<=>?
|
|
false, true, true, true, true, true, true, true, //@ABCDEFG
|
|
true, true, true, true, true, true, true, true, //HIJKLMNO
|
|
true, true, true, true, true, true, true, true, //PQRSTUVW
|
|
true, true, true, false, false, false, true, true, //XYZ[\]^_
|
|
true, true, true, true, true, true, true, true, //`abcdefg
|
|
true, true, true, true, true, true, true, true, //hijklmno
|
|
true, true, true, true, true, true, true, true, //pqrstuvw
|
|
true, true, true, true, true, true, true, false //xyz{|}~
|
|
};
|
|
return isUSASCII(nChar) && aMap[nChar];
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
bool INetMIME::isEncodedWordTokenChar(sal_uInt32 nChar)
|
|
{
|
|
static const sal_Char aMap[128]
|
|
= { false, false, false, false, false, false, false, false,
|
|
false, false, false, false, false, false, false, false,
|
|
false, false, false, false, false, false, false, false,
|
|
false, false, false, false, false, false, false, false,
|
|
false, true, false, true, true, true, true, true, // !"#$%&'
|
|
false, false, true, true, false, true, false, false, //()*+,-./
|
|
true, true, true, true, true, true, true, true, //01234567
|
|
true, true, false, false, false, false, false, false, //89:;<=>?
|
|
false, true, true, true, true, true, true, true, //@ABCDEFG
|
|
true, true, true, true, true, true, true, true, //HIJKLMNO
|
|
true, true, true, true, true, true, true, true, //PQRSTUVW
|
|
true, true, true, false, false, false, true, true, //XYZ[\]^_
|
|
true, true, true, true, true, true, true, true, //`abcdefg
|
|
true, true, true, true, true, true, true, true, //hijklmno
|
|
true, true, true, true, true, true, true, true, //pqrstuvw
|
|
true, true, true, true, true, true, true, false //xyz{|}~
|
|
};
|
|
return isUSASCII(nChar) && aMap[nChar];
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
bool INetMIME::isIMAPAtomChar(sal_uInt32 nChar)
|
|
{
|
|
static const sal_Char aMap[128]
|
|
= { false, false, false, false, false, false, false, false,
|
|
false, false, false, false, false, false, false, false,
|
|
false, false, false, false, false, false, false, false,
|
|
false, false, false, false, false, false, false, false,
|
|
false, true, false, true, true, false, true, true, // !"#$%&'
|
|
false, false, false, true, true, true, true, true, //()*+,-./
|
|
true, true, true, true, true, true, true, true, //01234567
|
|
true, true, true, true, true, true, true, true, //89:;<=>?
|
|
true, true, true, true, true, true, true, true, //@ABCDEFG
|
|
true, true, true, true, true, true, true, true, //HIJKLMNO
|
|
true, true, true, true, true, true, true, true, //PQRSTUVW
|
|
true, true, true, true, false, true, true, true, //XYZ[\]^_
|
|
true, true, true, true, true, true, true, true, //`abcdefg
|
|
true, true, true, true, true, true, true, true, //hijklmno
|
|
true, true, true, true, true, true, true, true, //pqrstuvw
|
|
true, true, true, false, true, true, true, false //xyz{|}~
|
|
};
|
|
return isUSASCII(nChar) && aMap[nChar];
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
sal_uInt32 INetMIME::getDigit(int nWeight)
|
|
{
|
|
DBG_ASSERT(nWeight >= 0 && nWeight < 10,
|
|
"INetMIME::getDigit(): Bad weight");
|
|
|
|
static const sal_Char aDigits[16]
|
|
= { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
|
|
return aDigits[nWeight];
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
sal_uInt32 INetMIME::getHexDigit(int nWeight)
|
|
{
|
|
DBG_ASSERT(nWeight >= 0 && nWeight < 16,
|
|
"INetMIME::getHexDigit(): Bad weight");
|
|
|
|
static const sal_Char aDigits[16]
|
|
= { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
|
|
'D', 'E', 'F' };
|
|
return aDigits[nWeight];
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
sal_uInt32 INetMIME::getBase64Digit(int nWeight)
|
|
{
|
|
DBG_ASSERT(nWeight >= 0 && nWeight < 64,
|
|
"INetMIME::getBase64Digit(): Bad weight");
|
|
|
|
static const sal_Char aDigits[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', '+', '/' };
|
|
return aDigits[nWeight];
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
bool INetMIME::equalIgnoreCase(const sal_Char * pBegin1,
|
|
const sal_Char * pEnd1,
|
|
const sal_Char * pBegin2,
|
|
const sal_Char * pEnd2)
|
|
{
|
|
DBG_ASSERT(pBegin1 && pBegin1 <= pEnd1 && pBegin2 && pBegin2 <= pEnd2,
|
|
"INetMIME::equalIgnoreCase(): Bad sequences");
|
|
|
|
if (pEnd1 - pBegin1 != pEnd2 - pBegin2)
|
|
return false;
|
|
while (pBegin1 != pEnd1)
|
|
if (toUpperCase(*pBegin1++) != toUpperCase(*pBegin2++))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
bool INetMIME::equalIgnoreCase(const sal_Char * pBegin1,
|
|
const sal_Char * pEnd1,
|
|
const sal_Char * pString2)
|
|
{
|
|
DBG_ASSERT(pBegin1 && pBegin1 <= pEnd1 && pString2,
|
|
"INetMIME::equalIgnoreCase(): Bad sequences");
|
|
|
|
while (*pString2 != 0)
|
|
if (pBegin1 == pEnd1
|
|
|| toUpperCase(*pBegin1++) != toUpperCase(*pString2++))
|
|
return false;
|
|
return pBegin1 == pEnd1;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
bool INetMIME::equalIgnoreCase(const sal_Unicode * pBegin1,
|
|
const sal_Unicode * pEnd1,
|
|
const sal_Char * pString2)
|
|
{
|
|
DBG_ASSERT(pBegin1 && pBegin1 <= pEnd1 && pString2,
|
|
"INetMIME::equalIgnoreCase(): Bad sequences");
|
|
|
|
while (*pString2 != 0)
|
|
if (pBegin1 == pEnd1
|
|
|| toUpperCase(*pBegin1++) != toUpperCase(*pString2++))
|
|
return false;
|
|
return pBegin1 == pEnd1;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
const sal_Char * INetMIME::skipLinearWhiteSpace(const sal_Char * pBegin,
|
|
const sal_Char * pEnd)
|
|
{
|
|
DBG_ASSERT(pBegin && pBegin <= pEnd,
|
|
"INetMIME::skipLinearWhiteSpace(): Bad sequence");
|
|
|
|
while (pBegin != pEnd)
|
|
switch (*pBegin)
|
|
{
|
|
case '\t':
|
|
case ' ':
|
|
++pBegin;
|
|
break;
|
|
|
|
case 0x0D: // CR
|
|
if (startsWithLineFolding(pBegin, pEnd))
|
|
pBegin += 3;
|
|
else
|
|
return pBegin;
|
|
break;
|
|
|
|
default:
|
|
return pBegin;
|
|
}
|
|
return pBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
const sal_Unicode * INetMIME::skipLinearWhiteSpace(const sal_Unicode * pBegin,
|
|
const sal_Unicode * pEnd)
|
|
{
|
|
DBG_ASSERT(pBegin && pBegin <= pEnd,
|
|
"INetMIME::skipLinearWhiteSpace(): Bad sequence");
|
|
|
|
while (pBegin != pEnd)
|
|
switch (*pBegin)
|
|
{
|
|
case '\t':
|
|
case ' ':
|
|
++pBegin;
|
|
break;
|
|
|
|
case 0x0D: // CR
|
|
if (startsWithLineFolding(pBegin, pEnd))
|
|
pBegin += 3;
|
|
else
|
|
return pBegin;
|
|
break;
|
|
|
|
default:
|
|
return pBegin;
|
|
}
|
|
return pBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
const sal_Char * INetMIME::skipComment(const sal_Char * pBegin,
|
|
const sal_Char * pEnd)
|
|
{
|
|
DBG_ASSERT(pBegin && pBegin <= pEnd,
|
|
"INetMIME::skipComment(): Bad sequence");
|
|
|
|
if (pBegin != pEnd && *pBegin == '(')
|
|
{
|
|
sal_uInt32 nLevel = 0;
|
|
for (const sal_Char * p = pBegin; p != pEnd;)
|
|
switch (*p++)
|
|
{
|
|
case '(':
|
|
++nLevel;
|
|
break;
|
|
|
|
case ')':
|
|
if (--nLevel == 0)
|
|
return p;
|
|
break;
|
|
|
|
case '\\':
|
|
if (p != pEnd)
|
|
++p;
|
|
break;
|
|
}
|
|
}
|
|
return pBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
const sal_Unicode * INetMIME::skipComment(const sal_Unicode * pBegin,
|
|
const sal_Unicode * pEnd)
|
|
{
|
|
DBG_ASSERT(pBegin && pBegin <= pEnd,
|
|
"INetMIME::skipComment(): Bad sequence");
|
|
|
|
if (pBegin != pEnd && *pBegin == '(')
|
|
{
|
|
sal_uInt32 nLevel = 0;
|
|
for (const sal_Unicode * p = pBegin; p != pEnd;)
|
|
switch (*p++)
|
|
{
|
|
case '(':
|
|
++nLevel;
|
|
break;
|
|
|
|
case ')':
|
|
if (--nLevel == 0)
|
|
return p;
|
|
break;
|
|
|
|
case '\\':
|
|
if (p != pEnd)
|
|
++p;
|
|
break;
|
|
}
|
|
}
|
|
return pBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
const sal_Char * INetMIME::skipLinearWhiteSpaceComment(const sal_Char *
|
|
pBegin,
|
|
const sal_Char * pEnd)
|
|
{
|
|
DBG_ASSERT(pBegin && pBegin <= pEnd,
|
|
"INetMIME::skipLinearWhiteSpaceComment(): Bad sequence");
|
|
|
|
while (pBegin != pEnd)
|
|
switch (*pBegin)
|
|
{
|
|
case '\t':
|
|
case ' ':
|
|
++pBegin;
|
|
break;
|
|
|
|
case 0x0D: // CR
|
|
if (startsWithLineFolding(pBegin, pEnd))
|
|
pBegin += 3;
|
|
else
|
|
return pBegin;
|
|
break;
|
|
|
|
case '(':
|
|
{
|
|
const sal_Char * p = skipComment(pBegin, pEnd);
|
|
if (p == pBegin)
|
|
return pBegin;
|
|
pBegin = p;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return pBegin;
|
|
}
|
|
return pBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
const sal_Unicode * INetMIME::skipLinearWhiteSpaceComment(const sal_Unicode *
|
|
pBegin,
|
|
const sal_Unicode *
|
|
pEnd)
|
|
{
|
|
DBG_ASSERT(pBegin && pBegin <= pEnd,
|
|
"INetMIME::skipLinearWhiteSpaceComment(): Bad sequence");
|
|
|
|
while (pBegin != pEnd)
|
|
switch (*pBegin)
|
|
{
|
|
case '\t':
|
|
case ' ':
|
|
++pBegin;
|
|
break;
|
|
|
|
case 0x0D: // CR
|
|
if (startsWithLineFolding(pBegin, pEnd))
|
|
pBegin += 3;
|
|
else
|
|
return pBegin;
|
|
break;
|
|
|
|
case '(':
|
|
{
|
|
const sal_Unicode * p = skipComment(pBegin, pEnd);
|
|
if (p == pBegin)
|
|
return pBegin;
|
|
pBegin = p;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return pBegin;
|
|
}
|
|
return pBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
const sal_Char * INetMIME::skipQuotedString(const sal_Char * pBegin,
|
|
const sal_Char * pEnd)
|
|
{
|
|
DBG_ASSERT(pBegin && pBegin <= pEnd,
|
|
"INetMIME::skipQuotedString(): Bad sequence");
|
|
|
|
if (pBegin != pEnd && *pBegin == '"')
|
|
for (const sal_Char * p = pBegin + 1; p != pEnd;)
|
|
switch (*p++)
|
|
{
|
|
case 0x0D: // CR
|
|
if (pEnd - p < 2 || *p++ != 0x0A // LF
|
|
|| !isWhiteSpace(*p++))
|
|
return pBegin;
|
|
break;
|
|
|
|
case '"':
|
|
return p;
|
|
|
|
case '\\':
|
|
if (p != pEnd)
|
|
++p;
|
|
break;
|
|
}
|
|
return pBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
const sal_Unicode * INetMIME::skipQuotedString(const sal_Unicode * pBegin,
|
|
const sal_Unicode * pEnd)
|
|
{
|
|
DBG_ASSERT(pBegin && pBegin <= pEnd,
|
|
"INetMIME::skipQuotedString(): Bad sequence");
|
|
|
|
if (pBegin != pEnd && *pBegin == '"')
|
|
for (const sal_Unicode * p = pBegin + 1; p != pEnd;)
|
|
switch (*p++)
|
|
{
|
|
case 0x0D: // CR
|
|
if (pEnd - p < 2 || *p++ != 0x0A // LF
|
|
|| !isWhiteSpace(*p++))
|
|
return pBegin;
|
|
break;
|
|
|
|
case '"':
|
|
return p;
|
|
|
|
case '\\':
|
|
if (p != pEnd)
|
|
++p;
|
|
break;
|
|
}
|
|
return pBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
const sal_Char * INetMIME::scanAtom(const sal_Char * pBegin,
|
|
const sal_Char * pEnd)
|
|
{
|
|
while (pBegin != pEnd && isAtomChar(*pBegin))
|
|
++pBegin;
|
|
return pBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
const sal_Unicode * INetMIME::scanAtom(const sal_Unicode * pBegin,
|
|
const sal_Unicode * pEnd)
|
|
{
|
|
while (pBegin != pEnd && isAtomChar(*pBegin))
|
|
++pBegin;
|
|
return pBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
bool INetMIME::scanUnsigned(const sal_Char *& rBegin, const sal_Char * pEnd,
|
|
bool bLeadingZeroes, sal_uInt32 & rValue)
|
|
{
|
|
sal_uInt64 nTheValue = 0;
|
|
const sal_Char * p = rBegin;
|
|
for ( ; p != pEnd; ++p)
|
|
{
|
|
int nWeight = getWeight(*p);
|
|
if (nWeight < 0)
|
|
break;
|
|
nTheValue = 10 * nTheValue + nWeight;
|
|
if (nTheValue > std::numeric_limits< sal_uInt32 >::max())
|
|
return false;
|
|
}
|
|
if (nTheValue == 0 && (p == rBegin || !bLeadingZeroes && p - rBegin != 1))
|
|
return false;
|
|
rBegin = p;
|
|
rValue = sal_uInt32(nTheValue);
|
|
return true;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
bool INetMIME::scanUnsigned(const sal_Unicode *& rBegin,
|
|
const sal_Unicode * pEnd, bool bLeadingZeroes,
|
|
sal_uInt32 & rValue)
|
|
{
|
|
sal_uInt64 nTheValue = 0;
|
|
const sal_Unicode * p = rBegin;
|
|
for ( ; p != pEnd; ++p)
|
|
{
|
|
int nWeight = getWeight(*p);
|
|
if (nWeight < 0)
|
|
break;
|
|
nTheValue = 10 * nTheValue + nWeight;
|
|
if (nTheValue > std::numeric_limits< sal_uInt32 >::max())
|
|
return false;
|
|
}
|
|
if (nTheValue == 0 && (p == rBegin || !bLeadingZeroes && p - rBegin != 1))
|
|
return false;
|
|
rBegin = p;
|
|
rValue = sal_uInt32(nTheValue);
|
|
return true;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
bool INetMIME::scanUnsignedHex(const sal_Char *& rBegin,
|
|
const sal_Char * pEnd, bool bLeadingZeroes,
|
|
sal_uInt32 & rValue)
|
|
{
|
|
sal_uInt64 nTheValue = 0;
|
|
const sal_Char * p = rBegin;
|
|
for ( p = rBegin; p != pEnd; ++p)
|
|
{
|
|
int nWeight = getHexWeight(*p);
|
|
if (nWeight < 0)
|
|
break;
|
|
nTheValue = nTheValue << 4 | nWeight;
|
|
if (nTheValue > std::numeric_limits< sal_uInt32 >::max())
|
|
return false;
|
|
}
|
|
if (nTheValue == 0 && (p == rBegin || !bLeadingZeroes && p - rBegin != 1))
|
|
return false;
|
|
rBegin = p;
|
|
rValue = sal_uInt32(nTheValue);
|
|
return true;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
bool INetMIME::scanUnsignedHex(const sal_Unicode *& rBegin,
|
|
const sal_Unicode * pEnd, bool bLeadingZeroes,
|
|
sal_uInt32 & rValue)
|
|
{
|
|
sal_uInt64 nTheValue = 0;
|
|
const sal_Unicode * p = rBegin;
|
|
for ( ; p != pEnd; ++p)
|
|
{
|
|
int nWeight = getHexWeight(*p);
|
|
if (nWeight < 0)
|
|
break;
|
|
nTheValue = nTheValue << 4 | nWeight;
|
|
if (nTheValue > std::numeric_limits< sal_uInt32 >::max())
|
|
return false;
|
|
}
|
|
if (nTheValue == 0 && (p == rBegin || !bLeadingZeroes && p - rBegin != 1))
|
|
return false;
|
|
rBegin = p;
|
|
rValue = sal_uInt32(nTheValue);
|
|
return true;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
const sal_Char * INetMIME::scanQuotedBlock(const sal_Char * pBegin,
|
|
const sal_Char * pEnd,
|
|
sal_uInt32 nOpening,
|
|
sal_uInt32 nClosing,
|
|
sal_Size & rLength,
|
|
bool & rModify)
|
|
{
|
|
DBG_ASSERT(pBegin && pBegin <= pEnd,
|
|
"INetMIME::scanQuotedBlock(): Bad sequence");
|
|
|
|
if (pBegin != pEnd && static_cast< unsigned char >(*pBegin) == nOpening)
|
|
{
|
|
++rLength;
|
|
++pBegin;
|
|
while (pBegin != pEnd)
|
|
if (static_cast< unsigned char >(*pBegin) == nClosing)
|
|
{
|
|
++rLength;
|
|
return ++pBegin;
|
|
}
|
|
else
|
|
{
|
|
sal_uInt32 c = *pBegin++;
|
|
switch (c)
|
|
{
|
|
case 0x0D: // CR
|
|
if (pBegin != pEnd && *pBegin == 0x0A) // LF
|
|
if (pEnd - pBegin >= 2 && isWhiteSpace(pBegin[1]))
|
|
{
|
|
++rLength;
|
|
rModify = true;
|
|
pBegin += 2;
|
|
}
|
|
else
|
|
{
|
|
rLength += 3;
|
|
rModify = true;
|
|
++pBegin;
|
|
}
|
|
else
|
|
++rLength;
|
|
break;
|
|
|
|
case '\\':
|
|
++rLength;
|
|
if (pBegin != pEnd)
|
|
if (startsWithLineBreak(pBegin, pEnd)
|
|
&& (pEnd - pBegin < 3
|
|
|| !isWhiteSpace(pBegin[2])))
|
|
{
|
|
rLength += 3;
|
|
rModify = true;
|
|
pBegin += 2;
|
|
}
|
|
else
|
|
++pBegin;
|
|
break;
|
|
|
|
default:
|
|
++rLength;
|
|
if (!isUSASCII(c))
|
|
rModify = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return pBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
const sal_Unicode * INetMIME::scanQuotedBlock(const sal_Unicode * pBegin,
|
|
const sal_Unicode * pEnd,
|
|
sal_uInt32 nOpening,
|
|
sal_uInt32 nClosing,
|
|
sal_Size & rLength,
|
|
bool & rModify)
|
|
{
|
|
DBG_ASSERT(pBegin && pBegin <= pEnd,
|
|
"INetMIME::scanQuotedBlock(): Bad sequence");
|
|
|
|
if (pBegin != pEnd && *pBegin == nOpening)
|
|
{
|
|
++rLength;
|
|
++pBegin;
|
|
while (pBegin != pEnd)
|
|
if (*pBegin == nClosing)
|
|
{
|
|
++rLength;
|
|
return ++pBegin;
|
|
}
|
|
else
|
|
{
|
|
sal_uInt32 c = *pBegin++;
|
|
switch (c)
|
|
{
|
|
case 0x0D: // CR
|
|
if (pBegin != pEnd && *pBegin == 0x0A) // LF
|
|
if (pEnd - pBegin >= 2 && isWhiteSpace(pBegin[1]))
|
|
{
|
|
++rLength;
|
|
rModify = true;
|
|
pBegin += 2;
|
|
}
|
|
else
|
|
{
|
|
rLength += 3;
|
|
rModify = true;
|
|
++pBegin;
|
|
}
|
|
else
|
|
++rLength;
|
|
break;
|
|
|
|
case '\\':
|
|
++rLength;
|
|
if (pBegin != pEnd)
|
|
if (startsWithLineBreak(pBegin, pEnd)
|
|
&& (pEnd - pBegin < 3
|
|
|| !isWhiteSpace(pBegin[2])))
|
|
{
|
|
rLength += 3;
|
|
rModify = true;
|
|
pBegin += 2;
|
|
}
|
|
else
|
|
++pBegin;
|
|
break;
|
|
|
|
default:
|
|
++rLength;
|
|
if (!isUSASCII(c))
|
|
rModify = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return pBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
sal_Char const * INetMIME::scanParameters(sal_Char const * pBegin,
|
|
sal_Char const * pEnd,
|
|
INetContentTypeParameterList *
|
|
pParameters)
|
|
{
|
|
ParameterList aList;
|
|
sal_Char const * pParameterBegin = pBegin;
|
|
for (sal_Char const * p = pParameterBegin;; pParameterBegin = p)
|
|
{
|
|
pParameterBegin = skipLinearWhiteSpaceComment(p, pEnd);
|
|
if (pParameterBegin == pEnd || *pParameterBegin != ';')
|
|
break;
|
|
p = pParameterBegin + 1;
|
|
|
|
sal_Char const * pAttributeBegin = skipLinearWhiteSpaceComment(p,
|
|
pEnd);
|
|
p = pAttributeBegin;
|
|
bool bDowncaseAttribute = false;
|
|
while (p != pEnd && isTokenChar(*p) && *p != '*')
|
|
{
|
|
bDowncaseAttribute = bDowncaseAttribute || isUpperCase(*p);
|
|
++p;
|
|
}
|
|
if (p == pAttributeBegin)
|
|
break;
|
|
ByteString aAttribute(
|
|
pAttributeBegin, static_cast< xub_StrLen >(p - pAttributeBegin));
|
|
if (bDowncaseAttribute)
|
|
aAttribute.ToLowerAscii();
|
|
|
|
sal_uInt32 nSection = 0;
|
|
if (p != pEnd && *p == '*')
|
|
{
|
|
++p;
|
|
if (p != pEnd && isDigit(*p)
|
|
&& !scanUnsigned(p, pEnd, false, nSection))
|
|
break;
|
|
}
|
|
|
|
bool bPresent;
|
|
Parameter ** pPos = aList.find(aAttribute, nSection, bPresent);
|
|
if (bPresent)
|
|
break;
|
|
|
|
bool bExtended = false;
|
|
if (p != pEnd && *p == '*')
|
|
{
|
|
++p;
|
|
bExtended = true;
|
|
}
|
|
|
|
p = skipLinearWhiteSpaceComment(p, pEnd);
|
|
|
|
if (p == pEnd || *p != '=')
|
|
break;
|
|
|
|
p = skipLinearWhiteSpaceComment(p + 1, pEnd);
|
|
|
|
ByteString aCharset;
|
|
ByteString aLanguage;
|
|
ByteString aValue;
|
|
if (bExtended)
|
|
{
|
|
if (nSection == 0)
|
|
{
|
|
sal_Char const * pCharsetBegin = p;
|
|
bool bDowncaseCharset = false;
|
|
while (p != pEnd && isTokenChar(*p) && *p != '\'')
|
|
{
|
|
bDowncaseCharset = bDowncaseCharset || isUpperCase(*p);
|
|
++p;
|
|
}
|
|
if (p == pCharsetBegin)
|
|
break;
|
|
if (pParameters)
|
|
{
|
|
aCharset = ByteString(
|
|
pCharsetBegin,
|
|
static_cast< xub_StrLen >(p - pCharsetBegin));
|
|
if (bDowncaseCharset)
|
|
aCharset.ToLowerAscii();
|
|
}
|
|
|
|
if (p == pEnd || *p != '\'')
|
|
break;
|
|
++p;
|
|
|
|
sal_Char const * pLanguageBegin = p;
|
|
bool bDowncaseLanguage = false;
|
|
int nLetters = 0;
|
|
for (; p != pEnd; ++p)
|
|
if (isAlpha(*p))
|
|
{
|
|
if (++nLetters > 8)
|
|
break;
|
|
bDowncaseLanguage = bDowncaseLanguage
|
|
|| isUpperCase(*p);
|
|
}
|
|
else if (*p == '-')
|
|
{
|
|
if (nLetters == 0)
|
|
break;
|
|
nLetters = 0;
|
|
}
|
|
else
|
|
break;
|
|
if (nLetters == 0 || nLetters > 8)
|
|
break;
|
|
if (pParameters)
|
|
{
|
|
aLanguage = ByteString(
|
|
pLanguageBegin,
|
|
static_cast< xub_StrLen >(p - pLanguageBegin));
|
|
if (bDowncaseLanguage)
|
|
aLanguage.ToLowerAscii();
|
|
}
|
|
|
|
if (p == pEnd || *p != '\'')
|
|
break;
|
|
++p;
|
|
}
|
|
if (pParameters)
|
|
while (p != pEnd && (isTokenChar(*p) || !isUSASCII(*p)))
|
|
{
|
|
if (*p == '%')
|
|
{
|
|
if (p + 2 < pEnd)
|
|
{
|
|
int nWeight1 = getHexWeight(p[1]);
|
|
int nWeight2 = getHexWeight(p[2]);
|
|
if (nWeight1 >= 0 && nWeight2 >= 0)
|
|
{
|
|
aValue += sal_Char(nWeight1 << 4 | nWeight2);
|
|
p += 3;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
aValue += *p++;
|
|
}
|
|
else
|
|
while (p != pEnd && (isTokenChar(*p) || !isUSASCII(*p)))
|
|
++p;
|
|
}
|
|
else if (p != pEnd && *p == '"')
|
|
if (pParameters)
|
|
{
|
|
bool bInvalid = false;
|
|
for (++p;;)
|
|
{
|
|
if (p == pEnd)
|
|
{
|
|
bInvalid = true;
|
|
break;
|
|
}
|
|
else if (*p == '"')
|
|
{
|
|
++p;
|
|
break;
|
|
}
|
|
else if (*p == 0x0D) // CR
|
|
{
|
|
if (pEnd - p < 3 || p[1] != 0x0A // LF
|
|
|| !isWhiteSpace(p[2]))
|
|
{
|
|
bInvalid = true;
|
|
break;
|
|
}
|
|
p += 2;
|
|
}
|
|
else if (*p == '\\' && ++p == pEnd)
|
|
{
|
|
bInvalid = true;
|
|
break;
|
|
}
|
|
aValue += *p++;
|
|
}
|
|
if (bInvalid)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
sal_Char const * pStringEnd = skipQuotedString(p, pEnd);
|
|
if (p == pStringEnd)
|
|
break;
|
|
p = pStringEnd;
|
|
}
|
|
else
|
|
{
|
|
sal_Char const * pTokenBegin = p;
|
|
while (p != pEnd && (isTokenChar(*p) || !isUSASCII(*p)))
|
|
++p;
|
|
if (p == pTokenBegin)
|
|
break;
|
|
if (pParameters)
|
|
aValue = ByteString(
|
|
pTokenBegin, static_cast< xub_StrLen >(p - pTokenBegin));
|
|
}
|
|
|
|
*pPos = new Parameter(*pPos, aAttribute, aCharset, aLanguage, aValue,
|
|
nSection, bExtended);
|
|
}
|
|
return parseParameters(aList, pParameters) ? pParameterBegin : pBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
sal_Unicode const * INetMIME::scanParameters(sal_Unicode const * pBegin,
|
|
sal_Unicode const * pEnd,
|
|
INetContentTypeParameterList *
|
|
pParameters)
|
|
{
|
|
ParameterList aList;
|
|
sal_Unicode const * pParameterBegin = pBegin;
|
|
for (sal_Unicode const * p = pParameterBegin;; pParameterBegin = p)
|
|
{
|
|
pParameterBegin = skipLinearWhiteSpaceComment(p, pEnd);
|
|
if (pParameterBegin == pEnd || *pParameterBegin != ';')
|
|
break;
|
|
p = pParameterBegin + 1;
|
|
|
|
sal_Unicode const * pAttributeBegin
|
|
= skipLinearWhiteSpaceComment(p, pEnd);
|
|
p = pAttributeBegin;
|
|
bool bDowncaseAttribute = false;
|
|
while (p != pEnd && isTokenChar(*p) && *p != '*')
|
|
{
|
|
bDowncaseAttribute = bDowncaseAttribute || isUpperCase(*p);
|
|
++p;
|
|
}
|
|
if (p == pAttributeBegin)
|
|
break;
|
|
ByteString aAttribute = ByteString(
|
|
pAttributeBegin, static_cast< xub_StrLen >(p - pAttributeBegin),
|
|
RTL_TEXTENCODING_ASCII_US);
|
|
if (bDowncaseAttribute)
|
|
aAttribute.ToLowerAscii();
|
|
|
|
sal_uInt32 nSection = 0;
|
|
if (p != pEnd && *p == '*')
|
|
{
|
|
++p;
|
|
if (p != pEnd && isDigit(*p)
|
|
&& !scanUnsigned(p, pEnd, false, nSection))
|
|
break;
|
|
}
|
|
|
|
bool bPresent;
|
|
Parameter ** pPos = aList.find(aAttribute, nSection, bPresent);
|
|
if (bPresent)
|
|
break;
|
|
|
|
bool bExtended = false;
|
|
if (p != pEnd && *p == '*')
|
|
{
|
|
++p;
|
|
bExtended = true;
|
|
}
|
|
|
|
p = skipLinearWhiteSpaceComment(p, pEnd);
|
|
|
|
if (p == pEnd || *p != '=')
|
|
break;
|
|
|
|
p = skipLinearWhiteSpaceComment(p + 1, pEnd);
|
|
|
|
ByteString aCharset;
|
|
ByteString aLanguage;
|
|
ByteString aValue;
|
|
if (bExtended)
|
|
{
|
|
if (nSection == 0)
|
|
{
|
|
sal_Unicode const * pCharsetBegin = p;
|
|
bool bDowncaseCharset = false;
|
|
while (p != pEnd && isTokenChar(*p) && *p != '\'')
|
|
{
|
|
bDowncaseCharset = bDowncaseCharset || isUpperCase(*p);
|
|
++p;
|
|
}
|
|
if (p == pCharsetBegin)
|
|
break;
|
|
if (pParameters)
|
|
{
|
|
aCharset = ByteString(
|
|
pCharsetBegin,
|
|
static_cast< xub_StrLen >(p - pCharsetBegin),
|
|
RTL_TEXTENCODING_ASCII_US);
|
|
if (bDowncaseCharset)
|
|
aCharset.ToLowerAscii();
|
|
}
|
|
|
|
if (p == pEnd || *p != '\'')
|
|
break;
|
|
++p;
|
|
|
|
sal_Unicode const * pLanguageBegin = p;
|
|
bool bDowncaseLanguage = false;
|
|
int nLetters = 0;
|
|
for (; p != pEnd; ++p)
|
|
if (isAlpha(*p))
|
|
{
|
|
if (++nLetters > 8)
|
|
break;
|
|
bDowncaseLanguage = bDowncaseLanguage
|
|
|| isUpperCase(*p);
|
|
}
|
|
else if (*p == '-')
|
|
{
|
|
if (nLetters == 0)
|
|
break;
|
|
nLetters = 0;
|
|
}
|
|
else
|
|
break;
|
|
if (nLetters == 0 || nLetters > 8)
|
|
break;
|
|
if (pParameters)
|
|
{
|
|
aLanguage = ByteString(
|
|
pLanguageBegin,
|
|
static_cast< xub_StrLen >(p - pLanguageBegin),
|
|
RTL_TEXTENCODING_ASCII_US);
|
|
if (bDowncaseLanguage)
|
|
aLanguage.ToLowerAscii();
|
|
}
|
|
|
|
if (p == pEnd || *p != '\'')
|
|
break;
|
|
++p;
|
|
}
|
|
if (pParameters)
|
|
{
|
|
INetMIMEStringOutputSink
|
|
aSink(0, INetMIMEOutputSink::NO_LINE_LENGTH_LIMIT);
|
|
while (p != pEnd)
|
|
{
|
|
sal_uInt32 nChar = INetMIME::getUTF32Character(p, pEnd);
|
|
if (isUSASCII(nChar) && !isTokenChar(nChar))
|
|
break;
|
|
if (nChar == '%' && p + 1 < pEnd)
|
|
{
|
|
int nWeight1 = getHexWeight(p[0]);
|
|
int nWeight2 = getHexWeight(p[1]);
|
|
if (nWeight1 >= 0 && nWeight2 >= 0)
|
|
{
|
|
aSink << sal_Char(nWeight1 << 4 | nWeight2);
|
|
p += 2;
|
|
continue;
|
|
}
|
|
}
|
|
INetMIME::writeUTF8(aSink, nChar);
|
|
}
|
|
aValue = aSink.takeBuffer();
|
|
}
|
|
else
|
|
while (p != pEnd && (isTokenChar(*p) || !isUSASCII(*p)))
|
|
++p;
|
|
}
|
|
else if (p != pEnd && *p == '"')
|
|
if (pParameters)
|
|
{
|
|
INetMIMEStringOutputSink
|
|
aSink(0, INetMIMEOutputSink::NO_LINE_LENGTH_LIMIT);
|
|
bool bInvalid = false;
|
|
for (++p;;)
|
|
{
|
|
if (p == pEnd)
|
|
{
|
|
bInvalid = true;
|
|
break;
|
|
}
|
|
sal_uInt32 nChar = INetMIME::getUTF32Character(p, pEnd);
|
|
if (nChar == '"')
|
|
break;
|
|
else if (nChar == 0x0D) // CR
|
|
{
|
|
if (pEnd - p < 2 || *p++ != 0x0A // LF
|
|
|| !isWhiteSpace(*p))
|
|
{
|
|
bInvalid = true;
|
|
break;
|
|
}
|
|
nChar = sal_uChar(*p++);
|
|
}
|
|
else if (nChar == '\\')
|
|
{
|
|
if (p == pEnd)
|
|
{
|
|
bInvalid = true;
|
|
break;
|
|
}
|
|
nChar = INetMIME::getUTF32Character(p, pEnd);
|
|
}
|
|
INetMIME::writeUTF8(aSink, nChar);
|
|
}
|
|
if (bInvalid)
|
|
break;
|
|
aValue = aSink.takeBuffer();
|
|
}
|
|
else
|
|
{
|
|
sal_Unicode const * pStringEnd = skipQuotedString(p, pEnd);
|
|
if (p == pStringEnd)
|
|
break;
|
|
p = pStringEnd;
|
|
}
|
|
else
|
|
{
|
|
sal_Unicode const * pTokenBegin = p;
|
|
while (p != pEnd && (isTokenChar(*p) || !isUSASCII(*p)))
|
|
++p;
|
|
if (p == pTokenBegin)
|
|
break;
|
|
if (pParameters)
|
|
aValue = ByteString(
|
|
pTokenBegin, static_cast< xub_StrLen >(p - pTokenBegin),
|
|
RTL_TEXTENCODING_UTF8);
|
|
}
|
|
|
|
*pPos = new Parameter(*pPos, aAttribute, aCharset, aLanguage, aValue,
|
|
nSection, bExtended);
|
|
}
|
|
return parseParameters(aList, pParameters) ? pParameterBegin : pBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
const sal_Char * INetMIME::getCharsetName(rtl_TextEncoding eEncoding)
|
|
{
|
|
if (rtl_isOctetTextEncoding(eEncoding))
|
|
{
|
|
char const * p = rtl_getMimeCharsetFromTextEncoding(eEncoding);
|
|
DBG_ASSERT(p, "INetMIME::getCharsetName(): Unsupported encoding");
|
|
return p;
|
|
}
|
|
else
|
|
switch (eEncoding)
|
|
{
|
|
case RTL_TEXTENCODING_UCS4:
|
|
return "ISO-10646-UCS-4";
|
|
|
|
case RTL_TEXTENCODING_UCS2:
|
|
return "ISO-10646-UCS-2";
|
|
|
|
default:
|
|
DBG_ERROR("INetMIME::getCharsetName(): Unsupported encoding");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
namespace unnamed_tools_inetmime {
|
|
|
|
struct EncodingEntry
|
|
{
|
|
sal_Char const * m_aName;
|
|
rtl_TextEncoding m_eEncoding;
|
|
};
|
|
|
|
//============================================================================
|
|
// The source for the following table is <ftp://ftp.iana.org/in-notes/iana/
|
|
// assignments/character-sets> as of Jan, 21 2000 12:46:00, unless otherwise
|
|
// noted:
|
|
EncodingEntry const aEncodingMap[]
|
|
= { { "US-ASCII", RTL_TEXTENCODING_ASCII_US },
|
|
{ "ANSI_X3.4-1968", RTL_TEXTENCODING_ASCII_US },
|
|
{ "ISO-IR-6", RTL_TEXTENCODING_ASCII_US },
|
|
{ "ANSI_X3.4-1986", RTL_TEXTENCODING_ASCII_US },
|
|
{ "ISO_646.IRV:1991", RTL_TEXTENCODING_ASCII_US },
|
|
{ "ASCII", RTL_TEXTENCODING_ASCII_US },
|
|
{ "ISO646-US", RTL_TEXTENCODING_ASCII_US },
|
|
{ "US", RTL_TEXTENCODING_ASCII_US },
|
|
{ "IBM367", RTL_TEXTENCODING_ASCII_US },
|
|
{ "CP367", RTL_TEXTENCODING_ASCII_US },
|
|
{ "CSASCII", RTL_TEXTENCODING_ASCII_US },
|
|
{ "ISO-8859-1", RTL_TEXTENCODING_ISO_8859_1 },
|
|
{ "ISO_8859-1:1987", RTL_TEXTENCODING_ISO_8859_1 },
|
|
{ "ISO-IR-100", RTL_TEXTENCODING_ISO_8859_1 },
|
|
{ "ISO_8859-1", RTL_TEXTENCODING_ISO_8859_1 },
|
|
{ "LATIN1", RTL_TEXTENCODING_ISO_8859_1 },
|
|
{ "L1", RTL_TEXTENCODING_ISO_8859_1 },
|
|
{ "IBM819", RTL_TEXTENCODING_ISO_8859_1 },
|
|
{ "CP819", RTL_TEXTENCODING_ISO_8859_1 },
|
|
{ "CSISOLATIN1", RTL_TEXTENCODING_ISO_8859_1 },
|
|
{ "ISO-8859-2", RTL_TEXTENCODING_ISO_8859_2 },
|
|
{ "ISO_8859-2:1987", RTL_TEXTENCODING_ISO_8859_2 },
|
|
{ "ISO-IR-101", RTL_TEXTENCODING_ISO_8859_2 },
|
|
{ "ISO_8859-2", RTL_TEXTENCODING_ISO_8859_2 },
|
|
{ "LATIN2", RTL_TEXTENCODING_ISO_8859_2 },
|
|
{ "L2", RTL_TEXTENCODING_ISO_8859_2 },
|
|
{ "CSISOLATIN2", RTL_TEXTENCODING_ISO_8859_2 },
|
|
{ "ISO-8859-3", RTL_TEXTENCODING_ISO_8859_3 },
|
|
{ "ISO_8859-3:1988", RTL_TEXTENCODING_ISO_8859_3 },
|
|
{ "ISO-IR-109", RTL_TEXTENCODING_ISO_8859_3 },
|
|
{ "ISO_8859-3", RTL_TEXTENCODING_ISO_8859_3 },
|
|
{ "LATIN3", RTL_TEXTENCODING_ISO_8859_3 },
|
|
{ "L3", RTL_TEXTENCODING_ISO_8859_3 },
|
|
{ "CSISOLATIN3", RTL_TEXTENCODING_ISO_8859_3 },
|
|
{ "ISO-8859-4", RTL_TEXTENCODING_ISO_8859_4 },
|
|
{ "ISO_8859-4:1988", RTL_TEXTENCODING_ISO_8859_4 },
|
|
{ "ISO-IR-110", RTL_TEXTENCODING_ISO_8859_4 },
|
|
{ "ISO_8859-4", RTL_TEXTENCODING_ISO_8859_4 },
|
|
{ "LATIN4", RTL_TEXTENCODING_ISO_8859_4 },
|
|
{ "L4", RTL_TEXTENCODING_ISO_8859_4 },
|
|
{ "CSISOLATIN4", RTL_TEXTENCODING_ISO_8859_4 },
|
|
{ "ISO-8859-5", RTL_TEXTENCODING_ISO_8859_5 },
|
|
{ "ISO_8859-5:1988", RTL_TEXTENCODING_ISO_8859_5 },
|
|
{ "ISO-IR-144", RTL_TEXTENCODING_ISO_8859_5 },
|
|
{ "ISO_8859-5", RTL_TEXTENCODING_ISO_8859_5 },
|
|
{ "CYRILLIC", RTL_TEXTENCODING_ISO_8859_5 },
|
|
{ "CSISOLATINCYRILLIC", RTL_TEXTENCODING_ISO_8859_5 },
|
|
{ "ISO-8859-6", RTL_TEXTENCODING_ISO_8859_6 },
|
|
{ "ISO_8859-6:1987", RTL_TEXTENCODING_ISO_8859_6 },
|
|
{ "ISO-IR-127", RTL_TEXTENCODING_ISO_8859_6 },
|
|
{ "ISO_8859-6", RTL_TEXTENCODING_ISO_8859_6 },
|
|
{ "ECMA-114", RTL_TEXTENCODING_ISO_8859_6 },
|
|
{ "ASMO-708", RTL_TEXTENCODING_ISO_8859_6 },
|
|
{ "ARABIC", RTL_TEXTENCODING_ISO_8859_6 },
|
|
{ "CSISOLATINARABIC", RTL_TEXTENCODING_ISO_8859_6 },
|
|
{ "ISO-8859-7", RTL_TEXTENCODING_ISO_8859_7 },
|
|
{ "ISO_8859-7:1987", RTL_TEXTENCODING_ISO_8859_7 },
|
|
{ "ISO-IR-126", RTL_TEXTENCODING_ISO_8859_7 },
|
|
{ "ISO_8859-7", RTL_TEXTENCODING_ISO_8859_7 },
|
|
{ "ELOT_928", RTL_TEXTENCODING_ISO_8859_7 },
|
|
{ "ECMA-118", RTL_TEXTENCODING_ISO_8859_7 },
|
|
{ "GREEK", RTL_TEXTENCODING_ISO_8859_7 },
|
|
{ "GREEK8", RTL_TEXTENCODING_ISO_8859_7 },
|
|
{ "CSISOLATINGREEK", RTL_TEXTENCODING_ISO_8859_7 },
|
|
{ "ISO-8859-8", RTL_TEXTENCODING_ISO_8859_8 },
|
|
{ "ISO_8859-8:1988", RTL_TEXTENCODING_ISO_8859_8 },
|
|
{ "ISO-IR-138", RTL_TEXTENCODING_ISO_8859_8 },
|
|
{ "ISO_8859-8", RTL_TEXTENCODING_ISO_8859_8 },
|
|
{ "HEBREW", RTL_TEXTENCODING_ISO_8859_8 },
|
|
{ "CSISOLATINHEBREW", RTL_TEXTENCODING_ISO_8859_8 },
|
|
{ "ISO-8859-9", RTL_TEXTENCODING_ISO_8859_9 },
|
|
{ "ISO_8859-9:1989", RTL_TEXTENCODING_ISO_8859_9 },
|
|
{ "ISO-IR-148", RTL_TEXTENCODING_ISO_8859_9 },
|
|
{ "ISO_8859-9", RTL_TEXTENCODING_ISO_8859_9 },
|
|
{ "LATIN5", RTL_TEXTENCODING_ISO_8859_9 },
|
|
{ "L5", RTL_TEXTENCODING_ISO_8859_9 },
|
|
{ "CSISOLATIN5", RTL_TEXTENCODING_ISO_8859_9 },
|
|
{ "ISO-8859-14", RTL_TEXTENCODING_ISO_8859_14 }, // RFC 2047
|
|
{ "ISO_8859-15", RTL_TEXTENCODING_ISO_8859_15 },
|
|
{ "ISO-8859-15", RTL_TEXTENCODING_ISO_8859_15 }, // RFC 2047
|
|
{ "MACINTOSH", RTL_TEXTENCODING_APPLE_ROMAN },
|
|
{ "MAC", RTL_TEXTENCODING_APPLE_ROMAN },
|
|
{ "CSMACINTOSH", RTL_TEXTENCODING_APPLE_ROMAN },
|
|
{ "IBM437", RTL_TEXTENCODING_IBM_437 },
|
|
{ "CP437", RTL_TEXTENCODING_IBM_437 },
|
|
{ "437", RTL_TEXTENCODING_IBM_437 },
|
|
{ "CSPC8CODEPAGE437", RTL_TEXTENCODING_IBM_437 },
|
|
{ "IBM850", RTL_TEXTENCODING_IBM_850 },
|
|
{ "CP850", RTL_TEXTENCODING_IBM_850 },
|
|
{ "850", RTL_TEXTENCODING_IBM_850 },
|
|
{ "CSPC850MULTILINGUAL", RTL_TEXTENCODING_IBM_850 },
|
|
{ "IBM860", RTL_TEXTENCODING_IBM_860 },
|
|
{ "CP860", RTL_TEXTENCODING_IBM_860 },
|
|
{ "860", RTL_TEXTENCODING_IBM_860 },
|
|
{ "CSIBM860", RTL_TEXTENCODING_IBM_860 },
|
|
{ "IBM861", RTL_TEXTENCODING_IBM_861 },
|
|
{ "CP861", RTL_TEXTENCODING_IBM_861 },
|
|
{ "861", RTL_TEXTENCODING_IBM_861 },
|
|
{ "CP-IS", RTL_TEXTENCODING_IBM_861 },
|
|
{ "CSIBM861", RTL_TEXTENCODING_IBM_861 },
|
|
{ "IBM863", RTL_TEXTENCODING_IBM_863 },
|
|
{ "CP863", RTL_TEXTENCODING_IBM_863 },
|
|
{ "863", RTL_TEXTENCODING_IBM_863 },
|
|
{ "CSIBM863", RTL_TEXTENCODING_IBM_863 },
|
|
{ "IBM865", RTL_TEXTENCODING_IBM_865 },
|
|
{ "CP865", RTL_TEXTENCODING_IBM_865 },
|
|
{ "865", RTL_TEXTENCODING_IBM_865 },
|
|
{ "CSIBM865", RTL_TEXTENCODING_IBM_865 },
|
|
{ "IBM775", RTL_TEXTENCODING_IBM_775 },
|
|
{ "CP775", RTL_TEXTENCODING_IBM_775 },
|
|
{ "CSPC775BALTIC", RTL_TEXTENCODING_IBM_775 },
|
|
{ "IBM852", RTL_TEXTENCODING_IBM_852 },
|
|
{ "CP852", RTL_TEXTENCODING_IBM_852 },
|
|
{ "852", RTL_TEXTENCODING_IBM_852 },
|
|
{ "CSPCP852", RTL_TEXTENCODING_IBM_852 },
|
|
{ "IBM855", RTL_TEXTENCODING_IBM_855 },
|
|
{ "CP855", RTL_TEXTENCODING_IBM_855 },
|
|
{ "855", RTL_TEXTENCODING_IBM_855 },
|
|
{ "CSIBM855", RTL_TEXTENCODING_IBM_855 },
|
|
{ "IBM857", RTL_TEXTENCODING_IBM_857 },
|
|
{ "CP857", RTL_TEXTENCODING_IBM_857 },
|
|
{ "857", RTL_TEXTENCODING_IBM_857 },
|
|
{ "CSIBM857", RTL_TEXTENCODING_IBM_857 },
|
|
{ "IBM862", RTL_TEXTENCODING_IBM_862 },
|
|
{ "CP862", RTL_TEXTENCODING_IBM_862 },
|
|
{ "862", RTL_TEXTENCODING_IBM_862 },
|
|
{ "CSPC862LATINHEBREW", RTL_TEXTENCODING_IBM_862 },
|
|
{ "IBM864", RTL_TEXTENCODING_IBM_864 },
|
|
{ "CP864", RTL_TEXTENCODING_IBM_864 },
|
|
{ "CSIBM864", RTL_TEXTENCODING_IBM_864 },
|
|
{ "IBM866", RTL_TEXTENCODING_IBM_866 },
|
|
{ "CP866", RTL_TEXTENCODING_IBM_866 },
|
|
{ "866", RTL_TEXTENCODING_IBM_866 },
|
|
{ "CSIBM866", RTL_TEXTENCODING_IBM_866 },
|
|
{ "IBM869", RTL_TEXTENCODING_IBM_869 },
|
|
{ "CP869", RTL_TEXTENCODING_IBM_869 },
|
|
{ "869", RTL_TEXTENCODING_IBM_869 },
|
|
{ "CP-GR", RTL_TEXTENCODING_IBM_869 },
|
|
{ "CSIBM869", RTL_TEXTENCODING_IBM_869 },
|
|
{ "WINDOWS-1250", RTL_TEXTENCODING_MS_1250 },
|
|
{ "WINDOWS-1251", RTL_TEXTENCODING_MS_1251 },
|
|
{ "WINDOWS-1253", RTL_TEXTENCODING_MS_1253 },
|
|
{ "WINDOWS-1254", RTL_TEXTENCODING_MS_1254 },
|
|
{ "WINDOWS-1255", RTL_TEXTENCODING_MS_1255 },
|
|
{ "WINDOWS-1256", RTL_TEXTENCODING_MS_1256 },
|
|
{ "WINDOWS-1257", RTL_TEXTENCODING_MS_1257 },
|
|
{ "WINDOWS-1258", RTL_TEXTENCODING_MS_1258 },
|
|
{ "SHIFT_JIS", RTL_TEXTENCODING_SHIFT_JIS },
|
|
{ "MS_KANJI", RTL_TEXTENCODING_SHIFT_JIS },
|
|
{ "CSSHIFTJIS", RTL_TEXTENCODING_SHIFT_JIS },
|
|
{ "GB2312", RTL_TEXTENCODING_GB_2312 },
|
|
{ "CSGB2312", RTL_TEXTENCODING_GB_2312 },
|
|
{ "BIG5", RTL_TEXTENCODING_BIG5 },
|
|
{ "CSBIG5", RTL_TEXTENCODING_BIG5 },
|
|
{ "EUC-JP", RTL_TEXTENCODING_EUC_JP },
|
|
{ "EXTENDED_UNIX_CODE_PACKED_FORMAT_FOR_JAPANESE",
|
|
RTL_TEXTENCODING_EUC_JP },
|
|
{ "CSEUCPKDFMTJAPANESE", RTL_TEXTENCODING_EUC_JP },
|
|
{ "ISO-2022-JP", RTL_TEXTENCODING_ISO_2022_JP },
|
|
{ "CSISO2022JP", RTL_TEXTENCODING_ISO_2022_JP },
|
|
{ "ISO-2022-CN", RTL_TEXTENCODING_ISO_2022_CN },
|
|
{ "KOI8-R", RTL_TEXTENCODING_KOI8_R },
|
|
{ "CSKOI8R", RTL_TEXTENCODING_KOI8_R },
|
|
{ "UTF-7", RTL_TEXTENCODING_UTF7 },
|
|
{ "UTF-8", RTL_TEXTENCODING_UTF8 },
|
|
{ "ISO-8859-10", RTL_TEXTENCODING_ISO_8859_10 }, // RFC 2047
|
|
{ "ISO-8859-13", RTL_TEXTENCODING_ISO_8859_13 }, // RFC 2047
|
|
{ "EUC-KR", RTL_TEXTENCODING_EUC_KR },
|
|
{ "CSEUCKR", RTL_TEXTENCODING_EUC_KR },
|
|
{ "ISO-2022-KR", RTL_TEXTENCODING_ISO_2022_KR },
|
|
{ "CSISO2022KR", RTL_TEXTENCODING_ISO_2022_KR },
|
|
{ "ISO-10646-UCS-4", RTL_TEXTENCODING_UCS4 },
|
|
{ "CSUCS4", RTL_TEXTENCODING_UCS4 },
|
|
{ "ISO-10646-UCS-2", RTL_TEXTENCODING_UCS2 },
|
|
{ "CSUNICODE", RTL_TEXTENCODING_UCS2 } };
|
|
|
|
//============================================================================
|
|
template< typename T >
|
|
inline rtl_TextEncoding getCharsetEncoding_Impl(T const * pBegin,
|
|
T const * pEnd)
|
|
{
|
|
for (sal_Size i = 0; i < sizeof aEncodingMap / sizeof (EncodingEntry);
|
|
++i)
|
|
if (INetMIME::equalIgnoreCase(pBegin, pEnd, aEncodingMap[i].m_aName))
|
|
return aEncodingMap[i].m_eEncoding;
|
|
return RTL_TEXTENCODING_DONTKNOW;
|
|
}
|
|
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
rtl_TextEncoding INetMIME::getCharsetEncoding(sal_Char const * pBegin,
|
|
sal_Char const * pEnd)
|
|
{
|
|
return getCharsetEncoding_Impl(pBegin, pEnd);
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
rtl_TextEncoding INetMIME::getCharsetEncoding(sal_Unicode const * pBegin,
|
|
sal_Unicode const * pEnd)
|
|
{
|
|
return getCharsetEncoding_Impl(pBegin, pEnd);
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
INetMIMECharsetList_Impl *
|
|
INetMIME::createPreferredCharsetList(rtl_TextEncoding eEncoding)
|
|
{
|
|
static const sal_uInt32 aUSASCIIRanges[] = { 0, 0x7F, sal_uInt32(-1) };
|
|
|
|
static const sal_uInt32 aISO88591Ranges[] = { 0, 0xFF, sal_uInt32(-1) };
|
|
// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT> version
|
|
// 1.0 of 1999 July 27
|
|
|
|
static const sal_uInt32 aISO88592Ranges[]
|
|
= { 0, 0xA0, 0xA4, 0xA4, 0xA7, 0xA8, 0xAD, 0xAD, 0xB0, 0xB0,
|
|
0xB4, 0xB4, 0xB8, 0xB8, 0xC1, 0xC2, 0xC4, 0xC4, 0xC7, 0xC7,
|
|
0xC9, 0xC9, 0xCB, 0xCB, 0xCD, 0xCE, 0xD3, 0xD4, 0xD6, 0xD7,
|
|
0xDA, 0xDA, 0xDC, 0xDD, 0xDF, 0xDF, 0xE1, 0xE2, 0xE4, 0xE4,
|
|
0xE7, 0xE7, 0xE9, 0xE9, 0xEB, 0xEB, 0xED, 0xEE, 0xF3, 0xF4,
|
|
0xF6, 0xF7, 0xFA, 0xFA, 0xFC, 0xFD, 0x102, 0x107, 0x10C, 0x111,
|
|
0x118, 0x11B, 0x139, 0x13A, 0x13D, 0x13E, 0x141, 0x144,
|
|
0x147, 0x148, 0x150, 0x151, 0x154, 0x155, 0x158, 0x15B,
|
|
0x15E, 0x165, 0x16E, 0x171, 0x179, 0x17E, 0x2C7, 0x2C7,
|
|
0x2D8, 0x2D9, 0x2DB, 0x2DB, 0x2DD, 0x2DD, sal_uInt32(-1) };
|
|
// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-2.TXT> version
|
|
// 1.0 of 1999 July 27
|
|
|
|
static const sal_uInt32 aISO88593Ranges[]
|
|
= { 0, 0xA0, 0xA3, 0xA4, 0xA7, 0xA8, 0xAD, 0xAD, 0xB0, 0xB0,
|
|
0xB2, 0xB5, 0xB7, 0xB8, 0xBD, 0xBD, 0xC0, 0xC2, 0xC4, 0xC4,
|
|
0xC7, 0xCF, 0xD1, 0xD4, 0xD6, 0xD7, 0xD9, 0xDC, 0xDF, 0xE2,
|
|
0xE4, 0xE4, 0xE7, 0xEF, 0xF1, 0xF4, 0xF6, 0xF7, 0xF9, 0xFC,
|
|
0x108, 0x10B, 0x11C, 0x121, 0x124, 0x127, 0x130, 0x131,
|
|
0x134, 0x135, 0x15C, 0x15F, 0x16C, 0x16D, 0x17B, 0x17C,
|
|
0x2D8, 0x2D9, sal_uInt32(-1) };
|
|
// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-3.TXT> version
|
|
// 1.0 of 1999 July 27
|
|
|
|
static const sal_uInt32 aISO88594Ranges[]
|
|
= { 0, 0xA0, 0xA4, 0xA4, 0xA7, 0xA8, 0xAD, 0xAD, 0xAF, 0xB0,
|
|
0xB4, 0xB4, 0xB8, 0xB8, 0xC1, 0xC6, 0xC9, 0xC9, 0xCB, 0xCB,
|
|
0xCD, 0xCE, 0xD4, 0xD8, 0xDA, 0xDC, 0xDF, 0xDF, 0xE1, 0xE6,
|
|
0xE9, 0xE9, 0xEB, 0xEB, 0xED, 0xEE, 0xF4, 0xF8, 0xFA, 0xFC,
|
|
0x100, 0x101, 0x104, 0x105, 0x10C, 0x10D, 0x110, 0x113,
|
|
0x116, 0x119, 0x122, 0x123, 0x128, 0x12B, 0x12E, 0x12F,
|
|
0x136, 0x138, 0x13B, 0x13C, 0x145, 0x146, 0x14A, 0x14D,
|
|
0x156, 0x157, 0x160, 0x161, 0x166, 0x16B, 0x172, 0x173,
|
|
0x17D, 0x17E, 0x2C7, 0x2C7, 0x2D9, 0x2D9, 0x2DB, 0x2DB,
|
|
sal_uInt32(-1) };
|
|
// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-4.TXT> version
|
|
// 1.0 of 1999 July 27
|
|
|
|
static const sal_uInt32 aISO88595Ranges[]
|
|
= { 0, 0xA0, 0xA7, 0xA7, 0xAD, 0xAD, 0x401, 0x40C, 0x40E, 0x44F,
|
|
0x451, 0x45C, 0x45E, 0x45F, 0x2116, 0x2116, sal_uInt32(-1) };
|
|
// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-5.TXT> version
|
|
// 1.0 of 1999 July 27
|
|
|
|
static const sal_uInt32 aISO88596Ranges[]
|
|
= { 0, 0xA0, 0xA4, 0xA4, 0xAD, 0xAD, 0x60C, 0x60C, 0x61B, 0x61B,
|
|
0x61F, 0x61F, 0x621, 0x63A, 0x640, 0x652, sal_uInt32(-1) };
|
|
// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-6.TXT> version
|
|
// 1.0 of 1999 July 27
|
|
|
|
static const sal_uInt32 aISO88597Ranges[]
|
|
= { 0, 0xA0, 0xA3, 0xA3, 0xA6, 0xA9, 0xAB, 0xAD, 0xB0, 0xB3,
|
|
0xB7, 0xB7, 0xBB, 0xBB, 0xBD, 0xBD, 0x384, 0x386, 0x388, 0x38A,
|
|
0x38C, 0x38C, 0x38E, 0x3A1, 0x3A3, 0x3CE, 0x2015, 0x2015,
|
|
0x2018, 0x2019, sal_uInt32(-1) };
|
|
// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-7.TXT> version
|
|
// 1.0 of 1999 July 27
|
|
|
|
static const sal_uInt32 aISO88598Ranges[]
|
|
= { 0, 0xA0, 0xA2, 0xA9, 0xAB, 0xB9, 0xBB, 0xBE, 0xD7, 0xD7,
|
|
0xF7, 0xF7, 0x5D0, 0x5EA, 0x200E, 0x200F, 0x2017, 0x2017,
|
|
sal_uInt32(-1) };
|
|
// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-8.TXT> version
|
|
// 1.1 of 2000-Jan-03
|
|
|
|
static const sal_uInt32 aISO88599Ranges[]
|
|
= { 0, 0xCF, 0xD1, 0xDC, 0xDF, 0xEF, 0xF1, 0xFC, 0xFF, 0xFF,
|
|
0x11E, 0x11F, 0x130, 0x131, 0x15E, 0x15F, sal_uInt32(-1) };
|
|
// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-9.TXT> version
|
|
// 1.0 of 1999 July 27
|
|
|
|
static const sal_uInt32 aISO885910Ranges[]
|
|
= { 0, 0xA0, 0xA7, 0xA7, 0xAD, 0xAD, 0xB0, 0xB0, 0xB7, 0xB7,
|
|
0xC1, 0xC6, 0xC9, 0xC9, 0xCB, 0xCB, 0xCD, 0xD0, 0xD3, 0xD6,
|
|
0xD8, 0xD8, 0xDA, 0xDF, 0xE1, 0xE6, 0xE9, 0xE9, 0xEB, 0xEB,
|
|
0xED, 0xF0, 0xF3, 0xF6, 0xF8, 0xF8, 0xFA, 0xFE, 0x100, 0x101,
|
|
0x104, 0x105, 0x10C, 0x10D, 0x110, 0x113, 0x116, 0x119,
|
|
0x122, 0x123, 0x128, 0x12B, 0x12E, 0x12F, 0x136, 0x138,
|
|
0x13B, 0x13C, 0x145, 0x146, 0x14A, 0x14D, 0x160, 0x161,
|
|
0x166, 0x16B, 0x172, 0x173, 0x17D, 0x17E, 0x2015, 0x2015,
|
|
sal_uInt32(-1) };
|
|
// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-10.TXT> version
|
|
// 1.1 of 1999 October 11
|
|
|
|
static const sal_uInt32 aISO885913Ranges[]
|
|
= { 0, 0xA0, 0xA2, 0xA4, 0xA6, 0xA7, 0xA9, 0xA9, 0xAB, 0xAE,
|
|
0xB0, 0xB3, 0xB5, 0xB7, 0xB9, 0xB9, 0xBB, 0xBE, 0xC4, 0xC6,
|
|
0xC9, 0xC9, 0xD3, 0xD3, 0xD5, 0xD8, 0xDC, 0xDC, 0xDF, 0xDF,
|
|
0xE4, 0xE6, 0xE9, 0xE9, 0xF3, 0xF3, 0xF5, 0xF8, 0xFC, 0xFC,
|
|
0x100, 0x101, 0x104, 0x107, 0x10C, 0x10D, 0x112, 0x113,
|
|
0x116, 0x119, 0x122, 0x123, 0x12A, 0x12B, 0x12E, 0x12F,
|
|
0x136, 0x137, 0x13B, 0x13C, 0x141, 0x146, 0x14C, 0x14D,
|
|
0x156, 0x157, 0x15A, 0x15B, 0x160, 0x161, 0x16A, 0x16B,
|
|
0x172, 0x173, 0x179, 0x17E, 0x2019, 0x2019, 0x201C, 0x201E,
|
|
sal_uInt32(-1) };
|
|
// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-13.TXT> version
|
|
// 1.0 of 1999 July 27
|
|
|
|
static const sal_uInt32 aISO885914Ranges[]
|
|
= { 0, 0xA0, 0xA3, 0xA3, 0xA7, 0xA7, 0xA9, 0xA9, 0xAD, 0xAE,
|
|
0xB6, 0xB6, 0xC0, 0xCF, 0xD1, 0xD6, 0xD8, 0xDD, 0xDF, 0xEF,
|
|
0xF1, 0xF6, 0xF8, 0xFD, 0xFF, 0xFF, 0x10A, 0x10B, 0x120, 0x121,
|
|
0x174, 0x178, 0x1E02, 0x1E03, 0x1E0A, 0x1E0B, 0x1E1E, 0x1E1F,
|
|
0x1E40, 0x1E41, 0x1E56, 0x1E57, 0x1E60, 0x1E61, 0x1E6A, 0x1E6B,
|
|
0x1E80, 0x1E85, 0x1EF2, 0x1EF3, sal_uInt32(-1) };
|
|
// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-14.TXT> version
|
|
// 1.0 of 1999 July 27
|
|
|
|
static const sal_uInt32 aISO885915Ranges[]
|
|
= { 0, 0xA3, 0xA5, 0xA5, 0xA7, 0xA7, 0xA9, 0xB3, 0xB5, 0xB7,
|
|
0xB9, 0xBB, 0xBF, 0xFF, 0x152, 0x153, 0x160, 0x161, 0x178, 0x178,
|
|
0x17D, 0x17E, 0x20AC, 0x20AC, sal_uInt32(-1) };
|
|
// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-15.TXT> version
|
|
// 1.0 of 1999 July 27
|
|
|
|
static const sal_uInt32 aKOI8RRanges[]
|
|
= { 0, 0x7F, 0xA0, 0xA0, 0xA9, 0xA9, 0xB0, 0xB0, 0xB2, 0xB2,
|
|
0xB7, 0xB7, 0xF7, 0xF7, 0x401, 0x401, 0x410, 0x44F, 0x451, 0x451,
|
|
0x2219, 0x221A, 0x2248, 0x2248, 0x2264, 0x2265, 0x2320, 0x2321,
|
|
0x2500, 0x2500, 0x2502, 0x2502, 0x250C, 0x250C, 0x2510, 0x2510,
|
|
0x2514, 0x2514, 0x2518, 0x2518, 0x251C, 0x251C, 0x2524, 0x2524,
|
|
0x252C, 0x252C, 0x2534, 0x2534, 0x253C, 0x253C, 0x2550, 0x256C,
|
|
0x2580, 0x2580, 0x2584, 0x2584, 0x2588, 0x2588, 0x258C, 0x258C,
|
|
0x2590, 0x2593, 0x25A0, 0x25A0, sal_uInt32(-1) };
|
|
// <ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MISC/KOI8-R.TXT>
|
|
// version 1.0 of 18 August 1999
|
|
|
|
#if defined WNT
|
|
static const sal_uInt32 aWindows1252Ranges[]
|
|
= { 0, 0x7F, 0xA0, 0xFF, 0x152, 0x153, 0x160, 0x161, 0x178, 0x178,
|
|
0x17D, 0x17E, 0x192, 0x192, 0x2C6, 0x2C6, 0x2DC, 0x2DC,
|
|
0x2013, 0x2014, 0x2018, 0x201A, 0x201C, 0x201E, 0x2020, 0x2022,
|
|
0x2026, 0x2026, 0x2030, 0x2030, 0x2039, 0x203A, 0x20AC, 0x20AC,
|
|
0x2122, 0x2122, sal_uInt32(-1) };
|
|
// <ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/
|
|
// CP1252.TXT> version 2.01 of 04/15/98
|
|
#endif // WNT
|
|
|
|
INetMIMECharsetList_Impl * pList = new INetMIMECharsetList_Impl;
|
|
switch (eEncoding)
|
|
{
|
|
case RTL_TEXTENCODING_MS_1252:
|
|
#if defined WNT
|
|
pList->prepend(Charset(RTL_TEXTENCODING_MS_1252,
|
|
aWindows1252Ranges));
|
|
#endif // WNT
|
|
case RTL_TEXTENCODING_ISO_8859_1:
|
|
case RTL_TEXTENCODING_UTF7:
|
|
case RTL_TEXTENCODING_UTF8:
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_ISO_8859_2:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_2,
|
|
aISO88592Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_ISO_8859_3:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_3,
|
|
aISO88593Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_ISO_8859_4:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_4,
|
|
aISO88594Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_ISO_8859_5:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_5,
|
|
aISO88595Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_ISO_8859_6:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_6,
|
|
aISO88596Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_ISO_8859_7:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_7,
|
|
aISO88597Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_ISO_8859_8:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_8,
|
|
aISO88598Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_ISO_8859_9:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_9,
|
|
aISO88599Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_ISO_8859_10:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_10,
|
|
aISO885910Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_ISO_8859_13:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_13,
|
|
aISO885913Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_ISO_8859_14:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_14,
|
|
aISO885914Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_ISO_8859_15:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_15,
|
|
aISO885915Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_MS_1250:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_2,
|
|
aISO88592Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_MS_1251:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_5,
|
|
aISO88595Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_MS_1253:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_7,
|
|
aISO88597Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_MS_1254:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_9,
|
|
aISO88599Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_MS_1255:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_8,
|
|
aISO88598Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_MS_1256:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_6,
|
|
aISO88596Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_MS_1257:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_4,
|
|
aISO88594Ranges));
|
|
break;
|
|
|
|
case RTL_TEXTENCODING_KOI8_R:
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_5,
|
|
aISO88595Ranges));
|
|
pList->prepend(Charset(RTL_TEXTENCODING_KOI8_R, aKOI8RRanges));
|
|
break;
|
|
|
|
default: //@@@ more cases are missing!
|
|
DBG_ERROR("INetMIME::createPreferredCharsetList():"
|
|
" Unsupported encoding");
|
|
break;
|
|
}
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_1, aISO88591Ranges));
|
|
pList->prepend(Charset(RTL_TEXTENCODING_ASCII_US, aUSASCIIRanges));
|
|
return pList;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
sal_Unicode * INetMIME::convertToUnicode(const sal_Char * pBegin,
|
|
const sal_Char * pEnd,
|
|
rtl_TextEncoding eEncoding,
|
|
sal_Size & rSize)
|
|
{
|
|
if (eEncoding == RTL_TEXTENCODING_DONTKNOW)
|
|
return 0;
|
|
rtl_TextToUnicodeConverter hConverter
|
|
= rtl_createTextToUnicodeConverter(eEncoding);
|
|
rtl_TextToUnicodeContext hContext
|
|
= rtl_createTextToUnicodeContext(hConverter);
|
|
sal_Unicode * pBuffer;
|
|
sal_uInt32 nInfo;
|
|
for (sal_Size nBufferSize = pEnd - pBegin;;
|
|
nBufferSize += nBufferSize / 3 + 1)
|
|
{
|
|
pBuffer = new sal_Unicode[nBufferSize];
|
|
sal_Size nSrcCvtBytes;
|
|
rSize = rtl_convertTextToUnicode(
|
|
hConverter, hContext, pBegin, pEnd - pBegin, pBuffer,
|
|
nBufferSize,
|
|
RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
|
|
| RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
|
|
| RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR,
|
|
&nInfo, &nSrcCvtBytes);
|
|
if (nInfo != RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOSMALL)
|
|
break;
|
|
delete[] pBuffer;
|
|
rtl_resetTextToUnicodeContext(hConverter, hContext);
|
|
}
|
|
rtl_destroyTextToUnicodeContext(hConverter, hContext);
|
|
rtl_destroyTextToUnicodeConverter(hConverter);
|
|
if (nInfo != 0)
|
|
{
|
|
delete[] pBuffer;
|
|
pBuffer = 0;
|
|
}
|
|
return pBuffer;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
sal_Char * INetMIME::convertFromUnicode(const sal_Unicode * pBegin,
|
|
const sal_Unicode * pEnd,
|
|
rtl_TextEncoding eEncoding,
|
|
sal_Size & rSize)
|
|
{
|
|
if (eEncoding == RTL_TEXTENCODING_DONTKNOW)
|
|
return 0;
|
|
rtl_UnicodeToTextConverter hConverter
|
|
= rtl_createUnicodeToTextConverter(eEncoding);
|
|
rtl_UnicodeToTextContext hContext
|
|
= rtl_createUnicodeToTextContext(hConverter);
|
|
sal_Char * pBuffer;
|
|
sal_uInt32 nInfo;
|
|
for (sal_Size nBufferSize = pEnd - pBegin;;
|
|
nBufferSize += nBufferSize / 3 + 1)
|
|
{
|
|
pBuffer = new sal_Char[nBufferSize];
|
|
sal_Size nSrcCvtBytes;
|
|
rSize = rtl_convertUnicodeToText(
|
|
hConverter, hContext, pBegin, pEnd - pBegin, pBuffer,
|
|
nBufferSize,
|
|
RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
|
|
| RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
|
|
| RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE
|
|
| RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACESTR,
|
|
&nInfo, &nSrcCvtBytes);
|
|
if (nInfo != RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL)
|
|
break;
|
|
delete[] pBuffer;
|
|
rtl_resetUnicodeToTextContext(hConverter, hContext);
|
|
}
|
|
rtl_destroyUnicodeToTextContext(hConverter, hContext);
|
|
rtl_destroyUnicodeToTextConverter(hConverter);
|
|
if (nInfo != 0)
|
|
{
|
|
delete[] pBuffer;
|
|
pBuffer = 0;
|
|
}
|
|
return pBuffer;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
void INetMIME::writeUTF8(INetMIMEOutputSink & rSink, sal_uInt32 nChar)
|
|
{
|
|
// See RFC 2279 for a discussion of UTF-8.
|
|
DBG_ASSERT(nChar < 0x80000000, "INetMIME::writeUTF8(): Bad char");
|
|
|
|
if (nChar < 0x80)
|
|
rSink << sal_Char(nChar);
|
|
else if (nChar < 0x800)
|
|
rSink << sal_Char(nChar >> 6 | 0xC0)
|
|
<< sal_Char(nChar & 0x3F | 0x80);
|
|
else if (nChar < 0x10000)
|
|
rSink << sal_Char(nChar >> 12 | 0xE0)
|
|
<< sal_Char(nChar >> 6 & 0x3F | 0x80)
|
|
<< sal_Char(nChar & 0x3F | 0x80);
|
|
else if (nChar < 0x200000)
|
|
rSink << sal_Char(nChar >> 18 | 0xF0)
|
|
<< sal_Char(nChar >> 12 & 0x3F | 0x80)
|
|
<< sal_Char(nChar >> 6 & 0x3F | 0x80)
|
|
<< sal_Char(nChar & 0x3F | 0x80);
|
|
else if (nChar < 0x4000000)
|
|
rSink << sal_Char(nChar >> 24 | 0xF8)
|
|
<< sal_Char(nChar >> 18 & 0x3F | 0x80)
|
|
<< sal_Char(nChar >> 12 & 0x3F | 0x80)
|
|
<< sal_Char(nChar >> 6 & 0x3F | 0x80)
|
|
<< sal_Char(nChar & 0x3F | 0x80);
|
|
else
|
|
rSink << sal_Char(nChar >> 30 | 0xFC)
|
|
<< sal_Char(nChar >> 24 & 0x3F | 0x80)
|
|
<< sal_Char(nChar >> 18 & 0x3F | 0x80)
|
|
<< sal_Char(nChar >> 12 & 0x3F | 0x80)
|
|
<< sal_Char(nChar >> 6 & 0x3F | 0x80)
|
|
<< sal_Char(nChar & 0x3F | 0x80);
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
void INetMIME::writeUnsigned(INetMIMEOutputSink & rSink, sal_uInt32 nValue,
|
|
int nMinDigits)
|
|
{
|
|
sal_Char aBuffer[10];
|
|
// max unsigned 32 bit value (4294967295) has 10 places
|
|
sal_Char * p = aBuffer;
|
|
for (; nValue > 0; nValue /= 10)
|
|
*p++ = sal_Char(getDigit(nValue % 10));
|
|
nMinDigits -= p - aBuffer;
|
|
while (nMinDigits-- > 0)
|
|
rSink << '0';
|
|
while (p != aBuffer)
|
|
rSink << *--p;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
void INetMIME::writeDateTime(INetMIMEOutputSink & rSink,
|
|
const DateTime & rUTC)
|
|
{
|
|
static const sal_Char aDay[7][3]
|
|
= { { 'M', 'o', 'n' },
|
|
{ 'T', 'u', 'e' },
|
|
{ 'W', 'e', 'd' },
|
|
{ 'T', 'h', 'u' },
|
|
{ 'F', 'r', 'i' },
|
|
{ 'S', 'a', 't' },
|
|
{ 'S', 'u', 'n' } };
|
|
const sal_Char * pTheDay = aDay[rUTC.GetDayOfWeek()];
|
|
rSink.write(pTheDay, pTheDay + 3);
|
|
rSink << ", ";
|
|
writeUnsigned(rSink, rUTC.GetDay());
|
|
rSink << ' ';
|
|
static const sal_Char aMonth[12][3]
|
|
= { { 'J', 'a', 'n' },
|
|
{ 'F', 'e', 'b' },
|
|
{ 'M', 'a', 'r' },
|
|
{ 'A', 'p', 'r' },
|
|
{ 'M', 'a', 'y' },
|
|
{ 'J', 'u', 'n' },
|
|
{ 'J', 'u', 'l' },
|
|
{ 'A', 'u', 'g' },
|
|
{ 'S', 'e', 'p' },
|
|
{ 'O', 'c', 't' },
|
|
{ 'N', 'o', 'v' },
|
|
{ 'D', 'e', 'c' } };
|
|
const sal_Char * pTheMonth = aMonth[rUTC.GetMonth() - 1];
|
|
rSink.write(pTheMonth, pTheMonth + 3);
|
|
rSink << ' ';
|
|
writeUnsigned(rSink, rUTC.GetYear());
|
|
rSink << ' ';
|
|
writeUnsigned(rSink, rUTC.GetHour(), 2);
|
|
rSink << ':';
|
|
writeUnsigned(rSink, rUTC.GetMin(), 2);
|
|
rSink << ':';
|
|
writeUnsigned(rSink, rUTC.GetSec(), 2);
|
|
rSink << " +0000";
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
void INetMIME::writeHeaderFieldBody(INetMIMEOutputSink & rSink,
|
|
HeaderFieldType eType,
|
|
const ByteString & rBody,
|
|
rtl_TextEncoding ePreferredEncoding,
|
|
bool bInitialSpace)
|
|
{
|
|
writeHeaderFieldBody(rSink, eType,
|
|
UniString(rBody, RTL_TEXTENCODING_UTF8),
|
|
ePreferredEncoding, bInitialSpace);
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
void INetMIME::writeHeaderFieldBody(INetMIMEOutputSink & rSink,
|
|
HeaderFieldType eType,
|
|
const UniString & rBody,
|
|
rtl_TextEncoding ePreferredEncoding,
|
|
bool bInitialSpace)
|
|
{
|
|
if (eType == HEADER_FIELD_TEXT)
|
|
{
|
|
INetMIMEEncodedWordOutputSink
|
|
aOutput(rSink, INetMIMEEncodedWordOutputSink::CONTEXT_TEXT,
|
|
bInitialSpace ?
|
|
INetMIMEEncodedWordOutputSink::SPACE_ALWAYS :
|
|
INetMIMEEncodedWordOutputSink::SPACE_NO,
|
|
ePreferredEncoding);
|
|
aOutput.write(rBody.GetBuffer(), rBody.GetBuffer() + rBody.Len());
|
|
aOutput.flush();
|
|
}
|
|
else
|
|
{
|
|
enum Brackets { BRACKETS_OUTSIDE, BRACKETS_OPENING, BRACKETS_INSIDE };
|
|
Brackets eBrackets = BRACKETS_OUTSIDE;
|
|
|
|
const sal_Unicode * pBodyPtr = rBody.GetBuffer();
|
|
const sal_Unicode * pBodyEnd = pBodyPtr + rBody.Len();
|
|
while (pBodyPtr != pBodyEnd)
|
|
switch (*pBodyPtr)
|
|
{
|
|
case '\t':
|
|
case ' ':
|
|
// A WSP adds to accumulated space:
|
|
bInitialSpace = true;
|
|
++pBodyPtr;
|
|
break;
|
|
|
|
case '(':
|
|
{
|
|
// Write a pending '<' if necessary:
|
|
if (eBrackets == BRACKETS_OPENING)
|
|
{
|
|
if (rSink.getColumn() + (bInitialSpace ? 1 : 0)
|
|
>= rSink.getLineLengthLimit())
|
|
rSink << INetMIMEOutputSink::endl << ' ';
|
|
else if (bInitialSpace)
|
|
rSink << ' ';
|
|
rSink << '<';
|
|
bInitialSpace = false;
|
|
eBrackets = BRACKETS_INSIDE;
|
|
}
|
|
|
|
// Write the comment, introducing encoded-words where
|
|
// necessary:
|
|
int nLevel = 0;
|
|
INetMIMEEncodedWordOutputSink
|
|
aOutput(
|
|
rSink,
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT,
|
|
INetMIMEEncodedWordOutputSink::SPACE_NO,
|
|
ePreferredEncoding);
|
|
while (pBodyPtr != pBodyEnd)
|
|
switch (*pBodyPtr)
|
|
{
|
|
case '(':
|
|
aOutput.flush();
|
|
if (rSink.getColumn()
|
|
+ (bInitialSpace ? 1 : 0)
|
|
>= rSink.getLineLengthLimit())
|
|
rSink << INetMIMEOutputSink::endl << ' ';
|
|
else if (bInitialSpace)
|
|
rSink << ' ';
|
|
rSink << '(';
|
|
bInitialSpace = false;
|
|
++nLevel;
|
|
++pBodyPtr;
|
|
break;
|
|
|
|
case ')':
|
|
aOutput.flush();
|
|
if (rSink.getColumn()
|
|
>= rSink.getLineLengthLimit())
|
|
rSink << INetMIMEOutputSink::endl << ' ';
|
|
rSink << ')';
|
|
++pBodyPtr;
|
|
if (--nLevel == 0)
|
|
goto comment_done;
|
|
break;
|
|
|
|
case '\\':
|
|
if (++pBodyPtr == pBodyEnd)
|
|
break;
|
|
default:
|
|
aOutput << *pBodyPtr++;
|
|
break;
|
|
}
|
|
comment_done:
|
|
break;
|
|
}
|
|
|
|
case '<':
|
|
// Write an already pending '<' if necessary:
|
|
if (eBrackets == BRACKETS_OPENING)
|
|
{
|
|
if (rSink.getColumn() + (bInitialSpace ? 1 : 0)
|
|
>= rSink.getLineLengthLimit())
|
|
rSink << INetMIMEOutputSink::endl << ' ';
|
|
else if (bInitialSpace)
|
|
rSink << ' ';
|
|
rSink << '<';
|
|
bInitialSpace = false;
|
|
}
|
|
|
|
// Remember this '<' as pending, and open a bracketed
|
|
// block:
|
|
eBrackets = BRACKETS_OPENING;
|
|
++pBodyPtr;
|
|
break;
|
|
|
|
case '>':
|
|
// Write a pending '<' if necessary:
|
|
if (eBrackets == BRACKETS_OPENING)
|
|
{
|
|
if (rSink.getColumn() + (bInitialSpace ? 1 : 0)
|
|
>= rSink.getLineLengthLimit())
|
|
rSink << INetMIMEOutputSink::endl << ' ';
|
|
else if (bInitialSpace)
|
|
rSink << ' ';
|
|
rSink << '<';
|
|
bInitialSpace = false;
|
|
}
|
|
|
|
// Write this '>', and close any bracketed block:
|
|
if (rSink.getColumn() + (bInitialSpace ? 1 : 0)
|
|
>= rSink.getLineLengthLimit())
|
|
rSink << INetMIMEOutputSink::endl << ' ';
|
|
else if (bInitialSpace)
|
|
rSink << ' ';
|
|
rSink << '>';
|
|
bInitialSpace = false;
|
|
eBrackets = BRACKETS_OUTSIDE;
|
|
++pBodyPtr;
|
|
break;
|
|
|
|
case ',':
|
|
case ':':
|
|
case ';':
|
|
case '\\':
|
|
case ']':
|
|
// Write a pending '<' if necessary:
|
|
if (eBrackets == BRACKETS_OPENING)
|
|
{
|
|
if (rSink.getColumn() + (bInitialSpace ? 1 : 0)
|
|
>= rSink.getLineLengthLimit())
|
|
rSink << INetMIMEOutputSink::endl << ' ';
|
|
else if (bInitialSpace)
|
|
rSink << ' ';
|
|
rSink << '<';
|
|
bInitialSpace = false;
|
|
eBrackets = BRACKETS_INSIDE;
|
|
}
|
|
|
|
// Write this specials:
|
|
if (rSink.getColumn() + (bInitialSpace ? 1 : 0)
|
|
>= rSink.getLineLengthLimit())
|
|
rSink << INetMIMEOutputSink::endl << ' ';
|
|
else if (bInitialSpace)
|
|
rSink << ' ';
|
|
rSink << sal_Char(*pBodyPtr++);
|
|
bInitialSpace = false;
|
|
break;
|
|
|
|
case '\x0D': // CR
|
|
// A <CRLF WSP> adds to accumulated space, a <CR> not
|
|
// followed by <LF WSP> starts 'junk':
|
|
if (startsWithLineFolding(pBodyPtr, pBodyEnd))
|
|
{
|
|
bInitialSpace = true;
|
|
pBodyPtr += 3;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// The next token is either one of <"." / "@" / atom /
|
|
// quoted-string / domain-literal>, or it's 'junk'; if it
|
|
// is not 'junk', it is either a 'phrase' (i.e., it may
|
|
// contain encoded-words) or a 'non-phrase' (i.e., it may
|
|
// not contain encoded-words):
|
|
enum Entity { ENTITY_JUNK, ENTITY_NON_PHRASE,
|
|
ENTITY_PHRASE };
|
|
Entity eEntity = ENTITY_JUNK;
|
|
switch (*pBodyPtr)
|
|
{
|
|
case '.':
|
|
case '@':
|
|
case '[':
|
|
// A token of <"." / "@" / domain-literal> always
|
|
// starts a 'non-phrase':
|
|
eEntity = ENTITY_NON_PHRASE;
|
|
break;
|
|
|
|
default:
|
|
if (isUSASCII(*pBodyPtr)
|
|
&& !isAtomChar(*pBodyPtr))
|
|
{
|
|
eEntity = ENTITY_JUNK;
|
|
break;
|
|
}
|
|
case '"':
|
|
// A token of <atom / quoted-string> can either be
|
|
// a 'phrase' or a 'non-phrase':
|
|
switch (eType)
|
|
{
|
|
case HEADER_FIELD_STRUCTURED:
|
|
eEntity = ENTITY_NON_PHRASE;
|
|
break;
|
|
|
|
case HEADER_FIELD_PHRASE:
|
|
eEntity = ENTITY_PHRASE;
|
|
break;
|
|
|
|
case HEADER_FIELD_MESSAGE_ID:
|
|
// A 'phrase' if and only if outside any
|
|
// bracketed block:
|
|
eEntity
|
|
= eBrackets == BRACKETS_OUTSIDE ?
|
|
ENTITY_PHRASE :
|
|
ENTITY_NON_PHRASE;
|
|
break;
|
|
|
|
case HEADER_FIELD_ADDRESS:
|
|
{
|
|
// A 'non-phrase' if and only if, after
|
|
// skipping this token and any following
|
|
// <linear-white-space> and <comment>s,
|
|
// there is no token left, or the next
|
|
// token is any of <"." / "@" / ">" / ","
|
|
// / ";">, or the next token is <":"> and
|
|
// is within a bracketed block:
|
|
const sal_Unicode * pLookAhead = pBodyPtr;
|
|
if (*pLookAhead == '"')
|
|
{
|
|
pLookAhead
|
|
= skipQuotedString(pLookAhead,
|
|
pBodyEnd);
|
|
if (pLookAhead == pBodyPtr)
|
|
pLookAhead = pBodyEnd;
|
|
}
|
|
else
|
|
while (pLookAhead != pBodyEnd
|
|
&& (isAtomChar(*pLookAhead)
|
|
|| !isUSASCII(
|
|
*pLookAhead)))
|
|
++pLookAhead;
|
|
while (pLookAhead != pBodyEnd)
|
|
switch (*pLookAhead)
|
|
{
|
|
case '\t':
|
|
case ' ':
|
|
++pLookAhead;
|
|
break;
|
|
|
|
case '(':
|
|
{
|
|
const sal_Unicode * pPast
|
|
= skipComment(pLookAhead,
|
|
pBodyEnd);
|
|
pLookAhead
|
|
= pPast == pLookAhead ?
|
|
pBodyEnd : pPast;
|
|
break;
|
|
}
|
|
|
|
case ',':
|
|
case '.':
|
|
case ';':
|
|
case '>':
|
|
case '@':
|
|
eEntity = ENTITY_NON_PHRASE;
|
|
goto entity_determined;
|
|
|
|
case ':':
|
|
eEntity
|
|
= eBrackets
|
|
== BRACKETS_OUTSIDE ?
|
|
ENTITY_PHRASE :
|
|
ENTITY_NON_PHRASE;
|
|
goto entity_determined;
|
|
|
|
case '\x0D': // CR
|
|
if (startsWithLineFolding(
|
|
pLookAhead, pBodyEnd))
|
|
{
|
|
pLookAhead += 3;
|
|
break;
|
|
}
|
|
default:
|
|
eEntity = ENTITY_PHRASE;
|
|
goto entity_determined;
|
|
}
|
|
eEntity = ENTITY_NON_PHRASE;
|
|
entity_determined:
|
|
break;
|
|
}
|
|
|
|
case HEADER_FIELD_TEXT:
|
|
OSL_ASSERT(false);
|
|
break;
|
|
}
|
|
|
|
// In a 'non-phrase', a non-US-ASCII character
|
|
// cannot be part of an <atom>, but instead the
|
|
// whole entity is 'junk' rather than 'non-
|
|
// phrase':
|
|
if (eEntity == ENTITY_NON_PHRASE
|
|
&& !isUSASCII(*pBodyPtr))
|
|
eEntity = ENTITY_JUNK;
|
|
break;
|
|
}
|
|
|
|
switch (eEntity)
|
|
{
|
|
case ENTITY_JUNK:
|
|
{
|
|
// Write a pending '<' if necessary:
|
|
if (eBrackets == BRACKETS_OPENING)
|
|
{
|
|
if (rSink.getColumn()
|
|
+ (bInitialSpace ? 1 : 0)
|
|
>= rSink.getLineLengthLimit())
|
|
rSink << INetMIMEOutputSink::endl << ' ';
|
|
else if (bInitialSpace)
|
|
rSink << ' ';
|
|
rSink << '<';
|
|
bInitialSpace = false;
|
|
eBrackets = BRACKETS_INSIDE;
|
|
}
|
|
|
|
// Calculate the length of in- and output:
|
|
const sal_Unicode * pStart = pBodyPtr;
|
|
sal_Size nLength = 0;
|
|
bool bModify = false;
|
|
bool bEnd = false;
|
|
while (pBodyPtr != pBodyEnd && !bEnd)
|
|
switch (*pBodyPtr)
|
|
{
|
|
case '\x0D': // CR
|
|
if (startsWithLineFolding(pBodyPtr,
|
|
pBodyEnd))
|
|
bEnd = true;
|
|
else if (startsWithLineBreak(
|
|
pBodyPtr, pBodyEnd))
|
|
{
|
|
nLength += 3;
|
|
bModify = true;
|
|
pBodyPtr += 2;
|
|
}
|
|
else
|
|
{
|
|
++nLength;
|
|
++pBodyPtr;
|
|
}
|
|
break;
|
|
|
|
case '\t':
|
|
case ' ':
|
|
bEnd = true;
|
|
break;
|
|
|
|
default:
|
|
if (isVisible(*pBodyPtr))
|
|
bEnd = true;
|
|
else if (isUSASCII(*pBodyPtr))
|
|
{
|
|
++nLength;
|
|
++pBodyPtr;
|
|
}
|
|
else
|
|
{
|
|
nLength += getUTF8OctetCount(
|
|
*pBodyPtr++);
|
|
bModify = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Write the output:
|
|
if (rSink.getColumn() + (bInitialSpace ? 1 : 0)
|
|
+ nLength
|
|
> rSink.getLineLengthLimit())
|
|
rSink << INetMIMEOutputSink::endl << ' ';
|
|
else if (bInitialSpace)
|
|
rSink << ' ';
|
|
bInitialSpace = false;
|
|
if (bModify)
|
|
while (pStart != pBodyPtr)
|
|
if (startsWithLineBreak(pStart, pBodyPtr))
|
|
{
|
|
rSink << "\x0D\\\x0A"; // CR, '\', LF
|
|
pStart += 2;
|
|
}
|
|
else
|
|
writeUTF8(rSink, *pStart++);
|
|
else
|
|
rSink.write(pStart, pBodyPtr);
|
|
break;
|
|
}
|
|
|
|
case ENTITY_NON_PHRASE:
|
|
{
|
|
// Calculate the length of in- and output:
|
|
const sal_Unicode * pStart = pBodyPtr;
|
|
sal_Size nLength = 0;
|
|
bool bBracketedBlock = false;
|
|
bool bSymbol = *pStart != '.' && *pStart != '@';
|
|
bool bModify = false;
|
|
bool bEnd = false;
|
|
while (pBodyPtr != pBodyEnd && !bEnd)
|
|
switch (*pBodyPtr)
|
|
{
|
|
case '\t':
|
|
case ' ':
|
|
case '\x0D': // CR
|
|
{
|
|
const sal_Unicode * pLookAhead
|
|
= skipLinearWhiteSpace(pBodyPtr,
|
|
pBodyEnd);
|
|
if (pLookAhead < pBodyEnd
|
|
&& (bSymbol ?
|
|
isAtomChar(*pLookAhead)
|
|
|| *pLookAhead == '"'
|
|
|| *pLookAhead == '[' :
|
|
*pLookAhead == '.'
|
|
|| *pLookAhead == '@'
|
|
|| *pLookAhead == '>'
|
|
&& eType
|
|
>= HEADER_FIELD_MESSAGE_ID
|
|
&& eBrackets
|
|
== BRACKETS_OPENING))
|
|
{
|
|
bModify = true;
|
|
pBodyPtr = pLookAhead;
|
|
}
|
|
else
|
|
bEnd = true;
|
|
break;
|
|
}
|
|
|
|
case '"':
|
|
if (bSymbol)
|
|
{
|
|
pBodyPtr
|
|
= scanQuotedBlock(pBodyPtr,
|
|
pBodyEnd,
|
|
'"', '"',
|
|
nLength,
|
|
bModify);
|
|
bSymbol = false;
|
|
}
|
|
else
|
|
bEnd = true;
|
|
break;
|
|
|
|
case '[':
|
|
if (bSymbol)
|
|
{
|
|
pBodyPtr
|
|
= scanQuotedBlock(pBodyPtr,
|
|
pBodyEnd,
|
|
'[', ']',
|
|
nLength,
|
|
bModify);
|
|
bSymbol = false;
|
|
}
|
|
else
|
|
bEnd = true;
|
|
break;
|
|
|
|
case '.':
|
|
case '@':
|
|
if (bSymbol)
|
|
bEnd = true;
|
|
else
|
|
{
|
|
++nLength;
|
|
bSymbol = true;
|
|
++pBodyPtr;
|
|
}
|
|
break;
|
|
|
|
case '>':
|
|
if (eBrackets == BRACKETS_OPENING
|
|
&& eType
|
|
>= HEADER_FIELD_MESSAGE_ID)
|
|
{
|
|
++nLength;
|
|
bBracketedBlock = true;
|
|
++pBodyPtr;
|
|
}
|
|
bEnd = true;
|
|
break;
|
|
|
|
default:
|
|
if (isAtomChar(*pBodyPtr) && bSymbol)
|
|
{
|
|
while (pBodyPtr != pBodyEnd
|
|
&& isAtomChar(*pBodyPtr))
|
|
{
|
|
++nLength;
|
|
++pBodyPtr;
|
|
}
|
|
bSymbol = false;
|
|
}
|
|
else
|
|
{
|
|
if (!isUSASCII(*pBodyPtr))
|
|
bModify = true;
|
|
bEnd = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Write a pending '<' if necessary:
|
|
if (eBrackets == BRACKETS_OPENING
|
|
&& !bBracketedBlock)
|
|
{
|
|
if (rSink.getColumn()
|
|
+ (bInitialSpace ? 1 : 0)
|
|
>= rSink.getLineLengthLimit())
|
|
rSink << INetMIMEOutputSink::endl << ' ';
|
|
else if (bInitialSpace)
|
|
rSink << ' ';
|
|
rSink << '<';
|
|
bInitialSpace = false;
|
|
eBrackets = BRACKETS_INSIDE;
|
|
}
|
|
|
|
// Write the output:
|
|
if (rSink.getColumn() + (bInitialSpace ? 1 : 0)
|
|
+ nLength
|
|
> rSink.getLineLengthLimit())
|
|
rSink << INetMIMEOutputSink::endl << ' ';
|
|
else if (bInitialSpace)
|
|
rSink << ' ';
|
|
bInitialSpace = false;
|
|
if (bBracketedBlock)
|
|
{
|
|
rSink << '<';
|
|
eBrackets = BRACKETS_OUTSIDE;
|
|
}
|
|
if (bModify)
|
|
{
|
|
enum Mode { MODE_PLAIN, MODE_QUOTED_STRING,
|
|
MODE_DOMAIN_LITERAL };
|
|
Mode eMode = MODE_PLAIN;
|
|
while (pStart != pBodyPtr)
|
|
switch (*pStart)
|
|
{
|
|
case '\x0D': // CR
|
|
if (startsWithLineFolding(
|
|
pStart, pBodyPtr))
|
|
{
|
|
if (eMode != MODE_PLAIN)
|
|
rSink << sal_Char(
|
|
pStart[2]);
|
|
pStart += 3;
|
|
}
|
|
else if (startsWithLineBreak(
|
|
pStart, pBodyPtr))
|
|
{
|
|
rSink << "\x0D\\\x0A";
|
|
// CR, '\', LF
|
|
pStart += 2;
|
|
}
|
|
else
|
|
{
|
|
rSink << '\x0D'; // CR
|
|
++pStart;
|
|
}
|
|
break;
|
|
|
|
case '\t':
|
|
case ' ':
|
|
if (eMode != MODE_PLAIN)
|
|
rSink << sal_Char(*pStart);
|
|
++pStart;
|
|
break;
|
|
|
|
case '"':
|
|
if (eMode == MODE_PLAIN)
|
|
eMode = MODE_QUOTED_STRING;
|
|
else if (eMode
|
|
== MODE_QUOTED_STRING)
|
|
eMode = MODE_PLAIN;
|
|
rSink << '"';
|
|
++pStart;
|
|
break;
|
|
|
|
case '[':
|
|
if (eMode == MODE_PLAIN)
|
|
eMode = MODE_DOMAIN_LITERAL;
|
|
rSink << '[';
|
|
++pStart;
|
|
break;
|
|
|
|
case ']':
|
|
if (eMode == MODE_DOMAIN_LITERAL)
|
|
eMode = MODE_PLAIN;
|
|
rSink << ']';
|
|
++pStart;
|
|
break;
|
|
|
|
case '\\':
|
|
rSink << '\\';
|
|
if (++pStart < pBodyPtr)
|
|
writeUTF8(rSink, *pStart++);
|
|
break;
|
|
|
|
default:
|
|
writeUTF8(rSink, *pStart++);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
rSink.write(pStart, pBodyPtr);
|
|
break;
|
|
}
|
|
|
|
case ENTITY_PHRASE:
|
|
{
|
|
// Write a pending '<' if necessary:
|
|
if (eBrackets == BRACKETS_OPENING)
|
|
{
|
|
if (rSink.getColumn()
|
|
+ (bInitialSpace ? 1 : 0)
|
|
>= rSink.getLineLengthLimit())
|
|
rSink << INetMIMEOutputSink::endl << ' ';
|
|
else if (bInitialSpace)
|
|
rSink << ' ';
|
|
rSink << '<';
|
|
bInitialSpace = false;
|
|
eBrackets = BRACKETS_INSIDE;
|
|
}
|
|
|
|
// Calculate the length of in- and output:
|
|
const sal_Unicode * pStart = pBodyPtr;
|
|
bool bQuotedString = false;
|
|
bool bEnd = false;
|
|
while (pBodyPtr != pBodyEnd && !bEnd)
|
|
switch (*pBodyPtr)
|
|
{
|
|
case '\t':
|
|
case ' ':
|
|
case '\x0D': // CR
|
|
if (bQuotedString)
|
|
++pBodyPtr;
|
|
else
|
|
{
|
|
const sal_Unicode * pLookAhead
|
|
= skipLinearWhiteSpace(
|
|
pBodyPtr, pBodyEnd);
|
|
if (pLookAhead != pBodyEnd
|
|
&& (isAtomChar(*pLookAhead)
|
|
|| !isUSASCII(*pLookAhead)
|
|
|| *pLookAhead == '"'))
|
|
pBodyPtr = pLookAhead;
|
|
else
|
|
bEnd = true;
|
|
}
|
|
break;
|
|
|
|
case '"':
|
|
bQuotedString = !bQuotedString;
|
|
++pBodyPtr;
|
|
break;
|
|
|
|
case '\\':
|
|
if (bQuotedString)
|
|
{
|
|
if (++pBodyPtr != pBodyEnd)
|
|
++pBodyPtr;
|
|
}
|
|
else
|
|
bEnd = true;
|
|
break;
|
|
|
|
default:
|
|
if (bQuotedString
|
|
|| isAtomChar(*pBodyPtr)
|
|
|| !isUSASCII(*pBodyPtr))
|
|
++pBodyPtr;
|
|
else
|
|
bEnd = true;
|
|
break;
|
|
}
|
|
|
|
// Write the phrase, introducing encoded-words
|
|
// where necessary:
|
|
INetMIMEEncodedWordOutputSink
|
|
aOutput(
|
|
rSink,
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,
|
|
bInitialSpace ?
|
|
INetMIMEEncodedWordOutputSink::SPACE_ALWAYS :
|
|
INetMIMEEncodedWordOutputSink::SPACE_ENCODED,
|
|
ePreferredEncoding);
|
|
while (pStart != pBodyPtr)
|
|
switch (*pStart)
|
|
{
|
|
case '"':
|
|
++pStart;
|
|
break;
|
|
|
|
case '\\':
|
|
if (++pStart != pBodyPtr)
|
|
aOutput << *pStart++;
|
|
break;
|
|
|
|
case '\x0D': // CR
|
|
pStart += 2;
|
|
aOutput << *pStart++;
|
|
break;
|
|
|
|
default:
|
|
aOutput << *pStart++;
|
|
break;
|
|
}
|
|
bInitialSpace = aOutput.flush();
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
bool INetMIME::translateUTF8Char(const sal_Char *& rBegin,
|
|
const sal_Char * pEnd,
|
|
rtl_TextEncoding eEncoding,
|
|
sal_uInt32 & rCharacter)
|
|
{
|
|
if (rBegin == pEnd || static_cast< unsigned char >(*rBegin) < 0x80
|
|
|| static_cast< unsigned char >(*rBegin) >= 0xFE)
|
|
return false;
|
|
|
|
int nCount;
|
|
sal_uInt32 nMin;
|
|
sal_uInt32 nUCS4;
|
|
const sal_Char * p = rBegin;
|
|
if (static_cast< unsigned char >(*p) < 0xE0)
|
|
{
|
|
nCount = 1;
|
|
nMin = 0x80;
|
|
nUCS4 = static_cast< unsigned char >(*p) & 0x1F;
|
|
}
|
|
else if (static_cast< unsigned char >(*p) < 0xF0)
|
|
{
|
|
nCount = 2;
|
|
nMin = 0x800;
|
|
nUCS4 = static_cast< unsigned char >(*p) & 0xF;
|
|
}
|
|
else if (static_cast< unsigned char >(*p) < 0xF8)
|
|
{
|
|
nCount = 3;
|
|
nMin = 0x10000;
|
|
nUCS4 = static_cast< unsigned char >(*p) & 7;
|
|
}
|
|
else if (static_cast< unsigned char >(*p) < 0xFC)
|
|
{
|
|
nCount = 4;
|
|
nMin = 0x200000;
|
|
nUCS4 = static_cast< unsigned char >(*p) & 3;
|
|
}
|
|
else
|
|
{
|
|
nCount = 5;
|
|
nMin = 0x4000000;
|
|
nUCS4 = static_cast< unsigned char >(*p) & 1;
|
|
}
|
|
++p;
|
|
|
|
for (; nCount-- > 0; ++p)
|
|
if ((static_cast< unsigned char >(*p) & 0xC0) == 0x80)
|
|
nUCS4 = nUCS4 << 6 | static_cast< unsigned char >(*p) & 0x3F;
|
|
else
|
|
return false;
|
|
|
|
if (nUCS4 < nMin || nUCS4 > 0x10FFFF)
|
|
return false;
|
|
|
|
if (eEncoding >= RTL_TEXTENCODING_UCS4)
|
|
rCharacter = nUCS4;
|
|
else
|
|
{
|
|
sal_Unicode aUTF16[2];
|
|
const sal_Unicode * pUTF16End = putUTF32Character(aUTF16, nUCS4);
|
|
sal_Size nSize;
|
|
sal_Char * pBuffer = convertFromUnicode(aUTF16, pUTF16End, eEncoding,
|
|
nSize);
|
|
if (!pBuffer)
|
|
return false;
|
|
DBG_ASSERT(nSize == 1,
|
|
"INetMIME::translateUTF8Char(): Bad conversion");
|
|
rCharacter = *pBuffer;
|
|
delete[] pBuffer;
|
|
}
|
|
rBegin = p;
|
|
return true;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
ByteString INetMIME::decodeUTF8(const ByteString & rText,
|
|
rtl_TextEncoding eEncoding)
|
|
{
|
|
const sal_Char * p = rText.GetBuffer();
|
|
const sal_Char * pEnd = p + rText.Len();
|
|
ByteString sDecoded;
|
|
while (p != pEnd)
|
|
{
|
|
sal_uInt32 nCharacter;
|
|
if (translateUTF8Char(p, pEnd, eEncoding, nCharacter))
|
|
sDecoded += sal_Char(nCharacter);
|
|
else
|
|
sDecoded += sal_Char(*p++);
|
|
}
|
|
return sDecoded;
|
|
}
|
|
|
|
//============================================================================
|
|
// static
|
|
UniString INetMIME::decodeHeaderFieldBody(HeaderFieldType eType,
|
|
const ByteString & rBody)
|
|
{
|
|
// Due to a bug in INetCoreRFC822MessageStream::ConvertTo7Bit(), old
|
|
// versions of StarOffice send mails with header fields where encoded
|
|
// words can be preceded by '=', ',', '.', '"', or '(', and followed by
|
|
// '=', ',', '.', '"', ')', without any required white space in between.
|
|
// And there appear to exist some broken mailers that only encode single
|
|
// letters within words, like "Appel
|
|
// =?iso-8859-1?Q?=E0?=t=?iso-8859-1?Q?=E9?=moin", so it seems best to
|
|
// detect encoded words even when not propperly surrounded by white space.
|
|
//
|
|
// Non US-ASCII characters in rBody are treated as ISO-8859-1.
|
|
//
|
|
// encoded-word = "=?"
|
|
// 1*(%x21 / %x23-27 / %x2A-2B / %x2D / %30-39 / %x41-5A / %x5E-7E)
|
|
// ["*" 1*8ALPHA *("-" 1*8ALPHA)] "?"
|
|
// ("B?" *(4base64) (4base64 / 3base64 "=" / 2base64 "==")
|
|
// / "Q?" 1*(%x21-3C / %x3E / %x40-7E / "=" 2HEXDIG))
|
|
// "?="
|
|
//
|
|
// base64 = ALPHA / DIGIT / "+" / "/"
|
|
|
|
const sal_Char * pBegin = rBody.GetBuffer();
|
|
const sal_Char * pEnd = pBegin + rBody.Len();
|
|
|
|
UniString sDecoded;
|
|
const sal_Char * pCopyBegin = pBegin;
|
|
|
|
/* bool bStartEncodedWord = true; */
|
|
const sal_Char * pWSPBegin = pBegin;
|
|
UniString sEncodedText;
|
|
bool bQuotedEncodedText = false;
|
|
sal_uInt32 nCommentLevel = 0;
|
|
|
|
for (const sal_Char * p = pBegin; p != pEnd;)
|
|
{
|
|
if (p != pEnd && *p == '=' /* && bStartEncodedWord */)
|
|
{
|
|
const sal_Char * q = p + 1;
|
|
bool bEncodedWord = q != pEnd && *q++ == '?';
|
|
|
|
rtl_TextEncoding eCharsetEncoding = RTL_TEXTENCODING_DONTKNOW;
|
|
if (bEncodedWord)
|
|
{
|
|
const sal_Char * pCharsetBegin = q;
|
|
const sal_Char * pLanguageBegin = 0;
|
|
int nAlphaCount = 0;
|
|
for (bool bDone = false; !bDone;)
|
|
if (q == pEnd)
|
|
{
|
|
bEncodedWord = false;
|
|
bDone = true;
|
|
}
|
|
else
|
|
{
|
|
sal_Char cChar = *q++;
|
|
switch (cChar)
|
|
{
|
|
case '*':
|
|
pLanguageBegin = q - 1;
|
|
nAlphaCount = 0;
|
|
break;
|
|
|
|
case '-':
|
|
if (pLanguageBegin != 0)
|
|
{
|
|
if (nAlphaCount == 0)
|
|
pLanguageBegin = 0;
|
|
else
|
|
nAlphaCount = 0;
|
|
}
|
|
break;
|
|
|
|
case '?':
|
|
if (pCharsetBegin == q - 1)
|
|
bEncodedWord = false;
|
|
else
|
|
{
|
|
eCharsetEncoding
|
|
= getCharsetEncoding(
|
|
pCharsetBegin,
|
|
pLanguageBegin == 0
|
|
|| nAlphaCount == 0 ?
|
|
q - 1 : pLanguageBegin);
|
|
bEncodedWord = isMIMECharsetEncoding(
|
|
eCharsetEncoding);
|
|
eCharsetEncoding
|
|
= translateFromMIME(eCharsetEncoding);
|
|
}
|
|
bDone = true;
|
|
break;
|
|
|
|
default:
|
|
if (pLanguageBegin != 0
|
|
&& (!isAlpha(cChar) || ++nAlphaCount > 8))
|
|
pLanguageBegin = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool bEncodingB = false;
|
|
if (bEncodedWord)
|
|
if (q == pEnd)
|
|
bEncodedWord = false;
|
|
else
|
|
switch (*q++)
|
|
{
|
|
case 'B':
|
|
case 'b':
|
|
bEncodingB = true;
|
|
break;
|
|
|
|
case 'Q':
|
|
case 'q':
|
|
bEncodingB = false;
|
|
break;
|
|
|
|
default:
|
|
bEncodedWord = false;
|
|
break;
|
|
}
|
|
|
|
bEncodedWord = bEncodedWord && q != pEnd && *q++ == '?';
|
|
|
|
ByteString sText;
|
|
if (bEncodedWord)
|
|
if (bEncodingB)
|
|
for (bool bDone = false; !bDone;)
|
|
{
|
|
if (pEnd - q < 4)
|
|
{
|
|
bEncodedWord = false;
|
|
bDone = true;
|
|
}
|
|
else
|
|
{
|
|
bool bFinal = false;
|
|
int nCount = 3;
|
|
sal_uInt32 nValue = 0;
|
|
for (int nShift = 18; nShift >= 0; nShift -= 6)
|
|
{
|
|
int nWeight = getBase64Weight(*q++);
|
|
if (nWeight == -2)
|
|
{
|
|
bEncodedWord = false;
|
|
bDone = true;
|
|
break;
|
|
}
|
|
if (nWeight == -1)
|
|
{
|
|
if (!bFinal)
|
|
{
|
|
if (nShift >= 12)
|
|
{
|
|
bEncodedWord = false;
|
|
bDone = true;
|
|
break;
|
|
}
|
|
bFinal = true;
|
|
nCount = nShift == 6 ? 1 : 2;
|
|
}
|
|
}
|
|
else
|
|
nValue |= nWeight << nShift;
|
|
}
|
|
if (bEncodedWord)
|
|
{
|
|
for (int nShift = 16; nCount-- > 0;
|
|
nShift -= 8)
|
|
sText += sal_Char(nValue >> nShift
|
|
& 0xFF);
|
|
if (*q == '?')
|
|
{
|
|
++q;
|
|
bDone = true;
|
|
}
|
|
if (bFinal && !bDone)
|
|
{
|
|
bEncodedWord = false;
|
|
bDone = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const sal_Char * pEncodedTextBegin = q;
|
|
const sal_Char * pEncodedTextCopyBegin = q;
|
|
for (bool bDone = false; !bDone;)
|
|
if (q == pEnd)
|
|
{
|
|
bEncodedWord = false;
|
|
bDone = true;
|
|
}
|
|
else
|
|
{
|
|
sal_uInt32 nChar = *q++;
|
|
switch (nChar)
|
|
{
|
|
case '=':
|
|
{
|
|
if (pEnd - q < 2)
|
|
{
|
|
bEncodedWord = false;
|
|
bDone = true;
|
|
break;
|
|
}
|
|
int nDigit1 = getHexWeight(q[0]);
|
|
int nDigit2 = getHexWeight(q[1]);
|
|
if (nDigit1 < 0 || nDigit2 < 0)
|
|
{
|
|
bEncodedWord = false;
|
|
bDone = true;
|
|
break;
|
|
}
|
|
sText += rBody.Copy(
|
|
static_cast< xub_StrLen >(
|
|
pEncodedTextCopyBegin - pBegin),
|
|
static_cast< xub_StrLen >(
|
|
q - 1 - pEncodedTextCopyBegin));
|
|
sText += sal_Char(nDigit1 << 4 | nDigit2);
|
|
q += 2;
|
|
pEncodedTextCopyBegin = q;
|
|
break;
|
|
}
|
|
|
|
case '?':
|
|
if (q - pEncodedTextBegin > 1)
|
|
sText += rBody.Copy(
|
|
static_cast< xub_StrLen >(
|
|
pEncodedTextCopyBegin - pBegin),
|
|
static_cast< xub_StrLen >(
|
|
q - 1 - pEncodedTextCopyBegin));
|
|
else
|
|
bEncodedWord = false;
|
|
bDone = true;
|
|
break;
|
|
|
|
case '_':
|
|
sText += rBody.Copy(
|
|
static_cast< xub_StrLen >(
|
|
pEncodedTextCopyBegin - pBegin),
|
|
static_cast< xub_StrLen >(
|
|
q - 1 - pEncodedTextCopyBegin));
|
|
sText += ' ';
|
|
pEncodedTextCopyBegin = q;
|
|
break;
|
|
|
|
default:
|
|
if (!isVisible(nChar))
|
|
{
|
|
bEncodedWord = false;
|
|
bDone = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bEncodedWord = bEncodedWord && q != pEnd && *q++ == '=';
|
|
|
|
// if (bEncodedWord && q != pEnd)
|
|
// switch (*q)
|
|
// {
|
|
// case '\t':
|
|
// case ' ':
|
|
// case '"':
|
|
// case ')':
|
|
// case ',':
|
|
// case '.':
|
|
// case '=':
|
|
// break;
|
|
//
|
|
// default:
|
|
// bEncodedWord = false;
|
|
// break;
|
|
// }
|
|
|
|
sal_Unicode * pUnicodeBuffer = 0;
|
|
sal_Size nUnicodeSize = 0;
|
|
if (bEncodedWord)
|
|
{
|
|
pUnicodeBuffer
|
|
= convertToUnicode(sText.GetBuffer(),
|
|
sText.GetBuffer() + sText.Len(),
|
|
eCharsetEncoding, nUnicodeSize);
|
|
if (pUnicodeBuffer == 0)
|
|
bEncodedWord = false;
|
|
}
|
|
|
|
if (bEncodedWord)
|
|
{
|
|
appendISO88591(sDecoded, pCopyBegin, pWSPBegin);
|
|
if (eType == HEADER_FIELD_TEXT)
|
|
sDecoded.Append(
|
|
pUnicodeBuffer,
|
|
static_cast< xub_StrLen >(nUnicodeSize));
|
|
else if (nCommentLevel == 0)
|
|
{
|
|
sEncodedText.Append(
|
|
pUnicodeBuffer,
|
|
static_cast< xub_StrLen >(nUnicodeSize));
|
|
if (!bQuotedEncodedText)
|
|
{
|
|
const sal_Unicode * pTextPtr = pUnicodeBuffer;
|
|
const sal_Unicode * pTextEnd = pTextPtr
|
|
+ nUnicodeSize;
|
|
for (; pTextPtr != pTextEnd; ++pTextPtr)
|
|
if (!isEncodedWordTokenChar(*pTextPtr))
|
|
{
|
|
bQuotedEncodedText = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const sal_Unicode * pTextPtr = pUnicodeBuffer;
|
|
const sal_Unicode * pTextEnd = pTextPtr + nUnicodeSize;
|
|
for (; pTextPtr != pTextEnd; ++pTextPtr)
|
|
{
|
|
switch (*pTextPtr)
|
|
{
|
|
case '(':
|
|
case ')':
|
|
case '\\':
|
|
case '\x0D':
|
|
case '=':
|
|
sDecoded += '\\';
|
|
break;
|
|
}
|
|
sDecoded += *pTextPtr;
|
|
}
|
|
}
|
|
delete[] pUnicodeBuffer;
|
|
p = q;
|
|
pCopyBegin = p;
|
|
|
|
pWSPBegin = p;
|
|
while (p != pEnd && isWhiteSpace(*p))
|
|
++p;
|
|
/* bStartEncodedWord = p != pWSPBegin; */
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (sEncodedText.Len() != 0)
|
|
{
|
|
if (bQuotedEncodedText)
|
|
{
|
|
sDecoded += '"';
|
|
const sal_Unicode * pTextPtr = sEncodedText.GetBuffer();
|
|
const sal_Unicode * pTextEnd = pTextPtr + sEncodedText.Len();
|
|
for (;pTextPtr != pTextEnd; ++pTextPtr)
|
|
{
|
|
switch (*pTextPtr)
|
|
{
|
|
case '"':
|
|
case '\\':
|
|
case '\x0D':
|
|
sDecoded += '\\';
|
|
break;
|
|
}
|
|
sDecoded += *pTextPtr;
|
|
}
|
|
sDecoded += '"';
|
|
}
|
|
else
|
|
sDecoded += sEncodedText;
|
|
sEncodedText.Erase();
|
|
bQuotedEncodedText = false;
|
|
}
|
|
|
|
if (p == pEnd)
|
|
break;
|
|
|
|
switch (*p++)
|
|
{
|
|
// case '\t':
|
|
// case ' ':
|
|
// case ',':
|
|
// case '.':
|
|
// case '=':
|
|
// bStartEncodedWord = true;
|
|
// break;
|
|
|
|
case '"':
|
|
if (eType != HEADER_FIELD_TEXT && nCommentLevel == 0)
|
|
{
|
|
const sal_Char * pQuotedStringEnd
|
|
= skipQuotedString(p - 1, pEnd);
|
|
p = pQuotedStringEnd == p - 1 ? pEnd : pQuotedStringEnd;
|
|
}
|
|
/* bStartEncodedWord = true; */
|
|
break;
|
|
|
|
case '(':
|
|
if (eType != HEADER_FIELD_TEXT)
|
|
++nCommentLevel;
|
|
/* bStartEncodedWord = true; */
|
|
break;
|
|
|
|
case ')':
|
|
if (nCommentLevel > 0)
|
|
--nCommentLevel;
|
|
/* bStartEncodedWord = false; */
|
|
break;
|
|
|
|
default:
|
|
{
|
|
const sal_Char * pUTF8Begin = p - 1;
|
|
const sal_Char * pUTF8End = pUTF8Begin;
|
|
sal_uInt32 nCharacter;
|
|
if (translateUTF8Char(pUTF8End, pEnd, RTL_TEXTENCODING_UCS4,
|
|
nCharacter))
|
|
{
|
|
appendISO88591(sDecoded, pCopyBegin, p - 1);
|
|
sal_Unicode aUTF16Buf[2];
|
|
xub_StrLen nUTF16Len = static_cast< xub_StrLen >(
|
|
putUTF32Character(aUTF16Buf, nCharacter) - aUTF16Buf);
|
|
sDecoded.Append(aUTF16Buf, nUTF16Len);
|
|
p = pUTF8End;
|
|
pCopyBegin = p;
|
|
}
|
|
/* bStartEncodedWord = false; */
|
|
break;
|
|
}
|
|
}
|
|
pWSPBegin = p;
|
|
}
|
|
|
|
appendISO88591(sDecoded, pCopyBegin, pEnd);
|
|
return sDecoded;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// INetMIMEOutputSink
|
|
//
|
|
//============================================================================
|
|
|
|
// virtual
|
|
sal_Size INetMIMEOutputSink::writeSequence(const sal_Char * pSequence)
|
|
{
|
|
sal_Size nLength = rtl_str_getLength(pSequence);
|
|
writeSequence(pSequence, pSequence + nLength);
|
|
return nLength;
|
|
}
|
|
|
|
//============================================================================
|
|
// virtual
|
|
void INetMIMEOutputSink::writeSequence(const sal_uInt32 * pBegin,
|
|
const sal_uInt32 * pEnd)
|
|
{
|
|
DBG_ASSERT(pBegin && pBegin <= pEnd,
|
|
"INetMIMEOutputSink::writeSequence(): Bad sequence");
|
|
|
|
sal_Char * pBufferBegin = new sal_Char[pEnd - pBegin];
|
|
sal_Char * pBufferEnd = pBufferBegin;
|
|
while (pBegin != pEnd)
|
|
{
|
|
DBG_ASSERT(*pBegin < 256,
|
|
"INetMIMEOutputSink::writeSequence(): Bad octet");
|
|
*pBufferEnd++ = sal_Char(*pBegin++);
|
|
}
|
|
writeSequence(pBufferBegin, pBufferEnd);
|
|
delete[] pBufferBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// virtual
|
|
void INetMIMEOutputSink::writeSequence(const sal_Unicode * pBegin,
|
|
const sal_Unicode * pEnd)
|
|
{
|
|
DBG_ASSERT(pBegin && pBegin <= pEnd,
|
|
"INetMIMEOutputSink::writeSequence(): Bad sequence");
|
|
|
|
sal_Char * pBufferBegin = new sal_Char[pEnd - pBegin];
|
|
sal_Char * pBufferEnd = pBufferBegin;
|
|
while (pBegin != pEnd)
|
|
{
|
|
DBG_ASSERT(*pBegin < 256,
|
|
"INetMIMEOutputSink::writeSequence(): Bad octet");
|
|
*pBufferEnd++ = sal_Char(*pBegin++);
|
|
}
|
|
writeSequence(pBufferBegin, pBufferEnd);
|
|
delete[] pBufferBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// virtual
|
|
ErrCode INetMIMEOutputSink::getError() const
|
|
{
|
|
return ERRCODE_NONE;
|
|
}
|
|
|
|
//============================================================================
|
|
void INetMIMEOutputSink::writeLineEnd()
|
|
{
|
|
static const sal_Char aCRLF[2] = { 0x0D, 0x0A };
|
|
writeSequence(aCRLF, aCRLF + 2);
|
|
m_nColumn = 0;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// INetMIMEStringOutputSink
|
|
//
|
|
//============================================================================
|
|
|
|
// virtual
|
|
void INetMIMEStringOutputSink::writeSequence(const sal_Char * pBegin,
|
|
const sal_Char * pEnd)
|
|
{
|
|
DBG_ASSERT(pBegin && pBegin <= pEnd,
|
|
"INetMIMEStringOutputSink::writeSequence(): Bad sequence");
|
|
|
|
m_bOverflow = m_bOverflow
|
|
|| pEnd - pBegin > STRING_MAXLEN - m_aBuffer.Len();
|
|
if (!m_bOverflow)
|
|
m_aBuffer.Append(pBegin, static_cast< xub_StrLen >(pEnd - pBegin));
|
|
}
|
|
|
|
//============================================================================
|
|
// virtual
|
|
ErrCode INetMIMEStringOutputSink::getError() const
|
|
{
|
|
return m_bOverflow ? ERRCODE_IO_OUTOFMEMORY : ERRCODE_NONE;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// INetMIMEUnicodeOutputSink
|
|
//
|
|
//============================================================================
|
|
|
|
// virtual
|
|
void INetMIMEUnicodeOutputSink::writeSequence(const sal_Char * pBegin,
|
|
const sal_Char * pEnd)
|
|
{
|
|
DBG_ASSERT(pBegin && pBegin <= pEnd,
|
|
"INetMIMEUnicodeOutputSink::writeSequence(): Bad sequence");
|
|
|
|
sal_Unicode * pBufferBegin = new sal_Unicode[pEnd - pBegin];
|
|
sal_Unicode * pBufferEnd = pBufferBegin;
|
|
while (pBegin != pEnd)
|
|
*pBufferEnd++ = sal_uChar(*pBegin++);
|
|
writeSequence(pBufferBegin, pBufferEnd);
|
|
delete[] pBufferBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// virtual
|
|
void INetMIMEUnicodeOutputSink::writeSequence(const sal_uInt32 * pBegin,
|
|
const sal_uInt32 * pEnd)
|
|
{
|
|
DBG_ASSERT(pBegin && pBegin <= pEnd,
|
|
"INetMIMEUnicodeOutputSink::writeSequence(): Bad sequence");
|
|
|
|
sal_Unicode * pBufferBegin = new sal_Unicode[pEnd - pBegin];
|
|
sal_Unicode * pBufferEnd = pBufferBegin;
|
|
while (pBegin != pEnd)
|
|
{
|
|
DBG_ASSERT(*pBegin < 256,
|
|
"INetMIMEOutputSink::writeSequence(): Bad octet");
|
|
*pBufferEnd++ = sal_Unicode(*pBegin++);
|
|
}
|
|
writeSequence(pBufferBegin, pBufferEnd);
|
|
delete[] pBufferBegin;
|
|
}
|
|
|
|
//============================================================================
|
|
// virtual
|
|
void INetMIMEUnicodeOutputSink::writeSequence(const sal_Unicode * pBegin,
|
|
const sal_Unicode * pEnd)
|
|
{
|
|
DBG_ASSERT(pBegin && pBegin <= pEnd,
|
|
"INetMIMEUnicodeOutputSink::writeSequence(): Bad sequence");
|
|
|
|
m_bOverflow = m_bOverflow
|
|
|| pEnd - pBegin > STRING_MAXLEN - m_aBuffer.Len();
|
|
if (!m_bOverflow)
|
|
m_aBuffer.Append(pBegin, static_cast< xub_StrLen >(pEnd - pBegin));
|
|
}
|
|
|
|
//============================================================================
|
|
// virtual
|
|
ErrCode INetMIMEUnicodeOutputSink::getError() const
|
|
{
|
|
return m_bOverflow ? ERRCODE_IO_OUTOFMEMORY : ERRCODE_NONE;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// INetMIMEEncodedWordOutputSink
|
|
//
|
|
//============================================================================
|
|
|
|
static const sal_Char aEscape[128]
|
|
= { INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x00
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x01
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x02
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x03
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x04
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x05
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x06
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x07
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x08
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x09
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x0A
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x0B
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x0C
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x0D
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x0E
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x0F
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x10
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x11
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x12
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x13
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x14
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x15
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x16
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x17
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x18
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x19
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x1A
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x1B
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x1C
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x1D
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x1E
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x1F
|
|
0, // ' '
|
|
0, // '!'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '"'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '#'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '$'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '%'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '&'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '''
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '('
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // ')'
|
|
0, // '*'
|
|
0, // '+'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // ','
|
|
0, // '-'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '.'
|
|
0, // '/'
|
|
0, // '0'
|
|
0, // '1'
|
|
0, // '2'
|
|
0, // '3'
|
|
0, // '4'
|
|
0, // '5'
|
|
0, // '6'
|
|
0, // '7'
|
|
0, // '8'
|
|
0, // '9'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // ':'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // ';'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '<'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '='
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '>'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '?'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '@'
|
|
0, // 'A'
|
|
0, // 'B'
|
|
0, // 'C'
|
|
0, // 'D'
|
|
0, // 'E'
|
|
0, // 'F'
|
|
0, // 'G'
|
|
0, // 'H'
|
|
0, // 'I'
|
|
0, // 'J'
|
|
0, // 'K'
|
|
0, // 'L'
|
|
0, // 'M'
|
|
0, // 'N'
|
|
0, // 'O'
|
|
0, // 'P'
|
|
0, // 'Q'
|
|
0, // 'R'
|
|
0, // 'S'
|
|
0, // 'T'
|
|
0, // 'U'
|
|
0, // 'V'
|
|
0, // 'W'
|
|
0, // 'X'
|
|
0, // 'Y'
|
|
0, // 'Z'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '['
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '\'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // ']'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '^'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '_'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '`'
|
|
0, // 'a'
|
|
0, // 'b'
|
|
0, // 'c'
|
|
0, // 'd'
|
|
0, // 'e'
|
|
0, // 'f'
|
|
0, // 'g'
|
|
0, // 'h'
|
|
0, // 'i'
|
|
0, // 'j'
|
|
0, // 'k'
|
|
0, // 'l'
|
|
0, // 'm'
|
|
0, // 'n'
|
|
0, // 'o'
|
|
0, // 'p'
|
|
0, // 'q'
|
|
0, // 'r'
|
|
0, // 's'
|
|
0, // 't'
|
|
0, // 'u'
|
|
0, // 'v'
|
|
0, // 'w'
|
|
0, // 'x'
|
|
0, // 'y'
|
|
0, // 'z'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '{'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '|'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '}'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '~'
|
|
INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE }; // DEL
|
|
|
|
inline bool
|
|
INetMIMEEncodedWordOutputSink::needsEncodedWordEscape(sal_uInt32 nChar) const
|
|
{
|
|
return !INetMIME::isUSASCII(nChar) || aEscape[nChar] & m_eContext;
|
|
}
|
|
|
|
//============================================================================
|
|
void INetMIMEEncodedWordOutputSink::finish(bool bWriteTrailer)
|
|
{
|
|
if (m_eInitialSpace == SPACE_ALWAYS && m_nExtraSpaces == 0)
|
|
m_nExtraSpaces = 1;
|
|
|
|
if (m_eEncodedWordState == STATE_SECOND_EQUALS)
|
|
{
|
|
// If the text is already an encoded word, copy it verbatim:
|
|
sal_uInt32 nSize = m_pBufferEnd - m_pBuffer;
|
|
switch (m_ePrevCoding)
|
|
{
|
|
case CODING_QUOTED:
|
|
m_rSink << '"';
|
|
case CODING_NONE:
|
|
if (m_eInitialSpace == SPACE_ENCODED && m_nExtraSpaces == 0)
|
|
m_nExtraSpaces = 1;
|
|
for (; m_nExtraSpaces > 1; --m_nExtraSpaces)
|
|
{
|
|
if (m_rSink.getColumn() >= m_rSink.getLineLengthLimit())
|
|
m_rSink << INetMIMEOutputSink::endl;
|
|
m_rSink << ' ';
|
|
}
|
|
if (m_nExtraSpaces == 1)
|
|
{
|
|
if (m_rSink.getColumn() + nSize
|
|
>= m_rSink.getLineLengthLimit())
|
|
m_rSink << INetMIMEOutputSink::endl;
|
|
m_rSink << ' ';
|
|
}
|
|
break;
|
|
|
|
case CODING_ENCODED:
|
|
{
|
|
const sal_Char * pCharsetName
|
|
= INetMIME::getCharsetName(m_ePrevMIMEEncoding);
|
|
while (m_nExtraSpaces-- > 0)
|
|
{
|
|
if (m_rSink.getColumn()
|
|
> m_rSink.getLineLengthLimit() - 3)
|
|
m_rSink << "?=" << INetMIMEOutputSink::endl << " =?"
|
|
<< pCharsetName << "?Q?";
|
|
m_rSink << '_';
|
|
}
|
|
m_rSink << "?=";
|
|
}
|
|
case CODING_ENCODED_TERMINATED:
|
|
if (m_rSink.getColumn() + nSize
|
|
> m_rSink.getLineLengthLimit() - 1)
|
|
m_rSink << INetMIMEOutputSink::endl;
|
|
m_rSink << ' ';
|
|
break;
|
|
}
|
|
m_rSink.write(m_pBuffer, m_pBufferEnd);
|
|
m_eCoding = CODING_ENCODED_TERMINATED;
|
|
}
|
|
else
|
|
{
|
|
// If the text itself is too long to fit into a single line, make it
|
|
// into multiple encoded words:
|
|
switch (m_eCoding)
|
|
{
|
|
case CODING_NONE:
|
|
if (m_nExtraSpaces == 0)
|
|
{
|
|
DBG_ASSERT(m_ePrevCoding == CODING_NONE
|
|
|| m_pBuffer == m_pBufferEnd,
|
|
"INetMIMEEncodedWordOutputSink::finish():"
|
|
" Bad state");
|
|
if (m_rSink.getColumn() + (m_pBufferEnd - m_pBuffer)
|
|
> m_rSink.getLineLengthLimit())
|
|
m_eCoding = CODING_ENCODED;
|
|
}
|
|
else
|
|
{
|
|
OSL_ASSERT(m_pBufferEnd >= m_pBuffer);
|
|
if (static_cast< std::size_t >(m_pBufferEnd - m_pBuffer)
|
|
> m_rSink.getLineLengthLimit() - 1)
|
|
{
|
|
m_eCoding = CODING_ENCODED;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CODING_QUOTED:
|
|
if (m_nExtraSpaces == 0)
|
|
{
|
|
DBG_ASSERT(m_ePrevCoding == CODING_NONE,
|
|
"INetMIMEEncodedWordOutputSink::finish():"
|
|
" Bad state");
|
|
if (m_rSink.getColumn() + (m_pBufferEnd - m_pBuffer)
|
|
+ m_nQuotedEscaped
|
|
> m_rSink.getLineLengthLimit() - 2)
|
|
m_eCoding = CODING_ENCODED;
|
|
}
|
|
else if ((m_pBufferEnd - m_pBuffer) + m_nQuotedEscaped
|
|
> m_rSink.getLineLengthLimit() - 3)
|
|
m_eCoding = CODING_ENCODED;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (m_eCoding)
|
|
{
|
|
case CODING_NONE:
|
|
switch (m_ePrevCoding)
|
|
{
|
|
case CODING_QUOTED:
|
|
if (m_rSink.getColumn() + m_nExtraSpaces
|
|
+ (m_pBufferEnd - m_pBuffer)
|
|
< m_rSink.getLineLengthLimit())
|
|
m_eCoding = CODING_QUOTED;
|
|
else
|
|
m_rSink << '"';
|
|
break;
|
|
|
|
case CODING_ENCODED:
|
|
m_rSink << "?=";
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
for (; m_nExtraSpaces > 1; --m_nExtraSpaces)
|
|
{
|
|
if (m_rSink.getColumn() >= m_rSink.getLineLengthLimit())
|
|
m_rSink << INetMIMEOutputSink::endl;
|
|
m_rSink << ' ';
|
|
}
|
|
if (m_nExtraSpaces == 1)
|
|
{
|
|
if (m_rSink.getColumn() + (m_pBufferEnd - m_pBuffer)
|
|
>= m_rSink.getLineLengthLimit())
|
|
m_rSink << INetMIMEOutputSink::endl;
|
|
m_rSink << ' ';
|
|
}
|
|
m_rSink.write(m_pBuffer, m_pBufferEnd);
|
|
if (m_eCoding == CODING_QUOTED && bWriteTrailer)
|
|
{
|
|
m_rSink << '"';
|
|
m_eCoding = CODING_NONE;
|
|
}
|
|
break;
|
|
|
|
case CODING_QUOTED:
|
|
{
|
|
bool bInsertLeadingQuote = true;
|
|
sal_uInt32 nSize = (m_pBufferEnd - m_pBuffer)
|
|
+ m_nQuotedEscaped + 2;
|
|
switch (m_ePrevCoding)
|
|
{
|
|
case CODING_QUOTED:
|
|
if (m_rSink.getColumn() + m_nExtraSpaces + nSize - 1
|
|
< m_rSink.getLineLengthLimit())
|
|
{
|
|
bInsertLeadingQuote = false;
|
|
--nSize;
|
|
}
|
|
else
|
|
m_rSink << '"';
|
|
break;
|
|
|
|
case CODING_ENCODED:
|
|
m_rSink << "?=";
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
for (; m_nExtraSpaces > 1; --m_nExtraSpaces)
|
|
{
|
|
if (m_rSink.getColumn() >= m_rSink.getLineLengthLimit())
|
|
m_rSink << INetMIMEOutputSink::endl;
|
|
m_rSink << ' ';
|
|
}
|
|
if (m_nExtraSpaces == 1)
|
|
{
|
|
if (m_rSink.getColumn() + nSize
|
|
>= m_rSink.getLineLengthLimit())
|
|
m_rSink << INetMIMEOutputSink::endl;
|
|
m_rSink << ' ';
|
|
}
|
|
if (bInsertLeadingQuote)
|
|
m_rSink << '"';
|
|
for (const sal_Unicode * p = m_pBuffer; p != m_pBufferEnd;
|
|
++p)
|
|
{
|
|
if (INetMIME::needsQuotedStringEscape(*p))
|
|
m_rSink << '\\';
|
|
m_rSink << sal_Char(*p);
|
|
}
|
|
if (bWriteTrailer)
|
|
{
|
|
m_rSink << '"';
|
|
m_eCoding = CODING_NONE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CODING_ENCODED:
|
|
{
|
|
rtl_TextEncoding eCharsetEncoding
|
|
= m_pEncodingList->
|
|
getPreferredEncoding(RTL_TEXTENCODING_UTF8);
|
|
rtl_TextEncoding eMIMEEncoding
|
|
= INetMIME::translateToMIME(eCharsetEncoding);
|
|
|
|
// The non UTF-8 code will only work for stateless single byte
|
|
// character encodings (see also below):
|
|
sal_Char * pTargetBuffer = NULL;
|
|
sal_Size nTargetSize = 0;
|
|
sal_uInt32 nSize;
|
|
if (eMIMEEncoding == RTL_TEXTENCODING_UTF8)
|
|
{
|
|
nSize = 0;
|
|
for (sal_Unicode const * p = m_pBuffer;
|
|
p != m_pBufferEnd;)
|
|
{
|
|
sal_uInt32 nUTF32
|
|
= INetMIME::getUTF32Character(p, m_pBufferEnd);
|
|
nSize += needsEncodedWordEscape(nUTF32) ?
|
|
3 * INetMIME::getUTF8OctetCount(nUTF32) :
|
|
1;
|
|
// only US-ASCII characters (that are converted to
|
|
// a single byte by UTF-8) need no encoded word
|
|
// escapes...
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rtl_UnicodeToTextConverter hConverter
|
|
= rtl_createUnicodeToTextConverter(eCharsetEncoding);
|
|
rtl_UnicodeToTextContext hContext
|
|
= rtl_createUnicodeToTextContext(hConverter);
|
|
for (sal_Size nBufferSize = m_pBufferEnd - m_pBuffer;;
|
|
nBufferSize += nBufferSize / 3 + 1)
|
|
{
|
|
pTargetBuffer = new sal_Char[nBufferSize];
|
|
sal_uInt32 nInfo;
|
|
sal_Size nSrcCvtBytes;
|
|
nTargetSize
|
|
= rtl_convertUnicodeToText(
|
|
hConverter, hContext, m_pBuffer,
|
|
m_pBufferEnd - m_pBuffer, pTargetBuffer,
|
|
nBufferSize,
|
|
RTL_UNICODETOTEXT_FLAGS_UNDEFINED_IGNORE
|
|
| RTL_UNICODETOTEXT_FLAGS_INVALID_IGNORE,
|
|
&nInfo, &nSrcCvtBytes);
|
|
if (!(nInfo
|
|
& RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL))
|
|
break;
|
|
delete[] pTargetBuffer;
|
|
pTargetBuffer = NULL;
|
|
rtl_resetUnicodeToTextContext(hConverter, hContext);
|
|
}
|
|
rtl_destroyUnicodeToTextContext(hConverter, hContext);
|
|
rtl_destroyUnicodeToTextConverter(hConverter);
|
|
|
|
nSize = nTargetSize;
|
|
for (sal_Size k = 0; k < nTargetSize; ++k)
|
|
if (needsEncodedWordEscape(sal_uChar(
|
|
pTargetBuffer[k])))
|
|
nSize += 2;
|
|
}
|
|
|
|
const sal_Char * pCharsetName
|
|
= INetMIME::getCharsetName(eMIMEEncoding);
|
|
sal_uInt32 nWrapperSize = rtl_str_getLength(pCharsetName) + 7;
|
|
// '=?', '?Q?', '?='
|
|
|
|
switch (m_ePrevCoding)
|
|
{
|
|
case CODING_QUOTED:
|
|
m_rSink << '"';
|
|
case CODING_NONE:
|
|
if (m_eInitialSpace == SPACE_ENCODED
|
|
&& m_nExtraSpaces == 0)
|
|
m_nExtraSpaces = 1;
|
|
nSize += nWrapperSize;
|
|
for (; m_nExtraSpaces > 1; --m_nExtraSpaces)
|
|
{
|
|
if (m_rSink.getColumn()
|
|
>= m_rSink.getLineLengthLimit())
|
|
m_rSink << INetMIMEOutputSink::endl;
|
|
m_rSink << ' ';
|
|
}
|
|
if (m_nExtraSpaces == 1)
|
|
{
|
|
if (m_rSink.getColumn() + nSize
|
|
>= m_rSink.getLineLengthLimit())
|
|
m_rSink << INetMIMEOutputSink::endl;
|
|
m_rSink << ' ';
|
|
}
|
|
m_rSink << "=?" << pCharsetName << "?Q?";
|
|
break;
|
|
|
|
case CODING_ENCODED:
|
|
if (m_ePrevMIMEEncoding != eMIMEEncoding
|
|
|| m_rSink.getColumn() + m_nExtraSpaces + nSize
|
|
> m_rSink.getLineLengthLimit() - 2)
|
|
{
|
|
m_rSink << "?=";
|
|
if (m_rSink.getColumn() + nWrapperSize
|
|
+ m_nExtraSpaces + nSize
|
|
> m_rSink.getLineLengthLimit() - 1)
|
|
m_rSink << INetMIMEOutputSink::endl;
|
|
m_rSink << " =?" << pCharsetName << "?Q?";
|
|
}
|
|
while (m_nExtraSpaces-- > 0)
|
|
{
|
|
if (m_rSink.getColumn()
|
|
> m_rSink.getLineLengthLimit() - 3)
|
|
m_rSink << "?=" << INetMIMEOutputSink::endl
|
|
<< " =?" << pCharsetName << "?Q?";
|
|
m_rSink << '_';
|
|
}
|
|
break;
|
|
|
|
case CODING_ENCODED_TERMINATED:
|
|
if (m_rSink.getColumn() + nWrapperSize
|
|
+ m_nExtraSpaces + nSize
|
|
> m_rSink.getLineLengthLimit() - 1)
|
|
m_rSink << INetMIMEOutputSink::endl;
|
|
m_rSink << " =?" << pCharsetName << "?Q?";
|
|
while (m_nExtraSpaces-- > 0)
|
|
{
|
|
if (m_rSink.getColumn()
|
|
> m_rSink.getLineLengthLimit() - 3)
|
|
m_rSink << "?=" << INetMIMEOutputSink::endl
|
|
<< " =?" << pCharsetName << "?Q?";
|
|
m_rSink << '_';
|
|
}
|
|
break;
|
|
}
|
|
|
|
// The non UTF-8 code will only work for stateless single byte
|
|
// character encodings (see also above):
|
|
if (eMIMEEncoding == RTL_TEXTENCODING_UTF8)
|
|
{
|
|
bool bInitial = true;
|
|
for (sal_Unicode const * p = m_pBuffer;
|
|
p != m_pBufferEnd;)
|
|
{
|
|
sal_uInt32 nUTF32
|
|
= INetMIME::getUTF32Character(p, m_pBufferEnd);
|
|
bool bEscape = needsEncodedWordEscape(nUTF32);
|
|
sal_uInt32 nWidth
|
|
= bEscape ?
|
|
3 * INetMIME::getUTF8OctetCount(nUTF32) : 1;
|
|
// only US-ASCII characters (that are converted to
|
|
// a single byte by UTF-8) need no encoded word
|
|
// escapes...
|
|
if (!bInitial
|
|
&& m_rSink.getColumn() + nWidth + 2
|
|
> m_rSink.getLineLengthLimit())
|
|
m_rSink << "?=" << INetMIMEOutputSink::endl
|
|
<< " =?" << pCharsetName << "?Q?";
|
|
if (bEscape)
|
|
{
|
|
DBG_ASSERT(
|
|
nUTF32 < 0x10FFFF,
|
|
"INetMIMEEncodedWordOutputSink::finish():"
|
|
" Bad char");
|
|
if (nUTF32 < 0x80)
|
|
INetMIME::writeEscapeSequence(m_rSink,
|
|
nUTF32);
|
|
else if (nUTF32 < 0x800)
|
|
{
|
|
INetMIME::writeEscapeSequence(m_rSink,
|
|
nUTF32 >> 6
|
|
| 0xC0);
|
|
INetMIME::writeEscapeSequence(m_rSink,
|
|
nUTF32 & 0x3F
|
|
| 0x80);
|
|
}
|
|
else if (nUTF32 < 0x10000)
|
|
{
|
|
INetMIME::writeEscapeSequence(m_rSink,
|
|
nUTF32 >> 12
|
|
| 0xE0);
|
|
INetMIME::writeEscapeSequence(m_rSink,
|
|
nUTF32 >> 6
|
|
& 0x3F
|
|
| 0x80);
|
|
INetMIME::writeEscapeSequence(m_rSink,
|
|
nUTF32 & 0x3F
|
|
| 0x80);
|
|
}
|
|
else
|
|
{
|
|
INetMIME::writeEscapeSequence(m_rSink,
|
|
nUTF32 >> 18
|
|
| 0xF0);
|
|
INetMIME::writeEscapeSequence(m_rSink,
|
|
nUTF32 >> 12
|
|
& 0x3F
|
|
| 0x80);
|
|
INetMIME::writeEscapeSequence(m_rSink,
|
|
nUTF32 >> 6
|
|
& 0x3F
|
|
| 0x80);
|
|
INetMIME::writeEscapeSequence(m_rSink,
|
|
nUTF32 & 0x3F
|
|
| 0x80);
|
|
}
|
|
}
|
|
else
|
|
m_rSink << sal_Char(nUTF32);
|
|
bInitial = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (sal_Size k = 0; k < nTargetSize; ++k)
|
|
{
|
|
sal_uInt32 nUCS4 = sal_uChar(pTargetBuffer[k]);
|
|
bool bEscape = needsEncodedWordEscape(nUCS4);
|
|
if (k > 0
|
|
&& m_rSink.getColumn() + (bEscape ? 5 : 3)
|
|
> m_rSink.getLineLengthLimit())
|
|
m_rSink << "?=" << INetMIMEOutputSink::endl
|
|
<< " =?" << pCharsetName << "?Q?";
|
|
if (bEscape)
|
|
INetMIME::writeEscapeSequence(m_rSink, nUCS4);
|
|
else
|
|
m_rSink << sal_Char(nUCS4);
|
|
}
|
|
delete[] pTargetBuffer;
|
|
}
|
|
|
|
if (bWriteTrailer)
|
|
{
|
|
m_rSink << "?=";
|
|
m_eCoding = CODING_ENCODED_TERMINATED;
|
|
}
|
|
|
|
m_ePrevMIMEEncoding = eMIMEEncoding;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
OSL_ASSERT(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_eInitialSpace = SPACE_NO;
|
|
m_nExtraSpaces = 0;
|
|
m_pEncodingList->reset();
|
|
m_pBufferEnd = m_pBuffer;
|
|
m_ePrevCoding = m_eCoding;
|
|
m_eCoding = CODING_NONE;
|
|
m_nQuotedEscaped = 0;
|
|
m_eEncodedWordState = STATE_INITIAL;
|
|
}
|
|
|
|
//============================================================================
|
|
INetMIMEEncodedWordOutputSink::~INetMIMEEncodedWordOutputSink()
|
|
{
|
|
rtl_freeMemory(m_pBuffer);
|
|
delete m_pEncodingList;
|
|
}
|
|
|
|
//============================================================================
|
|
INetMIMEEncodedWordOutputSink &
|
|
INetMIMEEncodedWordOutputSink::operator <<(sal_uInt32 nChar)
|
|
{
|
|
if (nChar == ' ')
|
|
{
|
|
if (m_pBufferEnd != m_pBuffer)
|
|
finish(false);
|
|
++m_nExtraSpaces;
|
|
}
|
|
else
|
|
{
|
|
// Check for an already encoded word:
|
|
switch (m_eEncodedWordState)
|
|
{
|
|
case STATE_INITIAL:
|
|
if (nChar == '=')
|
|
m_eEncodedWordState = STATE_FIRST_EQUALS;
|
|
else
|
|
m_eEncodedWordState = STATE_BAD;
|
|
break;
|
|
|
|
case STATE_FIRST_EQUALS:
|
|
if (nChar == '?')
|
|
m_eEncodedWordState = STATE_FIRST_EQUALS;
|
|
else
|
|
m_eEncodedWordState = STATE_BAD;
|
|
break;
|
|
|
|
case STATE_FIRST_QUESTION:
|
|
if (INetMIME::isEncodedWordTokenChar(nChar))
|
|
m_eEncodedWordState = STATE_CHARSET;
|
|
else
|
|
m_eEncodedWordState = STATE_BAD;
|
|
break;
|
|
|
|
case STATE_CHARSET:
|
|
if (nChar == '?')
|
|
m_eEncodedWordState = STATE_SECOND_QUESTION;
|
|
else if (!INetMIME::isEncodedWordTokenChar(nChar))
|
|
m_eEncodedWordState = STATE_BAD;
|
|
break;
|
|
|
|
case STATE_SECOND_QUESTION:
|
|
if (nChar == 'B' || nChar == 'Q'
|
|
|| nChar == 'b' || nChar == 'q')
|
|
m_eEncodedWordState = STATE_ENCODING;
|
|
else
|
|
m_eEncodedWordState = STATE_BAD;
|
|
break;
|
|
|
|
case STATE_ENCODING:
|
|
if (nChar == '?')
|
|
m_eEncodedWordState = STATE_THIRD_QUESTION;
|
|
else
|
|
m_eEncodedWordState = STATE_BAD;
|
|
break;
|
|
|
|
case STATE_THIRD_QUESTION:
|
|
if (INetMIME::isVisible(nChar) && nChar != '?')
|
|
m_eEncodedWordState = STATE_ENCODED_TEXT;
|
|
else
|
|
m_eEncodedWordState = STATE_BAD;
|
|
break;
|
|
|
|
case STATE_ENCODED_TEXT:
|
|
if (nChar == '?')
|
|
m_eEncodedWordState = STATE_FOURTH_QUESTION;
|
|
else if (!INetMIME::isVisible(nChar))
|
|
m_eEncodedWordState = STATE_BAD;
|
|
break;
|
|
|
|
case STATE_FOURTH_QUESTION:
|
|
if (nChar == '=')
|
|
m_eEncodedWordState = STATE_SECOND_EQUALS;
|
|
else
|
|
m_eEncodedWordState = STATE_BAD;
|
|
break;
|
|
|
|
case STATE_SECOND_EQUALS:
|
|
m_eEncodedWordState = STATE_BAD;
|
|
break;
|
|
|
|
case STATE_BAD:
|
|
break;
|
|
}
|
|
|
|
// Update encoding:
|
|
m_pEncodingList->includes(nChar);
|
|
|
|
// Update coding:
|
|
enum { TENQ = 1, // CONTEXT_TEXT, CODING_ENCODED
|
|
CENQ = 2, // CONTEXT_COMMENT, CODING_ENCODED
|
|
PQTD = 4, // CONTEXT_PHRASE, CODING_QUOTED
|
|
PENQ = 8 }; // CONTEXT_PHRASE, CODING_ENCODED
|
|
static const sal_Char aMinimal[128]
|
|
= { TENQ | CENQ | PENQ, // 0x00
|
|
TENQ | CENQ | PENQ, // 0x01
|
|
TENQ | CENQ | PENQ, // 0x02
|
|
TENQ | CENQ | PENQ, // 0x03
|
|
TENQ | CENQ | PENQ, // 0x04
|
|
TENQ | CENQ | PENQ, // 0x05
|
|
TENQ | CENQ | PENQ, // 0x06
|
|
TENQ | CENQ | PENQ, // 0x07
|
|
TENQ | CENQ | PENQ, // 0x08
|
|
TENQ | CENQ | PENQ, // 0x09
|
|
TENQ | CENQ | PENQ, // 0x0A
|
|
TENQ | CENQ | PENQ, // 0x0B
|
|
TENQ | CENQ | PENQ, // 0x0C
|
|
TENQ | CENQ | PENQ, // 0x0D
|
|
TENQ | CENQ | PENQ, // 0x0E
|
|
TENQ | CENQ | PENQ, // 0x0F
|
|
TENQ | CENQ | PENQ, // 0x10
|
|
TENQ | CENQ | PENQ, // 0x11
|
|
TENQ | CENQ | PENQ, // 0x12
|
|
TENQ | CENQ | PENQ, // 0x13
|
|
TENQ | CENQ | PENQ, // 0x14
|
|
TENQ | CENQ | PENQ, // 0x15
|
|
TENQ | CENQ | PENQ, // 0x16
|
|
TENQ | CENQ | PENQ, // 0x17
|
|
TENQ | CENQ | PENQ, // 0x18
|
|
TENQ | CENQ | PENQ, // 0x19
|
|
TENQ | CENQ | PENQ, // 0x1A
|
|
TENQ | CENQ | PENQ, // 0x1B
|
|
TENQ | CENQ | PENQ, // 0x1C
|
|
TENQ | CENQ | PENQ, // 0x1D
|
|
TENQ | CENQ | PENQ, // 0x1E
|
|
TENQ | CENQ | PENQ, // 0x1F
|
|
0, // ' '
|
|
0, // '!'
|
|
PQTD , // '"'
|
|
0, // '#'
|
|
0, // '$'
|
|
0, // '%'
|
|
0, // '&'
|
|
0, // '''
|
|
CENQ | PQTD , // '('
|
|
CENQ | PQTD , // ')'
|
|
0, // '*'
|
|
0, // '+'
|
|
PQTD , // ','
|
|
0, // '-'
|
|
PQTD , // '.'
|
|
0, // '/'
|
|
0, // '0'
|
|
0, // '1'
|
|
0, // '2'
|
|
0, // '3'
|
|
0, // '4'
|
|
0, // '5'
|
|
0, // '6'
|
|
0, // '7'
|
|
0, // '8'
|
|
0, // '9'
|
|
PQTD , // ':'
|
|
PQTD , // ';'
|
|
PQTD , // '<'
|
|
0, // '='
|
|
PQTD , // '>'
|
|
0, // '?'
|
|
PQTD , // '@'
|
|
0, // 'A'
|
|
0, // 'B'
|
|
0, // 'C'
|
|
0, // 'D'
|
|
0, // 'E'
|
|
0, // 'F'
|
|
0, // 'G'
|
|
0, // 'H'
|
|
0, // 'I'
|
|
0, // 'J'
|
|
0, // 'K'
|
|
0, // 'L'
|
|
0, // 'M'
|
|
0, // 'N'
|
|
0, // 'O'
|
|
0, // 'P'
|
|
0, // 'Q'
|
|
0, // 'R'
|
|
0, // 'S'
|
|
0, // 'T'
|
|
0, // 'U'
|
|
0, // 'V'
|
|
0, // 'W'
|
|
0, // 'X'
|
|
0, // 'Y'
|
|
0, // 'Z'
|
|
PQTD , // '['
|
|
CENQ | PQTD , // '\'
|
|
PQTD , // ']'
|
|
0, // '^'
|
|
0, // '_'
|
|
0, // '`'
|
|
0, // 'a'
|
|
0, // 'b'
|
|
0, // 'c'
|
|
0, // 'd'
|
|
0, // 'e'
|
|
0, // 'f'
|
|
0, // 'g'
|
|
0, // 'h'
|
|
0, // 'i'
|
|
0, // 'j'
|
|
0, // 'k'
|
|
0, // 'l'
|
|
0, // 'm'
|
|
0, // 'n'
|
|
0, // 'o'
|
|
0, // 'p'
|
|
0, // 'q'
|
|
0, // 'r'
|
|
0, // 's'
|
|
0, // 't'
|
|
0, // 'u'
|
|
0, // 'v'
|
|
0, // 'w'
|
|
0, // 'x'
|
|
0, // 'y'
|
|
0, // 'z'
|
|
0, // '{'
|
|
0, // '|'
|
|
0, // '}'
|
|
0, // '~'
|
|
TENQ | CENQ | PENQ }; // DEL
|
|
Coding eNewCoding = !INetMIME::isUSASCII(nChar) ? CODING_ENCODED :
|
|
m_eContext == CONTEXT_PHRASE ?
|
|
Coding(aMinimal[nChar] >> 2) :
|
|
aMinimal[nChar] & m_eContext ? CODING_ENCODED :
|
|
CODING_NONE;
|
|
if (eNewCoding > m_eCoding)
|
|
m_eCoding = eNewCoding;
|
|
if (m_eCoding == CODING_QUOTED
|
|
&& INetMIME::needsQuotedStringEscape(nChar))
|
|
++m_nQuotedEscaped;
|
|
|
|
// Append to buffer:
|
|
if (sal_uInt32(m_pBufferEnd - m_pBuffer) == m_nBufferSize)
|
|
{
|
|
m_pBuffer
|
|
= static_cast< sal_Unicode * >(
|
|
rtl_reallocateMemory(m_pBuffer,
|
|
(m_nBufferSize + BUFFER_SIZE)
|
|
* sizeof (sal_Unicode)));
|
|
m_pBufferEnd = m_pBuffer + m_nBufferSize;
|
|
m_nBufferSize += BUFFER_SIZE;
|
|
}
|
|
*m_pBufferEnd++ = sal_Unicode(nChar);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// INetContentTypeParameterList
|
|
//
|
|
//============================================================================
|
|
|
|
void INetContentTypeParameterList::Clear()
|
|
{
|
|
while (Count() > 0)
|
|
delete static_cast< INetContentTypeParameter * >(Remove(Count() - 1));
|
|
}
|
|
|
|
//============================================================================
|
|
const INetContentTypeParameter *
|
|
INetContentTypeParameterList::find(const ByteString & rAttribute) const
|
|
{
|
|
for (ULONG i = 0; i < Count(); ++i)
|
|
{
|
|
const INetContentTypeParameter * pParameter = GetObject(i);
|
|
if (pParameter->m_sAttribute.EqualsIgnoreCaseAscii(rAttribute))
|
|
return pParameter;
|
|
}
|
|
return 0;
|
|
}
|
|
|