kmail

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 "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   // set the type
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   // copy the data of msgPart
00088   *this = msgPart;
00089   // detach the explicitely shared QByteArray
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; // Can't know the decoded size
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 /* no 8bit */ );
00123 }
00124 
00125 const QTextCodec * KMMessagePart::codec() const {
00126   const QTextCodec * c = KMMsgBase::codecForName( charset() );
00127 
00128   if ( !c ) {
00129     // Ok, no override and nothing in the message, let's use the fallback
00130     // the user configured
00131     c = KMMsgBase::codecForName( GlobalSettings::self()->fallbackCharacterEncoding().latin1() );
00132   }
00133   if ( !c )
00134     // no charset means us-ascii (RFC 2045), so using local encoding should
00135     // be okay
00136     c = kmkernel->networkCodec();
00137   assert( c );
00138   return c;
00139 }
00140 
00141 QString KMMessagePart::bodyToUnicode(const QTextCodec* codec) const {
00142   if ( !codec )
00143     // No codec was given, so try the charset in the mail
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       // we can't use the convenience function here, since aStr is not
00174       // a QByteArray...:
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     // fall through
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 ); // save to pass null arrays...
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] ); // choose best fitting
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 ); // save to pass null strings
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] ); // choose best fitting
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       // Nice: We can use the convenience function :-)
00263       mBody = codec->encode( aStr );
00264       break;
00265     }
00266   default:
00267     kdWarning(5006) << "setBodyEncodedBinary: unknown encoding '" << cteStr()
00268             << "'. Assuming binary." << endl;
00269     // fall through
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         // Nice: we can use the convenience function :-)
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(); // cache the decoded 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         // We can't use the codec convenience functions, since we must
00330         // return a QCString, not a QByteArray:
00331         int bufSize = codec->maxDecodedSizeFor( mBody.size() ) + 1; // trailing NUL
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 ); // adds trailing NUL
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 /* trailing NUL */ );
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" ); // CRLF -> LF conversion
00360 
00361   assert( mBodyDecodedSize < 0 || mBodyDecodedSize == len );
00362   if ( mBodyDecodedSize < 0 )
00363     mBodyDecodedSize = len; // cache decoded size
00364 
00365   return result;
00366 }
00367 
00368 
00369 //-----------------------------------------------------------------------------
00370 void KMMessagePart::magicSetType(bool aAutoDecode)
00371 {
00372   KMimeMagic::self()->setFollowLinks( true ); // is it necessary ?
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   // Allow for multiple filname*0, filename*1, ... params (defined by RFC 2231) 
00510   // in the Content-Disposision
00511   if ( mContentDisposition.contains( "filename*", FALSE ) ) {
00512   
00513     // It's RFC 2231 encoded, so extract the file name with the 2231 method
00514     str = KMMsgBase::extractRFC2231HeaderField( mContentDisposition, "filename" );
00515     return KMMsgBase::decodeRFC2231String(str);
00516   
00517   } else {
00518     
00519     // Standard RFC 2047-encoded
00520     // search the start of the filename
00521     int startOfFilename = mContentDisposition.find("filename=", 0, FALSE);
00522     if (startOfFilename < 0)
00523       return QString::null;
00524     startOfFilename += 9;
00525 
00526     // search the end of the filename
00527     int endOfFilename;
00528     if ( '"' == mContentDisposition[startOfFilename] ) {
00529       startOfFilename++; // the double quote isn't part of the filename
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 ); // space for trailing NUL
00552 }
00553 
KDE Home | KDE Accessibility Home | Description of Access Keys