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