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