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 <kasciistringtools.h>
16 #include <kmime_charfreq.h>
17 #include <kmime_codecs.h>
18 #include <mimelib/enum.h>
19 #include <mimelib/utility.h>
20 #include <mimelib/string.h>
21 
22 #include <kiconloader.h>
23 #include <tqtextcodec.h>
24 
25 #include <assert.h>
26 
27 using namespace KMime;
28 
29 //-----------------------------------------------------------------------------
30 KMMessagePart::KMMessagePart()
31  : mType("text"), mSubtype("plain"), mCte("7bit"), mBodyDecodedSize(0),
32  mParent(0), mLoadHeaders(false), mLoadPart(false)
33 {
34 }
35 
36 //-----------------------------------------------------------------------------
37 KMMessagePart::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  KPIM::kAsciiToLower( mContentDisposition.data() );
45  KPIM::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 //-----------------------------------------------------------------------------
57 KMMessagePart::~KMMessagePart()
58 {
59 }
60 
61 
62 //-----------------------------------------------------------------------------
63 void 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 //-----------------------------------------------------------------------------
86 void 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 //-----------------------------------------------------------------------------
95 int KMMessagePart::decodedSize(void) const
96 {
97  if (mBodyDecodedSize < 0)
98  mBodyDecodedSize = bodyDecodedBinary().size();
99  return mBodyDecodedSize;
100 }
101 
102 
103 //-----------------------------------------------------------------------------
104 void 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 
115 void 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 
126 void 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 
137 void 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 
148 const 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 
164 TQString 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 
173 void 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 //-----------------------------------------------------------------------------
185 void 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 
222 void 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 
245 void 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 //-----------------------------------------------------------------------------
269 void 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 
306 void 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 //-----------------------------------------------------------------------------
331 TQByteArray 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 
362 TQCString 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 //-----------------------------------------------------------------------------
420 void 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 //-----------------------------------------------------------------------------
435 TQString KMMessagePart::iconName( int size ) const
436 {
437  TQCString mimeType( mType + "/" + mSubtype );
438  KPIM::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 //-----------------------------------------------------------------------------
459 int KMMessagePart::type() const {
460  return DwTypeStrToEnum(DwString(mType));
461 }
462 
463 
464 //-----------------------------------------------------------------------------
465 void KMMessagePart::setType(int aType)
466 {
467  DwString dwType;
468  DwTypeEnumToStr(aType, dwType);
469  mType = dwType.c_str();
470 }
471 
472 //-----------------------------------------------------------------------------
473 int KMMessagePart::subtype() const {
474  return DwSubtypeStrToEnum(DwString(mSubtype));
475 }
476 
477 
478 //-----------------------------------------------------------------------------
479 void KMMessagePart::setSubtype(int aSubtype)
480 {
481  DwString dwSubtype;
482  DwSubtypeEnumToStr(aSubtype, dwSubtype);
483  mSubtype = dwSubtype.c_str();
484 }
485 
486 //-----------------------------------------------------------------------------
487 TQCString KMMessagePart::parameterAttribute(void) const
488 {
489  return mParameterAttribute;
490 }
491 
492 //-----------------------------------------------------------------------------
493 TQString KMMessagePart::parameterValue(void) const
494 {
495  return mParameterValue;
496 }
497 
498 //-----------------------------------------------------------------------------
499 void KMMessagePart::setParameter(const TQCString &attribute,
500  const TQString &value)
501 {
502  mParameterAttribute = attribute;
503  mParameterValue = value;
504 }
505 
506 //-----------------------------------------------------------------------------
507 TQCString KMMessagePart::contentTransferEncodingStr(void) const
508 {
509  return mCte;
510 }
511 
512 
513 //-----------------------------------------------------------------------------
514 int KMMessagePart::contentTransferEncoding(void) const
515 {
516  return DwCteStrToEnum(DwString(mCte));
517 }
518 
519 
520 //-----------------------------------------------------------------------------
521 void KMMessagePart::setContentTransferEncodingStr(const TQCString &aStr)
522 {
523  mCte = aStr;
524 }
525 
526 
527 //-----------------------------------------------------------------------------
528 void KMMessagePart::setContentTransferEncoding(int aCte)
529 {
530  DwString dwCte;
531  DwCteEnumToStr(aCte, dwCte);
532  mCte = dwCte.c_str();
533 
534 }
535 
536 
537 //-----------------------------------------------------------------------------
538 TQString KMMessagePart::contentDescription(void) const
539 {
540  return KMMsgBase::decodeRFC2047String(mContentDescription, charset());
541 }
542 
543 
544 //-----------------------------------------------------------------------------
545 void 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 //-----------------------------------------------------------------------------
555 TQString 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 
597 TQCString KMMessagePart::body() const
598 {
599  return TQCString( mBody.data(), mBody.size() + 1 ); // space for trailing NUL
600 }
601 
602 DwString 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:2639
static const TQStringList & preferredCharsets()
Get a list of preferred message charsets.
Definition: kmmessage.cpp:4094
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