kmail

kmmessage.cpp
1 // -*- mode: C++; c-file-style: "gnu" -*-
2 // kmmessage.cpp
3 
4 // if you do not want GUI elements in here then set ALLOW_GUI to 0.
5 #include <config.h>
6 // needed temporarily until KMime is replacing the partNode helper class:
7 #include "partNode.h"
8 
9 
10 #define ALLOW_GUI 1
11 #include "kmkernel.h"
12 #include "kmmessage.h"
13 #include "mailinglist-magic.h"
14 #include "messageproperty.h"
15 using KMail::MessageProperty;
16 #include "objecttreeparser.h"
17 using KMail::ObjectTreeParser;
18 #include "kmfolderindex.h"
19 #include "undostack.h"
20 #include "kmversion.h"
21 #include "headerstrategy.h"
22 #include "globalsettings.h"
23 using KMail::HeaderStrategy;
24 #include "kmaddrbook.h"
25 #include "kcursorsaver.h"
26 #include "templateparser.h"
27 
28 #include <libkpimidentities/identity.h>
29 #include <libkpimidentities/identitymanager.h>
30 #include <libemailfunctions/email.h>
31 
32 #include <kasciistringtools.h>
33 
34 #include <kpgpblock.h>
35 #include <kaddrbook.h>
36 
37 #include <tdeapplication.h>
38 #include <tdeglobalsettings.h>
39 #include <kdebug.h>
40 #include <tdeconfig.h>
41 #include <tdehtml_part.h>
42 #include <kuser.h>
43 #include <kidna.h>
44 #include <kasciistricmp.h>
45 
46 #include <tqcursor.h>
47 #include <tqtextcodec.h>
48 #include <tqmessagebox.h>
49 #include <kmime_util.h>
50 #include <kmime_charfreq.h>
51 
52 #include <kmime_header_parsing.h>
53 using KMime::HeaderParsing::parseAddressList;
54 using namespace KMime::Types;
55 
56 #include <mimelib/body.h>
57 #include <mimelib/field.h>
58 #include <mimelib/mimepp.h>
59 #include <mimelib/string.h>
60 #include <assert.h>
61 #include <sys/time.h>
62 #include <time.h>
63 #include <tdelocale.h>
64 #include <stdlib.h>
65 #include <unistd.h>
66 #include "util.h"
67 
68 #if ALLOW_GUI
69 #include <tdemessagebox.h>
70 #endif
71 
72 using namespace KMime;
73 
74 static DwString emptyString("");
75 
76 // Values that are set from the config file with KMMessage::readConfig()
77 static TQString sReplyLanguage, sReplyStr, sReplyAllStr, sIndentPrefixStr;
78 static bool sSmartQuote,
79  sWordWrap;
80 static int sWrapCol;
81 static TQStringList sPrefCharsets;
82 
83 TQString KMMessage::sForwardStr;
84 const HeaderStrategy * KMMessage::sHeaderStrategy = HeaderStrategy::rich();
85 //helper
86 static void applyHeadersToMessagePart( DwHeaders& headers, KMMessagePart* aPart );
87 
88 TQValueList<KMMessage*> KMMessage::sPendingDeletes;
89 
90 //-----------------------------------------------------------------------------
91 KMMessage::KMMessage(DwMessage* aMsg)
92  : KMMsgBase()
93 {
94  init( aMsg );
95  // aMsg might need assembly
96  mNeedsAssembly = true;
97 }
98 
99 //-----------------------------------------------------------------------------
100 KMMessage::KMMessage(KMFolder* parent): KMMsgBase(parent)
101 {
102  init();
103 }
104 
105 
106 //-----------------------------------------------------------------------------
107 KMMessage::KMMessage(KMMsgInfo& msgInfo): KMMsgBase()
108 {
109  init();
110  // now overwrite a few from the msgInfo
111  mMsgSize = msgInfo.msgSize();
112  mFolderOffset = msgInfo.folderOffset();
113  mStatus = msgInfo.status();
114  mEncryptionState = msgInfo.encryptionState();
115  mSignatureState = msgInfo.signatureState();
116  mMDNSentState = msgInfo.mdnSentState();
117  mDate = msgInfo.date();
118  mFileName = msgInfo.fileName();
119  KMMsgBase::assign(&msgInfo);
120 }
121 
122 
123 //-----------------------------------------------------------------------------
125  KMMsgBase( other ),
126  ISubject(),
127  mMsg(0)
128 {
129  init(); // to be safe
130  assign( other );
131 }
132 
133 void KMMessage::init( DwMessage* aMsg )
134 {
135  mNeedsAssembly = false;
136  if ( aMsg ) {
137  mMsg = aMsg;
138  } else {
139  mMsg = new DwMessage;
140  }
141  mOverrideCodec = 0;
142  mDecodeHTML = false;
143  mComplete = true;
144  mReadyToShow = true;
145  mMsgSize = 0;
146  mMsgLength = 0;
147  mFolderOffset = 0;
148  mStatus = KMMsgStatusNew;
149  mEncryptionState = KMMsgEncryptionStateUnknown;
150  mSignatureState = KMMsgSignatureStateUnknown;
151  mMDNSentState = KMMsgMDNStateUnknown;
152  mDate = 0;
153  mUnencryptedMsg = 0;
154  mLastUpdated = 0;
155  mCursorPos = 0;
156  mMsgInfo = 0;
157  mIsParsed = false;
158 }
159 
160 void KMMessage::assign( const KMMessage& other )
161 {
162  MessageProperty::forget( this );
163  delete mMsg;
164  delete mUnencryptedMsg;
165 
166  mNeedsAssembly = true;//other.mNeedsAssembly;
167  if( other.mMsg )
168  mMsg = new DwMessage( *(other.mMsg) );
169  else
170  mMsg = 0;
171  mOverrideCodec = other.mOverrideCodec;
172  mDecodeHTML = other.mDecodeHTML;
173  mMsgSize = other.mMsgSize;
174  mMsgLength = other.mMsgLength;
175  mFolderOffset = other.mFolderOffset;
176  mStatus = other.mStatus;
177  mEncryptionState = other.mEncryptionState;
178  mSignatureState = other.mSignatureState;
179  mMDNSentState = other.mMDNSentState;
180  mIsParsed = other.mIsParsed;
181  mDate = other.mDate;
182  if( other.hasUnencryptedMsg() )
183  mUnencryptedMsg = new KMMessage( *other.unencryptedMsg() );
184  else
185  mUnencryptedMsg = 0;
186  setDrafts( other.drafts() );
187  setTemplates( other.templates() );
188  //mFileName = ""; // we might not want to copy the other messages filename (?)
189  //KMMsgBase::assign( &other );
190 }
191 
192 //-----------------------------------------------------------------------------
194 {
195  delete mMsgInfo;
196  delete mMsg;
197  kmkernel->undoStack()->msgDestroyed( this );
198 }
199 
200 
201 //-----------------------------------------------------------------------------
202 void KMMessage::setReferences(const TQCString& aStr)
203 {
204  if (aStr.isNull()) return;
205  mMsg->Headers().References().FromString(aStr);
206  mNeedsAssembly = true;
207 }
208 
209 
210 //-----------------------------------------------------------------------------
211 TQCString KMMessage::id() const
212 {
213  DwHeaders& header = mMsg->Headers();
214  if (header.HasMessageId())
215  return KMail::Util::CString( header.MessageId().AsString() );
216  else
217  return "";
218 }
219 
220 
221 //-----------------------------------------------------------------------------
222 //WARNING: This method updates the memory resident cache of serial numbers
223 //WARNING: held in MessageProperty, but it does not update the persistent
224 //WARNING: store of serial numbers on the file system that is managed by
225 //WARNING: KMMsgDict
226 void KMMessage::setMsgSerNum(unsigned long newMsgSerNum)
227 {
228  MessageProperty::setSerialCache( this, newMsgSerNum );
229 }
230 
231 
232 //-----------------------------------------------------------------------------
234 {
235  return true;
236 }
237 
238 //-----------------------------------------------------------------------------
240 {
241  return MessageProperty::transferInProgress( getMsgSerNum() );
242 }
243 
244 
245 //-----------------------------------------------------------------------------
246 void KMMessage::setTransferInProgress(bool value, bool force)
247 {
248  MessageProperty::setTransferInProgress( getMsgSerNum(), value, force );
249  if ( !transferInProgress() && sPendingDeletes.contains( this ) ) {
250  sPendingDeletes.remove( this );
251  if ( parent() ) {
252  int idx = parent()->find( this );
253  if ( idx > 0 ) {
254  parent()->removeMsg( idx );
255  }
256  }
257  }
258 }
259 
260 
261 
262 bool KMMessage::isUrgent() const {
263  return headerField( "Priority" ).contains( "urgent", false )
264  || headerField( "X-Priority" ).startsWith( "2" );
265 }
266 
267 //-----------------------------------------------------------------------------
269 {
270  delete mUnencryptedMsg;
271  mUnencryptedMsg = unencrypted;
272 }
273 
274 //-----------------------------------------------------------------------------
275 //FIXME: move to libemailfunctions
276 KPIM::EmailParseResult KMMessage::isValidEmailAddressList( const TQString& aStr,
277  TQString& brokenAddress )
278 {
279  if ( aStr.isEmpty() ) {
280  return KPIM::AddressEmpty;
281  }
282 
283  TQStringList list = KPIM::splitEmailAddrList( aStr );
284  for( TQStringList::const_iterator it = list.begin(); it != list.end(); ++it ) {
285  KPIM::EmailParseResult errorCode = KPIM::isValidEmailAddress( *it );
286  if ( errorCode != KPIM::AddressOk ) {
287  brokenAddress = ( *it );
288  return errorCode;
289  }
290  }
291  return KPIM::AddressOk;
292 }
293 
294 //-----------------------------------------------------------------------------
295 const DwString& KMMessage::asDwString() const
296 {
297  if (mNeedsAssembly)
298  {
299  mNeedsAssembly = false;
300  mMsg->Assemble();
301  }
302  return mMsg->AsString();
303 }
304 
305 //-----------------------------------------------------------------------------
306 const DwMessage* KMMessage::asDwMessage()
307 {
308  if (mNeedsAssembly)
309  {
310  mNeedsAssembly = false;
311  mMsg->Assemble();
312  }
313  return mMsg;
314 }
315 
316 //-----------------------------------------------------------------------------
317 TQCString KMMessage::asString() const {
318  return KMail::Util::CString( asDwString() );
319 }
320 
321 
322 TQByteArray KMMessage::asSendableString() const
323 {
324  KMMessage msg( new DwMessage( *this->mMsg ) );
326  msg.removeHeaderField("Bcc");
327  return KMail::Util::ByteArray( msg.asDwString() ); // and another copy again!
328 }
329 
331 {
332  KMMessage msg( new DwMessage( *this->mMsg ) );
334  msg.removeHeaderField("Bcc");
335  return msg.headerAsString().latin1();
336 }
337 
339  removeHeaderField("Status");
340  removeHeaderField("X-Status");
341  removeHeaderField("X-KMail-EncryptionState");
342  removeHeaderField("X-KMail-SignatureState");
343  removeHeaderField("X-KMail-MDN-Sent");
344  removeHeaderField("X-KMail-Transport");
345  removeHeaderField("X-KMail-Identity");
346  removeHeaderField("X-KMail-Fcc");
347  removeHeaderField("X-KMail-Redirect-From");
348  removeHeaderField("X-KMail-Link-Message");
349  removeHeaderField("X-KMail-Link-Type");
350  removeHeaderField( "X-KMail-Markup" );
351 }
352 
353 //-----------------------------------------------------------------------------
355 {
356  char str[2] = { 0, 0 };
357 
358  setHeaderField("Status", status() & KMMsgStatusNew ? "R" : "RO");
359  setHeaderField("X-Status", statusToStr(status()));
360 
361  str[0] = (char)encryptionState();
362  setHeaderField("X-KMail-EncryptionState", str);
363 
364  str[0] = (char)signatureState();
365  //kdDebug(5006) << "Setting SignatureState header field to " << str[0] << endl;
366  setHeaderField("X-KMail-SignatureState", str);
367 
368  str[0] = static_cast<char>( mdnSentState() );
369  setHeaderField("X-KMail-MDN-Sent", str);
370 
371  // We better do the assembling ourselves now to prevent the
372  // mimelib from changing the message *body*. (khz, 10.8.2002)
373  mNeedsAssembly = false;
374  mMsg->Headers().Assemble();
375  mMsg->Assemble( mMsg->Headers(),
376  mMsg->Body() );
377 }
378 
379 
380 //----------------------------------------------------------------------------
382 {
383  DwHeaders& header = mMsg->Headers();
384  header.Assemble();
385  if ( header.AsString().empty() )
386  return TQString();
387  return TQString::fromLatin1( header.AsString().c_str() );
388 }
389 
390 
391 //-----------------------------------------------------------------------------
393 {
394  return mMsg->Headers().ContentType();
395 }
396 
397 void KMMessage::fromByteArray( const TQByteArray & ba, bool setStatus ) {
398  return fromDwString( DwString( ba.data(), ba.size() ), setStatus );
399 }
400 
401 void KMMessage::fromString( const TQCString & str, bool aSeStatus ) {
402  return fromDwString( KMail::Util::dwString( str ), aSeStatus );
403 }
404 
405 void KMMessage::fromDwString(const DwString& str, bool aSeStatus)
406 {
407  delete mMsg;
408  mMsg = new DwMessage;
409  mMsg->FromString( str );
410  mMsg->Parse();
411 
412  if (aSeStatus) {
413  setStatus(headerField("Status").latin1(), headerField("X-Status").latin1());
414  setEncryptionStateChar( headerField("X-KMail-EncryptionState").at(0) );
415  setSignatureStateChar( headerField("X-KMail-SignatureState").at(0) );
416  setMDNSentState( static_cast<KMMsgMDNSentState>( headerField("X-KMail-MDN-Sent").at(0).latin1() ) );
417  }
418  if ( invitationState() == KMMsgInvitationUnknown && readyToShow() )
419  updateInvitationState();
420  if ( attachmentState() == KMMsgAttachmentUnknown && readyToShow() )
421  updateAttachmentState();
422 
423  mNeedsAssembly = false;
424  mDate = date();
425 }
426 
427 
428 //-----------------------------------------------------------------------------
429 TQString KMMessage::formatString(const TQString& aStr) const
430 {
431  TQString result, str;
432  TQChar ch;
433  uint j;
434 
435  if (aStr.isEmpty())
436  return aStr;
437 
438  unsigned int strLength(aStr.length());
439  for (uint i=0; i<strLength;) {
440  ch = aStr[i++];
441  if (ch == '%') {
442  ch = aStr[i++];
443  switch ((char)ch) {
444  case 'D':
445  /* I'm not too sure about this change. Is it not possible
446  to have a long form of the date used? I don't
447  like this change to a short XX/XX/YY date format.
448  At least not for the default. -sanders */
449  result += KMime::DateFormatter::formatDate( KMime::DateFormatter::Localized,
450  date(), sReplyLanguage, false );
451  break;
452  case 'e':
453  result += from();
454  break;
455  case 'F':
456  result += fromStrip();
457  break;
458  case 'f':
459  {
460  str = fromStrip();
461 
462  for (j=0; str[j]>' '; j++)
463  ;
464  unsigned int strLength(str.length());
465  for (; j < strLength && str[j] <= ' '; j++)
466  ;
467  result += str[0];
468  if (str[j]>' ')
469  result += str[j];
470  else
471  if (str[1]>' ')
472  result += str[1];
473  }
474  break;
475  case 'T':
476  result += toStrip();
477  break;
478  case 't':
479  result += to();
480  break;
481  case 'C':
482  result += ccStrip();
483  break;
484  case 'c':
485  result += cc();
486  break;
487  case 'S':
488  result += subject();
489  break;
490  case '_':
491  result += ' ';
492  break;
493  case 'L':
494  result += "\n";
495  break;
496  case '%':
497  result += '%';
498  break;
499  default:
500  result += '%';
501  result += ch;
502  break;
503  }
504  } else
505  result += ch;
506  }
507  return result;
508 }
509 
510 static void removeTrailingSpace( TQString &line )
511 {
512  int i = line.length()-1;
513  while( (i >= 0) && ((line[i] == ' ') || (line[i] == '\t')))
514  i--;
515  line.truncate( i+1);
516 }
517 
518 static TQString splitLine( TQString &line)
519 {
520  removeTrailingSpace( line );
521  int i = 0;
522  int j = -1;
523  int l = line.length();
524 
525  // TODO: Replace tabs with spaces first.
526 
527  while(i < l)
528  {
529  TQChar c = line[i];
530  if ((c == '>') || (c == ':') || (c == '|'))
531  j = i+1;
532  else if ((c != ' ') && (c != '\t'))
533  break;
534  i++;
535  }
536 
537  if ( j <= 0 )
538  {
539  return "";
540  }
541  if ( i == l )
542  {
543  TQString result = line.left(j);
544  line = TQString();
545  return result;
546  }
547 
548  TQString result = line.left(j);
549  line = line.mid(j);
550  return result;
551 }
552 
553 static TQString flowText(TQString &text, const TQString& indent, int maxLength)
554 {
555  maxLength--;
556  if (text.isEmpty())
557  {
558  return indent+"<NULL>\n";
559  }
560  TQString result;
561  while (1)
562  {
563  int i;
564  if ((int) text.length() > maxLength)
565  {
566  i = maxLength;
567  while( (i >= 0) && (text[i] != ' '))
568  i--;
569  if (i <= 0)
570  {
571  // Couldn't break before maxLength.
572  i = maxLength;
573 // while( (i < (int) text.length()) && (text[i] != ' '))
574 // i++;
575  }
576  }
577  else
578  {
579  i = text.length();
580  }
581 
582  TQString line = text.left(i);
583  if (i < (int) text.length())
584  text = text.mid(i);
585  else
586  text = TQString();
587 
588  result += indent + line + '\n';
589 
590  if (text.isEmpty())
591  return result;
592  }
593 }
594 
595 static bool flushPart(TQString &msg, TQStringList &part,
596  const TQString &indent, int maxLength)
597 {
598  maxLength -= indent.length();
599  if (maxLength < 20) maxLength = 20;
600 
601  // Remove empty lines at end of quote
602  while ((part.begin() != part.end()) && part.last().isEmpty())
603  {
604  part.remove(part.fromLast());
605  }
606 
607  TQString text;
608  for(TQStringList::Iterator it2 = part.begin();
609  it2 != part.end();
610  it2++)
611  {
612  TQString line = (*it2);
613 
614  if (line.isEmpty())
615  {
616  if (!text.isEmpty())
617  msg += flowText(text, indent, maxLength);
618  msg += indent + '\n';
619  }
620  else
621  {
622  if (text.isEmpty())
623  text = line;
624  else
625  text += ' '+line.stripWhiteSpace();
626 
627  if (((int) text.length() < maxLength) || ((int) line.length() < (maxLength-10)))
628  msg += flowText(text, indent, maxLength);
629  }
630  }
631  if (!text.isEmpty())
632  msg += flowText(text, indent, maxLength);
633 
634  bool appendEmptyLine = true;
635  if (!part.count())
636  appendEmptyLine = false;
637 
638  part.clear();
639  return appendEmptyLine;
640 }
641 
642 static TQString stripSignature( const TQString & msg, bool clearSigned ) {
643  if ( clearSigned )
644  return msg.left( msg.findRev( TQRegExp( "\n--\\s?\n" ) ) );
645  else
646  return msg.left( msg.findRev( "\n-- \n" ) );
647 }
648 
649 TQString KMMessage::smartQuote( const TQString & msg, int maxLineLength )
650 {
651  TQStringList part;
652  TQString oldIndent;
653  bool firstPart = true;
654 
655 
656  const TQStringList lines = TQStringList::split('\n', msg, true);
657 
658  TQString result;
659  for(TQStringList::const_iterator it = lines.begin();
660  it != lines.end();
661  ++it)
662  {
663  TQString line = *it;
664 
665  const TQString indent = splitLine( line );
666 
667  if ( line.isEmpty())
668  {
669  if (!firstPart)
670  part.append(TQString());
671  continue;
672  };
673 
674  if (firstPart)
675  {
676  oldIndent = indent;
677  firstPart = false;
678  }
679 
680  if (oldIndent != indent)
681  {
682  TQString fromLine;
683  // Search if the last non-blank line could be "From" line
684  if (part.count() && (oldIndent.length() < indent.length()))
685  {
686  TQStringList::Iterator it2 = part.fromLast();
687  while( (it2 != part.end()) && (*it2).isEmpty())
688  --it2;
689 
690  if ((it2 != part.end()) && ((*it2).endsWith(":")))
691  {
692  fromLine = oldIndent + (*it2) + '\n';
693  part.remove(it2);
694  }
695  }
696  if (flushPart( result, part, oldIndent, maxLineLength))
697  {
698  if (oldIndent.length() > indent.length())
699  result += indent + '\n';
700  else
701  result += oldIndent + '\n';
702  }
703  if (!fromLine.isEmpty())
704  {
705  result += fromLine;
706  }
707  oldIndent = indent;
708  }
709  part.append(line);
710  }
711  flushPart( result, part, oldIndent, maxLineLength);
712  return result;
713 }
714 
715 
716 //-----------------------------------------------------------------------------
718  TQCString& parsedString,
719  const TQTextCodec*& codec,
720  bool& isHTML ) const
721 {
722  if ( !root ) return;
723 
724  isHTML = false;
725  partNode * curNode = root->findType( DwMime::kTypeText,
726  DwMime::kSubtypeUnknown,
727  true,
728  false );
729  kdDebug(5006) << "\n\n======= KMMessage::parseTextStringFromDwPart() - "
730  << ( curNode ? "text part found!\n" : "sorry, no text node!\n" ) << endl;
731  if( curNode ) {
732  isHTML = DwMime::kSubtypeHtml == curNode->subType();
733  // now parse the TEXT message part we want to quote
734  ObjectTreeParser otp( 0, 0, true, false, true );
735  otp.parseObjectTree( curNode );
736  parsedString = otp.rawReplyString();
737  codec = curNode->msgPart().codec();
738  }
739 }
740 
741 //-----------------------------------------------------------------------------
742 
743 TQString KMMessage::asPlainTextFromObjectTree( partNode *root, bool aStripSignature,
744  bool allowDecryption ) const
745 {
746  Q_ASSERT( root );
747  Q_ASSERT( root->processed() );
748 
749  TQCString parsedString;
750  bool isHTML = false;
751  const TQTextCodec * codec = 0;
752 
753  if ( !root ) return TQString();
754  parseTextStringFromDwPart( root, parsedString, codec, isHTML );
755 
756  if ( mOverrideCodec || !codec )
757  codec = this->codec();
758 
759  if ( parsedString.isEmpty() )
760  return TQString();
761 
762  bool clearSigned = false;
763  TQString result;
764 
765  // decrypt
766  if ( allowDecryption ) {
767  TQPtrList<Kpgp::Block> pgpBlocks;
768  TQStrList nonPgpBlocks;
769  if ( Kpgp::Module::prepareMessageForDecryption( parsedString,
770  pgpBlocks,
771  nonPgpBlocks ) ) {
772  // Only decrypt/strip off the signature if there is only one OpenPGP
773  // block in the message
774  if ( pgpBlocks.count() == 1 ) {
775  Kpgp::Block * block = pgpBlocks.first();
776  if ( block->type() == Kpgp::PgpMessageBlock ||
777  block->type() == Kpgp::ClearsignedBlock ) {
778  if ( block->type() == Kpgp::PgpMessageBlock ) {
779  // try to decrypt this OpenPGP block
780  block->decrypt();
781  } else {
782  // strip off the signature
783  block->verify();
784  clearSigned = true;
785  }
786 
787  result = codec->toUnicode( nonPgpBlocks.first() )
788  + codec->toUnicode( block->text() )
789  + codec->toUnicode( nonPgpBlocks.last() );
790  }
791  }
792  }
793  }
794 
795  if ( result.isEmpty() ) {
796  result = codec->toUnicode( parsedString );
797  if ( result.isEmpty() )
798  return result;
799  }
800 
801  // html -> plaintext conversion, if necessary:
802  if ( isHTML && mDecodeHTML ) {
803  TDEHTMLPart htmlPart;
804  htmlPart.setOnlyLocalReferences( true );
805  htmlPart.setMetaRefreshEnabled( false );
806  htmlPart.setPluginsEnabled( false );
807  htmlPart.setJScriptEnabled( false );
808  htmlPart.setJavaEnabled( false );
809  htmlPart.begin();
810  htmlPart.write( result );
811  htmlPart.end();
812  htmlPart.selectAll();
813  result = htmlPart.selectedText();
814  }
815 
816  // strip the signature (footer):
817  if ( aStripSignature )
818  return stripSignature( result, clearSigned );
819  else
820  return result;
821 }
822 
823 //-----------------------------------------------------------------------------
824 
825 TQString KMMessage::asPlainText( bool aStripSignature, bool allowDecryption ) const
826 {
827  partNode *root = partNode::fromMessage( this );
828  if ( !root )
829  return TQString();
830 
831  ObjectTreeParser otp;
832  otp.parseObjectTree( root );
833  TQString result = asPlainTextFromObjectTree( root, aStripSignature, allowDecryption );
834  delete root;
835  return result;
836 }
837 
838 TQString KMMessage::asQuotedString( const TQString& aHeaderStr,
839  const TQString& aIndentStr,
840  const TQString& selection /* = TQString() */,
841  bool aStripSignature /* = true */,
842  bool allowDecryption /* = true */) const
843 {
844  TQString content = selection.isEmpty() ?
845  asPlainText( aStripSignature, allowDecryption ) : selection ;
846 
847  // Remove blank lines at the beginning:
848  const int firstNonWS = content.find( TQRegExp( "\\S" ) );
849  const int lineStart = content.findRev( '\n', firstNonWS );
850  if ( lineStart >= 0 )
851  content.remove( 0, static_cast<unsigned int>( lineStart ) );
852 
853  const TQString indentStr = formatString( aIndentStr );
854 
855  content.replace( '\n', '\n' + indentStr );
856  content.prepend( indentStr );
857  content += '\n';
858 
859  const TQString headerStr = formatString( aHeaderStr );
860  if ( sSmartQuote && sWordWrap )
861  return headerStr + smartQuote( content, sWrapCol );
862  return headerStr + content;
863 }
864 
865 //-----------------------------------------------------------------------------
866 KMMessage* KMMessage::createReply( KMail::ReplyStrategy replyStrategy,
867  TQString selection /* = TQString() */,
868  bool noQuote /* = false */,
869  bool allowDecryption /* = true */,
870  const TQString &tmpl /* = TQString() */,
871  const TQString &originatingAccount /* = TQString() */ )
872 {
873  KMMessage* msg = new KMMessage;
874  TQString mailingListStr, replyToStr, toStr;
875  TQStringList mailingListAddresses;
876  TQCString refStr, headerName;
877  bool replyAll = true;
878 
879  msg->initFromMessage(this);
880 
881  MailingList::name(this, headerName, mailingListStr);
882  replyToStr = replyTo();
883 
884  msg->setCharset("utf-8");
885 
886  // determine the mailing list posting address
887  if ( parent() && parent()->isMailingListEnabled() &&
888  !parent()->mailingListPostAddress().isEmpty() ) {
889  mailingListAddresses << parent()->mailingListPostAddress();
890  }
891  if ( headerField("List-Post").find( "mailto:", 0, false ) != -1 ) {
892  TQString listPost = headerField("List-Post");
893  TQRegExp rx( "<mailto:([^@>]+)@([^>]+)>", false );
894  if ( rx.search( listPost, 0 ) != -1 ) // matched
895  mailingListAddresses << rx.cap(1) + '@' + rx.cap(2);
896  }
897 
898  // use the "On ... Joe User wrote:" header by default
899  switch( replyStrategy ) {
900  case KMail::ReplySmart : {
901  if ( !headerField( "Mail-Followup-To" ).isEmpty() ) {
902  toStr = headerField( "Mail-Followup-To" );
903  }
904  else if ( !replyToStr.isEmpty() ) {
905  // assume a Reply-To header mangling mailing list
906  toStr = replyToStr;
907  }
908  else if ( !mailingListAddresses.isEmpty() ) {
909  toStr = mailingListAddresses[0];
910  }
911  else {
912  // doesn't seem to be a mailing list, reply to From: address
913  toStr = from();
914  //replyStr = sReplyStr; // reply to author, so use "On ... you wrote:"
915  replyAll = false;
916  }
917  // strip all my addresses from the list of recipients
918  TQStringList recipients = KPIM::splitEmailAddrList( toStr );
919  toStr = stripMyAddressesFromAddressList( recipients ).join(", ");
920  // ... unless the list contains only my addresses (reply to self)
921  if ( toStr.isEmpty() && !recipients.isEmpty() )
922  toStr = recipients[0];
923 
924  break;
925  }
926  case KMail::ReplyList : {
927  if ( !headerField( "Mail-Followup-To" ).isEmpty() ) {
928  toStr = headerField( "Mail-Followup-To" );
929  }
930  else if ( !mailingListAddresses.isEmpty() ) {
931  toStr = mailingListAddresses[0];
932  }
933  else if ( !replyToStr.isEmpty() ) {
934  // assume a Reply-To header mangling mailing list
935  toStr = replyToStr;
936  }
937  // strip all my addresses from the list of recipients
938  TQStringList recipients = KPIM::splitEmailAddrList( toStr );
939  toStr = stripMyAddressesFromAddressList( recipients ).join(", ");
940 
941  break;
942  }
943  case KMail::ReplyAll : {
944  TQStringList recipients;
945  TQStringList ccRecipients;
946 
947  // add addresses from the Reply-To header to the list of recipients
948  if( !replyToStr.isEmpty() ) {
949  recipients += KPIM::splitEmailAddrList( replyToStr );
950  // strip all possible mailing list addresses from the list of Reply-To
951  // addresses
952  for ( TQStringList::const_iterator it = mailingListAddresses.begin();
953  it != mailingListAddresses.end();
954  ++it ) {
955  recipients = stripAddressFromAddressList( *it, recipients );
956  }
957  }
958 
959  if ( !mailingListAddresses.isEmpty() ) {
960  // this is a mailing list message
961  if ( recipients.isEmpty() && !from().isEmpty() ) {
962  // The sender didn't set a Reply-to address, so we add the From
963  // address to the list of CC recipients.
964  ccRecipients += from();
965  kdDebug(5006) << "Added " << from() << " to the list of CC recipients"
966  << endl;
967  }
968  // if it is a mailing list, add the posting address
969  recipients.prepend( mailingListAddresses[0] );
970  }
971  else {
972  // this is a normal message
973  if ( recipients.isEmpty() && !from().isEmpty() ) {
974  // in case of replying to a normal message only then add the From
975  // address to the list of recipients if there was no Reply-to address
976  recipients += from();
977  kdDebug(5006) << "Added " << from() << " to the list of recipients"
978  << endl;
979  }
980  }
981 
982  // strip all my addresses from the list of recipients
983  toStr = stripMyAddressesFromAddressList( recipients ).join(", ");
984 
985  // merge To header and CC header into a list of CC recipients
986  if( !cc().isEmpty() || !to().isEmpty() ) {
987  TQStringList list;
988  if (!to().isEmpty())
989  list += KPIM::splitEmailAddrList(to());
990  if (!cc().isEmpty())
991  list += KPIM::splitEmailAddrList(cc());
992  for( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
993  if( !addressIsInAddressList( *it, recipients )
994  && !addressIsInAddressList( *it, ccRecipients ) ) {
995  ccRecipients += *it;
996  kdDebug(5006) << "Added " << *it << " to the list of CC recipients"
997  << endl;
998  }
999  }
1000  }
1001 
1002  if ( !ccRecipients.isEmpty() ) {
1003  // strip all my addresses from the list of CC recipients
1004  ccRecipients = stripMyAddressesFromAddressList( ccRecipients );
1005 
1006  // in case of a reply to self toStr might be empty. if that's the case
1007  // then propagate a cc recipient to To: (if there is any).
1008  if ( toStr.isEmpty() && !ccRecipients.isEmpty() ) {
1009  toStr = ccRecipients[0];
1010  ccRecipients.pop_front();
1011  }
1012 
1013  msg->setCc( ccRecipients.join(", ") );
1014  }
1015 
1016  if ( toStr.isEmpty() && !recipients.isEmpty() ) {
1017  // reply to self without other recipients
1018  toStr = recipients[0];
1019  }
1020  break;
1021  }
1022  case KMail::ReplyAuthor : {
1023  if ( !replyToStr.isEmpty() ) {
1024  TQStringList recipients = KPIM::splitEmailAddrList( replyToStr );
1025  // strip the mailing list post address from the list of Reply-To
1026  // addresses since we want to reply in private
1027  for ( TQStringList::const_iterator it = mailingListAddresses.begin();
1028  it != mailingListAddresses.end();
1029  ++it ) {
1030  recipients = stripAddressFromAddressList( *it, recipients );
1031  }
1032  if ( !recipients.isEmpty() ) {
1033  toStr = recipients.join(", ");
1034  }
1035  else {
1036  // there was only the mailing list post address in the Reply-To header,
1037  // so use the From address instead
1038  toStr = from();
1039  }
1040  }
1041  else if ( !from().isEmpty() ) {
1042  toStr = from();
1043  }
1044  replyAll = false;
1045  break;
1046  }
1047  case KMail::ReplyNone : {
1048  // the addressees will be set by the caller
1049  }
1050  }
1051 
1052  if (!originatingAccount.isEmpty()) {
1053  msg->setOriginatingAccountName(originatingAccount);
1054  }
1055 
1056  msg->setTo(toStr);
1057 
1058  refStr = getRefStr();
1059  if (!refStr.isEmpty())
1060  msg->setReferences(refStr);
1061  //In-Reply-To = original msg-id
1062  msg->setReplyToId(msgId());
1063 
1064 // if (!noQuote) {
1065 // if( selectionIsBody ){
1066 // TQCString cStr = selection.latin1();
1067 // msg->setBody( cStr );
1068 // }else{
1069 // msg->setBody(asQuotedString(replyStr + "\n", sIndentPrefixStr, selection,
1070 // sSmartQuote, allowDecryption).utf8());
1071 // }
1072 // }
1073 
1074  msg->setSubject( replySubject() );
1075  msg->setHeaderField( "X-KMail-QuotePrefix",
1076  formatString( GlobalSettings::self()->quoteString() ) );
1077  if( !noQuote ) {
1078  TemplateParser parser( msg, ( replyAll ? TemplateParser::ReplyAll : TemplateParser::Reply ) );
1079  parser.setAllowDecryption( allowDecryption );
1080  if ( GlobalSettings::quoteSelectionOnly() ) {
1081  parser.setSelection( selection );
1082  }
1083  if ( !tmpl.isEmpty() ) {
1084  parser.process( tmpl, this );
1085  } else {
1086  parser.process( this );
1087  }
1088  }
1089  // setStatus(KMMsgStatusReplied);
1090  msg->link(this, KMMsgStatusReplied);
1091 
1092  if ( parent() && parent()->putRepliesInSameFolder() )
1093  msg->setFcc( parent()->idString() );
1094 
1095  // replies to an encrypted message should be encrypted as well
1096  if ( encryptionState() == KMMsgPartiallyEncrypted ||
1097  encryptionState() == KMMsgFullyEncrypted ) {
1098  msg->setEncryptionState( KMMsgFullyEncrypted );
1099  }
1100 
1101  return msg;
1102 }
1103 
1104 
1105 //-----------------------------------------------------------------------------
1106 TQCString KMMessage::getRefStr() const
1107 {
1108  TQCString firstRef, lastRef, refStr, retRefStr;
1109  int i, j;
1110 
1111  refStr = headerField("References").stripWhiteSpace().latin1();
1112 
1113  if (refStr.isEmpty())
1114  return headerField("Message-Id").latin1();
1115 
1116  i = refStr.find('<');
1117  j = refStr.find('>');
1118  firstRef = refStr.mid(i, j-i+1);
1119  if (!firstRef.isEmpty())
1120  retRefStr = firstRef + ' ';
1121 
1122  i = refStr.findRev('<');
1123  j = refStr.findRev('>');
1124 
1125  lastRef = refStr.mid(i, j-i+1);
1126  if (!lastRef.isEmpty() && lastRef != firstRef)
1127  retRefStr += lastRef + ' ';
1128 
1129  retRefStr += headerField("Message-Id").latin1();
1130  return retRefStr;
1131 }
1132 
1133 
1134 KMMessage* KMMessage::createRedirect( const TQString &toStr )
1135 {
1136  // copy the message 1:1
1137  KMMessage* msg = new KMMessage( new DwMessage( *this->mMsg ) );
1138  KMMessagePart msgPart;
1139 
1140  uint id = 0;
1141  TQString strId = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace();
1142  if ( !strId.isEmpty())
1143  id = strId.toUInt();
1144  const KPIM::Identity & ident =
1145  kmkernel->identityManager()->identityForUoidOrDefault( id );
1146 
1147  // X-KMail-Redirect-From: content
1148  TQString strByWayOf = TQString("%1 (by way of %2 <%3>)")
1149  .arg( from() )
1150  .arg( ident.fullName() )
1151  .arg( ident.primaryEmailAddress() );
1152 
1153  // Resent-From: content
1154  TQString strFrom = TQString("%1 <%2>")
1155  .arg( ident.fullName() )
1156  .arg( ident.primaryEmailAddress() );
1157 
1158  // format the current date to be used in Resent-Date:
1159  TQString origDate = msg->headerField( "Date" );
1160  msg->setDateToday();
1161  TQString newDate = msg->headerField( "Date" );
1162  // make sure the Date: header is valid
1163  if ( origDate.isEmpty() )
1164  msg->removeHeaderField( "Date" );
1165  else
1166  msg->setHeaderField( "Date", origDate );
1167 
1168  // prepend Resent-*: headers (c.f. RFC2822 3.6.6)
1169  msg->setHeaderField( "Resent-Message-ID", generateMessageId( msg->sender() ),
1170  Structured, true);
1171  msg->setHeaderField( "Resent-Date", newDate, Structured, true );
1172  msg->setHeaderField( "Resent-To", toStr, Address, true );
1173  msg->setHeaderField( "Resent-From", strFrom, Address, true );
1174 
1175  msg->setHeaderField( "X-KMail-Redirect-From", strByWayOf );
1176  msg->setHeaderField( "X-KMail-Recipients", toStr, Address );
1177 
1178  msg->link(this, KMMsgStatusForwarded);
1179 
1180  return msg;
1181 }
1182 
1183 
1184 //-----------------------------------------------------------------------------
1186 {
1187  TQString s;
1188  TQCString str;
1189 
1190  if (sHeaderStrategy == HeaderStrategy::all()) {
1191  s = "\n\n---------- " + sForwardStr + " ----------\n\n";
1192  s += headerAsString();
1193  str = asQuotedString(s, "", TQString(), false, false).utf8();
1194  str += "\n-------------------------------------------------------\n";
1195  } else {
1196  s = "\n\n---------- " + sForwardStr + " ----------\n\n";
1197  s += "Subject: " + subject() + "\n";
1198  s += "Date: "
1199  + KMime::DateFormatter::formatDate( KMime::DateFormatter::Localized,
1200  date(), sReplyLanguage, false )
1201  + "\n";
1202  s += "From: " + from() + "\n";
1203  s += "To: " + to() + "\n";
1204  if (!cc().isEmpty()) s += "Cc: " + cc() + "\n";
1205  s += "\n";
1206  str = asQuotedString(s, "", TQString(), false, false).utf8();
1207  str += "\n-------------------------------------------------------\n";
1208  }
1209 
1210  return str;
1211 }
1212 
1213 void KMMessage::sanitizeHeaders( const TQStringList& whiteList )
1214 {
1215  // Strip out all headers apart from the content description and other
1216  // whitelisted ones, because we don't want to inherit them.
1217  DwHeaders& header = mMsg->Headers();
1218  DwField* field = header.FirstField();
1219  DwField* nextField;
1220  while (field)
1221  {
1222  nextField = field->Next();
1223  if ( field->FieldNameStr().find( "ontent" ) == DwString::npos
1224  && !whiteList.contains( TQString::fromLatin1( field->FieldNameStr().c_str() ) ) )
1225  header.RemoveField(field);
1226  field = nextField;
1227  }
1228  mMsg->Assemble();
1229 }
1230 
1231 //-----------------------------------------------------------------------------
1232 KMMessage* KMMessage::createForward( const TQString &tmpl /* = TQString() */ )
1233 {
1234  KMMessage* msg = new KMMessage();
1235 
1236  // If this is a multipart mail or if the main part is only the text part,
1237  // Make an identical copy of the mail, minus headers, so attachments are
1238  // preserved
1239  if ( type() == DwMime::kTypeMultipart ||
1240  ( type() == DwMime::kTypeText && subtype() == DwMime::kSubtypePlain ) ) {
1241  // ## slow, we could probably use: delete msg->mMsg; msg->mMsg = new DwMessage( this->mMsg );
1242  msg->fromDwString( this->asDwString() );
1243  // remember the type and subtype, initFromMessage sets the contents type to
1244  // text/plain, via initHeader, for unclear reasons
1245  DwMediaType oldContentType = msg->mMsg->Headers().ContentType();
1246 
1247  msg->sanitizeHeaders();
1248 
1249  // strip blacklisted parts
1250  TQStringList blacklist = GlobalSettings::self()->mimetypesToStripWhenInlineForwarding();
1251  for ( TQStringList::Iterator it = blacklist.begin(); it != blacklist.end(); ++it ) {
1252  TQString entry = (*it);
1253  int sep = entry.find( '/' );
1254  TQCString type = entry.left( sep ).latin1();
1255  TQCString subtype = entry.mid( sep+1 ).latin1();
1256  kdDebug( 5006 ) << "Looking for blacklisted type: " << type << "/" << subtype << endl;
1257  while ( DwBodyPart * part = msg->findDwBodyPart( type, subtype ) ) {
1258  msg->mMsg->Body().RemoveBodyPart( part );
1259  }
1260  }
1261  msg->mMsg->Assemble();
1262  msg->initFromMessage( this );
1263 
1264  //restore type
1265  msg->mMsg->Headers().ContentType().FromString( oldContentType.AsString() );
1266  msg->mMsg->Headers().ContentType().Parse();
1267  msg->mMsg->Assemble();
1268  }
1269  else if( type() == DwMime::kTypeText && subtype() == DwMime::kSubtypeHtml ) {
1270  // This is non-multipart html mail. Let`s make it text/plain and allow
1271  // template parser do the hard job.
1272  msg->initFromMessage( this );
1273  msg->setType( DwMime::kTypeText );
1274  msg->setSubtype( DwMime::kSubtypeHtml );
1275  msg->mNeedsAssembly = true;
1276  msg->cleanupHeader();
1277  }
1278  else {
1279  // This is a non-multipart, non-text mail (e.g. text/calendar). Construct
1280  // a multipart/mixed mail and add the original body as an attachment.
1281  msg->initFromMessage( this );
1282  msg->removeHeaderField("Content-Type");
1283  msg->removeHeaderField("Content-Transfer-Encoding");
1284  // Modify the ContentType directly (replaces setAutomaticFields(true))
1285  DwHeaders & header = msg->mMsg->Headers();
1286  header.MimeVersion().FromString("1.0");
1287  DwMediaType & contentType = msg->dwContentType();
1288  contentType.SetType( DwMime::kTypeMultipart );
1289  contentType.SetSubtype( DwMime::kSubtypeMixed );
1290  contentType.CreateBoundary(0);
1291  contentType.Assemble();
1292 
1293  // empty text part
1294  KMMessagePart msgPart;
1295  bodyPart( 0, &msgPart );
1296  msg->addBodyPart(&msgPart);
1297  // the old contents of the mail
1298  KMMessagePart secondPart;
1299  secondPart.setType( type() );
1300  secondPart.setSubtype( subtype() );
1301  secondPart.setBody( mMsg->Body().AsString() );
1302  // use the headers of the original mail
1303  applyHeadersToMessagePart( mMsg->Headers(), &secondPart );
1304  msg->addBodyPart(&secondPart);
1305  msg->mNeedsAssembly = true;
1306  msg->cleanupHeader();
1307  }
1308  // TQString st = TQString::fromUtf8(createForwardBody());
1309 
1310  msg->setSubject( forwardSubject() );
1311 
1312  TemplateParser parser( msg, TemplateParser::Forward );
1313  if ( !tmpl.isEmpty() ) {
1314  parser.process( tmpl, this );
1315  } else {
1316  parser.process( this );
1317  }
1318 
1319  // TQCString encoding = autoDetectCharset(charset(), sPrefCharsets, msg->body());
1320  // if (encoding.isEmpty()) encoding = "utf-8";
1321  // msg->setCharset(encoding);
1322 
1323  // force utf-8
1324  // msg->setCharset( "utf-8" );
1325 
1326  msg->link(this, KMMsgStatusForwarded);
1327  return msg;
1328 }
1329 
1330 static const struct {
1331  const char * dontAskAgainID;
1332  bool canDeny;
1333  const char * text;
1334 } mdnMessageBoxes[] = {
1335  { "mdnNormalAsk", true,
1336  I18N_NOOP("This message contains a request to return a notification "
1337  "about your reception of the message.\n"
1338  "You can either ignore the request or let KMail send a "
1339  "\"denied\" or normal response.") },
1340  { "mdnUnknownOption", false,
1341  I18N_NOOP("This message contains a request to send a notification "
1342  "about your reception of the message.\n"
1343  "It contains a processing instruction that is marked as "
1344  "\"required\", but which is unknown to KMail.\n"
1345  "You can either ignore the request or let KMail send a "
1346  "\"failed\" response.") },
1347  { "mdnMultipleAddressesInReceiptTo", true,
1348  I18N_NOOP("This message contains a request to send a notification "
1349  "about your reception of the message,\n"
1350  "but it is requested to send the notification to more "
1351  "than one address.\n"
1352  "You can either ignore the request or let KMail send a "
1353  "\"denied\" or normal response.") },
1354  { "mdnReturnPathEmpty", true,
1355  I18N_NOOP("This message contains a request to send a notification "
1356  "about your reception of the message,\n"
1357  "but there is no return-path set.\n"
1358  "You can either ignore the request or let KMail send a "
1359  "\"denied\" or normal response.") },
1360  { "mdnReturnPathNotInReceiptTo", true,
1361  I18N_NOOP("This message contains a request to send a notification "
1362  "about your reception of the message,\n"
1363  "but the return-path address differs from the address "
1364  "the notification was requested to be sent to.\n"
1365  "You can either ignore the request or let KMail send a "
1366  "\"denied\" or normal response.") },
1367 };
1368 
1369 static const int numMdnMessageBoxes
1370  = sizeof mdnMessageBoxes / sizeof *mdnMessageBoxes;
1371 
1372 
1373 static int requestAdviceOnMDN( const char * what ) {
1374  for ( int i = 0 ; i < numMdnMessageBoxes ; ++i ) {
1375  if ( !qstrcmp( what, mdnMessageBoxes[i].dontAskAgainID ) ) {
1376  if ( mdnMessageBoxes[i].canDeny ) {
1377  const KCursorSaver saver( TQCursor::ArrowCursor );
1378  int answer = TQMessageBox::information( 0,
1379  i18n("Message Disposition Notification Request"),
1380  i18n( mdnMessageBoxes[i].text ),
1381  i18n("&Ignore"), i18n("Send \"&denied\""), i18n("&Send") );
1382  return answer ? answer + 1 : 0 ; // map to "mode" in createMDN
1383  } else {
1384  const KCursorSaver saver( TQCursor::ArrowCursor );
1385  int answer = TQMessageBox::information( 0,
1386  i18n("Message Disposition Notification Request"),
1387  i18n( mdnMessageBoxes[i].text ),
1388  i18n("&Ignore"), i18n("&Send") );
1389  return answer ? answer + 2 : 0 ; // map to "mode" in createMDN
1390  }
1391  }
1392  }
1393  kdWarning(5006) << "didn't find data for message box \""
1394  << what << "\"" << endl;
1395  return 0;
1396 }
1397 
1398 KMMessage* KMMessage::createMDN( MDN::ActionMode a,
1399  MDN::DispositionType d,
1400  bool allowGUI,
1401  TQValueList<MDN::DispositionModifier> m )
1402 {
1403  // RFC 2298: At most one MDN may be issued on behalf of each
1404  // particular recipient by their user agent. That is, once an MDN
1405  // has been issued on behalf of a recipient, no further MDNs may be
1406  // issued on behalf of that recipient, even if another disposition
1407  // is performed on the message.
1408 //#define MDN_DEBUG 1
1409 #ifndef MDN_DEBUG
1410  if ( mdnSentState() != KMMsgMDNStateUnknown &&
1411  mdnSentState() != KMMsgMDNNone )
1412  return 0;
1413 #else
1414  char st[2]; st[0] = (char)mdnSentState(); st[1] = 0;
1415  kdDebug(5006) << "mdnSentState() == '" << st << "'" << endl;
1416 #endif
1417 
1418  // RFC 2298: An MDN MUST NOT be generated in response to an MDN.
1419  if ( findDwBodyPart( DwMime::kTypeMessage,
1420  DwMime::kSubtypeDispositionNotification ) ) {
1421  setMDNSentState( KMMsgMDNIgnore );
1422  return 0;
1423  }
1424 
1425  // extract where to send to:
1426  TQString receiptTo = headerField("Disposition-Notification-To");
1427  if ( receiptTo.stripWhiteSpace().isEmpty() ) return 0;
1428  receiptTo.remove( '\n' );
1429 
1430 
1431  MDN::SendingMode s = MDN::SentAutomatically; // set to manual if asked user
1432  TQString special; // fill in case of error, warning or failure
1433  TDEConfigGroup mdnConfig( KMKernel::config(), "MDN" );
1434 
1435  // default:
1436  int mode = mdnConfig.readNumEntry( "default-policy", 0 );
1437  if ( !mode || mode < 0 || mode > 3 ) {
1438  // early out for ignore:
1439  setMDNSentState( KMMsgMDNIgnore );
1440  return 0;
1441  }
1442 
1443  // RFC 2298: An importance of "required" indicates that
1444  // interpretation of the parameter is necessary for proper
1445  // generation of an MDN in response to this request. If a UA does
1446  // not understand the meaning of the parameter, it MUST NOT generate
1447  // an MDN with any disposition type other than "failed" in response
1448  // to the request.
1449  TQString notificationOptions = headerField("Disposition-Notification-Options");
1450  if ( notificationOptions.contains( "required", false ) ) {
1451  // ### hacky; should parse...
1452  // There is a required option that we don't understand. We need to
1453  // ask the user what we should do:
1454  if ( !allowGUI ) return 0; // don't setMDNSentState here!
1455  mode = requestAdviceOnMDN( "mdnUnknownOption" );
1456  s = MDN::SentManually;
1457 
1458  special = i18n("Header \"Disposition-Notification-Options\" contained "
1459  "required, but unknown parameter");
1460  d = MDN::Failed;
1461  m.clear(); // clear modifiers
1462  }
1463 
1464  // RFC 2298: [ Confirmation from the user SHOULD be obtained (or no
1465  // MDN sent) ] if there is more than one distinct address in the
1466  // Disposition-Notification-To header.
1467  kdDebug(5006) << "KPIM::splitEmailAddrList(receiptTo): "
1468  << KPIM::splitEmailAddrList(receiptTo).join("\n") << endl;
1469  if ( KPIM::splitEmailAddrList(receiptTo).count() > 1 ) {
1470  if ( !allowGUI ) return 0; // don't setMDNSentState here!
1471  mode = requestAdviceOnMDN( "mdnMultipleAddressesInReceiptTo" );
1472  s = MDN::SentManually;
1473  }
1474 
1475  // RFC 2298: MDNs SHOULD NOT be sent automatically if the address in
1476  // the Disposition-Notification-To header differs from the address
1477  // in the Return-Path header. [...] Confirmation from the user
1478  // SHOULD be obtained (or no MDN sent) if there is no Return-Path
1479  // header in the message [...]
1480  AddrSpecList returnPathList = extractAddrSpecs("Return-Path");
1481  TQString returnPath = returnPathList.isEmpty() ? TQString()
1482  : returnPathList.front().localPart + '@' + returnPathList.front().domain ;
1483  kdDebug(5006) << "clean return path: " << returnPath << endl;
1484  if ( returnPath.isEmpty() || !receiptTo.contains( returnPath, false ) ) {
1485  if ( !allowGUI ) return 0; // don't setMDNSentState here!
1486  mode = requestAdviceOnMDN( returnPath.isEmpty() ?
1487  "mdnReturnPathEmpty" :
1488  "mdnReturnPathNotInReceiptTo" );
1489  s = MDN::SentManually;
1490  }
1491 
1492  if ( a != KMime::MDN::AutomaticAction ) {
1493  //TODO: only ingore user settings for AutomaticAction if requested
1494  if ( mode == 1 ) { // ask
1495  if ( !allowGUI ) return 0; // don't setMDNSentState here!
1496  mode = requestAdviceOnMDN( "mdnNormalAsk" );
1497  s = MDN::SentManually; // asked user
1498  }
1499 
1500  switch ( mode ) {
1501  case 0: // ignore:
1502  setMDNSentState( KMMsgMDNIgnore );
1503  return 0;
1504  default:
1505  case 1:
1506  kdFatal(5006) << "KMMessage::createMDN(): The \"ask\" mode should "
1507  << "never appear here!" << endl;
1508  break;
1509  case 2: // deny
1510  d = MDN::Denied;
1511  m.clear();
1512  break;
1513  case 3:
1514  break;
1515  }
1516  }
1517 
1518 
1519  // extract where to send from:
1520  TQString finalRecipient = kmkernel->identityManager()
1521  ->identityForUoidOrDefault( identityUoid() ).fullEmailAddr();
1522 
1523  //
1524  // Generate message:
1525  //
1526 
1527  KMMessage * receipt = new KMMessage();
1528  receipt->initFromMessage( this );
1529  receipt->removeHeaderField("Content-Type");
1530  receipt->removeHeaderField("Content-Transfer-Encoding");
1531  // Modify the ContentType directly (replaces setAutomaticFields(true))
1532  DwHeaders & header = receipt->mMsg->Headers();
1533  header.MimeVersion().FromString("1.0");
1534  DwMediaType & contentType = receipt->dwContentType();
1535  contentType.SetType( DwMime::kTypeMultipart );
1536  contentType.SetSubtype( DwMime::kSubtypeReport );
1537  contentType.CreateBoundary(0);
1538  receipt->mNeedsAssembly = true;
1539  receipt->setContentTypeParam( "report-type", "disposition-notification" );
1540 
1541  TQString description = replaceHeadersInString( MDN::descriptionFor( d, m ) );
1542 
1543  // text/plain part:
1544  KMMessagePart firstMsgPart;
1545  firstMsgPart.setTypeStr( "text" );
1546  firstMsgPart.setSubtypeStr( "plain" );
1547  firstMsgPart.setBodyFromUnicode( description );
1548  receipt->addBodyPart( &firstMsgPart );
1549 
1550  // message/disposition-notification part:
1551  KMMessagePart secondMsgPart;
1552  secondMsgPart.setType( DwMime::kTypeMessage );
1553  secondMsgPart.setSubtype( DwMime::kSubtypeDispositionNotification );
1554  //secondMsgPart.setCharset( "us-ascii" );
1555  //secondMsgPart.setCteStr( "7bit" );
1556  secondMsgPart.setBodyEncoded( MDN::dispositionNotificationBodyContent(
1557  finalRecipient,
1558  rawHeaderField("Original-Recipient"),
1559  id(), /* Message-ID */
1560  d, a, s, m, special ) );
1561  receipt->addBodyPart( &secondMsgPart );
1562 
1563  // message/rfc822 or text/rfc822-headers body part:
1564  int num = mdnConfig.readNumEntry( "quote-message", 0 );
1565  if ( num < 0 || num > 2 ) num = 0;
1566  MDN::ReturnContent returnContent = static_cast<MDN::ReturnContent>( num );
1567 
1568  KMMessagePart thirdMsgPart;
1569  switch ( returnContent ) {
1570  case MDN::All:
1571  thirdMsgPart.setTypeStr( "message" );
1572  thirdMsgPart.setSubtypeStr( "rfc822" );
1573  thirdMsgPart.setBody( asSendableString() );
1574  receipt->addBodyPart( &thirdMsgPart );
1575  break;
1576  case MDN::HeadersOnly:
1577  thirdMsgPart.setTypeStr( "text" );
1578  thirdMsgPart.setSubtypeStr( "rfc822-headers" );
1579  thirdMsgPart.setBody( headerAsSendableString() );
1580  receipt->addBodyPart( &thirdMsgPart );
1581  break;
1582  case MDN::Nothing:
1583  default:
1584  break;
1585  };
1586 
1587  receipt->setTo( receiptTo );
1588  receipt->setSubject( "Message Disposition Notification" );
1589  receipt->setReplyToId( msgId() );
1590  receipt->setReferences( getRefStr() );
1591 
1592  receipt->cleanupHeader();
1593 
1594  kdDebug(5006) << "final message:\n" + receipt->asString() << endl;
1595 
1596  //
1597  // Set "MDN sent" status:
1598  //
1599  KMMsgMDNSentState state = KMMsgMDNStateUnknown;
1600  switch ( d ) {
1601  case MDN::Displayed: state = KMMsgMDNDisplayed; break;
1602  case MDN::Deleted: state = KMMsgMDNDeleted; break;
1603  case MDN::Dispatched: state = KMMsgMDNDispatched; break;
1604  case MDN::Processed: state = KMMsgMDNProcessed; break;
1605  case MDN::Denied: state = KMMsgMDNDenied; break;
1606  case MDN::Failed: state = KMMsgMDNFailed; break;
1607  };
1608  setMDNSentState( state );
1609 
1610  return receipt;
1611 }
1612 
1613 TQString KMMessage::replaceHeadersInString( const TQString & s ) const {
1614  TQString result = s;
1615  TQRegExp rx( "\\$\\{([a-z0-9-]+)\\}", false );
1616  Q_ASSERT( rx.isValid() );
1617 
1618  TQRegExp rxDate( "\\$\\{date\\}" );
1619  Q_ASSERT( rxDate.isValid() );
1620 
1621  TQString sDate = KMime::DateFormatter::formatDate(
1622  KMime::DateFormatter::Localized, date() );
1623 
1624  int idx = 0;
1625  if( ( idx = rxDate.search( result, idx ) ) != -1 ) {
1626  result.replace( idx, rxDate.matchedLength(), sDate );
1627  }
1628 
1629  idx = 0;
1630  while ( ( idx = rx.search( result, idx ) ) != -1 ) {
1631  TQString replacement = headerField( TQString(rx.cap(1)).latin1() );
1632  result.replace( idx, rx.matchedLength(), replacement );
1633  idx += replacement.length();
1634  }
1635  return result;
1636 }
1637 
1639 {
1640  TQString str, receiptTo;
1641  KMMessage *receipt;
1642 
1643  receiptTo = headerField("Disposition-Notification-To");
1644  if ( receiptTo.stripWhiteSpace().isEmpty() ) return 0;
1645  receiptTo.remove( '\n' );
1646 
1647  receipt = new KMMessage;
1648  receipt->initFromMessage(this);
1649  receipt->setTo(receiptTo);
1650  receipt->setSubject(i18n("Receipt: ") + subject());
1651 
1652  str = "Your message was successfully delivered.";
1653  str += "\n\n---------- Message header follows ----------\n";
1654  str += headerAsString();
1655  str += "--------------------------------------------\n";
1656  // Conversion to latin1 is correct here as Mail headers should contain
1657  // ascii only
1658  receipt->setBody(str.latin1());
1659  receipt->setAutomaticFields();
1660 
1661  return receipt;
1662 }
1663 
1664 
1666 {
1667  const KPIM::Identity & ident =
1668  kmkernel->identityManager()->identityForUoidOrDefault( id );
1669 
1670  if(ident.fullEmailAddr().isEmpty())
1671  setFrom("");
1672  else
1673  setFrom(ident.fullEmailAddr());
1674 
1675  if(ident.replyToAddr().isEmpty())
1676  setReplyTo("");
1677  else
1678  setReplyTo(ident.replyToAddr());
1679 
1680  if(ident.bcc().isEmpty())
1681  setBcc("");
1682  else
1683  setBcc(ident.bcc());
1684 
1685  if (ident.organization().isEmpty())
1686  removeHeaderField("Organization");
1687  else
1688  setHeaderField("Organization", ident.organization());
1689 
1690  if (ident.isDefault())
1691  removeHeaderField("X-KMail-Identity");
1692  else
1693  setHeaderField("X-KMail-Identity", TQString::number( ident.uoid() ));
1694 
1695  if ( ident.transport().isEmpty() )
1696  removeHeaderField( "X-KMail-Transport" );
1697  else
1698  setHeaderField( "X-KMail-Transport", ident.transport() );
1699 
1700  if ( ident.fcc().isEmpty() )
1701  setFcc( TQString() );
1702  else
1703  setFcc( ident.fcc() );
1704 
1705  if ( ident.drafts().isEmpty() )
1706  setDrafts( TQString() );
1707  else
1708  setDrafts( ident.drafts() );
1709 
1710  if ( ident.templates().isEmpty() )
1711  setTemplates( TQString() );
1712  else
1713  setTemplates( ident.templates() );
1714 
1715 }
1716 
1717 //-----------------------------------------------------------------------------
1718 void KMMessage::initHeader( uint id )
1719 {
1720  applyIdentity( id );
1721  setTo("");
1722  setSubject("");
1723  setDateToday();
1724 
1725  setHeaderField("User-Agent", "KMail/" KMAIL_VERSION );
1726  // This will allow to change Content-Type:
1727  setHeaderField("Content-Type","text/plain");
1728 }
1729 
1731  TQString idString = headerField("X-KMail-Identity").stripWhiteSpace();
1732  bool ok = false;
1733  int id = idString.toUInt( &ok );
1734 
1735  if ( !ok || id == 0 )
1736  id = kmkernel->identityManager()->identityForAddress( to() + ", " + cc() ).uoid();
1737  if ( id == 0 && parent() )
1738  id = parent()->identity();
1739 
1740  return id;
1741 }
1742 
1743 
1744 //-----------------------------------------------------------------------------
1745 void KMMessage::initFromMessage(const KMMessage *msg, bool idHeaders)
1746 {
1747  uint id = msg->identityUoid();
1748 
1749  if ( idHeaders ) initHeader(id);
1750  else setHeaderField("X-KMail-Identity", TQString::number(id));
1751  if (!msg->headerField("X-KMail-Transport").isEmpty())
1752  setHeaderField("X-KMail-Transport", msg->headerField("X-KMail-Transport"));
1753 }
1754 
1755 
1756 //-----------------------------------------------------------------------------
1758 {
1759  DwHeaders& header = mMsg->Headers();
1760  DwField* field = header.FirstField();
1761  DwField* nextField;
1762 
1763  if (mNeedsAssembly) mMsg->Assemble();
1764  mNeedsAssembly = false;
1765 
1766  while (field)
1767  {
1768  nextField = field->Next();
1769  if (field->FieldBody()->AsString().empty())
1770  {
1771  header.RemoveField(field);
1772  mNeedsAssembly = true;
1773  }
1774  field = nextField;
1775  }
1776 }
1777 
1778 
1779 //-----------------------------------------------------------------------------
1781 {
1782  DwHeaders& header = mMsg->Headers();
1783  header.MimeVersion().FromString("1.0");
1784 
1785  if (aIsMulti || numBodyParts() > 1)
1786  {
1787  // Set the type to 'Multipart' and the subtype to 'Mixed'
1788  DwMediaType& contentType = dwContentType();
1789  contentType.SetType( DwMime::kTypeMultipart);
1790  contentType.SetSubtype(DwMime::kSubtypeMixed );
1791 
1792  // Create a random printable string and set it as the boundary parameter
1793  contentType.CreateBoundary(0);
1794  }
1795  mNeedsAssembly = true;
1796 }
1797 
1798 
1799 //-----------------------------------------------------------------------------
1800 TQString KMMessage::dateStr() const
1801 {
1802  TDEConfigGroup general( KMKernel::config(), "General" );
1803  DwHeaders& header = mMsg->Headers();
1804  time_t unixTime;
1805 
1806  if (!header.HasDate()) return "";
1807  unixTime = header.Date().AsUnixTime();
1808 
1809  //kdDebug(5006)<<"#### Date = "<<header.Date().AsString().c_str()<<endl;
1810 
1811  return KMime::DateFormatter::formatDate(
1812  static_cast<KMime::DateFormatter::FormatType>(general.readNumEntry( "dateFormat", KMime::DateFormatter::Fancy )),
1813  unixTime, general.readEntry( "customDateFormat" ));
1814 }
1815 
1816 
1817 //-----------------------------------------------------------------------------
1818 TQCString KMMessage::dateShortStr() const
1819 {
1820  DwHeaders& header = mMsg->Headers();
1821  time_t unixTime;
1822 
1823  if (!header.HasDate()) return "";
1824  unixTime = header.Date().AsUnixTime();
1825 
1826  TQCString result = ctime(&unixTime);
1827  int len = result.length();
1828  if (result[len-1]=='\n')
1829  result.truncate(len-1);
1830 
1831  return result;
1832 }
1833 
1834 
1835 //-----------------------------------------------------------------------------
1836 TQString KMMessage::dateIsoStr() const
1837 {
1838  DwHeaders& header = mMsg->Headers();
1839  time_t unixTime;
1840 
1841  if (!header.HasDate()) return "";
1842  unixTime = header.Date().AsUnixTime();
1843 
1844  char cstr[64];
1845  strftime(cstr, 63, "%Y-%m-%d %H:%M:%S", localtime(&unixTime));
1846  return TQString(cstr);
1847 }
1848 
1849 
1850 //-----------------------------------------------------------------------------
1851 time_t KMMessage::date() const
1852 {
1853  time_t res = ( time_t )-1;
1854  DwHeaders& header = mMsg->Headers();
1855  if (header.HasDate())
1856  res = header.Date().AsUnixTime();
1857  return res;
1858 }
1859 
1860 
1861 //-----------------------------------------------------------------------------
1863 {
1864  struct timeval tval;
1865  gettimeofday(&tval, 0);
1866  setDate((time_t)tval.tv_sec);
1867 }
1868 
1869 
1870 //-----------------------------------------------------------------------------
1871 void KMMessage::setDate(time_t aDate)
1872 {
1873  mDate = aDate;
1874  mMsg->Headers().Date().FromCalendarTime(aDate);
1875  mMsg->Headers().Date().Assemble();
1876  mNeedsAssembly = true;
1877  mDirty = true;
1878 }
1879 
1880 
1881 //-----------------------------------------------------------------------------
1882 void KMMessage::setDate(const TQCString& aStr)
1883 {
1884  DwHeaders& header = mMsg->Headers();
1885 
1886  header.Date().FromString(aStr);
1887  header.Date().Parse();
1888  mNeedsAssembly = true;
1889  mDirty = true;
1890 
1891  if (header.HasDate())
1892  mDate = header.Date().AsUnixTime();
1893 }
1894 
1895 
1896 //-----------------------------------------------------------------------------
1897 TQString KMMessage::to() const
1898 {
1899  // handle To same as Cc below, bug 80747
1900  TQValueList<TQCString> rawHeaders = rawHeaderFields( "To" );
1901  TQStringList headers;
1902  for ( TQValueList<TQCString>::Iterator it = rawHeaders.begin(); it != rawHeaders.end(); ++it ) {
1903  headers << *it;
1904  }
1905  return KPIM::normalizeAddressesAndDecodeIDNs( headers.join( ", " ) );
1906 }
1907 
1908 
1909 //-----------------------------------------------------------------------------
1910 void KMMessage::setTo(const TQString& aStr)
1911 {
1912  setHeaderField( "To", aStr, Address );
1913 }
1914 
1915 //-----------------------------------------------------------------------------
1916 TQString KMMessage::toStrip() const
1917 {
1918  return stripEmailAddr( to() );
1919 }
1920 
1921 //-----------------------------------------------------------------------------
1922 TQString KMMessage::replyTo() const
1923 {
1924  return KPIM::normalizeAddressesAndDecodeIDNs( rawHeaderField("Reply-To") );
1925 }
1926 
1927 
1928 //-----------------------------------------------------------------------------
1929 void KMMessage::setReplyTo(const TQString& aStr)
1930 {
1931  setHeaderField( "Reply-To", aStr, Address );
1932 }
1933 
1934 
1935 //-----------------------------------------------------------------------------
1936 void KMMessage::setReplyTo(KMMessage* aMsg)
1937 {
1938  setHeaderField( "Reply-To", aMsg->from(), Address );
1939 }
1940 
1941 
1942 //-----------------------------------------------------------------------------
1943 TQString KMMessage::cc() const
1944 {
1945  // get the combined contents of all Cc headers (as workaround for invalid
1946  // messages with multiple Cc headers)
1947  TQValueList<TQCString> rawHeaders = rawHeaderFields( "Cc" );
1948  TQStringList headers;
1949  for ( TQValueList<TQCString>::Iterator it = rawHeaders.begin(); it != rawHeaders.end(); ++it ) {
1950  headers << *it;
1951  }
1952  return KPIM::normalizeAddressesAndDecodeIDNs( headers.join( ", " ) );
1953 }
1954 
1955 
1956 //-----------------------------------------------------------------------------
1957 void KMMessage::setCc(const TQString& aStr)
1958 {
1959  setHeaderField( "Cc", aStr, Address );
1960 }
1961 
1962 
1963 //-----------------------------------------------------------------------------
1964 TQString KMMessage::ccStrip() const
1965 {
1966  return stripEmailAddr( cc() );
1967 }
1968 
1969 
1970 //-----------------------------------------------------------------------------
1971 TQString KMMessage::bcc() const
1972 {
1973  return KPIM::normalizeAddressesAndDecodeIDNs( rawHeaderField("Bcc") );
1974 }
1975 
1976 
1977 //-----------------------------------------------------------------------------
1978 void KMMessage::setBcc(const TQString& aStr)
1979 {
1980  setHeaderField( "Bcc", aStr, Address );
1981 }
1982 
1983 //-----------------------------------------------------------------------------
1984 TQString KMMessage::fcc() const
1985 {
1986  return headerField( "X-KMail-Fcc" );
1987 }
1988 
1989 
1990 //-----------------------------------------------------------------------------
1991 void KMMessage::setFcc( const TQString &aStr )
1992 {
1993  setHeaderField( "X-KMail-Fcc", aStr );
1994 }
1995 
1996 //-----------------------------------------------------------------------------
1997 void KMMessage::setDrafts( const TQString &aStr )
1998 {
1999  mDrafts = aStr;
2000 }
2001 
2002 //-----------------------------------------------------------------------------
2003 void KMMessage::setTemplates( const TQString &aStr )
2004 {
2005  mTemplates = aStr;
2006 }
2007 
2008 //-----------------------------------------------------------------------------
2009 TQString KMMessage::who() const
2010 {
2011  if (mParent)
2012  return KPIM::normalizeAddressesAndDecodeIDNs( rawHeaderField(mParent->whoField().utf8()) );
2013  return from();
2014 }
2015 
2016 
2017 //-----------------------------------------------------------------------------
2018 TQString KMMessage::from() const
2019 {
2020  return KPIM::normalizeAddressesAndDecodeIDNs( rawHeaderField("From") );
2021 }
2022 
2023 
2024 //-----------------------------------------------------------------------------
2025 void KMMessage::setFrom(const TQString& bStr)
2026 {
2027  TQString aStr = bStr;
2028  if (aStr.isNull())
2029  aStr = "";
2030  setHeaderField( "From", aStr, Address );
2031  mDirty = true;
2032 }
2033 
2034 
2035 //-----------------------------------------------------------------------------
2036 TQString KMMessage::fromStrip() const
2037 {
2038  return stripEmailAddr( from() );
2039 }
2040 
2041 //-----------------------------------------------------------------------------
2042 TQString KMMessage::sender() const {
2043  AddrSpecList asl = extractAddrSpecs( "Sender" );
2044  if ( asl.empty() )
2045  asl = extractAddrSpecs( "From" );
2046  if ( asl.empty() )
2047  return TQString();
2048  return asl.front().asString();
2049 }
2050 
2051 //-----------------------------------------------------------------------------
2052 TQString KMMessage::subject() const
2053 {
2054  return headerField("Subject");
2055 }
2056 
2057 
2058 //-----------------------------------------------------------------------------
2059 void KMMessage::setSubject(const TQString& aStr)
2060 {
2061  setHeaderField("Subject",aStr);
2062  mDirty = true;
2063 }
2064 
2065 
2066 //-----------------------------------------------------------------------------
2067 TQString KMMessage::xmark() const
2068 {
2069  return headerField("X-KMail-Mark");
2070 }
2071 
2072 
2073 //-----------------------------------------------------------------------------
2074 void KMMessage::setXMark(const TQString& aStr)
2075 {
2076  setHeaderField("X-KMail-Mark", aStr);
2077  mDirty = true;
2078 }
2079 
2080 
2081 //-----------------------------------------------------------------------------
2082 TQString KMMessage::replyToId() const
2083 {
2084  int leftAngle, rightAngle;
2085  TQString replyTo, references;
2086 
2087  replyTo = headerField("In-Reply-To");
2088  // search the end of the (first) message id in the In-Reply-To header
2089  rightAngle = replyTo.find( '>' );
2090  if (rightAngle != -1)
2091  replyTo.truncate( rightAngle + 1 );
2092  // now search the start of the message id
2093  leftAngle = replyTo.findRev( '<' );
2094  if (leftAngle != -1)
2095  replyTo = replyTo.mid( leftAngle );
2096 
2097  // if we have found a good message id we can return immediately
2098  // We ignore mangled In-Reply-To headers which are created by a
2099  // misconfigured Mutt. They look like this <"from foo"@bar.baz>, i.e.
2100  // they contain double quotes and spaces. We only check for '"'.
2101  if (!replyTo.isEmpty() && (replyTo[0] == '<') &&
2102  ( -1 == replyTo.find( '"' ) ) )
2103  return replyTo;
2104 
2105  references = headerField("References");
2106  leftAngle = references.findRev( '<' );
2107  if (leftAngle != -1)
2108  references = references.mid( leftAngle );
2109  rightAngle = references.find( '>' );
2110  if (rightAngle != -1)
2111  references.truncate( rightAngle + 1 );
2112 
2113  // if we found a good message id in the References header return it
2114  if (!references.isEmpty() && references[0] == '<')
2115  return references;
2116  // else return the broken message id we found in the In-Reply-To header
2117  else
2118  return replyTo;
2119 }
2120 
2121 
2122 //-----------------------------------------------------------------------------
2123 TQString KMMessage::replyToIdMD5() const {
2124  return base64EncodedMD5( replyToId() );
2125 }
2126 
2127 //-----------------------------------------------------------------------------
2128 TQString KMMessage::references() const
2129 {
2130  int leftAngle, rightAngle;
2131  TQString references = headerField( "References" );
2132 
2133  // keep the last two entries for threading
2134  leftAngle = references.findRev( '<' );
2135  leftAngle = references.findRev( '<', leftAngle - 1 );
2136  if( leftAngle != -1 )
2137  references = references.mid( leftAngle );
2138  rightAngle = references.findRev( '>' );
2139  if( rightAngle != -1 )
2140  references.truncate( rightAngle + 1 );
2141 
2142  if( !references.isEmpty() && references[0] == '<' )
2143  return references;
2144  else
2145  return TQString();
2146 }
2147 
2148 //-----------------------------------------------------------------------------
2150 {
2151  TQString result = references();
2152  // references contains two items, use the first one
2153  // (the second to last reference)
2154  const int rightAngle = result.find( '>' );
2155  if( rightAngle != -1 )
2156  result.truncate( rightAngle + 1 );
2157 
2158  return base64EncodedMD5( result );
2159 }
2160 
2161 //-----------------------------------------------------------------------------
2163  return base64EncodedMD5( stripOffPrefixes( subject() ), true /*utf8*/ );
2164 }
2165 
2166 //-----------------------------------------------------------------------------
2167 TQString KMMessage::subjectMD5() const {
2168  return base64EncodedMD5( subject(), true /*utf8*/ );
2169 }
2170 
2171 //-----------------------------------------------------------------------------
2173  return subjectMD5() != strippedSubjectMD5();
2174 }
2175 
2176 //-----------------------------------------------------------------------------
2177 void KMMessage::setReplyToId(const TQString& aStr)
2178 {
2179  setHeaderField("In-Reply-To", aStr);
2180  mDirty = true;
2181 }
2182 
2183 
2184 //-----------------------------------------------------------------------------
2185 TQString KMMessage::msgId() const
2186 {
2187  TQString msgId = headerField("Message-Id");
2188 
2189  // search the end of the message id
2190  const int rightAngle = msgId.find( '>' );
2191  if (rightAngle != -1)
2192  msgId.truncate( rightAngle + 1 );
2193  // now search the start of the message id
2194  const int leftAngle = msgId.findRev( '<' );
2195  if (leftAngle != -1)
2196  msgId = msgId.mid( leftAngle );
2197  return msgId;
2198 }
2199 
2200 
2201 //-----------------------------------------------------------------------------
2202 TQString KMMessage::msgIdMD5() const {
2203  return base64EncodedMD5( msgId() );
2204 }
2205 
2206 
2207 //-----------------------------------------------------------------------------
2208 void KMMessage::setMsgId(const TQString& aStr)
2209 {
2210  setHeaderField("Message-Id", aStr);
2211  mDirty = true;
2212 }
2213 
2214 //-----------------------------------------------------------------------------
2216  return headerField( "X-Length" ).toULong();
2217 }
2218 
2219 
2220 //-----------------------------------------------------------------------------
2221 void KMMessage::setMsgSizeServer(size_t size)
2222 {
2223  setHeaderField("X-Length", TQCString().setNum(size));
2224  mDirty = true;
2225 }
2226 
2227 //-----------------------------------------------------------------------------
2228 ulong KMMessage::UID() const {
2229  return headerField( "X-UID" ).toULong();
2230 }
2231 
2232 
2233 //-----------------------------------------------------------------------------
2234 void KMMessage::setUID(ulong uid)
2235 {
2236  setHeaderField("X-UID", TQCString().setNum(uid));
2237  mDirty = true;
2238 }
2239 
2240 //-----------------------------------------------------------------------------
2241 AddressList KMMessage::splitAddrField( const TQCString & str )
2242 {
2243  AddressList result;
2244  const char * scursor = str.begin();
2245  if ( !scursor )
2246  return AddressList();
2247  const char * const send = str.begin() + str.length();
2248  if ( !parseAddressList( scursor, send, result ) )
2249  kdDebug(5006) << "Error in address splitting: parseAddressList returned false!"
2250  << endl;
2251  return result;
2252 }
2253 
2254 AddressList KMMessage::headerAddrField( const TQCString & aName ) const {
2255  return KMMessage::splitAddrField( rawHeaderField( aName ) );
2256 }
2257 
2258 AddrSpecList KMMessage::extractAddrSpecs( const TQCString & header ) const {
2259  AddressList al = headerAddrField( header );
2260  AddrSpecList result;
2261  for ( AddressList::const_iterator ait = al.begin() ; ait != al.end() ; ++ait )
2262  for ( MailboxList::const_iterator mit = (*ait).mailboxList.begin() ; mit != (*ait).mailboxList.end() ; ++mit )
2263  result.push_back( (*mit).addrSpec );
2264  return result;
2265 }
2266 
2267 TQCString KMMessage::rawHeaderField( const TQCString & name ) const {
2268  if ( name.isEmpty() ) return TQCString();
2269 
2270  DwHeaders & header = mMsg->Headers();
2271  DwField * field = header.FindField( name );
2272 
2273  if ( !field ) return TQCString();
2274 
2275  return header.FieldBody( name.data() ).AsString().c_str();
2276 }
2277 
2278 TQValueList<TQCString> KMMessage::rawHeaderFields( const TQCString& field ) const
2279 {
2280  if ( field.isEmpty() || !mMsg->Headers().FindField( field ) )
2281  return TQValueList<TQCString>();
2282 
2283  std::vector<DwFieldBody*> v = mMsg->Headers().AllFieldBodies( field.data() );
2284  TQValueList<TQCString> headerFields;
2285  for ( uint i = 0; i < v.size(); ++i ) {
2286  headerFields.append( v[i]->AsString().c_str() );
2287  }
2288 
2289  return headerFields;
2290 }
2291 
2292 TQString KMMessage::headerField(const TQCString& aName) const
2293 {
2294  if ( aName.isEmpty() )
2295  return TQString();
2296 
2297  if ( !mMsg->Headers().FindField( aName ) )
2298  return TQString();
2299 
2300  return decodeRFC2047String( mMsg->Headers().FieldBody( aName.data() ).AsString().c_str(),
2301  charset() );
2302 
2303 }
2304 
2305 TQStringList KMMessage::headerFields( const TQCString& field ) const
2306 {
2307  if ( field.isEmpty() || !mMsg->Headers().FindField( field ) )
2308  return TQStringList();
2309 
2310  std::vector<DwFieldBody*> v = mMsg->Headers().AllFieldBodies( field.data() );
2311  TQStringList headerFields;
2312  for ( uint i = 0; i < v.size(); ++i ) {
2313  headerFields.append( decodeRFC2047String( v[i]->AsString().c_str(), charset() ) );
2314  }
2315 
2316  return headerFields;
2317 }
2318 
2319 //-----------------------------------------------------------------------------
2320 void KMMessage::removeHeaderField(const TQCString& aName)
2321 {
2322  DwHeaders & header = mMsg->Headers();
2323  DwField * field = header.FindField(aName);
2324  if (!field) return;
2325 
2326  header.RemoveField(field);
2327  mNeedsAssembly = true;
2328 }
2329 
2330 //-----------------------------------------------------------------------------
2331 void KMMessage::removeHeaderFields(const TQCString& aName)
2332 {
2333  DwHeaders & header = mMsg->Headers();
2334  while ( DwField * field = header.FindField(aName) ) {
2335  header.RemoveField(field);
2336  mNeedsAssembly = true;
2337  }
2338 }
2339 
2340 
2341 //-----------------------------------------------------------------------------
2342 void KMMessage::setHeaderField( const TQCString& aName, const TQString& bValue,
2343  HeaderFieldType type, bool prepend )
2344 {
2345 #if 0
2346  if ( type != Unstructured )
2347  kdDebug(5006) << "KMMessage::setHeaderField( \"" << aName << "\", \""
2348  << bValue << "\", " << type << " )" << endl;
2349 #endif
2350  if (aName.isEmpty()) return;
2351 
2352  DwHeaders& header = mMsg->Headers();
2353 
2354  DwString str;
2355  DwField* field;
2356  TQCString aValue;
2357  if (!bValue.isEmpty())
2358  {
2359  TQString value = bValue;
2360  if ( type == Address )
2361  value = KPIM::normalizeAddressesAndEncodeIDNs( value );
2362 #if 0
2363  if ( type != Unstructured )
2364  kdDebug(5006) << "value: \"" << value << "\"" << endl;
2365 #endif
2366  TQCString encoding = autoDetectCharset( charset(), sPrefCharsets, value );
2367  if (encoding.isEmpty())
2368  encoding = "utf-8";
2369  aValue = encodeRFC2047String( value, encoding );
2370 #if 0
2371  if ( type != Unstructured )
2372  kdDebug(5006) << "aValue: \"" << aValue << "\"" << endl;
2373 #endif
2374  }
2375  str = aName.data();
2376  if (str[str.length()-1] != ':') str += ": ";
2377  else str += ' ';
2378  if ( !aValue.isEmpty() )
2379  str += aValue.data();
2380  if (str[str.length()-1] != '\n') str += '\n';
2381 
2382  field = new DwField(str, mMsg);
2383  field->Parse();
2384 
2385  if ( prepend )
2386  header.AddFieldAt( 1, field );
2387  else
2388  header.AddOrReplaceField( field );
2389  mNeedsAssembly = true;
2390 }
2391 
2392 
2393 //-----------------------------------------------------------------------------
2394 TQCString KMMessage::typeStr() const
2395 {
2396  DwHeaders& header = mMsg->Headers();
2397  if (header.HasContentType()) return header.ContentType().TypeStr().c_str();
2398  else return "";
2399 }
2400 
2401 
2402 //-----------------------------------------------------------------------------
2403 int KMMessage::type() const
2404 {
2405  DwHeaders& header = mMsg->Headers();
2406  if (header.HasContentType()) return header.ContentType().Type();
2407  else return DwMime::kTypeNull;
2408 }
2409 
2410 
2411 //-----------------------------------------------------------------------------
2412 void KMMessage::setTypeStr(const TQCString& aStr)
2413 {
2414  dwContentType().SetTypeStr(DwString(aStr));
2415  dwContentType().Parse();
2416  mNeedsAssembly = true;
2417 }
2418 
2419 
2420 //-----------------------------------------------------------------------------
2421 void KMMessage::setType(int aType)
2422 {
2423  dwContentType().SetType(aType);
2424  dwContentType().Assemble();
2425  mNeedsAssembly = true;
2426 }
2427 
2428 
2429 
2430 //-----------------------------------------------------------------------------
2431 TQCString KMMessage::subtypeStr() const
2432 {
2433  DwHeaders& header = mMsg->Headers();
2434  if (header.HasContentType()) return header.ContentType().SubtypeStr().c_str();
2435  else return "";
2436 }
2437 
2438 
2439 //-----------------------------------------------------------------------------
2440 int KMMessage::subtype() const
2441 {
2442  DwHeaders& header = mMsg->Headers();
2443  if (header.HasContentType()) return header.ContentType().Subtype();
2444  else return DwMime::kSubtypeNull;
2445 }
2446 
2447 
2448 //-----------------------------------------------------------------------------
2449 void KMMessage::setSubtypeStr(const TQCString& aStr)
2450 {
2451  dwContentType().SetSubtypeStr(DwString(aStr));
2452  dwContentType().Parse();
2453  mNeedsAssembly = true;
2454 }
2455 
2456 
2457 //-----------------------------------------------------------------------------
2458 void KMMessage::setSubtype(int aSubtype)
2459 {
2460  dwContentType().SetSubtype(aSubtype);
2461  dwContentType().Assemble();
2462  mNeedsAssembly = true;
2463 }
2464 
2465 
2466 //-----------------------------------------------------------------------------
2467 void KMMessage::setDwMediaTypeParam( DwMediaType &mType,
2468  const TQCString& attr,
2469  const TQCString& val )
2470 {
2471  mType.Parse();
2472  DwParameter *param = mType.FirstParameter();
2473  while(param) {
2474  if (!kasciistricmp(param->Attribute().c_str(), attr))
2475  break;
2476  else
2477  param = param->Next();
2478  }
2479  if (!param){
2480  param = new DwParameter;
2481  param->SetAttribute(DwString( attr ));
2482  mType.AddParameter( param );
2483  }
2484  else
2485  mType.SetModified();
2486  param->SetValue(DwString( val ));
2487  mType.Assemble();
2488 }
2489 
2490 
2491 //-----------------------------------------------------------------------------
2492 void KMMessage::setContentTypeParam(const TQCString& attr, const TQCString& val)
2493 {
2494  if (mNeedsAssembly) mMsg->Assemble();
2495  mNeedsAssembly = false;
2496  setDwMediaTypeParam( dwContentType(), attr, val );
2497  mNeedsAssembly = true;
2498 }
2499 
2500 
2501 //-----------------------------------------------------------------------------
2503 {
2504  DwHeaders& header = mMsg->Headers();
2505  if (header.HasContentTransferEncoding())
2506  return header.ContentTransferEncoding().AsString().c_str();
2507  else return "";
2508 }
2509 
2510 
2511 //-----------------------------------------------------------------------------
2512 int KMMessage::contentTransferEncoding( DwEntity *entity ) const
2513 {
2514  if ( !entity )
2515  entity = mMsg;
2516 
2517  DwHeaders& header = entity->Headers();
2518  if ( header.HasContentTransferEncoding() )
2519  return header.ContentTransferEncoding().AsEnum();
2520  else return DwMime::kCteNull;
2521 }
2522 
2523 
2524 //-----------------------------------------------------------------------------
2525 void KMMessage::setContentTransferEncodingStr( const TQCString& cteString,
2526  DwEntity *entity )
2527 {
2528  if ( !entity )
2529  entity = mMsg;
2530 
2531  entity->Headers().ContentTransferEncoding().FromString( cteString );
2532  entity->Headers().ContentTransferEncoding().Parse();
2533  mNeedsAssembly = true;
2534 }
2535 
2536 
2537 //-----------------------------------------------------------------------------
2538 void KMMessage::setContentTransferEncoding( int cte, DwEntity *entity )
2539 {
2540  if ( !entity )
2541  entity = mMsg;
2542 
2543  entity->Headers().ContentTransferEncoding().FromEnum( cte );
2544  mNeedsAssembly = true;
2545 }
2546 
2547 
2548 //-----------------------------------------------------------------------------
2549 DwHeaders& KMMessage::headers() const
2550 {
2551  return mMsg->Headers();
2552 }
2553 
2554 
2555 //-----------------------------------------------------------------------------
2557 {
2558  mNeedsAssembly = true;
2559 }
2560 
2561 //-----------------------------------------------------------------------------
2563 {
2564  Q_ASSERT( mMsg );
2565 
2566  if ( mNeedsAssembly ) {
2567  mMsg->Assemble();
2568  mNeedsAssembly = false;
2569  }
2570 }
2571 
2572 //-----------------------------------------------------------------------------
2573 TQCString KMMessage::body() const
2574 {
2575  const DwString& body = mMsg->Body().AsString();
2576  TQCString str = KMail::Util::CString( body );
2577  // Calls length() -> slow
2578  //kdWarning( str.length() != body.length(), 5006 )
2579  // << "KMMessage::body(): body is binary but used as text!" << endl;
2580  return str;
2581 }
2582 
2583 
2584 //-----------------------------------------------------------------------------
2585 TQByteArray KMMessage::bodyDecodedBinary() const
2586 {
2587  DwString dwstr;
2588  const DwString& dwsrc = mMsg->Body().AsString();
2589 
2590  switch (cte())
2591  {
2592  case DwMime::kCteBase64:
2593  DwDecodeBase64(dwsrc, dwstr);
2594  break;
2595  case DwMime::kCteQuotedPrintable:
2596  DwDecodeQuotedPrintable(dwsrc, dwstr);
2597  break;
2598  default:
2599  dwstr = dwsrc;
2600  break;
2601  }
2602 
2603  int len = dwstr.size();
2604  TQByteArray ba(len);
2605  memcpy(ba.data(),dwstr.data(),len);
2606  return ba;
2607 }
2608 
2609 
2610 //-----------------------------------------------------------------------------
2611 TQCString KMMessage::bodyDecoded() const
2612 {
2613  DwString dwstr;
2614  DwString dwsrc = mMsg->Body().AsString();
2615 
2616  switch (cte())
2617  {
2618  case DwMime::kCteBase64:
2619  DwDecodeBase64(dwsrc, dwstr);
2620  break;
2621  case DwMime::kCteQuotedPrintable:
2622  DwDecodeQuotedPrintable(dwsrc, dwstr);
2623  break;
2624  default:
2625  dwstr = dwsrc;
2626  break;
2627  }
2628 
2629  return KMail::Util::CString( dwstr );
2630 
2631  // Calling TQCString::length() is slow
2632  //TQCString result = KMail::Util::CString( dwstr );
2633  //kdWarning(result.length() != len, 5006)
2634  // << "KMMessage::bodyDecoded(): body is binary but used as text!" << endl;
2635  //return result;
2636 }
2637 
2638 
2639 //-----------------------------------------------------------------------------
2640 TQValueList<int> KMMessage::determineAllowedCtes( const CharFreq& cf,
2641  bool allow8Bit,
2642  bool willBeSigned )
2643 {
2644  TQValueList<int> allowedCtes;
2645 
2646  switch ( cf.type() ) {
2647  case CharFreq::SevenBitText:
2648  allowedCtes << DwMime::kCte7bit;
2649  case CharFreq::EightBitText:
2650  if ( allow8Bit )
2651  allowedCtes << DwMime::kCte8bit;
2652  case CharFreq::SevenBitData:
2653  if ( cf.printableRatio() > 5.0/6.0 ) {
2654  // let n the length of data and p the number of printable chars.
2655  // Then base64 \approx 4n/3; qp \approx p + 3(n-p)
2656  // => qp < base64 iff p > 5n/6.
2657  allowedCtes << DwMime::kCteQp;
2658  allowedCtes << DwMime::kCteBase64;
2659  } else {
2660  allowedCtes << DwMime::kCteBase64;
2661  allowedCtes << DwMime::kCteQp;
2662  }
2663  break;
2664  case CharFreq::EightBitData:
2665  allowedCtes << DwMime::kCteBase64;
2666  break;
2667  case CharFreq::None:
2668  default:
2669  // just nothing (avoid compiler warning)
2670  ;
2671  }
2672 
2673  // In the following cases only QP and Base64 are allowed:
2674  // - the buffer will be OpenPGP/MIME signed and it contains trailing
2675  // whitespace (cf. RFC 3156)
2676  // - a line starts with "From "
2677  if ( ( willBeSigned && cf.hasTrailingWhitespace() ) ||
2678  cf.hasLeadingFrom() ) {
2679  allowedCtes.remove( DwMime::kCte8bit );
2680  allowedCtes.remove( DwMime::kCte7bit );
2681  }
2682 
2683  return allowedCtes;
2684 }
2685 
2686 
2687 //-----------------------------------------------------------------------------
2688 void KMMessage::setBodyAndGuessCte( const TQByteArray& aBuf,
2689  TQValueList<int> & allowedCte,
2690  bool allow8Bit,
2691  bool willBeSigned,
2692  DwEntity *entity )
2693 {
2694  if ( !entity )
2695  entity = mMsg;
2696 
2697  CharFreq cf( aBuf ); // it's safe to pass null arrays
2698  allowedCte = determineAllowedCtes( cf, allow8Bit, willBeSigned );
2699  setCte( allowedCte[0], entity ); // choose best fitting
2700  setBodyEncodedBinary( aBuf, entity );
2701 }
2702 
2703 
2704 //-----------------------------------------------------------------------------
2705 void KMMessage::setBodyAndGuessCte( const TQCString& aBuf,
2706  TQValueList<int> & allowedCte,
2707  bool allow8Bit,
2708  bool willBeSigned,
2709  DwEntity *entity )
2710 {
2711  if ( !entity )
2712  entity = mMsg;
2713 
2714  CharFreq cf( aBuf.data(), aBuf.size()-1 ); // it's safe to pass null strings
2715  allowedCte = determineAllowedCtes( cf, allow8Bit, willBeSigned );
2716  setCte( allowedCte[0], entity ); // choose best fitting
2717  setBodyEncoded( aBuf, entity );
2718 }
2719 
2720 
2721 //-----------------------------------------------------------------------------
2722 void KMMessage::setBodyEncoded(const TQCString& aStr, DwEntity *entity )
2723 {
2724  if ( !entity )
2725  entity = mMsg;
2726 
2727  DwString dwSrc(aStr.data(), aStr.size()-1 /* not the trailing NUL */);
2728  DwString dwResult;
2729 
2730  switch (cte( entity ))
2731  {
2732  case DwMime::kCteBase64:
2733  DwEncodeBase64(dwSrc, dwResult);
2734  break;
2735  case DwMime::kCteQuotedPrintable:
2736  DwEncodeQuotedPrintable(dwSrc, dwResult);
2737  break;
2738  default:
2739  dwResult = dwSrc;
2740  break;
2741  }
2742 
2743  entity->Body().FromString(dwResult);
2744  mNeedsAssembly = true;
2745 }
2746 
2747 //-----------------------------------------------------------------------------
2748 void KMMessage::setBodyEncodedBinary( const TQByteArray& aStr, DwEntity *entity )
2749 {
2750  if ( !entity )
2751  entity = mMsg;
2752 
2753  DwString dwSrc(aStr.data(), aStr.size());
2754  DwString dwResult;
2755 
2756  switch ( cte( entity ) )
2757  {
2758  case DwMime::kCteBase64:
2759  DwEncodeBase64( dwSrc, dwResult );
2760  break;
2761  case DwMime::kCteQuotedPrintable:
2762  DwEncodeQuotedPrintable( dwSrc, dwResult );
2763  break;
2764  default:
2765  dwResult = dwSrc;
2766  break;
2767  }
2768 
2769  entity->Body().FromString( dwResult );
2770  entity->Body().Parse();
2771 
2772  mNeedsAssembly = true;
2773 }
2774 
2775 
2776 //-----------------------------------------------------------------------------
2777 void KMMessage::setBody(const TQCString& aStr)
2778 {
2779  mMsg->Body().FromString(KMail::Util::dwString(aStr));
2780  mNeedsAssembly = true;
2781 }
2782 void KMMessage::setBody(const DwString& aStr)
2783 {
2784  mMsg->Body().FromString(aStr);
2785  mNeedsAssembly = true;
2786 }
2787 void KMMessage::setBody(const char* aStr)
2788 {
2789  mMsg->Body().FromString(aStr);
2790  mNeedsAssembly = true;
2791 }
2792 
2793 //-----------------------------------------------------------------------------
2794 void KMMessage::setMultiPartBody( const TQCString & aStr ) {
2795  setBody( aStr );
2796  mMsg->Body().Parse();
2797  mNeedsAssembly = true;
2798 }
2799 
2800 
2801 // Patched by Daniel Moisset <dmoisset@grulic.org.ar>
2802 // modified numbodyparts, bodypart to take nested body parts as
2803 // a linear sequence.
2804 // third revision, Sep 26 2000
2805 
2806 // this is support structure for traversing tree without recursion
2807 
2808 //-----------------------------------------------------------------------------
2810 {
2811  int count = 0;
2812  DwBodyPart* part = getFirstDwBodyPart();
2813  TQPtrList< DwBodyPart > parts;
2814 
2815  while (part)
2816  {
2817  //dive into multipart messages
2818  while ( part
2819  && part->hasHeaders()
2820  && part->Headers().HasContentType()
2821  && part->Body().FirstBodyPart()
2822  && (DwMime::kTypeMultipart == part->Headers().ContentType().Type()) )
2823  {
2824  parts.append( part );
2825  part = part->Body().FirstBodyPart();
2826  }
2827  // this is where currPart->msgPart contains a leaf message part
2828  count++;
2829  // go up in the tree until reaching a node with next
2830  // (or the last top-level node)
2831  while (part && !(part->Next()) && !(parts.isEmpty()))
2832  {
2833  part = parts.getLast();
2834  parts.removeLast();
2835  }
2836 
2837  if (part && part->Body().Message() &&
2838  part->Body().Message()->Body().FirstBodyPart())
2839  {
2840  part = part->Body().Message()->Body().FirstBodyPart();
2841  } else if (part) {
2842  part = part->Next();
2843  }
2844  }
2845 
2846  return count;
2847 }
2848 
2849 
2850 //-----------------------------------------------------------------------------
2851 DwBodyPart * KMMessage::getFirstDwBodyPart() const
2852 {
2853  return mMsg->Body().FirstBodyPart();
2854 }
2855 
2856 
2857 //-----------------------------------------------------------------------------
2858 int KMMessage::partNumber( DwBodyPart * aDwBodyPart ) const
2859 {
2860  DwBodyPart *curpart;
2861  TQPtrList< DwBodyPart > parts;
2862  int curIdx = 0;
2863  int idx = 0;
2864  // Get the DwBodyPart for this index
2865 
2866  curpart = getFirstDwBodyPart();
2867 
2868  while (curpart && !idx) {
2869  //dive into multipart messages
2870  while( curpart
2871  && curpart->hasHeaders()
2872  && curpart->Headers().HasContentType()
2873  && curpart->Body().FirstBodyPart()
2874  && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) )
2875  {
2876  parts.append( curpart );
2877  curpart = curpart->Body().FirstBodyPart();
2878  }
2879  // this is where currPart->msgPart contains a leaf message part
2880  if (curpart == aDwBodyPart)
2881  idx = curIdx;
2882  curIdx++;
2883  // go up in the tree until reaching a node with next
2884  // (or the last top-level node)
2885  while (curpart && !(curpart->Next()) && !(parts.isEmpty()))
2886  {
2887  curpart = parts.getLast();
2888  parts.removeLast();
2889  } ;
2890  if (curpart)
2891  curpart = curpart->Next();
2892  }
2893  return idx;
2894 }
2895 
2896 
2897 //-----------------------------------------------------------------------------
2898 DwBodyPart * KMMessage::dwBodyPart( int aIdx ) const
2899 {
2900  DwBodyPart *part, *curpart;
2901  TQPtrList< DwBodyPart > parts;
2902  int curIdx = 0;
2903  // Get the DwBodyPart for this index
2904 
2905  curpart = getFirstDwBodyPart();
2906  part = 0;
2907 
2908  while (curpart && !part) {
2909  //dive into multipart messages
2910  while( curpart
2911  && curpart->hasHeaders()
2912  && curpart->Headers().HasContentType()
2913  && curpart->Body().FirstBodyPart()
2914  && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) )
2915  {
2916  parts.append( curpart );
2917  curpart = curpart->Body().FirstBodyPart();
2918  }
2919  // this is where currPart->msgPart contains a leaf message part
2920  if (curIdx==aIdx)
2921  part = curpart;
2922  curIdx++;
2923  // go up in the tree until reaching a node with next
2924  // (or the last top-level node)
2925  while (curpart && !(curpart->Next()) && !(parts.isEmpty()))
2926  {
2927  curpart = parts.getLast();
2928  parts.removeLast();
2929  }
2930  if (curpart)
2931  curpart = curpart->Next();
2932  }
2933  return part;
2934 }
2935 
2936 
2937 //-----------------------------------------------------------------------------
2938 DwBodyPart * KMMessage::findDwBodyPart( int type, int subtype ) const
2939 {
2940  DwBodyPart *part, *curpart;
2941  TQPtrList< DwBodyPart > parts;
2942  // Get the DwBodyPart for this index
2943 
2944  curpart = getFirstDwBodyPart();
2945  part = 0;
2946 
2947  while (curpart && !part) {
2948  //dive into multipart messages
2949  while(curpart
2950  && curpart->hasHeaders()
2951  && curpart->Headers().HasContentType()
2952  && curpart->Body().FirstBodyPart()
2953  && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) ) {
2954  parts.append( curpart );
2955  curpart = curpart->Body().FirstBodyPart();
2956  }
2957  // this is where curPart->msgPart contains a leaf message part
2958 
2959  // pending(khz): Find out WHY this look does not travel down *into* an
2960  // embedded "Message/RfF822" message containing a "Multipart/Mixed"
2961  if ( curpart && curpart->hasHeaders() && curpart->Headers().HasContentType() ) {
2962  kdDebug(5006) << curpart->Headers().ContentType().TypeStr().c_str()
2963  << " " << curpart->Headers().ContentType().SubtypeStr().c_str() << endl;
2964  }
2965 
2966  if (curpart &&
2967  curpart->hasHeaders() &&
2968  curpart->Headers().HasContentType() &&
2969  curpart->Headers().ContentType().Type() == type &&
2970  curpart->Headers().ContentType().Subtype() == subtype) {
2971  part = curpart;
2972  } else {
2973  // go up in the tree until reaching a node with next
2974  // (or the last top-level node)
2975  while (curpart && !(curpart->Next()) && !(parts.isEmpty())) {
2976  curpart = parts.getLast();
2977  parts.removeLast();
2978  } ;
2979  if (curpart)
2980  curpart = curpart->Next();
2981  }
2982  }
2983  return part;
2984 }
2985 
2986 //-----------------------------------------------------------------------------
2987 DwBodyPart * KMMessage::findDwBodyPart( const TQCString& type, const TQCString& subtype ) const
2988 {
2989  DwBodyPart *part, *curpart;
2990  TQPtrList< DwBodyPart > parts;
2991  // Get the DwBodyPart for this index
2992 
2993  curpart = getFirstDwBodyPart();
2994  part = 0;
2995 
2996  while (curpart && !part) {
2997  //dive into multipart messages
2998  while(curpart
2999  && curpart->hasHeaders()
3000  && curpart->Headers().HasContentType()
3001  && curpart->Body().FirstBodyPart()
3002  && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) ) {
3003  parts.append( curpart );
3004  curpart = curpart->Body().FirstBodyPart();
3005  }
3006  // this is where curPart->msgPart contains a leaf message part
3007 
3008  // pending(khz): Find out WHY this look does not travel down *into* an
3009  // embedded "Message/RfF822" message containing a "Multipart/Mixed"
3010  if (curpart && curpart->hasHeaders() && curpart->Headers().HasContentType() ) {
3011  kdDebug(5006) << curpart->Headers().ContentType().TypeStr().c_str()
3012  << " " << curpart->Headers().ContentType().SubtypeStr().c_str() << endl;
3013  }
3014 
3015  if (curpart &&
3016  curpart->hasHeaders() &&
3017  curpart->Headers().HasContentType() &&
3018  curpart->Headers().ContentType().TypeStr().c_str() == type &&
3019  curpart->Headers().ContentType().SubtypeStr().c_str() == subtype) {
3020  part = curpart;
3021  } else {
3022  // go up in the tree until reaching a node with next
3023  // (or the last top-level node)
3024  while (curpart && !(curpart->Next()) && !(parts.isEmpty())) {
3025  curpart = parts.getLast();
3026  parts.removeLast();
3027  } ;
3028  if (curpart)
3029  curpart = curpart->Next();
3030  }
3031  }
3032  return part;
3033 }
3034 
3035 
3036 void applyHeadersToMessagePart( DwHeaders& headers, KMMessagePart* aPart )
3037 {
3038  // TODO: Instead of manually implementing RFC2231 header encoding (i.e.
3039  // possibly multiple values given as paramname*0=..; parmaname*1=..;...
3040  // or par as paramname*0*=..; parmaname*1*=..;..., which should be
3041  // concatenated), use a generic method to decode the header, using RFC
3042  // 2047 or 2231, or whatever future RFC might be appropriate!
3043  // Right now, some fields are decoded, while others are not. E.g.
3044  // Content-Disposition is not decoded here, rather only on demand in
3045  // KMMsgPart::fileName; Name however is decoded here and stored as a
3046  // decoded String in KMMsgPart...
3047  // Content-type
3048  TQCString additionalCTypeParams;
3049  if (headers.HasContentType())
3050  {
3051  DwMediaType& ct = headers.ContentType();
3052  aPart->setOriginalContentTypeStr( ct.AsString().c_str() );
3053  aPart->setTypeStr(ct.TypeStr().c_str());
3054  aPart->setSubtypeStr(ct.SubtypeStr().c_str());
3055  DwParameter *param = ct.FirstParameter();
3056  while(param)
3057  {
3058  if (!tqstricmp(param->Attribute().c_str(), "charset")) {
3059  if (aPart->type() == DwMime::kTypeText) {
3060  aPart->setCharset(TQCString(param->Value().c_str()).lower());
3061  }
3062  }
3063  else if (!tqstrnicmp(param->Attribute().c_str(), "name*", 5))
3064  aPart->setName(KMMsgBase::decodeRFC2231String(KMMsgBase::extractRFC2231HeaderField( param->Value().c_str(), "name" )));
3065  else {
3066  additionalCTypeParams += ';';
3067  additionalCTypeParams += param->AsString().c_str();
3068  }
3069  param=param->Next();
3070  }
3071  }
3072  else
3073  {
3074  aPart->setTypeStr("text"); // Set to defaults
3075  aPart->setSubtypeStr("plain");
3076  }
3077  aPart->setAdditionalCTypeParamStr( additionalCTypeParams );
3078  // Modification by Markus
3079  if (aPart->name().isEmpty())
3080  {
3081  if (headers.HasContentType() && !headers.ContentType().Name().empty()) {
3082  aPart->setName(KMMsgBase::decodeRFC2047String(headers.
3083  ContentType().Name().c_str()) );
3084  } else if (headers.HasSubject() && !headers.Subject().AsString().empty()) {
3085  aPart->setName( KMMsgBase::decodeRFC2047String(headers.
3086  Subject().AsString().c_str()) );
3087  }
3088  }
3089 
3090  // Content-transfer-encoding
3091  if (headers.HasContentTransferEncoding())
3092  aPart->setCteStr(headers.ContentTransferEncoding().AsString().c_str());
3093  else
3094  aPart->setCteStr("7bit");
3095 
3096  // Content-description
3097  if (headers.HasContentDescription())
3098  aPart->setContentDescription( KMMsgBase::decodeRFC2047String(
3099  headers.ContentDescription().AsString().c_str() ) );
3100  else
3101  aPart->setContentDescription("");
3102 
3103  // Content-disposition
3104  if (headers.HasContentDisposition())
3105  aPart->setContentDisposition(headers.ContentDisposition().AsString().c_str());
3106  else
3107  aPart->setContentDisposition("");
3108 }
3109 
3110 //-----------------------------------------------------------------------------
3111 void KMMessage::bodyPart(DwBodyPart* aDwBodyPart, KMMessagePart* aPart,
3112  bool withBody)
3113 {
3114  if ( !aPart )
3115  return;
3116 
3117  aPart->clear();
3118 
3119  if( aDwBodyPart && aDwBodyPart->hasHeaders() ) {
3120  // This must not be an empty string, because we'll get a
3121  // spurious empty Subject: line in some of the parts.
3122  //aPart->setName(" ");
3123  // partSpecifier
3124  TQString partId( aDwBodyPart->partId() );
3125  aPart->setPartSpecifier( partId );
3126 
3127  DwHeaders& headers = aDwBodyPart->Headers();
3128  applyHeadersToMessagePart( headers, aPart );
3129 
3130  // Body
3131  if (withBody)
3132  aPart->setBody( aDwBodyPart->Body().AsString() );
3133  else
3134  aPart->setBody( TQCString("") );
3135 
3136  // Content-id
3137  if ( headers.HasContentId() ) {
3138  const TQCString contentId = headers.ContentId().AsString().c_str();
3139  // ignore leading '<' and trailing '>'
3140  aPart->setContentId( contentId.mid( 1, contentId.length() - 2 ) );
3141  }
3142  }
3143  // If no valid body part was given,
3144  // set all MultipartBodyPart attributes to empty values.
3145  else
3146  {
3147  aPart->setTypeStr("");
3148  aPart->setSubtypeStr("");
3149  aPart->setCteStr("");
3150  // This must not be an empty string, because we'll get a
3151  // spurious empty Subject: line in some of the parts.
3152  //aPart->setName(" ");
3153  aPart->setContentDescription("");
3154  aPart->setContentDisposition("");
3155  aPart->setBody(TQCString(""));
3156  aPart->setContentId("");
3157  }
3158 }
3159 
3160 
3161 //-----------------------------------------------------------------------------
3162 void KMMessage::bodyPart(int aIdx, KMMessagePart* aPart) const
3163 {
3164  if ( !aPart )
3165  return;
3166 
3167  // If the DwBodyPart was found get the header fields and body
3168  if ( DwBodyPart *part = dwBodyPart( aIdx ) ) {
3169  KMMessage::bodyPart(part, aPart);
3170  if( aPart->name().isEmpty() )
3171  aPart->setName( i18n("Attachment: %1").arg( aIdx ) );
3172  }
3173 }
3174 
3175 
3176 //-----------------------------------------------------------------------------
3178 {
3179  mMsg->Body().DeleteBodyParts();
3180 }
3181 
3182 //-----------------------------------------------------------------------------
3183 
3184 bool KMMessage::deleteBodyPart( int partIndex )
3185 {
3186  KMMessagePart part;
3187  DwBodyPart *dwpart = findPart( partIndex );
3188  if ( !dwpart )
3189  return false;
3190  KMMessage::bodyPart( dwpart, &part, true );
3191  if ( !part.isComplete() )
3192  return false;
3193 
3194  DwBody *parentNode = dynamic_cast<DwBody*>( dwpart->Parent() );
3195  if ( !parentNode )
3196  return false;
3197  parentNode->RemoveBodyPart( dwpart );
3198 
3199  // add dummy part to show that a attachment has been deleted
3200  KMMessagePart dummyPart;
3201  dummyPart.duplicate( part );
3202  TQString comment = i18n("This attachment has been deleted.");
3203  if ( !part.fileName().isEmpty() )
3204  comment = i18n( "The attachment '%1' has been deleted." ).arg( part.fileName() );
3205  dummyPart.setContentDescription( comment );
3206  dummyPart.setBodyEncodedBinary( TQByteArray() );
3207  TQCString cd = dummyPart.contentDisposition();
3208  if ( cd.find( "inline", 0, false ) == 0 ) {
3209  cd.replace( 0, 10, "attachment" );
3210  dummyPart.setContentDisposition( cd );
3211  } else if ( cd.isEmpty() ) {
3212  dummyPart.setContentDisposition( "attachment" );
3213  }
3214  DwBodyPart* newDwPart = createDWBodyPart( &dummyPart );
3215  parentNode->AddBodyPart( newDwPart );
3216  getTopLevelPart()->Assemble();
3217  return true;
3218 }
3219 
3220 //-----------------------------------------------------------------------------
3221 DwBodyPart* KMMessage::createDWBodyPart(const KMMessagePart* aPart)
3222 {
3223  DwBodyPart* part = DwBodyPart::NewBodyPart(emptyString, 0);
3224 
3225  if ( !aPart )
3226  return part;
3227 
3228  TQCString charset = aPart->charset();
3229  TQCString type = aPart->typeStr();
3230  TQCString subtype = aPart->subtypeStr();
3231  TQCString cte = aPart->cteStr();
3232  TQCString contDesc = aPart->contentDescriptionEncoded();
3233  TQCString contDisp = aPart->contentDisposition();
3234  TQCString name = KMMsgBase::encodeRFC2231StringAutoDetectCharset( aPart->name(), charset );
3235  bool RFC2231encoded = aPart->name() != TQString(name);
3236  TQCString paramAttr = aPart->parameterAttribute();
3237 
3238  DwHeaders& headers = part->Headers();
3239 
3240  DwMediaType& ct = headers.ContentType();
3241  if (!type.isEmpty() && !subtype.isEmpty())
3242  {
3243  ct.SetTypeStr(type.data());
3244  ct.SetSubtypeStr(subtype.data());
3245  if (!charset.isEmpty()){
3246  DwParameter *param;
3247  param=new DwParameter;
3248  param->SetAttribute("charset");
3249  param->SetValue(charset.data());
3250  ct.AddParameter(param);
3251  }
3252  }
3253 
3254  TQCString additionalParam = aPart->additionalCTypeParamStr();
3255  if( !additionalParam.isEmpty() )
3256  {
3257  TQCString parAV;
3258  DwString parA, parV;
3259  int iL, i1, i2, iM;
3260  iL = additionalParam.length();
3261  i1 = 0;
3262  i2 = additionalParam.find(';', i1, false);
3263  while ( i1 < iL )
3264  {
3265  if( -1 == i2 )
3266  i2 = iL;
3267  if( i1+1 < i2 ) {
3268  parAV = additionalParam.mid( i1, (i2-i1) );
3269  iM = parAV.find('=');
3270  if( -1 < iM )
3271  {
3272  parA = parAV.left( iM ).data();
3273  parV = parAV.right( parAV.length() - iM - 1 ).data();
3274  if( ('"' == parV.at(0)) && ('"' == parV.at(parV.length()-1)) )
3275  {
3276  parV.erase( 0, 1);
3277  parV.erase( parV.length()-1 );
3278  }
3279  }
3280  else
3281  {
3282  parA = parAV.data();
3283  parV = "";
3284  }
3285  DwParameter *param;
3286  param = new DwParameter;
3287  param->SetAttribute( parA );
3288  param->SetValue( parV );
3289  ct.AddParameter( param );
3290  }
3291  i1 = i2+1;
3292  i2 = additionalParam.find(';', i1, false);
3293  }
3294  }
3295 
3296  if ( !name.isEmpty() ) {
3297  if (RFC2231encoded)
3298  {
3299  DwParameter *nameParam;
3300  nameParam = new DwParameter;
3301  nameParam->SetAttribute("name*");
3302  nameParam->SetValue(name.data(),true);
3303  ct.AddParameter(nameParam);
3304  } else {
3305  ct.SetName(name.data());
3306  }
3307  }
3308 
3309  if (!paramAttr.isEmpty())
3310  {
3311  TQCString paramValue;
3312  paramValue = KMMsgBase::encodeRFC2231StringAutoDetectCharset( aPart->parameterValue(), charset );
3313  DwParameter *param = new DwParameter;
3314  if (aPart->parameterValue() != TQString(paramValue))
3315  {
3316  param->SetAttribute((paramAttr + '*').data());
3317  param->SetValue(paramValue.data(),true);
3318  } else {
3319  param->SetAttribute(paramAttr.data());
3320  param->SetValue(paramValue.data());
3321  }
3322  ct.AddParameter(param);
3323  }
3324 
3325  if (!cte.isEmpty())
3326  headers.Cte().FromString(cte);
3327 
3328  if (!contDesc.isEmpty())
3329  headers.ContentDescription().FromString(contDesc);
3330 
3331  if (!contDisp.isEmpty())
3332  headers.ContentDisposition().FromString(contDisp);
3333 
3334  const DwString bodyStr = aPart->dwBody();
3335  if (!bodyStr.empty())
3336  part->Body().FromString(bodyStr);
3337  else
3338  part->Body().FromString("");
3339 
3340  if (!aPart->partSpecifier().isNull())
3341  part->SetPartId( aPart->partSpecifier().latin1() );
3342 
3343  if (aPart->decodedSize() > 0)
3344  part->SetBodySize( aPart->decodedSize() );
3345 
3346  return part;
3347 }
3348 
3349 
3350 //-----------------------------------------------------------------------------
3351 void KMMessage::addDwBodyPart(DwBodyPart * aDwPart)
3352 {
3353  mMsg->Body().AddBodyPart( aDwPart );
3354  mNeedsAssembly = true;
3355 }
3356 
3357 
3358 //-----------------------------------------------------------------------------
3359 void KMMessage::addBodyPart(const KMMessagePart* aPart)
3360 {
3361  DwBodyPart* part = createDWBodyPart( aPart );
3362  addDwBodyPart( part );
3363 }
3364 
3365 
3366 //-----------------------------------------------------------------------------
3367 TQString KMMessage::generateMessageId( const TQString& addr )
3368 {
3369  TQDateTime datetime = TQDateTime::currentDateTime();
3370  TQString msgIdStr;
3371 
3372  msgIdStr = '<' + datetime.toString( "yyyyMMddhhmm.sszzz" );
3373 
3374  TQString msgIdSuffix;
3375  TDEConfigGroup general( KMKernel::config(), "General" );
3376 
3377  if( general.readBoolEntry( "useCustomMessageIdSuffix", false ) )
3378  msgIdSuffix = general.readEntry( "myMessageIdSuffix" );
3379 
3380  if( !msgIdSuffix.isEmpty() )
3381  msgIdStr += '@' + msgIdSuffix;
3382  else
3383  msgIdStr += '.' + KPIM::encodeIDN( addr );
3384 
3385  msgIdStr += '>';
3386 
3387  return msgIdStr;
3388 }
3389 
3390 
3391 //-----------------------------------------------------------------------------
3392 TQCString KMMessage::html2source( const TQCString & src )
3393 {
3394  TQCString result( 1 + 6*(src.size()-1) ); // maximal possible length
3395 
3396  TQCString::ConstIterator s = src.begin();
3397  TQCString::Iterator d = result.begin();
3398  while ( *s ) {
3399  switch ( *s ) {
3400  case '<': {
3401  *d++ = '&';
3402  *d++ = 'l';
3403  *d++ = 't';
3404  *d++ = ';';
3405  ++s;
3406  }
3407  break;
3408  case '\r': {
3409  ++s;
3410  }
3411  break;
3412  case '\n': {
3413  *d++ = '<';
3414  *d++ = 'b';
3415  *d++ = 'r';
3416  *d++ = '>';
3417  ++s;
3418  }
3419  break;
3420  case '>': {
3421  *d++ = '&';
3422  *d++ = 'g';
3423  *d++ = 't';
3424  *d++ = ';';
3425  ++s;
3426  }
3427  break;
3428  case '&': {
3429  *d++ = '&';
3430  *d++ = 'a';
3431  *d++ = 'm';
3432  *d++ = 'p';
3433  *d++ = ';';
3434  ++s;
3435  }
3436  break;
3437  case '"': {
3438  *d++ = '&';
3439  *d++ = 'q';
3440  *d++ = 'u';
3441  *d++ = 'o';
3442  *d++ = 't';
3443  *d++ = ';';
3444  ++s;
3445  }
3446  break;
3447  case '\'': {
3448  *d++ = '&';
3449  *d++ = 'a';
3450  *d++ = 'p';
3451  *d++ = 's';
3452  *d++ = ';';
3453  ++s;
3454  }
3455  break;
3456  default:
3457  *d++ = *s++;
3458  }
3459  }
3460  result.truncate( d - result.begin() ); // adds trailing NUL
3461  return result;
3462 }
3463 
3464 //-----------------------------------------------------------------------------
3465 TQString KMMessage::encodeMailtoUrl( const TQString& str )
3466 {
3467  TQString result;
3468  result = TQString::fromLatin1( KMMsgBase::encodeRFC2047String( str,
3469  "utf-8" ) );
3470  result = KURL::encode_string( result );
3471  return result;
3472 }
3473 
3474 
3475 //-----------------------------------------------------------------------------
3476 TQString KMMessage::decodeMailtoUrl( const TQString& url )
3477 {
3478  TQString result;
3479  result = KURL::decode_string( url );
3480  result = KMMsgBase::decodeRFC2047String( result.latin1() );
3481  return result;
3482 }
3483 
3484 
3485 //-----------------------------------------------------------------------------
3486 TQCString KMMessage::stripEmailAddr( const TQCString& aStr )
3487 {
3488  //kdDebug(5006) << "KMMessage::stripEmailAddr( " << aStr << " )" << endl;
3489 
3490  if ( aStr.isEmpty() )
3491  return TQCString();
3492 
3493  TQCString result;
3494 
3495  // The following is a primitive parser for a mailbox-list (cf. RFC 2822).
3496  // The purpose is to extract a displayable string from the mailboxes.
3497  // Comments in the addr-spec are not handled. No error checking is done.
3498 
3499  TQCString name;
3500  TQCString comment;
3501  TQCString angleAddress;
3502  enum { TopLevel, InComment, InAngleAddress } context = TopLevel;
3503  bool inQuotedString = false;
3504  int commentLevel = 0;
3505 
3506  for ( const char* p = aStr.data(); *p; ++p ) {
3507  switch ( context ) {
3508  case TopLevel : {
3509  switch ( *p ) {
3510  case '"' : inQuotedString = !inQuotedString;
3511  break;
3512  case '(' : if ( !inQuotedString ) {
3513  context = InComment;
3514  commentLevel = 1;
3515  }
3516  else
3517  name += *p;
3518  break;
3519  case '<' : if ( !inQuotedString ) {
3520  context = InAngleAddress;
3521  }
3522  else
3523  name += *p;
3524  break;
3525  case '\\' : // quoted character
3526  ++p; // skip the '\'
3527  if ( *p )
3528  name += *p;
3529  break;
3530  case ',' : if ( !inQuotedString ) {
3531  // next email address
3532  if ( !result.isEmpty() )
3533  result += ", ";
3534  name = name.stripWhiteSpace();
3535  comment = comment.stripWhiteSpace();
3536  angleAddress = angleAddress.stripWhiteSpace();
3537  /*
3538  kdDebug(5006) << "Name : \"" << name
3539  << "\"" << endl;
3540  kdDebug(5006) << "Comment : \"" << comment
3541  << "\"" << endl;
3542  kdDebug(5006) << "Address : \"" << angleAddress
3543  << "\"" << endl;
3544  */
3545  if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
3546  // handle Outlook-style addresses like
3547  // john.doe@invalid (John Doe)
3548  result += comment;
3549  }
3550  else if ( !name.isEmpty() ) {
3551  result += name;
3552  }
3553  else if ( !comment.isEmpty() ) {
3554  result += comment;
3555  }
3556  else if ( !angleAddress.isEmpty() ) {
3557  result += angleAddress;
3558  }
3559  name = TQCString();
3560  comment = TQCString();
3561  angleAddress = TQCString();
3562  }
3563  else
3564  name += *p;
3565  break;
3566  default : name += *p;
3567  }
3568  break;
3569  }
3570  case InComment : {
3571  switch ( *p ) {
3572  case '(' : ++commentLevel;
3573  comment += *p;
3574  break;
3575  case ')' : --commentLevel;
3576  if ( commentLevel == 0 ) {
3577  context = TopLevel;
3578  comment += ' '; // separate the text of several comments
3579  }
3580  else
3581  comment += *p;
3582  break;
3583  case '\\' : // quoted character
3584  ++p; // skip the '\'
3585  if ( *p )
3586  comment += *p;
3587  break;
3588  default : comment += *p;
3589  }
3590  break;
3591  }
3592  case InAngleAddress : {
3593  switch ( *p ) {
3594  case '"' : inQuotedString = !inQuotedString;
3595  angleAddress += *p;
3596  break;
3597  case '>' : if ( !inQuotedString ) {
3598  context = TopLevel;
3599  }
3600  else
3601  angleAddress += *p;
3602  break;
3603  case '\\' : // quoted character
3604  ++p; // skip the '\'
3605  if ( *p )
3606  angleAddress += *p;
3607  break;
3608  default : angleAddress += *p;
3609  }
3610  break;
3611  }
3612  } // switch ( context )
3613  }
3614  if ( !result.isEmpty() )
3615  result += ", ";
3616  name = name.stripWhiteSpace();
3617  comment = comment.stripWhiteSpace();
3618  angleAddress = angleAddress.stripWhiteSpace();
3619  /*
3620  kdDebug(5006) << "Name : \"" << name << "\"" << endl;
3621  kdDebug(5006) << "Comment : \"" << comment << "\"" << endl;
3622  kdDebug(5006) << "Address : \"" << angleAddress << "\"" << endl;
3623  */
3624  if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
3625  // handle Outlook-style addresses like
3626  // john.doe@invalid (John Doe)
3627  result += comment;
3628  }
3629  else if ( !name.isEmpty() ) {
3630  result += name;
3631  }
3632  else if ( !comment.isEmpty() ) {
3633  result += comment;
3634  }
3635  else if ( !angleAddress.isEmpty() ) {
3636  result += angleAddress;
3637  }
3638 
3639  //kdDebug(5006) << "KMMessage::stripEmailAddr(...) returns \"" << result
3640  // << "\"" << endl;
3641  return result;
3642 }
3643 
3644 //-----------------------------------------------------------------------------
3645 TQString KMMessage::stripEmailAddr( const TQString& aStr )
3646 {
3647  //kdDebug(5006) << "KMMessage::stripEmailAddr( " << aStr << " )" << endl;
3648 
3649  if ( aStr.isEmpty() )
3650  return TQString();
3651 
3652  TQString result;
3653 
3654  // The following is a primitive parser for a mailbox-list (cf. RFC 2822).
3655  // The purpose is to extract a displayable string from the mailboxes.
3656  // Comments in the addr-spec are not handled. No error checking is done.
3657 
3658  TQString name;
3659  TQString comment;
3660  TQString angleAddress;
3661  enum { TopLevel, InComment, InAngleAddress } context = TopLevel;
3662  bool inQuotedString = false;
3663  int commentLevel = 0;
3664 
3665  TQChar ch;
3666  unsigned int strLength(aStr.length());
3667  for ( uint index = 0; index < strLength; ++index ) {
3668  ch = aStr[index];
3669  switch ( context ) {
3670  case TopLevel : {
3671  switch ( ch.latin1() ) {
3672  case '"' : inQuotedString = !inQuotedString;
3673  break;
3674  case '(' : if ( !inQuotedString ) {
3675  context = InComment;
3676  commentLevel = 1;
3677  }
3678  else
3679  name += ch;
3680  break;
3681  case '<' : if ( !inQuotedString ) {
3682  context = InAngleAddress;
3683  }
3684  else
3685  name += ch;
3686  break;
3687  case '\\' : // quoted character
3688  ++index; // skip the '\'
3689  if ( index < aStr.length() )
3690  name += aStr[index];
3691  break;
3692  case ',' : if ( !inQuotedString ) {
3693  // next email address
3694  if ( !result.isEmpty() )
3695  result += ", ";
3696  name = name.stripWhiteSpace();
3697  comment = comment.stripWhiteSpace();
3698  angleAddress = angleAddress.stripWhiteSpace();
3699  /*
3700  kdDebug(5006) << "Name : \"" << name
3701  << "\"" << endl;
3702  kdDebug(5006) << "Comment : \"" << comment
3703  << "\"" << endl;
3704  kdDebug(5006) << "Address : \"" << angleAddress
3705  << "\"" << endl;
3706  */
3707  if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
3708  // handle Outlook-style addresses like
3709  // john.doe@invalid (John Doe)
3710  result += comment;
3711  }
3712  else if ( !name.isEmpty() ) {
3713  result += name;
3714  }
3715  else if ( !comment.isEmpty() ) {
3716  result += comment;
3717  }
3718  else if ( !angleAddress.isEmpty() ) {
3719  result += angleAddress;
3720  }
3721  name = TQString();
3722  comment = TQString();
3723  angleAddress = TQString();
3724  }
3725  else
3726  name += ch;
3727  break;
3728  default : name += ch;
3729  }
3730  break;
3731  }
3732  case InComment : {
3733  switch ( ch.latin1() ) {
3734  case '(' : ++commentLevel;
3735  comment += ch;
3736  break;
3737  case ')' : --commentLevel;
3738  if ( commentLevel == 0 ) {
3739  context = TopLevel;
3740  comment += ' '; // separate the text of several comments
3741  }
3742  else
3743  comment += ch;
3744  break;
3745  case '\\' : // quoted character
3746  ++index; // skip the '\'
3747  if ( index < aStr.length() )
3748  comment += aStr[index];
3749  break;
3750  default : comment += ch;
3751  }
3752  break;
3753  }
3754  case InAngleAddress : {
3755  switch ( ch.latin1() ) {
3756  case '"' : inQuotedString = !inQuotedString;
3757  angleAddress += ch;
3758  break;
3759  case '>' : if ( !inQuotedString ) {
3760  context = TopLevel;
3761  }
3762  else
3763  angleAddress += ch;
3764  break;
3765  case '\\' : // quoted character
3766  ++index; // skip the '\'
3767  if ( index < aStr.length() )
3768  angleAddress += aStr[index];
3769  break;
3770  default : angleAddress += ch;
3771  }
3772  break;
3773  }
3774  } // switch ( context )
3775  }
3776  if ( !result.isEmpty() )
3777  result += ", ";
3778  name = name.stripWhiteSpace();
3779  comment = comment.stripWhiteSpace();
3780  angleAddress = angleAddress.stripWhiteSpace();
3781  /*
3782  kdDebug(5006) << "Name : \"" << name << "\"" << endl;
3783  kdDebug(5006) << "Comment : \"" << comment << "\"" << endl;
3784  kdDebug(5006) << "Address : \"" << angleAddress << "\"" << endl;
3785  */
3786  if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
3787  // handle Outlook-style addresses like
3788  // john.doe@invalid (John Doe)
3789  result += comment;
3790  }
3791  else if ( !name.isEmpty() ) {
3792  result += name;
3793  }
3794  else if ( !comment.isEmpty() ) {
3795  result += comment;
3796  }
3797  else if ( !angleAddress.isEmpty() ) {
3798  result += angleAddress;
3799  }
3800 
3801  //kdDebug(5006) << "KMMessage::stripEmailAddr(...) returns \"" << result
3802  // << "\"" << endl;
3803  return result;
3804 }
3805 
3806 //-----------------------------------------------------------------------------
3807 TQString KMMessage::quoteHtmlChars( const TQString& str, bool removeLineBreaks )
3808 {
3809  TQString result;
3810 
3811  unsigned int strLength(str.length());
3812  result.reserve( 6*strLength ); // maximal possible length
3813  for( unsigned int i = 0; i < strLength; ++i )
3814  switch ( str[i].latin1() ) {
3815  case '<':
3816  result += "&lt;";
3817  break;
3818  case '>':
3819  result += "&gt;";
3820  break;
3821  case '&':
3822  result += "&amp;";
3823  break;
3824  case '"':
3825  result += "&quot;";
3826  break;
3827  case '\n':
3828  if ( !removeLineBreaks )
3829  result += "<br>";
3830  break;
3831  case '\r':
3832  // ignore CR
3833  break;
3834  default:
3835  result += str[i];
3836  }
3837 
3838  result.squeeze();
3839  return result;
3840 }
3841 
3842 //-----------------------------------------------------------------------------
3843 TQString KMMessage::emailAddrAsAnchor(const TQString& aEmail, bool stripped, const TQString& cssStyle, bool aLink)
3844 {
3845  if( aEmail.isEmpty() )
3846  return aEmail;
3847 
3848  TQStringList addressList = KPIM::splitEmailAddrList( aEmail );
3849  TQString result;
3850 
3851  for( TQStringList::ConstIterator it = addressList.begin();
3852  ( it != addressList.end() );
3853  ++it ) {
3854  if( !(*it).isEmpty() ) {
3855 
3856  // Extract the name, mail and some pretty versions out of the mail address
3857  TQString name, mail;
3858  KPIM::getNameAndMail( *it, name, mail );
3859  TQString pretty;
3860  TQString prettyStripped;
3861  if ( name.stripWhiteSpace().isEmpty() ) {
3862  pretty = mail;
3863  prettyStripped = mail;
3864  } else {
3865  pretty = KPIM::quoteNameIfNecessary( name ) + " <" + mail + ">";
3866  prettyStripped = name;
3867  }
3868 
3869  if(aLink) {
3870  result += "<a href=\"mailto:"
3871  + KMMessage::encodeMailtoUrl( pretty )
3872  + "\" "+cssStyle+">";
3873  }
3874 
3875  if ( stripped ) {
3876  result += KMMessage::quoteHtmlChars( prettyStripped, true );
3877  }
3878  else {
3879  result += KMMessage::quoteHtmlChars( pretty, true );
3880  }
3881 
3882  if(aLink)
3883  result += "</a>, ";
3884  }
3885  }
3886  // cut of the trailing ", "
3887  if(aLink)
3888  result.truncate( result.length() - 2 );
3889 
3890  //kdDebug(5006) << "KMMessage::emailAddrAsAnchor('" << aEmail
3891  // << "') returns:\n-->" << result << "<--" << endl;
3892  return result;
3893 }
3894 
3895 //-----------------------------------------------------------------------------
3896 //static
3897 TQStringList KMMessage::stripAddressFromAddressList( const TQString& address,
3898  const TQStringList& list )
3899 {
3900  TQStringList addresses( list );
3901  TQString addrSpec( KPIM::getEmailAddress( address ) );
3902  for ( TQStringList::Iterator it = addresses.begin();
3903  it != addresses.end(); ) {
3904  if ( kasciistricmp( addrSpec.utf8().data(),
3905  KPIM::getEmailAddress( *it ).utf8().data() ) == 0 ) {
3906  kdDebug(5006) << "Removing " << *it << " from the address list"
3907  << endl;
3908  it = addresses.remove( it );
3909  }
3910  else
3911  ++it;
3912  }
3913  return addresses;
3914 }
3915 
3916 
3917 //-----------------------------------------------------------------------------
3918 //static
3919 TQStringList KMMessage::stripMyAddressesFromAddressList( const TQStringList& list )
3920 {
3921  TQStringList addresses = list;
3922  for( TQStringList::Iterator it = addresses.begin();
3923  it != addresses.end(); ) {
3924  kdDebug(5006) << "Check whether " << *it << " is one of my addresses"
3925  << endl;
3926  if( kmkernel->identityManager()->thatIsMe( KPIM::getEmailAddress( *it ) ) ) {
3927  kdDebug(5006) << "Removing " << *it << " from the address list"
3928  << endl;
3929  it = addresses.remove( it );
3930  }
3931  else
3932  ++it;
3933  }
3934  return addresses;
3935 }
3936 
3937 
3938 //-----------------------------------------------------------------------------
3939 //static
3940 bool KMMessage::addressIsInAddressList( const TQString& address,
3941  const TQStringList& addresses )
3942 {
3943  TQString addrSpec = KPIM::getEmailAddress( address );
3944  for( TQStringList::ConstIterator it = addresses.begin();
3945  it != addresses.end(); ++it ) {
3946  if ( kasciistricmp( addrSpec.utf8().data(),
3947  KPIM::getEmailAddress( *it ).utf8().data() ) == 0 )
3948  return true;
3949  }
3950  return false;
3951 }
3952 
3953 
3954 //-----------------------------------------------------------------------------
3955 //static
3956 TQString KMMessage::expandAliases( const TQString& recipients )
3957 {
3958  if ( recipients.isEmpty() )
3959  return TQString();
3960 
3961  TQStringList recipientList = KPIM::splitEmailAddrList( recipients );
3962 
3963  TQString expandedRecipients;
3964  for ( TQStringList::Iterator it = recipientList.begin();
3965  it != recipientList.end(); ++it ) {
3966  if ( !expandedRecipients.isEmpty() )
3967  expandedRecipients += ", ";
3968  TQString receiver = (*it).stripWhiteSpace();
3969 
3970  // try to expand distribution list
3971  TQString expandedList = KAddrBookExternal::expandDistributionList( receiver );
3972  if ( !expandedList.isEmpty() ) {
3973  expandedRecipients += expandedList;
3974  continue;
3975  }
3976 
3977  // try to expand nick name
3978  TQString expandedNickName = KabcBridge::expandNickName( receiver );
3979  if ( !expandedNickName.isEmpty() ) {
3980  expandedRecipients += expandedNickName;
3981  continue;
3982  }
3983 
3984  // check whether the address is missing the domain part
3985  // FIXME: looking for '@' might be wrong
3986  if ( receiver.find('@') == -1 ) {
3987  TDEConfigGroup general( KMKernel::config(), "General" );
3988  TQString defaultdomain = general.readEntry( "Default domain" );
3989  if( !defaultdomain.isEmpty() ) {
3990  expandedRecipients += receiver + "@" + defaultdomain;
3991  }
3992  else {
3993  expandedRecipients += guessEmailAddressFromLoginName( receiver );
3994  }
3995  }
3996  else
3997  expandedRecipients += receiver;
3998  }
3999 
4000  return expandedRecipients;
4001 }
4002 
4003 
4004 //-----------------------------------------------------------------------------
4005 //static
4006 TQString KMMessage::guessEmailAddressFromLoginName( const TQString& loginName )
4007 {
4008  if ( loginName.isEmpty() )
4009  return TQString();
4010 
4011  char hostnameC[256];
4012  // null terminate this C string
4013  hostnameC[255] = '\0';
4014  // set the string to 0 length if gethostname fails
4015  if ( gethostname( hostnameC, 255 ) )
4016  hostnameC[0] = '\0';
4017  TQString address = loginName;
4018  address += '@';
4019  address += TQString::fromLocal8Bit( hostnameC );
4020 
4021  // try to determine the real name
4022  const KUser user( loginName );
4023  if ( user.isValid() ) {
4024  TQString fullName = user.fullName();
4025  if ( fullName.find( TQRegExp( "[^ 0-9A-Za-z\\x0080-\\xFFFF]" ) ) != -1 )
4026  address = '"' + fullName.replace( '\\', "\\" ).replace( '"', "\\" )
4027  + "\" <" + address + '>';
4028  else
4029  address = fullName + " <" + address + '>';
4030  }
4031 
4032  return address;
4033 }
4034 
4035 //-----------------------------------------------------------------------------
4037 {
4038  KMMsgBase::readConfig();
4039 
4040  TDEConfig *config=KMKernel::config();
4041  TDEConfigGroupSaver saver(config, "General");
4042 
4043  config->setGroup("General");
4044 
4045  int languageNr = config->readNumEntry("reply-current-language",0);
4046 
4047  { // area for config group "KMMessage #n"
4048  TDEConfigGroupSaver saver(config, TQString("KMMessage #%1").arg(languageNr));
4049  sReplyLanguage = config->readEntry("language",TDEGlobal::locale()->language());
4050  sReplyStr = config->readEntry("phrase-reply",
4051  i18n("On %D, you wrote:"));
4052  sReplyAllStr = config->readEntry("phrase-reply-all",
4053  i18n("On %D, %F wrote:"));
4054  sForwardStr = config->readEntry("phrase-forward",
4055  i18n("Forwarded Message"));
4056  sIndentPrefixStr = config->readEntry("indent-prefix",">%_");
4057  }
4058 
4059  { // area for config group "Composer"
4060  TDEConfigGroupSaver saver(config, "Composer");
4061  sSmartQuote = GlobalSettings::self()->smartQuote();
4062  sWordWrap = GlobalSettings::self()->wordWrap();
4063  sWrapCol = GlobalSettings::self()->lineWrapWidth();
4064  if ((sWrapCol == 0) || (sWrapCol > 78))
4065  sWrapCol = 78;
4066  if (sWrapCol < 30)
4067  sWrapCol = 30;
4068 
4069  sPrefCharsets = config->readListEntry("pref-charsets");
4070  }
4071 
4072  { // area for config group "Reader"
4073  TDEConfigGroupSaver saver(config, "Reader");
4074  sHeaderStrategy = HeaderStrategy::create( config->readEntry( "header-set-displayed", "rich" ) );
4075  }
4076 }
4077 
4079 {
4080  TQCString retval;
4081 
4082  if (!sPrefCharsets.isEmpty())
4083  retval = sPrefCharsets[0].latin1();
4084 
4085  if (retval.isEmpty() || (retval == "locale")) {
4086  retval = TQCString(kmkernel->networkCodec()->mimeName());
4087  KPIM::kAsciiToLower( retval.data() );
4088  }
4089 
4090  if (retval == "jisx0208.1983-0") retval = "iso-2022-jp";
4091  else if (retval == "ksc5601.1987-0") retval = "euc-kr";
4092  return retval;
4093 }
4094 
4095 const TQStringList &KMMessage::preferredCharsets()
4096 {
4097  return sPrefCharsets;
4098 }
4099 
4100 //-----------------------------------------------------------------------------
4101 TQCString KMMessage::charset() const
4102 {
4103  if ( mMsg->Headers().HasContentType() ) {
4104  DwMediaType &mType=mMsg->Headers().ContentType();
4105  mType.Parse();
4106  DwParameter *param=mType.FirstParameter();
4107  while(param){
4108  if (!kasciistricmp(param->Attribute().c_str(), "charset"))
4109  return param->Value().c_str();
4110  else param=param->Next();
4111  }
4112  }
4113  return ""; // us-ascii, but we don't have to specify it
4114 }
4115 
4116 //-----------------------------------------------------------------------------
4117 void KMMessage::setCharset( const TQCString &charset, DwEntity *entity )
4118 {
4119  kdWarning( type() != DwMime::kTypeText )
4120  << "KMMessage::setCharset(): trying to set a charset for a non-textual mimetype." << endl
4121  << "Fix this caller:" << endl
4122  << "====================================================================" << endl
4123  << kdBacktrace( 5 ) << endl
4124  << "====================================================================" << endl;
4125 
4126  if ( !entity )
4127  entity = mMsg;
4128 
4129  DwMediaType &mType = entity->Headers().ContentType();
4130  mType.Parse();
4131  DwParameter *param = mType.FirstParameter();
4132  while( param ) {
4133 
4134  // FIXME use the mimelib functions here for comparison.
4135  if ( !kasciistricmp( param->Attribute().c_str(), "charset" ) )
4136  break;
4137 
4138  param = param->Next();
4139  }
4140  if ( !param ) {
4141  param = new DwParameter;
4142  param->SetAttribute( "charset" );
4143  mType.AddParameter( param );
4144  }
4145  else
4146  mType.SetModified();
4147 
4148  TQCString lowerCharset = charset;
4149  KPIM::kAsciiToLower( lowerCharset.data() );
4150  param->SetValue( DwString( lowerCharset ) );
4151  mType.Assemble();
4152 }
4153 
4154 
4155 //-----------------------------------------------------------------------------
4156 void KMMessage::setStatus(const KMMsgStatus aStatus, int idx)
4157 {
4158  if (mStatus == aStatus)
4159  return;
4160  KMMsgBase::setStatus(aStatus, idx);
4161 }
4162 
4163 void KMMessage::setEncryptionState(const KMMsgEncryptionState s, int idx)
4164 {
4165  if( mEncryptionState == s )
4166  return;
4167  mEncryptionState = s;
4168  mDirty = true;
4169  KMMsgBase::setEncryptionState(s, idx);
4170 }
4171 
4172 void KMMessage::setSignatureState(KMMsgSignatureState s, int idx)
4173 {
4174  if( mSignatureState == s )
4175  return;
4176  mSignatureState = s;
4177  mDirty = true;
4178  KMMsgBase::setSignatureState(s, idx);
4179 }
4180 
4181 void KMMessage::setMDNSentState( KMMsgMDNSentState status, int idx )
4182 {
4183  if ( mMDNSentState == status )
4184  return;
4185  if ( status == 0 )
4186  status = KMMsgMDNStateUnknown;
4187  mMDNSentState = status;
4188  mDirty = true;
4189  KMMsgBase::setMDNSentState( status, idx );
4190 }
4191 
4192 //-----------------------------------------------------------------------------
4193 void KMMessage::link( const KMMessage *aMsg, KMMsgStatus aStatus )
4194 {
4195  Q_ASSERT( aStatus == KMMsgStatusReplied
4196  || aStatus == KMMsgStatusForwarded
4197  || aStatus == KMMsgStatusDeleted );
4198 
4199  TQString message = headerField( "X-KMail-Link-Message" );
4200  if ( !message.isEmpty() )
4201  message += ',';
4202  TQString type = headerField( "X-KMail-Link-Type" );
4203  if ( !type.isEmpty() )
4204  type += ',';
4205 
4206  message += TQString::number( aMsg->getMsgSerNum() );
4207  if ( aStatus == KMMsgStatusReplied )
4208  type += "reply";
4209  else if ( aStatus == KMMsgStatusForwarded )
4210  type += "forward";
4211  else if ( aStatus == KMMsgStatusDeleted )
4212  type += "deleted";
4213 
4214  setHeaderField( "X-KMail-Link-Message", message );
4215  setHeaderField( "X-KMail-Link-Type", type );
4216 }
4217 
4218 //-----------------------------------------------------------------------------
4219 void KMMessage::getLink(int n, ulong *retMsgSerNum, KMMsgStatus *reStatus) const
4220 {
4221  *retMsgSerNum = 0;
4222  *reStatus = KMMsgStatusUnknown;
4223 
4224  TQString message = headerField("X-KMail-Link-Message");
4225  TQString type = headerField("X-KMail-Link-Type");
4226  message = message.section(',', n, n);
4227  type = type.section(',', n, n);
4228 
4229  if ( !message.isEmpty() && !type.isEmpty() ) {
4230  *retMsgSerNum = message.toULong();
4231  if ( type == "reply" )
4232  *reStatus = KMMsgStatusReplied;
4233  else if ( type == "forward" )
4234  *reStatus = KMMsgStatusForwarded;
4235  else if ( type == "deleted" )
4236  *reStatus = KMMsgStatusDeleted;
4237  }
4238 }
4239 
4240 //-----------------------------------------------------------------------------
4241 DwBodyPart* KMMessage::findDwBodyPart( DwBodyPart* part, const TQString & partSpecifier )
4242 {
4243  if ( !part ) return 0;
4244  DwBodyPart* current;
4245 
4246  if ( part->partId() == partSpecifier )
4247  return part;
4248 
4249  // multipart
4250  if ( part->hasHeaders() &&
4251  part->Headers().HasContentType() &&
4252  part->Body().FirstBodyPart() &&
4253  (DwMime::kTypeMultipart == part->Headers().ContentType().Type() ) &&
4254  (current = findDwBodyPart( part->Body().FirstBodyPart(), partSpecifier )) )
4255  {
4256  return current;
4257  }
4258 
4259  // encapsulated message
4260  if ( part->Body().Message() &&
4261  part->Body().Message()->Body().FirstBodyPart() &&
4262  (current = findDwBodyPart( part->Body().Message()->Body().FirstBodyPart(),
4263  partSpecifier )) )
4264  {
4265  return current;
4266  }
4267 
4268  // next part
4269  return findDwBodyPart( part->Next(), partSpecifier );
4270 }
4271 
4272 //-----------------------------------------------------------------------------
4273 void KMMessage::updateBodyPart(const TQString partSpecifier, const TQByteArray & data)
4274 {
4275  if ( !data.data() || !data.size() )
4276  return;
4277 
4278  DwString content( data.data(), data.size() );
4279  if ( numBodyParts() > 0 &&
4280  partSpecifier != "0" &&
4281  partSpecifier != "TEXT" )
4282  {
4283  TQString specifier = partSpecifier;
4284  if ( partSpecifier.endsWith(".HEADER") ||
4285  partSpecifier.endsWith(".MIME") ) {
4286  // get the parent bodypart
4287  specifier = partSpecifier.section( '.', 0, -2 );
4288  }
4289 
4290  // search for the bodypart
4291  mLastUpdated = findDwBodyPart( getFirstDwBodyPart(), specifier );
4292  kdDebug(5006) << "KMMessage::updateBodyPart " << specifier << endl;
4293  if (!mLastUpdated)
4294  {
4295  kdWarning(5006) << "KMMessage::updateBodyPart - can not find part "
4296  << specifier << endl;
4297  return;
4298  }
4299  if ( partSpecifier.endsWith(".MIME") )
4300  {
4301  // update headers
4302  // get rid of EOL
4303  content.resize( TQMAX( content.length(), 2 ) - 2 );
4304  // we have to delete the fields first as they might have been created by
4305  // an earlier call to DwHeaders::FieldBody
4306  mLastUpdated->Headers().DeleteAllFields();
4307  mLastUpdated->Headers().FromString( content );
4308  mLastUpdated->Headers().Parse();
4309  } else if ( partSpecifier.endsWith(".HEADER") )
4310  {
4311  // update header of embedded message
4312  mLastUpdated->Body().Message()->Headers().FromString( content );
4313  mLastUpdated->Body().Message()->Headers().Parse();
4314  } else {
4315  // update body
4316  mLastUpdated->Body().FromString( content );
4317  TQString parentSpec = partSpecifier.section( '.', 0, -2 );
4318  if ( !parentSpec.isEmpty() )
4319  {
4320  DwBodyPart* parent = findDwBodyPart( getFirstDwBodyPart(), parentSpec );
4321  if ( parent && parent->hasHeaders() && parent->Headers().HasContentType() )
4322  {
4323  const DwMediaType& contentType = parent->Headers().ContentType();
4324  if ( contentType.Type() == DwMime::kTypeMessage &&
4325  contentType.Subtype() == DwMime::kSubtypeRfc822 )
4326  {
4327  // an embedded message that is not multipart
4328  // update this directly
4329  parent->Body().Message()->Body().FromString( content );
4330  }
4331  }
4332  }
4333  }
4334 
4335  } else
4336  {
4337  // update text-only messages
4338  if ( partSpecifier == "TEXT" )
4339  deleteBodyParts(); // delete empty parts first
4340  mMsg->Body().FromString( content );
4341  mMsg->Body().Parse();
4342  }
4343  mNeedsAssembly = true;
4344  if (! partSpecifier.endsWith(".HEADER") )
4345  {
4346  // notify observers
4347  notify();
4348  }
4349 }
4350 
4351 void KMMessage::updateInvitationState()
4352 {
4353  if ( mMsg && mMsg->hasHeaders() && mMsg->Headers().HasContentType() ) {
4354  TQString cntType = mMsg->Headers().ContentType().TypeStr().c_str();
4355  cntType += '/';
4356  cntType += mMsg->Headers().ContentType().SubtypeStr().c_str();
4357  if ( cntType.lower() == "text/calendar" ) {
4358  setStatus( KMMsgStatusHasInvitation );
4359  return;
4360  }
4361  }
4362  setStatus( KMMsgStatusHasNoInvitation );
4363  return;
4364 }
4365 
4366 //-----------------------------------------------------------------------------
4367 void KMMessage::updateAttachmentState( DwBodyPart* part )
4368 {
4369  if ( !part )
4370  part = getFirstDwBodyPart();
4371 
4372  if ( !part )
4373  {
4374  // kdDebug(5006) << "updateAttachmentState - no part!" << endl;
4375  setStatus( KMMsgStatusHasNoAttach );
4376  return;
4377  }
4378 
4379  bool filenameEmpty = true;
4380  if ( part->hasHeaders() ) {
4381  if ( part->Headers().HasContentDisposition() ) {
4382  DwDispositionType cd = part->Headers().ContentDisposition();
4383  filenameEmpty = cd.Filename().empty();
4384  if ( filenameEmpty ) {
4385  // let's try if it is rfc 2231 encoded which mimelib can't handle
4386  filenameEmpty = KMMsgBase::decodeRFC2231String( KMMsgBase::extractRFC2231HeaderField( cd.AsString().c_str(), "filename" ) ).isEmpty();
4387  }
4388  }
4389 
4390  // Filename still empty? Check if the content-type has a "name" parameter and try to use that as
4391  // the attachment name
4392  if ( filenameEmpty && part->Headers().HasContentType() ) {
4393  DwMediaType contentType = part->Headers().ContentType();
4394  filenameEmpty = contentType.Name().empty();
4395  if ( filenameEmpty ) {
4396  // let's try if it is rfc 2231 encoded which mimelib can't handle
4397  filenameEmpty = KMMsgBase::decodeRFC2231String( KMMsgBase::extractRFC2231HeaderField(
4398  contentType.AsString().c_str(), "name" ) ).isEmpty();
4399  }
4400  }
4401  }
4402 
4403  if ( part->hasHeaders() &&
4404  ( ( part->Headers().HasContentDisposition() &&
4405  !part->Headers().ContentDisposition().Filename().empty() ) ||
4406  ( part->Headers().HasContentType() &&
4407  !filenameEmpty ) ) )
4408  {
4409  // now blacklist certain ContentTypes
4410  if ( !part->Headers().HasContentType() ||
4411  ( part->Headers().HasContentType() &&
4412  part->Headers().ContentType().Subtype() != DwMime::kSubtypePgpSignature &&
4413  part->Headers().ContentType().Subtype() != DwMime::kSubtypePkcs7Signature ) )
4414  {
4415  setStatus( KMMsgStatusHasAttach );
4416  }
4417  return;
4418  }
4419 
4420  // multipart
4421  if ( part->hasHeaders() &&
4422  part->Headers().HasContentType() &&
4423  part->Body().FirstBodyPart() &&
4424  (DwMime::kTypeMultipart == part->Headers().ContentType().Type() ) )
4425  {
4426  updateAttachmentState( part->Body().FirstBodyPart() );
4427  }
4428 
4429  // encapsulated message
4430  if ( part->Body().Message() &&
4431  part->Body().Message()->Body().FirstBodyPart() )
4432  {
4433  updateAttachmentState( part->Body().Message()->Body().FirstBodyPart() );
4434  }
4435 
4436  // next part
4437  if ( part->Next() )
4438  updateAttachmentState( part->Next() );
4439  else if ( attachmentState() == KMMsgAttachmentUnknown )
4440  setStatus( KMMsgStatusHasNoAttach );
4441 }
4442 
4443 void KMMessage::setBodyFromUnicode( const TQString &str, DwEntity *entity )
4444 {
4445  TQCString encoding = KMMsgBase::autoDetectCharset( charset(), KMMessage::preferredCharsets(), str );
4446  if ( encoding.isEmpty() )
4447  encoding = "utf-8";
4448  const TQTextCodec * codec = KMMsgBase::codecForName( encoding );
4449  assert( codec );
4450  TQValueList<int> dummy;
4451  setCharset( encoding, entity );
4452  setBodyAndGuessCte( codec->fromUnicode( str ), dummy, false /* no 8bit */,
4453  false, entity );
4454 }
4455 
4456 const TQTextCodec * KMMessage::codec() const {
4457  const TQTextCodec * c = mOverrideCodec;
4458  if ( !c )
4459  // no override-codec set for this message, try the CT charset parameter:
4460  c = KMMsgBase::codecForName( charset() );
4461  if ( !c ) {
4462  // Ok, no override and nothing in the message, let's use the fallback
4463  // the user configured
4464  c = KMMsgBase::codecForName( GlobalSettings::self()->fallbackCharacterEncoding().latin1() );
4465  }
4466  if ( !c )
4467  // no charset means us-ascii (RFC 2045), so using local encoding should
4468  // be okay
4469  c = kmkernel->networkCodec();
4470  assert( c );
4471  return c;
4472 }
4473 
4474 TQString KMMessage::bodyToUnicode(const TQTextCodec* codec) const {
4475  if ( !codec )
4476  // No codec was given, so try the charset in the mail
4477  codec = this->codec();
4478  assert( codec );
4479 
4480  return codec->toUnicode( bodyDecoded() );
4481 }
4482 
4483 //-----------------------------------------------------------------------------
4485 {
4486  TQCString str( KPIM::getFirstEmailAddress( rawHeaderField("From") ) );
4487  if ( str.isEmpty() )
4488  str = "unknown@unknown.invalid";
4489  TQCString dateStr( dateShortStr() );
4490  if ( dateStr.isEmpty() ) {
4491  time_t t = ::time( 0 );
4492  dateStr = ctime( &t );
4493  const int len = dateStr.length();
4494  if ( dateStr[len-1] == '\n' )
4495  dateStr.truncate( len - 1 );
4496  }
4497  return "From " + str + " " + dateStr + "\n";
4498 }
4499 
4501 {
4502  sPendingDeletes << this;
4503 }
4504 
4505 DwBodyPart* KMMessage::findPart( int index )
4506 {
4507  int accu = 0;
4508  return findPartInternal( getTopLevelPart(), index, accu );
4509 }
4510 
4511 DwBodyPart* KMMessage::findPartInternal(DwEntity * root, int index, int & accu)
4512 {
4513  accu++;
4514  if ( index < accu ) // should not happen
4515  return 0;
4516  DwBodyPart *current = dynamic_cast<DwBodyPart*>( root );
4517  if ( index == accu )
4518  return current;
4519  DwBodyPart *rv = 0;
4520  if ( root->Body().FirstBodyPart() )
4521  rv = findPartInternal( root->Body().FirstBodyPart(), index, accu );
4522  if ( !rv && current && current->Next() )
4523  rv = findPartInternal( current->Next(), index, accu );
4524  if ( !rv && root->Body().Message() )
4525  rv = findPartInternal( root->Body().Message(), index, accu );
4526  return rv;
4527 }
4528 
TQString asPlainTextFromObjectTree(partNode *root, bool stripSignature, bool allowDecryption) const
Same as asPlainText(), only that this method expects an already parsed object tree as paramter...
Definition: kmmessage.cpp:743
static TQCString defaultCharset()
Get the default message charset.
Definition: kmmessage.cpp:4078
static TQStringList stripMyAddressesFromAddressList(const TQStringList &list)
Strips all the user&#39;s addresses from an address list.
Definition: kmmessage.cpp:3919
void addDwBodyPart(DwBodyPart *aDwPart)
Append a DwBodyPart to the message.
Definition: kmmessage.cpp:3351
void setSignatureState(const KMMsgSignatureState, int idx=-1)
Set signature status of the message.
Definition: kmmessage.cpp:4172
TQString replaceHeadersInString(const TQString &s) const
Replaces every occurrence of "${foo}" in s with headerField("foo")
Definition: kmmessage.cpp:1613
KMMessage * createMDN(KMime::MDN::ActionMode a, KMime::MDN::DispositionType d, bool allowGUI=false, TQValueList< KMime::MDN::DispositionModifier > m=TQValueList< KMime::MDN::DispositionModifier >())
Create a new message that is a MDN for this message, filling all required fields with proper values...
Definition: kmmessage.cpp:1398
TQString headerAsString() const
Return header as string.
Definition: kmmessage.cpp:381
DwString dwString(const TQCString &str)
Construct a DwString from a TQCString.
Definition: util.cpp:130
DwBodyPart * dwBodyPart(int aIdx) const
Get the DwBodyPart at position in aIdx.
Definition: kmmessage.cpp:2898
uint identityUoid() const
Definition: kmmessage.cpp:1730
static TQString smartQuote(const TQString &msg, int maxLineLength)
Given argument msg add quoting characters and relayout for max width maxLength.
Definition: kmmessage.cpp:649
int partNumber(DwBodyPart *aDwBodyPart) const
Get the number of the given DwBodyPart.
Definition: kmmessage.cpp:2858
void setSelection(const TQString &selection)
Sets the selection.
bool hasUnencryptedMsg() const
Returns TRUE if the message contains an unencrypted copy of itself.
Definition: kmmessage.h:135
virtual ~KMMessage()
Destructor.
Definition: kmmessage.cpp:193
TQString bcc() const
Get or set the &#39;Bcc&#39; header field.
Definition: kmmessage.cpp:1971
DwMediaType & dwContentType()
Return reference to Content-Type header for direct manipulation.
Definition: kmmessage.cpp:392
TQCString typeStr() const
Get or set the &#39;Content-Type&#39; header field The member functions that involve enumerated types (ints) ...
Definition: kmmessage.cpp:2394
void cleanupHeader()
Removes empty fields from the header, e.g.
Definition: kmmessage.cpp:1757
void setStatus(const KMMsgStatus status, int idx=-1)
Set status and mark dirty.
Definition: kmmessage.cpp:4156
TQCString rawHeaderField(const TQCString &name) const
Returns the raw value of a header field with the given name.
Definition: kmmessage.cpp:2267
void applyIdentity(uint id)
Set the from, to, cc, bcc, encrytion etc headers as specified in the given identity.
Definition: kmmessage.cpp:1665
void initFromMessage(const KMMessage *msg, bool idHeaders=true)
Initialize headers fields according to the identity and the transport header of the given original me...
Definition: kmmessage.cpp:1745
void initHeader(uint identity=0)
Initialize header fields.
Definition: kmmessage.cpp:1718
static KMime::Types::AddressList splitAddrField(const TQCString &str)
Splits the given address list into separate addresses.
Definition: kmmessage.cpp:2241
const DwString & asDwString() const
Return the entire message contents in the DwString.
Definition: kmmessage.cpp:295
TQString sender() const
Definition: kmmessage.cpp:2042
void setBodyFromUnicode(const TQString &str, DwEntity *entity=0)
Sets this body&#39;s content to str.
Definition: kmmessage.cpp:4443
KMime::Types::AddressList headerAddrField(const TQCString &name) const
Returns header address list as string list.
Definition: kmmessage.cpp:2254
static KPIM::EmailParseResult isValidEmailAddressList(const TQString &aStr, TQString &brokenAddress)
Validate a list of email addresses, and also allow aliases and distribution lists to be expanded befo...
Definition: kmmessage.cpp:276
void setBody(const TQCString &aStr)
Set the message body.
Definition: kmmessage.cpp:2777
static void setDwMediaTypeParam(DwMediaType &mType, const TQCString &attr, const TQCString &val)
add or change a parameter of a DwMediaType field
Definition: kmmessage.cpp:2467
TQCString subtypeStr() const
Subtype.
Definition: kmmessage.cpp:2431
static TQString quoteHtmlChars(const TQString &str, bool removeLineBreaks=false)
Quotes the following characters which have a special meaning in HTML: &#39;<&#39; &#39;>&#39; &#39;&&#39; &#39;"&#39;...
Definition: kmmessage.cpp:3807
void setNeedsAssembly()
tell the message that internal data were changed (must be called after directly modifying message str...
Definition: kmmessage.cpp:2556
void setBodyEncoded(const TQCString &aStr, DwEntity *entity=0)
Set the message body, encoding it according to the current content transfer encoding.
Definition: kmmessage.cpp:2722
TQString from() const
Get or set the &#39;From&#39; header field.
Definition: kmmessage.cpp:2018
TQCString contentTransferEncodingStr() const
Get or set the &#39;Content-Transfer-Encoding&#39; header field The member functions that involve enumerated ...
Definition: kmmessage.cpp:2502
void setCharset(const TQCString &charset, DwEntity *entity=0)
Sets the charset of the message or a subpart of the message.
Definition: kmmessage.cpp:4117
static void bodyPart(DwBodyPart *aDwBodyPart, KMMessagePart *aPart, bool withBody=true)
Fill the KMMessagePart structure for a given DwBodyPart.
Definition: kmmessage.cpp:3111
void setStatusFields()
Set "Status" and "X-Status" fields of the message from the internal message status.
Definition: kmmessage.cpp:354
static TQStringList stripAddressFromAddressList(const TQString &address, const TQStringList &addresses)
Strips an address from an address list.
Definition: kmmessage.cpp:3897
void setHeaderField(const TQCString &name, const TQString &value, HeaderFieldType type=Unstructured, bool prepend=false)
Set the header field with the given name to the given value.
Definition: kmmessage.cpp:2342
void removeHeaderFields(const TQCString &name)
Remove all header fields with given name.
Definition: kmmessage.cpp:2331
static TQString generateMessageId(const TQString &addr)
Generates the Message-Id.
Definition: kmmessage.cpp:3367
DwBodyPart * createDWBodyPart(const KMMessagePart *aPart)
Compose a DwBodyPart (needed for adding a part to the message).
Definition: kmmessage.cpp:3221
static TQCString html2source(const TQCString &src)
Convert &#39;<&#39; into "&lt;" resp.
Definition: kmmessage.cpp:3392
TQString msgId() const
Get or set the &#39;Message-Id&#39; header field.
Definition: kmmessage.cpp:2185
TQString replyTo() const
Get or set the &#39;ReplyTo&#39; header field.
Definition: kmmessage.cpp:1922
KMMessage * createReply(KMail::ReplyStrategy replyStrategy=KMail::ReplySmart, TQString selection=TQString(), bool noQuote=false, bool allowDecryption=true, const TQString &tmpl=TQString(), const TQString &originatingAccount=TQString())
Create a new message that is a reply to this message, filling all required header fields with the pro...
Definition: kmmessage.cpp:866
KMMsgInfo * msgInfo()
Get the KMMsgInfo object that was set with setMsgInfo().
Definition: kmmessage.h:931
void removePrivateHeaderFields()
Remove all private header fields: *Status: and X-KMail-*.
Definition: kmmessage.cpp:338
bool deleteBodyPart(int partIndex)
Delete a body part with the specified part index.
Definition: kmmessage.cpp:3184
void setMsgSerNum(unsigned long newMsgSerNum=0)
Sets the message serial number.
Definition: kmmessage.cpp:226
The TemplateParser transforms a message with a given template.
TQString xmark() const
Get or set the &#39;X-Mark&#39; header field.
Definition: kmmessage.cpp:2067
size_t msgSizeServer() const
Get/set size on server.
Definition: kmmessage.cpp:2215
void setAllowDecryption(const bool allowDecryption)
Sets whether the template parser is allowed to decrypt the original message when needing its message ...
ulong UID() const
Get/set UID.
Definition: kmmessage.cpp:2228
TQValueList< TQCString > rawHeaderFields(const TQCString &field) const
Returns a list of the raw values of all header fields with the given name.
Definition: kmmessage.cpp:2278
DwBodyPart * getFirstDwBodyPart() const
Get the 1st DwBodyPart.
Definition: kmmessage.cpp:2851
void setBodyAndGuessCte(const TQByteArray &aBuf, TQValueList< int > &allowedCte, bool allow8Bit=false, bool willBeSigned=false, DwEntity *entity=0)
Sets body, encoded in the best fitting content-transfer-encoding, which is determined by character fr...
Definition: kmmessage.cpp:2688
KMMessage * createDeliveryReceipt() const
Create a new message that is a delivery receipt of this message, filling required header fileds with ...
Definition: kmmessage.cpp:1638
static TQString expandAliases(const TQString &recipients)
Expands aliases (distribution lists and nick names) and appends a domain part to all email addresses ...
Definition: kmmessage.cpp:3956
static TQValueList< int > determineAllowedCtes(const KMime::CharFreq &cf, bool allow8Bit, bool willBeSigned)
Returns a list of content-transfer-encodings that can be used with the given result of the character ...
Definition: kmmessage.cpp:2640
TQString replyToAuxIdMD5() const
Get the second to last id from the References header field.
Definition: kmmessage.cpp:2149
TQString dateStr() const
Get or set the &#39;Date&#39; header field.
Definition: kmmessage.cpp:1800
TQString templates() const
Get or set the &#39;Templates&#39; folder.
Definition: kmmessage.h:338
static bool addressIsInAddressList(const TQString &address, const TQStringList &addresses)
Returns true if the given address is contained in the given address list.
Definition: kmmessage.cpp:3940
Mail folder.
Definition: kmfolder.h:68
void setMultiPartBody(const TQCString &aStr)
Hack to enable structured body parts to be set as flat text...
Definition: kmmessage.cpp:2794
TQString subjectMD5() const
Get a hash of the subject.
Definition: kmmessage.cpp:2167
void updateBodyPart(const TQString partSpecifier, const TQByteArray &data)
Sets the body of the specified part.
Definition: kmmessage.cpp:4273
void setDateToday()
Set the &#39;Date&#39; header field to the current date.
Definition: kmmessage.cpp:1862
KMMessage * createRedirect(const TQString &toStr)
Create a new message that is a redirect to this message, filling all required header fields with the ...
Definition: kmmessage.cpp:1134
TQString replyToId() const
Get or set the &#39;In-Reply-To&#39; header field.
Definition: kmmessage.cpp:2082
static TQString guessEmailAddressFromLoginName(const TQString &userName)
Uses the hostname as domain part and tries to determine the real name from the entries in the passwor...
Definition: kmmessage.cpp:4006
TQCString bodyDecoded() const
Returns a decoded version of the body from the current content transfer encoding. ...
Definition: kmmessage.cpp:2611
void setContentTypeParam(const TQCString &attr, const TQCString &val)
add or change a parameter of the Content-Type field
Definition: kmmessage.cpp:2492
static TQString encodeMailtoUrl(const TQString &str)
Encodes an email address as mailto URL.
Definition: kmmessage.cpp:3465
TQCString asString() const
Return the entire message contents as a string.
Definition: kmmessage.cpp:317
TQCString dateShortStr() const
Returns the message date in asctime format or an empty string if the message lacks a Date header...
Definition: kmmessage.cpp:1818
bool isUrgent() const
Definition: kmmessage.cpp:262
void fromDwString(const DwString &str, bool setStatus=false)
Parse the string and create this message from it.
Definition: kmmessage.cpp:405
TQCString headerAsSendableString() const
Return the message header with the headers that should not be sent stripped off.
Definition: kmmessage.cpp:330
void getLink(int n, ulong *retMsgSerNum, KMMsgStatus *reStatus) const
Returns the information for the Nth link into retMsg and reStatus.
Definition: kmmessage.cpp:4219
void deleteBodyParts()
Delete all body parts.
Definition: kmmessage.cpp:3177
TQString subject() const
Get or set the &#39;Subject&#39; header field.
Definition: kmmessage.cpp:2052
bool subjectIsPrefixed() const
Is the subject prefixed by Re: or similar?
Definition: kmmessage.cpp:2172
void setUnencryptedMsg(KMMessage *unencrypted)
Specifies an unencrypted copy of this message to be stored in a separate member variable to allow sav...
Definition: kmmessage.cpp:268
bool isMessage() const
Returns TRUE if object is a real message (not KMMsgInfo or KMMsgBase)
Definition: kmmessage.cpp:233
bool transferInProgress() const
Return, if the message should not be deleted.
Definition: kmmessage.cpp:239
KMMessage * createForward(const TQString &tmpl=TQString())
Create a new message that is a forward of this message, filling all required header fields with the p...
Definition: kmmessage.cpp:1232
KMMessage * unencryptedMsg() const
Returns an unencrypted copy of this message or 0 if none exists.
Definition: kmmessage.h:138
void assembleIfNeeded()
Assemble the internal message.
Definition: kmmessage.cpp:2562
TQCString charset() const
Get the message charset.
Definition: kmmessage.cpp:4101
TQString formatString(const TQString &) const
Convert wildcards into normal string.
Definition: kmmessage.cpp:429
TQString drafts() const
Get or set the &#39;Drafts&#39; folder.
Definition: kmmessage.h:334
TQCString body() const
Get the message body.
Definition: kmmessage.cpp:2573
void parseTextStringFromDwPart(partNode *root, TQCString &parsedString, const TQTextCodec *&codec, bool &isHTML) const
Returns a decoded body part string to be further processed by function asQuotedString().
Definition: kmmessage.cpp:717
KMMessage(KMFolder *parent=0)
Straight forward initialization.
Definition: kmmessage.cpp:100
TQCString getRefStr() const
Creates reference string for reply to messages.
Definition: kmmessage.cpp:1106
TQString asQuotedString(const TQString &headerStr, const TQString &indentStr, const TQString &selection=TQString(), bool aStripSignature=true, bool allowDecryption=true) const
Returns message body with quoting header and indented by the given indentation string.
Definition: kmmessage.cpp:838
static TQString decodeMailtoUrl(const TQString &url)
Decodes a mailto URL.
Definition: kmmessage.cpp:3476
const TQTextCodec * codec() const
Get a TQTextCodec suitable for this message part.
Definition: kmmessage.cpp:4456
void addBodyPart(const KMMessagePart *aPart)
Append a body part to the message.
Definition: kmmessage.cpp:3359
sets a cursor and makes sure it&#39;s restored on destruction Create a KCursorSaver object when you want ...
Definition: kcursorsaver.h:13
void sanitizeHeaders(const TQStringList &whiteList=TQStringList())
Remove all headers but the content description ones, and those in the white list. ...
Definition: kmmessage.cpp:1213
TQStringList headerFields(const TQCString &name) const
Returns a list of the values of all header fields with the given name.
Definition: kmmessage.cpp:2305
TQByteArray ByteArray(const DwString &str)
Construct a TQByteArray from a DwString.
Definition: util.cpp:122
static TQCString stripEmailAddr(const TQCString &emailAddr)
This function generates a displayable string from a list of email addresses.
Definition: kmmessage.cpp:3486
KMMsgStatus status() const
Status of the message.
Definition: kmmessage.h:831
static TQString emailAddrAsAnchor(const TQString &emailAddr, bool stripped=true, const TQString &cssStyle=TQString(), bool link=true)
Converts the email address(es) to (a) nice HTML mailto: anchor(s).
Definition: kmmessage.cpp:3843
static const TQStringList & preferredCharsets()
Get a list of preferred message charsets.
Definition: kmmessage.cpp:4095
TQString fcc() const
Get or set the &#39;Fcc&#39; header field.
Definition: kmmessage.cpp:1984
TQString bodyToUnicode(const TQTextCodec *codec=0) const
Returns the body part decoded to unicode.
Definition: kmmessage.cpp:4474
TQCString CString(const DwString &str)
Construct a TQCString from a DwString.
Definition: util.cpp:113
TQString cc() const
Get or set the &#39;Cc&#39; header field.
Definition: kmmessage.cpp:1943
TQString who() const
Get or set the &#39;Who&#39; header field.
Definition: kmmessage.cpp:2009
TQCString id() const
Returns the message ID, useful for followups.
Definition: kmmessage.cpp:211
This is a Mime Message.
Definition: kmmessage.h:68
TQString references() const
Get or set the references for this message.
Definition: kmmessage.cpp:2128
static void readConfig()
Reads config settings from group "KMMessage" and sets all internal variables (e.g.
Definition: kmmessage.cpp:4036
TQCString mboxMessageSeparator()
Returns an mbox message separator line for this message, i.e.
Definition: kmmessage.cpp:4484
TQByteArray asSendableString() const
Return the message contents with the headers that should not be sent stripped off.
Definition: kmmessage.cpp:322
void setTransferInProgress(bool value, bool force=false)
Set that the message shall not be deleted because it is still required.
Definition: kmmessage.cpp:246
KMMsgSignatureState signatureState() const
Signature status of the message.
Definition: kmmessage.h:848
TQString strippedSubjectMD5() const
Get a hash of the subject with all prefixes such as Re: removed.
Definition: kmmessage.cpp:2162
void removeHeaderField(const TQCString &name)
Remove header field with given name.
Definition: kmmessage.cpp:2320
TQString asPlainText(bool stripSignature, bool allowDecryption) const
Return the textual content of the message as plain text, converting HTML to plain text if necessary...
Definition: kmmessage.cpp:825
TQString to() const
Get or set the &#39;To&#39; header field.
Definition: kmmessage.cpp:1897
DwHeaders & headers() const
get the DwHeaders (make sure to call setNeedsAssembly() function after directly modyfying internal da...
Definition: kmmessage.cpp:2549
int numBodyParts() const
Number of body parts the message has.
Definition: kmmessage.cpp:2809
KMMsgEncryptionState encryptionState() const
Encryption status of the message.
Definition: kmmessage.h:845
bool readyToShow() const
Return if the message is ready to be shown.
Definition: kmmessage.h:873
void link(const KMMessage *aMsg, KMMsgStatus aStatus)
Links this message to aMsg, setting link type to aStatus.
Definition: kmmessage.cpp:4193
void setEncryptionState(const KMMsgEncryptionState, int idx=-1)
Set encryption status of the message.
Definition: kmmessage.cpp:4163
TQString headerField(const TQCString &name) const
Returns the value of a header field with the given name.
Definition: kmmessage.cpp:2292
void setAutomaticFields(bool isMultipart=false)
Set fields that are either automatically set (Message-id) or that do not change from one message to a...
Definition: kmmessage.cpp:1780
DwBodyPart * findDwBodyPart(int type, int subtype) const
Return the first DwBodyPart matching a given Content-Type or zero, if no found.
Definition: kmmessage.cpp:2938
void deleteWhenUnused()
Delete this message as soon as it no longer in use.
Definition: kmmessage.cpp:4500
TQCString createForwardBody()
Create the forwarded body for the message.
Definition: kmmessage.cpp:1185