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