kmail

kmmessage.cpp

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