kmail

headerstyle.cpp

00001 /*  -*- c++ -*-
00002     headerstyle.cpp
00003 
00004     This file is part of KMail, the KDE mail client.
00005     Copyright (c) 2003 Marc Mutz <mutz@kde.org>
00006 
00007     KMail is free software; you can redistribute it and/or modify it
00008     under the terms of the GNU General Public License, version 2, as
00009     published by the Free Software Foundation.
00010 
00011     KMail is distributed in the hope that it will be useful, but
00012     WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014     General Public License for more details.
00015 
00016     You should have received a copy of the GNU General Public License
00017     along with this program; if not, write to the Free Software
00018     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00019 
00020     In addition, as a special exception, the copyright holders give
00021     permission to link the code of this program with any edition of
00022     the Qt library by Trolltech AS, Norway (or with modified versions
00023     of Qt that use the same license as Qt), and distribute linked
00024     combinations including the two.  You must obey the GNU General
00025     Public License in all respects for all of the code used other than
00026     Qt.  If you modify this file, you may extend this exception to
00027     your version of the file, but you are not obligated to do so.  If
00028     you do not wish to do so, delete this exception statement from
00029     your version.
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 "kmkernel.h"
00040 #include "linklocator.h"
00041 #include "kmmessage.h"
00042 #include "spamheaderanalyzer.h"
00043 #include "globalsettings.h"
00044 
00045 #include <libemailfunctions/email.h>
00046 #include <libkdepim/kxface.h>
00047 using namespace KPIM;
00048 
00049 #include <mimelib/string.h>
00050 #include <mimelib/field.h>
00051 #include <mimelib/headers.h>
00052 
00053 #include <kdebug.h>
00054 #include <klocale.h>
00055 #include <kglobal.h>
00056 #include <kimproxy.h>
00057 #include <kabc/stdaddressbook.h>
00058 #include <kabc/addresseelist.h>
00059 #include <kmdcodec.h>
00060 #include <qdatetime.h>
00061 #include <qbuffer.h>
00062 #include <qbitmap.h>
00063 #include <qimage.h>
00064 #include <qapplication.h>
00065 #include <qregexp.h>
00066 
00067 #include <kstandarddirs.h>
00068 
00069 namespace KMail {
00070 
00071   //
00072   // Convenience functions:
00073   //
00074   static inline QString directionOf( const QString & str ) {
00075     return str.isRightToLeft() ? "rtl" : "ltr" ;
00076   }
00077 
00078 #if 0
00079   // Converts to html. Changes URLs into href's, escapes HTML special
00080   // chars and inserts the result into an <div> or <span> tag with
00081   // "dir" set to "rtl" or "ltr" depending on the direction of @p str.
00082   static QString convertToHtmlBlock( const QString & str, bool useSpan=false ) {
00083     QString dir = directionOf( str );
00084     QString format = "<%1 dir=\"%3\">%4</%2>";
00085     return format.arg( useSpan ? "span" : "div" )
00086                  .arg( useSpan ? "span" : "div" )
00087                  .arg( dir )
00088                  .arg( LinkLocator::convertToHtml( str ) );
00089   }
00090 #endif
00091 
00092   // ### tmp wrapper to make kmreaderwin code working:
00093   static QString strToHtml( const QString & str,
00094                             int flags = LinkLocator::PreserveSpaces ) {
00095     return LinkLocator::convertToHtml( str, flags );
00096   }
00097 
00098   //
00099   // BriefHeaderStyle
00100   //   Show everything in a single line, don't show header field names.
00101   //
00102 
00103   class BriefHeaderStyle : public HeaderStyle {
00104     friend class ::KMail::HeaderStyle;
00105   protected:
00106     BriefHeaderStyle() : HeaderStyle() {}
00107     virtual ~BriefHeaderStyle() {}
00108 
00109   public:
00110     const char * name() const { return "brief"; }
00111     const HeaderStyle * next() const { return plain(); }
00112     const HeaderStyle * prev() const { return fancy(); }
00113 
00114     QString format( const KMMessage * message, const HeaderStrategy * strategy,
00115                     const QString & vCardName, bool printing, bool topLevel ) const;
00116   };
00117 
00118   QString BriefHeaderStyle::format( const KMMessage * message,
00119                                     const HeaderStrategy * strategy,
00120                                     const QString & vCardName, bool printing, bool topLevel ) const {
00121     Q_UNUSED( topLevel );
00122     if ( !message ) return QString::null;
00123     if ( !strategy )
00124       strategy = HeaderStrategy::brief();
00125 
00126     // The direction of the header is determined according to the direction
00127     // of the application layout.
00128 
00129     QString dir = QApplication::reverseLayout() ? "rtl" : "ltr" ;
00130 
00131     // However, the direction of the message subject within the header is
00132     // determined according to the contents of the subject itself. Since
00133     // the "Re:" and "Fwd:" prefixes would always cause the subject to be
00134     // considered left-to-right, they are ignored when determining its
00135     // direction.
00136 
00137     QString subjectDir;
00138     if (!message->subject().isEmpty())
00139       subjectDir = directionOf( message->cleanSubject() );
00140     else
00141       subjectDir = directionOf( i18n("No Subject") );
00142 
00143     // Prepare the date string (when printing always use the localized date)
00144     QString dateString;
00145     if( printing ) {
00146       QDateTime dateTime;
00147       KLocale * locale = KGlobal::locale();
00148       dateTime.setTime_t( message->date() );
00149       dateString = locale->formatDateTime( dateTime );
00150     } else {
00151       dateString = message->dateStr();
00152     }
00153 
00154     QString headerStr = "<div class=\"header\" dir=\"" + dir + "\">\n";
00155 
00156     if ( strategy->showHeader( "subject" ) )
00157       headerStr += "<div dir=\"" + subjectDir + "\">\n"
00158                    "<b style=\"font-size:130%\">" +
00159                            strToHtml( message->subject() ) +
00160                            "</b></div>\n";
00161 
00162     QStringList headerParts;
00163 
00164     if ( strategy->showHeader( "from" ) ) {
00165       QString fromStr = message->from();
00166       if ( fromStr.isEmpty() ) // no valid email in from, maybe just a name
00167         fromStr = message->fromStrip(); // let's use that
00168       QString fromPart = KMMessage::emailAddrAsAnchor( fromStr, true );
00169       if ( !vCardName.isEmpty() )
00170         fromPart += "&nbsp;&nbsp;<a href=\"" + vCardName + "\">" + i18n("[vCard]") + "</a>";
00171       headerParts << fromPart;
00172     }
00173 
00174     if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty() )
00175       headerParts << i18n("CC: ") + KMMessage::emailAddrAsAnchor( message->cc(), true );
00176 
00177     if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty() )
00178       headerParts << i18n("BCC: ") + KMMessage::emailAddrAsAnchor( message->bcc(), true );
00179 
00180     if ( strategy->showHeader( "date" ) )
00181       headerParts << strToHtml(message->dateShortStr());
00182 
00183     // remove all empty (modulo whitespace) entries and joins them via ", \n"
00184     headerStr += " (" + headerParts.grep( QRegExp( "\\S" ) ).join( ",\n" ) + ')';
00185 
00186     headerStr += "</div>\n";
00187 
00188     // ### iterate over the rest of strategy->headerToDisplay() (or
00189     // ### all headers if DefaultPolicy == Display) (elsewhere, too)
00190     return headerStr;
00191   }
00192 
00193   //
00194   // PlainHeaderStyle:
00195   //   show every header field on a line by itself,
00196   //   show subject larger
00197   //
00198 
00199   class PlainHeaderStyle : public HeaderStyle {
00200     friend class ::KMail::HeaderStyle;
00201   protected:
00202     PlainHeaderStyle() : HeaderStyle() {}
00203     virtual ~PlainHeaderStyle() {}
00204 
00205   public:
00206     const char * name() const { return "plain"; }
00207     const HeaderStyle * next() const { return fancy(); }
00208     const HeaderStyle * prev() const { return brief(); }
00209 
00210     QString format( const KMMessage * message, const HeaderStrategy * strategy,
00211                     const QString & vCardName, bool printing, bool topLevel ) const;
00212 
00213   private:
00214     QString formatAllMessageHeaders( const KMMessage * message ) const;
00215   };
00216 
00217   QString PlainHeaderStyle::format( const KMMessage * message,
00218                                     const HeaderStrategy * strategy,
00219                                     const QString & vCardName, bool printing, bool topLevel ) const {
00220     Q_UNUSED( topLevel );
00221     if ( !message ) return QString::null;
00222     if ( !strategy )
00223       strategy = HeaderStrategy::rich();
00224 
00225     // The direction of the header is determined according to the direction
00226     // of the application layout.
00227 
00228     QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
00229 
00230     // However, the direction of the message subject within the header is
00231     // determined according to the contents of the subject itself. Since
00232     // the "Re:" and "Fwd:" prefixes would always cause the subject to be
00233     // considered left-to-right, they are ignored when determining its
00234     // direction.
00235 
00236     QString subjectDir;
00237     if (!message->subject().isEmpty())
00238       subjectDir = directionOf( message->cleanSubject() );
00239     else
00240       subjectDir = directionOf( i18n("No Subject") );
00241 
00242     // Prepare the date string (when printing always use the localized date)
00243     QString dateString;
00244     if( printing ) {
00245       QDateTime dateTime;
00246       KLocale* locale = KGlobal::locale();
00247       dateTime.setTime_t( message->date() );
00248       dateString = locale->formatDateTime( dateTime );
00249     }
00250     else {
00251       dateString = message->dateStr();
00252     }
00253 
00254     QString headerStr;
00255 
00256     if ( strategy->headersToDisplay().isEmpty()
00257          && strategy->defaultPolicy() == HeaderStrategy::Display ) {
00258       // crude way to emulate "all" headers - Note: no strings have
00259       // i18n(), so direction should always be ltr.
00260       headerStr= QString("<div class=\"header\" dir=\"ltr\">");
00261       headerStr += formatAllMessageHeaders( message );
00262       return headerStr + "</div>";
00263     }
00264 
00265     headerStr = QString("<div class=\"header\" dir=\"%1\">").arg(dir);
00266 
00267     //case HdrLong:
00268     if ( strategy->showHeader( "subject" ) )
00269       headerStr += QString("<div dir=\"%1\"><b style=\"font-size:130%\">" +
00270                            strToHtml(message->subject()) + "</b></div>\n")
00271                         .arg(subjectDir);
00272 
00273     if ( strategy->showHeader( "date" ) )
00274       headerStr.append(i18n("Date: ") + strToHtml(dateString)+"<br>\n");
00275 
00276 #if 0
00277     // Get Instant Messaging presence
00278     QString presence;
00279     QString kabcUid;
00280     if ( strategy->showHeader( "status" ) )
00281     {
00282       KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );
00283       KABC::AddresseeList addresses = addressBook->findByEmail( KPIM::getFirstEmailAddress( message->from() ) );
00284       ::KIMProxy *imProxy = KMKernel::self()->imProxy();
00285       kabcUid = addresses[0].uid();
00286       presence = imProxy->presenceString( kabcUid );
00287     }
00288 #endif
00289 
00290     if ( strategy->showHeader( "from" ) ) {
00291       QString fromStr = message->from();
00292       if ( fromStr.isEmpty() ) // no valid email in from, maybe just a name
00293         fromStr = message->fromStrip(); // let's use that
00294       headerStr.append(i18n("From: ") +
00295           KMMessage::emailAddrAsAnchor( fromStr, false, "", true ) );
00296       if ( !vCardName.isEmpty() )
00297         headerStr.append("&nbsp;&nbsp;<a href=\"" + vCardName +
00298               "\">" + i18n("[vCard]") + "</a>" );
00299 #if 0
00300       if ( !presence.isEmpty() && strategy->showHeader( "status" ) )
00301         headerStr.append("&nbsp;&nbsp;(<span name=\"presence-" + kabcUid + "\">" + presence + "</span>)" );
00302 #endif
00303 
00304       if ( strategy->showHeader( "organization" )
00305           && !message->headerField("Organization").isEmpty())
00306         headerStr.append("&nbsp;&nbsp;(" +
00307               strToHtml(message->headerField("Organization")) + ")");
00308       headerStr.append("<br>\n");
00309     }
00310 
00311     if ( strategy->showHeader( "to" ) )
00312       headerStr.append(i18n("To: ")+
00313                        KMMessage::emailAddrAsAnchor(message->to(),false) + "<br>\n");
00314 
00315     if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty() )
00316       headerStr.append(i18n("CC: ")+
00317                        KMMessage::emailAddrAsAnchor(message->cc(),false) + "<br>\n");
00318 
00319     if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty() )
00320       headerStr.append(i18n("BCC: ")+
00321                        KMMessage::emailAddrAsAnchor(message->bcc(),false) + "<br>\n");
00322 
00323     if ( strategy->showHeader( "reply-to" ) && !message->replyTo().isEmpty())
00324       headerStr.append(i18n("Reply to: ")+
00325                      KMMessage::emailAddrAsAnchor(message->replyTo(),false) + "<br>\n");
00326 
00327     headerStr += "</div>\n";
00328 
00329     return headerStr;
00330   }
00331 
00332   QString PlainHeaderStyle::formatAllMessageHeaders( const KMMessage * message ) const {
00333     const DwHeaders & headers = message->headers();
00334     QString result;
00335 
00336     for ( const DwField * field = headers.FirstField() ; field ; field = field->Next() ) {
00337       result += ( field->FieldNameStr() + ": " ).c_str();
00338       result += strToHtml( field->FieldBodyStr().c_str() );
00339       result += "<br>\n";
00340     }
00341 
00342     return result;
00343   }
00344 
00345   //
00346   // FancyHeaderStyle:
00347   //   Like PlainHeaderStyle, but with slick frames and background colours.
00348   //
00349 
00350   class FancyHeaderStyle : public HeaderStyle {
00351     friend class ::KMail::HeaderStyle;
00352   protected:
00353     FancyHeaderStyle() : HeaderStyle() {}
00354     virtual ~FancyHeaderStyle() {}
00355 
00356   public:
00357     const char * name() const { return "fancy"; }
00358     const HeaderStyle * next() const { return enterprise(); }
00359     const HeaderStyle * prev() const { return plain(); }
00360 
00361     QString format( const KMMessage * message, const HeaderStrategy * strategy,
00362                     const QString & vCardName, bool printing, bool topLevel ) const;
00363     static QString imgToDataUrl( const QImage & image,
00364                                  const char *fmt = "PNG" );
00365 
00366   private:
00367     static QString drawSpamMeter( double percent, const QString & filterHeader );
00368 
00369   };
00370 
00371   QString FancyHeaderStyle::drawSpamMeter( double percent,
00372                                            const QString & filterHeader )
00373   {
00374     QImage meterBar( 20, 1, 8, 24 );
00375     const unsigned short gradient[20][3] = {
00376       {   0, 255,   0 },
00377       {  27, 254,   0 },
00378       {  54, 252,   0 },
00379       {  80, 250,   0 },
00380       { 107, 249,   0 },
00381       { 135, 247,   0 },
00382       { 161, 246,   0 },
00383       { 187, 244,   0 },
00384       { 214, 242,   0 },
00385       { 241, 241,   0 },
00386       { 255, 228,   0 },
00387       { 255, 202,   0 },
00388       { 255, 177,   0 },
00389       { 255, 151,   0 },
00390       { 255, 126,   0 },
00391       { 255, 101,   0 },
00392       { 255,  76,   0 },
00393       { 255,  51,   0 },
00394       { 255,  25,   0 },
00395       { 255,   0,   0 }
00396     };
00397     meterBar.setColor( 21, qRgb( 255, 255, 255 ) );
00398     meterBar.setColor( 22, qRgb( 170, 170, 170 ) );
00399     if ( percent < 0 ) // grey is for errors
00400       meterBar.fill( 22 );
00401     else {
00402       meterBar.fill( 21 );
00403       int max = QMIN( 20, static_cast<int>( percent ) / 5 );
00404       for ( int i = 0; i < max; ++i ) {
00405         meterBar.setColor( i+1, qRgb( gradient[i][0], gradient[i][1],
00406                                       gradient[i][2] ) );
00407         meterBar.setPixel( i, 0, i+1 );
00408       }
00409     }
00410     QString titleText = i18n("%1% probability of being spam.\n\nFull report:\n%2")
00411                      .arg( QString::number( percent ), filterHeader );
00412     return QString("<img src=\"%1\" width=\"%2\" height=\"%3\" style=\"border: 1px solid black;\" title=\"%4\"> &nbsp;")
00413       .arg( imgToDataUrl( meterBar, "PPM" ), QString::number( 20 ),
00414             QString::number( 5 ), titleText );
00415   }
00416 
00417 
00418   QString FancyHeaderStyle::format( const KMMessage * message,
00419                                     const HeaderStrategy * strategy,
00420                                     const QString & vCardName, bool printing, bool topLevel ) const {
00421     Q_UNUSED( topLevel );
00422     if ( !message ) return QString::null;
00423     if ( !strategy )
00424       strategy = HeaderStrategy::rich();
00425 
00426     KConfigGroup configReader( KMKernel::config(), "Reader" );
00427 
00428     // ### from kmreaderwin begin
00429     // The direction of the header is determined according to the direction
00430     // of the application layout.
00431 
00432     QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
00433     QString headerStr = QString("<div class=\"fancy header\" dir=\"%1\">\n").arg(dir);
00434 
00435     // However, the direction of the message subject within the header is
00436     // determined according to the contents of the subject itself. Since
00437     // the "Re:" and "Fwd:" prefixes would always cause the subject to be
00438     // considered left-to-right, they are ignored when determining its
00439     // direction.
00440 
00441     QString subjectDir;
00442     if ( !message->subject().isEmpty() )
00443       subjectDir = directionOf( message->cleanSubject() );
00444     else
00445       subjectDir = directionOf( i18n("No Subject") );
00446 
00447     // Prepare the date string (when printing always use the localized date)
00448     QString dateString;
00449     if( printing ) {
00450       QDateTime dateTime;
00451       KLocale* locale = KGlobal::locale();
00452       dateTime.setTime_t( message->date() );
00453       dateString = locale->formatDateTime( dateTime );
00454     }
00455     else {
00456       dateString = message->dateStr();
00457     }
00458 
00459     // Spam header display.
00460     // If the spamSpamStatus config value is true then we look for headers
00461     // from a few spam filters and try to create visually meaningful graphics
00462     // out of the spam scores.
00463 
00464     QString spamHTML;
00465 
00466     if ( configReader.readBoolEntry( "showSpamStatus", true ) ) {
00467       SpamScores scores = SpamHeaderAnalyzer::getSpamScores( message );
00468       for ( SpamScoresIterator it = scores.begin(); it != scores.end(); ++it )
00469         spamHTML += (*it).agent() + " " +
00470                     drawSpamMeter( (*it).score(), (*it).spamHeader() );
00471     }
00472 
00473     QString userHTML;
00474     QString presence;
00475 
00476     // IM presence and kabc photo
00477 
00478     ::KIMProxy *imProxy = KMKernel::self()->imProxy();
00479     QString kabcUid;
00480     KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );
00481     KABC::AddresseeList addresses = addressBook->findByEmail( KPIM::getFirstEmailAddress( message->from() ) );
00482 
00483     QString photoURL;
00484     int photoWidth = 60;
00485     int photoHeight = 60;
00486     if( addresses.count() == 1 )
00487     {
00488       // kabcUid is embedded in im: URIs to indicate which IM contact to message
00489       kabcUid = addresses[0].uid();
00490 
00491       if ( imProxy->initialize() ) {
00492           // im status
00493           presence = imProxy->presenceString( kabcUid );
00494           if ( !presence.isEmpty() )
00495           {
00496             QString presenceIcon = QString::fromLatin1( " <img src=\"%1\"/>" )
00497                 .arg( imgToDataUrl( imProxy->presenceIcon( kabcUid ).convertToImage() ) );
00498             presence += presenceIcon;
00499           }
00500       }
00501       // picture
00502       if ( addresses[0].photo().isIntern() )
00503       {
00504         // get photo data and convert to data: url
00505         //kdDebug( 5006 ) << "INTERNAL photo found" << endl;
00506         QImage photo = addresses[0].photo().data();
00507         if ( !photo.isNull() )
00508         {
00509           photoWidth = photo.width();
00510           photoHeight = photo.height();
00511           // scale below 60, otherwise it can get way too large
00512           if ( photoHeight > 60 ) {
00513             double ratio = ( double )photoHeight / ( double )photoWidth;
00514             photoHeight = 60;
00515             photoWidth = (int)( 60 / ratio );
00516             photo = photo.smoothScale( photoWidth, photoHeight );
00517           }
00518           photoURL = imgToDataUrl( photo );
00519         }
00520       }
00521       else
00522       {
00523         //kdDebug( 5006 ) << "URL found" << endl;
00524         photoURL = addresses[0].photo().url();
00525         if ( photoURL.startsWith("/") )
00526           photoURL.prepend( "file:" );
00527       }
00528     }
00529     else // TODO: find a usable one
00530     {
00531       kdDebug( 5006 ) << "Multiple / No addressees matched email address; Count is " << addresses.count() << endl;
00532       userHTML = "&nbsp;";
00533     }
00534 
00535     if( photoURL.isEmpty() ) {
00536       // no photo, look for a Face header
00537       QString faceheader = message->headerField( "Face" );
00538       if ( !faceheader.isEmpty() ) {
00539         QImage faceimage;
00540 
00541         kdDebug( 5006 ) << "Found Face: header" << endl;
00542 
00543         QCString facestring = faceheader.utf8();
00544         // Spec says header should be less than 998 bytes
00545         // Face: is 5 characters
00546         if ( facestring.length() < 993 ) {
00547           QByteArray facearray;
00548           KCodecs::base64Decode(facestring, facearray);
00549 
00550           QImage faceimage;
00551           if ( faceimage.loadFromData( facearray, "png" ) ) {
00552             // Spec says image must be 48x48 pixels
00553             if ( (48 == faceimage.width()) && (48 == faceimage.height()) ) {
00554               photoURL = imgToDataUrl( faceimage );
00555               photoWidth = 48;
00556               photoHeight = 48;
00557             } else {
00558               kdDebug( 5006 ) << "Face: header image is" << faceimage.width() << "by" << faceimage.height() <<"not 48x48 Pixels" << endl;
00559             }
00560           } else {
00561             kdDebug( 5006 ) << "Failed to load decoded png from Face: header" << endl;
00562           }
00563         } else {
00564           kdDebug( 5006 ) << "Face: header too long at " << facestring.length() << endl;
00565         }
00566       }
00567     }
00568 
00569     if( photoURL.isEmpty() )
00570     {
00571       // no photo, look for a X-Face header
00572       QString xfaceURL;
00573       QString xfhead = message->headerField( "X-Face" );
00574       if ( !xfhead.isEmpty() )
00575       {
00576         KXFace xf;
00577         photoURL = imgToDataUrl( xf.toImage( xfhead ) );
00578         photoWidth = 48;
00579         photoHeight = 48;
00580 
00581       }
00582     }
00583 
00584     if( !photoURL.isEmpty() )
00585     {
00586         //kdDebug( 5006 ) << "Got a photo: " << photoURL << endl;
00587       userHTML = QString("<img src=\"%1\" width=\"%2\" height=\"%3\">")
00588           .arg( photoURL ).arg( photoWidth ).arg( photoHeight );
00589       if ( presence.isEmpty() ) {
00590         userHTML = QString("<div class=\"senderpic\">") + userHTML + "</div>";
00591       } else {
00592         userHTML = QString( "<div class=\"senderpic\">"
00593             "<a href=\"im:%1\">%2<div class=\"senderstatus\">"
00594             "<span name=\"presence-%3\">%4</span></div></a>"
00595             "</div>" ).arg( kabcUid )
00596             .arg( userHTML )
00597              .arg( kabcUid )
00598             .arg( presence );
00599       }
00600     } else {
00601        // we don't have a photo, just show presence, if we have it
00602       if ( !presence.isEmpty() )
00603         userHTML = QString( "<a href=\"im:%1\"><div class=\"senderstatus\">"
00604             "<span name=\"presence-%2\">%3</span></div></a>" )
00605             .arg( kabcUid )
00606             .arg( kabcUid )
00607             .arg( presence );
00608     }
00609 #if 0
00610     // Disabled 'Launch IM' link in headers - Will
00611     if ( imProxy->imAppsAvailable() )
00612       presence = "<a name=\"launchim\" href=\"kmail:startIMApp\">" + i18n("Launch IM") + "</a></span>";
00613     // do nothing - no im apps available, leave presence empty
00614     //presence = i18n( "DCOP/InstantMessenger not installed" );
00615     kdDebug( 5006 ) << "final presence: '" << presence << "'" << endl;
00616 #endif
00617 
00618 
00619     //case HdrFancy:
00620     // the subject line and box below for details
00621     if ( strategy->showHeader( "subject" ) ) {
00622       const int flags = LinkLocator::PreserveSpaces |
00623                         ( GlobalSettings::self()->showEmoticons() ?
00624                           LinkLocator::ReplaceSmileys : 0 );
00625       headerStr += QString("<div dir=\"%1\">%2</div>\n")
00626                         .arg(subjectDir)
00627                         .arg(message->subject().isEmpty()?
00628                              i18n("No Subject") :
00629                              strToHtml( message->subject(), flags ));
00630     }
00631     headerStr += "<table class=\"outer\"><tr><td width=\"100%\"><table>\n";
00632     //headerStr += "<table>\n";
00633     // from line
00634     // the mailto: URLs can contain %3 etc., therefore usage of multiple
00635     // QString::arg is not possible
00636     if ( strategy->showHeader( "from" ) ) {
00637       QString fromStr = message->from();
00638       if ( fromStr.isEmpty() ) // no valid email in from, maybe just a name
00639         fromStr = message->fromStrip(); // let's use that
00640       headerStr += QString("<tr><th>%1</th>\n"
00641                            "<td>")
00642                            .arg(i18n("From: "))
00643                  + KMMessage::emailAddrAsAnchor( fromStr, false )
00644                  + ( !message->headerField( "Resent-From" ).isEmpty() ? "&nbsp;"
00645                                 + i18n("(resent from %1)")
00646                                   .arg( KMMessage::emailAddrAsAnchor(
00647                                     message->headerField( "Resent-From" ),false) )
00648                               : QString("") )
00649                  + ( !vCardName.isEmpty() ? "&nbsp;&nbsp;<a href=\"" + vCardName + "\">"
00650                                 + i18n("[vCard]") + "</a>"
00651                               : QString("") )
00652 #if 0
00653                  + ( ( !presence.isEmpty() )
00654                               ? "&nbsp;&nbsp;(<span name=\"presence-" + kabcUid + "\">" + presence + "</span>)"
00655                               : QString("") )
00656 #endif
00657                  + ( message->headerField("Organization").isEmpty()
00658                               ? QString("")
00659                               : "&nbsp;&nbsp;("
00660                                 + strToHtml(message->headerField("Organization"))
00661                                 + ")")
00662                  + "</td></tr>\n";
00663     }
00664     // to line
00665     if ( strategy->showHeader( "to" ) )
00666       headerStr.append(QString("<tr><th>%1</th>\n"
00667                                "<td>%2</td></tr>\n")
00668                        .arg(i18n("To: "))
00669                        .arg(KMMessage::emailAddrAsAnchor(message->to(),false)));
00670 
00671     // cc line, if any
00672     if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty())
00673       headerStr.append(QString("<tr><th>%1</th>\n"
00674                                "<td>%2</td></tr>\n")
00675                        .arg(i18n("CC: "))
00676                        .arg(KMMessage::emailAddrAsAnchor(message->cc(),false)));
00677 
00678     // Bcc line, if any
00679     if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty())
00680       headerStr.append(QString("<tr><th>%1</th>\n"
00681                                "<td>%2</td></tr>\n")
00682                        .arg(i18n("BCC: "))
00683                        .arg(KMMessage::emailAddrAsAnchor(message->bcc(),false)));
00684 
00685     if ( strategy->showHeader( "date" ) )
00686       headerStr.append(QString("<tr><th>%1</th>\n"
00687                                "<td dir=\"%2\">%3</td></tr>\n")
00688                        .arg(i18n("Date: "))
00689                        .arg( directionOf( message->dateStr() ) )
00690                        .arg(strToHtml(dateString)));
00691 
00692     if ( GlobalSettings::self()->showUserAgent() ) {
00693       if ( strategy->showHeader( "user-agent" ) ) {
00694         if ( !message->headerField("User-Agent").isEmpty() ) {
00695           headerStr.append(QString("<tr><th>%1</th>\n"
00696                                    "<td>%2</td></tr>\n")
00697                            .arg(i18n("User-Agent: "))
00698                            .arg( strToHtml( message->headerField("User-Agent") ) ) );
00699         }
00700       }
00701 
00702       if ( strategy->showHeader( "x-mailer" ) ) {
00703         if ( !message->headerField("X-Mailer").isEmpty() ) {
00704           headerStr.append(QString("<tr><th>%1</th>\n"
00705                                    "<td>%2</td></tr>\n")
00706                            .arg(i18n("X-Mailer: "))
00707                            .arg( strToHtml( message->headerField("X-Mailer") ) ) );
00708         }
00709       }
00710     }
00711     if (configReader.readBoolEntry( "showSpamStatus", true )  ) {
00712       if (strategy->showHeader( "x-text-classification" ) && strategy->showHeader( "x-popfile-link" ) )
00713       {
00714         if ( ! message->headerField( "X-Text-Classification" ).isEmpty() && ! message->headerField( "X-POPFile-Link" ).isEmpty() )
00715     {
00716           headerStr.append(QString("<tr><th>%1</th>\n"
00717                                    "<td>%2&nbsp;&nbsp;(<a href=\"%3\">%4</a>)</td></tr>\n")
00718                            .arg("Popfile:")
00719                            .arg( strToHtml( message->headerField("X-Text-Classification") ) )
00720                                 .arg( message->headerField("X-POPFile-Link") )
00721                         .arg(i18n("Reclassify") ) );
00722     }
00723       }
00724     }
00725 
00726     // FIXME: Show status in synthetic header style field.  Decide whether this or current in brackets style is best and remove one.
00727     /*    if( strategy->showHeader( "status" ) )
00728       headerStr.append( QString( "<tr><th>%1</th>\n"
00729                                  "<td dir=\"%2\">%3</td></tr>\n")
00730                                     .arg(i18n("Sender status: "))
00731                                     .arg( directionOf( onlineStatus ) )
00732                                     .arg(onlineStatus));
00733     */
00734     headerStr.append( QString("<tr><td colspan=\"2\"><div id=\"attachmentInjectionPoint\"></div></td></tr>" ) );
00735     headerStr.append(
00736           QString( "</table></td><td align=\"center\">%1</td></tr></table>\n" ).arg(userHTML) );
00737 
00738     if ( !spamHTML.isEmpty() )
00739       headerStr.append( QString( "<div class=\"spamheader\" dir=\"%1\"><b>%2</b>&nbsp;<span style=\"padding-left: 20px;\">%3</span></div>\n")
00740                         .arg( subjectDir, i18n("Spam Status:"), spamHTML ) );
00741 
00742     headerStr += "</div>\n\n";
00743     return headerStr;
00744   }
00745 
00746   QString FancyHeaderStyle::imgToDataUrl( const QImage &image, const char* fmt  )
00747   {
00748     QByteArray ba;
00749     QBuffer buffer( ba );
00750     buffer.open( IO_WriteOnly );
00751     image.save( &buffer, fmt );
00752     return QString::fromLatin1("data:image/%1;base64,%2")
00753            .arg( fmt, KCodecs::base64Encode( ba ) );
00754   }
00755 
00756 // #####################
00757 
00758   class EnterpriseHeaderStyle : public HeaderStyle {
00759     friend class ::KMail::HeaderStyle;
00760   protected:
00761     EnterpriseHeaderStyle() : HeaderStyle() {}
00762     virtual ~EnterpriseHeaderStyle() {}
00763 
00764   public:
00765     const char * name() const { return "enterprise"; }
00766     const HeaderStyle * next() const { return brief(); }
00767     const HeaderStyle * prev() const { return fancy(); }
00768 
00769     QString format( const KMMessage * message, const HeaderStrategy * strategy,
00770                     const QString & vCardName, bool printing, bool topLevel ) const;
00771   };
00772 
00773     QString EnterpriseHeaderStyle::format( const KMMessage * message,
00774                                     const HeaderStrategy * strategy,
00775                                     const QString & vCardName, bool printing, bool topLevel ) const {
00776     if ( !message ) return QString::null;
00777     if ( !strategy )
00778         strategy = HeaderStrategy::brief();
00779 
00780     // The direction of the header is determined according to the direction
00781     // of the application layout.
00782 
00783     QString dir = QApplication::reverseLayout() ? "rtl" : "ltr" ;
00784 
00785     // However, the direction of the message subject within the header is
00786     // determined according to the contents of the subject itself. Since
00787     // the "Re:" and "Fwd:" prefixes would always cause the subject to be
00788     // considered left-to-right, they are ignored when determining its
00789     // direction.
00790 
00791     QString subjectDir;
00792     if (!message->subject().isEmpty())
00793         subjectDir = directionOf( message->cleanSubject() );
00794     else
00795         subjectDir = directionOf( i18n("No Subject") );
00796 
00797     // colors depend on if its encapsulated or not
00798         QColor fontColor(Qt::white);
00799     QString linkColor = "class =\"white\"";
00800     const QColor activeColor = qApp->palette().active().highlight();
00801     QColor activeColorDark = activeColor.dark(130);
00802         // reverse colors for encapsulated
00803         if( !topLevel ){
00804             activeColorDark = activeColor.dark(50);
00805             fontColor = QColor(Qt::black);
00806         linkColor = "class =\"black\"";
00807         }
00808 
00809     QStringList headerParts;
00810     if( strategy->showHeader( "to" ) )
00811         headerParts << KMMessage::emailAddrAsAnchor( message->to(), false, linkColor );
00812 
00813     if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty() )
00814         headerParts << KMMessage::emailAddrAsAnchor( message->cc(), true, linkColor );
00815 
00816     if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty() )
00817         headerParts << KMMessage::emailAddrAsAnchor( message->bcc(), true, linkColor );
00818 
00819     // remove all empty (modulo whitespace) entries and joins them via ", \n"
00820     QString headerPart = " " + headerParts.grep( QRegExp( "\\S" ) ).join( ", " );
00821 
00822     // Prepare the date string (when printing always use the localized date)
00823     QString dateString;
00824     if( printing ) {
00825         QDateTime dateTime;
00826         KLocale * locale = KGlobal::locale();
00827         dateTime.setTime_t( message->date() );
00828         dateString = locale->formatDateTime( dateTime );
00829     } else {
00830         dateString = message->dateStr();
00831     }
00832 
00833     QString imgpath(locate("data","kmail/pics/"));
00834     imgpath.append("enterprise_");
00835     const QString borderSettings(" padding-top: 0px; padding-bottom: 0px; border-width: 0px ");
00836     QString headerStr ("");
00837 
00838     // 3D borders
00839     if(topLevel)
00840         headerStr +=
00841         "<div style=\"position: fixed; top: 0px; left: 0px; background-color: #606060; "
00842         "background-image: url("+imgpath+"shadow_left.png); width: 10px; min-height: 100%;\">&nbsp;</div>"
00843         "<div style=\"position: fixed; top: 0px; right: 0px;  background-color: #606060; "
00844         "background-image: url("+imgpath+"shadow_right.png); width: 10px; min-height: 100%;\">&nbsp;</div>";
00845 
00846     headerStr += ""
00847         "<div style=\"margin-left: 10px; top: 0px;\"><span style=\"font-size: 10px; font-weight: bold;\">"+dateString+"</span></div>"
00848         // #0057ae
00849         "<table style=\"background: "+activeColorDark.name()+"; border-collapse:collapse; top: 14px; min-width: 200px; \" cellpadding=0> \n"
00850         "  <tr> \n"
00851         "   <td style=\"min-width: 6px; background-image: url("+imgpath+"top_left.png); \"></td> \n"
00852         "   <td style=\"height: 6px; width: 100%; background: url("+imgpath+"top.png); \"></td> \n"
00853         "   <td style=\"min-width: 6px; background: url("+imgpath+"top_right.png); \"></td> </tr> \n"
00854         "   <tr> \n"
00855         "   <td style=\"min-width: 6px; max-width: 6px; background: url("+imgpath+"left.png); \"></td> \n"
00856         "   <td style=\"\"> \n"
00857         "    <table style=\"color: "+fontColor.name()+" ! important; margin: 1px; border-spacing: 0px;\" cellpadding=0> \n";
00858 
00859     // subject
00860     //strToHtml( message->subject() )
00861     if ( strategy->showHeader( "subject" ) ){
00862         headerStr +=
00863         "     <tr> \n"
00864         "      <td style=\"font-size: 6px; text-align: right; padding-left: 5px; padding-right: 24px; "+borderSettings+"\"></td> \n"
00865         "      <td style=\"font-weight: bolder; font-size: 120%; padding-right: 91px; "+borderSettings+"\">"+message->subject()+"</td> \n"
00866         "     </tr> \n";
00867     }
00868 
00869     // from
00870     if ( strategy->showHeader( "from" ) ){
00871         QString fromStr = message->from();
00872         if ( fromStr.isEmpty() ) // no valid email in from, maybe just a name
00873         fromStr = message->fromStrip(); // let's use that
00874             // TODO vcard
00875         QString fromPart = KMMessage::emailAddrAsAnchor( fromStr, true, linkColor );
00876         if ( !vCardName.isEmpty() )
00877         fromPart += "&nbsp;&nbsp;<a href=\"" + vCardName + "\" "+linkColor+">" + i18n("[vCard]") + "</a>";
00878         //TDDO strategy date
00879         //if ( strategy->showHeader( "date" ) )
00880         headerStr +=
00881         "     <tr> \n"
00882         "      <td style=\"font-size: 6px; padding-left: 5px; padding-right: 24px; text-align: right; "+borderSettings+"\">"+i18n("From: ")+"</td> \n"
00883         "      <td style=\""+borderSettings+"\">"+ fromPart +"</td> "
00884         "     </tr> ";
00885     }
00886 
00887     // to, cc, bcc
00888     headerStr +=
00889         "     <tr> "
00890         "      <td style=\"font-size: 6px; text-align: right; padding-left: 5px; padding-right: 24px; "+borderSettings+"\">"+i18n("To: ")+"</td> "
00891         "      <td style=\""+borderSettings+"\">"
00892         +headerPart+
00893         "      </td> "
00894         "     </tr> ";
00895 
00896     // header-bottom
00897     headerStr +=
00898         "    </table> \n"
00899         "   </td> \n"
00900         "   <td style=\"min-width: 6px; max-height: 15px; background: url("+imgpath+"right.png); \"></td> \n"
00901         "  </tr> \n"
00902         "  <tr> \n"
00903         "   <td style=\"min-width: 6px; background: url("+imgpath+"s_left.png); \"></td> \n"
00904         "   <td style=\"height: 35px; width: 80%; background: url("+imgpath+"sbar.png);\"> \n"
00905         "    <img src=\""+imgpath+"sw.png\" style=\"margin: 0px; height: 30px; overflow:hidden; \"> \n"
00906         "    <img src=\""+imgpath+"sp_right.png\" style=\"float: right; \"> </td> \n"
00907         "   <td style=\"min-width: 6px; background: url("+imgpath+"s_right.png); \"></td> \n"
00908         "  </tr> \n"
00909         " </table> \n";
00910 
00911     // kmail icon
00912     if(topLevel) {
00913         headerStr +=
00914         "<div class=\"noprint\" style=\"position: absolute; top: -14px; right: 30px; width: 91px; height: 91px;\">\n"
00915         "<img style=\"float: right;\" src=\""+imgpath+"icon.png\">\n"
00916         "</div>\n";
00917 
00918         // attachments
00919         headerStr +=
00920         "<div class=\"noprint\" style=\"position: absolute; top: 60px; right: 20px; width: 91px; height: 200px;\">"
00921         "<div id=\"attachmentInjectionPoint\"></div>"
00922         "</div>\n";
00923     }
00924 
00925     if ( printing ) {
00926       //provide a bit more left padding when printing
00927       //kolab/issue3254 (printed mail cut at the left side)
00928       headerStr += "<div style=\"padding: 6px; padding-left: 10px;\">";
00929     } else {
00930       headerStr += "<div style=\"padding: 6px;\">";
00931     }
00932 
00933     // TODO
00934     // spam status
00935     // ### iterate over the rest of strategy->headerToDisplay() (or
00936     // ### all headers if DefaultPolicy == Display) (elsewhere, too)
00937     return headerStr;
00938   }
00939 
00940 // #####################
00941 
00942   //
00943   // HeaderStyle abstract base:
00944   //
00945 
00946   HeaderStyle::HeaderStyle() {
00947 
00948   }
00949 
00950   HeaderStyle::~HeaderStyle() {
00951 
00952   }
00953 
00954   const HeaderStyle * HeaderStyle::create( Type type ) {
00955     switch ( type ) {
00956     case Brief:  return brief();
00957     case Plain:  return plain();
00958     case Fancy:   return fancy();
00959     case Enterprise: return enterprise();
00960     }
00961     kdFatal( 5006 ) << "HeaderStyle::create(): Unknown header style ( type == "
00962                     << (int)type << " ) requested!" << endl;
00963     return 0; // make compiler happy
00964   }
00965 
00966   const HeaderStyle * HeaderStyle::create( const QString & type ) {
00967     QString lowerType = type.lower();
00968     if ( lowerType == "brief" ) return brief();
00969     if ( lowerType == "plain" )  return plain();
00970     if ( lowerType == "enterprise" )  return enterprise();
00971     //if ( lowerType == "fancy" ) return fancy(); // not needed, see below
00972     // don't kdFatal here, b/c the strings are user-provided
00973     // (KConfig), so fail gracefully to the default:
00974     return fancy();
00975   }
00976 
00977   static const HeaderStyle * briefStyle = 0;
00978   static const HeaderStyle * plainStyle = 0;
00979   static const HeaderStyle * fancyStyle = 0;
00980   static const HeaderStyle * enterpriseStyle = 0;
00981 
00982   const HeaderStyle * HeaderStyle::brief() {
00983     if ( !briefStyle )
00984       briefStyle = new BriefHeaderStyle();
00985     return briefStyle;
00986   }
00987 
00988   const HeaderStyle * HeaderStyle::plain() {
00989     if ( !plainStyle )
00990       plainStyle = new PlainHeaderStyle();
00991     return plainStyle;
00992   }
00993 
00994   const HeaderStyle * HeaderStyle::fancy() {
00995     if ( !fancyStyle )
00996       fancyStyle = new FancyHeaderStyle();
00997     return fancyStyle;
00998   }
00999 
01000   const HeaderStyle * HeaderStyle::enterprise() {
01001     if ( !enterpriseStyle )
01002       enterpriseStyle = new EnterpriseHeaderStyle();
01003     return enterpriseStyle;
01004   }
01005 
01006 } // namespace KMail
KDE Home | KDE Accessibility Home | Description of Access Keys