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