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