kmail

headerstyle.cpp

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