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