kmail

kmmsgpart.cpp
1// kmmsgpart.cpp
2
3#include <config.h>
4#include <kmimemagic.h>
5#include <kmimetype.h>
6#include <kdebug.h>
7#include <kmdcodec.h>
8
9#include "kmmsgpart.h"
10#include "kmkernel.h"
11#include "kmmessage.h"
12#include "globalsettings.h"
13#include "util.h"
14
15#include <kmime_charfreq.h>
16#include <kmime_codecs.h>
17#include <mimelib/enum.h>
18#include <mimelib/utility.h>
19#include <mimelib/string.h>
20
21#include <kiconloader.h>
22#include <tqtextcodec.h>
23#include <tqglobal.h>
24
25#include <assert.h>
26
27using namespace KMime;
28
29//-----------------------------------------------------------------------------
30KMMessagePart::KMMessagePart()
31 : mType("text"), mSubtype("plain"), mCte("7bit"), mBodyDecodedSize(0),
32 mParent(0), mLoadHeaders(false), mLoadPart(false)
33{
34}
35
36//-----------------------------------------------------------------------------
37KMMessagePart::KMMessagePart( TQDataStream & stream )
38 : mParent(0), mLoadHeaders(false), mLoadPart(false)
39{
40 unsigned long size;
41 stream >> mOriginalContentTypeStr >> mName >> mContentDescription
42 >> mContentDisposition >> mCte >> size >> mPartSpecifier;
43
44 kasciitolower( mContentDisposition.data() );
45 kasciitoupper( mOriginalContentTypeStr.data() );
46
47 // set the type
48 int sep = mOriginalContentTypeStr.find('/');
49 mType = mOriginalContentTypeStr.left(sep);
50 mSubtype = mOriginalContentTypeStr.mid(sep+1);
51
52 mBodyDecodedSize = size;
53}
54
55
56//-----------------------------------------------------------------------------
57KMMessagePart::~KMMessagePart()
58{
59}
60
61
62//-----------------------------------------------------------------------------
63void KMMessagePart::clear()
64{
65 mOriginalContentTypeStr = TQCString();
66 mType = "text";
67 mSubtype = "plain";
68 mCte = "7bit";
69 mContentDescription = TQCString();
70 mContentDisposition = TQCString();
71 mBody.truncate( 0 );
72 mAdditionalCTypeParamStr = TQCString();
73 mName = TQString();
74 mParameterAttribute = TQCString();
75 mParameterValue = TQString();
76 mCharset = TQCString();
77 mPartSpecifier = TQString();
78 mBodyDecodedSize = 0;
79 mParent = 0;
80 mLoadHeaders = false;
81 mLoadPart = false;
82}
83
84
85//-----------------------------------------------------------------------------
86void KMMessagePart::duplicate( const KMMessagePart & msgPart )
87{
88 // copy the data of msgPart
89 *this = msgPart;
90 // detach the explicitely shared TQByteArray
91 mBody.detach();
92}
93
94//-----------------------------------------------------------------------------
95int KMMessagePart::decodedSize(void) const
96{
97 if (mBodyDecodedSize < 0)
98 mBodyDecodedSize = bodyDecodedBinary().size();
99 return mBodyDecodedSize;
100}
101
102
103//-----------------------------------------------------------------------------
104void KMMessagePart::setBody(const TQCString &aStr)
105{
106 KMail::Util::setFromTQCString( mBody, aStr );
107
108 int enc = cte();
109 if (enc == DwMime::kCte7bit || enc == DwMime::kCte8bit || enc == DwMime::kCteBinary)
110 mBodyDecodedSize = mBody.size();
111 else
112 mBodyDecodedSize = -1; // Can't know the decoded size
113}
114
115void KMMessagePart::setBody(const DwString &aStr)
116{
117 mBody.duplicate( aStr.c_str(), aStr.length() );
118
119 int enc = cte();
120 if (enc == DwMime::kCte7bit || enc == DwMime::kCte8bit || enc == DwMime::kCteBinary)
121 mBodyDecodedSize = mBody.size();
122 else
123 mBodyDecodedSize = -1; // Can't know the decoded size
124}
125
126void KMMessagePart::setBody(const TQByteArray &aStr)
127{
128 mBody = aStr;
129
130 int enc = cte();
131 if (enc == DwMime::kCte7bit || enc == DwMime::kCte8bit || enc == DwMime::kCteBinary)
132 mBodyDecodedSize = mBody.size();
133 else
134 mBodyDecodedSize = -1; // Can't know the decoded size
135}
136
137void KMMessagePart::setBodyFromUnicode( const TQString & str ) {
138 TQCString encoding = KMMsgBase::autoDetectCharset( charset(), KMMessage::preferredCharsets(), str );
139 if ( encoding.isEmpty() )
140 encoding = "utf-8";
141 const TQTextCodec * codec = KMMsgBase::codecForName( encoding );
142 assert( codec );
143 TQValueList<int> dummy;
144 setCharset( encoding );
145 setBodyAndGuessCte( codec->fromUnicode( str ), dummy, false /* no 8bit */ );
146}
147
148const TQTextCodec * KMMessagePart::codec() const {
149 const TQTextCodec * c = KMMsgBase::codecForName( charset() );
150
151 if ( !c ) {
152 // Ok, no override and nothing in the message, let's use the fallback
153 // the user configured
154 c = KMMsgBase::codecForName( GlobalSettings::self()->fallbackCharacterEncoding().latin1() );
155 }
156 if ( !c )
157 // no charset means us-ascii (RFC 2045), so using local encoding should
158 // be okay
159 c = kmkernel->networkCodec();
160 assert( c );
161 return c;
162}
163
164TQString KMMessagePart::bodyToUnicode(const TQTextCodec* codec) const {
165 if ( !codec )
166 // No codec was given, so try the charset in the mail
167 codec = this->codec();
168 assert( codec );
169
170 return codec->toUnicode( bodyDecoded() );
171}
172
173void KMMessagePart::setCharset( const TQCString & c ) {
174 if ( type() != DwMime::kTypeText )
175 kdWarning()
176 << "KMMessagePart::setCharset(): trying to set a charset for a non-textual mimetype." << endl
177 << "Fix this caller:" << endl
178 << "====================================================================" << endl
179 << kdBacktrace( 5 ) << endl
180 << "====================================================================" << endl;
181 mCharset = c;
182}
183
184//-----------------------------------------------------------------------------
185void KMMessagePart::setBodyEncoded(const TQCString& aStr)
186{
187 mBodyDecodedSize = aStr.size() - 1; // same as aStr.length() but faster - assuming no embedded nuls
188 switch (cte())
189 {
190 case DwMime::kCteQuotedPrintable:
191 case DwMime::kCteBase64:
192 {
193 Codec * codec = Codec::codecForName( cteStr() );
194 assert( codec );
195 // we can't use the convenience function here, since aStr is not
196 // a TQByteArray...:
197 mBody.resize( codec->maxEncodedSizeFor( mBodyDecodedSize ) );
198 TQCString::ConstIterator iit = aStr.data();
199 TQCString::ConstIterator iend = aStr.data() + mBodyDecodedSize;
200 TQByteArray::Iterator oit = mBody.begin();
201 TQByteArray::ConstIterator oend = mBody.end();
202 if ( !codec->encode( iit, iend, oit, oend ) )
203 kdWarning(5006) << codec->name()
204 << " codec lies about it's maxEncodedSizeFor( "
205 << mBodyDecodedSize << " ). Result truncated!" << endl;
206 mBody.truncate( oit - mBody.begin() );
207 break;
208 }
209 default:
210 kdWarning(5006) << "setBodyEncoded: unknown encoding '" << cteStr()
211 << "'. Assuming binary." << endl;
212 // fall through
213 case DwMime::kCte7bit:
214 case DwMime::kCte8bit:
215 case DwMime::kCteBinary:
216 // This is slow and memory hungry - consider using setBodyEncodedBinary instead!
217 mBody.duplicate( aStr.data(), mBodyDecodedSize );
218 break;
219 }
220}
221
222void KMMessagePart::setBodyAndGuessCte(const TQByteArray& aBuf,
223 TQValueList<int> & allowedCte,
224 bool allow8Bit,
225 bool willBeSigned )
226{
227 mBodyDecodedSize = aBuf.size();
228
229 CharFreq cf( aBuf ); // save to pass null arrays...
230
231 allowedCte = KMMessage::determineAllowedCtes( cf, allow8Bit, willBeSigned );
232
233#ifndef NDEBUG
234 DwString dwCte;
235 DwCteEnumToStr(allowedCte[0], dwCte);
236 kdDebug(5006) << "CharFreq returned " << cf.type() << "/"
237 << cf.printableRatio() << " and I chose "
238 << dwCte.c_str() << endl;
239#endif
240
241 setCte( allowedCte[0] ); // choose best fitting
242 setBodyEncodedBinary( aBuf );
243}
244
245void KMMessagePart::setBodyAndGuessCte(const TQCString& aBuf,
246 TQValueList<int> & allowedCte,
247 bool allow8Bit,
248 bool willBeSigned )
249{
250 mBodyDecodedSize = aBuf.size() - 1; // same as aStr.length() but faster - assuming no embedded nuls
251
252 CharFreq cf( aBuf.data(), mBodyDecodedSize ); // save to pass null strings
253
254 allowedCte = KMMessage::determineAllowedCtes( cf, allow8Bit, willBeSigned );
255
256#ifndef NDEBUG
257 DwString dwCte;
258 DwCteEnumToStr(allowedCte[0], dwCte);
259 kdDebug(5006) << "CharFreq returned " << cf.type() << "/"
260 << cf.printableRatio() << " and I chose "
261 << dwCte.c_str() << endl;
262#endif
263
264 setCte( allowedCte[0] ); // choose best fitting
265 setBodyEncoded( aBuf );
266}
267
268//-----------------------------------------------------------------------------
269void KMMessagePart::setBodyEncodedBinary(const TQByteArray& aStr)
270{
271 mBodyDecodedSize = aStr.size();
272 if (aStr.isEmpty())
273 {
274 mBody.resize(0);
275 return;
276 }
277
278 switch (cte())
279 {
280 case DwMime::kCteQuotedPrintable:
281 case DwMime::kCteBase64:
282 {
283 Codec * codec = Codec::codecForName( cteStr() );
284 assert( codec );
285 // Nice: We can use the convenience function :-)
286 mBody = codec->encode( aStr );
287 // QP encoding does CRLF -> LF conversion, which can change the size after decoding again
288 // and a size mismatch triggers an assert in various other methods
289 mBodyDecodedSize = -1;
290 break;
291 }
292 default:
293 kdWarning(5006) << "setBodyEncodedBinary: unknown encoding '" << cteStr()
294 << "'. Assuming binary." << endl;
295 // fall through
296 case DwMime::kCte7bit:
297 case DwMime::kCte8bit:
298 case DwMime::kCteBinary:
299 //mBody.duplicate( aStr );
300 mBody = aStr;
301 // Caller has to detach before it modifies aStr!
302 break;
303 }
304}
305
306void KMMessagePart::setMessageBody( const TQByteArray& aBuf )
307{
308 CharFreq cf( aBuf ); // it's safe to pass null arrays
309 mBodyDecodedSize = aBuf.size();
310
311 int cte;
312 switch ( cf.type() ) {
313 case CharFreq::SevenBitText:
314 case CharFreq::SevenBitData:
315 cte = DwMime::kCte7bit;
316 break;
317 case CharFreq::EightBitText:
318 case CharFreq::EightBitData:
319 cte = DwMime::kCte8bit;
320 break;
321 default:
322 kdWarning(5006) << "Calling " << k_funcinfo
323 << " with something containing neither 7 nor 8 bit text!"
324 << " Fix this caller: " << kdBacktrace() << endl;
325 }
326 setCte( cte );
327 setBodyEncodedBinary( aBuf );
328}
329
330//-----------------------------------------------------------------------------
331TQByteArray KMMessagePart::bodyDecodedBinary() const
332{
333 if (mBody.isEmpty()) return TQByteArray();
334 TQByteArray result;
335
336 switch (cte())
337 {
338 case DwMime::kCte7bit:
339 case DwMime::kCte8bit:
340 case DwMime::kCteBinary:
341 result.duplicate(mBody);
342 break;
343 default:
344 if ( const Codec * codec = Codec::codecForName( cteStr() ) )
345 // Nice: we can use the convenience function :-)
346 result = codec->decode( mBody );
347 else {
348 kdWarning(5006) << "bodyDecodedBinary: unknown encoding '" << cteStr()
349 << "'. Assuming binary." << endl;
350 result.duplicate(mBody);
351 }
352 }
353
354 assert( mBodyDecodedSize < 0
355 || (unsigned int)mBodyDecodedSize == result.size() );
356 if ( mBodyDecodedSize < 0 )
357 mBodyDecodedSize = result.size(); // cache the decoded size.
358
359 return result;
360}
361
362TQCString KMMessagePart::bodyDecoded(void) const
363{
364 if (mBody.isEmpty()) return TQCString("");
365 bool decodeBinary = false;
366 TQCString result;
367 int len;
368
369 switch (cte())
370 {
371 case DwMime::kCte7bit:
372 case DwMime::kCte8bit:
373 case DwMime::kCteBinary:
374 {
375 decodeBinary = true;
376 break;
377 }
378 default:
379 if ( const Codec * codec = Codec::codecForName( cteStr() ) ) {
380 // We can't use the codec convenience functions, since we must
381 // return a TQCString, not a TQByteArray:
382 int bufSize = codec->maxDecodedSizeFor( mBody.size() ) + 1; // trailing NUL
383 result.resize( bufSize );
384 TQByteArray::ConstIterator iit = mBody.begin();
385 TQCString::Iterator oit = result.begin();
386 TQCString::ConstIterator oend = result.begin() + bufSize;
387 if ( !codec->decode( iit, mBody.end(), oit, oend ) )
388 kdWarning(5006) << codec->name()
389 << " lies about it's maxDecodedSizeFor( "
390 << mBody.size() << " ). Result truncated!" << endl;
391 len = oit - result.begin();
392 result.truncate( len ); // adds trailing NUL
393 } else {
394 kdWarning(5006) << "bodyDecoded: unknown encoding '" << cteStr()
395 << "'. Assuming binary." << endl;
396 decodeBinary = true;
397 }
398 }
399
400 if ( decodeBinary ) {
401 len = mBody.size();
402 KMail::Util::setFromByteArray( result, mBody );
403 }
404
405 // Calls length -> slow
406 //kdWarning( result.length() != (unsigned int)len, 5006 )
407 // << "KMMessagePart::bodyDecoded(): body is binary but used as text!" << endl;
408
409 result = result.replace( "\r\n", "\n" ); // CRLF -> LF conversion
410
411 assert( mBodyDecodedSize < 0 || mBodyDecodedSize == len );
412 if ( mBodyDecodedSize < 0 )
413 mBodyDecodedSize = len; // cache decoded size
414
415 return result;
416}
417
418
419//-----------------------------------------------------------------------------
420void KMMessagePart::magicSetType(bool aAutoDecode)
421{
422 KMimeMagic::self()->setFollowLinks( true ); // is it necessary ?
423
424 const TQByteArray body = ( aAutoDecode ) ? bodyDecodedBinary() : mBody ;
425 KMimeMagicResult * result = KMimeMagic::self()->findBufferType( body );
426
427 TQString mimetype = result->mimeType();
428 const int sep = mimetype.find('/');
429 mType = mimetype.left(sep).latin1();
430 mSubtype = mimetype.mid(sep+1).latin1();
431}
432
433
434//-----------------------------------------------------------------------------
435TQString KMMessagePart::iconName( int size ) const
436{
437 TQCString mimeType( mType + "/" + mSubtype );
438 kasciitolower( mimeType.data() );
439
440 TQString fileName =
441 KMimeType::mimeType( mimeType )->icon( TQString(), false );
442 if ( fileName.isEmpty() )
443 {
444 fileName = this->fileName();
445 if ( fileName.isEmpty() ) fileName = this->name();
446 if ( !fileName.isEmpty() )
447 {
448 fileName = KMimeType::findByPath( "/tmp/"+fileName, 0, true )->icon( TQString(), true );
449 }
450 }
451
452 fileName =
453 TDEGlobal::instance()->iconLoader()->iconPath( fileName, size );
454 return fileName;
455}
456
457
458//-----------------------------------------------------------------------------
459int KMMessagePart::type() const {
460 return DwTypeStrToEnum(DwString(mType));
461}
462
463
464//-----------------------------------------------------------------------------
465void KMMessagePart::setType(int aType)
466{
467 DwString dwType;
468 DwTypeEnumToStr(aType, dwType);
469 mType = dwType.c_str();
470}
471
472//-----------------------------------------------------------------------------
473int KMMessagePart::subtype() const {
474 return DwSubtypeStrToEnum(DwString(mSubtype));
475}
476
477
478//-----------------------------------------------------------------------------
479void KMMessagePart::setSubtype(int aSubtype)
480{
481 DwString dwSubtype;
482 DwSubtypeEnumToStr(aSubtype, dwSubtype);
483 mSubtype = dwSubtype.c_str();
484}
485
486//-----------------------------------------------------------------------------
487TQCString KMMessagePart::parameterAttribute(void) const
488{
489 return mParameterAttribute;
490}
491
492//-----------------------------------------------------------------------------
493TQString KMMessagePart::parameterValue(void) const
494{
495 return mParameterValue;
496}
497
498//-----------------------------------------------------------------------------
499void KMMessagePart::setParameter(const TQCString &attribute,
500 const TQString &value)
501{
502 mParameterAttribute = attribute;
503 mParameterValue = value;
504}
505
506//-----------------------------------------------------------------------------
507TQCString KMMessagePart::contentTransferEncodingStr(void) const
508{
509 return mCte;
510}
511
512
513//-----------------------------------------------------------------------------
514int KMMessagePart::contentTransferEncoding(void) const
515{
516 return DwCteStrToEnum(DwString(mCte));
517}
518
519
520//-----------------------------------------------------------------------------
521void KMMessagePart::setContentTransferEncodingStr(const TQCString &aStr)
522{
523 mCte = aStr;
524}
525
526
527//-----------------------------------------------------------------------------
528void KMMessagePart::setContentTransferEncoding(int aCte)
529{
530 DwString dwCte;
531 DwCteEnumToStr(aCte, dwCte);
532 mCte = dwCte.c_str();
533
534}
535
536
537//-----------------------------------------------------------------------------
538TQString KMMessagePart::contentDescription(void) const
539{
540 return KMMsgBase::decodeRFC2047String(mContentDescription, charset());
541}
542
543
544//-----------------------------------------------------------------------------
545void KMMessagePart::setContentDescription(const TQString &aStr)
546{
547 TQCString encoding = KMMsgBase::autoDetectCharset(charset(),
549 if (encoding.isEmpty()) encoding = "utf-8";
550 mContentDescription = KMMsgBase::encodeRFC2047String(aStr, encoding);
551}
552
553
554//-----------------------------------------------------------------------------
555TQString KMMessagePart::fileName(void) const
556{
557 TQCString str;
558
559 // Allow for multiple filname*0, filename*1, ... params (defined by RFC 2231)
560 // in the Content-Disposision
561 if ( mContentDisposition.contains( "filename*", false ) ) {
562
563 // It's RFC 2231 encoded, so extract the file name with the 2231 method
564 str = KMMsgBase::extractRFC2231HeaderField( mContentDisposition, "filename" );
565 return KMMsgBase::decodeRFC2231String(str);
566
567 } else {
568
569 // Standard RFC 2047-encoded
570 // search the start of the filename
571 int startOfFilename = mContentDisposition.find("filename=", 0, false);
572 if (startOfFilename < 0)
573 return TQString();
574 startOfFilename += 9;
575
576 // search the end of the filename
577 int endOfFilename;
578 if ( '"' == mContentDisposition[startOfFilename] ) {
579 startOfFilename++; // the double quote isn't part of the filename
580 endOfFilename = mContentDisposition.find('"', startOfFilename) - 1;
581 }
582 else {
583 endOfFilename = mContentDisposition.find(';', startOfFilename) - 1;
584 }
585 if (endOfFilename < 0)
586 endOfFilename = 32767;
587
588 const TQCString str = mContentDisposition.mid(startOfFilename,
589 endOfFilename-startOfFilename+1)
590 .stripWhiteSpace();
591 return KMMsgBase::decodeRFC2047String(str);
592 }
593
594 return TQString();
595}
596
597TQCString KMMessagePart::body() const
598{
599 return TQCString( mBody.data(), mBody.size() + 1 ); // space for trailing NUL
600}
601
602DwString KMMessagePart::dwBody() const
603{
604 return KMail::Util::dwString( mBody );
605}
static TQValueList< int > determineAllowedCtes(const KMime::CharFreq &cf, bool allow8Bit, bool willBeSigned)
Returns a list of content-transfer-encodings that can be used with the given result of the character ...
Definition: kmmessage.cpp:2637
static const TQStringList & preferredCharsets()
Get a list of preferred message charsets.
Definition: kmmessage.cpp:4092
void setFromTQCString(TQByteArray &arr, const TQCString &cstr)
Fills a TQByteArray from a TQCString - removing the trailing null.
Definition: util.h:110
void setFromByteArray(TQCString &cstr, const TQByteArray &arr)
Fills a TQCString from a TQByteArray - adding the trailing null.
Definition: util.h:147
DwString dwString(const TQCString &str)
Construct a DwString from a TQCString.
Definition: util.cpp:130