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