Skip to content

Commit

Permalink
(feat) better support for RFC 6638 (schedule-agent) (fixes #2599)
Browse files Browse the repository at this point in the history
  • Loading branch information
extrafu committed Dec 16, 2016
1 parent 9882052 commit ab1c699
Showing 1 changed file with 126 additions and 128 deletions.
254 changes: 126 additions & 128 deletions SoObjects/Appointments/SOGoAppointmentObject.m
Expand Up @@ -369,6 +369,48 @@ - (BOOL) _requireResponseFromAttendees: (iCalEvent *) event
return listHasChanged;
}

//
//
//
- (BOOL) _shouldScheduleEvent: (iCalPerson *) thePerson
{
NSArray *userAgents;
NSString *v;
BOOL b;
int i;

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
//
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;
}
}

return b;
}

//
//
//
Expand All @@ -394,11 +436,12 @@ - (void) _handleSequenceUpdateInEvent: (iCalEvent *) newEvent
owner: owner];
}

[self sendEMailUsingTemplateNamed: @"Update"
forObject: [newEvent itipEntryWithMethod: @"request"]
previousObject: oldEvent
toAttendees: updateAttendees
withType: @"calendar:invitation-update"];
if ([self _shouldScheduleEvent: [newEvent organizer]])
[self sendEMailUsingTemplateNamed: @"Update"
forObject: [newEvent itipEntryWithMethod: @"request"]
previousObject: oldEvent
toAttendees: updateAttendees
withType: @"calendar:invitation-update"];
}

// This method scans the list of attendees.
Expand Down Expand Up @@ -823,11 +866,12 @@ - (NSException *) _handleUpdatedEvent: (iCalEvent *) newEvent
{
[self _handleRemovedUsers: deletedAttendees
withRecurrenceId: [newEvent recurrenceId]];
[self sendEMailUsingTemplateNamed: @"Deletion"
forObject: [newEvent itipEntryWithMethod: @"cancel"]
previousObject: oldEvent
toAttendees: deletedAttendees
withType: @"calendar:cancellation"];
if ([self _shouldScheduleEvent: [newEvent organizer]])
[self sendEMailUsingTemplateNamed: @"Deletion"
forObject: [newEvent itipEntryWithMethod: @"cancel"]
previousObject: oldEvent
toAttendees: deletedAttendees
withType: @"calendar:cancellation"];
}

if ((ex = [self _handleAttendeesConflicts: [newEvent attendees] forEvent: newEvent force: forceSave]))
Expand Down Expand Up @@ -883,12 +927,13 @@ - (NSException *) _handleUpdatedEvent: (iCalEvent *) newEvent
// Send an invitation to new attendees
if ((ex = [self _handleAddedUsers: addedAttendees fromEvent: newEvent force: forceSave]))
return ex;

[self sendEMailUsingTemplateNamed: @"Invitation"
forObject: [newEvent itipEntryWithMethod: @"request"]
previousObject: oldEvent
toAttendees: addedAttendees
withType: @"calendar:invitation"];

if ([self _shouldScheduleEvent: [newEvent organizer]])
[self sendEMailUsingTemplateNamed: @"Invitation"
forObject: [newEvent itipEntryWithMethod: @"request"]
previousObject: oldEvent
toAttendees: addedAttendees
withType: @"calendar:invitation"];
}

if ([changes hasMajorChanges])
Expand Down Expand Up @@ -962,11 +1007,12 @@ - (NSException *) saveComponent: (iCalEvent *) newEvent

if ([attendees count])
{
[self sendEMailUsingTemplateNamed: @"Invitation"
forObject: [newEvent itipEntryWithMethod: @"request"]
previousObject: nil
toAttendees: attendees
withType: @"calendar:invitation"];
if ([self _shouldScheduleEvent: [newEvent organizer]])
[self sendEMailUsingTemplateNamed: @"Invitation"
forObject: [newEvent itipEntryWithMethod: @"request"]
previousObject: nil
toAttendees: attendees
withType: @"calendar:invitation"];
}

[self sendReceiptEmailForObject: newEvent
Expand Down Expand Up @@ -1166,19 +1212,17 @@ - (NSException *) _handleAttendee: (iCalPerson *) attendee
statusChange: (NSString *) newStatus
inEvent: (iCalEvent *) event
{
iCalPerson *otherAttendee, *otherDelegate;
NSString *currentStatus, *organizerUID;
SOGoUser *ownerUser, *currentUser;
NSString *delegateEmail;
NSException *ex;

ex = nil;
BOOL addDelegate, removeDelegate;

currentStatus = [attendee partStat];

iCalPerson *otherAttendee, *otherDelegate;
NSString *delegateEmail;
BOOL addDelegate, removeDelegate;

otherAttendee = attendee;
ex = nil;

delegateEmail = [otherAttendee delegatedTo];
if ([delegateEmail length])
Expand All @@ -1189,7 +1233,7 @@ - (NSException *) _handleAttendee: (iCalPerson *) attendee
else
otherDelegate = nil;

/* We handle the addition/deletion of delegate users */
// We handle the addition/deletion of delegate users
addDelegate = NO;
removeDelegate = NO;
if (delegate)
Expand Down Expand Up @@ -1272,12 +1316,13 @@ - (NSException *) _handleAttendee: (iCalPerson *) attendee
else
otherDelegate = nil;
}

[self sendEMailUsingTemplateNamed: @"Deletion"
forObject: [event itipEntryWithMethod: @"cancel"]
previousObject: nil
toAttendees: delegates
withType: @"calendar:cancellation"];

if ([self _shouldScheduleEvent: [event organizer]])
[self sendEMailUsingTemplateNamed: @"Deletion"
forObject: [event itipEntryWithMethod: @"cancel"]
previousObject: nil
toAttendees: delegates
withType: @"calendar:cancellation"];
} // if (removeDelegate)

