diff options
Diffstat (limited to 'kmail/kmmsgpart.cpp')
-rw-r--r-- | kmail/kmmsgpart.cpp | 602 |
1 files changed, 602 insertions, 0 deletions
diff --git a/kmail/kmmsgpart.cpp b/kmail/kmmsgpart.cpp new file mode 100644 index 000000000..48dc55ca8 --- /dev/null +++ b/kmail/kmmsgpart.cpp @@ -0,0 +1,602 @@ +// kmmsgpart.cpp + +#include <config.h> +#include <kmimemagic.h> +#include <kmimetype.h> +#include <kdebug.h> +#include <kmdcodec.h> + +#include "kmmsgpart.h" +#include "kmkernel.h" +#include "kmmessage.h" +#include "globalsettings.h" +#include "util.h" + +#include <kasciistringtools.h> +#include <kmime_charfreq.h> +#include <kmime_codecs.h> +#include <mimelib/enum.h> +#include <mimelib/utility.h> +#include <mimelib/string.h> + +#include <kiconloader.h> +#include <qtextcodec.h> + +#include <assert.h> + +using namespace KMime; + +//----------------------------------------------------------------------------- +KMMessagePart::KMMessagePart() + : mType("text"), mSubtype("plain"), mCte("7bit"), mBodyDecodedSize(0), + mParent(0), mLoadHeaders(false), mLoadPart(false) +{ +} + +//----------------------------------------------------------------------------- +KMMessagePart::KMMessagePart( QDataStream & stream ) + : mParent(0), mLoadHeaders(false), mLoadPart(false) +{ + unsigned long size; + stream >> mOriginalContentTypeStr >> mName >> mContentDescription + >> mContentDisposition >> mCte >> size >> mPartSpecifier; + + KPIM::kAsciiToLower( mContentDisposition.data() ); + KPIM::kAsciiToUpper( mOriginalContentTypeStr.data() ); + + // set the type + int sep = mOriginalContentTypeStr.find('/'); + mType = mOriginalContentTypeStr.left(sep); + mSubtype = mOriginalContentTypeStr.mid(sep+1); + + mBodyDecodedSize = size; +} + + +//----------------------------------------------------------------------------- +KMMessagePart::~KMMessagePart() +{ +} + + +//----------------------------------------------------------------------------- +void KMMessagePart::clear() +{ + mOriginalContentTypeStr = QCString(); + mType = "text"; + mSubtype = "plain"; + mCte = "7bit"; + mContentDescription = QCString(); + mContentDisposition = QCString(); + mBody.truncate( 0 ); + mAdditionalCTypeParamStr = QCString(); + mName = QString::null; + mParameterAttribute = QCString(); + mParameterValue = QString::null; + mCharset = QCString(); + mPartSpecifier = QString::null; + mBodyDecodedSize = 0; + mParent = 0; + mLoadHeaders = false; + mLoadPart = false; +} + + +//----------------------------------------------------------------------------- +void KMMessagePart::duplicate( const KMMessagePart & msgPart ) +{ + // copy the data of msgPart + *this = msgPart; + // detach the explicitely shared QByteArray + mBody.detach(); +} + +//----------------------------------------------------------------------------- +int KMMessagePart::decodedSize(void) const +{ + if (mBodyDecodedSize < 0) + mBodyDecodedSize = bodyDecodedBinary().size(); + return mBodyDecodedSize; +} + + +//----------------------------------------------------------------------------- +void KMMessagePart::setBody(const QCString &aStr) +{ + KMail::Util::setFromQCString( mBody, aStr ); + + int enc = cte(); + if (enc == DwMime::kCte7bit || enc == DwMime::kCte8bit || enc == DwMime::kCteBinary) + mBodyDecodedSize = mBody.size(); + else + mBodyDecodedSize = -1; // Can't know the decoded size +} + +void KMMessagePart::setBody(const DwString &aStr) +{ + mBody.duplicate( aStr.c_str(), aStr.length() ); + + int enc = cte(); + if (enc == DwMime::kCte7bit || enc == DwMime::kCte8bit || enc == DwMime::kCteBinary) + mBodyDecodedSize = mBody.size(); + else + mBodyDecodedSize = -1; // Can't know the decoded size +} + +void KMMessagePart::setBody(const QByteArray &aStr) +{ + mBody = aStr; + + int enc = cte(); + if (enc == DwMime::kCte7bit || enc == DwMime::kCte8bit || enc == DwMime::kCteBinary) + mBodyDecodedSize = mBody.size(); + else + mBodyDecodedSize = -1; // Can't know the decoded size +} + +void KMMessagePart::setBodyFromUnicode( const QString & str ) { + QCString encoding = KMMsgBase::autoDetectCharset( charset(), KMMessage::preferredCharsets(), str ); + if ( encoding.isEmpty() ) + encoding = "utf-8"; + const QTextCodec * codec = KMMsgBase::codecForName( encoding ); + assert( codec ); + QValueList<int> dummy; + setCharset( encoding ); + setBodyAndGuessCte( codec->fromUnicode( str ), dummy, false /* no 8bit */ ); +} + +const QTextCodec * KMMessagePart::codec() const { + const QTextCodec * c = KMMsgBase::codecForName( charset() ); + + if ( !c ) { + // Ok, no override and nothing in the message, let's use the fallback + // the user configured + c = KMMsgBase::codecForName( GlobalSettings::self()->fallbackCharacterEncoding().latin1() ); + } + if ( !c ) + // no charset means us-ascii (RFC 2045), so using local encoding should + // be okay + c = kmkernel->networkCodec(); + assert( c ); + return c; +} + +QString KMMessagePart::bodyToUnicode(const QTextCodec* codec) const { + if ( !codec ) + // No codec was given, so try the charset in the mail + codec = this->codec(); + assert( codec ); + + return codec->toUnicode( bodyDecoded() ); +} + +void KMMessagePart::setCharset( const QCString & c ) { + if ( type() != DwMime::kTypeText ) + kdWarning() + << "KMMessagePart::setCharset(): trying to set a charset for a non-textual mimetype." << endl + << "Fix this caller:" << endl + << "====================================================================" << endl + << kdBacktrace( 5 ) << endl + << "====================================================================" << endl; + mCharset = c; +} + +//----------------------------------------------------------------------------- +void KMMessagePart::setBodyEncoded(const QCString& aStr) +{ + mBodyDecodedSize = aStr.size() - 1; // same as aStr.length() but faster - assuming no embedded nuls + switch (cte()) + { + case DwMime::kCteQuotedPrintable: + case DwMime::kCteBase64: + { + Codec * codec = Codec::codecForName( cteStr() ); + assert( codec ); + // we can't use the convenience function here, since aStr is not + // a QByteArray...: + mBody.resize( codec->maxEncodedSizeFor( mBodyDecodedSize ) ); + QCString::ConstIterator iit = aStr.data(); + QCString::ConstIterator iend = aStr.data() + mBodyDecodedSize; + QByteArray::Iterator oit = mBody.begin(); + QByteArray::ConstIterator oend = mBody.end(); + if ( !codec->encode( iit, iend, oit, oend ) ) + kdWarning(5006) << codec->name() + << " codec lies about it's maxEncodedSizeFor( " + << mBodyDecodedSize << " ). Result truncated!" << endl; + mBody.truncate( oit - mBody.begin() ); + break; + } + default: + kdWarning(5006) << "setBodyEncoded: unknown encoding '" << cteStr() + << "'. Assuming binary." << endl; + // fall through + case DwMime::kCte7bit: + case DwMime::kCte8bit: + case DwMime::kCteBinary: + // This is slow and memory hungry - consider using setBodyEncodedBinary instead! + mBody.duplicate( aStr.data(), mBodyDecodedSize ); + break; + } +} + +void KMMessagePart::setBodyAndGuessCte(const QByteArray& aBuf, + QValueList<int> & allowedCte, + bool allow8Bit, + bool willBeSigned ) +{ + mBodyDecodedSize = aBuf.size(); + + CharFreq cf( aBuf ); // save to pass null arrays... + + allowedCte = KMMessage::determineAllowedCtes( cf, allow8Bit, willBeSigned ); + +#ifndef NDEBUG + DwString dwCte; + DwCteEnumToStr(allowedCte[0], dwCte); + kdDebug(5006) << "CharFreq returned " << cf.type() << "/" + << cf.printableRatio() << " and I chose " + << dwCte.c_str() << endl; +#endif + + setCte( allowedCte[0] ); // choose best fitting + setBodyEncodedBinary( aBuf ); +} + +void KMMessagePart::setBodyAndGuessCte(const QCString& aBuf, + QValueList<int> & allowedCte, + bool allow8Bit, + bool willBeSigned ) +{ + mBodyDecodedSize = aBuf.size() - 1; // same as aStr.length() but faster - assuming no embedded nuls + + CharFreq cf( aBuf.data(), mBodyDecodedSize ); // save to pass null strings + + allowedCte = KMMessage::determineAllowedCtes( cf, allow8Bit, willBeSigned ); + +#ifndef NDEBUG + DwString dwCte; + DwCteEnumToStr(allowedCte[0], dwCte); + kdDebug(5006) << "CharFreq returned " << cf.type() << "/" + << cf.printableRatio() << " and I chose " + << dwCte.c_str() << endl; +#endif + + setCte( allowedCte[0] ); // choose best fitting + setBodyEncoded( aBuf ); +} + +//----------------------------------------------------------------------------- +void KMMessagePart::setBodyEncodedBinary(const QByteArray& aStr) +{ + mBodyDecodedSize = aStr.size(); + if (aStr.isEmpty()) + { + mBody.resize(0); + return; + } + + switch (cte()) + { + case DwMime::kCteQuotedPrintable: + case DwMime::kCteBase64: + { + Codec * codec = Codec::codecForName( cteStr() ); + assert( codec ); + // Nice: We can use the convenience function :-) + mBody = codec->encode( aStr ); + break; + } + default: + kdWarning(5006) << "setBodyEncodedBinary: unknown encoding '" << cteStr() + << "'. Assuming binary." << endl; + // fall through + case DwMime::kCte7bit: + case DwMime::kCte8bit: + case DwMime::kCteBinary: + //mBody.duplicate( aStr ); + mBody = aStr; + // Caller has to detach before it modifies aStr! + break; + } +} + +void KMMessagePart::setMessageBody( const QByteArray& aBuf ) +{ + CharFreq cf( aBuf ); // it's safe to pass null arrays + mBodyDecodedSize = aBuf.size(); + + int cte; + switch ( cf.type() ) { + case CharFreq::SevenBitText: + case CharFreq::SevenBitData: + cte = DwMime::kCte7bit; + break; + case CharFreq::EightBitText: + case CharFreq::EightBitData: + cte = DwMime::kCte8bit; + break; + default: + kdWarning(5006) << "Calling " << k_funcinfo + << " with something containing neither 7 nor 8 bit text!" + << " Fix this caller: " << kdBacktrace() << endl; + } + setCte( cte ); + setBodyEncodedBinary( aBuf ); +} + +//----------------------------------------------------------------------------- +QByteArray KMMessagePart::bodyDecodedBinary() const +{ + if (mBody.isEmpty()) return QByteArray(); + QByteArray result; + + switch (cte()) + { + case DwMime::kCte7bit: + case DwMime::kCte8bit: + case DwMime::kCteBinary: + result.duplicate(mBody); + break; + default: + if ( const Codec * codec = Codec::codecForName( cteStr() ) ) + // Nice: we can use the convenience function :-) + result = codec->decode( mBody ); + else { + kdWarning(5006) << "bodyDecodedBinary: unknown encoding '" << cteStr() + << "'. Assuming binary." << endl; + result.duplicate(mBody); + } + } + + assert( mBodyDecodedSize < 0 + || (unsigned int)mBodyDecodedSize == result.size() ); + if ( mBodyDecodedSize < 0 ) + mBodyDecodedSize = result.size(); // cache the decoded size. + + return result; +} + +QCString KMMessagePart::bodyDecoded(void) const +{ + if (mBody.isEmpty()) return QCString(""); + bool decodeBinary = false; + QCString result; + int len; + + switch (cte()) + { + case DwMime::kCte7bit: + case DwMime::kCte8bit: + case DwMime::kCteBinary: + { + decodeBinary = true; + break; + } + default: + if ( const Codec * codec = Codec::codecForName( cteStr() ) ) { + // We can't use the codec convenience functions, since we must + // return a QCString, not a QByteArray: + int bufSize = codec->maxDecodedSizeFor( mBody.size() ) + 1; // trailing NUL + result.resize( bufSize ); + QByteArray::ConstIterator iit = mBody.begin(); + QCString::Iterator oit = result.begin(); + QCString::ConstIterator oend = result.begin() + bufSize; + if ( !codec->decode( iit, mBody.end(), oit, oend ) ) + kdWarning(5006) << codec->name() + << " lies about it's maxDecodedSizeFor( " + << mBody.size() << " ). Result truncated!" << endl; + len = oit - result.begin(); + result.truncate( len ); // adds trailing NUL + } else { + kdWarning(5006) << "bodyDecoded: unknown encoding '" << cteStr() + << "'. Assuming binary." << endl; + decodeBinary = true; + } + } + + if ( decodeBinary ) { + len = mBody.size(); + KMail::Util::setFromByteArray( result, mBody ); + } + + // Calls length -> slow + //kdWarning( result.length() != (unsigned int)len, 5006 ) + // << "KMMessagePart::bodyDecoded(): body is binary but used as text!" << endl; + + result = result.replace( "\r\n", "\n" ); // CRLF -> LF conversion + + assert( mBodyDecodedSize < 0 || mBodyDecodedSize == len ); + if ( mBodyDecodedSize < 0 ) + mBodyDecodedSize = len; // cache decoded size + + return result; +} + + +//----------------------------------------------------------------------------- +void KMMessagePart::magicSetType(bool aAutoDecode) +{ + KMimeMagic::self()->setFollowLinks( true ); // is it necessary ? + + const QByteArray body = ( aAutoDecode ) ? bodyDecodedBinary() : mBody ; + KMimeMagicResult * result = KMimeMagic::self()->findBufferType( body ); + + QString mimetype = result->mimeType(); + const int sep = mimetype.find('/'); + mType = mimetype.left(sep).latin1(); + mSubtype = mimetype.mid(sep+1).latin1(); +} + + +//----------------------------------------------------------------------------- +QString KMMessagePart::iconName( int size ) const +{ + QCString mimeType( mType + "/" + mSubtype ); + KPIM::kAsciiToLower( mimeType.data() ); + + QString fileName = + KMimeType::mimeType( mimeType )->icon( QString::null, false ); + if ( fileName.isEmpty() ) + { + fileName = this->fileName(); + if ( fileName.isEmpty() ) fileName = this->name(); + if ( !fileName.isEmpty() ) + { + fileName = KMimeType::findByPath( "/tmp/"+fileName, 0, true )->icon( QString::null, true ); + } + } + + fileName = + KGlobal::instance()->iconLoader()->iconPath( fileName, size ); + return fileName; +} + + +//----------------------------------------------------------------------------- +int KMMessagePart::type() const { + return DwTypeStrToEnum(DwString(mType)); +} + + +//----------------------------------------------------------------------------- +void KMMessagePart::setType(int aType) +{ + DwString dwType; + DwTypeEnumToStr(aType, dwType); + mType = dwType.c_str(); +} + +//----------------------------------------------------------------------------- +int KMMessagePart::subtype() const { + return DwSubtypeStrToEnum(DwString(mSubtype)); +} + + +//----------------------------------------------------------------------------- +void KMMessagePart::setSubtype(int aSubtype) +{ + DwString dwSubtype; + DwSubtypeEnumToStr(aSubtype, dwSubtype); + mSubtype = dwSubtype.c_str(); +} + +//----------------------------------------------------------------------------- +QCString KMMessagePart::parameterAttribute(void) const +{ + return mParameterAttribute; +} + +//----------------------------------------------------------------------------- +QString KMMessagePart::parameterValue(void) const +{ + return mParameterValue; +} + +//----------------------------------------------------------------------------- +void KMMessagePart::setParameter(const QCString &attribute, + const QString &value) +{ + mParameterAttribute = attribute; + mParameterValue = value; +} + +//----------------------------------------------------------------------------- +QCString KMMessagePart::contentTransferEncodingStr(void) const +{ + return mCte; +} + + +//----------------------------------------------------------------------------- +int KMMessagePart::contentTransferEncoding(void) const +{ + return DwCteStrToEnum(DwString(mCte)); +} + + +//----------------------------------------------------------------------------- +void KMMessagePart::setContentTransferEncodingStr(const QCString &aStr) +{ + mCte = aStr; +} + + +//----------------------------------------------------------------------------- +void KMMessagePart::setContentTransferEncoding(int aCte) +{ + DwString dwCte; + DwCteEnumToStr(aCte, dwCte); + mCte = dwCte.c_str(); + +} + + +//----------------------------------------------------------------------------- +QString KMMessagePart::contentDescription(void) const +{ + return KMMsgBase::decodeRFC2047String(mContentDescription, charset()); +} + + +//----------------------------------------------------------------------------- +void KMMessagePart::setContentDescription(const QString &aStr) +{ + QCString encoding = KMMsgBase::autoDetectCharset(charset(), + KMMessage::preferredCharsets(), aStr); + if (encoding.isEmpty()) encoding = "utf-8"; + mContentDescription = KMMsgBase::encodeRFC2047String(aStr, encoding); +} + + +//----------------------------------------------------------------------------- +QString KMMessagePart::fileName(void) const +{ + QCString str; + + // Allow for multiple filname*0, filename*1, ... params (defined by RFC 2231) + // in the Content-Disposision + if ( mContentDisposition.contains( "filename*", false ) ) { + + // It's RFC 2231 encoded, so extract the file name with the 2231 method + str = KMMsgBase::extractRFC2231HeaderField( mContentDisposition, "filename" ); + return KMMsgBase::decodeRFC2231String(str); + + } else { + + // Standard RFC 2047-encoded + // search the start of the filename + int startOfFilename = mContentDisposition.find("filename=", 0, false); + if (startOfFilename < 0) + return QString::null; + startOfFilename += 9; + + // search the end of the filename + int endOfFilename; + if ( '"' == mContentDisposition[startOfFilename] ) { + startOfFilename++; // the double quote isn't part of the filename + endOfFilename = mContentDisposition.find('"', startOfFilename) - 1; + } + else { + endOfFilename = mContentDisposition.find(';', startOfFilename) - 1; + } + if (endOfFilename < 0) + endOfFilename = 32767; + + const QCString str = mContentDisposition.mid(startOfFilename, + endOfFilename-startOfFilename+1) + .stripWhiteSpace(); + return KMMsgBase::decodeRFC2047String(str, charset()); + } + + return QString::null; +} + +QCString KMMessagePart::body() const +{ + return QCString( mBody.data(), mBody.size() + 1 ); // space for trailing NUL +} + +DwString KMMessagePart::dwBody() const +{ + return KMail::Util::dwString( mBody ); +} |