kmail

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