kmail

kmmessage.cpp

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