KCal Library
scheduler.cpp
00001 /* 00002 This file is part of the kcal library. 00003 00004 Copyright (c) 2001,2004 Cornelius Schumacher <schumacher@kde.org> 00005 Copyright (C) 2004 Reinhold Kainhofer <reinhold@kainhofer.com> 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License as published by the Free Software Foundation; either 00010 version 2 of the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Library General Public License for more details. 00016 00017 You should have received a copy of the GNU Library General Public License 00018 along with this library; see the file COPYING.LIB. If not, write to 00019 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00020 Boston, MA 02110-1301, USA. 00021 */ 00022 00023 #include "scheduler.h" 00024 #include "calendar.h" 00025 #ifndef KDEPIM_NO_KRESOURCES 00026 #include "calendarresources.h" 00027 #endif 00028 #include "event.h" 00029 #include "todo.h" 00030 #include "freebusy.h" 00031 #include "freebusycache.h" 00032 #include "icalformat.h" 00033 #include "assignmentvisitor.h" 00034 00035 #include <klocale.h> 00036 #include <kdebug.h> 00037 #include <kmessagebox.h> 00038 #include <kstandarddirs.h> 00039 00040 using namespace KCal; 00041 00042 //@cond PRIVATE 00043 class KCal::ScheduleMessage::Private 00044 { 00045 public: 00046 Private() {} 00047 00048 IncidenceBase *mIncidence; 00049 iTIPMethod mMethod; 00050 Status mStatus; 00051 QString mError; 00052 }; 00053 //@endcond 00054 00055 ScheduleMessage::ScheduleMessage( IncidenceBase *incidence, 00056 iTIPMethod method, 00057 ScheduleMessage::Status status ) 00058 : d( new KCal::ScheduleMessage::Private ) 00059 { 00060 d->mIncidence = incidence; 00061 d->mMethod = method; 00062 d->mStatus = status; 00063 } 00064 00065 ScheduleMessage::~ScheduleMessage() 00066 { 00067 delete d; 00068 } 00069 00070 IncidenceBase *ScheduleMessage::event() 00071 { 00072 return d->mIncidence; 00073 } 00074 00075 iTIPMethod ScheduleMessage::method() 00076 { 00077 return d->mMethod; 00078 } 00079 00080 ScheduleMessage::Status ScheduleMessage::status() 00081 { 00082 return d->mStatus; 00083 } 00084 00085 QString ScheduleMessage::statusName( ScheduleMessage::Status status ) 00086 { 00087 switch( status ) { 00088 case PublishNew: 00089 return i18nc( "@item new message posting", "New Message Publish" ); 00090 case PublishUpdate: 00091 return i18nc( "@item updated message", "Updated Message Published" ); 00092 case Obsolete: 00093 return i18nc( "@item obsolete status", "Obsolete" ); 00094 case RequestNew: 00095 return i18nc( "@item request new message posting", "Request New Message" ); 00096 case RequestUpdate: 00097 return i18nc( "@item request updated posting", "Request Updated Message" ); 00098 default: 00099 return i18nc( "@item unknown status", "Unknown Status: %1", int( status ) ); 00100 } 00101 } 00102 00103 QString ScheduleMessage::error() 00104 { 00105 return d->mError; 00106 } 00107 00108 //@cond PRIVATE 00109 struct KCal::Scheduler::Private 00110 { 00111 Private() 00112 : mFreeBusyCache( 0 ) 00113 { 00114 } 00115 FreeBusyCache *mFreeBusyCache; 00116 }; 00117 //@endcond 00118 00119 Scheduler::Scheduler( Calendar *calendar ) : d( new KCal::Scheduler::Private ) 00120 { 00121 mCalendar = calendar; 00122 mFormat = new ICalFormat(); 00123 mFormat->setTimeSpec( calendar->timeSpec() ); 00124 } 00125 00126 Scheduler::~Scheduler() 00127 { 00128 delete mFormat; 00129 delete d; 00130 } 00131 00132 void Scheduler::setFreeBusyCache( FreeBusyCache *c ) 00133 { 00134 d->mFreeBusyCache = c; 00135 } 00136 00137 FreeBusyCache *Scheduler::freeBusyCache() const 00138 { 00139 return d->mFreeBusyCache; 00140 } 00141 00142 bool Scheduler::acceptTransaction( IncidenceBase *incidence, 00143 iTIPMethod method, 00144 ScheduleMessage::Status status ) 00145 { 00146 return acceptTransaction( incidence, method, status, QString() ); 00147 } 00148 00149 bool Scheduler::acceptTransaction( IncidenceBase *incidence, 00150 iTIPMethod method, 00151 ScheduleMessage::Status status, 00152 const QString &email ) 00153 { 00154 kDebug() << "method=" << methodName( method ); 00155 00156 switch ( method ) { 00157 case iTIPPublish: 00158 return acceptPublish( incidence, status, method ); 00159 case iTIPRequest: 00160 return acceptRequest( incidence, status, email ); 00161 case iTIPAdd: 00162 return acceptAdd( incidence, status ); 00163 case iTIPCancel: 00164 return acceptCancel( incidence, status, email ); 00165 case iTIPDeclineCounter: 00166 return acceptDeclineCounter( incidence, status ); 00167 case iTIPReply: 00168 return acceptReply( incidence, status, method ); 00169 case iTIPRefresh: 00170 return acceptRefresh( incidence, status ); 00171 case iTIPCounter: 00172 return acceptCounter( incidence, status ); 00173 default: 00174 break; 00175 } 00176 deleteTransaction( incidence ); 00177 return false; 00178 } 00179 00180 QString Scheduler::methodName( iTIPMethod method ) 00181 { 00182 switch ( method ) { 00183 case iTIPPublish: 00184 return QLatin1String( "Publish" ); 00185 case iTIPRequest: 00186 return QLatin1String( "Request" ); 00187 case iTIPRefresh: 00188 return QLatin1String( "Refresh" ); 00189 case iTIPCancel: 00190 return QLatin1String( "Cancel" ); 00191 case iTIPAdd: 00192 return QLatin1String( "Add" ); 00193 case iTIPReply: 00194 return QLatin1String( "Reply" ); 00195 case iTIPCounter: 00196 return QLatin1String( "Counter" ); 00197 case iTIPDeclineCounter: 00198 return QLatin1String( "Decline Counter" ); 00199 default: 00200 return QLatin1String( "Unknown" ); 00201 } 00202 } 00203 00204 QString Scheduler::translatedMethodName( iTIPMethod method ) 00205 { 00206 switch ( method ) { 00207 case iTIPPublish: 00208 return i18nc( "@item event, to-do, journal or freebusy posting", "Publish" ); 00209 case iTIPRequest: 00210 return i18nc( "@item event, to-do or freebusy scheduling requests", "Request" ); 00211 case iTIPReply: 00212 return i18nc( "@item event, to-do or freebusy reply to request", "Reply" ); 00213 case iTIPAdd: 00214 return i18nc( 00215 "@item event, to-do or journal additional property request", "Add" ); 00216 case iTIPCancel: 00217 return i18nc( "@item event, to-do or journal cancellation notice", "Cancel" ); 00218 case iTIPRefresh: 00219 return i18nc( "@item event or to-do description update request", "Refresh" ); 00220 case iTIPCounter: 00221 return i18nc( "@item event or to-do submit counter proposal", "Counter" ); 00222 case iTIPDeclineCounter: 00223 return i18nc( "@item event or to-do decline a counter proposal", "Decline Counter" ); 00224 default: 00225 return i18nc( "@item no method", "Unknown" ); 00226 } 00227 } 00228 00229 bool Scheduler::deleteTransaction( IncidenceBase * ) 00230 { 00231 return true; 00232 } 00233 00234 bool Scheduler::acceptPublish( IncidenceBase *newIncBase, 00235 ScheduleMessage::Status status, 00236 iTIPMethod method ) 00237 { 00238 if( newIncBase->type() == "FreeBusy" ) { 00239 return acceptFreeBusy( newIncBase, method ); 00240 } 00241 00242 bool res = false; 00243 00244 kDebug() << "status=" << ScheduleMessage::statusName( status ); 00245 00246 Incidence *newInc = static_cast<Incidence *>( newIncBase ); 00247 Incidence *calInc = mCalendar->incidence( newIncBase->uid() ); 00248 switch ( status ) { 00249 case ScheduleMessage::Unknown: 00250 case ScheduleMessage::PublishNew: 00251 case ScheduleMessage::PublishUpdate: 00252 if ( calInc && newInc ) { 00253 if ( ( newInc->revision() > calInc->revision() ) || 00254 ( newInc->revision() == calInc->revision() && 00255 newInc->lastModified() > calInc->lastModified() ) ) { 00256 AssignmentVisitor visitor; 00257 const QString oldUid = calInc->uid(); 00258 if ( !visitor.assign( calInc, newInc ) ) { 00259 kError() << "assigning different incidence types"; 00260 } else { 00261 calInc->setSchedulingID( newInc->uid() ); 00262 calInc->setUid( oldUid ); 00263 res = true; 00264 } 00265 } 00266 } 00267 break; 00268 case ScheduleMessage::Obsolete: 00269 res = true; 00270 break; 00271 default: 00272 break; 00273 } 00274 deleteTransaction( newIncBase ); 00275 return res; 00276 } 00277 00278 bool Scheduler::acceptRequest( IncidenceBase *incidence, 00279 ScheduleMessage::Status status ) 00280 { 00281 return acceptRequest( incidence, status, QString() ); 00282 } 00283 00284 bool Scheduler::acceptRequest( IncidenceBase *incidence, 00285 ScheduleMessage::Status status, 00286 const QString &email ) 00287 { 00288 Incidence *inc = static_cast<Incidence *>( incidence ); 00289 if ( !inc ) { 00290 return false; 00291 } 00292 if ( inc->type() == "FreeBusy" ) { 00293 // reply to this request is handled in korganizer's incomingdialog 00294 return true; 00295 } 00296 00297 const Incidence::List existingIncidences = mCalendar->incidencesFromSchedulingID( inc->uid() ); 00298 kDebug() << "status=" << ScheduleMessage::statusName( status ) 00299 << ": found " << existingIncidences.count() 00300 << " incidences with schedulingID " << inc->schedulingID(); 00301 Incidence::List::ConstIterator incit = existingIncidences.begin(); 00302 for ( ; incit != existingIncidences.end() ; ++incit ) { 00303 Incidence *i = *incit; 00304 kDebug() << "Considering this found event (" 00305 << ( i->isReadOnly() ? "readonly" : "readwrite" ) 00306 << ") :" << mFormat->toString( i ); 00307 // If it's readonly, we can't possible update it. 00308 if ( i->isReadOnly() ) { 00309 continue; 00310 } 00311 if ( i->revision() <= inc->revision() ) { 00312 // The new incidence might be an update for the found one 00313 bool isUpdate = true; 00314 // Code for new invitations: 00315 // If you think we could check the value of "status" to be RequestNew: we can't. 00316 // It comes from a similar check inside libical, where the event is compared to 00317 // other events in the calendar. But if we have another version of the event around 00318 // (e.g. shared folder for a group), the status could be RequestNew, Obsolete or Updated. 00319 kDebug() << "looking in " << i->uid() << "'s attendees"; 00320 // This is supposed to be a new request, not an update - however we want to update 00321 // the existing one to handle the "clicking more than once on the invitation" case. 00322 // So check the attendee status of the attendee. 00323 const KCal::Attendee::List attendees = i->attendees(); 00324 KCal::Attendee::List::ConstIterator ait; 00325 for ( ait = attendees.begin(); ait != attendees.end(); ++ait ) { 00326 if( (*ait)->email() == email && (*ait)->status() == Attendee::NeedsAction ) { 00327 // This incidence wasn't created by me - it's probably in a shared folder 00328 // and meant for someone else, ignore it. 00329 kDebug() << "ignoring " << i->uid() << " since I'm still NeedsAction there"; 00330 isUpdate = false; 00331 break; 00332 } 00333 } 00334 if ( isUpdate ) { 00335 if ( i->revision() == inc->revision() && 00336 i->lastModified() > inc->lastModified() ) { 00337 // This isn't an update - the found incidence was modified more recently 00338 kDebug() << "This isn't an update - the found incidence was modified more recently"; 00339 deleteTransaction( i ); 00340 return false; 00341 } 00342 kDebug() << "replacing existing incidence " << i->uid(); 00343 bool res = true; 00344 AssignmentVisitor visitor; 00345 const QString oldUid = i->uid(); 00346 if ( !visitor.assign( i, inc ) ) { 00347 kError() << "assigning different incidence types"; 00348 res = false; 00349 } else { 00350 i->setUid( oldUid ); 00351 i->setSchedulingID( inc->uid() ); 00352 } 00353 deleteTransaction( incidence ); 00354 return res; 00355 } 00356 } else { 00357 // This isn't an update - the found incidence has a bigger revision number 00358 kDebug() << "This isn't an update - the found incidence has a bigger revision number"; 00359 deleteTransaction( incidence ); 00360 return false; 00361 } 00362 } 00363 00364 // Move the uid to be the schedulingID and make a unique UID 00365 inc->setSchedulingID( inc->uid() ); 00366 inc->setUid( CalFormat::createUniqueId() ); 00367 // in case this is an update and we didn't find the to-be-updated incidence, 00368 // ask whether we should create a new one, or drop the update 00369 if ( existingIncidences.count() > 0 || inc->revision() == 0 || 00370 KMessageBox::questionYesNo( 00371 0, 00372 i18nc( "@info", 00373 "The event, to-do or journal to be updated could not be found. " 00374 "Maybe it has already been deleted, or the calendar that " 00375 "contains it is disabled. Press 'Store' to create a new " 00376 "one or 'Throw away' to discard this update." ), 00377 i18nc( "@title", "Discard this update?" ), 00378 KGuiItem( i18nc( "@option", "Store" ) ), 00379 KGuiItem( i18nc( "@option", "Throw away" ) ), 00380 "AcceptCantFindIncidence" ) == KMessageBox::Yes ) { 00381 kDebug() << "Storing new incidence with scheduling uid=" << inc->schedulingID() 00382 << " and uid=" << inc->uid(); 00383 00384 #ifndef KDEPIM_NO_KRESOURCES 00385 CalendarResources *stdcal = dynamic_cast<CalendarResources *>( mCalendar ); 00386 if( stdcal && !stdcal->hasCalendarResources() ) { 00387 KMessageBox::sorry( 00388 0, 00389 i18nc( "@info", "No calendars found, unable to save the invitation." ) ); 00390 return false; 00391 } 00392 00393 // FIXME: This is a nasty hack, since we need to set a parent for the 00394 // resource selection dialog. However, we don't have any UI methods 00395 // in the calendar, only in the CalendarResources::DestinationPolicy 00396 // So we need to type-cast it and extract it from the CalendarResources 00397 QWidget *tmpparent = 0; 00398 if ( stdcal ) { 00399 tmpparent = stdcal->dialogParentWidget(); 00400 stdcal->setDialogParentWidget( 0 ); 00401 } 00402 #endif 00403 00404 TryAgain: 00405 bool success = false; 00406 #ifndef KDEPIM_NO_KRESOURCES 00407 if ( stdcal ) 00408 success = stdcal->addIncidence( inc ); 00409 else 00410 #endif 00411 success = mCalendar->addIncidence( inc ); 00412 00413 if ( !success ) { 00414 #ifndef KDEPIM_NO_KRESOURCES 00415 ErrorFormat *e = stdcal ? stdcal->exception() : 0; 00416 #else 00417 ErrorFormat *e = 0; 00418 #endif 00419 00420 if ( e && e->errorCode() == KCal::ErrorFormat::UserCancel && 00421 KMessageBox::warningYesNo( 00422 0, 00423 i18nc( "@info", 00424 "You canceled the save operation. Therefore, the appointment will not be " 00425 "stored in your calendar even though you accepted the invitation. " 00426 "Are you certain you want to discard this invitation? " ), 00427 i18nc( "@title", "Discard this invitation?" ), 00428 KGuiItem( i18nc( "@option", "Discard" ) ), 00429 KGuiItem( i18nc( "@option", "Go Back to Folder Selection" ) ) ) == KMessageBox::Yes ) { 00430 KMessageBox::information( 00431 0, 00432 i18nc( "@info", 00433 "The invitation \"%1\" was not saved to your calendar " 00434 "but you are still listed as an attendee for that appointment.\n" 00435 "If you mistakenly accepted the invitation or do not plan to attend, please " 00436 "notify the organizer %2 and ask them to remove you from the attendee list.", 00437 inc->summary(), inc->organizer().fullName() ) ); 00438 deleteTransaction( incidence ); 00439 return true; 00440 } else { 00441 goto TryAgain; 00442 } 00443 00444 // We can have a failure if the user pressed [cancel] in the resource 00445 // selectdialog, so check the exception. 00446 if ( !e || 00447 ( e && ( e->errorCode() != KCal::ErrorFormat::UserCancel && 00448 e->errorCode() != KCal::ErrorFormat::NoWritableFound ) ) ) { 00449 QString errMessage = i18nc( "@info", "Unable to save %1 \"%2\".", 00450 i18n( inc->type() ), inc->summary() ); 00451 KMessageBox::sorry( 0, errMessage ); 00452 } 00453 return false; 00454 } 00455 } 00456 deleteTransaction( incidence ); 00457 return true; 00458 } 00459 00460 bool Scheduler::acceptAdd( IncidenceBase *incidence, ScheduleMessage::Status /* status */) 00461 { 00462 deleteTransaction( incidence ); 00463 return false; 00464 } 00465 00466 bool Scheduler::acceptCancel( IncidenceBase *incidence, 00467 ScheduleMessage::Status status, 00468 const QString &attendee ) 00469 { 00470 Incidence *inc = static_cast<Incidence *>( incidence ); 00471 if ( !inc ) { 00472 return false; 00473 } 00474 00475 if ( inc->type() == "FreeBusy" ) { 00476 // reply to this request is handled in korganizer's incomingdialog 00477 return true; 00478 } 00479 00480 const Incidence::List existingIncidences = mCalendar->incidencesFromSchedulingID( inc->uid() ); 00481 kDebug() << "Scheduler::acceptCancel=" 00482 << ScheduleMessage::statusName( status ) 00483 << ": found " << existingIncidences.count() 00484 << " incidences with schedulingID " << inc->schedulingID(); 00485 00486 bool ret = false; 00487 Incidence::List::ConstIterator incit = existingIncidences.begin(); 00488 for ( ; incit != existingIncidences.end() ; ++incit ) { 00489 Incidence *i = *incit; 00490 kDebug() << "Considering this found event (" 00491 << ( i->isReadOnly() ? "readonly" : "readwrite" ) 00492 << ") :" << mFormat->toString( i ); 00493 00494 // If it's readonly, we can't possible remove it. 00495 if ( i->isReadOnly() ) { 00496 continue; 00497 } 00498 00499 // Code for new invitations: 00500 // We cannot check the value of "status" to be RequestNew because 00501 // "status" comes from a similar check inside libical, where the event 00502 // is compared to other events in the calendar. But if we have another 00503 // version of the event around (e.g. shared folder for a group), the 00504 // status could be RequestNew, Obsolete or Updated. 00505 kDebug() << "looking in " << i->uid() << "'s attendees"; 00506 00507 // This is supposed to be a new request, not an update - however we want 00508 // to update the existing one to handle the "clicking more than once 00509 // on the invitation" case. So check the attendee status of the attendee. 00510 bool isMine = true; 00511 const KCal::Attendee::List attendees = i->attendees(); 00512 KCal::Attendee::List::ConstIterator ait; 00513 for ( ait = attendees.begin(); ait != attendees.end(); ++ait ) { 00514 if ( (*ait)->email() == attendee && 00515 (*ait)->status() == Attendee::NeedsAction ) { 00516 // This incidence wasn't created by me - it's probably in a shared 00517 // folder and meant for someone else, ignore it. 00518 kDebug() << "ignoring " << i->uid() 00519 << " since I'm still NeedsAction there"; 00520 isMine = false; 00521 break; 00522 } 00523 } 00524 00525 if ( isMine ) { 00526 kDebug() << "removing existing incidence " << i->uid(); 00527 if ( i->type() == "Event" ) { 00528 Event *event = mCalendar->event( i->uid() ); 00529 ret = ( event && mCalendar->deleteEvent( event ) ); 00530 } else if ( i->type() == "Todo" ) { 00531 Todo *todo = mCalendar->todo( i->uid() ); 00532 ret = ( todo && mCalendar->deleteTodo( todo ) ); 00533 } 00534 deleteTransaction( incidence ); 00535 return ret; 00536 } 00537 } 00538 00539 // in case we didn't find the to-be-removed incidence 00540 if ( existingIncidences.count() > 0 && inc->revision() > 0 ) { 00541 KMessageBox::information( 00542 0, 00543 i18nc( "@info", 00544 "The event or task could not be removed from your calendar. " 00545 "Maybe it has already been deleted or is not owned by you. " 00546 "Or it might belong to a read-only or disabled calendar." ) ); 00547 } 00548 deleteTransaction( incidence ); 00549 return ret; 00550 } 00551 00552 bool Scheduler::acceptCancel( IncidenceBase *incidence, 00553 ScheduleMessage::Status status ) 00554 { 00555 Q_UNUSED( status ); 00556 00557 const IncidenceBase *toDelete = mCalendar->incidenceFromSchedulingID( incidence->uid() ); 00558 00559 bool ret = true; 00560 if ( toDelete ) { 00561 if ( toDelete->type() == "Event" ) { 00562 Event *event = mCalendar->event( toDelete->uid() ); 00563 ret = ( event && mCalendar->deleteEvent( event ) ); 00564 } else if ( toDelete->type() == "Todo" ) { 00565 Todo *todo = mCalendar->todo( toDelete->uid() ); 00566 ret = ( todo && mCalendar->deleteTodo( todo ) ); 00567 } 00568 } else { 00569 // only complain if we failed to determine the toDelete incidence 00570 // on non-initial request. 00571 Incidence *inc = static_cast<Incidence *>( incidence ); 00572 if ( inc->revision() > 0 ) { 00573 ret = false; 00574 } 00575 } 00576 00577 if ( !ret ) { 00578 KMessageBox::information( 00579 0, 00580 i18nc( "@info", 00581 "The event or task to be canceled could not be removed from your calendar. " 00582 "Maybe it has already been deleted or is not owned by you. " 00583 "Or it might belong to a read-only or disabled calendar." ) ); 00584 } 00585 deleteTransaction( incidence ); 00586 return ret; 00587 } 00588 00589 bool Scheduler::acceptDeclineCounter( IncidenceBase *incidence, 00590 ScheduleMessage::Status status ) 00591 { 00592 Q_UNUSED( status ); 00593 deleteTransaction( incidence ); 00594 return false; 00595 } 00596 00597 bool Scheduler::acceptReply( IncidenceBase *incidence, 00598 ScheduleMessage::Status status, 00599 iTIPMethod method ) 00600 { 00601 Q_UNUSED( status ); 00602 if ( incidence->type() == "FreeBusy" ) { 00603 return acceptFreeBusy( incidence, method ); 00604 } 00605 bool ret = false; 00606 Event *ev = mCalendar->event( incidence->uid() ); 00607 Todo *to = mCalendar->todo( incidence->uid() ); 00608 00609 // try harder to find the correct incidence 00610 if ( !ev && !to ) { 00611 const Incidence::List list = mCalendar->incidences(); 00612 for ( Incidence::List::ConstIterator it=list.constBegin(), end=list.constEnd(); 00613 it != end; ++it ) { 00614 if ( (*it)->schedulingID() == incidence->uid() ) { 00615 ev = dynamic_cast<Event*>( *it ); 00616 to = dynamic_cast<Todo*>( *it ); 00617 break; 00618 } 00619 } 00620 } 00621 00622 if ( ev || to ) { 00623 //get matching attendee in calendar 00624 kDebug() << "match found!"; 00625 Attendee::List attendeesIn = incidence->attendees(); 00626 Attendee::List attendeesEv; 00627 Attendee::List attendeesNew; 00628 if ( ev ) { 00629 attendeesEv = ev->attendees(); 00630 } 00631 if ( to ) { 00632 attendeesEv = to->attendees(); 00633 } 00634 Attendee::List::ConstIterator inIt; 00635 Attendee::List::ConstIterator evIt; 00636 for ( inIt = attendeesIn.constBegin(); inIt != attendeesIn.constEnd(); ++inIt ) { 00637 Attendee *attIn = *inIt; 00638 bool found = false; 00639 for ( evIt = attendeesEv.constBegin(); evIt != attendeesEv.constEnd(); ++evIt ) { 00640 Attendee *attEv = *evIt; 00641 if ( attIn->email().toLower() == attEv->email().toLower() ) { 00642 //update attendee-info 00643 kDebug() << "update attendee"; 00644 attEv->setStatus( attIn->status() ); 00645 attEv->setDelegate( attIn->delegate() ); 00646 attEv->setDelegator( attIn->delegator() ); 00647 ret = true; 00648 found = true; 00649 } 00650 } 00651 if ( !found && attIn->status() != Attendee::Declined ) { 00652 attendeesNew.append( attIn ); 00653 } 00654 } 00655 00656 bool attendeeAdded = false; 00657 for ( Attendee::List::ConstIterator it = attendeesNew.constBegin(); 00658 it != attendeesNew.constEnd(); ++it ) { 00659 Attendee *attNew = *it; 00660 QString msg = 00661 i18nc( "@info", "%1 wants to attend %2 but was not invited.", 00662 attNew->fullName(), 00663 ( ev ? ev->summary() : to->summary() ) ); 00664 if ( !attNew->delegator().isEmpty() ) { 00665 msg = i18nc( "@info", "%1 wants to attend %2 on behalf of %3.", 00666 attNew->fullName(), 00667 ( ev ? ev->summary() : to->summary() ), attNew->delegator() ); 00668 } 00669 if ( KMessageBox::questionYesNo( 00670 0, msg, i18nc( "@title", "Uninvited attendee" ), 00671 KGuiItem( i18nc( "@option", "Accept Attendance" ) ), 00672 KGuiItem( i18nc( "@option", "Reject Attendance" ) ) ) != KMessageBox::Yes ) { 00673 KCal::Incidence *cancel = dynamic_cast<Incidence*>( incidence ); 00674 if ( cancel ) { 00675 cancel->addComment( 00676 i18nc( "@info", 00677 "The organizer rejected your attendance at this meeting." ) ); 00678 } 00679 performTransaction( cancel ? cancel : incidence, iTIPCancel, attNew->fullName() ); 00680 // ### can't delete cancel here because it is aliased to incidence which 00681 // is accessed in the next loop iteration (CID 4232) 00682 // delete cancel; 00683 continue; 00684 } 00685 00686 Attendee *a = new Attendee( attNew->name(), attNew->email(), attNew->RSVP(), 00687 attNew->status(), attNew->role(), attNew->uid() ); 00688 a->setDelegate( attNew->delegate() ); 00689 a->setDelegator( attNew->delegator() ); 00690 if ( ev ) { 00691 ev->addAttendee( a ); 00692 } else if ( to ) { 00693 to->addAttendee( a ); 00694 } 00695 ret = true; 00696 attendeeAdded = true; 00697 } 00698 00699 // send update about new participants 00700 if ( attendeeAdded ) { 00701 bool sendMail = false; 00702 if ( ev || to ) { 00703 if ( KMessageBox::questionYesNo( 00704 0, 00705 i18nc( "@info", 00706 "An attendee was added to the incidence. " 00707 "Do you want to email the attendees an update message?" ), 00708 i18nc( "@title", "Attendee Added" ), 00709 KGuiItem( i18nc( "@option", "Send Messages" ) ), 00710 KGuiItem( i18nc( "@option", "Do Not Send" ) ) ) == KMessageBox::Yes ) { 00711 sendMail = true; 00712 } 00713 } 00714 00715 if ( ev ) { 00716 ev->setRevision( ev->revision() + 1 ); 00717 if ( sendMail ) { 00718 performTransaction( ev, iTIPRequest ); 00719 } 00720 } 00721 if ( to ) { 00722 to->setRevision( to->revision() + 1 ); 00723 if ( sendMail ) { 00724 performTransaction( to, iTIPRequest ); 00725 } 00726 } 00727 } 00728 00729 if ( ret ) { 00730 // We set at least one of the attendees, so the incidence changed 00731 // Note: This should not result in a sequence number bump 00732 if ( ev ) { 00733 ev->updated(); 00734 } else if ( to ) { 00735 to->updated(); 00736 } 00737 } 00738 if ( to ) { 00739 // for VTODO a REPLY can be used to update the completion status of 00740 // a to-do. see RFC2446 3.4.3 00741 Todo *update = dynamic_cast<Todo*> ( incidence ); 00742 Q_ASSERT( update ); 00743 if ( update && ( to->percentComplete() != update->percentComplete() ) ) { 00744 to->setPercentComplete( update->percentComplete() ); 00745 to->updated(); 00746 } 00747 } 00748 } else { 00749 kError() << "No incidence for scheduling."; 00750 } 00751 00752 if ( ret ) { 00753 deleteTransaction( incidence ); 00754 } 00755 return ret; 00756 } 00757 00758 bool Scheduler::acceptRefresh( IncidenceBase *incidence, ScheduleMessage::Status status ) 00759 { 00760 Q_UNUSED( status ); 00761 // handled in korganizer's IncomingDialog 00762 deleteTransaction( incidence ); 00763 return false; 00764 } 00765 00766 bool Scheduler::acceptCounter( IncidenceBase *incidence, ScheduleMessage::Status status ) 00767 { 00768 Q_UNUSED( status ); 00769 deleteTransaction( incidence ); 00770 return false; 00771 } 00772 00773 bool Scheduler::acceptFreeBusy( IncidenceBase *incidence, iTIPMethod method ) 00774 { 00775 if ( !d->mFreeBusyCache ) { 00776 kError() << "KCal::Scheduler: no FreeBusyCache."; 00777 return false; 00778 } 00779 00780 FreeBusy *freebusy = static_cast<FreeBusy *>(incidence); 00781 00782 kDebug() << "freeBusyDirName:" << freeBusyDir(); 00783 00784 Person from; 00785 if( method == iTIPPublish ) { 00786 from = freebusy->organizer(); 00787 } 00788 if ( ( method == iTIPReply ) && ( freebusy->attendeeCount() == 1 ) ) { 00789 Attendee *attendee = freebusy->attendees().first(); 00790 from.setName( attendee->name() ); 00791 from.setEmail( attendee->email() ); 00792 } 00793 00794 if ( !d->mFreeBusyCache->saveFreeBusy( freebusy, from ) ) { 00795 return false; 00796 } 00797 00798 deleteTransaction( incidence ); 00799 return true; 00800 }