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