2006-06-15 21:34:10 +02:00
/ *
2020-01-06 15:40:25 +01:00
Copyright ( C ) 2007 -2020 Inverse inc .
2006-06-15 21:34:10 +02:00
2011-04-25 12:31:08 +02:00
This file is part of SOGo
2006-06-15 21:34:10 +02:00
2009-04-30 23:17:55 +02:00
SOGo is free software ; you can redistribute it and / or modify it under
2006-06-15 21:34:10 +02:00
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation ; either version 2 , or ( at your option ) any
later version .
2009-04-30 23:17:55 +02:00
SOGo is distributed in the hope that it will be useful , but WITHOUT ANY
2006-06-15 21:34:10 +02:00
WARRANTY ; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE . See the GNU Lesser General Public
License for more details .
You should have received a copy of the GNU Lesser General Public
License along with OGo ; see the file COPYING . If not , write to the
Free Software Foundation , 59 Temple Place - Suite 330 , Boston , MA
02111 -1307 , USA .
* /
2008-07-05 00:25:27 +02:00
# import < Foundation / NSCalendarDate . h >
2010-03-12 21:24:59 +01:00
# import < Foundation / NSTimeZone . h >
2010-04-21 16:35:58 +02:00
# import < Foundation / NSValue . h >
2007-09-06 22:51:59 +02:00
2007-08-07 20:37:31 +02:00
# import < NGObjWeb / NSException + HTTP . h >
2007-11-18 11:16:25 +01:00
# import < NGObjWeb / WOContext + SoObjects . h >
2012-07-13 22:37:16 +02:00
# import < NGObjWeb / WOResponse . h >
# import < NGExtensions / NGCalendarDateRange . h >
2018-09-13 21:56:31 +02:00
# import < NGExtensions / NSCalendarDate + misc . h >
2007-08-07 20:37:31 +02:00
# import < NGExtensions / NSNull + misc . h >
# import < NGExtensions / NSObject + Logs . h >
2010-08-11 16:52:11 +02:00
# import < NGCards / iCalDateTime . h >
2006-10-11 20:58:16 +02:00
# import < NGCards / iCalEvent . h >
2015-02-18 20:52:22 +01:00
# import < NGCards / iCalToDo . h >
2009-08-10 22:59:49 +02:00
# import < NGCards / NSCalendarDate + NGCards . h >
2008-07-05 00:25:27 +02:00
# import < SaxObjC / XMLNamespaces . h >
2006-10-31 19:54:56 +01:00
2014-09-24 18:02:12 +02:00
# import < NGCards / iCalDateTime . h >
# import < NGCards / iCalTimeZone . h >
# import < NGCards / iCalTimeZonePeriod . h >
2015-11-03 22:28:03 +01:00
# import < NGCards / iCalToDo . h >
2012-07-13 22:37:16 +02:00
# import < NGCards / NSString + NGCards . h >
2009-09-10 20:31:20 +02:00
2010-02-02 22:42:17 +01:00
# import < SOGo / NSArray + Utilities . h >
2011-04-25 12:31:08 +02:00
# import < SOGo / NSDictionary + Utilities . h >
2010-02-02 22:42:17 +01:00
# import < SOGo / NSObject + DAV . h >
2014-09-15 15:32:36 +02:00
# import < SOGo / NSString + Utilities . h >
2016-02-11 16:54:07 +01:00
# import < SOGo / SOGoDateFormatter . h >
2019-11-26 15:29:02 +01:00
# import < SOGo / SOGoUserManager . h >
2010-02-02 22:42:17 +01:00
# import < SOGo / SOGoUser . h >
2014-07-07 19:38:37 +02:00
# import < SOGo / SOGoUserSettings . h >
2010-04-20 21:45:16 +02:00
# import < SOGo / SOGoDomainDefaults . h >
2010-02-02 22:42:17 +01:00
# import < SOGo / WORequest + SOGo . h >
2006-10-31 19:54:56 +01:00
2012-07-13 22:31:41 +02:00
# import "iCalCalendar+SOGo.h"
2019-11-27 23:00:28 +01:00
# import "iCalEvent+SOGo.h"
2007-11-18 11:16:25 +01:00
# import "iCalEventChanges+SOGo.h"
# import "iCalEntityObject+SOGo.h"
# import "iCalPerson+SOGo.h"
2008-07-17 23:12:43 +02:00
# import "NSArray+Appointments.h"
# import "SOGoAppointmentFolder.h"
# import "SOGoAppointmentOccurence.h"
2018-09-13 21:56:31 +02:00
# import "SOGoFreeBusyObject.h"
2007-09-15 00:01:02 +02:00
2007-08-07 20:37:31 +02:00
# import "SOGoAppointmentObject.h"
2006-06-15 21:34:10 +02:00
@ implementation SOGoAppointmentObject
2007-02-15 21:59:02 +01:00
- ( NSString * ) componentTag
2006-11-07 19:15:42 +01:00
{
2007-02-15 21:59:02 +01:00
return @ "vevent" ;
2006-06-15 21:34:10 +02:00
}
2008-07-17 23:12:43 +02:00
- ( SOGoComponentOccurence * ) occurence : ( iCalRepeatableEntityObject * ) occ
{
2009-03-16 22:08:19 +01:00
NSArray * allEvents ;
allEvents = [ [ occ parent ] events ] ;
return [ SOGoAppointmentOccurence
occurenceWithComponent : occ
withMasterComponent : [ allEvents objectAtIndex : 0 ]
inContainer : self ] ;
2008-07-17 23:12:43 +02:00
}
2011-01-14 03:54:33 +01:00
/ * *
2011-06-01 19:49:12 +02:00
* Return a new exception in the recurrent event .
2011-01-14 03:54:33 +01:00
* @ param theRecurrenceID the ID of the occurence .
* @ return a new occurence .
* /
- ( iCalRepeatableEntityObject * ) newOccurenceWithID : ( NSString * ) theRecurrenceID
2008-07-17 23:12:43 +02:00
{
2010-03-12 21:24:59 +01:00
iCalEvent * newOccurence , * master ;
NSCalendarDate * date , * firstDate ;
2008-12-30 17:57:46 +01:00
unsigned int interval , nbrDays ;
2008-07-17 23:12:43 +02:00
2011-01-14 03:54:33 +01:00
newOccurence = ( iCalEvent * ) [ super newOccurenceWithID : theRecurrenceID ] ;
2008-07-17 23:12:43 +02:00
date = [ newOccurence recurrenceId ] ;
2010-03-12 21:24:59 +01:00
master = [ self component : NO secure : NO ] ;
firstDate = [ master startDate ] ;
interval = [ [ master endDate ]
timeIntervalSinceDate : firstDate ] ;
2008-12-30 17:57:46 +01:00
if ( [ newOccurence isAllDay ] )
{
2013-01-21 20:11:38 +01:00
nbrDays = ( ( float ) abs ( interval ) / 86400 ) ;
2008-12-30 17:57:46 +01:00
[ newOccurence setAllDayWithStartDate : date
2011-07-08 02:22:10 +02:00
duration : nbrDays ] ;
2008-12-30 17:57:46 +01:00
}
else
2010-03-12 21:24:59 +01:00
{
[ newOccurence setStartDate : date ] ;
[ newOccurence setEndDate : [ date addYear : 0
month : 0
day : 0
hour : 0
minute : 0
second : interval ] ] ;
}
2011-07-08 02:22:10 +02:00
2008-07-17 23:12:43 +02:00
return newOccurence ;
}
2012-07-13 22:31:41 +02:00
- ( iCalRepeatableEntityObject * ) lookupOccurrence : ( NSString * ) recID
{
return [ [ self calendar : NO secure : NO ] eventWithRecurrenceID : recID ] ;
}
2007-11-18 11:16:25 +01:00
- ( SOGoAppointmentObject * ) _lookupEvent : ( NSString * ) eventUID
2016-08-09 22:17:17 +02:00
forUID : ( NSString * ) uid
2007-01-31 21:15:28 +01:00
{
2007-11-18 11:16:25 +01:00
SOGoAppointmentFolder * folder ;
SOGoAppointmentObject * object ;
2009-03-23 22:19:55 +01:00
NSArray * folders ;
NSEnumerator * e ;
2007-11-18 11:16:25 +01:00
NSString * possibleName ;
2009-03-23 22:19:55 +01:00
object = nil ;
folders = [ container lookupCalendarFoldersForUID : uid ] ;
e = [ folders objectEnumerator ] ;
while ( object = = nil && ( folder = [ e nextObject ] ) )
2007-02-15 21:59:02 +01:00
{
2009-03-23 22:19:55 +01:00
object = [ folder lookupName : nameInContainer
2014-07-18 19:57:18 +02:00
inContext : context
acquire : NO ] ;
2014-05-28 17:39:25 +02:00
if ( [ object isKindOfClass : [ NSException class ] ] || [ object isNew ] )
2014-07-18 19:57:18 +02:00
{
possibleName = [ folder resourceNameForEventUID : eventUID ] ;
if ( possibleName )
{
object = [ folder lookupName : possibleName
inContext : context acquire : NO ] ;
if ( [ object isKindOfClass : [ NSException class ] ] || [ object isNew ] )
object = nil ;
}
else
2014-09-24 18:02:12 +02:00
object = nil ;
2014-07-18 19:57:18 +02:00
}
2006-06-15 21:34:10 +02:00
}
2014-07-18 19:57:18 +02:00
2007-11-18 11:16:25 +01:00
if ( ! object )
2008-07-14 18:47:10 +02:00
{
2009-05-06 23:57:21 +02:00
// Create the event in the user ' s personal calendar .
2010-07-15 19:05:29 +02:00
folder = [ [ SOGoUser userWithLogin : uid ]
personalCalendarFolderInContext : context ] ;
2008-07-14 18:47:10 +02:00
object = [ SOGoAppointmentObject objectWithName : nameInContainer
2014-07-18 19:57:18 +02:00
inContainer : folder ] ;
2008-07-14 18:47:10 +02:00
[ object setIsNew : YES ] ;
}
2006-06-15 21:34:10 +02:00
2007-11-18 11:16:25 +01:00
return object ;
}
2006-06-15 21:34:10 +02:00
2011-03-23 21:14:33 +01:00
//
2017-07-19 17:05:16 +02:00
// This method will * ONLY * add or update event information in attendees ' calendars .
// It will NOT touch to the organizer calendar in anyway . This method is meant
// to reflect changes in attendees ' calendars when the organizer makes changes
// to the event .
2011-03-23 21:14:33 +01:00
//
2017-07-19 17:05:16 +02:00
- ( void ) _addOrUpdateEvent : ( iCalEvent * ) newEvent
oldEvent : ( iCalEvent * ) oldEvent
2014-09-24 18:02:12 +02:00
forUID : ( NSString * ) theUID
owner : ( NSString * ) theOwner
2007-08-24 00:11:19 +02:00
{
2008-11-26 13:20:26 +01:00
if ( ! [ theUID isEqualToString : theOwner ] )
2007-11-08 00:35:26 +01:00
{
2009-06-02 23:51:17 +02:00
SOGoAppointmentObject * attendeeObject ;
2014-07-30 20:51:00 +02:00
iCalCalendar * iCalendarToSave ;
2019-02-04 17:52:26 +01:00
iCalPerson * attendee ;
SOGoUser * user ;
2014-07-30 20:51:00 +02:00
iCalendarToSave = nil ;
2019-02-04 17:52:26 +01:00
user = [ SOGoUser userWithLogin : theUID ] ;
2017-07-19 17:05:16 +02:00
attendeeObject = [ self _lookupEvent : [ newEvent uid ] forUID : theUID ] ;
2019-02-04 17:52:26 +01:00
attendee = [ newEvent userAsAttendee : user ] ;
// If the atttende ' s role is NON - PARTICIPANT , we write nothing to its calendar
if ( [ [ attendee role ] caseInsensitiveCompare : @ "NON-PARTICIPANT" ] = = NSOrderedSame )
2019-02-07 20:10:39 +01:00
{
// If the attendee ' s previous role was not NON - PARTICIPANT we must also delete
// the event from its calendar
attendee = [ oldEvent userAsAttendee : user ] ;
if ( [ [ attendee role ] caseInsensitiveCompare : @ "NON-PARTICIPANT" ] ! = NSOrderedSame )
{
NSString * currentUID ;
currentUID = [ attendee uidInContext : context ] ;
if ( currentUID )
[ self _removeEventFromUID : currentUID
owner : owner
withRecurrenceId : [ oldEvent recurrenceId ] ] ;
}
return ;
}
2017-07-19 17:05:16 +02:00
if ( [ newEvent recurrenceId ] )
2014-07-30 20:51:00 +02:00
{
2017-07-19 17:05:16 +02:00
// We must add an occurence to a non - existing event .
if ( [ attendeeObject isNew ] )
2014-07-18 19:57:18 +02:00
{
2017-07-19 17:05:16 +02:00
iCalEvent * ownerEvent ;
2019-02-04 17:52:26 +01:00
2017-07-19 17:05:16 +02:00
// We check if the attendee that was added to a single occurence is
2018-09-06 20:45:19 +02:00
// present in the master component . If not , we create a calendar with
// a single event for the occurence .
2017-07-19 17:05:16 +02:00
ownerEvent = [ [ [ newEvent parent ] events ] objectAtIndex : 0 ] ;
if ( ! [ ownerEvent userAsAttendee : user ] )
{
iCalendarToSave = [ [ [ newEvent parent ] mutableCopy ] autorelease ] ;
[ iCalendarToSave removeChildren : [ iCalendarToSave childrenWithTag : @ "vevent" ] ] ;
[ iCalendarToSave addChild : [ [ newEvent copy ] autorelease ] ] ;
}
}
else
{
// Only update this occurrence in attendee ' s calendar
// TODO : when updating the master event , handle exception dates
// in attendee ' s calendar ( add exception dates and remove matching
// occurrences ) - - see _updateRecurrenceIDsWithEvent :
NSCalendarDate * currentId ;
NSArray * occurences ;
iCalEvent * occurence ;
int max , count ;
iCalendarToSave = [ attendeeObject calendar : NO secure : NO ] ;
// If recurrenceId is defined , remove the occurence from
// the repeating event . If a recurrenceId is defined in the
// new event , let ' s make sure we don ' t already have one in
2017-08-31 20:40:54 +02:00
// the calendar already . If so , also remove it .
2017-07-19 17:05:16 +02:00
if ( [ oldEvent recurrenceId ] || [ newEvent recurrenceId ] )
{
// FIXME : use _eventFromRecurrenceId : . . .
occurences = [ iCalendarToSave events ] ;
2017-08-31 20:40:54 +02:00
currentId = ( [ oldEvent recurrenceId ] ? [ oldEvent recurrenceId ] : [ newEvent recurrenceId ] ) ;
if ( currentId )
2017-07-19 17:05:16 +02:00
{
2017-08-31 20:40:54 +02:00
max = [ occurences count ] ;
count = 0 ;
while ( count < max )
2017-07-19 17:05:16 +02:00
{
2017-08-31 20:40:54 +02:00
occurence = [ occurences objectAtIndex : count ] ;
if ( [ occurence recurrenceId ] &&
[ [ occurence recurrenceId ] compare : currentId ] = = NSOrderedSame )
{
[ iCalendarToSave removeChild : occurence ] ;
break ;
}
count + + ;
2017-07-19 17:05:16 +02:00
}
}
}
2014-07-18 19:57:18 +02:00
2017-07-19 17:05:16 +02:00
[ iCalendarToSave addChild : [ [ newEvent copy ] autorelease ] ] ;
2014-07-18 19:57:18 +02:00
}
}
2017-11-10 17:25:59 +01:00
else
2014-07-18 19:57:18 +02:00
{
2017-07-19 17:05:16 +02:00
iCalendarToSave = [ newEvent parent ] ;
2014-07-18 19:57:18 +02:00
}
2017-07-19 17:05:16 +02:00
2009-06-02 23:51:17 +02:00
// Save the event in the attendee ' s calendar
2014-07-30 20:51:00 +02:00
if ( iCalendarToSave )
[ attendeeObject saveCalendar : iCalendarToSave ] ;
2006-06-15 21:34:10 +02:00
}
}
2007-01-31 21:15:28 +01:00
2017-07-19 17:05:16 +02:00
2011-03-23 21:14:33 +01:00
//
2017-07-19 17:05:16 +02:00
// This method will * ONLY * delete event information in attendees ' calendars .
// It will NOT touch to the organizer calendar in anyway . This method is meant
// to reflect changes in attendees ' calendars when the organizer makes changes
// to the event .
2011-03-23 21:14:33 +01:00
//
2008-11-26 13:20:26 +01:00
- ( void ) _removeEventFromUID : ( NSString * ) theUID
owner : ( NSString * ) theOwner
2014-07-18 19:57:18 +02:00
withRecurrenceId : ( NSCalendarDate * ) recurrenceId
2007-11-07 16:58:43 +01:00
{
2008-11-26 13:20:26 +01:00
if ( ! [ theUID isEqualToString : theOwner ] )
2007-11-07 16:58:43 +01:00
{
2008-11-26 13:20:26 +01:00
SOGoAppointmentFolder * folder ;
SOGoAppointmentObject * object ;
2008-12-05 17:11:41 +01:00
iCalEntityObject * currentOccurence ;
2008-12-04 00:08:37 +01:00
iCalRepeatableEntityObject * event ;
iCalCalendar * calendar ;
2008-12-05 17:11:41 +01:00
NSCalendarDate * currentId ;
2009-04-01 15:49:24 +02:00
NSArray * occurences ;
2008-12-05 17:11:41 +01:00
int max , count ;
2009-03-23 22:19:55 +01:00
// Invitations are always written to the personal folder ; it ' s not necessay
// to look into all folders of the user
2017-07-19 17:05:16 +02:00
// FIXME : why look only in the personal calendar here ?
2010-07-15 19:05:29 +02:00
folder = [ [ SOGoUser userWithLogin : theUID ]
2014-07-18 19:57:18 +02:00
personalCalendarFolderInContext : context ] ;
2007-11-18 11:16:25 +01:00
object = [ folder lookupName : nameInContainer
2017-07-19 17:05:16 +02:00
inContext : context
acquire : NO ] ;
2007-11-18 11:16:25 +01:00
if ( ! [ object isKindOfClass : [ NSException class ] ] )
2014-07-18 19:57:18 +02:00
{
if ( recurrenceId = = nil )
2017-07-19 17:05:16 +02:00
[ object delete ] ;
2014-07-18 19:57:18 +02:00
else
{
calendar = [ object calendar : NO secure : NO ] ;
// If recurrenceId is defined , remove the occurence from
// the repeating event .
occurences = [ calendar events ] ;
max = [ occurences count ] ;
2017-07-19 17:05:16 +02:00
count = 0 ;
2014-07-18 19:57:18 +02:00
while ( count < max )
{
currentOccurence = [ occurences objectAtIndex : count ] ;
currentId = [ currentOccurence recurrenceId ] ;
2018-06-18 15:50:24 +02:00
if ( currentId && [ currentId compare : recurrenceId ] = = NSOrderedSame )
2014-07-18 19:57:18 +02:00
{
[ [ calendar children ] removeObject : currentOccurence ] ;
break ;
}
count + + ;
}
2018-05-16 20:58:04 +02:00
2014-07-18 19:57:18 +02:00
// Add an date exception .
event = ( iCalRepeatableEntityObject * ) [ calendar firstChildWithTag : [ object componentTag ] ] ;
2018-05-16 20:58:04 +02:00
if ( event )
{
[ event addToExceptionDates : recurrenceId ] ;
[ event increaseSequence ] ;
[ event setLastModified : [ NSCalendarDate calendarDate ] ] ;
2018-03-15 20:43:26 +01:00
2018-05-16 20:58:04 +02:00
// We save the updated iCalendar in the database .
[ object saveCalendar : calendar ] ;
}
else
{
// No more child ; kill the parent
[ object delete ] ;
}
2014-07-18 19:57:18 +02:00
}
}
2017-07-19 17:05:16 +02:00
else
2019-02-04 17:52:26 +01:00
[ self errorWithFormat : @ "Unable to find event with UID %@ in %@'s calendar - skipping delete operation. This can be normal for NON-PARTICIPANT attendees." , nameInContainer , theUID ] ;
2006-06-15 21:34:10 +02:00
}
}
2011-03-23 21:14:33 +01:00
//
//
//
2007-11-18 11:16:25 +01:00
- ( void ) _handleRemovedUsers : ( NSArray * ) attendees
2008-12-04 00:08:37 +01:00
withRecurrenceId : ( NSCalendarDate * ) recurrenceId
2007-09-06 22:51:59 +02:00
{
2007-11-18 11:16:25 +01:00
NSEnumerator * enumerator ;
iCalPerson * currentAttendee ;
NSString * currentUID ;
2007-09-06 22:51:59 +02:00
2007-11-18 11:16:25 +01:00
enumerator = [ attendees objectEnumerator ] ;
while ( ( currentAttendee = [ enumerator nextObject ] ) )
{
2015-07-22 22:45:42 +02:00
currentUID = [ currentAttendee uidInContext : context ] ;
2007-11-18 11:16:25 +01:00
if ( currentUID )
2014-07-18 19:57:18 +02:00
[ self _removeEventFromUID : currentUID
owner : owner
2017-07-19 17:05:16 +02:00
withRecurrenceId : recurrenceId ] ;
2007-11-18 11:16:25 +01:00
}
2007-09-06 22:51:59 +02:00
}
2006-06-15 21:34:10 +02:00
2011-03-23 21:14:33 +01:00
//
//
//
2010-07-16 17:03:16 +02:00
- ( void ) _removeDelegationChain : ( iCalPerson * ) delegate
inEvent : ( iCalEvent * ) event
2006-09-28 19:23:39 +02:00
{
2010-07-16 17:03:16 +02:00
NSString * delegatedTo , * mailTo ;
delegatedTo = [ delegate delegatedTo ] ;
if ( [ delegatedTo length ] > 0 )
{
mailTo = [ delegatedTo rfc822Email ] ;
delegate = [ event findAttendeeWithEmail : mailTo ] ;
if ( delegate )
{
[ self _removeDelegationChain : delegate
inEvent : event ] ;
[ event removeFromAttendees : delegate ] ;
}
else
2014-07-18 19:57:18 +02:00
[ self errorWithFormat : @ "broken chain: delegate with email '%@' was not found" , mailTo ] ;
2010-07-16 17:03:16 +02:00
}
}
2011-03-23 21:14:33 +01:00
//
// This method returns YES when any attendee has been removed
// and NO otherwise .
//
2010-07-16 17:03:16 +02:00
- ( BOOL ) _requireResponseFromAttendees : ( iCalEvent * ) event
{
NSArray * attendees ;
2007-11-18 11:16:25 +01:00
iCalPerson * currentAttendee ;
2012-01-10 18:42:33 +01:00
BOOL listHasChanged = NO ;
2010-07-16 17:03:16 +02:00
int count , max ;
2006-06-15 21:34:10 +02:00
2010-07-16 17:03:16 +02:00
attendees = [ event attendees ] ;
max = [ attendees count ] ;
for ( count = 0 ; count < max ; count + + )
2007-11-18 11:16:25 +01:00
{
2010-07-16 17:03:16 +02:00
currentAttendee = [ attendees objectAtIndex : count ] ;
if ( [ [ currentAttendee delegatedTo ] length ] > 0 )
{
[ self _removeDelegationChain : currentAttendee
inEvent : event ] ;
[ currentAttendee setDelegatedTo : nil ] ;
listHasChanged = YES ;
}
2007-11-18 11:16:25 +01:00
[ currentAttendee setRsvp : @ "TRUE" ] ;
[ currentAttendee setParticipationStatus : iCalPersonPartStatNeedsAction ] ;
}
2010-07-16 17:03:16 +02:00
return listHasChanged ;
2007-11-18 11:16:25 +01:00
}
2007-11-08 20:56:18 +01:00
2016-12-16 21:51:19 +01:00
//
//
//
- ( BOOL ) _shouldScheduleEvent : ( iCalPerson * ) thePerson
{
2016-12-22 15:41:39 +01:00
// NSArray * userAgents ;
2016-12-16 21:51:19 +01:00
NSString * v ;
BOOL b ;
2016-12-22 15:41:39 +01:00
// int i ;
2016-12-16 21:51:19 +01:00
b = YES ;
if ( thePerson && ( v = [ thePerson value : 0 ofAttribute : @ "SCHEDULE-AGENT" ] ) )
{
if ( [ v caseInsensitiveCompare : @ "NONE" ] = = NSOrderedSame ||
[ v caseInsensitiveCompare : @ "CLIENT" ] = = NSOrderedSame )
b = NO ;
}
//
// If we have to deal with Thunderbird / Lightning , we always send invitation
// reponses , as Lightning v2 .6 ( at least this version ) sets SCHEDULE - AGENT
// to NONE / CLIENT when responding to an external invitation received by
// SOGo - so no invitation responses are ever sent by Lightning . See
// https : // bugzilla . mozilla . org / show_bug . cgi ? id = 865726 and
// https : // bugzilla . mozilla . org / show_bug . cgi ? id = 997784
//
2016-12-22 14:55:12 +01:00
// This code has been disabled - see 0003274.
//
# if 0
2016-12-16 21:51:19 +01:00
userAgents = [ [ context request ] headersForKey : @ "User-Agent" ] ;
for ( i = 0 ; i < [ userAgents count ] ; i + + )
{
if ( [ [ userAgents objectAtIndex : i ] rangeOfString : @ "Thunderbird" ] . location ! = NSNotFound &&
[ [ userAgents objectAtIndex : i ] rangeOfString : @ "Lightning" ] . location ! = NSNotFound )
{
b = YES ;
break ;
}
}
2016-12-22 14:55:12 +01:00
# endif
2016-12-16 21:51:19 +01:00
return b ;
}
2011-03-23 21:14:33 +01:00
//
//
//
2007-11-18 11:16:25 +01:00
- ( void ) _handleSequenceUpdateInEvent : ( iCalEvent * ) newEvent
ignoringAttendees : ( NSArray * ) attendees
fromOldEvent : ( iCalEvent * ) oldEvent
{
2012-02-03 16:40:36 +01:00
NSMutableArray * updateAttendees ;
2007-11-18 11:16:25 +01:00
NSEnumerator * enumerator ;
iCalPerson * currentAttendee ;
NSString * currentUID ;
2007-11-08 20:56:18 +01:00
2007-11-18 11:16:25 +01:00
updateAttendees = [ NSMutableArray arrayWithArray : [ newEvent attendees ] ] ;
[ updateAttendees removeObjectsInArray : attendees ] ;
2007-11-08 20:56:18 +01:00
2007-11-18 11:16:25 +01:00
enumerator = [ updateAttendees objectEnumerator ] ;
while ( ( currentAttendee = [ enumerator nextObject ] ) )
{
2015-07-22 22:45:42 +02:00
currentUID = [ currentAttendee uidInContext : context ] ;
2007-11-18 11:16:25 +01:00
if ( currentUID )
2014-12-22 21:26:22 +01:00
[ self _addOrUpdateEvent : newEvent
2017-07-19 17:05:16 +02:00
oldEvent : oldEvent
2014-12-22 21:26:22 +01:00
forUID : currentUID
owner : owner ] ;
2007-11-18 11:16:25 +01:00
}
2006-06-15 21:34:10 +02:00
2016-12-16 21:51:19 +01:00
if ( [ self _shouldScheduleEvent : [ newEvent organizer ] ] )
[ self sendEMailUsingTemplateNamed : @ "Update"
forObject : [ newEvent itipEntryWithMethod : @ "request" ]
previousObject : oldEvent
toAttendees : updateAttendees
withType : @ "calendar:invitation-update" ] ;
2007-11-18 11:16:25 +01:00
}
2006-06-15 21:34:10 +02:00
2014-07-15 23:34:49 +02:00
// This method scans the list of attendees .
2016-02-11 16:54:07 +01:00
- ( NSException * ) _handleAttendeesAvailability : ( NSArray * ) theAttendees
forEvent : ( iCalEvent * ) theEvent
2014-07-04 15:51:41 +02:00
{
iCalPerson * currentAttendee ;
2014-09-09 16:28:59 +02:00
SOGoUser * user ;
SOGoUserSettings * us ;
NSMutableArray * unavailableAttendees ;
2014-07-04 15:51:41 +02:00
NSEnumerator * enumerator ;
2015-04-17 15:41:39 +02:00
NSString * currentUID , * ownerUID ;
2014-07-04 15:51:41 +02:00
NSMutableString * reason ;
NSDictionary * values ;
2015-04-17 15:41:39 +02:00
NSMutableDictionary * value , * moduleSettings ;
id whiteList ;
2014-07-30 20:51:00 +02:00
int i , count ;
2015-04-17 15:41:39 +02:00
2014-07-30 20:51:00 +02:00
i = count = 0 ;
2014-09-09 16:28:59 +02:00
2016-02-10 21:03:20 +01:00
// Build list of the attendees uids
2014-07-04 15:51:41 +02:00
unavailableAttendees = [ [ NSMutableArray alloc ] init ] ;
enumerator = [ theAttendees objectEnumerator ] ;
2014-07-07 23:42:43 +02:00
ownerUID = [ [ [ self context ] activeUser ] login ] ;
2014-09-09 16:28:59 +02:00
2014-07-04 15:51:41 +02:00
while ( ( currentAttendee = [ enumerator nextObject ] ) )
{
2015-07-22 22:45:42 +02:00
currentUID = [ currentAttendee uidInContext : context ] ;
2014-09-24 18:02:12 +02:00
2014-07-18 19:57:18 +02:00
if ( currentUID )
2014-07-04 15:51:41 +02:00
{
2014-07-18 19:57:18 +02:00
user = [ SOGoUser userWithLogin : currentUID ] ;
us = [ user userSettings ] ;
moduleSettings = [ us objectForKey : @ "Calendar" ] ;
2014-09-24 18:02:12 +02:00
2014-12-19 01:34:05 +01:00
// Check if the user prevented their account from beeing invited to events
2016-02-10 21:03:20 +01:00
if ( [ [ moduleSettings objectForKey : @ "PreventInvitations" ] boolValue ] )
2014-07-18 19:57:18 +02:00
{
// Check if the user have a whiteList
2015-04-17 15:41:39 +02:00
whiteList = [ moduleSettings objectForKey : @ "PreventInvitationsWhitelist" ] ;
// For backward <= 2.2 .17 compatibility
if ( [ whiteList isKindOfClass : [ NSString class ] ] )
whiteList = [ whiteList objectFromJSONString ] ;
2014-09-24 18:02:12 +02:00
2014-07-18 19:57:18 +02:00
// If the filter have a hit , do not add the currentUID to the unavailableAttendees array
2014-09-09 16:28:59 +02:00
if ( ! [ whiteList objectForKey : ownerUID ] )
2014-07-18 19:57:18 +02:00
{
values = [ NSDictionary dictionaryWithObject : [ user cn ] forKey : @ "Cn" ] ;
[ unavailableAttendees addObject : values ] ;
}
}
2014-07-04 15:51:41 +02:00
}
}
2014-07-07 19:38:37 +02:00
2014-07-04 15:51:41 +02:00
count = [ unavailableAttendees count ] ;
2014-09-24 18:02:12 +02:00
2014-07-15 23:34:49 +02:00
if ( count > 0 )
2014-07-04 15:51:41 +02:00
{
2014-07-29 16:36:20 +02:00
reason = [ NSMutableString stringWithString : [ self labelForKey : @ "Inviting the following persons is prohibited:" ] ] ;
2014-09-24 18:02:12 +02:00
2014-07-18 19:57:18 +02:00
// Add all the unavailable users in the warning message
for ( i = 0 ; i < count ; i + + )
{
value = [ unavailableAttendees objectAtIndex : i ] ;
[ reason appendString : [ value keysWithFormat : @ "\n %{Cn}" ] ] ;
if ( i < count -2 )
[ reason appendString : @ ", " ] ;
}
2014-09-24 18:02:12 +02:00
2014-07-18 19:57:18 +02:00
[ unavailableAttendees release ] ;
2014-09-24 18:02:12 +02:00
2014-07-18 19:57:18 +02:00
return [ NSException exceptionWithHTTPStatus : 409 reason : reason ] ;
2014-07-04 15:51:41 +02:00
}
2014-09-24 18:02:12 +02:00
2014-07-10 17:13:07 +02:00
[ unavailableAttendees release ] ;
2014-09-24 18:02:12 +02:00
2014-07-10 17:13:07 +02:00
return nil ;
2014-07-04 15:51:41 +02:00
}
2011-04-25 12:31:08 +02:00
//
// This methods scans the list of attendees . If they are
// considered as resource , it checks for conflicting
2016-02-11 16:54:07 +01:00
// dates for the event and potentially auto - accept / decline
// the invitation .
2011-04-25 12:31:08 +02:00
//
2016-02-11 16:54:07 +01:00
// For normal attendees , it ' ll return an exception with
// conflicting dates , unless we force the save . //
2011-04-25 12:31:08 +02:00
// We check for between startDate + 1 second and
// endDate - 1 second
2011-03-23 21:14:33 +01:00
//
2011-04-25 12:31:08 +02:00
// Note that it doesn ' t matter if it changes the participation
// status since in case of an error , nothing will get saved .
//
2016-02-11 16:54:07 +01:00
- ( NSException * ) _handleAttendeesConflicts : ( NSArray * ) theAttendees
2011-04-25 12:31:08 +02:00
forEvent : ( iCalEvent * ) theEvent
2016-02-11 16:54:07 +01:00
force : ( BOOL ) forceSave
2007-11-18 11:16:25 +01:00
{
2011-04-25 12:31:08 +02:00
iCalPerson * currentAttendee ;
2014-02-03 17:03:23 +01:00
NSMutableArray * attendees ;
2007-11-18 11:16:25 +01:00
NSEnumerator * enumerator ;
2011-04-25 12:31:08 +02:00
NSString * currentUID ;
2014-07-30 20:51:00 +02:00
SOGoUser * user , * currentUser ;
2017-01-23 14:53:41 +01:00
2020-01-06 15:40:25 +01:00
_resourceHasAutoAccepted = NO ;
2014-02-03 17:03:23 +01:00
// Build a list of the attendees uids
attendees = [ NSMutableArray arrayWithCapacity : [ theAttendees count ] ] ;
2011-04-25 12:31:08 +02:00
enumerator = [ theAttendees objectEnumerator ] ;
while ( ( currentAttendee = [ enumerator nextObject ] ) )
{
2015-07-22 22:45:42 +02:00
currentUID = [ currentAttendee uidInContext : context ] ;
2014-07-18 19:57:18 +02:00
if ( currentUID )
{
[ attendees addObject : currentUID ] ;
}
2014-02-03 17:03:23 +01:00
}
2014-07-18 19:57:18 +02:00
2014-02-03 17:03:23 +01:00
// If the active user is not the owner of the calendar , check possible conflict when
// the owner is a resource
currentUser = [ context activeUser ] ;
if ( ! activeUserIsOwner && ! [ currentUser isSuperUser ] )
2014-07-18 19:57:18 +02:00
{
[ attendees addObject : owner ] ;
}
2014-07-04 15:51:41 +02:00
2014-02-03 17:03:23 +01:00
enumerator = [ attendees objectEnumerator ] ;
while ( ( currentUID = [ enumerator nextObject ] ) )
2014-07-04 15:51:41 +02:00
{
2016-02-11 16:54:07 +01:00
NSCalendarDate * start , * end , * rangeStartDate , * rangeEndDate ;
SOGoAppointmentFolder * folder ;
2018-09-13 21:56:31 +02:00
SOGoFreeBusyObject * fb ;
2016-02-11 16:54:07 +01:00
NGCalendarDateRange * range ;
NSMutableArray * fbInfo ;
NSArray * allOccurences ;
2014-07-18 19:57:18 +02:00
2016-02-11 16:54:07 +01:00
BOOL must_delete ;
int i , j , delta ;
user = [ SOGoUser userWithLogin : currentUID ] ;
// We get the start / end date for our conflict range . If the event to be added is recurring , we
// check for at least a year to start with .
start = [ [ theEvent startDate ] dateByAddingYears : 0 months : 0 days : 0 hours : 0 minutes : 0 seconds : 1 ] ;
end = [ [ theEvent endDate ] dateByAddingYears : ( [ theEvent isRecurrent ] ? 1 : 0 ) months : 0 days : 0 hours : 0 minutes : 0 seconds : -1 ] ;
2014-07-18 19:57:18 +02:00
2016-02-11 16:54:07 +01:00
folder = [ user personalCalendarFolderInContext : context ] ;
2018-09-13 21:56:31 +02:00
2016-02-11 16:54:07 +01:00
// Deny access to the resource if the ACLs don ' t allow the user
2016-02-12 20:40:51 +01:00
if ( [ user isResource ] && ! [ folder aclSQLListingFilter ] )
2016-02-11 16:54:07 +01:00
{
NSDictionary * values ;
NSString * reason ;
2014-07-18 19:57:18 +02:00
2016-02-11 16:54:07 +01:00
values = [ NSDictionary dictionaryWithObjectsAndKeys :
[ user cn ] , @ "Cn" ,
2016-06-14 14:31:37 +02:00
[ user systemEmail ] , @ "SystemEmail" , nil ] ;
2016-02-11 16:54:07 +01:00
reason = [ values keysWithFormat : [ self labelForKey : @ "Cannot access resource: \" % { Cn } % { SystemEmail } \ "" ] ] ;
2016-08-04 20:13:00 +02:00
return [ NSException exceptionWithHTTPStatus : 409 reason : reason ] ;
2016-02-11 16:54:07 +01:00
}
2016-08-05 20:53:38 +02:00
2018-09-13 21:56:31 +02:00
fb = [ SOGoFreeBusyObject objectWithName : @ "freebusy.ifb" inContainer : [ user homeFolderInContext : context ] ] ;
2018-10-22 17:20:06 +02:00
fbInfo = ( NSMutableArray * ) [ fb fetchFreeBusyInfosFrom : start to : end ] ;
2018-09-13 21:56:31 +02:00
2016-02-11 16:54:07 +01:00
//
// We must also check here for repetitive events that don ' t overlap our event .
// We remove all events that don ' t overlap . The events here are already
// decomposed .
//
if ( [ theEvent isRecurrent ] )
allOccurences = [ theEvent recurrenceRangesWithinCalendarDateRange : [ NGCalendarDateRange calendarDateRangeWithStartDate : start
endDate : end ]
firstInstanceCalendarDateRange : [ NGCalendarDateRange calendarDateRangeWithStartDate : [ theEvent startDate ]
endDate : [ theEvent endDate ] ] ] ;
else
allOccurences = nil ;
for ( i = [ fbInfo count ] -1 ; i >= 0 ; i - - )
{
2016-08-05 20:53:38 +02:00
// We first remove any occurences in the freebusy that corresponds to the
// current event . We do this to avoid raising a conflict if we move a 1 hour
// meeting from 12 : 00 -13 : 00 to 12 : 15 -13 : 15. We would overlap on ourself otherwise .
2016-02-11 16:54:07 +01:00
if ( [ [ [ fbInfo objectAtIndex : i ] objectForKey : @ "c_uid" ] compare : [ theEvent uid ] ] = = NSOrderedSame )
2014-07-18 19:57:18 +02:00
{
2016-02-11 16:54:07 +01:00
[ fbInfo removeObjectAtIndex : i ] ;
continue ;
}
2019-01-15 23:11:27 +01:00
// Ignore transparent events
if ( ! [ [ [ fbInfo objectAtIndex : i ] objectForKey : @ "c_isopaque" ] boolValue ] )
{
[ fbInfo removeObjectAtIndex : i ] ;
continue ;
}
2016-02-11 16:54:07 +01:00
// No need to check if the event isn ' t recurrent here as it ' s handled correctly
// when we compute the "end" date .
if ( [ allOccurences count ] )
{
must_delete = YES ;
2019-01-15 23:11:27 +01:00
// We MUST use the - uniqueChildWithTag method here because the event has been flattened , so its timezone has been
// modified in SOGoAppointmentFolder : - fixupCycleRecord : . . . .
rangeStartDate = [ [ fbInfo objectAtIndex : i ] objectForKey : @ "startDate" ] ;
delta = [ [ rangeStartDate timeZoneDetail ] timeZoneSecondsFromGMT ] - [ [ [ ( iCalDateTime * ) [ theEvent uniqueChildWithTag : @ "dtstart" ] timeZone ] periodForDate : [ theEvent startDate ] ] secondsOffsetFromGMT ] ;
rangeStartDate = [ rangeStartDate dateByAddingYears : 0 months : 0 days : 0 hours : 0 minutes : 0 seconds : delta ] ;
rangeEndDate = [ [ fbInfo objectAtIndex : i ] objectForKey : @ "endDate" ] ;
delta = [ [ rangeEndDate timeZoneDetail ] timeZoneSecondsFromGMT ] - [ [ [ ( iCalDateTime * ) [ theEvent uniqueChildWithTag : @ "dtend" ] timeZone ] periodForDate : [ theEvent endDate ] ] secondsOffsetFromGMT ] ;
rangeEndDate = [ rangeEndDate dateByAddingYears : 0 months : 0 days : 0 hours : 0 minutes : 0 seconds : delta ] ;
range = [ NGCalendarDateRange calendarDateRangeWithStartDate : rangeStartDate
endDate : rangeEndDate ] ;
2016-02-11 16:54:07 +01:00
for ( j = 0 ; j < [ allOccurences count ] ; j + + )
2014-07-18 19:57:18 +02:00
{
2016-02-11 16:54:07 +01:00
if ( [ range doesIntersectWithDateRange : [ allOccurences objectAtIndex : j ] ] )
2014-07-18 19:57:18 +02:00
{
2016-02-11 16:54:07 +01:00
must_delete = NO ;
break ;
2014-07-18 19:57:18 +02:00
}
}
2016-02-11 16:54:07 +01:00
if ( must_delete )
[ fbInfo removeObjectAtIndex : i ] ;
2014-07-18 19:57:18 +02:00
}
2016-02-11 16:54:07 +01:00
}
// Find the attendee associated to the current UID
2018-10-22 18:08:46 +02:00
currentAttendee = nil ;
2016-02-11 16:54:07 +01:00
for ( i = 0 ; i < [ theAttendees count ] ; i + + )
{
currentAttendee = [ theAttendees objectAtIndex : i ] ;
if ( [ [ currentAttendee uidInContext : context ] isEqualToString : currentUID ] )
break ;
else
currentAttendee = nil ;
}
if ( [ fbInfo count ] )
{
SOGoDateFormatter * formatter ;
formatter = [ [ context activeUser ] dateFormatterInContext : context ] ;
2014-07-04 15:51:41 +02:00
2016-02-11 16:54:07 +01:00
if ( [ user isResource ] )
2014-07-18 19:57:18 +02:00
{
2019-12-03 22:42:36 +01:00
// We always force the auto - accept if numberOfSimultaneousBookings <= 0 ( ie . , no limit
2014-07-18 19:57:18 +02:00
// is imposed ) or if numberOfSimultaneousBookings is greater than the number of
2019-12-03 22:42:36 +01:00
// overlapping events .
// When numberOfSimultaneousBookings is set to -1 , only force the auto - accept
// once the conflict has been raised and the action is forced by the user .
2015-01-05 19:49:28 +01:00
if ( [ user numberOfSimultaneousBookings ] <= 0 ||
2014-07-18 19:57:18 +02:00
[ user numberOfSimultaneousBookings ] > [ fbInfo count ] )
{
2019-12-03 22:42:36 +01:00
if ( currentAttendee && ( [ user numberOfSimultaneousBookings ] >= 0 || forceSave ) )
2014-07-18 19:57:18 +02:00
{
[ [ currentAttendee attributes ] removeObjectForKey : @ "RSVP" ] ;
[ currentAttendee setParticipationStatus : iCalPersonPartStatAccepted ] ;
2020-01-06 15:40:25 +01:00
_resourceHasAutoAccepted = YES ;
2014-07-18 19:57:18 +02:00
}
}
else
{
iCalCalendar * calendar ;
2016-02-16 18:40:59 +01:00
NSDictionary * values , * info ;
2014-07-18 19:57:18 +02:00
NSString * reason ;
iCalEvent * event ;
2016-02-11 16:54:07 +01:00
2014-07-18 19:57:18 +02:00
calendar = [ iCalCalendar parseSingleFromSource : [ [ fbInfo objectAtIndex : 0 ] objectForKey : @ "c_content" ] ] ;
event = [ [ calendar events ] lastObject ] ;
2016-02-11 16:54:07 +01:00
2014-07-18 19:57:18 +02:00
values = [ NSDictionary dictionaryWithObjectsAndKeys :
2016-02-11 16:54:07 +01:00
[ NSString stringWithFormat : @ "%d" , [ user numberOfSimultaneousBookings ] ] , @ "NumberOfSimultaneousBookings" ,
[ user cn ] , @ "Cn" ,
[ user systemEmail ] , @ "SystemEmail" ,
( [ event summary ] ? [ event summary ] : @ "" ) , @ "EventTitle" ,
[ formatter formattedDateAndTime : [ [ fbInfo objectAtIndex : 0 ] objectForKey : @ "startDate" ] ] , @ "StartDate" ,
nil ] ;
2014-07-18 19:57:18 +02:00
reason = [ values keysWithFormat : [ self labelForKey : @ "Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \" % { Cn } % { SystemEmail } \ ". The conflicting event is \" % { EventTitle } \ ", and starts on %{StartDate}." ] ] ;
2016-02-11 16:54:07 +01:00
2016-02-16 18:40:59 +01:00
info = [ NSDictionary dictionaryWithObject : reason forKey : @ "reject" ] ;
2016-08-04 20:13:00 +02:00
return [ NSException exceptionWithHTTPStatus : 409
2016-02-16 18:40:59 +01:00
reason : [ info jsonRepresentation ] ] ;
2014-07-18 19:57:18 +02:00
}
}
2016-02-11 16:54:07 +01:00
//
// We are dealing with a normal attendee . Lets check if we have conflicts , unless
// we are being asked to force the save anyway
//
2020-01-06 15:40:25 +01:00
if ( ! forceSave && ! _resourceHasAutoAccepted )
2014-07-18 19:57:18 +02:00
{
2016-02-11 16:54:07 +01:00
NSMutableDictionary * info ;
NSMutableArray * conflicts ;
2018-09-13 21:56:31 +02:00
NSString * formattedEnd ;
2018-10-22 18:08:46 +02:00
SOGoUser * ownerUser ;
2016-02-11 16:54:07 +01:00
id o ;
info = [ NSMutableDictionary dictionary ] ;
conflicts = [ NSMutableArray array ] ;
2016-03-09 16:06:18 +01:00
if ( currentAttendee )
{
if ( [ currentAttendee cn ] )
[ info setObject : [ currentAttendee cn ] forKey : @ "attendee_name" ] ;
if ( [ currentAttendee rfc822Email ] )
[ info setObject : [ currentAttendee rfc822Email ] forKey : @ "attendee_email" ] ;
}
2018-10-22 18:08:46 +02:00
else if ( [ owner isEqualToString : currentUID ] )
{
ownerUser = [ SOGoUser userWithLogin : owner ] ;
if ( [ ownerUser cn ] )
[ info setObject : [ ownerUser cn ] forKey : @ "attendee_name" ] ;
if ( [ ownerUser systemEmail ] )
[ info setObject : [ ownerUser systemEmail ] forKey : @ "attendee_email" ] ;
}
2016-02-11 16:54:07 +01:00
for ( i = 0 ; i < [ fbInfo count ] ; i + + )
{
o = [ fbInfo objectAtIndex : i ] ;
2018-09-13 21:56:31 +02:00
end = [ o objectForKey : @ "endDate" ] ;
if ( [ [ o objectForKey : @ "startDate" ] isDateOnSameDay : end ] )
formattedEnd = [ formatter formattedTime : end ] ;
else
2018-10-22 17:20:06 +02:00
formattedEnd = [ formatter formattedDateAndTime : end ] ;
2018-09-13 21:56:31 +02:00
2016-02-11 16:54:07 +01:00
[ conflicts addObject : [ NSDictionary dictionaryWithObjectsAndKeys : [ formatter formattedDateAndTime : [ o objectForKey : @ "startDate" ] ] , @ "startDate" ,
2018-09-13 21:56:31 +02:00
formattedEnd , @ "endDate" , nil ] ] ;
2016-02-11 16:54:07 +01:00
}
[ info setObject : conflicts forKey : @ "conflicts" ] ;
2018-09-13 21:56:31 +02:00
// We immediately raise an exception , without processing the possible other attendees .
2016-08-04 20:13:00 +02:00
return [ NSException exceptionWithHTTPStatus : 409
2016-02-11 16:54:07 +01:00
reason : [ info jsonRepresentation ] ] ;
2014-07-18 19:57:18 +02:00
}
2016-02-11 16:54:07 +01:00
} // if ( [ fbInfo count ] ) . . .
else if ( currentAttendee && [ user isResource ] )
{
// No conflict , we auto - accept . We do this for resources automatically if no
// double - booking is observed . If it ' s not the desired behavior , just don ' t
// set the resource as one !
[ [ currentAttendee attributes ] removeObjectForKey : @ "RSVP" ] ;
[ currentAttendee setParticipationStatus : iCalPersonPartStatAccepted ] ;
2020-01-06 15:40:25 +01:00
_resourceHasAutoAccepted = YES ;
2014-07-04 15:51:41 +02:00
}
2018-09-13 21:56:31 +02:00
}
2016-02-11 16:54:07 +01:00
2011-04-25 12:31:08 +02:00
return nil ;
}
//
//
//
- ( NSException * ) _handleAddedUsers : ( NSArray * ) attendees
2014-07-04 15:51:41 +02:00
fromEvent : ( iCalEvent * ) newEvent
2016-02-11 16:54:07 +01:00
force : ( BOOL ) forceSave
2011-04-25 12:31:08 +02:00
{
2007-11-18 11:16:25 +01:00
iCalPerson * currentAttendee ;
2011-04-25 12:31:08 +02:00
NSEnumerator * enumerator ;
2007-11-18 11:16:25 +01:00
NSString * currentUID ;
2011-04-25 12:31:08 +02:00
NSException * e ;
2014-07-04 15:51:41 +02:00
2011-04-25 12:31:08 +02:00
// We check for conflicts
2016-02-11 16:54:07 +01:00
if ( ( e = [ self _handleAttendeesConflicts : attendees forEvent : newEvent force : forceSave ] ) )
2011-04-25 12:31:08 +02:00
return e ;
2016-02-11 16:54:07 +01:00
if ( ( e = [ self _handleAttendeesAvailability : attendees forEvent : newEvent ] ) )
2014-07-04 15:51:41 +02:00
return e ;
2007-11-18 11:16:25 +01:00
enumerator = [ attendees objectEnumerator ] ;
while ( ( currentAttendee = [ enumerator nextObject ] ) )
2014-07-18 19:57:18 +02:00
{
2015-07-22 22:45:42 +02:00
currentUID = [ currentAttendee uidInContext : context ] ;
2014-07-18 19:57:18 +02:00
if ( currentUID )
2014-07-04 15:51:41 +02:00
[ self _addOrUpdateEvent : newEvent
2017-07-19 17:05:16 +02:00
oldEvent : nil
2014-07-04 15:51:41 +02:00
forUID : currentUID
owner : owner ] ;
2014-07-18 19:57:18 +02:00
}
2014-07-04 15:51:41 +02:00
2011-04-25 12:31:08 +02:00
return nil ;
2007-11-18 11:16:25 +01:00
}
2006-06-15 21:34:10 +02:00
2012-04-23 14:58:56 +02:00
//
//
//
- ( void ) _addOrDeleteAttendees : ( NSArray * ) theAttendees
inRecurrenceExceptionsForEvent : ( iCalEvent * ) theEvent
add : ( BOOL ) shouldAdd
{
NSArray * events ;
iCalEvent * e ;
int i , j ;
// We don ' t add / delete attendees to all recurrence exceptions if
// the modification was actually NOT made on the master event
if ( [ theEvent recurrenceId ] )
return ;
2014-12-22 21:26:22 +01:00
2012-04-23 14:58:56 +02:00
events = [ [ theEvent parent ] events ] ;
for ( i = 0 ; i < [ events count ] ; i + + )
{
e = [ events objectAtIndex : i ] ;
if ( [ e recurrenceId ] )
2015-11-03 22:28:03 +01:00
for ( j = 0 ; j < [ theAttendees count ] ; j + + ) {
2017-07-19 17:05:16 +02:00
if ( shouldAdd )
2014-12-22 21:26:22 +01:00
[ e addToAttendees : [ theAttendees objectAtIndex : j ] ] ;
2017-07-19 17:05:16 +02:00
else
2014-12-22 21:26:22 +01:00
[ e removeFromAttendees : [ theAttendees objectAtIndex : j ] ] ;
2015-11-03 22:28:03 +01:00
}
2012-04-23 14:58:56 +02:00
}
}
2014-12-22 21:26:22 +01:00
2009-04-30 23:17:55 +02:00
//
//
//
2011-04-25 12:31:08 +02:00
- ( NSException * ) _handleUpdatedEvent : ( iCalEvent * ) newEvent
fromOldEvent : ( iCalEvent * ) oldEvent
2016-02-11 16:54:07 +01:00
force : ( BOOL ) forceSave
2007-11-18 11:16:25 +01:00
{
2012-07-10 02:29:13 +02:00
NSArray * addedAttendees , * deletedAttendees , * updatedAttendees ;
2007-11-18 11:16:25 +01:00
iCalEventChanges * changes ;
2011-04-25 12:31:08 +02:00
NSException * ex ;
2006-06-15 21:34:10 +02:00
2012-07-10 02:29:13 +02:00
addedAttendees = nil ;
deletedAttendees = nil ;
updatedAttendees = nil ;
2007-11-18 11:16:25 +01:00
changes = [ newEvent getChangesRelativeToEvent : oldEvent ] ;
2010-07-16 17:03:16 +02:00
if ( [ changes sequenceShouldBeIncreased ] )
{
// Set new attendees status to "needs action" and recompute changes when
2011-04-25 12:31:08 +02:00
// the list of attendees has changed . The list might have changed since
// by changing a major property of the event , we remove all the delegation
// chains to "other" attendees
2010-07-16 17:03:16 +02:00
if ( [ self _requireResponseFromAttendees : newEvent ] )
changes = [ newEvent getChangesRelativeToEvent : oldEvent ] ;
}
2011-04-25 12:31:08 +02:00
2012-07-10 02:29:13 +02:00
deletedAttendees = [ changes deletedAttendees ] ;
2012-04-23 14:58:56 +02:00
2012-07-10 02:29:13 +02:00
if ( [ deletedAttendees count ] )
2007-11-18 11:16:25 +01:00
{
2017-11-10 17:16:46 +01:00
// We delete the attendees in all exception occurences , if
// the attendees were removed from the master event .
[ self _addOrDeleteAttendees : deletedAttendees
inRecurrenceExceptionsForEvent : newEvent
add : NO ] ;
2012-07-10 02:29:13 +02:00
[ self _handleRemovedUsers : deletedAttendees
2011-06-01 19:49:12 +02:00
withRecurrenceId : [ newEvent recurrenceId ] ] ;
2016-12-16 21:51:19 +01:00
if ( [ self _shouldScheduleEvent : [ newEvent organizer ] ] )
[ self sendEMailUsingTemplateNamed : @ "Deletion"
forObject : [ newEvent itipEntryWithMethod : @ "cancel" ]
previousObject : oldEvent
toAttendees : deletedAttendees
withType : @ "calendar:cancellation" ] ;
2007-11-18 11:16:25 +01:00
}
2011-06-01 19:49:12 +02:00
2016-02-11 16:54:07 +01:00
if ( ( ex = [ self _handleAttendeesConflicts : [ newEvent attendees ] forEvent : newEvent force : forceSave ] ) )
2014-07-04 15:51:41 +02:00
return ex ;
2016-02-11 16:54:07 +01:00
if ( ( ex = [ self _handleAttendeesAvailability : [ newEvent attendees ] forEvent : newEvent ] ) )
2011-04-25 12:31:08 +02:00
return ex ;
2012-07-10 02:29:13 +02:00
addedAttendees = [ changes insertedAttendees ] ;
2012-04-23 14:58:56 +02:00
// We insert the attendees in all exception occurences , if
// the attendees were added to the master event .
2012-07-10 02:29:13 +02:00
[ self _addOrDeleteAttendees : addedAttendees
2014-12-22 21:26:22 +01:00
inRecurrenceExceptionsForEvent : newEvent
add : YES ] ;
2012-04-23 14:58:56 +02:00
2007-11-18 11:16:25 +01:00
if ( [ changes sequenceShouldBeIncreased ] )
{
[ newEvent increaseSequence ] ;
2014-12-22 21:26:22 +01:00
2008-12-09 16:29:39 +01:00
// Update attendees calendars and send them an update
2014-12-22 21:26:22 +01:00
// notification by email . We ignore the newly added
// attendees as we don ' t want to send them invitation
// update emails
2007-11-18 11:16:25 +01:00
[ self _handleSequenceUpdateInEvent : newEvent
2012-07-10 02:29:13 +02:00
ignoringAttendees : addedAttendees
2011-06-01 19:49:12 +02:00
fromOldEvent : oldEvent ] ;
2007-11-18 11:16:25 +01:00
}
else
2008-11-29 00:10:41 +01:00
{
// If other attributes have changed , update the event
// in each attendee ' s calendar
if ( [ [ changes updatedProperties ] count ] )
2014-07-18 19:57:18 +02:00
{
NSEnumerator * enumerator ;
iCalPerson * currentAttendee ;
NSString * currentUID ;
2009-08-25 23:28:24 +02:00
updatedAttendees = [ newEvent attendees ] ;
2014-07-18 19:57:18 +02:00
enumerator = [ updatedAttendees objectEnumerator ] ;
while ( ( currentAttendee = [ enumerator nextObject ] ) )
{
2015-07-22 22:45:42 +02:00
currentUID = [ currentAttendee uidInContext : context ] ;
2014-07-18 19:57:18 +02:00
if ( currentUID )
[ self _addOrUpdateEvent : newEvent
2017-07-19 17:05:16 +02:00
oldEvent : oldEvent
2014-07-18 19:57:18 +02:00
forUID : currentUID
owner : owner ] ;
}
}
2008-11-29 00:10:41 +01:00
}
2006-06-15 21:34:10 +02:00
2012-07-10 02:29:13 +02:00
if ( [ addedAttendees count ] )
2007-11-18 11:16:25 +01:00
{
2008-11-29 00:10:41 +01:00
// Send an invitation to new attendees
2016-02-11 16:54:07 +01:00
if ( ( ex = [ self _handleAddedUsers : addedAttendees fromEvent : newEvent force : forceSave ] ) )
2014-07-18 19:57:18 +02:00
return ex ;
2016-12-16 21:51:19 +01:00
if ( [ self _shouldScheduleEvent : [ newEvent organizer ] ] )
[ self sendEMailUsingTemplateNamed : @ "Invitation"
forObject : [ newEvent itipEntryWithMethod : @ "request" ]
previousObject : oldEvent
toAttendees : addedAttendees
withType : @ "calendar:invitation" ] ;
2007-11-18 11:16:25 +01:00
}
2016-11-01 20:26:59 +01:00
2016-11-03 16:47:21 +01:00
if ( [ changes hasMajorChanges ] )
2016-11-01 20:26:59 +01:00
[ self sendReceiptEmailForObject : newEvent
addedAttendees : addedAttendees
deletedAttendees : deletedAttendees
updatedAttendees : updatedAttendees
operation : EventUpdated ] ;
2012-07-10 02:29:13 +02:00
2011-04-25 12:31:08 +02:00
return nil ;
2007-11-18 11:16:25 +01:00
}
2006-06-15 21:34:10 +02:00
2010-08-11 16:52:11 +02:00
//
// Workflow : + - - - - - - - - - - - - - - - - - - - - - - +
// | |
// [ saveComponent : ] - - -> _handleAddedUsers : fromEvent : < - + |
// | | v
2017-07-19 17:05:16 +02:00
// + - - - - - - - - - - - -> _handleUpdatedEvent : fromOldEvent : - - -> _addOrUpdateEvent : oldEvent : forUID : owner : < - - - - - - - - - - - +
2010-08-11 16:52:11 +02:00
// | | ^ |
// v v | |
2014-07-04 15:51:41 +02:00
// _handleRemovedUsers : withRecurrenceId : _handleSequenceUpdateInEvent : ignoringAttendees : fromOldEvent : |
2010-08-11 16:52:11 +02:00
// | |
// | [ DELETEAction : ] |
// | | { _handleAdded / Updated . . . } < - - + |
// | v | |
// | [ prepareDeleteOccurence : ] [ PUTAction : ] |
// | | | | |
// v v v v |
// _removeEventFromUID : owner : withRecurrenceId : [ changeParticipationStatus : withDelegate : forRecurrenceId : ] |
// | | |
// | v |
// + - - - - - - - - - - - - - - - - - - - - - - - -> _handleAttendee : withDelegate : ownerUser : statusChange : inEvent : - - -> [ sendResponseToOrganizer : from : ]
// |
// v
// _updateAttendee : withDelegate : ownerUser : forEventUID : withRecurrenceId : withSequence : forUID : shouldAddSentBy :
//
//
2011-04-25 12:31:08 +02:00
- ( NSException * ) saveComponent : ( iCalEvent * ) newEvent
2016-02-11 16:54:07 +01:00
{
return [ self saveComponent : newEvent force : NO ] ;
}
- ( NSException * ) saveComponent : ( iCalEvent * ) newEvent
force : ( BOOL ) forceSave
2007-11-18 11:16:25 +01:00
{
2009-08-25 23:28:24 +02:00
iCalEvent * oldEvent , * oldMasterEvent ;
2008-12-03 17:18:12 +01:00
NSCalendarDate * recurrenceId ;
NSString * recurrenceTime ;
2008-08-14 00:30:55 +02:00
SOGoUser * ownerUser ;
2011-04-25 12:31:08 +02:00
NSArray * attendees ;
NSException * ex ;
2012-07-13 22:59:09 +02:00
2007-11-18 11:16:25 +01:00
[ [ newEvent parent ] setMethod : @ "" ] ;
2009-08-25 23:28:24 +02:00
ownerUser = [ SOGoUser userWithLogin : owner ] ;
2008-12-17 16:35:53 +01:00
2009-05-06 23:57:21 +02:00
[ self expandGroupsInEvent : newEvent ] ;
2014-02-03 17:03:23 +01:00
// We first update the event . It is important to do this initially
2011-03-30 01:25:40 +02:00
// as the event ' s UID might get modified .
[ super updateComponent : newEvent ] ;
2008-11-26 17:26:32 +01:00
2009-08-11 04:06:27 +02:00
if ( [ self isNew ] )
2007-11-18 11:16:25 +01:00
{
2009-08-11 04:06:27 +02:00
// New event - - send invitation to all attendees
attendees = [ newEvent attendeesWithoutUser : ownerUser ] ;
2014-07-18 19:57:18 +02:00
2014-02-24 13:55:21 +01:00
// We catch conflicts and abort the save process immediately
// in case of one with resources
2016-02-11 16:54:07 +01:00
if ( ( ex = [ self _handleAddedUsers : attendees fromEvent : newEvent force : forceSave ] ) )
2014-02-24 13:55:21 +01:00
return ex ;
2014-07-18 19:57:18 +02:00
2009-08-11 04:06:27 +02:00
if ( [ attendees count ] )
2014-07-18 19:57:18 +02:00
{
2016-12-16 21:51:19 +01:00
if ( [ self _shouldScheduleEvent : [ newEvent organizer ] ] )
[ self sendEMailUsingTemplateNamed : @ "Invitation"
forObject : [ newEvent itipEntryWithMethod : @ "request" ]
previousObject : nil
toAttendees : attendees
withType : @ "calendar:invitation" ] ;
2014-07-18 19:57:18 +02:00
}
2012-07-10 02:29:13 +02:00
[ self sendReceiptEmailForObject : newEvent
2014-07-18 19:57:18 +02:00
addedAttendees : attendees
deletedAttendees : nil
updatedAttendees : nil
operation : EventCreated ] ;
2009-08-11 04:06:27 +02:00
}
2014-09-12 14:34:15 +02:00
else
2009-08-11 04:06:27 +02:00
{
2009-10-19 14:56:52 +02:00
BOOL hasOrganizer ;
2014-07-18 19:57:18 +02:00
2009-08-11 04:06:27 +02:00
// Event is modified - - sent update status to all attendees
// and modify their calendars .
recurrenceId = [ newEvent recurrenceId ] ;
if ( recurrenceId = = nil )
2014-07-18 19:57:18 +02:00
oldEvent = [ self component : NO secure : NO ] ;
2008-11-28 20:54:51 +01:00
else
2014-07-18 19:57:18 +02:00
{
// If recurrenceId is defined , find the specified occurence
// within the repeating vEvent .
recurrenceTime = [ NSString stringWithFormat : @ "%f" , [ recurrenceId timeIntervalSince1970 ] ] ;
oldEvent = ( iCalEvent * ) [ self lookupOccurrence : recurrenceTime ] ;
if ( oldEvent = = nil ) // If no occurence found , create one
oldEvent = ( iCalEvent * ) [ self newOccurenceWithID : recurrenceTime ] ;
}
2011-06-01 19:49:12 +02:00
oldMasterEvent = ( iCalEvent * ) [ [ oldEvent parent ] firstChildWithTag : [ self componentTag ] ] ;
2009-10-19 14:56:52 +02:00
hasOrganizer = [ [ [ oldMasterEvent organizer ] email ] length ] ;
2014-07-18 19:57:18 +02:00
2009-10-19 14:56:52 +02:00
if ( ! hasOrganizer || [ oldMasterEvent userIsOrganizer : ownerUser ] )
2014-07-18 19:57:18 +02:00
// The owner is the organizer of the event ; handle the modifications . We aslo
// catch conflicts just like when the events are created
2016-02-11 16:54:07 +01:00
if ( ( ex = [ self _handleUpdatedEvent : newEvent fromOldEvent : oldEvent force : forceSave ] ) )
2014-07-18 19:57:18 +02:00
return ex ;
2007-01-31 21:15:28 +01:00
}
2014-07-18 19:57:18 +02:00
2011-03-30 01:25:40 +02:00
[ super saveComponent : newEvent ] ;
2011-07-05 15:24:25 +02:00
[ self flush ] ;
2011-04-25 12:31:08 +02:00
return nil ;
2006-06-15 21:34:10 +02:00
}
2008-11-10 16:38:05 +01:00
//
// This method is used to update the status of an attendee .
//
// - theOwnerUser is owner of the calendar where the attendee
// participation state has changed .
// - uid is the actual UID of the user for whom we must
// update the calendar event ( with the participation change )
2010-08-19 19:40:28 +02:00
// - delegate is the delegate attendee if any
2008-11-10 16:38:05 +01:00
//
// This method is called multiple times , in order to update the
// status of the attendee in calendars for the particular event UID .
//
2007-11-18 11:16:25 +01:00
- ( NSException * ) _updateAttendee : ( iCalPerson * ) attendee
2009-08-27 18:20:41 +02:00
withDelegate : ( iCalPerson * ) delegate
2008-11-05 22:04:16 +01:00
ownerUser : ( SOGoUser * ) theOwnerUser
2014-09-24 18:02:12 +02:00
forEventUID : ( NSString * ) eventUID
withRecurrenceId : ( NSCalendarDate * ) recurrenceId
withSequence : ( NSNumber * ) sequence
forUID : ( NSString * ) uid
2014-07-18 19:57:18 +02:00
shouldAddSentBy : ( BOOL ) b
2007-11-08 20:56:18 +01:00
{
2007-11-18 11:16:25 +01:00
SOGoAppointmentObject * eventObject ;
2008-11-22 08:20:22 +01:00
iCalCalendar * calendar ;
iCalEntityObject * event ;
2009-08-27 18:20:41 +02:00
iCalPerson * otherAttendee , * otherDelegate ;
2014-07-30 20:51:00 +02:00
NSString * recurrenceTime , * delegateEmail ;
2007-11-08 20:56:18 +01:00
NSException * error ;
2009-08-27 18:20:41 +02:00
BOOL addDelegate , removeDelegate ;
2006-06-15 21:34:10 +02:00
2019-02-04 17:52:26 +01:00
// If the atttende ' s role is NON - PARTICIPANT , we write nothing to its calendar
if ( [ [ attendee role ] caseInsensitiveCompare : @ "NON-PARTICIPANT" ] = = NSOrderedSame )
2019-02-07 20:10:39 +01:00
return nil ;
2019-02-04 17:52:26 +01:00
2007-11-18 11:16:25 +01:00
error = nil ;
eventObject = [ self _lookupEvent : eventUID forUID : uid ] ;
if ( ! [ eventObject isNew ] )
2007-11-08 20:56:18 +01:00
{
2008-11-22 08:20:22 +01:00
if ( recurrenceId = = nil )
2014-07-18 19:57:18 +02:00
{
// We must update main event and all its occurences ( if any ) .
calendar = [ eventObject calendar : NO secure : NO ] ;
event = ( iCalEntityObject * ) [ calendar firstChildWithTag : [ self componentTag ] ] ;
}
2008-11-22 08:20:22 +01:00
else
2014-07-18 19:57:18 +02:00
{
// If recurrenceId is defined , find the specified occurence
// within the repeating vEvent .
recurrenceTime = [ NSString stringWithFormat : @ "%f" , [ recurrenceId timeIntervalSince1970 ] ] ;
event = [ eventObject lookupOccurrence : recurrenceTime ] ;
if ( event = = nil )
// If no occurence found , create one
event = [ eventObject newOccurenceWithID : recurrenceTime ] ;
}
2010-08-11 16:52:11 +02:00
if ( [ [ event sequence ] intValue ] <= [ sequence intValue ] )
2014-07-18 19:57:18 +02:00
{
SOGoUser * currentUser ;
currentUser = [ context activeUser ] ;
otherAttendee = [ event userAsAttendee : theOwnerUser ] ;
2009-08-27 18:20:41 +02:00
delegateEmail = [ otherAttendee delegatedTo ] ;
if ( [ delegateEmail length ] )
2009-09-10 20:31:20 +02:00
delegateEmail = [ delegateEmail rfc822Email ] ;
2009-08-27 18:20:41 +02:00
if ( [ delegateEmail length ] )
2010-05-05 15:56:19 +02:00
otherDelegate = [ event findAttendeeWithEmail : delegateEmail ] ;
2009-08-27 18:20:41 +02:00
else
2015-11-03 22:28:03 +01:00
otherDelegate = nil ;
2014-07-18 19:57:18 +02:00
2010-08-19 19:40:28 +02:00
/ * we handle the addition / deletion of delegate users * /
2009-08-27 18:20:41 +02:00
addDelegate = NO ;
removeDelegate = NO ;
if ( delegate )
{
if ( otherDelegate )
{
if ( ! [ delegate hasSameEmailAddress : otherDelegate ] )
{
removeDelegate = YES ;
addDelegate = YES ;
}
}
else
2014-09-24 18:02:12 +02:00
addDelegate = YES ;
2009-08-27 18:20:41 +02:00
}
else
{
if ( otherDelegate )
2014-09-24 18:02:12 +02:00
removeDelegate = YES ;
2009-08-27 18:20:41 +02:00
}
2014-07-18 19:57:18 +02:00
2009-08-27 18:20:41 +02:00
if ( removeDelegate )
2014-07-18 19:57:18 +02:00
{
while ( otherDelegate )
{
[ event removeFromAttendees : otherDelegate ] ;
// Verify if the delegate was already delegate
delegateEmail = [ otherDelegate delegatedTo ] ;
if ( [ delegateEmail length ] )
delegateEmail = [ delegateEmail rfc822Email ] ;
if ( [ delegateEmail length ] )
otherDelegate = [ event findAttendeeWithEmail : delegateEmail ] ;
else
2015-11-03 22:28:03 +01:00
otherDelegate = nil ;
2014-07-18 19:57:18 +02:00
}
}
2009-08-27 18:20:41 +02:00
if ( addDelegate )
[ event addToAttendees : delegate ] ;
2014-07-18 19:57:18 +02:00
[ otherAttendee setPartStat : [ attendee partStat ] ] ;
2009-08-27 18:20:41 +02:00
[ otherAttendee setDelegatedTo : [ attendee delegatedTo ] ] ;
[ otherAttendee setDelegatedFrom : [ attendee delegatedFrom ] ] ;
2014-07-18 19:57:18 +02:00
2012-11-30 19:57:55 +01:00
// Remove the RSVP attribute , as an action from the attendee
// was actually performed , and this confuses iCal ( bug #1850 )
[ [ otherAttendee attributes ] removeObjectForKey : @ "RSVP" ] ;
2014-07-18 19:57:18 +02:00
// If one has accepted / declined an invitation on behalf of
// the attendee , we add the user to the SENT - BY attribute .
if ( b && ! [ [ currentUser login ] isEqualToString : [ theOwnerUser login ] ] )
{
NSString * currentEmail , * quotedEmail ;
currentEmail = [ [ currentUser allEmails ] objectAtIndex : 0 ] ;
quotedEmail = [ NSString stringWithFormat : @ "\" MAILTO : % @ \ "" , currentEmail ] ;
[ otherAttendee setValue : 0 ofAttribute : @ "SENT-BY"
2009-08-11 18:52:10 +02:00
to : quotedEmail ] ;
2014-07-18 19:57:18 +02:00
}
else
{
// We must REMOVE any SENT - BY here . This is important since if A accepted
2014-12-19 01:34:05 +01:00
// the event for B and then , B changes by theirself their participation status ,
2014-07-18 19:57:18 +02:00
// we don ' t want to keep the previous SENT - BY attribute there .
[ ( NSMutableDictionary * ) [ otherAttendee attributes ] removeObjectForKey : @ "SENT-BY" ] ;
}
}
2008-12-09 16:29:39 +01:00
2014-07-30 20:51:00 +02:00
// We save the updated iCalendar in the database .
2018-03-15 20:43:26 +01:00
[ event setLastModified : [ NSCalendarDate calendarDate ] ] ;
2014-07-30 20:51:00 +02:00
error = [ eventObject saveCalendar : [ event parent ] ] ;
2007-11-18 11:16:25 +01:00
}
2014-07-18 19:57:18 +02:00
2007-11-18 11:16:25 +01:00
return error ;
}
2008-11-10 16:38:05 +01:00
//
2012-11-30 19:57:55 +01:00
// This method is invoked from the SOGo Web interface or from the DAV interface .
2008-11-10 16:38:05 +01:00
//
// - theOwnerUser is owner of the calendar where the attendee
// participation state has changed .
//
2007-11-18 11:16:25 +01:00
- ( NSException * ) _handleAttendee : ( iCalPerson * ) attendee
2009-08-27 18:20:41 +02:00
withDelegate : ( iCalPerson * ) delegate
2008-11-05 22:04:16 +01:00
ownerUser : ( SOGoUser * ) theOwnerUser
2014-09-24 18:02:12 +02:00
statusChange : ( NSString * ) newStatus
inEvent : ( iCalEvent * ) event
2007-11-18 11:16:25 +01:00
{
2016-12-16 21:51:19 +01:00
iCalPerson * otherAttendee , * otherDelegate ;
2012-07-13 22:59:09 +02:00
NSString * currentStatus , * organizerUID ;
2008-11-18 01:06:37 +01:00
SOGoUser * ownerUser , * currentUser ;
2016-12-16 21:51:19 +01:00
NSString * delegateEmail ;
2008-11-10 16:38:05 +01:00
NSException * ex ;
2006-06-15 21:34:10 +02:00
2016-12-16 21:51:19 +01:00
BOOL addDelegate , removeDelegate ;
2007-11-18 11:16:25 +01:00
currentStatus = [ attendee partStat ] ;
2009-09-10 20:31:20 +02:00
otherAttendee = attendee ;
2016-12-16 21:51:19 +01:00
ex = nil ;
2009-09-10 20:31:20 +02:00
delegateEmail = [ otherAttendee delegatedTo ] ;
if ( [ delegateEmail length ] )
delegateEmail = [ delegateEmail rfc822Email ] ;
if ( [ delegateEmail length ] )
2010-05-05 15:56:19 +02:00
otherDelegate = [ event findAttendeeWithEmail : delegateEmail ] ;
2009-09-10 20:31:20 +02:00
else
2010-08-19 19:40:28 +02:00
otherDelegate = nil ;
2009-09-10 20:31:20 +02:00
2016-12-16 21:51:19 +01:00
// We handle the addition / deletion of delegate users
2009-09-10 20:31:20 +02:00
addDelegate = NO ;
removeDelegate = NO ;
if ( delegate )
{
if ( otherDelegate )
2014-07-18 19:57:18 +02:00
{
// There was already a delegate
if ( ! [ delegate hasSameEmailAddress : otherDelegate ] )
{
// The delegate has changed
removeDelegate = YES ;
addDelegate = YES ;
}
}
2009-09-10 20:31:20 +02:00
else
2014-07-18 19:57:18 +02:00
// There was no previous delegate
addDelegate = YES ;
2009-09-10 20:31:20 +02:00
}
else
{
if ( otherDelegate )
2014-07-18 19:57:18 +02:00
// The user has removed the delegate
removeDelegate = YES ;
2009-09-10 20:31:20 +02:00
}
if ( addDelegate || removeDelegate
2014-07-18 19:57:18 +02:00
|| [ currentStatus caseInsensitiveCompare : newStatus ] ! = NSOrderedSame )
2007-11-18 11:16:25 +01:00
{
2012-11-30 19:57:55 +01:00
NSMutableArray * delegates ;
NSString * delegatedUID ;
delegatedUID = nil ;
2007-11-18 11:16:25 +01:00
[ attendee setPartStat : newStatus ] ;
2008-11-05 22:04:16 +01:00
// If one has accepted / declined an invitation on behalf of
// the attendee , we add the user to the SENT - BY attribute .
currentUser = [ context activeUser ] ;
if ( ! [ [ currentUser login ] isEqualToString : [ theOwnerUser login ] ] )
2014-07-18 19:57:18 +02:00
{
2009-08-11 18:52:10 +02:00
NSString * currentEmail , * quotedEmail ;
currentEmail = [ [ currentUser allEmails ] objectAtIndex : 0 ] ;
2014-07-18 19:57:18 +02:00
quotedEmail = [ NSString stringWithFormat : @ "\" MAILTO : % @ \ "" , currentEmail ] ;
2009-08-11 18:52:10 +02:00
[ attendee setValue : 0 ofAttribute : @ "SENT-BY"
to : quotedEmail ] ;
2014-07-18 19:57:18 +02:00
}
2008-11-13 14:41:43 +01:00
else
2014-07-18 19:57:18 +02:00
{
// We must REMOVE any SENT - BY here . This is important since if A accepted
2014-12-19 01:34:05 +01:00
// the event for B and then , B changes by theirself their participation status ,
2014-07-18 19:57:18 +02:00
// we don ' t want to keep the previous SENT - BY attribute there .
[ ( NSMutableDictionary * ) [ attendee attributes ] removeObjectForKey : @ "SENT-BY" ] ;
}
2009-09-10 20:31:20 +02:00
[ attendee setDelegatedTo : [ delegate email ] ] ;
2014-07-18 19:57:18 +02:00
2009-09-10 20:31:20 +02:00
if ( removeDelegate )
2014-07-18 19:57:18 +02:00
{
delegates = [ NSMutableArray array ] ;
while ( otherDelegate )
{
[ delegates addObject : otherDelegate ] ;
2015-07-22 22:45:42 +02:00
delegatedUID = [ otherDelegate uidInContext : context ] ;
2014-07-18 19:57:18 +02:00
if ( delegatedUID )
2014-12-19 01:34:05 +01:00
// Delegate attendee is a local user ; remove event from their calendar
2014-07-18 19:57:18 +02:00
[ self _removeEventFromUID : delegatedUID
owner : [ theOwnerUser login ]
withRecurrenceId : [ event recurrenceId ] ] ;
[ event removeFromAttendees : otherDelegate ] ;
// Verify if the delegate was already delegated
delegateEmail = [ otherDelegate delegatedTo ] ;
if ( [ delegateEmail length ] )
delegateEmail = [ delegateEmail rfc822Email ] ;
if ( [ delegateEmail length ] )
otherDelegate = [ event findAttendeeWithEmail : delegateEmail ] ;
else
2015-11-03 22:28:03 +01:00
otherDelegate = nil ;
2014-07-18 19:57:18 +02:00
}
2016-12-16 21:51:19 +01:00
if ( [ self _shouldScheduleEvent : [ event organizer ] ] )
[ self sendEMailUsingTemplateNamed : @ "Deletion"
forObject : [ event itipEntryWithMethod : @ "cancel" ]
previousObject : nil
toAttendees : delegates
withType : @ "calendar:cancellation" ] ;
2014-07-18 19:57:18 +02:00
} // if ( removeDelegate )
2009-09-10 20:31:20 +02:00
if ( addDelegate )
2014-07-18 19:57:18 +02:00
{
2015-07-22 22:45:42 +02:00
delegatedUID = [ delegate uidInContext : context ] ;
2014-07-18 19:57:18 +02:00
delegates = [ NSArray arrayWithObject : delegate ] ;
[ event addToAttendees : delegate ] ;
if ( delegatedUID )
2014-12-19 01:34:05 +01:00
// Delegate attendee is a local user ; add event to their calendar
2014-07-18 19:57:18 +02:00
[ self _addOrUpdateEvent : event
2017-07-19 17:05:16 +02:00
oldEvent : nil
2014-07-18 19:57:18 +02:00
forUID : delegatedUID
owner : [ theOwnerUser login ] ] ;
2016-12-16 21:51:19 +01:00
if ( [ self _shouldScheduleEvent : [ event organizer ] ] )
[ self sendEMailUsingTemplateNamed : @ "Invitation"
forObject : [ event itipEntryWithMethod : @ "request" ]
previousObject : nil
toAttendees : delegates
withType : @ "calendar:invitation" ] ;
2014-07-18 19:57:18 +02:00
} // if ( addDelegate )
2009-09-10 20:31:20 +02:00
2008-11-10 16:38:05 +01:00
// If the current user isn ' t the organizer of the event
// that has just been updated , we update the event and
// send a notification
2009-08-25 23:28:24 +02:00
ownerUser = [ SOGoUser userWithLogin : owner ] ;
2008-08-09 18:47:13 +02:00
if ( ! ( ex || [ event userIsOrganizer : ownerUser ] ) )
2014-07-18 19:57:18 +02:00
{
if ( [ event isStillRelevant ] )
[ self sendResponseToOrganizer : event
2012-11-30 21:11:03 +01:00
from : ownerUser ] ;
2014-07-18 19:57:18 +02:00
2015-07-22 22:45:42 +02:00
organizerUID = [ [ event organizer ] uidInContext : context ] ;
2014-07-18 19:57:18 +02:00
// Event is an exception to a recurring event ; retrieve organizer from master event
if ( ! organizerUID )
2015-07-22 22:45:42 +02:00
organizerUID = [ [ ( iCalEntityObject * ) [ [ event parent ] firstChildWithTag : [ self componentTag ] ] organizer ] uidInContext : context ] ;
2014-07-18 19:57:18 +02:00
if ( organizerUID )
// Update the attendee in organizer ' s calendar .
ex = [ self _updateAttendee : attendee
2009-08-27 18:20:41 +02:00
withDelegate : delegate
ownerUser : theOwnerUser
forEventUID : [ event uid ]
withRecurrenceId : [ event recurrenceId ]
withSequence : [ event sequence ]
forUID : organizerUID
2014-07-18 19:57:18 +02:00
shouldAddSentBy : YES ] ;
}
2010-05-05 15:56:19 +02:00
// We update the calendar of all attendees that are
2008-11-05 22:04:16 +01:00
// local to the system . This is useful in case user A accepts
// invitation from organizer B and users C , D , E who are also
// attendees need to verify if A has accepted .
NSArray * attendees ;
iCalPerson * att ;
NSString * uid ;
int i ;
2014-07-18 19:57:18 +02:00
2008-11-05 22:04:16 +01:00
attendees = [ event attendees ] ;
for ( i = 0 ; i < [ attendees count ] ; i + + )
2014-07-18 19:57:18 +02:00
{
att = [ attendees objectAtIndex : i ] ;
2015-07-22 22:45:42 +02:00
uid = [ att uidInContext : context ] ;
2014-07-18 19:57:18 +02:00
if ( uid && att ! = attendee && ! [ uid isEqualToString : delegatedUID ] )
2009-08-27 18:20:41 +02:00
[ self _updateAttendee : attendee
withDelegate : delegate
ownerUser : theOwnerUser
forEventUID : [ event uid ]
withRecurrenceId : [ event recurrenceId ]
withSequence : [ event sequence ]
forUID : uid
shouldAddSentBy : YES ] ;
2014-07-18 19:57:18 +02:00
}
2007-11-18 11:16:25 +01:00
}
2014-07-18 19:57:18 +02:00
2007-11-18 11:16:25 +01:00
return ex ;
}
2006-06-15 21:34:10 +02:00
2014-12-16 15:20:27 +01:00
//
//
//
2008-07-09 17:43:48 +02:00
- ( NSDictionary * ) _caldavSuccessCodeWithRecipient : ( NSString * ) recipient
{
NSMutableArray * element ;
NSDictionary * code ;
2009-10-13 16:10:17 +02:00
element = [ NSMutableArray array ] ;
2014-07-18 19:57:18 +02:00
[ element addObject : davElementWithContent ( @ "recipient" , XMLNS_CALDAV , recipient ) ] ;
[ element addObject : davElementWithContent ( @ "request-status" , XMLNS_CALDAV , @ "2.0;Success" ) ] ;
code = davElementWithContent ( @ "response" , XMLNS_CALDAV , element ) ;
2008-07-09 17:43:48 +02:00
return code ;
}
2008-11-26 13:20:26 +01:00
//
2010-08-11 16:52:11 +02:00
// Old CalDAV scheduling ( draft 4 and below ) methods . We keep them since we still
// advertise for its support but we do everything within the calendar - auto - scheduling code
2008-11-26 13:20:26 +01:00
//
2008-07-05 00:25:27 +02:00
- ( NSArray * ) postCalDAVEventRequestTo : ( NSArray * ) recipients
2014-07-18 19:57:18 +02:00
from : ( NSString * ) originator
2008-07-05 00:25:27 +02:00
{
NSEnumerator * recipientsEnum ;
2010-08-11 16:52:11 +02:00
NSMutableArray * elements ;
NSString * recipient ;
2008-11-18 21:59:08 +01:00
2008-07-05 00:25:27 +02:00
elements = [ NSMutableArray array ] ;
2009-08-25 23:28:24 +02:00
2008-07-05 00:25:27 +02:00
recipientsEnum = [ recipients objectEnumerator ] ;
2010-08-11 16:52:11 +02:00
2008-07-05 00:25:27 +02:00
while ( ( recipient = [ recipientsEnum nextObject ] ) )
2008-07-08 17:42:40 +02:00
if ( [ [ recipient lowercaseString ] hasPrefix : @ "mailto:" ] )
{
2014-07-18 19:57:18 +02:00
[ elements addObject : [ self _caldavSuccessCodeWithRecipient : recipient ] ] ;
2008-07-09 17:43:48 +02:00
}
return elements ;
}
- ( NSArray * ) postCalDAVEventCancelTo : ( NSArray * ) recipients
2014-07-18 19:57:18 +02:00
from : ( NSString * ) originator
2008-07-09 17:43:48 +02:00
{
NSEnumerator * recipientsEnum ;
2010-08-11 16:52:11 +02:00
NSMutableArray * elements ;
NSString * recipient ;
2008-07-09 17:43:48 +02:00
elements = [ NSMutableArray array ] ;
recipientsEnum = [ recipients objectEnumerator ] ;
2010-08-11 16:52:11 +02:00
2008-07-09 17:43:48 +02:00
while ( ( recipient = [ recipientsEnum nextObject ] ) )
if ( [ [ recipient lowercaseString ] hasPrefix : @ "mailto:" ] )
{
2014-07-18 19:57:18 +02:00
[ elements addObject : [ self _caldavSuccessCodeWithRecipient : recipient ] ] ;
2008-07-08 17:42:40 +02:00
}
return elements ;
}
- ( NSArray * ) postCalDAVEventReplyTo : ( NSArray * ) recipients
2014-07-18 19:57:18 +02:00
from : ( NSString * ) originator
2008-07-08 17:42:40 +02:00
{
NSEnumerator * recipientsEnum ;
2010-08-11 16:52:11 +02:00
NSMutableArray * elements ;
NSString * recipient ;
2008-07-08 17:42:40 +02:00
elements = [ NSMutableArray array ] ;
recipientsEnum = [ recipients objectEnumerator ] ;
2010-08-11 16:52:11 +02:00
2008-07-08 17:42:40 +02:00
while ( ( recipient = [ recipientsEnum nextObject ] ) )
if ( [ [ recipient lowercaseString ] hasPrefix : @ "mailto:" ] )
{
2014-07-18 19:57:18 +02:00
[ elements addObject : [ self _caldavSuccessCodeWithRecipient : recipient ] ] ;
2008-07-08 17:42:40 +02:00
}
2008-07-05 00:25:27 +02:00
return elements ;
}
2008-11-10 16:38:05 +01:00
//
2010-08-11 16:52:11 +02:00
//
2008-11-10 16:38:05 +01:00
//
2009-08-27 18:20:41 +02:00
- ( NSException * ) changeParticipationStatus : ( NSString * ) status
withDelegate : ( iCalPerson * ) delegate
2014-12-16 15:20:27 +01:00
alarm : ( iCalAlarm * ) alarm
2007-11-18 11:16:25 +01:00
{
2009-09-10 20:31:20 +02:00
return [ self changeParticipationStatus : status
2014-07-18 19:57:18 +02:00
withDelegate : delegate
2014-12-16 15:20:27 +01:00
alarm : alarm
2009-08-27 18:20:41 +02:00
forRecurrenceId : nil ] ;
2008-11-22 08:20:22 +01:00
}
2011-03-23 21:14:33 +01:00
//
//
//
2009-08-11 18:52:10 +02:00
- ( NSException * ) changeParticipationStatus : ( NSString * ) _status
2009-08-27 18:20:41 +02:00
withDelegate : ( iCalPerson * ) delegate
2014-12-16 15:20:27 +01:00
alarm : ( iCalAlarm * ) alarm
2009-08-11 18:52:10 +02:00
forRecurrenceId : ( NSCalendarDate * ) _recurrenceId
2008-11-22 08:20:22 +01:00
{
iCalCalendar * calendar ;
2007-11-18 11:16:25 +01:00
iCalEvent * event ;
iCalPerson * attendee ;
NSException * ex ;
2009-10-28 18:48:22 +01:00
SOGoUser * ownerUser , * delegatedUser ;
2019-12-04 10:37:08 +01:00
NSString * recurrenceTime , * delegatedUid , * domain ;
2008-08-09 17:21:25 +02:00
2008-11-22 08:20:22 +01:00
event = nil ;
2007-11-18 11:16:25 +01:00
ex = nil ;
2009-10-28 18:48:22 +01:00
delegatedUser = nil ;
2007-11-18 11:16:25 +01:00
2012-07-13 22:59:09 +02:00
calendar = [ [ self calendar : NO secure : NO ] mutableCopy ] ;
[ calendar autorelease ] ;
2012-07-14 04:41:29 +02:00
if ( _recurrenceId )
2008-11-22 08:20:22 +01:00
{
2012-07-14 04:41:29 +02:00
// If _recurrenceId is defined , find the specified occurence
// within the repeating vEvent .
recurrenceTime = [ NSString stringWithFormat : @ "%f" , [ _recurrenceId timeIntervalSince1970 ] ] ;
event = ( iCalEvent * ) [ self lookupOccurrence : recurrenceTime ] ;
2016-12-16 21:51:19 +01:00
// If no occurence found , create one
2012-07-14 04:41:29 +02:00
if ( event = = nil )
event = ( iCalEvent * ) [ self newOccurenceWithID : recurrenceTime ] ;
2008-11-22 08:20:22 +01:00
}
2012-07-14 04:41:29 +02:00
else
// No specific occurence specified ; return the first vEvent of
// the vCalendar .
event = ( iCalEvent * ) [ calendar firstChildWithTag : [ self componentTag ] ] ;
2007-11-18 11:16:25 +01:00
if ( event )
{
2009-08-27 18:20:41 +02:00
// ownerUser will actually be the owner of the calendar
2009-09-10 20:31:20 +02:00
// where the participation change on the event occurs . The particpation
// change will be on the attendee corresponding to the ownerUser .
2009-08-25 23:28:24 +02:00
ownerUser = [ SOGoUser userWithLogin : owner ] ;
2012-07-14 04:41:29 +02:00
2010-05-05 15:56:19 +02:00
attendee = [ event userAsAttendee : ownerUser ] ;
2007-11-18 11:16:25 +01:00
if ( attendee )
2012-07-14 04:41:29 +02:00
{
2014-07-18 19:57:18 +02:00
if ( delegate && ! [ [ delegate email ] isEqualToString : [ attendee delegatedTo ] ] )
2012-07-14 04:41:29 +02:00
{
2015-07-22 22:45:42 +02:00
delegatedUid = [ delegate uidInContext : context ] ;
2012-07-14 04:41:29 +02:00
if ( delegatedUid )
delegatedUser = [ SOGoUser userWithLogin : delegatedUid ] ;
if ( delegatedUser ! = nil && [ event userIsOrganizer : delegatedUser ] )
2016-08-04 20:13:00 +02:00
ex = [ NSException exceptionWithHTTPStatus : 409
2014-07-18 19:57:18 +02:00
reason : @ "delegate is organizer" ] ;
2012-07-14 04:41:29 +02:00
if ( [ event isAttendee : [ [ delegate email ] rfc822Email ] ] )
2016-08-04 20:13:00 +02:00
ex = [ NSException exceptionWithHTTPStatus : 409
2012-09-24 20:27:22 +02:00
reason : @ "delegate is a participant" ] ;
2019-11-26 15:29:02 +01:00
else {
NSDictionary * dict ;
2019-12-04 10:37:08 +01:00
domain = [ [ context activeUser ] domain ] ;
dict = [ [ SOGoUserManager sharedUserManager ] contactInfosForUserWithUIDorEmail : [ [ delegate email ] rfc822Email ]
inDomain : domain ] ;
2019-11-26 15:29:02 +01:00
if ( dict && [ [ dict objectForKey : @ "isGroup" ] boolValue ] )
ex = [ NSException exceptionWithHTTPStatus : 409
reason : @ "delegate is a group" ] ;
}
2012-07-14 04:41:29 +02:00
}
if ( ex = = nil )
2012-11-30 19:57:55 +01:00
{
// Remove the RSVP attribute , as an action from the attendee
// was actually performed , and this confuses iCal ( bug #1850 )
[ [ attendee attributes ] removeObjectForKey : @ "RSVP" ] ;
ex = [ self _handleAttendee : attendee
withDelegate : delegate
ownerUser : ownerUser
statusChange : _status
inEvent : event ] ;
}
2012-07-14 04:41:29 +02:00
if ( ex = = nil )
{
// We generate the updated iCalendar file and we save it in
// the database . We do this ONLY when using SOGo from the
2014-01-31 21:05:33 +01:00
// Web interface or over ActiveSync .
// Over DAV , it ' ll be handled directly in PUTAction :
2015-08-06 22:18:39 +02:00
if ( ! [ context request ] ||
[ [ context request ] handledByDefaultHandler ] ||
[ [ [ context request ] requestHandlerKey ] isEqualToString : @ "Microsoft-Server-ActiveSync" ] )
2014-12-16 15:20:27 +01:00
{
// If an alarm was specified , let ' s use it . This would happen if an attendee accepts / declines / etc . an
// event invitation and also sets an alarm along the way . This would happen ONLY from the web interface .
[ event removeAllAlarms ] ;
if ( alarm )
{
[ event addToAlarms : alarm ] ;
}
2018-03-15 20:43:26 +01:00
[ event setLastModified : [ NSCalendarDate calendarDate ] ] ;
2014-12-16 15:20:27 +01:00
ex = [ self saveCalendar : [ event parent ] ] ;
}
2012-07-14 04:41:29 +02:00
}
}
2007-11-18 11:16:25 +01:00
else
2008-11-22 08:20:22 +01:00
ex = [ NSException exceptionWithHTTPStatus : 404 // Not Found
2012-07-14 04:41:29 +02:00
reason : @ "user does not participate in this calendar event" ] ;
2007-11-08 20:56:18 +01:00
}
2014-07-18 19:57:18 +02:00
else
ex = [ NSException exceptionWithHTTPStatus : 500 // Server Error
reason : @ "unable to parse event record" ] ;
2008-11-22 08:20:22 +01:00
2007-11-18 11:16:25 +01:00
return ex ;
2006-06-15 21:34:10 +02:00
}
2010-08-11 16:52:11 +02:00
//
//
//
2008-07-17 23:12:43 +02:00
- ( void ) prepareDeleteOccurence : ( iCalEvent * ) occurence
2007-01-31 21:15:28 +01:00
{
2008-08-14 00:30:55 +02:00
SOGoUser * ownerUser , * currentUser ;
2008-12-01 22:15:07 +01:00
NSCalendarDate * recurrenceId ;
2012-07-11 16:06:41 +02:00
NSArray * attendees ;
iCalEvent * event ;
BOOL send_receipt ;
2010-08-11 16:52:11 +02:00
ownerUser = [ SOGoUser userWithLogin : owner ] ;
event = [ self component : NO secure : NO ] ;
2012-07-11 16:06:41 +02:00
send_receipt = YES ;
2010-08-11 16:52:11 +02:00
if ( occurence = = nil )
2007-11-18 11:16:25 +01:00
{
2010-08-11 16:52:11 +02:00
// No occurence specified ; use the master event .
occurence = event ;
recurrenceId = nil ;
}
else
// Retrieve this occurence ID .
recurrenceId = [ occurence recurrenceId ] ;
2017-07-19 17:05:16 +02:00
if ( [ occurence userIsAttendee : ownerUser ] )
{
// The current user deletes the occurence ; let the organizer know that
// the user has declined this occurence .
[ self changeParticipationStatus : @ "DECLINED"
withDelegate : nil
alarm : nil
forRecurrenceId : recurrenceId ] ;
send_receipt = NO ;
}
else
2010-08-11 16:52:11 +02:00
{
// The organizer deletes an occurence .
currentUser = [ context activeUser ] ;
2017-07-19 17:05:16 +02:00
if ( recurrenceId )
attendees = [ occurence attendeesWithoutUser : currentUser ] ;
else
attendees = [ [ event parent ] attendeesWithoutUser : currentUser ] ;
2010-08-11 16:52:11 +02:00
2017-07-19 17:05:16 +02:00
// if ( ! [ attendees count ] && event ! = occurence )
// attendees = [ event attendeesWithoutUser : currentUser ] ;
2010-08-11 16:52:11 +02:00
if ( [ attendees count ] )
2014-07-18 19:57:18 +02:00
{
// Remove the event from all attendees calendars
// and send them an email .
[ self _handleRemovedUsers : attendees
withRecurrenceId : recurrenceId ] ;
2016-12-16 21:51:19 +01:00
if ( [ self _shouldScheduleEvent : [ event organizer ] ] )
[ self sendEMailUsingTemplateNamed : @ "Deletion"
forObject : [ occurence itipEntryWithMethod : @ "cancel" ]
previousObject : nil
toAttendees : attendees
withType : @ "calendar:cancellation" ] ;
2014-07-18 19:57:18 +02:00
}
2007-11-18 11:16:25 +01:00
}
2017-07-19 17:05:16 +02:00
2016-12-16 21:51:19 +01:00
if ( send_receipt )
[ self sendReceiptEmailForObject : event
addedAttendees : nil
deletedAttendees : nil
updatedAttendees : nil
operation : EventDeleted ] ;
2006-06-15 21:34:10 +02:00
}
2006-10-31 19:54:56 +01:00
2010-01-22 17:33:35 +01:00
- ( NSException * ) prepareDelete
2008-07-17 23:12:43 +02:00
{
[ self prepareDeleteOccurence : nil ] ;
2010-01-22 17:33:35 +01:00
2010-08-19 19:40:28 +02:00
return [ super prepareDelete ] ;
2008-07-17 23:12:43 +02:00
}
2009-08-10 22:59:49 +02:00
- ( NSDictionary * ) _partStatsFromCalendar : ( iCalCalendar * ) calendar
2009-04-30 23:17:55 +02:00
{
2009-08-10 22:59:49 +02:00
NSMutableDictionary * partStats ;
2009-04-30 23:17:55 +02:00
NSArray * allEvents ;
2009-08-10 22:59:49 +02:00
int count , max ;
iCalEvent * currentEvent ;
2010-05-05 15:56:19 +02:00
iCalPerson * ownerAttendee ;
2009-08-10 22:59:49 +02:00
NSString * key ;
SOGoUser * ownerUser ;
2009-05-01 13:35:02 +02:00
2009-08-25 23:28:24 +02:00
ownerUser = [ SOGoUser userWithLogin : owner ] ;
2009-04-30 23:17:55 +02:00
2009-08-10 22:59:49 +02:00
allEvents = [ calendar events ] ;
max = [ allEvents count ] ;
partStats = [ NSMutableDictionary dictionaryWithCapacity : max ] ;
for ( count = 0 ; count < max ; count + + )
{
currentEvent = [ allEvents objectAtIndex : count ] ;
2010-05-05 15:56:19 +02:00
ownerAttendee = [ currentEvent userAsAttendee : ownerUser ] ;
if ( ownerAttendee )
2009-08-10 22:59:49 +02:00
{
if ( count = = 0 )
key = @ "master" ;
else
key = [ [ currentEvent recurrenceId ] iCalFormattedDateTimeString ] ;
2010-05-05 15:56:19 +02:00
[ partStats setObject : ownerAttendee forKey : key ] ;
2009-08-10 22:59:49 +02:00
}
}
return partStats ;
}
2012-07-18 21:05:58 +02:00
- ( iCalCalendar * ) _setupResponseInRequestCalendar : ( iCalCalendar * ) rqCalendar
2009-08-10 22:59:49 +02:00
{
2012-07-13 22:59:09 +02:00
iCalCalendar * calendar ;
2009-08-10 22:59:49 +02:00
NSArray * keys ;
NSDictionary * partStats , * newPartStats ;
NSString * partStat , * key ;
int count , max ;
calendar = [ self calendar : NO secure : NO ] ;
partStats = [ self _partStatsFromCalendar : calendar ] ;
keys = [ partStats allKeys ] ;
max = [ keys count ] ;
if ( max > 0 )
{
2012-07-13 22:59:09 +02:00
newPartStats = [ self _partStatsFromCalendar : rqCalendar ] ;
2009-08-10 22:59:49 +02:00
if ( [ keys isEqualToArray : [ newPartStats allKeys ] ] )
{
for ( count = 0 ; count < max ; count + + )
{
key = [ keys objectAtIndex : count ] ;
partStat = [ [ newPartStats objectForKey : key ] partStat ] ;
[ [ partStats objectForKey : key ] setPartStat : partStat ] ;
}
}
}
2012-07-18 21:05:58 +02:00
return calendar ;
2009-08-10 22:59:49 +02:00
}
2009-04-30 23:17:55 +02:00
2012-07-13 22:59:09 +02:00
- ( void ) _adjustTransparencyInRequestCalendar : ( iCalCalendar * ) rqCalendar
2010-03-19 14:08:15 +01:00
{
NSArray * allEvents ;
iCalEvent * event ;
int i ;
2012-07-13 22:59:09 +02:00
allEvents = [ rqCalendar events ] ;
2010-03-19 14:08:15 +01:00
for ( i = 0 ; i < [ allEvents count ] ; i + + )
{
event = [ allEvents objectAtIndex : i ] ;
if ( [ event isAllDay ] && [ event isOpaque ] )
2014-07-18 19:57:18 +02:00
[ event setTransparency : @ "TRANSPARENT" ] ;
2010-03-19 14:08:15 +01:00
}
}
2014-09-23 19:46:44 +02:00
//
// iOS devices ( and potentially others ) send event invitations with no PARTSTAT defined .
// This confuses DAV clients like Thunderbird , or event SOGo web . The RFC says :
//
// Description : This parameter can be specified on properties with a
// CAL - ADDRESS value type . The parameter identifies the participation
// status for the calendar user specified by the property value . The
// parameter values differ depending on whether they are associated with
// a group scheduled "VEVENT" , "VTODO" or "VJOURNAL" . The values MUST
// match one of the values allowed for the given calendar component . If
// not specified on a property that allows this parameter , the default
// value is NEEDS - ACTION .
//
- ( void ) _adjustPartStatInRequestCalendar : ( iCalCalendar * ) rqCalendar
{
NSArray * allObjects , * allAttendees ;
iCalPerson * attendee ;
id entity ;
int i , j ;
allObjects = [ rqCalendar allObjects ] ;
for ( i = 0 ; i < [ allObjects count ] ; i + + )
{
entity = [ allObjects objectAtIndex : i ] ;
if ( [ entity isKindOfClass : [ iCalEvent class ] ] )
{
allAttendees = [ entity attendees ] ;
for ( j = 0 ; j < [ allAttendees count ] ; j + + )
{
attendee = [ allAttendees objectAtIndex : j ] ;
if ( ! [ [ attendee partStat ] length ] )
[ attendee setPartStat : @ "NEEDS-ACTION" ] ;
}
}
}
}
2011-04-16 03:35:06 +02:00
/ * *
* Verify vCalendar for any inconsistency or missing attributes .
* Currently only check if the events have an end date or a duration .
2014-09-15 15:32:36 +02:00
* We also check for the default transparency parameters .
2014-11-07 16:06:53 +01:00
* We also check for broken ORGANIZER such as "ORGANIZER;:mailto:sogo3@example.com"
2011-04-16 03:35:06 +02:00
* @ param rq the HTTP PUT request
* /
2012-07-13 22:59:09 +02:00
- ( void ) _adjustEventsInRequestCalendar : ( iCalCalendar * ) rqCalendar
2011-04-16 03:35:06 +02:00
{
NSArray * allEvents ;
iCalEvent * event ;
2019-11-27 23:00:28 +01:00
iCalTimeZone * tz ;
2017-03-08 15:31:19 +01:00
NSUInteger i ;
int j ;
2011-04-16 03:35:06 +02:00
2012-07-13 22:59:09 +02:00
allEvents = [ rqCalendar events ] ;
2011-04-16 03:35:06 +02:00
for ( i = 0 ; i < [ allEvents count ] ; i + + )
{
event = [ allEvents objectAtIndex : i ] ;
2020-01-29 18:50:58 +01:00
tz = [ event adjustInContext : context withTimezones : nil ] ;
2019-11-27 23:00:28 +01:00
if ( tz )
[ rqCalendar addTimeZone : tz ] ;
2014-09-15 15:32:36 +02:00
2015-06-04 17:46:59 +02:00
if ( [ event organizer ] )
2015-06-04 17:39:54 +02:00
{
2015-06-04 17:46:59 +02:00
NSString * uid ;
2015-06-04 17:39:54 +02:00
2015-06-04 19:46:50 +02:00
if ( ! [ [ [ event organizer ] cn ] length ] )
{
[ [ event organizer ] setCn : [ [ event organizer ] rfc822Email ] ] ;
}
// We now make sure that the organizer , if managed by SOGo , is using
// its default email when creating events and inviting attendees .
2015-07-22 22:45:42 +02:00
uid = [ [ event organizer ] uidInContext : context ] ;
2015-06-04 17:46:59 +02:00
if ( uid )
{
2017-03-24 16:11:10 +01:00
iCalPerson * attendee , * organizer ;
2015-06-04 17:46:59 +02:00
NSDictionary * defaultIdentity ;
2017-03-24 16:37:54 +01:00
SOGoUser * organizerUser ;
2017-03-07 16:45:44 +01:00
NSArray * allAttendees ;
2015-06-04 17:46:59 +02:00
2017-03-24 16:37:54 +01:00
organizerUser = [ SOGoUser userWithLogin : uid ] ;
2020-07-06 18:40:30 +02:00
defaultIdentity = [ organizerUser primaryIdentity ] ;
2017-03-24 16:11:10 +01:00
organizer = [ [ event organizer ] copy ] ;
[ organizer setCn : [ defaultIdentity objectForKey : @ "fullName" ] ] ;
[ organizer setEmail : [ defaultIdentity objectForKey : @ "email" ] ] ;
2017-03-07 16:45:44 +01:00
// We now check if one of the attendee is also the organizer . If so ,
// we remove it . See bug #3905 ( https : // sogo . nu / bugs / view . php ? id = 3905 )
// for more details . This is a Calendar app bug on Apple Yosemite .
allAttendees = [ event attendees ] ;
for ( j = [ allAttendees count ] -1 ; j >= 0 ; j - - )
{
attendee = [ allAttendees objectAtIndex : j ] ;
2017-03-24 16:37:54 +01:00
if ( [ organizerUser hasEmail : [ attendee rfc822Email ] ] )
2017-03-07 16:45:44 +01:00
[ event removeFromAttendees : attendee ] ;
}
2017-03-24 16:11:10 +01:00
// We reset the organizer
[ event setOrganizer : organizer ] ;
RELEASE ( organizer ) ;
2015-06-04 17:46:59 +02:00
}
2015-06-04 17:39:54 +02:00
}
2011-04-16 03:35:06 +02:00
}
}
2016-12-30 18:26:31 +01:00
2012-07-13 22:59:09 +02:00
- ( void ) _decomposeGroupsInRequestCalendar : ( iCalCalendar * ) rqCalendar
2009-08-10 22:59:49 +02:00
{
NSArray * allEvents ;
iCalEvent * event ;
int i ;
2009-04-30 23:17:55 +02:00
2009-08-10 22:59:49 +02:00
// The algorithm is pretty straightforward :
2009-04-30 23:17:55 +02:00
//
// We get all events
// We get all attendees
// If some are groups , we decompose them
// We regenerate the iCalendar string
//
2012-07-13 22:59:09 +02:00
allEvents = [ rqCalendar events ] ;
2009-04-30 23:17:55 +02:00
for ( i = 0 ; i < [ allEvents count ] ; i + + )
{
event = [ allEvents objectAtIndex : i ] ;
2012-07-13 22:59:09 +02:00
[ self expandGroupsInEvent : event ] ;
2009-04-30 23:17:55 +02:00
}
2009-08-10 22:59:49 +02:00
}
2010-08-11 16:52:11 +02:00
//
// If theRecurrenceId is nil , it returns immediately the
// first event that has a RECURRENCE - ID .
//
// Otherwise , it return values that matches .
//
- ( iCalEvent * ) _eventFromRecurrenceId : ( NSCalendarDate * ) theRecurrenceId
events : ( NSArray * ) allEvents
{
iCalEvent * event ;
int i ;
for ( i = 0 ; i < [ allEvents count ] ; i + + )
{
event = [ allEvents objectAtIndex : i ] ;
if ( [ event recurrenceId ] && ! theRecurrenceId )
2014-07-18 19:57:18 +02:00
return event ;
2010-08-11 16:52:11 +02:00
if ( [ event recurrenceId ] && [ [ event recurrenceId ] compare : theRecurrenceId ] = = NSOrderedSame )
2014-07-18 19:57:18 +02:00
return event ;
2010-08-11 16:52:11 +02:00
}
return nil ;
}
//
//
//
- ( NSCalendarDate * ) _addedExDate : ( iCalEvent * ) oldEvent
newEvent : ( iCalEvent * ) newEvent
{
NSArray * oldExDates , * newExDates ;
NSMutableArray * dates ;
int i ;
dates = [ NSMutableArray array ] ;
newExDates = [ newEvent childrenWithTag : @ "exdate" ] ;
for ( i = 0 ; i < [ newExDates count ] ; i + + )
[ dates addObject : [ [ newExDates objectAtIndex : i ] dateTime ] ] ;
oldExDates = [ oldEvent childrenWithTag : @ "exdate" ] ;
for ( i = 0 ; i < [ oldExDates count ] ; i + + )
[ dates removeObject : [ [ oldExDates objectAtIndex : i ] dateTime ] ] ;
return [ dates lastObject ] ;
}
//
//
//
- ( id ) DELETEAction : ( WOContext * ) _ctx
{
2010-08-19 19:40:28 +02:00
[ self prepareDelete ] ;
2010-08-11 16:52:11 +02:00
return [ super DELETEAction : _ctx ] ;
}
2009-08-10 22:59:49 +02:00
//
2012-07-13 22:59:09 +02:00
// This method is meant to be the common point of any save operation from web
// and DAV requests , as well as from code making use of SOGo as a library
// ( OpenChange )
2010-03-19 14:08:15 +01:00
//
2012-07-13 22:59:09 +02:00
- ( NSException * ) updateContentWithCalendar : ( iCalCalendar * ) calendar
fromRequest : ( WORequest * ) rq
2009-08-10 22:59:49 +02:00
{
2016-12-23 16:04:43 +01:00
SOGoUser * ownerUser ;
2011-04-25 12:31:08 +02:00
NSException * ex ;
2009-08-10 22:59:49 +02:00
NSArray * roles ;
2016-12-23 16:04:43 +01:00
2019-02-14 17:19:58 +01:00
BOOL ownerIsOrganizer ;
2010-08-19 19:40:28 +02:00
2014-07-18 19:57:18 +02:00
if ( calendar = = fullCalendar || calendar = = safeCalendar
|| calendar = = originalCalendar )
[ NSException raise : NSInvalidArgumentException format : @ "the 'calendar' argument must be a distinct instance" @ " from the original object" ] ;
2012-04-24 13:47:32 +02:00
2012-07-13 22:59:09 +02:00
ownerUser = [ SOGoUser userWithLogin : owner ] ;
2010-08-19 19:40:28 +02:00
2012-07-13 22:59:09 +02:00
roles = [ [ context activeUser ] rolesForObject : self
inContext : context ] ;
2010-08-11 16:52:11 +02:00
//
// We check if we gave only the "Respond To" right and someone is actually
// responding to one of our invitation . In this case , _setupResponseCalendarInRequest
// will only take the new attendee status and actually discard any other modifications .
//
2014-07-18 19:57:18 +02:00
if ( [ roles containsObject : @ "ComponentResponder" ] && ! [ roles containsObject : @ "ComponentModifier" ] )
2012-07-18 21:05:58 +02:00
calendar = [ self _setupResponseInRequestCalendar : calendar ] ;
2009-08-10 22:59:49 +02:00
else
{
2014-07-18 19:57:18 +02:00
if ( ! [ [ rq headersForKey : @ "X-SOGo" ] containsObject : @ "NoGroupsDecomposition" ] )
2012-07-13 22:59:09 +02:00
[ self _decomposeGroupsInRequestCalendar : calendar ] ;
2014-07-18 19:57:18 +02:00
if ( [ [ ownerUser domainDefaults ] iPhoneForceAllDayTransparency ] && [ rq isIPhone ] )
2017-03-07 16:45:44 +01:00
[ self _adjustTransparencyInRequestCalendar : calendar ] ;
2014-07-18 19:57:18 +02:00
2012-07-13 22:59:09 +02:00
[ self _adjustEventsInRequestCalendar : calendar ] ;
2016-03-23 18:41:08 +01:00
[ self adjustClassificationInRequestCalendar : calendar ] ;
2014-09-23 19:46:44 +02:00
[ self _adjustPartStatInRequestCalendar : calendar ] ;
2009-08-10 22:59:49 +02:00
}
2014-07-18 19:57:18 +02:00
2010-08-11 16:52:11 +02:00
//
// We first check if it ' s a new event
//
if ( [ self isNew ] )
{
2012-07-13 22:59:09 +02:00
iCalEvent * event ;
2012-07-10 02:29:13 +02:00
NSArray * attendees ;
2012-06-27 18:06:20 +02:00
NSString * eventUID ;
2010-08-19 19:40:28 +02:00
2010-08-11 16:52:11 +02:00
event = [ [ calendar events ] objectAtIndex : 0 ] ;
2012-06-27 18:06:20 +02:00
eventUID = [ event uid ] ;
2016-12-16 21:51:19 +01:00
attendees = nil ;
2012-06-27 18:06:20 +02:00
// make sure eventUID doesn ' t conflict with an existing event - see bug #1853
// TODO : send out a no - uid - conflict ( DAV : href ) xml element ( rfc4791 section 5.3 .2 .1 )
2012-07-13 22:59:09 +02:00
if ( [ container resourceNameForEventUID : eventUID ] )
2012-06-27 18:06:20 +02:00
{
2016-08-04 20:13:00 +02:00
return [ NSException exceptionWithHTTPStatus : 409
2015-10-31 07:10:03 +01:00
reason : [ NSString stringWithFormat : @ "Event UID already in use. (%@)" , eventUID ] ] ;
2012-06-27 18:06:20 +02:00
}
2010-08-17 16:30:09 +02:00
2010-08-11 16:52:11 +02:00
//
// New event and we ' re the organizer - - send invitation to all attendees
//
2019-02-14 17:19:58 +01:00
ownerIsOrganizer = [ event userIsOrganizer : ownerUser ] ;
2016-12-23 16:04:43 +01:00
// We handle the situation where the SOGo Integrator extension isn ' t installed or
// if the SENT - BY isn ' t set . That can happen if Bob invites Alice by creating the event
// in Annie ' s calendar . Annie should be the organizer , and Bob the SENT - BY . But most
// broken CalDAV client that aren ' t identity - aware will create the event in Annie ' s calendar
// and set Bob as the organizer . We fix this for them . See #3368 for details .
2019-02-14 17:19:58 +01:00
if ( ! ownerIsOrganizer &&
2017-03-03 14:47:59 +01:00
[ [ context activeUser ] hasEmail : [ [ event organizer ] rfc822Email ] ] )
2016-12-23 16:04:43 +01:00
{
[ [ event organizer ] setCn : [ ownerUser cn ] ] ;
[ [ event organizer ] setEmail : [ [ ownerUser allEmails ] objectAtIndex : 0 ] ] ;
[ [ event organizer ] setSentBy : [ NSString stringWithFormat : @ "\" MAILTO : % @ \ "" , [ [ [ context activeUser ] allEmails ] objectAtIndex : 0 ] ] ] ;
2019-02-14 17:19:58 +01:00
ownerIsOrganizer = YES ;
2016-12-23 16:04:43 +01:00
}
2019-02-14 17:19:58 +01:00
if ( ownerIsOrganizer )
2016-12-16 21:51:19 +01:00
{
attendees = [ event attendeesWithoutUser : ownerUser ] ;
if ( [ attendees count ] )
{
if ( ( ex = [ self _handleAddedUsers : attendees fromEvent : event force : YES ] ) )
return ex ;
else
{
// We might have auto - accepted resources here . If that ' s the
// case , let ' s regenerate the versitstring and replace the
// one from the request .
[ rq setContent : [ [ [ event parent ] versitString ] dataUsingEncoding : [ rq contentEncoding ] ] ] ;
}
if ( [ self _shouldScheduleEvent : [ event organizer ] ] )
[ self sendEMailUsingTemplateNamed : @ "Invitation"
forObject : [ event itipEntryWithMethod : @ "request" ]
previousObject : nil
toAttendees : attendees
withType : @ "calendar:invitation" ] ;
}
}
2010-08-11 16:52:11 +02:00
//
// We aren ' t the organizer but we ' re an attendee . That can happen when
// we receive an external invitation ( IMIP / ITIP ) and we accept it
// from a CUA - it gets added to a specific CalDAV calendar using a PUT
//
2016-12-16 21:51:19 +01:00
else if ( [ event userIsAttendee : ownerUser ] && [ self _shouldScheduleEvent : [ event userAsAttendee : ownerUser ] ] )
2014-07-18 19:57:18 +02:00
{
[ self sendIMIPReplyForEvent : event
from : ownerUser
to : [ event organizer ] ] ;
}
2012-07-10 02:29:13 +02:00
[ self sendReceiptEmailForObject : event
addedAttendees : attendees
deletedAttendees : nil
updatedAttendees : nil
operation : EventCreated ] ;
2012-07-11 16:06:41 +02:00
} // if ( [ self isNew ] )
2010-08-11 16:52:11 +02:00
else
{
2012-07-13 22:59:09 +02:00
iCalCalendar * oldCalendar ;
2010-08-11 16:52:11 +02:00
iCalEvent * oldEvent , * newEvent ;
iCalEventChanges * changes ;
2013-03-07 15:29:56 +01:00
NSMutableArray * oldEvents , * newEvents ;
2010-08-11 16:52:11 +02:00
NSCalendarDate * recurrenceId ;
int i ;
//
// We check what has changed in the event and react accordingly .
//
2013-03-07 15:29:56 +01:00
newEvents = [ NSMutableArray arrayWithArray : [ calendar events ] ] ;
2010-08-11 16:52:11 +02:00
oldCalendar = [ self calendar : NO secure : NO ] ;
2013-03-07 15:29:56 +01:00
oldEvents = [ NSMutableArray arrayWithArray : [ oldCalendar events ] ] ;
2010-08-11 16:52:11 +02:00
recurrenceId = nil ;
for ( i = [ newEvents count ] -1 ; i >= 0 ; i - - )
2014-07-18 19:57:18 +02:00
{
newEvent = [ newEvents objectAtIndex : i ] ;
if ( [ newEvent recurrenceId ] )
{
// Find the corresponding RECURRENCE - ID in the old calendar
// If not present , we assume it was created before the PUT
oldEvent = [ self _eventFromRecurrenceId : [ newEvent recurrenceId ]
events : oldEvents ] ;
if ( oldEvent = = nil )
{
NSString * recurrenceTime ;
recurrenceTime = [ NSString stringWithFormat : @ "%f" , [ [ newEvent recurrenceId ] timeIntervalSince1970 ] ] ;
oldEvent = ( iCalEvent * ) [ self newOccurenceWithID : recurrenceTime ] ;
}
// If present , we look for changes
changes = [ iCalEventChanges changesFromEvent : oldEvent toEvent : newEvent ] ;
if ( [ changes sequenceShouldBeIncreased ] | [ changes hasAttendeeChanges ] )
{
// We found a RECURRENCE - ID with changes , we consider it
recurrenceId = [ newEvent recurrenceId ] ;
break ;
}
else
{
2013-03-07 15:29:56 +01:00
[ newEvents removeObject : newEvent ] ;
[ oldEvents removeObject : oldEvent ] ;
2014-07-18 19:57:18 +02:00
}
}
oldEvent = nil ;
newEvent = nil ;
}
2010-08-11 16:52:11 +02:00
// If no changes were observed , let ' s see if we have any left overs
// in the oldEvents or in the newEvents array
if ( ! oldEvent && ! newEvent )
2014-07-18 19:57:18 +02:00
{
// We check if we only have to deal with the MASTER event
2020-02-27 20:46:21 +01:00
if ( [ oldEvents count ] && [ newEvents count ] = = [ oldEvents count ] )
2014-07-18 19:57:18 +02:00
{
oldEvent = [ oldEvents objectAtIndex : 0 ] ;
newEvent = [ newEvents objectAtIndex : 0 ] ;
}
// A RECURRENCE - ID was added
else if ( [ newEvents count ] > [ oldEvents count ] )
{
oldEvent = nil ;
newEvent = [ self _eventFromRecurrenceId : nil events : newEvents ] ;
recurrenceId = [ newEvent recurrenceId ] ;
}
// A RECURRENCE - ID was removed
else
{
oldEvent = [ self _eventFromRecurrenceId : nil events : oldEvents ] ;
newEvent = nil ;
recurrenceId = [ oldEvent recurrenceId ] ;
}
}
2010-08-11 16:52:11 +02:00
// We check if the PUT call is actually an PART - STATE change
// from one of the attendees - here ' s the logic :
//
2014-07-18 19:57:18 +02:00
// if owner = = organizer
2010-08-11 16:52:11 +02:00
//
// if [ context activeUser ] = = organizer
// [ send the invitation update ]
// else
// [ react on SENT - BY as someone else is acting for the organizer ]
2014-07-18 19:57:18 +02:00
//
//
2010-08-11 16:52:11 +02:00
if ( [ [ newEvent attendees ] count ] || [ [ oldEvent attendees ] count ] )
2014-07-18 19:57:18 +02:00
{
2019-02-14 17:19:58 +01:00
BOOL userIsOrganizer ;
2013-03-07 15:29:56 +01:00
// newEvent might be nil here , if we ' re deleting a RECURRENCE - ID with attendees
2015-07-22 15:05:17 +02:00
// If that ' s the case , we use the oldEvent to obtain the organizer
if ( newEvent )
2019-02-14 17:19:58 +01:00
{
ownerIsOrganizer = [ newEvent userIsOrganizer : ownerUser ] ;
userIsOrganizer = [ newEvent userIsOrganizer : [ context activeUser ] ] ;
}
2015-07-22 15:05:17 +02:00
else
2019-02-14 17:19:58 +01:00
{
ownerIsOrganizer = [ oldEvent userIsOrganizer : ownerUser ] ;
userIsOrganizer = [ oldEvent userIsOrganizer : [ context activeUser ] ] ;
}
2015-07-22 15:05:17 +02:00
2016-12-23 16:04:43 +01:00
// We handle the situation where the SOGo Integrator extension isn ' t installed or
// if the SENT - BY isn ' t set . That can happen if Bob invites Alice by creating the event
// in Annie ' s calendar . Annie should be the organizer , and Bob the SENT - BY . But most
// broken CalDAV client that aren ' t identity - aware will create the event in Annie ' s calendar
// and set Bob as the organizer . We fix this for them . See #3368 for details .
2019-02-14 17:19:58 +01:00
//
// We also handle the case where Bob invites Alice and Bob has full access to Alice ' s calendar
// After inviting ALice , Bob opens the event in Alice ' s calendar and accept / declines the event .
//
2016-12-23 16:04:43 +01:00
if ( ! userIsOrganizer &&
2019-02-14 17:19:58 +01:00
! ownerIsOrganizer &&
2017-03-03 14:47:59 +01:00
[ [ context activeUser ] hasEmail : [ [ newEvent organizer ] rfc822Email ] ] )
2016-12-23 16:04:43 +01:00
{
[ [ newEvent organizer ] setCn : [ ownerUser cn ] ] ;
[ [ newEvent organizer ] setEmail : [ [ ownerUser allEmails ] objectAtIndex : 0 ] ] ;
[ [ newEvent organizer ] setSentBy : [ NSString stringWithFormat : @ "\" MAILTO : % @ \ "" , [ [ [ context activeUser ] allEmails ] objectAtIndex : 0 ] ] ] ;
2019-02-14 17:19:58 +01:00
ownerIsOrganizer = YES ;
2016-12-23 16:04:43 +01:00
}
2014-07-18 19:57:18 +02:00
// With Thunderbird 10 , if you create a recurring event with an exception
// occurence , and invite someone , the PUT will have the organizer in the
// recurrence - id and not in the master event . We must fix this , otherwise
// SOGo will break .
2015-07-22 22:45:42 +02:00
if ( ! recurrenceId && ! [ [ [ [ [ newEvent parent ] events ] objectAtIndex : 0 ] organizer ] uidInContext : context ] )
2014-07-18 19:57:18 +02:00
[ [ [ [ newEvent parent ] events ] objectAtIndex : 0 ] setOrganizer : [ newEvent organizer ] ] ;
2015-07-22 15:05:17 +02:00
2019-02-14 17:19:58 +01:00
if ( ownerIsOrganizer )
2014-07-18 19:57:18 +02:00
{
2016-08-09 22:17:17 +02:00
// We check ACLs of the ' organizer ' - in case someone forges the SENT - BY
NSString * uid ;
uid = [ [ oldEvent organizer ] uidInContext : context ] ;
2016-08-09 22:22:09 +02:00
if ( uid && [ [ [ context activeUser ] login ] caseInsensitiveCompare : uid ] ! = NSOrderedSame )
2016-08-09 22:17:17 +02:00
{
SOGoAppointmentObject * organizerObject ;
organizerObject = [ self _lookupEvent : [ oldEvent uid ] forUID : uid ] ;
roles = [ [ context activeUser ] rolesForObject : organizerObject
inContext : context ] ;
2019-03-13 14:08:31 +01:00
if ( ! [ roles containsObject : @ "ComponentModifier" ] && ! [ [ context activeUser ] isSuperUser ] )
2016-08-09 22:17:17 +02:00
{
return [ NSException exceptionWithHTTPStatus : 409
reason : @ "Not allowed to perform this action. Wrong SENT-BY being used regarding access rights on organizer's calendar." ] ;
}
}
2013-03-07 15:29:56 +01:00
// A RECCURENCE - ID was removed
if ( ! newEvent && oldEvent )
[ self prepareDeleteOccurence : oldEvent ] ;
// The master event was changed , A RECCURENCE - ID was added or modified
2016-02-11 16:54:07 +01:00
else if ( ( ex = [ self _handleUpdatedEvent : newEvent fromOldEvent : oldEvent force : YES ] ) )
2014-07-18 19:57:18 +02:00
return ex ;
2019-02-14 17:19:58 +01:00
} // if ( ownerIsOrganizer ) . .
2014-07-18 19:57:18 +02:00
//
// else = > attendee is responding
//
// if [ context activeUser ] = = attendee
// [ we change the PART - STATE ]
// else
// [ react on SENT - BY as someone else is acting for the attendee ]
else
{
iCalPerson * attendee , * delegate ;
NSString * delegateEmail ;
2015-12-15 17:08:30 +01:00
attendee = [ oldEvent userAsAttendee : [ SOGoUser userWithLogin : owner ] ] ;
if ( ! attendee )
2015-12-15 17:14:58 +01:00
attendee = [ newEvent userAsAttendee : [ SOGoUser userWithLogin : owner ] ] ;
2015-12-15 17:08:30 +01:00
else
{
// We must do an extra check here since Bob could have invited Alice
// using alice @ example . com but she would have accepted with ATTENDEE set
// to sexy @ example . com . That would duplicate the ATTENDEE and set the
// participation status to ACCEPTED for sexy @ example . com but leave it
// to NEEDS - ACTION to alice @ example . This can happen in Mozilla Thunderbird / Lightning
// when a user with multiple identities accepts an event invitation to one
// of its identity ( which is different than the email address associated with
// the mail account ) prior doing a calendar refresh .
NSMutableArray * attendees ;
2015-12-15 21:05:40 +01:00
iCalPerson * participant ;
2015-12-15 17:08:30 +01:00
attendees = [ NSMutableArray arrayWithArray : [ newEvent attendeesWithoutUser : [ SOGoUser userWithLogin : owner ] ] ] ;
2015-12-15 21:05:40 +01:00
participant = [ newEvent participantForUser : [ SOGoUser userWithLogin : owner ]
attendee : attendee ] ;
[ attendee setPartStat : [ participant partStat ] ] ;
2015-12-15 21:21:33 +01:00
[ attendee setDelegatedFrom : [ participant delegatedFrom ] ] ;
2015-12-15 21:05:40 +01:00
[ attendee setDelegatedTo : [ participant delegatedTo ] ] ;
2015-12-15 17:08:30 +01:00
[ attendees addObject : attendee ] ;
[ newEvent setAttendees : attendees ] ;
}
2014-07-18 19:57:18 +02:00
// We first check of the sequences are alright . We don ' t accept attendees
2016-08-04 20:13:00 +02:00
// accepting "old" invitations . If that ' s the case , we return a 409
2011-10-03 21:00:50 +02:00
if ( [ [ newEvent sequence ] intValue ] < [ [ oldEvent sequence ] intValue ] )
2016-08-04 20:13:00 +02:00
return [ NSException exceptionWithHTTPStatus : 409
2014-07-18 19:57:18 +02:00
reason : @ "sequences don't match" ] ;
2012-12-05 14:31:35 +01:00
// Remove the RSVP attribute , as an action from the attendee
// was actually performed , and this confuses iCal ( bug #1850 )
2014-07-18 19:57:18 +02:00
[ [ attendee attributes ] removeObjectForKey : @ "RSVP" ] ;
delegate = nil ;
delegateEmail = [ attendee delegatedTo ] ;
if ( [ delegateEmail length ] )
{
2019-10-02 16:45:37 +02:00
if ( [ [ delegateEmail lowercaseString ] hasPrefix : @ "mailto:" ] )
delegateEmail = [ delegateEmail substringFromIndex : 7 ] ;
2014-07-18 19:57:18 +02:00
if ( [ delegateEmail length ] )
delegate = [ newEvent findAttendeeWithEmail : delegateEmail ] ;
}
changes = [ iCalEventChanges changesFromEvent : oldEvent toEvent : newEvent ] ;
// The current user deletes the occurence ; let the organizer know that
// the user has declined this occurence .
if ( [ [ changes updatedProperties ] containsObject : @ "exdate" ] )
{
[ self changeParticipationStatus : @ "DECLINED"
withDelegate : nil // FIXME ( specify delegate ? )
2014-12-16 15:20:27 +01:00
alarm : nil
2014-07-18 19:57:18 +02:00
forRecurrenceId : [ self _addedExDate : oldEvent newEvent : newEvent ] ] ;
}
else if ( attendee )
{
[ self changeParticipationStatus : [ attendee partStat ]
withDelegate : delegate
2014-12-16 15:20:27 +01:00
alarm : nil
2014-07-18 19:57:18 +02:00
forRecurrenceId : recurrenceId ] ;
}
// All attendees and the organizer field were removed . Apple iCal does
// that when we remove the last attendee of an event .
//
// We must update previous ' s attendees ' calendars to actually
// remove the event in each of them .
else
{
[ self _handleRemovedUsers : [ changes deletedAttendees ]
withRecurrenceId : recurrenceId ] ;
}
}
} // if ( [ [ newEvent attendees ] count ] || [ [ oldEvent attendees ] count ] )
2012-07-11 16:06:41 +02:00
else
2014-07-18 19:57:18 +02:00
{
2016-11-03 16:47:21 +01:00
changes = [ iCalEventChanges changesFromEvent : oldEvent toEvent : newEvent ] ;
if ( [ changes hasMajorChanges ] )
[ self sendReceiptEmailForObject : newEvent
addedAttendees : nil
deletedAttendees : nil
updatedAttendees : nil
operation : EventUpdated ] ;
2014-07-18 19:57:18 +02:00
}
2012-07-10 02:29:13 +02:00
} // else of if ( isNew ) . . .
2014-07-18 19:57:18 +02:00
2012-07-13 22:59:09 +02:00
unsigned int baseVersion ;
2012-04-24 13:47:32 +02:00
// We must NOT invoke [ super PUTAction : ] here as it ' ll resave
// the content string and we could have etag mismatches .
baseVersion = ( isNew ? 0 : version ) ;
2014-07-18 19:57:18 +02:00
2014-07-30 20:51:00 +02:00
ex = [ self saveComponent : calendar
baseVersion : baseVersion ] ;
2014-07-18 19:57:18 +02:00
return ex ;
2012-07-13 22:59:09 +02:00
}
//
// If we see "X-SOGo: NoGroupsDecomposition" in the HTTP headers , we
// simply invoke super ' s PUTAction .
//
// We also check if we must force transparency on all day events
// from iPhone clients .
//
- ( id ) PUTAction : ( WOContext * ) _ctx
{
NSException * ex ;
NSString * etag ;
WORequest * rq ;
WOResponse * response ;
iCalCalendar * rqCalendar ;
2014-09-15 15:32:36 +02:00
2012-07-13 22:59:09 +02:00
rq = [ _ctx request ] ;
rqCalendar = [ iCalCalendar parseSingleFromSource : [ rq contentAsString ] ] ;
2018-10-09 20:47:22 +02:00
// We are unable to parse the received calendar , we return right away
// with a 400 error code .
if ( ! rqCalendar )
{
return [ NSException exceptionWithHTTPStatus : 400
reason : @ "Unable to parse event." ] ;
}
2014-09-15 15:32:36 +02:00
2012-07-13 22:59:09 +02:00
if ( ! [ self isNew ] )
{
//
// We must check for etag changes prior doing anything since an attendee could
// have changed its participation status and the organizer didn ' t get the
// copy and is trying to do a modification to the event .
//
ex = [ self matchesRequestConditionInContext : context ] ;
if ( ex )
2014-07-18 19:57:18 +02:00
return ex ;
2012-07-13 22:59:09 +02:00
}
2014-09-15 15:32:36 +02:00
ex = [ self updateContentWithCalendar : rqCalendar fromRequest : rq ] ;
if ( ex )
response = ( WOResponse * ) ex ;
2012-04-24 13:47:32 +02:00
else
{
2012-07-13 22:59:09 +02:00
response = [ _ctx response ] ;
2012-04-24 13:47:32 +02:00
if ( isNew )
2014-07-18 19:57:18 +02:00
[ response setStatus : 201 / * Created * / ] ;
2012-04-24 13:47:32 +02:00
else
2014-07-18 19:57:18 +02:00
[ response setStatus : 204 / * No Content * / ] ;
2012-04-24 13:47:32 +02:00
etag = [ self davEntityTag ] ;
if ( etag )
2014-07-18 19:57:18 +02:00
[ response setHeader : etag forKey : @ "etag" ] ;
2012-04-24 13:47:32 +02:00
}
2014-07-18 19:57:18 +02:00
2010-08-11 16:52:11 +02:00
return response ;
2009-04-30 23:17:55 +02:00
}
2020-01-16 16:43:42 +01:00
- ( BOOL ) resourceHasAutoAccepted
{
return _resourceHasAutoAccepted ;
}
2006-06-15 21:34:10 +02:00
@ end / * SOGoAppointmentObject * /