• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepim API Reference
  • KDE Home
  • Contact Us
 

messageviewer

  • sources
  • kde-4.14
  • kdepim
  • messageviewer
  • header
headerstyle_util.cpp
Go to the documentation of this file.
1 /*
2  Copyright (c) 2013-2015 Montel Laurent <montel@kde.org>
3 
4  This program is free software; you can redistribute it and/or modify it
5  under the terms of the GNU General Public License, version 2, as
6  published by the Free Software Foundation.
7 
8  This program is distributed in the hope that it will be useful, but
9  WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License along
14  with this program; if not, write to the Free Software Foundation, Inc.,
15  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17 
18 #include "headerstyle_util.h"
19 #include "viewer/nodehelper.h"
20 #include "header/headerstyle.h"
21 
22 #include <messagecore/utils/stringutil.h>
23 
24 #include "messagecore/settings/globalsettings.h"
25 #include "settings/globalsettings.h"
26 
27 
28 
29 #include <KLocalizedString>
30 #include <KGlobal>
31 
32 #include <QBuffer>
33 #include "contactdisplaymessagememento.h"
34 #include "kxface.h"
35 
36 #include <kpimutils/email.h>
37 
38 using namespace MessageCore;
39 
40 namespace MessageViewer {
41 namespace HeaderStyleUtil {
42 //
43 // Convenience functions:
44 //
45 QString directionOf( const QString &str ) {
46  return str.isRightToLeft() ? QLatin1String("rtl") : QLatin1String("ltr");
47 }
48 
49 QString strToHtml( const QString &str, int flags ) {
50  return LinkLocator::convertToHtml( str, flags );
51 }
52 
53 // Prepare the date string (when printing always use the localized date)
54 QString dateString( KMime::Message *message, bool printing, bool shortDate ) {
55  const KDateTime dateTime = message->date()->dateTime();
56  if ( !dateTime.isValid() )
57  return i18nc( "Unknown date", "Unknown" );
58  if( printing ) {
59  KLocale * locale = KGlobal::locale();
60  return locale->formatDateTime( dateTime );
61  } else {
62  if ( shortDate )
63  return dateShortStr( dateTime );
64  else
65  return dateStr( dateTime );
66  }
67 }
68 
69 QString subjectString( KMime::Message *message, int flags )
70 {
71  QString subjectStr;
72  const KMime::Headers::Subject * const subject = message->subject(false);
73  if ( subject ) {
74  subjectStr = subject->asUnicodeString();
75  if ( subjectStr.isEmpty() )
76  subjectStr = i18n("No Subject");
77  else
78  subjectStr = strToHtml( subjectStr, flags );
79  } else {
80  subjectStr = i18n("No Subject");
81  }
82  return subjectStr;
83 }
84 
85 QString subjectDirectionString( KMime::Message *message )
86 {
87  QString subjectDir;
88  if ( message->subject(false) )
89  subjectDir = directionOf( NodeHelper::cleanSubject( message ) );
90  else
91  subjectDir = directionOf( i18n("No Subject") );
92  return subjectDir;
93 }
94 
95 QString spamStatus(KMime::Message *message)
96 {
97  QString spamHTML;
98  if ( GlobalSettings::self()->showSpamStatus() ) {
99  const SpamScores scores = SpamHeaderAnalyzer::getSpamScores( message );
100 
101  for ( SpamScores::const_iterator it = scores.constBegin(), end = scores.constEnd() ; it != end ; ++it )
102  spamHTML += (*it).agent() + QLatin1Char(' ') +
103  MessageViewer::HeaderStyleUtil::drawSpamMeter( (*it).error(), (*it).score(), (*it).confidence(), (*it).spamHeader(), (*it).confidenceHeader() );
104  }
105  return spamHTML;
106 }
107 
108 
109 QString drawSpamMeter( SpamError spamError, double percent, double confidence,
110  const QString &filterHeader, const QString &confidenceHeader )
111 {
112  static const int meterWidth = 20;
113  static const int meterHeight = 5;
114  QImage meterBar( meterWidth, 1, QImage::Format_Indexed8/*QImage::Format_RGB32*/ );
115  meterBar.setNumColors( 24 );
116 
117  meterBar.setColor( meterWidth + 1, qRgb( 255, 255, 255 ) );
118  meterBar.setColor( meterWidth + 2, qRgb( 170, 170, 170 ) );
119  if ( spamError != noError ) // grey is for errors
120  meterBar.fill( meterWidth + 2 );
121  else {
122  static const unsigned short gradient[meterWidth][3] = {
123  { 0, 255, 0 },
124  { 27, 254, 0 },
125  { 54, 252, 0 },
126  { 80, 250, 0 },
127  { 107, 249, 0 },
128  { 135, 247, 0 },
129  { 161, 246, 0 },
130  { 187, 244, 0 },
131  { 214, 242, 0 },
132  { 241, 241, 0 },
133  { 255, 228, 0 },
134  { 255, 202, 0 },
135  { 255, 177, 0 },
136  { 255, 151, 0 },
137  { 255, 126, 0 },
138  { 255, 101, 0 },
139  { 255, 76, 0 },
140  { 255, 51, 0 },
141  { 255, 25, 0 },
142  { 255, 0, 0 }
143  };
144 
145  meterBar.fill( meterWidth + 1 );
146  const int max = qMin( meterWidth, static_cast<int>( percent ) / 5 );
147  for ( int i = 0; i < max; ++i ) {
148  meterBar.setColor( i+1, qRgb( gradient[i][0], gradient[i][1],
149  gradient[i][2] ) );
150  meterBar.setPixel( i, 0, i+1 );
151  }
152  }
153 
154  QString titleText;
155  QString confidenceString;
156  if ( spamError == noError ) {
157  if ( confidence >= 0 ) {
158  confidenceString = QString::number( confidence ) + QLatin1String("% &nbsp;");
159  titleText = i18n("%1% probability of being spam with confidence %3%.\n\n"
160  "Full report:\nProbability=%2\nConfidence=%4",
161  QString::number(percent,'f',2), filterHeader, confidence, confidenceHeader );
162  } else { // do not show negative confidence
163  confidenceString = QString() + QLatin1String("&nbsp;");
164  titleText = i18n("%1% probability of being spam.\n\n"
165  "Full report:\nProbability=%2",
166  QString::number(percent,'f',2), filterHeader);
167  }
168  } else {
169  QString errorMsg;
170  switch ( spamError )
171  {
172  case errorExtractingAgentString:
173  errorMsg = i18n( "No Spam agent" );
174  break;
175  case couldNotConverScoreToFloat:
176  errorMsg = i18n( "Spam filter score not a number" );
177  break;
178  case couldNotConvertThresholdToFloatOrThresholdIsNegative:
179  errorMsg = i18n( "Threshold not a valid number" );
180  break;
181  case couldNotFindTheScoreField:
182  errorMsg = i18n( "Spam filter score could not be extracted from header" );
183  break;
184  case couldNotFindTheThresholdField:
185  errorMsg = i18n( "Threshold could not be extracted from header" );
186  break;
187  default:
188  errorMsg = i18n( "Error evaluating spam score" );
189  break;
190  }
191  // report the error in the spam filter
192  titleText = i18n("%1.\n\n"
193  "Full report:\n%2",
194  errorMsg, filterHeader );
195  }
196  return QString::fromLatin1("<img src=\"%1\" width=\"%2\" height=\"%3\" style=\"border: 1px solid black;\" title=\"%4\"> &nbsp;")
197  .arg( imgToDataUrl( meterBar ), QString::number( meterWidth ),
198  QString::number( meterHeight ), titleText ) + confidenceString;
199 }
200 
201 QString imgToDataUrl( const QImage &image )
202 {
203  QByteArray ba;
204  QBuffer buffer( &ba );
205  buffer.open( QIODevice::WriteOnly );
206  image.save( &buffer, "PNG" );
207  return QString::fromLatin1("data:image/%1;base64,%2").arg( QString::fromLatin1( "PNG" ), QString::fromLatin1( ba.toBase64() ) );
208 }
209 
210 QString dateStr(const KDateTime &dateTime)
211 {
212  const time_t unixTime = dateTime.toTime_t();
213  return KMime::DateFormatter::formatDate(
214  static_cast<KMime::DateFormatter::FormatType>(
215  MessageCore::GlobalSettings::self()->dateFormat() ),
216  unixTime, MessageCore::GlobalSettings::self()->customDateFormat() );
217 }
218 
219 QString dateShortStr(const KDateTime &dateTime)
220 {
221  return KGlobal::locale()->formatDateTime( dateTime, KLocale::FancyShortDate );
222 }
223 
224 
225 QList<KMime::Types::Mailbox> resentFromList(KMime::Message *message)
226 {
227  // Get the resent-from header into a Mailbox
228  QList<KMime::Types::Mailbox> resentFrom;
229  if ( message->headerByType( "Resent-From" ) ) {
230  const QByteArray data = message->headerByType( "Resent-From" )->as7BitString( false );
231  const char * start = data.data();
232  const char * end = start + data.length();
233  KMime::Types::AddressList addressList;
234  KMime::HeaderParsing::parseAddressList( start, end, addressList );
235  foreach ( const KMime::Types::Address &addr, addressList ) {
236  foreach ( const KMime::Types::Mailbox &mbox, addr.mailboxList ) {
237  resentFrom.append( mbox );
238  }
239  }
240  }
241  return resentFrom;
242 }
243 
244 QList<KMime::Types::Mailbox> resentToList(KMime::Message *message)
245 {
246  // Get the resent-from header into a Mailbox
247  QList<KMime::Types::Mailbox> resentTo;
248  if ( message->headerByType( "Resent-To" ) ) {
249  const QByteArray data = message->headerByType( "Resent-To" )->as7BitString( false );
250  const char * start = data.data();
251  const char * end = start + data.length();
252  KMime::Types::AddressList addressList;
253  KMime::HeaderParsing::parseAddressList( start, end, addressList );
254  foreach ( const KMime::Types::Address &addr, addressList ) {
255  foreach ( const KMime::Types::Mailbox &mbox, addr.mailboxList ) {
256  resentTo.append( mbox );
257  }
258  }
259  }
260  return resentTo;
261 }
262 
263 void updateXFaceSettings(QImage photo, xfaceSettings &settings)
264 {
265  if ( !photo.isNull() ) {
266  settings.photoWidth = photo.width();
267  settings.photoHeight = photo.height();
268  // scale below 60, otherwise it can get way too large
269  if ( settings.photoHeight > 60 ) {
270  double ratio = ( double )settings.photoHeight / ( double )settings.photoWidth;
271  settings.photoHeight = 60;
272  settings.photoWidth = (int)( 60 / ratio );
273  photo = photo.scaled( settings.photoWidth, settings.photoHeight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
274  }
275  settings.photoURL = MessageViewer::HeaderStyleUtil::imgToDataUrl( photo );
276  }
277 }
278 
279 xfaceSettings xface(const MessageViewer::HeaderStyle *style, KMime::Message *message)
280 {
281 
282  xfaceSettings settings;
283  bool useOtherPhotoSources = false;
284 
285  if ( style->allowAsync() ) {
286 
287  Q_ASSERT( style->nodeHelper() );
288  Q_ASSERT( style->sourceObject() );
289 
290  ContactDisplayMessageMemento *photoMemento =
291  dynamic_cast<ContactDisplayMessageMemento*>( style->nodeHelper()->bodyPartMemento( message, "contactphoto" ) );
292  if ( !photoMemento ) {
293  const QString email = QString::fromLatin1(KPIMUtils::firstEmailAddress( message->from()->as7BitString(false) ));
294  photoMemento = new ContactDisplayMessageMemento( email );
295  style->nodeHelper()->setBodyPartMemento( message, "contactphoto", photoMemento );
296  QObject::connect( photoMemento, SIGNAL(update(MessageViewer::Viewer::UpdateMode)),
297  style->sourceObject(), SLOT(update(MessageViewer::Viewer::UpdateMode)) );
298 
299  QObject::connect( photoMemento, SIGNAL(changeDisplayMail(Viewer::DisplayFormatMessage,bool)),
300  style->sourceObject(), SIGNAL(changeDisplayMail(Viewer::DisplayFormatMessage,bool)) );
301  }
302 
303  if ( photoMemento->finished() ) {
304 
305  useOtherPhotoSources = true;
306  if ( photoMemento->photo().isIntern() ) {
307  // get photo data and convert to data: url
308  const QImage photo = photoMemento->photo().data();
309  updateXFaceSettings(photo, settings);
310  } else if (!photoMemento->photo().url().isEmpty()){
311  settings.photoURL = photoMemento->photo().url();
312  if ( settings.photoURL.startsWith(QLatin1Char('/')) )
313  settings.photoURL.prepend( QLatin1String("file:") );
314  } else if (!photoMemento->gravatarPixmap().isNull()) {
315  const QImage photo = photoMemento->gravatarPixmap().toImage();
316  updateXFaceSettings(photo, settings);
317  }
318  } else {
319  // if the memento is not finished yet, use other photo sources instead
320  useOtherPhotoSources = true;
321  }
322  } else {
323  useOtherPhotoSources = true;
324  }
325 
326  if( settings.photoURL.isEmpty() && message->headerByType( "Face" ) && useOtherPhotoSources ) {
327  // no photo, look for a Face header
328  const QString faceheader = message->headerByType( "Face" )->asUnicodeString();
329  if ( !faceheader.isEmpty() ) {
330 
331  kDebug() << "Found Face: header";
332 
333  const QByteArray facestring = faceheader.toUtf8();
334  // Spec says header should be less than 998 bytes
335  // Face: is 5 characters
336  if ( facestring.length() < 993 ) {
337  const QByteArray facearray = QByteArray::fromBase64( facestring );
338 
339  QImage faceimage;
340  if ( faceimage.loadFromData( facearray, "png" ) ) {
341  // Spec says image must be 48x48 pixels
342  if ( ( 48 == faceimage.width() ) && ( 48 == faceimage.height() ) ) {
343  settings.photoURL = MessageViewer::HeaderStyleUtil::imgToDataUrl( faceimage );
344  settings.photoWidth = 48;
345  settings.photoHeight = 48;
346  } else {
347  kDebug() << "Face: header image is" << faceimage.width() << "by"
348  << faceimage.height() << "not 48x48 Pixels";
349  }
350  } else {
351  kDebug() << "Failed to load decoded png from Face: header";
352  }
353  } else {
354  kDebug() << "Face: header too long at" << facestring.length();
355  }
356  }
357  }
358 
359  if( settings.photoURL.isEmpty() && message->headerByType( "X-Face" ) && useOtherPhotoSources ) {
360  // no photo, look for a X-Face header
361  const QString xfhead = message->headerByType( "X-Face" )->asUnicodeString();
362  if ( !xfhead.isEmpty() ) {
363  MessageViewer::KXFace xf;
364  settings.photoURL = MessageViewer::HeaderStyleUtil::imgToDataUrl( xf.toImage( xfhead ) );
365  settings.photoWidth = 48;
366  settings.photoHeight = 48;
367  }
368  }
369 
370  return settings;
371 }
372 
373 }
374 }
MessageViewer::Viewer::DisplayFormatMessage
DisplayFormatMessage
Definition: viewer.h:108
MessageViewer::HeaderStyleUtil::directionOf
QString directionOf(const QString &str)
Definition: headerstyle_util.cpp:45
MessageViewer::HeaderStyle::allowAsync
bool allowAsync() const
Definition: headerstyle.h:124
globalsettings.h
QImage::loadFromData
bool loadFromData(const uchar *data, int len, const char *format)
MessageViewer::HeaderStyleUtil::drawSpamMeter
QString drawSpamMeter(SpamError spamError, double percent, double confidence, const QString &filterHeader, const QString &confidenceHeader)
Definition: headerstyle_util.cpp:109
MessageViewer::HeaderStyle
This class encapsulates the visual appearance of message headers.
Definition: headerstyle.h:60
MessageViewer::HeaderStyleUtil::dateString
QString dateString(KMime::Message *message, bool printing, bool shortDate)
Definition: headerstyle_util.cpp:54
QByteArray
QImage::save
bool save(const QString &fileName, const char *format, int quality) const
QString::prepend
QString & prepend(QChar ch)
MessageViewer::HeaderStyleUtil::resentToList
QList< KMime::Types::Mailbox > resentToList(KMime::Message *message)
Definition: headerstyle_util.cpp:244
MessageViewer::noError
Definition: spamheaderanalyzer.h:45
QVector::constEnd
const_iterator constEnd() const
QImage::setPixel
void setPixel(int x, int y, uint index_or_rgb)
MessageViewer::HeaderStyleUtil::xface
xfaceSettings xface(const MessageViewer::HeaderStyle *style, KMime::Message *message)
Definition: headerstyle_util.cpp:279
MessageViewer::HeaderStyleUtil::xfaceSettings::photoHeight
int photoHeight
Definition: headerstyle_util.h:70
MessageViewer::HeaderStyleUtil::xfaceSettings::photoWidth
int photoWidth
Definition: headerstyle_util.h:69
QBuffer
MessageViewer::Viewer::UpdateMode
UpdateMode
The display update mode: Force updates the display immediately, Delayed updates after some time (150m...
Definition: viewer.h:132
kxface.h
QImage::isNull
bool isNull() const
nodehelper.h
QByteArray::length
int length() const
MessageViewer::HeaderStyleUtil::xfaceSettings
Definition: headerstyle_util.h:61
MessageViewer::couldNotConverScoreToFloat
Definition: spamheaderanalyzer.h:48
headerstyle.h
MessageViewer::SpamError
SpamError
Definition: spamheaderanalyzer.h:44
QImage::setColor
void setColor(int index, QRgb colorValue)
MessageViewer::HeaderStyleUtil::dateShortStr
QString dateShortStr(const KDateTime &dateTime)
Definition: headerstyle_util.cpp:219
QImage::setNumColors
void setNumColors(int numColors)
MessageViewer::HeaderStyleUtil::dateStr
QString dateStr(const KDateTime &dateTime)
Definition: headerstyle_util.cpp:210
QString::isRightToLeft
bool isRightToLeft() const
MessageViewer::HeaderStyle::nodeHelper
NodeHelper * nodeHelper() const
Definition: headerstyle.h:121
QString::number
QString number(int n, int base)
QList::append
void append(const T &value)
MessageViewer::ContactDisplayMessageMemento
Definition: contactdisplaymessagememento.h:38
MessageViewer::HeaderStyleUtil::updateXFaceSettings
void updateXFaceSettings(QImage photo, xfaceSettings &settings)
Definition: headerstyle_util.cpp:263
QImage::fill
void fill(uint pixelValue)
MessageViewer::KXFace::toImage
QImage toImage(const QString &xface)
creates a pixmap from xface
Definition: kxface.cpp:154
QImage::width
int width() const
QString::isEmpty
bool isEmpty() const
QString::startsWith
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
MessageViewer::couldNotFindTheScoreField
Definition: spamheaderanalyzer.h:50
contactdisplaymessagememento.h
QBuffer::open
virtual bool open(QFlags< QIODevice::OpenModeFlag > flags)
QString
QList
MessageViewer::KXFace
Definition: kxface.h:521
MessageViewer::HeaderStyleUtil::subjectDirectionString
QString subjectDirectionString(KMime::Message *message)
Definition: headerstyle_util.cpp:85
MessageViewer::NodeHelper::setBodyPartMemento
void setBodyPartMemento(KMime::Content *node, const QByteArray &which, Interface::BodyPartMemento *memento)
Definition: nodehelper.cpp:612
MessageViewer::HeaderStyleUtil::subjectString
QString subjectString(KMime::Message *message, int flags)
Definition: headerstyle_util.cpp:69
QLatin1Char
MessageViewer::HeaderStyleUtil::imgToDataUrl
QString imgToDataUrl(const QImage &image)
Definition: headerstyle_util.cpp:201
QImage
MessageViewer::NodeHelper::bodyPartMemento
Interface::BodyPartMemento * bodyPartMemento(KMime::Content *node, const QByteArray &which) const
Definition: nodehelper.cpp:599
MessageViewer::HeaderStyleUtil::spamStatus
QString spamStatus(KMime::Message *message)
Definition: headerstyle_util.cpp:95
headerstyle_util.h
QVector::constBegin
const_iterator constBegin() const
QVector
QLatin1String
QByteArray::fromBase64
QByteArray fromBase64(const QByteArray &base64)
MessageViewer::couldNotFindTheThresholdField
Definition: spamheaderanalyzer.h:51
QByteArray::data
char * data()
QString::fromLatin1
QString fromLatin1(const char *str, int size)
MessageViewer::errorExtractingAgentString
Definition: spamheaderanalyzer.h:47
QImage::height
int height() const
MessageViewer::HeaderStyle::sourceObject
QObject * sourceObject() const
Definition: headerstyle.h:127
QByteArray::toBase64
QByteArray toBase64() const
MessageViewer::HeaderStyleUtil::resentFromList
QList< KMime::Types::Mailbox > resentFromList(KMime::Message *message)
Definition: headerstyle_util.cpp:225
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
MessageViewer::HeaderStyleUtil::xfaceSettings::photoURL
QString photoURL
Definition: headerstyle_util.h:68
MessageViewer::HeaderStyleUtil::strToHtml
QString strToHtml(const QString &str, int flags)
Definition: headerstyle_util.cpp:49
QImage::scaled
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const
MessageViewer::couldNotConvertThresholdToFloatOrThresholdIsNegative
Definition: spamheaderanalyzer.h:49
QString::toUtf8
QByteArray toUtf8() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:32:45 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

messageviewer

Skip menu "messageviewer"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdepim API Reference

Skip menu "kdepim API Reference"
  • akonadi_next
  • akregator
  • blogilo
  • calendarsupport
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt2
  • kjots
  • kleopatra
  • kmail
  • knode
  • knotes
  • kontact
  • korgac
  • korganizer
  • ktimetracker
  • libkdepim
  • libkleo
  • libkpgp
  • mailcommon
  • messagelist
  • messageviewer
  • pimprint

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal