00001
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
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
00089 *this = msgPart;
00090
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;
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;
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;
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 );
00146 }
00147
00148 const TQTextCodec * KMMessagePart::codec() const {
00149 const TQTextCodec * c = KMMsgBase::codecForName( charset() );
00150
00151 if ( !c ) {
00152
00153
00154 c = KMMsgBase::codecForName( GlobalSettings::self()->fallbackCharacterEncoding().latin1() );
00155 }
00156 if ( !c )
00157
00158
00159 c = kmkernel->networkCodec();
00160 assert( c );
00161 return c;
00162 }
00163
00164 TQString KMMessagePart::bodyToUnicode(const TQTextCodec* codec) const {
00165 if ( !codec )
00166
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;
00188 switch (cte())
00189 {
00190 case DwMime::kCteQuotedPrintable:
00191 case DwMime::kCteBase64:
00192 {
00193 Codec * codec = Codec::codecForName( cteStr() );
00194 assert( codec );
00195
00196
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
00213 case DwMime::kCte7bit:
00214 case DwMime::kCte8bit:
00215 case DwMime::kCteBinary:
00216
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 );
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] );
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;
00251
00252 CharFreq cf( aBuf.data(), mBodyDecodedSize );
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] );
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
00286 mBody = codec->encode( aStr );
00287
00288
00289 mBodyDecodedSize = -1;
00290 break;
00291 }
00292 default:
00293 kdWarning(5006) << "setBodyEncodedBinary: unknown encoding '" << cteStr()
00294 << "'. Assuming binary." << endl;
00295
00296 case DwMime::kCte7bit:
00297 case DwMime::kCte8bit:
00298 case DwMime::kCteBinary:
00299
00300 mBody = aStr;
00301
00302 break;
00303 }
00304 }
00305
00306 void KMMessagePart::setMessageBody( const TQByteArray& aBuf )
00307 {
00308 CharFreq cf( aBuf );
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
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();
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
00381
00382 int bufSize = codec->maxDecodedSizeFor( mBody.size() ) + 1;
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 );
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
00406
00407
00408
00409 result = result.replace( "\r\n", "\n" );
00410
00411 assert( mBodyDecodedSize < 0 || mBodyDecodedSize == len );
00412 if ( mBodyDecodedSize < 0 )
00413 mBodyDecodedSize = len;
00414
00415 return result;
00416 }
00417
00418
00419
00420 void KMMessagePart::magicSetType(bool aAutoDecode)
00421 {
00422 KMimeMagic::self()->setFollowLinks( true );
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 KGlobal::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
00560
00561 if ( mContentDisposition.contains( "filename*", false ) ) {
00562
00563
00564 str = KMMsgBase::extractRFC2231HeaderField( mContentDisposition, "filename" );
00565 return KMMsgBase::decodeRFC2231String(str);
00566
00567 } else {
00568
00569
00570
00571 int startOfFilename = mContentDisposition.find("filename=", 0, false);
00572 if (startOfFilename < 0)
00573 return TQString();
00574 startOfFilename += 9;
00575
00576
00577 int endOfFilename;
00578 if ( '"' == mContentDisposition[startOfFilename] ) {
00579 startOfFilename++;
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 );
00600 }
00601
00602 DwString KMMessagePart::dwBody() const
00603 {
00604 return KMail::Util::dwString( mBody );
00605 }