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