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