kmail

headerstyle.cpp
1 /*
2  headerstyle.cpp
3 
4  This file is part of KMail, the KDE mail client.
5  Copyright (c) 2003 Marc Mutz <mutz@kde.org>
6 
7  KMail is free software; you can redistribute it and/or modify it
8  under the terms of the GNU General Public License, version 2, as
9  published by the Free Software Foundation.
10 
11  KMail is distributed in the hope that it will be useful, but
12  WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program; if not, write to the Free Software
18  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 
20  In addition, as a special exception, the copyright holders give
21  permission to link the code of this program with any edition of
22  the TQt library by Trolltech AS, Norway (or with modified versions
23  of TQt that use the same license as TQt), and distribute linked
24  combinations including the two. You must obey the GNU General
25  Public License in all respects for all of the code used other than
26  TQt. If you modify this file, you may extend this exception to
27  your version of the file, but you are not obligated to do so. If
28  you do not wish to do so, delete this exception statement from
29  your version.
30 */
31 
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35 
36 #include "headerstyle.h"
37 
38 #include "headerstrategy.h"
39 #include "kmkernel.h"
40 #include "linklocator.h"
41 #include "kmmessage.h"
42 #include "spamheaderanalyzer.h"
43 #include "globalsettings.h"
44 
45 #include <libemailfunctions/email.h>
46 #include <libtdepim/kxface.h>
47 using namespace KPIM;
48 
49 #include <mimelib/string.h>
50 #include <mimelib/field.h>
51 #include <mimelib/headers.h>
52 
53 #include <kdebug.h>
54 #include <tdelocale.h>
55 #include <tdeglobal.h>
56 #include <tdeimproxy.h>
57 #include <tdeabc/stdaddressbook.h>
58 #include <tdeabc/addresseelist.h>
59 #include <kmdcodec.h>
60 #include <tqdatetime.h>
61 #include <tqbuffer.h>
62 #include <tqbitmap.h>
63 #include <tqimage.h>
64 #include <tqapplication.h>
65 #include <tqregexp.h>
66 
67 #include <kstandarddirs.h>
68 
69 namespace KMail {
70 
71  //
72  // Convenience functions:
73  //
74  static inline TQString directionOf( const TQString & str ) {
75  return str.isRightToLeft() ? "rtl" : "ltr" ;
76  }
77 
78 #if 0
79  // Converts to html. Changes URLs into href's, escapes HTML special
80  // chars and inserts the result into an <div> or <span> tag with
81  // "dir" set to "rtl" or "ltr" depending on the direction of @p str.
82  static TQString convertToHtmlBlock( const TQString & str, bool useSpan=false ) {
83  TQString dir = directionOf( str );
84  TQString format = "<%1 dir=\"%3\">%4</%2>";
85  return format.arg( useSpan ? "span" : "div" )
86  .arg( useSpan ? "span" : "div" )
87  .arg( dir )
88  .arg( LinkLocator::convertToHtml( str ) );
89  }
90 #endif
91 
92  // ### tmp wrapper to make kmreaderwin code working:
93  static TQString strToHtml( const TQString & str,
94  int flags = LinkLocator::PreserveSpaces ) {
95  return LinkLocator::convertToHtml( str, flags );
96  }
97 
98  //
99  // BriefHeaderStyle
100  // Show everything in a single line, don't show header field names.
101  //
102 
103  class BriefHeaderStyle : public HeaderStyle {
104  friend class ::KMail::HeaderStyle;
105  protected:
106  BriefHeaderStyle() : HeaderStyle() {}
107  virtual ~BriefHeaderStyle() {}
108 
109  public:
110  const char * name() const { return "brief"; }
111  const HeaderStyle * next() const { return plain(); }
112  const HeaderStyle * prev() const { return fancy(); }
113 
114  TQString format( const KMMessage * message, const HeaderStrategy * strategy,
115  const TQString & vCardName, bool printing, bool topLevel ) const;
116  };
117 
118  TQString BriefHeaderStyle::format( const KMMessage * message,
119  const HeaderStrategy * strategy,
120  const TQString & vCardName, bool printing, bool topLevel ) const {
121  Q_UNUSED( topLevel );
122  if ( !message ) return TQString();
123  if ( !strategy )
124  strategy = HeaderStrategy::brief();
125 
126  // The direction of the header is determined according to the direction
127  // of the application layout.
128 
129  TQString dir = TQApplication::reverseLayout() ? "rtl" : "ltr" ;
130 
131  // However, the direction of the message subject within the header is
132  // determined according to the contents of the subject itself. Since
133  // the "Re:" and "Fwd:" prefixes would always cause the subject to be
134  // considered left-to-right, they are ignored when determining its
135  // direction.
136 
137  TQString subjectDir;
138  if (!message->subject().isEmpty())
139  subjectDir = directionOf( message->cleanSubject() );
140  else
141  subjectDir = directionOf( i18n("No Subject") );
142 
143  // Prepare the date string (when printing always use the localized date)
144  TQString dateString;
145  if( printing ) {
146  TQDateTime dateTime;
147  TDELocale * locale = TDEGlobal::locale();
148  dateTime.setTime_t( message->date() );
149  dateString = locale->formatDateTime( dateTime );
150  } else {
151  dateString = message->dateStr();
152  }
153 
154  TQString headerStr = "<div class=\"header\" dir=\"" + dir + "\">\n";
155 
156  if ( strategy->showHeader( "subject" ) )
157  headerStr += "<div dir=\"" + subjectDir + "\">\n"
158  "<b style=\"font-size:130%\">" +
159  strToHtml( message->subject() ) +
160  "</b></div>\n";
161 
162  TQStringList headerParts;
163 
164  if ( strategy->showHeader( "from" ) ) {
165  TQString fromStr = message->from();
166  if ( fromStr.isEmpty() ) // no valid email in from, maybe just a name
167  fromStr = message->fromStrip(); // let's use that
168  TQString fromPart = KMMessage::emailAddrAsAnchor( fromStr, true );
169  if ( !vCardName.isEmpty() )
170  fromPart += "&nbsp;&nbsp;<a href=\"" + vCardName + "\">" + i18n("[vCard]") + "</a>";
171  headerParts << fromPart;
172  }
173 
174  if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty() )
175  headerParts << i18n("CC: ") + KMMessage::emailAddrAsAnchor( message->cc(), true );
176 
177  if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty() )
178  headerParts << i18n("BCC: ") + KMMessage::emailAddrAsAnchor( message->bcc(), true );
179 
180  if ( strategy->showHeader( "date" ) )
181  headerParts << strToHtml(message->dateShortStr());
182 
183  // remove all empty (modulo whitespace) entries and joins them via ", \n"
184  headerStr += " (" + headerParts.grep( TQRegExp( "\\S" ) ).join( ",\n" ) + ')';
185 
186  headerStr += "</div>\n";
187 
188  // ### iterate over the rest of strategy->headerToDisplay() (or
189  // ### all headers if DefaultPolicy == Display) (elsewhere, too)
190  return headerStr;
191  }
192 
193  //
194  // PlainHeaderStyle:
195  // show every header field on a line by itself,
196  // show subject larger
197  //
198 
199  class PlainHeaderStyle : public HeaderStyle {
200  friend class ::KMail::HeaderStyle;
201  protected:
202  PlainHeaderStyle() : HeaderStyle() {}
203  virtual ~PlainHeaderStyle() {}
204 
205  public:
206  const char * name() const { return "plain"; }
207  const HeaderStyle * next() const { return fancy(); }
208  const HeaderStyle * prev() const { return brief(); }
209 
210  TQString format( const KMMessage * message, const HeaderStrategy * strategy,
211  const TQString & vCardName, bool printing, bool topLevel ) const;
212 
213  private:
214  TQString formatAllMessageHeaders( const KMMessage * message ) const;
215  };
216 
217  TQString PlainHeaderStyle::format( const KMMessage * message,
218  const HeaderStrategy * strategy,
219  const TQString & vCardName, bool printing, bool topLevel ) const {
220  Q_UNUSED( topLevel );
221  if ( !message ) return TQString();
222  if ( !strategy )
223  strategy = HeaderStrategy::rich();
224 
225  // The direction of the header is determined according to the direction
226  // of the application layout.
227 
228  TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" );
229 
230  // However, the direction of the message subject within the header is
231  // determined according to the contents of the subject itself. Since
232  // the "Re:" and "Fwd:" prefixes would always cause the subject to be
233  // considered left-to-right, they are ignored when determining its
234  // direction.
235 
236  TQString subjectDir;
237  if (!message->subject().isEmpty())
238  subjectDir = directionOf( message->cleanSubject() );
239  else
240  subjectDir = directionOf( i18n("No Subject") );
241 
242  // Prepare the date string (when printing always use the localized date)
243  TQString dateString;
244  if( printing ) {
245  TQDateTime dateTime;
246  TDELocale* locale = TDEGlobal::locale();
247  dateTime.setTime_t( message->date() );
248  dateString = locale->formatDateTime( dateTime );
249  }
250  else {
251  dateString = message->dateStr();
252  }
253 
254  TQString headerStr;
255 
256  if ( strategy->headersToDisplay().isEmpty()
257  && strategy->defaultPolicy() == HeaderStrategy::Display ) {
258  // crude way to emulate "all" headers - Note: no strings have
259  // i18n(), so direction should always be ltr.
260  headerStr= TQString("<div class=\"header\" dir=\"ltr\">");
261  headerStr += formatAllMessageHeaders( message );
262  return headerStr + "</div>";
263  }
264 
265  headerStr = TQString("<div class=\"header\" dir=\"%1\">").arg(dir);
266 
267  //case HdrLong:
268  if ( strategy->showHeader( "subject" ) )
269  headerStr += TQString("<div dir=\"%1\"><b style=\"font-size:130%\">" +
270  strToHtml(message->subject()) + "</b></div>\n")
271  .arg(subjectDir);
272 
273  if ( strategy->showHeader( "date" ) )
274  headerStr.append(i18n("Date: ") + strToHtml(dateString)+"<br>\n");
275 
276 #if 0
277  // Get Instant Messaging presence
278  TQString presence;
279  TQString tdeabcUid;
280  if ( strategy->showHeader( "status" ) )
281  {
282  TDEABC::AddressBook *addressBook = TDEABC::StdAddressBook::self( true );
283  TDEABC::AddresseeList addresses = addressBook->findByEmail( KPIM::getFirstEmailAddress( message->from() ) );
284  ::KIMProxy *imProxy = KMKernel::self()->imProxy();
285  tdeabcUid = addresses[0].uid();
286  presence = imProxy->presenceString( tdeabcUid );
287  }
288 #endif
289 
290  if ( strategy->showHeader( "from" ) ) {
291  TQString fromStr = message->from();
292  if ( fromStr.isEmpty() ) // no valid email in from, maybe just a name
293  fromStr = message->fromStrip(); // let's use that
294  headerStr.append(i18n("From: ") +
295  KMMessage::emailAddrAsAnchor( fromStr, false, "", true ) );
296  if ( !vCardName.isEmpty() )
297  headerStr.append("&nbsp;&nbsp;<a href=\"" + vCardName +
298  "\">" + i18n("[vCard]") + "</a>" );
299 #if 0
300  if ( !presence.isEmpty() && strategy->showHeader( "status" ) )
301  headerStr.append("&nbsp;&nbsp;(<span name=\"presence-" + tdeabcUid + "\">" + presence + "</span>)" );
302 #endif
303 
304  if ( strategy->showHeader( "organization" )
305  && !message->headerField("Organization").isEmpty())
306  headerStr.append("&nbsp;&nbsp;(" +
307  strToHtml(message->headerField("Organization")) + ")");
308  headerStr.append("<br>\n");
309  }
310 
311  if ( strategy->showHeader( "to" ) )
312  headerStr.append(i18n("To: ")+
313  KMMessage::emailAddrAsAnchor(message->to(),false) + "<br>\n");
314 
315  if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty() )
316  headerStr.append(i18n("CC: ")+
317  KMMessage::emailAddrAsAnchor(message->cc(),false) + "<br>\n");
318 
319  if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty() )
320  headerStr.append(i18n("BCC: ")+
321  KMMessage::emailAddrAsAnchor(message->bcc(),false) + "<br>\n");
322 
323  if ( strategy->showHeader( "reply-to" ) && !message->replyTo().isEmpty())
324  headerStr.append(i18n("Reply to: ")+
325  KMMessage::emailAddrAsAnchor(message->replyTo(),false) + "<br>\n");
326 
327  headerStr += "</div>\n";
328 
329  return headerStr;
330  }
331 
332  TQString PlainHeaderStyle::formatAllMessageHeaders( const KMMessage * message ) const {
333  const DwHeaders & headers = message->headers();
334  TQString result;
335 
336  for ( const DwField * field = headers.FirstField() ; field ; field = field->Next() ) {
337  result += ( field->FieldNameStr() + ": " ).c_str();
338  result += strToHtml( field->FieldBodyStr().c_str() );
339  result += "<br>\n";
340  }
341 
342  return result;
343  }
344 
345  //
346  // FancyHeaderStyle:
347  // Like PlainHeaderStyle, but with slick frames and background colours.
348  //
349 
350  class FancyHeaderStyle : public HeaderStyle {
351  friend class ::KMail::HeaderStyle;
352  protected:
353  FancyHeaderStyle() : HeaderStyle() {}
354  virtual ~FancyHeaderStyle() {}
355 
356  public:
357  const char * name() const { return "fancy"; }
358  const HeaderStyle * next() const { return enterprise(); }
359  const HeaderStyle * prev() const { return plain(); }
360 
361  TQString format( const KMMessage * message, const HeaderStrategy * strategy,
362  const TQString & vCardName, bool printing, bool topLevel ) const;
363  static TQString imgToDataUrl( const TQImage & image,
364  const char *fmt = "PNG" );
365 
366  private:
367  static TQString drawSpamMeter( double percent, const TQString & filterHeader );
368 
369  };
370 
371  TQString FancyHeaderStyle::drawSpamMeter( double percent,
372  const TQString & filterHeader )
373  {
374  TQImage meterBar( 20, 1, 8, 24 );
375  const unsigned short gradient[20][3] = {
376  { 0, 255, 0 },
377  { 27, 254, 0 },
378  { 54, 252, 0 },
379  { 80, 250, 0 },
380  { 107, 249, 0 },
381  { 135, 247, 0 },
382  { 161, 246, 0 },
383  { 187, 244, 0 },
384  { 214, 242, 0 },
385  { 241, 241, 0 },
386  { 255, 228, 0 },
387  { 255, 202, 0 },
388  { 255, 177, 0 },
389  { 255, 151, 0 },
390  { 255, 126, 0 },
391  { 255, 101, 0 },
392  { 255, 76, 0 },
393  { 255, 51, 0 },
394  { 255, 25, 0 },
395  { 255, 0, 0 }
396  };
397  meterBar.setColor( 21, tqRgb( 255, 255, 255 ) );
398  meterBar.setColor( 22, tqRgb( 170, 170, 170 ) );
399  if ( percent < 0 ) // grey is for errors
400  meterBar.fill( 22 );
401  else {
402  meterBar.fill( 21 );
403  int max = TQMIN( 20, static_cast<int>( percent ) / 5 );
404  for ( int i = 0; i < max; ++i ) {
405  meterBar.setColor( i+1, tqRgb( gradient[i][0], gradient[i][1],
406  gradient[i][2] ) );
407  meterBar.setPixel( i, 0, i+1 );
408  }
409  }
410  TQString titleText = i18n("%1% probability of being spam.\n\nFull report:\n%2")
411  .arg( TQString::number( percent ), filterHeader );
412  return TQString("<img src=\"%1\" width=\"%2\" height=\"%3\" style=\"border: 1px solid black;\" title=\"%4\"> &nbsp;")
413  .arg( imgToDataUrl( meterBar, "PPM" ), TQString::number( 20 ),
414  TQString::number( 5 ), titleText );
415  }
416 
417 
418  TQString FancyHeaderStyle::format( const KMMessage * message,
419  const HeaderStrategy * strategy,
420  const TQString & vCardName, bool printing, bool topLevel ) const {
421  Q_UNUSED( topLevel );
422  if ( !message ) return TQString();
423  if ( !strategy )
424  strategy = HeaderStrategy::rich();
425 
426  TDEConfigGroup configReader( KMKernel::config(), "Reader" );
427 
428  // ### from kmreaderwin begin
429  // The direction of the header is determined according to the direction
430  // of the application layout.
431 
432  TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" );
433  TQString headerStr = TQString("<div class=\"fancy header\" dir=\"%1\">\n").arg(dir);
434 
435  // However, the direction of the message subject within the header is
436  // determined according to the contents of the subject itself. Since
437  // the "Re:" and "Fwd:" prefixes would always cause the subject to be
438  // considered left-to-right, they are ignored when determining its
439  // direction.
440 
441  TQString subjectDir;
442  if ( !message->subject().isEmpty() )
443  subjectDir = directionOf( message->cleanSubject() );
444  else
445  subjectDir = directionOf( i18n("No Subject") );
446 
447  // Prepare the date string (when printing always use the localized date)
448  TQString dateString;
449  if( printing ) {
450  TQDateTime dateTime;
451  TDELocale* locale = TDEGlobal::locale();
452  dateTime.setTime_t( message->date() );
453  dateString = locale->formatDateTime( dateTime );
454  }
455  else {
456  dateString = message->dateStr();
457  }
458 
459  // Spam header display.
460  // If the spamSpamStatus config value is true then we look for headers
461  // from a few spam filters and try to create visually meaningful graphics
462  // out of the spam scores.
463 
464  TQString spamHTML;
465 
466  if ( configReader.readBoolEntry( "showSpamStatus", true ) ) {
467  SpamScores scores = SpamHeaderAnalyzer::getSpamScores( message );
468  for ( SpamScoresIterator it = scores.begin(); it != scores.end(); ++it )
469  spamHTML += (*it).agent() + " " +
470  drawSpamMeter( (*it).score(), (*it).spamHeader() );
471  }
472 
473  TQString userHTML;
474  TQString presence;
475 
476  // IM presence and tdeabc photo
477 
478  ::KIMProxy *imProxy = KMKernel::self()->imProxy();
479  TQString tdeabcUid;
480  TDEABC::AddressBook *addressBook = TDEABC::StdAddressBook::self( true );
481  TDEABC::AddresseeList addresses = addressBook->findByEmail( KPIM::getFirstEmailAddress( message->from() ) );
482 
483  TQString photoURL;
484  int photoWidth = 60;
485  int photoHeight = 60;
486  if( addresses.count() == 1 )
487  {
488  // tdeabcUid is embedded in im: URIs to indicate which IM contact to message
489  tdeabcUid = addresses[0].uid();
490 
491  if ( imProxy->initialize() ) {
492  // im status
493  presence = imProxy->presenceString( tdeabcUid );
494  if ( !presence.isEmpty() )
495  {
496  TQString presenceIcon = TQString::fromLatin1( " <img src=\"%1\"/>" )
497  .arg( imgToDataUrl( imProxy->presenceIcon( tdeabcUid ).convertToImage() ) );
498  presence += presenceIcon;
499  }
500  }
501  // picture
502  if ( addresses[0].photo().isIntern() )
503  {
504  // get photo data and convert to data: url
505  //kdDebug( 5006 ) << "INTERNAL photo found" << endl;
506  TQImage photo = addresses[0].photo().data();
507  if ( !photo.isNull() )
508  {
509  photoWidth = photo.width();
510  photoHeight = photo.height();
511  // scale below 60, otherwise it can get way too large
512  if ( photoHeight > 60 ) {
513  double ratio = ( double )photoHeight / ( double )photoWidth;
514  photoHeight = 60;
515  photoWidth = (int)( 60 / ratio );
516  photo = photo.smoothScale( photoWidth, photoHeight );
517  }
518  photoURL = imgToDataUrl( photo );
519  }
520  }
521  else
522  {
523  //kdDebug( 5006 ) << "URL found" << endl;
524  photoURL = addresses[0].photo().url();
525  if ( photoURL.startsWith("/") )
526  photoURL.prepend( "file:" );
527  }
528  }
529  else // TODO: find a usable one
530  {
531  kdDebug( 5006 ) << "Multiple / No addressees matched email address; Count is " << addresses.count() << endl;
532  userHTML = "&nbsp;";
533  }
534 
535  if( photoURL.isEmpty() ) {
536  // no photo, look for a Face header
537  TQString faceheader = message->headerField( "Face" );
538  if ( !faceheader.isEmpty() ) {
539  TQImage faceimage;
540 
541  kdDebug( 5006 ) << "Found Face: header" << endl;
542 
543  TQCString facestring = faceheader.utf8();
544  // Spec says header should be less than 998 bytes
545  // Face: is 5 characters
546  if ( facestring.length() < 993 ) {
547  TQByteArray facearray;
548  KCodecs::base64Decode(facestring, facearray);
549 
550  TQImage faceimage;
551  if ( faceimage.loadFromData( facearray, "png" ) ) {
552  // Spec says image must be 48x48 pixels
553  if ( (48 == faceimage.width()) && (48 == faceimage.height()) ) {
554  photoURL = imgToDataUrl( faceimage );
555  photoWidth = 48;
556  photoHeight = 48;
557  } else {
558  kdDebug( 5006 ) << "Face: header image is" << faceimage.width() << "by" << faceimage.height() <<"not 48x48 Pixels" << endl;
559  }
560  } else {
561  kdDebug( 5006 ) << "Failed to load decoded png from Face: header" << endl;
562  }
563  } else {
564  kdDebug( 5006 ) << "Face: header too long at " << facestring.length() << endl;
565  }
566  }
567  }
568 
569  if( photoURL.isEmpty() )
570  {
571  // no photo, look for a X-Face header
572  TQString xfaceURL;
573  TQString xfhead = message->headerField( "X-Face" );
574  if ( !xfhead.isEmpty() )
575  {
576  KXFace xf;
577  photoURL = imgToDataUrl( xf.toImage( xfhead ) );
578  photoWidth = 48;
579  photoHeight = 48;
580 
581  }
582  }
583 
584  if( !photoURL.isEmpty() )
585  {
586  //kdDebug( 5006 ) << "Got a photo: " << photoURL << endl;
587  userHTML = TQString("<img src=\"%1\" width=\"%2\" height=\"%3\">")
588  .arg( photoURL ).arg( photoWidth ).arg( photoHeight );
589  if ( presence.isEmpty() ) {
590  userHTML = TQString("<div class=\"senderpic\">") + userHTML + "</div>";
591  } else {
592  userHTML = TQString( "<div class=\"senderpic\">"
593  "<a href=\"im:%1\">%2<div class=\"senderstatus\">"
594  "<span name=\"presence-%3\">%4</span></div></a>"
595  "</div>" ).arg( tdeabcUid )
596  .arg( userHTML )
597  .arg( tdeabcUid )
598  .arg( presence );
599  }
600  } else {
601  // we don't have a photo, just show presence, if we have it
602  if ( !presence.isEmpty() )
603  userHTML = TQString( "<a href=\"im:%1\"><div class=\"senderstatus\">"
604  "<span name=\"presence-%2\">%3</span></div></a>" )
605  .arg( tdeabcUid )
606  .arg( tdeabcUid )
607  .arg( presence );
608  }
609 #if 0
610  // Disabled 'Launch IM' link in headers - Will
611  if ( imProxy->imAppsAvailable() )
612  presence = "<a name=\"launchim\" href=\"kmail:startIMApp\">" + i18n("Launch IM") + "</a></span>";
613  // do nothing - no im apps available, leave presence empty
614  //presence = i18n( "DCOP/InstantMessenger not installed" );
615  kdDebug( 5006 ) << "final presence: '" << presence << "'" << endl;
616 #endif
617 
618  TQString timeHTML;
619  if ( GlobalSettings::self()->showCurrentTime() && strategy->showHeader( "date" ) ) {
620  DwHeaders& header = message->headers();
621  if ( header.HasDate() ) {
622  DwDateTime& origDate = header.Date();
623  int zone = origDate.Zone();
624  // kdDebug() << "FancyHeaderStyle::format() zone offset (in minutes): " << zone << endl;
625 
626  // copyed from mimelib -- code to determine local timezone
627  time_t t_now = time((time_t*) 0);
628 #if defined(HAVE_GMTIME_R)
629  struct tm utc;
630  gmtime_r(&t_now, &utc);
631  struct tm local;
632  localtime_r(&t_now, &local);
633 #else
634  struct tm utc = *gmtime(&t_now);
635  struct tm local = *localtime(&t_now);
636 #endif
637  DwUint32 t_local = 0;
638  t_local = 24 * t_local + local.tm_hour;
639  t_local = 60 * t_local + local.tm_min;
640  t_local = 60 * t_local + local.tm_sec;
641  DwUint32 t_utc = 0;
642  t_utc = 24 * t_utc + utc.tm_hour;
643  t_utc = 60 * t_utc + utc.tm_min;
644  t_utc = 60 * t_utc + utc.tm_sec;
645 
646  // kdDebug() << "FancyHeaderStyle::format() local zone offset (in minutes): " << lzone << endl;
647 
648  TQTime currTime = TQTime::currentTime( TQt::UTC );
649 
650  // kdDebug() << "FancyHeaderStyle::format() current time: " << currTime << endl;
651 
652  // now currTime contain message sender local time
653  currTime = currTime.addSecs( zone * 60 );
654 
655  TQString timeofday;
656  TQString color;
657  TQString bg_color;
658  TQString bg_image;
659  if ( currTime > TQTime( 0, 0, 0 ) && currTime <= TQTime( 6, 0, 0 ) ) {
660  timeofday = i18n( "Night" );
661  color = "white";
662  bg_color = "#000B6B";
663  bg_image = "url(data:image/png;base64,"
664  "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAyCAIAAAASmSbdAAAAS0lEQVQI11WOsRGAQAzDOG/LHoz9"
665  "kikIcF+kSBxbPs7LoNGVapAI0Zn+O+8NUwldozn6io7G7kdS/5zi7i+BvUM/5uSXlIfzMHx/bmWR"
666  "k++yj9rZAAAAAElFTkSuQmCC)";
667  }
668  else if ( currTime > TQTime( 6, 0, 0 ) && currTime <= TQTime( 12, 0, 0 ) ) {
669  timeofday = i18n( "Morning" );
670  color = "black";
671  bg_color = "#00A6FF";
672  bg_image = "url(data:image/png;base64,"
673  "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAyCAYAAACd+7GKAAAAWklEQVQI122OQQ7DMAzDaP3/dfuO"
674  "pWSHJgva7iZIBk3m/Ew5hexCHVCilewzFHKEbFZqgxJQWyzKhWKl9unqddJj8+L9sl0oR2gUim+o"
675  "zu4uSh7kn67/DNv+C4tsZOtjAWEHAAAAAElFTkSuQmCC)";
676  }
677  else if ( currTime > TQTime( 12, 0, 0 ) && currTime <= TQTime( 18, 0, 0 ) ) {
678  timeofday = i18n( "Afternoon" );
679  color = "black";
680  bg_color = "#00A6FF";
681  bg_image = "url(data:image/png;base64,"
682  "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAyCAYAAACd+7GKAAAAPUlEQVQI132OwQ0AIAwCSfcfw91c"
683  "QsCfRm399HFwoWjdDhMICQhxHSWMQPhkTCoqWRZU2h5i9tr4GZfmV5t3wWUI3h+NugAAAABJRU5E"
684  "rkJggg==)";
685  }
686  else {
687  timeofday = i18n( "Evening" );
688  color = "white";
689  bg_color = "#0014CC";
690  bg_image = "url(data:image/png;base64,"
691  "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAyCAYAAACd+7GKAAAAWklEQVQI11WOyRHAMAgDNQuUlBrS"
692  "fyFpAfKwje0PwyEt0vN+hVsJpzS6QML2ziWcFI6mZBZNSVDXYehyUgI1XsLI9eimHDH6kW0ddVIO"
693  "xx7JjrtshlbXlLDSD+WhJ+hwqWo8AAAAAElFTkSuQmCC)";
694  }
695 
696  TQString tformat;
697  if ( TDEGlobal::locale()->use12Clock() ) {
698  tformat = "h:mm AP";
699  }
700  else {
701  tformat = "h:mm";
702  }
703 
704  // kdDebug() << "FancyHeaderStyle::format() current time: " << currTime << " (" << timeofday << ")" << endl;
705 
706  timeHTML.append( TQString(
707  "<div id=\"sendersCurrentTime\" style=\""
708  "border:1px solid %1;"
709  "color:%2;"
710  "background-image:%3;"
711  "background-position:center left;"
712  "background-repeat:repeat-x;"
713  "text-align:center;"
714  "font-size:12px;"
715  "padding:2px;"
716  "width:150px;"
717  "height:45px;"
718  "margin: 0px 0px 3px 0px;"
719  "\" class=\"curtime\">%4<br />%5<br />%6</div>"
720  )
721  .arg( bg_color )
722  .arg( color )
723  .arg( bg_image )
724  .arg( i18n( "Sender's Current Time:" ) )
725  .arg( currTime.toString( tformat ) )
726  .arg( timeofday )
727  );
728  }
729  else {
730  // kdDebug() << "FancyHeaderStyle::format() no date header to display" << endl;
731  }
732  }
733 
734  //case HdrFancy:
735  // the subject line and box below for details
736  if ( strategy->showHeader( "subject" ) ) {
737  const int flags = LinkLocator::PreserveSpaces |
738  ( GlobalSettings::self()->showEmoticons() ?
739  LinkLocator::ReplaceSmileys : 0 );
740  headerStr += TQString("<div dir=\"%1\">%2</div>\n")
741  .arg(subjectDir)
742  .arg(message->subject().isEmpty()?
743  i18n("No Subject") :
744  strToHtml( message->subject(), flags ));
745  }
746  headerStr += "<table class=\"outer\"><tr><td width=\"100%\"><table>\n";
747  //headerStr += "<table>\n";
748  // from line
749  // the mailto: URLs can contain %3 etc., therefore usage of multiple
750  // TQString::arg is not possible
751  if ( strategy->showHeader( "from" ) ) {
752  TQString fromStr = message->from();
753  if ( fromStr.isEmpty() ) // no valid email in from, maybe just a name
754  fromStr = message->fromStrip(); // let's use that
755  headerStr += TQString("<tr><th>%1</th>\n"
756  "<td>")
757  .arg(i18n("From: "))
758  + KMMessage::emailAddrAsAnchor( fromStr, false )
759  + ( !message->headerField( "Resent-From" ).isEmpty() ? "&nbsp;"
760  + i18n("(resent from %1)")
762  message->headerField( "Resent-From" ),false) )
763  : TQString("") )
764  + ( !vCardName.isEmpty() ? "&nbsp;&nbsp;<a href=\"" + vCardName + "\">"
765  + i18n("[vCard]") + "</a>"
766  : TQString("") )
767 #if 0
768  + ( ( !presence.isEmpty() )
769  ? "&nbsp;&nbsp;(<span name=\"presence-" + tdeabcUid + "\">" + presence + "</span>)"
770  : TQString("") )
771 #endif
772  + ( message->headerField("Organization").isEmpty()
773  ? TQString("")
774  : "&nbsp;&nbsp;("
775  + strToHtml(message->headerField("Organization"))
776  + ")")
777  + "</td></tr>\n";
778  }
779  // to line
780  if ( strategy->showHeader( "to" ) )
781  headerStr.append(TQString("<tr><th>%1</th>\n"
782  "<td>%2</td></tr>\n")
783  .arg(i18n("To: "))
784  .arg(KMMessage::emailAddrAsAnchor(message->to(),false)));
785 
786  // cc line, if any
787  if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty())
788  headerStr.append(TQString("<tr><th>%1</th>\n"
789  "<td>%2</td></tr>\n")
790  .arg(i18n("CC: "))
791  .arg(KMMessage::emailAddrAsAnchor(message->cc(),false)));
792 
793  // Bcc line, if any
794  if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty())
795  headerStr.append(TQString("<tr><th>%1</th>\n"
796  "<td>%2</td></tr>\n")
797  .arg(i18n("BCC: "))
798  .arg(KMMessage::emailAddrAsAnchor(message->bcc(),false)));
799 
800  if ( strategy->showHeader( "date" ) )
801  headerStr.append(TQString("<tr><th>%1</th>\n"
802  "<td dir=\"%2\">%3</td></tr>\n")
803  .arg(i18n("Date: "))
804  .arg( directionOf( message->dateStr() ) )
805  .arg(strToHtml(dateString)));
806 
807  if ( GlobalSettings::self()->showUserAgent() ) {
808  if ( strategy->showHeader( "user-agent" ) ) {
809  if ( !message->headerField("User-Agent").isEmpty() ) {
810  headerStr.append(TQString("<tr><th>%1</th>\n"
811  "<td>%2</td></tr>\n")
812  .arg(i18n("User-Agent: "))
813  .arg( strToHtml( message->headerField("User-Agent") ) ) );
814  }
815  }
816 
817  if ( strategy->showHeader( "x-mailer" ) ) {
818  if ( !message->headerField("X-Mailer").isEmpty() ) {
819  headerStr.append(TQString("<tr><th>%1</th>\n"
820  "<td>%2</td></tr>\n")
821  .arg(i18n("X-Mailer: "))
822  .arg( strToHtml( message->headerField("X-Mailer") ) ) );
823  }
824  }
825  }
826 
827  // FIXME: Show status in synthetic header style field. Decide whether this or current in brackets style is best and remove one.
828  /* if( strategy->showHeader( "status" ) )
829  headerStr.append( TQString( "<tr><th>%1</th>\n"
830  "<td dir=\"%2\">%3</td></tr>\n")
831  .arg(i18n("Sender status: "))
832  .arg( directionOf( onlineStatus ) )
833  .arg(onlineStatus));
834  */
835  headerStr.append( TQString("<tr><td colspan=\"2\"><div id=\"attachmentInjectionPoint\"></div></td></tr>" ) );
836  headerStr.append(
837  TQString( "</table></td><td align=\"center\" valign=\"top\">%1%2</td></tr></table>\n" )
838  .arg(timeHTML)
839  .arg(userHTML) );
840 
841  if ( !spamHTML.isEmpty() )
842  headerStr.append( TQString( "<div class=\"spamheader\" dir=\"%1\"><b>%2</b>&nbsp;<span style=\"padding-left: 20px;\">%3</span></div>\n")
843  .arg( subjectDir, i18n("Spam Status:"), spamHTML ) );
844 
845  headerStr += "</div>\n\n";
846  return headerStr;
847  }
848 
849  TQString FancyHeaderStyle::imgToDataUrl( const TQImage &image, const char* fmt )
850  {
851  TQByteArray ba;
852  TQBuffer buffer( ba );
853  buffer.open( IO_WriteOnly );
854  image.save( &buffer, fmt );
855  return TQString::fromLatin1("data:image/%1;base64,%2")
856  .arg( fmt, KCodecs::base64Encode( ba ).data() );
857  }
858 
859 // #####################
860 
861  class EnterpriseHeaderStyle : public HeaderStyle {
862  friend class ::KMail::HeaderStyle;
863  protected:
864  EnterpriseHeaderStyle() : HeaderStyle() {}
865  virtual ~EnterpriseHeaderStyle() {}
866 
867  public:
868  const char * name() const { return "enterprise"; }
869  const HeaderStyle * next() const { return brief(); }
870  const HeaderStyle * prev() const { return fancy(); }
871 
872  TQString format( const KMMessage * message, const HeaderStrategy * strategy,
873  const TQString & vCardName, bool printing, bool topLevel ) const;
874  };
875 
876  TQString EnterpriseHeaderStyle::format( const KMMessage * message,
877  const HeaderStrategy * strategy,
878  const TQString & vCardName, bool printing, bool topLevel ) const {
879  if ( !message ) return TQString();
880  if ( !strategy )
881  strategy = HeaderStrategy::brief();
882 
883  // The direction of the header is determined according to the direction
884  // of the application layout.
885 
886  TQString dir = TQApplication::reverseLayout() ? "rtl" : "ltr" ;
887 
888  // However, the direction of the message subject within the header is
889  // determined according to the contents of the subject itself. Since
890  // the "Re:" and "Fwd:" prefixes would always cause the subject to be
891  // considered left-to-right, they are ignored when determining its
892  // direction.
893 
894  TQString subjectDir;
895  if (!message->subject().isEmpty())
896  subjectDir = directionOf( message->cleanSubject() );
897  else
898  subjectDir = directionOf( i18n("No Subject") );
899 
900  // colors depend on if its encapsulated or not
901  TQColor fontColor(TQt::white);
902  TQString linkColor = "class =\"white\"";
903  const TQColor activeColor = tqApp->palette().active().highlight();
904  TQColor activeColorDark = activeColor.dark(130);
905  // reverse colors for encapsulated
906  if( !topLevel ){
907  activeColorDark = activeColor.dark(50);
908  fontColor = TQColor(TQt::black);
909  linkColor = "class =\"black\"";
910  }
911 
912  // Prepare the date string (when printing always use the localized date)
913  TQString dateString;
914  if( printing ) {
915  TQDateTime dateTime;
916  TDELocale * locale = TDEGlobal::locale();
917  dateTime.setTime_t( message->date() );
918  dateString = locale->formatDateTime( dateTime );
919  } else {
920  dateString = message->dateStr();
921  }
922 
923  TQString imgpath(locate("data","kmail/pics/"));
924  imgpath.append("enterprise_");
925  const TQString borderSettings(" padding-top: 0px; padding-bottom: 0px; border-width: 0px ");
926  TQString headerStr ("");
927 
928  // 3D borders
929  if(topLevel)
930  headerStr +=
931  "<div style=\"position: fixed; top: 0px; left: 0px; background-color: #606060; "
932  "width: 10px; min-height: 100%;\">&nbsp;</div>"
933  "<div style=\"position: fixed; top: 0px; right: 0px; background-color: #606060; "
934  "width: 10px; min-height: 100%;\">&nbsp;</div>";
935 
936  headerStr += ""
937  "<div style=\"margin-left: 10px; top: 0px;\"><span style=\"font-size: 10px; font-weight: bold;\">"+dateString+"</span></div>"
938  // #0057ae
939  "<table style=\"background: "+activeColorDark.name()+"; border-collapse:collapse; top: 14px; min-width: 200px; \" cellpadding=0> \n"
940  " <tr> \n"
941  " <td style=\"min-width: 6px; background-image: url("+imgpath+"top_left.png); \"></td> \n"
942  " <td style=\"height: 6px; width: 100%; background: url("+imgpath+"top.png); \"></td> \n"
943  " <td style=\"min-width: 6px; background: url("+imgpath+"top_right.png); \"></td> </tr> \n"
944  " <tr> \n"
945  " <td style=\"min-width: 6px; max-width: 6px; background: url("+imgpath+"left.png); \"></td> \n"
946  " <td style=\"\"> \n";
947 
948  headerStr +=
949  " <div class=\"noprint\" style=\"z-index: 1; float:right; position: relative; top: -35px; right: 20px ;\">\n"
950  " <img src=\""+imgpath+"icon.png\">\n"
951  " </div>\n";
952 
953  headerStr +=
954  " <table style=\"color: "+fontColor.name()+" ! important; margin: 1px; border-spacing: 0px;\" cellpadding=0> \n";
955 
956  // subject
957  //strToHtml( message->subject() )
958  if ( strategy->showHeader( "subject" ) ){
959  headerStr +=
960  " <tr> \n"
961  " <td style=\"font-size: 6px; text-align: right; padding-left: 5px; padding-right: 24px; "+borderSettings+"\"></td> \n"
962  " <td style=\"font-weight: bolder; font-size: 120%; padding-right: 91px; "+borderSettings+"\">"+message->subject()+"</td> \n"
963  " </tr> \n";
964  }
965 
966  // from
967  if ( strategy->showHeader( "from" ) ){
968  TQString fromStr = message->from();
969  if ( fromStr.isEmpty() ) // no valid email in from, maybe just a name
970  fromStr = message->fromStrip(); // let's use that
971  // TODO vcard
972  TQString fromPart = KMMessage::emailAddrAsAnchor( fromStr, true, linkColor );
973  if ( !vCardName.isEmpty() )
974  fromPart += "&nbsp;&nbsp;<a href=\"" + vCardName + "\" "+linkColor+">" + i18n("[vCard]") + "</a>";
975  //TODO strategy date
976  //if ( strategy->showHeader( "date" ) )
977  headerStr +=
978  " <tr> \n"
979  " <td style=\"font-size: 6px; padding-left: 5px; padding-right: 24px; text-align: right; "+borderSettings+"\">"+i18n("From: ")+"</td> \n"
980  " <td style=\""+borderSettings+"\">"+ fromPart +"</td> "
981  " </tr> ";
982  }
983 
984  // to line
985  if( strategy->showHeader( "to" ) )
986  headerStr +=
987  " <tr> "
988  " <td style=\"font-size: 6px; text-align: right; padding-left: 5px; padding-right: 24px; " + borderSettings + "\">" + i18n("To: ") + "</td> "
989  " <td style=\"" + borderSettings + "\">" +
990  KMMessage::emailAddrAsAnchor( message->to(), false, linkColor ) +
991  " </td> "
992  " </tr>\n";
993 
994  // cc line, if any
995  if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty() )
996  headerStr +=
997  " <tr> "
998  " <td style=\"font-size: 6px; text-align: right; padding-left: 5px; padding-right: 24px; " + borderSettings + "\">" + i18n("CC: ") + "</td> "
999  " <td style=\"" + borderSettings + "\">" +
1000  KMMessage::emailAddrAsAnchor( message->cc(), false, linkColor ) +
1001  " </td> "
1002  " </tr>\n";
1003 
1004  // bcc line, if any
1005  if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty() )
1006  headerStr +=
1007  " <tr> "
1008  " <td style=\"font-size: 6px; text-align: right; padding-left: 5px; padding-right: 24px; " + borderSettings + "\">" + i18n("BCC: ") + "</td> "
1009  " <td style=\"" + borderSettings + "\">" +
1010  KMMessage::emailAddrAsAnchor( message->bcc(), false, linkColor ) +
1011  " </td> "
1012  " </tr>\n";
1013 
1014  // header-bottom
1015  headerStr +=
1016  " </table> \n"
1017  " </td> \n"
1018  " <td style=\"min-width: 6px; max-height: 15px; background: url("+imgpath+"right.png); \"></td> \n"
1019  " </tr> \n"
1020  " <tr> \n"
1021  " <td style=\"min-width: 6px; background: url("+imgpath+"s_left.png); \"></td> \n"
1022  " <td style=\"height: 35px; width: 80%; background: url("+imgpath+"sbar.png);\"> \n"
1023  " <img src=\""+imgpath+"sw.png\" style=\"margin: 0px; height: 30px; overflow:hidden; \"> \n"
1024  " <img src=\""+imgpath+"sp_right.png\" style=\"float: right; \"> </td> \n"
1025  " <td style=\"min-width: 6px; background: url("+imgpath+"s_right.png); \"></td> \n"
1026  " </tr> \n"
1027  " </table> \n";
1028 
1029  // kmail icon
1030  if(topLevel) {
1031 
1032  // attachments
1033  headerStr +=
1034  "<div class=\"noprint\" style=\"position: absolute; top: 60px; right: 20px; width: 91px; height: 200px;\">"
1035  "<div id=\"attachmentInjectionPoint\"></div>"
1036  "</div>\n";
1037  }
1038 
1039  if ( printing ) {
1040  //provide a bit more left padding when printing
1041  //kolab/issue3254 (printed mail cut at the left side)
1042  headerStr += "<div style=\"padding: 6px; padding-left: 10px;\">";
1043  } else {
1044  headerStr += "<div style=\"padding: 6px;\">";
1045  }
1046 
1047  // TODO
1048  // spam status
1049  // ### iterate over the rest of strategy->headerToDisplay() (or
1050  // ### all headers if DefaultPolicy == Display) (elsewhere, too)
1051  return headerStr;
1052  }
1053 
1054 // #####################
1055 
1056  //
1057  // HeaderStyle abstract base:
1058  //
1059 
1060  HeaderStyle::HeaderStyle() {
1061 
1062  }
1063 
1064  HeaderStyle::~HeaderStyle() {
1065 
1066  }
1067 
1068  const HeaderStyle * HeaderStyle::create( Type type ) {
1069  switch ( type ) {
1070  case Brief: return brief();
1071  case Plain: return plain();
1072  case Fancy: return fancy();
1073  case Enterprise: return enterprise();
1074  }
1075  kdFatal( 5006 ) << "HeaderStyle::create(): Unknown header style ( type == "
1076  << (int)type << " ) requested!" << endl;
1077  return 0; // make compiler happy
1078  }
1079 
1080  const HeaderStyle * HeaderStyle::create( const TQString & type ) {
1081  TQString lowerType = type.lower();
1082  if ( lowerType == "brief" ) return brief();
1083  if ( lowerType == "plain" ) return plain();
1084  if ( lowerType == "enterprise" ) return enterprise();
1085  //if ( lowerType == "fancy" ) return fancy(); // not needed, see below
1086  // don't kdFatal here, b/c the strings are user-provided
1087  // (TDEConfig), so fail gracefully to the default:
1088  return fancy();
1089  }
1090 
1091  static const HeaderStyle * briefStyle = 0;
1092  static const HeaderStyle * plainStyle = 0;
1093  static const HeaderStyle * fancyStyle = 0;
1094  static const HeaderStyle * enterpriseStyle = 0;
1095 
1096  const HeaderStyle * HeaderStyle::brief() {
1097  if ( !briefStyle )
1098  briefStyle = new BriefHeaderStyle();
1099  return briefStyle;
1100  }
1101 
1102  const HeaderStyle * HeaderStyle::plain() {
1103  if ( !plainStyle )
1104  plainStyle = new PlainHeaderStyle();
1105  return plainStyle;
1106  }
1107 
1108  const HeaderStyle * HeaderStyle::fancy() {
1109  if ( !fancyStyle )
1110  fancyStyle = new FancyHeaderStyle();
1111  return fancyStyle;
1112  }
1113 
1114  const HeaderStyle * HeaderStyle::enterprise() {
1115  if ( !enterpriseStyle )
1116  enterpriseStyle = new EnterpriseHeaderStyle();
1117  return enterpriseStyle;
1118  }
1119 
1120 } // namespace KMail
::KIMProxy * imProxy()
Get a reference to KMail's KIMProxy instance.
Definition: kmkernel.cpp:2421
static KMKernel * self()
normal control stuff
Definition: kmkernel.h:259
This is a Mime Message.
Definition: kmmessage.h:68
TQString from() const
Get or set the 'From' header field.
Definition: kmmessage.cpp:2017
TQString bcc() const
Get or set the 'Bcc' header field.
Definition: kmmessage.cpp:1970
TQString to() const
Get or set the 'To' header field.
Definition: kmmessage.cpp:1896
TQString subject() const
Get or set the 'Subject' header field.
Definition: kmmessage.cpp:2051
static TQString emailAddrAsAnchor(const TQString &emailAddr, bool stripped=true, const TQString &cssStyle=TQString(), bool link=true)
Converts the email address(es) to (a) nice HTML mailto: anchor(s).
Definition: kmmessage.cpp:3842
TQString cc() const
Get or set the 'Cc' header field.
Definition: kmmessage.cpp:1942
TQString headerField(const TQCString &name) const
Returns the value of a header field with the given name.
Definition: kmmessage.cpp:2291
TQString replyTo() const
Get or set the 'ReplyTo' header field.
Definition: kmmessage.cpp:1921
TQCString dateShortStr() const
Returns the message date in asctime format or an empty string if the message lacks a Date header.
Definition: kmmessage.cpp:1817
DwHeaders & headers() const
get the DwHeaders (make sure to call setNeedsAssembly() function after directly modyfying internal da...
Definition: kmmessage.cpp:2548
TQString dateStr() const
Get or set the 'Date' header field.
Definition: kmmessage.cpp:1799
folderdiaquotatab.h
Definition: aboutdata.cpp:40