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