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