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