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 ...
Mail folder.
Definition kmfolder.h:69
This is a Mime Message.
Definition kmmessage.h:68
uint identityUoid() const
int partNumber(DwBodyPart *aDwBodyPart) const
Get the number of the given DwBodyPart.
void link(const KMMessage *aMsg, KMMsgStatus aStatus)
Links this message to aMsg, setting link type to aStatus.
void updateBodyPart(const TQString partSpecifier, const TQByteArray &data)
Sets the body of the specified part.
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...
TQString who() const
Get or set the 'Who' header field.
void setBody(const TQCString &aStr)
Set the message body.
static TQString generateMessageId(const TQString &addr)
Generates the Message-Id.
DwBodyPart * getFirstDwBodyPart() const
Get the 1st DwBodyPart.
TQCString bodyDecoded() const
Returns a decoded version of the body from the current content transfer encoding.
TQString formatString(const TQString &) const
Convert wildcards into normal string.
void setBodyFromUnicode(const TQString &str, DwEntity *entity=0)
Sets this body's content to str.
TQCString typeStr() const
Get or set the 'Content-Type' header field The member functions that involve enumerated types (ints) ...
static TQString quoteHtmlChars(const TQString &str, bool removeLineBreaks=false)
Quotes the following characters which have a special meaning in HTML: '<' '>' '&' '"'....
TQCString getRefStr() const
Creates reference string for reply to messages.
void setBodyEncoded(const TQCString &aStr, DwEntity *entity=0)
Set the message body, encoding it according to the current content transfer encoding.
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.
void setDateToday()
Set the 'Date' header field to the current date.
bool isUrgent() const
void removeHeaderFields(const TQCString &name)
Remove all header fields with given name.
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().
static KMime::Types::AddressList splitAddrField(const TQCString &str)
Splits the given address list into separate addresses.
size_t msgSizeServer() const
Get/set size on server.
void setTransferInProgress(bool value, bool force=false)
Set that the message shall not be deleted because it is still required.
TQCString body() const
Get the message body.
TQString msgId() const
Get or set the 'Message-Id' header field.
TQString from() const
Get or set the 'From' header field.
TQCString charset() const
Get the message charset.
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 ...
void setAutomaticFields(bool isMultipart=false)
Set fields that are either automatically set (Message-id) or that do not change from one message to a...
void setCharset(const TQCString &charset, DwEntity *entity=0)
Sets the charset of the message or a subpart of the message.
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.
bool readyToShow() const
Return if the message is ready to be shown.
Definition kmmessage.h:872
TQString sender() const
TQString xmark() const
Get or set the 'X-Mark' header field.
static TQCString html2source(const TQCString &src)
Convert '<' into "<" resp.
TQString bcc() const
Get or set the 'Bcc' header field.
static void readConfig()
Reads config settings from group "KMMessage" and sets all internal variables (e.g.
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...
void setStatus(const KMMsgStatus status, int idx=-1)
Set status and mark dirty.
static TQStringList stripMyAddressesFromAddressList(const TQStringList &list)
Strips all the user's addresses from an address list.
void setUnencryptedMsg(KMMessage *unencrypted)
Specifies an unencrypted copy of this message to be stored in a separate member variable to allow sav...
static bool addressIsInAddressList(const TQString &address, const TQStringList &addresses)
Returns true if the given address is contained in the given address list.
DwMediaType & dwContentType()
Return reference to Content-Type header for direct manipulation.
bool deleteBodyPart(int partIndex)
Delete a body part with the specified part index.
TQString replyToAuxIdMD5() const
Get the second to last id from the References header field.
TQString to() const
Get or set the 'To' header field.
TQString subject() const
Get or set the 'Subject' header field.
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...
void setStatusFields()
Set "Status" and "X-Status" fields of the message from the internal message status.
void removeHeaderField(const TQCString &name)
Remove header field with given name.
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.
TQCString headerAsSendableString() const
Return the message header with the headers that should not be sent stripped off.
TQCString subtypeStr() const
Subtype.
static TQString smartQuote(const TQString &msg, int maxLineLength)
Given argument msg add quoting characters and relayout for max width maxLength.
void addDwBodyPart(DwBodyPart *aDwPart)
Append a DwBodyPart to the message.
static const TQStringList & preferredCharsets()
Get a list of preferred message charsets.
void fromDwString(const DwString &str, bool setStatus=false)
Parse the string and create this message from it.
TQValueList< TQCString > rawHeaderFields(const TQCString &field) const
Returns a list of the raw values of all header fields with the given name.
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...
static TQString expandAliases(const TQString &recipients)
Expands aliases (distribution lists and nick names) and appends a domain part to all email addresses ...
virtual ~KMMessage()
Destructor.
TQStringList headerFields(const TQCString &name) const
Returns a list of the values of all header fields with the given name.
TQString asPlainTextFromObjectTree(partNode *root, bool stripSignature, bool allowDecryption) const
Same as asPlainText(), only that this method expects an already parsed object tree as paramter.
static void setDwMediaTypeParam(DwMediaType &mType, const TQCString &attr, const TQCString &val)
add or change a parameter of a DwMediaType field
TQCString asString() const
Return the entire message contents as a string.
static TQString encodeMailtoUrl(const TQString &str)
Encodes an email address as mailto URL.
KMime::Types::AddressList headerAddrField(const TQCString &name) const
Returns header address list as string list.
TQByteArray asSendableString() const
Return the message contents with the headers that should not be sent stripped off.
static TQCString stripEmailAddr(const TQCString &emailAddr)
This function generates a displayable string from a list of email addresses.
KMMessage * createRedirect(const TQString &toStr)
Create a new message that is a redirect to this message, filling all required header fields with the ...
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).
void setSignatureState(const KMMsgSignatureState, int idx=-1)
Set signature status of the message.
TQString bodyToUnicode(const TQTextCodec *codec=0) const
Returns the body part decoded to unicode.
TQString asPlainText(bool stripSignature, bool allowDecryption) const
Return the textual content of the message as plain text, converting HTML to plain text if necessary.
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.
void addBodyPart(const KMMessagePart *aPart)
Append a body part to the message.
TQString references() const
Get or set the references for this message.
void removePrivateHeaderFields()
Remove all private header fields: Status: and X-KMail-
const DwString & asDwString() const
Return the entire message contents in the DwString.
bool transferInProgress() const
Return, if the message should not be deleted.
void setMsgSerNum(unsigned long newMsgSerNum=0)
Sets the message serial number.
void sanitizeHeaders(const TQStringList &whiteList=TQStringList())
Remove all headers but the content description ones, and those in the white list.
static TQString decodeMailtoUrl(const TQString &url)
Decodes a mailto URL.
TQString cc() const
Get or set the 'Cc' header field.
const TQTextCodec * codec() const
Get a TQTextCodec suitable for this message part.
bool isMessage() const
Returns TRUE if object is a real message (not KMMsgInfo or KMMsgBase)
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.
void applyIdentity(uint id)
Set the from, to, cc, bcc, encrytion etc headers as specified in the given identity.
void deleteWhenUnused()
Delete this message as soon as it no longer in use.
TQString strippedSubjectMD5() const
Get a hash of the subject with all prefixes such as Re: removed.
static TQCString defaultCharset()
Get the default message charset.
TQCString mboxMessageSeparator()
Returns an mbox message separator line for this message, i.e.
TQString replaceHeadersInString(const TQString &s) const
Replaces every occurrence of "${foo}" in s with headerField("foo")
TQCString id() const
Returns the message ID, useful for followups.
void initFromMessage(const KMMessage *msg, bool idHeaders=true)
Initialize headers fields according to the identity and the transport header of the given original me...
TQCString createForwardBody()
Create the forwarded body for the message.
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...
void cleanupHeader()
Removes empty fields from the header, e.g.
int numBodyParts() const
Number of body parts the message has.
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...
KMMessage(KMFolder *parent=0)
Straight forward initialization.
Definition kmmessage.cpp:99
TQString replyToId() const
Get or set the 'In-Reply-To' header field.
TQCString rawHeaderField(const TQCString &name) const
Returns the raw value of a header field with the given name.
void setContentTypeParam(const TQCString &attr, const TQCString &val)
add or change a parameter of the Content-Type field
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.
void setMultiPartBody(const TQCString &aStr)
Hack to enable structured body parts to be set as flat text...
TQCString contentTransferEncodingStr() const
Get or set the 'Content-Transfer-Encoding' header field The member functions that involve enumerated ...
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.
KMMessage * createDeliveryReceipt() const
Create a new message that is a delivery receipt of this message, filling required header fileds with ...
void setEncryptionState(const KMMsgEncryptionState, int idx=-1)
Set encryption status of the message.
TQString fcc() const
Get or set the 'Fcc' header field.
void getLink(int n, ulong *retMsgSerNum, KMMsgStatus *reStatus) const
Returns the information for the Nth link into retMsg and reStatus.
TQCString dateShortStr() const
Returns the message date in asctime format or an empty string if the message lacks a Date header.
DwHeaders & headers() const
get the DwHeaders (make sure to call setNeedsAssembly() function after directly modyfying internal da...
TQString subjectMD5() const
Get a hash of the subject.
TQString headerAsString() const
Return header as string.
void initHeader(uint identity=0)
Initialize header fields.
bool subjectIsPrefixed() const
Is the subject prefixed by Re: or similar?
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.
void assembleIfNeeded()
Assemble the internal message.
ulong UID() const
Get/set UID.
DwBodyPart * findDwBodyPart(int type, int subtype) const
Return the first DwBodyPart matching a given Content-Type or zero, if no found.
void deleteBodyParts()
Delete all body parts.
TQString dateStr() const
Get or set the 'Date' header field.
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).
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