00001
00002
00003
00004
00005 #include <config.h>
00006
00007 #include "partNode.h"
00008
00009
00010 #define ALLOW_GUI 1
00011 #include "kmkernel.h"
00012 #include "kmmessage.h"
00013 #include "mailinglist-magic.h"
00014 #include "messageproperty.h"
00015 using KMail::MessageProperty;
00016 #include "objecttreeparser.h"
00017 using KMail::ObjectTreeParser;
00018 #include "kmfolderindex.h"
00019 #include "undostack.h"
00020 #include "kmversion.h"
00021 #include "headerstrategy.h"
00022 #include "globalsettings.h"
00023 using KMail::HeaderStrategy;
00024 #include "kmaddrbook.h"
00025 #include "kcursorsaver.h"
00026 #include "templateparser.h"
00027
00028 #include <libkpimidentities/identity.h>
00029 #include <libkpimidentities/identitymanager.h>
00030 #include <libemailfunctions/email.h>
00031
00032 #include <kasciistringtools.h>
00033
00034 #include <kpgpblock.h>
00035 #include <kaddrbook.h>
00036
00037 #include <tdeapplication.h>
00038 #include <tdeglobalsettings.h>
00039 #include <kdebug.h>
00040 #include <tdeconfig.h>
00041 #include <tdehtml_part.h>
00042 #include <kuser.h>
00043 #include <kidna.h>
00044 #include <kasciistricmp.h>
00045
00046 #include <tqcursor.h>
00047 #include <tqtextcodec.h>
00048 #include <tqmessagebox.h>
00049 #include <kmime_util.h>
00050 #include <kmime_charfreq.h>
00051
00052 #include <kmime_header_parsing.h>
00053 using KMime::HeaderParsing::parseAddressList;
00054 using namespace KMime::Types;
00055
00056 #include <mimelib/body.h>
00057 #include <mimelib/field.h>
00058 #include <mimelib/mimepp.h>
00059 #include <mimelib/string.h>
00060 #include <assert.h>
00061 #include <sys/time.h>
00062 #include <time.h>
00063 #include <tdelocale.h>
00064 #include <stdlib.h>
00065 #include <unistd.h>
00066 #include "util.h"
00067
00068 #if ALLOW_GUI
00069 #include <tdemessagebox.h>
00070 #endif
00071
00072 using namespace KMime;
00073
00074 static DwString emptyString("");
00075
00076
00077 static TQString sReplyLanguage, sReplyStr, sReplyAllStr, sIndentPrefixStr;
00078 static bool sSmartQuote,
00079 sWordWrap;
00080 static int sWrapCol;
00081 static TQStringList sPrefCharsets;
00082
00083 TQString KMMessage::sForwardStr;
00084 const HeaderStrategy * KMMessage::sHeaderStrategy = HeaderStrategy::rich();
00085
00086 static void applyHeadersToMessagePart( DwHeaders& headers, KMMessagePart* aPart );
00087
00088 TQValueList<KMMessage*> KMMessage::sPendingDeletes;
00089
00090
00091 KMMessage::KMMessage(DwMessage* aMsg)
00092 : KMMsgBase()
00093 {
00094 init( aMsg );
00095
00096 mNeedsAssembly = true;
00097 }
00098
00099
00100 KMMessage::KMMessage(KMFolder* parent): KMMsgBase(parent)
00101 {
00102 init();
00103 }
00104
00105
00106
00107 KMMessage::KMMessage(KMMsgInfo& msgInfo): KMMsgBase()
00108 {
00109 init();
00110
00111 mMsgSize = msgInfo.msgSize();
00112 mFolderOffset = msgInfo.folderOffset();
00113 mStatus = msgInfo.status();
00114 mEncryptionState = msgInfo.encryptionState();
00115 mSignatureState = msgInfo.signatureState();
00116 mMDNSentState = msgInfo.mdnSentState();
00117 mDate = msgInfo.date();
00118 mFileName = msgInfo.fileName();
00119 KMMsgBase::assign(&msgInfo);
00120 }
00121
00122
00123
00124 KMMessage::KMMessage(const KMMessage& other) :
00125 KMMsgBase( other ),
00126 ISubject(),
00127 mMsg(0)
00128 {
00129 init();
00130 assign( other );
00131 }
00132
00133 void KMMessage::init( DwMessage* aMsg )
00134 {
00135 mNeedsAssembly = false;
00136 if ( aMsg ) {
00137 mMsg = aMsg;
00138 } else {
00139 mMsg = new DwMessage;
00140 }
00141 mOverrideCodec = 0;
00142 mDecodeHTML = false;
00143 mComplete = true;
00144 mReadyToShow = true;
00145 mMsgSize = 0;
00146 mMsgLength = 0;
00147 mFolderOffset = 0;
00148 mStatus = KMMsgStatusNew;
00149 mEncryptionState = KMMsgEncryptionStateUnknown;
00150 mSignatureState = KMMsgSignatureStateUnknown;
00151 mMDNSentState = KMMsgMDNStateUnknown;
00152 mDate = 0;
00153 mUnencryptedMsg = 0;
00154 mLastUpdated = 0;
00155 mCursorPos = 0;
00156 mMsgInfo = 0;
00157 mIsParsed = false;
00158 }
00159
00160 void KMMessage::assign( const KMMessage& other )
00161 {
00162 MessageProperty::forget( this );
00163 delete mMsg;
00164 delete mUnencryptedMsg;
00165
00166 mNeedsAssembly = true;
00167 if( other.mMsg )
00168 mMsg = new DwMessage( *(other.mMsg) );
00169 else
00170 mMsg = 0;
00171 mOverrideCodec = other.mOverrideCodec;
00172 mDecodeHTML = other.mDecodeHTML;
00173 mMsgSize = other.mMsgSize;
00174 mMsgLength = other.mMsgLength;
00175 mFolderOffset = other.mFolderOffset;
00176 mStatus = other.mStatus;
00177 mEncryptionState = other.mEncryptionState;
00178 mSignatureState = other.mSignatureState;
00179 mMDNSentState = other.mMDNSentState;
00180 mIsParsed = other.mIsParsed;
00181 mDate = other.mDate;
00182 if( other.hasUnencryptedMsg() )
00183 mUnencryptedMsg = new KMMessage( *other.unencryptedMsg() );
00184 else
00185 mUnencryptedMsg = 0;
00186 setDrafts( other.drafts() );
00187 setTemplates( other.templates() );
00188
00189
00190 }
00191
00192
00193 KMMessage::~KMMessage()
00194 {
00195 delete mMsgInfo;
00196 delete mMsg;
00197 kmkernel->undoStack()->msgDestroyed( this );
00198 }
00199
00200
00201
00202 void KMMessage::setReferences(const TQCString& aStr)
00203 {
00204 if (aStr.isNull()) return;
00205 mMsg->Headers().References().FromString(aStr);
00206 mNeedsAssembly = true;
00207 }
00208
00209
00210
00211 TQCString KMMessage::id() const
00212 {
00213 DwHeaders& header = mMsg->Headers();
00214 if (header.HasMessageId())
00215 return KMail::Util::CString( header.MessageId().AsString() );
00216 else
00217 return "";
00218 }
00219
00220
00221
00222
00223
00224
00225
00226 void KMMessage::setMsgSerNum(unsigned long newMsgSerNum)
00227 {
00228 MessageProperty::setSerialCache( this, newMsgSerNum );
00229 }
00230
00231
00232
00233 bool KMMessage::isMessage() const
00234 {
00235 return true;
00236 }
00237
00238
00239 bool KMMessage::transferInProgress() const
00240 {
00241 return MessageProperty::transferInProgress( getMsgSerNum() );
00242 }
00243
00244
00245
00246 void KMMessage::setTransferInProgress(bool value, bool force)
00247 {
00248 MessageProperty::setTransferInProgress( getMsgSerNum(), value, force );
00249 if ( !transferInProgress() && sPendingDeletes.contains( this ) ) {
00250 sPendingDeletes.remove( this );
00251 if ( parent() ) {
00252 int idx = parent()->find( this );
00253 if ( idx > 0 ) {
00254 parent()->removeMsg( idx );
00255 }
00256 }
00257 }
00258 }
00259
00260
00261
00262 bool KMMessage::isUrgent() const {
00263 return headerField( "Priority" ).contains( "urgent", false )
00264 || headerField( "X-Priority" ).startsWith( "2" );
00265 }
00266
00267
00268 void KMMessage::setUnencryptedMsg( KMMessage* unencrypted )
00269 {
00270 delete mUnencryptedMsg;
00271 mUnencryptedMsg = unencrypted;
00272 }
00273
00274
00275
00276 KPIM::EmailParseResult KMMessage::isValidEmailAddressList( const TQString& aStr,
00277 TQString& brokenAddress )
00278 {
00279 if ( aStr.isEmpty() ) {
00280 return KPIM::AddressEmpty;
00281 }
00282
00283 TQStringList list = KPIM::splitEmailAddrList( aStr );
00284 for( TQStringList::const_iterator it = list.begin(); it != list.end(); ++it ) {
00285 KPIM::EmailParseResult errorCode = KPIM::isValidEmailAddress( *it );
00286 if ( errorCode != KPIM::AddressOk ) {
00287 brokenAddress = ( *it );
00288 return errorCode;
00289 }
00290 }
00291 return KPIM::AddressOk;
00292 }
00293
00294
00295 const DwString& KMMessage::asDwString() const
00296 {
00297 if (mNeedsAssembly)
00298 {
00299 mNeedsAssembly = false;
00300 mMsg->Assemble();
00301 }
00302 return mMsg->AsString();
00303 }
00304
00305
00306 const DwMessage* KMMessage::asDwMessage()
00307 {
00308 if (mNeedsAssembly)
00309 {
00310 mNeedsAssembly = false;
00311 mMsg->Assemble();
00312 }
00313 return mMsg;
00314 }
00315
00316
00317 TQCString KMMessage::asString() const {
00318 return KMail::Util::CString( asDwString() );
00319 }
00320
00321
00322 TQByteArray KMMessage::asSendableString() const
00323 {
00324 KMMessage msg( new DwMessage( *this->mMsg ) );
00325 msg.removePrivateHeaderFields();
00326 msg.removeHeaderField("Bcc");
00327 return KMail::Util::ByteArray( msg.asDwString() );
00328 }
00329
00330 TQCString KMMessage::headerAsSendableString() const
00331 {
00332 KMMessage msg( new DwMessage( *this->mMsg ) );
00333 msg.removePrivateHeaderFields();
00334 msg.removeHeaderField("Bcc");
00335 return msg.headerAsString().latin1();
00336 }
00337
00338 void KMMessage::removePrivateHeaderFields() {
00339 removeHeaderField("Status");
00340 removeHeaderField("X-Status");
00341 removeHeaderField("X-KMail-EncryptionState");
00342 removeHeaderField("X-KMail-SignatureState");
00343 removeHeaderField("X-KMail-MDN-Sent");
00344 removeHeaderField("X-KMail-Transport");
00345 removeHeaderField("X-KMail-Identity");
00346 removeHeaderField("X-KMail-Fcc");
00347 removeHeaderField("X-KMail-Redirect-From");
00348 removeHeaderField("X-KMail-Link-Message");
00349 removeHeaderField("X-KMail-Link-Type");
00350 removeHeaderField( "X-KMail-Markup" );
00351 }
00352
00353
00354 void KMMessage::setStatusFields()
00355 {
00356 char str[2] = { 0, 0 };
00357
00358 setHeaderField("Status", status() & KMMsgStatusNew ? "R" : "RO");
00359 setHeaderField("X-Status", statusToStr(status()));
00360
00361 str[0] = (char)encryptionState();
00362 setHeaderField("X-KMail-EncryptionState", str);
00363
00364 str[0] = (char)signatureState();
00365
00366 setHeaderField("X-KMail-SignatureState", str);
00367
00368 str[0] = static_cast<char>( mdnSentState() );
00369 setHeaderField("X-KMail-MDN-Sent", str);
00370
00371
00372
00373 mNeedsAssembly = false;
00374 mMsg->Headers().Assemble();
00375 mMsg->Assemble( mMsg->Headers(),
00376 mMsg->Body() );
00377 }
00378
00379
00380
00381 TQString KMMessage::headerAsString() const
00382 {
00383 DwHeaders& header = mMsg->Headers();
00384 header.Assemble();
00385 if ( header.AsString().empty() )
00386 return TQString();
00387 return TQString::fromLatin1( header.AsString().c_str() );
00388 }
00389
00390
00391
00392 DwMediaType& KMMessage::dwContentType()
00393 {
00394 return mMsg->Headers().ContentType();
00395 }
00396
00397 void KMMessage::fromByteArray( const TQByteArray & ba, bool setStatus ) {
00398 return fromDwString( DwString( ba.data(), ba.size() ), setStatus );
00399 }
00400
00401 void KMMessage::fromString( const TQCString & str, bool aSeStatus ) {
00402 return fromDwString( KMail::Util::dwString( str ), aSeStatus );
00403 }
00404
00405 void KMMessage::fromDwString(const DwString& str, bool aSeStatus)
00406 {
00407 delete mMsg;
00408 mMsg = new DwMessage;
00409 mMsg->FromString( str );
00410 mMsg->Parse();
00411
00412 if (aSeStatus) {
00413 setStatus(headerField("Status").latin1(), headerField("X-Status").latin1());
00414 setEncryptionStateChar( headerField("X-KMail-EncryptionState").at(0) );
00415 setSignatureStateChar( headerField("X-KMail-SignatureState").at(0) );
00416 setMDNSentState( static_cast<KMMsgMDNSentState>( headerField("X-KMail-MDN-Sent").at(0).latin1() ) );
00417 }
00418 if ( invitationState() == KMMsgInvitationUnknown && readyToShow() )
00419 updateInvitationState();
00420 if ( attachmentState() == KMMsgAttachmentUnknown && readyToShow() )
00421 updateAttachmentState();
00422
00423 mNeedsAssembly = false;
00424 mDate = date();
00425 }
00426
00427
00428
00429 TQString KMMessage::formatString(const TQString& aStr) const
00430 {
00431 TQString result, str;
00432 TQChar ch;
00433 uint j;
00434
00435 if (aStr.isEmpty())
00436 return aStr;
00437
00438 unsigned int strLength(aStr.length());
00439 for (uint i=0; i<strLength;) {
00440 ch = aStr[i++];
00441 if (ch == '%') {
00442 ch = aStr[i++];
00443 switch ((char)ch) {
00444 case 'D':
00445
00446
00447
00448
00449 result += KMime::DateFormatter::formatDate( KMime::DateFormatter::Localized,
00450 date(), sReplyLanguage, false );
00451 break;
00452 case 'e':
00453 result += from();
00454 break;
00455 case 'F':
00456 result += fromStrip();
00457 break;
00458 case 'f':
00459 {
00460 str = fromStrip();
00461
00462 for (j=0; str[j]>' '; j++)
00463 ;
00464 unsigned int strLength(str.length());
00465 for (; j < strLength && str[j] <= ' '; j++)
00466 ;
00467 result += str[0];
00468 if (str[j]>' ')
00469 result += str[j];
00470 else
00471 if (str[1]>' ')
00472 result += str[1];
00473 }
00474 break;
00475 case 'T':
00476 result += toStrip();
00477 break;
00478 case 't':
00479 result += to();
00480 break;
00481 case 'C':
00482 result += ccStrip();
00483 break;
00484 case 'c':
00485 result += cc();
00486 break;
00487 case 'S':
00488 result += subject();
00489 break;
00490 case '_':
00491 result += ' ';
00492 break;
00493 case 'L':
00494 result += "\n";
00495 break;
00496 case '%':
00497 result += '%';
00498 break;
00499 default:
00500 result += '%';
00501 result += ch;
00502 break;
00503 }
00504 } else
00505 result += ch;
00506 }
00507 return result;
00508 }
00509
00510 static void removeTrailingSpace( TQString &line )
00511 {
00512 int i = line.length()-1;
00513 while( (i >= 0) && ((line[i] == ' ') || (line[i] == '\t')))
00514 i--;
00515 line.truncate( i+1);
00516 }
00517
00518 static TQString splitLine( TQString &line)
00519 {
00520 removeTrailingSpace( line );
00521 int i = 0;
00522 int j = -1;
00523 int l = line.length();
00524
00525
00526
00527 while(i < l)
00528 {
00529 TQChar c = line[i];
00530 if ((c == '>') || (c == ':') || (c == '|'))
00531 j = i+1;
00532 else if ((c != ' ') && (c != '\t'))
00533 break;
00534 i++;
00535 }
00536
00537 if ( j <= 0 )
00538 {
00539 return "";
00540 }
00541 if ( i == l )
00542 {
00543 TQString result = line.left(j);
00544 line = TQString();
00545 return result;
00546 }
00547
00548 TQString result = line.left(j);
00549 line = line.mid(j);
00550 return result;
00551 }
00552
00553 static TQString flowText(TQString &text, const TQString& indent, int maxLength)
00554 {
00555 maxLength--;
00556 if (text.isEmpty())
00557 {
00558 return indent+"<NULL>\n";
00559 }
00560 TQString result;
00561 while (1)
00562 {
00563 int i;
00564 if ((int) text.length() > maxLength)
00565 {
00566 i = maxLength;
00567 while( (i >= 0) && (text[i] != ' '))
00568 i--;
00569 if (i <= 0)
00570 {
00571
00572 i = maxLength;
00573
00574
00575 }
00576 }
00577 else
00578 {
00579 i = text.length();
00580 }
00581
00582 TQString line = text.left(i);
00583 if (i < (int) text.length())
00584 text = text.mid(i);
00585 else
00586 text = TQString();
00587
00588 result += indent + line + '\n';
00589
00590 if (text.isEmpty())
00591 return result;
00592 }
00593 }
00594
00595 static bool flushPart(TQString &msg, TQStringList &part,
00596 const TQString &indent, int maxLength)
00597 {
00598 maxLength -= indent.length();
00599 if (maxLength < 20) maxLength = 20;
00600
00601
00602 while ((part.begin() != part.end()) && part.last().isEmpty())
00603 {
00604 part.remove(part.fromLast());
00605 }
00606
00607 TQString text;
00608 for(TQStringList::Iterator it2 = part.begin();
00609 it2 != part.end();
00610 it2++)
00611 {
00612 TQString line = (*it2);
00613
00614 if (line.isEmpty())
00615 {
00616 if (!text.isEmpty())
00617 msg += flowText(text, indent, maxLength);
00618 msg += indent + '\n';
00619 }
00620 else
00621 {
00622 if (text.isEmpty())
00623 text = line;
00624 else
00625 text += ' '+line.stripWhiteSpace();
00626
00627 if (((int) text.length() < maxLength) || ((int) line.length() < (maxLength-10)))
00628 msg += flowText(text, indent, maxLength);
00629 }
00630 }
00631 if (!text.isEmpty())
00632 msg += flowText(text, indent, maxLength);
00633
00634 bool appendEmptyLine = true;
00635 if (!part.count())
00636 appendEmptyLine = false;
00637
00638 part.clear();
00639 return appendEmptyLine;
00640 }
00641
00642 static TQString stripSignature( const TQString & msg, bool clearSigned ) {
00643 if ( clearSigned )
00644 return msg.left( msg.findRev( TQRegExp( "\n--\\s?\n" ) ) );
00645 else
00646 return msg.left( msg.findRev( "\n-- \n" ) );
00647 }
00648
00649 TQString KMMessage::smartQuote( const TQString & msg, int maxLineLength )
00650 {
00651 TQStringList part;
00652 TQString oldIndent;
00653 bool firstPart = true;
00654
00655
00656 const TQStringList lines = TQStringList::split('\n', msg, true);
00657
00658 TQString result;
00659 for(TQStringList::const_iterator it = lines.begin();
00660 it != lines.end();
00661 ++it)
00662 {
00663 TQString line = *it;
00664
00665 const TQString indent = splitLine( line );
00666
00667 if ( line.isEmpty())
00668 {
00669 if (!firstPart)
00670 part.append(TQString());
00671 continue;
00672 };
00673
00674 if (firstPart)
00675 {
00676 oldIndent = indent;
00677 firstPart = false;
00678 }
00679
00680 if (oldIndent != indent)
00681 {
00682 TQString fromLine;
00683
00684 if (part.count() && (oldIndent.length() < indent.length()))
00685 {
00686 TQStringList::Iterator it2 = part.fromLast();
00687 while( (it2 != part.end()) && (*it2).isEmpty())
00688 --it2;
00689
00690 if ((it2 != part.end()) && ((*it2).endsWith(":")))
00691 {
00692 fromLine = oldIndent + (*it2) + '\n';
00693 part.remove(it2);
00694 }
00695 }
00696 if (flushPart( result, part, oldIndent, maxLineLength))
00697 {
00698 if (oldIndent.length() > indent.length())
00699 result += indent + '\n';
00700 else
00701 result += oldIndent + '\n';
00702 }
00703 if (!fromLine.isEmpty())
00704 {
00705 result += fromLine;
00706 }
00707 oldIndent = indent;
00708 }
00709 part.append(line);
00710 }
00711 flushPart( result, part, oldIndent, maxLineLength);
00712 return result;
00713 }
00714
00715
00716
00717 void KMMessage::parseTextStringFromDwPart( partNode * root,
00718 TQCString& parsedString,
00719 const TQTextCodec*& codec,
00720 bool& isHTML ) const
00721 {
00722 if ( !root ) return;
00723
00724 isHTML = false;
00725 partNode * curNode = root->findType( DwMime::kTypeText,
00726 DwMime::kSubtypeUnknown,
00727 true,
00728 false );
00729 kdDebug(5006) << "\n\n======= KMMessage::parseTextStringFromDwPart() - "
00730 << ( curNode ? "text part found!\n" : "sorry, no text node!\n" ) << endl;
00731 if( curNode ) {
00732 isHTML = DwMime::kSubtypeHtml == curNode->subType();
00733
00734 ObjectTreeParser otp( 0, 0, true, false, true );
00735 otp.parseObjectTree( curNode );
00736 parsedString = otp.rawReplyString();
00737 codec = curNode->msgPart().codec();
00738 }
00739 }
00740
00741
00742
00743 TQString KMMessage::asPlainTextFromObjectTree( partNode *root, bool aStripSignature,
00744 bool allowDecryption ) const
00745 {
00746 Q_ASSERT( root );
00747 Q_ASSERT( root->processed() );
00748
00749 TQCString parsedString;
00750 bool isHTML = false;
00751 const TQTextCodec * codec = 0;
00752
00753 if ( !root ) return TQString();
00754 parseTextStringFromDwPart( root, parsedString, codec, isHTML );
00755
00756 if ( mOverrideCodec || !codec )
00757 codec = this->codec();
00758
00759 if ( parsedString.isEmpty() )
00760 return TQString();
00761
00762 bool clearSigned = false;
00763 TQString result;
00764
00765
00766 if ( allowDecryption ) {
00767 TQPtrList<Kpgp::Block> pgpBlocks;
00768 TQStrList nonPgpBlocks;
00769 if ( Kpgp::Module::prepareMessageForDecryption( parsedString,
00770 pgpBlocks,
00771 nonPgpBlocks ) ) {
00772
00773
00774 if ( pgpBlocks.count() == 1 ) {
00775 Kpgp::Block * block = pgpBlocks.first();
00776 if ( block->type() == Kpgp::PgpMessageBlock ||
00777 block->type() == Kpgp::ClearsignedBlock ) {
00778 if ( block->type() == Kpgp::PgpMessageBlock ) {
00779
00780 block->decrypt();
00781 } else {
00782
00783 block->verify();
00784 clearSigned = true;
00785 }
00786
00787 result = codec->toUnicode( nonPgpBlocks.first() )
00788 + codec->toUnicode( block->text() )
00789 + codec->toUnicode( nonPgpBlocks.last() );
00790 }
00791 }
00792 }
00793 }
00794
00795 if ( result.isEmpty() ) {
00796 result = codec->toUnicode( parsedString );
00797 if ( result.isEmpty() )
00798 return result;
00799 }
00800
00801
00802 if ( isHTML && mDecodeHTML ) {
00803 TDEHTMLPart htmlPart;
00804 htmlPart.setOnlyLocalReferences( true );
00805 htmlPart.setMetaRefreshEnabled( false );
00806 htmlPart.setPluginsEnabled( false );
00807 htmlPart.setJScriptEnabled( false );
00808 htmlPart.setJavaEnabled( false );
00809 htmlPart.begin();
00810 htmlPart.write( result );
00811 htmlPart.end();
00812 htmlPart.selectAll();
00813 result = htmlPart.selectedText();
00814 }
00815
00816
00817 if ( aStripSignature )
00818 return stripSignature( result, clearSigned );
00819 else
00820 return result;
00821 }
00822
00823
00824
00825 TQString KMMessage::asPlainText( bool aStripSignature, bool allowDecryption ) const
00826 {
00827 partNode *root = partNode::fromMessage( this );
00828 if ( !root )
00829 return TQString();
00830
00831 ObjectTreeParser otp;
00832 otp.parseObjectTree( root );
00833 TQString result = asPlainTextFromObjectTree( root, aStripSignature, allowDecryption );
00834 delete root;
00835 return result;
00836 }
00837
00838 TQString KMMessage::asQuotedString( const TQString& aHeaderStr,
00839 const TQString& aIndentStr,
00840 const TQString& selection ,
00841 bool aStripSignature ,
00842 bool allowDecryption ) const
00843 {
00844 TQString content = selection.isEmpty() ?
00845 asPlainText( aStripSignature, allowDecryption ) : selection ;
00846
00847
00848 const int firstNonWS = content.find( TQRegExp( "\\S" ) );
00849 const int lineStart = content.findRev( '\n', firstNonWS );
00850 if ( lineStart >= 0 )
00851 content.remove( 0, static_cast<unsigned int>( lineStart ) );
00852
00853 const TQString indentStr = formatString( aIndentStr );
00854
00855 content.replace( '\n', '\n' + indentStr );
00856 content.prepend( indentStr );
00857 content += '\n';
00858
00859 const TQString headerStr = formatString( aHeaderStr );
00860 if ( sSmartQuote && sWordWrap )
00861 return headerStr + smartQuote( content, sWrapCol );
00862 return headerStr + content;
00863 }
00864
00865
00866 KMMessage* KMMessage::createReply( KMail::ReplyStrategy replyStrategy,
00867 TQString selection ,
00868 bool noQuote ,
00869 bool allowDecryption ,
00870 const TQString &tmpl ,
00871 const TQString &originatingAccount )
00872 {
00873 KMMessage* msg = new KMMessage;
00874 TQString mailingListStr, replyToStr, toStr;
00875 TQStringList mailingListAddresses;
00876 TQCString refStr, headerName;
00877 bool replyAll = true;
00878
00879 msg->initFromMessage(this);
00880
00881 MailingList::name(this, headerName, mailingListStr);
00882 replyToStr = replyTo();
00883
00884 msg->setCharset("utf-8");
00885
00886
00887 if ( parent() && parent()->isMailingListEnabled() &&
00888 !parent()->mailingListPostAddress().isEmpty() ) {
00889 mailingListAddresses << parent()->mailingListPostAddress();
00890 }
00891 if ( headerField("List-Post").find( "mailto:", 0, false ) != -1 ) {
00892 TQString listPost = headerField("List-Post");
00893 TQRegExp rx( "<mailto:([^@>]+)@([^>]+)>", false );
00894 if ( rx.search( listPost, 0 ) != -1 )
00895 mailingListAddresses << rx.cap(1) + '@' + rx.cap(2);
00896 }
00897
00898
00899 switch( replyStrategy ) {
00900 case KMail::ReplySmart : {
00901 if ( !headerField( "Mail-Followup-To" ).isEmpty() ) {
00902 toStr = headerField( "Mail-Followup-To" );
00903 }
00904 else if ( !replyToStr.isEmpty() ) {
00905
00906 toStr = replyToStr;
00907 }
00908 else if ( !mailingListAddresses.isEmpty() ) {
00909 toStr = mailingListAddresses[0];
00910 }
00911 else {
00912
00913 toStr = from();
00914
00915 replyAll = false;
00916 }
00917
00918 TQStringList recipients = KPIM::splitEmailAddrList( toStr );
00919 toStr = stripMyAddressesFromAddressList( recipients ).join(", ");
00920
00921 if ( toStr.isEmpty() && !recipients.isEmpty() )
00922 toStr = recipients[0];
00923
00924 break;
00925 }
00926 case KMail::ReplyList : {
00927 if ( !headerField( "Mail-Followup-To" ).isEmpty() ) {
00928 toStr = headerField( "Mail-Followup-To" );
00929 }
00930 else if ( !mailingListAddresses.isEmpty() ) {
00931 toStr = mailingListAddresses[0];
00932 }
00933 else if ( !replyToStr.isEmpty() ) {
00934
00935 toStr = replyToStr;
00936 }
00937
00938 TQStringList recipients = KPIM::splitEmailAddrList( toStr );
00939 toStr = stripMyAddressesFromAddressList( recipients ).join(", ");
00940
00941 break;
00942 }
00943 case KMail::ReplyAll : {
00944 TQStringList recipients;
00945 TQStringList ccRecipients;
00946
00947
00948 if( !replyToStr.isEmpty() ) {
00949 recipients += KPIM::splitEmailAddrList( replyToStr );
00950
00951
00952 for ( TQStringList::const_iterator it = mailingListAddresses.begin();
00953 it != mailingListAddresses.end();
00954 ++it ) {
00955 recipients = stripAddressFromAddressList( *it, recipients );
00956 }
00957 }
00958
00959 if ( !mailingListAddresses.isEmpty() ) {
00960
00961 if ( recipients.isEmpty() && !from().isEmpty() ) {
00962
00963
00964 ccRecipients += from();
00965 kdDebug(5006) << "Added " << from() << " to the list of CC recipients"
00966 << endl;
00967 }
00968
00969 recipients.prepend( mailingListAddresses[0] );
00970 }
00971 else {
00972
00973 if ( recipients.isEmpty() && !from().isEmpty() ) {
00974
00975
00976 recipients += from();
00977 kdDebug(5006) << "Added " << from() << " to the list of recipients"
00978 << endl;
00979 }
00980 }
00981
00982
00983 toStr = stripMyAddressesFromAddressList( recipients ).join(", ");
00984
00985
00986 if( !cc().isEmpty() || !to().isEmpty() ) {
00987 TQStringList list;
00988 if (!to().isEmpty())
00989 list += KPIM::splitEmailAddrList(to());
00990 if (!cc().isEmpty())
00991 list += KPIM::splitEmailAddrList(cc());
00992 for( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
00993 if( !addressIsInAddressList( *it, recipients )
00994 && !addressIsInAddressList( *it, ccRecipients ) ) {
00995 ccRecipients += *it;
00996 kdDebug(5006) << "Added " << *it << " to the list of CC recipients"
00997 << endl;
00998 }
00999 }
01000 }
01001
01002 if ( !ccRecipients.isEmpty() ) {
01003
01004 ccRecipients = stripMyAddressesFromAddressList( ccRecipients );
01005
01006
01007
01008 if ( toStr.isEmpty() && !ccRecipients.isEmpty() ) {
01009 toStr = ccRecipients[0];
01010 ccRecipients.pop_front();
01011 }
01012
01013 msg->setCc( ccRecipients.join(", ") );
01014 }
01015
01016 if ( toStr.isEmpty() && !recipients.isEmpty() ) {
01017
01018 toStr = recipients[0];
01019 }
01020 break;
01021 }
01022 case KMail::ReplyAuthor : {
01023 if ( !replyToStr.isEmpty() ) {
01024 TQStringList recipients = KPIM::splitEmailAddrList( replyToStr );
01025
01026
01027 for ( TQStringList::const_iterator it = mailingListAddresses.begin();
01028 it != mailingListAddresses.end();
01029 ++it ) {
01030 recipients = stripAddressFromAddressList( *it, recipients );
01031 }
01032 if ( !recipients.isEmpty() ) {
01033 toStr = recipients.join(", ");
01034 }
01035 else {
01036
01037
01038 toStr = from();
01039 }
01040 }
01041 else if ( !from().isEmpty() ) {
01042 toStr = from();
01043 }
01044 replyAll = false;
01045 break;
01046 }
01047 case KMail::ReplyNone : {
01048
01049 }
01050 }
01051
01052 if (!originatingAccount.isEmpty()) {
01053 msg->setOriginatingAccountName(originatingAccount);
01054 }
01055
01056 msg->setTo(toStr);
01057
01058 refStr = getRefStr();
01059 if (!refStr.isEmpty())
01060 msg->setReferences(refStr);
01061
01062 msg->setReplyToId(msgId());
01063
01064
01065
01066
01067
01068
01069
01070
01071
01072
01073
01074 msg->setSubject( replySubject() );
01075 msg->setHeaderField( "X-KMail-QuotePrefix",
01076 formatString( GlobalSettings::self()->quoteString() ) );
01077 if( !noQuote ) {
01078 TemplateParser parser( msg, ( replyAll ? TemplateParser::ReplyAll : TemplateParser::Reply ) );
01079 parser.setAllowDecryption( allowDecryption );
01080 if ( GlobalSettings::quoteSelectionOnly() ) {
01081 parser.setSelection( selection );
01082 }
01083 if ( !tmpl.isEmpty() ) {
01084 parser.process( tmpl, this );
01085 } else {
01086 parser.process( this );
01087 }
01088 }
01089
01090 msg->link(this, KMMsgStatusReplied);
01091
01092 if ( parent() && parent()->putRepliesInSameFolder() )
01093 msg->setFcc( parent()->idString() );
01094
01095
01096 if ( encryptionState() == KMMsgPartiallyEncrypted ||
01097 encryptionState() == KMMsgFullyEncrypted ) {
01098 msg->setEncryptionState( KMMsgFullyEncrypted );
01099 }
01100
01101 return msg;
01102 }
01103
01104
01105
01106 TQCString KMMessage::getRefStr() const
01107 {
01108 TQCString firstRef, lastRef, refStr, retRefStr;
01109 int i, j;
01110
01111 refStr = headerField("References").stripWhiteSpace().latin1();
01112
01113 if (refStr.isEmpty())
01114 return headerField("Message-Id").latin1();
01115
01116 i = refStr.find('<');
01117 j = refStr.find('>');
01118 firstRef = refStr.mid(i, j-i+1);
01119 if (!firstRef.isEmpty())
01120 retRefStr = firstRef + ' ';
01121
01122 i = refStr.findRev('<');
01123 j = refStr.findRev('>');
01124
01125 lastRef = refStr.mid(i, j-i+1);
01126 if (!lastRef.isEmpty() && lastRef != firstRef)
01127 retRefStr += lastRef + ' ';
01128
01129 retRefStr += headerField("Message-Id").latin1();
01130 return retRefStr;
01131 }
01132
01133
01134 KMMessage* KMMessage::createRedirect( const TQString &toStr )
01135 {
01136
01137 KMMessage* msg = new KMMessage( new DwMessage( *this->mMsg ) );
01138 KMMessagePart msgPart;
01139
01140 uint id = 0;
01141 TQString strId = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace();
01142 if ( !strId.isEmpty())
01143 id = strId.toUInt();
01144 const KPIM::Identity & ident =
01145 kmkernel->identityManager()->identityForUoidOrDefault( id );
01146
01147
01148 TQString strByWayOf = TQString("%1 (by way of %2 <%3>)")
01149 .arg( from() )
01150 .arg( ident.fullName() )
01151 .arg( ident.primaryEmailAddress() );
01152
01153
01154 TQString strFrom = TQString("%1 <%2>")
01155 .arg( ident.fullName() )
01156 .arg( ident.primaryEmailAddress() );
01157
01158
01159 TQString origDate = msg->headerField( "Date" );
01160 msg->setDateToday();
01161 TQString newDate = msg->headerField( "Date" );
01162
01163 if ( origDate.isEmpty() )
01164 msg->removeHeaderField( "Date" );
01165 else
01166 msg->setHeaderField( "Date", origDate );
01167
01168
01169 msg->setHeaderField( "Resent-Message-ID", generateMessageId( msg->sender() ),
01170 Structured, true);
01171 msg->setHeaderField( "Resent-Date", newDate, Structured, true );
01172 msg->setHeaderField( "Resent-To", toStr, Address, true );
01173 msg->setHeaderField( "Resent-From", strFrom, Address, true );
01174
01175 msg->setHeaderField( "X-KMail-Redirect-From", strByWayOf );
01176 msg->setHeaderField( "X-KMail-Recipients", toStr, Address );
01177
01178 msg->link(this, KMMsgStatusForwarded);
01179
01180 return msg;
01181 }
01182
01183
01184
01185 TQCString KMMessage::createForwardBody()
01186 {
01187 TQString s;
01188 TQCString str;
01189
01190 if (sHeaderStrategy == HeaderStrategy::all()) {
01191 s = "\n\n---------- " + sForwardStr + " ----------\n\n";
01192 s += headerAsString();
01193 str = asQuotedString(s, "", TQString(), false, false).utf8();
01194 str += "\n-------------------------------------------------------\n";
01195 } else {
01196 s = "\n\n---------- " + sForwardStr + " ----------\n\n";
01197 s += "Subject: " + subject() + "\n";
01198 s += "Date: "
01199 + KMime::DateFormatter::formatDate( KMime::DateFormatter::Localized,
01200 date(), sReplyLanguage, false )
01201 + "\n";
01202 s += "From: " + from() + "\n";
01203 s += "To: " + to() + "\n";
01204 if (!cc().isEmpty()) s += "Cc: " + cc() + "\n";
01205 s += "\n";
01206 str = asQuotedString(s, "", TQString(), false, false).utf8();
01207 str += "\n-------------------------------------------------------\n";
01208 }
01209
01210 return str;
01211 }
01212
01213 void KMMessage::sanitizeHeaders( const TQStringList& whiteList )
01214 {
01215
01216
01217 DwHeaders& header = mMsg->Headers();
01218 DwField* field = header.FirstField();
01219 DwField* nextField;
01220 while (field)
01221 {
01222 nextField = field->Next();
01223 if ( field->FieldNameStr().find( "ontent" ) == DwString::npos
01224 && !whiteList.contains( TQString::fromLatin1( field->FieldNameStr().c_str() ) ) )
01225 header.RemoveField(field);
01226 field = nextField;
01227 }
01228 mMsg->Assemble();
01229 }
01230
01231
01232 KMMessage* KMMessage::createForward( const TQString &tmpl )
01233 {
01234 KMMessage* msg = new KMMessage();
01235
01236
01237
01238
01239 if ( type() == DwMime::kTypeMultipart ||
01240 ( type() == DwMime::kTypeText && subtype() == DwMime::kSubtypePlain ) ) {
01241
01242 msg->fromDwString( this->asDwString() );
01243
01244
01245 DwMediaType oldContentType = msg->mMsg->Headers().ContentType();
01246
01247 msg->sanitizeHeaders();
01248
01249
01250 TQStringList blacklist = GlobalSettings::self()->mimetypesToStripWhenInlineForwarding();
01251 for ( TQStringList::Iterator it = blacklist.begin(); it != blacklist.end(); ++it ) {
01252 TQString entry = (*it);
01253 int sep = entry.find( '/' );
01254 TQCString type = entry.left( sep ).latin1();
01255 TQCString subtype = entry.mid( sep+1 ).latin1();
01256 kdDebug( 5006 ) << "Looking for blacklisted type: " << type << "/" << subtype << endl;
01257 while ( DwBodyPart * part = msg->findDwBodyPart( type, subtype ) ) {
01258 msg->mMsg->Body().RemoveBodyPart( part );
01259 }
01260 }
01261 msg->mMsg->Assemble();
01262 msg->initFromMessage( this );
01263
01264
01265 msg->mMsg->Headers().ContentType().FromString( oldContentType.AsString() );
01266 msg->mMsg->Headers().ContentType().Parse();
01267 msg->mMsg->Assemble();
01268 }
01269 else if( type() == DwMime::kTypeText && subtype() == DwMime::kSubtypeHtml ) {
01270
01271
01272 msg->initFromMessage( this );
01273 msg->setType( DwMime::kTypeText );
01274 msg->setSubtype( DwMime::kSubtypeHtml );
01275 msg->mNeedsAssembly = true;
01276 msg->cleanupHeader();
01277 }
01278 else {
01279
01280
01281 msg->initFromMessage( this );
01282 msg->removeHeaderField("Content-Type");
01283 msg->removeHeaderField("Content-Transfer-Encoding");
01284
01285 DwHeaders & header = msg->mMsg->Headers();
01286 header.MimeVersion().FromString("1.0");
01287 DwMediaType & contentType = msg->dwContentType();
01288 contentType.SetType( DwMime::kTypeMultipart );
01289 contentType.SetSubtype( DwMime::kSubtypeMixed );
01290 contentType.CreateBoundary(0);
01291 contentType.Assemble();
01292
01293
01294 KMMessagePart msgPart;
01295 bodyPart( 0, &msgPart );
01296 msg->addBodyPart(&msgPart);
01297
01298 KMMessagePart secondPart;
01299 secondPart.setType( type() );
01300 secondPart.setSubtype( subtype() );
01301 secondPart.setBody( mMsg->Body().AsString() );
01302
01303 applyHeadersToMessagePart( mMsg->Headers(), &secondPart );
01304 msg->addBodyPart(&secondPart);
01305 msg->mNeedsAssembly = true;
01306 msg->cleanupHeader();
01307 }
01308
01309
01310 msg->setSubject( forwardSubject() );
01311
01312 TemplateParser parser( msg, TemplateParser::Forward );
01313 if ( !tmpl.isEmpty() ) {
01314 parser.process( tmpl, this );
01315 } else {
01316 parser.process( this );
01317 }
01318
01319
01320
01321
01322
01323
01324
01325
01326 msg->link(this, KMMsgStatusForwarded);
01327 return msg;
01328 }
01329
01330 static const struct {
01331 const char * dontAskAgainID;
01332 bool canDeny;
01333 const char * text;
01334 } mdnMessageBoxes[] = {
01335 { "mdnNormalAsk", true,
01336 I18N_NOOP("This message contains a request to return a notification "
01337 "about your reception of the message.\n"
01338 "You can either ignore the request or let KMail send a "
01339 "\"denied\" or normal response.") },
01340 { "mdnUnknownOption", false,
01341 I18N_NOOP("This message contains a request to send a notification "
01342 "about your reception of the message.\n"
01343 "It contains a processing instruction that is marked as "
01344 "\"required\", but which is unknown to KMail.\n"
01345 "You can either ignore the request or let KMail send a "
01346 "\"failed\" response.") },
01347 { "mdnMultipleAddressesInReceiptTo", true,
01348 I18N_NOOP("This message contains a request to send a notification "
01349 "about your reception of the message,\n"
01350 "but it is requested to send the notification to more "
01351 "than one address.\n"
01352 "You can either ignore the request or let KMail send a "
01353 "\"denied\" or normal response.") },
01354 { "mdnReturnPathEmpty", true,
01355 I18N_NOOP("This message contains a request to send a notification "
01356 "about your reception of the message,\n"
01357 "but there is no return-path set.\n"
01358 "You can either ignore the request or let KMail send a "
01359 "\"denied\" or normal response.") },
01360 { "mdnReturnPathNotInReceiptTo", true,
01361 I18N_NOOP("This message contains a request to send a notification "
01362 "about your reception of the message,\n"
01363 "but the return-path address differs from the address "
01364 "the notification was requested to be sent to.\n"
01365 "You can either ignore the request or let KMail send a "
01366 "\"denied\" or normal response.") },
01367 };
01368
01369 static const int numMdnMessageBoxes
01370 = sizeof mdnMessageBoxes / sizeof *mdnMessageBoxes;
01371
01372
01373 static int requestAdviceOnMDN( const char * what ) {
01374 for ( int i = 0 ; i < numMdnMessageBoxes ; ++i ) {
01375 if ( !qstrcmp( what, mdnMessageBoxes[i].dontAskAgainID ) ) {
01376 if ( mdnMessageBoxes[i].canDeny ) {
01377 const KCursorSaver saver( TQCursor::ArrowCursor );
01378 int answer = TQMessageBox::information( 0,
01379 i18n("Message Disposition Notification Request"),
01380 i18n( mdnMessageBoxes[i].text ),
01381 i18n("&Ignore"), i18n("Send \"&denied\""), i18n("&Send") );
01382 return answer ? answer + 1 : 0 ;
01383 } else {
01384 const KCursorSaver saver( TQCursor::ArrowCursor );
01385 int answer = TQMessageBox::information( 0,
01386 i18n("Message Disposition Notification Request"),
01387 i18n( mdnMessageBoxes[i].text ),
01388 i18n("&Ignore"), i18n("&Send") );
01389 return answer ? answer + 2 : 0 ;
01390 }
01391 }
01392 }
01393 kdWarning(5006) << "didn't find data for message box \""
01394 << what << "\"" << endl;
01395 return 0;
01396 }
01397
01398 KMMessage* KMMessage::createMDN( MDN::ActionMode a,
01399 MDN::DispositionType d,
01400 bool allowGUI,
01401 TQValueList<MDN::DispositionModifier> m )
01402 {
01403
01404
01405
01406
01407
01408
01409 #ifndef MDN_DEBUG
01410 if ( mdnSentState() != KMMsgMDNStateUnknown &&
01411 mdnSentState() != KMMsgMDNNone )
01412 return 0;
01413 #else
01414 char st[2]; st[0] = (char)mdnSentState(); st[1] = 0;
01415 kdDebug(5006) << "mdnSentState() == '" << st << "'" << endl;
01416 #endif
01417
01418
01419 if ( findDwBodyPart( DwMime::kTypeMessage,
01420 DwMime::kSubtypeDispositionNotification ) ) {
01421 setMDNSentState( KMMsgMDNIgnore );
01422 return 0;
01423 }
01424
01425
01426 TQString receiptTo = headerField("Disposition-Notification-To");
01427 if ( receiptTo.stripWhiteSpace().isEmpty() ) return 0;
01428 receiptTo.remove( '\n' );
01429
01430
01431 MDN::SendingMode s = MDN::SentAutomatically;
01432 TQString special;
01433 TDEConfigGroup mdnConfig( KMKernel::config(), "MDN" );
01434
01435
01436 int mode = mdnConfig.readNumEntry( "default-policy", 0 );
01437 if ( !mode || mode < 0 || mode > 3 ) {
01438
01439 setMDNSentState( KMMsgMDNIgnore );
01440 return 0;
01441 }
01442
01443
01444
01445
01446
01447
01448
01449 TQString notificationOptions = headerField("Disposition-Notification-Options");
01450 if ( notificationOptions.contains( "required", false ) ) {
01451
01452
01453
01454 if ( !allowGUI ) return 0;
01455 mode = requestAdviceOnMDN( "mdnUnknownOption" );
01456 s = MDN::SentManually;
01457
01458 special = i18n("Header \"Disposition-Notification-Options\" contained "
01459 "required, but unknown parameter");
01460 d = MDN::Failed;
01461 m.clear();
01462 }
01463
01464
01465
01466
01467 kdDebug(5006) << "KPIM::splitEmailAddrList(receiptTo): "
01468 << KPIM::splitEmailAddrList(receiptTo).join("\n") << endl;
01469 if ( KPIM::splitEmailAddrList(receiptTo).count() > 1 ) {
01470 if ( !allowGUI ) return 0;
01471 mode = requestAdviceOnMDN( "mdnMultipleAddressesInReceiptTo" );
01472 s = MDN::SentManually;
01473 }
01474
01475
01476
01477
01478
01479
01480 AddrSpecList returnPathList = extractAddrSpecs("Return-Path");
01481 TQString returnPath = returnPathList.isEmpty() ? TQString()
01482 : returnPathList.front().localPart + '@' + returnPathList.front().domain ;
01483 kdDebug(5006) << "clean return path: " << returnPath << endl;
01484 if ( returnPath.isEmpty() || !receiptTo.contains( returnPath, false ) ) {
01485 if ( !allowGUI ) return 0;
01486 mode = requestAdviceOnMDN( returnPath.isEmpty() ?
01487 "mdnReturnPathEmpty" :
01488 "mdnReturnPathNotInReceiptTo" );
01489 s = MDN::SentManually;
01490 }
01491
01492 if ( a != KMime::MDN::AutomaticAction ) {
01493
01494 if ( mode == 1 ) {
01495 if ( !allowGUI ) return 0;
01496 mode = requestAdviceOnMDN( "mdnNormalAsk" );
01497 s = MDN::SentManually;
01498 }
01499
01500 switch ( mode ) {
01501 case 0:
01502 setMDNSentState( KMMsgMDNIgnore );
01503 return 0;
01504 default:
01505 case 1:
01506 kdFatal(5006) << "KMMessage::createMDN(): The \"ask\" mode should "
01507 << "never appear here!" << endl;
01508 break;
01509 case 2:
01510 d = MDN::Denied;
01511 m.clear();
01512 break;
01513 case 3:
01514 break;
01515 }
01516 }
01517
01518
01519
01520 TQString finalRecipient = kmkernel->identityManager()
01521 ->identityForUoidOrDefault( identityUoid() ).fullEmailAddr();
01522
01523
01524
01525
01526
01527 KMMessage * receipt = new KMMessage();
01528 receipt->initFromMessage( this );
01529 receipt->removeHeaderField("Content-Type");
01530 receipt->removeHeaderField("Content-Transfer-Encoding");
01531
01532 DwHeaders & header = receipt->mMsg->Headers();
01533 header.MimeVersion().FromString("1.0");
01534 DwMediaType & contentType = receipt->dwContentType();
01535 contentType.SetType( DwMime::kTypeMultipart );
01536 contentType.SetSubtype( DwMime::kSubtypeReport );
01537 contentType.CreateBoundary(0);
01538 receipt->mNeedsAssembly = true;
01539 receipt->setContentTypeParam( "report-type", "disposition-notification" );
01540
01541 TQString description = replaceHeadersInString( MDN::descriptionFor( d, m ) );
01542
01543
01544 KMMessagePart firstMsgPart;
01545 firstMsgPart.setTypeStr( "text" );
01546 firstMsgPart.setSubtypeStr( "plain" );
01547 firstMsgPart.setBodyFromUnicode( description );
01548 receipt->addBodyPart( &firstMsgPart );
01549
01550
01551 KMMessagePart secondMsgPart;
01552 secondMsgPart.setType( DwMime::kTypeMessage );
01553 secondMsgPart.setSubtype( DwMime::kSubtypeDispositionNotification );
01554
01555
01556 secondMsgPart.setBodyEncoded( MDN::dispositionNotificationBodyContent(
01557 finalRecipient,
01558 rawHeaderField("Original-Recipient"),
01559 id(),
01560 d, a, s, m, special ) );
01561 receipt->addBodyPart( &secondMsgPart );
01562
01563
01564 int num = mdnConfig.readNumEntry( "quote-message", 0 );
01565 if ( num < 0 || num > 2 ) num = 0;
01566 MDN::ReturnContent returnContent = static_cast<MDN::ReturnContent>( num );
01567
01568 KMMessagePart thirdMsgPart;
01569 switch ( returnContent ) {
01570 case MDN::All:
01571 thirdMsgPart.setTypeStr( "message" );
01572 thirdMsgPart.setSubtypeStr( "rfc822" );
01573 thirdMsgPart.setBody( asSendableString() );
01574 receipt->addBodyPart( &thirdMsgPart );
01575 break;
01576 case MDN::HeadersOnly:
01577 thirdMsgPart.setTypeStr( "text" );
01578 thirdMsgPart.setSubtypeStr( "rfc822-headers" );
01579 thirdMsgPart.setBody( headerAsSendableString() );
01580 receipt->addBodyPart( &thirdMsgPart );
01581 break;
01582 case MDN::Nothing:
01583 default:
01584 break;
01585 };
01586
01587 receipt->setTo( receiptTo );
01588 receipt->setSubject( "Message Disposition Notification" );
01589 receipt->setReplyToId( msgId() );
01590 receipt->setReferences( getRefStr() );
01591
01592 receipt->cleanupHeader();
01593
01594 kdDebug(5006) << "final message:\n" + receipt->asString() << endl;
01595
01596
01597
01598
01599 KMMsgMDNSentState state = KMMsgMDNStateUnknown;
01600 switch ( d ) {
01601 case MDN::Displayed: state = KMMsgMDNDisplayed; break;
01602 case MDN::Deleted: state = KMMsgMDNDeleted; break;
01603 case MDN::Dispatched: state = KMMsgMDNDispatched; break;
01604 case MDN::Processed: state = KMMsgMDNProcessed; break;
01605 case MDN::Denied: state = KMMsgMDNDenied; break;
01606 case MDN::Failed: state = KMMsgMDNFailed; break;
01607 };
01608 setMDNSentState( state );
01609
01610 return receipt;
01611 }
01612
01613 TQString KMMessage::replaceHeadersInString( const TQString & s ) const {
01614 TQString result = s;
01615 TQRegExp rx( "\\$\\{([a-z0-9-]+)\\}", false );
01616 Q_ASSERT( rx.isValid() );
01617
01618 TQRegExp rxDate( "\\$\\{date\\}" );
01619 Q_ASSERT( rxDate.isValid() );
01620
01621 TQString sDate = KMime::DateFormatter::formatDate(
01622 KMime::DateFormatter::Localized, date() );
01623
01624 int idx = 0;
01625 if( ( idx = rxDate.search( result, idx ) ) != -1 ) {
01626 result.replace( idx, rxDate.matchedLength(), sDate );
01627 }
01628
01629 idx = 0;
01630 while ( ( idx = rx.search( result, idx ) ) != -1 ) {
01631 TQString replacement = headerField( TQString(rx.cap(1)).latin1() );
01632 result.replace( idx, rx.matchedLength(), replacement );
01633 idx += replacement.length();
01634 }
01635 return result;
01636 }
01637
01638 KMMessage* KMMessage::createDeliveryReceipt() const
01639 {
01640 TQString str, receiptTo;
01641 KMMessage *receipt;
01642
01643 receiptTo = headerField("Disposition-Notification-To");
01644 if ( receiptTo.stripWhiteSpace().isEmpty() ) return 0;
01645 receiptTo.remove( '\n' );
01646
01647 receipt = new KMMessage;
01648 receipt->initFromMessage(this);
01649 receipt->setTo(receiptTo);
01650 receipt->setSubject(i18n("Receipt: ") + subject());
01651
01652 str = "Your message was successfully delivered.";
01653 str += "\n\n---------- Message header follows ----------\n";
01654 str += headerAsString();
01655 str += "--------------------------------------------\n";
01656
01657
01658 receipt->setBody(str.latin1());
01659 receipt->setAutomaticFields();
01660
01661 return receipt;
01662 }
01663
01664
01665 void KMMessage::applyIdentity( uint id )
01666 {
01667 const KPIM::Identity & ident =
01668 kmkernel->identityManager()->identityForUoidOrDefault( id );
01669
01670 if(ident.fullEmailAddr().isEmpty())
01671 setFrom("");
01672 else
01673 setFrom(ident.fullEmailAddr());
01674
01675 if(ident.replyToAddr().isEmpty())
01676 setReplyTo("");
01677 else
01678 setReplyTo(ident.replyToAddr());
01679
01680 if(ident.bcc().isEmpty())
01681 setBcc("");
01682 else
01683 setBcc(ident.bcc());
01684
01685 if (ident.organization().isEmpty())
01686 removeHeaderField("Organization");
01687 else
01688 setHeaderField("Organization", ident.organization());
01689
01690 if (ident.isDefault())
01691 removeHeaderField("X-KMail-Identity");
01692 else
01693 setHeaderField("X-KMail-Identity", TQString::number( ident.uoid() ));
01694
01695 if ( ident.transport().isEmpty() )
01696 removeHeaderField( "X-KMail-Transport" );
01697 else
01698 setHeaderField( "X-KMail-Transport", ident.transport() );
01699
01700 if ( ident.fcc().isEmpty() )
01701 setFcc( TQString() );
01702 else
01703 setFcc( ident.fcc() );
01704
01705 if ( ident.drafts().isEmpty() )
01706 setDrafts( TQString() );
01707 else
01708 setDrafts( ident.drafts() );
01709
01710 if ( ident.templates().isEmpty() )
01711 setTemplates( TQString() );
01712 else
01713 setTemplates( ident.templates() );
01714
01715 }
01716
01717
01718 void KMMessage::initHeader( uint id )
01719 {
01720 applyIdentity( id );
01721 setTo("");
01722 setSubject("");
01723 setDateToday();
01724
01725 setHeaderField("User-Agent", "KMail/" KMAIL_VERSION );
01726
01727 setHeaderField("Content-Type","text/plain");
01728 }
01729
01730 uint KMMessage::identityUoid() const {
01731 TQString idString = headerField("X-KMail-Identity").stripWhiteSpace();
01732 bool ok = false;
01733 int id = idString.toUInt( &ok );
01734
01735 if ( !ok || id == 0 )
01736 id = kmkernel->identityManager()->identityForAddress( to() + ", " + cc() ).uoid();
01737 if ( id == 0 && parent() )
01738 id = parent()->identity();
01739
01740 return id;
01741 }
01742
01743
01744
01745 void KMMessage::initFromMessage(const KMMessage *msg, bool idHeaders)
01746 {
01747 uint id = msg->identityUoid();
01748
01749 if ( idHeaders ) initHeader(id);
01750 else setHeaderField("X-KMail-Identity", TQString::number(id));
01751 if (!msg->headerField("X-KMail-Transport").isEmpty())
01752 setHeaderField("X-KMail-Transport", msg->headerField("X-KMail-Transport"));
01753 }
01754
01755
01756
01757 void KMMessage::cleanupHeader()
01758 {
01759 DwHeaders& header = mMsg->Headers();
01760 DwField* field = header.FirstField();
01761 DwField* nextField;
01762
01763 if (mNeedsAssembly) mMsg->Assemble();
01764 mNeedsAssembly = false;
01765
01766 while (field)
01767 {
01768 nextField = field->Next();
01769 if (field->FieldBody()->AsString().empty())
01770 {
01771 header.RemoveField(field);
01772 mNeedsAssembly = true;
01773 }
01774 field = nextField;
01775 }
01776 }
01777
01778
01779
01780 void KMMessage::setAutomaticFields(bool aIsMulti)
01781 {
01782 DwHeaders& header = mMsg->Headers();
01783 header.MimeVersion().FromString("1.0");
01784
01785 if (aIsMulti || numBodyParts() > 1)
01786 {
01787
01788 DwMediaType& contentType = dwContentType();
01789 contentType.SetType( DwMime::kTypeMultipart);
01790 contentType.SetSubtype(DwMime::kSubtypeMixed );
01791
01792
01793 contentType.CreateBoundary(0);
01794 }
01795 mNeedsAssembly = true;
01796 }
01797
01798
01799
01800 TQString KMMessage::dateStr() const
01801 {
01802 TDEConfigGroup general( KMKernel::config(), "General" );
01803 DwHeaders& header = mMsg->Headers();
01804 time_t unixTime;
01805
01806 if (!header.HasDate()) return "";
01807 unixTime = header.Date().AsUnixTime();
01808
01809
01810
01811 return KMime::DateFormatter::formatDate(
01812 static_cast<KMime::DateFormatter::FormatType>(general.readNumEntry( "dateFormat", KMime::DateFormatter::Fancy )),
01813 unixTime, general.readEntry( "customDateFormat" ));
01814 }
01815
01816
01817
01818 TQCString KMMessage::dateShortStr() const
01819 {
01820 DwHeaders& header = mMsg->Headers();
01821 time_t unixTime;
01822
01823 if (!header.HasDate()) return "";
01824 unixTime = header.Date().AsUnixTime();
01825
01826 TQCString result = ctime(&unixTime);
01827 int len = result.length();
01828 if (result[len-1]=='\n')
01829 result.truncate(len-1);
01830
01831 return result;
01832 }
01833
01834
01835
01836 TQString KMMessage::dateIsoStr() const
01837 {
01838 DwHeaders& header = mMsg->Headers();
01839 time_t unixTime;
01840
01841 if (!header.HasDate()) return "";
01842 unixTime = header.Date().AsUnixTime();
01843
01844 char cstr[64];
01845 strftime(cstr, 63, "%Y-%m-%d %H:%M:%S", localtime(&unixTime));
01846 return TQString(cstr);
01847 }
01848
01849
01850
01851 time_t KMMessage::date() const
01852 {
01853 time_t res = ( time_t )-1;
01854 DwHeaders& header = mMsg->Headers();
01855 if (header.HasDate())
01856 res = header.Date().AsUnixTime();
01857 return res;
01858 }
01859
01860
01861
01862 void KMMessage::setDateToday()
01863 {
01864 struct timeval tval;
01865 gettimeofday(&tval, 0);
01866 setDate((time_t)tval.tv_sec);
01867 }
01868
01869
01870
01871 void KMMessage::setDate(time_t aDate)
01872 {
01873 mDate = aDate;
01874 mMsg->Headers().Date().FromCalendarTime(aDate);
01875 mMsg->Headers().Date().Assemble();
01876 mNeedsAssembly = true;
01877 mDirty = true;
01878 }
01879
01880
01881
01882 void KMMessage::setDate(const TQCString& aStr)
01883 {
01884 DwHeaders& header = mMsg->Headers();
01885
01886 header.Date().FromString(aStr);
01887 header.Date().Parse();
01888 mNeedsAssembly = true;
01889 mDirty = true;
01890
01891 if (header.HasDate())
01892 mDate = header.Date().AsUnixTime();
01893 }
01894
01895
01896
01897 TQString KMMessage::to() const
01898 {
01899
01900 TQValueList<TQCString> rawHeaders = rawHeaderFields( "To" );
01901 TQStringList headers;
01902 for ( TQValueList<TQCString>::Iterator it = rawHeaders.begin(); it != rawHeaders.end(); ++it ) {
01903 headers << *it;
01904 }
01905 return KPIM::normalizeAddressesAndDecodeIDNs( headers.join( ", " ) );
01906 }
01907
01908
01909
01910 void KMMessage::setTo(const TQString& aStr)
01911 {
01912 setHeaderField( "To", aStr, Address );
01913 }
01914
01915
01916 TQString KMMessage::toStrip() const
01917 {
01918 return stripEmailAddr( to() );
01919 }
01920
01921
01922 TQString KMMessage::replyTo() const
01923 {
01924 return KPIM::normalizeAddressesAndDecodeIDNs( rawHeaderField("Reply-To") );
01925 }
01926
01927
01928
01929 void KMMessage::setReplyTo(const TQString& aStr)
01930 {
01931 setHeaderField( "Reply-To", aStr, Address );
01932 }
01933
01934
01935
01936 void KMMessage::setReplyTo(KMMessage* aMsg)
01937 {
01938 setHeaderField( "Reply-To", aMsg->from(), Address );
01939 }
01940
01941
01942
01943 TQString KMMessage::cc() const
01944 {
01945
01946
01947 TQValueList<TQCString> rawHeaders = rawHeaderFields( "Cc" );
01948 TQStringList headers;
01949 for ( TQValueList<TQCString>::Iterator it = rawHeaders.begin(); it != rawHeaders.end(); ++it ) {
01950 headers << *it;
01951 }
01952 return KPIM::normalizeAddressesAndDecodeIDNs( headers.join( ", " ) );
01953 }
01954
01955
01956
01957 void KMMessage::setCc(const TQString& aStr)
01958 {
01959 setHeaderField( "Cc", aStr, Address );
01960 }
01961
01962
01963
01964 TQString KMMessage::ccStrip() const
01965 {
01966 return stripEmailAddr( cc() );
01967 }
01968
01969
01970
01971 TQString KMMessage::bcc() const
01972 {
01973 return KPIM::normalizeAddressesAndDecodeIDNs( rawHeaderField("Bcc") );
01974 }
01975
01976
01977
01978 void KMMessage::setBcc(const TQString& aStr)
01979 {
01980 setHeaderField( "Bcc", aStr, Address );
01981 }
01982
01983
01984 TQString KMMessage::fcc() const
01985 {
01986 return headerField( "X-KMail-Fcc" );
01987 }
01988
01989
01990
01991 void KMMessage::setFcc( const TQString &aStr )
01992 {
01993 setHeaderField( "X-KMail-Fcc", aStr );
01994 }
01995
01996
01997 void KMMessage::setDrafts( const TQString &aStr )
01998 {
01999 mDrafts = aStr;
02000 }
02001
02002
02003 void KMMessage::setTemplates( const TQString &aStr )
02004 {
02005 mTemplates = aStr;
02006 }
02007
02008
02009 TQString KMMessage::who() const
02010 {
02011 if (mParent)
02012 return KPIM::normalizeAddressesAndDecodeIDNs( rawHeaderField(mParent->whoField().utf8()) );
02013 return from();
02014 }
02015
02016
02017
02018 TQString KMMessage::from() const
02019 {
02020 return KPIM::normalizeAddressesAndDecodeIDNs( rawHeaderField("From") );
02021 }
02022
02023
02024
02025 void KMMessage::setFrom(const TQString& bStr)
02026 {
02027 TQString aStr = bStr;
02028 if (aStr.isNull())
02029 aStr = "";
02030 setHeaderField( "From", aStr, Address );
02031 mDirty = true;
02032 }
02033
02034
02035
02036 TQString KMMessage::fromStrip() const
02037 {
02038 return stripEmailAddr( from() );
02039 }
02040
02041
02042 TQString KMMessage::sender() const {
02043 AddrSpecList asl = extractAddrSpecs( "Sender" );
02044 if ( asl.empty() )
02045 asl = extractAddrSpecs( "From" );
02046 if ( asl.empty() )
02047 return TQString();
02048 return asl.front().asString();
02049 }
02050
02051
02052 TQString KMMessage::subject() const
02053 {
02054 return headerField("Subject");
02055 }
02056
02057
02058
02059 void KMMessage::setSubject(const TQString& aStr)
02060 {
02061 setHeaderField("Subject",aStr);
02062 mDirty = true;
02063 }
02064
02065
02066
02067 TQString KMMessage::xmark() const
02068 {
02069 return headerField("X-KMail-Mark");
02070 }
02071
02072
02073
02074 void KMMessage::setXMark(const TQString& aStr)
02075 {
02076 setHeaderField("X-KMail-Mark", aStr);
02077 mDirty = true;
02078 }
02079
02080
02081
02082 TQString KMMessage::replyToId() const
02083 {
02084 int leftAngle, rightAngle;
02085 TQString replyTo, references;
02086
02087 replyTo = headerField("In-Reply-To");
02088
02089 rightAngle = replyTo.find( '>' );
02090 if (rightAngle != -1)
02091 replyTo.truncate( rightAngle + 1 );
02092
02093 leftAngle = replyTo.findRev( '<' );
02094 if (leftAngle != -1)
02095 replyTo = replyTo.mid( leftAngle );
02096
02097
02098
02099
02100
02101 if (!replyTo.isEmpty() && (replyTo[0] == '<') &&
02102 ( -1 == replyTo.find( '"' ) ) )
02103 return replyTo;
02104
02105 references = headerField("References");
02106 leftAngle = references.findRev( '<' );
02107 if (leftAngle != -1)
02108 references = references.mid( leftAngle );
02109 rightAngle = references.find( '>' );
02110 if (rightAngle != -1)
02111 references.truncate( rightAngle + 1 );
02112
02113
02114 if (!references.isEmpty() && references[0] == '<')
02115 return references;
02116
02117 else
02118 return replyTo;
02119 }
02120
02121
02122
02123 TQString KMMessage::replyToIdMD5() const {
02124 return base64EncodedMD5( replyToId() );
02125 }
02126
02127
02128 TQString KMMessage::references() const
02129 {
02130 int leftAngle, rightAngle;
02131 TQString references = headerField( "References" );
02132
02133
02134 leftAngle = references.findRev( '<' );
02135 leftAngle = references.findRev( '<', leftAngle - 1 );
02136 if( leftAngle != -1 )
02137 references = references.mid( leftAngle );
02138 rightAngle = references.findRev( '>' );
02139 if( rightAngle != -1 )
02140 references.truncate( rightAngle + 1 );
02141
02142 if( !references.isEmpty() && references[0] == '<' )
02143 return references;
02144 else
02145 return TQString();
02146 }
02147
02148
02149 TQString KMMessage::replyToAuxIdMD5() const
02150 {
02151 TQString result = references();
02152
02153
02154 const int rightAngle = result.find( '>' );
02155 if( rightAngle != -1 )
02156 result.truncate( rightAngle + 1 );
02157
02158 return base64EncodedMD5( result );
02159 }
02160
02161
02162 TQString KMMessage::strippedSubjectMD5() const {
02163 return base64EncodedMD5( stripOffPrefixes( subject() ), true );
02164 }
02165
02166
02167 TQString KMMessage::subjectMD5() const {
02168 return base64EncodedMD5( subject(), true );
02169 }
02170
02171
02172 bool KMMessage::subjectIsPrefixed() const {
02173 return subjectMD5() != strippedSubjectMD5();
02174 }
02175
02176
02177 void KMMessage::setReplyToId(const TQString& aStr)
02178 {
02179 setHeaderField("In-Reply-To", aStr);
02180 mDirty = true;
02181 }
02182
02183
02184
02185 TQString KMMessage::msgId() const
02186 {
02187 TQString msgId = headerField("Message-Id");
02188
02189
02190 const int rightAngle = msgId.find( '>' );
02191 if (rightAngle != -1)
02192 msgId.truncate( rightAngle + 1 );
02193
02194 const int leftAngle = msgId.findRev( '<' );
02195 if (leftAngle != -1)
02196 msgId = msgId.mid( leftAngle );
02197 return msgId;
02198 }
02199
02200
02201
02202 TQString KMMessage::msgIdMD5() const {
02203 return base64EncodedMD5( msgId() );
02204 }
02205
02206
02207
02208 void KMMessage::setMsgId(const TQString& aStr)
02209 {
02210 setHeaderField("Message-Id", aStr);
02211 mDirty = true;
02212 }
02213
02214
02215 size_t KMMessage::msgSizeServer() const {
02216 return headerField( "X-Length" ).toULong();
02217 }
02218
02219
02220
02221 void KMMessage::setMsgSizeServer(size_t size)
02222 {
02223 setHeaderField("X-Length", TQCString().setNum(size));
02224 mDirty = true;
02225 }
02226
02227
02228 ulong KMMessage::UID() const {
02229 return headerField( "X-UID" ).toULong();
02230 }
02231
02232
02233
02234 void KMMessage::setUID(ulong uid)
02235 {
02236 setHeaderField("X-UID", TQCString().setNum(uid));
02237 mDirty = true;
02238 }
02239
02240
02241 AddressList KMMessage::splitAddrField( const TQCString & str )
02242 {
02243 AddressList result;
02244 const char * scursor = str.begin();
02245 if ( !scursor )
02246 return AddressList();
02247 const char * const send = str.begin() + str.length();
02248 if ( !parseAddressList( scursor, send, result ) )
02249 kdDebug(5006) << "Error in address splitting: parseAddressList returned false!"
02250 << endl;
02251 return result;
02252 }
02253
02254 AddressList KMMessage::headerAddrField( const TQCString & aName ) const {
02255 return KMMessage::splitAddrField( rawHeaderField( aName ) );
02256 }
02257
02258 AddrSpecList KMMessage::extractAddrSpecs( const TQCString & header ) const {
02259 AddressList al = headerAddrField( header );
02260 AddrSpecList result;
02261 for ( AddressList::const_iterator ait = al.begin() ; ait != al.end() ; ++ait )
02262 for ( MailboxList::const_iterator mit = (*ait).mailboxList.begin() ; mit != (*ait).mailboxList.end() ; ++mit )
02263 result.push_back( (*mit).addrSpec );
02264 return result;
02265 }
02266
02267 TQCString KMMessage::rawHeaderField( const TQCString & name ) const {
02268 if ( name.isEmpty() ) return TQCString();
02269
02270 DwHeaders & header = mMsg->Headers();
02271 DwField * field = header.FindField( name );
02272
02273 if ( !field ) return TQCString();
02274
02275 return header.FieldBody( name.data() ).AsString().c_str();
02276 }
02277
02278 TQValueList<TQCString> KMMessage::rawHeaderFields( const TQCString& field ) const
02279 {
02280 if ( field.isEmpty() || !mMsg->Headers().FindField( field ) )
02281 return TQValueList<TQCString>();
02282
02283 std::vector<DwFieldBody*> v = mMsg->Headers().AllFieldBodies( field.data() );
02284 TQValueList<TQCString> headerFields;
02285 for ( uint i = 0; i < v.size(); ++i ) {
02286 headerFields.append( v[i]->AsString().c_str() );
02287 }
02288
02289 return headerFields;
02290 }
02291
02292 TQString KMMessage::headerField(const TQCString& aName) const
02293 {
02294 if ( aName.isEmpty() )
02295 return TQString();
02296
02297 if ( !mMsg->Headers().FindField( aName ) )
02298 return TQString();
02299
02300 return decodeRFC2047String( mMsg->Headers().FieldBody( aName.data() ).AsString().c_str(),
02301 charset() );
02302
02303 }
02304
02305 TQStringList KMMessage::headerFields( const TQCString& field ) const
02306 {
02307 if ( field.isEmpty() || !mMsg->Headers().FindField( field ) )
02308 return TQStringList();
02309
02310 std::vector<DwFieldBody*> v = mMsg->Headers().AllFieldBodies( field.data() );
02311 TQStringList headerFields;
02312 for ( uint i = 0; i < v.size(); ++i ) {
02313 headerFields.append( decodeRFC2047String( v[i]->AsString().c_str(), charset() ) );
02314 }
02315
02316 return headerFields;
02317 }
02318
02319
02320 void KMMessage::removeHeaderField(const TQCString& aName)
02321 {
02322 DwHeaders & header = mMsg->Headers();
02323 DwField * field = header.FindField(aName);
02324 if (!field) return;
02325
02326 header.RemoveField(field);
02327 mNeedsAssembly = true;
02328 }
02329
02330
02331 void KMMessage::removeHeaderFields(const TQCString& aName)
02332 {
02333 DwHeaders & header = mMsg->Headers();
02334 while ( DwField * field = header.FindField(aName) ) {
02335 header.RemoveField(field);
02336 mNeedsAssembly = true;
02337 }
02338 }
02339
02340
02341
02342 void KMMessage::setHeaderField( const TQCString& aName, const TQString& bValue,
02343 HeaderFieldType type, bool prepend )
02344 {
02345 #if 0
02346 if ( type != Unstructured )
02347 kdDebug(5006) << "KMMessage::setHeaderField( \"" << aName << "\", \""
02348 << bValue << "\", " << type << " )" << endl;
02349 #endif
02350 if (aName.isEmpty()) return;
02351
02352 DwHeaders& header = mMsg->Headers();
02353
02354 DwString str;
02355 DwField* field;
02356 TQCString aValue;
02357 if (!bValue.isEmpty())
02358 {
02359 TQString value = bValue;
02360 if ( type == Address )
02361 value = KPIM::normalizeAddressesAndEncodeIDNs( value );
02362 #if 0
02363 if ( type != Unstructured )
02364 kdDebug(5006) << "value: \"" << value << "\"" << endl;
02365 #endif
02366 TQCString encoding = autoDetectCharset( charset(), sPrefCharsets, value );
02367 if (encoding.isEmpty())
02368 encoding = "utf-8";
02369 aValue = encodeRFC2047String( value, encoding );
02370 #if 0
02371 if ( type != Unstructured )
02372 kdDebug(5006) << "aValue: \"" << aValue << "\"" << endl;
02373 #endif
02374 }
02375 str = aName.data();
02376 if (str[str.length()-1] != ':') str += ": ";
02377 else str += ' ';
02378 if ( !aValue.isEmpty() )
02379 str += aValue.data();
02380 if (str[str.length()-1] != '\n') str += '\n';
02381
02382 field = new DwField(str, mMsg);
02383 field->Parse();
02384
02385 if ( prepend )
02386 header.AddFieldAt( 1, field );
02387 else
02388 header.AddOrReplaceField( field );
02389 mNeedsAssembly = true;
02390 }
02391
02392
02393
02394 TQCString KMMessage::typeStr() const
02395 {
02396 DwHeaders& header = mMsg->Headers();
02397 if (header.HasContentType()) return header.ContentType().TypeStr().c_str();
02398 else return "";
02399 }
02400
02401
02402
02403 int KMMessage::type() const
02404 {
02405 DwHeaders& header = mMsg->Headers();
02406 if (header.HasContentType()) return header.ContentType().Type();
02407 else return DwMime::kTypeNull;
02408 }
02409
02410
02411
02412 void KMMessage::setTypeStr(const TQCString& aStr)
02413 {
02414 dwContentType().SetTypeStr(DwString(aStr));
02415 dwContentType().Parse();
02416 mNeedsAssembly = true;
02417 }
02418
02419
02420
02421 void KMMessage::setType(int aType)
02422 {
02423 dwContentType().SetType(aType);
02424 dwContentType().Assemble();
02425 mNeedsAssembly = true;
02426 }
02427
02428
02429
02430
02431 TQCString KMMessage::subtypeStr() const
02432 {
02433 DwHeaders& header = mMsg->Headers();
02434 if (header.HasContentType()) return header.ContentType().SubtypeStr().c_str();
02435 else return "";
02436 }
02437
02438
02439
02440 int KMMessage::subtype() const
02441 {
02442 DwHeaders& header = mMsg->Headers();
02443 if (header.HasContentType()) return header.ContentType().Subtype();
02444 else return DwMime::kSubtypeNull;
02445 }
02446
02447
02448
02449 void KMMessage::setSubtypeStr(const TQCString& aStr)
02450 {
02451 dwContentType().SetSubtypeStr(DwString(aStr));
02452 dwContentType().Parse();
02453 mNeedsAssembly = true;
02454 }
02455
02456
02457
02458 void KMMessage::setSubtype(int aSubtype)
02459 {
02460 dwContentType().SetSubtype(aSubtype);
02461 dwContentType().Assemble();
02462 mNeedsAssembly = true;
02463 }
02464
02465
02466
02467 void KMMessage::setDwMediaTypeParam( DwMediaType &mType,
02468 const TQCString& attr,
02469 const TQCString& val )
02470 {
02471 mType.Parse();
02472 DwParameter *param = mType.FirstParameter();
02473 while(param) {
02474 if (!kasciistricmp(param->Attribute().c_str(), attr))
02475 break;
02476 else
02477 param = param->Next();
02478 }
02479 if (!param){
02480 param = new DwParameter;
02481 param->SetAttribute(DwString( attr ));
02482 mType.AddParameter( param );
02483 }
02484 else
02485 mType.SetModified();
02486 param->SetValue(DwString( val ));
02487 mType.Assemble();
02488 }
02489
02490
02491
02492 void KMMessage::setContentTypeParam(const TQCString& attr, const TQCString& val)
02493 {
02494 if (mNeedsAssembly) mMsg->Assemble();
02495 mNeedsAssembly = false;
02496 setDwMediaTypeParam( dwContentType(), attr, val );
02497 mNeedsAssembly = true;
02498 }
02499
02500
02501
02502 TQCString KMMessage::contentTransferEncodingStr() const
02503 {
02504 DwHeaders& header = mMsg->Headers();
02505 if (header.HasContentTransferEncoding())
02506 return header.ContentTransferEncoding().AsString().c_str();
02507 else return "";
02508 }
02509
02510
02511
02512 int KMMessage::contentTransferEncoding( DwEntity *entity ) const
02513 {
02514 if ( !entity )
02515 entity = mMsg;
02516
02517 DwHeaders& header = entity->Headers();
02518 if ( header.HasContentTransferEncoding() )
02519 return header.ContentTransferEncoding().AsEnum();
02520 else return DwMime::kCteNull;
02521 }
02522
02523
02524
02525 void KMMessage::setContentTransferEncodingStr( const TQCString& cteString,
02526 DwEntity *entity )
02527 {
02528 if ( !entity )
02529 entity = mMsg;
02530
02531 entity->Headers().ContentTransferEncoding().FromString( cteString );
02532 entity->Headers().ContentTransferEncoding().Parse();
02533 mNeedsAssembly = true;
02534 }
02535
02536
02537
02538 void KMMessage::setContentTransferEncoding( int cte, DwEntity *entity )
02539 {
02540 if ( !entity )
02541 entity = mMsg;
02542
02543 entity->Headers().ContentTransferEncoding().FromEnum( cte );
02544 mNeedsAssembly = true;
02545 }
02546
02547
02548
02549 DwHeaders& KMMessage::headers() const
02550 {
02551 return mMsg->Headers();
02552 }
02553
02554
02555
02556 void KMMessage::setNeedsAssembly()
02557 {
02558 mNeedsAssembly = true;
02559 }
02560
02561
02562 void KMMessage::assembleIfNeeded()
02563 {
02564 Q_ASSERT( mMsg );
02565
02566 if ( mNeedsAssembly ) {
02567 mMsg->Assemble();
02568 mNeedsAssembly = false;
02569 }
02570 }
02571
02572
02573 TQCString KMMessage::body() const
02574 {
02575 const DwString& body = mMsg->Body().AsString();
02576 TQCString str = KMail::Util::CString( body );
02577
02578
02579
02580 return str;
02581 }
02582
02583
02584
02585 TQByteArray KMMessage::bodyDecodedBinary() const
02586 {
02587 DwString dwstr;
02588 const DwString& dwsrc = mMsg->Body().AsString();
02589
02590 switch (cte())
02591 {
02592 case DwMime::kCteBase64:
02593 DwDecodeBase64(dwsrc, dwstr);
02594 break;
02595 case DwMime::kCteQuotedPrintable:
02596 DwDecodeQuotedPrintable(dwsrc, dwstr);
02597 break;
02598 default:
02599 dwstr = dwsrc;
02600 break;
02601 }
02602
02603 int len = dwstr.size();
02604 TQByteArray ba(len);
02605 memcpy(ba.data(),dwstr.data(),len);
02606 return ba;
02607 }
02608
02609
02610
02611 TQCString KMMessage::bodyDecoded() const
02612 {
02613 DwString dwstr;
02614 DwString dwsrc = mMsg->Body().AsString();
02615
02616 switch (cte())
02617 {
02618 case DwMime::kCteBase64:
02619 DwDecodeBase64(dwsrc, dwstr);
02620 break;
02621 case DwMime::kCteQuotedPrintable:
02622 DwDecodeQuotedPrintable(dwsrc, dwstr);
02623 break;
02624 default:
02625 dwstr = dwsrc;
02626 break;
02627 }
02628
02629 return KMail::Util::CString( dwstr );
02630
02631
02632
02633
02634
02635
02636 }
02637
02638
02639
02640 TQValueList<int> KMMessage::determineAllowedCtes( const CharFreq& cf,
02641 bool allow8Bit,
02642 bool willBeSigned )
02643 {
02644 TQValueList<int> allowedCtes;
02645
02646 switch ( cf.type() ) {
02647 case CharFreq::SevenBitText:
02648 allowedCtes << DwMime::kCte7bit;
02649 case CharFreq::EightBitText:
02650 if ( allow8Bit )
02651 allowedCtes << DwMime::kCte8bit;
02652 case CharFreq::SevenBitData:
02653 if ( cf.printableRatio() > 5.0/6.0 ) {
02654
02655
02656
02657 allowedCtes << DwMime::kCteQp;
02658 allowedCtes << DwMime::kCteBase64;
02659 } else {
02660 allowedCtes << DwMime::kCteBase64;
02661 allowedCtes << DwMime::kCteQp;
02662 }
02663 break;
02664 case CharFreq::EightBitData:
02665 allowedCtes << DwMime::kCteBase64;
02666 break;
02667 case CharFreq::None:
02668 default:
02669
02670 ;
02671 }
02672
02673
02674
02675
02676
02677 if ( ( willBeSigned && cf.hasTrailingWhitespace() ) ||
02678 cf.hasLeadingFrom() ) {
02679 allowedCtes.remove( DwMime::kCte8bit );
02680 allowedCtes.remove( DwMime::kCte7bit );
02681 }
02682
02683 return allowedCtes;
02684 }
02685
02686
02687
02688 void KMMessage::setBodyAndGuessCte( const TQByteArray& aBuf,
02689 TQValueList<int> & allowedCte,
02690 bool allow8Bit,
02691 bool willBeSigned,
02692 DwEntity *entity )
02693 {
02694 if ( !entity )
02695 entity = mMsg;
02696
02697 CharFreq cf( aBuf );
02698 allowedCte = determineAllowedCtes( cf, allow8Bit, willBeSigned );
02699 setCte( allowedCte[0], entity );
02700 setBodyEncodedBinary( aBuf, entity );
02701 }
02702
02703
02704
02705 void KMMessage::setBodyAndGuessCte( const TQCString& aBuf,
02706 TQValueList<int> & allowedCte,
02707 bool allow8Bit,
02708 bool willBeSigned,
02709 DwEntity *entity )
02710 {
02711 if ( !entity )
02712 entity = mMsg;
02713
02714 CharFreq cf( aBuf.data(), aBuf.size()-1 );
02715 allowedCte = determineAllowedCtes( cf, allow8Bit, willBeSigned );
02716 setCte( allowedCte[0], entity );
02717 setBodyEncoded( aBuf, entity );
02718 }
02719
02720
02721
02722 void KMMessage::setBodyEncoded(const TQCString& aStr, DwEntity *entity )
02723 {
02724 if ( !entity )
02725 entity = mMsg;
02726
02727 DwString dwSrc(aStr.data(), aStr.size()-1 );
02728 DwString dwResult;
02729
02730 switch (cte( entity ))
02731 {
02732 case DwMime::kCteBase64:
02733 DwEncodeBase64(dwSrc, dwResult);
02734 break;
02735 case DwMime::kCteQuotedPrintable:
02736 DwEncodeQuotedPrintable(dwSrc, dwResult);
02737 break;
02738 default:
02739 dwResult = dwSrc;
02740 break;
02741 }
02742
02743 entity->Body().FromString(dwResult);
02744 mNeedsAssembly = true;
02745 }
02746
02747
02748 void KMMessage::setBodyEncodedBinary( const TQByteArray& aStr, DwEntity *entity )
02749 {
02750 if ( !entity )
02751 entity = mMsg;
02752
02753 DwString dwSrc(aStr.data(), aStr.size());
02754 DwString dwResult;
02755
02756 switch ( cte( entity ) )
02757 {
02758 case DwMime::kCteBase64:
02759 DwEncodeBase64( dwSrc, dwResult );
02760 break;
02761 case DwMime::kCteQuotedPrintable:
02762 DwEncodeQuotedPrintable( dwSrc, dwResult );
02763 break;
02764 default:
02765 dwResult = dwSrc;
02766 break;
02767 }
02768
02769 entity->Body().FromString( dwResult );
02770 entity->Body().Parse();
02771
02772 mNeedsAssembly = true;
02773 }
02774
02775
02776
02777 void KMMessage::setBody(const TQCString& aStr)
02778 {
02779 mMsg->Body().FromString(KMail::Util::dwString(aStr));
02780 mNeedsAssembly = true;
02781 }
02782 void KMMessage::setBody(const DwString& aStr)
02783 {
02784 mMsg->Body().FromString(aStr);
02785 mNeedsAssembly = true;
02786 }
02787 void KMMessage::setBody(const char* aStr)
02788 {
02789 mMsg->Body().FromString(aStr);
02790 mNeedsAssembly = true;
02791 }
02792
02793
02794 void KMMessage::setMultiPartBody( const TQCString & aStr ) {
02795 setBody( aStr );
02796 mMsg->Body().Parse();
02797 mNeedsAssembly = true;
02798 }
02799
02800
02801
02802
02803
02804
02805
02806
02807
02808
02809 int KMMessage::numBodyParts() const
02810 {
02811 int count = 0;
02812 DwBodyPart* part = getFirstDwBodyPart();
02813 TQPtrList< DwBodyPart > parts;
02814
02815 while (part)
02816 {
02817
02818 while ( part
02819 && part->hasHeaders()
02820 && part->Headers().HasContentType()
02821 && part->Body().FirstBodyPart()
02822 && (DwMime::kTypeMultipart == part->Headers().ContentType().Type()) )
02823 {
02824 parts.append( part );
02825 part = part->Body().FirstBodyPart();
02826 }
02827
02828 count++;
02829
02830
02831 while (part && !(part->Next()) && !(parts.isEmpty()))
02832 {
02833 part = parts.getLast();
02834 parts.removeLast();
02835 }
02836
02837 if (part && part->Body().Message() &&
02838 part->Body().Message()->Body().FirstBodyPart())
02839 {
02840 part = part->Body().Message()->Body().FirstBodyPart();
02841 } else if (part) {
02842 part = part->Next();
02843 }
02844 }
02845
02846 return count;
02847 }
02848
02849
02850
02851 DwBodyPart * KMMessage::getFirstDwBodyPart() const
02852 {
02853 return mMsg->Body().FirstBodyPart();
02854 }
02855
02856
02857
02858 int KMMessage::partNumber( DwBodyPart * aDwBodyPart ) const
02859 {
02860 DwBodyPart *curpart;
02861 TQPtrList< DwBodyPart > parts;
02862 int curIdx = 0;
02863 int idx = 0;
02864
02865
02866 curpart = getFirstDwBodyPart();
02867
02868 while (curpart && !idx) {
02869
02870 while( curpart
02871 && curpart->hasHeaders()
02872 && curpart->Headers().HasContentType()
02873 && curpart->Body().FirstBodyPart()
02874 && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) )
02875 {
02876 parts.append( curpart );
02877 curpart = curpart->Body().FirstBodyPart();
02878 }
02879
02880 if (curpart == aDwBodyPart)
02881 idx = curIdx;
02882 curIdx++;
02883
02884
02885 while (curpart && !(curpart->Next()) && !(parts.isEmpty()))
02886 {
02887 curpart = parts.getLast();
02888 parts.removeLast();
02889 } ;
02890 if (curpart)
02891 curpart = curpart->Next();
02892 }
02893 return idx;
02894 }
02895
02896
02897
02898 DwBodyPart * KMMessage::dwBodyPart( int aIdx ) const
02899 {
02900 DwBodyPart *part, *curpart;
02901 TQPtrList< DwBodyPart > parts;
02902 int curIdx = 0;
02903
02904
02905 curpart = getFirstDwBodyPart();
02906 part = 0;
02907
02908 while (curpart && !part) {
02909
02910 while( curpart
02911 && curpart->hasHeaders()
02912 && curpart->Headers().HasContentType()
02913 && curpart->Body().FirstBodyPart()
02914 && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) )
02915 {
02916 parts.append( curpart );
02917 curpart = curpart->Body().FirstBodyPart();
02918 }
02919
02920 if (curIdx==aIdx)
02921 part = curpart;
02922 curIdx++;
02923
02924
02925 while (curpart && !(curpart->Next()) && !(parts.isEmpty()))
02926 {
02927 curpart = parts.getLast();
02928 parts.removeLast();
02929 }
02930 if (curpart)
02931 curpart = curpart->Next();
02932 }
02933 return part;
02934 }
02935
02936
02937
02938 DwBodyPart * KMMessage::findDwBodyPart( int type, int subtype ) const
02939 {
02940 DwBodyPart *part, *curpart;
02941 TQPtrList< DwBodyPart > parts;
02942
02943
02944 curpart = getFirstDwBodyPart();
02945 part = 0;
02946
02947 while (curpart && !part) {
02948
02949 while(curpart
02950 && curpart->hasHeaders()
02951 && curpart->Headers().HasContentType()
02952 && curpart->Body().FirstBodyPart()
02953 && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) ) {
02954 parts.append( curpart );
02955 curpart = curpart->Body().FirstBodyPart();
02956 }
02957
02958
02959
02960
02961 if ( curpart && curpart->hasHeaders() && curpart->Headers().HasContentType() ) {
02962 kdDebug(5006) << curpart->Headers().ContentType().TypeStr().c_str()
02963 << " " << curpart->Headers().ContentType().SubtypeStr().c_str() << endl;
02964 }
02965
02966 if (curpart &&
02967 curpart->hasHeaders() &&
02968 curpart->Headers().HasContentType() &&
02969 curpart->Headers().ContentType().Type() == type &&
02970 curpart->Headers().ContentType().Subtype() == subtype) {
02971 part = curpart;
02972 } else {
02973
02974
02975 while (curpart && !(curpart->Next()) && !(parts.isEmpty())) {
02976 curpart = parts.getLast();
02977 parts.removeLast();
02978 } ;
02979 if (curpart)
02980 curpart = curpart->Next();
02981 }
02982 }
02983 return part;
02984 }
02985
02986
02987 DwBodyPart * KMMessage::findDwBodyPart( const TQCString& type, const TQCString& subtype ) const
02988 {
02989 DwBodyPart *part, *curpart;
02990 TQPtrList< DwBodyPart > parts;
02991
02992
02993 curpart = getFirstDwBodyPart();
02994 part = 0;
02995
02996 while (curpart && !part) {
02997
02998 while(curpart
02999 && curpart->hasHeaders()
03000 && curpart->Headers().HasContentType()
03001 && curpart->Body().FirstBodyPart()
03002 && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) ) {
03003 parts.append( curpart );
03004 curpart = curpart->Body().FirstBodyPart();
03005 }
03006
03007
03008
03009
03010 if (curpart && curpart->hasHeaders() && curpart->Headers().HasContentType() ) {
03011 kdDebug(5006) << curpart->Headers().ContentType().TypeStr().c_str()
03012 << " " << curpart->Headers().ContentType().SubtypeStr().c_str() << endl;
03013 }
03014
03015 if (curpart &&
03016 curpart->hasHeaders() &&
03017 curpart->Headers().HasContentType() &&
03018 curpart->Headers().ContentType().TypeStr().c_str() == type &&
03019 curpart->Headers().ContentType().SubtypeStr().c_str() == subtype) {
03020 part = curpart;
03021 } else {
03022
03023
03024 while (curpart && !(curpart->Next()) && !(parts.isEmpty())) {
03025 curpart = parts.getLast();
03026 parts.removeLast();
03027 } ;
03028 if (curpart)
03029 curpart = curpart->Next();
03030 }
03031 }
03032 return part;
03033 }
03034
03035
03036 void applyHeadersToMessagePart( DwHeaders& headers, KMMessagePart* aPart )
03037 {
03038
03039
03040
03041
03042
03043
03044
03045
03046
03047
03048 TQCString additionalCTypeParams;
03049 if (headers.HasContentType())
03050 {
03051 DwMediaType& ct = headers.ContentType();
03052 aPart->setOriginalContentTypeStr( ct.AsString().c_str() );
03053 aPart->setTypeStr(ct.TypeStr().c_str());
03054 aPart->setSubtypeStr(ct.SubtypeStr().c_str());
03055 DwParameter *param = ct.FirstParameter();
03056 while(param)
03057 {
03058 if (!tqstricmp(param->Attribute().c_str(), "charset")) {
03059 if (aPart->type() == DwMime::kTypeText) {
03060 aPart->setCharset(TQCString(param->Value().c_str()).lower());
03061 }
03062 }
03063 else if (!tqstrnicmp(param->Attribute().c_str(), "name*", 5))
03064 aPart->setName(KMMsgBase::decodeRFC2231String(KMMsgBase::extractRFC2231HeaderField( param->Value().c_str(), "name" )));
03065 else {
03066 additionalCTypeParams += ';';
03067 additionalCTypeParams += param->AsString().c_str();
03068 }
03069 param=param->Next();
03070 }
03071 }
03072 else
03073 {
03074 aPart->setTypeStr("text");
03075 aPart->setSubtypeStr("plain");
03076 }
03077 aPart->setAdditionalCTypeParamStr( additionalCTypeParams );
03078
03079 if (aPart->name().isEmpty())
03080 {
03081 if (headers.HasContentType() && !headers.ContentType().Name().empty()) {
03082 aPart->setName(KMMsgBase::decodeRFC2047String(headers.
03083 ContentType().Name().c_str()) );
03084 } else if (headers.HasSubject() && !headers.Subject().AsString().empty()) {
03085 aPart->setName( KMMsgBase::decodeRFC2047String(headers.
03086 Subject().AsString().c_str()) );
03087 }
03088 }
03089
03090
03091 if (headers.HasContentTransferEncoding())
03092 aPart->setCteStr(headers.ContentTransferEncoding().AsString().c_str());
03093 else
03094 aPart->setCteStr("7bit");
03095
03096
03097 if (headers.HasContentDescription())
03098 aPart->setContentDescription( KMMsgBase::decodeRFC2047String(
03099 headers.ContentDescription().AsString().c_str() ) );
03100 else
03101 aPart->setContentDescription("");
03102
03103
03104 if (headers.HasContentDisposition())
03105 aPart->setContentDisposition(headers.ContentDisposition().AsString().c_str());
03106 else
03107 aPart->setContentDisposition("");
03108 }
03109
03110
03111 void KMMessage::bodyPart(DwBodyPart* aDwBodyPart, KMMessagePart* aPart,
03112 bool withBody)
03113 {
03114 if ( !aPart )
03115 return;
03116
03117 aPart->clear();
03118
03119 if( aDwBodyPart && aDwBodyPart->hasHeaders() ) {
03120
03121
03122
03123
03124 TQString partId( aDwBodyPart->partId() );
03125 aPart->setPartSpecifier( partId );
03126
03127 DwHeaders& headers = aDwBodyPart->Headers();
03128 applyHeadersToMessagePart( headers, aPart );
03129
03130
03131 if (withBody)
03132 aPart->setBody( aDwBodyPart->Body().AsString() );
03133 else
03134 aPart->setBody( TQCString("") );
03135
03136
03137 if ( headers.HasContentId() ) {
03138 const TQCString contentId = headers.ContentId().AsString().c_str();
03139
03140 aPart->setContentId( contentId.mid( 1, contentId.length() - 2 ) );
03141 }
03142 }
03143
03144
03145 else
03146 {
03147 aPart->setTypeStr("");
03148 aPart->setSubtypeStr("");
03149 aPart->setCteStr("");
03150
03151
03152
03153 aPart->setContentDescription("");
03154 aPart->setContentDisposition("");
03155 aPart->setBody(TQCString(""));
03156 aPart->setContentId("");
03157 }
03158 }
03159
03160
03161
03162 void KMMessage::bodyPart(int aIdx, KMMessagePart* aPart) const
03163 {
03164 if ( !aPart )
03165 return;
03166
03167
03168 if ( DwBodyPart *part = dwBodyPart( aIdx ) ) {
03169 KMMessage::bodyPart(part, aPart);
03170 if( aPart->name().isEmpty() )
03171 aPart->setName( i18n("Attachment: %1").arg( aIdx ) );
03172 }
03173 }
03174
03175
03176
03177 void KMMessage::deleteBodyParts()
03178 {
03179 mMsg->Body().DeleteBodyParts();
03180 }
03181
03182
03183
03184 bool KMMessage::deleteBodyPart( int partIndex )
03185 {
03186 KMMessagePart part;
03187 DwBodyPart *dwpart = findPart( partIndex );
03188 if ( !dwpart )
03189 return false;
03190 KMMessage::bodyPart( dwpart, &part, true );
03191 if ( !part.isComplete() )
03192 return false;
03193
03194 DwBody *parentNode = dynamic_cast<DwBody*>( dwpart->Parent() );
03195 if ( !parentNode )
03196 return false;
03197 parentNode->RemoveBodyPart( dwpart );
03198
03199
03200 KMMessagePart dummyPart;
03201 dummyPart.duplicate( part );
03202 TQString comment = i18n("This attachment has been deleted.");
03203 if ( !part.fileName().isEmpty() )
03204 comment = i18n( "The attachment '%1' has been deleted." ).arg( part.fileName() );
03205 dummyPart.setContentDescription( comment );
03206 dummyPart.setBodyEncodedBinary( TQByteArray() );
03207 TQCString cd = dummyPart.contentDisposition();
03208 if ( cd.find( "inline", 0, false ) == 0 ) {
03209 cd.replace( 0, 10, "attachment" );
03210 dummyPart.setContentDisposition( cd );
03211 } else if ( cd.isEmpty() ) {
03212 dummyPart.setContentDisposition( "attachment" );
03213 }
03214 DwBodyPart* newDwPart = createDWBodyPart( &dummyPart );
03215 parentNode->AddBodyPart( newDwPart );
03216 getTopLevelPart()->Assemble();
03217 return true;
03218 }
03219
03220
03221 DwBodyPart* KMMessage::createDWBodyPart(const KMMessagePart* aPart)
03222 {
03223 DwBodyPart* part = DwBodyPart::NewBodyPart(emptyString, 0);
03224
03225 if ( !aPart )
03226 return part;
03227
03228 TQCString charset = aPart->charset();
03229 TQCString type = aPart->typeStr();
03230 TQCString subtype = aPart->subtypeStr();
03231 TQCString cte = aPart->cteStr();
03232 TQCString contDesc = aPart->contentDescriptionEncoded();
03233 TQCString contDisp = aPart->contentDisposition();
03234 TQCString name = KMMsgBase::encodeRFC2231StringAutoDetectCharset( aPart->name(), charset );
03235 bool RFC2231encoded = aPart->name() != TQString(name);
03236 TQCString paramAttr = aPart->parameterAttribute();
03237
03238 DwHeaders& headers = part->Headers();
03239
03240 DwMediaType& ct = headers.ContentType();
03241 if (!type.isEmpty() && !subtype.isEmpty())
03242 {
03243 ct.SetTypeStr(type.data());
03244 ct.SetSubtypeStr(subtype.data());
03245 if (!charset.isEmpty()){
03246 DwParameter *param;
03247 param=new DwParameter;
03248 param->SetAttribute("charset");
03249 param->SetValue(charset.data());
03250 ct.AddParameter(param);
03251 }
03252 }
03253
03254 TQCString additionalParam = aPart->additionalCTypeParamStr();
03255 if( !additionalParam.isEmpty() )
03256 {
03257 TQCString parAV;
03258 DwString parA, parV;
03259 int iL, i1, i2, iM;
03260 iL = additionalParam.length();
03261 i1 = 0;
03262 i2 = additionalParam.find(';', i1, false);
03263 while ( i1 < iL )
03264 {
03265 if( -1 == i2 )
03266 i2 = iL;
03267 if( i1+1 < i2 ) {
03268 parAV = additionalParam.mid( i1, (i2-i1) );
03269 iM = parAV.find('=');
03270 if( -1 < iM )
03271 {
03272 parA = parAV.left( iM ).data();
03273 parV = parAV.right( parAV.length() - iM - 1 ).data();
03274 if( ('"' == parV.at(0)) && ('"' == parV.at(parV.length()-1)) )
03275 {
03276 parV.erase( 0, 1);
03277 parV.erase( parV.length()-1 );
03278 }
03279 }
03280 else
03281 {
03282 parA = parAV.data();
03283 parV = "";
03284 }
03285 DwParameter *param;
03286 param = new DwParameter;
03287 param->SetAttribute( parA );
03288 param->SetValue( parV );
03289 ct.AddParameter( param );
03290 }
03291 i1 = i2+1;
03292 i2 = additionalParam.find(';', i1, false);
03293 }
03294 }
03295
03296 if ( !name.isEmpty() ) {
03297 if (RFC2231encoded)
03298 {
03299 DwParameter *nameParam;
03300 nameParam = new DwParameter;
03301 nameParam->SetAttribute("name*");
03302 nameParam->SetValue(name.data(),true);
03303 ct.AddParameter(nameParam);
03304 } else {
03305 ct.SetName(name.data());
03306 }
03307 }
03308
03309 if (!paramAttr.isEmpty())
03310 {
03311 TQCString paramValue;
03312 paramValue = KMMsgBase::encodeRFC2231StringAutoDetectCharset( aPart->parameterValue(), charset );
03313 DwParameter *param = new DwParameter;
03314 if (aPart->parameterValue() != TQString(paramValue))
03315 {
03316 param->SetAttribute((paramAttr + '*').data());
03317 param->SetValue(paramValue.data(),true);
03318 } else {
03319 param->SetAttribute(paramAttr.data());
03320 param->SetValue(paramValue.data());
03321 }
03322 ct.AddParameter(param);
03323 }
03324
03325 if (!cte.isEmpty())
03326 headers.Cte().FromString(cte);
03327
03328 if (!contDesc.isEmpty())
03329 headers.ContentDescription().FromString(contDesc);
03330
03331 if (!contDisp.isEmpty())
03332 headers.ContentDisposition().FromString(contDisp);
03333
03334 const DwString bodyStr = aPart->dwBody();
03335 if (!bodyStr.empty())
03336 part->Body().FromString(bodyStr);
03337 else
03338 part->Body().FromString("");
03339
03340 if (!aPart->partSpecifier().isNull())
03341 part->SetPartId( aPart->partSpecifier().latin1() );
03342
03343 if (aPart->decodedSize() > 0)
03344 part->SetBodySize( aPart->decodedSize() );
03345
03346 return part;
03347 }
03348
03349
03350
03351 void KMMessage::addDwBodyPart(DwBodyPart * aDwPart)
03352 {
03353 mMsg->Body().AddBodyPart( aDwPart );
03354 mNeedsAssembly = true;
03355 }
03356
03357
03358
03359 void KMMessage::addBodyPart(const KMMessagePart* aPart)
03360 {
03361 DwBodyPart* part = createDWBodyPart( aPart );
03362 addDwBodyPart( part );
03363 }
03364
03365
03366
03367 TQString KMMessage::generateMessageId( const TQString& addr )
03368 {
03369 TQDateTime datetime = TQDateTime::currentDateTime();
03370 TQString msgIdStr;
03371
03372 msgIdStr = '<' + datetime.toString( "yyyyMMddhhmm.sszzz" );
03373
03374 TQString msgIdSuffix;
03375 TDEConfigGroup general( KMKernel::config(), "General" );
03376
03377 if( general.readBoolEntry( "useCustomMessageIdSuffix", false ) )
03378 msgIdSuffix = general.readEntry( "myMessageIdSuffix" );
03379
03380 if( !msgIdSuffix.isEmpty() )
03381 msgIdStr += '@' + msgIdSuffix;
03382 else
03383 msgIdStr += '.' + KPIM::encodeIDN( addr );
03384
03385 msgIdStr += '>';
03386
03387 return msgIdStr;
03388 }
03389
03390
03391
03392 TQCString KMMessage::html2source( const TQCString & src )
03393 {
03394 TQCString result( 1 + 6*(src.size()-1) );
03395
03396 TQCString::ConstIterator s = src.begin();
03397 TQCString::Iterator d = result.begin();
03398 while ( *s ) {
03399 switch ( *s ) {
03400 case '<': {
03401 *d++ = '&';
03402 *d++ = 'l';
03403 *d++ = 't';
03404 *d++ = ';';
03405 ++s;
03406 }
03407 break;
03408 case '\r': {
03409 ++s;
03410 }
03411 break;
03412 case '\n': {
03413 *d++ = '<';
03414 *d++ = 'b';
03415 *d++ = 'r';
03416 *d++ = '>';
03417 ++s;
03418 }
03419 break;
03420 case '>': {
03421 *d++ = '&';
03422 *d++ = 'g';
03423 *d++ = 't';
03424 *d++ = ';';
03425 ++s;
03426 }
03427 break;
03428 case '&': {
03429 *d++ = '&';
03430 *d++ = 'a';
03431 *d++ = 'm';
03432 *d++ = 'p';
03433 *d++ = ';';
03434 ++s;
03435 }
03436 break;
03437 case '"': {
03438 *d++ = '&';
03439 *d++ = 'q';
03440 *d++ = 'u';
03441 *d++ = 'o';
03442 *d++ = 't';
03443 *d++ = ';';
03444 ++s;
03445 }
03446 break;
03447 case '\'': {
03448 *d++ = '&';
03449 *d++ = 'a';
03450 *d++ = 'p';
03451 *d++ = 's';
03452 *d++ = ';';
03453 ++s;
03454 }
03455 break;
03456 default:
03457 *d++ = *s++;
03458 }
03459 }
03460 result.truncate( d - result.begin() );
03461 return result;
03462 }
03463
03464
03465 TQString KMMessage::encodeMailtoUrl( const TQString& str )
03466 {
03467 TQString result;
03468 result = TQString::fromLatin1( KMMsgBase::encodeRFC2047String( str,
03469 "utf-8" ) );
03470 result = KURL::encode_string( result );
03471 return result;
03472 }
03473
03474
03475
03476 TQString KMMessage::decodeMailtoUrl( const TQString& url )
03477 {
03478 TQString result;
03479 result = KURL::decode_string( url );
03480 result = KMMsgBase::decodeRFC2047String( result.latin1() );
03481 return result;
03482 }
03483
03484
03485
03486 TQCString KMMessage::stripEmailAddr( const TQCString& aStr )
03487 {
03488
03489
03490 if ( aStr.isEmpty() )
03491 return TQCString();
03492
03493 TQCString result;
03494
03495
03496
03497
03498
03499 TQCString name;
03500 TQCString comment;
03501 TQCString angleAddress;
03502 enum { TopLevel, InComment, InAngleAddress } context = TopLevel;
03503 bool inQuotedString = false;
03504 int commentLevel = 0;
03505
03506 for ( const char* p = aStr.data(); *p; ++p ) {
03507 switch ( context ) {
03508 case TopLevel : {
03509 switch ( *p ) {
03510 case '"' : inQuotedString = !inQuotedString;
03511 break;
03512 case '(' : if ( !inQuotedString ) {
03513 context = InComment;
03514 commentLevel = 1;
03515 }
03516 else
03517 name += *p;
03518 break;
03519 case '<' : if ( !inQuotedString ) {
03520 context = InAngleAddress;
03521 }
03522 else
03523 name += *p;
03524 break;
03525 case '\\' :
03526 ++p;
03527 if ( *p )
03528 name += *p;
03529 break;
03530 case ',' : if ( !inQuotedString ) {
03531
03532 if ( !result.isEmpty() )
03533 result += ", ";
03534 name = name.stripWhiteSpace();
03535 comment = comment.stripWhiteSpace();
03536 angleAddress = angleAddress.stripWhiteSpace();
03537
03538
03539
03540
03541
03542
03543
03544
03545 if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
03546
03547
03548 result += comment;
03549 }
03550 else if ( !name.isEmpty() ) {
03551 result += name;
03552 }
03553 else if ( !comment.isEmpty() ) {
03554 result += comment;
03555 }
03556 else if ( !angleAddress.isEmpty() ) {
03557 result += angleAddress;
03558 }
03559 name = TQCString();
03560 comment = TQCString();
03561 angleAddress = TQCString();
03562 }
03563 else
03564 name += *p;
03565 break;
03566 default : name += *p;
03567 }
03568 break;
03569 }
03570 case InComment : {
03571 switch ( *p ) {
03572 case '(' : ++commentLevel;
03573 comment += *p;
03574 break;
03575 case ')' : --commentLevel;
03576 if ( commentLevel == 0 ) {
03577 context = TopLevel;
03578 comment += ' ';
03579 }
03580 else
03581 comment += *p;
03582 break;
03583 case '\\' :
03584 ++p;
03585 if ( *p )
03586 comment += *p;
03587 break;
03588 default : comment += *p;
03589 }
03590 break;
03591 }
03592 case InAngleAddress : {
03593 switch ( *p ) {
03594 case '"' : inQuotedString = !inQuotedString;
03595 angleAddress += *p;
03596 break;
03597 case '>' : if ( !inQuotedString ) {
03598 context = TopLevel;
03599 }
03600 else
03601 angleAddress += *p;
03602 break;
03603 case '\\' :
03604 ++p;
03605 if ( *p )
03606 angleAddress += *p;
03607 break;
03608 default : angleAddress += *p;
03609 }
03610 break;
03611 }
03612 }
03613 }
03614 if ( !result.isEmpty() )
03615 result += ", ";
03616 name = name.stripWhiteSpace();
03617 comment = comment.stripWhiteSpace();
03618 angleAddress = angleAddress.stripWhiteSpace();
03619
03620
03621
03622
03623
03624 if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
03625
03626
03627 result += comment;
03628 }
03629 else if ( !name.isEmpty() ) {
03630 result += name;
03631 }
03632 else if ( !comment.isEmpty() ) {
03633 result += comment;
03634 }
03635 else if ( !angleAddress.isEmpty() ) {
03636 result += angleAddress;
03637 }
03638
03639
03640
03641 return result;
03642 }
03643
03644
03645 TQString KMMessage::stripEmailAddr( const TQString& aStr )
03646 {
03647
03648
03649 if ( aStr.isEmpty() )
03650 return TQString();
03651
03652 TQString result;
03653
03654
03655
03656
03657
03658 TQString name;
03659 TQString comment;
03660 TQString angleAddress;
03661 enum { TopLevel, InComment, InAngleAddress } context = TopLevel;
03662 bool inQuotedString = false;
03663 int commentLevel = 0;
03664
03665 TQChar ch;
03666 unsigned int strLength(aStr.length());
03667 for ( uint index = 0; index < strLength; ++index ) {
03668 ch = aStr[index];
03669 switch ( context ) {
03670 case TopLevel : {
03671 switch ( ch.latin1() ) {
03672 case '"' : inQuotedString = !inQuotedString;
03673 break;
03674 case '(' : if ( !inQuotedString ) {
03675 context = InComment;
03676 commentLevel = 1;
03677 }
03678 else
03679 name += ch;
03680 break;
03681 case '<' : if ( !inQuotedString ) {
03682 context = InAngleAddress;
03683 }
03684 else
03685 name += ch;
03686 break;
03687 case '\\' :
03688 ++index;
03689 if ( index < aStr.length() )
03690 name += aStr[index];
03691 break;
03692 case ',' : if ( !inQuotedString ) {
03693
03694 if ( !result.isEmpty() )
03695 result += ", ";
03696 name = name.stripWhiteSpace();
03697 comment = comment.stripWhiteSpace();
03698 angleAddress = angleAddress.stripWhiteSpace();
03699
03700
03701
03702
03703
03704
03705
03706
03707 if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
03708
03709
03710 result += comment;
03711 }
03712 else if ( !name.isEmpty() ) {
03713 result += name;
03714 }
03715 else if ( !comment.isEmpty() ) {
03716 result += comment;
03717 }
03718 else if ( !angleAddress.isEmpty() ) {
03719 result += angleAddress;
03720 }
03721 name = TQString();
03722 comment = TQString();
03723 angleAddress = TQString();
03724 }
03725 else
03726 name += ch;
03727 break;
03728 default : name += ch;
03729 }
03730 break;
03731 }
03732 case InComment : {
03733 switch ( ch.latin1() ) {
03734 case '(' : ++commentLevel;
03735 comment += ch;
03736 break;
03737 case ')' : --commentLevel;
03738 if ( commentLevel == 0 ) {
03739 context = TopLevel;
03740 comment += ' ';
03741 }
03742 else
03743 comment += ch;
03744 break;
03745 case '\\' :
03746 ++index;
03747 if ( index < aStr.length() )
03748 comment += aStr[index];
03749 break;
03750 default : comment += ch;
03751 }
03752 break;
03753 }
03754 case InAngleAddress : {
03755 switch ( ch.latin1() ) {
03756 case '"' : inQuotedString = !inQuotedString;
03757 angleAddress += ch;
03758 break;
03759 case '>' : if ( !inQuotedString ) {
03760 context = TopLevel;
03761 }
03762 else
03763 angleAddress += ch;
03764 break;
03765 case '\\' :
03766 ++index;
03767 if ( index < aStr.length() )
03768 angleAddress += aStr[index];
03769 break;
03770 default : angleAddress += ch;
03771 }
03772 break;
03773 }
03774 }
03775 }
03776 if ( !result.isEmpty() )
03777 result += ", ";
03778 name = name.stripWhiteSpace();
03779 comment = comment.stripWhiteSpace();
03780 angleAddress = angleAddress.stripWhiteSpace();
03781
03782
03783
03784
03785
03786 if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
03787
03788
03789 result += comment;
03790 }
03791 else if ( !name.isEmpty() ) {
03792 result += name;
03793 }
03794 else if ( !comment.isEmpty() ) {
03795 result += comment;
03796 }
03797 else if ( !angleAddress.isEmpty() ) {
03798 result += angleAddress;
03799 }
03800
03801
03802
03803 return result;
03804 }
03805
03806
03807 TQString KMMessage::quoteHtmlChars( const TQString& str, bool removeLineBreaks )
03808 {
03809 TQString result;
03810
03811 unsigned int strLength(str.length());
03812 result.reserve( 6*strLength );
03813 for( unsigned int i = 0; i < strLength; ++i )
03814 switch ( str[i].latin1() ) {
03815 case '<':
03816 result += "<";
03817 break;
03818 case '>':
03819 result += ">";
03820 break;
03821 case '&':
03822 result += "&";
03823 break;
03824 case '"':
03825 result += """;
03826 break;
03827 case '\n':
03828 if ( !removeLineBreaks )
03829 result += "<br>";
03830 break;
03831 case '\r':
03832
03833 break;
03834 default:
03835 result += str[i];
03836 }
03837
03838 result.squeeze();
03839 return result;
03840 }
03841
03842
03843 TQString KMMessage::emailAddrAsAnchor(const TQString& aEmail, bool stripped, const TQString& cssStyle, bool aLink)
03844 {
03845 if( aEmail.isEmpty() )
03846 return aEmail;
03847
03848 TQStringList addressList = KPIM::splitEmailAddrList( aEmail );
03849 TQString result;
03850
03851 for( TQStringList::ConstIterator it = addressList.begin();
03852 ( it != addressList.end() );
03853 ++it ) {
03854 if( !(*it).isEmpty() ) {
03855
03856
03857 TQString name, mail;
03858 KPIM::getNameAndMail( *it, name, mail );
03859 TQString pretty;
03860 TQString prettyStripped;
03861 if ( name.stripWhiteSpace().isEmpty() ) {
03862 pretty = mail;
03863 prettyStripped = mail;
03864 } else {
03865 pretty = KPIM::quoteNameIfNecessary( name ) + " <" + mail + ">";
03866 prettyStripped = name;
03867 }
03868
03869 if(aLink) {
03870 result += "<a href=\"mailto:"
03871 + KMMessage::encodeMailtoUrl( pretty )
03872 + "\" "+cssStyle+">";
03873 }
03874
03875 if ( stripped ) {
03876 result += KMMessage::quoteHtmlChars( prettyStripped, true );
03877 }
03878 else {
03879 result += KMMessage::quoteHtmlChars( pretty, true );
03880 }
03881
03882 if(aLink)
03883 result += "</a>, ";
03884 }
03885 }
03886
03887 if(aLink)
03888 result.truncate( result.length() - 2 );
03889
03890
03891
03892 return result;
03893 }
03894
03895
03896
03897 TQStringList KMMessage::stripAddressFromAddressList( const TQString& address,
03898 const TQStringList& list )
03899 {
03900 TQStringList addresses( list );
03901 TQString addrSpec( KPIM::getEmailAddress( address ) );
03902 for ( TQStringList::Iterator it = addresses.begin();
03903 it != addresses.end(); ) {
03904 if ( kasciistricmp( addrSpec.utf8().data(),
03905 KPIM::getEmailAddress( *it ).utf8().data() ) == 0 ) {
03906 kdDebug(5006) << "Removing " << *it << " from the address list"
03907 << endl;
03908 it = addresses.remove( it );
03909 }
03910 else
03911 ++it;
03912 }
03913 return addresses;
03914 }
03915
03916
03917
03918
03919 TQStringList KMMessage::stripMyAddressesFromAddressList( const TQStringList& list )
03920 {
03921 TQStringList addresses = list;
03922 for( TQStringList::Iterator it = addresses.begin();
03923 it != addresses.end(); ) {
03924 kdDebug(5006) << "Check whether " << *it << " is one of my addresses"
03925 << endl;
03926 if( kmkernel->identityManager()->thatIsMe( KPIM::getEmailAddress( *it ) ) ) {
03927 kdDebug(5006) << "Removing " << *it << " from the address list"
03928 << endl;
03929 it = addresses.remove( it );
03930 }
03931 else
03932 ++it;
03933 }
03934 return addresses;
03935 }
03936
03937
03938
03939
03940 bool KMMessage::addressIsInAddressList( const TQString& address,
03941 const TQStringList& addresses )
03942 {
03943 TQString addrSpec = KPIM::getEmailAddress( address );
03944 for( TQStringList::ConstIterator it = addresses.begin();
03945 it != addresses.end(); ++it ) {
03946 if ( kasciistricmp( addrSpec.utf8().data(),
03947 KPIM::getEmailAddress( *it ).utf8().data() ) == 0 )
03948 return true;
03949 }
03950 return false;
03951 }
03952
03953
03954
03955
03956 TQString KMMessage::expandAliases( const TQString& recipients )
03957 {
03958 if ( recipients.isEmpty() )
03959 return TQString();
03960
03961 TQStringList recipientList = KPIM::splitEmailAddrList( recipients );
03962
03963 TQString expandedRecipients;
03964 for ( TQStringList::Iterator it = recipientList.begin();
03965 it != recipientList.end(); ++it ) {
03966 if ( !expandedRecipients.isEmpty() )
03967 expandedRecipients += ", ";
03968 TQString receiver = (*it).stripWhiteSpace();
03969
03970
03971 TQString expandedList = KAddrBookExternal::expandDistributionList( receiver );
03972 if ( !expandedList.isEmpty() ) {
03973 expandedRecipients += expandedList;
03974 continue;
03975 }
03976
03977
03978 TQString expandedNickName = KabcBridge::expandNickName( receiver );
03979 if ( !expandedNickName.isEmpty() ) {
03980 expandedRecipients += expandedNickName;
03981 continue;
03982 }
03983
03984
03985
03986 if ( receiver.find('@') == -1 ) {
03987 TDEConfigGroup general( KMKernel::config(), "General" );
03988 TQString defaultdomain = general.readEntry( "Default domain" );
03989 if( !defaultdomain.isEmpty() ) {
03990 expandedRecipients += receiver + "@" + defaultdomain;
03991 }
03992 else {
03993 expandedRecipients += guessEmailAddressFromLoginName( receiver );
03994 }
03995 }
03996 else
03997 expandedRecipients += receiver;
03998 }
03999
04000 return expandedRecipients;
04001 }
04002
04003
04004
04005
04006 TQString KMMessage::guessEmailAddressFromLoginName( const TQString& loginName )
04007 {
04008 if ( loginName.isEmpty() )
04009 return TQString();
04010
04011 char hostnameC[256];
04012
04013 hostnameC[255] = '\0';
04014
04015 if ( gethostname( hostnameC, 255 ) )
04016 hostnameC[0] = '\0';
04017 TQString address = loginName;
04018 address += '@';
04019 address += TQString::fromLocal8Bit( hostnameC );
04020
04021
04022 const KUser user( loginName );
04023 if ( user.isValid() ) {
04024 TQString fullName = user.fullName();
04025 if ( fullName.find( TQRegExp( "[^ 0-9A-Za-z\\x0080-\\xFFFF]" ) ) != -1 )
04026 address = '"' + fullName.replace( '\\', "\\" ).replace( '"', "\\" )
04027 + "\" <" + address + '>';
04028 else
04029 address = fullName + " <" + address + '>';
04030 }
04031
04032 return address;
04033 }
04034
04035
04036 void KMMessage::readConfig()
04037 {
04038 KMMsgBase::readConfig();
04039
04040 TDEConfig *config=KMKernel::config();
04041 TDEConfigGroupSaver saver(config, "General");
04042
04043 config->setGroup("General");
04044
04045 int languageNr = config->readNumEntry("reply-current-language",0);
04046
04047 {
04048 TDEConfigGroupSaver saver(config, TQString("KMMessage #%1").arg(languageNr));
04049 sReplyLanguage = config->readEntry("language",TDEGlobal::locale()->language());
04050 sReplyStr = config->readEntry("phrase-reply",
04051 i18n("On %D, you wrote:"));
04052 sReplyAllStr = config->readEntry("phrase-reply-all",
04053 i18n("On %D, %F wrote:"));
04054 sForwardStr = config->readEntry("phrase-forward",
04055 i18n("Forwarded Message"));
04056 sIndentPrefixStr = config->readEntry("indent-prefix",">%_");
04057 }
04058
04059 {
04060 TDEConfigGroupSaver saver(config, "Composer");
04061 sSmartQuote = GlobalSettings::self()->smartQuote();
04062 sWordWrap = GlobalSettings::self()->wordWrap();
04063 sWrapCol = GlobalSettings::self()->lineWrapWidth();
04064 if ((sWrapCol == 0) || (sWrapCol > 78))
04065 sWrapCol = 78;
04066 if (sWrapCol < 30)
04067 sWrapCol = 30;
04068
04069 sPrefCharsets = config->readListEntry("pref-charsets");
04070 }
04071
04072 {
04073 TDEConfigGroupSaver saver(config, "Reader");
04074 sHeaderStrategy = HeaderStrategy::create( config->readEntry( "header-set-displayed", "rich" ) );
04075 }
04076 }
04077
04078 TQCString KMMessage::defaultCharset()
04079 {
04080 TQCString retval;
04081
04082 if (!sPrefCharsets.isEmpty())
04083 retval = sPrefCharsets[0].latin1();
04084
04085 if (retval.isEmpty() || (retval == "locale")) {
04086 retval = TQCString(kmkernel->networkCodec()->mimeName());
04087 KPIM::kAsciiToLower( retval.data() );
04088 }
04089
04090 if (retval == "jisx0208.1983-0") retval = "iso-2022-jp";
04091 else if (retval == "ksc5601.1987-0") retval = "euc-kr";
04092 return retval;
04093 }
04094
04095 const TQStringList &KMMessage::preferredCharsets()
04096 {
04097 return sPrefCharsets;
04098 }
04099
04100
04101 TQCString KMMessage::charset() const
04102 {
04103 if ( mMsg->Headers().HasContentType() ) {
04104 DwMediaType &mType=mMsg->Headers().ContentType();
04105 mType.Parse();
04106 DwParameter *param=mType.FirstParameter();
04107 while(param){
04108 if (!kasciistricmp(param->Attribute().c_str(), "charset"))
04109 return param->Value().c_str();
04110 else param=param->Next();
04111 }
04112 }
04113 return "";
04114 }
04115
04116
04117 void KMMessage::setCharset( const TQCString &charset, DwEntity *entity )
04118 {
04119 kdWarning( type() != DwMime::kTypeText )
04120 << "KMMessage::setCharset(): trying to set a charset for a non-textual mimetype." << endl
04121 << "Fix this caller:" << endl
04122 << "====================================================================" << endl
04123 << kdBacktrace( 5 ) << endl
04124 << "====================================================================" << endl;
04125
04126 if ( !entity )
04127 entity = mMsg;
04128
04129 DwMediaType &mType = entity->Headers().ContentType();
04130 mType.Parse();
04131 DwParameter *param = mType.FirstParameter();
04132 while( param ) {
04133
04134
04135 if ( !kasciistricmp( param->Attribute().c_str(), "charset" ) )
04136 break;
04137
04138 param = param->Next();
04139 }
04140 if ( !param ) {
04141 param = new DwParameter;
04142 param->SetAttribute( "charset" );
04143 mType.AddParameter( param );
04144 }
04145 else
04146 mType.SetModified();
04147
04148 TQCString lowerCharset = charset;
04149 KPIM::kAsciiToLower( lowerCharset.data() );
04150 param->SetValue( DwString( lowerCharset ) );
04151 mType.Assemble();
04152 }
04153
04154
04155
04156 void KMMessage::setStatus(const KMMsgStatus aStatus, int idx)
04157 {
04158 if (mStatus == aStatus)
04159 return;
04160 KMMsgBase::setStatus(aStatus, idx);
04161 }
04162
04163 void KMMessage::setEncryptionState(const KMMsgEncryptionState s, int idx)
04164 {
04165 if( mEncryptionState == s )
04166 return;
04167 mEncryptionState = s;
04168 mDirty = true;
04169 KMMsgBase::setEncryptionState(s, idx);
04170 }
04171
04172 void KMMessage::setSignatureState(KMMsgSignatureState s, int idx)
04173 {
04174 if( mSignatureState == s )
04175 return;
04176 mSignatureState = s;
04177 mDirty = true;
04178 KMMsgBase::setSignatureState(s, idx);
04179 }
04180
04181 void KMMessage::setMDNSentState( KMMsgMDNSentState status, int idx )
04182 {
04183 if ( mMDNSentState == status )
04184 return;
04185 if ( status == 0 )
04186 status = KMMsgMDNStateUnknown;
04187 mMDNSentState = status;
04188 mDirty = true;
04189 KMMsgBase::setMDNSentState( status, idx );
04190 }
04191
04192
04193 void KMMessage::link( const KMMessage *aMsg, KMMsgStatus aStatus )
04194 {
04195 Q_ASSERT( aStatus == KMMsgStatusReplied
04196 || aStatus == KMMsgStatusForwarded
04197 || aStatus == KMMsgStatusDeleted );
04198
04199 TQString message = headerField( "X-KMail-Link-Message" );
04200 if ( !message.isEmpty() )
04201 message += ',';
04202 TQString type = headerField( "X-KMail-Link-Type" );
04203 if ( !type.isEmpty() )
04204 type += ',';
04205
04206 message += TQString::number( aMsg->getMsgSerNum() );
04207 if ( aStatus == KMMsgStatusReplied )
04208 type += "reply";
04209 else if ( aStatus == KMMsgStatusForwarded )
04210 type += "forward";
04211 else if ( aStatus == KMMsgStatusDeleted )
04212 type += "deleted";
04213
04214 setHeaderField( "X-KMail-Link-Message", message );
04215 setHeaderField( "X-KMail-Link-Type", type );
04216 }
04217
04218
04219 void KMMessage::getLink(int n, ulong *retMsgSerNum, KMMsgStatus *reStatus) const
04220 {
04221 *retMsgSerNum = 0;
04222 *reStatus = KMMsgStatusUnknown;
04223
04224 TQString message = headerField("X-KMail-Link-Message");
04225 TQString type = headerField("X-KMail-Link-Type");
04226 message = message.section(',', n, n);
04227 type = type.section(',', n, n);
04228
04229 if ( !message.isEmpty() && !type.isEmpty() ) {
04230 *retMsgSerNum = message.toULong();
04231 if ( type == "reply" )
04232 *reStatus = KMMsgStatusReplied;
04233 else if ( type == "forward" )
04234 *reStatus = KMMsgStatusForwarded;
04235 else if ( type == "deleted" )
04236 *reStatus = KMMsgStatusDeleted;
04237 }
04238 }
04239
04240
04241 DwBodyPart* KMMessage::findDwBodyPart( DwBodyPart* part, const TQString & partSpecifier )
04242 {
04243 if ( !part ) return 0;
04244 DwBodyPart* current;
04245
04246 if ( part->partId() == partSpecifier )
04247 return part;
04248
04249
04250 if ( part->hasHeaders() &&
04251 part->Headers().HasContentType() &&
04252 part->Body().FirstBodyPart() &&
04253 (DwMime::kTypeMultipart == part->Headers().ContentType().Type() ) &&
04254 (current = findDwBodyPart( part->Body().FirstBodyPart(), partSpecifier )) )
04255 {
04256 return current;
04257 }
04258
04259
04260 if ( part->Body().Message() &&
04261 part->Body().Message()->Body().FirstBodyPart() &&
04262 (current = findDwBodyPart( part->Body().Message()->Body().FirstBodyPart(),
04263 partSpecifier )) )
04264 {
04265 return current;
04266 }
04267
04268
04269 return findDwBodyPart( part->Next(), partSpecifier );
04270 }
04271
04272
04273 void KMMessage::updateBodyPart(const TQString partSpecifier, const TQByteArray & data)
04274 {
04275 if ( !data.data() || !data.size() )
04276 return;
04277
04278 DwString content( data.data(), data.size() );
04279 if ( numBodyParts() > 0 &&
04280 partSpecifier != "0" &&
04281 partSpecifier != "TEXT" )
04282 {
04283 TQString specifier = partSpecifier;
04284 if ( partSpecifier.endsWith(".HEADER") ||
04285 partSpecifier.endsWith(".MIME") ) {
04286
04287 specifier = partSpecifier.section( '.', 0, -2 );
04288 }
04289
04290
04291 mLastUpdated = findDwBodyPart( getFirstDwBodyPart(), specifier );
04292 kdDebug(5006) << "KMMessage::updateBodyPart " << specifier << endl;
04293 if (!mLastUpdated)
04294 {
04295 kdWarning(5006) << "KMMessage::updateBodyPart - can not find part "
04296 << specifier << endl;
04297 return;
04298 }
04299 if ( partSpecifier.endsWith(".MIME") )
04300 {
04301
04302
04303 content.resize( TQMAX( content.length(), 2 ) - 2 );
04304
04305
04306 mLastUpdated->Headers().DeleteAllFields();
04307 mLastUpdated->Headers().FromString( content );
04308 mLastUpdated->Headers().Parse();
04309 } else if ( partSpecifier.endsWith(".HEADER") )
04310 {
04311
04312 mLastUpdated->Body().Message()->Headers().FromString( content );
04313 mLastUpdated->Body().Message()->Headers().Parse();
04314 } else {
04315
04316 mLastUpdated->Body().FromString( content );
04317 TQString parentSpec = partSpecifier.section( '.', 0, -2 );
04318 if ( !parentSpec.isEmpty() )
04319 {
04320 DwBodyPart* parent = findDwBodyPart( getFirstDwBodyPart(), parentSpec );
04321 if ( parent && parent->hasHeaders() && parent->Headers().HasContentType() )
04322 {
04323 const DwMediaType& contentType = parent->Headers().ContentType();
04324 if ( contentType.Type() == DwMime::kTypeMessage &&
04325 contentType.Subtype() == DwMime::kSubtypeRfc822 )
04326 {
04327
04328
04329 parent->Body().Message()->Body().FromString( content );
04330 }
04331 }
04332 }
04333 }
04334
04335 } else
04336 {
04337
04338 if ( partSpecifier == "TEXT" )
04339 deleteBodyParts();
04340 mMsg->Body().FromString( content );
04341 mMsg->Body().Parse();
04342 }
04343 mNeedsAssembly = true;
04344 if (! partSpecifier.endsWith(".HEADER") )
04345 {
04346
04347 notify();
04348 }
04349 }
04350
04351 void KMMessage::updateInvitationState()
04352 {
04353 if ( mMsg && mMsg->hasHeaders() && mMsg->Headers().HasContentType() ) {
04354 TQString cntType = mMsg->Headers().ContentType().TypeStr().c_str();
04355 cntType += '/';
04356 cntType += mMsg->Headers().ContentType().SubtypeStr().c_str();
04357 if ( cntType.lower() == "text/calendar" ) {
04358 setStatus( KMMsgStatusHasInvitation );
04359 return;
04360 }
04361 }
04362 setStatus( KMMsgStatusHasNoInvitation );
04363 return;
04364 }
04365
04366
04367 void KMMessage::updateAttachmentState( DwBodyPart* part )
04368 {
04369 if ( !part )
04370 part = getFirstDwBodyPart();
04371
04372 if ( !part )
04373 {
04374
04375 setStatus( KMMsgStatusHasNoAttach );
04376 return;
04377 }
04378
04379 bool filenameEmpty = true;
04380 if ( part->hasHeaders() ) {
04381 if ( part->Headers().HasContentDisposition() ) {
04382 DwDispositionType cd = part->Headers().ContentDisposition();
04383 filenameEmpty = cd.Filename().empty();
04384 if ( filenameEmpty ) {
04385
04386 filenameEmpty = KMMsgBase::decodeRFC2231String( KMMsgBase::extractRFC2231HeaderField( cd.AsString().c_str(), "filename" ) ).isEmpty();
04387 }
04388 }
04389
04390
04391
04392 if ( filenameEmpty && part->Headers().HasContentType() ) {
04393 DwMediaType contentType = part->Headers().ContentType();
04394 filenameEmpty = contentType.Name().empty();
04395 if ( filenameEmpty ) {
04396
04397 filenameEmpty = KMMsgBase::decodeRFC2231String( KMMsgBase::extractRFC2231HeaderField(
04398 contentType.AsString().c_str(), "name" ) ).isEmpty();
04399 }
04400 }
04401 }
04402
04403 if ( part->hasHeaders() &&
04404 ( ( part->Headers().HasContentDisposition() &&
04405 !part->Headers().ContentDisposition().Filename().empty() ) ||
04406 ( part->Headers().HasContentType() &&
04407 !filenameEmpty ) ) )
04408 {
04409
04410 if ( !part->Headers().HasContentType() ||
04411 ( part->Headers().HasContentType() &&
04412 part->Headers().ContentType().Subtype() != DwMime::kSubtypePgpSignature &&
04413 part->Headers().ContentType().Subtype() != DwMime::kSubtypePkcs7Signature ) )
04414 {
04415 setStatus( KMMsgStatusHasAttach );
04416 }
04417 return;
04418 }
04419
04420
04421 if ( part->hasHeaders() &&
04422 part->Headers().HasContentType() &&
04423 part->Body().FirstBodyPart() &&
04424 (DwMime::kTypeMultipart == part->Headers().ContentType().Type() ) )
04425 {
04426 updateAttachmentState( part->Body().FirstBodyPart() );
04427 }
04428
04429
04430 if ( part->Body().Message() &&
04431 part->Body().Message()->Body().FirstBodyPart() )
04432 {
04433 updateAttachmentState( part->Body().Message()->Body().FirstBodyPart() );
04434 }
04435
04436
04437 if ( part->Next() )
04438 updateAttachmentState( part->Next() );
04439 else if ( attachmentState() == KMMsgAttachmentUnknown )
04440 setStatus( KMMsgStatusHasNoAttach );
04441 }
04442
04443 void KMMessage::setBodyFromUnicode( const TQString &str, DwEntity *entity )
04444 {
04445 TQCString encoding = KMMsgBase::autoDetectCharset( charset(), KMMessage::preferredCharsets(), str );
04446 if ( encoding.isEmpty() )
04447 encoding = "utf-8";
04448 const TQTextCodec * codec = KMMsgBase::codecForName( encoding );
04449 assert( codec );
04450 TQValueList<int> dummy;
04451 setCharset( encoding, entity );
04452 setBodyAndGuessCte( codec->fromUnicode( str ), dummy, false ,
04453 false, entity );
04454 }
04455
04456 const TQTextCodec * KMMessage::codec() const {
04457 const TQTextCodec * c = mOverrideCodec;
04458 if ( !c )
04459
04460 c = KMMsgBase::codecForName( charset() );
04461 if ( !c ) {
04462
04463
04464 c = KMMsgBase::codecForName( GlobalSettings::self()->fallbackCharacterEncoding().latin1() );
04465 }
04466 if ( !c )
04467
04468
04469 c = kmkernel->networkCodec();
04470 assert( c );
04471 return c;
04472 }
04473
04474 TQString KMMessage::bodyToUnicode(const TQTextCodec* codec) const {
04475 if ( !codec )
04476
04477 codec = this->codec();
04478 assert( codec );
04479
04480 return codec->toUnicode( bodyDecoded() );
04481 }
04482
04483
04484 TQCString KMMessage::mboxMessageSeparator()
04485 {
04486 TQCString str( KPIM::getFirstEmailAddress( rawHeaderField("From") ) );
04487 if ( str.isEmpty() )
04488 str = "unknown@unknown.invalid";
04489 TQCString dateStr( dateShortStr() );
04490 if ( dateStr.isEmpty() ) {
04491 time_t t = ::time( 0 );
04492 dateStr = ctime( &t );
04493 const int len = dateStr.length();
04494 if ( dateStr[len-1] == '\n' )
04495 dateStr.truncate( len - 1 );
04496 }
04497 return "From " + str + " " + dateStr + "\n";
04498 }
04499
04500 void KMMessage::deleteWhenUnused()
04501 {
04502 sPendingDeletes << this;
04503 }
04504
04505 DwBodyPart* KMMessage::findPart( int index )
04506 {
04507 int accu = 0;
04508 return findPartInternal( getTopLevelPart(), index, accu );
04509 }
04510
04511 DwBodyPart* KMMessage::findPartInternal(DwEntity * root, int index, int & accu)
04512 {
04513 accu++;
04514 if ( index < accu )
04515 return 0;
04516 DwBodyPart *current = dynamic_cast<DwBodyPart*>( root );
04517 if ( index == accu )
04518 return current;
04519 DwBodyPart *rv = 0;
04520 if ( root->Body().FirstBodyPart() )
04521 rv = findPartInternal( root->Body().FirstBodyPart(), index, accu );
04522 if ( !rv && current && current->Next() )
04523 rv = findPartInternal( current->Next(), index, accu );
04524 if ( !rv && root->Body().Message() )
04525 rv = findPartInternal( root->Body().Message(), index, accu );
04526 return rv;
04527 }
04528