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