File indexing completed on 2024-05-12 05:10:49

0001 /*
0002   SPDX-FileCopyrightText: 2001, 2004 Cornelius Schumacher <schumacher@kde.org>
0003   SPDX-FileCopyrightText: 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
0004   SPDX-FileCopyrightText: 2012-2013 Sérgio Martins <iamsergio@gmail.com>
0005 
0006   SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 #include "scheduler_p.h"
0009 #include "calendarbase_p.h"
0010 
0011 #include <KCalUtils/Stringify>
0012 
0013 #include <KCalendarCore/FreeBusyCache>
0014 #include <KCalendarCore/ICalFormat>
0015 
0016 #include "akonadicalendar_debug.h"
0017 #include <KLocalizedString>
0018 #include <KMessageBox>
0019 #include <QTimeZone>
0020 
0021 using namespace KCalendarCore;
0022 using namespace Akonadi;
0023 
0024 class Akonadi::SchedulerPrivate
0025 {
0026 public:
0027     explicit SchedulerPrivate(Scheduler *qq)
0028         : q(qq)
0029     {
0030     }
0031 
0032     FreeBusyCache *mFreeBusyCache = nullptr;
0033     bool mShowDialogs = true;
0034     Scheduler *const q;
0035 };
0036 
0037 Scheduler::Scheduler(QObject *parent)
0038     : QObject(parent)
0039     , d(new SchedulerPrivate(this))
0040 {
0041     mFormat = new ICalFormat();
0042     mFormat->setTimeZone(QTimeZone::systemTimeZone());
0043 }
0044 
0045 Scheduler::~Scheduler()
0046 {
0047     delete mFormat;
0048 }
0049 
0050 void Scheduler::setShowDialogs(bool enable)
0051 {
0052     d->mShowDialogs = enable;
0053 }
0054 
0055 void Scheduler::setFreeBusyCache(FreeBusyCache *c)
0056 {
0057     d->mFreeBusyCache = c;
0058 }
0059 
0060 FreeBusyCache *Scheduler::freeBusyCache() const
0061 {
0062     return d->mFreeBusyCache;
0063 }
0064 
0065 void Scheduler::acceptTransaction(const IncidenceBase::Ptr &incidence,
0066                                   const Akonadi::CalendarBase::Ptr &calendar,
0067                                   iTIPMethod method,
0068                                   ScheduleMessage::Status status,
0069                                   const QString &email)
0070 {
0071     Q_ASSERT(incidence);
0072     Q_ASSERT(calendar);
0073     qCDebug(AKONADICALENDAR_LOG) << "method=" << ScheduleMessage::methodName(method);
0074     connectCalendar(calendar);
0075     switch (method) {
0076     case iTIPPublish:
0077         acceptPublish(incidence, calendar, status, method);
0078         break;
0079     case iTIPRequest:
0080         acceptRequest(incidence, calendar, status, email);
0081         break;
0082     case iTIPAdd:
0083         acceptAdd(incidence, status);
0084         break;
0085     case iTIPCancel:
0086         acceptCancel(incidence, calendar, status, email);
0087         break;
0088     case iTIPDeclineCounter:
0089         acceptDeclineCounter(incidence, status);
0090         break;
0091     case iTIPReply:
0092         acceptReply(incidence, calendar, status, method);
0093         break;
0094     case iTIPRefresh:
0095         acceptRefresh(incidence, status);
0096         break;
0097     case iTIPCounter:
0098         acceptCounter(incidence, status);
0099         break;
0100     default:
0101         qCWarning(AKONADICALENDAR_LOG) << "Unhandled method: " << method;
0102     }
0103 }
0104 
0105 void Scheduler::acceptPublish(const IncidenceBase::Ptr &newIncBase,
0106                               const Akonadi::CalendarBase::Ptr &calendar,
0107                               ScheduleMessage::Status status,
0108                               iTIPMethod method)
0109 {
0110     if (newIncBase->type() == IncidenceBase::TypeFreeBusy) {
0111         acceptFreeBusy(newIncBase, method);
0112         return;
0113     }
0114 
0115     QString errorString;
0116     Result result = ResultSuccess;
0117 
0118     qCDebug(AKONADICALENDAR_LOG) << "status=" << KCalUtils::Stringify::scheduleMessageStatus(status);
0119 
0120     Incidence::Ptr newInc = newIncBase.staticCast<Incidence>();
0121     Incidence::Ptr calInc = calendar->incidence(newIncBase->uid());
0122     switch (status) {
0123     case ScheduleMessage::Unknown:
0124     case ScheduleMessage::PublishNew:
0125     case ScheduleMessage::PublishUpdate:
0126         if (calInc && newInc) {
0127             if ((newInc->revision() > calInc->revision()) || (newInc->revision() == calInc->revision() && newInc->lastModified() > calInc->lastModified())) {
0128                 const QString oldUid = calInc->uid();
0129 
0130                 if (calInc->type() != newInc->type()) {
0131                     result = ResultAssigningDifferentTypes;
0132                     errorString = i18n("Error: Assigning different incidence types.");
0133                     qCritical() << errorString;
0134                 } else {
0135                     newInc->setSchedulingID(newInc->uid(), oldUid);
0136                     const bool success = calendar->modifyIncidence(newInc);
0137 
0138                     if (!success) {
0139                         Q_EMIT transactionFinished(ResultModifyingError, QStringLiteral("Error modifying incidence"));
0140                     } else {
0141                         // signal will be emitted in the handleModifyFinished() slot
0142                     }
0143 
0144                     return;
0145                 }
0146             }
0147         }
0148         break;
0149     case ScheduleMessage::Obsolete:
0150         break;
0151     default:
0152         break;
0153     }
0154 
0155     Q_EMIT transactionFinished(result, errorString);
0156 }
0157 
0158 void Scheduler::acceptRequest(const IncidenceBase::Ptr &incidenceBase,
0159                               const Akonadi::CalendarBase::Ptr &calendar,
0160                               ScheduleMessage::Status status,
0161                               const QString &email)
0162 {
0163     Incidence::Ptr incidence = incidenceBase.staticCast<Incidence>();
0164 
0165     if (incidence->type() == IncidenceBase::TypeFreeBusy) {
0166         // reply to this request is handled in korganizer's incomingdialog
0167         Q_EMIT transactionFinished(ResultSuccess, QString());
0168         return;
0169     }
0170 
0171     const QString schedulingUid = incidence->uid();
0172     QString errorString;
0173     Result result = ResultSuccess;
0174 
0175     const Incidence::List existingIncidences = calendar->incidencesFromSchedulingID(schedulingUid);
0176     qCDebug(AKONADICALENDAR_LOG) << "status=" << KCalUtils::Stringify::scheduleMessageStatus(status) << ": found " << existingIncidences.count()
0177                                  << " incidences with schedulingID " << incidence->schedulingID() << "; uid was = " << schedulingUid;
0178 
0179     if (existingIncidences.isEmpty()) {
0180         // Perfectly normal if the incidence doesn't exist. This is probably
0181         // a new invitation.
0182         qCDebug(AKONADICALENDAR_LOG) << "incidence not found; calendar = " << calendar.data() << "; incidence count = " << calendar->incidences().count();
0183     }
0184 
0185     for (const KCalendarCore::Incidence::Ptr &existingIncidence : existingIncidences) {
0186         qCDebug(AKONADICALENDAR_LOG) << "Considering this found event (" << (existingIncidence->isReadOnly() ? "readonly" : "readwrite")
0187                                      << ") :" << mFormat->toString(existingIncidence);
0188         // If it's readonly, we can't possible update it.
0189         if (existingIncidence->isReadOnly()) {
0190             continue;
0191         }
0192 
0193         const QString existingUid = existingIncidence->uid();
0194         const int existingRevision = existingIncidence->revision();
0195 
0196         if (existingRevision <= incidence->revision()) {
0197             // The new incidence might be an update for the found one
0198             bool isUpdate = true;
0199             // Code for new invitations:
0200             // If you think we could check the value of "status" to be RequestNew:  we can't.
0201             // It comes from a similar check inside libical, where the event is compared to
0202             // other events in the calendar. But if we have another version of the event around
0203             // (e.g. shared folder for a group), the status could be RequestNew, Obsolete or Updated.
0204             qCDebug(AKONADICALENDAR_LOG) << "looking in " << existingUid << "'s attendees";
0205             // This is supposed to be a new request, not an update - however we want to update
0206             // the existing one to handle the "clicking more than once on the invitation" case.
0207             // So check the attendee status of the attendee.
0208             const Attendee::List attendees = existingIncidence->attendees();
0209             Attendee::List::ConstIterator ait;
0210             for (ait = attendees.begin(); ait != attendees.end(); ++ait) {
0211                 if ((*ait).email() == email && (*ait).status() == Attendee::NeedsAction) {
0212                     // This incidence wasn't created by me - it's probably in a shared folder
0213                     // and meant for someone else, ignore it.
0214                     qCDebug(AKONADICALENDAR_LOG) << "ignoring " << existingUid << " since I'm still NeedsAction there";
0215                     isUpdate = false;
0216                     break;
0217                 }
0218             }
0219             if (isUpdate) {
0220                 if (existingRevision == incidence->revision() && existingIncidence->lastModified() > incidence->lastModified()) {
0221                     // This isn't an update - the found incidence was modified more recently
0222                     errorString = i18n(
0223                         "This isn't an update. "
0224                         "The found incidence was modified more recently.");
0225 // QT5 port
0226 #if 0
0227                     qCWarning(AKONADICALENDAR_LOG) << errorString
0228                                                    << "; revision=" << existingIncidence->revision()
0229                                                    << "; existing->lastModified=" << existingIncidence->lastModified()
0230                                                    << "; update->lastModified=" << incidence->lastModified();
0231 #endif
0232                     Q_EMIT transactionFinished(ResultOutatedUpdate, errorString);
0233                     return;
0234                 }
0235                 qCDebug(AKONADICALENDAR_LOG) << "replacing existing incidence " << existingUid;
0236                 if (existingIncidence->type() != incidence->type()) {
0237                     qCritical() << "assigning different incidence types";
0238                     result = ResultAssigningDifferentTypes;
0239                     errorString = i18n("Error: Assigning different incidence types.");
0240                     Q_EMIT transactionFinished(result, errorString);
0241                 } else {
0242                     incidence->setSchedulingID(schedulingUid, existingUid);
0243 
0244                     if (incidence->hasRecurrenceId()) {
0245                         Incidence::Ptr existingInstance = calendar->incidence(incidence->instanceIdentifier());
0246                         if (!existingInstance) {
0247                             // The organizer created an exception, lets create it in our calendar, we don't have it yet
0248                             const bool success = calendar->addIncidence(incidence);
0249 
0250                             if (!success) {
0251                                 Q_EMIT transactionFinished(ResultCreatingError, QStringLiteral("Error creating incidence"));
0252                             } else {
0253                                 // Signal emitted in the result slot of addFinished()
0254                             }
0255 
0256                             return;
0257                         }
0258                     }
0259 
0260                     const bool success = calendar->modifyIncidence(incidence);
0261 
0262                     if (!success) {
0263                         Q_EMIT transactionFinished(ResultModifyingError, i18n("Error modifying incidence"));
0264                     } else {
0265                         // handleModifyFinished() will Q_EMIT the final signal.
0266                     }
0267                 }
0268                 return;
0269             }
0270         } else {
0271             errorString = i18n(
0272                 "This isn't an update. "
0273                 "The found incidence was modified more recently.");
0274             qCWarning(AKONADICALENDAR_LOG) << errorString;
0275             // This isn't an update - the found incidence has a bigger revision number
0276             qCDebug(AKONADICALENDAR_LOG) << "This isn't an update - the found incidence has a bigger revision number";
0277             Q_EMIT transactionFinished(ResultOutatedUpdate, errorString);
0278             return;
0279         }
0280     }
0281 
0282     // Move the uid to be the schedulingID and make a unique UID
0283     incidence->setSchedulingID(schedulingUid, CalFormat::createUniqueId());
0284     // notify the user in case this is an update and we didn't find the to-be-updated incidence
0285     if (d->mShowDialogs && existingIncidences.isEmpty() && incidence->revision() > 0) {
0286         KMessageBox::information(nullptr,
0287                                  xi18nc("@info",
0288                                         "<para>You added an invitation update, but an earlier version of the "
0289                                         "item could not be found in your calendar.</para>"
0290                                         "<para>This may have occurred because:<list>"
0291                                         "<item>the organizer did not include you in the original invitation</item>"
0292                                         "<item>you did not accept the original invitation yet</item>"
0293                                         "<item>you deleted the original invitation from your calendar</item>"
0294                                         "<item>you no longer have access to the calendar containing the invitation</item>"
0295                                         "</list></para>"
0296                                         "<para>This is not a problem, but we thought you should know.</para>"),
0297                                  i18nc("@title:window", "Cannot Find Invitation to be Updated"),
0298                                  QStringLiteral("AcceptCantFindIncidence"));
0299     }
0300     qCDebug(AKONADICALENDAR_LOG) << "Storing new incidence with scheduling uid=" << schedulingUid << " and uid=" << incidence->uid();
0301 
0302     const bool success = calendar->addIncidence(incidence);
0303     if (!success) {
0304         Q_EMIT transactionFinished(ResultCreatingError, i18n("Error adding incidence"));
0305     } else {
0306         // The slot will Q_EMIT the result
0307     }
0308 }
0309 
0310 void Scheduler::acceptAdd(const IncidenceBase::Ptr &, ScheduleMessage::Status)
0311 {
0312     Q_EMIT transactionFinished(ResultSuccess, QString());
0313 }
0314 
0315 void Scheduler::acceptCancel(const IncidenceBase::Ptr &incidenceBase,
0316                              const Akonadi::CalendarBase::Ptr &calendar,
0317                              ScheduleMessage::Status status,
0318                              const QString &attendeeEmail)
0319 {
0320     Incidence::Ptr incidence = incidenceBase.staticCast<Incidence>();
0321 
0322     if (incidence->type() == IncidenceBase::TypeFreeBusy) {
0323         // reply to this request is handled in korganizer's incomingdialog
0324         Q_EMIT transactionFinished(ResultSuccess, QString());
0325         return;
0326     }
0327 
0328     if (incidence->type() == IncidenceBase::TypeJournal) {
0329         Q_EMIT transactionFinished(ResultUnsupported, QStringLiteral("Unsupported incidence type"));
0330         return;
0331     }
0332 
0333     const Incidence::List existingIncidences = calendar->incidencesFromSchedulingID(incidence->uid());
0334     qCDebug(AKONADICALENDAR_LOG) << "Scheduler::acceptCancel=" << KCalUtils::Stringify::scheduleMessageStatus(status) << ": found "
0335                                  << existingIncidences.count() << " incidences with schedulingID " << incidence->schedulingID();
0336 
0337     Result result = ResultIncidenceToDeleteNotFound;
0338     const QString errorString = i18n("Could not find incidence to delete.");
0339     for (const KCalendarCore::Incidence::Ptr &existingIncidence : existingIncidences) {
0340         qCDebug(AKONADICALENDAR_LOG) << "Considering this found event (" << (existingIncidence->isReadOnly() ? "readonly" : "readwrite")
0341                                      << ") :" << mFormat->toString(existingIncidence);
0342 
0343         // If it's readonly, we can't possible remove it.
0344         if (existingIncidence->isReadOnly()) {
0345             continue;
0346         }
0347 
0348         const QString existingUid = existingIncidence->uid();
0349 
0350         // Code for new invitations:
0351         // We cannot check the value of "status" to be RequestNew because
0352         // "status" comes from a similar check inside libical, where the event
0353         // is compared to other events in the calendar. But if we have another
0354         // version of the event around (e.g. shared folder for a group), the
0355         // status could be RequestNew, Obsolete or Updated.
0356         qCDebug(AKONADICALENDAR_LOG) << "looking in " << existingUid << "'s attendees";
0357 
0358         // This is supposed to be a new request, not an update - however we want
0359         // to update the existing one to handle the "clicking more than once
0360         // on the invitation" case. So check the attendee status of the attendee.
0361         bool isMine = true;
0362         const Attendee::List attendees = existingIncidence->attendees();
0363         for (const KCalendarCore::Attendee &attendee : attendees) {
0364             if (attendee.email() == attendeeEmail && attendee.status() == Attendee::NeedsAction) {
0365                 // This incidence wasn't created by me - it's probably in a shared
0366                 // folder and meant for someone else, ignore it.
0367                 qCDebug(AKONADICALENDAR_LOG) << "ignoring " << existingUid << " since I'm still NeedsAction there";
0368                 isMine = false;
0369                 break;
0370             }
0371         }
0372 
0373         if (!isMine) {
0374             continue;
0375         }
0376 
0377         qCDebug(AKONADICALENDAR_LOG) << "removing existing incidence " << existingUid;
0378         if (incidence->hasRecurrenceId()) {
0379             Incidence::Ptr existingInstance = calendar->incidence(incidence->instanceIdentifier());
0380 
0381             if (existingInstance) {
0382                 existingInstance->setStatus(Incidence::StatusCanceled);
0383                 result = calendar->modifyIncidence(existingInstance) ? ResultSuccess : ResultModifyingError;
0384             } else {
0385                 incidence->setSchedulingID(incidence->uid(), existingIncidence->uid());
0386                 incidence->setStatus(Incidence::StatusCanceled);
0387                 result = calendar->addIncidence(incidence) ? ResultSuccess : ResultCreatingError;
0388             }
0389 
0390             if (result != ResultSuccess) {
0391                 Q_EMIT transactionFinished(result, i18n("Error recording exception"));
0392             }
0393         } else {
0394             result = calendar->deleteIncidence(existingIncidence) ? ResultSuccess : ResultErrorDelete;
0395             if (result != ResultSuccess) {
0396                 Q_EMIT transactionFinished(result, errorString);
0397             }
0398         }
0399 
0400         // The success case will be handled in handleDeleteFinished()
0401         return;
0402     }
0403 
0404     // in case we didn't find the to-be-removed incidencez
0405     if (d->mShowDialogs && !existingIncidences.isEmpty() && incidence->revision() > 0) {
0406         KMessageBox::error(nullptr,
0407                            i18nc("@info",
0408                                  "The event or task could not be removed from your calendar. "
0409                                  "Maybe it has already been deleted or is not owned by you. "
0410                                  "Or it might belong to a read-only or disabled calendar."));
0411     }
0412     Q_EMIT transactionFinished(result, errorString);
0413 }
0414 
0415 void Scheduler::acceptDeclineCounter(const IncidenceBase::Ptr &, ScheduleMessage::Status)
0416 {
0417     // Not sure why KCalUtils::Scheduler returned false here
0418     Q_EMIT transactionFinished(ResultGenericError, i18n("Generic Error"));
0419 }
0420 
0421 void Scheduler::acceptReply(const IncidenceBase::Ptr &incidenceBase,
0422                             const Akonadi::CalendarBase::Ptr &calendar,
0423                             ScheduleMessage::Status status,
0424                             iTIPMethod method)
0425 {
0426     Q_UNUSED(status)
0427     if (incidenceBase->type() == IncidenceBase::TypeFreeBusy) {
0428         acceptFreeBusy(incidenceBase, method);
0429         return;
0430     }
0431 
0432     Result result = ResultGenericError;
0433     QString errorString = i18n("Generic Error");
0434 
0435     Incidence::Ptr incidence = calendar->incidence(incidenceBase->uid());
0436 
0437     // try harder to find the correct incidence
0438     if (!incidence) {
0439         const Incidence::List list = calendar->incidences();
0440         for (Incidence::List::ConstIterator it = list.constBegin(), end = list.constEnd(); it != end; ++it) {
0441             if ((*it)->schedulingID() == incidenceBase->uid()) {
0442                 incidence = (*it).dynamicCast<Incidence>();
0443                 break;
0444             }
0445         }
0446     }
0447 
0448     if (incidence) {
0449         // get matching attendee in calendar
0450         qCDebug(AKONADICALENDAR_LOG) << "match found!";
0451         const Attendee::List attendeesIn = incidenceBase->attendees();
0452         Attendee::List attendeesNew;
0453         Attendee::List attendeesEv = incidence->attendees();
0454         for (const auto &attIn : attendeesIn) {
0455             bool found = false;
0456             for (auto &attEv : attendeesEv) {
0457                 if (attIn.email().toLower() == attEv.email().toLower()) {
0458                     // update attendee-info
0459                     qCDebug(AKONADICALENDAR_LOG) << "update attendee";
0460                     attEv.setStatus(attIn.status());
0461                     attEv.setDelegate(attIn.delegate());
0462                     attEv.setDelegator(attIn.delegator());
0463                     result = ResultSuccess;
0464                     errorString.clear();
0465                     found = true;
0466                 }
0467             }
0468             if (!found && attIn.status() != Attendee::Declined) {
0469                 attendeesNew.append(attIn);
0470             }
0471         }
0472         incidence->setAttendees(attendeesEv);
0473 
0474         bool attendeeAdded = false;
0475         for (const auto &attNew : std::as_const(attendeesNew)) {
0476             QString msg = i18nc("@info", "%1 wants to attend %2 but was not invited.", attNew.fullName(), incidence->summary());
0477             if (!attNew.delegator().isEmpty()) {
0478                 msg = i18nc("@info", "%1 wants to attend %2 on behalf of %3.", attNew.fullName(), incidence->summary(), attNew.delegator());
0479             }
0480             if (KMessageBox::questionTwoActions(nullptr,
0481                                                 msg,
0482                                                 i18nc("@title:window", "Uninvited Attendee"),
0483                                                 KGuiItem(i18nc("@option", "Accept Attendance")),
0484                                                 KGuiItem(i18nc("@option", "Reject Attendance")))
0485                 != KMessageBox::ButtonCode::PrimaryAction) {
0486                 Incidence::Ptr cancel = incidence;
0487                 cancel->addComment(i18nc("@info", "The organizer rejected your attendance at this meeting."));
0488                 performTransaction(incidenceBase, iTIPCancel, attNew.fullName());
0489                 continue;
0490             }
0491 
0492             Attendee a(attNew.name(), attNew.email(), attNew.RSVP(), attNew.status(), attNew.role(), attNew.uid());
0493 
0494             a.setDelegate(attNew.delegate());
0495             a.setDelegator(attNew.delegator());
0496             incidence->addAttendee(a);
0497 
0498             result = ResultSuccess;
0499             errorString.clear();
0500             attendeeAdded = true;
0501         }
0502 
0503         // send update about new participants
0504         if (attendeeAdded) {
0505             bool sendMail = false;
0506             if (KMessageBox::questionTwoActions(nullptr,
0507                                                 i18nc("@info",
0508                                                       "An attendee was added to the incidence. "
0509                                                       "Do you want to email the attendees an update message?"),
0510                                                 i18nc("@title:window", "Attendee Added"),
0511                                                 KGuiItem(i18nc("@option", "Send Messages")),
0512                                                 KGuiItem(i18nc("@option", "Do Not Send")))
0513                 == KMessageBox::ButtonCode::PrimaryAction) {
0514                 sendMail = true;
0515             }
0516 
0517             incidence->setRevision(incidence->revision() + 1);
0518             if (sendMail) {
0519                 performTransaction(incidence, iTIPRequest);
0520             }
0521         }
0522 
0523         if (incidence->type() == Incidence::TypeTodo) {
0524             // for VTODO a REPLY can be used to update the completion status of
0525             // a to-do. see RFC2446 3.4.3
0526             Todo::Ptr update = incidenceBase.dynamicCast<Todo>();
0527             Todo::Ptr calendarTodo = incidence.staticCast<Todo>();
0528             Q_ASSERT(update);
0529             if (update && (calendarTodo->percentComplete() != update->percentComplete())) {
0530                 calendarTodo->setPercentComplete(update->percentComplete());
0531                 calendarTodo->updated();
0532                 const bool success = calendar->modifyIncidence(calendarTodo);
0533                 if (!success) {
0534                     Q_EMIT transactionFinished(ResultModifyingError, i18n("Error modifying incidence"));
0535                 } else {
0536                     // success will be emitted in the handleModifyFinished() slot
0537                 }
0538                 return;
0539             }
0540         }
0541 
0542         if (result == ResultSuccess) {
0543             // We set at least one of the attendees, so the incidence changed
0544             // Note: This should not result in a sequence number bump
0545             incidence->updated();
0546             const bool success = calendar->modifyIncidence(incidence);
0547 
0548             if (!success) {
0549                 Q_EMIT transactionFinished(ResultModifyingError, i18n("Error modifying incidence"));
0550             } else {
0551                 // success will be emitted in the handleModifyFinished() slot
0552             }
0553 
0554             return;
0555         }
0556     } else {
0557         result = ResultSuccess;
0558         errorString = i18n("No incidence for scheduling.");
0559         qCritical() << errorString;
0560     }
0561     Q_EMIT transactionFinished(result, errorString);
0562 }
0563 
0564 void Scheduler::acceptRefresh(const IncidenceBase::Ptr &, ScheduleMessage::Status)
0565 {
0566     // handled in korganizer's IncomingDialog
0567     // Not sure why it returns false here
0568     Q_EMIT transactionFinished(ResultGenericError, i18n("Generic Error"));
0569 }
0570 
0571 void Scheduler::acceptCounter(const IncidenceBase::Ptr &, ScheduleMessage::Status)
0572 {
0573     // Not sure why it returns false here
0574     Q_EMIT transactionFinished(ResultGenericError, i18n("Generic Error"));
0575 }
0576 
0577 void Scheduler::acceptFreeBusy(const IncidenceBase::Ptr &incidence, iTIPMethod method)
0578 {
0579     if (!d->mFreeBusyCache) {
0580         qCritical() << "Scheduler: no FreeBusyCache.";
0581         Q_EMIT transactionFinished(ResultNoFreeBusyCache, i18n("No Free Busy Cache"));
0582         return;
0583     }
0584 
0585     FreeBusy::Ptr freebusy = incidence.staticCast<FreeBusy>();
0586 
0587     qCDebug(AKONADICALENDAR_LOG) << "freeBusyDirName:" << freeBusyDir();
0588 
0589     Person from;
0590     if (method == iTIPPublish) {
0591         from = freebusy->organizer();
0592     }
0593     if ((method == iTIPReply) && (freebusy->attendeeCount() == 1)) {
0594         Attendee attendee = freebusy->attendees().at(0);
0595         from.setName(attendee.name());
0596         from.setEmail(attendee.email());
0597     }
0598 
0599     if (!d->mFreeBusyCache->saveFreeBusy(freebusy, from)) {
0600         Q_EMIT transactionFinished(ResultErrorSavingFreeBusy, i18n("Error saving freebusy object"));
0601     } else {
0602         Q_EMIT transactionFinished(ResultNoFreeBusyCache, QString());
0603     }
0604 }
0605 
0606 void Scheduler::handleCreateFinished(bool success, const QString &errorMessage)
0607 {
0608     auto calendar = qobject_cast<Akonadi::CalendarBase *>(sender());
0609     const bool cancelled = calendar && calendar->d_ptr->mLastCreationCancelled;
0610 
0611     Q_EMIT transactionFinished(success ? ResultSuccess : (cancelled ? ResultUserCancelled : ResultCreatingError), errorMessage);
0612 }
0613 
0614 void Scheduler::handleModifyFinished(bool success, const QString &errorMessage)
0615 {
0616     qCDebug(AKONADICALENDAR_LOG) << "Modification finished. Success=" << success << errorMessage;
0617     Q_EMIT transactionFinished(success ? ResultSuccess : ResultModifyingError, errorMessage);
0618 }
0619 
0620 void Scheduler::handleDeleteFinished(bool success, const QString &errorMessage)
0621 {
0622     Q_EMIT transactionFinished(success ? ResultSuccess : ResultDeletingError, errorMessage);
0623 }
0624 
0625 void Scheduler::connectCalendar(const Akonadi::CalendarBase::Ptr &calendar)
0626 {
0627     connect(calendar.data(), &CalendarBase::createFinished, this, &Scheduler::handleCreateFinished, Qt::UniqueConnection);
0628     connect(calendar.data(), &CalendarBase::modifyFinished, this, &Scheduler::handleModifyFinished, Qt::UniqueConnection);
0629     connect(calendar.data(), &CalendarBase::deleteFinished, this, &Scheduler::handleDeleteFinished, Qt::UniqueConnection);
0630 }
0631 
0632 #include "moc_scheduler_p.cpp"