kmmsgpart.cpp
00001 // kmmsgpart.cpp 00002 00003 #include <config.h> 00004 #include <kmimemagic.h> 00005 #include <kmimetype.h> 00006 #include <kdebug.h> 00007 #include <kmdcodec.h> 00008 00009 #include "kmmsgpart.h" 00010 #include "kmkernel.h" 00011 #include "kmmessage.h" 00012 #include "globalsettings.h" 00013 #include "util.h" 00014 00015 #include <kasciistringtools.h> 00016 #include <kmime_charfreq.h> 00017 #include <kmime_codecs.h> 00018 #include <mimelib/enum.h> 00019 #include <mimelib/utility.h> 00020 #include <mimelib/string.h> 00021 00022 #include <kiconloader.h> 00023 #include <tqtextcodec.h> 00024 00025 #include <assert.h> 00026 00027 using namespace KMime; 00028 00029 //----------------------------------------------------------------------------- 00030 KMMessagePart::KMMessagePart() 00031 : mType("text"), mSubtype("plain"), mCte("7bit"), mBodyDecodedSize(0), 00032 mParent(0), mLoadHeaders(false), mLoadPart(false) 00033 { 00034 } 00035 00036 //----------------------------------------------------------------------------- 00037 KMMessagePart::KMMessagePart( TQDataStream & stream ) 00038 : mParent(0), mLoadHeaders(false), mLoadPart(false) 00039 { 00040 unsigned long size; 00041 stream >> mOriginalContentTypeStr >> mName >> mContentDescription 00042 >> mContentDisposition >> mCte >> size >> mPartSpecifier; 00043 00044 KPIM::kAsciiToLower( mContentDisposition.data() ); 00045 KPIM::kAsciiToUpper( mOriginalContentTypeStr.data() ); 00046 00047 // set the type 00048 int sep = mOriginalContentTypeStr.find('/'); 00049 mType = mOriginalContentTypeStr.left(sep); 00050 mSubtype = mOriginalContentTypeStr.mid(sep+1); 00051 00052 mBodyDecodedSize = size; 00053 } 00054 00055 00056 //----------------------------------------------------------------------------- 00057 KMMessagePart::~KMMessagePart() 00058 { 00059 } 00060 00061 00062 //----------------------------------------------------------------------------- 00063 void KMMessagePart::clear() 00064 { 00065 mOriginalContentTypeStr = TQCString(); 00066 mType = "text"; 00067 mSubtype = "plain"; 00068 mCte = "7bit"; 00069 mContentDescription = TQCString(); 00070 mContentDisposition = TQCString(); 00071 mBody.truncate( 0 ); 00072 mAdditionalCTypeParamStr = TQCString(); 00073 mName = TQString(); 00074 mParameterAttribute = TQCString(); 00075 mParameterValue = TQString(); 00076 mCharset = TQCString(); 00077 mPartSpecifier = TQString(); 00078 mBodyDecodedSize = 0; 00079 mParent = 0; 00080 mLoadHeaders = false; 00081 mLoadPart = false; 00082 } 00083 00084 00085 //----------------------------------------------------------------------------- 00086 void KMMessagePart::duplicate( const KMMessagePart & msgPart ) 00087 { 00088 // copy the data of msgPart 00089 *this = msgPart; 00090 // detach the explicitely shared TQByteArray 00091 mBody.detach(); 00092 } 00093 00094 //----------------------------------------------------------------------------- 00095 int KMMessagePart::decodedSize(void) const 00096 { 00097 if (mBodyDecodedSize < 0) 00098 mBodyDecodedSize = bodyDecodedBinary().size(); 00099 return mBodyDecodedSize; 00100 } 00101 00102 00103 //----------------------------------------------------------------------------- 00104 void KMMessagePart::setBody(const TQCString &aStr) 00105 { 00106 KMail::Util::setFromTQCString( mBody, aStr ); 00107 00108 int enc = cte(); 00109 if (enc == DwMime::kCte7bit || enc == DwMime::kCte8bit || enc == DwMime::kCteBinary) 00110 mBodyDecodedSize = mBody.size(); 00111 else 00112 mBodyDecodedSize = -1; // Can't know the decoded size 00113 } 00114 00115 void KMMessagePart::setBody(const DwString &aStr) 00116 { 00117 mBody.duplicate( aStr.c_str(), aStr.length() ); 00118 00119 int enc = cte(); 00120 if (enc == DwMime::kCte7bit || enc == DwMime::kCte8bit || enc == DwMime::kCteBinary) 00121 mBodyDecodedSize = mBody.size(); 00122 else 00123 mBodyDecodedSize = -1; // Can't know the decoded size 00124 } 00125 00126 void KMMessagePart::setBody(const TQByteArray &aStr) 00127 { 00128 mBody = aStr; 00129 00130 int enc = cte(); 00131 if (enc == DwMime::kCte7bit || enc == DwMime::kCte8bit || enc == DwMime::kCteBinary) 00132 mBodyDecodedSize = mBody.size(); 00133 else 00134 mBodyDecodedSize = -1; // Can't know the decoded size 00135 } 00136 00137 void KMMessagePart::setBodyFromUnicode( const TQString & str ) { 00138 TQCString encoding = KMMsgBase::autoDetectCharset( charset(), KMMessage::preferredCharsets(), str ); 00139 if ( encoding.isEmpty() ) 00140 encoding = "utf-8"; 00141 const TQTextCodec * codec = KMMsgBase::codecForName( encoding ); 00142 assert( codec ); 00143 TQValueList<int> dummy; 00144 setCharset( encoding ); 00145 setBodyAndGuessCte( codec->fromUnicode( str ), dummy, false /* no 8bit */ ); 00146 } 00147 00148 const TQTextCodec * KMMessagePart::codec() const { 00149 const TQTextCodec * c = KMMsgBase::codecForName( charset() ); 00150 00151 if ( !c ) { 00152 // Ok, no override and nothing in the message, let's use the fallback 00153 // the user configured 00154 c = KMMsgBase::codecForName( GlobalSettings::self()->fallbackCharacterEncoding().latin1() ); 00155 } 00156 if ( !c ) 00157 // no charset means us-ascii (RFC 2045), so using local encoding should 00158 // be okay 00159 c = kmkernel->networkCodec(); 00160 assert( c ); 00161 return c; 00162 } 00163 00164 TQString KMMessagePart::bodyToUnicode(const TQTextCodec* codec) const { 00165 if ( !codec ) 00166 // No codec was given, so try the charset in the mail 00167 codec = this->codec(); 00168 assert( codec ); 00169 00170 return codec->toUnicode( bodyDecoded() ); 00171 } 00172 00173 void KMMessagePart::setCharset( const TQCString & c ) { 00174 if ( type() != DwMime::kTypeText ) 00175 kdWarning() 00176 << "KMMessagePart::setCharset(): trying to set a charset for a non-textual mimetype." << endl 00177 << "Fix this caller:" << endl 00178 << "====================================================================" << endl 00179 << kdBacktrace( 5 ) << endl 00180 << "====================================================================" << endl; 00181 mCharset = c; 00182 } 00183 00184 //----------------------------------------------------------------------------- 00185 void KMMessagePart::setBodyEncoded(const TQCString& aStr) 00186 { 00187 mBodyDecodedSize = aStr.size() - 1; // same as aStr.length() but faster - assuming no embedded nuls 00188 switch (cte()) 00189 { 00190 case DwMime::kCteQuotedPrintable: 00191 case DwMime::kCteBase64: 00192 { 00193 Codec * codec = Codec::codecForName( cteStr() ); 00194 assert( codec ); 00195 // we can't use the convenience function here, since aStr is not 00196 // a TQByteArray...: 00197 mBody.resize( codec->maxEncodedSizeFor( mBodyDecodedSize ) ); 00198 TQCString::ConstIterator iit = aStr.data(); 00199 TQCString::ConstIterator iend = aStr.data() + mBodyDecodedSize; 00200 TQByteArray::Iterator oit = mBody.begin(); 00201 TQByteArray::ConstIterator oend = mBody.end(); 00202 if ( !codec->encode( iit, iend, oit, oend ) ) 00203 kdWarning(5006) << codec->name() 00204 << " codec lies about it's maxEncodedSizeFor( " 00205 << mBodyDecodedSize << " ). Result truncated!" << endl; 00206 mBody.truncate( oit - mBody.begin() ); 00207 break; 00208 } 00209 default: 00210 kdWarning(5006) << "setBodyEncoded: unknown encoding '" << cteStr() 00211 << "'. Assuming binary." << endl; 00212 // fall through 00213 case DwMime::kCte7bit: 00214 case DwMime::kCte8bit: 00215 case DwMime::kCteBinary: 00216 // This is slow and memory hungry - consider using setBodyEncodedBinary instead! 00217 mBody.duplicate( aStr.data(), mBodyDecodedSize ); 00218 break; 00219 } 00220 } 00221 00222 void KMMessagePart::setBodyAndGuessCte(const TQByteArray& aBuf, 00223 TQValueList<int> & allowedCte, 00224 bool allow8Bit, 00225 bool willBeSigned ) 00226 { 00227 mBodyDecodedSize = aBuf.size(); 00228 00229 CharFreq cf( aBuf ); // save to pass null arrays... 00230 00231 allowedCte = KMMessage::determineAllowedCtes( cf, allow8Bit, willBeSigned ); 00232 00233 #ifndef NDEBUG 00234 DwString dwCte; 00235 DwCteEnumToStr(allowedCte[0], dwCte); 00236 kdDebug(5006) << "CharFreq returned " << cf.type() << "/" 00237 << cf.printableRatio() << " and I chose " 00238 << dwCte.c_str() << endl; 00239 #endif 00240 00241 setCte( allowedCte[0] ); // choose best fitting 00242 setBodyEncodedBinary( aBuf ); 00243 } 00244 00245 void KMMessagePart::setBodyAndGuessCte(const TQCString& aBuf, 00246 TQValueList<int> & allowedCte, 00247 bool allow8Bit, 00248 bool willBeSigned ) 00249 { 00250 mBodyDecodedSize = aBuf.size() - 1; // same as aStr.length() but faster - assuming no embedded nuls 00251 00252 CharFreq cf( aBuf.data(), mBodyDecodedSize ); // save to pass null strings 00253 00254 allowedCte = KMMessage::determineAllowedCtes( cf, allow8Bit, willBeSigned ); 00255 00256 #ifndef NDEBUG 00257 DwString dwCte; 00258 DwCteEnumToStr(allowedCte[0], dwCte); 00259 kdDebug(5006) << "CharFreq returned " << cf.type() << "/" 00260 << cf.printableRatio() << " and I chose " 00261 << dwCte.c_str() << endl; 00262 #endif 00263 00264 setCte( allowedCte[0] ); // choose best fitting 00265 setBodyEncoded( aBuf ); 00266 } 00267 00268 //----------------------------------------------------------------------------- 00269 void KMMessagePart::setBodyEncodedBinary(const TQByteArray& aStr) 00270 { 00271 mBodyDecodedSize = aStr.size(); 00272 if (aStr.isEmpty()) 00273 { 00274 mBody.resize(0); 00275 return; 00276 } 00277 00278 switch (cte()) 00279 { 00280 case DwMime::kCteQuotedPrintable: 00281 case DwMime::kCteBase64: 00282 { 00283 Codec * codec = Codec::codecForName( cteStr() ); 00284 assert( codec ); 00285 // Nice: We can use the convenience function :-) 00286 mBody = codec->encode( aStr ); 00287 // QP encoding does CRLF -> LF conversion, which can change the size after decoding again 00288 // and a size mismatch triggers an assert in various other methods 00289 mBodyDecodedSize = -1; 00290 break; 00291 } 00292 default: 00293 kdWarning(5006) << "setBodyEncodedBinary: unknown encoding '" << cteStr() 00294 << "'. Assuming binary." << endl; 00295 // fall through 00296 case DwMime::kCte7bit: 00297 case DwMime::kCte8bit: 00298 case DwMime::kCteBinary: 00299 //mBody.duplicate( aStr ); 00300 mBody = aStr; 00301 // Caller has to detach before it modifies aStr! 00302 break; 00303 } 00304 } 00305 00306 void KMMessagePart::setMessageBody( const TQByteArray& aBuf ) 00307 { 00308 CharFreq cf( aBuf ); // it's safe to pass null arrays 00309 mBodyDecodedSize = aBuf.size(); 00310 00311 int cte; 00312 switch ( cf.type() ) { 00313 case CharFreq::SevenBitText: 00314 case CharFreq::SevenBitData: 00315 cte = DwMime::kCte7bit; 00316 break; 00317 case CharFreq::EightBitText: 00318 case CharFreq::EightBitData: 00319 cte = DwMime::kCte8bit; 00320 break; 00321 default: 00322 kdWarning(5006) << "Calling " << k_funcinfo 00323 << " with something containing neither 7 nor 8 bit text!" 00324 << " Fix this caller: " << kdBacktrace() << endl; 00325 } 00326 setCte( cte ); 00327 setBodyEncodedBinary( aBuf ); 00328 } 00329 00330 //----------------------------------------------------------------------------- 00331 TQByteArray KMMessagePart::bodyDecodedBinary() const 00332 { 00333 if (mBody.isEmpty()) return TQByteArray(); 00334 TQByteArray result; 00335 00336 switch (cte()) 00337 { 00338 case DwMime::kCte7bit: 00339 case DwMime::kCte8bit: 00340 case DwMime::kCteBinary: 00341 result.duplicate(mBody); 00342 break; 00343 default: 00344 if ( const Codec * codec = Codec::codecForName( cteStr() ) ) 00345 // Nice: we can use the convenience function :-) 00346 result = codec->decode( mBody ); 00347 else { 00348 kdWarning(5006) << "bodyDecodedBinary: unknown encoding '" << cteStr() 00349 << "'. Assuming binary." << endl; 00350 result.duplicate(mBody); 00351 } 00352 } 00353 00354 assert( mBodyDecodedSize < 0 00355 || (unsigned int)mBodyDecodedSize == result.size() ); 00356 if ( mBodyDecodedSize < 0 ) 00357 mBodyDecodedSize = result.size(); // cache the decoded size. 00358 00359 return result; 00360 } 00361 00362 TQCString KMMessagePart::bodyDecoded(void) const 00363 { 00364 if (mBody.isEmpty()) return TQCString(""); 00365 bool decodeBinary = false; 00366 TQCString result; 00367 int len; 00368 00369 switch (cte()) 00370 { 00371 case DwMime::kCte7bit: 00372 case DwMime::kCte8bit: 00373 case DwMime::kCteBinary: 00374 { 00375 decodeBinary = true; 00376 break; 00377 } 00378 default: 00379 if ( const Codec * codec = Codec::codecForName( cteStr() ) ) { 00380 // We can't use the codec convenience functions, since we must 00381 // return a TQCString, not a TQByteArray: 00382 int bufSize = codec->maxDecodedSizeFor( mBody.size() ) + 1; // trailing NUL 00383 result.resize( bufSize ); 00384 TQByteArray::ConstIterator iit = mBody.begin(); 00385 TQCString::Iterator oit = result.begin(); 00386 TQCString::ConstIterator oend = result.begin() + bufSize; 00387 if ( !codec->decode( iit, mBody.end(), oit, oend ) ) 00388 kdWarning(5006) << codec->name() 00389 << " lies about it's maxDecodedSizeFor( " 00390 << mBody.size() << " ). Result truncated!" << endl; 00391 len = oit - result.begin(); 00392 result.truncate( len ); // adds trailing NUL 00393 } else { 00394 kdWarning(5006) << "bodyDecoded: unknown encoding '" << cteStr() 00395 << "'. Assuming binary." << endl; 00396 decodeBinary = true; 00397 } 00398 } 00399 00400 if ( decodeBinary ) { 00401 len = mBody.size(); 00402 KMail::Util::setFromByteArray( result, mBody ); 00403 } 00404 00405 // Calls length -> slow 00406 //kdWarning( result.length() != (unsigned int)len, 5006 ) 00407 // << "KMMessagePart::bodyDecoded(): body is binary but used as text!" << endl; 00408 00409 result = result.replace( "\r\n", "\n" ); // CRLF -> LF conversion 00410 00411 assert( mBodyDecodedSize < 0 || mBodyDecodedSize == len ); 00412 if ( mBodyDecodedSize < 0 ) 00413 mBodyDecodedSize = len; // cache decoded size 00414 00415 return result; 00416 } 00417 00418 00419 //----------------------------------------------------------------------------- 00420 void KMMessagePart::magicSetType(bool aAutoDecode) 00421 { 00422 KMimeMagic::self()->setFollowLinks( true ); // is it necessary ? 00423 00424 const TQByteArray body = ( aAutoDecode ) ? bodyDecodedBinary() : mBody ; 00425 KMimeMagicResult * result = KMimeMagic::self()->findBufferType( body ); 00426 00427 TQString mimetype = result->mimeType(); 00428 const int sep = mimetype.find('/'); 00429 mType = mimetype.left(sep).latin1(); 00430 mSubtype = mimetype.mid(sep+1).latin1(); 00431 } 00432 00433 00434 //----------------------------------------------------------------------------- 00435 TQString KMMessagePart::iconName( int size ) const 00436 { 00437 TQCString mimeType( mType + "/" + mSubtype ); 00438 KPIM::kAsciiToLower( mimeType.data() ); 00439 00440 TQString fileName = 00441 KMimeType::mimeType( mimeType )->icon( TQString(), false ); 00442 if ( fileName.isEmpty() ) 00443 { 00444 fileName = this->fileName(); 00445 if ( fileName.isEmpty() ) fileName = this->name(); 00446 if ( !fileName.isEmpty() ) 00447 { 00448 fileName = KMimeType::findByPath( "/tmp/"+fileName, 0, true )->icon( TQString(), true ); 00449 } 00450 } 00451 00452 fileName = 00453 TDEGlobal::instance()->iconLoader()->iconPath( fileName, size ); 00454 return fileName; 00455 } 00456 00457 00458 //----------------------------------------------------------------------------- 00459 int KMMessagePart::type() const { 00460 return DwTypeStrToEnum(DwString(mType)); 00461 } 00462 00463 00464 //----------------------------------------------------------------------------- 00465 void KMMessagePart::setType(int aType) 00466 { 00467 DwString dwType; 00468 DwTypeEnumToStr(aType, dwType); 00469 mType = dwType.c_str(); 00470 } 00471 00472 //----------------------------------------------------------------------------- 00473 int KMMessagePart::subtype() const { 00474 return DwSubtypeStrToEnum(DwString(mSubtype)); 00475 } 00476 00477 00478 //----------------------------------------------------------------------------- 00479 void KMMessagePart::setSubtype(int aSubtype) 00480 { 00481 DwString dwSubtype; 00482 DwSubtypeEnumToStr(aSubtype, dwSubtype); 00483 mSubtype = dwSubtype.c_str(); 00484 } 00485 00486 //----------------------------------------------------------------------------- 00487 TQCString KMMessagePart::parameterAttribute(void) const 00488 { 00489 return mParameterAttribute; 00490 } 00491 00492 //----------------------------------------------------------------------------- 00493 TQString KMMessagePart::parameterValue(void) const 00494 { 00495 return mParameterValue; 00496 } 00497 00498 //----------------------------------------------------------------------------- 00499 void KMMessagePart::setParameter(const TQCString &attribute, 00500 const TQString &value) 00501 { 00502 mParameterAttribute = attribute; 00503 mParameterValue = value; 00504 } 00505 00506 //----------------------------------------------------------------------------- 00507 TQCString KMMessagePart::contentTransferEncodingStr(void) const 00508 { 00509 return mCte; 00510 } 00511 00512 00513 //----------------------------------------------------------------------------- 00514 int KMMessagePart::contentTransferEncoding(void) const 00515 { 00516 return DwCteStrToEnum(DwString(mCte)); 00517 } 00518 00519 00520 //----------------------------------------------------------------------------- 00521 void KMMessagePart::setContentTransferEncodingStr(const TQCString &aStr) 00522 { 00523 mCte = aStr; 00524 } 00525 00526 00527 //----------------------------------------------------------------------------- 00528 void KMMessagePart::setContentTransferEncoding(int aCte) 00529 { 00530 DwString dwCte; 00531 DwCteEnumToStr(aCte, dwCte); 00532 mCte = dwCte.c_str(); 00533 00534 } 00535 00536 00537 //----------------------------------------------------------------------------- 00538 TQString KMMessagePart::contentDescription(void) const 00539 { 00540 return KMMsgBase::decodeRFC2047String(mContentDescription, charset()); 00541 } 00542 00543 00544 //----------------------------------------------------------------------------- 00545 void KMMessagePart::setContentDescription(const TQString &aStr) 00546 { 00547 TQCString encoding = KMMsgBase::autoDetectCharset(charset(), 00548 KMMessage::preferredCharsets(), aStr); 00549 if (encoding.isEmpty()) encoding = "utf-8"; 00550 mContentDescription = KMMsgBase::encodeRFC2047String(aStr, encoding); 00551 } 00552 00553 00554 //----------------------------------------------------------------------------- 00555 TQString KMMessagePart::fileName(void) const 00556 { 00557 TQCString str; 00558 00559 // Allow for multiple filname*0, filename*1, ... params (defined by RFC 2231) 00560 // in the Content-Disposision 00561 if ( mContentDisposition.contains( "filename*", false ) ) { 00562 00563 // It's RFC 2231 encoded, so extract the file name with the 2231 method 00564 str = KMMsgBase::extractRFC2231HeaderField( mContentDisposition, "filename" ); 00565 return KMMsgBase::decodeRFC2231String(str); 00566 00567 } else { 00568 00569 // Standard RFC 2047-encoded 00570 // search the start of the filename 00571 int startOfFilename = mContentDisposition.find("filename=", 0, false); 00572 if (startOfFilename < 0) 00573 return TQString(); 00574 startOfFilename += 9; 00575 00576 // search the end of the filename 00577 int endOfFilename; 00578 if ( '"' == mContentDisposition[startOfFilename] ) { 00579 startOfFilename++; // the double quote isn't part of the filename 00580 endOfFilename = mContentDisposition.find('"', startOfFilename) - 1; 00581 } 00582 else { 00583 endOfFilename = mContentDisposition.find(';', startOfFilename) - 1; 00584 } 00585 if (endOfFilename < 0) 00586 endOfFilename = 32767; 00587 00588 const TQCString str = mContentDisposition.mid(startOfFilename, 00589 endOfFilename-startOfFilename+1) 00590 .stripWhiteSpace(); 00591 return KMMsgBase::decodeRFC2047String(str); 00592 } 00593 00594 return TQString(); 00595 } 00596 00597 TQCString KMMessagePart::body() const 00598 { 00599 return TQCString( mBody.data(), mBody.size() + 1 ); // space for trailing NUL 00600 } 00601 00602 DwString KMMessagePart::dwBody() const 00603 { 00604 return KMail::Util::dwString( mBody ); 00605 }