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