Messagelib

urlhandlermanager.cpp
1 /* -*- c++ -*-
2  urlhandlermanager.cpp
3 
4  This file is part of KMail, the KDE mail client.
5  SPDX-FileCopyrightText: 2003 Marc Mutz <[email protected]>
6  SPDX-FileCopyrightText: 2002-2003, 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, [email protected]
7  SPDX-FileCopyrightText: 2009 Andras Mantia <[email protected]>
8 
9  SPDX-License-Identifier: GPL-2.0-or-later
10 */
11 
12 #include "urlhandlermanager.h"
13 #include "../utils/messageviewerutil_p.h"
14 #include "interfaces/bodyparturlhandler.h"
15 #include "messageviewer/messageviewerutil.h"
16 #include "messageviewer_debug.h"
17 #include "stl_util.h"
18 #include "urlhandlermanager_p.h"
19 #include "utils/mimetype.h"
20 #include "viewer/viewer_p.h"
21 
22 #include <MimeTreeParser/NodeHelper>
23 #include <MimeTreeParser/PartNodeBodyPart>
24 
25 #include <Akonadi/Contact/OpenEmailAddressJob>
26 #include <MessageCore/StringUtil>
27 #include <PimCommon/BroadcastStatus>
28 
29 #include <Akonadi/Contact/ContactSearchJob>
30 
31 #include "messageflags.h"
32 #include <KEmailAddress>
33 #include <KMbox/MBox>
34 #include <KMime/Content>
35 
36 #include <KIconLoader>
37 #include <KLocalizedString>
38 #include <KMessageBox>
39 
40 #include <QApplication>
41 #include <QClipboard>
42 #include <QDrag>
43 #include <QFile>
44 #include <QIcon>
45 #include <QMenu>
46 #include <QMimeData>
47 #include <QMimeDatabase>
48 #include <QProcess>
49 #include <QStandardPaths>
50 #include <QUrl>
51 #include <QUrlQuery>
52 
53 #include <algorithm>
54 
55 #include <Libkleo/MessageBox>
56 #include <chrono>
57 
58 using namespace std::chrono_literals;
59 
60 using std::for_each;
61 using std::remove;
62 using namespace MessageViewer;
63 using namespace MessageCore;
64 
65 URLHandlerManager *URLHandlerManager::self = nullptr;
66 
67 //
68 //
69 // BodyPartURLHandlerManager
70 //
71 //
72 
73 BodyPartURLHandlerManager::~BodyPartURLHandlerManager()
74 {
75  for_each(mHandlers.begin(), mHandlers.end(), [](QVector<const Interface::BodyPartURLHandler *> &handlers) {
76  for_each(handlers.begin(), handlers.end(), DeleteAndSetToZero<Interface::BodyPartURLHandler>());
77  });
78 }
79 
80 void BodyPartURLHandlerManager::registerHandler(const Interface::BodyPartURLHandler *handler, const QString &mimeType)
81 {
82  if (!handler) {
83  return;
84  }
85  unregisterHandler(handler); // don't produce duplicates
86  const auto mt = mimeType.toLatin1();
87  auto it = mHandlers.find(mt);
88  if (it == mHandlers.end()) {
89  it = mHandlers.insert(mt, {});
90  }
91  it->push_back(handler);
92 }
93 
94 void BodyPartURLHandlerManager::unregisterHandler(const Interface::BodyPartURLHandler *handler)
95 {
96  // don't delete them, only remove them from the list!
97  auto it = mHandlers.begin();
98  while (it != mHandlers.end()) {
99  it->erase(remove(it->begin(), it->end(), handler), it->end());
100  if (it->isEmpty()) {
101  it = mHandlers.erase(it);
102  } else {
103  ++it;
104  }
105  }
106 }
107 
108 static KMime::Content *partNodeFromXKMailUrl(const QUrl &url, ViewerPrivate *w, QString *path)
109 {
110  Q_ASSERT(path);
111 
112  if (!w || url.scheme() != QLatin1String("x-kmail")) {
113  return nullptr;
114  }
115  const QString urlPath = url.path();
116 
117  // urlPath format is: /bodypart/<random number>/<part id>/<path>
118 
119  qCDebug(MESSAGEVIEWER_LOG) << "BodyPartURLHandler: urlPath ==" << urlPath;
120  if (!urlPath.startsWith(QLatin1String("/bodypart/"))) {
121  return nullptr;
122  }
123 
124  const QStringList urlParts = urlPath.mid(10).split(QLatin1Char('/'));
125  if (urlParts.size() != 3) {
126  return nullptr;
127  }
128  // KMime::ContentIndex index( urlParts[1] );
129  QByteArray query(urlParts.at(2).toLatin1());
130  if (url.hasQuery()) {
131  query += "?" + url.query().toLatin1();
132  }
133  *path = QUrl::fromPercentEncoding(query);
134  return w->nodeFromUrl(QUrl(urlParts.at(1)));
135 }
136 
137 QVector<const Interface::BodyPartURLHandler *> BodyPartURLHandlerManager::handlersForPart(KMime::Content *node) const
138 {
139  if (auto ct = node->contentType(false)) {
140  auto mimeType = ct->mimeType();
141  if (!mimeType.isEmpty()) {
142  // Bug 390900
143  if (mimeType == "text/x-vcard") {
144  mimeType = "text/vcard";
145  }
146  return mHandlers.value(mimeType);
147  }
148  }
149 
150  return {};
151 }
152 
153 bool BodyPartURLHandlerManager::handleClick(const QUrl &url, ViewerPrivate *w) const
154 {
155  QString path;
156  KMime::Content *node = partNodeFromXKMailUrl(url, w, &path);
157  if (!node) {
158  return false;
159  }
160 
161  MimeTreeParser::PartNodeBodyPart part(nullptr, nullptr, w->message().data(), node, w->nodeHelper());
162 
163  for (const auto &handlers : {handlersForPart(node), mHandlers.value({})}) {
164  for (auto it = handlers.cbegin(), end = handlers.cend(); it != end; ++it) {
165  if ((*it)->handleClick(w->viewer(), &part, path)) {
166  return true;
167  }
168  }
169  }
170 
171  return false;
172 }
173 
174 bool BodyPartURLHandlerManager::handleContextMenuRequest(const QUrl &url, const QPoint &p, ViewerPrivate *w) const
175 {
176  QString path;
177  KMime::Content *node = partNodeFromXKMailUrl(url, w, &path);
178  if (!node) {
179  return false;
180  }
181 
182  MimeTreeParser::PartNodeBodyPart part(nullptr, nullptr, w->message().data(), node, w->nodeHelper());
183 
184  for (const auto &handlers : {handlersForPart(node), mHandlers.value({})}) {
185  for (auto it = handlers.cbegin(), end = handlers.cend(); it != end; ++it) {
186  if ((*it)->handleContextMenuRequest(&part, path, p)) {
187  return true;
188  }
189  }
190  }
191  return false;
192 }
193 
194 QString BodyPartURLHandlerManager::statusBarMessage(const QUrl &url, ViewerPrivate *w) const
195 {
196  QString path;
197  KMime::Content *node = partNodeFromXKMailUrl(url, w, &path);
198  if (!node) {
199  return {};
200  }
201 
202  MimeTreeParser::PartNodeBodyPart part(nullptr, nullptr, w->message().data(), node, w->nodeHelper());
203 
204  for (const auto &handlers : {handlersForPart(node), mHandlers.value({})}) {
205  for (auto it = handlers.cbegin(), end = handlers.cend(); it != end; ++it) {
206  const QString msg = (*it)->statusBarMessage(&part, path);
207  if (!msg.isEmpty()) {
208  return msg;
209  }
210  }
211  }
212  return {};
213 }
214 
215 //
216 //
217 // URLHandlerManager
218 //
219 //
220 
221 URLHandlerManager::URLHandlerManager()
222 {
223  registerHandler(new KMailProtocolURLHandler());
224  registerHandler(new ExpandCollapseQuoteURLManager());
225  registerHandler(new SMimeURLHandler());
226  registerHandler(new MailToURLHandler());
227  registerHandler(new ContactUidURLHandler());
228  registerHandler(new HtmlAnchorHandler());
229  registerHandler(new AttachmentURLHandler());
230  registerHandler(mBodyPartURLHandlerManager = new BodyPartURLHandlerManager());
231  registerHandler(new ShowAuditLogURLHandler());
232  registerHandler(new InternalImageURLHandler);
233  registerHandler(new KRunURLHandler());
234  registerHandler(new EmbeddedImageURLHandler());
235 }
236 
237 URLHandlerManager::~URLHandlerManager()
238 {
239  for_each(mHandlers.begin(), mHandlers.end(), DeleteAndSetToZero<MimeTreeParser::URLHandler>());
240 }
241 
242 URLHandlerManager *URLHandlerManager::instance()
243 {
244  if (!self) {
245  self = new URLHandlerManager();
246  }
247  return self;
248 }
249 
250 void URLHandlerManager::registerHandler(const MimeTreeParser::URLHandler *handler)
251 {
252  if (!handler) {
253  return;
254  }
255  unregisterHandler(handler); // don't produce duplicates
256  mHandlers.push_back(handler);
257 }
258 
259 void URLHandlerManager::unregisterHandler(const MimeTreeParser::URLHandler *handler)
260 {
261  // don't delete them, only remove them from the list!
262  mHandlers.erase(remove(mHandlers.begin(), mHandlers.end(), handler), mHandlers.end());
263 }
264 
265 void URLHandlerManager::registerHandler(const Interface::BodyPartURLHandler *handler, const QString &mimeType)
266 {
267  if (mBodyPartURLHandlerManager) {
268  mBodyPartURLHandlerManager->registerHandler(handler, mimeType);
269  }
270 }
271 
272 void URLHandlerManager::unregisterHandler(const Interface::BodyPartURLHandler *handler)
273 {
274  if (mBodyPartURLHandlerManager) {
275  mBodyPartURLHandlerManager->unregisterHandler(handler);
276  }
277 }
278 
279 bool URLHandlerManager::handleClick(const QUrl &url, ViewerPrivate *w) const
280 {
281  HandlerList::const_iterator end(mHandlers.constEnd());
282  for (HandlerList::const_iterator it = mHandlers.constBegin(); it != end; ++it) {
283  if ((*it)->handleClick(url, w)) {
284  return true;
285  }
286  }
287  return false;
288 }
289 
290 bool URLHandlerManager::handleShiftClick(const QUrl &url, ViewerPrivate *window) const
291 {
292  HandlerList::const_iterator end(mHandlers.constEnd());
293  for (HandlerList::const_iterator it = mHandlers.constBegin(); it != end; ++it) {
294  if ((*it)->handleShiftClick(url, window)) {
295  return true;
296  }
297  }
298  return false;
299 }
300 
301 bool URLHandlerManager::willHandleDrag(const QUrl &url, ViewerPrivate *window) const
302 {
303  HandlerList::const_iterator end(mHandlers.constEnd());
304 
305  for (HandlerList::const_iterator it = mHandlers.constBegin(); it != end; ++it) {
306  if ((*it)->willHandleDrag(url, window)) {
307  return true;
308  }
309  }
310  return false;
311 }
312 
313 bool URLHandlerManager::handleDrag(const QUrl &url, ViewerPrivate *window) const
314 {
315  HandlerList::const_iterator end(mHandlers.constEnd());
316  for (HandlerList::const_iterator it = mHandlers.constBegin(); it != end; ++it) {
317  if ((*it)->handleDrag(url, window)) {
318  return true;
319  }
320  }
321  return false;
322 }
323 
324 bool URLHandlerManager::handleContextMenuRequest(const QUrl &url, const QPoint &p, ViewerPrivate *w) const
325 {
326  HandlerList::const_iterator end(mHandlers.constEnd());
327  for (HandlerList::const_iterator it = mHandlers.constBegin(); it != end; ++it) {
328  if ((*it)->handleContextMenuRequest(url, p, w)) {
329  return true;
330  }
331  }
332  return false;
333 }
334 
335 QString URLHandlerManager::statusBarMessage(const QUrl &url, ViewerPrivate *w) const
336 {
337  HandlerList::const_iterator end(mHandlers.constEnd());
338  for (HandlerList::const_iterator it = mHandlers.constBegin(); it != end; ++it) {
339  const QString msg = (*it)->statusBarMessage(url, w);
340  if (!msg.isEmpty()) {
341  return msg;
342  }
343  }
344  return {};
345 }
346 
347 //
348 //
349 // URLHandler
350 //
351 //
352 
353 bool KMailProtocolURLHandler::handleClick(const QUrl &url, ViewerPrivate *w) const
354 {
355  if (url.scheme() == QLatin1String("kmail")) {
356  if (!w) {
357  return false;
358  }
359  const QString urlPath(url.path());
360  if (urlPath == QLatin1String("showHTML")) {
361  w->setDisplayFormatMessageOverwrite(MessageViewer::Viewer::Html);
362  w->update(MimeTreeParser::Force);
363  return true;
364  } else if (urlPath == QLatin1String("goOnline")) {
365  w->goOnline();
366  return true;
367  } else if (urlPath == QLatin1String("goResourceOnline")) {
368  w->goResourceOnline();
369  return true;
370  } else if (urlPath == QLatin1String("loadExternal")) {
371  w->setHtmlLoadExtOverride(!w->htmlLoadExtOverride());
372  w->update(MimeTreeParser::Force);
373  return true;
374  } else if (urlPath == QLatin1String("decryptMessage")) {
375  w->setDecryptMessageOverwrite(true);
376  w->update(MimeTreeParser::Force);
377  return true;
378  } else if (urlPath == QLatin1String("showSignatureDetails")) {
379  w->setShowSignatureDetails(true);
380  w->update(MimeTreeParser::Force);
381  return true;
382  } else if (urlPath == QLatin1String("hideSignatureDetails")) {
383  w->setShowSignatureDetails(false);
384  w->update(MimeTreeParser::Force);
385  return true;
386  } else if (urlPath == QLatin1String("showEncryptionDetails")) {
387  w->setShowEncryptionDetails(true);
388  w->update(MimeTreeParser::Force);
389  return true;
390  } else if (urlPath == QLatin1String("hideEncryptionDetails")) {
391  w->setShowEncryptionDetails(false);
392  w->update(MimeTreeParser::Force);
393  return true;
394  }
395  }
396  return false;
397 }
398 
399 QString KMailProtocolURLHandler::statusBarMessage(const QUrl &url, ViewerPrivate *) const
400 {
401  const QString schemeStr = url.scheme();
402  if (schemeStr == QLatin1String("kmail")) {
403  const QString urlPath(url.path());
404  if (urlPath == QLatin1String("showHTML")) {
405  return i18n("Turn on HTML rendering for this message.");
406  } else if (urlPath == QLatin1String("loadExternal")) {
407  return i18n("Load external references from the Internet for this message.");
408  } else if (urlPath == QLatin1String("goOnline")) {
409  return i18n("Work online.");
410  } else if (urlPath == QLatin1String("goResourceOnline")) {
411  return i18n("Make account online.");
412  } else if (urlPath == QLatin1String("decryptMessage")) {
413  return i18n("Decrypt message.");
414  } else if (urlPath == QLatin1String("showSignatureDetails")) {
415  return i18n("Show signature details.");
416  } else if (urlPath == QLatin1String("hideSignatureDetails")) {
417  return i18n("Hide signature details.");
418  } else if (urlPath == QLatin1String("showEncryptionDetails")) {
419  return i18n("Show encryption details.");
420  } else if (urlPath == QLatin1String("hideEncryptionDetails")) {
421  return i18n("Hide encryption details.");
422  } else {
423  return {};
424  }
425  } else if (schemeStr == QLatin1String("help")) {
426  return i18n("Open Documentation");
427  }
428  return {};
429 }
430 
431 bool ExpandCollapseQuoteURLManager::handleClick(const QUrl &url, ViewerPrivate *w) const
432 {
433  // kmail:levelquote/?num -> the level quote to collapse.
434  // kmail:levelquote/?-num -> expand all levels quote.
435  if (url.scheme() == QLatin1String("kmail") && url.path() == QLatin1String("levelquote")) {
436  const QString levelStr = url.query();
437  bool isNumber = false;
438  const int levelQuote = levelStr.toInt(&isNumber);
439  if (isNumber) {
440  w->slotLevelQuote(levelQuote);
441  }
442  return true;
443  }
444  return false;
445 }
446 
447 bool ExpandCollapseQuoteURLManager::handleDrag(const QUrl &url, ViewerPrivate *window) const
448 {
449  Q_UNUSED(url)
450  Q_UNUSED(window)
451  return false;
452 }
453 
454 QString ExpandCollapseQuoteURLManager::statusBarMessage(const QUrl &url, ViewerPrivate *) const
455 {
456  if (url.scheme() == QLatin1String("kmail") && url.path() == QLatin1String("levelquote")) {
457  const QString query = url.query();
458  if (query.length() >= 1) {
459  if (query[0] == QLatin1Char('-')) {
460  return i18n("Expand all quoted text.");
461  } else {
462  return i18n("Collapse quoted text.");
463  }
464  }
465  }
466  return {};
467 }
468 
469 bool foundSMIMEData(const QString &aUrl, QString &displayName, QString &libName, QString &keyId)
470 {
471  static QString showCertMan(QStringLiteral("showCertificate#"));
472  displayName.clear();
473  libName.clear();
474  keyId.clear();
475  int i1 = aUrl.indexOf(showCertMan);
476  if (-1 < i1) {
477  i1 += showCertMan.length();
478  int i2 = aUrl.indexOf(QLatin1String(" ### "), i1);
479  if (i1 < i2) {
480  displayName = aUrl.mid(i1, i2 - i1);
481  i1 = i2 + 5;
482  i2 = aUrl.indexOf(QLatin1String(" ### "), i1);
483  if (i1 < i2) {
484  libName = aUrl.mid(i1, i2 - i1);
485  i2 += 5;
486 
487  keyId = aUrl.mid(i2);
488  /*
489  int len = aUrl.length();
490  if( len > i2+1 ) {
491  keyId = aUrl.mid( i2, 2 );
492  i2 += 2;
493  while( len > i2+1 ) {
494  keyId += ':';
495  keyId += aUrl.mid( i2, 2 );
496  i2 += 2;
497  }
498  }
499  */
500  }
501  }
502  }
503  return !keyId.isEmpty();
504 }
505 
506 bool SMimeURLHandler::handleClick(const QUrl &url, ViewerPrivate *w) const
507 {
508  if (!url.hasFragment()) {
509  return false;
510  }
511  QString displayName;
512  QString libName;
513  QString keyId;
514  if (!foundSMIMEData(url.path() + QLatin1Char('#') + QUrl::fromPercentEncoding(url.fragment().toLatin1()), displayName, libName, keyId)) {
515  return false;
516  }
517  QStringList lst;
518  lst << QStringLiteral("--parent-windowid") << QString::number(static_cast<qlonglong>(w->viewer()->mainWindow()->winId())) << QStringLiteral("--query")
519  << keyId;
520  if (!QProcess::startDetached(QStringLiteral("kleopatra"), lst)) {
521  KMessageBox::error(w->mMainWindow,
522  i18n("Could not start certificate manager. "
523  "Please check your installation."),
524  i18n("KMail Error"));
525  }
526  return true;
527 }
528 
529 QString SMimeURLHandler::statusBarMessage(const QUrl &url, ViewerPrivate *) const
530 {
531  QString displayName;
532  QString libName;
533  QString keyId;
534  if (!foundSMIMEData(url.path() + QLatin1Char('#') + QUrl::fromPercentEncoding(url.fragment().toLatin1()), displayName, libName, keyId)) {
535  return {};
536  }
537  return i18n("Show certificate 0x%1", keyId);
538 }
539 
540 bool HtmlAnchorHandler::handleClick(const QUrl &url, ViewerPrivate *w) const
541 {
542  if (!url.host().isEmpty() || !url.hasFragment()) {
543  return false;
544  }
545 
546  w->scrollToAnchor(url.fragment());
547  return true;
548 }
549 
550 QString MailToURLHandler::statusBarMessage(const QUrl &url, ViewerPrivate *) const
551 {
552  if (url.scheme() == QLatin1String("mailto")) {
553  return KEmailAddress::decodeMailtoUrl(url);
554  }
555  return {};
556 }
557 
558 static QString searchFullEmailByUid(const QString &uid)
559 {
560  QString fullEmail;
561  auto job = new Akonadi::ContactSearchJob();
562  job->setLimit(1);
564  job->exec();
565  const KContacts::Addressee::List res = job->contacts();
566  if (!res.isEmpty()) {
567  KContacts::Addressee addr = res.at(0);
568  fullEmail = addr.fullEmail();
569  }
570  return fullEmail;
571 }
572 
573 static void runKAddressBook(const QUrl &url)
574 {
575  auto job = new Akonadi::OpenEmailAddressJob(url.path(), nullptr);
576  job->start();
577 }
578 
579 bool ContactUidURLHandler::handleClick(const QUrl &url, ViewerPrivate *) const
580 {
581  if (url.scheme() == QLatin1String("uid")) {
582  runKAddressBook(url);
583  return true;
584  } else {
585  return false;
586  }
587 }
588 
589 bool ContactUidURLHandler::handleContextMenuRequest(const QUrl &url, const QPoint &p, ViewerPrivate *) const
590 {
591  if (url.scheme() != QLatin1String("uid") || url.path().isEmpty()) {
592  return false;
593  }
594 
595  QMenu menu;
596  QAction *open = menu.addAction(QIcon::fromTheme(QStringLiteral("view-pim-contacts")), i18n("&Open in Address Book"));
597 #ifndef QT_NO_CLIPBOARD
598  QAction *copy = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("&Copy Email Address"));
599 #endif
600 
601  QAction *a = menu.exec(p);
602  if (a == open) {
603  runKAddressBook(url);
604 #ifndef QT_NO_CLIPBOARD
605  } else if (a == copy) {
606  const QString fullEmail = searchFullEmailByUid(url.path());
607  if (!fullEmail.isEmpty()) {
609  clip->setText(fullEmail, QClipboard::Clipboard);
610  clip->setText(fullEmail, QClipboard::Selection);
611  PimCommon::BroadcastStatus::instance()->setStatusMsg(i18n("Address copied to clipboard."));
612  }
613 #endif
614  }
615 
616  return true;
617 }
618 
619 QString ContactUidURLHandler::statusBarMessage(const QUrl &url, ViewerPrivate *) const
620 {
621  if (url.scheme() == QLatin1String("uid")) {
622  return i18n("Lookup the contact in KAddressbook");
623  } else {
624  return {};
625  }
626 }
627 
628 KMime::Content *AttachmentURLHandler::nodeForUrl(const QUrl &url, ViewerPrivate *w) const
629 {
630  if (!w || !w->mMessage) {
631  return nullptr;
632  }
633  if (url.scheme() == QLatin1String("attachment")) {
634  KMime::Content *node = w->nodeFromUrl(url);
635  return node;
636  }
637  return nullptr;
638 }
639 
640 bool AttachmentURLHandler::attachmentIsInHeader(const QUrl &url) const
641 {
642  bool inHeader = false;
643  QUrlQuery query(url);
644  const QString place = query.queryItemValue(QStringLiteral("place")).toLower();
645  if (!place.isNull()) {
646  inHeader = (place == QLatin1String("header"));
647  }
648  return inHeader;
649 }
650 
651 bool AttachmentURLHandler::handleClick(const QUrl &url, ViewerPrivate *w) const
652 {
653  KMime::Content *node = nodeForUrl(url, w);
654  if (!node) {
655  return false;
656  }
657  const bool inHeader = attachmentIsInHeader(url);
658  const bool shouldShowDialog = !w->nodeHelper()->isNodeDisplayedEmbedded(node) || !inHeader;
659  if (inHeader) {
660  w->scrollToAttachment(node);
661  }
662  // if (shouldShowDialog || w->nodeHelper()->isNodeDisplayedHidden(node)) {
663  w->openAttachment(node, w->nodeHelper()->tempFileUrlFromNode(node));
664  //}
665 
666  return true;
667 }
668 
669 bool AttachmentURLHandler::handleShiftClick(const QUrl &url, ViewerPrivate *window) const
670 {
671  KMime::Content *node = nodeForUrl(url, window);
672  if (!node) {
673  return false;
674  }
675  if (!window) {
676  return false;
677  }
678  if (node->contentType()->mimeType() == "text/x-moz-deleted") {
679  return false;
680  }
681 
682  const bool isEncapsulatedMessage = node->parent() && node->parent()->bodyIsMessage();
683  if (isEncapsulatedMessage) {
685  message->setContent(node->parent()->bodyAsMessage()->encodedContent());
686  message->parse();
687  Akonadi::Item item;
691  QUrl newUrl;
692  if (MessageViewer::Util::saveMessageInMboxAndGetUrl(newUrl, Akonadi::Item::List() << item, window->viewer())) {
693  window->viewer()->showOpenAttachmentFolderWidget(QList<QUrl>() << newUrl);
694  }
695  } else {
696  QList<QUrl> urlList;
697  if (Util::saveContents(window->viewer(), KMime::Content::List() << node, urlList)) {
698  window->viewer()->showOpenAttachmentFolderWidget(urlList);
699  }
700  }
701 
702  return true;
703 }
704 
705 bool AttachmentURLHandler::willHandleDrag(const QUrl &url, ViewerPrivate *window) const
706 {
707  return nodeForUrl(url, window) != nullptr;
708 }
709 
710 bool AttachmentURLHandler::handleDrag(const QUrl &url, ViewerPrivate *window) const
711 {
712 #ifndef QT_NO_DRAGANDDROP
713  KMime::Content *node = nodeForUrl(url, window);
714  if (!node) {
715  return false;
716  }
717  if (node->contentType()->mimeType() == "text/x-moz-deleted") {
718  return false;
719  }
720  QString fileName;
721  QUrl tUrl;
722  const bool isEncapsulatedMessage = node->parent() && node->parent()->bodyIsMessage();
723  if (isEncapsulatedMessage) {
725  message->setContent(node->parent()->bodyAsMessage()->encodedContent());
726  message->parse();
727  Akonadi::Item item;
731  fileName = window->nodeHelper()->writeFileToTempFile(node, Util::generateMboxFileName(item));
732 
733  KMBox::MBox mbox;
734  QFile::remove(fileName);
735 
736  if (!mbox.load(fileName)) {
737  qCWarning(MESSAGEVIEWER_LOG) << "MBOX: Impossible to open file";
738  return false;
739  }
741 
742  if (!mbox.save()) {
743  qCWarning(MESSAGEVIEWER_LOG) << "MBOX: Impossible to save file";
744  return false;
745  }
746  tUrl = QUrl::fromLocalFile(fileName);
747  } else {
748  if (node->header<KMime::Headers::Subject>()) {
749  if (!node->contents().isEmpty()) {
750  node = node->contents().constLast();
751  fileName = window->nodeHelper()->writeNodeToTempFile(node);
752  tUrl = QUrl::fromLocalFile(fileName);
753  }
754  }
755  if (fileName.isEmpty()) {
756  tUrl = window->nodeHelper()->tempFileUrlFromNode(node);
757  fileName = tUrl.path();
758  }
759  }
760  if (!fileName.isEmpty()) {
761  QFile f(fileName);
763  const QString icon = Util::iconPathForContent(node, KIconLoader::Small);
764  auto drag = new QDrag(window->viewer());
765  auto mimeData = new QMimeData();
766  mimeData->setUrls(QList<QUrl>() << tUrl);
767  drag->setMimeData(mimeData);
768  if (!icon.isEmpty()) {
769  drag->setPixmap(QIcon::fromTheme(icon).pixmap(16, 16));
770  }
771  drag->exec();
772  return true;
773  } else {
774 #endif
775  return false;
776 }
777 }
778 
779 bool AttachmentURLHandler::handleContextMenuRequest(const QUrl &url, const QPoint &p, ViewerPrivate *w) const
780 {
781  KMime::Content *node = nodeForUrl(url, w);
782  if (!node) {
783  return false;
784  }
785  // PENDING(romain_kdab) : replace with toLocalFile() ?
786  w->showAttachmentPopup(node, w->nodeHelper()->tempFileUrlFromNode(node).path(), p);
787  return true;
788 }
789 
790 QString AttachmentURLHandler::statusBarMessage(const QUrl &url, ViewerPrivate *w) const
791 {
792  KMime::Content *node = nodeForUrl(url, w);
793  if (!node) {
794  return {};
795  }
797  if (!name.isEmpty()) {
798  return i18n("Attachment: %1", name);
799  } else if (dynamic_cast<KMime::Message *>(node)) {
800  if (node->header<KMime::Headers::Subject>()) {
801  return i18n("Encapsulated Message (Subject: %1)", node->header<KMime::Headers::Subject>()->asUnicodeString());
802  } else {
803  return i18n("Encapsulated Message");
804  }
805  }
806  return i18n("Unnamed attachment");
807 }
808 
809 static QString extractAuditLog(const QUrl &url)
810 {
811  if (url.scheme() != QLatin1String("kmail") || url.path() != QLatin1String("showAuditLog")) {
812  return {};
813  }
814  QUrlQuery query(url);
815  Q_ASSERT(!query.queryItemValue(QStringLiteral("log")).isEmpty());
816  return query.queryItemValue(QStringLiteral("log"));
817 }
818 
819 bool ShowAuditLogURLHandler::handleClick(const QUrl &url, ViewerPrivate *w) const
820 {
821  const QString auditLog = extractAuditLog(url);
822  if (auditLog.isEmpty()) {
823  return false;
824  }
825  Kleo::MessageBox::auditLog(w->mMainWindow, auditLog);
826  return true;
827 }
828 
829 bool ShowAuditLogURLHandler::handleContextMenuRequest(const QUrl &url, const QPoint &, ViewerPrivate *w) const
830 {
831  Q_UNUSED(w)
832  // disable RMB for my own links:
833  return !extractAuditLog(url).isEmpty();
834 }
835 
836 QString ShowAuditLogURLHandler::statusBarMessage(const QUrl &url, ViewerPrivate *) const
837 {
838  if (extractAuditLog(url).isEmpty()) {
839  return {};
840  } else {
841  return i18n("Show GnuPG Audit Log for this operation");
842  }
843 }
844 
845 bool ShowAuditLogURLHandler::handleDrag(const QUrl &url, ViewerPrivate *window) const
846 {
847  Q_UNUSED(url)
848  Q_UNUSED(window)
849  return true;
850 }
851 
852 bool InternalImageURLHandler::handleDrag(const QUrl &url, ViewerPrivate *window) const
853 {
854  Q_UNUSED(window)
855  Q_UNUSED(url)
856 
857  // This will only be called when willHandleDrag() was true. Return false here, that will
858  // notify ViewerPrivate::eventFilter() that no drag was started.
859  return false;
860 }
861 
862 bool InternalImageURLHandler::willHandleDrag(const QUrl &url, ViewerPrivate *window) const
863 {
864  Q_UNUSED(window)
865  if (url.scheme() == QLatin1String("data") && url.path().startsWith(QLatin1String("image"))) {
866  return true;
867  }
868 
869  const QString imagePath =
871  return url.path().contains(imagePath);
872 }
873 
874 bool KRunURLHandler::handleClick(const QUrl &url, ViewerPrivate *w) const
875 {
876  const QString scheme(url.scheme());
877  if ((scheme == QLatin1String("http")) || (scheme == QLatin1String("https")) || (scheme == QLatin1String("ftp")) || (scheme == QLatin1String("file"))
878  || (scheme == QLatin1String("ftps")) || (scheme == QLatin1String("sftp")) || (scheme == QLatin1String("help")) || (scheme == QLatin1String("vnc"))
879  || (scheme == QLatin1String("smb")) || (scheme == QLatin1String("fish")) || (scheme == QLatin1String("news")) || (scheme == QLatin1String("tel"))) {
880  PimCommon::BroadcastStatus::instance()->setTransientStatusMsg(i18n("Opening URL..."));
881  QTimer::singleShot(2s, PimCommon::BroadcastStatus::instance(), &PimCommon::BroadcastStatus::reset);
882 
883  QMimeDatabase mimeDb;
884  auto mime = mimeDb.mimeTypeForUrl(url);
885  if (mime.name() == QLatin1String("application/x-desktop") || mime.name() == QLatin1String("application/x-executable")
886  || mime.name() == QLatin1String("application/x-ms-dos-executable") || mime.name() == QLatin1String("application/x-shellscript")) {
887  if (KMessageBox::warningYesNo(nullptr,
888  xi18nc("@info", "Do you really want to execute <filename>%1</filename>?", url.toDisplayString(QUrl::PreferLocalFile)),
889  QString(),
890  KGuiItem(i18n("Execute")),
892  != KMessageBox::Yes) {
893  return true;
894  }
895  }
896  w->checkPhishingUrl();
897  return true;
898  } else {
899  return false;
900  }
901 }
902 
903 bool EmbeddedImageURLHandler::handleDrag(const QUrl &url, ViewerPrivate *window) const
904 {
905  Q_UNUSED(url)
906  Q_UNUSED(window)
907  return false;
908 }
909 
910 bool EmbeddedImageURLHandler::willHandleDrag(const QUrl &url, ViewerPrivate *window) const
911 {
912  Q_UNUSED(window)
913  return url.scheme() == QLatin1String("cid");
914 }
PreferLocalFile
KCOREADDONS_EXPORT void message(KMessage::MessageType messageType, const QString &text, const QString &caption=QString())
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
bool save(const QString &fileName=QString())
QString toDisplayString(QUrl::FormattingOptions options) const const
void setMimeData(QMimeData *data)
std::optional< QSqlQuery > query(const QString &queryStatement)
QString xi18nc(const char *context, const char *text, const TYPE &arg...)
bool remove()
bool startDetached(qint64 *pid)
const T & at(int i) const const
QString host(QUrl::ComponentFormattingOptions options) const const
T * header(bool create=false)
QByteArray & insert(int i, char ch)
QByteArray mimeType() const
const QLatin1String name
KGuiItem cancel()
void setPayload(const T &p)
QAction * addAction(const QString &text)
T payload() const
int size() const const
bool isNull() const const
an implementation of the BodyPart interface using KMime::Content&#39;s
void clear()
MBoxEntry appendMessage(const KMime::Message::Ptr &message)
QString fullEmail(const QString &email=QString()) const
KIOCORE_EXPORT CopyJob * copy(const QUrl &src, const QUrl &dest, JobFlags flags=DefaultFlags)
QString number(int n, int base)
QVector< Content * > contents() const
void setMimeType(const QString &mimeType)
QMimeType mimeTypeForUrl(const QUrl &url) const const
int toInt(bool *ok, int base) const const
bool isEmpty() const const
QString fragment(QUrl::ComponentFormattingOptions options) const const
An interface to body part reader link handlers.
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Notify)
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString path(QUrl::ComponentFormattingOptions options) const const
Singleton to manage the list of URLHandlers.
QString fromPercentEncoding(const QByteArray &input)
bool bodyIsMessage() const
QAction * exec()
QString scheme() const const
Headers::ContentType * contentType(bool create=true)
void push_back(char ch)
QString toLower() const const
QString query(QUrl::ComponentFormattingOptions options) const const
QString i18n(const char *text, const TYPE &arg...)
const QList< QKeySequence > & end()
QByteArray toLatin1() const const
QString mid(int position, int n) const const
AddresseeList List
static QString fileName(const KMime::Content *node)
Returns a usable filename for a node, that can be the filename from the content disposition header...
QSharedPointer< Message > bodyAsMessage() const
static QString mimeType()
int length() const const
An interface to reader link handlers.
Definition: urlhandler.h:29
AKONADI_MIME_EXPORT void copyMessageFlags(KMime::Message &from, Akonadi::Item &to)
Content * parent() const
QIcon fromTheme(const QString &name)
void setText(const QString &text, QClipboard::Mode mode)
KCODECS_EXPORT QString decodeMailtoUrl(const QUrl &mailtoUrl)
bool hasFragment() const const
KIOCORE_EXPORT FileJob * open(const QUrl &url, QIODevice::OpenMode mode)
bool hasQuery() const const
QClipboard * clipboard()
QString asUnicodeString() const override
QVector< KMime::Content * > List
QUrl fromLocalFile(const QString &localFile)
QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
ButtonCode warningYesNo(QWidget *parent, const QString &text, const QString &caption=QString(), const KGuiItem &buttonYes=KStandardGuiItem::yes(), const KGuiItem &buttonNo=KStandardGuiItem::no(), const QString &dontAskAgainName=QString(), Options options=Options(Notify|Dangerous))
bool load(const QString &fileName)
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun Dec 5 2021 23:04:55 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.