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