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