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