kmail

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