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