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