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 QString KMMessage::cc() const
01938 {
01939
01940
01941 return normalizeAddressesAndDecodeIDNs( headerFields( "Cc" ).join( ", " ) );
01942 }
01943
01944
01945
01946 void KMMessage::setCc(const QString& aStr)
01947 {
01948 setHeaderField( "Cc", aStr, Address );
01949 }
01950
01951
01952
01953 QString KMMessage::ccStrip() const
01954 {
01955 return decodeRFC2047String( stripEmailAddr( rawHeaderField("Cc") ) );
01956 }
01957
01958
01959
01960 QString KMMessage::bcc() const
01961 {
01962 return normalizeAddressesAndDecodeIDNs( rawHeaderField("Bcc") );
01963 }
01964
01965
01966
01967 void KMMessage::setBcc(const QString& aStr)
01968 {
01969 setHeaderField( "Bcc", aStr, Address );
01970 }
01971
01972
01973 QString KMMessage::fcc() const
01974 {
01975 return headerField( "X-KMail-Fcc" );
01976 }
01977
01978
01979
01980 void KMMessage::setFcc(const QString& aStr)
01981 {
01982 setHeaderField( "X-KMail-Fcc", aStr );
01983 }
01984
01985
01986 void KMMessage::setDrafts(const QString& aStr)
01987 {
01988 mDrafts = aStr;
01989 kdDebug(5006) << "KMMessage::setDrafts " << aStr << endl;
01990 }
01991
01992
01993 QString KMMessage::who() const
01994 {
01995 if (mParent)
01996 return normalizeAddressesAndDecodeIDNs( rawHeaderField(mParent->whoField().utf8()) );
01997 return from();
01998 }
01999
02000
02001
02002 QString KMMessage::from() const
02003 {
02004 return normalizeAddressesAndDecodeIDNs( rawHeaderField("From") );
02005 }
02006
02007
02008
02009 void KMMessage::setFrom(const QString& bStr)
02010 {
02011 QString aStr = bStr;
02012 if (aStr.isNull())
02013 aStr = "";
02014 setHeaderField( "From", aStr, Address );
02015 mDirty = TRUE;
02016 }
02017
02018
02019
02020 QString KMMessage::fromStrip() const
02021 {
02022 return decodeRFC2047String( stripEmailAddr( rawHeaderField("From") ) );
02023 }
02024
02025
02026 QCString KMMessage::fromEmail() const
02027 {
02028 return KPIM::getEmailAddr( from() );
02029 }
02030
02031
02032 QString KMMessage::sender() const {
02033 AddrSpecList asl = extractAddrSpecs( "Sender" );
02034 if ( asl.empty() )
02035 asl = extractAddrSpecs( "From" );
02036 if ( asl.empty() )
02037 return QString::null;
02038 return asl.front().asString();
02039 }
02040
02041
02042 QString KMMessage::subject() const
02043 {
02044 return headerField("Subject");
02045 }
02046
02047
02048
02049 void KMMessage::setSubject(const QString& aStr)
02050 {
02051 setHeaderField("Subject",aStr);
02052 mDirty = TRUE;
02053 }
02054
02055
02056
02057 QString KMMessage::xmark() const
02058 {
02059 return headerField("X-KMail-Mark");
02060 }
02061
02062
02063
02064 void KMMessage::setXMark(const QString& aStr)
02065 {
02066 setHeaderField("X-KMail-Mark", aStr);
02067 mDirty = TRUE;
02068 }
02069
02070
02071
02072 QString KMMessage::replyToId() const
02073 {
02074 int leftAngle, rightAngle;
02075 QString replyTo, references;
02076
02077 replyTo = headerField("In-Reply-To");
02078
02079 rightAngle = replyTo.find( '>' );
02080 if (rightAngle != -1)
02081 replyTo.truncate( rightAngle + 1 );
02082
02083 leftAngle = replyTo.findRev( '<' );
02084 if (leftAngle != -1)
02085 replyTo = replyTo.mid( leftAngle );
02086
02087
02088
02089
02090
02091 if (!replyTo.isEmpty() && (replyTo[0] == '<') &&
02092 ( -1 == replyTo.find( '"' ) ) )
02093 return replyTo;
02094
02095 references = headerField("References");
02096 leftAngle = references.findRev( '<' );
02097 if (leftAngle != -1)
02098 references = references.mid( leftAngle );
02099 rightAngle = references.find( '>' );
02100 if (rightAngle != -1)
02101 references.truncate( rightAngle + 1 );
02102
02103
02104 if (!references.isEmpty() && references[0] == '<')
02105 return references;
02106
02107 else
02108 return replyTo;
02109 }
02110
02111
02112
02113 QString KMMessage::replyToIdMD5() const {
02114 return base64EncodedMD5( replyToId() );
02115 }
02116
02117
02118 QString KMMessage::references() const
02119 {
02120 int leftAngle, rightAngle;
02121 QString references = headerField( "References" );
02122
02123
02124 leftAngle = references.findRev( '<' );
02125 leftAngle = references.findRev( '<', leftAngle - 1 );
02126 if( leftAngle != -1 )
02127 references = references.mid( leftAngle );
02128 rightAngle = references.findRev( '>' );
02129 if( rightAngle != -1 )
02130 references.truncate( rightAngle + 1 );
02131
02132 if( !references.isEmpty() && references[0] == '<' )
02133 return references;
02134 else
02135 return QString::null;
02136 }
02137
02138
02139 QString KMMessage::replyToAuxIdMD5() const
02140 {
02141 QString result = references();
02142
02143
02144 const int rightAngle = result.find( '>' );
02145 if( rightAngle != -1 )
02146 result.truncate( rightAngle + 1 );
02147
02148 return base64EncodedMD5( result );
02149 }
02150
02151
02152 QString KMMessage::strippedSubjectMD5() const {
02153 return base64EncodedMD5( stripOffPrefixes( subject() ), true );
02154 }
02155
02156
02157 QString KMMessage::subjectMD5() const {
02158 return base64EncodedMD5( subject(), true );
02159 }
02160
02161
02162 bool KMMessage::subjectIsPrefixed() const {
02163 return subjectMD5() != strippedSubjectMD5();
02164 }
02165
02166
02167 void KMMessage::setReplyToId(const QString& aStr)
02168 {
02169 setHeaderField("In-Reply-To", aStr);
02170 mDirty = TRUE;
02171 }
02172
02173
02174
02175 QString KMMessage::msgId() const
02176 {
02177 QString msgId = headerField("Message-Id");
02178
02179
02180 const int rightAngle = msgId.find( '>' );
02181 if (rightAngle != -1)
02182 msgId.truncate( rightAngle + 1 );
02183
02184 const int leftAngle = msgId.findRev( '<' );
02185 if (leftAngle != -1)
02186 msgId = msgId.mid( leftAngle );
02187 return msgId;
02188 }
02189
02190
02191
02192 QString KMMessage::msgIdMD5() const {
02193 return base64EncodedMD5( msgId() );
02194 }
02195
02196
02197
02198 void KMMessage::setMsgId(const QString& aStr)
02199 {
02200 setHeaderField("Message-Id", aStr);
02201 mDirty = TRUE;
02202 }
02203
02204
02205 size_t KMMessage::msgSizeServer() const {
02206 return headerField( "X-Length" ).toULong();
02207 }
02208
02209
02210
02211 void KMMessage::setMsgSizeServer(size_t size)
02212 {
02213 setHeaderField("X-Length", QCString().setNum(size));
02214 mDirty = TRUE;
02215 }
02216
02217
02218 ulong KMMessage::UID() const {
02219 return headerField( "X-UID" ).toULong();
02220 }
02221
02222
02223
02224 void KMMessage::setUID(ulong uid)
02225 {
02226 setHeaderField("X-UID", QCString().setNum(uid));
02227 mDirty = TRUE;
02228 }
02229
02230
02231 AddressList KMMessage::splitAddrField( const QCString & str )
02232 {
02233 AddressList result;
02234 const char * scursor = str.begin();
02235 if ( !scursor )
02236 return AddressList();
02237 const char * const send = str.begin() + str.length();
02238 if ( !parseAddressList( scursor, send, result ) )
02239 kdDebug(5006) << "Error in address splitting: parseAddressList returned false!"
02240 << endl;
02241 return result;
02242 }
02243
02244 AddressList KMMessage::headerAddrField( const QCString & aName ) const {
02245 return KMMessage::splitAddrField( rawHeaderField( aName ) );
02246 }
02247
02248 AddrSpecList KMMessage::extractAddrSpecs( const QCString & header ) const {
02249 AddressList al = headerAddrField( header );
02250 AddrSpecList result;
02251 for ( AddressList::const_iterator ait = al.begin() ; ait != al.end() ; ++ait )
02252 for ( MailboxList::const_iterator mit = (*ait).mailboxList.begin() ; mit != (*ait).mailboxList.end() ; ++mit )
02253 result.push_back( (*mit).addrSpec );
02254 return result;
02255 }
02256
02257 QCString KMMessage::rawHeaderField( const QCString & name ) const {
02258 if ( name.isEmpty() ) return QCString();
02259
02260 DwHeaders & header = mMsg->Headers();
02261 DwField * field = header.FindField( name );
02262
02263 if ( !field ) return QCString();
02264
02265 return header.FieldBody( name.data() ).AsString().c_str();
02266 }
02267
02268 QValueList<QCString> KMMessage::rawHeaderFields( const QCString& field ) const
02269 {
02270 if ( field.isEmpty() || !mMsg->Headers().FindField( field ) )
02271 return QValueList<QCString>();
02272
02273 std::vector<DwFieldBody*> v = mMsg->Headers().AllFieldBodies( field.data() );
02274 QValueList<QCString> headerFields;
02275 for ( uint i = 0; i < v.size(); ++i ) {
02276 headerFields.append( v[i]->AsString().c_str() );
02277 }
02278
02279 return headerFields;
02280 }
02281
02282 QString KMMessage::headerField(const QCString& aName) const
02283 {
02284 if ( aName.isEmpty() )
02285 return QString::null;
02286
02287 if ( !mMsg->Headers().FindField( aName ) )
02288 return QString::null;
02289
02290 return decodeRFC2047String( mMsg->Headers().FieldBody( aName.data() ).AsString().c_str() );
02291 }
02292
02293 QStringList KMMessage::headerFields( const QCString& field ) const
02294 {
02295 if ( field.isEmpty() || !mMsg->Headers().FindField( field ) )
02296 return QStringList();
02297
02298 std::vector<DwFieldBody*> v = mMsg->Headers().AllFieldBodies( field.data() );
02299 QStringList headerFields;
02300 for ( uint i = 0; i < v.size(); ++i ) {
02301 headerFields.append( decodeRFC2047String( v[i]->AsString().c_str() ) );
02302 }
02303
02304 return headerFields;
02305 }
02306
02307
02308 void KMMessage::removeHeaderField(const QCString& aName)
02309 {
02310 DwHeaders & header = mMsg->Headers();
02311 DwField * field = header.FindField(aName);
02312 if (!field) return;
02313
02314 header.RemoveField(field);
02315 mNeedsAssembly = TRUE;
02316 }
02317
02318
02319
02320 void KMMessage::setHeaderField( const QCString& aName, const QString& bValue,
02321 HeaderFieldType type, bool prepend )
02322 {
02323 #if 0
02324 if ( type != Unstructured )
02325 kdDebug(5006) << "KMMessage::setHeaderField( \"" << aName << "\", \""
02326 << bValue << "\", " << type << " )" << endl;
02327 #endif
02328 if (aName.isEmpty()) return;
02329
02330 DwHeaders& header = mMsg->Headers();
02331
02332 DwString str;
02333 DwField* field;
02334 QCString aValue;
02335 if (!bValue.isEmpty())
02336 {
02337 QString value = bValue;
02338 if ( type == Address )
02339 value = normalizeAddressesAndEncodeIDNs( value );
02340 #if 0
02341 if ( type != Unstructured )
02342 kdDebug(5006) << "value: \"" << value << "\"" << endl;
02343 #endif
02344 QCString encoding = autoDetectCharset( charset(), sPrefCharsets, value );
02345 if (encoding.isEmpty())
02346 encoding = "utf-8";
02347 aValue = encodeRFC2047String( value, encoding );
02348 #if 0
02349 if ( type != Unstructured )
02350 kdDebug(5006) << "aValue: \"" << aValue << "\"" << endl;
02351 #endif
02352 }
02353 str = aName;
02354 if (str[str.length()-1] != ':') str += ": ";
02355 else str += ' ';
02356 if ( !aValue.isEmpty() )
02357 str += aValue;
02358 if (str[str.length()-1] != '\n') str += '\n';
02359
02360 field = new DwField(str, mMsg);
02361 field->Parse();
02362
02363 if ( prepend )
02364 header.AddFieldAt( 1, field );
02365 else
02366 header.AddOrReplaceField( field );
02367 mNeedsAssembly = TRUE;
02368 }
02369
02370
02371
02372 QCString KMMessage::typeStr() const
02373 {
02374 DwHeaders& header = mMsg->Headers();
02375 if (header.HasContentType()) return header.ContentType().TypeStr().c_str();
02376 else return "";
02377 }
02378
02379
02380
02381 int KMMessage::type() const
02382 {
02383 DwHeaders& header = mMsg->Headers();
02384 if (header.HasContentType()) return header.ContentType().Type();
02385 else return DwMime::kTypeNull;
02386 }
02387
02388
02389
02390 void KMMessage::setTypeStr(const QCString& aStr)
02391 {
02392 dwContentType().SetTypeStr(DwString(aStr));
02393 dwContentType().Parse();
02394 mNeedsAssembly = TRUE;
02395 }
02396
02397
02398
02399 void KMMessage::setType(int aType)
02400 {
02401 dwContentType().SetType(aType);
02402 dwContentType().Assemble();
02403 mNeedsAssembly = TRUE;
02404 }
02405
02406
02407
02408
02409 QCString KMMessage::subtypeStr() const
02410 {
02411 DwHeaders& header = mMsg->Headers();
02412 if (header.HasContentType()) return header.ContentType().SubtypeStr().c_str();
02413 else return "";
02414 }
02415
02416
02417
02418 int KMMessage::subtype() const
02419 {
02420 DwHeaders& header = mMsg->Headers();
02421 if (header.HasContentType()) return header.ContentType().Subtype();
02422 else return DwMime::kSubtypeNull;
02423 }
02424
02425
02426
02427 void KMMessage::setSubtypeStr(const QCString& aStr)
02428 {
02429 dwContentType().SetSubtypeStr(DwString(aStr));
02430 dwContentType().Parse();
02431 mNeedsAssembly = TRUE;
02432 }
02433
02434
02435
02436 void KMMessage::setSubtype(int aSubtype)
02437 {
02438 dwContentType().SetSubtype(aSubtype);
02439 dwContentType().Assemble();
02440 mNeedsAssembly = TRUE;
02441 }
02442
02443
02444
02445 void KMMessage::setDwMediaTypeParam( DwMediaType &mType,
02446 const QCString& attr,
02447 const QCString& val )
02448 {
02449 mType.Parse();
02450 DwParameter *param = mType.FirstParameter();
02451 while(param) {
02452 if (!qstricmp(param->Attribute().c_str(), attr))
02453 break;
02454 else
02455 param = param->Next();
02456 }
02457 if (!param){
02458 param = new DwParameter;
02459 param->SetAttribute(DwString( attr ));
02460 mType.AddParameter( param );
02461 }
02462 else
02463 mType.SetModified();
02464 param->SetValue(DwString( val ));
02465 mType.Assemble();
02466 }
02467
02468
02469
02470 void KMMessage::setContentTypeParam(const QCString& attr, const QCString& val)
02471 {
02472 if (mNeedsAssembly) mMsg->Assemble();
02473 mNeedsAssembly = FALSE;
02474 setDwMediaTypeParam( dwContentType(), attr, val );
02475 mNeedsAssembly = TRUE;
02476 }
02477
02478
02479
02480 QCString KMMessage::contentTransferEncodingStr() const
02481 {
02482 DwHeaders& header = mMsg->Headers();
02483 if (header.HasContentTransferEncoding())
02484 return header.ContentTransferEncoding().AsString().c_str();
02485 else return "";
02486 }
02487
02488
02489
02490 int KMMessage::contentTransferEncoding() const
02491 {
02492 DwHeaders& header = mMsg->Headers();
02493 if (header.HasContentTransferEncoding())
02494 return header.ContentTransferEncoding().AsEnum();
02495 else return DwMime::kCteNull;
02496 }
02497
02498
02499
02500 void KMMessage::setContentTransferEncodingStr(const QCString& aStr)
02501 {
02502 mMsg->Headers().ContentTransferEncoding().FromString(aStr);
02503 mMsg->Headers().ContentTransferEncoding().Parse();
02504 mNeedsAssembly = TRUE;
02505 }
02506
02507
02508
02509 void KMMessage::setContentTransferEncoding(int aCte)
02510 {
02511 mMsg->Headers().ContentTransferEncoding().FromEnum(aCte);
02512 mNeedsAssembly = TRUE;
02513 }
02514
02515
02516
02517 DwHeaders& KMMessage::headers() const
02518 {
02519 return mMsg->Headers();
02520 }
02521
02522
02523
02524 void KMMessage::setNeedsAssembly()
02525 {
02526 mNeedsAssembly = true;
02527 }
02528
02529
02530
02531 QCString KMMessage::body() const
02532 {
02533 DwString body = mMsg->Body().AsString();
02534 QCString str = body.c_str();
02535 kdWarning( str.length() != body.length(), 5006 )
02536 << "KMMessage::body(): body is binary but used as text!" << endl;
02537 return str;
02538 }
02539
02540
02541
02542 QByteArray KMMessage::bodyDecodedBinary() const
02543 {
02544 DwString dwstr;
02545 DwString dwsrc = mMsg->Body().AsString();
02546
02547 switch (cte())
02548 {
02549 case DwMime::kCteBase64:
02550 DwDecodeBase64(dwsrc, dwstr);
02551 break;
02552 case DwMime::kCteQuotedPrintable:
02553 DwDecodeQuotedPrintable(dwsrc, dwstr);
02554 break;
02555 default:
02556 dwstr = dwsrc;
02557 break;
02558 }
02559
02560 int len = dwstr.size();
02561 QByteArray ba(len);
02562 memcpy(ba.data(),dwstr.data(),len);
02563 return ba;
02564 }
02565
02566
02567
02568 QCString KMMessage::bodyDecoded() const
02569 {
02570 DwString dwstr;
02571 DwString dwsrc = mMsg->Body().AsString();
02572
02573 switch (cte())
02574 {
02575 case DwMime::kCteBase64:
02576 DwDecodeBase64(dwsrc, dwstr);
02577 break;
02578 case DwMime::kCteQuotedPrintable:
02579 DwDecodeQuotedPrintable(dwsrc, dwstr);
02580 break;
02581 default:
02582 dwstr = dwsrc;
02583 break;
02584 }
02585
02586 unsigned int len = dwstr.size();
02587 QCString result(len+1);
02588 memcpy(result.data(),dwstr.data(),len);
02589 result[len] = 0;
02590 kdWarning(result.length() != len, 5006)
02591 << "KMMessage::bodyDecoded(): body is binary but used as text!" << endl;
02592 return result;
02593 }
02594
02595
02596
02597 QValueList<int> KMMessage::determineAllowedCtes( const CharFreq& cf,
02598 bool allow8Bit,
02599 bool willBeSigned )
02600 {
02601 QValueList<int> allowedCtes;
02602
02603 switch ( cf.type() ) {
02604 case CharFreq::SevenBitText:
02605 allowedCtes << DwMime::kCte7bit;
02606 case CharFreq::EightBitText:
02607 if ( allow8Bit )
02608 allowedCtes << DwMime::kCte8bit;
02609 case CharFreq::SevenBitData:
02610 if ( cf.printableRatio() > 5.0/6.0 ) {
02611
02612
02613
02614 allowedCtes << DwMime::kCteQp;
02615 allowedCtes << DwMime::kCteBase64;
02616 } else {
02617 allowedCtes << DwMime::kCteBase64;
02618 allowedCtes << DwMime::kCteQp;
02619 }
02620 break;
02621 case CharFreq::EightBitData:
02622 allowedCtes << DwMime::kCteBase64;
02623 break;
02624 case CharFreq::None:
02625 default:
02626
02627 ;
02628 }
02629
02630
02631
02632
02633
02634 if ( ( willBeSigned && cf.hasTrailingWhitespace() ) ||
02635 cf.hasLeadingFrom() ) {
02636 allowedCtes.remove( DwMime::kCte8bit );
02637 allowedCtes.remove( DwMime::kCte7bit );
02638 }
02639
02640 return allowedCtes;
02641 }
02642
02643
02644
02645 void KMMessage::setBodyAndGuessCte( const QByteArray& aBuf,
02646 QValueList<int> & allowedCte,
02647 bool allow8Bit,
02648 bool willBeSigned )
02649 {
02650 CharFreq cf( aBuf );
02651
02652 allowedCte = determineAllowedCtes( cf, allow8Bit, willBeSigned );
02653
02654 #ifndef NDEBUG
02655 DwString dwCte;
02656 DwCteEnumToStr(allowedCte[0], dwCte);
02657 kdDebug(5006) << "CharFreq returned " << cf.type() << "/"
02658 << cf.printableRatio() << " and I chose "
02659 << dwCte.c_str() << endl;
02660 #endif
02661
02662 setCte( allowedCte[0] );
02663 setBodyEncodedBinary( aBuf );
02664 }
02665
02666
02667
02668 void KMMessage::setBodyAndGuessCte( const QCString& aBuf,
02669 QValueList<int> & allowedCte,
02670 bool allow8Bit,
02671 bool willBeSigned )
02672 {
02673 CharFreq cf( aBuf.data(), aBuf.length() );
02674
02675 allowedCte = determineAllowedCtes( cf, allow8Bit, willBeSigned );
02676
02677 #ifndef NDEBUG
02678 DwString dwCte;
02679 DwCteEnumToStr(allowedCte[0], dwCte);
02680 kdDebug(5006) << "CharFreq returned " << cf.type() << "/"
02681 << cf.printableRatio() << " and I chose "
02682 << dwCte.c_str() << endl;
02683 #endif
02684
02685 setCte( allowedCte[0] );
02686 setBodyEncoded( aBuf );
02687 }
02688
02689
02690
02691 void KMMessage::setBodyEncoded(const QCString& aStr)
02692 {
02693 DwString dwSrc(aStr.data(), aStr.size()-1 );
02694 DwString dwResult;
02695
02696 switch (cte())
02697 {
02698 case DwMime::kCteBase64:
02699 DwEncodeBase64(dwSrc, dwResult);
02700 break;
02701 case DwMime::kCteQuotedPrintable:
02702 DwEncodeQuotedPrintable(dwSrc, dwResult);
02703 break;
02704 default:
02705 dwResult = dwSrc;
02706 break;
02707 }
02708
02709 mMsg->Body().FromString(dwResult);
02710 mNeedsAssembly = TRUE;
02711 }
02712
02713
02714 void KMMessage::setBodyEncodedBinary(const QByteArray& aStr)
02715 {
02716 DwString dwSrc(aStr.data(), aStr.size());
02717 DwString dwResult;
02718
02719 switch (cte())
02720 {
02721 case DwMime::kCteBase64:
02722 DwEncodeBase64(dwSrc, dwResult);
02723 break;
02724 case DwMime::kCteQuotedPrintable:
02725 DwEncodeQuotedPrintable(dwSrc, dwResult);
02726 break;
02727 default:
02728 dwResult = dwSrc;
02729 break;
02730 }
02731
02732 mMsg->Body().FromString(dwResult);
02733 mNeedsAssembly = TRUE;
02734 }
02735
02736
02737
02738 void KMMessage::setBody(const QCString& aStr)
02739 {
02740 mMsg->Body().FromString(aStr.data());
02741 mNeedsAssembly = TRUE;
02742 }
02743
02744 void KMMessage::setMultiPartBody( const QCString & aStr ) {
02745 setBody( aStr );
02746 mMsg->Body().Parse();
02747 mNeedsAssembly = true;
02748 }
02749
02750
02751
02752
02753
02754
02755
02756
02757
02758
02759 int KMMessage::numBodyParts() const
02760 {
02761 int count = 0;
02762 DwBodyPart* part = getFirstDwBodyPart();
02763 QPtrList< DwBodyPart > parts;
02764
02765 while (part)
02766 {
02767
02768 while ( part
02769 && part->hasHeaders()
02770 && part->Headers().HasContentType()
02771 && part->Body().FirstBodyPart()
02772 && (DwMime::kTypeMultipart == part->Headers().ContentType().Type()) )
02773 {
02774 parts.append( part );
02775 part = part->Body().FirstBodyPart();
02776 }
02777
02778 count++;
02779
02780
02781 while (part && !(part->Next()) && !(parts.isEmpty()))
02782 {
02783 part = parts.getLast();
02784 parts.removeLast();
02785 }
02786
02787 if (part->Body().Message() &&
02788 part->Body().Message()->Body().FirstBodyPart())
02789 {
02790 part = part->Body().Message()->Body().FirstBodyPart();
02791 } else if (part) {
02792 part = part->Next();
02793 }
02794 }
02795
02796 return count;
02797 }
02798
02799
02800
02801 DwBodyPart * KMMessage::getFirstDwBodyPart() const
02802 {
02803 return mMsg->Body().FirstBodyPart();
02804 }
02805
02806
02807
02808 int KMMessage::partNumber( DwBodyPart * aDwBodyPart ) const
02809 {
02810 DwBodyPart *curpart;
02811 QPtrList< DwBodyPart > parts;
02812 int curIdx = 0;
02813 int idx = 0;
02814
02815
02816 curpart = getFirstDwBodyPart();
02817
02818 while (curpart && !idx) {
02819
02820 while( curpart
02821 && curpart->hasHeaders()
02822 && curpart->Headers().HasContentType()
02823 && curpart->Body().FirstBodyPart()
02824 && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) )
02825 {
02826 parts.append( curpart );
02827 curpart = curpart->Body().FirstBodyPart();
02828 }
02829
02830 if (curpart == aDwBodyPart)
02831 idx = curIdx;
02832 curIdx++;
02833
02834
02835 while (curpart && !(curpart->Next()) && !(parts.isEmpty()))
02836 {
02837 curpart = parts.getLast();
02838 parts.removeLast();
02839 } ;
02840 if (curpart)
02841 curpart = curpart->Next();
02842 }
02843 return idx;
02844 }
02845
02846
02847
02848 DwBodyPart * KMMessage::dwBodyPart( int aIdx ) const
02849 {
02850 DwBodyPart *part, *curpart;
02851 QPtrList< DwBodyPart > parts;
02852 int curIdx = 0;
02853
02854
02855 curpart = getFirstDwBodyPart();
02856 part = 0;
02857
02858 while (curpart && !part) {
02859
02860 while( curpart
02861 && curpart->hasHeaders()
02862 && curpart->Headers().HasContentType()
02863 && curpart->Body().FirstBodyPart()
02864 && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) )
02865 {
02866 parts.append( curpart );
02867 curpart = curpart->Body().FirstBodyPart();
02868 }
02869
02870 if (curIdx==aIdx)
02871 part = curpart;
02872 curIdx++;
02873
02874
02875 while (curpart && !(curpart->Next()) && !(parts.isEmpty()))
02876 {
02877 curpart = parts.getLast();
02878 parts.removeLast();
02879 }
02880 if (curpart)
02881 curpart = curpart->Next();
02882 }
02883 return part;
02884 }
02885
02886
02887
02888 DwBodyPart * KMMessage::findDwBodyPart( int type, int subtype ) const
02889 {
02890 DwBodyPart *part, *curpart;
02891 QPtrList< DwBodyPart > parts;
02892
02893
02894 curpart = getFirstDwBodyPart();
02895 part = 0;
02896
02897 while (curpart && !part) {
02898
02899 while(curpart
02900 && curpart->hasHeaders()
02901 && curpart->Headers().HasContentType()
02902 && curpart->Body().FirstBodyPart()
02903 && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) ) {
02904 parts.append( curpart );
02905 curpart = curpart->Body().FirstBodyPart();
02906 }
02907
02908
02909
02910
02911 if (curpart && curpart->hasHeaders() && curpart->Headers().HasContentType() ) {
02912 kdDebug(5006) << curpart->Headers().ContentType().TypeStr().c_str()
02913 << " " << curpart->Headers().ContentType().SubtypeStr().c_str() << endl;
02914 }
02915
02916 if (curpart &&
02917 curpart->hasHeaders() &&
02918 curpart->Headers().HasContentType() &&
02919 curpart->Headers().ContentType().Type() == type &&
02920 curpart->Headers().ContentType().Subtype() == subtype) {
02921 part = curpart;
02922 } else {
02923
02924
02925 while (curpart && !(curpart->Next()) && !(parts.isEmpty())) {
02926 curpart = parts.getLast();
02927 parts.removeLast();
02928 } ;
02929 if (curpart)
02930 curpart = curpart->Next();
02931 }
02932 }
02933 return part;
02934 }
02935
02936
02937 DwBodyPart * KMMessage::findDwBodyPart( const QCString& type, const QCString& subtype ) const
02938 {
02939 DwBodyPart *part, *curpart;
02940 QPtrList< DwBodyPart > parts;
02941
02942
02943 curpart = getFirstDwBodyPart();
02944 part = 0;
02945
02946 while (curpart && !part) {
02947
02948 while(curpart
02949 && curpart->hasHeaders()
02950 && curpart->Headers().HasContentType()
02951 && curpart->Body().FirstBodyPart()
02952 && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) ) {
02953 parts.append( curpart );
02954 curpart = curpart->Body().FirstBodyPart();
02955 }
02956
02957
02958
02959
02960 if (curpart && curpart->hasHeaders() && curpart->Headers().HasContentType() ) {
02961 kdDebug(5006) << curpart->Headers().ContentType().TypeStr().c_str()
02962 << " " << curpart->Headers().ContentType().SubtypeStr().c_str() << endl;
02963 }
02964
02965 if (curpart &&
02966 curpart->hasHeaders() &&
02967 curpart->Headers().HasContentType() &&
02968 curpart->Headers().ContentType().TypeStr().c_str() == type &&
02969 curpart->Headers().ContentType().SubtypeStr().c_str() == subtype) {
02970 part = curpart;
02971 } else {
02972
02973
02974 while (curpart && !(curpart->Next()) && !(parts.isEmpty())) {
02975 curpart = parts.getLast();
02976 parts.removeLast();
02977 } ;
02978 if (curpart)
02979 curpart = curpart->Next();
02980 }
02981 }
02982 return part;
02983 }
02984
02985
02986 void applyHeadersToMessagePart( DwHeaders& headers, KMMessagePart* aPart )
02987 {
02988
02989 QCString additionalCTypeParams;
02990 if (headers.HasContentType())
02991 {
02992 DwMediaType& ct = headers.ContentType();
02993 aPart->setOriginalContentTypeStr( ct.AsString().c_str() );
02994 aPart->setTypeStr(ct.TypeStr().c_str());
02995 aPart->setSubtypeStr(ct.SubtypeStr().c_str());
02996 DwParameter *param = ct.FirstParameter();
02997 while(param)
02998 {
02999 if (!qstricmp(param->Attribute().c_str(), "charset"))
03000 aPart->setCharset(QCString(param->Value().c_str()).lower());
03001 else if (!qstrnicmp(param->Attribute().c_str(), "name*", 5))
03002 aPart->setName(KMMsgBase::decodeRFC2231String(
03003 param->Value().c_str()));
03004 else {
03005 additionalCTypeParams += ';';
03006 additionalCTypeParams += param->AsString().c_str();
03007 }
03008 param=param->Next();
03009 }
03010 }
03011 else
03012 {
03013 aPart->setTypeStr("text");
03014 aPart->setSubtypeStr("plain");
03015 }
03016 aPart->setAdditionalCTypeParamStr( additionalCTypeParams );
03017
03018 if (aPart->name().isEmpty())
03019 {
03020 if (headers.HasContentType() && !headers.ContentType().Name().empty()) {
03021 aPart->setName(KMMsgBase::decodeRFC2047String(headers.
03022 ContentType().Name().c_str()) );
03023 } else if (headers.HasSubject() && !headers.Subject().AsString().empty()) {
03024 aPart->setName( KMMsgBase::decodeRFC2047String(headers.
03025 Subject().AsString().c_str()) );
03026 }
03027 }
03028
03029
03030 if (headers.HasContentTransferEncoding())
03031 aPart->setCteStr(headers.ContentTransferEncoding().AsString().c_str());
03032 else
03033 aPart->setCteStr("7bit");
03034
03035
03036 if (headers.HasContentDescription())
03037 aPart->setContentDescription(headers.ContentDescription().AsString().c_str());
03038 else
03039 aPart->setContentDescription("");
03040
03041
03042 if (headers.HasContentDisposition())
03043 aPart->setContentDisposition(headers.ContentDisposition().AsString().c_str());
03044 else
03045 aPart->setContentDisposition("");
03046 }
03047
03048
03049 void KMMessage::bodyPart(DwBodyPart* aDwBodyPart, KMMessagePart* aPart,
03050 bool withBody)
03051 {
03052 if ( !aPart )
03053 return;
03054
03055 aPart->clear();
03056
03057 if( aDwBodyPart && aDwBodyPart->hasHeaders() ) {
03058
03059
03060
03061
03062 QString partId( aDwBodyPart->partId() );
03063 aPart->setPartSpecifier( partId );
03064
03065 DwHeaders& headers = aDwBodyPart->Headers();
03066 applyHeadersToMessagePart( headers, aPart );
03067
03068
03069 if (withBody)
03070 aPart->setBody( aDwBodyPart->Body().AsString().c_str() );
03071 else
03072 aPart->setBody( "" );
03073
03074 }
03075
03076
03077 else
03078 {
03079 aPart->setTypeStr("");
03080 aPart->setSubtypeStr("");
03081 aPart->setCteStr("");
03082
03083
03084
03085 aPart->setContentDescription("");
03086 aPart->setContentDisposition("");
03087 aPart->setBody("");
03088 }
03089 }
03090
03091
03092
03093 void KMMessage::bodyPart(int aIdx, KMMessagePart* aPart) const
03094 {
03095 if ( !aPart )
03096 return;
03097
03098
03099 if ( DwBodyPart *part = dwBodyPart( aIdx ) ) {
03100 KMMessage::bodyPart(part, aPart);
03101 if( aPart->name().isEmpty() )
03102 aPart->setName( i18n("Attachment: %1").arg( aIdx ) );
03103 }
03104 }
03105
03106
03107
03108 void KMMessage::deleteBodyParts()
03109 {
03110 mMsg->Body().DeleteBodyParts();
03111 }
03112
03113
03114
03115 DwBodyPart* KMMessage::createDWBodyPart(const KMMessagePart* aPart)
03116 {
03117 DwBodyPart* part = DwBodyPart::NewBodyPart(emptyString, 0);
03118
03119 if ( !aPart )
03120 return part;
03121
03122 QCString charset = aPart->charset();
03123 QCString type = aPart->typeStr();
03124 QCString subtype = aPart->subtypeStr();
03125 QCString cte = aPart->cteStr();
03126 QCString contDesc = aPart->contentDescriptionEncoded();
03127 QCString contDisp = aPart->contentDisposition();
03128 QCString encoding = autoDetectCharset(charset, sPrefCharsets, aPart->name());
03129 if (encoding.isEmpty()) encoding = "utf-8";
03130 QCString name = KMMsgBase::encodeRFC2231String(aPart->name(), encoding);
03131 bool RFC2231encoded = aPart->name() != QString(name);
03132 QCString paramAttr = aPart->parameterAttribute();
03133
03134 DwHeaders& headers = part->Headers();
03135
03136 DwMediaType& ct = headers.ContentType();
03137 if (!type.isEmpty() && !subtype.isEmpty())
03138 {
03139 ct.SetTypeStr(type.data());
03140 ct.SetSubtypeStr(subtype.data());
03141 if (!charset.isEmpty()){
03142 DwParameter *param;
03143 param=new DwParameter;
03144 param->SetAttribute("charset");
03145 param->SetValue(charset.data());
03146 ct.AddParameter(param);
03147 }
03148 }
03149
03150 QCString additionalParam = aPart->additionalCTypeParamStr();
03151 if( !additionalParam.isEmpty() )
03152 {
03153 QCString parAV;
03154 DwString parA, parV;
03155 int iL, i1, i2, iM;
03156 iL = additionalParam.length();
03157 i1 = 0;
03158 i2 = additionalParam.find(';', i1, false);
03159 while ( i1 < iL )
03160 {
03161 if( -1 == i2 )
03162 i2 = iL;
03163 if( i1+1 < i2 ) {
03164 parAV = additionalParam.mid( i1, (i2-i1) );
03165 iM = parAV.find('=');
03166 if( -1 < iM )
03167 {
03168 parA = parAV.left( iM );
03169 parV = parAV.right( parAV.length() - iM - 1 );
03170 if( ('"' == parV.at(0)) && ('"' == parV.at(parV.length()-1)) )
03171 {
03172 parV.erase( 0, 1);
03173 parV.erase( parV.length()-1 );
03174 }
03175 }
03176 else
03177 {
03178 parA = parAV;
03179 parV = "";
03180 }
03181 DwParameter *param;
03182 param = new DwParameter;
03183 param->SetAttribute( parA );
03184 param->SetValue( parV );
03185 ct.AddParameter( param );
03186 }
03187 i1 = i2+1;
03188 i2 = additionalParam.find(';', i1, false);
03189 }
03190 }
03191
03192 if ( !name.isEmpty() ) {
03193 if (RFC2231encoded)
03194 {
03195 DwParameter *nameParam;
03196 nameParam = new DwParameter;
03197 nameParam->SetAttribute("name*");
03198 nameParam->SetValue(name.data(),true);
03199 ct.AddParameter(nameParam);
03200 } else {
03201 ct.SetName(name.data());
03202 }
03203 }
03204
03205 if (!paramAttr.isEmpty())
03206 {
03207 QCString encoding = autoDetectCharset(charset, sPrefCharsets,
03208 aPart->parameterValue());
03209 if (encoding.isEmpty()) encoding = "utf-8";
03210 QCString paramValue;
03211 paramValue = KMMsgBase::encodeRFC2231String(aPart->parameterValue(),
03212 encoding);
03213 DwParameter *param = new DwParameter;
03214 if (aPart->parameterValue() != QString(paramValue))
03215 {
03216 param->SetAttribute((paramAttr + '*').data());
03217 param->SetValue(paramValue.data(),true);
03218 } else {
03219 param->SetAttribute(paramAttr.data());
03220 param->SetValue(paramValue.data());
03221 }
03222 ct.AddParameter(param);
03223 }
03224
03225 if (!cte.isEmpty())
03226 headers.Cte().FromString(cte);
03227
03228 if (!contDesc.isEmpty())
03229 headers.ContentDescription().FromString(contDesc);
03230
03231 if (!contDisp.isEmpty())
03232 headers.ContentDisposition().FromString(contDisp);
03233
03234 if (!aPart->body().isNull())
03235 part->Body().FromString(aPart->body());
03236 else
03237 part->Body().FromString("");
03238
03239 if (!aPart->partSpecifier().isNull())
03240 part->SetPartId( aPart->partSpecifier().latin1() );
03241
03242 if (aPart->decodedSize() > 0)
03243 part->SetBodySize( aPart->decodedSize() );
03244
03245 return part;
03246 }
03247
03248
03249
03250 void KMMessage::addDwBodyPart(DwBodyPart * aDwPart)
03251 {
03252 mMsg->Body().AddBodyPart( aDwPart );
03253 mNeedsAssembly = TRUE;
03254 }
03255
03256
03257
03258 void KMMessage::addBodyPart(const KMMessagePart* aPart)
03259 {
03260 DwBodyPart* part = createDWBodyPart( aPart );
03261 addDwBodyPart( part );
03262 }
03263
03264
03265
03266 QString KMMessage::generateMessageId( const QString& addr )
03267 {
03268 QDateTime datetime = QDateTime::currentDateTime();
03269 QString msgIdStr;
03270
03271 msgIdStr = '<' + datetime.toString( "yyyyMMddhhmm.sszzz" );
03272
03273 QString msgIdSuffix;
03274 KConfigGroup general( KMKernel::config(), "General" );
03275
03276 if( general.readBoolEntry( "useCustomMessageIdSuffix", false ) )
03277 msgIdSuffix = general.readEntry( "myMessageIdSuffix" );
03278
03279 if( !msgIdSuffix.isEmpty() )
03280 msgIdStr += '@' + msgIdSuffix;
03281 else
03282 msgIdStr += '.' + encodeIDN( addr );
03283
03284 msgIdStr += '>';
03285
03286 return msgIdStr;
03287 }
03288
03289
03290
03291 QCString KMMessage::html2source( const QCString & src )
03292 {
03293 QCString result( 1 + 6*src.length() );
03294
03295 QCString::ConstIterator s = src.begin();
03296 QCString::Iterator d = result.begin();
03297 while ( *s ) {
03298 switch ( *s ) {
03299 case '<': {
03300 *d++ = '&';
03301 *d++ = 'l';
03302 *d++ = 't';
03303 *d++ = ';';
03304 ++s;
03305 }
03306 break;
03307 case '\r': {
03308 ++s;
03309 }
03310 break;
03311 case '\n': {
03312 *d++ = '<';
03313 *d++ = 'b';
03314 *d++ = 'r';
03315 *d++ = '>';
03316 ++s;
03317 }
03318 break;
03319 case '>': {
03320 *d++ = '&';
03321 *d++ = 'g';
03322 *d++ = 't';
03323 *d++ = ';';
03324 ++s;
03325 }
03326 break;
03327 case '&': {
03328 *d++ = '&';
03329 *d++ = 'a';
03330 *d++ = 'm';
03331 *d++ = 'p';
03332 *d++ = ';';
03333 ++s;
03334 }
03335 break;
03336 case '"': {
03337 *d++ = '&';
03338 *d++ = 'q';
03339 *d++ = 'u';
03340 *d++ = 'o';
03341 *d++ = 't';
03342 *d++ = ';';
03343 ++s;
03344 }
03345 break;
03346 case '\'': {
03347 *d++ = '&';
03348 *d++ = 'a';
03349 *d++ = 'p';
03350 *d++ = 's';
03351 *d++ = ';';
03352 ++s;
03353 }
03354 break;
03355 default:
03356 *d++ = *s++;
03357 }
03358 }
03359 result.truncate( d - result.begin() );
03360 return result;
03361 }
03362
03363
03364
03365 QCString KMMessage::lf2crlf( const QCString & src )
03366 {
03367 QCString result( 1 + 2*src.length() );
03368
03369 QCString::ConstIterator s = src.begin();
03370 QCString::Iterator d = result.begin();
03371
03372 char cPrev = '?';
03373 while ( *s ) {
03374 if ( ('\n' == *s) && ('\r' != cPrev) )
03375 *d++ = '\r';
03376 cPrev = *s;
03377 *d++ = *s++;
03378 }
03379 result.truncate( d - result.begin() );
03380 return result;
03381 }
03382
03383
03384
03385
03386 static QString escapeQuotes( const QString & str )
03387 {
03388 if ( str.isEmpty() )
03389 return QString();
03390
03391 QString escaped;
03392
03393 escaped.reserve( 2*str.length() );
03394 unsigned int len = 0;
03395 for ( unsigned int i = 0; i < str.length(); ++i, ++len ) {
03396 if ( str[i] == '"' ) {
03397 escaped[len] = '\\';
03398 ++len;
03399 }
03400 else if ( str[i] == '\\' ) {
03401 escaped[len] = '\\';
03402 ++len;
03403 ++i;
03404 if ( i >= str.length() )
03405 break;
03406 }
03407 escaped[len] = str[i];
03408 }
03409 escaped.truncate( len );
03410 return escaped;
03411 }
03412
03413
03414 static QString quoteNameIfNecessary( const QString &str )
03415 {
03416 QString quoted = str;
03417
03418 QRegExp needQuotes( "[^ 0-9A-Za-z\\x0080-\\xFFFF]" );
03419
03420 if ( ( quoted[0] == '"' ) && ( quoted[quoted.length() - 1] == '"' ) ) {
03421 quoted = "\"" + escapeQuotes( quoted.mid( 1, quoted.length() - 2 ) ) + "\"";
03422 }
03423 else if ( quoted.find( needQuotes ) != -1 ) {
03424 quoted = "\"" + escapeQuotes( quoted ) + "\"";
03425 }
03426
03427 return quoted;
03428 }
03429
03430
03431
03432 QString KMMessage::normalizedAddress( const QString & displayName_,
03433 const QString & addrSpec_,
03434 const QString & comment_ )
03435 {
03436 QString displayName( quoteNameIfNecessary( displayName_ ) );
03437 QString addrSpec( addrSpec_ );
03438 QString comment( quoteNameIfNecessary( comment_ ) );
03439 if ( displayName.isEmpty() && comment.isEmpty() )
03440 return addrSpec;
03441 else if ( comment.isEmpty() )
03442 return displayName + " <" + addrSpec + ">";
03443 else if ( displayName.isEmpty() )
03444 return comment + " <" + addrSpec + ">";
03445 else
03446 return displayName + " (" + comment + ") <" + addrSpec + ">";
03447 }
03448
03449
03450
03451 QString KMMessage::decodeIDN( const QString & addrSpec )
03452 {
03453 const int atPos = addrSpec.findRev( '@' );
03454 if ( atPos == -1 )
03455 return addrSpec;
03456
03457 QString idn = KIDNA::toUnicode( addrSpec.mid( atPos + 1 ) );
03458 if ( idn.isEmpty() )
03459 return QString::null;
03460
03461 return addrSpec.left( atPos + 1 ) + idn;
03462 }
03463
03464
03465
03466 QString KMMessage::encodeIDN( const QString & addrSpec )
03467 {
03468 const int atPos = addrSpec.findRev( '@' );
03469 if ( atPos == -1 )
03470 return addrSpec;
03471
03472 QString idn = KIDNA::toAscii( addrSpec.mid( atPos + 1 ) );
03473 if ( idn.isEmpty() )
03474 return addrSpec;
03475
03476 return addrSpec.left( atPos + 1 ) + idn;
03477 }
03478
03479
03480
03481 QString KMMessage::normalizeAddressesAndDecodeIDNs( const QString & str )
03482 {
03483
03484
03485 if( str.isEmpty() )
03486 return str;
03487
03488 const QStringList addressList = KPIM::splitEmailAddrList( str );
03489 QStringList normalizedAddressList;
03490
03491 QCString displayName, addrSpec, comment;
03492
03493 for( QStringList::ConstIterator it = addressList.begin();
03494 ( it != addressList.end() );
03495 ++it ) {
03496 if( !(*it).isEmpty() ) {
03497 if ( KMMessage::splitAddress( (*it).utf8(), displayName, addrSpec,
03498 comment )
03499 == AddressOk ) {
03500
03501 normalizedAddressList <<
03502 normalizedAddress( decodeRFC2047String( displayName ),
03503 decodeIDN( QString::fromUtf8( addrSpec ) ),
03504 decodeRFC2047String( comment ) );
03505 }
03506 else {
03507 kdDebug(5006) << "splitting address failed: " << *it << endl;
03508 }
03509 }
03510 }
03511
03512
03513
03514
03515
03516 return normalizedAddressList.join( ", " );
03517 }
03518
03519
03520 QString KMMessage::normalizeAddressesAndEncodeIDNs( const QString & str )
03521 {
03522 kdDebug(5006) << "KMMessage::normalizeAddressesAndEncodeIDNs( \""
03523 << str << "\" )" << endl;
03524 if( str.isEmpty() )
03525 return str;
03526
03527 const QStringList addressList = KPIM::splitEmailAddrList( str );
03528 QStringList normalizedAddressList;
03529
03530 QCString displayName, addrSpec, comment;
03531
03532 for( QStringList::ConstIterator it = addressList.begin();
03533 ( it != addressList.end() );
03534 ++it ) {
03535 if( !(*it).isEmpty() ) {
03536 if ( KMMessage::splitAddress( (*it).utf8(), displayName, addrSpec,
03537 comment )
03538 == AddressOk ) {
03539
03540 normalizedAddressList <<
03541 normalizedAddress( QString::fromUtf8( displayName ),
03542 encodeIDN( QString::fromUtf8( addrSpec ) ),
03543 QString::fromUtf8( comment ) );
03544 }
03545 else {
03546 kdDebug(5006) << "splitting address failed: " << *it << endl;
03547 }
03548 }
03549 }
03550
03551 kdDebug(5006) << "normalizedAddressList: \""
03552 << normalizedAddressList.join( ", " )
03553 << "\"" << endl;
03554 return normalizedAddressList.join( ", " );
03555 }
03556
03557
03558 QString KMMessage::encodeMailtoUrl( const QString& str )
03559 {
03560 QString result;
03561 result = QString::fromLatin1( KMMsgBase::encodeRFC2047String( str,
03562 "utf-8" ) );
03563 result = KURL::encode_string( result );
03564 return result;
03565 }
03566
03567
03568
03569 QString KMMessage::decodeMailtoUrl( const QString& url )
03570 {
03571 QString result;
03572 result = KURL::decode_string( url );
03573 result = KMMsgBase::decodeRFC2047String( result.latin1() );
03574 return result;
03575 }
03576
03577
03578
03579 KMMessage::AddressParseResult KMMessage::splitAddress( const QCString& address,
03580 QCString & displayName,
03581 QCString & addrSpec,
03582 QCString & comment )
03583 {
03584
03585
03586 displayName = "";
03587 addrSpec = "";
03588 comment = "";
03589
03590 if ( address.isEmpty() )
03591 return AddressEmpty;
03592
03593 QCString result;
03594
03595
03596
03597
03598
03599 enum { TopLevel, InComment, InAngleAddress } context = TopLevel;
03600 bool inQuotedString = false;
03601 int commentLevel = 0;
03602
03603 for ( char* p = address.data(); *p; ++p ) {
03604 switch ( context ) {
03605 case TopLevel : {
03606 switch ( *p ) {
03607 case '"' : inQuotedString = !inQuotedString;
03608 displayName += *p;
03609 break;
03610 case '(' : if ( !inQuotedString ) {
03611 context = InComment;
03612 commentLevel = 1;
03613 }
03614 else
03615 displayName += *p;
03616 break;
03617 case '<' : if ( !inQuotedString ) {
03618 context = InAngleAddress;
03619 }
03620 else
03621 displayName += *p;
03622 break;
03623 case '\\' :
03624 displayName += *p;
03625 ++p;
03626 if ( *p )
03627 displayName += *p;
03628 else
03629 return UnexpectedEnd;
03630 break;
03631 case ',' : if ( !inQuotedString )
03632 return UnexpectedComma;
03633 else
03634 displayName += *p;
03635 break;
03636 default : displayName += *p;
03637 }
03638 break;
03639 }
03640 case InComment : {
03641 switch ( *p ) {
03642 case '(' : ++commentLevel;
03643 comment += *p;
03644 break;
03645 case ')' : --commentLevel;
03646 if ( commentLevel == 0 ) {
03647 context = TopLevel;
03648 comment += ' ';
03649 }
03650 else
03651 comment += *p;
03652 break;
03653 case '\\' :
03654 comment += *p;
03655 ++p;
03656 if ( *p )
03657 comment += *p;
03658 else
03659 return UnexpectedEnd;
03660 break;
03661 default : comment += *p;
03662 }
03663 break;
03664 }
03665 case InAngleAddress : {
03666 switch ( *p ) {
03667 case '"' : inQuotedString = !inQuotedString;
03668 addrSpec += *p;
03669 break;
03670 case '>' : if ( !inQuotedString ) {
03671 context = TopLevel;
03672 }
03673 else
03674 addrSpec += *p;
03675 break;
03676 case '\\' :
03677 addrSpec += *p;
03678 ++p;
03679 if ( *p )
03680 addrSpec += *p;
03681 else
03682 return UnexpectedEnd;
03683 break;
03684 default : addrSpec += *p;
03685 }
03686 break;
03687 }
03688 }
03689 }
03690
03691 if ( inQuotedString )
03692 return UnbalancedQuote;
03693 if ( context == InComment )
03694 return UnbalancedParens;
03695 if ( context == InAngleAddress )
03696 return UnclosedAngleAddr;
03697
03698 displayName = displayName.stripWhiteSpace();
03699 comment = comment.stripWhiteSpace();
03700 addrSpec = addrSpec.stripWhiteSpace();
03701
03702 if ( addrSpec.isEmpty() ) {
03703 if ( displayName.isEmpty() )
03704 return NoAddressSpec;
03705 else {
03706 addrSpec = displayName;
03707 displayName.truncate( 0 );
03708 }
03709 }
03710
03711
03712
03713
03714
03715 return AddressOk;
03716 }
03717
03718
03719 QCString KMMessage::stripEmailAddr( const QCString& aStr )
03720 {
03721
03722
03723 if ( aStr.isEmpty() )
03724 return QCString();
03725
03726 QCString result;
03727
03728
03729
03730
03731
03732 QCString name;
03733 QCString comment;
03734 QCString angleAddress;
03735 enum { TopLevel, InComment, InAngleAddress } context = TopLevel;
03736 bool inQuotedString = false;
03737 int commentLevel = 0;
03738
03739 for ( char* p = aStr.data(); *p; ++p ) {
03740 switch ( context ) {
03741 case TopLevel : {
03742 switch ( *p ) {
03743 case '"' : inQuotedString = !inQuotedString;
03744 break;
03745 case '(' : if ( !inQuotedString ) {
03746 context = InComment;
03747 commentLevel = 1;
03748 }
03749 else
03750 name += *p;
03751 break;
03752 case '<' : if ( !inQuotedString ) {
03753 context = InAngleAddress;
03754 }
03755 else
03756 name += *p;
03757 break;
03758 case '\\' :
03759 ++p;
03760 if ( *p )
03761 name += *p;
03762 break;
03763 case ',' : if ( !inQuotedString ) {
03764
03765 if ( !result.isEmpty() )
03766 result += ", ";
03767 name = name.stripWhiteSpace();
03768 comment = comment.stripWhiteSpace();
03769 angleAddress = angleAddress.stripWhiteSpace();
03770
03771
03772
03773
03774
03775
03776
03777
03778 if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
03779
03780
03781 result += comment;
03782 }
03783 else if ( !name.isEmpty() ) {
03784 result += name;
03785 }
03786 else if ( !comment.isEmpty() ) {
03787 result += comment;
03788 }
03789 else if ( !angleAddress.isEmpty() ) {
03790 result += angleAddress;
03791 }
03792 name = QCString();
03793 comment = QCString();
03794 angleAddress = QCString();
03795 }
03796 else
03797 name += *p;
03798 break;
03799 default : name += *p;
03800 }
03801 break;
03802 }
03803 case InComment : {
03804 switch ( *p ) {
03805 case '(' : ++commentLevel;
03806 comment += *p;
03807 break;
03808 case ')' : --commentLevel;
03809 if ( commentLevel == 0 ) {
03810 context = TopLevel;
03811 comment += ' ';
03812 }
03813 else
03814 comment += *p;
03815 break;
03816 case '\\' :
03817 ++p;
03818 if ( *p )
03819 comment += *p;
03820 break;
03821 default : comment += *p;
03822 }
03823 break;
03824 }
03825 case InAngleAddress : {
03826 switch ( *p ) {
03827 case '"' : inQuotedString = !inQuotedString;
03828 angleAddress += *p;
03829 break;
03830 case '>' : if ( !inQuotedString ) {
03831 context = TopLevel;
03832 }
03833 else
03834 angleAddress += *p;
03835 break;
03836 case '\\' :
03837 ++p;
03838 if ( *p )
03839 angleAddress += *p;
03840 break;
03841 default : angleAddress += *p;
03842 }
03843 break;
03844 }
03845 }
03846 }
03847 if ( !result.isEmpty() )
03848 result += ", ";
03849 name = name.stripWhiteSpace();
03850 comment = comment.stripWhiteSpace();
03851 angleAddress = angleAddress.stripWhiteSpace();
03852
03853
03854
03855
03856
03857 if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
03858
03859
03860 result += comment;
03861 }
03862 else if ( !name.isEmpty() ) {
03863 result += name;
03864 }
03865 else if ( !comment.isEmpty() ) {
03866 result += comment;
03867 }
03868 else if ( !angleAddress.isEmpty() ) {
03869 result += angleAddress;
03870 }
03871
03872
03873
03874 return result;
03875 }
03876
03877
03878 QString KMMessage::stripEmailAddr( const QString& aStr )
03879 {
03880
03881
03882 if ( aStr.isEmpty() )
03883 return QString::null;
03884
03885 QString result;
03886
03887
03888
03889
03890
03891 QString name;
03892 QString comment;
03893 QString angleAddress;
03894 enum { TopLevel, InComment, InAngleAddress } context = TopLevel;
03895 bool inQuotedString = false;
03896 int commentLevel = 0;
03897
03898 QChar ch;
03899 for ( uint index = 0; index < aStr.length(); ++index ) {
03900 ch = aStr[index];
03901 switch ( context ) {
03902 case TopLevel : {
03903 switch ( ch.latin1() ) {
03904 case '"' : inQuotedString = !inQuotedString;
03905 break;
03906 case '(' : if ( !inQuotedString ) {
03907 context = InComment;
03908 commentLevel = 1;
03909 }
03910 else
03911 name += ch;
03912 break;
03913 case '<' : if ( !inQuotedString ) {
03914 context = InAngleAddress;
03915 }
03916 else
03917 name += ch;
03918 break;
03919 case '\\' :
03920 ++index;
03921 if ( index < aStr.length() )
03922 name += aStr[index];
03923 break;
03924 case ',' : if ( !inQuotedString ) {
03925
03926 if ( !result.isEmpty() )
03927 result += ", ";
03928 name = name.stripWhiteSpace();
03929 comment = comment.stripWhiteSpace();
03930 angleAddress = angleAddress.stripWhiteSpace();
03931
03932
03933
03934
03935
03936
03937
03938
03939 if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
03940
03941
03942 result += comment;
03943 }
03944 else if ( !name.isEmpty() ) {
03945 result += name;
03946 }
03947 else if ( !comment.isEmpty() ) {
03948 result += comment;
03949 }
03950 else if ( !angleAddress.isEmpty() ) {
03951 result += angleAddress;
03952 }
03953 name = QString::null;
03954 comment = QString::null;
03955 angleAddress = QString::null;
03956 }
03957 else
03958 name += ch;
03959 break;
03960 default : name += ch;
03961 }
03962 break;
03963 }
03964 case InComment : {
03965 switch ( ch.latin1() ) {
03966 case '(' : ++commentLevel;
03967 comment += ch;
03968 break;
03969 case ')' : --commentLevel;
03970 if ( commentLevel == 0 ) {
03971 context = TopLevel;
03972 comment += ' ';
03973 }
03974 else
03975 comment += ch;
03976 break;
03977 case '\\' :
03978 ++index;
03979 if ( index < aStr.length() )
03980 comment += aStr[index];
03981 break;
03982 default : comment += ch;
03983 }
03984 break;
03985 }
03986 case InAngleAddress : {
03987 switch ( ch.latin1() ) {
03988 case '"' : inQuotedString = !inQuotedString;
03989 angleAddress += ch;
03990 break;
03991 case '>' : if ( !inQuotedString ) {
03992 context = TopLevel;
03993 }
03994 else
03995 angleAddress += ch;
03996 break;
03997 case '\\' :
03998 ++index;
03999 if ( index < aStr.length() )
04000 angleAddress += aStr[index];
04001 break;
04002 default : angleAddress += ch;
04003 }
04004 break;
04005 }
04006 }
04007 }
04008 if ( !result.isEmpty() )
04009 result += ", ";
04010 name = name.stripWhiteSpace();
04011 comment = comment.stripWhiteSpace();
04012 angleAddress = angleAddress.stripWhiteSpace();
04013
04014
04015
04016
04017
04018 if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
04019
04020
04021 result += comment;
04022 }
04023 else if ( !name.isEmpty() ) {
04024 result += name;
04025 }
04026 else if ( !comment.isEmpty() ) {
04027 result += comment;
04028 }
04029 else if ( !angleAddress.isEmpty() ) {
04030 result += angleAddress;
04031 }
04032
04033
04034
04035 return result;
04036 }
04037
04038
04039 QString KMMessage::quoteHtmlChars( const QString& str, bool removeLineBreaks )
04040 {
04041 QString result;
04042 result.reserve( 6*str.length() );
04043
04044 for( unsigned int i = 0; i < str.length(); ++i )
04045 switch ( str[i].latin1() ) {
04046 case '<':
04047 result += "<";
04048 break;
04049 case '>':
04050 result += ">";
04051 break;
04052 case '&':
04053 result += "&";
04054 break;
04055 case '"':
04056 result += """;
04057 break;
04058 case '\n':
04059 if ( !removeLineBreaks )
04060 result += "<br>";
04061 break;
04062 case '\r':
04063
04064 break;
04065 default:
04066 result += str[i];
04067 }
04068
04069 result.squeeze();
04070 return result;
04071 }
04072
04073
04074 QString KMMessage::emailAddrAsAnchor(const QString& aEmail, bool stripped)
04075 {
04076 if( aEmail.isEmpty() )
04077 return aEmail;
04078
04079 QStringList addressList = KPIM::splitEmailAddrList( aEmail );
04080
04081 QString result;
04082
04083 for( QStringList::ConstIterator it = addressList.begin();
04084 ( it != addressList.end() );
04085 ++it ) {
04086 if( !(*it).isEmpty() ) {
04087 QString address = *it;
04088 result += "<a href=\"mailto:"
04089 + KMMessage::encodeMailtoUrl( address )
04090 + "\">";
04091 if( stripped )
04092 address = KMMessage::stripEmailAddr( address );
04093 result += KMMessage::quoteHtmlChars( address, true );
04094 result += "</a>, ";
04095 }
04096 }
04097
04098 result.truncate( result.length() - 2 );
04099
04100
04101
04102 return result;
04103 }
04104
04105
04106
04107
04108 QStringList KMMessage::stripAddressFromAddressList( const QString& address,
04109 const QStringList& list )
04110 {
04111 QStringList addresses = list;
04112 QCString addrSpec = KPIM::getEmailAddr( address ).lower();
04113 for( QStringList::Iterator it = addresses.begin();
04114 it != addresses.end(); ) {
04115 if( addrSpec == KPIM::getEmailAddr( *it ).lower() ) {
04116 kdDebug(5006) << "Removing " << *it << " from the address list"
04117 << endl;
04118 it = addresses.remove( it );
04119 }
04120 else
04121 ++it;
04122 }
04123 return addresses;
04124 }
04125
04126
04127
04128
04129 QStringList KMMessage::stripMyAddressesFromAddressList( const QStringList& list )
04130 {
04131 QStringList addresses = list;
04132 for( QStringList::Iterator it = addresses.begin();
04133 it != addresses.end(); ) {
04134 kdDebug(5006) << "Check whether " << *it << " is one of my addresses"
04135 << endl;
04136 if( kmkernel->identityManager()->thatIsMe( KPIM::getEmailAddr( *it ).lower() ) ) {
04137 kdDebug(5006) << "Removing " << *it << " from the address list"
04138 << endl;
04139 it = addresses.remove( it );
04140 }
04141 else
04142 ++it;
04143 }
04144 return addresses;
04145 }
04146
04147
04148
04149
04150 bool KMMessage::addressIsInAddressList( const QString& address,
04151 const QStringList& addresses )
04152 {
04153 QCString addrSpec = KPIM::getEmailAddr( address ).lower();
04154 for( QStringList::ConstIterator it = addresses.begin();
04155 it != addresses.end(); ++it ) {
04156 if( addrSpec == KPIM::getEmailAddr( *it ).lower() )
04157 return true;
04158 }
04159 return false;
04160 }
04161
04162
04163
04164
04165 QString KMMessage::expandAliases( const QString& recipients )
04166 {
04167 if ( recipients.isEmpty() )
04168 return QString();
04169
04170 QStringList recipientList = KPIM::splitEmailAddrList( recipients );
04171
04172 QString expandedRecipients;
04173 for ( QStringList::Iterator it = recipientList.begin();
04174 it != recipientList.end(); ++it ) {
04175 if ( !expandedRecipients.isEmpty() )
04176 expandedRecipients += ", ";
04177 QString receiver = (*it).stripWhiteSpace();
04178
04179
04180 QString expandedList = KAddrBookExternal::expandDistributionList( receiver );
04181 if ( !expandedList.isEmpty() ) {
04182 expandedRecipients += expandedList;
04183 continue;
04184 }
04185
04186
04187 QString expandedNickName = KabcBridge::expandNickName( receiver );
04188 if ( !expandedNickName.isEmpty() ) {
04189 expandedRecipients += expandedNickName;
04190 continue;
04191 }
04192
04193
04194
04195 if ( receiver.find('@') == -1 ) {
04196 KConfigGroup general( KMKernel::config(), "General" );
04197 QString defaultdomain = general.readEntry( "Default domain" );
04198 if( !defaultdomain.isEmpty() ) {
04199 expandedRecipients += receiver + "@" + defaultdomain;
04200 }
04201 else {
04202 expandedRecipients += guessEmailAddressFromLoginName( receiver );
04203 }
04204 }
04205 else
04206 expandedRecipients += receiver;
04207 }
04208
04209 return expandedRecipients;
04210 }
04211
04212
04213
04214
04215 QString KMMessage::guessEmailAddressFromLoginName( const QString& loginName )
04216 {
04217 if ( loginName.isEmpty() )
04218 return QString();
04219
04220 char hostnameC[256];
04221
04222 hostnameC[255] = '\0';
04223
04224 if ( gethostname( hostnameC, 255 ) )
04225 hostnameC[0] = '\0';
04226 QString address = loginName;
04227 address += '@';
04228 address += QString::fromLocal8Bit( hostnameC );
04229
04230
04231 const KUser user( loginName );
04232 if ( user.isValid() ) {
04233 QString fullName = user.fullName();
04234 if ( fullName.find( QRegExp( "[^ 0-9A-Za-z\\x0080-\\xFFFF]" ) ) != -1 )
04235 address = '"' + fullName.replace( '\\', "\\" ).replace( '"', "\\" )
04236 + "\" <" + address + '>';
04237 else
04238 address = fullName + " <" + address + '>';
04239 }
04240
04241 return address;
04242 }
04243
04244
04245 void KMMessage::readConfig()
04246 {
04247 KMMsgBase::readConfig();
04248
04249 KConfig *config=KMKernel::config();
04250 KConfigGroupSaver saver(config, "General");
04251
04252 config->setGroup("General");
04253
04254 int languageNr = config->readNumEntry("reply-current-language",0);
04255
04256 {
04257 KConfigGroupSaver saver(config, QString("KMMessage #%1").arg(languageNr));
04258 sReplyLanguage = config->readEntry("language",KGlobal::locale()->language());
04259 sReplyStr = config->readEntry("phrase-reply",
04260 i18n("On %D, you wrote:"));
04261 sReplyAllStr = config->readEntry("phrase-reply-all",
04262 i18n("On %D, %F wrote:"));
04263 sForwardStr = config->readEntry("phrase-forward",
04264 i18n("Forwarded Message"));
04265 sIndentPrefixStr = config->readEntry("indent-prefix",">%_");
04266 }
04267
04268 {
04269 KConfigGroupSaver saver(config, "Composer");
04270 sSmartQuote = config->readBoolEntry("smart-quote", true);
04271 sWordWrap = config->readBoolEntry( "word-wrap", true );
04272 sWrapCol = config->readNumEntry("break-at", 78);
04273 if ((sWrapCol == 0) || (sWrapCol > 78))
04274 sWrapCol = 78;
04275 if (sWrapCol < 30)
04276 sWrapCol = 30;
04277
04278 sPrefCharsets = config->readListEntry("pref-charsets");
04279 }
04280
04281 {
04282 KConfigGroupSaver saver(config, "Reader");
04283 sHeaderStrategy = HeaderStrategy::create( config->readEntry( "header-set-displayed", "rich" ) );
04284 }
04285 }
04286
04287 QCString KMMessage::defaultCharset()
04288 {
04289 QCString retval;
04290
04291 if (!sPrefCharsets.isEmpty())
04292 retval = sPrefCharsets[0].latin1();
04293
04294 if (retval.isEmpty() || (retval == "locale"))
04295 retval = QCString(kmkernel->networkCodec()->mimeName()).lower();
04296
04297 if (retval == "jisx0208.1983-0") retval = "iso-2022-jp";
04298 else if (retval == "ksc5601.1987-0") retval = "euc-kr";
04299 return retval;
04300 }
04301
04302 const QStringList &KMMessage::preferredCharsets()
04303 {
04304 return sPrefCharsets;
04305 }
04306
04307
04308 QCString KMMessage::charset() const
04309 {
04310 DwMediaType &mType=mMsg->Headers().ContentType();
04311 mType.Parse();
04312 DwParameter *param=mType.FirstParameter();
04313 while(param){
04314 if (!qstricmp(param->Attribute().c_str(), "charset"))
04315 return param->Value().c_str();
04316 else param=param->Next();
04317 }
04318 return "";
04319 }
04320
04321
04322 void KMMessage::setCharset(const QCString& bStr)
04323 {
04324 kdWarning( type() != DwMime::kTypeText )
04325 << "KMMessage::setCharset(): trying to set a charset for a non-textual mimetype." << endl
04326 << "Fix this caller:" << endl
04327 << "====================================================================" << endl
04328 << kdBacktrace( 5 ) << endl
04329 << "====================================================================" << endl;
04330 QCString aStr = bStr.lower();
04331 if (aStr.isNull())
04332 aStr = "";
04333 DwMediaType &mType = dwContentType();
04334 mType.Parse();
04335 DwParameter *param=mType.FirstParameter();
04336 while(param)
04337
04338 if (!qstricmp(param->Attribute().c_str(), "charset")) break;
04339 else param=param->Next();
04340 if (!param){
04341 param=new DwParameter;
04342 param->SetAttribute("charset");
04343 mType.AddParameter(param);
04344 }
04345 else
04346 mType.SetModified();
04347 param->SetValue(DwString(aStr));
04348 mType.Assemble();
04349 }
04350
04351
04352
04353 void KMMessage::setStatus(const KMMsgStatus aStatus, int idx)
04354 {
04355 if (mStatus == aStatus)
04356 return;
04357 KMMsgBase::setStatus(aStatus, idx);
04358 }
04359
04360 void KMMessage::setEncryptionState(const KMMsgEncryptionState s, int idx)
04361 {
04362 if( mEncryptionState == s )
04363 return;
04364 mEncryptionState = s;
04365 mDirty = true;
04366 KMMsgBase::setEncryptionState(s, idx);
04367 }
04368
04369 void KMMessage::setSignatureState(KMMsgSignatureState s, int idx)
04370 {
04371 if( mSignatureState == s )
04372 return;
04373 mSignatureState = s;
04374 mDirty = true;
04375 KMMsgBase::setSignatureState(s, idx);
04376 }
04377
04378 void KMMessage::setMDNSentState( KMMsgMDNSentState status, int idx ) {
04379 if ( mMDNSentState == status )
04380 return;
04381 if ( status == 0 )
04382 status = KMMsgMDNStateUnknown;
04383 mMDNSentState = status;
04384 mDirty = true;
04385 KMMsgBase::setMDNSentState( status, idx );
04386 }
04387
04388
04389 void KMMessage::link( const KMMessage *aMsg, KMMsgStatus aStatus )
04390 {
04391 Q_ASSERT( aStatus == KMMsgStatusReplied
04392 || aStatus == KMMsgStatusForwarded || aStatus == KMMsgStatusDeleted );
04393
04394 QString message = headerField( "X-KMail-Link-Message" );
04395 if ( !message.isEmpty() )
04396 message += ',';
04397 QString type = headerField( "X-KMail-Link-Type" );
04398 if ( !type.isEmpty() )
04399 type += ',';
04400
04401 message += QString::number( aMsg->getMsgSerNum() );
04402 if ( aStatus == KMMsgStatusReplied )
04403 type += "reply";
04404 else if ( aStatus == KMMsgStatusForwarded )
04405 type += "forward";
04406 else if ( aStatus == KMMsgStatusDeleted )
04407 type += "deleted";
04408
04409 setHeaderField( "X-KMail-Link-Message", message );
04410 setHeaderField( "X-KMail-Link-Type", type );
04411 }
04412
04413
04414 void KMMessage::getLink(int n, ulong *retMsgSerNum, KMMsgStatus *retStatus) const
04415 {
04416 *retMsgSerNum = 0;
04417 *retStatus = KMMsgStatusUnknown;
04418
04419 QString message = headerField("X-KMail-Link-Message");
04420 QString type = headerField("X-KMail-Link-Type");
04421 message = message.section(',', n, n);
04422 type = type.section(',', n, n);
04423
04424 if ( !message.isEmpty() && !type.isEmpty() ) {
04425 *retMsgSerNum = message.toULong();
04426 if ( type == "reply" )
04427 *retStatus = KMMsgStatusReplied;
04428 else if ( type == "forward" )
04429 *retStatus = KMMsgStatusForwarded;
04430 else if ( type == "deleted" )
04431 *retStatus = KMMsgStatusDeleted;
04432 }
04433 }
04434
04435
04436 DwBodyPart* KMMessage::findDwBodyPart( DwBodyPart* part, const QString & partSpecifier )
04437 {
04438 if ( !part ) return 0;
04439 DwBodyPart* current;
04440
04441 if ( part->partId() == partSpecifier )
04442 return part;
04443
04444
04445 if ( part->hasHeaders() &&
04446 part->Headers().HasContentType() &&
04447 part->Body().FirstBodyPart() &&
04448 (DwMime::kTypeMultipart == part->Headers().ContentType().Type() ) &&
04449 (current = findDwBodyPart( part->Body().FirstBodyPart(), partSpecifier )) )
04450 {
04451 return current;
04452 }
04453
04454
04455 if ( part->Body().Message() &&
04456 part->Body().Message()->Body().FirstBodyPart() &&
04457 (current = findDwBodyPart( part->Body().Message()->Body().FirstBodyPart(), partSpecifier )) )
04458 {
04459 return current;
04460 }
04461
04462
04463 return findDwBodyPart( part->Next(), partSpecifier );
04464 }
04465
04466
04467 void KMMessage::updateBodyPart(const QString partSpecifier, const QByteArray & data)
04468 {
04469 DwString content( data.data(), data.size() );
04470 if ( numBodyParts() > 0 &&
04471 partSpecifier != "0" &&
04472 partSpecifier != "TEXT" )
04473 {
04474 QString specifier = partSpecifier;
04475 if ( partSpecifier.endsWith(".HEADER") ||
04476 partSpecifier.endsWith(".MIME") ) {
04477
04478 specifier = partSpecifier.section( '.', 0, -2 );
04479 }
04480 kdDebug(5006) << "KMMessage::updateBodyPart " << specifier << endl;
04481
04482
04483 mLastUpdated = findDwBodyPart( getFirstDwBodyPart(), specifier );
04484 if (!mLastUpdated)
04485 {
04486 kdWarning(5006) << "KMMessage::updateBodyPart - can not find part "
04487 << specifier << endl;
04488 return;
04489 }
04490 if ( partSpecifier.endsWith(".MIME") )
04491 {
04492
04493
04494 content.resize( content.length()-2 );
04495
04496
04497 mLastUpdated->Headers().DeleteAllFields();
04498 mLastUpdated->Headers().FromString( content );
04499 mLastUpdated->Headers().Parse();
04500 } else {
04501
04502 mLastUpdated->Body().FromString( content );
04503 mLastUpdated->Body().Parse();
04504 }
04505
04506 } else
04507 {
04508
04509 if ( partSpecifier == "TEXT" )
04510 deleteBodyParts();
04511 mMsg->Body().FromString( content );
04512 mMsg->Body().Parse();
04513 }
04514 mNeedsAssembly = true;
04515 if (! partSpecifier.endsWith(".HEADER") )
04516 {
04517
04518 notify();
04519 }
04520 }
04521
04522
04523 void KMMessage::updateAttachmentState( DwBodyPart* part )
04524 {
04525 static const char cSMIMEData[] = "smime.p7s";
04526
04527 if ( !part )
04528 part = getFirstDwBodyPart();
04529 if ( !part )
04530 {
04531 setStatus( KMMsgStatusHasNoAttach );
04532 return;
04533 }
04534
04535 if ( part->hasHeaders() &&
04536 part->Headers().HasContentDisposition() &&
04537 !part->Headers().ContentDisposition().Filename().empty() &&
04538 0 != qstricmp(part->Headers().ContentDisposition().Filename().c_str(), cSMIMEData ))
04539 {
04540 setStatus( KMMsgStatusHasAttach );
04541 return;
04542 }
04543
04544
04545 if ( part->hasHeaders() &&
04546 part->Headers().HasContentType() &&
04547 part->Body().FirstBodyPart() &&
04548 (DwMime::kTypeMultipart == part->Headers().ContentType().Type() ) )
04549 {
04550 updateAttachmentState( part->Body().FirstBodyPart() );
04551 }
04552
04553
04554 if ( part->Body().Message() &&
04555 part->Body().Message()->Body().FirstBodyPart() )
04556 {
04557 updateAttachmentState( part->Body().Message()->Body().FirstBodyPart() );
04558 }
04559
04560
04561 if ( part->Next() )
04562 updateAttachmentState( part->Next() );
04563 else if ( attachmentState() == KMMsgAttachmentUnknown )
04564 setStatus( KMMsgStatusHasNoAttach );
04565 }
04566
04567 void KMMessage::setBodyFromUnicode( const QString & str ) {
04568 QCString encoding = KMMsgBase::autoDetectCharset( charset(), KMMessage::preferredCharsets(), str );
04569 if ( encoding.isEmpty() )
04570 encoding = "utf-8";
04571 const QTextCodec * codec = KMMsgBase::codecForName( encoding );
04572 assert( codec );
04573 QValueList<int> dummy;
04574 setCharset( encoding );
04575 setBodyAndGuessCte( codec->fromUnicode( str ), dummy, false );
04576 }
04577
04578 const QTextCodec * KMMessage::codec() const {
04579 const QTextCodec * c = mOverrideCodec;
04580 if ( !c )
04581
04582 c = KMMsgBase::codecForName( charset() );
04583 if ( !c ) {
04584
04585
04586 c = KMMsgBase::codecForName( GlobalSettings::self()->fallbackCharacterEncoding().latin1() );
04587 }
04588 if ( !c )
04589
04590
04591 c = kmkernel->networkCodec();
04592 assert( c );
04593 return c;
04594 }
04595
04596 QString KMMessage::bodyToUnicode(const QTextCodec* codec) const {
04597 if ( !codec )
04598
04599 codec = this->codec();
04600 assert( codec );
04601
04602 return codec->toUnicode( bodyDecoded() );
04603 }
04604
04605
04606 QCString KMMessage::mboxMessageSeparator()
04607 {
04608 QCString str( fromEmail() );
04609 if ( str.isEmpty() )
04610 str = "unknown@unknown.invalid";
04611 QCString dateStr( dateShortStr() );
04612 if ( dateStr.isEmpty() ) {
04613 time_t t = ::time( 0 );
04614 dateStr = ctime( &t );
04615 const int len = dateStr.length();
04616 if ( dateStr[len-1] == '\n' )
04617 dateStr.truncate( len - 1 );
04618 }
04619 return "From " + str + " " + dateStr + "\n";
04620 }