You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

485 lines
13 KiB

// kmmsgpart.cpp
#include "kmmsgpart.h"
#include "kmkernel.h"
#include "kmmessage.h"
#include "globalsettings.h"
#include <kmime/kmime_charfreq.h>
#include <kmime/kmime_codecs.h>
#include <mimelib/enum.h>
#include <mimelib/utility.h>
#include <mimelib/string.h>
#include <kmimetype.h>
#include <kdebug.h>
#include <kcodecs.h>
#include <kascii.h>
#include <kiconloader.h>
#include <QTextCodec>
#include <QList>
#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)
{
int size;
stream >> mOriginalContentTypeStr >> mName >> mContentDescription
>> mContentDisposition >> mCte >> size >> mPartSpecifier;
kAsciiToLower( mContentDisposition.data() );
kAsciiToUpper( mOriginalContentTypeStr.data() );
// set the type
int sep = mOriginalContentTypeStr.indexOf('/');
mType = mOriginalContentTypeStr.left(sep);
mSubtype = mOriginalContentTypeStr.mid(sep+1);
mBodyDecodedSize = size;
}
//-----------------------------------------------------------------------------
KMMessagePart::~KMMessagePart()
{
}
//-----------------------------------------------------------------------------
void KMMessagePart::clear()
{
mOriginalContentTypeStr = QByteArray();
mType = "text";
mSubtype = "plain";
mCte = "7bit";
mContentDescription = QByteArray();
mContentDisposition = QByteArray();
mBody.truncate( 0 );
mAdditionalCTypeParamStr = QByteArray();
mName.clear();
mParameterAttribute = QByteArray();
mParameterValue.clear();
mCharset = QByteArray();
mPartSpecifier.clear();
mBodyDecodedSize = 0;
mParent = 0;
mLoadHeaders = false;
mLoadPart = false;
}
//-----------------------------------------------------------------------------
void KMMessagePart::duplicate( const KMMessagePart & msgPart )
{
// copy the data of msgPart
*this = msgPart;
// detach the explicitly shared QByteArray
mBody.detach();
}
//-----------------------------------------------------------------------------
int KMMessagePart::decodedSize() const
{
if (mBodyDecodedSize < 0)
mBodyDecodedSize = bodyDecodedBinary().size();
return mBodyDecodedSize;
}
//-----------------------------------------------------------------------------
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 ) {
QByteArray encoding = KMMsgBase::autoDetectCharset( charset(), KMMessage::preferredCharsets(), str );
if ( encoding.isEmpty() )
encoding = "utf-8";
const QTextCodec * codec = KMMsgBase::codecForName( encoding );
assert( codec );
QList<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().toLatin1() );
}
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 QByteArray & c ) {
if ( type() != DwMime::kTypeText )
kWarning(5006)
<< "KMMessagePart::setCharset(): trying to set a charset for a non-textual mimetype." << endl
<< "Fix this caller:" << endl
<< "====================================================================" << endl
<< kBacktrace( 5 ) << endl
<< "====================================================================";
mCharset = c;
}
//-----------------------------------------------------------------------------
void KMMessagePart::setBodyEncoded(const QByteArray& aStr)
{
// Qt4: QCString and QByteArray have been merged; this method can be cleaned up
setBodyEncodedBinary( aStr );
}
void KMMessagePart::setBodyAndGuessCte(const QByteArray& aBuf,
QList<int> & allowedCte,
bool allow8Bit,
bool willBeSigned )
{
mBodyDecodedSize = aBuf.length();
CharFreq cf( aBuf.data(), mBodyDecodedSize ); // save to pass null strings
allowedCte = KMMessage::determineAllowedCtes( cf, allow8Bit, willBeSigned );
#ifndef NDEBUG
DwString dwCte;
DwCteEnumToStr(allowedCte[0], dwCte);
kDebug(5006) <<"CharFreq returned" << cf.type() <<"/"
<< cf.printableRatio() << "and I chose"
<< dwCte.c_str();
#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:
kWarning(5006) <<"setBodyEncodedBinary: unknown encoding '" << cteStr()
<< "'. Assuming binary.";
// fall through
case DwMime::kCte7bit:
case DwMime::kCte8bit:
case DwMime::kCteBinary:
mBody = 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:
kWarning(5006) <<"Calling"
<< "with something containing neither 7 nor 8 bit text!"
<< "Fix this caller:" << kBacktrace();
cte = 0;
}
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 = mBody;
break;
default:
if ( const Codec * codec = Codec::codecForName( cteStr() ) )
// Nice: we can use the convenience function :-)
result = codec->decode( mBody );
else {
kWarning(5006) <<"bodyDecodedBinary: unknown encoding '" << cteStr()
<< "'. Assuming binary.";
result = mBody;
}
}
assert( mBodyDecodedSize < 0 || mBodyDecodedSize == result.size() );
if ( mBodyDecodedSize < 0 )
mBodyDecodedSize = result.size(); // cache the decoded size.
return result;
}
QByteArray KMMessagePart::bodyDecoded(void) const
{
if (mBody.isEmpty()) return QByteArray("");
QByteArray result = bodyDecodedBinary();
//kWarning( qstrlen(result) != len, 5006 )
// << "KMMessagePart::bodyDecoded(): body is binary but used as text!";
result = result.replace( "\r\n", "\n" ); // CRLF -> LF conversion
return result;
}
//-----------------------------------------------------------------------------
void KMMessagePart::magicSetType(bool aAutoDecode)
{
const QByteArray body = ( aAutoDecode ) ? bodyDecodedBinary() : mBody ;
KMimeType::Ptr mime = KMimeType::findByContent( body );
QString mimetype = mime->name();
const int sep = mimetype.indexOf('/');
mType = mimetype.left(sep).toLatin1();
mSubtype = mimetype.mid(sep+1).toLatin1();
}
//-----------------------------------------------------------------------------
QString KMMessagePart::iconName() const
{
QByteArray mimeType( mType + '/' + mSubtype );
kAsciiToLower( mimeType.data() );
QString fileName;
KMimeType::Ptr mime = KMimeType::mimeType( mimeType );
if (mime) {
fileName = mime->iconName();
} else {
kWarning(5006) <<"unknown mimetype" << mimeType;
}
if ( fileName.isEmpty() )
{
fileName = this->fileName();
if ( fileName.isEmpty() ) fileName = this->name();
if ( !fileName.isEmpty() )
{
fileName = KMimeType::findByPath( "/tmp/"+fileName, 0, true )->iconName();
}
}
fileName =
KIconLoader::global()->iconPath( fileName, KIconLoader::Desktop );
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();
}
//-----------------------------------------------------------------------------
QByteArray KMMessagePart::parameterAttribute(void) const
{
return mParameterAttribute;
}
//-----------------------------------------------------------------------------
QString KMMessagePart::parameterValue(void) const
{
return mParameterValue;
}
//-----------------------------------------------------------------------------
void KMMessagePart::setParameter(const QByteArray &attribute,
const QString &value)
{
mParameterAttribute = attribute;
mParameterValue = value;
}
//-----------------------------------------------------------------------------
QByteArray KMMessagePart::contentTransferEncodingStr(void) const
{
return mCte;
}
//-----------------------------------------------------------------------------
int KMMessagePart::contentTransferEncoding(void) const
{
return DwCteStrToEnum(DwString(mCte));
}
//-----------------------------------------------------------------------------
void KMMessagePart::setContentTransferEncodingStr(const QByteArray &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 )
{
QByteArray encoding =
KMMsgBase::autoDetectCharset( charset(),
KMMessage::preferredCharsets(), aStr );
if ( encoding.isEmpty() ) {
encoding = "utf-8";
}
mContentDescription = KMMsgBase::encodeRFC2047String( aStr, encoding );
}
//-----------------------------------------------------------------------------
QString KMMessagePart::fileName( void ) const
{
bool bRFC2231encoded = false;
// search the start of the filename
QString cd( mContentDisposition );
int startOfFilename = cd.indexOf( "filename*=" );
if ( startOfFilename >= 0 ) {
bRFC2231encoded = true;
startOfFilename += 10;
} else {
startOfFilename = cd.indexOf( "filename=" );
if ( startOfFilename < 0 ) {
return QString();
}
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.indexOf( '"', startOfFilename ) - 1;
} else {
endOfFilename = mContentDisposition.indexOf( ';', startOfFilename ) - 1;
}
if ( endOfFilename < 0 ) {
endOfFilename = 32767;
}
const QByteArray str =
mContentDisposition.mid( startOfFilename,
endOfFilename-startOfFilename + 1 ).trimmed();
if ( bRFC2231encoded ) {
return KMMsgBase::decodeRFC2231String( str );
} else {
return KMMsgBase::decodeRFC2047String( str, charset() );
}
}
QByteArray KMMessagePart::body() const
{
return mBody;
}