00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #ifdef HAVE_CONFIG_H
00033 #include <config.h>
00034 #endif
00035
00036 #include "headerstyle.h"
00037
00038 #include "headerstrategy.h"
00039 #include "linklocator.h"
00040 #include "kmmessage.h"
00041 #include "kmkernel.h"
00042
00043 #include <libkdepim/email.h>
00044
00045 #include <mimelib/string.h>
00046 #include <mimelib/field.h>
00047 #include <mimelib/headers.h>
00048
00049 #include <kdebug.h>
00050 #include <klocale.h>
00051 #include <kglobal.h>
00052 #include "kimproxy.h"
00053 #include <kabc/stdaddressbook.h>
00054 #include <kabc/addresseelist.h>
00055 #include <kmdcodec.h>
00056 #include <qdatetime.h>
00057 #include <qbuffer.h>
00058 #include <qimage.h>
00059 #include <qapplication.h>
00060 #include <qregexp.h>
00061
00062 #include <kmime_header_parsing.h>
00063 using KMime::Types::AddrSpecList;
00064 #include <libkpimidentities/identity.h>
00065 #include <libkpimidentities/identitymanager.h>
00066
00067 namespace KMail {
00068
00069
00070
00071
00072 static inline QString directionOf( const QString & str ) {
00073 return str.isRightToLeft() ? "rtl" : "ltr" ;
00074 }
00075
00076 #if 0
00077
00078
00079
00080 static QString convertToHtmlBlock( const QString & str, bool useSpan=false ) {
00081 QString dir = directionOf( str );
00082 QString format = "<%1 dir=\"%3\">%4</%2>";
00083 return format.arg( useSpan ? "span" : "div" )
00084 .arg( useSpan ? "span" : "div" )
00085 .arg( dir )
00086 .arg( LinkLocator::convertToHtml( str ) );
00087 }
00088 #endif
00089
00090
00091 static QString strToHtml( const QString & str, bool preserveBlanks=true ) {
00092 return LinkLocator::convertToHtml( str, preserveBlanks );
00093 }
00094
00095
00096
00097
00098
00099
00100 class BriefHeaderStyle : public HeaderStyle {
00101 friend class HeaderStyle;
00102 protected:
00103 BriefHeaderStyle() : HeaderStyle() {}
00104 virtual ~BriefHeaderStyle() {}
00105
00106 public:
00107 const char * name() const { return "brief"; }
00108 const HeaderStyle * next() const { return plain(); }
00109 const HeaderStyle * prev() const { return minimal(); }
00110
00111 QString format( const KMMessage * message, const HeaderStrategy * strategy,
00112 const QString & vCardName, bool printing ) const;
00113 };
00114
00115 QString BriefHeaderStyle::format( const KMMessage * message,
00116 const HeaderStrategy * strategy,
00117 const QString & vCardName, bool printing ) const {
00118 if ( !message ) return QString::null;
00119 if ( !strategy )
00120 strategy = HeaderStrategy::brief();
00121
00122
00123
00124
00125 QString dir = QApplication::reverseLayout() ? "rtl" : "ltr" ;
00126
00127
00128
00129
00130
00131
00132
00133 QString subjectDir;
00134 if (!message->subject().isEmpty())
00135 subjectDir = directionOf( message->cleanSubject() );
00136 else
00137 subjectDir = directionOf( i18n("No Subject") );
00138
00139
00140 QString dateString;
00141 if( printing ) {
00142 QDateTime dateTime;
00143 KLocale * locale = KGlobal::locale();
00144 dateTime.setTime_t( message->date() );
00145 dateString = locale->formatDateTime( dateTime );
00146 } else {
00147 dateString = message->dateStr();
00148 }
00149
00150 QString headerStr = "<div class=\"header\" dir=\"" + dir + "\">\n";
00151
00152 if ( strategy->showHeader( "subject" ) )
00153 headerStr += "<div dir=\"" + subjectDir + "\">\n"
00154 "<b style=\"font-size:130%\">" +
00155 strToHtml( message->subject() ) +
00156 "</b></div>\n";
00157
00158 QStringList headerParts;
00159
00160 if ( strategy->showHeader( "from" ) ) {
00161 QString fromStr = message->from();
00162 if ( fromStr.isEmpty() )
00163 fromStr = message->fromStrip();
00164 QString fromPart = KMMessage::emailAddrAsAnchor( fromStr, true );
00165 if ( !vCardName.isEmpty() )
00166 fromPart += " <a href=\"" + vCardName + "\">" + i18n("[vCard]") + "</a>";
00167 headerParts << fromPart;
00168 }
00169
00170 if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty() )
00171 headerParts << i18n("CC: ") + KMMessage::emailAddrAsAnchor( message->cc(), true );
00172
00173 if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty() )
00174 headerParts << i18n("BCC: ") + KMMessage::emailAddrAsAnchor( message->bcc(), true );
00175
00176 if ( strategy->showHeader( "date" ) )
00177 headerParts << strToHtml(message->dateShortStr());
00178
00179
00180 headerStr += " (" + headerParts.grep( QRegExp( "\\S" ) ).join( ",\n" ) + ')';
00181
00182 headerStr += "</div>\n";
00183
00184
00185
00186 return headerStr;
00187 }
00188
00189
00190
00191
00192
00193
00194
00195 class PlainHeaderStyle : public HeaderStyle {
00196 friend class HeaderStyle;
00197 protected:
00198 PlainHeaderStyle() : HeaderStyle() {}
00199 virtual ~PlainHeaderStyle() {}
00200
00201 public:
00202 const char * name() const { return "plain"; }
00203 const HeaderStyle * next() const { return fancy(); }
00204 const HeaderStyle * prev() const { return brief(); }
00205
00206 QString format( const KMMessage * message, const HeaderStrategy * strategy,
00207 const QString & vCardName, bool printing ) const;
00208
00209 private:
00210 QString formatAllMessageHeaders( const KMMessage * message ) const;
00211 };
00212
00213 QString PlainHeaderStyle::format( const KMMessage * message,
00214 const HeaderStrategy * strategy,
00215 const QString & vCardName, bool printing ) const {
00216 if ( !message ) return QString::null;
00217 if ( !strategy )
00218 strategy = HeaderStrategy::rich();
00219
00220
00221
00222
00223 QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
00224
00225
00226
00227
00228
00229
00230
00231 QString subjectDir;
00232 if (!message->subject().isEmpty())
00233 subjectDir = directionOf( message->cleanSubject() );
00234 else
00235 subjectDir = directionOf( i18n("No Subject") );
00236
00237
00238 QString dateString;
00239 if( printing ) {
00240 QDateTime dateTime;
00241 KLocale* locale = KGlobal::locale();
00242 dateTime.setTime_t( message->date() );
00243 dateString = locale->formatDateTime( dateTime );
00244 }
00245 else {
00246 dateString = message->dateStr();
00247 }
00248
00249 QString headerStr = QString("<div class=\"header\" dir=\"%1\">").arg(dir);
00250
00251 if ( strategy->headersToDisplay().isEmpty()
00252 && strategy->defaultPolicy() == HeaderStrategy::Display ) {
00253
00254 headerStr += formatAllMessageHeaders( message );
00255 return headerStr + "</div>";
00256 }
00257
00258
00259 if ( strategy->showHeader( "subject" ) )
00260 headerStr += QString("<div dir=\"%1\"><b style=\"font-size:130%\">" +
00261 strToHtml(message->subject()) + "</b></div>\n")
00262 .arg(subjectDir);
00263
00264 if ( strategy->showHeader( "date" ) )
00265 headerStr.append(i18n("Date: ") + strToHtml(dateString)+"<br>\n");
00266
00267
00268 QString presence;
00269 QString kabcUid;
00270 if ( strategy->showHeader( "status" ) )
00271 {
00272 KABC::AddressBook *addressBook = KABC::StdAddressBook::self();
00273 KABC::AddresseeList addresses = addressBook->findByEmail( KPIM::getEmailAddr( message->from() ) );
00274 ::KIMProxy *imProxy = KMKernel::self()->imProxy();
00275 kabcUid = addresses[0].uid();
00276 presence = imProxy->presenceString( kabcUid );
00277 }
00278
00279 if ( strategy->showHeader( "from" ) ) {
00280 QString fromStr = message->from();
00281 if ( fromStr.isEmpty() )
00282 fromStr = message->fromStrip();
00283 headerStr.append(i18n("From: ") +
00284 KMMessage::emailAddrAsAnchor( fromStr, false) );
00285 if ( !vCardName.isEmpty() )
00286 headerStr.append(" <a href=\"" + vCardName +
00287 "\">" + i18n("[vCard]") + "</a>" );
00288 if ( !presence.isEmpty() && strategy->showHeader( "status" ) )
00289 headerStr.append(" (<span name=\"presence-" + kabcUid + "\">" + presence + "</span>)" );
00290 if ( strategy->showHeader( "organization" )
00291 && !message->headerField("Organization").isEmpty())
00292 headerStr.append(" (" +
00293 strToHtml(message->headerField("Organization")) + ")");
00294 headerStr.append("<br>\n");
00295 }
00296
00297 if ( strategy->showHeader( "to" ) )
00298 headerStr.append(i18n("To: ")+
00299 KMMessage::emailAddrAsAnchor(message->to(),FALSE) + "<br>\n");
00300
00301 if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty() )
00302 headerStr.append(i18n("CC: ")+
00303 KMMessage::emailAddrAsAnchor(message->cc(),FALSE) + "<br>\n");
00304
00305 if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty() )
00306 headerStr.append(i18n("BCC: ")+
00307 KMMessage::emailAddrAsAnchor(message->bcc(),FALSE) + "<br>\n");
00308
00309 if ( strategy->showHeader( "reply-to" ) && !message->replyTo().isEmpty())
00310 headerStr.append(i18n("Reply to: ")+
00311 KMMessage::emailAddrAsAnchor(message->replyTo(),FALSE) + "<br>\n");
00312
00313 headerStr += "</div>\n";
00314
00315 return headerStr;
00316 }
00317
00318 QString PlainHeaderStyle::formatAllMessageHeaders( const KMMessage * message ) const {
00319 const DwHeaders & headers = message->headers();
00320 QString result;
00321
00322 for ( const DwField * field = headers.FirstField() ; field ; field = field->Next() ) {
00323 result += ( field->FieldNameStr() + ": " ).c_str();
00324 result += strToHtml( field->FieldBodyStr().c_str() );
00325 result += "<br>\n";
00326 }
00327
00328 return result;
00329 }
00330
00331
00332
00333
00334
00335
00336 class FancyHeaderStyle : public HeaderStyle {
00337 friend class HeaderStyle;
00338 protected:
00339 FancyHeaderStyle() : HeaderStyle() {}
00340 virtual ~FancyHeaderStyle() {}
00341
00342 public:
00343 const char * name() const { return "fancy"; }
00344 const HeaderStyle * next() const { return minimal(); }
00345 const HeaderStyle * prev() const { return brief(); }
00346
00347 QString format( const KMMessage * message, const HeaderStrategy * strategy,
00348 const QString & vCardName, bool printing ) const;
00349 static QString imgToDataUrl( const QImage &image );
00350
00351 };
00352
00353 QString FancyHeaderStyle::format( const KMMessage * message,
00354 const HeaderStrategy * strategy,
00355 const QString & vCardName, bool printing ) const {
00356 if ( !message ) return QString::null;
00357 if ( !strategy )
00358 strategy = HeaderStrategy::rich();
00359
00360
00361
00362
00363
00364 QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
00365 QString headerStr = QString("<div class=\"fancy header\" dir=\"%1\">\n").arg(dir);
00366
00367
00368
00369
00370
00371
00372
00373 QString subjectDir;
00374 if ( !message->subject().isEmpty() )
00375 subjectDir = directionOf( message->cleanSubject() );
00376 else
00377 subjectDir = directionOf( i18n("No Subject") );
00378
00379
00380 QString dateString;
00381 if( printing ) {
00382 QDateTime dateTime;
00383 KLocale* locale = KGlobal::locale();
00384 dateTime.setTime_t( message->date() );
00385 dateString = locale->formatDateTime( dateTime );
00386 }
00387 else {
00388 dateString = message->dateStr();
00389 }
00390
00391 QString userHTML;
00392 QString presence;
00393
00394
00395
00396
00397 ::KIMProxy *imProxy = KMKernel::self()->imProxy();
00398 QString kabcUid;
00399 if ( ( strategy->showHeader( "status" ) || strategy->showHeader( "statuspic" ) ) )
00400 {
00401 if ( imProxy->initialize() )
00402 {
00403 KABC::AddressBook *addressBook = KABC::StdAddressBook::self();
00404 KABC::AddresseeList addresses = addressBook->findByEmail( KPIM::getEmailAddr( message->from() ) );
00405
00406 if( addresses.count() == 1 )
00407 {
00408
00409 kabcUid = addresses[0].uid();
00410
00411
00412 presence = imProxy->presenceString( kabcUid );
00413 if ( !presence.isEmpty() )
00414 {
00415 QString presenceIcon = QString::fromLatin1( " <img src=\"%1\"/>" )
00416 .arg( imgToDataUrl( imProxy->presenceIcon( kabcUid ).convertToImage() ) );
00417 presence += presenceIcon;
00418 }
00419
00420 if ( strategy->showHeader( "statuspic" ) )
00421 {
00422 QString photoURL;
00423 if ( addresses[0].photo().isIntern() )
00424 {
00425
00426
00427 QImage photo = addresses[0].photo().data();
00428 if ( !photo.isNull() )
00429 {
00430 photoURL = imgToDataUrl( photo );
00431 }
00432 }
00433 else
00434 {
00435
00436 photoURL = addresses[0].photo().url();
00437 if ( photoURL.startsWith("/") )
00438 photoURL.prepend( "file:" );
00439 }
00440 if( !photoURL.isEmpty() )
00441 {
00442
00443 userHTML = QString("<img src=\"%1\" width=\"60\" height=\"60\">").arg( photoURL );
00444 if ( presence.isEmpty() )
00445 {
00446 userHTML = QString("<div class=\"senderpic\">") + userHTML + "</div>";
00447 }
00448 else
00449 userHTML = QString( "<div class=\"senderpic\">"
00450 "<a href=\"im:%1\">%2<div class=\"senderstatus\"><span name=\"presence-%2\">%3</span></div></a>"
00451 "</div>" ).arg( kabcUid )
00452 .arg( userHTML )
00453 .arg( presence );
00454 }
00455 }
00456 }
00457 else
00458 {
00459 kdDebug( 5006 ) << "Multiple / No addressees matched email address; Count is " << addresses.count() << endl;
00460 userHTML = " ";
00461 }
00462 }
00463
00464
00465
00466
00467 }
00468
00469
00470 kdDebug( 5006 ) << "final presence: '" << presence << "'" << endl;
00471
00472
00473 if ( strategy->showHeader( "subject" ) )
00474 headerStr += QString("<div dir=\"%1\">%2</div>\n")
00475 .arg(subjectDir)
00476 .arg(message->subject().isEmpty()?
00477 i18n("No Subject") :
00478 strToHtml(message->subject()));
00479 headerStr += "<table class=\"outer\"><tr><td width=\"100%\"><table>\n";
00480
00481
00482
00483
00484 if ( strategy->showHeader( "from" ) ) {
00485 QString fromStr = message->from();
00486 if ( fromStr.isEmpty() )
00487 fromStr = message->fromStrip();
00488 headerStr += QString("<tr><th>%1</th>\n"
00489 "<td>")
00490 .arg(i18n("From: "))
00491 + KMMessage::emailAddrAsAnchor( fromStr, false )
00492 + ( !vCardName.isEmpty() ? " <a href=\"" + vCardName + "\">"
00493 + i18n("[vCard]") + "</a>"
00494 : QString("") )
00495 + ( ( !presence.isEmpty() && strategy->showHeader( "status" ) )
00496 ? " (<span name=\"presence-" + kabcUid + "\">" + presence + "</span>)"
00497 : QString("") )
00498 + ( message->headerField("Organization").isEmpty()
00499 ? QString("")
00500 : " ("
00501 + strToHtml(message->headerField("Organization"))
00502 + ")")
00503 + "</td></tr>\n";
00504 }
00505
00506 if ( strategy->showHeader( "to" ) )
00507 headerStr.append(QString("<tr><th>%1</th>\n"
00508 "<td>%2</td></tr>\n")
00509 .arg(i18n("To: "))
00510 .arg(KMMessage::emailAddrAsAnchor(message->to(),FALSE)));
00511
00512
00513 if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty())
00514 headerStr.append(QString("<tr><th>%1</th>\n"
00515 "<td>%2</td></tr>\n")
00516 .arg(i18n("CC: "))
00517 .arg(KMMessage::emailAddrAsAnchor(message->cc(),FALSE)));
00518
00519
00520 if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty())
00521 headerStr.append(QString("<tr><th>%1</th>\n"
00522 "<td>%2</td></tr>\n")
00523 .arg(i18n("BCC: "))
00524 .arg(KMMessage::emailAddrAsAnchor(message->bcc(),FALSE)));
00525
00526 if ( strategy->showHeader( "date" ) )
00527 headerStr.append(QString("<tr><th>%1</th>\n"
00528 "<td dir=\"%2\">%3</td></tr>\n")
00529 .arg(i18n("Date: "))
00530 .arg( directionOf( message->dateStr() ) )
00531 .arg(strToHtml(dateString)));
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541 headerStr.append(
00542 QString("</table></td><td align=\"center\">%1</td></tr></table>\n").arg(userHTML)
00543 );
00544
00545 headerStr += "</div>\n\n";
00546 return headerStr;
00547 }
00548
00549 QString FancyHeaderStyle::imgToDataUrl( const QImage &image )
00550 {
00551 QByteArray ba;
00552 QBuffer buffer( ba );
00553 buffer.open( IO_WriteOnly );
00554 image.save( &buffer, "PNG" );
00555 return QString::fromLatin1("data:image/png;base64,%1").arg( KCodecs::base64Encode( ba ) );
00556 }
00557
00558
00559
00560
00561 class MinimalHeaderStyle : public HeaderStyle {
00562 friend class HeaderStyle;
00563 protected:
00564 MinimalHeaderStyle() : HeaderStyle() {}
00565 virtual ~MinimalHeaderStyle() {}
00566
00567 public:
00568 const char * name() const { return "minimal"; }
00569 const HeaderStyle * next() const { return brief(); }
00570 const HeaderStyle * prev() const { return fancy(); }
00571
00572 QString format( const KMMessage * message, const HeaderStrategy * strategy,
00573 const QString & vCardName, bool printing ) const;
00574 };
00575
00576 QString MinimalHeaderStyle::format( const KMMessage * message,
00577 const HeaderStrategy * strategy,
00578 const QString & vCardName, bool printing ) const {
00579 if ( !message ) return QString::null;
00580 if ( !strategy )
00581 strategy = HeaderStrategy::minimal();
00582
00583
00584
00585
00586 QString dir = QApplication::reverseLayout() ? "rtl" : "ltr" ;
00587
00588
00589
00590
00591
00592
00593
00594 QString subjectDir;
00595 if (!message->subject().isEmpty())
00596 subjectDir = directionOf( message->cleanSubject() );
00597 else
00598 subjectDir = directionOf( i18n("No Subject") );
00599
00600
00601 QString dateString;
00602 if( printing ) {
00603 QDateTime dateTime;
00604 KLocale * locale = KGlobal::locale();
00605 dateTime.setTime_t( message->date() );
00606 dateString = locale->formatDateTime( dateTime );
00607 } else {
00608 dateString = message->dateStr();
00609 }
00610
00611 QString headerStr = "<div class=\"header\" dir=\"" + dir + "\">\n";
00612
00613 if ( strategy->showHeader( "subject" ) )
00614 headerStr += "<div dir=\"" + subjectDir + "\">\n"
00615 "<b style=\"font-size:130%\">" +
00616 strToHtml( message->subject() ) +
00617 "</b></div>\n";
00618
00619 QStringList headerParts;
00620
00621 if ( strategy->showHeader( "from" ) ) {
00622 QString fromStr = message->from();
00623 if ( fromStr.isEmpty() )
00624 fromStr = message->fromStrip();
00625 QString fromPart = KMMessage::emailAddrAsAnchor( fromStr, true );
00626 if ( !vCardName.isEmpty() )
00627 fromPart += " <a href=\"" + vCardName + "\">" + i18n("[vCard]") + "</a>";
00628 headerParts.append( i18n("From: ") + fromPart );
00629 }
00630
00631 if ( strategy->showHeader( "to" ) ) {
00632 QStringList identities;
00633 KMime::Types::AddrSpecList toList = message->extractAddrSpecs("To");
00634 toList += message->extractAddrSpecs("Cc");
00635 if ( !toList.isEmpty() ) {
00636 KMime::Types::AddrSpecList::iterator it;
00637 for ( it = toList.begin(); it != toList.end(); ++it ) {
00638 KPIM::Identity ident =
00639 KMKernel::self()->identityManager()->identityForAddress( (*it).asString() );
00640 if ( !ident.isNull() &&
00641 KMKernel::self()->identityManager()->thatIsMe( (*it).asString() ) ) {
00642 identities.append( KMMessage::emailAddrAsAnchor( ident.fullEmailAddr(), false ) );
00643 }
00644 }
00645 }
00646 if ( !identities.isEmpty() ) {
00647 headerParts.append( i18n("To: ") + identities.join(",") );
00648 } else {
00649
00650
00651 headerParts.append( i18n("To: ") +
00652 KMMessage::emailAddrAsAnchor( message->to( ), false) );
00653 }
00654 }
00655
00656 if ( strategy->showHeader( "date" ) ) {
00657 headerParts.append(i18n("Date: ") + strToHtml(message->dateShortStr()) );
00658 }
00659
00660
00661 headerStr += " (" + headerParts.grep( QRegExp( "\\S" ) ).join( ",\n" ) + ')';
00662
00663 headerStr += "</div>\n";
00664
00665 return headerStr;
00666 }
00667
00668
00669
00670
00671
00672
00673 HeaderStyle::HeaderStyle() {
00674
00675 }
00676
00677 HeaderStyle::~HeaderStyle() {
00678
00679 }
00680
00681 const HeaderStyle * HeaderStyle::create( Type type ) {
00682 switch ( type ) {
00683 case Brief: return brief();
00684 case Plain: return plain();
00685 case Fancy: return fancy();
00686 case Minimal: return minimal();
00687 }
00688 kdFatal( 5006 ) << "HeaderStyle::create(): Unknown header style ( type == "
00689 << (int)type << " ) requested!" << endl;
00690 return 0;
00691 }
00692
00693 const HeaderStyle * HeaderStyle::create( const QString & type ) {
00694 QString lowerType = type.lower();
00695 if ( lowerType == "brief" ) return brief();
00696 if ( lowerType == "plain" ) return plain();
00697 if ( lowerType == "minimal" ) return minimal();
00698
00699
00700
00701 return fancy();
00702 }
00703
00704 static const HeaderStyle * briefStyle = 0;
00705 static const HeaderStyle * plainStyle = 0;
00706 static const HeaderStyle * fancyStyle = 0;
00707 static const HeaderStyle * minimalStyle = 0;
00708
00709 const HeaderStyle * HeaderStyle::brief() {
00710 if ( !briefStyle )
00711 briefStyle = new BriefHeaderStyle();
00712 return briefStyle;
00713 }
00714
00715 const HeaderStyle * HeaderStyle::plain() {
00716 if ( !plainStyle )
00717 plainStyle = new PlainHeaderStyle();
00718 return plainStyle;
00719 }
00720
00721 const HeaderStyle * HeaderStyle::fancy() {
00722 if ( !fancyStyle )
00723 fancyStyle = new FancyHeaderStyle();
00724 return fancyStyle;
00725 }
00726
00727 const HeaderStyle * HeaderStyle::minimal() {
00728 if( !minimalStyle )
00729 minimalStyle = new MinimalHeaderStyle();
00730 return minimalStyle;
00731 }
00732
00733 }