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"
14using KMail::MessageProperty;
15#include "objecttreeparser.h"
16using KMail::ObjectTreeParser;
17#include "kmfolderindex.h"
18#include "undostack.h"
19#include "kmversion.h"
20#include "headerstrategy.h"
21#include "globalsettings.h"
22using 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>
50using KMime::HeaderParsing::parseAddressList;
51using 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
69using namespace KMime;
70
71static DwString emptyString("");
72
73// Values that are set from the config file with KMMessage::readConfig()
74static TQString sReplyLanguage, sReplyStr, sReplyAllStr, sIndentPrefixStr;
75static bool sSmartQuote,
76 sWordWrap;
77static int sWrapCol;
78static TQStringList sPrefCharsets;
79
80TQString KMMessage::sForwardStr;
81const HeaderStrategy * KMMessage::sHeaderStrategy = HeaderStrategy::rich();
82//helper
83static void applyHeadersToMessagePart( DwHeaders& headers, KMMessagePart* aPart );
84
85TQValueList<KMMessage*> KMMessage::sPendingDeletes;
86
87//-----------------------------------------------------------------------------
88KMMessage::KMMessage(DwMessage* aMsg)
89 : KMMsgBase()
90{
91 init( aMsg );
92 // aMsg might need assembly
93 mNeedsAssembly = true;
94}
95
96//-----------------------------------------------------------------------------
97KMMessage::KMMessage(KMFolder* parent): KMMsgBase(parent)
98{
99 init();
100}
101
102
103//-----------------------------------------------------------------------------
104KMMessage::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
130void 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
157void 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//-----------------------------------------------------------------------------
199void KMMessage::setReferences(const TQCString& aStr)
200{
201 if (aStr.isNull()) return;
202 mMsg->Headers().References().FromString(aStr);
203 mNeedsAssembly = true;
204}
205
206
207//-----------------------------------------------------------------------------
208TQCString 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
223void 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//-----------------------------------------------------------------------------
243void 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
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
273KPIM::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//-----------------------------------------------------------------------------
292const DwString& KMMessage::asDwString() const
293{
294 if (mNeedsAssembly)
295 {
296 mNeedsAssembly = false;
297 mMsg->Assemble();
298 }
299 return mMsg->AsString();
300}
301
302//-----------------------------------------------------------------------------
303const DwMessage* KMMessage::asDwMessage()
304{
305 if (mNeedsAssembly)
306 {
307 mNeedsAssembly = false;
308 mMsg->Assemble();
309 }
310 return mMsg;
311}
312
313//-----------------------------------------------------------------------------
314TQCString KMMessage::asString() const {
316}
317
318
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
394void KMMessage::fromByteArray( const TQByteArray & ba, bool setStatus ) {
395 return fromDwString( DwString( ba.data(), ba.size() ), setStatus );
396}
397
398void KMMessage::fromString( const TQCString & str, bool aSeStatus ) {
399 return fromDwString( KMail::Util::dwString( str ), aSeStatus );
400}
401
402void 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//-----------------------------------------------------------------------------
426TQString 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
507static 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
515static 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
550static 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
592static 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
639static 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
646TQString 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
740TQString 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
822TQString 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
835TQString 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//-----------------------------------------------------------------------------
863KMMessage* 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//-----------------------------------------------------------------------------
1103TQCString 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
1131KMMessage* 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
1210void 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//-----------------------------------------------------------------------------
1229KMMessage* 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
1327static 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
1366static const int numMdnMessageBoxes
1367 = sizeof mdnMessageBoxes / sizeof *mdnMessageBoxes;
1368
1369
1370static 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
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
1610TQString 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//-----------------------------------------------------------------------------
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//-----------------------------------------------------------------------------
1742void 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//-----------------------------------------------------------------------------
1797TQString 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//-----------------------------------------------------------------------------
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//-----------------------------------------------------------------------------
1833TQString 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//-----------------------------------------------------------------------------
1848time_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//-----------------------------------------------------------------------------
1868void 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//-----------------------------------------------------------------------------
1879void 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//-----------------------------------------------------------------------------
1894TQString 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//-----------------------------------------------------------------------------
1907void KMMessage::setTo(const TQString& aStr)
1908{
1909 setHeaderField( "To", aStr, Address );
1910}
1911
1912//-----------------------------------------------------------------------------
1913TQString KMMessage::toStrip() const
1914{
1915 return stripEmailAddr( to() );
1916}
1917
1918//-----------------------------------------------------------------------------
1919TQString KMMessage::replyTo() const
1920{
1921 return KPIM::normalizeAddressesAndDecodeIDNs( rawHeaderField("Reply-To") );
1922}
1923
1924
1925//-----------------------------------------------------------------------------
1926void KMMessage::setReplyTo(const TQString& aStr)
1927{
1928 setHeaderField( "Reply-To", aStr, Address );
1929}
1930
1931
1932//-----------------------------------------------------------------------------
1933void KMMessage::setReplyTo(KMMessage* aMsg)
1934{
1935 setHeaderField( "Reply-To", aMsg->from(), Address );
1936}
1937
1938
1939//-----------------------------------------------------------------------------
1940TQString 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//-----------------------------------------------------------------------------
1954void KMMessage::setCc(const TQString& aStr)
1955{
1956 setHeaderField( "Cc", aStr, Address );
1957}
1958
1959
1960//-----------------------------------------------------------------------------
1961TQString KMMessage::ccStrip() const
1962{
1963 return stripEmailAddr( cc() );
1964}
1965
1966
1967//-----------------------------------------------------------------------------
1968TQString KMMessage::bcc() const
1969{
1970 return KPIM::normalizeAddressesAndDecodeIDNs( rawHeaderField("Bcc") );
1971}
1972
1973
1974//-----------------------------------------------------------------------------
1975void KMMessage::setBcc(const TQString& aStr)
1976{
1977 setHeaderField( "Bcc", aStr, Address );
1978}
1979
1980//-----------------------------------------------------------------------------
1981TQString KMMessage::fcc() const
1982{
1983 return headerField( "X-KMail-Fcc" );
1984}
1985
1986
1987//-----------------------------------------------------------------------------
1988void KMMessage::setFcc( const TQString &aStr )
1989{
1990 setHeaderField( "X-KMail-Fcc", aStr );
1991}
1992
1993//-----------------------------------------------------------------------------
1994void KMMessage::setDrafts( const TQString &aStr )
1995{
1996 mDrafts = aStr;
1997}
1998
1999//-----------------------------------------------------------------------------
2000void KMMessage::setTemplates( const TQString &aStr )
2001{
2002 mTemplates = aStr;
2003}
2004
2005//-----------------------------------------------------------------------------
2006TQString KMMessage::who() const
2007{
2008 if (mParent)
2009 return KPIM::normalizeAddressesAndDecodeIDNs( rawHeaderField(mParent->whoField().utf8()) );
2010 return from();
2011}
2012
2013
2014//-----------------------------------------------------------------------------
2015TQString KMMessage::from() const
2016{
2017 return KPIM::normalizeAddressesAndDecodeIDNs( rawHeaderField("From") );
2018}
2019
2020
2021//-----------------------------------------------------------------------------
2022void 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//-----------------------------------------------------------------------------
2033TQString KMMessage::fromStrip() const
2034{
2035 return stripEmailAddr( from() );
2036}
2037
2038//-----------------------------------------------------------------------------
2039TQString 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//-----------------------------------------------------------------------------
2049TQString KMMessage::subject() const
2050{
2051 return headerField("Subject");
2052}
2053
2054
2055//-----------------------------------------------------------------------------
2056void KMMessage::setSubject(const TQString& aStr)
2057{
2058 setHeaderField("Subject",aStr);
2059 mDirty = true;
2060}
2061
2062
2063//-----------------------------------------------------------------------------
2064TQString KMMessage::xmark() const
2065{
2066 return headerField("X-KMail-Mark");
2067}
2068
2069
2070//-----------------------------------------------------------------------------
2071void KMMessage::setXMark(const TQString& aStr)
2072{
2073 setHeaderField("X-KMail-Mark", aStr);
2074 mDirty = true;
2075}
2076
2077
2078//-----------------------------------------------------------------------------
2079TQString 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//-----------------------------------------------------------------------------
2120TQString KMMessage::replyToIdMD5() const {
2121 return base64EncodedMD5( replyToId() );
2122}
2123
2124//-----------------------------------------------------------------------------
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//-----------------------------------------------------------------------------
2164TQString KMMessage::subjectMD5() const {
2165 return base64EncodedMD5( subject(), true /*utf8*/ );
2166}
2167
2168//-----------------------------------------------------------------------------
2170 return subjectMD5() != strippedSubjectMD5();
2171}
2172
2173//-----------------------------------------------------------------------------
2174void KMMessage::setReplyToId(const TQString& aStr)
2175{
2176 setHeaderField("In-Reply-To", aStr);
2177 mDirty = true;
2178}
2179
2180
2181//-----------------------------------------------------------------------------
2182TQString 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//-----------------------------------------------------------------------------
2199TQString KMMessage::msgIdMD5() const {
2200 return base64EncodedMD5( msgId() );
2201}
2202
2203
2204//-----------------------------------------------------------------------------
2205void 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//-----------------------------------------------------------------------------
2218void KMMessage::setMsgSizeServer(size_t size)
2219{
2220 setHeaderField("X-Length", TQCString().setNum(size));
2221 mDirty = true;
2222}
2223
2224//-----------------------------------------------------------------------------
2225ulong KMMessage::UID() const {
2226 return headerField( "X-UID" ).toULong();
2227}
2228
2229
2230//-----------------------------------------------------------------------------
2231void KMMessage::setUID(ulong uid)
2232{
2233 setHeaderField("X-UID", TQCString().setNum(uid));
2234 mDirty = true;
2235}
2236
2237//-----------------------------------------------------------------------------
2238AddressList 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
2251AddressList KMMessage::headerAddrField( const TQCString & aName ) const {
2252 return KMMessage::splitAddrField( rawHeaderField( aName ) );
2253}
2254
2255AddrSpecList 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
2264TQCString 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
2275TQValueList<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
2289TQString 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
2302TQStringList 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//-----------------------------------------------------------------------------
2317void 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//-----------------------------------------------------------------------------
2328void 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//-----------------------------------------------------------------------------
2339void 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//-----------------------------------------------------------------------------
2391TQCString 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//-----------------------------------------------------------------------------
2400int 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//-----------------------------------------------------------------------------
2409void KMMessage::setTypeStr(const TQCString& aStr)
2410{
2411 dwContentType().SetTypeStr(DwString(aStr));
2412 dwContentType().Parse();
2413 mNeedsAssembly = true;
2414}
2415
2416
2417//-----------------------------------------------------------------------------
2418void KMMessage::setType(int aType)
2419{
2420 dwContentType().SetType(aType);
2421 dwContentType().Assemble();
2422 mNeedsAssembly = true;
2423}
2424
2425
2426
2427//-----------------------------------------------------------------------------
2428TQCString 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//-----------------------------------------------------------------------------
2437int 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//-----------------------------------------------------------------------------
2446void KMMessage::setSubtypeStr(const TQCString& aStr)
2447{
2448 dwContentType().SetSubtypeStr(DwString(aStr));
2449 dwContentType().Parse();
2450 mNeedsAssembly = true;
2451}
2452
2453
2454//-----------------------------------------------------------------------------
2455void KMMessage::setSubtype(int aSubtype)
2456{
2457 dwContentType().SetSubtype(aSubtype);
2458 dwContentType().Assemble();
2459 mNeedsAssembly = true;
2460}
2461
2462
2463//-----------------------------------------------------------------------------
2464void 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//-----------------------------------------------------------------------------
2489void 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//-----------------------------------------------------------------------------
2509int 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//-----------------------------------------------------------------------------
2522void 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//-----------------------------------------------------------------------------
2535void 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//-----------------------------------------------------------------------------
2546DwHeaders& 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//-----------------------------------------------------------------------------
2570TQCString 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//-----------------------------------------------------------------------------
2582TQByteArray 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//-----------------------------------------------------------------------------
2608TQCString 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//-----------------------------------------------------------------------------
2637TQValueList<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//-----------------------------------------------------------------------------
2685void 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//-----------------------------------------------------------------------------
2702void 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//-----------------------------------------------------------------------------
2719void 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//-----------------------------------------------------------------------------
2745void 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//-----------------------------------------------------------------------------
2774void KMMessage::setBody(const TQCString& aStr)
2775{
2776 mMsg->Body().FromString(KMail::Util::dwString(aStr));
2777 mNeedsAssembly = true;
2778}
2779void KMMessage::setBody(const DwString& aStr)
2780{
2781 mMsg->Body().FromString(aStr);
2782 mNeedsAssembly = true;
2783}
2784void KMMessage::setBody(const char* aStr)
2785{
2786 mMsg->Body().FromString(aStr);
2787 mNeedsAssembly = true;
2788}
2789
2790//-----------------------------------------------------------------------------
2791void 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//-----------------------------------------------------------------------------
2849{
2850 return mMsg->Body().FirstBodyPart();
2851}
2852
2853
2854//-----------------------------------------------------------------------------
2855int 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//-----------------------------------------------------------------------------
2895DwBodyPart * 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//-----------------------------------------------------------------------------
2935DwBodyPart * 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//-----------------------------------------------------------------------------
2984DwBodyPart * 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
3033void 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//-----------------------------------------------------------------------------
3108void 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//-----------------------------------------------------------------------------
3159void 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
3181bool 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//-----------------------------------------------------------------------------
3218DwBodyPart* 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//-----------------------------------------------------------------------------
3348void KMMessage::addDwBodyPart(DwBodyPart * aDwPart)
3349{
3350 mMsg->Body().AddBodyPart( aDwPart );
3351 mNeedsAssembly = true;
3352}
3353
3354
3355//-----------------------------------------------------------------------------
3356void KMMessage::addBodyPart(const KMMessagePart* aPart)
3357{
3358 DwBodyPart* part = createDWBodyPart( aPart );
3359 addDwBodyPart( part );
3360}
3361
3362
3363//-----------------------------------------------------------------------------
3364TQString 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//-----------------------------------------------------------------------------
3389TQCString 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//-----------------------------------------------------------------------------
3462TQString 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//-----------------------------------------------------------------------------
3473TQString 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//-----------------------------------------------------------------------------
3483TQCString 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//-----------------------------------------------------------------------------
3642TQString 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//-----------------------------------------------------------------------------
3804TQString 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//-----------------------------------------------------------------------------
3840TQString 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
3894TQStringList 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
3916TQStringList 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
3937bool 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
3953TQString 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
4003TQString 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
4092const TQStringList &KMMessage::preferredCharsets()
4093{
4094 return sPrefCharsets;
4095}
4096
4097//-----------------------------------------------------------------------------
4098TQCString 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//-----------------------------------------------------------------------------
4114void 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//-----------------------------------------------------------------------------
4153void KMMessage::setStatus(const KMMsgStatus aStatus, int idx)
4154{
4155 if (mStatus == aStatus)
4156 return;
4157 KMMsgBase::setStatus(aStatus, idx);
4158}
4159
4160void 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
4169void 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
4178void 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//-----------------------------------------------------------------------------
4190void 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//-----------------------------------------------------------------------------
4216void 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//-----------------------------------------------------------------------------
4238DwBodyPart* 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//-----------------------------------------------------------------------------
4270void 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
4348void 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//-----------------------------------------------------------------------------
4364void 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
4440void 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
4453const 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
4471TQString 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
4502DwBodyPart* KMMessage::findPart( int index )
4503{
4504 int accu = 0;
4505 return findPartInternal( getTopLevelPart(), index, accu );
4506}
4507
4508DwBodyPart* 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
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
KMMsgInfo * msgInfo()
Get the KMMsgInfo object that was set with setMsgInfo().
Definition: kmmessage.h:930
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
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
KMMessage * unencryptedMsg() const
Returns an unencrypted copy of this message or 0 if none exists.
Definition: kmmessage.h:137
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