(feat) better support for RFC 6638 (schedule-agent) (fixes #2599)

This commit is contained in:
Ludovic Marcotte 2016-12-16 15:51:19 -05:00
parent 9882052e81
commit ab1c699bc7

View file

@ -369,6 +369,48 @@
return listHasChanged; 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;
}
// //
// //
// //
@ -394,11 +436,12 @@
owner: owner]; owner: owner];
} }
[self sendEMailUsingTemplateNamed: @"Update" if ([self _shouldScheduleEvent: [newEvent organizer]])
forObject: [newEvent itipEntryWithMethod: @"request"] [self sendEMailUsingTemplateNamed: @"Update"
previousObject: oldEvent forObject: [newEvent itipEntryWithMethod: @"request"]
toAttendees: updateAttendees previousObject: oldEvent
withType: @"calendar:invitation-update"]; toAttendees: updateAttendees
withType: @"calendar:invitation-update"];
} }
// This method scans the list of attendees. // This method scans the list of attendees.
@ -823,11 +866,12 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
{ {
[self _handleRemovedUsers: deletedAttendees [self _handleRemovedUsers: deletedAttendees
withRecurrenceId: [newEvent recurrenceId]]; withRecurrenceId: [newEvent recurrenceId]];
[self sendEMailUsingTemplateNamed: @"Deletion" if ([self _shouldScheduleEvent: [newEvent organizer]])
forObject: [newEvent itipEntryWithMethod: @"cancel"] [self sendEMailUsingTemplateNamed: @"Deletion"
previousObject: oldEvent forObject: [newEvent itipEntryWithMethod: @"cancel"]
toAttendees: deletedAttendees previousObject: oldEvent
withType: @"calendar:cancellation"]; toAttendees: deletedAttendees
withType: @"calendar:cancellation"];
} }
if ((ex = [self _handleAttendeesConflicts: [newEvent attendees] forEvent: newEvent force: forceSave])) if ((ex = [self _handleAttendeesConflicts: [newEvent attendees] forEvent: newEvent force: forceSave]))
@ -883,12 +927,13 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
// Send an invitation to new attendees // Send an invitation to new attendees
if ((ex = [self _handleAddedUsers: addedAttendees fromEvent: newEvent force: forceSave])) if ((ex = [self _handleAddedUsers: addedAttendees fromEvent: newEvent force: forceSave]))
return ex; return ex;
[self sendEMailUsingTemplateNamed: @"Invitation" if ([self _shouldScheduleEvent: [newEvent organizer]])
forObject: [newEvent itipEntryWithMethod: @"request"] [self sendEMailUsingTemplateNamed: @"Invitation"
previousObject: oldEvent forObject: [newEvent itipEntryWithMethod: @"request"]
toAttendees: addedAttendees previousObject: oldEvent
withType: @"calendar:invitation"]; toAttendees: addedAttendees
withType: @"calendar:invitation"];
} }
if ([changes hasMajorChanges]) if ([changes hasMajorChanges])
@ -962,11 +1007,12 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
if ([attendees count]) if ([attendees count])
{ {
[self sendEMailUsingTemplateNamed: @"Invitation" if ([self _shouldScheduleEvent: [newEvent organizer]])
forObject: [newEvent itipEntryWithMethod: @"request"] [self sendEMailUsingTemplateNamed: @"Invitation"
previousObject: nil forObject: [newEvent itipEntryWithMethod: @"request"]
toAttendees: attendees previousObject: nil
withType: @"calendar:invitation"]; toAttendees: attendees
withType: @"calendar:invitation"];
} }
[self sendReceiptEmailForObject: newEvent [self sendReceiptEmailForObject: newEvent
@ -1166,19 +1212,17 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
statusChange: (NSString *) newStatus statusChange: (NSString *) newStatus
inEvent: (iCalEvent *) event inEvent: (iCalEvent *) event
{ {
iCalPerson *otherAttendee, *otherDelegate;
NSString *currentStatus, *organizerUID; NSString *currentStatus, *organizerUID;
SOGoUser *ownerUser, *currentUser; SOGoUser *ownerUser, *currentUser;
NSString *delegateEmail;
NSException *ex; NSException *ex;
ex = nil; BOOL addDelegate, removeDelegate;
currentStatus = [attendee partStat]; currentStatus = [attendee partStat];
iCalPerson *otherAttendee, *otherDelegate;
NSString *delegateEmail;
BOOL addDelegate, removeDelegate;
otherAttendee = attendee; otherAttendee = attendee;
ex = nil;
delegateEmail = [otherAttendee delegatedTo]; delegateEmail = [otherAttendee delegatedTo];
if ([delegateEmail length]) if ([delegateEmail length])
@ -1189,7 +1233,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
else else
otherDelegate = nil; otherDelegate = nil;
/* We handle the addition/deletion of delegate users */ // We handle the addition/deletion of delegate users
addDelegate = NO; addDelegate = NO;
removeDelegate = NO; removeDelegate = NO;
if (delegate) if (delegate)
@ -1272,12 +1316,13 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
else else
otherDelegate = nil; otherDelegate = nil;
} }
[self sendEMailUsingTemplateNamed: @"Deletion" if ([self _shouldScheduleEvent: [event organizer]])
forObject: [event itipEntryWithMethod: @"cancel"] [self sendEMailUsingTemplateNamed: @"Deletion"
previousObject: nil forObject: [event itipEntryWithMethod: @"cancel"]
toAttendees: delegates previousObject: nil
withType: @"calendar:cancellation"]; toAttendees: delegates
withType: @"calendar:cancellation"];
} // if (removeDelegate) } // if (removeDelegate)
if (addDelegate) if (addDelegate)
@ -1291,12 +1336,13 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
[self _addOrUpdateEvent: event [self _addOrUpdateEvent: event
forUID: delegatedUID forUID: delegatedUID
owner: [theOwnerUser login]]; owner: [theOwnerUser login]];
[self sendEMailUsingTemplateNamed: @"Invitation" if ([self _shouldScheduleEvent: [event organizer]])
forObject: [event itipEntryWithMethod: @"request"] [self sendEMailUsingTemplateNamed: @"Invitation"
previousObject: nil forObject: [event itipEntryWithMethod: @"request"]
toAttendees: delegates previousObject: nil
withType: @"calendar:invitation"]; toAttendees: delegates
withType: @"calendar:invitation"];
} // if (addDelegate) } // if (addDelegate)
// If the current user isn't the organizer of the event // If the current user isn't the organizer of the event
@ -1477,9 +1523,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
// within the repeating vEvent. // within the repeating vEvent.
recurrenceTime = [NSString stringWithFormat: @"%f", [_recurrenceId timeIntervalSince1970]]; recurrenceTime = [NSString stringWithFormat: @"%f", [_recurrenceId timeIntervalSince1970]];
event = (iCalEvent*)[self lookupOccurrence: recurrenceTime]; event = (iCalEvent*)[self lookupOccurrence: recurrenceTime];
// If no occurence found, create one
if (event == nil) if (event == nil)
// If no occurence found, create one
event = (iCalEvent*)[self newOccurenceWithID: recurrenceTime]; event = (iCalEvent*)[self newOccurenceWithID: recurrenceTime];
} }
else else
@ -1558,50 +1604,6 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
return ex; 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;
}
// //
// //
// //
@ -1613,14 +1615,10 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
iCalEvent *event; iCalEvent *event;
BOOL send_receipt; BOOL send_receipt;
ownerUser = [SOGoUser userWithLogin: owner]; ownerUser = [SOGoUser userWithLogin: owner];
event = [self component: NO secure: NO]; event = [self component: NO secure: NO];
send_receipt = YES; send_receipt = YES;
if (![self _shouldScheduleEvent: [event organizer]])
return;
if (occurence == nil) if (occurence == nil)
{ {
// No occurence specified; use the master event. // No occurence specified; use the master event.
@ -1646,11 +1644,13 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
// and send them an email. // and send them an email.
[self _handleRemovedUsers: attendees [self _handleRemovedUsers: attendees
withRecurrenceId: recurrenceId]; withRecurrenceId: recurrenceId];
[self sendEMailUsingTemplateNamed: @"Deletion"
forObject: [occurence itipEntryWithMethod: @"cancel"] if ([self _shouldScheduleEvent: [event organizer]])
previousObject: nil [self sendEMailUsingTemplateNamed: @"Deletion"
toAttendees: attendees forObject: [occurence itipEntryWithMethod: @"cancel"]
withType: @"calendar:cancellation"]; previousObject: nil
toAttendees: attendees
withType: @"calendar:cancellation"];
} }
} }
else if ([occurence userIsAttendee: ownerUser]) else if ([occurence userIsAttendee: ownerUser])
@ -1664,12 +1664,12 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
send_receipt = NO; send_receipt = NO;
} }
if (send_receipt) if (send_receipt)
[self sendReceiptEmailForObject: event [self sendReceiptEmailForObject: event
addedAttendees: nil addedAttendees: nil
deletedAttendees: nil deletedAttendees: nil
updatedAttendees: nil updatedAttendees: nil
operation: EventDeleted]; operation: EventDeleted];
} }
- (NSException *) prepareDelete - (NSException *) prepareDelete
@ -1984,13 +1984,10 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
iCalEvent *event; iCalEvent *event;
NSArray *attendees; NSArray *attendees;
NSString *eventUID; NSString *eventUID;
BOOL scheduling;
attendees = nil;
event = [[calendar events] objectAtIndex: 0]; event = [[calendar events] objectAtIndex: 0];
eventUID = [event uid]; eventUID = [event uid];
scheduling = [self _shouldScheduleEvent: [event organizer]]; attendees = nil;
// make sure eventUID doesn't conflict with an existing event - see bug #1853 // 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) // TODO: send out a no-uid-conflict (DAV:href) xml element (rfc4791 section 5.3.2.1)
@ -2003,34 +2000,35 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
// //
// New event and we're the organizer -- send invitation to all attendees // New event and we're the organizer -- send invitation to all attendees
// //
if (scheduling && [event userIsOrganizer: ownerUser]) if ([event userIsOrganizer: ownerUser])
{ {
attendees = [event attendeesWithoutUser: ownerUser]; attendees = [event attendeesWithoutUser: ownerUser];
if ([attendees count]) if ([attendees count])
{ {
if ((ex = [self _handleAddedUsers: attendees fromEvent: event force: YES])) if ((ex = [self _handleAddedUsers: attendees fromEvent: event force: YES]))
return ex; return ex;
else else
{ {
// We might have auto-accepted resources here. If that's the // We might have auto-accepted resources here. If that's the
// case, let's regenerate the versitstring and replace the // case, let's regenerate the versitstring and replace the
// one from the request. // one from the request.
[rq setContent: [[[event parent] versitString] dataUsingEncoding: [rq contentEncoding]]]; [rq setContent: [[[event parent] versitString] dataUsingEncoding: [rq contentEncoding]]];
} }
[self sendEMailUsingTemplateNamed: @"Invitation" if ([self _shouldScheduleEvent: [event organizer]])
forObject: [event itipEntryWithMethod: @"request"] [self sendEMailUsingTemplateNamed: @"Invitation"
previousObject: nil forObject: [event itipEntryWithMethod: @"request"]
toAttendees: attendees previousObject: nil
withType: @"calendar:invitation"]; toAttendees: attendees
} withType: @"calendar:invitation"];
} }
}
// //
// We aren't the organizer but we're an attendee. That can happen when // 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 // 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 // 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 [self sendIMIPReplyForEvent: event
from: ownerUser from: ownerUser