00001
00002
00003
00004
00005 #include <config.h>
00006
00007 #include "partNode.h"
00008
00009
00010 #define ALLOW_GUI 1
00011 #include "kmkernel.h"
00012 #include "kmmessage.h"
00013 #include "mailinglist-magic.h"
00014 #include "messageproperty.h"
00015 using KMail::MessageProperty;
00016 #include "objecttreeparser.h"
00017 using KMail::ObjectTreeParser;
00018 #include "kmfolderindex.h"
00019 #include "undostack.h"
00020 #include "kmversion.h"
00021 #include "headerstrategy.h"
00022 #include "globalsettings.h"
00023 using KMail::HeaderStrategy;
00024 #include "kmaddrbook.h"
00025 #include "kcursorsaver.h"
00026 #include "templateparser.h"
00027
00028 #include <libkpimidentities/identity.h>
00029 #include <libkpimidentities/identitymanager.h>
00030 #include <libemailfunctions/email.h>
00031
00032 #include <kasciistringtools.h>
00033
00034 #include <kpgpblock.h>
00035 #include <kaddrbook.h>
00036
00037 #include <kapplication.h>
00038 #include <kglobalsettings.h>
00039 #include <kdebug.h>
00040 #include <kconfig.h>
00041 #include <khtml_part.h>
00042 #include <kuser.h>
00043 #include <kidna.h>
00044 #include <kasciistricmp.h>
00045
00046 #include <qcursor.h>
00047 #include <qtextcodec.h>
00048 #include <qmessagebox.h>
00049 #include <kmime_util.h>
00050 #include <kmime_charfreq.h>
00051
00052 #include <kmime_header_parsing.h>
00053 using KMime::HeaderParsing::parseAddressList;
00054 using namespace KMime::Types;
00055
00056 #include <mimelib/body.h>
00057 #include <mimelib/field.h>
00058 #include <mimelib/mimepp.h>
00059 #include <mimelib/string.h>
00060 #include <assert.h>
00061 #include <sys/time.h>
00062 #include <time.h>
00063 #include <klocale.h>
00064 #include <stdlib.h>
00065 #include <unistd.h>
00066 #include "util.h"
00067
00068 #if ALLOW_GUI
00069 #include <kmessagebox.h>
00070 #endif
00071
00072 using namespace KMime;
00073
00074 static DwString emptyString("");
00075
00076
00077 static QString sReplyLanguage, sReplyStr, sReplyAllStr, sIndentPrefixStr;
00078 static bool sSmartQuote,
00079 sWordWrap;
00080 static int sWrapCol;
00081 static QStringList sPrefCharsets;
00082
00083 QString KMMessage::sForwardStr;
00084 const HeaderStrategy * KMMessage::sHeaderStrategy = HeaderStrategy::rich();
00085
00086 static void applyHeadersToMessagePart( DwHeaders& headers, KMMessagePart* aPart );
00087
00088 QValueList<KMMessage*> KMMessage::sPendingDeletes;
00089
00090
00091 KMMessage::KMMessage(DwMessage* aMsg)
00092 : KMMsgBase()
00093 {
00094 init( aMsg );
00095
00096 mNeedsAssembly = true;
00097 }
00098
00099
00100 KMMessage::KMMessage(KMFolder* parent): KMMsgBase(parent)
00101 {
00102 init();
00103 }
00104
00105
00106
00107 KMMessage::KMMessage(KMMsgInfo& msgInfo): KMMsgBase()
00108 {
00109 init();
00110
00111 mMsgSize = msgInfo.msgSize();
00112 mFolderOffset = msgInfo.folderOffset();
00113 mStatus = msgInfo.status();
00114 mEncryptionState = msgInfo.encryptionState();
00115 mSignatureState = msgInfo.signatureState();
00116 mMDNSentState = msgInfo.mdnSentState();
00117 mDate = msgInfo.date();
00118 mFileName = msgInfo.fileName();
00119 KMMsgBase::assign(&msgInfo);
00120 }
00121
00122
00123
00124 KMMessage::KMMessage(const KMMessage& other) :
00125 KMMsgBase( other ),
00126 ISubject(),
00127 mMsg(0)
00128 {
00129 init();
00130 assign( other );
00131 }
00132
00133 void KMMessage::init( DwMessage* aMsg )
00134 {
00135 mNeedsAssembly = false;
00136 if ( aMsg ) {
00137 mMsg = aMsg;
00138 } else {
00139 mMsg = new DwMessage;
00140 }
00141 mOverrideCodec = 0;
00142 mDecodeHTML = false;
00143 mComplete = true;
00144 mReadyToShow = true;
00145 mMsgSize = 0;
00146 mMsgLength = 0;
00147 mFolderOffset = 0;
00148 mStatus = KMMsgStatusNew;
00149 mEncryptionState = KMMsgEncryptionStateUnknown;
00150 mSignatureState = KMMsgSignatureStateUnknown;
00151 mMDNSentState = KMMsgMDNStateUnknown;
00152 mDate = 0;
00153 mUnencryptedMsg = 0;
00154 mLastUpdated = 0;
00155 mCursorPos = 0;
00156 mMsgInfo = 0;
00157 mIsParsed = false;
00158 }
00159
00160 void KMMessage::assign( const KMMessage& other )
00161 {
00162 MessageProperty::forget( this );
00163 delete mMsg;
00164 delete mUnencryptedMsg;
00165
00166 mNeedsAssembly = true;
00167 if( other.mMsg )
00168 mMsg = new DwMessage( *(other.mMsg) );
00169 else
00170 mMsg = 0;
00171 mOverrideCodec = other.mOverrideCodec;
00172 mDecodeHTML = other.mDecodeHTML;
00173 mMsgSize = other.mMsgSize;
00174 mMsgLength = other.mMsgLength;
00175 mFolderOffset = other.mFolderOffset;
00176 mStatus = other.mStatus;
00177 mEncryptionState = other.mEncryptionState;
00178 mSignatureState = other.mSignatureState;
00179 mMDNSentState = other.mMDNSentState;
00180 mIsParsed = other.mIsParsed;
00181 mDate = other.mDate;
00182 if( other.hasUnencryptedMsg() )
00183 mUnencryptedMsg = new KMMessage( *other.unencryptedMsg() );
00184 else
00185 mUnencryptedMsg = 0;
00186 setDrafts( other.drafts() );
00187 setTemplates( other.templates() );
00188
00189
00190 }
00191
00192
00193 KMMessage::~KMMessage()
00194 {
00195 delete mMsgInfo;
00196 delete mMsg;
00197 kmkernel->undoStack()->msgDestroyed( this );
00198 }
00199
00200
00201
00202 void KMMessage::setReferences(const QCString& aStr)
00203 {
00204 if (!aStr) return;
00205 mMsg->Headers().References().FromString(aStr);
00206 mNeedsAssembly = true;
00207 }
00208
00209
00210
00211 QCString KMMessage::id() const
00212 {
00213 DwHeaders& header = mMsg->Headers();
00214 if (header.HasMessageId())
00215 return KMail::Util::CString( header.MessageId().AsString() );
00216 else
00217 return "";
00218 }
00219
00220
00221
00222
00223
00224
00225
00226 void KMMessage::setMsgSerNum(unsigned long newMsgSerNum)
00227 {
00228 MessageProperty::setSerialCache( this, newMsgSerNum );
00229 }
00230
00231
00232
00233 bool KMMessage::isMessage() const
00234 {
00235 return true;
00236 }
00237
00238
00239 bool KMMessage::transferInProgress() const
00240 {
00241 return MessageProperty::transferInProgress( getMsgSerNum() );
00242 }
00243
00244
00245
00246 void KMMessage::setTransferInProgress(bool value, bool force)
00247 {
00248 MessageProperty::setTransferInProgress( getMsgSerNum(), value, force );
00249 if ( !transferInProgress() && sPendingDeletes.contains( this ) ) {
00250 sPendingDeletes.remove( this );
00251 if ( parent() ) {
00252 int idx = parent()->find( this );
00253 if ( idx > 0 ) {
00254 parent()->removeMsg( idx );
00255 }
00256 }
00257 }
00258 }
00259
00260
00261
00262 bool KMMessage::isUrgent() const {
00263 return headerField( "Priority" ).contains( "urgent", false )
00264 || headerField( "X-Priority" ).startsWith( "2" );
00265 }
00266
00267
00268 void KMMessage::setUnencryptedMsg( KMMessage* unencrypted )
00269 {
00270 delete mUnencryptedMsg;
00271 mUnencryptedMsg = unencrypted;
00272 }
00273
00274
00275
00276 KPIM::EmailParseResult KMMessage::isValidEmailAddressList( const QString& aStr,
00277 QString& brokenAddress )
00278 {
00279 if ( aStr.isEmpty() ) {
00280 return KPIM::AddressEmpty;
00281 }
00282
00283 QStringList list = KPIM::splitEmailAddrList( aStr );
00284 for( QStringList::const_iterator it = list.begin(); it != list.end(); ++it ) {
00285 KPIM::EmailParseResult errorCode = KPIM::isValidEmailAddress( *it );
00286 if ( errorCode != KPIM::AddressOk ) {
00287 brokenAddress = ( *it );
00288 return errorCode;
00289 }
00290 }
00291 return KPIM::AddressOk;
00292 }
00293
00294
00295 const DwString& KMMessage::asDwString() const
00296 {
00297 if (mNeedsAssembly)
00298 {
00299 mNeedsAssembly = false;
00300 mMsg->Assemble();
00301 }
00302 return mMsg->AsString();
00303 }
00304
00305
00306 const DwMessage* KMMessage::asDwMessage()
00307 {
00308 if (mNeedsAssembly)
00309 {
00310 mNeedsAssembly = false;
00311 mMsg->Assemble();
00312 }
00313 return mMsg;
00314 }
00315
00316
00317 QCString KMMessage::asString() const {
00318 return KMail::Util::CString( asDwString() );
00319 }
00320
00321
00322 QByteArray KMMessage::asSendableString() const
00323 {
00324 KMMessage msg( new DwMessage( *this->mMsg ) );
00325 msg.removePrivateHeaderFields();
00326 msg.removeHeaderField("Bcc");
00327 return KMail::Util::ByteArray( msg.asDwString() );
00328 }
00329
00330 QCString KMMessage::headerAsSendableString() const
00331 {
00332 KMMessage msg( new DwMessage( *this->mMsg ) );
00333 msg.removePrivateHeaderFields();
00334 msg.removeHeaderField("Bcc");
00335 return msg.headerAsString().latin1();
00336 }
00337
00338 void KMMessage::removePrivateHeaderFields() {
00339 removeHeaderField("Status");
00340 removeHeaderField("X-Status");
00341 removeHeaderField("X-KMail-EncryptionState");
00342 removeHeaderField("X-KMail-SignatureState");
00343 removeHeaderField("X-KMail-MDN-Sent");
00344 removeHeaderField("X-KMail-Transport");
00345 removeHeaderField("X-KMail-Identity");
00346 removeHeaderField("X-KMail-Fcc");
00347 removeHeaderField("X-KMail-Redirect-From");
00348 removeHeaderField("X-KMail-Link-Message");
00349 removeHeaderField("X-KMail-Link-Type");
00350 removeHeaderField( "X-KMail-Markup" );
00351 }
00352
00353
00354 void KMMessage::setStatusFields()
00355 {
00356 char str[2] = { 0, 0 };
00357
00358 setHeaderField("Status", status() & KMMsgStatusNew ? "R" : "RO");
00359 setHeaderField("X-Status", statusToStr(status()));
00360
00361 str[0] = (char)encryptionState();
00362 setHeaderField("X-KMail-EncryptionState", str);
00363
00364 str[0] = (char)signatureState();
00365
00366 setHeaderField("X-KMail-SignatureState", str);
00367
00368 str[0] = static_cast<char>( mdnSentState() );
00369 setHeaderField("X-KMail-MDN-Sent", str);
00370
00371
00372
00373 mNeedsAssembly = false;
00374 mMsg->Headers().Assemble();
00375 mMsg->Assemble( mMsg->Headers(),
00376 mMsg->Body() );
00377 }
00378
00379
00380
00381 QString KMMessage::headerAsString() const
00382 {
00383 DwHeaders& header = mMsg->Headers();
00384 header.Assemble();
00385 if ( header.AsString().empty() )
00386 return QString::null;
00387 return QString::fromLatin1( header.AsString().c_str() );
00388 }
00389
00390
00391
00392 DwMediaType& KMMessage::dwContentType()
00393 {
00394 return mMsg->Headers().ContentType();
00395 }
00396
00397 void KMMessage::fromByteArray( const QByteArray & ba, bool setStatus ) {
00398 return fromDwString( DwString( ba.data(), ba.size() ), setStatus );
00399 }
00400
00401 void KMMessage::fromString( const QCString & str, bool aSetStatus ) {
00402 return fromDwString( KMail::Util::dwString( str ), aSetStatus );
00403 }
00404
00405 void KMMessage::fromDwString(const DwString& str, bool aSetStatus)
00406 {
00407 delete mMsg;
00408 mMsg = new DwMessage;
00409 mMsg->FromString( str );
00410 mMsg->Parse();
00411
00412 if (aSetStatus) {
00413 setStatus(headerField("Status").latin1(), headerField("X-Status").latin1());
00414 setEncryptionStateChar( headerField("X-KMail-EncryptionState").at(0) );
00415 setSignatureStateChar( headerField("X-KMail-SignatureState").at(0) );
00416 setMDNSentState( static_cast<KMMsgMDNSentState>( headerField("X-KMail-MDN-Sent").at(0).latin1() ) );
00417 }
00418 if (attachmentState() == KMMsgAttachmentUnknown && readyToShow())
00419 updateAttachmentState();
00420
00421 mNeedsAssembly = false;
00422 mDate = date();
00423 }
00424
00425
00426
00427 QString KMMessage::formatString(const QString& aStr) const
00428 {
00429 QString result, str;
00430 QChar ch;
00431 uint j;
00432
00433 if (aStr.isEmpty())
00434 return aStr;
00435
00436 unsigned int strLength(aStr.length());
00437 for (uint i=0; i<strLength;) {
00438 ch = aStr[i++];
00439 if (ch == '%') {
00440 ch = aStr[i++];
00441 switch ((char)ch) {
00442 case 'D':
00443
00444
00445
00446
00447 result += KMime::DateFormatter::formatDate( KMime::DateFormatter::Localized,
00448 date(), sReplyLanguage, false );
00449 break;
00450 case 'e':
00451 result += from();
00452 break;
00453 case 'F':
00454 result += fromStrip();
00455 break;
00456 case 'f':
00457 {
00458 str = fromStrip();
00459
00460 for (j=0; str[j]>' '; j++)
00461 ;
00462 unsigned int strLength(str.length());
00463 for (; j < strLength && str[j] <= ' '; j++)
00464 ;
00465 result += str[0];
00466 if (str[j]>' ')
00467 result += str[j];
00468 else
00469 if (str[1]>' ')
00470 result += str[1];
00471 }
00472 break;
00473 case 'T':
00474 result += toStrip();
00475 break;
00476 case 't':
00477 result += to();
00478 break;
00479 case 'C':
00480 result += ccStrip();
00481 break;
00482 case 'c':
00483 result += cc();
00484 break;
00485 case 'S':
00486 result += subject();
00487 break;
00488 case '_':
00489 result += ' ';
00490 break;
00491 case 'L':
00492 result += "\n";
00493 break;
00494 case '%':
00495 result += '%';
00496 break;
00497 default:
00498 result += '%';
00499 result += ch;
00500 break;
00501 }
00502 } else
00503 result += ch;
00504 }
00505 return result;
00506 }
00507
00508 static void removeTrailingSpace( QString &line )
00509 {
00510 int i = line.length()-1;
00511 while( (i >= 0) && ((line[i] == ' ') || (line[i] == '\t')))
00512 i--;
00513 line.truncate( i+1);
00514 }
00515
00516 static QString splitLine( QString &line)
00517 {
00518 removeTrailingSpace( line );
00519 int i = 0;
00520 int j = -1;
00521 int l = line.length();
00522
00523
00524
00525 while(i < l)
00526 {
00527 QChar c = line[i];
00528 if ((c == '>') || (c == ':') || (c == '|'))
00529 j = i+1;
00530 else if ((c != ' ') && (c != '\t'))
00531 break;
00532 i++;
00533 }
00534
00535 if ( j <= 0 )
00536 {
00537 return "";
00538 }
00539 if ( i == l )
00540 {
00541 QString result = line.left(j);
00542 line = QString::null;
00543 return result;
00544 }
00545
00546 QString result = line.left(j);
00547 line = line.mid(j);
00548 return result;
00549 }
00550
00551 static QString flowText(QString &text, const QString& indent, int maxLength)
00552 {
00553 maxLength--;
00554 if (text.isEmpty())
00555 {
00556 return indent+"<NULL>\n";
00557 }
00558 QString result;
00559 while (1)
00560 {
00561 int i;
00562 if ((int) text.length() > maxLength)
00563 {
00564 i = maxLength;
00565 while( (i >= 0) && (text[i] != ' '))
00566 i--;
00567 if (i <= 0)
00568 {
00569
00570 i = maxLength;
00571
00572
00573 }
00574 }
00575 else
00576 {
00577 i = text.length();
00578 }
00579
00580 QString line = text.left(i);
00581 if (i < (int) text.length())
00582 text = text.mid(i);
00583 else
00584 text = QString::null;
00585
00586 result += indent + line + '\n';
00587
00588 if (text.isEmpty())
00589 return result;
00590 }
00591 }
00592
00593 static bool flushPart(QString &msg, QStringList &part,
00594 const QString &indent, int maxLength)
00595 {
00596 maxLength -= indent.length();
00597 if (maxLength < 20) maxLength = 20;
00598
00599
00600 while ((part.begin() != part.end()) && part.last().isEmpty())
00601 {
00602 part.remove(part.fromLast());
00603 }
00604
00605 QString text;
00606 for(QStringList::Iterator it2 = part.begin();
00607 it2 != part.end();
00608 it2++)
00609 {
00610 QString line = (*it2);
00611
00612 if (line.isEmpty())
00613 {
00614 if (!text.isEmpty())
00615 msg += flowText(text, indent, maxLength);
00616 msg += indent + '\n';
00617 }
00618 else
00619 {
00620 if (text.isEmpty())
00621 text = line;
00622 else
00623 text += ' '+line.stripWhiteSpace();
00624
00625 if (((int) text.length() < maxLength) || ((int) line.length() < (maxLength-10)))
00626 msg += flowText(text, indent, maxLength);
00627 }
00628 }
00629 if (!text.isEmpty())
00630 msg += flowText(text, indent, maxLength);
00631
00632 bool appendEmptyLine = true;
00633 if (!part.count())
00634 appendEmptyLine = false;
00635
00636 part.clear();
00637 return appendEmptyLine;
00638 }
00639
00640 static QString stripSignature( const QString & msg, bool clearSigned ) {
00641 if ( clearSigned )
00642 return msg.left( msg.findRev( QRegExp( "\n--\\s?\n" ) ) );
00643 else
00644 return msg.left( msg.findRev( "\n-- \n" ) );
00645 }
00646
00647 QString KMMessage::smartQuote( const QString & msg, int maxLineLength )
00648 {
00649 QStringList part;
00650 QString oldIndent;
00651 bool firstPart = true;
00652
00653
00654 const QStringList lines = QStringList::split('\n', msg, true);
00655
00656 QString result;
00657 for(QStringList::const_iterator it = lines.begin();
00658 it != lines.end();
00659 ++it)
00660 {
00661 QString line = *it;
00662
00663 const QString indent = splitLine( line );
00664
00665 if ( line.isEmpty())
00666 {
00667 if (!firstPart)
00668 part.append(QString::null);
00669 continue;
00670 };
00671
00672 if (firstPart)
00673 {
00674 oldIndent = indent;
00675 firstPart = false;
00676 }
00677
00678 if (oldIndent != indent)
00679 {
00680 QString fromLine;
00681
00682 if (part.count() && (oldIndent.length() < indent.length()))
00683 {
00684 QStringList::Iterator it2 = part.fromLast();
00685 while( (it2 != part.end()) && (*it2).isEmpty())
00686 --it2;
00687
00688 if ((it2 != part.end()) && ((*it2).endsWith(":")))
00689 {
00690 fromLine = oldIndent + (*it2) + '\n';
00691 part.remove(it2);
00692 }
00693 }
00694 if (flushPart( result, part, oldIndent, maxLineLength))
00695 {
00696 if (oldIndent.length() > indent.length())
00697 result += indent + '\n';
00698 else
00699 result += oldIndent + '\n';
00700 }
00701 if (!fromLine.isEmpty())
00702 {
00703 result += fromLine;
00704 }
00705 oldIndent = indent;
00706 }
00707 part.append(line);
00708 }
00709 flushPart( result, part, oldIndent, maxLineLength);
00710 return result;
00711 }
00712
00713
00714
00715 void KMMessage::parseTextStringFromDwPart( partNode * root,
00716 QCString& parsedString,
00717 const QTextCodec*& codec,
00718 bool& isHTML ) const
00719 {
00720 if ( !root ) return;
00721
00722 isHTML = false;
00723 partNode * curNode = root->findType( DwMime::kTypeText,
00724 DwMime::kSubtypeUnknown,
00725 true,
00726 false );
00727 kdDebug(5006) << "\n\n======= KMMessage::parseTextStringFromDwPart() - "
00728 << ( curNode ? "text part found!\n" : "sorry, no text node!\n" ) << endl;
00729 if( curNode ) {
00730 isHTML = DwMime::kSubtypeHtml == curNode->subType();
00731
00732 ObjectTreeParser otp( 0, 0, true, false, true );
00733 otp.parseObjectTree( curNode );
00734 parsedString = otp.rawReplyString();
00735 codec = curNode->msgPart().codec();
00736 }
00737 }
00738
00739
00740
00741 QString KMMessage::asPlainTextFromObjectTree( partNode *root, bool aStripSignature,
00742 bool allowDecryption ) const
00743 {
00744 Q_ASSERT( root );
00745 Q_ASSERT( root->processed() );
00746
00747 QCString parsedString;
00748 bool isHTML = false;
00749 const QTextCodec * codec = 0;
00750
00751 if ( !root ) return QString::null;
00752 parseTextStringFromDwPart( root, parsedString, codec, isHTML );
00753
00754 if ( mOverrideCodec || !codec )
00755 codec = this->codec();
00756
00757 if ( parsedString.isEmpty() )
00758 return QString::null;
00759
00760 bool clearSigned = false;
00761 QString result;
00762
00763
00764 if ( allowDecryption ) {
00765 QPtrList<Kpgp::Block> pgpBlocks;
00766 QStrList nonPgpBlocks;
00767 if ( Kpgp::Module::prepareMessageForDecryption( parsedString,
00768 pgpBlocks,
00769 nonPgpBlocks ) ) {
00770
00771
00772 if ( pgpBlocks.count() == 1 ) {
00773 Kpgp::Block * block = pgpBlocks.first();
00774 if ( block->type() == Kpgp::PgpMessageBlock ||
00775 block->type() == Kpgp::ClearsignedBlock ) {
00776 if ( block->type() == Kpgp::PgpMessageBlock ) {
00777
00778 block->decrypt();
00779 } else {
00780
00781 block->verify();
00782 clearSigned = true;
00783 }
00784
00785 result = codec->toUnicode( nonPgpBlocks.first() )
00786 + codec->toUnicode( block->text() )
00787 + codec->toUnicode( nonPgpBlocks.last() );
00788 }
00789 }
00790 }
00791 }
00792
00793 if ( result.isEmpty() ) {
00794 result = codec->toUnicode( parsedString );
00795 if ( result.isEmpty() )
00796 return result;
00797 }
00798
00799
00800 if ( isHTML && mDecodeHTML ) {
00801 KHTMLPart htmlPart;
00802 htmlPart.setOnlyLocalReferences( true );
00803 htmlPart.setMetaRefreshEnabled( false );
00804 htmlPart.setPluginsEnabled( false );
00805 htmlPart.setJScriptEnabled( false );
00806 htmlPart.setJavaEnabled( false );
00807 htmlPart.begin();
00808 htmlPart.write( result );
00809 htmlPart.end();
00810 htmlPart.selectAll();
00811 result = htmlPart.selectedText();
00812 }
00813
00814
00815 if ( aStripSignature )
00816 return stripSignature( result, clearSigned );
00817 else
00818 return result;
00819 }
00820
00821
00822
00823 QString KMMessage::asPlainText( bool aStripSignature, bool allowDecryption ) const
00824 {
00825 partNode *root = partNode::fromMessage( this );
00826 if ( !root )
00827 return QString::null;
00828
00829 ObjectTreeParser otp;
00830 otp.parseObjectTree( root );
00831 QString result = asPlainTextFromObjectTree( root, aStripSignature, allowDecryption );
00832 delete root;
00833 return result;
00834 }
00835
00836 QString KMMessage::asQuotedString( const QString& aHeaderStr,
00837 const QString& aIndentStr,
00838 const QString& selection ,
00839 bool aStripSignature ,
00840 bool allowDecryption ) const
00841 {
00842 QString content = selection.isEmpty() ?
00843 asPlainText( aStripSignature, allowDecryption ) : selection ;
00844
00845
00846 const int firstNonWS = content.find( QRegExp( "\\S" ) );
00847 const int lineStart = content.findRev( '\n', firstNonWS );
00848 if ( lineStart >= 0 )
00849 content.remove( 0, static_cast<unsigned int>( lineStart ) );
00850
00851 const QString indentStr = formatString( aIndentStr );
00852
00853 content.replace( '\n', '\n' + indentStr );
00854 content.prepend( indentStr );
00855 content += '\n';
00856
00857 const QString headerStr = formatString( aHeaderStr );
00858 if ( sSmartQuote && sWordWrap )
00859 return headerStr + smartQuote( content, sWrapCol );
00860 return headerStr + content;
00861 }
00862
00863
00864 KMMessage* KMMessage::createReply( KMail::ReplyStrategy replyStrategy,
00865 QString selection ,
00866 bool noQuote ,
00867 bool allowDecryption ,
00868 bool selectionIsBody ,
00869 const QString &tmpl )
00870 {
00871 KMMessage* msg = new KMMessage;
00872 QString mailingListStr, replyToStr, toStr;
00873 QStringList mailingListAddresses;
00874 QCString refStr, headerName;
00875 bool replyAll = true;
00876
00877 msg->initFromMessage(this);
00878
00879 MailingList::name(this, headerName, mailingListStr);
00880 replyToStr = replyTo();
00881
00882 msg->setCharset("utf-8");
00883
00884
00885 if ( parent() && parent()->isMailingListEnabled() &&
00886 !parent()->mailingListPostAddress().isEmpty() ) {
00887 mailingListAddresses << parent()->mailingListPostAddress();
00888 }
00889 if ( headerField("List-Post").find( "mailto:", 0, false ) != -1 ) {
00890 QString listPost = headerField("List-Post");
00891 QRegExp rx( "<mailto:([^@>]+)@([^>]+)>", false );
00892 if ( rx.search( listPost, 0 ) != -1 )
00893 mailingListAddresses << rx.cap(1) + '@' + rx.cap(2);
00894 }
00895
00896
00897 switch( replyStrategy ) {
00898 case KMail::ReplySmart : {
00899 if ( !headerField( "Mail-Followup-To" ).isEmpty() ) {
00900 toStr = headerField( "Mail-Followup-To" );
00901 }
00902 else if ( !replyToStr.isEmpty() ) {
00903
00904 toStr = replyToStr;
00905 }
00906 else if ( !mailingListAddresses.isEmpty() ) {
00907 toStr = mailingListAddresses[0];
00908 }
00909 else {
00910
00911 toStr = from();
00912
00913 replyAll = false;
00914 }
00915
00916 QStringList recipients = KPIM::splitEmailAddrList( toStr );
00917 toStr = stripMyAddressesFromAddressList( recipients ).join(", ");
00918
00919 if ( toStr.isEmpty() && !recipients.isEmpty() )
00920 toStr = recipients[0];
00921
00922 break;
00923 }
00924 case KMail::ReplyList : {
00925 if ( !headerField( "Mail-Followup-To" ).isEmpty() ) {
00926 toStr = headerField( "Mail-Followup-To" );
00927 }
00928 else if ( !mailingListAddresses.isEmpty() ) {
00929 toStr = mailingListAddresses[0];
00930 }
00931 else if ( !replyToStr.isEmpty() ) {
00932
00933 toStr = replyToStr;
00934 }
00935
00936 QStringList recipients = KPIM::splitEmailAddrList( toStr );
00937 toStr = stripMyAddressesFromAddressList( recipients ).join(", ");
00938
00939 break;
00940 }
00941 case KMail::ReplyAll : {
00942 QStringList recipients;
00943 QStringList ccRecipients;
00944
00945
00946 if( !replyToStr.isEmpty() ) {
00947 recipients += KPIM::splitEmailAddrList( replyToStr );
00948
00949
00950 for ( QStringList::const_iterator it = mailingListAddresses.begin();
00951 it != mailingListAddresses.end();
00952 ++it ) {
00953 recipients = stripAddressFromAddressList( *it, recipients );
00954 }
00955 }
00956
00957 if ( !mailingListAddresses.isEmpty() ) {
00958
00959 if ( recipients.isEmpty() && !from().isEmpty() ) {
00960
00961
00962 ccRecipients += from();
00963 kdDebug(5006) << "Added " << from() << " to the list of CC recipients"
00964 << endl;
00965 }
00966
00967 recipients.prepend( mailingListAddresses[0] );
00968 }
00969 else {
00970
00971 if ( recipients.isEmpty() && !from().isEmpty() ) {
00972
00973
00974 recipients += from();
00975 kdDebug(5006) << "Added " << from() << " to the list of recipients"
00976 << endl;
00977 }
00978 }
00979
00980
00981 toStr = stripMyAddressesFromAddressList( recipients ).join(", ");
00982
00983
00984 if( !cc().isEmpty() || !to().isEmpty() ) {
00985 QStringList list;
00986 if (!to().isEmpty())
00987 list += KPIM::splitEmailAddrList(to());
00988 if (!cc().isEmpty())
00989 list += KPIM::splitEmailAddrList(cc());
00990 for( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
00991 if( !addressIsInAddressList( *it, recipients )
00992 && !addressIsInAddressList( *it, ccRecipients ) ) {
00993 ccRecipients += *it;
00994 kdDebug(5006) << "Added " << *it << " to the list of CC recipients"
00995 << endl;
00996 }
00997 }
00998 }
00999
01000 if ( !ccRecipients.isEmpty() ) {
01001
01002 ccRecipients = stripMyAddressesFromAddressList( ccRecipients );
01003
01004
01005
01006 if ( toStr.isEmpty() && !ccRecipients.isEmpty() ) {
01007 toStr = ccRecipients[0];
01008 ccRecipients.pop_front();
01009 }
01010
01011 msg->setCc( ccRecipients.join(", ") );
01012 }
01013
01014 if ( toStr.isEmpty() && !recipients.isEmpty() ) {
01015
01016 toStr = recipients[0];
01017 }
01018 break;
01019 }
01020 case KMail::ReplyAuthor : {
01021 if ( !replyToStr.isEmpty() ) {
01022 QStringList recipients = KPIM::splitEmailAddrList( replyToStr );
01023
01024
01025 for ( QStringList::const_iterator it = mailingListAddresses.begin();
01026 it != mailingListAddresses.end();
01027 ++it ) {
01028 recipients = stripAddressFromAddressList( *it, recipients );
01029 }
01030 if ( !recipients.isEmpty() ) {
01031 toStr = recipients.join(", ");
01032 }
01033 else {
01034
01035
01036 toStr = from();
01037 }
01038 }
01039 else if ( !from().isEmpty() ) {
01040 toStr = from();
01041 }
01042 replyAll = false;
01043 break;
01044 }
01045 case KMail::ReplyNone : {
01046
01047 }
01048 }
01049
01050 msg->setTo(toStr);
01051
01052 refStr = getRefStr();
01053 if (!refStr.isEmpty())
01054 msg->setReferences(refStr);
01055
01056 msg->setReplyToId(msgId());
01057
01058
01059
01060
01061
01062
01063
01064
01065
01066
01067
01068 msg->setSubject( replySubject() );
01069 msg->setHeaderField( "X-KMail-QuotePrefix",
01070 formatString( GlobalSettings::self()->quoteString() ) );
01071 if( !noQuote ) {
01072 TemplateParser parser( msg, (replyAll ? TemplateParser::ReplyAll : TemplateParser::Reply),
01073 selection, sSmartQuote, noQuote, allowDecryption, selectionIsBody );
01074 if ( !tmpl.isEmpty() ) {
01075 parser.process( tmpl, this );
01076 } else {
01077 parser.process( this );
01078 }
01079 }
01080
01081 msg->link(this, KMMsgStatusReplied);
01082
01083 if ( parent() && parent()->putRepliesInSameFolder() )
01084 msg->setFcc( parent()->idString() );
01085
01086
01087 if ( encryptionState() == KMMsgPartiallyEncrypted ||
01088 encryptionState() == KMMsgFullyEncrypted ) {
01089 msg->setEncryptionState( KMMsgFullyEncrypted );
01090 }
01091
01092 return msg;
01093 }
01094
01095
01096
01097 QCString KMMessage::getRefStr() const
01098 {
01099 QCString firstRef, lastRef, refStr, retRefStr;
01100 int i, j;
01101
01102 refStr = headerField("References").stripWhiteSpace().latin1();
01103
01104 if (refStr.isEmpty())
01105 return headerField("Message-Id").latin1();
01106
01107 i = refStr.find('<');
01108 j = refStr.find('>');
01109 firstRef = refStr.mid(i, j-i+1);
01110 if (!firstRef.isEmpty())
01111 retRefStr = firstRef + ' ';
01112
01113 i = refStr.findRev('<');
01114 j = refStr.findRev('>');
01115
01116 lastRef = refStr.mid(i, j-i+1);
01117 if (!lastRef.isEmpty() && lastRef != firstRef)
01118 retRefStr += lastRef + ' ';
01119
01120 retRefStr += headerField("Message-Id").latin1();
01121 return retRefStr;
01122 }
01123
01124
01125 KMMessage* KMMessage::createRedirect( const QString &toStr )
01126 {
01127
01128 KMMessage* msg = new KMMessage( new DwMessage( *this->mMsg ) );
01129 KMMessagePart msgPart;
01130
01131 uint id = 0;
01132 QString strId = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace();
01133 if ( !strId.isEmpty())
01134 id = strId.toUInt();
01135 const KPIM::Identity & ident =
01136 kmkernel->identityManager()->identityForUoidOrDefault( id );
01137
01138
01139 QString strByWayOf = QString("%1 (by way of %2 <%3>)")
01140 .arg( from() )
01141 .arg( ident.fullName() )
01142 .arg( ident.emailAddr() );
01143
01144
01145 QString strFrom = QString("%1 <%2>")
01146 .arg( ident.fullName() )
01147 .arg( ident.emailAddr() );
01148
01149
01150 QString origDate = msg->headerField( "Date" );
01151 msg->setDateToday();
01152 QString newDate = msg->headerField( "Date" );
01153
01154 if ( origDate.isEmpty() )
01155 msg->removeHeaderField( "Date" );
01156 else
01157 msg->setHeaderField( "Date", origDate );
01158
01159
01160 msg->setHeaderField( "Resent-Message-ID", generateMessageId( msg->sender() ),
01161 Structured, true);
01162 msg->setHeaderField( "Resent-Date", newDate, Structured, true );
01163 msg->setHeaderField( "Resent-To", toStr, Address, true );
01164 msg->setHeaderField( "Resent-From", strFrom, Address, true );
01165
01166 msg->setHeaderField( "X-KMail-Redirect-From", strByWayOf );
01167 msg->setHeaderField( "X-KMail-Recipients", toStr, Address );
01168
01169 msg->link(this, KMMsgStatusForwarded);
01170
01171 return msg;
01172 }
01173
01174
01175
01176 QCString KMMessage::createForwardBody()
01177 {
01178 QString s;
01179 QCString str;
01180
01181 if (sHeaderStrategy == HeaderStrategy::all()) {
01182 s = "\n\n---------- " + sForwardStr + " ----------\n\n";
01183 s += headerAsString();
01184 str = asQuotedString(s, "", QString::null, false, false).utf8();
01185 str += "\n-------------------------------------------------------\n";
01186 } else {
01187 s = "\n\n---------- " + sForwardStr + " ----------\n\n";
01188 s += "Subject: " + subject() + "\n";
01189 s += "Date: "
01190 + KMime::DateFormatter::formatDate( KMime::DateFormatter::Localized,
01191 date(), sReplyLanguage, false )
01192 + "\n";
01193 s += "From: " + from() + "\n";
01194 s += "To: " + to() + "\n";
01195 if (!cc().isEmpty()) s += "Cc: " + cc() + "\n";
01196 s += "\n";
01197 str = asQuotedString(s, "", QString::null, false, false).utf8();
01198 str += "\n-------------------------------------------------------\n";
01199 }
01200
01201 return str;
01202 }
01203
01204 void KMMessage::sanitizeHeaders( const QStringList& whiteList )
01205 {
01206
01207
01208 DwHeaders& header = mMsg->Headers();
01209 DwField* field = header.FirstField();
01210 DwField* nextField;
01211 while (field)
01212 {
01213 nextField = field->Next();
01214 if ( field->FieldNameStr().find( "ontent" ) == DwString::npos
01215 && !whiteList.contains( QString::fromLatin1( field->FieldNameStr().c_str() ) ) )
01216 header.RemoveField(field);
01217 field = nextField;
01218 }
01219 mMsg->Assemble();
01220 }
01221
01222
01223 KMMessage* KMMessage::createForward( const QString &tmpl )
01224 {
01225 KMMessage* msg = new KMMessage();
01226
01227
01228
01229
01230 if ( type() == DwMime::kTypeMultipart ||
01231 ( type() == DwMime::kTypeText && subtype() == DwMime::kSubtypePlain ) ) {
01232
01233 msg->fromDwString( this->asDwString() );
01234
01235
01236 DwMediaType oldContentType = msg->mMsg->Headers().ContentType();
01237
01238 msg->sanitizeHeaders();
01239
01240
01241 QStringList blacklist = GlobalSettings::self()->mimetypesToStripWhenInlineForwarding();
01242 for ( QStringList::Iterator it = blacklist.begin(); it != blacklist.end(); ++it ) {
01243 QString entry = (*it);
01244 int sep = entry.find( '/' );
01245 QCString type = entry.left( sep ).latin1();
01246 QCString subtype = entry.mid( sep+1 ).latin1();
01247 kdDebug( 5006 ) << "Looking for blacklisted type: " << type << "/" << subtype << endl;
01248 while ( DwBodyPart * part = msg->findDwBodyPart( type, subtype ) ) {
01249 msg->mMsg->Body().RemoveBodyPart( part );
01250 }
01251 }
01252 msg->mMsg->Assemble();
01253 msg->initFromMessage( this );
01254
01255
01256 msg->mMsg->Headers().ContentType().FromString( oldContentType.AsString() );
01257 msg->mMsg->Headers().ContentType().Parse();
01258 msg->mMsg->Assemble();
01259 }
01260 else if( type() == DwMime::kTypeText && subtype() == DwMime::kSubtypeHtml ) {
01261
01262
01263 msg->initFromMessage( this );
01264 msg->setType( DwMime::kTypeText );
01265 msg->setSubtype( DwMime::kSubtypeHtml );
01266 msg->mNeedsAssembly = true;
01267 msg->cleanupHeader();
01268 }
01269 else {
01270
01271
01272 msg->initFromMessage( this );
01273 msg->removeHeaderField("Content-Type");
01274 msg->removeHeaderField("Content-Transfer-Encoding");
01275
01276 DwHeaders & header = msg->mMsg->Headers();
01277 header.MimeVersion().FromString("1.0");
01278 DwMediaType & contentType = msg->dwContentType();
01279 contentType.SetType( DwMime::kTypeMultipart );
01280 contentType.SetSubtype( DwMime::kSubtypeMixed );
01281 contentType.CreateBoundary(0);
01282 contentType.Assemble();
01283
01284
01285 KMMessagePart msgPart;
01286 bodyPart( 0, &msgPart );
01287 msg->addBodyPart(&msgPart);
01288
01289 KMMessagePart secondPart;
01290 secondPart.setType( type() );
01291 secondPart.setSubtype( subtype() );
01292 secondPart.setBody( mMsg->Body().AsString() );
01293
01294 applyHeadersToMessagePart( mMsg->Headers(), &secondPart );
01295 msg->addBodyPart(&secondPart);
01296 msg->mNeedsAssembly = true;
01297 msg->cleanupHeader();
01298 }
01299
01300
01301 msg->setSubject( forwardSubject() );
01302
01303 TemplateParser parser( msg, TemplateParser::Forward,
01304 QString(),
01305 false, false, false, false);
01306 if ( !tmpl.isEmpty() ) {
01307 parser.process( tmpl, this );
01308 } else {
01309 parser.process( this );
01310 }
01311
01312
01313
01314
01315
01316
01317
01318
01319 msg->link(this, KMMsgStatusForwarded);
01320 return msg;
01321 }
01322
01323 static const struct {
01324 const char * dontAskAgainID;
01325 bool canDeny;
01326 const char * text;
01327 } mdnMessageBoxes[] = {
01328 { "mdnNormalAsk", true,
01329 I18N_NOOP("This message contains a request to return a notification "
01330 "about your reception of the message.\n"
01331 "You can either ignore the request or let KMail send a "
01332 "\"denied\" or normal response.") },
01333 { "mdnUnknownOption", false,
01334 I18N_NOOP("This message contains a request to send a notification "
01335 "about your reception of the message.\n"
01336 "It contains a processing instruction that is marked as "
01337 "\"required\", but which is unknown to KMail.\n"
01338 "You can either ignore the request or let KMail send a "
01339 "\"failed\" response.") },
01340 { "mdnMultipleAddressesInReceiptTo", true,
01341 I18N_NOOP("This message contains a request to send a notification "
01342 "about your reception of the message,\n"
01343 "but it is requested to send the notification to more "
01344 "than one address.\n"
01345 "You can either ignore the request or let KMail send a "
01346 "\"denied\" or normal response.") },
01347 { "mdnReturnPathEmpty", true,
01348 I18N_NOOP("This message contains a request to send a notification "
01349 "about your reception of the message,\n"
01350 "but there is no return-path set.\n"
01351 "You can either ignore the request or let KMail send a "
01352 "\"denied\" or normal response.") },
01353 { "mdnReturnPathNotInReceiptTo", true,
01354 I18N_NOOP("This message contains a request to send a notification "
01355 "about your reception of the message,\n"
01356 "but the return-path address differs from the address "
01357 "the notification was requested to be sent to.\n"
01358 "You can either ignore the request or let KMail send a "
01359 "\"denied\" or normal response.") },
01360 };
01361
01362 static const int numMdnMessageBoxes
01363 = sizeof mdnMessageBoxes / sizeof *mdnMessageBoxes;
01364
01365
01366 static int requestAdviceOnMDN( const char * what ) {
01367 for ( int i = 0 ; i < numMdnMessageBoxes ; ++i ) {
01368 if ( !qstrcmp( what, mdnMessageBoxes[i].dontAskAgainID ) ) {
01369 if ( mdnMessageBoxes[i].canDeny ) {
01370 const KCursorSaver saver( QCursor::ArrowCursor );
01371 int answer = QMessageBox::information( 0,
01372 i18n("Message Disposition Notification Request"),
01373 i18n( mdnMessageBoxes[i].text ),
01374 i18n("&Ignore"), i18n("Send \"&denied\""), i18n("&Send") );
01375 return answer ? answer + 1 : 0 ;
01376 } else {
01377 const KCursorSaver saver( QCursor::ArrowCursor );
01378 int answer = QMessageBox::information( 0,
01379 i18n("Message Disposition Notification Request"),
01380 i18n( mdnMessageBoxes[i].text ),
01381 i18n("&Ignore"), i18n("&Send") );
01382 return answer ? answer + 2 : 0 ;
01383 }
01384 }
01385 }
01386 kdWarning(5006) << "didn't find data for message box \""
01387 << what << "\"" << endl;
01388 return 0;
01389 }
01390
01391 KMMessage* KMMessage::createMDN( MDN::ActionMode a,
01392 MDN::DispositionType d,
01393 bool allowGUI,
01394 QValueList<MDN::DispositionModifier> m )
01395 {
01396
01397
01398
01399
01400
01401
01402 #ifndef MDN_DEBUG
01403 if ( mdnSentState() != KMMsgMDNStateUnknown &&
01404 mdnSentState() != KMMsgMDNNone )
01405 return 0;
01406 #else
01407 char st[2]; st[0] = (char)mdnSentState(); st[1] = 0;
01408 kdDebug(5006) << "mdnSentState() == '" << st << "'" << endl;
01409 #endif
01410
01411
01412 if ( findDwBodyPart( DwMime::kTypeMessage,
01413 DwMime::kSubtypeDispositionNotification ) ) {
01414 setMDNSentState( KMMsgMDNIgnore );
01415 return 0;
01416 }
01417
01418
01419 QString receiptTo = headerField("Disposition-Notification-To");
01420 if ( receiptTo.stripWhiteSpace().isEmpty() ) return 0;
01421 receiptTo.remove( '\n' );
01422
01423
01424 MDN::SendingMode s = MDN::SentAutomatically;
01425 QString special;
01426 KConfigGroup mdnConfig( KMKernel::config(), "MDN" );
01427
01428
01429 int mode = mdnConfig.readNumEntry( "default-policy", 0 );
01430 if ( !mode || mode < 0 || mode > 3 ) {
01431
01432 setMDNSentState( KMMsgMDNIgnore );
01433 return 0;
01434 }
01435
01436
01437
01438
01439
01440
01441
01442 QString notificationOptions = headerField("Disposition-Notification-Options");
01443 if ( notificationOptions.contains( "required", false ) ) {
01444
01445
01446
01447 if ( !allowGUI ) return 0;
01448 mode = requestAdviceOnMDN( "mdnUnknownOption" );
01449 s = MDN::SentManually;
01450
01451 special = i18n("Header \"Disposition-Notification-Options\" contained "
01452 "required, but unknown parameter");
01453 d = MDN::Failed;
01454 m.clear();
01455 }
01456
01457
01458
01459
01460 kdDebug(5006) << "KPIM::splitEmailAddrList(receiptTo): "
01461 << KPIM::splitEmailAddrList(receiptTo).join("\n") << endl;
01462 if ( KPIM::splitEmailAddrList(receiptTo).count() > 1 ) {
01463 if ( !allowGUI ) return 0;
01464 mode = requestAdviceOnMDN( "mdnMultipleAddressesInReceiptTo" );
01465 s = MDN::SentManually;
01466 }
01467
01468
01469
01470
01471
01472
01473 AddrSpecList returnPathList = extractAddrSpecs("Return-Path");
01474 QString returnPath = returnPathList.isEmpty() ? QString::null
01475 : returnPathList.front().localPart + '@' + returnPathList.front().domain ;
01476 kdDebug(5006) << "clean return path: " << returnPath << endl;
01477 if ( returnPath.isEmpty() || !receiptTo.contains( returnPath, false ) ) {
01478 if ( !allowGUI ) return 0;
01479 mode = requestAdviceOnMDN( returnPath.isEmpty() ?
01480 "mdnReturnPathEmpty" :
01481 "mdnReturnPathNotInReceiptTo" );
01482 s = MDN::SentManually;
01483 }
01484
01485 if ( a != KMime::MDN::AutomaticAction ) {
01486
01487 if ( mode == 1 ) {
01488 if ( !allowGUI ) return 0;
01489 mode = requestAdviceOnMDN( "mdnNormalAsk" );
01490 s = MDN::SentManually;
01491 }
01492
01493 switch ( mode ) {
01494 case 0:
01495 setMDNSentState( KMMsgMDNIgnore );
01496 return 0;
01497 default:
01498 case 1:
01499 kdFatal(5006) << "KMMessage::createMDN(): The \"ask\" mode should "
01500 << "never appear here!" << endl;
01501 break;
01502 case 2:
01503 d = MDN::Denied;
01504 m.clear();
01505 break;
01506 case 3:
01507 break;
01508 }
01509 }
01510
01511
01512
01513 QString finalRecipient = kmkernel->identityManager()
01514 ->identityForUoidOrDefault( identityUoid() ).fullEmailAddr();
01515
01516
01517
01518
01519
01520 KMMessage * receipt = new KMMessage();
01521 receipt->initFromMessage( this );
01522 receipt->removeHeaderField("Content-Type");
01523 receipt->removeHeaderField("Content-Transfer-Encoding");
01524
01525 DwHeaders & header = receipt->mMsg->Headers();
01526 header.MimeVersion().FromString("1.0");
01527 DwMediaType & contentType = receipt->dwContentType();
01528 contentType.SetType( DwMime::kTypeMultipart );
01529 contentType.SetSubtype( DwMime::kSubtypeReport );
01530 contentType.CreateBoundary(0);
01531 receipt->mNeedsAssembly = true;
01532 receipt->setContentTypeParam( "report-type", "disposition-notification" );
01533
01534 QString description = replaceHeadersInString( MDN::descriptionFor( d, m ) );
01535
01536
01537 KMMessagePart firstMsgPart;
01538 firstMsgPart.setTypeStr( "text" );
01539 firstMsgPart.setSubtypeStr( "plain" );
01540 firstMsgPart.setBodyFromUnicode( description );
01541 receipt->addBodyPart( &firstMsgPart );
01542
01543
01544 KMMessagePart secondMsgPart;
01545 secondMsgPart.setType( DwMime::kTypeMessage );
01546 secondMsgPart.setSubtype( DwMime::kSubtypeDispositionNotification );
01547
01548
01549 secondMsgPart.setBodyEncoded( MDN::dispositionNotificationBodyContent(
01550 finalRecipient,
01551 rawHeaderField("Original-Recipient"),
01552 id(),
01553 d, a, s, m, special ) );
01554 receipt->addBodyPart( &secondMsgPart );
01555
01556
01557 int num = mdnConfig.readNumEntry( "quote-message", 0 );
01558 if ( num < 0 || num > 2 ) num = 0;
01559 MDN::ReturnContent returnContent = static_cast<MDN::ReturnContent>( num );
01560
01561 KMMessagePart thirdMsgPart;
01562 switch ( returnContent ) {
01563 case MDN::All:
01564 thirdMsgPart.setTypeStr( "message" );
01565 thirdMsgPart.setSubtypeStr( "rfc822" );
01566 thirdMsgPart.setBody( asSendableString() );
01567 receipt->addBodyPart( &thirdMsgPart );
01568 break;
01569 case MDN::HeadersOnly:
01570 thirdMsgPart.setTypeStr( "text" );
01571 thirdMsgPart.setSubtypeStr( "rfc822-headers" );
01572 thirdMsgPart.setBody( headerAsSendableString() );
01573 receipt->addBodyPart( &thirdMsgPart );
01574 break;
01575 case MDN::Nothing:
01576 default:
01577 break;
01578 };
01579
01580 receipt->setTo( receiptTo );
01581 receipt->setSubject( "Message Disposition Notification" );
01582 receipt->setReplyToId( msgId() );
01583 receipt->setReferences( getRefStr() );
01584
01585 receipt->cleanupHeader();
01586
01587 kdDebug(5006) << "final message:\n" + receipt->asString() << endl;
01588
01589
01590
01591
01592 KMMsgMDNSentState state = KMMsgMDNStateUnknown;
01593 switch ( d ) {
01594 case MDN::Displayed: state = KMMsgMDNDisplayed; break;
01595 case MDN::Deleted: state = KMMsgMDNDeleted; break;
01596 case MDN::Dispatched: state = KMMsgMDNDispatched; break;
01597 case MDN::Processed: state = KMMsgMDNProcessed; break;
01598 case MDN::Denied: state = KMMsgMDNDenied; break;
01599 case MDN::Failed: state = KMMsgMDNFailed; break;
01600 };
01601 setMDNSentState( state );
01602
01603 return receipt;
01604 }
01605
01606 QString KMMessage::replaceHeadersInString( const QString & s ) const {
01607 QString result = s;
01608 QRegExp rx( "\\$\\{([a-z0-9-]+)\\}", false );
01609 Q_ASSERT( rx.isValid() );
01610
01611 QRegExp rxDate( "\\$\\{date\\}" );
01612 Q_ASSERT( rxDate.isValid() );
01613
01614 QString sDate = KMime::DateFormatter::formatDate(
01615 KMime::DateFormatter::Localized, date() );
01616
01617 int idx = 0;
01618 if( ( idx = rxDate.search( result, idx ) ) != -1 ) {
01619 result.replace( idx, rxDate.matchedLength(), sDate );
01620 }
01621
01622 idx = 0;
01623 while ( ( idx = rx.search( result, idx ) ) != -1 ) {
01624 QString replacement = headerField( rx.cap(1).latin1() );
01625 result.replace( idx, rx.matchedLength(), replacement );
01626 idx += replacement.length();
01627 }
01628 return result;
01629 }
01630
01631 KMMessage* KMMessage::createDeliveryReceipt() const
01632 {
01633 QString str, receiptTo;
01634 KMMessage *receipt;
01635
01636 receiptTo = headerField("Disposition-Notification-To");
01637 if ( receiptTo.stripWhiteSpace().isEmpty() ) return 0;
01638 receiptTo.remove( '\n' );
01639
01640 receipt = new KMMessage;
01641 receipt->initFromMessage(this);
01642 receipt->setTo(receiptTo);
01643 receipt->setSubject(i18n("Receipt: ") + subject());
01644
01645 str = "Your message was successfully delivered.";
01646 str += "\n\n---------- Message header follows ----------\n";
01647 str += headerAsString();
01648 str += "--------------------------------------------\n";
01649
01650
01651 receipt->setBody(str.latin1());
01652 receipt->setAutomaticFields();
01653
01654 return receipt;
01655 }
01656
01657
01658 void KMMessage::applyIdentity( uint id )
01659 {
01660 const KPIM::Identity & ident =
01661 kmkernel->identityManager()->identityForUoidOrDefault( id );
01662
01663 if(ident.fullEmailAddr().isEmpty())
01664 setFrom("");
01665 else
01666 setFrom(ident.fullEmailAddr());
01667
01668 if(ident.replyToAddr().isEmpty())
01669 setReplyTo("");
01670 else
01671 setReplyTo(ident.replyToAddr());
01672
01673 if(ident.bcc().isEmpty())
01674 setBcc("");
01675 else
01676 setBcc(ident.bcc());
01677
01678 if (ident.organization().isEmpty())
01679 removeHeaderField("Organization");
01680 else
01681 setHeaderField("Organization", ident.organization());
01682
01683 if (ident.isDefault())
01684 removeHeaderField("X-KMail-Identity");
01685 else
01686 setHeaderField("X-KMail-Identity", QString::number( ident.uoid() ));
01687
01688 if ( ident.transport().isEmpty() )
01689 removeHeaderField( "X-KMail-Transport" );
01690 else
01691 setHeaderField( "X-KMail-Transport", ident.transport() );
01692
01693 if ( ident.fcc().isEmpty() )
01694 setFcc( QString::null );
01695 else
01696 setFcc( ident.fcc() );
01697
01698 if ( ident.drafts().isEmpty() )
01699 setDrafts( QString::null );
01700 else
01701 setDrafts( ident.drafts() );
01702
01703 if ( ident.templates().isEmpty() )
01704 setTemplates( QString::null );
01705 else
01706 setTemplates( ident.templates() );
01707
01708 }
01709
01710
01711 void KMMessage::initHeader( uint id )
01712 {
01713 applyIdentity( id );
01714 setTo("");
01715 setSubject("");
01716 setDateToday();
01717
01718 setHeaderField("User-Agent", "KMail/" KMAIL_VERSION );
01719
01720 setHeaderField("Content-Type","text/plain");
01721 }
01722
01723 uint KMMessage::identityUoid() const {
01724 QString idString = headerField("X-KMail-Identity").stripWhiteSpace();
01725 bool ok = false;
01726 int id = idString.toUInt( &ok );
01727
01728 if ( !ok || id == 0 )
01729 id = kmkernel->identityManager()->identityForAddress( to() + ", " + cc() ).uoid();
01730 if ( id == 0 && parent() )
01731 id = parent()->identity();
01732
01733 return id;
01734 }
01735
01736
01737
01738 void KMMessage::initFromMessage(const KMMessage *msg, bool idHeaders)
01739 {
01740 uint id = msg->identityUoid();
01741
01742 if ( idHeaders ) initHeader(id);
01743 else setHeaderField("X-KMail-Identity", QString::number(id));
01744 if (!msg->headerField("X-KMail-Transport").isEmpty())
01745 setHeaderField("X-KMail-Transport", msg->headerField("X-KMail-Transport"));
01746 }
01747
01748
01749
01750 void KMMessage::cleanupHeader()
01751 {
01752 DwHeaders& header = mMsg->Headers();
01753 DwField* field = header.FirstField();
01754 DwField* nextField;
01755
01756 if (mNeedsAssembly) mMsg->Assemble();
01757 mNeedsAssembly = false;
01758
01759 while (field)
01760 {
01761 nextField = field->Next();
01762 if (field->FieldBody()->AsString().empty())
01763 {
01764 header.RemoveField(field);
01765 mNeedsAssembly = true;
01766 }
01767 field = nextField;
01768 }
01769 }
01770
01771
01772
01773 void KMMessage::setAutomaticFields(bool aIsMulti)
01774 {
01775 DwHeaders& header = mMsg->Headers();
01776 header.MimeVersion().FromString("1.0");
01777
01778 if (aIsMulti || numBodyParts() > 1)
01779 {
01780
01781 DwMediaType& contentType = dwContentType();
01782 contentType.SetType( DwMime::kTypeMultipart);
01783 contentType.SetSubtype(DwMime::kSubtypeMixed );
01784
01785
01786 contentType.CreateBoundary(0);
01787 }
01788 mNeedsAssembly = true;
01789 }
01790
01791
01792
01793 QString KMMessage::dateStr() const
01794 {
01795 KConfigGroup general( KMKernel::config(), "General" );
01796 DwHeaders& header = mMsg->Headers();
01797 time_t unixTime;
01798
01799 if (!header.HasDate()) return "";
01800 unixTime = header.Date().AsUnixTime();
01801
01802
01803
01804 return KMime::DateFormatter::formatDate(
01805 static_cast<KMime::DateFormatter::FormatType>(general.readNumEntry( "dateFormat", KMime::DateFormatter::Fancy )),
01806 unixTime, general.readEntry( "customDateFormat" ));
01807 }
01808
01809
01810
01811 QCString KMMessage::dateShortStr() const
01812 {
01813 DwHeaders& header = mMsg->Headers();
01814 time_t unixTime;
01815
01816 if (!header.HasDate()) return "";
01817 unixTime = header.Date().AsUnixTime();
01818
01819 QCString result = ctime(&unixTime);
01820 int len = result.length();
01821 if (result[len-1]=='\n')
01822 result.truncate(len-1);
01823
01824 return result;
01825 }
01826
01827
01828
01829 QString KMMessage::dateIsoStr() const
01830 {
01831 DwHeaders& header = mMsg->Headers();
01832 time_t unixTime;
01833
01834 if (!header.HasDate()) return "";
01835 unixTime = header.Date().AsUnixTime();
01836
01837 char cstr[64];
01838 strftime(cstr, 63, "%Y-%m-%d %H:%M:%S", localtime(&unixTime));
01839 return QString(cstr);
01840 }
01841
01842
01843
01844 time_t KMMessage::date() const
01845 {
01846 time_t res = ( time_t )-1;
01847 DwHeaders& header = mMsg->Headers();
01848 if (header.HasDate())
01849 res = header.Date().AsUnixTime();
01850 return res;
01851 }
01852
01853
01854
01855 void KMMessage::setDateToday()
01856 {
01857 struct timeval tval;
01858 gettimeofday(&tval, 0);
01859 setDate((time_t)tval.tv_sec);
01860 }
01861
01862
01863
01864 void KMMessage::setDate(time_t aDate)
01865 {
01866 mDate = aDate;
01867 mMsg->Headers().Date().FromCalendarTime(aDate);
01868 mMsg->Headers().Date().Assemble();
01869 mNeedsAssembly = true;
01870 mDirty = true;
01871 }
01872
01873
01874
01875 void KMMessage::setDate(const QCString& aStr)
01876 {
01877 DwHeaders& header = mMsg->Headers();
01878
01879 header.Date().FromString(aStr);
01880 header.Date().Parse();
01881 mNeedsAssembly = true;
01882 mDirty = true;
01883
01884 if (header.HasDate())
01885 mDate = header.Date().AsUnixTime();
01886 }
01887
01888
01889
01890 QString KMMessage::to() const
01891 {
01892
01893 QValueList<QCString> rawHeaders = rawHeaderFields( "To" );
01894 QStringList headers;
01895 for ( QValueList<QCString>::Iterator it = rawHeaders.begin(); it != rawHeaders.end(); ++it ) {
01896 headers << *it;
01897 }
01898 return KPIM::normalizeAddressesAndDecodeIDNs( headers.join( ", " ) );
01899 }
01900
01901
01902
01903 void KMMessage::setTo(const QString& aStr)
01904 {
01905 setHeaderField( "To", aStr, Address );
01906 }
01907
01908
01909 QString KMMessage::toStrip() const
01910 {
01911 return stripEmailAddr( to() );
01912 }
01913
01914
01915 QString KMMessage::replyTo() const
01916 {
01917 return KPIM::normalizeAddressesAndDecodeIDNs( rawHeaderField("Reply-To") );
01918 }
01919
01920
01921
01922 void KMMessage::setReplyTo(const QString& aStr)
01923 {
01924 setHeaderField( "Reply-To", aStr, Address );
01925 }
01926
01927
01928
01929 void KMMessage::setReplyTo(KMMessage* aMsg)
01930 {
01931 setHeaderField( "Reply-To", aMsg->from(), Address );
01932 }
01933
01934
01935
01936 QString KMMessage::cc() const
01937 {
01938
01939
01940 QValueList<QCString> rawHeaders = rawHeaderFields( "Cc" );
01941 QStringList headers;
01942 for ( QValueList<QCString>::Iterator it = rawHeaders.begin(); it != rawHeaders.end(); ++it ) {
01943 headers << *it;
01944 }
01945 return KPIM::normalizeAddressesAndDecodeIDNs( headers.join( ", " ) );
01946 }
01947
01948
01949
01950 void KMMessage::setCc(const QString& aStr)
01951 {
01952 setHeaderField( "Cc", aStr, Address );
01953 }
01954
01955
01956
01957 QString KMMessage::ccStrip() const
01958 {
01959 return stripEmailAddr( cc() );
01960 }
01961
01962
01963
01964 QString KMMessage::bcc() const
01965 {
01966 return KPIM::normalizeAddressesAndDecodeIDNs( rawHeaderField("Bcc") );
01967 }
01968
01969
01970
01971 void KMMessage::setBcc(const QString& aStr)
01972 {
01973 setHeaderField( "Bcc", aStr, Address );
01974 }
01975
01976
01977 QString KMMessage::fcc() const
01978 {
01979 return headerField( "X-KMail-Fcc" );
01980 }
01981
01982
01983
01984 void KMMessage::setFcc( const QString &aStr )
01985 {
01986 setHeaderField( "X-KMail-Fcc", aStr );
01987 }
01988
01989
01990 void KMMessage::setDrafts( const QString &aStr )
01991 {
01992 mDrafts = aStr;
01993 }
01994
01995
01996 void KMMessage::setTemplates( const QString &aStr )
01997 {
01998 mTemplates = aStr;
01999 }
02000
02001
02002 QString KMMessage::who() const
02003 {
02004 if (mParent)
02005 return KPIM::normalizeAddressesAndDecodeIDNs( rawHeaderField(mParent->whoField().utf8()) );
02006 return from();
02007 }
02008
02009
02010
02011 QString KMMessage::from() const
02012 {
02013 return KPIM::normalizeAddressesAndDecodeIDNs( rawHeaderField("From") );
02014 }
02015
02016
02017
02018 void KMMessage::setFrom(const QString& bStr)
02019 {
02020 QString aStr = bStr;
02021 if (aStr.isNull())
02022 aStr = "";
02023 setHeaderField( "From", aStr, Address );
02024 mDirty = true;
02025 }
02026
02027
02028
02029 QString KMMessage::fromStrip() const
02030 {
02031 return stripEmailAddr( from() );
02032 }
02033
02034
02035 QString KMMessage::sender() const {
02036 AddrSpecList asl = extractAddrSpecs( "Sender" );
02037 if ( asl.empty() )
02038 asl = extractAddrSpecs( "From" );
02039 if ( asl.empty() )
02040 return QString::null;
02041 return asl.front().asString();
02042 }
02043
02044
02045 QString KMMessage::subject() const
02046 {
02047 return headerField("Subject");
02048 }
02049
02050
02051
02052 void KMMessage::setSubject(const QString& aStr)
02053 {
02054 setHeaderField("Subject",aStr);
02055 mDirty = true;
02056 }
02057
02058
02059
02060 QString KMMessage::xmark() const
02061 {
02062 return headerField("X-KMail-Mark");
02063 }
02064
02065
02066
02067 void KMMessage::setXMark(const QString& aStr)
02068 {
02069 setHeaderField("X-KMail-Mark", aStr);
02070 mDirty = true;
02071 }
02072
02073
02074
02075 QString KMMessage::replyToId() const
02076 {
02077 int leftAngle, rightAngle;
02078 QString replyTo, references;
02079
02080 replyTo = headerField("In-Reply-To");
02081
02082 rightAngle = replyTo.find( '>' );
02083 if (rightAngle != -1)
02084 replyTo.truncate( rightAngle + 1 );
02085
02086 leftAngle = replyTo.findRev( '<' );
02087 if (leftAngle != -1)
02088 replyTo = replyTo.mid( leftAngle );
02089
02090
02091
02092
02093
02094 if (!replyTo.isEmpty() && (replyTo[0] == '<') &&
02095 ( -1 == replyTo.find( '"' ) ) )
02096 return replyTo;
02097
02098 references = headerField("References");
02099 leftAngle = references.findRev( '<' );
02100 if (leftAngle != -1)
02101 references = references.mid( leftAngle );
02102 rightAngle = references.find( '>' );
02103 if (rightAngle != -1)
02104 references.truncate( rightAngle + 1 );
02105
02106
02107 if (!references.isEmpty() && references[0] == '<')
02108 return references;
02109
02110 else
02111 return replyTo;
02112 }
02113
02114
02115
02116 QString KMMessage::replyToIdMD5() const {
02117 return base64EncodedMD5( replyToId() );
02118 }
02119
02120
02121 QString KMMessage::references() const
02122 {
02123 int leftAngle, rightAngle;
02124 QString references = headerField( "References" );
02125
02126
02127 leftAngle = references.findRev( '<' );
02128 leftAngle = references.findRev( '<', leftAngle - 1 );
02129 if( leftAngle != -1 )
02130 references = references.mid( leftAngle );
02131 rightAngle = references.findRev( '>' );
02132 if( rightAngle != -1 )
02133 references.truncate( rightAngle + 1 );
02134
02135 if( !references.isEmpty() && references[0] == '<' )
02136 return references;
02137 else
02138 return QString::null;
02139 }
02140
02141
02142 QString KMMessage::replyToAuxIdMD5() const
02143 {
02144 QString result = references();
02145
02146
02147 const int rightAngle = result.find( '>' );
02148 if( rightAngle != -1 )
02149 result.truncate( rightAngle + 1 );
02150
02151 return base64EncodedMD5( result );
02152 }
02153
02154
02155 QString KMMessage::strippedSubjectMD5() const {
02156 return base64EncodedMD5( stripOffPrefixes( subject() ), true );
02157 }
02158
02159
02160 QString KMMessage::subjectMD5() const {
02161 return base64EncodedMD5( subject(), true );
02162 }
02163
02164
02165 bool KMMessage::subjectIsPrefixed() const {
02166 return subjectMD5() != strippedSubjectMD5();
02167 }
02168
02169
02170 void KMMessage::setReplyToId(const QString& aStr)
02171 {
02172 setHeaderField("In-Reply-To", aStr);
02173 mDirty = true;
02174 }
02175
02176
02177
02178 QString KMMessage::msgId() const
02179 {
02180 QString msgId = headerField("Message-Id");
02181
02182
02183 const int rightAngle = msgId.find( '>' );
02184 if (rightAngle != -1)
02185 msgId.truncate( rightAngle + 1 );
02186
02187 const int leftAngle = msgId.findRev( '<' );
02188 if (leftAngle != -1)
02189 msgId = msgId.mid( leftAngle );
02190 return msgId;
02191 }
02192
02193
02194
02195 QString KMMessage::msgIdMD5() const {
02196 return base64EncodedMD5( msgId() );
02197 }
02198
02199
02200
02201 void KMMessage::setMsgId(const QString& aStr)
02202 {
02203 setHeaderField("Message-Id", aStr);
02204 mDirty = true;
02205 }
02206
02207
02208 size_t KMMessage::msgSizeServer() const {
02209 return headerField( "X-Length" ).toULong();
02210 }
02211
02212
02213
02214 void KMMessage::setMsgSizeServer(size_t size)
02215 {
02216 setHeaderField("X-Length", QCString().setNum(size));
02217 mDirty = true;
02218 }
02219
02220
02221 ulong KMMessage::UID() const {
02222 return headerField( "X-UID" ).toULong();
02223 }
02224
02225
02226
02227 void KMMessage::setUID(ulong uid)
02228 {
02229 setHeaderField("X-UID", QCString().setNum(uid));
02230 mDirty = true;
02231 }
02232
02233
02234 AddressList KMMessage::splitAddrField( const QCString & str )
02235 {
02236 AddressList result;
02237 const char * scursor = str.begin();
02238 if ( !scursor )
02239 return AddressList();
02240 const char * const send = str.begin() + str.length();
02241 if ( !parseAddressList( scursor, send, result ) )
02242 kdDebug(5006) << "Error in address splitting: parseAddressList returned false!"
02243 << endl;
02244 return result;
02245 }
02246
02247 AddressList KMMessage::headerAddrField( const QCString & aName ) const {
02248 return KMMessage::splitAddrField( rawHeaderField( aName ) );
02249 }
02250
02251 AddrSpecList KMMessage::extractAddrSpecs( const QCString & header ) const {
02252 AddressList al = headerAddrField( header );
02253 AddrSpecList result;
02254 for ( AddressList::const_iterator ait = al.begin() ; ait != al.end() ; ++ait )
02255 for ( MailboxList::const_iterator mit = (*ait).mailboxList.begin() ; mit != (*ait).mailboxList.end() ; ++mit )
02256 result.push_back( (*mit).addrSpec );
02257 return result;
02258 }
02259
02260 QCString KMMessage::rawHeaderField( const QCString & name ) const {
02261 if ( name.isEmpty() ) return QCString();
02262
02263 DwHeaders & header = mMsg->Headers();
02264 DwField * field = header.FindField( name );
02265
02266 if ( !field ) return QCString();
02267
02268 return header.FieldBody( name.data() ).AsString().c_str();
02269 }
02270
02271 QValueList<QCString> KMMessage::rawHeaderFields( const QCString& field ) const
02272 {
02273 if ( field.isEmpty() || !mMsg->Headers().FindField( field ) )
02274 return QValueList<QCString>();
02275
02276 std::vector<DwFieldBody*> v = mMsg->Headers().AllFieldBodies( field.data() );
02277 QValueList<QCString> headerFields;
02278 for ( uint i = 0; i < v.size(); ++i ) {
02279 headerFields.append( v[i]->AsString().c_str() );
02280 }
02281
02282 return headerFields;
02283 }
02284
02285 QString KMMessage::headerField(const QCString& aName) const
02286 {
02287 if ( aName.isEmpty() )
02288 return QString::null;
02289
02290 if ( !mMsg->Headers().FindField( aName ) )
02291 return QString::null;
02292
02293 return decodeRFC2047String( mMsg->Headers().FieldBody( aName.data() ).AsString().c_str(),
02294 charset() );
02295
02296 }
02297
02298 QStringList KMMessage::headerFields( const QCString& field ) const
02299 {
02300 if ( field.isEmpty() || !mMsg->Headers().FindField( field ) )
02301 return QStringList();
02302
02303 std::vector<DwFieldBody*> v = mMsg->Headers().AllFieldBodies( field.data() );
02304 QStringList headerFields;
02305 for ( uint i = 0; i < v.size(); ++i ) {
02306 headerFields.append( decodeRFC2047String( v[i]->AsString().c_str(), charset() ) );
02307 }
02308
02309 return headerFields;
02310 }
02311
02312
02313 void KMMessage::removeHeaderField(const QCString& aName)
02314 {
02315 DwHeaders & header = mMsg->Headers();
02316 DwField * field = header.FindField(aName);
02317 if (!field) return;
02318
02319 header.RemoveField(field);
02320 mNeedsAssembly = true;
02321 }
02322
02323
02324 void KMMessage::removeHeaderFields(const QCString& aName)
02325 {
02326 DwHeaders & header = mMsg->Headers();
02327 while ( DwField * field = header.FindField(aName) ) {
02328 header.RemoveField(field);
02329 mNeedsAssembly = true;
02330 }
02331 }
02332
02333
02334
02335 void KMMessage::setHeaderField( const QCString& aName, const QString& bValue,
02336 HeaderFieldType type, bool prepend )
02337 {
02338 #if 0
02339 if ( type != Unstructured )
02340 kdDebug(5006) << "KMMessage::setHeaderField( \"" << aName << "\", \""
02341 << bValue << "\", " << type << " )" << endl;
02342 #endif
02343 if (aName.isEmpty()) return;
02344
02345 DwHeaders& header = mMsg->Headers();
02346
02347 DwString str;
02348 DwField* field;
02349 QCString aValue;
02350 if (!bValue.isEmpty())
02351 {
02352 QString value = bValue;
02353 if ( type == Address )
02354 value = KPIM::normalizeAddressesAndEncodeIDNs( value );
02355 #if 0
02356 if ( type != Unstructured )
02357 kdDebug(5006) << "value: \"" << value << "\"" << endl;
02358 #endif
02359 QCString encoding = autoDetectCharset( charset(), sPrefCharsets, value );
02360 if (encoding.isEmpty())
02361 encoding = "utf-8";
02362 aValue = encodeRFC2047String( value, encoding );
02363 #if 0
02364 if ( type != Unstructured )
02365 kdDebug(5006) << "aValue: \"" << aValue << "\"" << endl;
02366 #endif
02367 }
02368 str = aName;
02369 if (str[str.length()-1] != ':') str += ": ";
02370 else str += ' ';
02371 if ( !aValue.isEmpty() )
02372 str += aValue;
02373 if (str[str.length()-1] != '\n') str += '\n';
02374
02375 field = new DwField(str, mMsg);
02376 field->Parse();
02377
02378 if ( prepend )
02379 header.AddFieldAt( 1, field );
02380 else
02381 header.AddOrReplaceField( field );
02382 mNeedsAssembly = true;
02383 }
02384
02385
02386
02387 QCString KMMessage::typeStr() const
02388 {
02389 DwHeaders& header = mMsg->Headers();
02390 if (header.HasContentType()) return header.ContentType().TypeStr().c_str();
02391 else return "";
02392 }
02393
02394
02395
02396 int KMMessage::type() const
02397 {
02398 DwHeaders& header = mMsg->Headers();
02399 if (header.HasContentType()) return header.ContentType().Type();
02400 else return DwMime::kTypeNull;
02401 }
02402
02403
02404
02405 void KMMessage::setTypeStr(const QCString& aStr)
02406 {
02407 dwContentType().SetTypeStr(DwString(aStr));
02408 dwContentType().Parse();
02409 mNeedsAssembly = true;
02410 }
02411
02412
02413
02414 void KMMessage::setType(int aType)
02415 {
02416 dwContentType().SetType(aType);
02417 dwContentType().Assemble();
02418 mNeedsAssembly = true;
02419 }
02420
02421
02422
02423
02424 QCString KMMessage::subtypeStr() const
02425 {
02426 DwHeaders& header = mMsg->Headers();
02427 if (header.HasContentType()) return header.ContentType().SubtypeStr().c_str();
02428 else return "";
02429 }
02430
02431
02432
02433 int KMMessage::subtype() const
02434 {
02435 DwHeaders& header = mMsg->Headers();
02436 if (header.HasContentType()) return header.ContentType().Subtype();
02437 else return DwMime::kSubtypeNull;
02438 }
02439
02440
02441
02442 void KMMessage::setSubtypeStr(const QCString& aStr)
02443 {
02444 dwContentType().SetSubtypeStr(DwString(aStr));
02445 dwContentType().Parse();
02446 mNeedsAssembly = true;
02447 }
02448
02449
02450
02451 void KMMessage::setSubtype(int aSubtype)
02452 {
02453 dwContentType().SetSubtype(aSubtype);
02454 dwContentType().Assemble();
02455 mNeedsAssembly = true;
02456 }
02457
02458
02459
02460 void KMMessage::setDwMediaTypeParam( DwMediaType &mType,
02461 const QCString& attr,
02462 const QCString& val )
02463 {
02464 mType.Parse();
02465 DwParameter *param = mType.FirstParameter();
02466 while(param) {
02467 if (!kasciistricmp(param->Attribute().c_str(), attr))
02468 break;
02469 else
02470 param = param->Next();
02471 }
02472 if (!param){
02473 param = new DwParameter;
02474 param->SetAttribute(DwString( attr ));
02475 mType.AddParameter( param );
02476 }
02477 else
02478 mType.SetModified();
02479 param->SetValue(DwString( val ));
02480 mType.Assemble();
02481 }
02482
02483
02484
02485 void KMMessage::setContentTypeParam(const QCString& attr, const QCString& val)
02486 {
02487 if (mNeedsAssembly) mMsg->Assemble();
02488 mNeedsAssembly = false;
02489 setDwMediaTypeParam( dwContentType(), attr, val );
02490 mNeedsAssembly = true;
02491 }
02492
02493
02494
02495 QCString KMMessage::contentTransferEncodingStr() const
02496 {
02497 DwHeaders& header = mMsg->Headers();
02498 if (header.HasContentTransferEncoding())
02499 return header.ContentTransferEncoding().AsString().c_str();
02500 else return "";
02501 }
02502
02503
02504
02505 int KMMessage::contentTransferEncoding( DwEntity *entity ) const
02506 {
02507 if ( !entity )
02508 entity = mMsg;
02509
02510 DwHeaders& header = entity->Headers();
02511 if ( header.HasContentTransferEncoding() )
02512 return header.ContentTransferEncoding().AsEnum();
02513 else return DwMime::kCteNull;
02514 }
02515
02516
02517
02518 void KMMessage::setContentTransferEncodingStr( const QCString& cteString,
02519 DwEntity *entity )
02520 {
02521 if ( !entity )
02522 entity = mMsg;
02523
02524 entity->Headers().ContentTransferEncoding().FromString( cteString );
02525 entity->Headers().ContentTransferEncoding().Parse();
02526 mNeedsAssembly = true;
02527 }
02528
02529
02530
02531 void KMMessage::setContentTransferEncoding( int cte, DwEntity *entity )
02532 {
02533 if ( !entity )
02534 entity = mMsg;
02535
02536 entity->Headers().ContentTransferEncoding().FromEnum( cte );
02537 mNeedsAssembly = true;
02538 }
02539
02540
02541
02542 DwHeaders& KMMessage::headers() const
02543 {
02544 return mMsg->Headers();
02545 }
02546
02547
02548
02549 void KMMessage::setNeedsAssembly()
02550 {
02551 mNeedsAssembly = true;
02552 }
02553
02554
02555 void KMMessage::assembleIfNeeded()
02556 {
02557 Q_ASSERT( mMsg );
02558
02559 if ( mNeedsAssembly ) {
02560 mMsg->Assemble();
02561 mNeedsAssembly = false;
02562 }
02563 }
02564
02565
02566 QCString KMMessage::body() const
02567 {
02568 const DwString& body = mMsg->Body().AsString();
02569 QCString str = KMail::Util::CString( body );
02570
02571
02572
02573 return str;
02574 }
02575
02576
02577
02578 QByteArray KMMessage::bodyDecodedBinary() const
02579 {
02580 DwString dwstr;
02581 const DwString& dwsrc = mMsg->Body().AsString();
02582
02583 switch (cte())
02584 {
02585 case DwMime::kCteBase64:
02586 DwDecodeBase64(dwsrc, dwstr);
02587 break;
02588 case DwMime::kCteQuotedPrintable:
02589 DwDecodeQuotedPrintable(dwsrc, dwstr);
02590 break;
02591 default:
02592 dwstr = dwsrc;
02593 break;
02594 }
02595
02596 int len = dwstr.size();
02597 QByteArray ba(len);
02598 memcpy(ba.data(),dwstr.data(),len);
02599 return ba;
02600 }
02601
02602
02603
02604 QCString KMMessage::bodyDecoded() const
02605 {
02606 DwString dwstr;
02607 DwString dwsrc = mMsg->Body().AsString();
02608
02609 switch (cte())
02610 {
02611 case DwMime::kCteBase64:
02612 DwDecodeBase64(dwsrc, dwstr);
02613 break;
02614 case DwMime::kCteQuotedPrintable:
02615 DwDecodeQuotedPrintable(dwsrc, dwstr);
02616 break;
02617 default:
02618 dwstr = dwsrc;
02619 break;
02620 }
02621
02622 return KMail::Util::CString( dwstr );
02623
02624
02625
02626
02627
02628
02629 }
02630
02631
02632
02633 QValueList<int> KMMessage::determineAllowedCtes( const CharFreq& cf,
02634 bool allow8Bit,
02635 bool willBeSigned )
02636 {
02637 QValueList<int> allowedCtes;
02638
02639 switch ( cf.type() ) {
02640 case CharFreq::SevenBitText:
02641 allowedCtes << DwMime::kCte7bit;
02642 case CharFreq::EightBitText:
02643 if ( allow8Bit )
02644 allowedCtes << DwMime::kCte8bit;
02645 case CharFreq::SevenBitData:
02646 if ( cf.printableRatio() > 5.0/6.0 ) {
02647
02648
02649
02650 allowedCtes << DwMime::kCteQp;
02651 allowedCtes << DwMime::kCteBase64;
02652 } else {
02653 allowedCtes << DwMime::kCteBase64;
02654 allowedCtes << DwMime::kCteQp;
02655 }
02656 break;
02657 case CharFreq::EightBitData:
02658 allowedCtes << DwMime::kCteBase64;
02659 break;
02660 case CharFreq::None:
02661 default:
02662
02663 ;
02664 }
02665
02666
02667
02668
02669
02670 if ( ( willBeSigned && cf.hasTrailingWhitespace() ) ||
02671 cf.hasLeadingFrom() ) {
02672 allowedCtes.remove( DwMime::kCte8bit );
02673 allowedCtes.remove( DwMime::kCte7bit );
02674 }
02675
02676 return allowedCtes;
02677 }
02678
02679
02680
02681 void KMMessage::setBodyAndGuessCte( const QByteArray& aBuf,
02682 QValueList<int> & allowedCte,
02683 bool allow8Bit,
02684 bool willBeSigned,
02685 DwEntity *entity )
02686 {
02687 if ( !entity )
02688 entity = mMsg;
02689
02690 CharFreq cf( aBuf );
02691 allowedCte = determineAllowedCtes( cf, allow8Bit, willBeSigned );
02692 setCte( allowedCte[0], entity );
02693 setBodyEncodedBinary( aBuf, entity );
02694 }
02695
02696
02697
02698 void KMMessage::setBodyAndGuessCte( const QCString& aBuf,
02699 QValueList<int> & allowedCte,
02700 bool allow8Bit,
02701 bool willBeSigned,
02702 DwEntity *entity )
02703 {
02704 if ( !entity )
02705 entity = mMsg;
02706
02707 CharFreq cf( aBuf.data(), aBuf.size()-1 );
02708 allowedCte = determineAllowedCtes( cf, allow8Bit, willBeSigned );
02709 setCte( allowedCte[0], entity );
02710 setBodyEncoded( aBuf, entity );
02711 }
02712
02713
02714
02715 void KMMessage::setBodyEncoded(const QCString& aStr, DwEntity *entity )
02716 {
02717 if ( !entity )
02718 entity = mMsg;
02719
02720 DwString dwSrc(aStr.data(), aStr.size()-1 );
02721 DwString dwResult;
02722
02723 switch (cte( entity ))
02724 {
02725 case DwMime::kCteBase64:
02726 DwEncodeBase64(dwSrc, dwResult);
02727 break;
02728 case DwMime::kCteQuotedPrintable:
02729 DwEncodeQuotedPrintable(dwSrc, dwResult);
02730 break;
02731 default:
02732 dwResult = dwSrc;
02733 break;
02734 }
02735
02736 entity->Body().FromString(dwResult);
02737 mNeedsAssembly = true;
02738 }
02739
02740
02741 void KMMessage::setBodyEncodedBinary( const QByteArray& aStr, DwEntity *entity )
02742 {
02743 if ( !entity )
02744 entity = mMsg;
02745
02746 DwString dwSrc(aStr.data(), aStr.size());
02747 DwString dwResult;
02748
02749 switch ( cte( entity ) )
02750 {
02751 case DwMime::kCteBase64:
02752 DwEncodeBase64( dwSrc, dwResult );
02753 break;
02754 case DwMime::kCteQuotedPrintable:
02755 DwEncodeQuotedPrintable( dwSrc, dwResult );
02756 break;
02757 default:
02758 dwResult = dwSrc;
02759 break;
02760 }
02761
02762 entity->Body().FromString( dwResult );
02763 entity->Body().Parse();
02764
02765 mNeedsAssembly = true;
02766 }
02767
02768
02769
02770 void KMMessage::setBody(const QCString& aStr)
02771 {
02772 mMsg->Body().FromString(KMail::Util::dwString(aStr));
02773 mNeedsAssembly = true;
02774 }
02775 void KMMessage::setBody(const DwString& aStr)
02776 {
02777 mMsg->Body().FromString(aStr);
02778 mNeedsAssembly = true;
02779 }
02780 void KMMessage::setBody(const char* aStr)
02781 {
02782 mMsg->Body().FromString(aStr);
02783 mNeedsAssembly = true;
02784 }
02785
02786
02787 void KMMessage::setMultiPartBody( const QCString & aStr ) {
02788 setBody( aStr );
02789 mMsg->Body().Parse();
02790 mNeedsAssembly = true;
02791 }
02792
02793
02794
02795
02796
02797
02798
02799
02800
02801
02802 int KMMessage::numBodyParts() const
02803 {
02804 int count = 0;
02805 DwBodyPart* part = getFirstDwBodyPart();
02806 QPtrList< DwBodyPart > parts;
02807
02808 while (part)
02809 {
02810
02811 while ( part
02812 && part->hasHeaders()
02813 && part->Headers().HasContentType()
02814 && part->Body().FirstBodyPart()
02815 && (DwMime::kTypeMultipart == part->Headers().ContentType().Type()) )
02816 {
02817 parts.append( part );
02818 part = part->Body().FirstBodyPart();
02819 }
02820
02821 count++;
02822
02823
02824 while (part && !(part->Next()) && !(parts.isEmpty()))
02825 {
02826 part = parts.getLast();
02827 parts.removeLast();
02828 }
02829
02830 if (part && part->Body().Message() &&
02831 part->Body().Message()->Body().FirstBodyPart())
02832 {
02833 part = part->Body().Message()->Body().FirstBodyPart();
02834 } else if (part) {
02835 part = part->Next();
02836 }
02837 }
02838
02839 return count;
02840 }
02841
02842
02843
02844 DwBodyPart * KMMessage::getFirstDwBodyPart() const
02845 {
02846 return mMsg->Body().FirstBodyPart();
02847 }
02848
02849
02850
02851 int KMMessage::partNumber( DwBodyPart * aDwBodyPart ) const
02852 {
02853 DwBodyPart *curpart;
02854 QPtrList< DwBodyPart > parts;
02855 int curIdx = 0;
02856 int idx = 0;
02857
02858
02859 curpart = getFirstDwBodyPart();
02860
02861 while (curpart && !idx) {
02862
02863 while( curpart
02864 && curpart->hasHeaders()
02865 && curpart->Headers().HasContentType()
02866 && curpart->Body().FirstBodyPart()
02867 && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) )
02868 {
02869 parts.append( curpart );
02870 curpart = curpart->Body().FirstBodyPart();
02871 }
02872
02873 if (curpart == aDwBodyPart)
02874 idx = curIdx;
02875 curIdx++;
02876
02877
02878 while (curpart && !(curpart->Next()) && !(parts.isEmpty()))
02879 {
02880 curpart = parts.getLast();
02881 parts.removeLast();
02882 } ;
02883 if (curpart)
02884 curpart = curpart->Next();
02885 }
02886 return idx;
02887 }
02888
02889
02890
02891 DwBodyPart * KMMessage::dwBodyPart( int aIdx ) const
02892 {
02893 DwBodyPart *part, *curpart;
02894 QPtrList< DwBodyPart > parts;
02895 int curIdx = 0;
02896
02897
02898 curpart = getFirstDwBodyPart();
02899 part = 0;
02900
02901 while (curpart && !part) {
02902
02903 while( curpart
02904 && curpart->hasHeaders()
02905 && curpart->Headers().HasContentType()
02906 && curpart->Body().FirstBodyPart()
02907 && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) )
02908 {
02909 parts.append( curpart );
02910 curpart = curpart->Body().FirstBodyPart();
02911 }
02912
02913 if (curIdx==aIdx)
02914 part = curpart;
02915 curIdx++;
02916
02917
02918 while (curpart && !(curpart->Next()) && !(parts.isEmpty()))
02919 {
02920 curpart = parts.getLast();
02921 parts.removeLast();
02922 }
02923 if (curpart)
02924 curpart = curpart->Next();
02925 }
02926 return part;
02927 }
02928
02929
02930
02931 DwBodyPart * KMMessage::findDwBodyPart( int type, int subtype ) const
02932 {
02933 DwBodyPart *part, *curpart;
02934 QPtrList< DwBodyPart > parts;
02935
02936
02937 curpart = getFirstDwBodyPart();
02938 part = 0;
02939
02940 while (curpart && !part) {
02941
02942 while(curpart
02943 && curpart->hasHeaders()
02944 && curpart->Headers().HasContentType()
02945 && curpart->Body().FirstBodyPart()
02946 && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) ) {
02947 parts.append( curpart );
02948 curpart = curpart->Body().FirstBodyPart();
02949 }
02950
02951
02952
02953
02954 if ( curpart && curpart->hasHeaders() && curpart->Headers().HasContentType() ) {
02955 kdDebug(5006) << curpart->Headers().ContentType().TypeStr().c_str()
02956 << " " << curpart->Headers().ContentType().SubtypeStr().c_str() << endl;
02957 }
02958
02959 if (curpart &&
02960 curpart->hasHeaders() &&
02961 curpart->Headers().HasContentType() &&
02962 curpart->Headers().ContentType().Type() == type &&
02963 curpart->Headers().ContentType().Subtype() == subtype) {
02964 part = curpart;
02965 } else {
02966
02967
02968 while (curpart && !(curpart->Next()) && !(parts.isEmpty())) {
02969 curpart = parts.getLast();
02970 parts.removeLast();
02971 } ;
02972 if (curpart)
02973 curpart = curpart->Next();
02974 }
02975 }
02976 return part;
02977 }
02978
02979
02980 DwBodyPart * KMMessage::findDwBodyPart( const QCString& type, const QCString& subtype ) const
02981 {
02982 DwBodyPart *part, *curpart;
02983 QPtrList< DwBodyPart > parts;
02984
02985
02986 curpart = getFirstDwBodyPart();
02987 part = 0;
02988
02989 while (curpart && !part) {
02990
02991 while(curpart
02992 && curpart->hasHeaders()
02993 && curpart->Headers().HasContentType()
02994 && curpart->Body().FirstBodyPart()
02995 && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) ) {
02996 parts.append( curpart );
02997 curpart = curpart->Body().FirstBodyPart();
02998 }
02999
03000
03001
03002
03003 if (curpart && curpart->hasHeaders() && curpart->Headers().HasContentType() ) {
03004 kdDebug(5006) << curpart->Headers().ContentType().TypeStr().c_str()
03005 << " " << curpart->Headers().ContentType().SubtypeStr().c_str() << endl;
03006 }
03007
03008 if (curpart &&
03009 curpart->hasHeaders() &&
03010 curpart->Headers().HasContentType() &&
03011 curpart->Headers().ContentType().TypeStr().c_str() == type &&
03012 curpart->Headers().ContentType().SubtypeStr().c_str() == subtype) {
03013 part = curpart;
03014 } else {
03015
03016
03017 while (curpart && !(curpart->Next()) && !(parts.isEmpty())) {
03018 curpart = parts.getLast();
03019 parts.removeLast();
03020 } ;
03021 if (curpart)
03022 curpart = curpart->Next();
03023 }
03024 }
03025 return part;
03026 }
03027
03028
03029 void applyHeadersToMessagePart( DwHeaders& headers, KMMessagePart* aPart )
03030 {
03031
03032
03033
03034
03035
03036
03037
03038
03039
03040
03041 QCString additionalCTypeParams;
03042 if (headers.HasContentType())
03043 {
03044 DwMediaType& ct = headers.ContentType();
03045 aPart->setOriginalContentTypeStr( ct.AsString().c_str() );
03046 aPart->setTypeStr(ct.TypeStr().c_str());
03047 aPart->setSubtypeStr(ct.SubtypeStr().c_str());
03048 DwParameter *param = ct.FirstParameter();
03049 while(param)
03050 {
03051 if (!qstricmp(param->Attribute().c_str(), "charset"))
03052 aPart->setCharset(QCString(param->Value().c_str()).lower());
03053 else if (!qstrnicmp(param->Attribute().c_str(), "name*", 5))
03054 aPart->setName(KMMsgBase::decodeRFC2231String(KMMsgBase::extractRFC2231HeaderField( param->Value().c_str(), "name" )));
03055 else {
03056 additionalCTypeParams += ';';
03057 additionalCTypeParams += param->AsString().c_str();
03058 }
03059 param=param->Next();
03060 }
03061 }
03062 else
03063 {
03064 aPart->setTypeStr("text");
03065 aPart->setSubtypeStr("plain");
03066 }
03067 aPart->setAdditionalCTypeParamStr( additionalCTypeParams );
03068
03069 if (aPart->name().isEmpty())
03070 {
03071 if (headers.HasContentType() && !headers.ContentType().Name().empty()) {
03072 aPart->setName(KMMsgBase::decodeRFC2047String(headers.
03073 ContentType().Name().c_str()) );
03074 } else if (headers.HasSubject() && !headers.Subject().AsString().empty()) {
03075 aPart->setName( KMMsgBase::decodeRFC2047String(headers.
03076 Subject().AsString().c_str()) );
03077 }
03078 }
03079
03080
03081 if (headers.HasContentTransferEncoding())
03082 aPart->setCteStr(headers.ContentTransferEncoding().AsString().c_str());
03083 else
03084 aPart->setCteStr("7bit");
03085
03086
03087 if (headers.HasContentDescription())
03088 aPart->setContentDescription(headers.ContentDescription().AsString().c_str());
03089 else
03090 aPart->setContentDescription("");
03091
03092
03093 if (headers.HasContentDisposition())
03094 aPart->setContentDisposition(headers.ContentDisposition().AsString().c_str());
03095 else
03096 aPart->setContentDisposition("");
03097 }
03098
03099
03100 void KMMessage::bodyPart(DwBodyPart* aDwBodyPart, KMMessagePart* aPart,
03101 bool withBody)
03102 {
03103 if ( !aPart )
03104 return;
03105
03106 aPart->clear();
03107
03108 if( aDwBodyPart && aDwBodyPart->hasHeaders() ) {
03109
03110
03111
03112
03113 QString partId( aDwBodyPart->partId() );
03114 aPart->setPartSpecifier( partId );
03115
03116 DwHeaders& headers = aDwBodyPart->Headers();
03117 applyHeadersToMessagePart( headers, aPart );
03118
03119
03120 if (withBody)
03121 aPart->setBody( aDwBodyPart->Body().AsString() );
03122 else
03123 aPart->setBody( QCString("") );
03124
03125
03126 if ( headers.HasContentId() ) {
03127 const QCString contentId = headers.ContentId().AsString().c_str();
03128
03129 aPart->setContentId( contentId.mid( 1, contentId.length() - 2 ) );
03130 }
03131 }
03132
03133
03134 else
03135 {
03136 aPart->setTypeStr("");
03137 aPart->setSubtypeStr("");
03138 aPart->setCteStr("");
03139
03140
03141
03142 aPart->setContentDescription("");
03143 aPart->setContentDisposition("");
03144 aPart->setBody(QCString(""));
03145 aPart->setContentId("");
03146 }
03147 }
03148
03149
03150
03151 void KMMessage::bodyPart(int aIdx, KMMessagePart* aPart) const
03152 {
03153 if ( !aPart )
03154 return;
03155
03156
03157 if ( DwBodyPart *part = dwBodyPart( aIdx ) ) {
03158 KMMessage::bodyPart(part, aPart);
03159 if( aPart->name().isEmpty() )
03160 aPart->setName( i18n("Attachment: %1").arg( aIdx ) );
03161 }
03162 }
03163
03164
03165
03166 void KMMessage::deleteBodyParts()
03167 {
03168 mMsg->Body().DeleteBodyParts();
03169 }
03170
03171
03172
03173 bool KMMessage::deleteBodyPart( int partIndex )
03174 {
03175 KMMessagePart part;
03176 DwBodyPart *dwpart = findPart( partIndex );
03177 if ( !dwpart )
03178 return false;
03179 KMMessage::bodyPart( dwpart, &part, true );
03180 if ( !part.isComplete() )
03181 return false;
03182
03183 DwBody *parentNode = dynamic_cast<DwBody*>( dwpart->Parent() );
03184 if ( !parentNode )
03185 return false;
03186 parentNode->RemoveBodyPart( dwpart );
03187
03188
03189 KMMessagePart dummyPart;
03190 dummyPart.duplicate( part );
03191 QString comment = i18n("This attachment has been deleted.");
03192 if ( !part.fileName().isEmpty() )
03193 comment = i18n( "The attachment '%1' has been deleted." ).arg( part.fileName() );
03194 dummyPart.setContentDescription( comment );
03195 dummyPart.setBodyEncodedBinary( QByteArray() );
03196 QCString cd = dummyPart.contentDisposition();
03197 if ( cd.find( "inline", 0, false ) == 0 ) {
03198 cd.replace( 0, 10, "attachment" );
03199 dummyPart.setContentDisposition( cd );
03200 } else if ( cd.isEmpty() ) {
03201 dummyPart.setContentDisposition( "attachment" );
03202 }
03203 DwBodyPart* newDwPart = createDWBodyPart( &dummyPart );
03204 parentNode->AddBodyPart( newDwPart );
03205 getTopLevelPart()->Assemble();
03206 return true;
03207 }
03208
03209
03210 DwBodyPart* KMMessage::createDWBodyPart(const KMMessagePart* aPart)
03211 {
03212 DwBodyPart* part = DwBodyPart::NewBodyPart(emptyString, 0);
03213
03214 if ( !aPart )
03215 return part;
03216
03217 QCString charset = aPart->charset();
03218 QCString type = aPart->typeStr();
03219 QCString subtype = aPart->subtypeStr();
03220 QCString cte = aPart->cteStr();
03221 QCString contDesc = aPart->contentDescriptionEncoded();
03222 QCString contDisp = aPart->contentDisposition();
03223 QCString encoding = autoDetectCharset(charset, sPrefCharsets, aPart->name());
03224 if (encoding.isEmpty()) encoding = "utf-8";
03225 QCString name = KMMsgBase::encodeRFC2231String(aPart->name(), encoding);
03226 bool RFC2231encoded = aPart->name() != QString(name);
03227 QCString paramAttr = aPart->parameterAttribute();
03228
03229 DwHeaders& headers = part->Headers();
03230
03231 DwMediaType& ct = headers.ContentType();
03232 if (!type.isEmpty() && !subtype.isEmpty())
03233 {
03234 ct.SetTypeStr(type.data());
03235 ct.SetSubtypeStr(subtype.data());
03236 if (!charset.isEmpty()){
03237 DwParameter *param;
03238 param=new DwParameter;
03239 param->SetAttribute("charset");
03240 param->SetValue(charset.data());
03241 ct.AddParameter(param);
03242 }
03243 }
03244
03245 QCString additionalParam = aPart->additionalCTypeParamStr();
03246 if( !additionalParam.isEmpty() )
03247 {
03248 QCString parAV;
03249 DwString parA, parV;
03250 int iL, i1, i2, iM;
03251 iL = additionalParam.length();
03252 i1 = 0;
03253 i2 = additionalParam.find(';', i1, false);
03254 while ( i1 < iL )
03255 {
03256 if( -1 == i2 )
03257 i2 = iL;
03258 if( i1+1 < i2 ) {
03259 parAV = additionalParam.mid( i1, (i2-i1) );
03260 iM = parAV.find('=');
03261 if( -1 < iM )
03262 {
03263 parA = parAV.left( iM );
03264 parV = parAV.right( parAV.length() - iM - 1 );
03265 if( ('"' == parV.at(0)) && ('"' == parV.at(parV.length()-1)) )
03266 {
03267 parV.erase( 0, 1);
03268 parV.erase( parV.length()-1 );
03269 }
03270 }
03271 else
03272 {
03273 parA = parAV;
03274 parV = "";
03275 }
03276 DwParameter *param;
03277 param = new DwParameter;
03278 param->SetAttribute( parA );
03279 param->SetValue( parV );
03280 ct.AddParameter( param );
03281 }
03282 i1 = i2+1;
03283 i2 = additionalParam.find(';', i1, false);
03284 }
03285 }
03286
03287 if ( !name.isEmpty() ) {
03288 if (RFC2231encoded)
03289 {
03290 DwParameter *nameParam;
03291 nameParam = new DwParameter;
03292 nameParam->SetAttribute("name*");
03293 nameParam->SetValue(name.data(),true);
03294 ct.AddParameter(nameParam);
03295 } else {
03296 ct.SetName(name.data());
03297 }
03298 }
03299
03300 if (!paramAttr.isEmpty())
03301 {
03302 QCString encoding = autoDetectCharset(charset, sPrefCharsets,
03303 aPart->parameterValue());
03304 if (encoding.isEmpty()) encoding = "utf-8";
03305 QCString paramValue;
03306 paramValue = KMMsgBase::encodeRFC2231String(aPart->parameterValue(),
03307 encoding);
03308 DwParameter *param = new DwParameter;
03309 if (aPart->parameterValue() != QString(paramValue))
03310 {
03311 param->SetAttribute((paramAttr + '*').data());
03312 param->SetValue(paramValue.data(),true);
03313 } else {
03314 param->SetAttribute(paramAttr.data());
03315 param->SetValue(paramValue.data());
03316 }
03317 ct.AddParameter(param);
03318 }
03319
03320 if (!cte.isEmpty())
03321 headers.Cte().FromString(cte);
03322
03323 if (!contDesc.isEmpty())
03324 headers.ContentDescription().FromString(contDesc);
03325
03326 if (!contDisp.isEmpty())
03327 headers.ContentDisposition().FromString(contDisp);
03328
03329 const DwString bodyStr = aPart->dwBody();
03330 if (!bodyStr.empty())
03331 part->Body().FromString(bodyStr);
03332 else
03333 part->Body().FromString("");
03334
03335 if (!aPart->partSpecifier().isNull())
03336 part->SetPartId( aPart->partSpecifier().latin1() );
03337
03338 if (aPart->decodedSize() > 0)
03339 part->SetBodySize( aPart->decodedSize() );
03340
03341 return part;
03342 }
03343
03344
03345
03346 void KMMessage::addDwBodyPart(DwBodyPart * aDwPart)
03347 {
03348 mMsg->Body().AddBodyPart( aDwPart );
03349 mNeedsAssembly = true;
03350 }
03351
03352
03353
03354 void KMMessage::addBodyPart(const KMMessagePart* aPart)
03355 {
03356 DwBodyPart* part = createDWBodyPart( aPart );
03357 addDwBodyPart( part );
03358 }
03359
03360
03361
03362 QString KMMessage::generateMessageId( const QString& addr )
03363 {
03364 QDateTime datetime = QDateTime::currentDateTime();
03365 QString msgIdStr;
03366
03367 msgIdStr = '<' + datetime.toString( "yyyyMMddhhmm.sszzz" );
03368
03369 QString msgIdSuffix;
03370 KConfigGroup general( KMKernel::config(), "General" );
03371
03372 if( general.readBoolEntry( "useCustomMessageIdSuffix", false ) )
03373 msgIdSuffix = general.readEntry( "myMessageIdSuffix" );
03374
03375 if( !msgIdSuffix.isEmpty() )
03376 msgIdStr += '@' + msgIdSuffix;
03377 else
03378 msgIdStr += '.' + KPIM::encodeIDN( addr );
03379
03380 msgIdStr += '>';
03381
03382 return msgIdStr;
03383 }
03384
03385
03386
03387 QCString KMMessage::html2source( const QCString & src )
03388 {
03389 QCString result( 1 + 6*(src.size()-1) );
03390
03391 QCString::ConstIterator s = src.begin();
03392 QCString::Iterator d = result.begin();
03393 while ( *s ) {
03394 switch ( *s ) {
03395 case '<': {
03396 *d++ = '&';
03397 *d++ = 'l';
03398 *d++ = 't';
03399 *d++ = ';';
03400 ++s;
03401 }
03402 break;
03403 case '\r': {
03404 ++s;
03405 }
03406 break;
03407 case '\n': {
03408 *d++ = '<';
03409 *d++ = 'b';
03410 *d++ = 'r';
03411 *d++ = '>';
03412 ++s;
03413 }
03414 break;
03415 case '>': {
03416 *d++ = '&';
03417 *d++ = 'g';
03418 *d++ = 't';
03419 *d++ = ';';
03420 ++s;
03421 }
03422 break;
03423 case '&': {
03424 *d++ = '&';
03425 *d++ = 'a';
03426 *d++ = 'm';
03427 *d++ = 'p';
03428 *d++ = ';';
03429 ++s;
03430 }
03431 break;
03432 case '"': {
03433 *d++ = '&';
03434 *d++ = 'q';
03435 *d++ = 'u';
03436 *d++ = 'o';
03437 *d++ = 't';
03438 *d++ = ';';
03439 ++s;
03440 }
03441 break;
03442 case '\'': {
03443 *d++ = '&';
03444 *d++ = 'a';
03445 *d++ = 'p';
03446 *d++ = 's';
03447 *d++ = ';';
03448 ++s;
03449 }
03450 break;
03451 default:
03452 *d++ = *s++;
03453 }
03454 }
03455 result.truncate( d - result.begin() );
03456 return result;
03457 }
03458
03459
03460 QString KMMessage::encodeMailtoUrl( const QString& str )
03461 {
03462 QString result;
03463 result = QString::fromLatin1( KMMsgBase::encodeRFC2047String( str,
03464 "utf-8" ) );
03465 result = KURL::encode_string( result );
03466 return result;
03467 }
03468
03469
03470
03471 QString KMMessage::decodeMailtoUrl( const QString& url )
03472 {
03473 QString result;
03474 result = KURL::decode_string( url );
03475 result = KMMsgBase::decodeRFC2047String( result.latin1() );
03476 return result;
03477 }
03478
03479
03480
03481 QCString KMMessage::stripEmailAddr( const QCString& aStr )
03482 {
03483
03484
03485 if ( aStr.isEmpty() )
03486 return QCString();
03487
03488 QCString result;
03489
03490
03491
03492
03493
03494 QCString name;
03495 QCString comment;
03496 QCString angleAddress;
03497 enum { TopLevel, InComment, InAngleAddress } context = TopLevel;
03498 bool inQuotedString = false;
03499 int commentLevel = 0;
03500
03501 for ( char* p = aStr.data(); *p; ++p ) {
03502 switch ( context ) {
03503 case TopLevel : {
03504 switch ( *p ) {
03505 case '"' : inQuotedString = !inQuotedString;
03506 break;
03507 case '(' : if ( !inQuotedString ) {
03508 context = InComment;
03509 commentLevel = 1;
03510 }
03511 else
03512 name += *p;
03513 break;
03514 case '<' : if ( !inQuotedString ) {
03515 context = InAngleAddress;
03516 }
03517 else
03518 name += *p;
03519 break;
03520 case '\\' :
03521 ++p;
03522 if ( *p )
03523 name += *p;
03524 break;
03525 case ',' : if ( !inQuotedString ) {
03526
03527 if ( !result.isEmpty() )
03528 result += ", ";
03529 name = name.stripWhiteSpace();
03530 comment = comment.stripWhiteSpace();
03531 angleAddress = angleAddress.stripWhiteSpace();
03532
03533
03534
03535
03536
03537
03538
03539
03540 if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
03541
03542
03543 result += comment;
03544 }
03545 else if ( !name.isEmpty() ) {
03546 result += name;
03547 }
03548 else if ( !comment.isEmpty() ) {
03549 result += comment;
03550 }
03551 else if ( !angleAddress.isEmpty() ) {
03552 result += angleAddress;
03553 }
03554 name = QCString();
03555 comment = QCString();
03556 angleAddress = QCString();
03557 }
03558 else
03559 name += *p;
03560 break;
03561 default : name += *p;
03562 }
03563 break;
03564 }
03565 case InComment : {
03566 switch ( *p ) {
03567 case '(' : ++commentLevel;
03568 comment += *p;
03569 break;
03570 case ')' : --commentLevel;
03571 if ( commentLevel == 0 ) {
03572 context = TopLevel;
03573 comment += ' ';
03574 }
03575 else
03576 comment += *p;
03577 break;
03578 case '\\' :
03579 ++p;
03580 if ( *p )
03581 comment += *p;
03582 break;
03583 default : comment += *p;
03584 }
03585 break;
03586 }
03587 case InAngleAddress : {
03588 switch ( *p ) {
03589 case '"' : inQuotedString = !inQuotedString;
03590 angleAddress += *p;
03591 break;
03592 case '>' : if ( !inQuotedString ) {
03593 context = TopLevel;
03594 }
03595 else
03596 angleAddress += *p;
03597 break;
03598 case '\\' :
03599 ++p;
03600 if ( *p )
03601 angleAddress += *p;
03602 break;
03603 default : angleAddress += *p;
03604 }
03605 break;
03606 }
03607 }
03608 }
03609 if ( !result.isEmpty() )
03610 result += ", ";
03611 name = name.stripWhiteSpace();
03612 comment = comment.stripWhiteSpace();
03613 angleAddress = angleAddress.stripWhiteSpace();
03614
03615
03616
03617
03618
03619 if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
03620
03621
03622 result += comment;
03623 }
03624 else if ( !name.isEmpty() ) {
03625 result += name;
03626 }
03627 else if ( !comment.isEmpty() ) {
03628 result += comment;
03629 }
03630 else if ( !angleAddress.isEmpty() ) {
03631 result += angleAddress;
03632 }
03633
03634
03635
03636 return result;
03637 }
03638
03639
03640 QString KMMessage::stripEmailAddr( const QString& aStr )
03641 {
03642
03643
03644 if ( aStr.isEmpty() )
03645 return QString::null;
03646
03647 QString result;
03648
03649
03650
03651
03652
03653 QString name;
03654 QString comment;
03655 QString angleAddress;
03656 enum { TopLevel, InComment, InAngleAddress } context = TopLevel;
03657 bool inQuotedString = false;
03658 int commentLevel = 0;
03659
03660 QChar ch;
03661 unsigned int strLength(aStr.length());
03662 for ( uint index = 0; index < strLength; ++index ) {
03663 ch = aStr[index];
03664 switch ( context ) {
03665 case TopLevel : {
03666 switch ( ch.latin1() ) {
03667 case '"' : inQuotedString = !inQuotedString;
03668 break;
03669 case '(' : if ( !inQuotedString ) {
03670 context = InComment;
03671 commentLevel = 1;
03672 }
03673 else
03674 name += ch;
03675 break;
03676 case '<' : if ( !inQuotedString ) {
03677 context = InAngleAddress;
03678 }
03679 else
03680 name += ch;
03681 break;
03682 case '\\' :
03683 ++index;
03684 if ( index < aStr.length() )
03685 name += aStr[index];
03686 break;
03687 case ',' : if ( !inQuotedString ) {
03688
03689 if ( !result.isEmpty() )
03690 result += ", ";
03691 name = name.stripWhiteSpace();
03692 comment = comment.stripWhiteSpace();
03693 angleAddress = angleAddress.stripWhiteSpace();
03694
03695
03696
03697
03698
03699
03700
03701
03702 if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
03703
03704
03705 result += comment;
03706 }
03707 else if ( !name.isEmpty() ) {
03708 result += name;
03709 }
03710 else if ( !comment.isEmpty() ) {
03711 result += comment;
03712 }
03713 else if ( !angleAddress.isEmpty() ) {
03714 result += angleAddress;
03715 }
03716 name = QString::null;
03717 comment = QString::null;
03718 angleAddress = QString::null;
03719 }
03720 else
03721 name += ch;
03722 break;
03723 default : name += ch;
03724 }
03725 break;
03726 }
03727 case InComment : {
03728 switch ( ch.latin1() ) {
03729 case '(' : ++commentLevel;
03730 comment += ch;
03731 break;
03732 case ')' : --commentLevel;
03733 if ( commentLevel == 0 ) {
03734 context = TopLevel;
03735 comment += ' ';
03736 }
03737 else
03738 comment += ch;
03739 break;
03740 case '\\' :
03741 ++index;
03742 if ( index < aStr.length() )
03743 comment += aStr[index];
03744 break;
03745 default : comment += ch;
03746 }
03747 break;
03748 }
03749 case InAngleAddress : {
03750 switch ( ch.latin1() ) {
03751 case '"' : inQuotedString = !inQuotedString;
03752 angleAddress += ch;
03753 break;
03754 case '>' : if ( !inQuotedString ) {
03755 context = TopLevel;
03756 }
03757 else
03758 angleAddress += ch;
03759 break;
03760 case '\\' :
03761 ++index;
03762 if ( index < aStr.length() )
03763 angleAddress += aStr[index];
03764 break;
03765 default : angleAddress += ch;
03766 }
03767 break;
03768 }
03769 }
03770 }
03771 if ( !result.isEmpty() )
03772 result += ", ";
03773 name = name.stripWhiteSpace();
03774 comment = comment.stripWhiteSpace();
03775 angleAddress = angleAddress.stripWhiteSpace();
03776
03777
03778
03779
03780
03781 if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
03782
03783
03784 result += comment;
03785 }
03786 else if ( !name.isEmpty() ) {
03787 result += name;
03788 }
03789 else if ( !comment.isEmpty() ) {
03790 result += comment;
03791 }
03792 else if ( !angleAddress.isEmpty() ) {
03793 result += angleAddress;
03794 }
03795
03796
03797
03798 return result;
03799 }
03800
03801
03802 QString KMMessage::quoteHtmlChars( const QString& str, bool removeLineBreaks )
03803 {
03804 QString result;
03805
03806 unsigned int strLength(str.length());
03807 result.reserve( 6*strLength );
03808 for( unsigned int i = 0; i < strLength; ++i )
03809 switch ( str[i].latin1() ) {
03810 case '<':
03811 result += "<";
03812 break;
03813 case '>':
03814 result += ">";
03815 break;
03816 case '&':
03817 result += "&";
03818 break;
03819 case '"':
03820 result += """;
03821 break;
03822 case '\n':
03823 if ( !removeLineBreaks )
03824 result += "<br>";
03825 break;
03826 case '\r':
03827
03828 break;
03829 default:
03830 result += str[i];
03831 }
03832
03833 result.squeeze();
03834 return result;
03835 }
03836
03837
03838 QString KMMessage::emailAddrAsAnchor(const QString& aEmail, bool stripped, const QString& cssStyle, bool aLink)
03839 {
03840 if( aEmail.isEmpty() )
03841 return aEmail;
03842
03843 QStringList addressList = KPIM::splitEmailAddrList( aEmail );
03844
03845 QString result;
03846
03847 for( QStringList::ConstIterator it = addressList.begin();
03848 ( it != addressList.end() );
03849 ++it ) {
03850 if( !(*it).isEmpty() ) {
03851 QString address = *it;
03852 if(aLink) {
03853 result += "<a href=\"mailto:"
03854 + KMMessage::encodeMailtoUrl( address )
03855 + "\" "+cssStyle+">";
03856 }
03857 if( stripped )
03858 address = KMMessage::stripEmailAddr( address );
03859 result += KMMessage::quoteHtmlChars( address, true );
03860 if(aLink)
03861 result += "</a>, ";
03862 }
03863 }
03864
03865 if(aLink)
03866 result.truncate( result.length() - 2 );
03867
03868
03869
03870 return result;
03871 }
03872
03873
03874
03875
03876 QStringList KMMessage::stripAddressFromAddressList( const QString& address,
03877 const QStringList& list )
03878 {
03879 QStringList addresses( list );
03880 QString addrSpec( KPIM::getEmailAddress( address ) );
03881 for ( QStringList::Iterator it = addresses.begin();
03882 it != addresses.end(); ) {
03883 if ( kasciistricmp( addrSpec.utf8().data(),
03884 KPIM::getEmailAddress( *it ).utf8().data() ) == 0 ) {
03885 kdDebug(5006) << "Removing " << *it << " from the address list"
03886 << endl;
03887 it = addresses.remove( it );
03888 }
03889 else
03890 ++it;
03891 }
03892 return addresses;
03893 }
03894
03895
03896
03897
03898 QStringList KMMessage::stripMyAddressesFromAddressList( const QStringList& list )
03899 {
03900 QStringList addresses = list;
03901 for( QStringList::Iterator it = addresses.begin();
03902 it != addresses.end(); ) {
03903 kdDebug(5006) << "Check whether " << *it << " is one of my addresses"
03904 << endl;
03905 if( kmkernel->identityManager()->thatIsMe( KPIM::getEmailAddress( *it ) ) ) {
03906 kdDebug(5006) << "Removing " << *it << " from the address list"
03907 << endl;
03908 it = addresses.remove( it );
03909 }
03910 else
03911 ++it;
03912 }
03913 return addresses;
03914 }
03915
03916
03917
03918
03919 bool KMMessage::addressIsInAddressList( const QString& address,
03920 const QStringList& addresses )
03921 {
03922 QString addrSpec = KPIM::getEmailAddress( address );
03923 for( QStringList::ConstIterator it = addresses.begin();
03924 it != addresses.end(); ++it ) {
03925 if ( kasciistricmp( addrSpec.utf8().data(),
03926 KPIM::getEmailAddress( *it ).utf8().data() ) == 0 )
03927 return true;
03928 }
03929 return false;
03930 }
03931
03932
03933
03934
03935 QString KMMessage::expandAliases( const QString& recipients )
03936 {
03937 if ( recipients.isEmpty() )
03938 return QString();
03939
03940 QStringList recipientList = KPIM::splitEmailAddrList( recipients );
03941
03942 QString expandedRecipients;
03943 for ( QStringList::Iterator it = recipientList.begin();
03944 it != recipientList.end(); ++it ) {
03945 if ( !expandedRecipients.isEmpty() )
03946 expandedRecipients += ", ";
03947 QString receiver = (*it).stripWhiteSpace();
03948
03949
03950 QString expandedList = KAddrBookExternal::expandDistributionList( receiver );
03951 if ( !expandedList.isEmpty() ) {
03952 expandedRecipients += expandedList;
03953 continue;
03954 }
03955
03956
03957 QString expandedNickName = KabcBridge::expandNickName( receiver );
03958 if ( !expandedNickName.isEmpty() ) {
03959 expandedRecipients += expandedNickName;
03960 continue;
03961 }
03962
03963
03964
03965 if ( receiver.find('@') == -1 ) {
03966 KConfigGroup general( KMKernel::config(), "General" );
03967 QString defaultdomain = general.readEntry( "Default domain" );
03968 if( !defaultdomain.isEmpty() ) {
03969 expandedRecipients += receiver + "@" + defaultdomain;
03970 }
03971 else {
03972 expandedRecipients += guessEmailAddressFromLoginName( receiver );
03973 }
03974 }
03975 else
03976 expandedRecipients += receiver;
03977 }
03978
03979 return expandedRecipients;
03980 }
03981
03982
03983
03984
03985 QString KMMessage::guessEmailAddressFromLoginName( const QString& loginName )
03986 {
03987 if ( loginName.isEmpty() )
03988 return QString();
03989
03990 char hostnameC[256];
03991
03992 hostnameC[255] = '\0';
03993
03994 if ( gethostname( hostnameC, 255 ) )
03995 hostnameC[0] = '\0';
03996 QString address = loginName;
03997 address += '@';
03998 address += QString::fromLocal8Bit( hostnameC );
03999
04000
04001 const KUser user( loginName );
04002 if ( user.isValid() ) {
04003 QString fullName = user.fullName();
04004 if ( fullName.find( QRegExp( "[^ 0-9A-Za-z\\x0080-\\xFFFF]" ) ) != -1 )
04005 address = '"' + fullName.replace( '\\', "\\" ).replace( '"', "\\" )
04006 + "\" <" + address + '>';
04007 else
04008 address = fullName + " <" + address + '>';
04009 }
04010
04011 return address;
04012 }
04013
04014
04015 void KMMessage::readConfig()
04016 {
04017 KMMsgBase::readConfig();
04018
04019 KConfig *config=KMKernel::config();
04020 KConfigGroupSaver saver(config, "General");
04021
04022 config->setGroup("General");
04023
04024 int languageNr = config->readNumEntry("reply-current-language",0);
04025
04026 {
04027 KConfigGroupSaver saver(config, QString("KMMessage #%1").arg(languageNr));
04028 sReplyLanguage = config->readEntry("language",KGlobal::locale()->language());
04029 sReplyStr = config->readEntry("phrase-reply",
04030 i18n("On %D, you wrote:"));
04031 sReplyAllStr = config->readEntry("phrase-reply-all",
04032 i18n("On %D, %F wrote:"));
04033 sForwardStr = config->readEntry("phrase-forward",
04034 i18n("Forwarded Message"));
04035 sIndentPrefixStr = config->readEntry("indent-prefix",">%_");
04036 }
04037
04038 {
04039 KConfigGroupSaver saver(config, "Composer");
04040 sSmartQuote = GlobalSettings::self()->smartQuote();
04041 sWordWrap = GlobalSettings::self()->wordWrap();
04042 sWrapCol = GlobalSettings::self()->lineWrapWidth();
04043 if ((sWrapCol == 0) || (sWrapCol > 78))
04044 sWrapCol = 78;
04045 if (sWrapCol < 30)
04046 sWrapCol = 30;
04047
04048 sPrefCharsets = config->readListEntry("pref-charsets");
04049 }
04050
04051 {
04052 KConfigGroupSaver saver(config, "Reader");
04053 sHeaderStrategy = HeaderStrategy::create( config->readEntry( "header-set-displayed", "rich" ) );
04054 }
04055 }
04056
04057 QCString KMMessage::defaultCharset()
04058 {
04059 QCString retval;
04060
04061 if (!sPrefCharsets.isEmpty())
04062 retval = sPrefCharsets[0].latin1();
04063
04064 if (retval.isEmpty() || (retval == "locale")) {
04065 retval = QCString(kmkernel->networkCodec()->mimeName());
04066 KPIM::kAsciiToLower( retval.data() );
04067 }
04068
04069 if (retval == "jisx0208.1983-0") retval = "iso-2022-jp";
04070 else if (retval == "ksc5601.1987-0") retval = "euc-kr";
04071 return retval;
04072 }
04073
04074 const QStringList &KMMessage::preferredCharsets()
04075 {
04076 return sPrefCharsets;
04077 }
04078
04079
04080 QCString KMMessage::charset() const
04081 {
04082 if ( mMsg->Headers().HasContentType() ) {
04083 DwMediaType &mType=mMsg->Headers().ContentType();
04084 mType.Parse();
04085 DwParameter *param=mType.FirstParameter();
04086 while(param){
04087 if (!kasciistricmp(param->Attribute().c_str(), "charset"))
04088 return param->Value().c_str();
04089 else param=param->Next();
04090 }
04091 }
04092 return "";
04093 }
04094
04095
04096 void KMMessage::setCharset( const QCString &charset, DwEntity *entity )
04097 {
04098 kdWarning( type() != DwMime::kTypeText )
04099 << "KMMessage::setCharset(): trying to set a charset for a non-textual mimetype." << endl
04100 << "Fix this caller:" << endl
04101 << "====================================================================" << endl
04102 << kdBacktrace( 5 ) << endl
04103 << "====================================================================" << endl;
04104
04105 if ( !entity )
04106 entity = mMsg;
04107
04108 DwMediaType &mType = entity->Headers().ContentType();
04109 mType.Parse();
04110 DwParameter *param = mType.FirstParameter();
04111 while( param ) {
04112
04113
04114 if ( !kasciistricmp( param->Attribute().c_str(), "charset" ) )
04115 break;
04116
04117 param = param->Next();
04118 }
04119 if ( !param ) {
04120 param = new DwParameter;
04121 param->SetAttribute( "charset" );
04122 mType.AddParameter( param );
04123 }
04124 else
04125 mType.SetModified();
04126
04127 QCString lowerCharset = charset;
04128 KPIM::kAsciiToLower( lowerCharset.data() );
04129 param->SetValue( DwString( lowerCharset ) );
04130 mType.Assemble();
04131 }
04132
04133
04134
04135 void KMMessage::setStatus(const KMMsgStatus aStatus, int idx)
04136 {
04137 if (mStatus == aStatus)
04138 return;
04139 KMMsgBase::setStatus(aStatus, idx);
04140 }
04141
04142 void KMMessage::setEncryptionState(const KMMsgEncryptionState s, int idx)
04143 {
04144 if( mEncryptionState == s )
04145 return;
04146 mEncryptionState = s;
04147 mDirty = true;
04148 KMMsgBase::setEncryptionState(s, idx);
04149 }
04150
04151 void KMMessage::setSignatureState(KMMsgSignatureState s, int idx)
04152 {
04153 if( mSignatureState == s )
04154 return;
04155 mSignatureState = s;
04156 mDirty = true;
04157 KMMsgBase::setSignatureState(s, idx);
04158 }
04159
04160 void KMMessage::setMDNSentState( KMMsgMDNSentState status, int idx ) {
04161 if ( mMDNSentState == status )
04162 return;
04163 if ( status == 0 )
04164 status = KMMsgMDNStateUnknown;
04165 mMDNSentState = status;
04166 mDirty = true;
04167 KMMsgBase::setMDNSentState( status, idx );
04168 }
04169
04170
04171 void KMMessage::link( const KMMessage *aMsg, KMMsgStatus aStatus )
04172 {
04173 Q_ASSERT( aStatus == KMMsgStatusReplied
04174 || aStatus == KMMsgStatusForwarded
04175 || aStatus == KMMsgStatusDeleted );
04176
04177 QString message = headerField( "X-KMail-Link-Message" );
04178 if ( !message.isEmpty() )
04179 message += ',';
04180 QString type = headerField( "X-KMail-Link-Type" );
04181 if ( !type.isEmpty() )
04182 type += ',';
04183
04184 message += QString::number( aMsg->getMsgSerNum() );
04185 if ( aStatus == KMMsgStatusReplied )
04186 type += "reply";
04187 else if ( aStatus == KMMsgStatusForwarded )
04188 type += "forward";
04189 else if ( aStatus == KMMsgStatusDeleted )
04190 type += "deleted";
04191
04192 setHeaderField( "X-KMail-Link-Message", message );
04193 setHeaderField( "X-KMail-Link-Type", type );
04194 }
04195
04196
04197 void KMMessage::getLink(int n, ulong *retMsgSerNum, KMMsgStatus *retStatus) const
04198 {
04199 *retMsgSerNum = 0;
04200 *retStatus = KMMsgStatusUnknown;
04201
04202 QString message = headerField("X-KMail-Link-Message");
04203 QString type = headerField("X-KMail-Link-Type");
04204 message = message.section(',', n, n);
04205 type = type.section(',', n, n);
04206
04207 if ( !message.isEmpty() && !type.isEmpty() ) {
04208 *retMsgSerNum = message.toULong();
04209 if ( type == "reply" )
04210 *retStatus = KMMsgStatusReplied;
04211 else if ( type == "forward" )
04212 *retStatus = KMMsgStatusForwarded;
04213 else if ( type == "deleted" )
04214 *retStatus = KMMsgStatusDeleted;
04215 }
04216 }
04217
04218
04219 DwBodyPart* KMMessage::findDwBodyPart( DwBodyPart* part, const QString & partSpecifier )
04220 {
04221 if ( !part ) return 0;
04222 DwBodyPart* current;
04223
04224 if ( part->partId() == partSpecifier )
04225 return part;
04226
04227
04228 if ( part->hasHeaders() &&
04229 part->Headers().HasContentType() &&
04230 part->Body().FirstBodyPart() &&
04231 (DwMime::kTypeMultipart == part->Headers().ContentType().Type() ) &&
04232 (current = findDwBodyPart( part->Body().FirstBodyPart(), partSpecifier )) )
04233 {
04234 return current;
04235 }
04236
04237
04238 if ( part->Body().Message() &&
04239 part->Body().Message()->Body().FirstBodyPart() &&
04240 (current = findDwBodyPart( part->Body().Message()->Body().FirstBodyPart(),
04241 partSpecifier )) )
04242 {
04243 return current;
04244 }
04245
04246
04247 return findDwBodyPart( part->Next(), partSpecifier );
04248 }
04249
04250
04251 void KMMessage::updateBodyPart(const QString partSpecifier, const QByteArray & data)
04252 {
04253 if ( !data.data() || !data.size() )
04254 return;
04255
04256 DwString content( data.data(), data.size() );
04257 if ( numBodyParts() > 0 &&
04258 partSpecifier != "0" &&
04259 partSpecifier != "TEXT" )
04260 {
04261 QString specifier = partSpecifier;
04262 if ( partSpecifier.endsWith(".HEADER") ||
04263 partSpecifier.endsWith(".MIME") ) {
04264
04265 specifier = partSpecifier.section( '.', 0, -2 );
04266 }
04267
04268
04269 mLastUpdated = findDwBodyPart( getFirstDwBodyPart(), specifier );
04270 kdDebug(5006) << "KMMessage::updateBodyPart " << specifier << endl;
04271 if (!mLastUpdated)
04272 {
04273 kdWarning(5006) << "KMMessage::updateBodyPart - can not find part "
04274 << specifier << endl;
04275 return;
04276 }
04277 if ( partSpecifier.endsWith(".MIME") )
04278 {
04279
04280
04281 content.resize( QMAX( content.length(), 2 ) - 2 );
04282
04283
04284 mLastUpdated->Headers().DeleteAllFields();
04285 mLastUpdated->Headers().FromString( content );
04286 mLastUpdated->Headers().Parse();
04287 } else if ( partSpecifier.endsWith(".HEADER") )
04288 {
04289
04290 mLastUpdated->Body().Message()->Headers().FromString( content );
04291 mLastUpdated->Body().Message()->Headers().Parse();
04292 } else {
04293
04294 mLastUpdated->Body().FromString( content );
04295 QString parentSpec = partSpecifier.section( '.', 0, -2 );
04296 if ( !parentSpec.isEmpty() )
04297 {
04298 DwBodyPart* parent = findDwBodyPart( getFirstDwBodyPart(), parentSpec );
04299 if ( parent && parent->hasHeaders() && parent->Headers().HasContentType() )
04300 {
04301 const DwMediaType& contentType = parent->Headers().ContentType();
04302 if ( contentType.Type() == DwMime::kTypeMessage &&
04303 contentType.Subtype() == DwMime::kSubtypeRfc822 )
04304 {
04305
04306
04307 parent->Body().Message()->Body().FromString( content );
04308 }
04309 }
04310 }
04311 }
04312
04313 } else
04314 {
04315
04316 if ( partSpecifier == "TEXT" )
04317 deleteBodyParts();
04318 mMsg->Body().FromString( content );
04319 mMsg->Body().Parse();
04320 }
04321 mNeedsAssembly = true;
04322 if (! partSpecifier.endsWith(".HEADER") )
04323 {
04324
04325 notify();
04326 }
04327 }
04328
04329
04330 void KMMessage::updateAttachmentState( DwBodyPart* part )
04331 {
04332 if ( !part )
04333 part = getFirstDwBodyPart();
04334
04335 if ( !part )
04336 {
04337
04338 setStatus( KMMsgStatusHasNoAttach );
04339 return;
04340 }
04341
04342 bool filenameEmpty = true;
04343 if ( part->hasHeaders() ) {
04344 if ( part->Headers().HasContentDisposition() ) {
04345 DwDispositionType cd = part->Headers().ContentDisposition();
04346 filenameEmpty = cd.Filename().empty();
04347 if ( filenameEmpty ) {
04348
04349 filenameEmpty = KMMsgBase::decodeRFC2231String( KMMsgBase::extractRFC2231HeaderField( cd.AsString().c_str(), "filename" ) ).isEmpty();
04350 }
04351 }
04352
04353
04354
04355 if ( filenameEmpty && part->Headers().HasContentType() ) {
04356 DwMediaType contentType = part->Headers().ContentType();
04357 filenameEmpty = contentType.Name().empty();
04358 if ( filenameEmpty ) {
04359
04360 filenameEmpty = KMMsgBase::decodeRFC2231String( KMMsgBase::extractRFC2231HeaderField(
04361 contentType.AsString().c_str(), "name" ) ).isEmpty();
04362 }
04363 }
04364 }
04365
04366 if ( part->hasHeaders() &&
04367 ( ( part->Headers().HasContentDisposition() &&
04368 !part->Headers().ContentDisposition().Filename().empty() ) ||
04369 ( part->Headers().HasContentType() &&
04370 !filenameEmpty ) ) )
04371 {
04372
04373 if ( !part->Headers().HasContentType() ||
04374 ( part->Headers().HasContentType() &&
04375 part->Headers().ContentType().Subtype() != DwMime::kSubtypePgpSignature &&
04376 part->Headers().ContentType().Subtype() != DwMime::kSubtypePkcs7Signature ) )
04377 {
04378 setStatus( KMMsgStatusHasAttach );
04379 }
04380 return;
04381 }
04382
04383
04384 if ( part->hasHeaders() &&
04385 part->Headers().HasContentType() &&
04386 part->Body().FirstBodyPart() &&
04387 (DwMime::kTypeMultipart == part->Headers().ContentType().Type() ) )
04388 {
04389 updateAttachmentState( part->Body().FirstBodyPart() );
04390 }
04391
04392
04393 if ( part->Body().Message() &&
04394 part->Body().Message()->Body().FirstBodyPart() )
04395 {
04396 updateAttachmentState( part->Body().Message()->Body().FirstBodyPart() );
04397 }
04398
04399
04400 if ( part->Next() )
04401 updateAttachmentState( part->Next() );
04402 else if ( attachmentState() == KMMsgAttachmentUnknown )
04403 setStatus( KMMsgStatusHasNoAttach );
04404 }
04405
04406 void KMMessage::setBodyFromUnicode( const QString &str, DwEntity *entity )
04407 {
04408 QCString encoding = KMMsgBase::autoDetectCharset( charset(), KMMessage::preferredCharsets(), str );
04409 if ( encoding.isEmpty() )
04410 encoding = "utf-8";
04411 const QTextCodec * codec = KMMsgBase::codecForName( encoding );
04412 assert( codec );
04413 QValueList<int> dummy;
04414 setCharset( encoding, entity );
04415 setBodyAndGuessCte( codec->fromUnicode( str ), dummy, false ,
04416 false, entity );
04417 }
04418
04419 const QTextCodec * KMMessage::codec() const {
04420 const QTextCodec * c = mOverrideCodec;
04421 if ( !c )
04422
04423 c = KMMsgBase::codecForName( charset() );
04424 if ( !c ) {
04425
04426
04427 c = KMMsgBase::codecForName( GlobalSettings::self()->fallbackCharacterEncoding().latin1() );
04428 }
04429 if ( !c )
04430
04431
04432 c = kmkernel->networkCodec();
04433 assert( c );
04434 return c;
04435 }
04436
04437 QString KMMessage::bodyToUnicode(const QTextCodec* codec) const {
04438 if ( !codec )
04439
04440 codec = this->codec();
04441 assert( codec );
04442
04443 return codec->toUnicode( bodyDecoded() );
04444 }
04445
04446
04447 QCString KMMessage::mboxMessageSeparator()
04448 {
04449 QCString str( KPIM::getFirstEmailAddress( rawHeaderField("From") ) );
04450 if ( str.isEmpty() )
04451 str = "unknown@unknown.invalid";
04452 QCString dateStr( dateShortStr() );
04453 if ( dateStr.isEmpty() ) {
04454 time_t t = ::time( 0 );
04455 dateStr = ctime( &t );
04456 const int len = dateStr.length();
04457 if ( dateStr[len-1] == '\n' )
04458 dateStr.truncate( len - 1 );
04459 }
04460 return "From " + str + " " + dateStr + "\n";
04461 }
04462
04463 void KMMessage::deleteWhenUnused()
04464 {
04465 sPendingDeletes << this;
04466 }
04467
04468 DwBodyPart* KMMessage::findPart( int index )
04469 {
04470 int accu = 0;
04471 return findPartInternal( getTopLevelPart(), index, accu );
04472 }
04473
04474 DwBodyPart* KMMessage::findPartInternal(DwEntity * root, int index, int & accu)
04475 {
04476 accu++;
04477 if ( index < accu )
04478 return 0;
04479 DwBodyPart *current = dynamic_cast<DwBodyPart*>( root );
04480 if ( index == accu )
04481 return current;
04482 DwBodyPart *rv = 0;
04483 if ( root->Body().FirstBodyPart() )
04484 rv = findPartInternal( root->Body().FirstBodyPart(), index, accu );
04485 if ( !rv && current && current->Next() )
04486 rv = findPartInternal( current->Next(), index, accu );
04487 if ( !rv && root->Body().Message() )
04488 rv = findPartInternal( root->Body().Message(), index, accu );
04489 return rv;
04490 }
04491