kmail Library API Documentation

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 "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   // set the type
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   // copy the data of msgPart
00087   *this = msgPart;
00088   // detach the explicitely shared QByteArray
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; // Can't know the decoded size
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 /* no 8bit */ );
00122 }
00123 
00124 const QTextCodec * KMMessagePart::codec() const {
00125   const QTextCodec * c = KMMsgBase::codecForName( charset() );
00126 
00127   if ( !c ) {
00128     // Ok, no override and nothing in the message, let's use the fallback
00129     // the user configured
00130     c = KMMsgBase::codecForName( GlobalSettings::self()->fallbackCharacterEncoding().latin1() );
00131   }
00132   if ( !c )
00133     // no charset means us-ascii (RFC 2045), so using local encoding should
00134     // be okay
00135     c = kmkernel->networkCodec();
00136   assert( c );
00137   return c;
00138 }
00139 
00140 QString KMMessagePart::bodyToUnicode(const QTextCodec* codec) const {
00141   if ( !codec )
00142     // No codec was given, so try the charset in the mail
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       // we can't use the convenience function here, since aStr is not
00173       // a QByteArray...:
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 ); // save to pass null arrays...
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] ); // choose best fitting
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 ); // save to pass null strings
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] ); // choose best fitting
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       // Nice: We can use the convenience function :-)
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 ); // it's safe to pass null arrays
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         // Nice: we can use the convenience function :-)
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(); // cache the decoded 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         // We can't use the codec convenience functions, since we must
00352         // return a QCString, not a QByteArray:
00353         int bufSize = codec->maxDecodedSizeFor( mBody.size() ) + 1; // trailing NUL
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 ); // adds trailing NUL
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 /* trailing NUL */ );
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" ); // CRLF -> LF conversion
00382 
00383   assert( mBodyDecodedSize < 0 || mBodyDecodedSize == len );
00384   if ( mBodyDecodedSize < 0 )
00385     mBodyDecodedSize = len; // cache decoded size
00386 
00387   return result;
00388 }
00389 
00390 
00391 //-----------------------------------------------------------------------------
00392 void KMMessagePart::magicSetType(bool aAutoDecode)
00393 {
00394   KMimeMagic::self()->setFollowLinks( true ); // is it necessary ?
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   // search the start of the filename
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   // search the end of the filename
00532   int endOfFilename;
00533   if ( '"' == mContentDisposition[startOfFilename] ) {
00534     startOfFilename++; // the double quote isn't part of the filename
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 ); // space for trailing NUL
00558 }
00559 
KDE Logo
This file is part of the documentation for kmail Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Aug 23 18:21:30 2007 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003