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"
21#include "calendarsupport_debug.h"
22
23#include <Akonadi/CalendarUtils>
24#include <Akonadi/ItemFetchJob>
25
26#include <KIO/FileCopyJob>
27#include <KIO/JobUiDelegate>
28#include <KIO/OpenUrlJob>
29#include <KIO/StatJob>
30#include <KJob>
31#include <KJobWidgets>
32#include <KLocalizedString>
33#include <KMessageBox>
34
35#include <QDesktopServices>
36#include <QFile>
37#include <QFileDialog>
38#include <QMimeDatabase>
39#include <QPointer>
40#include <QTemporaryFile>
41
42using namespace KCalendarCore;
43using namespace Akonadi;
44
45namespace CalendarSupport
46{
47struct ReceivedInfo {
48 QString uid;
49 QString attachmentName;
50};
51
52class AttachmentHandlerPrivate
53{
54public:
55 explicit AttachmentHandlerPrivate(QWidget *parent)
56 : mParent(parent)
57 {
58 }
59
60 QMap<KJob *, ReceivedInfo> mJobToReceivedInfo;
61 QPointer<QWidget> const mParent;
62};
63
65 : QObject(parent)
66 , d(new AttachmentHandlerPrivate(parent))
67{
68}
69
70AttachmentHandler::~AttachmentHandler() = default;
71
72Attachment AttachmentHandler::find(const QString &attachmentName, const Incidence::Ptr &incidence)
73{
74 if (!incidence) {
75 return Attachment();
76 }
77
78 // get the attachment by name from the incidence
79 const Attachment::List as = incidence->attachments();
80 Attachment a;
81 if (!as.isEmpty()) {
84
85 for (it = as.constBegin(); it != end; ++it) {
86 if ((*it).label() == attachmentName) {
87 a = *it;
88 break;
89 }
90 }
91 }
92
93 if (a.isEmpty()) {
94 KMessageBox::error(d->mParent, i18n("No attachment named \"%1\" found in the incidence.", attachmentName));
95 return Attachment();
96 }
97
98 if (a.isUri()) {
100
101 KJobWidgets::setWindow(job, d->mParent);
102 if (!job->exec()) {
104 d->mParent,
105 i18n("The attachment \"%1\" is a web link that is inaccessible from this computer. ", QUrl::fromPercentEncoding(a.uri().toLatin1())));
106 return Attachment();
107 }
108 }
109 return a;
110}
111
113{
114 if (!message) {
115 return Attachment();
116 }
117
118 Incidence::Ptr incidence = message->event().dynamicCast<Incidence>();
119 if (!incidence) {
120 KMessageBox::error(d->mParent,
121 i18n("The calendar invitation stored in this email message is broken in some way. "
122 "Unable to continue."));
123 return Attachment();
124 }
125
126 return find(attachmentName, incidence);
127}
128
129static QTemporaryFile *s_tempFile = nullptr;
130
131static QUrl tempFileForAttachment(const Attachment &attachment)
132{
133 QUrl url;
134
135 QMimeDatabase db;
136 QStringList patterns = db.mimeTypeForName(attachment.mimeType()).globPatterns();
137 if (!patterns.empty()) {
138 s_tempFile = new QTemporaryFile(QDir::tempPath() + QLatin1StringView("/attachementview_XXXXXX") + patterns.first().remove(QLatin1Char('*')));
139 } else {
140 s_tempFile = new QTemporaryFile();
141 }
142 s_tempFile->setAutoRemove(false);
143 s_tempFile->open();
144 s_tempFile->setPermissions(QFile::ReadUser);
145 s_tempFile->write(QByteArray::fromBase64(attachment.data()));
146 s_tempFile->close();
147 QFile tf(s_tempFile->fileName());
148 if (tf.size() != attachment.size()) {
149 // whoops. failed to write the entire attachment. return an invalid URL.
150 delete s_tempFile;
151 s_tempFile = nullptr;
152 return url;
153 }
154
155 url.setPath(s_tempFile->fileName());
156 return url;
157}
158
159bool AttachmentHandler::view(const Attachment &attachment)
160{
161 if (attachment.isEmpty()) {
162 return false;
163 }
164
165 bool stat = true;
166 if (attachment.isUri()) {
167 QDesktopServices::openUrl(QUrl(attachment.uri()));
168 } else {
169 // put the attachment in a temporary file and launch it
170 QUrl tempUrl = tempFileForAttachment(attachment);
171 if (tempUrl.isValid()) {
172 auto job = new KIO::OpenUrlJob(tempUrl, attachment.mimeType());
173 job->setDeleteTemporaryFile(true);
174 job->setRunExecutables(true);
175 job->start();
176 } else {
177 stat = false;
178 KMessageBox::error(d->mParent, i18n("Unable to create a temporary file for the attachment."));
179 }
180 delete s_tempFile;
181 s_tempFile = nullptr;
182 }
183 return stat;
184}
185
186bool AttachmentHandler::view(const QString &attachmentName, const Incidence::Ptr &incidence)
187{
188 return view(find(attachmentName, incidence));
189}
190
191void AttachmentHandler::view(const QString &attachmentName, const QString &uid)
192{
193 Item item;
194 item.setGid(uid);
195 auto job = new ItemFetchJob(item);
196 connect(job, &ItemFetchJob::result, this, &AttachmentHandler::slotFinishView);
197 ReceivedInfo info;
198 info.attachmentName = attachmentName;
199 info.uid = uid;
200 d->mJobToReceivedInfo[job] = info;
201}
202
203bool AttachmentHandler::view(const QString &attachmentName, const ScheduleMessage::Ptr &message)
204{
205 return view(find(attachmentName, message));
206}
207
209{
210 // get the saveas file name
211 const QString saveAsFile = QFileDialog::getSaveFileName(d->mParent, i18n("Save Attachment"), attachment.label());
212 if (saveAsFile.isEmpty()) {
213 return false;
214 }
215
216 bool stat = false;
217 if (attachment.isUri()) {
218 // save the attachment url
219 auto job = KIO::file_copy(QUrl(attachment.uri()), QUrl::fromLocalFile(saveAsFile));
220 stat = job->exec();
221 } else {
222 // put the attachment in a temporary file and save it
223 QUrl tempUrl = tempFileForAttachment(attachment);
224 if (tempUrl.isValid()) {
226 stat = job->exec();
227 if (!stat && job->error()) {
228 KMessageBox::error(d->mParent, job->errorString());
229 }
230 } else {
231 stat = false;
232 KMessageBox::error(d->mParent, i18n("Unable to create a temporary file for the attachment."));
233 }
234 delete s_tempFile;
235 s_tempFile = nullptr;
236 }
237 return stat;
238}
239
240bool AttachmentHandler::saveAs(const QString &attachmentName, const Incidence::Ptr &incidence)
241{
242 return saveAs(find(attachmentName, incidence));
243}
244
245void AttachmentHandler::saveAs(const QString &attachmentName, const QString &uid)
246{
247 Item item;
248 item.setGid(uid);
249 auto job = new ItemFetchJob(item);
250 connect(job, &ItemFetchJob::result, this, &AttachmentHandler::slotFinishView);
251
252 ReceivedInfo info;
253 info.attachmentName = attachmentName;
254 info.uid = uid;
255 d->mJobToReceivedInfo[job] = info;
256}
257
258bool AttachmentHandler::saveAs(const QString &attachmentName, const ScheduleMessage::Ptr &message)
259{
260 return saveAs(find(attachmentName, message));
261}
262
263void AttachmentHandler::slotFinishSaveAs(KJob *job)
264{
265 ReceivedInfo info = d->mJobToReceivedInfo[job];
266 bool success = false;
267
268 if (job->error() != 0) {
270 const Item::List items = fetchJob->items();
271 if (!items.isEmpty()) {
273 success = incidence && saveAs(info.attachmentName, incidence);
274 } else {
275 qCWarning(CALENDARSUPPORT_LOG) << Q_FUNC_INFO << "No item found";
276 }
277 } else {
278 qCWarning(CALENDARSUPPORT_LOG) << Q_FUNC_INFO << "Job error:" << job->errorString();
279 }
280
281 Q_EMIT saveAsFinished(info.uid, info.attachmentName, success);
282 d->mJobToReceivedInfo.remove(job);
283}
284
285void AttachmentHandler::slotFinishView(KJob *job)
286{
287 ReceivedInfo info = d->mJobToReceivedInfo[job];
288 bool success = false;
289
290 if (job->error()) {
292 const Item::List items = fetchJob->items();
293 if (!items.isEmpty()) {
295 success = incidence && view(info.attachmentName, incidence);
296 } else {
297 qCWarning(CALENDARSUPPORT_LOG) << Q_FUNC_INFO << "No item found";
298 }
299 } else {
300 qCWarning(CALENDARSUPPORT_LOG) << Q_FUNC_INFO << "Job error:" << job->errorString();
301 }
302
303 Q_EMIT viewFinished(info.uid, info.attachmentName, success);
304 d->mJobToReceivedInfo.remove(job);
305}
306} // namespace CalendarSupport
307
308#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 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)
typedef ConstIterator
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)
QByteArray toLatin1() const const
virtual QString fileName() const const override
void setAutoRemove(bool b)
QUrl fromLocalFile(const QString &localFile)
QString fromPercentEncoding(const QByteArray &input)
void setPath(const QString &path, ParsingMode mode)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:16:31 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.