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