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"