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