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