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