xref: /trunk/main/tools/source/inet/inetmime.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_tools.hxx"
30 
31 #include <cstddef>
32 #include <limits>
33 
34 #include "rtl/tencinfo.h"
35 #include <tools/datetime.hxx>
36 #include <tools/inetmime.hxx>
37 
38 namespace unnamed_tools_inetmime {} using namespace unnamed_tools_inetmime;
39 	// unnamed namespaces don't work well yet
40 
41 //============================================================================
42 namespace unnamed_tools_inetmime {
43 
44 class Charset
45 {
46 	rtl_TextEncoding m_eEncoding;
47 	const sal_uInt32 * m_pRanges;
48 
49 public:
50 	inline Charset(rtl_TextEncoding eTheEncoding,
51 				   const sal_uInt32 * pTheRanges);
52 
53 	rtl_TextEncoding getEncoding() const { return m_eEncoding; }
54 
55 	bool contains(sal_uInt32 nChar) const;
56 };
57 
58 inline Charset::Charset(rtl_TextEncoding eTheEncoding,
59 						const sal_uInt32 * pTheRanges):
60 	m_eEncoding(eTheEncoding),
61 	m_pRanges(pTheRanges)
62 {
63 	DBG_ASSERT(m_pRanges, "Charset::Charset(): Bad ranges");
64 }
65 
66 //============================================================================
67 void appendISO88591(UniString & rText, sal_Char const * pBegin,
68 					sal_Char const * pEnd);
69 
70 }
71 
72 //============================================================================
73 class INetMIMECharsetList_Impl
74 {
75 	struct Node
76 	{
77 		Charset m_aCharset;
78 		bool m_bDisabled;
79 		Node * m_pNext;
80 
81 		inline Node(const Charset & rTheCharset, bool bTheDisabled,
82 					Node * pTheNext);
83 	};
84 
85 	Node * m_pFirst;
86 
87 public:
88 	INetMIMECharsetList_Impl(): m_pFirst(0) {}
89 
90 	~INetMIMECharsetList_Impl();
91 
92 	void prepend(const Charset & rCharset)
93 	{ m_pFirst = new Node(rCharset, false, m_pFirst); }
94 
95 	void includes(sal_uInt32 nChar);
96 
97 	rtl_TextEncoding getPreferredEncoding(rtl_TextEncoding eDefault
98 										      = RTL_TEXTENCODING_DONTKNOW)
99 		const;
100 
101 	void reset();
102 };
103 
104 inline INetMIMECharsetList_Impl::Node::Node(const Charset & rTheCharset,
105 											bool bTheDisabled,
106 											Node * pTheNext):
107 	m_aCharset(rTheCharset),
108 	m_bDisabled(bTheDisabled),
109 	m_pNext(pTheNext)
110 {}
111 
112 //============================================================================
113 namespace unnamed_tools_inetmime {
114 
115 struct Parameter
116 {
117 	Parameter * m_pNext;
118 	ByteString m_aAttribute;
119 	ByteString m_aCharset;
120 	ByteString m_aLanguage;
121 	ByteString m_aValue;
122 	sal_uInt32 m_nSection;
123 	bool m_bExtended;
124 
125 	inline Parameter(Parameter * pTheNext, ByteString const & rTheAttribute,
126 					 ByteString const & rTheCharset,
127 					 ByteString const & rTheLanguage,
128 					 ByteString const & rTheValue, sal_uInt32 nTheSection,
129 					 bool bTheExtended);
130 };
131 
132 inline Parameter::Parameter(Parameter * pTheNext,
133 							ByteString const & rTheAttribute,
134 							ByteString const & rTheCharset,
135 							ByteString const & rTheLanguage,
136 							ByteString const & rTheValue,
137 							sal_uInt32 nTheSection, bool bTheExtended):
138 	m_pNext(pTheNext),
139 	m_aAttribute(rTheAttribute),
140 	m_aCharset(rTheCharset),
141 	m_aLanguage(rTheLanguage),
142 	m_aValue(rTheValue),
143 	m_nSection(nTheSection),
144 	m_bExtended(bTheExtended)
145 {}
146 
147 //============================================================================
148 struct ParameterList
149 {
150 	Parameter * m_pList;
151 
152 	ParameterList(): m_pList(0) {}
153 
154 	inline ~ParameterList();
155 
156 	Parameter ** find(ByteString const & rAttribute, sal_uInt32 nSection,
157 					  bool & rPresent);
158 };
159 
160 inline ParameterList::~ParameterList()
161 {
162 	while (m_pList)
163 	{
164 		Parameter * pNext = m_pList->m_pNext;
165 		delete m_pList;
166 		m_pList = pNext;
167 	}
168 }
169 
170 //============================================================================
171 bool parseParameters(ParameterList const & rInput,
172 					 INetContentTypeParameterList * pOutput);
173 
174 }
175 
176 //============================================================================
177 //
178 //  Charset
179 //
180 //============================================================================
181 
182 bool Charset::contains(sal_uInt32 nChar) const
183 {
184 	for (const sal_uInt32 * p = m_pRanges;;)
185 	{
186 		if (nChar < *p++)
187 			return false;
188 		if (nChar <= *p++)
189 			return true;
190 	}
191 }
192 
193 //============================================================================
194 //
195 //  appendISO88591
196 //
197 //============================================================================
198 
199 namespace unnamed_tools_inetmime {
200 
201 void appendISO88591(UniString & rText, sal_Char const * pBegin,
202 					sal_Char const * pEnd)
203 {
204 	xub_StrLen nLength = static_cast< xub_StrLen >(pEnd - pBegin);
205 	sal_Unicode * pBuffer = new sal_Unicode[nLength];
206 	for (sal_Unicode * p = pBuffer; pBegin != pEnd;)
207 		*p++ = sal_uChar(*pBegin++);
208 	rText.Append(pBuffer, nLength);
209 	delete[] pBuffer;
210 }
211 
212 }
213 
214 //============================================================================
215 //
216 //  INetMIMECharsetList_Impl
217 //
218 //============================================================================
219 
220 INetMIMECharsetList_Impl::~INetMIMECharsetList_Impl()
221 {
222 	while (m_pFirst)
223 	{
224 		Node * pRemove = m_pFirst;
225 		m_pFirst = m_pFirst->m_pNext;
226 		delete pRemove;
227 	}
228 }
229 
230 //============================================================================
231 void INetMIMECharsetList_Impl::includes(sal_uInt32 nChar)
232 {
233 	for (Node * p = m_pFirst; p; p = p->m_pNext)
234 		if (!(p->m_bDisabled || p->m_aCharset.contains(nChar)))
235 			p->m_bDisabled = true;
236 }
237 
238 //============================================================================
239 rtl_TextEncoding
240 INetMIMECharsetList_Impl::getPreferredEncoding(rtl_TextEncoding eDefault)
241 	const
242 {
243 	for (Node * p = m_pFirst; p; p = p->m_pNext)
244 		if (!p->m_bDisabled)
245 			return p->m_aCharset.getEncoding();
246 	return eDefault;
247 }
248 
249 //============================================================================
250 void INetMIMECharsetList_Impl::reset()
251 {
252 	for (Node * p = m_pFirst; p; p = p->m_pNext)
253 		p->m_bDisabled = false;
254 }
255 
256 //============================================================================
257 //
258 //  ParameterList
259 //
260 //============================================================================
261 
262 Parameter ** ParameterList::find(ByteString const & rAttribute,
263 								 sal_uInt32 nSection, bool & rPresent)
264 {
265 	Parameter ** p = &m_pList;
266 	for (; *p; p = &(*p)->m_pNext)
267 	{
268 		StringCompare eCompare = rAttribute.CompareTo((*p)->m_aAttribute);
269 		if (eCompare == COMPARE_GREATER)
270 			break;
271 		else if (eCompare == COMPARE_EQUAL)
272         {
273 			if (nSection > (*p)->m_nSection)
274 				break;
275 			else if (nSection == (*p)->m_nSection)
276 			{
277 				rPresent = true;
278 				return p;
279 			}
280         }
281 	}
282 	rPresent = false;
283 	return p;
284 }
285 
286 //============================================================================
287 //
288 //  parseParameters
289 //
290 //============================================================================
291 
292 namespace unnamed_tools_inetmime {
293 
294 bool parseParameters(ParameterList const & rInput,
295 					 INetContentTypeParameterList * pOutput)
296 {
297 	if (pOutput)
298 		pOutput->Clear();
299 
300 	Parameter * pPrev = 0;
301 	for (Parameter * p = rInput.m_pList; p; p = p->m_pNext)
302 	{
303 		if (p->m_nSection > 0
304 			&& (!pPrev
305 				|| pPrev->m_nSection != p->m_nSection - 1
306 				|| pPrev->m_aAttribute != p->m_aAttribute))
307 			return false;
308 		pPrev = p;
309 	}
310 
311 	if (pOutput)
312 		for (Parameter * p = rInput.m_pList; p;)
313 		{
314 			bool bCharset = p->m_aCharset.Len() != 0;
315 			rtl_TextEncoding eEncoding = RTL_TEXTENCODING_DONTKNOW;
316 			if (bCharset)
317 				eEncoding
318 					= INetMIME::getCharsetEncoding(p->m_aCharset.GetBuffer(),
319 												   p->m_aCharset.GetBuffer()
320 											           + rInput.m_pList->
321 												             m_aCharset.
322 												                 Len());
323 			UniString aValue;
324 			bool bBadEncoding = false;
325 			Parameter * pNext = p;
326 			do
327 			{
328 				sal_Size nSize;
329 				sal_Unicode * pUnicode
330 					= INetMIME::convertToUnicode(pNext->m_aValue.GetBuffer(),
331 												 pNext->m_aValue.GetBuffer()
332 												     + pNext->m_aValue.Len(),
333 												 bCharset && p->m_bExtended ?
334 												     eEncoding :
335 												     RTL_TEXTENCODING_UTF8,
336 												 nSize);
337 				if (!pUnicode && !(bCharset && p->m_bExtended))
338 					pUnicode = INetMIME::convertToUnicode(
339 						           pNext->m_aValue.GetBuffer(),
340 								   pNext->m_aValue.GetBuffer()
341 								       + pNext->m_aValue.Len(),
342 								   RTL_TEXTENCODING_ISO_8859_1, nSize);
343 				if (!pUnicode)
344 				{
345 					bBadEncoding = true;
346 					break;
347 				}
348 				aValue += UniString(pUnicode, static_cast< xub_StrLen >(nSize));
349 				delete[] pUnicode;
350 				pNext = pNext->m_pNext;
351 			}
352 			while (pNext && pNext->m_nSection > 0);
353 			if (bBadEncoding)
354 			{
355 				aValue.Erase();
356 				for (pNext = p;;)
357 				{
358 					if (pNext->m_bExtended)
359 						for (xub_StrLen i = 0; i < pNext->m_aValue.Len(); ++i)
360 							aValue += sal_Unicode(
361                                 sal_Unicode(
362                                     sal_uChar(pNext->m_aValue.GetChar(i)))
363                                 | 0xF800);
364 					else
365 						for (xub_StrLen i = 0; i < pNext->m_aValue.Len(); ++i)
366 							aValue
367 								+= sal_Unicode(sal_uChar
368 											       (pNext->
369 													    m_aValue.GetChar(i)));
370 					pNext = pNext->m_pNext;
371 					if (!pNext || pNext->m_nSection == 0)
372 						break;
373 				};
374 			}
375 			pOutput->Insert(new INetContentTypeParameter(p->m_aAttribute,
376 															 p->m_aCharset,
377 															 p->m_aLanguage,
378 															 aValue,
379 															 !bBadEncoding),
380 								LIST_APPEND);
381 			p = pNext;
382 		}
383 	return true;
384 }
385 
386 }
387 
388 //============================================================================
389 //
390 //  INetMIME
391 //
392 //============================================================================
393 
394 // static
395 bool INetMIME::isAtomChar(sal_uInt32 nChar)
396 {
397 	static const bool aMap[128]
398 		= { false, false, false, false, false, false, false, false,
399 			false, false, false, false, false, false, false, false,
400 			false, false, false, false, false, false, false, false,
401 			false, false, false, false, false, false, false, false,
402 			false,  true, false,  true,  true,  true,  true,  true, // !"#$%&'
403 			false, false,  true,  true, false,  true, false,  true, //()*+,-./
404 			 true,  true,  true,  true,  true,  true,  true,  true, //01234567
405 			 true,  true, false, false, false,  true, false,  true, //89:;<=>?
406 			false,  true,  true,  true,  true,  true,  true,  true, //@ABCDEFG
407 			 true,  true,  true,  true,  true,  true,  true,  true, //HIJKLMNO
408 			 true,  true,  true,  true,  true,  true,  true,  true, //PQRSTUVW
409 			 true,  true,  true, false, false, false,  true,  true, //XYZ[\]^_
410 			 true,  true,  true,  true,  true,  true,  true,  true, //`abcdefg
411 			 true,  true,  true,  true,  true,  true,  true,  true, //hijklmno
412 			 true,  true,  true,  true,  true,  true,  true,  true, //pqrstuvw
413 			 true,  true,  true,  true,  true,  true,  true, false  //xyz{|}~
414 		  };
415 	return isUSASCII(nChar) && aMap[nChar];
416 }
417 
418 //============================================================================
419 // static
420 bool INetMIME::isTokenChar(sal_uInt32 nChar)
421 {
422 	static const sal_Char aMap[128]
423 		= { false, false, false, false, false, false, false, false,
424 			false, false, false, false, false, false, false, false,
425 			false, false, false, false, false, false, false, false,
426 			false, false, false, false, false, false, false, false,
427 			false,  true, false,  true,  true,  true,  true,  true, // !"#$%&'
428 			false, false,  true,  true, false,  true,  true, false, //()*+,-./
429 			 true,  true,  true,  true,  true,  true,  true,  true, //01234567
430 			 true,  true, false, false, false, false, false, false, //89:;<=>?
431 			false,  true,  true,  true,  true,  true,  true,  true, //@ABCDEFG
432 			 true,  true,  true,  true,  true,  true,  true,  true, //HIJKLMNO
433 			 true,  true,  true,  true,  true,  true,  true,  true, //PQRSTUVW
434 			 true,  true,  true, false, false, false,  true,  true, //XYZ[\]^_
435 			 true,  true,  true,  true,  true,  true,  true,  true, //`abcdefg
436 			 true,  true,  true,  true,  true,  true,  true,  true, //hijklmno
437 			 true,  true,  true,  true,  true,  true,  true,  true, //pqrstuvw
438 			 true,  true,  true,  true,  true,  true,  true, false  //xyz{|}~
439 		  };
440 	return isUSASCII(nChar) && aMap[nChar];
441 }
442 
443 //============================================================================
444 // static
445 bool INetMIME::isEncodedWordTokenChar(sal_uInt32 nChar)
446 {
447 	static const sal_Char aMap[128]
448 		= { false, false, false, false, false, false, false, false,
449 			false, false, false, false, false, false, false, false,
450 			false, false, false, false, false, false, false, false,
451 			false, false, false, false, false, false, false, false,
452 			false,  true, false,  true,  true,  true,  true,  true, // !"#$%&'
453 			false, false,  true,  true, false,  true, false, false, //()*+,-./
454 			 true,  true,  true,  true,  true,  true,  true,  true, //01234567
455 			 true,  true, false, false, false, false, false, false, //89:;<=>?
456 			false,  true,  true,  true,  true,  true,  true,  true, //@ABCDEFG
457 			 true,  true,  true,  true,  true,  true,  true,  true, //HIJKLMNO
458 			 true,  true,  true,  true,  true,  true,  true,  true, //PQRSTUVW
459 			 true,  true,  true, false, false, false,  true,  true, //XYZ[\]^_
460 			 true,  true,  true,  true,  true,  true,  true,  true, //`abcdefg
461 			 true,  true,  true,  true,  true,  true,  true,  true, //hijklmno
462 			 true,  true,  true,  true,  true,  true,  true,  true, //pqrstuvw
463 			 true,  true,  true,  true,  true,  true,  true, false  //xyz{|}~
464 		  };
465 	return isUSASCII(nChar) && aMap[nChar];
466 }
467 
468 //============================================================================
469 // static
470 bool INetMIME::isIMAPAtomChar(sal_uInt32 nChar)
471 {
472 	static const sal_Char aMap[128]
473 		= { false, false, false, false, false, false, false, false,
474 			false, false, false, false, false, false, false, false,
475 			false, false, false, false, false, false, false, false,
476 			false, false, false, false, false, false, false, false,
477 			false,  true, false,  true,  true, false,  true,  true, // !"#$%&'
478 			false, false, false,  true,  true,  true,  true,  true, //()*+,-./
479 			 true,  true,  true,  true,  true,  true,  true,  true, //01234567
480 			 true,  true,  true,  true,  true,  true,  true,  true, //89:;<=>?
481 			 true,  true,  true,  true,  true,  true,  true,  true, //@ABCDEFG
482 			 true,  true,  true,  true,  true,  true,  true,  true, //HIJKLMNO
483 			 true,  true,  true,  true,  true,  true,  true,  true, //PQRSTUVW
484 			 true,  true,  true,  true, false,  true,  true,  true, //XYZ[\]^_
485 			 true,  true,  true,  true,  true,  true,  true,  true, //`abcdefg
486 			 true,  true,  true,  true,  true,  true,  true,  true, //hijklmno
487 			 true,  true,  true,  true,  true,  true,  true,  true, //pqrstuvw
488 			 true,  true,  true, false,  true,  true,  true, false  //xyz{|}~
489 		  };
490 	return isUSASCII(nChar) && aMap[nChar];
491 }
492 
493 //============================================================================
494 // static
495 sal_uInt32 INetMIME::getDigit(int nWeight)
496 {
497 	DBG_ASSERT(nWeight >= 0 && nWeight < 10,
498 			   "INetMIME::getDigit(): Bad weight");
499 
500 	static const sal_Char aDigits[16]
501 		= { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
502 	return aDigits[nWeight];
503 }
504 
505 //============================================================================
506 // static
507 sal_uInt32 INetMIME::getHexDigit(int nWeight)
508 {
509 	DBG_ASSERT(nWeight >= 0 && nWeight < 16,
510 			   "INetMIME::getHexDigit(): Bad weight");
511 
512 	static const sal_Char aDigits[16]
513 		= { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
514 			'D', 'E', 'F' };
515 	return aDigits[nWeight];
516 }
517 
518 //============================================================================
519 // static
520 sal_uInt32 INetMIME::getBase64Digit(int nWeight)
521 {
522 	DBG_ASSERT(nWeight >= 0 && nWeight < 64,
523 			   "INetMIME::getBase64Digit(): Bad weight");
524 
525 	static const sal_Char aDigits[64]
526 		= { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
527 			'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
528 			'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
529 			'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
530 			'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
531 	return aDigits[nWeight];
532 }
533 
534 //============================================================================
535 // static
536 bool INetMIME::equalIgnoreCase(const sal_Char * pBegin1,
537 							   const sal_Char * pEnd1,
538 							   const sal_Char * pBegin2,
539 							   const sal_Char * pEnd2)
540 {
541 	DBG_ASSERT(pBegin1 && pBegin1 <= pEnd1 && pBegin2 && pBegin2 <= pEnd2,
542 			   "INetMIME::equalIgnoreCase(): Bad sequences");
543 
544 	if (pEnd1 - pBegin1 != pEnd2 - pBegin2)
545 		return false;
546 	while (pBegin1 != pEnd1)
547 		if (toUpperCase(*pBegin1++) != toUpperCase(*pBegin2++))
548 			return false;
549 	return true;
550 }
551 
552 //============================================================================
553 // static
554 bool INetMIME::equalIgnoreCase(const sal_Char * pBegin1,
555 							   const sal_Char * pEnd1,
556 							   const sal_Char * pString2)
557 {
558 	DBG_ASSERT(pBegin1 && pBegin1 <= pEnd1 && pString2,
559 			   "INetMIME::equalIgnoreCase(): Bad sequences");
560 
561 	while (*pString2 != 0)
562 		if (pBegin1 == pEnd1
563 			|| toUpperCase(*pBegin1++) != toUpperCase(*pString2++))
564 			return false;
565 	return pBegin1 == pEnd1;
566 }
567 
568 //============================================================================
569 // static
570 bool INetMIME::equalIgnoreCase(const sal_Unicode * pBegin1,
571 							   const sal_Unicode * pEnd1,
572 							   const sal_Char * pString2)
573 {
574 	DBG_ASSERT(pBegin1 && pBegin1 <= pEnd1 && pString2,
575 			   "INetMIME::equalIgnoreCase(): Bad sequences");
576 
577 	while (*pString2 != 0)
578 		if (pBegin1 == pEnd1
579 			|| toUpperCase(*pBegin1++) != toUpperCase(*pString2++))
580 			return false;
581 	return pBegin1 == pEnd1;
582 }
583 
584 //============================================================================
585 // static
586 const sal_Char * INetMIME::skipLinearWhiteSpace(const sal_Char * pBegin,
587 												const sal_Char * pEnd)
588 {
589 	DBG_ASSERT(pBegin && pBegin <= pEnd,
590 			   "INetMIME::skipLinearWhiteSpace(): Bad sequence");
591 
592 	while (pBegin != pEnd)
593 		switch (*pBegin)
594 		{
595 			case '\t':
596 			case ' ':
597 				++pBegin;
598 				break;
599 
600 			case 0x0D: // CR
601 				if (startsWithLineFolding(pBegin, pEnd))
602 					pBegin += 3;
603 				else
604 					return pBegin;
605 				break;
606 
607 			default:
608 				return pBegin;
609 		}
610 	return pBegin;
611 }
612 
613 //============================================================================
614 // static
615 const sal_Unicode * INetMIME::skipLinearWhiteSpace(const sal_Unicode * pBegin,
616 												   const sal_Unicode * pEnd)
617 {
618 	DBG_ASSERT(pBegin && pBegin <= pEnd,
619 			   "INetMIME::skipLinearWhiteSpace(): Bad sequence");
620 
621 	while (pBegin != pEnd)
622 		switch (*pBegin)
623 		{
624 			case '\t':
625 			case ' ':
626 				++pBegin;
627 				break;
628 
629 			case 0x0D: // CR
630 				if (startsWithLineFolding(pBegin, pEnd))
631 					pBegin += 3;
632 				else
633 					return pBegin;
634 				break;
635 
636 			default:
637 				return pBegin;
638 		}
639 	return pBegin;
640 }
641 
642 //============================================================================
643 // static
644 const sal_Char * INetMIME::skipComment(const sal_Char * pBegin,
645 									   const sal_Char * pEnd)
646 {
647 	DBG_ASSERT(pBegin && pBegin <= pEnd,
648 			   "INetMIME::skipComment(): Bad sequence");
649 
650 	if (pBegin != pEnd && *pBegin == '(')
651 	{
652 		sal_uInt32 nLevel = 0;
653 		for (const sal_Char * p = pBegin; p != pEnd;)
654 			switch (*p++)
655 			{
656 				case '(':
657 					++nLevel;
658 					break;
659 
660 				case ')':
661 					if (--nLevel == 0)
662 						return p;
663 					break;
664 
665 				case '\\':
666 					if (p != pEnd)
667 						++p;
668 					break;
669 			}
670 	}
671 	return pBegin;
672 }
673 
674 //============================================================================
675 // static
676 const sal_Unicode * INetMIME::skipComment(const sal_Unicode * pBegin,
677 										  const sal_Unicode * pEnd)
678 {
679 	DBG_ASSERT(pBegin && pBegin <= pEnd,
680 			   "INetMIME::skipComment(): Bad sequence");
681 
682 	if (pBegin != pEnd && *pBegin == '(')
683 	{
684 		sal_uInt32 nLevel = 0;
685 		for (const sal_Unicode * p = pBegin; p != pEnd;)
686 			switch (*p++)
687 			{
688 				case '(':
689 					++nLevel;
690 					break;
691 
692 				case ')':
693 					if (--nLevel == 0)
694 						return p;
695 					break;
696 
697 				case '\\':
698 					if (p != pEnd)
699 						++p;
700 					break;
701 			}
702 	}
703 	return pBegin;
704 }
705 
706 //============================================================================
707 // static
708 const sal_Char * INetMIME::skipLinearWhiteSpaceComment(const sal_Char *
709 													       pBegin,
710 													   const sal_Char * pEnd)
711 {
712 	DBG_ASSERT(pBegin && pBegin <= pEnd,
713 			   "INetMIME::skipLinearWhiteSpaceComment(): Bad sequence");
714 
715 	while (pBegin != pEnd)
716 		switch (*pBegin)
717 		{
718 			case '\t':
719 			case ' ':
720 				++pBegin;
721 				break;
722 
723 			case 0x0D: // CR
724 				if (startsWithLineFolding(pBegin, pEnd))
725 					pBegin += 3;
726 				else
727 					return pBegin;
728 				break;
729 
730 			case '(':
731 			{
732 				const sal_Char * p = skipComment(pBegin, pEnd);
733 				if (p == pBegin)
734 					return pBegin;
735 				pBegin = p;
736 				break;
737 			}
738 
739 			default:
740 				return pBegin;
741 		}
742 	return pBegin;
743 }
744 
745 //============================================================================
746 // static
747 const sal_Unicode * INetMIME::skipLinearWhiteSpaceComment(const sal_Unicode *
748 														      pBegin,
749 														  const sal_Unicode *
750 														      pEnd)
751 {
752 	DBG_ASSERT(pBegin && pBegin <= pEnd,
753 			   "INetMIME::skipLinearWhiteSpaceComment(): Bad sequence");
754 
755 	while (pBegin != pEnd)
756 		switch (*pBegin)
757 		{
758 			case '\t':
759 			case ' ':
760 				++pBegin;
761 				break;
762 
763 			case 0x0D: // CR
764 				if (startsWithLineFolding(pBegin, pEnd))
765 					pBegin += 3;
766 				else
767 					return pBegin;
768 				break;
769 
770 			case '(':
771 			{
772 				const sal_Unicode * p = skipComment(pBegin, pEnd);
773 				if (p == pBegin)
774 					return pBegin;
775 				pBegin = p;
776 				break;
777 			}
778 
779 			default:
780 				return pBegin;
781 		}
782 	return pBegin;
783 }
784 
785 //============================================================================
786 // static
787 const sal_Char * INetMIME::skipQuotedString(const sal_Char * pBegin,
788 											const sal_Char * pEnd)
789 {
790 	DBG_ASSERT(pBegin && pBegin <= pEnd,
791 			   "INetMIME::skipQuotedString(): Bad sequence");
792 
793 	if (pBegin != pEnd && *pBegin == '"')
794 		for (const sal_Char * p = pBegin + 1; p != pEnd;)
795 			switch (*p++)
796 			{
797 				case 0x0D: // CR
798 					if (pEnd - p < 2 || *p++ != 0x0A // LF
799 						|| !isWhiteSpace(*p++))
800 						return pBegin;
801 					break;
802 
803 				case '"':
804 					return p;
805 
806 				case '\\':
807 					if (p != pEnd)
808 						++p;
809 					break;
810 			}
811 	return pBegin;
812 }
813 
814 //============================================================================
815 // static
816 const sal_Unicode * INetMIME::skipQuotedString(const sal_Unicode * pBegin,
817 											   const sal_Unicode * pEnd)
818 {
819 	DBG_ASSERT(pBegin && pBegin <= pEnd,
820 			   "INetMIME::skipQuotedString(): Bad sequence");
821 
822 	if (pBegin != pEnd && *pBegin == '"')
823 		for (const sal_Unicode * p = pBegin + 1; p != pEnd;)
824 			switch (*p++)
825 			{
826 				case 0x0D: // CR
827 					if (pEnd - p < 2 || *p++ != 0x0A // LF
828 						|| !isWhiteSpace(*p++))
829 						return pBegin;
830 					break;
831 
832 				case '"':
833 					return p;
834 
835 				case '\\':
836 					if (p != pEnd)
837 						++p;
838 					break;
839 			}
840 	return pBegin;
841 }
842 
843 //============================================================================
844 // static
845 const sal_Char * INetMIME::scanAtom(const sal_Char * pBegin,
846 									const sal_Char * pEnd)
847 {
848 	while (pBegin != pEnd && isAtomChar(*pBegin))
849 		++pBegin;
850 	return pBegin;
851 }
852 
853 //============================================================================
854 // static
855 const sal_Unicode * INetMIME::scanAtom(const sal_Unicode * pBegin,
856 									   const sal_Unicode * pEnd)
857 {
858 	while (pBegin != pEnd && isAtomChar(*pBegin))
859 		++pBegin;
860 	return pBegin;
861 }
862 
863 //============================================================================
864 // static
865 bool INetMIME::scanUnsigned(const sal_Char *& rBegin, const sal_Char * pEnd,
866 							bool bLeadingZeroes, sal_uInt32 & rValue)
867 {
868 	sal_uInt64 nTheValue = 0;
869 	const sal_Char * p = rBegin;
870 	for ( ; p != pEnd; ++p)
871 	{
872 		int nWeight = getWeight(*p);
873 		if (nWeight < 0)
874 			break;
875 		nTheValue = 10 * nTheValue + nWeight;
876 		if (nTheValue > std::numeric_limits< sal_uInt32 >::max())
877 			return false;
878 	}
879 	if (nTheValue == 0 && (p == rBegin || (!bLeadingZeroes && p - rBegin != 1)))
880 		return false;
881 	rBegin = p;
882 	rValue = sal_uInt32(nTheValue);
883 	return true;
884 }
885 
886 //============================================================================
887 // static
888 bool INetMIME::scanUnsigned(const sal_Unicode *& rBegin,
889 							const sal_Unicode * pEnd, bool bLeadingZeroes,
890 							sal_uInt32 & rValue)
891 {
892 	sal_uInt64 nTheValue = 0;
893 	const sal_Unicode * p = rBegin;
894 	for ( ; p != pEnd; ++p)
895 	{
896 		int nWeight = getWeight(*p);
897 		if (nWeight < 0)
898 			break;
899 		nTheValue = 10 * nTheValue + nWeight;
900 		if (nTheValue > std::numeric_limits< sal_uInt32 >::max())
901 			return false;
902 	}
903 	if (nTheValue == 0 && (p == rBegin || (!bLeadingZeroes && p - rBegin != 1)))
904 		return false;
905 	rBegin = p;
906 	rValue = sal_uInt32(nTheValue);
907 	return true;
908 }
909 
910 //============================================================================
911 // static
912 bool INetMIME::scanUnsignedHex(const sal_Char *& rBegin,
913 							   const sal_Char * pEnd, bool bLeadingZeroes,
914 							   sal_uInt32 & rValue)
915 {
916 	sal_uInt64 nTheValue = 0;
917 	const sal_Char * p = rBegin;
918 	for ( p = rBegin; p != pEnd; ++p)
919 	{
920 		int nWeight = getHexWeight(*p);
921 		if (nWeight < 0)
922 			break;
923 		nTheValue = nTheValue << 4 | nWeight;
924 		if (nTheValue > std::numeric_limits< sal_uInt32 >::max())
925 			return false;
926 	}
927 	if (nTheValue == 0 && (p == rBegin || (!bLeadingZeroes && p - rBegin != 1)))
928 		return false;
929 	rBegin = p;
930 	rValue = sal_uInt32(nTheValue);
931 	return true;
932 }
933 
934 //============================================================================
935 // static
936 bool INetMIME::scanUnsignedHex(const sal_Unicode *& rBegin,
937 							   const sal_Unicode * pEnd, bool bLeadingZeroes,
938 							   sal_uInt32 & rValue)
939 {
940 	sal_uInt64 nTheValue = 0;
941 	const sal_Unicode * p = rBegin;
942 	for ( ; p != pEnd; ++p)
943 	{
944 		int nWeight = getHexWeight(*p);
945 		if (nWeight < 0)
946 			break;
947 		nTheValue = nTheValue << 4 | nWeight;
948 		if (nTheValue > std::numeric_limits< sal_uInt32 >::max())
949 			return false;
950 	}
951 	if (nTheValue == 0 && (p == rBegin || (!bLeadingZeroes && p - rBegin != 1)))
952 		return false;
953 	rBegin = p;
954 	rValue = sal_uInt32(nTheValue);
955 	return true;
956 }
957 
958 //============================================================================
959 // static
960 const sal_Char * INetMIME::scanQuotedBlock(const sal_Char * pBegin,
961 										   const sal_Char * pEnd,
962 										   sal_uInt32 nOpening,
963 										   sal_uInt32 nClosing,
964 										   sal_Size & rLength,
965 										   bool & rModify)
966 {
967 	DBG_ASSERT(pBegin && pBegin <= pEnd,
968 			   "INetMIME::scanQuotedBlock(): Bad sequence");
969 
970 	if (pBegin != pEnd && static_cast< unsigned char >(*pBegin) == nOpening)
971 	{
972 		++rLength;
973 		++pBegin;
974 		while (pBegin != pEnd)
975 			if (static_cast< unsigned char >(*pBegin) == nClosing)
976 			{
977 				++rLength;
978 				return ++pBegin;
979 			}
980 			else
981 			{
982 				sal_uInt32 c = *pBegin++;
983 				switch (c)
984 				{
985 					case 0x0D: // CR
986 						if (pBegin != pEnd && *pBegin == 0x0A) // LF
987 							if (pEnd - pBegin >= 2 && isWhiteSpace(pBegin[1]))
988 							{
989 								++rLength;
990 								rModify = true;
991 								pBegin += 2;
992 							}
993 							else
994 							{
995 								rLength += 3;
996 								rModify = true;
997 								++pBegin;
998 							}
999 						else
1000 							++rLength;
1001 						break;
1002 
1003 					case '\\':
1004 						++rLength;
1005 						if (pBegin != pEnd)
1006                         {
1007 							if (startsWithLineBreak(pBegin, pEnd)
1008 								&& (pEnd - pBegin < 3
1009 									|| !isWhiteSpace(pBegin[2])))
1010 							{
1011 								rLength += 3;
1012 								rModify = true;
1013 								pBegin += 2;
1014 							}
1015 							else
1016 								++pBegin;
1017                         }
1018 						break;
1019 
1020 					default:
1021 						++rLength;
1022 						if (!isUSASCII(c))
1023 							rModify = true;
1024 						break;
1025 				}
1026 			}
1027 	}
1028 	return pBegin;
1029 }
1030 
1031 //============================================================================
1032 // static
1033 const sal_Unicode * INetMIME::scanQuotedBlock(const sal_Unicode * pBegin,
1034 											  const sal_Unicode * pEnd,
1035 											  sal_uInt32 nOpening,
1036 											  sal_uInt32 nClosing,
1037 											  sal_Size & rLength,
1038 											  bool & rModify)
1039 {
1040 	DBG_ASSERT(pBegin && pBegin <= pEnd,
1041 			   "INetMIME::scanQuotedBlock(): Bad sequence");
1042 
1043 	if (pBegin != pEnd && *pBegin == nOpening)
1044 	{
1045 		++rLength;
1046 		++pBegin;
1047 		while (pBegin != pEnd)
1048 			if (*pBegin == nClosing)
1049 			{
1050 				++rLength;
1051 				return ++pBegin;
1052 			}
1053 			else
1054 			{
1055 				sal_uInt32 c = *pBegin++;
1056 				switch (c)
1057 				{
1058 					case 0x0D: // CR
1059 						if (pBegin != pEnd && *pBegin == 0x0A) // LF
1060 							if (pEnd - pBegin >= 2 && isWhiteSpace(pBegin[1]))
1061 							{
1062 								++rLength;
1063 								rModify = true;
1064 								pBegin += 2;
1065 							}
1066 							else
1067 							{
1068 								rLength += 3;
1069 								rModify = true;
1070 								++pBegin;
1071 							}
1072 						else
1073 							++rLength;
1074 						break;
1075 
1076 					case '\\':
1077 						++rLength;
1078 						if (pBegin != pEnd)
1079                         {
1080 							if (startsWithLineBreak(pBegin, pEnd)
1081 								&& (pEnd - pBegin < 3
1082 									|| !isWhiteSpace(pBegin[2])))
1083 							{
1084 								rLength += 3;
1085 								rModify = true;
1086 								pBegin += 2;
1087 							}
1088 							else
1089 								++pBegin;
1090                         }
1091 						break;
1092 
1093 					default:
1094 						++rLength;
1095 						if (!isUSASCII(c))
1096 							rModify = true;
1097 						break;
1098 				}
1099 			}
1100 	}
1101 	return pBegin;
1102 }
1103 
1104 //============================================================================
1105 // static
1106 sal_Char const * INetMIME::scanParameters(sal_Char const * pBegin,
1107 										  sal_Char const * pEnd,
1108 										  INetContentTypeParameterList *
1109 										      pParameters)
1110 {
1111 	ParameterList aList;
1112 	sal_Char const * pParameterBegin = pBegin;
1113 	for (sal_Char const * p = pParameterBegin;; pParameterBegin = p)
1114 	{
1115 		pParameterBegin = skipLinearWhiteSpaceComment(p, pEnd);
1116 		if (pParameterBegin == pEnd || *pParameterBegin != ';')
1117 			break;
1118 		p = pParameterBegin + 1;
1119 
1120 		sal_Char const * pAttributeBegin = skipLinearWhiteSpaceComment(p,
1121 																	   pEnd);
1122 		p = pAttributeBegin;
1123 		bool bDowncaseAttribute = false;
1124 		while (p != pEnd && isTokenChar(*p) && *p != '*')
1125 		{
1126 			bDowncaseAttribute = bDowncaseAttribute || isUpperCase(*p);
1127 			++p;
1128 		}
1129 		if (p == pAttributeBegin)
1130 			break;
1131 		ByteString aAttribute(
1132             pAttributeBegin, static_cast< xub_StrLen >(p - pAttributeBegin));
1133 		if (bDowncaseAttribute)
1134 			aAttribute.ToLowerAscii();
1135 
1136 		sal_uInt32 nSection = 0;
1137 		if (p != pEnd && *p == '*')
1138 		{
1139 			++p;
1140 			if (p != pEnd && isDigit(*p)
1141 				&& !scanUnsigned(p, pEnd, false, nSection))
1142 				break;
1143 		}
1144 
1145 		bool bPresent;
1146 		Parameter ** pPos = aList.find(aAttribute, nSection, bPresent);
1147 		if (bPresent)
1148 			break;
1149 
1150 		bool bExtended = false;
1151 		if (p != pEnd && *p == '*')
1152 		{
1153 			++p;
1154 			bExtended = true;
1155 		}
1156 
1157 		p = skipLinearWhiteSpaceComment(p, pEnd);
1158 
1159 		if (p == pEnd || *p != '=')
1160 			break;
1161 
1162 		p = skipLinearWhiteSpaceComment(p + 1, pEnd);
1163 
1164 		ByteString aCharset;
1165 		ByteString aLanguage;
1166  		ByteString aValue;
1167 		if (bExtended)
1168 		{
1169 			if (nSection == 0)
1170 			{
1171 				sal_Char const * pCharsetBegin = p;
1172 				bool bDowncaseCharset = false;
1173 				while (p != pEnd && isTokenChar(*p) && *p != '\'')
1174 				{
1175 					bDowncaseCharset = bDowncaseCharset || isUpperCase(*p);
1176 					++p;
1177 				}
1178 				if (p == pCharsetBegin)
1179 					break;
1180 				if (pParameters)
1181 				{
1182 					aCharset = ByteString(
1183                         pCharsetBegin,
1184                         static_cast< xub_StrLen >(p - pCharsetBegin));
1185 					if (bDowncaseCharset)
1186 						aCharset.ToLowerAscii();
1187 				}
1188 
1189 				if (p == pEnd || *p != '\'')
1190 					break;
1191 				++p;
1192 
1193 				sal_Char const * pLanguageBegin = p;
1194 				bool bDowncaseLanguage = false;
1195 				int nLetters = 0;
1196 				for (; p != pEnd; ++p)
1197 					if (isAlpha(*p))
1198 					{
1199 						if (++nLetters > 8)
1200 							break;
1201 						bDowncaseLanguage = bDowncaseLanguage
1202 							                || isUpperCase(*p);
1203 					}
1204 					else if (*p == '-')
1205 					{
1206 						if (nLetters == 0)
1207 							break;
1208 						nLetters = 0;
1209 					}
1210 					else
1211 						break;
1212 				if (nLetters == 0 || nLetters > 8)
1213 					break;
1214 				if (pParameters)
1215 				{
1216 					aLanguage = ByteString(
1217                         pLanguageBegin,
1218                         static_cast< xub_StrLen >(p - pLanguageBegin));
1219 					if (bDowncaseLanguage)
1220 						aLanguage.ToLowerAscii();
1221 				}
1222 
1223 				if (p == pEnd || *p != '\'')
1224 					break;
1225 				++p;
1226 			}
1227 			if (pParameters)
1228 				while (p != pEnd && (isTokenChar(*p) || !isUSASCII(*p)))
1229 				{
1230 					if (*p == '%')
1231 					{
1232 						if (p + 2 < pEnd)
1233 						{
1234 							int nWeight1 = getHexWeight(p[1]);
1235 							int nWeight2 = getHexWeight(p[2]);
1236 							if (nWeight1 >= 0 && nWeight2 >= 0)
1237 							{
1238 								aValue += sal_Char(nWeight1 << 4 | nWeight2);
1239 								p += 3;
1240 								continue;
1241 							}
1242 						}
1243 					}
1244 					aValue += *p++;
1245 				}
1246 			else
1247 				while (p != pEnd && (isTokenChar(*p) || !isUSASCII(*p)))
1248 					++p;
1249 		}
1250 		else if (p != pEnd && *p == '"')
1251 			if (pParameters)
1252 			{
1253 				bool bInvalid = false;
1254 				for (++p;;)
1255 				{
1256 					if (p == pEnd)
1257 					{
1258 						bInvalid = true;
1259 						break;
1260 					}
1261 					else if (*p == '"')
1262 					{
1263 						++p;
1264 						break;
1265 					}
1266 					else if (*p == 0x0D) // CR
1267 					{
1268 						if (pEnd - p < 3 || p[1] != 0x0A // LF
1269 							|| !isWhiteSpace(p[2]))
1270 						{
1271 							bInvalid = true;
1272 							break;
1273 						}
1274 						p += 2;
1275 					}
1276 					else if (*p == '\\' && ++p == pEnd)
1277 					{
1278 						bInvalid = true;
1279 						break;
1280 					}
1281 					aValue += *p++;
1282 				}
1283 				if (bInvalid)
1284 					break;
1285 			}
1286 			else
1287 			{
1288 				sal_Char const * pStringEnd = skipQuotedString(p, pEnd);
1289 				if (p == pStringEnd)
1290 					break;
1291 				p = pStringEnd;
1292 			}
1293 		else
1294 		{
1295 			sal_Char const * pTokenBegin = p;
1296 			while (p != pEnd && (isTokenChar(*p) || !isUSASCII(*p)))
1297 				++p;
1298 			if (p == pTokenBegin)
1299 				break;
1300 			if (pParameters)
1301 				aValue = ByteString(
1302                     pTokenBegin, static_cast< xub_StrLen >(p - pTokenBegin));
1303 		}
1304 
1305 		*pPos = new Parameter(*pPos, aAttribute, aCharset, aLanguage, aValue,
1306 							  nSection, bExtended);
1307 	}
1308 	return parseParameters(aList, pParameters) ? pParameterBegin : pBegin;
1309 }
1310 
1311 //============================================================================
1312 // static
1313 sal_Unicode const * INetMIME::scanParameters(sal_Unicode const * pBegin,
1314 											 sal_Unicode const * pEnd,
1315 											 INetContentTypeParameterList *
1316 											     pParameters)
1317 {
1318 	ParameterList aList;
1319 	sal_Unicode const * pParameterBegin = pBegin;
1320 	for (sal_Unicode const * p = pParameterBegin;; pParameterBegin = p)
1321 	{
1322 		pParameterBegin = skipLinearWhiteSpaceComment(p, pEnd);
1323 		if (pParameterBegin == pEnd || *pParameterBegin != ';')
1324 			break;
1325 		p = pParameterBegin + 1;
1326 
1327 		sal_Unicode const * pAttributeBegin
1328 			= skipLinearWhiteSpaceComment(p, pEnd);
1329 		p = pAttributeBegin;
1330 		bool bDowncaseAttribute = false;
1331 		while (p != pEnd && isTokenChar(*p) && *p != '*')
1332 		{
1333 			bDowncaseAttribute = bDowncaseAttribute || isUpperCase(*p);
1334 			++p;
1335 		}
1336 		if (p == pAttributeBegin)
1337 			break;
1338 		ByteString aAttribute = ByteString(
1339             pAttributeBegin, static_cast< xub_StrLen >(p - pAttributeBegin),
1340             RTL_TEXTENCODING_ASCII_US);
1341 		if (bDowncaseAttribute)
1342 			aAttribute.ToLowerAscii();
1343 
1344 		sal_uInt32 nSection = 0;
1345 		if (p != pEnd && *p == '*')
1346 		{
1347 			++p;
1348 			if (p != pEnd && isDigit(*p)
1349 				&& !scanUnsigned(p, pEnd, false, nSection))
1350 				break;
1351 		}
1352 
1353 		bool bPresent;
1354 		Parameter ** pPos = aList.find(aAttribute, nSection, bPresent);
1355 		if (bPresent)
1356 			break;
1357 
1358 		bool bExtended = false;
1359 		if (p != pEnd && *p == '*')
1360 		{
1361 			++p;
1362 			bExtended = true;
1363 		}
1364 
1365 		p = skipLinearWhiteSpaceComment(p, pEnd);
1366 
1367 		if (p == pEnd || *p != '=')
1368 			break;
1369 
1370 		p = skipLinearWhiteSpaceComment(p + 1, pEnd);
1371 
1372 		ByteString aCharset;
1373 		ByteString aLanguage;
1374  		ByteString aValue;
1375 		if (bExtended)
1376 		{
1377 			if (nSection == 0)
1378 			{
1379 				sal_Unicode const * pCharsetBegin = p;
1380 				bool bDowncaseCharset = false;
1381 				while (p != pEnd && isTokenChar(*p) && *p != '\'')
1382 				{
1383 					bDowncaseCharset = bDowncaseCharset || isUpperCase(*p);
1384 					++p;
1385 				}
1386 				if (p == pCharsetBegin)
1387 					break;
1388 				if (pParameters)
1389 				{
1390 					aCharset = ByteString(
1391                         pCharsetBegin,
1392                         static_cast< xub_StrLen >(p - pCharsetBegin),
1393                         RTL_TEXTENCODING_ASCII_US);
1394 					if (bDowncaseCharset)
1395 						aCharset.ToLowerAscii();
1396 				}
1397 
1398 				if (p == pEnd || *p != '\'')
1399 					break;
1400 				++p;
1401 
1402 				sal_Unicode const * pLanguageBegin = p;
1403 				bool bDowncaseLanguage = false;
1404 				int nLetters = 0;
1405 				for (; p != pEnd; ++p)
1406 					if (isAlpha(*p))
1407 					{
1408 						if (++nLetters > 8)
1409 							break;
1410 						bDowncaseLanguage = bDowncaseLanguage
1411 							                || isUpperCase(*p);
1412 					}
1413 					else if (*p == '-')
1414 					{
1415 						if (nLetters == 0)
1416 							break;
1417 						nLetters = 0;
1418 					}
1419 					else
1420 						break;
1421 				if (nLetters == 0 || nLetters > 8)
1422 					break;
1423 				if (pParameters)
1424 				{
1425 					aLanguage = ByteString(
1426                         pLanguageBegin,
1427                         static_cast< xub_StrLen >(p - pLanguageBegin),
1428                         RTL_TEXTENCODING_ASCII_US);
1429 					if (bDowncaseLanguage)
1430 						aLanguage.ToLowerAscii();
1431 				}
1432 
1433 				if (p == pEnd || *p != '\'')
1434 					break;
1435 				++p;
1436 			}
1437 			if (pParameters)
1438 			{
1439 				INetMIMEStringOutputSink
1440 					aSink(0, INetMIMEOutputSink::NO_LINE_LENGTH_LIMIT);
1441 				while (p != pEnd)
1442 				{
1443 					sal_uInt32 nChar = INetMIME::getUTF32Character(p, pEnd);
1444 					if (isUSASCII(nChar) && !isTokenChar(nChar))
1445 						break;
1446 					if (nChar == '%' && p + 1 < pEnd)
1447 					{
1448 						int nWeight1 = getHexWeight(p[0]);
1449 						int nWeight2 = getHexWeight(p[1]);
1450 						if (nWeight1 >= 0 && nWeight2 >= 0)
1451 						{
1452 							aSink << sal_Char(nWeight1 << 4 | nWeight2);
1453 							p += 2;
1454 							continue;
1455 						}
1456 					}
1457 					INetMIME::writeUTF8(aSink, nChar);
1458 				}
1459 				aValue = aSink.takeBuffer();
1460 			}
1461 			else
1462 				while (p != pEnd && (isTokenChar(*p) || !isUSASCII(*p)))
1463 					++p;
1464 		}
1465 		else if (p != pEnd && *p == '"')
1466 			if (pParameters)
1467 			{
1468 				INetMIMEStringOutputSink
1469 					aSink(0, INetMIMEOutputSink::NO_LINE_LENGTH_LIMIT);
1470 				bool bInvalid = false;
1471 				for (++p;;)
1472 				{
1473 					if (p == pEnd)
1474 					{
1475 						bInvalid = true;
1476 						break;
1477 					}
1478 					sal_uInt32 nChar = INetMIME::getUTF32Character(p, pEnd);
1479 					if (nChar == '"')
1480 						break;
1481 					else if (nChar == 0x0D) // CR
1482 					{
1483 						if (pEnd - p < 2 || *p++ != 0x0A // LF
1484 							|| !isWhiteSpace(*p))
1485 						{
1486 							bInvalid = true;
1487 							break;
1488 						}
1489 						nChar = sal_uChar(*p++);
1490 					}
1491 					else if (nChar == '\\')
1492 					{
1493 						if (p == pEnd)
1494 						{
1495 							bInvalid = true;
1496 							break;
1497 						}
1498 						nChar = INetMIME::getUTF32Character(p, pEnd);
1499 					}
1500 					INetMIME::writeUTF8(aSink, nChar);
1501 				}
1502 				if (bInvalid)
1503 					break;
1504 				aValue = aSink.takeBuffer();
1505 			}
1506 			else
1507 			{
1508 				sal_Unicode const * pStringEnd = skipQuotedString(p, pEnd);
1509 				if (p == pStringEnd)
1510 					break;
1511 				p = pStringEnd;
1512 			}
1513 		else
1514 		{
1515 			sal_Unicode const * pTokenBegin = p;
1516 			while (p != pEnd && (isTokenChar(*p) || !isUSASCII(*p)))
1517 				++p;
1518 			if (p == pTokenBegin)
1519 				break;
1520 			if (pParameters)
1521 				aValue = ByteString(
1522                     pTokenBegin, static_cast< xub_StrLen >(p - pTokenBegin),
1523                     RTL_TEXTENCODING_UTF8);
1524 		}
1525 
1526 		*pPos = new Parameter(*pPos, aAttribute, aCharset, aLanguage, aValue,
1527 							  nSection, bExtended);
1528 	}
1529 	return parseParameters(aList, pParameters) ? pParameterBegin : pBegin;
1530 }
1531 
1532 //============================================================================
1533 // static
1534 const sal_Char * INetMIME::getCharsetName(rtl_TextEncoding eEncoding)
1535 {
1536 	if (rtl_isOctetTextEncoding(eEncoding))
1537 	{
1538         char const * p = rtl_getMimeCharsetFromTextEncoding(eEncoding);
1539 		DBG_ASSERT(p, "INetMIME::getCharsetName(): Unsupported encoding");
1540 		return p;
1541 	}
1542 	else
1543 		switch (eEncoding)
1544 		{
1545 			case RTL_TEXTENCODING_UCS4:
1546 				return "ISO-10646-UCS-4";
1547 
1548 			case RTL_TEXTENCODING_UCS2:
1549 				return "ISO-10646-UCS-2";
1550 
1551 			default:
1552 				DBG_ERROR("INetMIME::getCharsetName(): Unsupported encoding");
1553 				return 0;
1554 		}
1555 }
1556 
1557 //============================================================================
1558 namespace unnamed_tools_inetmime {
1559 
1560 struct EncodingEntry
1561 {
1562 	sal_Char const * m_aName;
1563 	rtl_TextEncoding m_eEncoding;
1564 };
1565 
1566 //============================================================================
1567 // The source for the following table is <ftp://ftp.iana.org/in-notes/iana/
1568 // assignments/character-sets> as of Jan, 21 2000 12:46:00, unless  otherwise
1569 // noted:
1570 EncodingEntry const aEncodingMap[]
1571 	= { { "US-ASCII", RTL_TEXTENCODING_ASCII_US },
1572 		{ "ANSI_X3.4-1968", RTL_TEXTENCODING_ASCII_US },
1573 		{ "ISO-IR-6", RTL_TEXTENCODING_ASCII_US },
1574 		{ "ANSI_X3.4-1986", RTL_TEXTENCODING_ASCII_US },
1575 		{ "ISO_646.IRV:1991", RTL_TEXTENCODING_ASCII_US },
1576 		{ "ASCII", RTL_TEXTENCODING_ASCII_US },
1577 		{ "ISO646-US", RTL_TEXTENCODING_ASCII_US },
1578 		{ "US", RTL_TEXTENCODING_ASCII_US },
1579 		{ "IBM367", RTL_TEXTENCODING_ASCII_US },
1580 		{ "CP367", RTL_TEXTENCODING_ASCII_US },
1581 		{ "CSASCII", RTL_TEXTENCODING_ASCII_US },
1582 		{ "ISO-8859-1", RTL_TEXTENCODING_ISO_8859_1 },
1583 		{ "ISO_8859-1:1987", RTL_TEXTENCODING_ISO_8859_1 },
1584 		{ "ISO-IR-100", RTL_TEXTENCODING_ISO_8859_1 },
1585 		{ "ISO_8859-1", RTL_TEXTENCODING_ISO_8859_1 },
1586 		{ "LATIN1", RTL_TEXTENCODING_ISO_8859_1 },
1587 		{ "L1", RTL_TEXTENCODING_ISO_8859_1 },
1588 		{ "IBM819", RTL_TEXTENCODING_ISO_8859_1 },
1589 		{ "CP819", RTL_TEXTENCODING_ISO_8859_1 },
1590 		{ "CSISOLATIN1", RTL_TEXTENCODING_ISO_8859_1 },
1591 		{ "ISO-8859-2", RTL_TEXTENCODING_ISO_8859_2 },
1592 		{ "ISO_8859-2:1987", RTL_TEXTENCODING_ISO_8859_2 },
1593 		{ "ISO-IR-101", RTL_TEXTENCODING_ISO_8859_2 },
1594 		{ "ISO_8859-2", RTL_TEXTENCODING_ISO_8859_2 },
1595 		{ "LATIN2", RTL_TEXTENCODING_ISO_8859_2 },
1596 		{ "L2", RTL_TEXTENCODING_ISO_8859_2 },
1597 		{ "CSISOLATIN2", RTL_TEXTENCODING_ISO_8859_2 },
1598 		{ "ISO-8859-3", RTL_TEXTENCODING_ISO_8859_3 },
1599 		{ "ISO_8859-3:1988", RTL_TEXTENCODING_ISO_8859_3 },
1600 		{ "ISO-IR-109", RTL_TEXTENCODING_ISO_8859_3 },
1601 		{ "ISO_8859-3", RTL_TEXTENCODING_ISO_8859_3 },
1602 		{ "LATIN3", RTL_TEXTENCODING_ISO_8859_3 },
1603 		{ "L3", RTL_TEXTENCODING_ISO_8859_3 },
1604 		{ "CSISOLATIN3", RTL_TEXTENCODING_ISO_8859_3 },
1605 		{ "ISO-8859-4", RTL_TEXTENCODING_ISO_8859_4 },
1606 		{ "ISO_8859-4:1988", RTL_TEXTENCODING_ISO_8859_4 },
1607 		{ "ISO-IR-110", RTL_TEXTENCODING_ISO_8859_4 },
1608 		{ "ISO_8859-4", RTL_TEXTENCODING_ISO_8859_4 },
1609 		{ "LATIN4", RTL_TEXTENCODING_ISO_8859_4 },
1610 		{ "L4", RTL_TEXTENCODING_ISO_8859_4 },
1611 		{ "CSISOLATIN4", RTL_TEXTENCODING_ISO_8859_4 },
1612 		{ "ISO-8859-5", RTL_TEXTENCODING_ISO_8859_5 },
1613 		{ "ISO_8859-5:1988", RTL_TEXTENCODING_ISO_8859_5 },
1614 		{ "ISO-IR-144", RTL_TEXTENCODING_ISO_8859_5 },
1615 		{ "ISO_8859-5", RTL_TEXTENCODING_ISO_8859_5 },
1616 		{ "CYRILLIC", RTL_TEXTENCODING_ISO_8859_5 },
1617 		{ "CSISOLATINCYRILLIC", RTL_TEXTENCODING_ISO_8859_5 },
1618 		{ "ISO-8859-6", RTL_TEXTENCODING_ISO_8859_6 },
1619 		{ "ISO_8859-6:1987", RTL_TEXTENCODING_ISO_8859_6 },
1620 		{ "ISO-IR-127", RTL_TEXTENCODING_ISO_8859_6 },
1621 		{ "ISO_8859-6", RTL_TEXTENCODING_ISO_8859_6 },
1622 		{ "ECMA-114", RTL_TEXTENCODING_ISO_8859_6 },
1623 		{ "ASMO-708", RTL_TEXTENCODING_ISO_8859_6 },
1624 		{ "ARABIC", RTL_TEXTENCODING_ISO_8859_6 },
1625 		{ "CSISOLATINARABIC", RTL_TEXTENCODING_ISO_8859_6 },
1626 		{ "ISO-8859-7", RTL_TEXTENCODING_ISO_8859_7 },
1627 		{ "ISO_8859-7:1987", RTL_TEXTENCODING_ISO_8859_7 },
1628 		{ "ISO-IR-126", RTL_TEXTENCODING_ISO_8859_7 },
1629 		{ "ISO_8859-7", RTL_TEXTENCODING_ISO_8859_7 },
1630 		{ "ELOT_928", RTL_TEXTENCODING_ISO_8859_7 },
1631 		{ "ECMA-118", RTL_TEXTENCODING_ISO_8859_7 },
1632 		{ "GREEK", RTL_TEXTENCODING_ISO_8859_7 },
1633 		{ "GREEK8", RTL_TEXTENCODING_ISO_8859_7 },
1634 		{ "CSISOLATINGREEK", RTL_TEXTENCODING_ISO_8859_7 },
1635 		{ "ISO-8859-8", RTL_TEXTENCODING_ISO_8859_8 },
1636 		{ "ISO_8859-8:1988", RTL_TEXTENCODING_ISO_8859_8 },
1637 		{ "ISO-IR-138", RTL_TEXTENCODING_ISO_8859_8 },
1638 		{ "ISO_8859-8", RTL_TEXTENCODING_ISO_8859_8 },
1639 		{ "HEBREW", RTL_TEXTENCODING_ISO_8859_8 },
1640 		{ "CSISOLATINHEBREW", RTL_TEXTENCODING_ISO_8859_8 },
1641 		{ "ISO-8859-9", RTL_TEXTENCODING_ISO_8859_9 },
1642 		{ "ISO_8859-9:1989", RTL_TEXTENCODING_ISO_8859_9 },
1643 		{ "ISO-IR-148", RTL_TEXTENCODING_ISO_8859_9 },
1644 		{ "ISO_8859-9", RTL_TEXTENCODING_ISO_8859_9 },
1645 		{ "LATIN5", RTL_TEXTENCODING_ISO_8859_9 },
1646 		{ "L5", RTL_TEXTENCODING_ISO_8859_9 },
1647 		{ "CSISOLATIN5", RTL_TEXTENCODING_ISO_8859_9 },
1648 		{ "ISO-8859-14", RTL_TEXTENCODING_ISO_8859_14 }, // RFC 2047
1649 		{ "ISO_8859-15", RTL_TEXTENCODING_ISO_8859_15 },
1650 		{ "ISO-8859-15", RTL_TEXTENCODING_ISO_8859_15 }, // RFC 2047
1651 		{ "MACINTOSH", RTL_TEXTENCODING_APPLE_ROMAN },
1652 		{ "MAC", RTL_TEXTENCODING_APPLE_ROMAN },
1653 		{ "CSMACINTOSH", RTL_TEXTENCODING_APPLE_ROMAN },
1654 		{ "IBM437", RTL_TEXTENCODING_IBM_437 },
1655 		{ "CP437", RTL_TEXTENCODING_IBM_437 },
1656 		{ "437", RTL_TEXTENCODING_IBM_437 },
1657 		{ "CSPC8CODEPAGE437", RTL_TEXTENCODING_IBM_437 },
1658 		{ "IBM850", RTL_TEXTENCODING_IBM_850 },
1659 		{ "CP850", RTL_TEXTENCODING_IBM_850 },
1660 		{ "850", RTL_TEXTENCODING_IBM_850 },
1661 		{ "CSPC850MULTILINGUAL", RTL_TEXTENCODING_IBM_850 },
1662 		{ "IBM860", RTL_TEXTENCODING_IBM_860 },
1663 		{ "CP860", RTL_TEXTENCODING_IBM_860 },
1664 		{ "860", RTL_TEXTENCODING_IBM_860 },
1665 		{ "CSIBM860", RTL_TEXTENCODING_IBM_860 },
1666 		{ "IBM861", RTL_TEXTENCODING_IBM_861 },
1667 		{ "CP861", RTL_TEXTENCODING_IBM_861 },
1668 		{ "861", RTL_TEXTENCODING_IBM_861 },
1669 		{ "CP-IS", RTL_TEXTENCODING_IBM_861 },
1670 		{ "CSIBM861", RTL_TEXTENCODING_IBM_861 },
1671 		{ "IBM863", RTL_TEXTENCODING_IBM_863 },
1672 		{ "CP863", RTL_TEXTENCODING_IBM_863 },
1673 		{ "863", RTL_TEXTENCODING_IBM_863 },
1674 		{ "CSIBM863", RTL_TEXTENCODING_IBM_863 },
1675 		{ "IBM865", RTL_TEXTENCODING_IBM_865 },
1676 		{ "CP865", RTL_TEXTENCODING_IBM_865 },
1677 		{ "865", RTL_TEXTENCODING_IBM_865 },
1678 		{ "CSIBM865", RTL_TEXTENCODING_IBM_865 },
1679 		{ "IBM775", RTL_TEXTENCODING_IBM_775 },
1680 		{ "CP775", RTL_TEXTENCODING_IBM_775 },
1681 		{ "CSPC775BALTIC", RTL_TEXTENCODING_IBM_775 },
1682 		{ "IBM852", RTL_TEXTENCODING_IBM_852 },
1683 		{ "CP852", RTL_TEXTENCODING_IBM_852 },
1684 		{ "852", RTL_TEXTENCODING_IBM_852 },
1685 		{ "CSPCP852", RTL_TEXTENCODING_IBM_852 },
1686 		{ "IBM855", RTL_TEXTENCODING_IBM_855 },
1687 		{ "CP855", RTL_TEXTENCODING_IBM_855 },
1688 		{ "855", RTL_TEXTENCODING_IBM_855 },
1689 		{ "CSIBM855", RTL_TEXTENCODING_IBM_855 },
1690 		{ "IBM857", RTL_TEXTENCODING_IBM_857 },
1691 		{ "CP857", RTL_TEXTENCODING_IBM_857 },
1692 		{ "857", RTL_TEXTENCODING_IBM_857 },
1693 		{ "CSIBM857", RTL_TEXTENCODING_IBM_857 },
1694 		{ "IBM862", RTL_TEXTENCODING_IBM_862 },
1695 		{ "CP862", RTL_TEXTENCODING_IBM_862 },
1696 		{ "862", RTL_TEXTENCODING_IBM_862 },
1697 		{ "CSPC862LATINHEBREW", RTL_TEXTENCODING_IBM_862 },
1698 		{ "IBM864", RTL_TEXTENCODING_IBM_864 },
1699 		{ "CP864", RTL_TEXTENCODING_IBM_864 },
1700 		{ "CSIBM864", RTL_TEXTENCODING_IBM_864 },
1701 		{ "IBM866", RTL_TEXTENCODING_IBM_866 },
1702 		{ "CP866", RTL_TEXTENCODING_IBM_866 },
1703 		{ "866", RTL_TEXTENCODING_IBM_866 },
1704 		{ "CSIBM866", RTL_TEXTENCODING_IBM_866 },
1705 		{ "IBM869", RTL_TEXTENCODING_IBM_869 },
1706 		{ "CP869", RTL_TEXTENCODING_IBM_869 },
1707 		{ "869", RTL_TEXTENCODING_IBM_869 },
1708 		{ "CP-GR", RTL_TEXTENCODING_IBM_869 },
1709 		{ "CSIBM869", RTL_TEXTENCODING_IBM_869 },
1710 		{ "WINDOWS-1250", RTL_TEXTENCODING_MS_1250 },
1711 		{ "WINDOWS-1251", RTL_TEXTENCODING_MS_1251 },
1712 		{ "WINDOWS-1253", RTL_TEXTENCODING_MS_1253 },
1713 		{ "WINDOWS-1254", RTL_TEXTENCODING_MS_1254 },
1714 		{ "WINDOWS-1255", RTL_TEXTENCODING_MS_1255 },
1715 		{ "WINDOWS-1256", RTL_TEXTENCODING_MS_1256 },
1716 		{ "WINDOWS-1257", RTL_TEXTENCODING_MS_1257 },
1717 		{ "WINDOWS-1258", RTL_TEXTENCODING_MS_1258 },
1718 		{ "SHIFT_JIS", RTL_TEXTENCODING_SHIFT_JIS },
1719 		{ "MS_KANJI", RTL_TEXTENCODING_SHIFT_JIS },
1720 		{ "CSSHIFTJIS", RTL_TEXTENCODING_SHIFT_JIS },
1721 		{ "GB2312", RTL_TEXTENCODING_GB_2312 },
1722 		{ "CSGB2312", RTL_TEXTENCODING_GB_2312 },
1723 		{ "BIG5", RTL_TEXTENCODING_BIG5 },
1724 		{ "CSBIG5", RTL_TEXTENCODING_BIG5 },
1725 		{ "EUC-JP", RTL_TEXTENCODING_EUC_JP },
1726 		{ "EXTENDED_UNIX_CODE_PACKED_FORMAT_FOR_JAPANESE",
1727 		  RTL_TEXTENCODING_EUC_JP },
1728 		{ "CSEUCPKDFMTJAPANESE", RTL_TEXTENCODING_EUC_JP },
1729 		{ "ISO-2022-JP", RTL_TEXTENCODING_ISO_2022_JP },
1730 		{ "CSISO2022JP", RTL_TEXTENCODING_ISO_2022_JP },
1731 		{ "ISO-2022-CN", RTL_TEXTENCODING_ISO_2022_CN },
1732 		{ "KOI8-R", RTL_TEXTENCODING_KOI8_R },
1733 		{ "CSKOI8R", RTL_TEXTENCODING_KOI8_R },
1734 		{ "UTF-7", RTL_TEXTENCODING_UTF7 },
1735 		{ "UTF-8", RTL_TEXTENCODING_UTF8 },
1736 		{ "ISO-8859-10", RTL_TEXTENCODING_ISO_8859_10 }, // RFC 2047
1737 		{ "ISO-8859-13", RTL_TEXTENCODING_ISO_8859_13 }, // RFC 2047
1738 		{ "EUC-KR", RTL_TEXTENCODING_EUC_KR },
1739 		{ "CSEUCKR", RTL_TEXTENCODING_EUC_KR },
1740 		{ "ISO-2022-KR", RTL_TEXTENCODING_ISO_2022_KR },
1741 		{ "CSISO2022KR", RTL_TEXTENCODING_ISO_2022_KR },
1742 		{ "ISO-10646-UCS-4", RTL_TEXTENCODING_UCS4 },
1743 		{ "CSUCS4", RTL_TEXTENCODING_UCS4 },
1744 		{ "ISO-10646-UCS-2", RTL_TEXTENCODING_UCS2 },
1745 		{ "CSUNICODE", RTL_TEXTENCODING_UCS2 } };
1746 
1747 //============================================================================
1748 template< typename T >
1749 inline rtl_TextEncoding getCharsetEncoding_Impl(T const * pBegin,
1750 												T const * pEnd)
1751 {
1752 	for (sal_Size i = 0; i < sizeof aEncodingMap / sizeof (EncodingEntry);
1753 		 ++i)
1754 		if (INetMIME::equalIgnoreCase(pBegin, pEnd, aEncodingMap[i].m_aName))
1755 			return aEncodingMap[i].m_eEncoding;
1756 	return RTL_TEXTENCODING_DONTKNOW;
1757 }
1758 
1759 }
1760 
1761 //============================================================================
1762 // static
1763 rtl_TextEncoding INetMIME::getCharsetEncoding(sal_Char const * pBegin,
1764 											  sal_Char const * pEnd)
1765 {
1766 	return getCharsetEncoding_Impl(pBegin, pEnd);
1767 }
1768 
1769 //============================================================================
1770 // static
1771 rtl_TextEncoding INetMIME::getCharsetEncoding(sal_Unicode const * pBegin,
1772 											  sal_Unicode const * pEnd)
1773 {
1774 	return getCharsetEncoding_Impl(pBegin, pEnd);
1775 }
1776 
1777 //============================================================================
1778 // static
1779 INetMIMECharsetList_Impl *
1780 INetMIME::createPreferredCharsetList(rtl_TextEncoding eEncoding)
1781 {
1782 	static const sal_uInt32 aUSASCIIRanges[] = { 0, 0x7F, sal_uInt32(-1) };
1783 
1784 	static const sal_uInt32 aISO88591Ranges[] = { 0, 0xFF, sal_uInt32(-1) };
1785 		// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT> version
1786 		// 1.0 of 1999 July 27
1787 
1788 	static const sal_uInt32 aISO88592Ranges[]
1789 		= { 0, 0xA0, 0xA4, 0xA4, 0xA7, 0xA8, 0xAD, 0xAD, 0xB0, 0xB0,
1790 			0xB4, 0xB4, 0xB8, 0xB8, 0xC1, 0xC2, 0xC4, 0xC4, 0xC7, 0xC7,
1791 			0xC9, 0xC9, 0xCB, 0xCB, 0xCD, 0xCE, 0xD3, 0xD4, 0xD6, 0xD7,
1792 			0xDA, 0xDA, 0xDC, 0xDD, 0xDF, 0xDF, 0xE1, 0xE2, 0xE4, 0xE4,
1793 			0xE7, 0xE7, 0xE9, 0xE9, 0xEB, 0xEB, 0xED, 0xEE, 0xF3, 0xF4,
1794 			0xF6, 0xF7, 0xFA, 0xFA, 0xFC, 0xFD, 0x102, 0x107, 0x10C, 0x111,
1795 			0x118, 0x11B, 0x139, 0x13A, 0x13D, 0x13E, 0x141, 0x144,
1796 			0x147, 0x148, 0x150, 0x151, 0x154, 0x155, 0x158, 0x15B,
1797 			0x15E, 0x165, 0x16E, 0x171, 0x179, 0x17E, 0x2C7, 0x2C7,
1798 			0x2D8, 0x2D9, 0x2DB, 0x2DB, 0x2DD, 0x2DD, sal_uInt32(-1) };
1799 		// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-2.TXT> version
1800 		// 1.0 of 1999 July 27
1801 
1802 	static const sal_uInt32 aISO88593Ranges[]
1803 		= { 0, 0xA0, 0xA3, 0xA4, 0xA7, 0xA8, 0xAD, 0xAD, 0xB0, 0xB0,
1804 			0xB2, 0xB5, 0xB7, 0xB8, 0xBD, 0xBD, 0xC0, 0xC2, 0xC4, 0xC4,
1805 			0xC7, 0xCF, 0xD1, 0xD4, 0xD6, 0xD7, 0xD9, 0xDC, 0xDF, 0xE2,
1806 			0xE4, 0xE4, 0xE7, 0xEF, 0xF1, 0xF4, 0xF6, 0xF7, 0xF9, 0xFC,
1807 			0x108, 0x10B, 0x11C, 0x121, 0x124, 0x127, 0x130, 0x131,
1808 			0x134, 0x135, 0x15C, 0x15F, 0x16C, 0x16D, 0x17B, 0x17C,
1809 			0x2D8, 0x2D9, sal_uInt32(-1) };
1810 		// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-3.TXT> version
1811 		// 1.0 of 1999 July 27
1812 
1813 	static const sal_uInt32 aISO88594Ranges[]
1814 		= { 0, 0xA0, 0xA4, 0xA4, 0xA7, 0xA8, 0xAD, 0xAD, 0xAF, 0xB0,
1815 			0xB4, 0xB4, 0xB8, 0xB8, 0xC1, 0xC6, 0xC9, 0xC9, 0xCB, 0xCB,
1816 			0xCD, 0xCE, 0xD4, 0xD8, 0xDA, 0xDC, 0xDF, 0xDF, 0xE1, 0xE6,
1817 			0xE9, 0xE9, 0xEB, 0xEB, 0xED, 0xEE, 0xF4, 0xF8, 0xFA, 0xFC,
1818 			0x100, 0x101, 0x104, 0x105, 0x10C, 0x10D, 0x110, 0x113,
1819 			0x116, 0x119, 0x122, 0x123, 0x128, 0x12B, 0x12E, 0x12F,
1820 			0x136, 0x138, 0x13B, 0x13C, 0x145, 0x146, 0x14A, 0x14D,
1821 			0x156, 0x157, 0x160, 0x161, 0x166, 0x16B, 0x172, 0x173,
1822 			0x17D, 0x17E, 0x2C7, 0x2C7, 0x2D9, 0x2D9, 0x2DB, 0x2DB,
1823 			sal_uInt32(-1) };
1824 		// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-4.TXT> version
1825 		// 1.0 of 1999 July 27
1826 
1827 	static const sal_uInt32 aISO88595Ranges[]
1828 		= { 0, 0xA0, 0xA7, 0xA7, 0xAD, 0xAD, 0x401, 0x40C, 0x40E, 0x44F,
1829 			0x451, 0x45C, 0x45E, 0x45F, 0x2116, 0x2116, sal_uInt32(-1) };
1830 		// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-5.TXT> version
1831 		// 1.0 of 1999 July 27
1832 
1833 	static const sal_uInt32 aISO88596Ranges[]
1834 		= { 0, 0xA0, 0xA4, 0xA4, 0xAD, 0xAD, 0x60C, 0x60C, 0x61B, 0x61B,
1835 			0x61F, 0x61F, 0x621, 0x63A, 0x640, 0x652, sal_uInt32(-1) };
1836 		// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-6.TXT> version
1837 		// 1.0 of 1999 July 27
1838 
1839 	static const sal_uInt32 aISO88597Ranges[]
1840 		= { 0, 0xA0, 0xA3, 0xA3, 0xA6, 0xA9, 0xAB, 0xAD, 0xB0, 0xB3,
1841 			0xB7, 0xB7, 0xBB, 0xBB, 0xBD, 0xBD, 0x384, 0x386, 0x388, 0x38A,
1842 			0x38C, 0x38C, 0x38E, 0x3A1, 0x3A3, 0x3CE, 0x2015, 0x2015,
1843 			0x2018, 0x2019, sal_uInt32(-1) };
1844 		// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-7.TXT> version
1845 		// 1.0 of 1999 July 27
1846 
1847 	static const sal_uInt32 aISO88598Ranges[]
1848 		= { 0, 0xA0, 0xA2, 0xA9, 0xAB, 0xB9, 0xBB, 0xBE, 0xD7, 0xD7,
1849 			0xF7, 0xF7, 0x5D0, 0x5EA, 0x200E, 0x200F, 0x2017, 0x2017,
1850 			sal_uInt32(-1) };
1851 		// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-8.TXT> version
1852 		// 1.1 of 2000-Jan-03
1853 
1854 	static const sal_uInt32 aISO88599Ranges[]
1855 		= { 0, 0xCF, 0xD1, 0xDC, 0xDF, 0xEF, 0xF1, 0xFC, 0xFF, 0xFF,
1856 			0x11E, 0x11F, 0x130, 0x131, 0x15E, 0x15F, sal_uInt32(-1) };
1857 		// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-9.TXT> version
1858 		// 1.0 of 1999 July 27
1859 
1860 	static const sal_uInt32 aISO885910Ranges[]
1861 		= { 0, 0xA0, 0xA7, 0xA7, 0xAD, 0xAD, 0xB0, 0xB0, 0xB7, 0xB7,
1862 			0xC1, 0xC6, 0xC9, 0xC9, 0xCB, 0xCB, 0xCD, 0xD0, 0xD3, 0xD6,
1863 			0xD8, 0xD8, 0xDA, 0xDF, 0xE1, 0xE6, 0xE9, 0xE9, 0xEB, 0xEB,
1864 			0xED, 0xF0, 0xF3, 0xF6, 0xF8, 0xF8, 0xFA, 0xFE, 0x100, 0x101,
1865 			0x104, 0x105, 0x10C, 0x10D, 0x110, 0x113, 0x116, 0x119,
1866 			0x122, 0x123, 0x128, 0x12B, 0x12E, 0x12F, 0x136, 0x138,
1867 			0x13B, 0x13C, 0x145, 0x146, 0x14A, 0x14D, 0x160, 0x161,
1868 			0x166, 0x16B, 0x172, 0x173, 0x17D, 0x17E, 0x2015, 0x2015,
1869 			sal_uInt32(-1) };
1870 		// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-10.TXT> version
1871 		// 1.1 of 1999 October 11
1872 
1873 	static const sal_uInt32 aISO885913Ranges[]
1874 		= { 0, 0xA0, 0xA2, 0xA4, 0xA6, 0xA7, 0xA9, 0xA9, 0xAB, 0xAE,
1875 			0xB0, 0xB3, 0xB5, 0xB7, 0xB9, 0xB9, 0xBB, 0xBE, 0xC4, 0xC6,
1876 			0xC9, 0xC9, 0xD3, 0xD3, 0xD5, 0xD8, 0xDC, 0xDC, 0xDF, 0xDF,
1877 			0xE4, 0xE6, 0xE9, 0xE9, 0xF3, 0xF3, 0xF5, 0xF8, 0xFC, 0xFC,
1878 			0x100, 0x101, 0x104, 0x107, 0x10C, 0x10D, 0x112, 0x113,
1879 			0x116, 0x119, 0x122, 0x123, 0x12A, 0x12B, 0x12E, 0x12F,
1880 			0x136, 0x137, 0x13B, 0x13C, 0x141, 0x146, 0x14C, 0x14D,
1881 			0x156, 0x157, 0x15A, 0x15B, 0x160, 0x161, 0x16A, 0x16B,
1882 			0x172, 0x173, 0x179, 0x17E, 0x2019, 0x2019, 0x201C, 0x201E,
1883 			sal_uInt32(-1) };
1884 		// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-13.TXT> version
1885 		// 1.0 of 1999 July 27
1886 
1887 	static const sal_uInt32 aISO885914Ranges[]
1888 		= { 0, 0xA0, 0xA3, 0xA3, 0xA7, 0xA7, 0xA9, 0xA9, 0xAD, 0xAE,
1889 			0xB6, 0xB6, 0xC0, 0xCF, 0xD1, 0xD6, 0xD8, 0xDD, 0xDF, 0xEF,
1890 			0xF1, 0xF6, 0xF8, 0xFD, 0xFF, 0xFF, 0x10A, 0x10B, 0x120, 0x121,
1891 			0x174, 0x178, 0x1E02, 0x1E03, 0x1E0A, 0x1E0B, 0x1E1E, 0x1E1F,
1892 			0x1E40, 0x1E41, 0x1E56, 0x1E57, 0x1E60, 0x1E61, 0x1E6A, 0x1E6B,
1893 			0x1E80, 0x1E85, 0x1EF2, 0x1EF3, sal_uInt32(-1) };
1894 		// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-14.TXT> version
1895 		// 1.0 of 1999 July 27
1896 
1897 	static const sal_uInt32 aISO885915Ranges[]
1898 		= { 0, 0xA3, 0xA5, 0xA5, 0xA7, 0xA7, 0xA9, 0xB3, 0xB5, 0xB7,
1899 			0xB9, 0xBB, 0xBF, 0xFF, 0x152, 0x153, 0x160, 0x161, 0x178, 0x178,
1900 			0x17D, 0x17E, 0x20AC, 0x20AC, sal_uInt32(-1) };
1901 		// <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-15.TXT> version
1902 		// 1.0 of 1999 July 27
1903 
1904 	static const sal_uInt32 aKOI8RRanges[]
1905 		= { 0, 0x7F, 0xA0, 0xA0, 0xA9, 0xA9, 0xB0, 0xB0, 0xB2, 0xB2,
1906 			0xB7, 0xB7, 0xF7, 0xF7, 0x401, 0x401, 0x410, 0x44F, 0x451, 0x451,
1907 			0x2219, 0x221A, 0x2248, 0x2248, 0x2264, 0x2265, 0x2320, 0x2321,
1908 			0x2500, 0x2500, 0x2502, 0x2502, 0x250C, 0x250C, 0x2510, 0x2510,
1909 			0x2514, 0x2514, 0x2518, 0x2518, 0x251C, 0x251C, 0x2524, 0x2524,
1910 			0x252C, 0x252C, 0x2534, 0x2534, 0x253C, 0x253C, 0x2550, 0x256C,
1911 			0x2580, 0x2580, 0x2584, 0x2584, 0x2588, 0x2588, 0x258C, 0x258C,
1912 			0x2590, 0x2593, 0x25A0, 0x25A0, sal_uInt32(-1) };
1913 		// <ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MISC/KOI8-R.TXT>
1914 		// version 1.0 of 18 August 1999
1915 
1916 #if defined WNT
1917 	static const sal_uInt32 aWindows1252Ranges[]
1918 		= { 0, 0x7F, 0xA0, 0xFF, 0x152, 0x153, 0x160, 0x161, 0x178, 0x178,
1919 			0x17D, 0x17E, 0x192, 0x192, 0x2C6, 0x2C6, 0x2DC, 0x2DC,
1920 			0x2013, 0x2014, 0x2018, 0x201A, 0x201C, 0x201E, 0x2020, 0x2022,
1921 			0x2026, 0x2026, 0x2030, 0x2030, 0x2039, 0x203A, 0x20AC, 0x20AC,
1922 			0x2122, 0x2122, sal_uInt32(-1) };
1923 		// <ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/
1924 		// CP1252.TXT> version 2.01 of 04/15/98
1925 #endif // WNT
1926 
1927 	INetMIMECharsetList_Impl * pList = new INetMIMECharsetList_Impl;
1928 	switch (eEncoding)
1929 	{
1930 		case RTL_TEXTENCODING_MS_1252:
1931 #if defined WNT
1932 			pList->prepend(Charset(RTL_TEXTENCODING_MS_1252,
1933 								   aWindows1252Ranges));
1934 #endif // WNT
1935 		case RTL_TEXTENCODING_ISO_8859_1:
1936 		case RTL_TEXTENCODING_UTF7:
1937 		case RTL_TEXTENCODING_UTF8:
1938 			break;
1939 
1940 		case RTL_TEXTENCODING_ISO_8859_2:
1941 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_2,
1942 								   aISO88592Ranges));
1943 			break;
1944 
1945 		case RTL_TEXTENCODING_ISO_8859_3:
1946 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_3,
1947 								   aISO88593Ranges));
1948 			break;
1949 
1950 		case RTL_TEXTENCODING_ISO_8859_4:
1951 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_4,
1952 								   aISO88594Ranges));
1953 			break;
1954 
1955 		case RTL_TEXTENCODING_ISO_8859_5:
1956 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_5,
1957 								   aISO88595Ranges));
1958 			break;
1959 
1960 		case RTL_TEXTENCODING_ISO_8859_6:
1961 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_6,
1962 								   aISO88596Ranges));
1963 			break;
1964 
1965 		case RTL_TEXTENCODING_ISO_8859_7:
1966 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_7,
1967 								   aISO88597Ranges));
1968 			break;
1969 
1970 		case RTL_TEXTENCODING_ISO_8859_8:
1971 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_8,
1972 								   aISO88598Ranges));
1973 			break;
1974 
1975 		case RTL_TEXTENCODING_ISO_8859_9:
1976 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_9,
1977 								   aISO88599Ranges));
1978 			break;
1979 
1980 		case RTL_TEXTENCODING_ISO_8859_10:
1981 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_10,
1982 								   aISO885910Ranges));
1983 			break;
1984 
1985 		case RTL_TEXTENCODING_ISO_8859_13:
1986 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_13,
1987 								   aISO885913Ranges));
1988 			break;
1989 
1990 		case RTL_TEXTENCODING_ISO_8859_14:
1991 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_14,
1992 								   aISO885914Ranges));
1993 			break;
1994 
1995 		case RTL_TEXTENCODING_ISO_8859_15:
1996 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_15,
1997 								   aISO885915Ranges));
1998 			break;
1999 
2000 		case RTL_TEXTENCODING_MS_1250:
2001 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_2,
2002 								   aISO88592Ranges));
2003 			break;
2004 
2005 		case RTL_TEXTENCODING_MS_1251:
2006 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_5,
2007 								   aISO88595Ranges));
2008 			break;
2009 
2010 		case RTL_TEXTENCODING_MS_1253:
2011 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_7,
2012 								   aISO88597Ranges));
2013 			break;
2014 
2015 		case RTL_TEXTENCODING_MS_1254:
2016 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_9,
2017 								   aISO88599Ranges));
2018 			break;
2019 
2020 		case RTL_TEXTENCODING_MS_1255:
2021 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_8,
2022 								   aISO88598Ranges));
2023 			break;
2024 
2025 		case RTL_TEXTENCODING_MS_1256:
2026 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_6,
2027 								   aISO88596Ranges));
2028 			break;
2029 
2030 		case RTL_TEXTENCODING_MS_1257:
2031 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_4,
2032 								   aISO88594Ranges));
2033 			break;
2034 
2035 		case RTL_TEXTENCODING_KOI8_R:
2036 			pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_5,
2037 								   aISO88595Ranges));
2038 			pList->prepend(Charset(RTL_TEXTENCODING_KOI8_R, aKOI8RRanges));
2039 			break;
2040 
2041 		default: //@@@ more cases are missing!
2042 			DBG_ERROR("INetMIME::createPreferredCharsetList():"
2043 					      " Unsupported encoding");
2044 			break;
2045 	}
2046 	pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_1, aISO88591Ranges));
2047 	pList->prepend(Charset(RTL_TEXTENCODING_ASCII_US, aUSASCIIRanges));
2048 	return pList;
2049 }
2050 
2051 //============================================================================
2052 // static
2053 sal_Unicode * INetMIME::convertToUnicode(const sal_Char * pBegin,
2054 										 const sal_Char * pEnd,
2055 										 rtl_TextEncoding eEncoding,
2056 										 sal_Size & rSize)
2057 {
2058 	if (eEncoding == RTL_TEXTENCODING_DONTKNOW)
2059 		return 0;
2060 	rtl_TextToUnicodeConverter hConverter
2061 		= rtl_createTextToUnicodeConverter(eEncoding);
2062 	rtl_TextToUnicodeContext hContext
2063 		= rtl_createTextToUnicodeContext(hConverter);
2064 	sal_Unicode * pBuffer;
2065 	sal_uInt32 nInfo;
2066 	for (sal_Size nBufferSize = pEnd - pBegin;;
2067 		 nBufferSize += nBufferSize / 3 + 1)
2068 	{
2069 		pBuffer = new sal_Unicode[nBufferSize];
2070 		sal_Size nSrcCvtBytes;
2071 		rSize = rtl_convertTextToUnicode(
2072 			        hConverter, hContext, pBegin, pEnd - pBegin, pBuffer,
2073 					nBufferSize,
2074 					RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
2075 					    | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
2076 					    | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR,
2077 					&nInfo, &nSrcCvtBytes);
2078 		if (nInfo != RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOSMALL)
2079 			break;
2080 		delete[] pBuffer;
2081 		rtl_resetTextToUnicodeContext(hConverter, hContext);
2082 	}
2083 	rtl_destroyTextToUnicodeContext(hConverter, hContext);
2084 	rtl_destroyTextToUnicodeConverter(hConverter);
2085 	if (nInfo != 0)
2086 	{
2087 		delete[] pBuffer;
2088 		pBuffer = 0;
2089 	}
2090 	return pBuffer;
2091 }
2092 
2093 //============================================================================
2094 // static
2095 sal_Char * INetMIME::convertFromUnicode(const sal_Unicode * pBegin,
2096 										const sal_Unicode * pEnd,
2097 										rtl_TextEncoding eEncoding,
2098 										sal_Size & rSize)
2099 {
2100 	if (eEncoding == RTL_TEXTENCODING_DONTKNOW)
2101 		return 0;
2102 	rtl_UnicodeToTextConverter hConverter
2103 		= rtl_createUnicodeToTextConverter(eEncoding);
2104 	rtl_UnicodeToTextContext hContext
2105 		= rtl_createUnicodeToTextContext(hConverter);
2106 	sal_Char * pBuffer;
2107 	sal_uInt32 nInfo;
2108 	for (sal_Size nBufferSize = pEnd - pBegin;;
2109 		 nBufferSize += nBufferSize / 3 + 1)
2110 	{
2111 		pBuffer = new sal_Char[nBufferSize];
2112 		sal_Size nSrcCvtBytes;
2113 		rSize = rtl_convertUnicodeToText(
2114 			        hConverter, hContext, pBegin, pEnd - pBegin, pBuffer,
2115 					nBufferSize,
2116 					RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
2117 					    | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
2118 					    | RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE
2119 					    | RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACESTR,
2120 					&nInfo, &nSrcCvtBytes);
2121 		if (nInfo != RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL)
2122 			break;
2123 		delete[] pBuffer;
2124 		rtl_resetUnicodeToTextContext(hConverter, hContext);
2125 	}
2126 	rtl_destroyUnicodeToTextContext(hConverter, hContext);
2127 	rtl_destroyUnicodeToTextConverter(hConverter);
2128 	if (nInfo != 0)
2129 	{
2130 		delete[] pBuffer;
2131 		pBuffer = 0;
2132 	}
2133 	return pBuffer;
2134 }
2135 
2136 //============================================================================
2137 // static
2138 void INetMIME::writeUTF8(INetMIMEOutputSink & rSink, sal_uInt32 nChar)
2139 {
2140 	// See RFC 2279 for a discussion of UTF-8.
2141 	DBG_ASSERT(nChar < 0x80000000, "INetMIME::writeUTF8(): Bad char");
2142 
2143 	if (nChar < 0x80)
2144 		rSink << sal_Char(nChar);
2145 	else if (nChar < 0x800)
2146 		rSink << sal_Char(nChar >> 6 | 0xC0)
2147 			  << sal_Char((nChar & 0x3F) | 0x80);
2148 	else if (nChar < 0x10000)
2149 		rSink << sal_Char(nChar >> 12 | 0xE0)
2150 			  << sal_Char((nChar >> 6 & 0x3F) | 0x80)
2151 			  << sal_Char((nChar & 0x3F) | 0x80);
2152 	else if (nChar < 0x200000)
2153 		rSink << sal_Char(nChar >> 18 | 0xF0)
2154 			  << sal_Char((nChar >> 12 & 0x3F) | 0x80)
2155 			  << sal_Char((nChar >> 6 & 0x3F) | 0x80)
2156 			  << sal_Char((nChar & 0x3F) | 0x80);
2157 	else if (nChar < 0x4000000)
2158 		rSink << sal_Char(nChar >> 24 | 0xF8)
2159 			  << sal_Char((nChar >> 18 & 0x3F) | 0x80)
2160 			  << sal_Char((nChar >> 12 & 0x3F) | 0x80)
2161 			  << sal_Char((nChar >> 6 & 0x3F) | 0x80)
2162 			  << sal_Char((nChar & 0x3F) | 0x80);
2163 	else
2164 		rSink << sal_Char(nChar >> 30 | 0xFC)
2165 			  << sal_Char((nChar >> 24 & 0x3F) | 0x80)
2166 			  << sal_Char((nChar >> 18 & 0x3F) | 0x80)
2167 			  << sal_Char((nChar >> 12 & 0x3F) | 0x80)
2168 			  << sal_Char((nChar >> 6 & 0x3F) | 0x80)
2169 			  << sal_Char((nChar & 0x3F) | 0x80);
2170 }
2171 
2172 //============================================================================
2173 // static
2174 void INetMIME::writeUnsigned(INetMIMEOutputSink & rSink, sal_uInt32 nValue,
2175 							 int nMinDigits)
2176 {
2177 	sal_Char aBuffer[10];
2178 	    // max unsigned 32 bit value (4294967295) has 10 places
2179 	sal_Char * p = aBuffer;
2180 	for (; nValue > 0; nValue /= 10)
2181 		*p++ = sal_Char(getDigit(nValue % 10));
2182 	nMinDigits -= p - aBuffer;
2183 	while (nMinDigits-- > 0)
2184 		rSink << '0';
2185 	while (p != aBuffer)
2186 		rSink << *--p;
2187 }
2188 
2189 //============================================================================
2190 // static
2191 void INetMIME::writeDateTime(INetMIMEOutputSink & rSink,
2192 							 const DateTime & rUTC)
2193 {
2194 	static const sal_Char aDay[7][3]
2195 		= { { 'M', 'o', 'n' },
2196 			{ 'T', 'u', 'e' },
2197 			{ 'W', 'e', 'd' },
2198 			{ 'T', 'h', 'u' },
2199 			{ 'F', 'r', 'i' },
2200 			{ 'S', 'a', 't' },
2201 			{ 'S', 'u', 'n' } };
2202 	const sal_Char * pTheDay = aDay[rUTC.GetDayOfWeek()];
2203 	rSink.write(pTheDay, pTheDay + 3);
2204 	rSink << ", ";
2205 	writeUnsigned(rSink, rUTC.GetDay());
2206 	rSink << ' ';
2207 	static const sal_Char aMonth[12][3]
2208 		= { { 'J', 'a', 'n' },
2209 			{ 'F', 'e', 'b' },
2210 			{ 'M', 'a', 'r' },
2211 			{ 'A', 'p', 'r' },
2212 			{ 'M', 'a', 'y' },
2213 			{ 'J', 'u', 'n' },
2214 			{ 'J', 'u', 'l' },
2215 			{ 'A', 'u', 'g' },
2216 			{ 'S', 'e', 'p' },
2217 			{ 'O', 'c', 't' },
2218 			{ 'N', 'o', 'v' },
2219 			{ 'D', 'e', 'c' } };
2220 	const sal_Char * pTheMonth = aMonth[rUTC.GetMonth() - 1];
2221 	rSink.write(pTheMonth, pTheMonth + 3);
2222 	rSink << ' ';
2223 	writeUnsigned(rSink, rUTC.GetYear());
2224 	rSink << ' ';
2225 	writeUnsigned(rSink, rUTC.GetHour(), 2);
2226 	rSink << ':';
2227 	writeUnsigned(rSink, rUTC.GetMin(), 2);
2228 	rSink << ':';
2229 	writeUnsigned(rSink, rUTC.GetSec(), 2);
2230 	rSink << " +0000";
2231 }
2232 
2233 //============================================================================
2234 // static
2235 void INetMIME::writeHeaderFieldBody(INetMIMEOutputSink & rSink,
2236 									HeaderFieldType eType,
2237 									const ByteString & rBody,
2238 									rtl_TextEncoding ePreferredEncoding,
2239 									bool bInitialSpace)
2240 {
2241 	writeHeaderFieldBody(rSink, eType,
2242 						 UniString(rBody, RTL_TEXTENCODING_UTF8),
2243 						 ePreferredEncoding, bInitialSpace);
2244 }
2245 
2246 //============================================================================
2247 // static
2248 void INetMIME::writeHeaderFieldBody(INetMIMEOutputSink & rSink,
2249 									HeaderFieldType eType,
2250 									const UniString & rBody,
2251 									rtl_TextEncoding ePreferredEncoding,
2252 									bool bInitialSpace)
2253 {
2254 	if (eType == HEADER_FIELD_TEXT)
2255 	{
2256 		INetMIMEEncodedWordOutputSink
2257 			aOutput(rSink, INetMIMEEncodedWordOutputSink::CONTEXT_TEXT,
2258 					bInitialSpace ?
2259 					    INetMIMEEncodedWordOutputSink::SPACE_ALWAYS :
2260 					    INetMIMEEncodedWordOutputSink::SPACE_NO,
2261 					ePreferredEncoding);
2262 		aOutput.write(rBody.GetBuffer(), rBody.GetBuffer() + rBody.Len());
2263 		aOutput.flush();
2264 	}
2265 	else
2266 	{
2267 		enum Brackets { BRACKETS_OUTSIDE, BRACKETS_OPENING, BRACKETS_INSIDE };
2268 		Brackets eBrackets = BRACKETS_OUTSIDE;
2269 
2270 		const sal_Unicode * pBodyPtr = rBody.GetBuffer();
2271 		const sal_Unicode * pBodyEnd = pBodyPtr + rBody.Len();
2272 		while (pBodyPtr != pBodyEnd)
2273 			switch (*pBodyPtr)
2274 			{
2275 				case '\t':
2276 				case ' ':
2277 					// A WSP adds to accumulated space:
2278 					bInitialSpace = true;
2279 					++pBodyPtr;
2280 					break;
2281 
2282 				case '(':
2283 				{
2284 					// Write a pending '<' if necessary:
2285 					if (eBrackets == BRACKETS_OPENING)
2286 					{
2287 						if (rSink.getColumn() + (bInitialSpace ? 1 : 0)
2288 							    >= rSink.getLineLengthLimit())
2289 							rSink << INetMIMEOutputSink::endl << ' ';
2290 						else if (bInitialSpace)
2291 							rSink << ' ';
2292 						rSink << '<';
2293 						bInitialSpace = false;
2294 						eBrackets = BRACKETS_INSIDE;
2295 					}
2296 
2297 					// Write the comment, introducing encoded-words where
2298 					// necessary:
2299 					int nLevel = 0;
2300 					INetMIMEEncodedWordOutputSink
2301 						aOutput(
2302 							rSink,
2303 							INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT,
2304 							INetMIMEEncodedWordOutputSink::SPACE_NO,
2305 							ePreferredEncoding);
2306 					while (pBodyPtr != pBodyEnd)
2307 						switch (*pBodyPtr)
2308 						{
2309 							case '(':
2310 								aOutput.flush();
2311 								if (rSink.getColumn()
2312 									        + (bInitialSpace ? 1 : 0)
2313 									    >= rSink.getLineLengthLimit())
2314 									rSink << INetMIMEOutputSink::endl << ' ';
2315 								else if (bInitialSpace)
2316 									rSink << ' ';
2317 								rSink << '(';
2318 								bInitialSpace = false;
2319 								++nLevel;
2320 								++pBodyPtr;
2321 								break;
2322 
2323 							case ')':
2324 								aOutput.flush();
2325 								if (rSink.getColumn()
2326 									    >= rSink.getLineLengthLimit())
2327 									rSink << INetMIMEOutputSink::endl << ' ';
2328 								rSink << ')';
2329 								++pBodyPtr;
2330 								if (--nLevel == 0)
2331 									goto comment_done;
2332 								break;
2333 
2334 							case '\\':
2335 								if (++pBodyPtr == pBodyEnd)
2336 									break;
2337 							default:
2338 								aOutput << *pBodyPtr++;
2339 								break;
2340 						}
2341 				comment_done:
2342 					break;
2343 				}
2344 
2345 				case '<':
2346 					// Write an already pending '<' if necessary:
2347 					if (eBrackets == BRACKETS_OPENING)
2348 					{
2349 						if (rSink.getColumn() + (bInitialSpace ? 1 : 0)
2350 							    >= rSink.getLineLengthLimit())
2351 							rSink << INetMIMEOutputSink::endl << ' ';
2352 						else if (bInitialSpace)
2353 							rSink << ' ';
2354 						rSink << '<';
2355 						bInitialSpace = false;
2356 					}
2357 
2358 					// Remember this '<' as pending, and open a bracketed
2359 					// block:
2360 					eBrackets = BRACKETS_OPENING;
2361 					++pBodyPtr;
2362 					break;
2363 
2364 				case '>':
2365 					// Write a pending '<' if necessary:
2366 					if (eBrackets == BRACKETS_OPENING)
2367 					{
2368 						if (rSink.getColumn() + (bInitialSpace ? 1 : 0)
2369 							    >= rSink.getLineLengthLimit())
2370 							rSink << INetMIMEOutputSink::endl << ' ';
2371 						else if (bInitialSpace)
2372 							rSink << ' ';
2373 						rSink << '<';
2374 						bInitialSpace = false;
2375 					}
2376 
2377 					// Write this '>', and close any bracketed block:
2378 					if (rSink.getColumn() + (bInitialSpace ? 1 : 0)
2379 						    >= rSink.getLineLengthLimit())
2380 						rSink << INetMIMEOutputSink::endl << ' ';
2381 					else if (bInitialSpace)
2382 						rSink << ' ';
2383 					rSink << '>';
2384 					bInitialSpace = false;
2385 					eBrackets = BRACKETS_OUTSIDE;
2386 					++pBodyPtr;
2387 					break;
2388 
2389 				case ',':
2390 				case ':':
2391 				case ';':
2392 				case '\\':
2393 				case ']':
2394 					// Write a pending '<' if necessary:
2395 					if (eBrackets == BRACKETS_OPENING)
2396 					{
2397 						if (rSink.getColumn() + (bInitialSpace ? 1 : 0)
2398 							    >= rSink.getLineLengthLimit())
2399 							rSink << INetMIMEOutputSink::endl << ' ';
2400 						else if (bInitialSpace)
2401 							rSink << ' ';
2402 						rSink << '<';
2403 						bInitialSpace = false;
2404 						eBrackets = BRACKETS_INSIDE;
2405 					}
2406 
2407 					// Write this specials:
2408 					if (rSink.getColumn() + (bInitialSpace ? 1 : 0)
2409 						    >= rSink.getLineLengthLimit())
2410 						rSink << INetMIMEOutputSink::endl << ' ';
2411 					else if (bInitialSpace)
2412 						rSink << ' ';
2413 					rSink << sal_Char(*pBodyPtr++);
2414 					bInitialSpace = false;
2415 					break;
2416 
2417 				case '\x0D': // CR
2418 					// A <CRLF WSP> adds to accumulated space, a <CR> not
2419 					// followed by <LF WSP> starts 'junk':
2420 					if (startsWithLineFolding(pBodyPtr, pBodyEnd))
2421 					{
2422 						bInitialSpace = true;
2423 						pBodyPtr += 3;
2424 						break;
2425 					}
2426 				default:
2427 				{
2428 					// The next token is either one of <"." / "@" / atom /
2429 					// quoted-string / domain-literal>, or it's 'junk'; if it
2430 					// is not 'junk', it is either a 'phrase' (i.e., it may
2431 					// contain encoded-words) or a 'non-phrase' (i.e., it may
2432 					// not contain encoded-words):
2433 					enum Entity { ENTITY_JUNK, ENTITY_NON_PHRASE,
2434 								  ENTITY_PHRASE };
2435 					Entity eEntity = ENTITY_JUNK;
2436 					switch (*pBodyPtr)
2437 					{
2438 						case '.':
2439 						case '@':
2440 						case '[':
2441 							// A token of <"." / "@" / domain-literal> always
2442 							// starts a 'non-phrase':
2443 							eEntity = ENTITY_NON_PHRASE;
2444 							break;
2445 
2446 						default:
2447 							if (isUSASCII(*pBodyPtr)
2448 								&& !isAtomChar(*pBodyPtr))
2449 							{
2450 								eEntity = ENTITY_JUNK;
2451 								break;
2452 							}
2453 						case '"':
2454 							// A token of <atom / quoted-string> can either be
2455 							// a 'phrase' or a 'non-phrase':
2456 							switch (eType)
2457 							{
2458 								case HEADER_FIELD_STRUCTURED:
2459 									eEntity = ENTITY_NON_PHRASE;
2460 									break;
2461 
2462 								case HEADER_FIELD_PHRASE:
2463 									eEntity = ENTITY_PHRASE;
2464 									break;
2465 
2466 								case HEADER_FIELD_MESSAGE_ID:
2467 									// A 'phrase' if and only if outside any
2468 									// bracketed block:
2469 									eEntity
2470 										= eBrackets == BRACKETS_OUTSIDE ?
2471 									          ENTITY_PHRASE :
2472 											  ENTITY_NON_PHRASE;
2473 									break;
2474 
2475 								case HEADER_FIELD_ADDRESS:
2476 								{
2477 									// A 'non-phrase' if and only if, after
2478 									// skipping this token and any following
2479 									// <linear-white-space> and <comment>s,
2480 									// there is no token left, or the next
2481 									// token is any of <"." / "@" / ">" / ","
2482 									// / ";">, or the next token is <":"> and
2483 									// is within a bracketed block:
2484 									const sal_Unicode * pLookAhead = pBodyPtr;
2485 									if (*pLookAhead == '"')
2486 									{
2487 										pLookAhead
2488 											= skipQuotedString(pLookAhead,
2489 															   pBodyEnd);
2490 										if (pLookAhead == pBodyPtr)
2491 											pLookAhead = pBodyEnd;
2492 									}
2493 									else
2494 										while (pLookAhead != pBodyEnd
2495 											   && (isAtomChar(*pLookAhead)
2496 												   || !isUSASCII(
2497 													       *pLookAhead)))
2498 											++pLookAhead;
2499 									while (pLookAhead != pBodyEnd)
2500 										switch (*pLookAhead)
2501 										{
2502 											case '\t':
2503 											case ' ':
2504 												++pLookAhead;
2505 												break;
2506 
2507 											case '(':
2508 											{
2509 												const sal_Unicode * pPast
2510 													= skipComment(pLookAhead,
2511 																  pBodyEnd);
2512 												pLookAhead
2513 													= pPast == pLookAhead ?
2514 													      pBodyEnd : pPast;
2515 												break;
2516 											}
2517 
2518 											case ',':
2519 											case '.':
2520 											case ';':
2521 											case '>':
2522 											case '@':
2523 												eEntity = ENTITY_NON_PHRASE;
2524 												goto entity_determined;
2525 
2526 											case ':':
2527 												eEntity
2528 													= eBrackets
2529 													     == BRACKETS_OUTSIDE ?
2530 												          ENTITY_PHRASE :
2531 														  ENTITY_NON_PHRASE;
2532 												goto entity_determined;
2533 
2534 											case '\x0D': // CR
2535 												if (startsWithLineFolding(
2536 													    pLookAhead, pBodyEnd))
2537 												{
2538 													pLookAhead += 3;
2539 													break;
2540 												}
2541 											default:
2542 												eEntity = ENTITY_PHRASE;
2543 												goto entity_determined;
2544 										}
2545 									eEntity = ENTITY_NON_PHRASE;
2546 								entity_determined:
2547 									break;
2548 								}
2549 
2550                                 case HEADER_FIELD_TEXT:
2551                                     OSL_ASSERT(false);
2552                                     break;
2553 							}
2554 
2555 							// In a 'non-phrase', a non-US-ASCII character
2556 							// cannot be part of an <atom>, but instead the
2557 							// whole entity is 'junk' rather than 'non-
2558 							// phrase':
2559 							if (eEntity == ENTITY_NON_PHRASE
2560 								&& !isUSASCII(*pBodyPtr))
2561 								eEntity = ENTITY_JUNK;
2562 							break;
2563 					}
2564 
2565 					switch (eEntity)
2566 					{
2567 						case ENTITY_JUNK:
2568 						{
2569 							// Write a pending '<' if necessary:
2570 							if (eBrackets == BRACKETS_OPENING)
2571 							{
2572 								if (rSink.getColumn()
2573 									        + (bInitialSpace ? 1 : 0)
2574 									    >= rSink.getLineLengthLimit())
2575 									rSink << INetMIMEOutputSink::endl << ' ';
2576 								else if (bInitialSpace)
2577 									rSink << ' ';
2578 								rSink << '<';
2579 								bInitialSpace = false;
2580 								eBrackets = BRACKETS_INSIDE;
2581 							}
2582 
2583 							// Calculate the length of in- and output:
2584 							const sal_Unicode * pStart = pBodyPtr;
2585 							sal_Size nLength = 0;
2586 							bool bModify = false;
2587 							bool bEnd = false;
2588 							while (pBodyPtr != pBodyEnd && !bEnd)
2589 								switch (*pBodyPtr)
2590 								{
2591 									case '\x0D': // CR
2592 										if (startsWithLineFolding(pBodyPtr,
2593 																  pBodyEnd))
2594 											bEnd = true;
2595 										else if (startsWithLineBreak(
2596 											         pBodyPtr, pBodyEnd))
2597 										{
2598 											nLength += 3;
2599 											bModify = true;
2600 											pBodyPtr += 2;
2601 										}
2602 										else
2603 										{
2604 											++nLength;
2605 											++pBodyPtr;
2606 										}
2607 										break;
2608 
2609 									case '\t':
2610 									case ' ':
2611 										bEnd = true;
2612 										break;
2613 
2614 									default:
2615 										if (isVisible(*pBodyPtr))
2616 											bEnd = true;
2617 										else if (isUSASCII(*pBodyPtr))
2618 										{
2619 											++nLength;
2620 											++pBodyPtr;
2621 										}
2622 										else
2623 										{
2624 											nLength += getUTF8OctetCount(
2625 												           *pBodyPtr++);
2626 											bModify = true;
2627 										}
2628 										break;
2629 								}
2630 
2631 							// Write the output:
2632 							if (rSink.getColumn() + (bInitialSpace ? 1 : 0)
2633 								        + nLength
2634 								    > rSink.getLineLengthLimit())
2635 								rSink << INetMIMEOutputSink::endl << ' ';
2636 							else if (bInitialSpace)
2637 								rSink << ' ';
2638 							bInitialSpace = false;
2639 							if (bModify)
2640 								while (pStart != pBodyPtr)
2641 									if (startsWithLineBreak(pStart, pBodyPtr))
2642 									{
2643 										rSink << "\x0D\\\x0A"; // CR, '\', LF
2644 										pStart += 2;
2645 									}
2646 									else
2647 										writeUTF8(rSink, *pStart++);
2648 							else
2649 								rSink.write(pStart, pBodyPtr);
2650 							break;
2651 						}
2652 
2653 						case ENTITY_NON_PHRASE:
2654 						{
2655 							// Calculate the length of in- and output:
2656 							const sal_Unicode * pStart = pBodyPtr;
2657 							sal_Size nLength = 0;
2658 							bool bBracketedBlock = false;
2659 							bool bSymbol = *pStart != '.' && *pStart != '@';
2660 							bool bModify = false;
2661 							bool bEnd = false;
2662 							while (pBodyPtr != pBodyEnd && !bEnd)
2663 								switch (*pBodyPtr)
2664 								{
2665 									case '\t':
2666 									case ' ':
2667 									case '\x0D': // CR
2668 									{
2669 										const sal_Unicode * pLookAhead
2670 											= skipLinearWhiteSpace(pBodyPtr,
2671 																   pBodyEnd);
2672 										if (pLookAhead < pBodyEnd
2673 											&& (bSymbol ?
2674 												    isAtomChar(*pLookAhead)
2675 												    || *pLookAhead == '"'
2676 												    || *pLookAhead == '[' :
2677 												    *pLookAhead == '.'
2678 												    || *pLookAhead == '@'
2679 												    || (*pLookAhead == '>'
2680 												       && eType
2681 												    >= HEADER_FIELD_MESSAGE_ID
2682 												       && eBrackets
2683 												         == BRACKETS_OPENING)))
2684 										{
2685 											bModify = true;
2686 											pBodyPtr = pLookAhead;
2687 										}
2688 										else
2689 											bEnd = true;
2690 										break;
2691 									}
2692 
2693 									case '"':
2694 										if (bSymbol)
2695 										{
2696 											pBodyPtr
2697 												= scanQuotedBlock(pBodyPtr,
2698 																  pBodyEnd,
2699 																  '"', '"',
2700 																  nLength,
2701 																  bModify);
2702 											bSymbol = false;
2703 										}
2704 										else
2705 											bEnd = true;
2706 										break;
2707 
2708 									case '[':
2709 										if (bSymbol)
2710 										{
2711 											pBodyPtr
2712 												= scanQuotedBlock(pBodyPtr,
2713 																  pBodyEnd,
2714 																  '[', ']',
2715 																  nLength,
2716 																  bModify);
2717 											bSymbol = false;
2718 										}
2719 										else
2720 											bEnd = true;
2721 										break;
2722 
2723 									case '.':
2724 									case '@':
2725 										if (bSymbol)
2726 											bEnd = true;
2727 										else
2728 										{
2729 											++nLength;
2730 											bSymbol = true;
2731 											++pBodyPtr;
2732 										}
2733 										break;
2734 
2735 									case '>':
2736 										if (eBrackets == BRACKETS_OPENING
2737 											&& eType
2738 											       >= HEADER_FIELD_MESSAGE_ID)
2739 										{
2740 											++nLength;
2741 											bBracketedBlock = true;
2742 											++pBodyPtr;
2743 										}
2744 										bEnd = true;
2745 										break;
2746 
2747 									default:
2748 										if (isAtomChar(*pBodyPtr) && bSymbol)
2749 										{
2750 											while (pBodyPtr != pBodyEnd
2751 												   && isAtomChar(*pBodyPtr))
2752 											{
2753 												++nLength;
2754 												++pBodyPtr;
2755 											}
2756 											bSymbol = false;
2757 										}
2758 										else
2759 										{
2760 											if (!isUSASCII(*pBodyPtr))
2761 												bModify = true;
2762 											bEnd = true;
2763 										}
2764 										break;
2765 								}
2766 
2767 							// Write a pending '<' if necessary:
2768 							if (eBrackets == BRACKETS_OPENING
2769 								&& !bBracketedBlock)
2770 							{
2771 								if (rSink.getColumn()
2772 									        + (bInitialSpace ? 1 : 0)
2773 									    >= rSink.getLineLengthLimit())
2774 									rSink << INetMIMEOutputSink::endl << ' ';
2775 								else if (bInitialSpace)
2776 									rSink << ' ';
2777 								rSink << '<';
2778 								bInitialSpace = false;
2779 								eBrackets = BRACKETS_INSIDE;
2780 							}
2781 
2782 							// Write the output:
2783 							if (rSink.getColumn() + (bInitialSpace ? 1 : 0)
2784 								        + nLength
2785 								    > rSink.getLineLengthLimit())
2786 								rSink << INetMIMEOutputSink::endl << ' ';
2787 							else if (bInitialSpace)
2788 								rSink << ' ';
2789 							bInitialSpace = false;
2790 							if (bBracketedBlock)
2791 							{
2792 								rSink << '<';
2793 								eBrackets = BRACKETS_OUTSIDE;
2794 							}
2795 							if (bModify)
2796 							{
2797 								enum Mode { MODE_PLAIN, MODE_QUOTED_STRING,
2798 											MODE_DOMAIN_LITERAL };
2799 								Mode eMode = MODE_PLAIN;
2800 								while (pStart != pBodyPtr)
2801 									switch (*pStart)
2802 									{
2803 										case '\x0D': // CR
2804 											if (startsWithLineFolding(
2805 												    pStart, pBodyPtr))
2806 											{
2807 												if (eMode != MODE_PLAIN)
2808 													rSink << sal_Char(
2809 														         pStart[2]);
2810 												pStart += 3;
2811 											}
2812 											else if (startsWithLineBreak(
2813 												         pStart, pBodyPtr))
2814 											{
2815 												rSink << "\x0D\\\x0A";
2816 												    // CR, '\', LF
2817 												pStart += 2;
2818 											}
2819 											else
2820 											{
2821 												rSink << '\x0D'; // CR
2822 												++pStart;
2823 											}
2824 											break;
2825 
2826 										case '\t':
2827 										case ' ':
2828 											if (eMode != MODE_PLAIN)
2829 												rSink << sal_Char(*pStart);
2830 											++pStart;
2831 											break;
2832 
2833 										case '"':
2834 											if (eMode == MODE_PLAIN)
2835 												eMode = MODE_QUOTED_STRING;
2836 											else if (eMode
2837 													    == MODE_QUOTED_STRING)
2838 												eMode = MODE_PLAIN;
2839 											rSink << '"';
2840 											++pStart;
2841 											break;
2842 
2843 										case '[':
2844 											if (eMode == MODE_PLAIN)
2845 												eMode = MODE_DOMAIN_LITERAL;
2846 											rSink << '[';
2847 											++pStart;
2848 											break;
2849 
2850 										case ']':
2851 											if (eMode == MODE_DOMAIN_LITERAL)
2852 												eMode = MODE_PLAIN;
2853 											rSink << ']';
2854 											++pStart;
2855 											break;
2856 
2857 										case '\\':
2858 											rSink << '\\';
2859 											if (++pStart < pBodyPtr)
2860 												writeUTF8(rSink, *pStart++);
2861 											break;
2862 
2863 										default:
2864 											writeUTF8(rSink, *pStart++);
2865 											break;
2866 									}
2867 							}
2868 							else
2869 								rSink.write(pStart, pBodyPtr);
2870 							break;
2871 						}
2872 
2873 						case ENTITY_PHRASE:
2874 						{
2875 							// Write a pending '<' if necessary:
2876 							if (eBrackets == BRACKETS_OPENING)
2877 							{
2878 								if (rSink.getColumn()
2879 									        + (bInitialSpace ? 1 : 0)
2880 									    >= rSink.getLineLengthLimit())
2881 									rSink << INetMIMEOutputSink::endl << ' ';
2882 								else if (bInitialSpace)
2883 									rSink << ' ';
2884 								rSink << '<';
2885 								bInitialSpace = false;
2886 								eBrackets = BRACKETS_INSIDE;
2887 							}
2888 
2889 							// Calculate the length of in- and output:
2890 							const sal_Unicode * pStart = pBodyPtr;
2891 							bool bQuotedString = false;
2892 							bool bEnd = false;
2893 							while (pBodyPtr != pBodyEnd && !bEnd)
2894 								switch (*pBodyPtr)
2895 								{
2896 									case '\t':
2897 									case ' ':
2898 									case '\x0D': // CR
2899 										if (bQuotedString)
2900 											++pBodyPtr;
2901 										else
2902 										{
2903 											const sal_Unicode * pLookAhead
2904 												= skipLinearWhiteSpace(
2905 													  pBodyPtr, pBodyEnd);
2906 											if (pLookAhead != pBodyEnd
2907 												&& (isAtomChar(*pLookAhead)
2908 													|| !isUSASCII(*pLookAhead)
2909 													|| *pLookAhead == '"'))
2910 												pBodyPtr = pLookAhead;
2911 											else
2912 												bEnd = true;
2913 										}
2914 										break;
2915 
2916 									case '"':
2917 										bQuotedString = !bQuotedString;
2918 										++pBodyPtr;
2919 										break;
2920 
2921 									case '\\':
2922 										if (bQuotedString)
2923 										{
2924 											if (++pBodyPtr != pBodyEnd)
2925 												++pBodyPtr;
2926 										}
2927 										else
2928 											bEnd = true;
2929 										break;
2930 
2931 									default:
2932 										if (bQuotedString
2933 											|| isAtomChar(*pBodyPtr)
2934 											|| !isUSASCII(*pBodyPtr))
2935 											++pBodyPtr;
2936 										else
2937 											bEnd = true;
2938 										break;
2939 								}
2940 
2941 							// Write the phrase, introducing encoded-words
2942 							// where necessary:
2943 							INetMIMEEncodedWordOutputSink
2944 								aOutput(
2945 									rSink,
2946 								INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,
2947 									bInitialSpace ?
2948 								 INetMIMEEncodedWordOutputSink::SPACE_ALWAYS :
2949 								 INetMIMEEncodedWordOutputSink::SPACE_ENCODED,
2950 							   ePreferredEncoding);
2951 							while (pStart != pBodyPtr)
2952 								switch (*pStart)
2953 								{
2954 									case '"':
2955 										++pStart;
2956 										break;
2957 
2958 									case '\\':
2959 										if (++pStart != pBodyPtr)
2960 											aOutput << *pStart++;
2961 										break;
2962 
2963 									case '\x0D': // CR
2964 										pStart += 2;
2965 										aOutput << *pStart++;
2966 										break;
2967 
2968 									default:
2969 										aOutput << *pStart++;
2970 										break;
2971 								}
2972 							bInitialSpace = aOutput.flush();
2973 							break;
2974 						}
2975 					}
2976 					break;
2977 				}
2978 			}
2979 	}
2980 }
2981 
2982 //============================================================================
2983 // static
2984 bool INetMIME::translateUTF8Char(const sal_Char *& rBegin,
2985 								 const sal_Char * pEnd,
2986 								 rtl_TextEncoding eEncoding,
2987 								 sal_uInt32 & rCharacter)
2988 {
2989 	if (rBegin == pEnd || static_cast< unsigned char >(*rBegin) < 0x80
2990         || static_cast< unsigned char >(*rBegin) >= 0xFE)
2991 		return false;
2992 
2993 	int nCount;
2994 	sal_uInt32 nMin;
2995 	sal_uInt32 nUCS4;
2996 	const sal_Char * p = rBegin;
2997 	if (static_cast< unsigned char >(*p) < 0xE0)
2998 	{
2999 		nCount = 1;
3000 		nMin = 0x80;
3001 		nUCS4 = static_cast< unsigned char >(*p) & 0x1F;
3002 	}
3003 	else if (static_cast< unsigned char >(*p) < 0xF0)
3004 	{
3005 		nCount = 2;
3006 		nMin = 0x800;
3007 		nUCS4 = static_cast< unsigned char >(*p) & 0xF;
3008 	}
3009 	else if (static_cast< unsigned char >(*p) < 0xF8)
3010 	{
3011 		nCount = 3;
3012 		nMin = 0x10000;
3013 		nUCS4 = static_cast< unsigned char >(*p) & 7;
3014 	}
3015 	else if (static_cast< unsigned char >(*p) < 0xFC)
3016 	{
3017 		nCount = 4;
3018 		nMin = 0x200000;
3019 		nUCS4 = static_cast< unsigned char >(*p) & 3;
3020 	}
3021 	else
3022 	{
3023 		nCount = 5;
3024 		nMin = 0x4000000;
3025 		nUCS4 = static_cast< unsigned char >(*p) & 1;
3026 	}
3027 	++p;
3028 
3029 	for (; nCount-- > 0; ++p)
3030 		if ((static_cast< unsigned char >(*p) & 0xC0) == 0x80)
3031 			nUCS4 = (nUCS4 << 6) | (static_cast< unsigned char >(*p) & 0x3F);
3032 		else
3033 			return false;
3034 
3035 	if (nUCS4 < nMin || nUCS4 > 0x10FFFF)
3036 		return false;
3037 
3038 	if (eEncoding >= RTL_TEXTENCODING_UCS4)
3039 		rCharacter = nUCS4;
3040 	else
3041 	{
3042 		sal_Unicode aUTF16[2];
3043 		const sal_Unicode * pUTF16End = putUTF32Character(aUTF16, nUCS4);
3044 		sal_Size nSize;
3045 		sal_Char * pBuffer = convertFromUnicode(aUTF16, pUTF16End, eEncoding,
3046 												nSize);
3047 		if (!pBuffer)
3048 			return false;
3049 		DBG_ASSERT(nSize == 1,
3050 				   "INetMIME::translateUTF8Char(): Bad conversion");
3051 		rCharacter = *pBuffer;
3052 		delete[] pBuffer;
3053 	}
3054 	rBegin = p;
3055 	return true;
3056 }
3057 
3058 //============================================================================
3059 // static
3060 ByteString INetMIME::decodeUTF8(const ByteString & rText,
3061 								rtl_TextEncoding eEncoding)
3062 {
3063 	const sal_Char * p = rText.GetBuffer();
3064 	const sal_Char * pEnd = p + rText.Len();
3065 	ByteString sDecoded;
3066 	while (p != pEnd)
3067 	{
3068 		sal_uInt32 nCharacter;
3069 		if (translateUTF8Char(p, pEnd, eEncoding, nCharacter))
3070 			sDecoded += sal_Char(nCharacter);
3071 		else
3072 			sDecoded += sal_Char(*p++);
3073 	}
3074 	return sDecoded;
3075 }
3076 
3077 //============================================================================
3078 // static
3079 UniString INetMIME::decodeHeaderFieldBody(HeaderFieldType eType,
3080 										  const ByteString & rBody)
3081 {
3082 	// Due to a bug in INetCoreRFC822MessageStream::ConvertTo7Bit(), old
3083 	// versions of StarOffice send mails with header fields where encoded
3084 	// words can be preceded by '=', ',', '.', '"', or '(', and followed by
3085 	// '=', ',', '.', '"', ')', without any required white space in between.
3086 	// And there appear to exist some broken mailers that only encode single
3087 	// letters within words, like "Appel
3088 	// =?iso-8859-1?Q?=E0?=t=?iso-8859-1?Q?=E9?=moin", so it seems best to
3089 	// detect encoded words even when not propperly surrounded by white space.
3090 	//
3091 	// Non US-ASCII characters in rBody are treated as ISO-8859-1.
3092 	//
3093 	// encoded-word = "=?"
3094 	//     1*(%x21 / %x23-27 / %x2A-2B / %x2D / %30-39 / %x41-5A / %x5E-7E)
3095 	//     ["*" 1*8ALPHA *("-" 1*8ALPHA)] "?"
3096 	//     ("B?" *(4base64) (4base64 / 3base64 "=" / 2base64 "==")
3097 	//      / "Q?" 1*(%x21-3C / %x3E / %x40-7E / "=" 2HEXDIG))
3098 	//     "?="
3099 	//
3100 	// base64 = ALPHA / DIGIT / "+" / "/"
3101 
3102 	const sal_Char * pBegin = rBody.GetBuffer();
3103 	const sal_Char * pEnd = pBegin + rBody.Len();
3104 
3105 	UniString sDecoded;
3106 	const sal_Char * pCopyBegin = pBegin;
3107 
3108 	/* bool bStartEncodedWord = true; */
3109 	const sal_Char * pWSPBegin = pBegin;
3110 	UniString sEncodedText;
3111 	bool bQuotedEncodedText = false;
3112 	sal_uInt32 nCommentLevel = 0;
3113 
3114 	for (const sal_Char * p = pBegin; p != pEnd;)
3115 	{
3116 		if (p != pEnd && *p == '=' /* && bStartEncodedWord */)
3117 		{
3118 			const sal_Char * q = p + 1;
3119 			bool bEncodedWord = q != pEnd && *q++ == '?';
3120 
3121 			rtl_TextEncoding eCharsetEncoding = RTL_TEXTENCODING_DONTKNOW;
3122 			if (bEncodedWord)
3123 			{
3124 				const sal_Char * pCharsetBegin = q;
3125 				const sal_Char * pLanguageBegin = 0;
3126 				int nAlphaCount = 0;
3127 				for (bool bDone = false; !bDone;)
3128 					if (q == pEnd)
3129 					{
3130 						bEncodedWord = false;
3131 						bDone = true;
3132 					}
3133 					else
3134 					{
3135 						sal_Char cChar = *q++;
3136 						switch (cChar)
3137 						{
3138 							case '*':
3139 								pLanguageBegin = q - 1;
3140 								nAlphaCount = 0;
3141 								break;
3142 
3143 							case '-':
3144 								if (pLanguageBegin != 0)
3145 								{
3146 									if (nAlphaCount == 0)
3147 										pLanguageBegin = 0;
3148 									else
3149 										nAlphaCount = 0;
3150 								}
3151 								break;
3152 
3153 							case '?':
3154 								if (pCharsetBegin == q - 1)
3155 									bEncodedWord = false;
3156 								else
3157 								{
3158 									eCharsetEncoding
3159 										= getCharsetEncoding(
3160 											  pCharsetBegin,
3161 											  pLanguageBegin == 0
3162 											  || nAlphaCount == 0 ?
3163 											      q - 1 : pLanguageBegin);
3164 									bEncodedWord = isMIMECharsetEncoding(
3165 										               eCharsetEncoding);
3166 									eCharsetEncoding
3167 										= translateFromMIME(eCharsetEncoding);
3168 								}
3169 								bDone = true;
3170 								break;
3171 
3172 							default:
3173 								if (pLanguageBegin != 0
3174 									&& (!isAlpha(cChar) || ++nAlphaCount > 8))
3175 									pLanguageBegin = 0;
3176 								break;
3177 						}
3178 					}
3179 			}
3180 
3181 			bool bEncodingB = false;
3182 			if (bEncodedWord)
3183             {
3184 				if (q == pEnd)
3185 					bEncodedWord = false;
3186 				else
3187                 {
3188 					switch (*q++)
3189 					{
3190 						case 'B':
3191 						case 'b':
3192 							bEncodingB = true;
3193 							break;
3194 
3195 						case 'Q':
3196 						case 'q':
3197 							bEncodingB = false;
3198 							break;
3199 
3200 						default:
3201 							bEncodedWord = false;
3202 							break;
3203 					}
3204                 }
3205             }
3206 
3207 			bEncodedWord = bEncodedWord && q != pEnd && *q++ == '?';
3208 
3209 			ByteString sText;
3210 			if (bEncodedWord)
3211             {
3212 				if (bEncodingB)
3213                 {
3214 					for (bool bDone = false; !bDone;)
3215 					{
3216 						if (pEnd - q < 4)
3217 						{
3218 							bEncodedWord = false;
3219 							bDone = true;
3220 						}
3221 						else
3222 						{
3223 							bool bFinal = false;
3224 							int nCount = 3;
3225 							sal_uInt32 nValue = 0;
3226 							for (int nShift = 18; nShift >= 0; nShift -= 6)
3227 							{
3228 								int nWeight = getBase64Weight(*q++);
3229 								if (nWeight == -2)
3230 								{
3231 									bEncodedWord = false;
3232 									bDone = true;
3233 									break;
3234 								}
3235 								if (nWeight == -1)
3236 								{
3237 									if (!bFinal)
3238 									{
3239 										if (nShift >= 12)
3240 										{
3241 											bEncodedWord = false;
3242 											bDone = true;
3243 											break;
3244 										}
3245 										bFinal = true;
3246 										nCount = nShift == 6 ? 1 : 2;
3247 									}
3248 								}
3249 								else
3250 									nValue |= nWeight << nShift;
3251 							}
3252 							if (bEncodedWord)
3253 							{
3254 								for (int nShift = 16; nCount-- > 0;
3255 									 nShift -= 8)
3256 									sText += sal_Char(nValue >> nShift
3257 													      & 0xFF);
3258 								if (*q == '?')
3259 								{
3260 									++q;
3261 									bDone = true;
3262 								}
3263 								if (bFinal && !bDone)
3264 								{
3265 									bEncodedWord = false;
3266 									bDone = true;
3267 								}
3268 							}
3269 						}
3270 					}
3271                 }
3272 				else
3273 				{
3274 					const sal_Char * pEncodedTextBegin = q;
3275 					const sal_Char * pEncodedTextCopyBegin = q;
3276 					for (bool bDone = false; !bDone;)
3277 						if (q == pEnd)
3278 						{
3279 							bEncodedWord = false;
3280 							bDone = true;
3281 						}
3282 						else
3283 						{
3284 							sal_uInt32 nChar = *q++;
3285 							switch (nChar)
3286 							{
3287 								case '=':
3288 								{
3289 									if (pEnd - q < 2)
3290 									{
3291 										bEncodedWord = false;
3292 										bDone = true;
3293 										break;
3294 									}
3295 									int nDigit1 = getHexWeight(q[0]);
3296 									int nDigit2 = getHexWeight(q[1]);
3297 									if (nDigit1 < 0 || nDigit2 < 0)
3298 									{
3299 										bEncodedWord = false;
3300 										bDone = true;
3301 										break;
3302 									}
3303 									sText += rBody.Copy(
3304                                         static_cast< xub_StrLen >(
3305                                             pEncodedTextCopyBegin - pBegin),
3306                                         static_cast< xub_StrLen >(
3307                                             q - 1 - pEncodedTextCopyBegin));
3308 									sText += sal_Char(nDigit1 << 4 | nDigit2);
3309 									q += 2;
3310 									pEncodedTextCopyBegin = q;
3311 									break;
3312 								}
3313 
3314 								case '?':
3315 									if (q - pEncodedTextBegin > 1)
3316 										sText += rBody.Copy(
3317                                             static_cast< xub_StrLen >(
3318                                                 pEncodedTextCopyBegin - pBegin),
3319                                             static_cast< xub_StrLen >(
3320                                                 q - 1 - pEncodedTextCopyBegin));
3321 									else
3322 										bEncodedWord = false;
3323 									bDone = true;
3324 									break;
3325 
3326 								case '_':
3327 									sText += rBody.Copy(
3328                                         static_cast< xub_StrLen >(
3329                                             pEncodedTextCopyBegin - pBegin),
3330                                         static_cast< xub_StrLen >(
3331                                             q - 1 - pEncodedTextCopyBegin));
3332 									sText += ' ';
3333 									pEncodedTextCopyBegin = q;
3334 									break;
3335 
3336 								default:
3337 									if (!isVisible(nChar))
3338 									{
3339 										bEncodedWord = false;
3340 										bDone = true;
3341 									}
3342 									break;
3343 							}
3344 						}
3345 				}
3346             }
3347 
3348 			bEncodedWord = bEncodedWord && q != pEnd && *q++ == '=';
3349 
3350 //			if (bEncodedWord && q != pEnd)
3351 //				switch (*q)
3352 //				{
3353 //					case '\t':
3354 //					case ' ':
3355 //					case '"':
3356 //					case ')':
3357 //					case ',':
3358 //					case '.':
3359 //					case '=':
3360 //						break;
3361 //
3362 //					default:
3363 //						bEncodedWord = false;
3364 //						break;
3365 //				}
3366 
3367 			sal_Unicode * pUnicodeBuffer = 0;
3368 			sal_Size nUnicodeSize = 0;
3369 			if (bEncodedWord)
3370 			{
3371 				pUnicodeBuffer
3372 					= convertToUnicode(sText.GetBuffer(),
3373 									   sText.GetBuffer() + sText.Len(),
3374 									   eCharsetEncoding, nUnicodeSize);
3375 				if (pUnicodeBuffer == 0)
3376 					bEncodedWord = false;
3377 			}
3378 
3379 			if (bEncodedWord)
3380 			{
3381 				appendISO88591(sDecoded, pCopyBegin, pWSPBegin);
3382 				if (eType == HEADER_FIELD_TEXT)
3383 					sDecoded.Append(
3384                         pUnicodeBuffer,
3385                         static_cast< xub_StrLen >(nUnicodeSize));
3386 				else if (nCommentLevel == 0)
3387 				{
3388 					sEncodedText.Append(
3389                         pUnicodeBuffer,
3390                         static_cast< xub_StrLen >(nUnicodeSize));
3391 					if (!bQuotedEncodedText)
3392 					{
3393 						const sal_Unicode * pTextPtr = pUnicodeBuffer;
3394 						const sal_Unicode * pTextEnd = pTextPtr
3395 							                               + nUnicodeSize;
3396 						for (; pTextPtr != pTextEnd; ++pTextPtr)
3397 							if (!isEncodedWordTokenChar(*pTextPtr))
3398 							{
3399 								bQuotedEncodedText = true;
3400 								break;
3401 							}
3402 					}
3403 				}
3404 				else
3405 				{
3406 					const sal_Unicode * pTextPtr = pUnicodeBuffer;
3407 					const sal_Unicode * pTextEnd = pTextPtr + nUnicodeSize;
3408 					for (; pTextPtr != pTextEnd; ++pTextPtr)
3409 					{
3410 						switch (*pTextPtr)
3411 						{
3412 							case '(':
3413 							case ')':
3414 							case '\\':
3415 							case '\x0D':
3416 							case '=':
3417 								sDecoded += '\\';
3418 								break;
3419 						}
3420 						sDecoded += *pTextPtr;
3421 					}
3422 				}
3423 				delete[] pUnicodeBuffer;
3424 				p = q;
3425 				pCopyBegin = p;
3426 
3427 				pWSPBegin = p;
3428 				while (p != pEnd && isWhiteSpace(*p))
3429 					++p;
3430 				/* bStartEncodedWord = p != pWSPBegin; */
3431 				continue;
3432 			}
3433 		}
3434 
3435 		if (sEncodedText.Len() != 0)
3436 		{
3437 			if (bQuotedEncodedText)
3438 			{
3439 				sDecoded += '"';
3440 				const sal_Unicode * pTextPtr = sEncodedText.GetBuffer();
3441 				const sal_Unicode * pTextEnd = pTextPtr + sEncodedText.Len();
3442 				for (;pTextPtr != pTextEnd; ++pTextPtr)
3443 				{
3444 					switch (*pTextPtr)
3445 					{
3446 						case '"':
3447 						case '\\':
3448 						case '\x0D':
3449 							sDecoded += '\\';
3450 							break;
3451 					}
3452 					sDecoded += *pTextPtr;
3453 				}
3454 				sDecoded += '"';
3455 			}
3456 			else
3457 				sDecoded += sEncodedText;
3458 			sEncodedText.Erase();
3459 			bQuotedEncodedText = false;
3460 		}
3461 
3462 		if (p == pEnd)
3463 			break;
3464 
3465 		switch (*p++)
3466 		{
3467 //			case '\t':
3468 //			case ' ':
3469 //			case ',':
3470 //			case '.':
3471 //			case '=':
3472 //				bStartEncodedWord = true;
3473 //				break;
3474 
3475 			case '"':
3476 				if (eType != HEADER_FIELD_TEXT && nCommentLevel == 0)
3477 				{
3478 					const sal_Char * pQuotedStringEnd
3479 						= skipQuotedString(p - 1, pEnd);
3480 					p = pQuotedStringEnd == p - 1 ? pEnd : pQuotedStringEnd;
3481 				}
3482 				/* bStartEncodedWord = true; */
3483 				break;
3484 
3485 			case '(':
3486 				if (eType != HEADER_FIELD_TEXT)
3487 					++nCommentLevel;
3488 				/* bStartEncodedWord = true; */
3489 				break;
3490 
3491 			case ')':
3492 				if (nCommentLevel > 0)
3493 					--nCommentLevel;
3494 				/* bStartEncodedWord = false; */
3495 				break;
3496 
3497 			default:
3498 			{
3499 				const sal_Char * pUTF8Begin = p - 1;
3500 				const sal_Char * pUTF8End = pUTF8Begin;
3501 				sal_uInt32 nCharacter;
3502 				if (translateUTF8Char(pUTF8End, pEnd, RTL_TEXTENCODING_UCS4,
3503 									  nCharacter))
3504 				{
3505 					appendISO88591(sDecoded, pCopyBegin, p - 1);
3506 					sal_Unicode aUTF16Buf[2];
3507 					xub_StrLen nUTF16Len = static_cast< xub_StrLen >(
3508                         putUTF32Character(aUTF16Buf, nCharacter) - aUTF16Buf);
3509 					sDecoded.Append(aUTF16Buf, nUTF16Len);
3510 					p = pUTF8End;
3511 					pCopyBegin = p;
3512 				}
3513 				/* bStartEncodedWord = false; */
3514 				break;
3515 			}
3516 		}
3517 		pWSPBegin = p;
3518 	}
3519 
3520 	appendISO88591(sDecoded, pCopyBegin, pEnd);
3521 	return sDecoded;
3522 }
3523 
3524 //============================================================================
3525 //
3526 //  INetMIMEOutputSink
3527 //
3528 //============================================================================
3529 
3530 // virtual
3531 sal_Size INetMIMEOutputSink::writeSequence(const sal_Char * pSequence)
3532 {
3533 	sal_Size nLength = rtl_str_getLength(pSequence);
3534 	writeSequence(pSequence, pSequence + nLength);
3535 	return nLength;
3536 }
3537 
3538 //============================================================================
3539 // virtual
3540 void INetMIMEOutputSink::writeSequence(const sal_uInt32 * pBegin,
3541 									   const sal_uInt32 * pEnd)
3542 {
3543 	DBG_ASSERT(pBegin && pBegin <= pEnd,
3544 			   "INetMIMEOutputSink::writeSequence(): Bad sequence");
3545 
3546 	sal_Char * pBufferBegin = new sal_Char[pEnd - pBegin];
3547 	sal_Char * pBufferEnd = pBufferBegin;
3548 	while (pBegin != pEnd)
3549 	{
3550 		DBG_ASSERT(*pBegin < 256,
3551 				   "INetMIMEOutputSink::writeSequence(): Bad octet");
3552 		*pBufferEnd++ = sal_Char(*pBegin++);
3553 	}
3554 	writeSequence(pBufferBegin, pBufferEnd);
3555 	delete[] pBufferBegin;
3556 }
3557 
3558 //============================================================================
3559 // virtual
3560 void INetMIMEOutputSink::writeSequence(const sal_Unicode * pBegin,
3561 									   const sal_Unicode * pEnd)
3562 {
3563 	DBG_ASSERT(pBegin && pBegin <= pEnd,
3564 			   "INetMIMEOutputSink::writeSequence(): Bad sequence");
3565 
3566 	sal_Char * pBufferBegin = new sal_Char[pEnd - pBegin];
3567 	sal_Char * pBufferEnd = pBufferBegin;
3568 	while (pBegin != pEnd)
3569 	{
3570 		DBG_ASSERT(*pBegin < 256,
3571 				   "INetMIMEOutputSink::writeSequence(): Bad octet");
3572 		*pBufferEnd++ = sal_Char(*pBegin++);
3573 	}
3574 	writeSequence(pBufferBegin, pBufferEnd);
3575 	delete[] pBufferBegin;
3576 }
3577 
3578 //============================================================================
3579 // virtual
3580 ErrCode INetMIMEOutputSink::getError() const
3581 {
3582 	return ERRCODE_NONE;
3583 }
3584 
3585 //============================================================================
3586 void INetMIMEOutputSink::writeLineEnd()
3587 {
3588 	static const sal_Char aCRLF[2] = { 0x0D, 0x0A };
3589 	writeSequence(aCRLF, aCRLF + 2);
3590 	m_nColumn = 0;
3591 }
3592 
3593 //============================================================================
3594 //
3595 //  INetMIMEStringOutputSink
3596 //
3597 //============================================================================
3598 
3599 // virtual
3600 void INetMIMEStringOutputSink::writeSequence(const sal_Char * pBegin,
3601 											 const sal_Char * pEnd)
3602 {
3603 	DBG_ASSERT(pBegin && pBegin <= pEnd,
3604 			   "INetMIMEStringOutputSink::writeSequence(): Bad sequence");
3605 
3606 	m_bOverflow = m_bOverflow
3607 		          || pEnd - pBegin > STRING_MAXLEN - m_aBuffer.Len();
3608 	if (!m_bOverflow)
3609 		m_aBuffer.Append(pBegin, static_cast< xub_StrLen >(pEnd - pBegin));
3610 }
3611 
3612 //============================================================================
3613 // virtual
3614 ErrCode INetMIMEStringOutputSink::getError() const
3615 {
3616 	return m_bOverflow ? ERRCODE_IO_OUTOFMEMORY : ERRCODE_NONE;
3617 }
3618 
3619 //============================================================================
3620 //
3621 //  INetMIMEUnicodeOutputSink
3622 //
3623 //============================================================================
3624 
3625 // virtual
3626 void INetMIMEUnicodeOutputSink::writeSequence(const sal_Char * pBegin,
3627 											  const sal_Char * pEnd)
3628 {
3629 	DBG_ASSERT(pBegin && pBegin <= pEnd,
3630 			   "INetMIMEUnicodeOutputSink::writeSequence(): Bad sequence");
3631 
3632 	sal_Unicode * pBufferBegin = new sal_Unicode[pEnd - pBegin];
3633 	sal_Unicode * pBufferEnd = pBufferBegin;
3634 	while (pBegin != pEnd)
3635 		*pBufferEnd++ = sal_uChar(*pBegin++);
3636 	writeSequence(pBufferBegin, pBufferEnd);
3637 	delete[] pBufferBegin;
3638 }
3639 
3640 //============================================================================
3641 // virtual
3642 void INetMIMEUnicodeOutputSink::writeSequence(const sal_uInt32 * pBegin,
3643 											  const sal_uInt32 * pEnd)
3644 {
3645 	DBG_ASSERT(pBegin && pBegin <= pEnd,
3646 			   "INetMIMEUnicodeOutputSink::writeSequence(): Bad sequence");
3647 
3648 	sal_Unicode * pBufferBegin = new sal_Unicode[pEnd - pBegin];
3649 	sal_Unicode * pBufferEnd = pBufferBegin;
3650 	while (pBegin != pEnd)
3651 	{
3652 		DBG_ASSERT(*pBegin < 256,
3653 				   "INetMIMEOutputSink::writeSequence(): Bad octet");
3654 		*pBufferEnd++ = sal_Unicode(*pBegin++);
3655 	}
3656 	writeSequence(pBufferBegin, pBufferEnd);
3657 	delete[] pBufferBegin;
3658 }
3659 
3660 //============================================================================
3661 // virtual
3662 void INetMIMEUnicodeOutputSink::writeSequence(const sal_Unicode * pBegin,
3663 											  const sal_Unicode * pEnd)
3664 {
3665 	DBG_ASSERT(pBegin && pBegin <= pEnd,
3666 			   "INetMIMEUnicodeOutputSink::writeSequence(): Bad sequence");
3667 
3668 	m_bOverflow = m_bOverflow
3669 		          || pEnd - pBegin > STRING_MAXLEN - m_aBuffer.Len();
3670 	if (!m_bOverflow)
3671 		m_aBuffer.Append(pBegin, static_cast< xub_StrLen >(pEnd - pBegin));
3672 }
3673 
3674 //============================================================================
3675 // virtual
3676 ErrCode INetMIMEUnicodeOutputSink::getError() const
3677 {
3678 	return m_bOverflow ? ERRCODE_IO_OUTOFMEMORY : ERRCODE_NONE;
3679 }
3680 
3681 //============================================================================
3682 //
3683 //  INetMIMEEncodedWordOutputSink
3684 //
3685 //============================================================================
3686 
3687 static const sal_Char aEscape[128]
3688 	= { INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x00
3689 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x01
3690 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x02
3691 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x03
3692 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x04
3693 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x05
3694 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x06
3695 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x07
3696 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x08
3697 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x09
3698 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x0A
3699 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x0B
3700 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x0C
3701 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x0D
3702 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x0E
3703 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x0F
3704 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x10
3705 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x11
3706 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x12
3707 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x13
3708 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x14
3709 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x15
3710 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x16
3711 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x17
3712 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x18
3713 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x19
3714 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x1A
3715 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x1B
3716 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x1C
3717 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x1D
3718 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x1E
3719 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // 0x1F
3720 		0,   // ' '
3721 		0,   // '!'
3722 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '"'
3723 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '#'
3724 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '$'
3725 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '%'
3726 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '&'
3727 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '''
3728 		INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '('
3729 		INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // ')'
3730 		0,   // '*'
3731 		0,   // '+'
3732 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // ','
3733 		0,   // '-'
3734 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '.'
3735 		0,   // '/'
3736 		0,   // '0'
3737 		0,   // '1'
3738 		0,   // '2'
3739 		0,   // '3'
3740 		0,   // '4'
3741 		0,   // '5'
3742 		0,   // '6'
3743 		0,   // '7'
3744 		0,   // '8'
3745 		0,   // '9'
3746 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // ':'
3747 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // ';'
3748 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '<'
3749 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '='
3750 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '>'
3751 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '?'
3752 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '@'
3753 		0,   // 'A'
3754 		0,   // 'B'
3755 		0,   // 'C'
3756 		0,   // 'D'
3757 		0,   // 'E'
3758 		0,   // 'F'
3759 		0,   // 'G'
3760 		0,   // 'H'
3761 		0,   // 'I'
3762 		0,   // 'J'
3763 		0,   // 'K'
3764 		0,   // 'L'
3765 		0,   // 'M'
3766 		0,   // 'N'
3767 		0,   // 'O'
3768 		0,   // 'P'
3769 		0,   // 'Q'
3770 		0,   // 'R'
3771 		0,   // 'S'
3772 		0,   // 'T'
3773 		0,   // 'U'
3774 		0,   // 'V'
3775 		0,   // 'W'
3776 		0,   // 'X'
3777 		0,   // 'Y'
3778 		0,   // 'Z'
3779 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '['
3780 		INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '\'
3781 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // ']'
3782 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '^'
3783 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '_'
3784 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '`'
3785 		0,   // 'a'
3786 		0,   // 'b'
3787 		0,   // 'c'
3788 		0,   // 'd'
3789 		0,   // 'e'
3790 		0,   // 'f'
3791 		0,   // 'g'
3792 		0,   // 'h'
3793 		0,   // 'i'
3794 		0,   // 'j'
3795 		0,   // 'k'
3796 		0,   // 'l'
3797 		0,   // 'm'
3798 		0,   // 'n'
3799 		0,   // 'o'
3800 		0,   // 'p'
3801 		0,   // 'q'
3802 		0,   // 'r'
3803 		0,   // 's'
3804 		0,   // 't'
3805 		0,   // 'u'
3806 		0,   // 'v'
3807 		0,   // 'w'
3808 		0,   // 'x'
3809 		0,   // 'y'
3810 		0,   // 'z'
3811 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '{'
3812 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '|'
3813 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '}'
3814 		INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE,   // '~'
3815 		INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE }; // DEL
3816 
3817 inline bool
3818 INetMIMEEncodedWordOutputSink::needsEncodedWordEscape(sal_uInt32 nChar) const
3819 {
3820 	return !INetMIME::isUSASCII(nChar) || aEscape[nChar] & m_eContext;
3821 }
3822 
3823 //============================================================================
3824 void INetMIMEEncodedWordOutputSink::finish(bool bWriteTrailer)
3825 {
3826 	if (m_eInitialSpace == SPACE_ALWAYS && m_nExtraSpaces == 0)
3827 		m_nExtraSpaces = 1;
3828 
3829 	if (m_eEncodedWordState == STATE_SECOND_EQUALS)
3830 	{
3831 		// If the text is already an encoded word, copy it verbatim:
3832 		sal_uInt32 nSize = m_pBufferEnd - m_pBuffer;
3833 		switch (m_ePrevCoding)
3834 		{
3835 			case CODING_QUOTED:
3836 				m_rSink << '"';
3837 			case CODING_NONE:
3838 				if (m_eInitialSpace == SPACE_ENCODED && m_nExtraSpaces == 0)
3839 					m_nExtraSpaces = 1;
3840 				for (; m_nExtraSpaces > 1; --m_nExtraSpaces)
3841 				{
3842 					if (m_rSink.getColumn() >= m_rSink.getLineLengthLimit())
3843 						m_rSink << INetMIMEOutputSink::endl;
3844 					m_rSink << ' ';
3845 				}
3846 				if (m_nExtraSpaces == 1)
3847 				{
3848 					if (m_rSink.getColumn() + nSize
3849 						    >= m_rSink.getLineLengthLimit())
3850 						m_rSink << INetMIMEOutputSink::endl;
3851 					m_rSink << ' ';
3852 				}
3853 				break;
3854 
3855 			case CODING_ENCODED:
3856 			{
3857 				const sal_Char * pCharsetName
3858 				 = INetMIME::getCharsetName(m_ePrevMIMEEncoding);
3859 				while (m_nExtraSpaces-- > 0)
3860 				{
3861 					if (m_rSink.getColumn()
3862 						    > m_rSink.getLineLengthLimit() - 3)
3863 						m_rSink << "?=" << INetMIMEOutputSink::endl << " =?"
3864 								<< pCharsetName << "?Q?";
3865 					m_rSink << '_';
3866 				}
3867 				m_rSink << "?=";
3868 			}
3869 			case CODING_ENCODED_TERMINATED:
3870 				if (m_rSink.getColumn() + nSize
3871 					    > m_rSink.getLineLengthLimit() - 1)
3872 					m_rSink << INetMIMEOutputSink::endl;
3873 				m_rSink << ' ';
3874 				break;
3875 		}
3876 		m_rSink.write(m_pBuffer, m_pBufferEnd);
3877 		m_eCoding = CODING_ENCODED_TERMINATED;
3878 	}
3879 	else
3880 	{
3881 		// If the text itself is too long to fit into a single line, make it
3882 		// into multiple encoded words:
3883 		switch (m_eCoding)
3884 		{
3885 			case CODING_NONE:
3886 				if (m_nExtraSpaces == 0)
3887 				{
3888 					DBG_ASSERT(m_ePrevCoding == CODING_NONE
3889 							   || m_pBuffer == m_pBufferEnd,
3890 							   "INetMIMEEncodedWordOutputSink::finish():"
3891 							       " Bad state");
3892 					if (m_rSink.getColumn() + (m_pBufferEnd - m_pBuffer)
3893 						    > m_rSink.getLineLengthLimit())
3894 						m_eCoding = CODING_ENCODED;
3895 				}
3896 				else
3897                 {
3898                     OSL_ASSERT(m_pBufferEnd >= m_pBuffer);
3899                     if (static_cast< std::size_t >(m_pBufferEnd - m_pBuffer)
3900                         > m_rSink.getLineLengthLimit() - 1)
3901                     {
3902                         m_eCoding = CODING_ENCODED;
3903                     }
3904                 }
3905 				break;
3906 
3907 			case CODING_QUOTED:
3908 				if (m_nExtraSpaces == 0)
3909 				{
3910 					DBG_ASSERT(m_ePrevCoding == CODING_NONE,
3911 							   "INetMIMEEncodedWordOutputSink::finish():"
3912 							       " Bad state");
3913 					if (m_rSink.getColumn() + (m_pBufferEnd - m_pBuffer)
3914 						        + m_nQuotedEscaped
3915 						    > m_rSink.getLineLengthLimit() - 2)
3916 						m_eCoding = CODING_ENCODED;
3917 				}
3918 				else if ((m_pBufferEnd - m_pBuffer) + m_nQuotedEscaped
3919 						     > m_rSink.getLineLengthLimit() - 3)
3920 					m_eCoding = CODING_ENCODED;
3921 				break;
3922 
3923             default:
3924                 break;
3925 		}
3926 
3927 		switch (m_eCoding)
3928 		{
3929 			case CODING_NONE:
3930 				switch (m_ePrevCoding)
3931 				{
3932 					case CODING_QUOTED:
3933 						if (m_rSink.getColumn() + m_nExtraSpaces
3934 							        + (m_pBufferEnd - m_pBuffer)
3935 							    < m_rSink.getLineLengthLimit())
3936 							m_eCoding = CODING_QUOTED;
3937 						else
3938 							m_rSink << '"';
3939 						break;
3940 
3941 					case CODING_ENCODED:
3942 						m_rSink << "?=";
3943 						break;
3944 
3945                     default:
3946                         break;
3947 				}
3948 				for (; m_nExtraSpaces > 1; --m_nExtraSpaces)
3949 				{
3950 					if (m_rSink.getColumn() >= m_rSink.getLineLengthLimit())
3951 						m_rSink << INetMIMEOutputSink::endl;
3952 					m_rSink << ' ';
3953 				}
3954 				if (m_nExtraSpaces == 1)
3955 				{
3956 					if (m_rSink.getColumn() + (m_pBufferEnd - m_pBuffer)
3957 						    >= m_rSink.getLineLengthLimit())
3958 						m_rSink << INetMIMEOutputSink::endl;
3959 					m_rSink << ' ';
3960 				}
3961 				m_rSink.write(m_pBuffer, m_pBufferEnd);
3962 				if (m_eCoding == CODING_QUOTED && bWriteTrailer)
3963 				{
3964 					m_rSink << '"';
3965 					m_eCoding = CODING_NONE;
3966 				}
3967 				break;
3968 
3969 			case CODING_QUOTED:
3970 			{
3971 				bool bInsertLeadingQuote = true;
3972 				sal_uInt32 nSize = (m_pBufferEnd - m_pBuffer)
3973 					                   + m_nQuotedEscaped + 2;
3974 				switch (m_ePrevCoding)
3975 				{
3976 					case CODING_QUOTED:
3977 						if (m_rSink.getColumn() + m_nExtraSpaces + nSize - 1
3978 							    < m_rSink.getLineLengthLimit())
3979 						{
3980 							bInsertLeadingQuote = false;
3981 							--nSize;
3982 						}
3983 						else
3984 							m_rSink << '"';
3985 						break;
3986 
3987 					case CODING_ENCODED:
3988 						m_rSink << "?=";
3989 						break;
3990 
3991                     default:
3992                         break;
3993 				}
3994 				for (; m_nExtraSpaces > 1; --m_nExtraSpaces)
3995 				{
3996 					if (m_rSink.getColumn() >= m_rSink.getLineLengthLimit())
3997 						m_rSink << INetMIMEOutputSink::endl;
3998 					m_rSink << ' ';
3999 				}
4000 				if (m_nExtraSpaces == 1)
4001 				{
4002 					if (m_rSink.getColumn() + nSize
4003 						    >= m_rSink.getLineLengthLimit())
4004 						m_rSink << INetMIMEOutputSink::endl;
4005 					m_rSink << ' ';
4006 				}
4007 				if (bInsertLeadingQuote)
4008 					m_rSink << '"';
4009 				for (const sal_Unicode * p = m_pBuffer; p != m_pBufferEnd;
4010 					 ++p)
4011 				{
4012 					if (INetMIME::needsQuotedStringEscape(*p))
4013 						m_rSink << '\\';
4014 					m_rSink << sal_Char(*p);
4015 				}
4016 				if (bWriteTrailer)
4017 				{
4018 					m_rSink << '"';
4019 					m_eCoding = CODING_NONE;
4020 				}
4021 				break;
4022 			}
4023 
4024 			case CODING_ENCODED:
4025 			{
4026 				rtl_TextEncoding eCharsetEncoding
4027 					= m_pEncodingList->
4028 					      getPreferredEncoding(RTL_TEXTENCODING_UTF8);
4029 				rtl_TextEncoding eMIMEEncoding
4030 					= INetMIME::translateToMIME(eCharsetEncoding);
4031 
4032 				// The non UTF-8 code will only work for stateless single byte
4033 				// character encodings (see also below):
4034 				sal_Char * pTargetBuffer = NULL;
4035 				sal_Size nTargetSize = 0;
4036 				sal_uInt32 nSize;
4037 				if (eMIMEEncoding == RTL_TEXTENCODING_UTF8)
4038 				{
4039 					nSize = 0;
4040 					for (sal_Unicode const * p = m_pBuffer;
4041 						 p != m_pBufferEnd;)
4042 					{
4043 						sal_uInt32 nUTF32
4044 							= INetMIME::getUTF32Character(p, m_pBufferEnd);
4045 						nSize += needsEncodedWordEscape(nUTF32) ?
4046 							         3 * INetMIME::getUTF8OctetCount(nUTF32) :
4047 							         1;
4048 							// only US-ASCII characters (that are converted to
4049 							// a single byte by UTF-8) need no encoded word
4050 							// escapes...
4051 					}
4052 				}
4053 				else
4054 				{
4055 					rtl_UnicodeToTextConverter hConverter
4056 						= rtl_createUnicodeToTextConverter(eCharsetEncoding);
4057 					rtl_UnicodeToTextContext hContext
4058 						= rtl_createUnicodeToTextContext(hConverter);
4059 					for (sal_Size nBufferSize = m_pBufferEnd - m_pBuffer;;
4060 						 nBufferSize += nBufferSize / 3 + 1)
4061 					{
4062 						pTargetBuffer = new sal_Char[nBufferSize];
4063 						sal_uInt32 nInfo;
4064 						sal_Size nSrcCvtBytes;
4065 						nTargetSize
4066 							= rtl_convertUnicodeToText(
4067 								  hConverter, hContext, m_pBuffer,
4068 								  m_pBufferEnd - m_pBuffer, pTargetBuffer,
4069 								  nBufferSize,
4070 								  RTL_UNICODETOTEXT_FLAGS_UNDEFINED_IGNORE
4071 							         | RTL_UNICODETOTEXT_FLAGS_INVALID_IGNORE,
4072 								  &nInfo, &nSrcCvtBytes);
4073 						if (!(nInfo
4074 							      & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL))
4075 							break;
4076 						delete[] pTargetBuffer;
4077 						pTargetBuffer = NULL;
4078 						rtl_resetUnicodeToTextContext(hConverter, hContext);
4079 					}
4080 					rtl_destroyUnicodeToTextContext(hConverter, hContext);
4081 					rtl_destroyUnicodeToTextConverter(hConverter);
4082 
4083 					nSize = nTargetSize;
4084 					for (sal_Size k = 0; k < nTargetSize; ++k)
4085 						if (needsEncodedWordEscape(sal_uChar(
4086 							                           pTargetBuffer[k])))
4087 							nSize += 2;
4088 				}
4089 
4090 				const sal_Char * pCharsetName
4091 					= INetMIME::getCharsetName(eMIMEEncoding);
4092 				sal_uInt32 nWrapperSize = rtl_str_getLength(pCharsetName) + 7;
4093 					// '=?', '?Q?', '?='
4094 
4095 				switch (m_ePrevCoding)
4096 				{
4097 					case CODING_QUOTED:
4098 						m_rSink << '"';
4099 					case CODING_NONE:
4100 						if (m_eInitialSpace == SPACE_ENCODED
4101 							&& m_nExtraSpaces == 0)
4102 							m_nExtraSpaces = 1;
4103 						nSize += nWrapperSize;
4104 						for (; m_nExtraSpaces > 1; --m_nExtraSpaces)
4105 						{
4106 							if (m_rSink.getColumn()
4107 								    >= m_rSink.getLineLengthLimit())
4108 								m_rSink << INetMIMEOutputSink::endl;
4109 							m_rSink << ' ';
4110 						}
4111 						if (m_nExtraSpaces == 1)
4112 						{
4113 							if (m_rSink.getColumn() + nSize
4114 								    >= m_rSink.getLineLengthLimit())
4115 							m_rSink << INetMIMEOutputSink::endl;
4116 							m_rSink << ' ';
4117 						}
4118 						m_rSink << "=?" << pCharsetName << "?Q?";
4119 						break;
4120 
4121 					case CODING_ENCODED:
4122 						if (m_ePrevMIMEEncoding != eMIMEEncoding
4123 							|| m_rSink.getColumn() + m_nExtraSpaces + nSize
4124 							       > m_rSink.getLineLengthLimit() - 2)
4125 						{
4126 							m_rSink << "?=";
4127 							if (m_rSink.getColumn() + nWrapperSize
4128 								        + m_nExtraSpaces + nSize
4129 								    > m_rSink.getLineLengthLimit() - 1)
4130 								m_rSink << INetMIMEOutputSink::endl;
4131 							m_rSink << " =?" << pCharsetName << "?Q?";
4132 						}
4133 						while (m_nExtraSpaces-- > 0)
4134 						{
4135 							if (m_rSink.getColumn()
4136 								    > m_rSink.getLineLengthLimit() - 3)
4137 								m_rSink << "?=" << INetMIMEOutputSink::endl
4138 										<< " =?" << pCharsetName << "?Q?";
4139 							m_rSink << '_';
4140 						}
4141 						break;
4142 
4143 					case CODING_ENCODED_TERMINATED:
4144 						if (m_rSink.getColumn() + nWrapperSize
4145 							        + m_nExtraSpaces + nSize
4146 							    > m_rSink.getLineLengthLimit() - 1)
4147 							m_rSink << INetMIMEOutputSink::endl;
4148 						m_rSink << " =?" << pCharsetName << "?Q?";
4149 						while (m_nExtraSpaces-- > 0)
4150 						{
4151 							if (m_rSink.getColumn()
4152 								    > m_rSink.getLineLengthLimit() - 3)
4153 								m_rSink << "?=" << INetMIMEOutputSink::endl
4154 										<< " =?" << pCharsetName << "?Q?";
4155 							m_rSink << '_';
4156 						}
4157 						break;
4158 				}
4159 
4160 				// The non UTF-8 code will only work for stateless single byte
4161 				// character encodings (see also above):
4162 				if (eMIMEEncoding == RTL_TEXTENCODING_UTF8)
4163 				{
4164 					bool bInitial = true;
4165 					for (sal_Unicode const * p = m_pBuffer;
4166 						 p != m_pBufferEnd;)
4167 					{
4168 						sal_uInt32 nUTF32
4169 							= INetMIME::getUTF32Character(p, m_pBufferEnd);
4170 						bool bEscape = needsEncodedWordEscape(nUTF32);
4171 						sal_uInt32 nWidth
4172 							= bEscape ?
4173 							      3 * INetMIME::getUTF8OctetCount(nUTF32) : 1;
4174 							// only US-ASCII characters (that are converted to
4175 							// a single byte by UTF-8) need no encoded word
4176 							// escapes...
4177 						if (!bInitial
4178 							&& m_rSink.getColumn() + nWidth + 2
4179 							       > m_rSink.getLineLengthLimit())
4180 							m_rSink << "?=" << INetMIMEOutputSink::endl
4181 									<< " =?" << pCharsetName << "?Q?";
4182 						if (bEscape)
4183 						{
4184 							DBG_ASSERT(
4185 								nUTF32 < 0x10FFFF,
4186 								"INetMIMEEncodedWordOutputSink::finish():"
4187 								    " Bad char");
4188 							if (nUTF32 < 0x80)
4189 								INetMIME::writeEscapeSequence(m_rSink,
4190 															  nUTF32);
4191 							else if (nUTF32 < 0x800)
4192 							{
4193 								INetMIME::writeEscapeSequence(m_rSink,
4194 															  (nUTF32 >> 6)
4195 															      | 0xC0);
4196 								INetMIME::writeEscapeSequence(m_rSink,
4197 															  (nUTF32 & 0x3F)
4198 															      | 0x80);
4199 							}
4200 							else if (nUTF32 < 0x10000)
4201 							{
4202 								INetMIME::writeEscapeSequence(m_rSink,
4203 															  (nUTF32 >> 12)
4204 															      | 0xE0);
4205 								INetMIME::writeEscapeSequence(m_rSink,
4206 															  ((nUTF32 >> 6)
4207 															          & 0x3F)
4208 															      | 0x80);
4209 								INetMIME::writeEscapeSequence(m_rSink,
4210 															  (nUTF32 & 0x3F)
4211 															      | 0x80);
4212 							}
4213 							else
4214 							{
4215 								INetMIME::writeEscapeSequence(m_rSink,
4216 															  (nUTF32 >> 18)
4217 															      | 0xF0);
4218 								INetMIME::writeEscapeSequence(m_rSink,
4219 															  ((nUTF32 >> 12)
4220 															          & 0x3F)
4221 															      | 0x80);
4222 								INetMIME::writeEscapeSequence(m_rSink,
4223 															  ((nUTF32 >> 6)
4224 															          & 0x3F)
4225 															      | 0x80);
4226 								INetMIME::writeEscapeSequence(m_rSink,
4227 															  (nUTF32 & 0x3F)
4228 															      | 0x80);
4229 							}
4230 						}
4231 						else
4232 							m_rSink << sal_Char(nUTF32);
4233 						bInitial = false;
4234 					}
4235 				}
4236 				else
4237 				{
4238 					for (sal_Size k = 0; k < nTargetSize; ++k)
4239 					{
4240 						sal_uInt32 nUCS4 = sal_uChar(pTargetBuffer[k]);
4241 						bool bEscape = needsEncodedWordEscape(nUCS4);
4242 						if (k > 0
4243 							&& m_rSink.getColumn() + (bEscape ? 5 : 3)
4244 							       > m_rSink.getLineLengthLimit())
4245 							m_rSink << "?=" << INetMIMEOutputSink::endl
4246 									<< " =?" << pCharsetName << "?Q?";
4247 						if (bEscape)
4248 							INetMIME::writeEscapeSequence(m_rSink, nUCS4);
4249 						else
4250 							m_rSink << sal_Char(nUCS4);
4251 					}
4252 					delete[] pTargetBuffer;
4253 				}
4254 
4255 				if (bWriteTrailer)
4256 				{
4257 					m_rSink << "?=";
4258 					m_eCoding = CODING_ENCODED_TERMINATED;
4259 				}
4260 
4261 				m_ePrevMIMEEncoding = eMIMEEncoding;
4262 				break;
4263 			}
4264 
4265             default:
4266                 OSL_ASSERT(false);
4267                 break;
4268 		}
4269 	}
4270 
4271 	m_eInitialSpace = SPACE_NO;
4272 	m_nExtraSpaces = 0;
4273 	m_pEncodingList->reset();
4274 	m_pBufferEnd = m_pBuffer;
4275 	m_ePrevCoding = m_eCoding;
4276 	m_eCoding = CODING_NONE;
4277 	m_nQuotedEscaped = 0;
4278 	m_eEncodedWordState = STATE_INITIAL;
4279 }
4280 
4281 //============================================================================
4282 INetMIMEEncodedWordOutputSink::~INetMIMEEncodedWordOutputSink()
4283 {
4284 	rtl_freeMemory(m_pBuffer);
4285 	delete m_pEncodingList;
4286 }
4287 
4288 //============================================================================
4289 INetMIMEEncodedWordOutputSink &
4290 INetMIMEEncodedWordOutputSink::operator <<(sal_uInt32 nChar)
4291 {
4292 	if (nChar == ' ')
4293 	{
4294 		if (m_pBufferEnd != m_pBuffer)
4295 			finish(false);
4296 		++m_nExtraSpaces;
4297 	}
4298 	else
4299 	{
4300 		// Check for an already encoded word:
4301 		switch (m_eEncodedWordState)
4302 		{
4303 			case STATE_INITIAL:
4304 				if (nChar == '=')
4305 					m_eEncodedWordState = STATE_FIRST_EQUALS;
4306 				else
4307 					m_eEncodedWordState = STATE_BAD;
4308 				break;
4309 
4310 			case STATE_FIRST_EQUALS:
4311 				if (nChar == '?')
4312 					m_eEncodedWordState = STATE_FIRST_EQUALS;
4313 				else
4314 					m_eEncodedWordState = STATE_BAD;
4315 				break;
4316 
4317 			case STATE_FIRST_QUESTION:
4318 				if (INetMIME::isEncodedWordTokenChar(nChar))
4319 					m_eEncodedWordState = STATE_CHARSET;
4320 				else
4321 					m_eEncodedWordState = STATE_BAD;
4322 				break;
4323 
4324 			case STATE_CHARSET:
4325 				if (nChar == '?')
4326 					m_eEncodedWordState = STATE_SECOND_QUESTION;
4327 				else if (!INetMIME::isEncodedWordTokenChar(nChar))
4328 					m_eEncodedWordState = STATE_BAD;
4329 				break;
4330 
4331 			case STATE_SECOND_QUESTION:
4332 				if (nChar == 'B' || nChar == 'Q'
4333 					|| nChar == 'b' || nChar == 'q')
4334 					m_eEncodedWordState = STATE_ENCODING;
4335 				else
4336 					m_eEncodedWordState = STATE_BAD;
4337 				break;
4338 
4339 			case STATE_ENCODING:
4340 				if (nChar == '?')
4341 					m_eEncodedWordState = STATE_THIRD_QUESTION;
4342 				else
4343 					m_eEncodedWordState = STATE_BAD;
4344 				break;
4345 
4346 			case STATE_THIRD_QUESTION:
4347 				if (INetMIME::isVisible(nChar) && nChar != '?')
4348 					m_eEncodedWordState = STATE_ENCODED_TEXT;
4349 				else
4350 					m_eEncodedWordState = STATE_BAD;
4351 				break;
4352 
4353 			case STATE_ENCODED_TEXT:
4354 				if (nChar == '?')
4355 					m_eEncodedWordState = STATE_FOURTH_QUESTION;
4356 				else if (!INetMIME::isVisible(nChar))
4357 					m_eEncodedWordState = STATE_BAD;
4358 				break;
4359 
4360 			case STATE_FOURTH_QUESTION:
4361 				if (nChar == '=')
4362 					m_eEncodedWordState = STATE_SECOND_EQUALS;
4363 				else
4364 					m_eEncodedWordState = STATE_BAD;
4365 				break;
4366 
4367 			case STATE_SECOND_EQUALS:
4368 				m_eEncodedWordState = STATE_BAD;
4369 				break;
4370 
4371             case STATE_BAD:
4372                 break;
4373 		}
4374 
4375 		// Update encoding:
4376 		m_pEncodingList->includes(nChar);
4377 
4378 		// Update coding:
4379 		enum { TENQ = 1,   // CONTEXT_TEXT, CODING_ENCODED
4380 			   CENQ = 2,   // CONTEXT_COMMENT, CODING_ENCODED
4381 			   PQTD = 4,   // CONTEXT_PHRASE, CODING_QUOTED
4382 			   PENQ = 8 }; // CONTEXT_PHRASE, CODING_ENCODED
4383 		static const sal_Char aMinimal[128]
4384 			= { TENQ | CENQ        | PENQ,   // 0x00
4385 				TENQ | CENQ        | PENQ,   // 0x01
4386 				TENQ | CENQ        | PENQ,   // 0x02
4387 				TENQ | CENQ        | PENQ,   // 0x03
4388 				TENQ | CENQ        | PENQ,   // 0x04
4389 				TENQ | CENQ        | PENQ,   // 0x05
4390 				TENQ | CENQ        | PENQ,   // 0x06
4391 				TENQ | CENQ        | PENQ,   // 0x07
4392 				TENQ | CENQ        | PENQ,   // 0x08
4393 				TENQ | CENQ        | PENQ,   // 0x09
4394 				TENQ | CENQ        | PENQ,   // 0x0A
4395 				TENQ | CENQ        | PENQ,   // 0x0B
4396 				TENQ | CENQ        | PENQ,   // 0x0C
4397 				TENQ | CENQ        | PENQ,   // 0x0D
4398 				TENQ | CENQ        | PENQ,   // 0x0E
4399 				TENQ | CENQ        | PENQ,   // 0x0F
4400 				TENQ | CENQ        | PENQ,   // 0x10
4401 				TENQ | CENQ        | PENQ,   // 0x11
4402 				TENQ | CENQ        | PENQ,   // 0x12
4403 				TENQ | CENQ        | PENQ,   // 0x13
4404 				TENQ | CENQ        | PENQ,   // 0x14
4405 				TENQ | CENQ        | PENQ,   // 0x15
4406 				TENQ | CENQ        | PENQ,   // 0x16
4407 				TENQ | CENQ        | PENQ,   // 0x17
4408 				TENQ | CENQ        | PENQ,   // 0x18
4409 				TENQ | CENQ        | PENQ,   // 0x19
4410 				TENQ | CENQ        | PENQ,   // 0x1A
4411 				TENQ | CENQ        | PENQ,   // 0x1B
4412 				TENQ | CENQ        | PENQ,   // 0x1C
4413 				TENQ | CENQ        | PENQ,   // 0x1D
4414 				TENQ | CENQ        | PENQ,   // 0x1E
4415 				TENQ | CENQ        | PENQ,   // 0x1F
4416 										0,   // ' '
4417 										0,   // '!'
4418 							  PQTD       ,   // '"'
4419 										0,   // '#'
4420 										0,   // '$'
4421 										0,   // '%'
4422 										0,   // '&'
4423 										0,   // '''
4424 					   CENQ | PQTD       ,   // '('
4425 					   CENQ | PQTD       ,   // ')'
4426 										0,   // '*'
4427 										0,   // '+'
4428 							  PQTD       ,   // ','
4429 										0,   // '-'
4430 							  PQTD       ,   // '.'
4431 										0,   // '/'
4432 										0,   // '0'
4433 										0,   // '1'
4434 										0,   // '2'
4435 										0,   // '3'
4436 										0,   // '4'
4437 										0,   // '5'
4438 										0,   // '6'
4439 										0,   // '7'
4440 										0,   // '8'
4441 										0,   // '9'
4442 							  PQTD       ,   // ':'
4443 							  PQTD       ,   // ';'
4444 							  PQTD       ,   // '<'
4445 										0,   // '='
4446 							  PQTD       ,   // '>'
4447 										0,   // '?'
4448 							  PQTD       ,   // '@'
4449 										0,   // 'A'
4450 										0,   // 'B'
4451 										0,   // 'C'
4452 										0,   // 'D'
4453 										0,   // 'E'
4454 										0,   // 'F'
4455 										0,   // 'G'
4456 										0,   // 'H'
4457 										0,   // 'I'
4458 										0,   // 'J'
4459 										0,   // 'K'
4460 										0,   // 'L'
4461 										0,   // 'M'
4462 										0,   // 'N'
4463 										0,   // 'O'
4464 										0,   // 'P'
4465 										0,   // 'Q'
4466 										0,   // 'R'
4467 										0,   // 'S'
4468 										0,   // 'T'
4469 										0,   // 'U'
4470 										0,   // 'V'
4471 										0,   // 'W'
4472 										0,   // 'X'
4473 										0,   // 'Y'
4474 										0,   // 'Z'
4475 							  PQTD       ,   // '['
4476 					   CENQ | PQTD       ,   // '\'
4477 							  PQTD       ,   // ']'
4478 										0,   // '^'
4479 										0,   // '_'
4480 										0,   // '`'
4481 										0,   // 'a'
4482 										0,   // 'b'
4483 										0,   // 'c'
4484 										0,   // 'd'
4485 										0,   // 'e'
4486 										0,   // 'f'
4487 										0,   // 'g'
4488 										0,   // 'h'
4489 										0,   // 'i'
4490 										0,   // 'j'
4491 										0,   // 'k'
4492 										0,   // 'l'
4493 										0,   // 'm'
4494 										0,   // 'n'
4495 										0,   // 'o'
4496 										0,   // 'p'
4497 										0,   // 'q'
4498 										0,   // 'r'
4499 										0,   // 's'
4500 										0,   // 't'
4501 										0,   // 'u'
4502 										0,   // 'v'
4503 										0,   // 'w'
4504 										0,   // 'x'
4505 										0,   // 'y'
4506 										0,   // 'z'
4507 										0,   // '{'
4508 										0,   // '|'
4509 										0,   // '}'
4510 										0,   // '~'
4511 				TENQ | CENQ        | PENQ }; // DEL
4512 		Coding eNewCoding = !INetMIME::isUSASCII(nChar) ? CODING_ENCODED :
4513 		                    m_eContext == CONTEXT_PHRASE ?
4514 			                    Coding(aMinimal[nChar] >> 2) :
4515 		                    aMinimal[nChar] & m_eContext ? CODING_ENCODED :
4516 		                                                   CODING_NONE;
4517 		if (eNewCoding > m_eCoding)
4518 			m_eCoding = eNewCoding;
4519 		if (m_eCoding == CODING_QUOTED
4520 			&& INetMIME::needsQuotedStringEscape(nChar))
4521 			++m_nQuotedEscaped;
4522 
4523 		// Append to buffer:
4524 		if (sal_uInt32(m_pBufferEnd - m_pBuffer) == m_nBufferSize)
4525 		{
4526 			m_pBuffer
4527 				= static_cast< sal_Unicode * >(
4528 					  rtl_reallocateMemory(m_pBuffer,
4529 										   (m_nBufferSize + BUFFER_SIZE)
4530 										       * sizeof (sal_Unicode)));
4531 			m_pBufferEnd = m_pBuffer + m_nBufferSize;
4532 			m_nBufferSize += BUFFER_SIZE;
4533 		}
4534 		*m_pBufferEnd++ = sal_Unicode(nChar);
4535 	}
4536 	return *this;
4537 }
4538 
4539 //============================================================================
4540 //
4541 //  INetContentTypeParameterList
4542 //
4543 //============================================================================
4544 
4545 void INetContentTypeParameterList::Clear()
4546 {
4547 	while (Count() > 0)
4548 		delete static_cast< INetContentTypeParameter * >(Remove(Count() - 1));
4549 }
4550 
4551 //============================================================================
4552 const INetContentTypeParameter *
4553 INetContentTypeParameterList::find(const ByteString & rAttribute) const
4554 {
4555 	for (sal_uIntPtr i = 0; i < Count(); ++i)
4556 	{
4557 		const INetContentTypeParameter * pParameter = GetObject(i);
4558 		if (pParameter->m_sAttribute.EqualsIgnoreCaseAscii(rAttribute))
4559 			return pParameter;
4560 	}
4561 	return 0;
4562 }
4563 
4564