From 132987d8bce87163e09390d8bdd80db512e06303 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 3 Oct 2011 20:55:02 +0000 Subject: [PATCH] Monotone-Parent: cb5283601b9539bb382aa64a739bf758b9e2ac7f Monotone-Revision: e064a5558d641325b03001de47cede6db61eed32 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2011-10-03T20:55:02 Monotone-Branch: ca.inverse.sogo --- OpenChange/MAPIStoreAppointmentWrapper.h | 32 +++ OpenChange/MAPIStoreAppointmentWrapper.m | 261 +++++++++++++++++++- OpenChange/MAPIStoreCalendarMessage.m | 297 ++++++++++++++++++++++- OpenChange/MAPIStoreDraftsMessage.m | 5 + OpenChange/MAPIStoreFolder.m | 3 +- OpenChange/MAPIStoreMailMessage.m | 222 ++++++++++++++--- OpenChange/MAPIStoreMapping.m | 26 +- OpenChange/SOGoMAPIFSMessage.m | 3 + 8 files changed, 793 insertions(+), 56 deletions(-) diff --git a/OpenChange/MAPIStoreAppointmentWrapper.h b/OpenChange/MAPIStoreAppointmentWrapper.h index 07df6df95..b2be82db4 100644 --- a/OpenChange/MAPIStoreAppointmentWrapper.h +++ b/OpenChange/MAPIStoreAppointmentWrapper.h @@ -25,11 +25,15 @@ #import +#import + @class NSTimeZone; @class iCalCalendar; @class iCalEvent; +@class SOGoUser; + extern NSTimeZone *utcTZ; @interface MAPIStoreAppointmentWrapper : NSObject @@ -39,17 +43,38 @@ extern NSTimeZone *utcTZ; NSTimeZone *timeZone; NSData *globalObjectId; NSData *cleanGlobalObjectId; + SOGoUser *user; } + (id) wrapperWithICalEvent: (iCalEvent *) newEvent + andUser: (SOGoUser *) newUser inTimeZone: (NSTimeZone *) newTimeZone; - (id) initWithICalEvent: (iCalEvent *) newEvent + andUser: (SOGoUser *) newUser inTimeZone: (NSTimeZone *) newTimeZone; /* getters */ - (void) fillMessageData: (struct mapistore_message *) dataPtr inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPrSenderEmailAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPrSenderAddrtype: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPrSenderName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPrSenderEntryid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; + +- (int) getPrReceivedByAddrtype: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPrReceivedByEmailAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPrReceivedByName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPrReceivedByEntryid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; + - (int) getPrIconIndex: (void **) data inMemCtx: (TALLOC_CTX *) memCtx; - (int) getPrOwnerApptId: (void **) data @@ -64,6 +89,9 @@ extern NSTimeZone *utcTZ; inMemCtx: (TALLOC_CTX *) memCtx; - (int) getPidLidAppointmentStateFlags: (void **) data inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidLidResponseStatus: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; + - (int) getPidLidAppointmentStartWhole: (void **) data inMemCtx: (TALLOC_CTX *) memCtx; - (int) getPidLidCommonStart: (void **) data @@ -104,6 +132,10 @@ extern NSTimeZone *utcTZ; inMemCtx: (TALLOC_CTX *) memCtx; - (int) getPidLidServerProcessed: (void **) data inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidLidServerProcessingActions: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidLidAppointmentReplyTime: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; @end diff --git a/OpenChange/MAPIStoreAppointmentWrapper.m b/OpenChange/MAPIStoreAppointmentWrapper.m index 70c11eb03..c05dab7ac 100644 --- a/OpenChange/MAPIStoreAppointmentWrapper.m +++ b/OpenChange/MAPIStoreAppointmentWrapper.m @@ -73,11 +73,13 @@ static NSCharacterSet *hexCharacterSet = nil; } + (id) wrapperWithICalEvent: (iCalEvent *) newEvent + andUser: (SOGoUser *) newUser inTimeZone: (NSTimeZone *) newTimeZone { MAPIStoreAppointmentWrapper *wrapper; wrapper = [[self alloc] initWithICalEvent: newEvent + andUser: newUser inTimeZone: newTimeZone]; [wrapper autorelease]; @@ -93,12 +95,14 @@ static NSCharacterSet *hexCharacterSet = nil; timeZone = nil; globalObjectId = nil; cleanGlobalObjectId = nil; + user = nil; } return self; } - (id) initWithICalEvent: (iCalEvent *) newEvent + andUser: (SOGoUser *) newUser inTimeZone: (NSTimeZone *) newTimeZone { if ((self = [self init])) @@ -106,6 +110,7 @@ static NSCharacterSet *hexCharacterSet = nil; ASSIGN (event, newEvent); ASSIGN (calendar, [event parent]); ASSIGN (timeZone, newTimeZone); + ASSIGN (user, newUser); } return self; @@ -118,6 +123,7 @@ static NSCharacterSet *hexCharacterSet = nil; [timeZone release]; [globalObjectId release]; [cleanGlobalObjectId release]; + [user release]; [super dealloc]; } @@ -448,11 +454,23 @@ static NSCharacterSet *hexCharacterSet = nil; - (int) getPrMessageClass: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - *data = talloc_strdup(memCtx, "IPM.Appointment"); + const char *className; + + if ([[event attendees] count] > 0) + className = "IPM.Schedule.Meeting.Request"; + else + className = "IPM.Appointment"; + *data = talloc_strdup(memCtx, className); return MAPISTORE_SUCCESS; } +- (int) getPidLidFInvited: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getYes: data inMemCtx: memCtx]; +} + - (int) getPrBody: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -488,13 +506,56 @@ static NSCharacterSet *hexCharacterSet = nil; uint32_t flags = 0x00; if ([[event attendees] count] > 0) - flags |= 0x01; /* asfMeeting */ + { + flags |= 0x01; /* asfMeeting */ + if ([event userAsAttendee: user]) + flags |= 0x02; /* asfReceived */ + /* TODO: asfCancelled */ + } *data = MAPILongValue (memCtx, flags); return MAPISTORE_SUCCESS; } +- (int) getPidLidResponseStatus: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + uint32_t status = 0x00; + iCalPerson *person; + + if ([[event attendees] count] > 0) + { + if ([event userIsOrganizer: user]) + status = 1; + else + { + person = [event userAsAttendee: user]; + if (person) + { + switch ([person participationStatus]) + { + case iCalPersonPartStatTentative: + status = 2; + break; + case iCalPersonPartStatAccepted: + status = 3; + break; + case iCalPersonPartStatDeclined: + status = 4; + break; + default: + status = 5; + } + } + } + } + + *data = MAPILongValue (memCtx, status); + + return MAPISTORE_SUCCESS; +} + - (int) getPidLidAppointmentStartWhole: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -507,6 +568,167 @@ static NSCharacterSet *hexCharacterSet = nil; return [self getPidLidAppointmentStartWhole: data inMemCtx: memCtx]; } +- (int) _getEntryIdFromCN: (NSString *) cn + andEmail: (NSString *) email + inData: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *username; + SOGoUserManager *mgr; + NSDictionary *contactInfos; + NSData *entryId; + + mgr = [SOGoUserManager sharedUserManager]; + contactInfos = [mgr contactInfosForUserWithUIDorEmail: email]; + if (contactInfos) + { + username = [contactInfos objectForKey: @"c_uid"]; + entryId = MAPIStoreInternalEntryId (username); + } + else + entryId = MAPIStoreExternalEntryId (cn, email); + + *data = [entryId asBinaryInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) _getEmailAddress: (void **) data + forICalPerson: (iCalPerson *) person + inMemCtx: (TALLOC_CTX *) memCtx +{ + int rc; + NSString *email; + + email = [person rfc822Email]; + if ([email length] > 0) + { + *data = [email asUnicodeInMemCtx: memCtx]; + rc = MAPISTORE_SUCCESS; + } + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + +- (int) _getAddrType: (void **) data + forICalPerson: (iCalPerson *) person + inMemCtx: (TALLOC_CTX *) memCtx +{ + *data = [@"SMTP" asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) _getName: (void **) data + forICalPerson: (iCalPerson *) person + inMemCtx: (TALLOC_CTX *) memCtx +{ + int rc; + NSString *cn; + + cn = [person cn]; + if ([cn length] > 0) + { + *data = [cn asUnicodeInMemCtx: memCtx]; + rc = MAPISTORE_SUCCESS; + } + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + +- (int) _getEntryid: (void **) data + forICalPerson: (iCalPerson *) person + inMemCtx: (TALLOC_CTX *) memCtx +{ + int rc = MAPISTORE_ERR_NOT_FOUND; + NSString *email, *cn; + + if (person) + { + email = [person rfc822Email]; + if ([email length] > 0) + { + cn = [person cn]; + rc = [self _getEntryIdFromCN: cn andEmail: email + inData: data + inMemCtx: memCtx]; + } + } + + return rc; +} + +/* sender (organizer) */ +- (int) getPrSenderEmailAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getEmailAddress: data + forICalPerson: [event organizer] + inMemCtx: memCtx]; +} + +- (int) getPrSenderAddrtype: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getAddrType: data + forICalPerson: [event organizer] + inMemCtx: memCtx]; +} + +- (int) getPrSenderName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getName: data + forICalPerson: [event organizer] + inMemCtx: memCtx]; +} + +- (int) getPrSenderEntryid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getEntryid: data + forICalPerson: [event organizer] + inMemCtx: memCtx]; +} + +/* attendee */ +- (int) getPrReceivedByEmailAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getEmailAddress: data + forICalPerson: [event userAsAttendee: user] + inMemCtx: memCtx]; +} + +- (int) getPrReceivedByAddrtype: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getAddrType: data + forICalPerson: [event userAsAttendee: user] + inMemCtx: memCtx]; +} + +- (int) getPrReceivedByName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getName: data + forICalPerson: [event userAsAttendee: user] + inMemCtx: memCtx]; +} + +- (int) getPrReceivedByEntryid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getEntryid: data + forICalPerson: [event userAsAttendee: user] + inMemCtx: memCtx]; +} +/* /attendee */ + - (int) getPrEndDate: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -601,8 +823,7 @@ static NSCharacterSet *hexCharacterSet = nil; return [self getPidLidLocation: data inMemCtx: memCtx]; } -- (int) getPidLidServerProcessed: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx +- (int) getPidLidServerProcessed: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { /* TODO: we need to check whether the event has been processed internally by SOGo or if it was received only by mail. We only assume the SOGo case @@ -610,6 +831,16 @@ static NSCharacterSet *hexCharacterSet = nil; return [self getYes: data inMemCtx: memCtx]; } +- (int) getPidLidServerProcessingActions: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +{ + *data = MAPILongValue (memCtx, + 0x00000010 /* cpsCreatedOnPrincipal */ + | 0x00000080 /* cpsUpdatedCalItem */ + | 0x00000100 /* cpsCopiedOldProperties */); + + return MAPISTORE_SUCCESS; +} + - (int) getPidLidPrivate: (void **) data // private (bool), should depend on CLASS and permissions inMemCtx: (TALLOC_CTX *) memCtx { @@ -923,4 +1154,26 @@ _fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp, return rc; } +- (int) getPidLidAppointmentReplyTime: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + /* We always return LAST-MODIFIED, which is a hack, but one that works + because: the user is either (NOT recipient OR (is recipient AND its + status is N/A), where this value should not be taken into account by the + client OR the user is recipient and its status is defined, where this + value is thus correct because the recipient status is the only property + that can be changed. */ + int rc = MAPISTORE_ERR_NOT_FOUND; + NSCalendarDate *lastModified; + + lastModified = [event lastModified]; + if (lastModified) + { + *data = [lastModified asFileTimeInMemCtx: memCtx]; + rc = MAPISTORE_SUCCESS; + } + + return rc; +} + @end diff --git a/OpenChange/MAPIStoreCalendarMessage.m b/OpenChange/MAPIStoreCalendarMessage.m index f4827a078..6f399a4ed 100644 --- a/OpenChange/MAPIStoreCalendarMessage.m +++ b/OpenChange/MAPIStoreCalendarMessage.m @@ -40,6 +40,7 @@ #import #import #import +#import #import #import @@ -47,6 +48,7 @@ #import "MAPIStoreCalendarAttachment.h" #import "MAPIStoreCalendarFolder.h" #import "MAPIStoreContext.h" +#import "MAPIStoreMapping.h" #import "MAPIStoreRecurrenceUtils.h" #import "MAPIStoreTypes.h" #import "NSDate+MAPIStore.h" @@ -104,7 +106,8 @@ event = [sogoObject component: NO secure: NO]; ASSIGN (appointmentWrapper, [MAPIStoreAppointmentWrapper wrapperWithICalEvent: event - inTimeZone: [self ownerTimeZone]]); + andUser: [[self context] activeUser] + inTimeZone: [self ownerTimeZone]]); } return appointmentWrapper; @@ -117,10 +120,18 @@ return [[self appointmentWrapper] getPrIconIndex: data inMemCtx: memCtx]; } +- (int) getPidLidFInvited: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getYes: data inMemCtx: memCtx]; +} + - (int) getPrMessageClass: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - return [[self appointmentWrapper] getPrMessageClass: data inMemCtx: memCtx]; + *data = talloc_strdup (memCtx, "IPM.Appointment"); + + return MAPISTORE_SUCCESS; } - (int) getPrOwnerApptId: (void **) data @@ -141,6 +152,13 @@ return [[self appointmentWrapper] getPidLidAppointmentStateFlags: data inMemCtx: memCtx]; } +- (int) getPidLidResponseStatus: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [[self appointmentWrapper] getPidLidResponseStatus: data + inMemCtx: memCtx]; +} + - (int) getPidLidAppointmentStartWhole: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -252,6 +270,29 @@ inMemCtx: memCtx]; } +- (int) getPrProcessed: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getYes: data inMemCtx: memCtx]; +} + +- (int) getPidLidServerProcessed: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +{ + return [[self appointmentWrapper] getPidLidServerProcessed: data + inMemCtx: memCtx]; +} + +- (int) getPidLidServerProcessingActions: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +{ + return [[self appointmentWrapper] getPidLidServerProcessingActions: data + inMemCtx: memCtx]; +} + +- (int) getPidLidAppointmentReplyTime: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +{ + return [[self appointmentWrapper] getPidLidAppointmentReplyTime: data + inMemCtx: memCtx]; +} + - (void) getMessageData: (struct mapistore_message **) dataPtr inMemCtx: (TALLOC_CTX *) memCtx { @@ -263,6 +304,120 @@ *dataPtr = msgData; } +/* sender */ +- (int) getPrSenderEmailAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [[self appointmentWrapper] getPrSenderEmailAddress: data + inMemCtx: memCtx]; +} + +- (int) getPrSenderAddrtype: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [[self appointmentWrapper] getPrSenderAddrtype: data + inMemCtx: memCtx]; +} + +- (int) getPrSenderName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [[self appointmentWrapper] getPrSenderName: data + inMemCtx: memCtx]; +} + +- (int) getPrSenderEntryid: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +{ + return [[self appointmentWrapper] getPrSenderEntryid: data + inMemCtx: memCtx]; +} + +/* sender representing */ +- (int) getPrSentRepresentingEmailAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPrSenderEmailAddress: data inMemCtx: memCtx]; +} + +- (int) getPrSentRepresentingAddrtype: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getSMTPAddrType: data inMemCtx: memCtx]; +} + +- (int) getPrSentRepresentingName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPrSenderName: data inMemCtx: memCtx]; +} + +- (int) getPrSentRepresentingEntryid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPrSenderEntryid: data inMemCtx: memCtx]; +} + +/* attendee */ +// - (int) getPrReceivedByAddrtype: (void **) data +// inMemCtx: (TALLOC_CTX *) memCtx +// { +// return [[self appointmentWrapper] getPrReceivedByAddrtype: data +// inMemCtx: memCtx]; +// } + +// - (int) getPrReceivedByEmailAddress: (void **) data +// inMemCtx: (TALLOC_CTX *) memCtx +// { +// return [[self appointmentWrapper] getPrReceivedByEmailAddress: data +// inMemCtx: memCtx]; +// } + +// - (int) getPrReceivedByName: (void **) data +// inMemCtx: (TALLOC_CTX *) memCtx +// { +// return [[self appointmentWrapper] getPrReceivedByName: data +// inMemCtx: memCtx]; +// } + +// - (int) getPrReceivedByEntryid: (void **) data +// inMemCtx: (TALLOC_CTX *) memCtx +// { +// return [[self appointmentWrapper] getPrReceivedByEntryid: data +// inMemCtx: memCtx]; +// } + +// /* attendee representing */ +// - (int) getPrRcvdRepresentingEmailAddress: (void **) data +// inMemCtx: (TALLOC_CTX *) memCtx +// { +// return [self getPrReceivedByEmailAddress: data inMemCtx: memCtx]; +// } + +// - (int) getPrRcvdRepresentingAddrtype: (void **) data +// inMemCtx: (TALLOC_CTX *) memCtx +// { +// return [self getSMTPAddrType: data inMemCtx: memCtx]; +// } + +// - (int) getPrRcvdRepresentingName: (void **) data +// inMemCtx: (TALLOC_CTX *) memCtx +// { +// return [self getPrReceivedByName: data inMemCtx: memCtx]; +// } + +// - (int) getPrRcvdRepresentingEntryid: (void **) data +// inMemCtx: (TALLOC_CTX *) memCtx +// { +// return [self getPrReceivedByEntryid: data inMemCtx: memCtx]; +// } + +- (int) getPrResponseRequested: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getYes: data inMemCtx: memCtx]; +} + +/* attendee */ - (void) _setupRecurrenceInCalendar: (iCalCalendar *) calendar withEvent: (iCalEvent *) event fromData: (NSData *) mapiRecurrenceData @@ -284,6 +439,73 @@ talloc_free (blob); } +- (NSString *) _uidFromGlobalObjectId +{ + NSData *objectId; + NSString *uid = nil; + char *bytesDup, *uidStart; + NSUInteger length; + + /* NOTE: we only handle the generic case at the moment, see + MAPIStoreAppointmentWrapper */ + objectId = [newProperties objectForKey: MAPIPropertyKey (PidLidGlobalObjectId)]; + if (objectId) + { + length = [objectId length]; + bytesDup = talloc_array (NULL, char, length + 1); + memcpy (bytesDup, [objectId bytes], length); + bytesDup[length] = 0; + uidStart = bytesDup + length - 1; + while (uidStart != bytesDup && *(uidStart - 1)) + uidStart--; + if (uidStart > bytesDup && *uidStart) + uid = [NSString stringWithUTF8String: uidStart]; + talloc_free (bytesDup); + } + + return uid; +} + +- (void) _fixupEventWithExistingUID +{ + NSString *uid, *existingCName, *existingURL; + MAPIStoreMapping *mapping; + uint64_t objectId; + SOGoAppointmentObject *existingObject; + WOContext *woContext; + + uid = [self _uidFromGlobalObjectId]; + existingCName = [[container sogoObject] resourceNameForEventUID: uid]; + if (existingCName) + { + /* Steps: + unregister self'url mapping + unregister old object's url mapping to discard further delete operation + set new object nameInContainer + register new url mapping */ + + mapping = [[self context] mapping]; + + existingURL = [NSString stringWithFormat: @"%@%@", + [container url], existingCName]; + objectId = [mapping idFromURL: existingURL]; + [mapping unregisterURLWithID: objectId]; + + objectId = [self objectId]; + [mapping unregisterURLWithID: objectId]; + + [mapping registerURL: existingURL withID: objectId]; + + woContext = [[self context] woContext]; + existingObject = [[container sogoObject] lookupName: existingCName + inContext: woContext + acquire: NO]; + [existingObject setContext: woContext]; + ASSIGN (sogoObject, existingObject); + isNew = NO; + } +} + - (void) save { iCalCalendar *vCalendar; @@ -297,6 +519,17 @@ SOGoUser *activeUser; id value; + if (isNew) + { + /* big response hack: + - if isNew: + - deduce UID from GlobalObjectId + - if UID already exist in db: + - invoke setNameInContainer on sogoObject with proper cname */ + + [self _fixupEventWithExistingUID]; + } + [self logWithFormat: @"-save, event props:"]; // MAPIStoreDumpMessageProperties (newProperties); @@ -329,6 +562,8 @@ if (value) responseStatus = [value unsignedLongValue]; + /* FIXME: we should provide a data converter between OL partstats and + SOGo */ switch (responseStatus) { case 0x02: /* respTentative */ @@ -452,15 +687,23 @@ withEvent: newEvent fromData: value]; + [newEvent setOrganizer: nil]; + [newEvent removeAllAttendees]; + // Organizer value = [newProperties objectForKey: @"recipients"]; if (value) { NSArray *recipients; - NSDictionary *dict; + NSDictionary *dict, *properties; iCalPerson *person; + iCalPersonPartStat newPartStat; + NSNumber *flags, *trackStatus; int i; + /* We must set the organizer preliminarily here because, unlike what + the doc states, Outlook does not always pass the real organizer + in the recipients list. */ dict = [activeUser primaryIdentity]; person = [iCalPerson new]; [person setCn: [dict objectForKey: @"fullName"]]; @@ -473,18 +716,50 @@ for (i = 0; i < [recipients count]; i++) { dict = [recipients objectAtIndex: i]; + properties = [dict objectForKey: @"properties"]; + flags = [properties + objectForKey: MAPIPropertyKey (PR_RECIPIENT_FLAGS)]; + if (!flags) + { + [self logWithFormat: @"no recipient flags specified"]; + break; + } + person = [iCalPerson new]; - [person setCn: [dict objectForKey: @"fullName"]]; [person setEmail: [dict objectForKey: @"email"]]; - [person setParticipationStatus: iCalPersonPartStatNeedsAction]; - [person setRsvp: @"TRUE"]; - [person setRole: @"REQ-PARTICIPANT"]; - // FIXME: We must NOT always rely on this - if (![newEvent isAttendee: [person rfc822Email]]) - [newEvent addToAttendees: person]; - + if (([flags unsignedIntValue] & 0x0002)) /* recipOrganizer */ + [newEvent setOrganizer: person]; + else + { + trackStatus + = [properties + objectForKey: MAPIPropertyKey (PR_RECIPIENT_TRACKSTATUS)]; + + /* FIXME: we should provide a data converter between OL + partstats and SOGo */ + switch ([trackStatus unsignedIntValue]) + { + case 0x02: /* respTentative */ + newPartStat = iCalPersonPartStatTentative; + break; + case 0x03: /* respAccepted */ + newPartStat = iCalPersonPartStatAccepted; + break; + case 0x04: /* respDeclined */ + newPartStat = iCalPersonPartStatDeclined; + break; + default: + newPartStat = iCalPersonPartStatNeedsAction; + } + + [person setParticipationStatus: newPartStat]; + [person setRsvp: @"TRUE"]; + [person setRole: @"REQ-PARTICIPANT"]; + [newEvent addToAttendees: person]; + } + [person release]; } } diff --git a/OpenChange/MAPIStoreDraftsMessage.m b/OpenChange/MAPIStoreDraftsMessage.m index 6a2cc41a1..cea145c2a 100644 --- a/OpenChange/MAPIStoreDraftsMessage.m +++ b/OpenChange/MAPIStoreDraftsMessage.m @@ -33,6 +33,7 @@ #import #import "MAPIStoreContext.h" +#import "MAPIStoreMapping.h" #import "MAPIStoreTypes.h" #import "NSData+MAPIStore.h" #import "NSObject+MAPIStore.h" @@ -572,6 +573,7 @@ e) { NSString *msgClass; NSException *error; + MAPIStoreMapping *mapping; msgClass = [newProperties objectForKey: MAPIPropertyKey (PR_MESSAGE_CLASS_UNICODE)]; @@ -584,6 +586,9 @@ e) } else [self logWithFormat: @"ignored scheduling message"]; + + mapping = [[self context] mapping]; + [mapping unregisterURLWithID: [self objectId]]; } - (int) submitWithFlags: (enum SubmitFlags) flags diff --git a/OpenChange/MAPIStoreFolder.m b/OpenChange/MAPIStoreFolder.m index 2caec5e65..34c57ae30 100644 --- a/OpenChange/MAPIStoreFolder.m +++ b/OpenChange/MAPIStoreFolder.m @@ -711,6 +711,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe MAPIStoreMapping *mapping; struct mapistore_object_notification_parameters *notif_parameters; struct mapistore_connection_info *connInfo; + + connInfo = [[self context] connectionInfo]; // For the "source folder, we ensure the table caches are loaded so // that old and new state can be compared @@ -733,7 +735,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe notif_parameters->tags[4] = PR_RECIPIENT_ON_NORMAL_MSG_COUNT; notif_parameters->new_message_count = true; notif_parameters->message_count = [[sourceFolder messageKeys] count] - midCount; - connInfo = [[self context] connectionInfo]; mapistore_push_notification (connInfo->mstore_ctx, MAPISTORE_FOLDER, MAPISTORE_OBJECT_MODIFIED, diff --git a/OpenChange/MAPIStoreMailMessage.m b/OpenChange/MAPIStoreMailMessage.m index 16c073f64..8f9ccac78 100644 --- a/OpenChange/MAPIStoreMailMessage.m +++ b/OpenChange/MAPIStoreMailMessage.m @@ -28,6 +28,8 @@ #import #import #import +#import +#import #import #import #import @@ -272,6 +274,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) event = [events objectAtIndex: 0]; appointmentWrapper = [MAPIStoreAppointmentWrapper wrapperWithICalEvent: event + andUser: [[self context] activeUser] inTimeZone: [self ownerTimeZone]]; [appointmentWrapper retain]; } @@ -414,6 +417,14 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) return MAPISTORE_SUCCESS; } +- (int) getPidLidResponseStatus: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + *data = MAPILongValue (memCtx, 0); + + return MAPISTORE_SUCCESS; +} + - (int) getPidLidImapDeleted: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -481,6 +492,12 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) return MAPISTORE_SUCCESS; } +- (int) getPidLidFInvited: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getYes: data inMemCtx: memCtx]; +} + - (int) getPrMessageClass: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -504,6 +521,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) return MAPISTORE_SUCCESS; } +/* Note: this applies to regular mails... */ // - (int) getPrReplyRequested: (void **) data // TODO // inMemCtx: (TALLOC_CTX *) memCtx // { @@ -515,6 +533,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) // : [self getNo: data inMemCtx: memCtx]); // } +/* ... while this applies to invitations. */ - (int) getPrResponseRequested: (void **) data // TODO inMemCtx: (TALLOC_CTX *) memCtx { @@ -650,24 +669,117 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) return [self getSMTPAddrType: data inMemCtx: memCtx]; } +- (int) _getEmailAddressFromEmail: (NSString *) fullMail + inData: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NGMailAddress *ngAddress; + NSString *email; + + if (!fullMail) + fullMail = @""; + + ngAddress = [[NGMailAddressParser mailAddressParserWithString: fullMail] + parse]; + if ([ngAddress isKindOfClass: [NGMailAddress class]]) + email = [ngAddress address]; + else + email = @""; + + *data = [email asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) _getCNFromEmail: (NSString *) fullMail + inData: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NGMailAddress *ngAddress; + NSString *cn; + + if (!fullMail) + fullMail = @""; + + ngAddress = [[NGMailAddressParser mailAddressParserWithString: fullMail] + parse]; + if ([ngAddress isKindOfClass: [NGMailAddress class]]) + cn = [ngAddress address]; + else + cn = @""; + + *data = [cn asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) _getEntryIdFromEmail: (NSString *) fullMail + inData: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *username, *cn, *email; + SOGoUserManager *mgr; + NSDictionary *contactInfos; + NGMailAddress *ngAddress; + NSData *entryId; + int rc; + + if (fullMail) + { + ngAddress = [[NGMailAddressParser mailAddressParserWithString: fullMail] + parse]; + if ([ngAddress isKindOfClass: [NGMailAddress class]]) + { + email = [ngAddress address]; + cn = [ngAddress displayName]; + } + else + { + email = fullMail; + cn = @""; + } + + mgr = [SOGoUserManager sharedUserManager]; + contactInfos = [mgr contactInfosForUserWithUIDorEmail: email]; + if (contactInfos) + { + username = [contactInfos objectForKey: @"c_uid"]; + entryId = MAPIStoreInternalEntryId (username); + } + else + entryId = MAPIStoreExternalEntryId (cn, email); + + *data = [entryId asBinaryInMemCtx: memCtx]; + + rc = MAPISTORE_SUCCESS; + } + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + - (int) getPrSenderEmailAddress: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - NSString *stringValue; - - stringValue = [sogoObject from]; - if (!stringValue) - stringValue = @""; - - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; + return [self _getEmailAddressFromEmail: [sogoObject from] + inData: data + inMemCtx: memCtx]; } - (int) getPrSenderName: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - return [self getPrSenderEmailAddress: data inMemCtx: memCtx]; + return [self _getCNFromEmail: [sogoObject from] + inData: data + inMemCtx: memCtx]; +} + +- (int) getPrSenderEntryid: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getEntryIdFromEmail: [sogoObject from] + inData: data + inMemCtx: memCtx]; } - (int) getPrOriginalAuthorName: (void **) data @@ -685,33 +797,43 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) - (int) getPrSentRepresentingName: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - return [self getPrSenderEmailAddress: data inMemCtx: memCtx]; + return [self getPrSenderName: data inMemCtx: memCtx]; +} + +- (int) getPrSentRepresentingEntryid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPrSenderEntryid: data inMemCtx: memCtx]; } - (int) getPrReceivedByEmailAddress: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - NSString *stringValue; - - stringValue = [sogoObject to]; - if (!stringValue) - stringValue = @""; - - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; + return [self _getEmailAddressFromEmail: [sogoObject to] + inData: data + inMemCtx: memCtx]; } - (int) getPrReceivedByName: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - return [self getPrReceivedByEmailAddress: data inMemCtx: memCtx]; + return [self _getCNFromEmail: [sogoObject to] + inData: data + inMemCtx: memCtx]; +} + +- (int) getPrReceivedByEntryid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getEntryIdFromEmail: [sogoObject to] + inData: data + inMemCtx: memCtx]; } - (int) getPrRcvdRepresentingName: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - return [self getPrReceivedByEmailAddress: data inMemCtx: memCtx]; + return [self getPrReceivedByName: data inMemCtx: memCtx]; } - (int) getPrRcvdRepresentingEmailAddress: (void **) data @@ -720,10 +842,18 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) return [self getPrReceivedByEmailAddress: data inMemCtx: memCtx]; } +- (int) getPrRcvdRepresentingEntryid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPrReceivedByEntryid: data inMemCtx: memCtx]; +} + - (int) getPrDisplayTo: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - return [self getPrReceivedByEmailAddress: data inMemCtx: memCtx]; + *data = [[sogoObject to] asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; } - (int) getPrOriginalDisplayTo: (void **) data @@ -910,8 +1040,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) : MAPISTORE_ERR_NOT_FOUND); } -- (int) getPidLidServerProcessed: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx +- (int) getPidLidServerProcessed: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { if (!headerSetup) [self _fetchHeaderData]; @@ -922,6 +1051,44 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) : MAPISTORE_ERR_NOT_FOUND); } +- (int) getPidLidServerProcessingActions: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +{ + if (!headerSetup) + [self _fetchHeaderData]; + + return (mailIsEvent + ? [[self _appointmentWrapper] getPidLidServerProcessingActions: data + inMemCtx: memCtx] + : MAPISTORE_ERR_NOT_FOUND); +} + +- (int) getPrProcessed: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +{ + int rc; + + if (!headerSetup) + [self _fetchHeaderData]; + + if (mailIsEvent) + rc = [self getYes: data inMemCtx: memCtx]; + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + +// - (int) getPidLidServerProcessed: (void **) data +// inMemCtx: (TALLOC_CTX *) memCtx +// { +// if (!headerSetup) +// [self _fetchHeaderData]; + +// return (mailIsEvent +// ? [[self _appointmentWrapper] getPidLidServerProcessed: data +// inMemCtx: memCtx] +// : MAPISTORE_ERR_NOT_FOUND); +// } + - (int) getPidLidPrivate: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -1232,9 +1399,8 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) if (contactInfos) { username = [contactInfos objectForKey: @"c_uid"]; - // recipient->username = [username asUnicodeInMemCtx: msgData]; - // entryId = MAPIStoreInternalEntryId (username); - entryId = MAPIStoreExternalEntryId (cn, email); + recipient->username = [username asUnicodeInMemCtx: msgData]; + entryId = MAPIStoreInternalEntryId (username); } else entryId = MAPIStoreExternalEntryId (cn, email); diff --git a/OpenChange/MAPIStoreMapping.m b/OpenChange/MAPIStoreMapping.m index 0cd2c5cd2..ccacec96a 100644 --- a/OpenChange/MAPIStoreMapping.m +++ b/OpenChange/MAPIStoreMapping.m @@ -220,8 +220,8 @@ MAPIStoreMappingTDBTraverse (TDB_CONTEXT *ctx, TDB_DATA data1, TDB_DATA data2, [mapping setObject: urlString forKey: idKey]; [reverseMapping setObject: idKey forKey: urlString]; rc = YES; - // [self logWithFormat: @"registered url '%@' with id %lld (0x%.16"PRIx64")", - // urlString, idNbr, idNbr]; + [self logWithFormat: @"registered url '%@' with id %lld (0x%.16"PRIx64")", + urlString, idNbr, idNbr]; /* Add the record given its fid and mapistore_uri */ key.dptr = (unsigned char *) talloc_asprintf(NULL, "0x%.16"PRIx64, idNbr); @@ -245,18 +245,20 @@ MAPIStoreMappingTDBTraverse (TDB_CONTEXT *ctx, TDB_DATA data1, TDB_DATA data2, idKey = [NSNumber numberWithUnsignedLongLong: idNbr]; urlString = [mapping objectForKey: idKey]; - [reverseMapping removeObjectForKey: urlString]; - [mapping removeObjectForKey: idKey]; + if (urlString) + { + [self logWithFormat: @"unregistering url '%@' with id %lld (0x%.16"PRIx64")", + urlString, idNbr, idNbr]; + [reverseMapping removeObjectForKey: urlString]; + [mapping removeObjectForKey: idKey]; - /* We hard-delete the entry from the indexing database */ - key.dptr = (unsigned char *) talloc_asprintf(NULL, "0x%.16"PRIx64, idNbr); - key.dsize = strlen((const char *) key.dptr); + /* We hard-delete the entry from the indexing database */ + key.dptr = (unsigned char *) talloc_asprintf(NULL, "0x%.16"PRIx64, idNbr); + key.dsize = strlen((const char *) key.dptr); - tdb_delete(indexing->tdb, key); - talloc_free(key.dptr); - - // [self logWithFormat: @"unregistered url '%@' with id %lld (0x%.16"PRIx64")", - // urlString, idNbr, idNbr]; + tdb_delete(indexing->tdb, key); + talloc_free(key.dptr); + } } @end diff --git a/OpenChange/SOGoMAPIFSMessage.m b/OpenChange/SOGoMAPIFSMessage.m index 3403d0ccb..bc2377121 100644 --- a/OpenChange/SOGoMAPIFSMessage.m +++ b/OpenChange/SOGoMAPIFSMessage.m @@ -129,12 +129,15 @@ [container ensureDirectory]; + [self logWithFormat: @"%d props in whole dict", [properties count]]; + content = [NSPropertyListSerialization dataFromPropertyList: properties format: NSPropertyListBinaryFormat_v1_0 errorDescription: NULL]; if (![content writeToFile: [self completeFilename] atomically: NO]) [NSException raise: @"MAPIStoreIOException" format: @"could not save message"]; + [self logWithFormat: @"fs message written to '%@'", [self completeFilename]]; } - (NSString *) davEntityTag