1 /*
2   SPDX-FileCopyrightText: 2001, 2004 Cornelius Schumacher <schumacher@kde.org>
3   SPDX-FileCopyrightText: 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
4   SPDX-FileCopyrightText: 2012-2013 Sérgio Martins <iamsergio@gmail.com>
5 
6   SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 #include "scheduler_p.h"
9 #include "calendarbase_p.h"
10 
11 #include <KCalUtils/Stringify>
12 
13 #include <KCalendarCore/FreeBusyCache>
14 #include <KCalendarCore/ICalFormat>
15 
16 #include "akonadicalendar_debug.h"
17 #include <KLocalizedString>
18 #include <KMessageBox>
19 #include <QTimeZone>
20 
21 using namespace KCalendarCore;
22 using namespace Akonadi;
23 
24 class Akonadi::SchedulerPrivate
25 {
26 public:
SchedulerPrivate(Scheduler * qq)27     explicit SchedulerPrivate(Scheduler *qq)
28         : q(qq)
29     {
30     }
31 
32     FreeBusyCache *mFreeBusyCache = nullptr;
33     bool mShowDialogs = true;
34     Scheduler *const q;
35 };
36 
Scheduler(QObject * parent)37 Scheduler::Scheduler(QObject *parent)
38     : QObject(parent)
39     , d(new SchedulerPrivate(this))
40 {
41     mFormat = new ICalFormat();
42     mFormat->setTimeZone(QTimeZone::systemTimeZone());
43 }
44 
~Scheduler()45 Scheduler::~Scheduler()
46 {
47     delete mFormat;
48 }
49 
setShowDialogs(bool enable)50 void Scheduler::setShowDialogs(bool enable)
51 {
52     d->mShowDialogs = enable;
53 }
54 
setFreeBusyCache(FreeBusyCache * c)55 void Scheduler::setFreeBusyCache(FreeBusyCache *c)
56 {
57     d->mFreeBusyCache = c;
58 }
59 
freeBusyCache() const60 FreeBusyCache *Scheduler::freeBusyCache() const
61 {
62     return d->mFreeBusyCache;
63 }
64 
acceptTransaction(const IncidenceBase::Ptr & incidence,const Akonadi::CalendarBase::Ptr & calendar,iTIPMethod method,ScheduleMessage::Status status,const QString & email)65 void Scheduler::acceptTransaction(const IncidenceBase::Ptr &incidence,
66                                   const Akonadi::CalendarBase::Ptr &calendar,
67                                   iTIPMethod method,
68                                   ScheduleMessage::Status status,
69                                   const QString &email)
70 {
71     Q_ASSERT(incidence);
72     Q_ASSERT(calendar);
73     qCDebug(AKONADICALENDAR_LOG) << "method=" << ScheduleMessage::methodName(method);
74     connectCalendar(calendar);
75     switch (method) {
76     case iTIPPublish:
77         acceptPublish(incidence, calendar, status, method);
78         break;
79     case iTIPRequest:
80         acceptRequest(incidence, calendar, status, email);
81         break;
82     case iTIPAdd:
83         acceptAdd(incidence, status);
84         break;
85     case iTIPCancel:
86         acceptCancel(incidence, calendar, status, email);
87         break;
88     case iTIPDeclineCounter:
89         acceptDeclineCounter(incidence, status);
90         break;
91     case iTIPReply:
92         acceptReply(incidence, calendar, status, method);
93         break;
94     case iTIPRefresh:
95         acceptRefresh(incidence, status);
96         break;
97     case iTIPCounter:
98         acceptCounter(incidence, status);
99         break;
100     default:
101         qCWarning(AKONADICALENDAR_LOG) << "Unhandled method: " << method;
102     }
103 }
104 
acceptPublish(const IncidenceBase::Ptr & newIncBase,const Akonadi::CalendarBase::Ptr & calendar,ScheduleMessage::Status status,iTIPMethod method)105 void Scheduler::acceptPublish(const IncidenceBase::Ptr &newIncBase,
106                               const Akonadi::CalendarBase::Ptr &calendar,
107                               ScheduleMessage::Status status,
108                               iTIPMethod method)
109 {
110     if (newIncBase->type() == IncidenceBase::TypeFreeBusy) {
111         acceptFreeBusy(newIncBase, method);
112         return;
113     }
114 
115     QString errorString;
116     Result result = ResultSuccess;
117 
118     qCDebug(AKONADICALENDAR_LOG) << "status=" << KCalUtils::Stringify::scheduleMessageStatus(status);
119 
120     Incidence::Ptr newInc = newIncBase.staticCast<Incidence>();
121     Incidence::Ptr calInc = calendar->incidence(newIncBase->uid());
122     switch (status) {
123     case ScheduleMessage::Unknown:
124     case ScheduleMessage::PublishNew:
125     case ScheduleMessage::PublishUpdate:
126         if (calInc && newInc) {
127             if ((newInc->revision() > calInc->revision()) || (newInc->revision() == calInc->revision() && newInc->lastModified() > calInc->lastModified())) {
128                 const QString oldUid = calInc->uid();
129 
130                 if (calInc->type() != newInc->type()) {
131                     result = ResultAssigningDifferentTypes;
132                     errorString = i18n("Error: Assigning different incidence types.");
133                     qCritical() << errorString;
134                 } else {
135                     newInc->setSchedulingID(newInc->uid(), oldUid);
136                     const bool success = calendar->modifyIncidence(newInc);
137 
138                     if (!success) {
139                         Q_EMIT transactionFinished(ResultModifyingError, QStringLiteral("Error modifying incidence"));
140                     } else {
141                         // signal will be emitted in the handleModifyFinished() slot
142                     }
143 
144                     return;
145                 }
146             }
147         }
148         break;
149     case ScheduleMessage::Obsolete:
150         break;
151     default:
152         break;
153     }
154 
155     Q_EMIT transactionFinished(result, errorString);
156 }
157 
acceptRequest(const IncidenceBase::Ptr & incidenceBase,const Akonadi::CalendarBase::Ptr & calendar,ScheduleMessage::Status status,const QString & email)158 void Scheduler::acceptRequest(const IncidenceBase::Ptr &incidenceBase,
159                               const Akonadi::CalendarBase::Ptr &calendar,
160                               ScheduleMessage::Status status,
161                               const QString &email)
162 {
163     Incidence::Ptr incidence = incidenceBase.staticCast<Incidence>();
164 
165     if (incidence->type() == IncidenceBase::TypeFreeBusy) {
166         // reply to this request is handled in korganizer's incomingdialog
167         Q_EMIT transactionFinished(ResultSuccess, QString());
168         return;
169     }
170 
171     const QString schedulingUid = incidence->uid();
172     QString errorString;
173     Result result = ResultSuccess;
174 
175     const Incidence::List existingIncidences = calendar->incidencesFromSchedulingID(schedulingUid);
176     qCDebug(AKONADICALENDAR_LOG) << "status=" << KCalUtils::Stringify::scheduleMessageStatus(status) << ": found " << existingIncidences.count()
177                                  << " incidences with schedulingID " << incidence->schedulingID() << "; uid was = " << schedulingUid;
178 
179     if (existingIncidences.isEmpty()) {
180         // Perfectly normal if the incidence doesn't exist. This is probably
181         // a new invitation.
182         qCDebug(AKONADICALENDAR_LOG) << "incidence not found; calendar = " << calendar.data() << "; incidence count = " << calendar->incidences().count();
183     }
184 
185     for (const KCalendarCore::Incidence::Ptr &existingIncidence : existingIncidences) {
186         qCDebug(AKONADICALENDAR_LOG) << "Considering this found event (" << (existingIncidence->isReadOnly() ? "readonly" : "readwrite")
187                                      << ") :" << mFormat->toString(existingIncidence);
188         // If it's readonly, we can't possible update it.
189         if (existingIncidence->isReadOnly()) {
190             continue;
191         }
192 
193         const QString existingUid = existingIncidence->uid();
194         const int existingRevision = existingIncidence->revision();
195 
196         if (existingRevision <= incidence->revision()) {
197             // The new incidence might be an update for the found one
198             bool isUpdate = true;
199             // Code for new invitations:
200             // If you think we could check the value of "status" to be RequestNew:  we can't.
201             // It comes from a similar check inside libical, where the event is compared to
202             // other events in the calendar. But if we have another version of the event around
203             // (e.g. shared folder for a group), the status could be RequestNew, Obsolete or Updated.
204             qCDebug(AKONADICALENDAR_LOG) << "looking in " << existingUid << "'s attendees";
205             // This is supposed to be a new request, not an update - however we want to update
206             // the existing one to handle the "clicking more than once on the invitation" case.
207             // So check the attendee status of the attendee.
208             const Attendee::List attendees = existingIncidence->attendees();
209             Attendee::List::ConstIterator ait;
210             for (ait = attendees.begin(); ait != attendees.end(); ++ait) {
211                 if ((*ait).email() == email && (*ait).status() == Attendee::NeedsAction) {
212                     // This incidence wasn't created by me - it's probably in a shared folder
213                     // and meant for someone else, ignore it.
214                     qCDebug(AKONADICALENDAR_LOG) << "ignoring " << existingUid << " since I'm still NeedsAction there";
215                     isUpdate = false;
216                     break;
217                 }
218             }
219             if (isUpdate) {
220                 if (existingRevision == incidence->revision() && existingIncidence->lastModified() > incidence->lastModified()) {
221                     // This isn't an update - the found incidence was modified more recently
222                     errorString = i18n(
223                         "This isn't an update. "
224                         "The found incidence was modified more recently.");
225 // QT5 port
226 #if 0
227                     qCWarning(AKONADICALENDAR_LOG) << errorString
228                                                    << "; revision=" << existingIncidence->revision()
229                                                    << "; existing->lastModified=" << existingIncidence->lastModified()
230                                                    << "; update->lastModified=" << incidence->lastModified();
231 #endif
232                     Q_EMIT transactionFinished(ResultOutatedUpdate, errorString);
233                     return;
234                 }
235                 qCDebug(AKONADICALENDAR_LOG) << "replacing existing incidence " << existingUid;
236                 if (existingIncidence->type() != incidence->type()) {
237                     qCritical() << "assigning different incidence types";
238                     result = ResultAssigningDifferentTypes;
239                     errorString = i18n("Error: Assigning different incidence types.");
240                     Q_EMIT transactionFinished(result, errorString);
241                 } else {
242                     incidence->setSchedulingID(schedulingUid, existingUid);
243 
244                     if (incidence->hasRecurrenceId()) {
245                         Incidence::Ptr existingInstance = calendar->incidence(incidence->instanceIdentifier());
246                         if (!existingInstance) {
247                             // The organizer created an exception, lets create it in our calendar, we don't have it yet
248                             const bool success = calendar->addIncidence(incidence);
249 
250                             if (!success) {
251                                 Q_EMIT transactionFinished(ResultCreatingError, QStringLiteral("Error creating incidence"));
252                             } else {
253                                 // Signal emitted in the result slot of addFinished()
254                             }
255 
256                             return;
257                         }
258                     }
259 
260                     const bool success = calendar->modifyIncidence(incidence);
261 
262                     if (!success) {
263                         Q_EMIT transactionFinished(ResultModifyingError, i18n("Error modifying incidence"));
264                     } else {
265                         // handleModifyFinished() will Q_EMIT the final signal.
266                     }
267                 }
268                 return;
269             }
270         } else {
271             errorString = i18n(
272                 "This isn't an update. "
273                 "The found incidence was modified more recently.");
274             qCWarning(AKONADICALENDAR_LOG) << errorString;
275             // This isn't an update - the found incidence has a bigger revision number
276             qCDebug(AKONADICALENDAR_LOG) << "This isn't an update - the found incidence has a bigger revision number";
277             Q_EMIT transactionFinished(ResultOutatedUpdate, errorString);
278             return;
279         }
280     }
281 
282     // Move the uid to be the schedulingID and make a unique UID
283     incidence->setSchedulingID(schedulingUid, CalFormat::createUniqueId());
284     // notify the user in case this is an update and we didn't find the to-be-updated incidence
285     if (d->mShowDialogs && existingIncidences.isEmpty() && incidence->revision() > 0) {
286         KMessageBox::information(nullptr,
287                                  xi18nc("@info",
288                                         "<para>You added an invitation update, but an earlier version of the "
289                                         "item could not be found in your calendar.</para>"
290                                         "<para>This may have occurred because:<list>"
291                                         "<item>the organizer did not include you in the original invitation</item>"
292                                         "<item>you did not accept the original invitation yet</item>"
293                                         "<item>you deleted the original invitation from your calendar</item>"
294                                         "<item>you no longer have access to the calendar containing the invitation</item>"
295                                         "</list></para>"
296                                         "<para>This is not a problem, but we thought you should know.</para>"),
297                                  i18nc("@title", "Cannot find invitation to be updated"),
298                                  QStringLiteral("AcceptCantFindIncidence"));
299     }
300     qCDebug(AKONADICALENDAR_LOG) << "Storing new incidence with scheduling uid=" << schedulingUid << " and uid=" << incidence->uid();
301 
302     const bool success = calendar->addIncidence(incidence);
303     if (!success) {
304         Q_EMIT transactionFinished(ResultCreatingError, i18n("Error adding incidence"));
305     } else {
306         // The slot will Q_EMIT the result
307     }
308 }
309 
acceptAdd(const IncidenceBase::Ptr &,ScheduleMessage::Status)310 void Scheduler::acceptAdd(const IncidenceBase::Ptr &, ScheduleMessage::Status)
311 {
312     Q_EMIT transactionFinished(ResultSuccess, QString());
313 }
314 
acceptCancel(const IncidenceBase::Ptr & incidenceBase,const Akonadi::CalendarBase::Ptr & calendar,ScheduleMessage::Status status,const QString & attendeeEmail)315 void Scheduler::acceptCancel(const IncidenceBase::Ptr &incidenceBase,
316                              const Akonadi::CalendarBase::Ptr &calendar,
317                              ScheduleMessage::Status status,
318                              const QString &attendeeEmail)
319 {
320     Incidence::Ptr incidence = incidenceBase.staticCast<Incidence>();
321 
322     if (incidence->type() == IncidenceBase::TypeFreeBusy) {
323         // reply to this request is handled in korganizer's incomingdialog
324         Q_EMIT transactionFinished(ResultSuccess, QString());
325         return;
326     }
327 
328     if (incidence->type() == IncidenceBase::TypeJournal) {
329         Q_EMIT transactionFinished(ResultUnsupported, QStringLiteral("Unsupported incidence type"));
330         return;
331     }
332 
333     const Incidence::List existingIncidences = calendar->incidencesFromSchedulingID(incidence->uid());
334     qCDebug(AKONADICALENDAR_LOG) << "Scheduler::acceptCancel=" << KCalUtils::Stringify::scheduleMessageStatus(status) << ": found "
335                                  << existingIncidences.count() << " incidences with schedulingID " << incidence->schedulingID();
336 
337     Result result = ResultIncidenceToDeleteNotFound;
338     const QString errorString = i18n("Could not find incidence to delete.");
339     for (const KCalendarCore::Incidence::Ptr &existingIncidence : existingIncidences) {
340         qCDebug(AKONADICALENDAR_LOG) << "Considering this found event (" << (existingIncidence->isReadOnly() ? "readonly" : "readwrite")
341                                      << ") :" << mFormat->toString(existingIncidence);
342 
343         // If it's readonly, we can't possible remove it.
344         if (existingIncidence->isReadOnly()) {
345             continue;
346         }
347 
348         const QString existingUid = existingIncidence->uid();
349 
350         // Code for new invitations:
351         // We cannot check the value of "status" to be RequestNew because
352         // "status" comes from a similar check inside libical, where the event
353         // is compared to other events in the calendar. But if we have another
354         // version of the event around (e.g. shared folder for a group), the
355         // status could be RequestNew, Obsolete or Updated.
356         qCDebug(AKONADICALENDAR_LOG) << "looking in " << existingUid << "'s attendees";
357 
358         // This is supposed to be a new request, not an update - however we want
359         // to update the existing one to handle the "clicking more than once
360         // on the invitation" case. So check the attendee status of the attendee.
361         bool isMine = true;
362         const Attendee::List attendees = existingIncidence->attendees();
363         for (const KCalendarCore::Attendee &attendee : attendees) {
364             if (attendee.email() == attendeeEmail && attendee.status() == Attendee::NeedsAction) {
365                 // This incidence wasn't created by me - it's probably in a shared
366                 // folder and meant for someone else, ignore it.
367                 qCDebug(AKONADICALENDAR_LOG) << "ignoring " << existingUid << " since I'm still NeedsAction there";
368                 isMine = false;
369                 break;
370             }
371         }
372 
373         if (!isMine) {
374             continue;
375         }
376 
377         qCDebug(AKONADICALENDAR_LOG) << "removing existing incidence " << existingUid;
378         if (incidence->hasRecurrenceId()) {
379             Incidence::Ptr existingInstance = calendar->incidence(incidence->instanceIdentifier());
380 
381             if (existingInstance) {
382                 existingInstance->setStatus(Incidence::StatusCanceled);
383                 result = calendar->modifyIncidence(existingInstance) ? ResultSuccess : ResultModifyingError;
384             } else {
385                 incidence->setSchedulingID(incidence->uid(), existingIncidence->uid());
386                 incidence->setStatus(Incidence::StatusCanceled);
387                 result = calendar->addIncidence(incidence) ? ResultSuccess : ResultCreatingError;
388             }
389 
390             if (result != ResultSuccess) {
391                 Q_EMIT transactionFinished(result, i18n("Error recording exception"));
392             }
393         } else {
394             result = calendar->deleteIncidence(existingIncidence) ? ResultSuccess : ResultErrorDelete;
395             if (result != ResultSuccess) {
396                 Q_EMIT transactionFinished(result, errorString);
397             }
398         }
399 
400         // The success case will be handled in handleDeleteFinished()
401         return;
402     }
403 
404     // in case we didn't find the to-be-removed incidencez
405     if (d->mShowDialogs && !existingIncidences.isEmpty() && incidence->revision() > 0) {
406         KMessageBox::error(nullptr,
407                            i18nc("@info",
408                                  "The event or task could not be removed from your calendar. "
409                                  "Maybe it has already been deleted or is not owned by you. "
410                                  "Or it might belong to a read-only or disabled calendar."));
411     }
412     Q_EMIT transactionFinished(result, errorString);
413 }
414 
acceptDeclineCounter(const IncidenceBase::Ptr &,ScheduleMessage::Status)415 void Scheduler::acceptDeclineCounter(const IncidenceBase::Ptr &, ScheduleMessage::Status)
416 {
417     // Not sure why KCalUtils::Scheduler returned false here
418     Q_EMIT transactionFinished(ResultGenericError, i18n("Generic Error"));
419 }
420 
acceptReply(const IncidenceBase::Ptr & incidenceBase,const Akonadi::CalendarBase::Ptr & calendar,ScheduleMessage::Status status,iTIPMethod method)421 void Scheduler::acceptReply(const IncidenceBase::Ptr &incidenceBase,
422                             const Akonadi::CalendarBase::Ptr &calendar,
423                             ScheduleMessage::Status status,
424                             iTIPMethod method)
425 {
426     Q_UNUSED(status)
427     if (incidenceBase->type() == IncidenceBase::TypeFreeBusy) {
428         acceptFreeBusy(incidenceBase, method);
429         return;
430     }
431 
432     Result result = ResultGenericError;
433     QString errorString = i18n("Generic Error");
434 
435     Incidence::Ptr incidence = calendar->incidence(incidenceBase->uid());
436 
437     // try harder to find the correct incidence
438     if (!incidence) {
439         const Incidence::List list = calendar->incidences();
440         for (Incidence::List::ConstIterator it = list.constBegin(), end = list.constEnd(); it != end; ++it) {
441             if ((*it)->schedulingID() == incidenceBase->uid()) {
442                 incidence = (*it).dynamicCast<Incidence>();
443                 break;
444             }
445         }
446     }
447 
448     if (incidence) {
449         // get matching attendee in calendar
450         qCDebug(AKONADICALENDAR_LOG) << "match found!";
451         const Attendee::List attendeesIn = incidenceBase->attendees();
452         Attendee::List attendeesNew;
453         Attendee::List attendeesEv = incidence->attendees();
454         for (const auto &attIn : attendeesIn) {
455             bool found = false;
456             for (auto &attEv : attendeesEv) {
457                 if (attIn.email().toLower() == attEv.email().toLower()) {
458                     // update attendee-info
459                     qCDebug(AKONADICALENDAR_LOG) << "update attendee";
460                     attEv.setStatus(attIn.status());
461                     attEv.setDelegate(attIn.delegate());
462                     attEv.setDelegator(attIn.delegator());
463                     result = ResultSuccess;
464                     errorString.clear();
465                     found = true;
466                 }
467             }
468             if (!found && attIn.status() != Attendee::Declined) {
469                 attendeesNew.append(attIn);
470             }
471         }
472         incidence->setAttendees(attendeesEv);
473 
474         bool attendeeAdded = false;
475         for (const auto &attNew : std::as_const(attendeesNew)) {
476             QString msg = i18nc("@info", "%1 wants to attend %2 but was not invited.", attNew.fullName(), incidence->summary());
477             if (!attNew.delegator().isEmpty()) {
478                 msg = i18nc("@info", "%1 wants to attend %2 on behalf of %3.", attNew.fullName(), incidence->summary(), attNew.delegator());
479             }
480             if (KMessageBox::questionYesNo(nullptr,
481                                            msg,
482                                            i18nc("@title", "Uninvited attendee"),
483                                            KGuiItem(i18nc("@option", "Accept Attendance")),
484                                            KGuiItem(i18nc("@option", "Reject Attendance")))
485                 != KMessageBox::Yes) {
486                 Incidence::Ptr cancel = incidence;
487                 cancel->addComment(i18nc("@info", "The organizer rejected your attendance at this meeting."));
488                 performTransaction(incidenceBase, iTIPCancel, attNew.fullName());
489                 continue;
490             }
491 
492             Attendee a(attNew.name(), attNew.email(), attNew.RSVP(), attNew.status(), attNew.role(), attNew.uid());
493 
494             a.setDelegate(attNew.delegate());
495             a.setDelegator(attNew.delegator());
496             incidence->addAttendee(a);
497 
498             result = ResultSuccess;
499             errorString.clear();
500             attendeeAdded = true;
501         }
502 
503         // send update about new participants
504         if (attendeeAdded) {
505             bool sendMail = false;
506             if (KMessageBox::questionYesNo(nullptr,
507                                            i18nc("@info",
508                                                  "An attendee was added to the incidence. "
509                                                  "Do you want to email the attendees an update message?"),
510                                            i18nc("@title", "Attendee Added"),
511                                            KGuiItem(i18nc("@option", "Send Messages")),
512                                            KGuiItem(i18nc("@option", "Do Not Send")))
513                 == KMessageBox::Yes) {
514                 sendMail = true;
515             }
516 
517             incidence->setRevision(incidence->revision() + 1);
518             if (sendMail) {
519                 performTransaction(incidence, iTIPRequest);
520             }
521         }
522 
523         if (incidence->type() == Incidence::TypeTodo) {
524             // for VTODO a REPLY can be used to update the completion status of
525             // a to-do. see RFC2446 3.4.3
526             Todo::Ptr update = incidenceBase.dynamicCast<Todo>();
527             Todo::Ptr calendarTodo = incidence.staticCast<Todo>();
528             Q_ASSERT(update);
529             if (update && (calendarTodo->percentComplete() != update->percentComplete())) {
530                 calendarTodo->setPercentComplete(update->percentComplete());
531                 calendarTodo->updated();
532                 const bool success = calendar->modifyIncidence(calendarTodo);
533                 if (!success) {
534                     Q_EMIT transactionFinished(ResultModifyingError, i18n("Error modifying incidence"));
535                 } else {
536                     // success will be emitted in the handleModifyFinished() slot
537                 }
538                 return;
539             }
540         }
541 
542         if (result == ResultSuccess) {
543             // We set at least one of the attendees, so the incidence changed
544             // Note: This should not result in a sequence number bump
545             incidence->updated();
546             const bool success = calendar->modifyIncidence(incidence);
547 
548             if (!success) {
549                 Q_EMIT transactionFinished(ResultModifyingError, i18n("Error modifying incidence"));
550             } else {
551                 // success will be emitted in the handleModifyFinished() slot
552             }
553 
554             return;
555         }
556     } else {
557         result = ResultSuccess;
558         errorString = i18n("No incidence for scheduling.");
559         qCritical() << errorString;
560     }
561     Q_EMIT transactionFinished(result, errorString);
562 }
563 
acceptRefresh(const IncidenceBase::Ptr &,ScheduleMessage::Status)564 void Scheduler::acceptRefresh(const IncidenceBase::Ptr &, ScheduleMessage::Status)
565 {
566     // handled in korganizer's IncomingDialog
567     // Not sure why it returns false here
568     Q_EMIT transactionFinished(ResultGenericError, i18n("Generic Error"));
569 }
570 
acceptCounter(const IncidenceBase::Ptr &,ScheduleMessage::Status)571 void Scheduler::acceptCounter(const IncidenceBase::Ptr &, ScheduleMessage::Status)
572 {
573     // Not sure why it returns false here
574     Q_EMIT transactionFinished(ResultGenericError, i18n("Generic Error"));
575 }
576 
acceptFreeBusy(const IncidenceBase::Ptr & incidence,iTIPMethod method)577 void Scheduler::acceptFreeBusy(const IncidenceBase::Ptr &incidence, iTIPMethod method)
578 {
579     if (!d->mFreeBusyCache) {
580         qCritical() << "Scheduler: no FreeBusyCache.";
581         Q_EMIT transactionFinished(ResultNoFreeBusyCache, i18n("No Free Busy Cache"));
582         return;
583     }
584 
585     FreeBusy::Ptr freebusy = incidence.staticCast<FreeBusy>();
586 
587     qCDebug(AKONADICALENDAR_LOG) << "freeBusyDirName:" << freeBusyDir();
588 
589     Person from;
590     if (method == iTIPPublish) {
591         from = freebusy->organizer();
592     }
593     if ((method == iTIPReply) && (freebusy->attendeeCount() == 1)) {
594         Attendee attendee = freebusy->attendees().at(0);
595         from.setName(attendee.name());
596         from.setEmail(attendee.email());
597     }
598 
599     if (!d->mFreeBusyCache->saveFreeBusy(freebusy, from)) {
600         Q_EMIT transactionFinished(ResultErrorSavingFreeBusy, i18n("Error saving freebusy object"));
601     } else {
602         Q_EMIT transactionFinished(ResultNoFreeBusyCache, QString());
603     }
604 }
605 
handleCreateFinished(bool success,const QString & errorMessage)606 void Scheduler::handleCreateFinished(bool success, const QString &errorMessage)
607 {
608     auto calendar = qobject_cast<Akonadi::CalendarBase *>(sender());
609     const bool cancelled = calendar && calendar->d_ptr->mLastCreationCancelled;
610 
611     Q_EMIT transactionFinished(success ? ResultSuccess : (cancelled ? ResultUserCancelled : ResultCreatingError), errorMessage);
612 }
613 
handleModifyFinished(bool success,const QString & errorMessage)614 void Scheduler::handleModifyFinished(bool success, const QString &errorMessage)
615 {
616     qCDebug(AKONADICALENDAR_LOG) << "Modification finished. Success=" << success << errorMessage;
617     Q_EMIT transactionFinished(success ? ResultSuccess : ResultModifyingError, errorMessage);
618 }
619 
handleDeleteFinished(bool success,const QString & errorMessage)620 void Scheduler::handleDeleteFinished(bool success, const QString &errorMessage)
621 {
622     Q_EMIT transactionFinished(success ? ResultSuccess : ResultDeletingError, errorMessage);
623 }
624 
connectCalendar(const Akonadi::CalendarBase::Ptr & calendar)625 void Scheduler::connectCalendar(const Akonadi::CalendarBase::Ptr &calendar)
626 {
627     connect(calendar.data(), &CalendarBase::createFinished, this, &Scheduler::handleCreateFinished, Qt::UniqueConnection);
628     connect(calendar.data(), &CalendarBase::modifyFinished, this, &Scheduler::handleModifyFinished, Qt::UniqueConnection);
629     connect(calendar.data(), &CalendarBase::deleteFinished, this, &Scheduler::handleDeleteFinished, Qt::UniqueConnection);
630 }
631