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