CalendarSupport

attachmenthandler.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2010 Klarlvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
3 SPDX-FileContributor: Allen Winter <allen.winter@kdab.com>
4
5 SPDX-FileCopyrightText: 2014 Sergio Martins <iamsergio@gmail.com>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10/**
11 @file
12 This file is part of the API for handling calendar data and provides
13 static functions for dealing with calendar incidence attachments.
14
15 @brief
16 vCalendar/iCalendar attachment handling.
17
18 @author Allen Winter <winter@kde.org>
19*/
20#include "attachmenthandler.h"
21using namespace Qt::Literals::StringLiterals;
22
23#include "calendarsupport_debug.h"
24
25#include <Akonadi/CalendarUtils>
26#include <Akonadi/ItemFetchJob>
27
28#include <KIO/FileCopyJob>
29#include <KIO/JobUiDelegate>
30#include <KIO/OpenUrlJob>
31#include <KIO/StatJob>
32#include <KJob>
33#include <KJobWidgets>
34#include <KLocalizedString>
35#include <KMessageBox>
36
37#include <QDesktopServices>
38#include <QFile>
39#include <QFileDialog>
40#include <QMimeDatabase>
41#include <QPointer>
42#include <QTemporaryFile>
43
44using namespace KCalendarCore;
45using namespace Akonadi;
46
47namespace CalendarSupport
48{
49struct ReceivedInfo {
50 QString uid;
51 QString attachmentName;
52};
53
54class AttachmentHandlerPrivate
55{
56public:
57 explicit AttachmentHandlerPrivate(QWidget *parent)
58 : mParent(parent)
59 {
60 }
61
62 QMap<KJob *, ReceivedInfo> mJobToReceivedInfo;
63 QPointer<QWidget> const mParent;
64};
65
67 : QObject(parent)
68 , d(new AttachmentHandlerPrivate(parent))
69{
70}
71
72AttachmentHandler::~AttachmentHandler() = default;
73
74Attachment AttachmentHandler::find(const QString &attachmentName, const Incidence::Ptr &incidence)
75{
76 if (!incidence) {
77 return Attachment();
78 }
79
80 // get the attachment by name from the incidence
81 const Attachment::List as = incidence->attachments();
82 Attachment a;
83 if (!as.isEmpty()) {
86
87 for (it = as.constBegin(); it != end; ++it) {
88 if ((*it).label() == attachmentName) {
89 a = *it;
90 break;
91 }
92 }
93 }
94
95 if (a.isEmpty()) {
96 KMessageBox::error(d->mParent, i18n("No attachment named \"%1\" found in the incidence.", attachmentName));
97 return Attachment();
98 }
99
100 if (a.isUri()) {
101 auto job = KIO::stat(QUrl(a.uri()), KIO::StatJob::SourceSide, KIO::StatBasic);
102
103 KJobWidgets::setWindow(job, d->mParent);
104 if (!job->exec()) {
106 d->mParent,
107 i18n("The attachment \"%1\" is a web link that is inaccessible from this computer. ", QUrl::fromPercentEncoding(a.uri().toLatin1())));
108 return Attachment();
109 }
110 }
111 return a;
112}
113
115{
116 if (!message) {
117 return Attachment();
118 }
119
120 Incidence::Ptr incidence = message->event().dynamicCast<Incidence>();
121 if (!incidence) {
122 KMessageBox::error(d->mParent,
123 i18n("The calendar invitation stored in this email message is broken in some way. "
124 "Unable to continue."));
125 return Attachment();
126 }
127
128 return find(attachmentName, incidence);
129}
130
131static QTemporaryFile *s_tempFile = nullptr;
132
133static QUrl tempFileForAttachment(const Attachment &attachment)
134{
135 QUrl url;
136
137 QMimeDatabase db;
138 QStringList patterns = db.mimeTypeForName(attachment.mimeType()).globPatterns();
139 if (!patterns.empty()) {
140 s_tempFile = new QTemporaryFile(QDir::tempPath() + "/attachementview_XXXXXX"_L1 + patterns.first().remove(QLatin1Char('*')));
141 } else {
142 s_tempFile = new QTemporaryFile();
143 }
144 s_tempFile->setAutoRemove(false);
145 s_tempFile->open();
146 s_tempFile->setPermissions(QFile::ReadUser);
147 s_tempFile->write(QByteArray::fromBase64(attachment.data()));
148 s_tempFile->close();
149 QFile tf(s_tempFile->fileName());
150 if (tf.size() != attachment.size()) {
151 // whoops. failed to write the entire attachment. return an invalid URL.
152 delete s_tempFile;
153 s_tempFile = nullptr;
154 return url;
155 }
156
157 url.setPath(s_tempFile->fileName());
158 return url;
159}
160
161bool AttachmentHandler::view(const Attachment &attachment)
162{
163 if (attachment.isEmpty()) {
164 return false;
165 }
166
167 bool stat = true;
168 if (attachment.isUri()) {
169 QDesktopServices::openUrl(QUrl(attachment.uri()));
170 } else {
171 // put the attachment in a temporary file and launch it
172 QUrl tempUrl = tempFileForAttachment(attachment);
173 if (tempUrl.isValid()) {
174 auto job = new KIO::OpenUrlJob(tempUrl, attachment.mimeType());
175 job->setDeleteTemporaryFile(true);
176 job->setRunExecutables(true);
177 job->start();
178 } else {
179 stat = false;
180 KMessageBox::error(d->mParent, i18n("Unable to create a temporary file for the attachment."));
181 }
182 delete s_tempFile;
183 s_tempFile = nullptr;
184 }
185 return stat;
186}
187
188bool AttachmentHandler::view(const QString &attachmentName, const Incidence::Ptr &incidence)
189{
190 return view(find(attachmentName, incidence));
191}
192
193void AttachmentHandler::view(const QString &attachmentName, const QString &uid)
194{
195 Item item;
196 item.setGid(uid);
197 auto job = new ItemFetchJob(item);
198 connect(job, &ItemFetchJob::result, this, &AttachmentHandler::slotFinishView);
199 ReceivedInfo info;
200 info.attachmentName = attachmentName;
201 info.uid = uid;
202 d->mJobToReceivedInfo[job] = info;
203}
204
205bool AttachmentHandler::view(const QString &attachmentName, const ScheduleMessage::Ptr &message)
206{
207 return view(find(attachmentName, message));
208}
209
211{
212 // get the saveas file name
213 const QString saveAsFile = QFileDialog::getSaveFileName(d->mParent, i18nc("@title:window", "Save Attachment"), attachment.label());
214 if (saveAsFile.isEmpty()) {
215 return false;
216 }
217
218 bool stat = false;
219 if (attachment.isUri()) {
220 // save the attachment url
221 auto job = KIO::file_copy(QUrl(attachment.uri()), QUrl::fromLocalFile(saveAsFile));
222 stat = job->exec();
223 } else {
224 // put the attachment in a temporary file and save it
225 QUrl tempUrl = tempFileForAttachment(attachment);
226 if (tempUrl.isValid()) {
227 auto job = KIO::file_copy(tempUrl, QUrl::fromLocalFile(saveAsFile));
228 stat = job->exec();
229 if (!stat && job->error()) {
230 KMessageBox::error(d->mParent, job->errorString());
231 }
232 } else {
233 stat = false;
234 KMessageBox::error(d->mParent, i18n("Unable to create a temporary file for the attachment."));
235 }
236 delete s_tempFile;
237 s_tempFile = nullptr;
238 }
239 return stat;
240}
241
242bool AttachmentHandler::saveAs(const QString &attachmentName, const Incidence::Ptr &incidence)
243{
244 return saveAs(find(attachmentName, incidence));
245}
246
247void AttachmentHandler::saveAs(const QString &attachmentName, const QString &uid)
248{
249 Item item;
250 item.setGid(uid);
251 auto job = new ItemFetchJob(item);
252 connect(job, &ItemFetchJob::result, this, &AttachmentHandler::slotFinishView);
253
254 ReceivedInfo info;
255 info.attachmentName = attachmentName;
256 info.uid = uid;
257 d->mJobToReceivedInfo[job] = info;
258}
259
260bool AttachmentHandler::saveAs(const QString &attachmentName, const ScheduleMessage::Ptr &message)
261{
262 return saveAs(find(attachmentName, message));
263}
264
265void AttachmentHandler::slotFinishSaveAs(KJob *job)
266{
267 ReceivedInfo info = d->mJobToReceivedInfo[job];
268 bool success = false;
269
270 if (job->error() != 0) {
271 auto fetchJob = qobject_cast<ItemFetchJob *>(job);
272 const Item::List items = fetchJob->items();
273 if (!items.isEmpty()) {
275 success = incidence && saveAs(info.attachmentName, incidence);
276 } else {
277 qCWarning(CALENDARSUPPORT_LOG) << Q_FUNC_INFO << "No item found";
278 }
279 } else {
280 qCWarning(CALENDARSUPPORT_LOG) << Q_FUNC_INFO << "Job error:" << job->errorString();
281 }
282
283 Q_EMIT saveAsFinished(info.uid, info.attachmentName, success);
284 d->mJobToReceivedInfo.remove(job);
285}
286
287void AttachmentHandler::slotFinishView(KJob *job)
288{
289 ReceivedInfo info = d->mJobToReceivedInfo[job];
290 bool success = false;
291
292 if (job->error()) {
293 auto fetchJob = qobject_cast<ItemFetchJob *>(job);
294 const Item::List items = fetchJob->items();
295 if (!items.isEmpty()) {
297 success = incidence && view(info.attachmentName, incidence);
298 } else {
299 qCWarning(CALENDARSUPPORT_LOG) << Q_FUNC_INFO << "No item found";
300 }
301 } else {
302 qCWarning(CALENDARSUPPORT_LOG) << Q_FUNC_INFO << "Job error:" << job->errorString();
303 }
304
305 Q_EMIT viewFinished(info.uid, info.attachmentName, success);
306 d->mJobToReceivedInfo.remove(job);
307}
308} // namespace CalendarSupport
309
310#include "moc_attachmenthandler.cpp"
This file is part of the API for handling calendar data and provides static functions for dealing wit...
void setGid(const QString &gid)
bool saveAs(const KCalendarCore::Attachment &attachment)
Saves the specified attachment to a file of the user's choice.
KCalendarCore::Attachment find(const QString &attachmentName, const KCalendarCore::Incidence::Ptr &incidence)
Finds the attachment in the user's calendar, by attachmentName and incidence.
AttachmentHandler(QWidget *parent)
Constructs an AttachmentHandler.
bool view(const KCalendarCore::Attachment &attachment)
Launches a viewer on the specified attachment.
QString mimeType() const
QByteArray data() const
bool exec()
virtual QString errorString() const
int error() const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT KCalendarCore::Incidence::Ptr incidence(const Akonadi::Item &item)
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
KIOCORE_EXPORT FileCopyJob * file_copy(const QUrl &src, const QUrl &dest, int permissions=-1, JobFlags flags=DefaultFlags)
void setWindow(QObject *job, QWidget *widget)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QByteArray fromBase64(const QByteArray &base64, Base64Options options)
bool openUrl(const QUrl &url)
QString tempPath()
virtual bool setPermissions(Permissions permissions) override
virtual void close() override
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
qint64 write(const QByteArray &data)
const_iterator constBegin() const const
const_iterator constEnd() const const
bool empty() const const
T & first()
bool isEmpty() const const
QMimeType mimeTypeForName(const QString &nameOrAlias) const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
T qobject_cast(QObject *object)
bool isEmpty() const const
QByteArray toLatin1() const const
virtual QString fileName() const const override
void setAutoRemove(bool b)
QUrl fromLocalFile(const QString &localFile)
QString fromPercentEncoding(const QByteArray &input)
bool isValid() const const
void setPath(const QString &path, ParsingMode mode)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:19:27 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.