if (addDelegate)
Expand All @@ -1291,12 +1336,13 @@ - (NSException *) _handleAttendee: (iCalPerson *) attendee
[self _addOrUpdateEvent: event
forUID: delegatedUID
owner: [theOwnerUser login]];

[self sendEMailUsingTemplateNamed: @"Invitation"
forObject: [event itipEntryWithMethod: @"request"]
previousObject: nil
toAttendees: delegates
withType: @"calendar:invitation"];

if ([self _shouldScheduleEvent: [event organizer]])
[self sendEMailUsingTemplateNamed: @"Invitation"
forObject: [event itipEntryWithMethod: @"request"]
previousObject: nil
toAttendees: delegates
withType: @"calendar:invitation"];
} // if (addDelegate)

// If the current user isn't the organizer of the event
Expand Down Expand Up @@ -1477,9 +1523,9 @@ - (NSException *) changeParticipationStatus: (NSString *) _status
// within the repeating vEvent.
recurrenceTime = [NSString stringWithFormat: @"%f", [_recurrenceId timeIntervalSince1970]];
event = (iCalEvent*)[self lookupOccurrence: recurrenceTime];


// If no occurence found, create one
if (event == nil)
// If no occurence found, create one
event = (iCalEvent*)[self newOccurenceWithID: recurrenceTime];
}
else
Expand Down Expand Up @@ -1558,50 +1604,6 @@ - (NSException *) changeParticipationStatus: (NSString *) _status
return ex;
}


//
//
//
- (BOOL) _shouldScheduleEvent: (iCalPerson *) theOrganizer
{
NSArray *userAgents;
NSString *v;
BOOL b;
int i;

b = YES;

if (theOrganizer && (v = [theOrganizer 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
//
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;
}
}

return b;
}


//
//
//
Expand All @@ -1613,14 +1615,10 @@ - (void) prepareDeleteOccurence: (iCalEvent *) occurence
iCalEvent *event;
BOOL send_receipt;


ownerUser = [SOGoUser userWithLogin: owner];
event = [self component: NO secure: NO];
send_receipt = YES;

if (![self _shouldScheduleEvent: [event organizer]])
return;

if (occurence == nil)
{
// No occurence specified; use the master event.
Expand All @@ -1646,11 +1644,13 @@ - (void) prepareDeleteOccurence: (iCalEvent *) occurence
// and send them an email.
[self _handleRemovedUsers: attendees
withRecurrenceId: recurrenceId];
[self sendEMailUsingTemplateNamed: @"Deletion"
forObject: [occurence itipEntryWithMethod: @"cancel"]
previousObject: nil
toAttendees: attendees
withType: @"calendar:cancellation"];

if ([self _shouldScheduleEvent: [event organizer]])
[self sendEMailUsingTemplateNamed: @"Deletion"
forObject: [occurence itipEntryWithMethod: @"cancel"]
previousObject: nil
toAttendees: attendees
withType: @"calendar:cancellation"];
}
}
else if ([occurence userIsAttendee: ownerUser])
Expand All @@ -1664,12 +1664,12 @@ - (void) prepareDeleteOccurence: (iCalEvent *) occurence
send_receipt = NO;
}

if (send_receipt)
[self sendReceiptEmailForObject: event
addedAttendees: nil
deletedAttendees: nil
updatedAttendees: nil
operation: EventDeleted];
if (send_receipt)
[self sendReceiptEmailForObject: event
addedAttendees: nil
deletedAttendees: nil
updatedAttendees: nil
operation: EventDeleted];
}

- (NSException *) prepareDelete
Expand Down Expand Up @@ -1984,13 +1984,10 @@ - (NSException *) updateContentWithCalendar: (iCalCalendar *) calendar
iCalEvent *event;
NSArray *attendees;
NSString *eventUID;
BOOL scheduling;

attendees = nil;

event = [[calendar events] objectAtIndex: 0];
eventUID = [event uid];
scheduling = [self _shouldScheduleEvent: [event organizer]];
attendees = nil;

// 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)
Expand All @@ -2003,34 +2000,35 @@ - (NSException *) updateContentWithCalendar: (iCalCalendar *) calendar
//
// New event and we're the organizer -- send invitation to all attendees
//
if (scheduling && [event userIsOrganizer: ownerUser])
{
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]]];
}

[self sendEMailUsingTemplateNamed: @"Invitation"
forObject: [event itipEntryWithMethod: @"request"]
previousObject: nil
toAttendees: attendees
withType: @"calendar:invitation"];
}
}
if ([event userIsOrganizer: ownerUser])
{
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"];
}
}
//
// 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
//
else if (scheduling && [event userIsAttendee: ownerUser])
else if ([event userIsAttendee: ownerUser] && [self _shouldScheduleEvent: [event userAsAttendee: ownerUser]])
{
[self sendIMIPReplyForEvent: event
from: ownerUser
Expand Down

0 comments on commit ab1c699

Please sign in to comment.