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