diff --git a/NEWS b/NEWS index 190c1da46..beb3ba617 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,7 @@ Bug fixes - EAS fix on qp-encoded subjects (#3390) - correctly handle all-day event exceptions when the master event changes - prevent characters in calendar component UID causing issues during import process + - avoid duplicating attendees when accepting event using a different identity over CalDAV 2.3.3a (2015-11-18) ------------------- diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index 4bf315e71..a1a840ebe 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -2173,13 +2173,37 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent { iCalPerson *attendee, *delegate; NSString *delegateEmail; - - attendee = [newEvent userAsAttendee: [SOGoUser userWithLogin: owner]]; + + attendee = [oldEvent userAsAttendee: [SOGoUser userWithLogin: owner]]; + + if (!attendee) + [newEvent userAsAttendee: [SOGoUser userWithLogin: owner]]; + 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; + NSString *partStat; + + attendees = [NSMutableArray arrayWithArray: [newEvent attendeesWithoutUser: [SOGoUser userWithLogin: owner]]]; + + partStat = [newEvent participationStatusForUser: [SOGoUser userWithLogin: owner] + attendee: attendee]; + [attendee setPartStat: partStat]; + [attendees addObject: attendee]; + [newEvent setAttendees: attendees]; + } // We first check of the sequences are alright. We don't accept attendees // accepting "old" invitations. If that's the case, we return a 403 if ([[newEvent sequence] intValue] < [[oldEvent sequence] intValue]) - return [NSException exceptionWithHTTPStatus:403 + return [NSException exceptionWithHTTPStatus: 403 reason: @"sequences don't match"]; // Remove the RSVP attribute, as an action from the attendee diff --git a/SoObjects/Appointments/iCalEntityObject+SOGo.h b/SoObjects/Appointments/iCalEntityObject+SOGo.h index 88e7d3b24..1f0365cf2 100644 --- a/SoObjects/Appointments/iCalEntityObject+SOGo.h +++ b/SoObjects/Appointments/iCalEntityObject+SOGo.h @@ -44,6 +44,9 @@ extern NSNumber *iCalDistantFutureNumber; - (iCalPerson *) userAsAttendee: (SOGoUser *) user; +- (NSString *) participationStatusForUser: (SOGoUser *) theUser + attendee: (iCalPerson *) theAttendee; +- (NSArray *) attendeeUIDs; - (BOOL) isStillRelevant; - (id) itipEntryWithMethod: (NSString *) method; diff --git a/SoObjects/Appointments/iCalEntityObject+SOGo.m b/SoObjects/Appointments/iCalEntityObject+SOGo.m index 2fd9f9cae..9d859c6e5 100644 --- a/SoObjects/Appointments/iCalEntityObject+SOGo.m +++ b/SoObjects/Appointments/iCalEntityObject+SOGo.m @@ -422,8 +422,8 @@ NSNumber *iCalDistantFutureNumber = nil; - (iCalPerson *) userAsAttendee: (SOGoUser *) user { - NSEnumerator *attendees; iCalPerson *currentAttendee, *userAttendee; + NSEnumerator *attendees; userAttendee = nil; @@ -436,6 +436,34 @@ NSNumber *iCalDistantFutureNumber = nil; return userAttendee; } +- (NSString *) participationStatusForUser: (SOGoUser *) theUser + attendee: (iCalPerson *) theAttendee +{ + iCalPerson *currentAttendee; + NSEnumerator *attendees; + NSMutableArray *a; + + a = [NSMutableArray array]; + + attendees = [[self attendees] objectEnumerator]; + while ((currentAttendee = [attendees nextObject])) + if ([theUser hasEmail: [currentAttendee rfc822Email]]) + { + // If the attendee is the same but the partStat is + // different, we prioritize this one. + if ([[currentAttendee rfc822Email] caseInsensitiveCompare: [theAttendee rfc822Email]] == NSOrderedSame && + [[currentAttendee partStat] caseInsensitiveCompare: [theAttendee partStat]] != NSOrderedSame) + [a insertObject: currentAttendee atIndex: 0]; + else if ([[currentAttendee rfc822Email] caseInsensitiveCompare: [theAttendee rfc822Email]] != NSOrderedSame) + [a addObject: currentAttendee]; + } + + if ([a count] > 0) + return [[a objectAtIndex: 0] partStat]; + + return [theAttendee partStat]; +} + - (BOOL) userIsOrganizer: (SOGoUser *) user { NSString *mail;