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 "kmmessage.h"
00011 #include "kmkernel.h"
00012 #include "globalsettings.h"
00013
00014 #include <kmime_charfreq.h>
00015 #include <kmime_codecs.h>
00016 #include <mimelib/enum.h>
00017 #include <mimelib/utility.h>
00018 #include <mimelib/string.h>
00019
00020 #include <kiconloader.h>
00021 #include <qtextcodec.h>
00022
00023 #include <assert.h>
00024
00025 using namespace KMime;
00026
00027
00028 KMMessagePart::KMMessagePart()
00029 : mType("text"), mSubtype("plain"), mCte("7bit"), mBodyDecodedSize(0),
00030 mParent(0), mLoadHeaders(false), mLoadPart(false)
00031 {
00032 }
00033
00034
00035 KMMessagePart::KMMessagePart( QDataStream & stream )
00036 : mParent(0), mLoadHeaders(false), mLoadPart(false)
00037 {
00038 unsigned long size;
00039 stream >> mOriginalContentTypeStr >> mName >> mContentDescription
00040 >> mContentDisposition >> mCte >> size >> mPartSpecifier;
00041
00042 mContentDisposition = mContentDisposition.lower();
00043 mOriginalContentTypeStr = mOriginalContentTypeStr.upper();
00044
00045
00046 int sep = mOriginalContentTypeStr.find('/');
00047 mType = mOriginalContentTypeStr.left(sep);
00048 mSubtype = mOriginalContentTypeStr.mid(sep+1);
00049
00050 mBodyDecodedSize = size;
00051 }
00052
00053
00054
00055 KMMessagePart::~KMMessagePart()
00056 {
00057 }
00058
00059
00060
00061 void KMMessagePart::clear()
00062 {
00063 mOriginalContentTypeStr = QCString();
00064 mType = "text";
00065 mSubtype = "plain";
00066 mCte = "7bit";
00067 mContentDescription = QCString();
00068 mContentDisposition = QCString();
00069 mBody.truncate( 0 );
00070 mAdditionalCTypeParamStr = QCString();
00071 mName = QString::null;
00072 mParameterAttribute = QCString();
00073 mParameterValue = QString::null;
00074 mCharset = QCString();
00075 mPartSpecifier = QString::null;
00076 mBodyDecodedSize = 0;
00077 mParent = 0;
00078 mLoadHeaders = false;
00079 mLoadPart = false;
00080 }
00081
00082
00083
00084 void KMMessagePart::duplicate( const KMMessagePart & msgPart )
00085 {
00086
00087 *this = msgPart;
00088
00089 mBody.detach();
00090 }
00091
00092
00093 int KMMessagePart::decodedSize(void) const
00094 {
00095 if (mBodyDecodedSize < 0)
00096 mBodyDecodedSize = bodyDecodedBinary().size();
00097 return mBodyDecodedSize;
00098 }
00099
00100
00101
00102 void KMMessagePart::setBody(const QCString &aStr)
00103 {
00104 mBody.duplicate( aStr.data(), aStr.length() );
00105
00106 int enc = cte();
00107 if (enc == DwMime::kCte7bit || enc == DwMime::kCte8bit || enc == DwMime::kCteBinary)
00108 mBodyDecodedSize = mBody.size();
00109 else
00110 mBodyDecodedSize = -1;
00111 }
00112
00113 void KMMessagePart::setBodyFromUnicode( const QString & str ) {
00114 QCString encoding = KMMsgBase::autoDetectCharset( charset(), KMMessage::preferredCharsets(), str );
00115 if ( encoding.isEmpty() )
00116 encoding = "utf-8";
00117 const QTextCodec * codec = KMMsgBase::codecForName( encoding );
00118 assert( codec );
00119 QValueList<int> dummy;
00120 setCharset( encoding );
00121 setBodyAndGuessCte( codec->fromUnicode( str ), dummy, false );
00122 }
00123
00124 const QTextCodec * KMMessagePart::codec() const {
00125 const QTextCodec * c = KMMsgBase::codecForName( charset() );
00126
00127 if ( !c ) {
00128
00129
00130 c = KMMsgBase::codecForName( GlobalSettings::self()->fallbackCharacterEncoding().latin1() );
00131 }
00132 if ( !c )
00133
00134
00135 c = kmkernel->networkCodec();
00136 assert( c );
00137 return c;
00138 }
00139
00140 QString KMMessagePart::bodyToUnicode(const QTextCodec* codec) const {
00141 if ( !codec )
00142
00143 codec = this->codec();
00144 assert( codec );
00145
00146 return codec->toUnicode( bodyDecoded() );
00147 }
00148
00149 void KMMessagePart::setCharset( const QCString & c ) {
00150 if ( type() != DwMime::kTypeText )
00151 kdWarning()
00152 << "KMMessagePart::setCharset(): trying to set a charset for a non-textual mimetype." << endl
00153 << "Fix this caller:" << endl
00154 << "====================================================================" << endl
00155 << kdBacktrace( 5 ) << endl
00156 << "====================================================================" << endl;
00157 mCharset = c;
00158 }
00159
00160
00161 void KMMessagePart::setBodyEncoded(const QCString& aStr)
00162 {
00163 mBodyDecodedSize = aStr.length();
00164
00165 switch (cte())
00166 {
00167 case DwMime::kCteQuotedPrintable:
00168 case DwMime::kCteBase64:
00169 {
00170 Codec * codec = Codec::codecForName( cteStr() );
00171 assert( codec );
00172
00173
00174 mBody.resize( codec->maxEncodedSizeFor( mBodyDecodedSize ) );
00175 QCString::ConstIterator iit = aStr.data();
00176 QCString::ConstIterator iend = aStr.data() + mBodyDecodedSize;
00177 QByteArray::Iterator oit = mBody.begin();
00178 QByteArray::ConstIterator oend = mBody.end();
00179 if ( !codec->encode( iit, iend, oit, oend ) )
00180 kdWarning(5006) << codec->name()
00181 << " codec lies about it's maxEncodedSizeFor( "
00182 << mBodyDecodedSize << " ). Result truncated!" << endl;
00183 mBody.truncate( oit - mBody.begin() );
00184 break;
00185 }
00186 default:
00187 kdWarning(5006) << "setBodyEncoded: unknown encoding '" << cteStr()
00188 << "'. Assuming binary." << endl;
00189 case DwMime::kCte7bit:
00190 case DwMime::kCte8bit:
00191 case DwMime::kCteBinary:
00192 mBody.duplicate( aStr.data(), mBodyDecodedSize );
00193 break;
00194 }
00195 }
00196
00197 void KMMessagePart::setBodyAndGuessCte(const QByteArray& aBuf,
00198 QValueList<int> & allowedCte,
00199 bool allow8Bit,
00200 bool willBeSigned )
00201 {
00202 mBodyDecodedSize = aBuf.size();
00203
00204 CharFreq cf( aBuf );
00205
00206 allowedCte = KMMessage::determineAllowedCtes( cf, allow8Bit, willBeSigned );
00207
00208 #ifndef NDEBUG
00209 DwString dwCte;
00210 DwCteEnumToStr(allowedCte[0], dwCte);
00211 kdDebug(5006) << "CharFreq returned " << cf.type() << "/"
00212 << cf.printableRatio() << " and I chose "
00213 << dwCte.c_str() << endl;
00214 #endif
00215
00216 setCte( allowedCte[0] );
00217 setBodyEncodedBinary( aBuf );
00218 }
00219
00220 void KMMessagePart::setBodyAndGuessCte(const QCString& aBuf,
00221 QValueList<int> & allowedCte,
00222 bool allow8Bit,
00223 bool willBeSigned )
00224 {
00225 mBodyDecodedSize = aBuf.length();
00226
00227 CharFreq cf( aBuf.data(), mBodyDecodedSize );
00228
00229 allowedCte = KMMessage::determineAllowedCtes( cf, allow8Bit, willBeSigned );
00230
00231 #ifndef NDEBUG
00232 DwString dwCte;
00233 DwCteEnumToStr(allowedCte[0], dwCte);
00234 kdDebug(5006) << "CharFreq returned " << cf.type() << "/"
00235 << cf.printableRatio() << " and I chose "
00236 << dwCte.c_str() << endl;
00237 #endif
00238
00239 setCte( allowedCte[0] );
00240 setBodyEncoded( aBuf );
00241 }
00242
00243
00244 void KMMessagePart::setBodyEncodedBinary(const QByteArray& aStr)
00245 {
00246 mBodyDecodedSize = aStr.size();
00247 if (aStr.isEmpty())
00248 {
00249 mBody.resize(0);
00250 return;
00251 }
00252
00253 switch (cte())
00254 {
00255 case DwMime::kCteQuotedPrintable:
00256 case DwMime::kCteBase64:
00257 {
00258 Codec * codec = Codec::codecForName( cteStr() );
00259 assert( codec );
00260
00261 mBody = codec->encode( aStr );
00262 break;
00263 }
00264 default:
00265 kdWarning(5006) << "setBodyEncodedBinary: unknown encoding '" << cteStr()
00266 << "'. Assuming binary." << endl;
00267 case DwMime::kCte7bit:
00268 case DwMime::kCte8bit:
00269 case DwMime::kCteBinary:
00270 mBody.duplicate( aStr );
00271 break;
00272 }
00273 }
00274
00275 void KMMessagePart::setMessageBody( const QByteArray& aBuf )
00276 {
00277 CharFreq cf( aBuf );
00278 mBodyDecodedSize = aBuf.size();
00279
00280 int cte;
00281 switch ( cf.type() ) {
00282 case CharFreq::SevenBitText:
00283 case CharFreq::SevenBitData:
00284 cte = DwMime::kCte7bit;
00285 break;
00286 case CharFreq::EightBitText:
00287 case CharFreq::EightBitData:
00288 cte = DwMime::kCte8bit;
00289 break;
00290 default:
00291 kdWarning(5006) << "Calling " << k_funcinfo
00292 << " with something containing neither 7 nor 8 bit text!"
00293 << " Fix this caller: " << kdBacktrace() << endl;
00294 }
00295 setCte( cte );
00296 setBodyEncodedBinary( aBuf );
00297 }
00298
00299
00300
00301
00302 QByteArray KMMessagePart::bodyDecodedBinary() const
00303 {
00304 if (mBody.isEmpty()) return QByteArray();
00305 QByteArray result;
00306
00307 switch (cte())
00308 {
00309 case DwMime::kCte7bit:
00310 case DwMime::kCte8bit:
00311 case DwMime::kCteBinary:
00312 result.duplicate(mBody);
00313 break;
00314 default:
00315 if ( const Codec * codec = Codec::codecForName( cteStr() ) )
00316
00317 result = codec->decode( mBody );
00318 else {
00319 kdWarning(5006) << "bodyDecodedBinary: unknown encoding '" << cteStr()
00320 << "'. Assuming binary." << endl;
00321 result.duplicate(mBody);
00322 }
00323 }
00324
00325 assert( mBodyDecodedSize < 0
00326 || (unsigned int)mBodyDecodedSize == result.size() );
00327 if ( mBodyDecodedSize < 0 )
00328 mBodyDecodedSize = result.size();
00329
00330 return result;
00331 }
00332
00333 QCString KMMessagePart::bodyDecoded(void) const
00334 {
00335 if (mBody.isEmpty()) return QCString("");
00336 bool decodeBinary = false;
00337 QCString result;
00338 int len;
00339
00340 switch (cte())
00341 {
00342 case DwMime::kCte7bit:
00343 case DwMime::kCte8bit:
00344 case DwMime::kCteBinary:
00345 {
00346 decodeBinary = true;
00347 break;
00348 }
00349 default:
00350 if ( const Codec * codec = Codec::codecForName( cteStr() ) ) {
00351
00352
00353 int bufSize = codec->maxDecodedSizeFor( mBody.size() ) + 1;
00354 result.resize( bufSize );
00355 QByteArray::ConstIterator iit = mBody.begin();
00356 QCString::Iterator oit = result.begin();
00357 QCString::ConstIterator oend = result.begin() + bufSize;
00358 if ( !codec->decode( iit, mBody.end(), oit, oend ) )
00359 kdWarning(5006) << codec->name()
00360 << " lies about it's maxDecodedSizeFor( "
00361 << mBody.size() << " ). Result truncated!" << endl;
00362 len = oit - result.begin();
00363 result.truncate( len );
00364 } else {
00365 kdWarning(5006) << "bodyDecoded: unknown encoding '" << cteStr()
00366 << "'. Assuming binary." << endl;
00367 decodeBinary = true;
00368 }
00369 }
00370
00371 if ( decodeBinary ) {
00372 len = mBody.size();
00373 result.resize( len+1 );
00374 memcpy(result.data(), mBody.data(), len);
00375 result[len] = 0;
00376 }
00377
00378 kdWarning( result.length() != (unsigned int)len, 5006 )
00379 << "KMMessagePart::bodyDecoded(): body is binary but used as text!" << endl;
00380
00381 result = result.replace( "\r\n", "\n" );
00382
00383 assert( mBodyDecodedSize < 0 || mBodyDecodedSize == len );
00384 if ( mBodyDecodedSize < 0 )
00385 mBodyDecodedSize = len;
00386
00387 return result;
00388 }
00389
00390
00391
00392 void KMMessagePart::magicSetType(bool aAutoDecode)
00393 {
00394 KMimeMagic::self()->setFollowLinks( true );
00395
00396 const QByteArray body = ( aAutoDecode ) ? bodyDecodedBinary() : mBody ;
00397 KMimeMagicResult * result = KMimeMagic::self()->findBufferType( body );
00398
00399 QString mimetype = result->mimeType();
00400 const int sep = mimetype.find('/');
00401 mType = mimetype.left(sep).latin1();
00402 mSubtype = mimetype.mid(sep+1).latin1();
00403 }
00404
00405
00406
00407 QString KMMessagePart::iconName(const QString& mimeType) const
00408 {
00409 QString fileName = KMimeType::mimeType(mimeType.isEmpty() ?
00410 (mType + "/" + mSubtype).lower() : mimeType.lower())->icon(QString::null,FALSE);
00411 fileName = KGlobal::instance()->iconLoader()->iconPath( fileName,
00412 KIcon::Desktop );
00413 return fileName;
00414 }
00415
00416
00417
00418 int KMMessagePart::type() const {
00419 return DwTypeStrToEnum(DwString(mType));
00420 }
00421
00422
00423
00424 void KMMessagePart::setType(int aType)
00425 {
00426 DwString dwType;
00427 DwTypeEnumToStr(aType, dwType);
00428 mType = dwType.c_str();
00429 }
00430
00431
00432 int KMMessagePart::subtype() const {
00433 return DwSubtypeStrToEnum(DwString(mSubtype));
00434 }
00435
00436
00437
00438 void KMMessagePart::setSubtype(int aSubtype)
00439 {
00440 DwString dwSubtype;
00441 DwSubtypeEnumToStr(aSubtype, dwSubtype);
00442 mSubtype = dwSubtype.c_str();
00443 }
00444
00445
00446 QCString KMMessagePart::parameterAttribute(void) const
00447 {
00448 return mParameterAttribute;
00449 }
00450
00451
00452 QString KMMessagePart::parameterValue(void) const
00453 {
00454 return mParameterValue;
00455 }
00456
00457
00458 void KMMessagePart::setParameter(const QCString &attribute,
00459 const QString &value)
00460 {
00461 mParameterAttribute = attribute;
00462 mParameterValue = value;
00463 }
00464
00465
00466 QCString KMMessagePart::contentTransferEncodingStr(void) const
00467 {
00468 return mCte;
00469 }
00470
00471
00472
00473 int KMMessagePart::contentTransferEncoding(void) const
00474 {
00475 return DwCteStrToEnum(DwString(mCte));
00476 }
00477
00478
00479
00480 void KMMessagePart::setContentTransferEncodingStr(const QCString &aStr)
00481 {
00482 mCte = aStr;
00483 }
00484
00485
00486
00487 void KMMessagePart::setContentTransferEncoding(int aCte)
00488 {
00489 DwString dwCte;
00490 DwCteEnumToStr(aCte, dwCte);
00491 mCte = dwCte.c_str();
00492
00493 }
00494
00495
00496
00497 QString KMMessagePart::contentDescription(void) const
00498 {
00499 return KMMsgBase::decodeRFC2047String(mContentDescription);
00500 }
00501
00502
00503
00504 void KMMessagePart::setContentDescription(const QString &aStr)
00505 {
00506 QCString encoding = KMMsgBase::autoDetectCharset(charset(),
00507 KMMessage::preferredCharsets(), aStr);
00508 if (encoding.isEmpty()) encoding = "utf-8";
00509 mContentDescription = KMMsgBase::encodeRFC2047String(aStr, encoding);
00510 }
00511
00512
00513
00514 QString KMMessagePart::fileName(void) const
00515 {
00516 bool bRFC2231encoded = false;
00517
00518
00519 int startOfFilename = mContentDisposition.find("filename*=", 0, FALSE);
00520 if (startOfFilename >= 0) {
00521 bRFC2231encoded = true;
00522 startOfFilename += 10;
00523 }
00524 else {
00525 startOfFilename = mContentDisposition.find("filename=", 0, FALSE);
00526 if (startOfFilename < 0)
00527 return QString::null;
00528 startOfFilename += 9;
00529 }
00530
00531
00532 int endOfFilename;
00533 if ( '"' == mContentDisposition[startOfFilename] ) {
00534 startOfFilename++;
00535 endOfFilename = mContentDisposition.find('"', startOfFilename) - 1;
00536 }
00537 else {
00538 endOfFilename = mContentDisposition.find(';', startOfFilename) - 1;
00539 }
00540 if (endOfFilename < 0)
00541 endOfFilename = 32767;
00542
00543 const QCString str = mContentDisposition.mid(startOfFilename,
00544 endOfFilename-startOfFilename+1)
00545 .stripWhiteSpace();
00546
00547 if (bRFC2231encoded)
00548 return KMMsgBase::decodeRFC2231String(str);
00549 else
00550 return KMMsgBase::decodeRFC2047String(str);
00551 }
00552
00553
00554
00555 QCString KMMessagePart::body() const
00556 {
00557 return QCString( mBody.data(), mBody.size() + 1 );
00558 }
00559