From ae6ed0c055bbc0d25ef8b033d870585ba29b3aeb Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 30 Dec 2015 09:22:08 -0500 Subject: [PATCH] (feat) applied all changes as a patch coming from PR #180 --- OpenChange/GNUmakefile | 3 +- OpenChange/MAPIStoreAppointmentWrapper.h | 5 +- OpenChange/MAPIStoreAppointmentWrapper.m | 155 +-- OpenChange/MAPIStoreCalendarEmbeddedMessage.m | 1 - OpenChange/MAPIStoreCalendarFolder.m | 4 +- OpenChange/MAPIStoreCalendarMessage.m | 1 - OpenChange/MAPIStoreContactsFolder.m | 4 +- OpenChange/MAPIStoreContactsMessage.m | 1213 ++++++++++------- OpenChange/MAPIStoreDBFolder.m | 6 +- OpenChange/MAPIStoreDBMessage.m | 72 +- OpenChange/MAPIStoreDBMessageTable.m | 9 +- OpenChange/MAPIStoreFolder.m | 5 +- OpenChange/MAPIStoreGCSFolder.m | 6 +- OpenChange/MAPIStoreGCSMessage.m | 13 +- OpenChange/MAPIStoreGCSMessageTable.m | 18 +- OpenChange/MAPIStoreMailFolder.m | 82 +- OpenChange/MAPIStoreMailMessage.m | 56 +- OpenChange/MAPIStoreMailMessageTable.m | 4 +- OpenChange/MAPIStoreMailVolatileMessage.m | 33 +- OpenChange/MAPIStorePermissionsTable.m | 2 +- OpenChange/MAPIStoreRecurrenceUtils.h | 8 +- OpenChange/MAPIStoreRecurrenceUtils.m | 14 +- OpenChange/MAPIStoreSOGoObject.m | 4 +- OpenChange/MAPIStoreSamDBUtils.h | 6 +- OpenChange/MAPIStoreSamDBUtils.m | 23 +- OpenChange/MAPIStoreTasksFolder.m | 4 +- OpenChange/MAPIStoreTypes.m | 2 +- OpenChange/MAPIStoreUserContext.m | 2 + OpenChange/NSArray+MAPIStore.m | 2 +- OpenChange/NSDate+MAPIStore.h | 2 + OpenChange/NSDate+MAPIStore.m | 61 +- OpenChange/RTFHandler.h | 3 +- OpenChange/RTFHandler.m | 1010 ++++++++++---- OpenChange/dbmsgreader.m | 47 +- OpenChange/iCalEvent+MAPIStore.m | 65 +- OpenChange/iCalTimeZone+MAPIStore.h | 5 + OpenChange/iCalTimeZone+MAPIStore.m | 217 ++- .../Appointments/SOGoAppointmentFolder.m | 10 +- SoObjects/Contacts/SOGoContactSourceFolder.m | 21 +- SoObjects/Mailer/NSDictionary+Mail.m | 59 +- SoObjects/SOGo/GNUmakefile | 6 + SoObjects/SOGo/LDAPSource.h | 6 +- SoObjects/SOGo/LDAPSource.m | 4 + SoObjects/SOGo/SOGoDomainDefaults.m | 3 + SoObjects/SOGo/SOGoGroup.m | 10 +- SoObjects/SOGo/SOGoObject.m | 3 + SoObjects/SOGo/SOGoSession.m | 3 +- SoObjects/SOGo/SOGoSource.h | 3 +- SoObjects/SOGo/SOGoSystemDefaults.m | 2 +- SoObjects/SOGo/SOGoUser.m | 2 +- SoObjects/SOGo/SOGoUserManager.h | 1 + SoObjects/SOGo/SOGoUserManager.m | 321 +++-- SoObjects/SOGo/SQLSource.m | 3 +- Tools/SOGoToolBackup.m | 14 +- UI/WebServerResources/angular-material | 2 +- 55 files changed, 2397 insertions(+), 1243 deletions(-) diff --git a/OpenChange/GNUmakefile b/OpenChange/GNUmakefile index 8006b21b9..4fc207904 100644 --- a/OpenChange/GNUmakefile +++ b/OpenChange/GNUmakefile @@ -145,7 +145,8 @@ $(DBMSGREADER_TOOL)_LIB_DIRS += \ -L../SoObjects/SOGo/SOGo.framework/sogo -lSOGo \ -L../SOPE/GDLContentStore/obj/ -lGDLContentStore \ -L../SOPE/NGCards/obj/ -lNGCards \ - -lNGObjWeb + -lNGObjWeb \ + $(LIBMAPI_LIBS) TEST_TOOL_NAME += $(PLREADER_TOOL) $(DBMSGREADER_TOOL) diff --git a/OpenChange/MAPIStoreAppointmentWrapper.h b/OpenChange/MAPIStoreAppointmentWrapper.h index aecf2200f..d114076fe 100644 --- a/OpenChange/MAPIStoreAppointmentWrapper.h +++ b/OpenChange/MAPIStoreAppointmentWrapper.h @@ -24,6 +24,7 @@ #define MAPISTORECALENDARWRAPPER_H #import +#import #import #import "MAPIStoreObjectProxy.h" @@ -42,7 +43,7 @@ iCalCalendar *calendar; iCalEvent *firstEvent; iCalEvent *event; - NSTimeZone *timeZone; + iCalTimeZone *timeZone; SOGoUser *user; NSString *senderEmail; NSData *globalObjectId; @@ -57,12 +58,10 @@ + (id) wrapperWithICalEvent: (iCalEvent *) newEvent andUser: (SOGoUser *) newUser andSenderEmail: (NSString *) newSenderEmail - inTimeZone: (NSTimeZone *) newTimeZone withConnectionInfo: (struct mapistore_connection_info *) newConnInfo; - (id) initWithICalEvent: (iCalEvent *) newEvent andUser: (SOGoUser *) newUser andSenderEmail: (NSString *) newSenderEmail - inTimeZone: (NSTimeZone *) newTimeZone withConnectionInfo: (struct mapistore_connection_info *) newConnInfo; /* getters */ diff --git a/OpenChange/MAPIStoreAppointmentWrapper.m b/OpenChange/MAPIStoreAppointmentWrapper.m index 4b96a3515..11a189500 100644 --- a/OpenChange/MAPIStoreAppointmentWrapper.m +++ b/OpenChange/MAPIStoreAppointmentWrapper.m @@ -37,7 +37,9 @@ #import #import #import +#import #import +#import #import #import @@ -80,7 +82,6 @@ static NSCharacterSet *hexCharacterSet = nil; + (id) wrapperWithICalEvent: (iCalEvent *) newEvent andUser: (SOGoUser *) newUser andSenderEmail: (NSString *) newSenderEmail - inTimeZone: (NSTimeZone *) newTimeZone withConnectionInfo: (struct mapistore_connection_info *) newConnInfo { MAPIStoreAppointmentWrapper *wrapper; @@ -88,7 +89,6 @@ static NSCharacterSet *hexCharacterSet = nil; wrapper = [[self alloc] initWithICalEvent: newEvent andUser: newUser andSenderEmail: newSenderEmail - inTimeZone: newTimeZone withConnectionInfo: newConnInfo]; [wrapper autorelease]; @@ -182,10 +182,10 @@ static NSCharacterSet *hexCharacterSet = nil; - (id) initWithICalEvent: (iCalEvent *) newEvent andUser: (SOGoUser *) newUser andSenderEmail: (NSString *) newSenderEmail - inTimeZone: (NSTimeZone *) newTimeZone withConnectionInfo: (struct mapistore_connection_info *) newConnInfo { NSArray *events; + iCalTimeZone *tz; if ((self = [self init])) { @@ -194,9 +194,20 @@ static NSCharacterSet *hexCharacterSet = nil; event = newEvent; events = [calendar events]; firstEvent = [events objectAtIndex: 0]; - ASSIGN (timeZone, newTimeZone); ASSIGN (user, newUser); ASSIGN (senderEmail, newSenderEmail); + /* If newEvent comes from the client, we set its time zone in + updateFromMAPIProperties. If it is not present, we use the + time zone of the user */ + tz = (iCalTimeZone *) [calendar firstChildWithTag: @"vtimezone"]; + if (!tz) + { + tz = [iCalTimeZone timeZoneForName: [[[user userDefaults] timeZone] name]]; + if (!tz) + [self logWithFormat: @"no time zone could be set"]; + } + ASSIGN (timeZone, tz); + [self _setupITIPContext]; } @@ -266,7 +277,7 @@ static NSCharacterSet *hexCharacterSet = nil; { username = [contactInfos objectForKey: @"sAMAccountName"]; recipient->username = [username asUnicodeInMemCtx: msgData]; - entryId = MAPIStoreInternalEntryId (connInfo->sam_ctx, username); + entryId = MAPIStoreInternalEntryId (connInfo, username); } else { @@ -367,7 +378,7 @@ static NSCharacterSet *hexCharacterSet = nil; { username = [contactInfos objectForKey: @"sAMAccountName"]; recipient->username = [username asUnicodeInMemCtx: msgData]; - entryId = MAPIStoreInternalEntryId (connInfo->sam_ctx, username); + entryId = MAPIStoreInternalEntryId (connInfo, username); } else { @@ -721,22 +732,15 @@ static NSCharacterSet *hexCharacterSet = nil; inMemCtx: (TALLOC_CTX *) memCtx { NSCalendarDate *dateValue; - NSInteger offset; // if ([event isRecurrent]) // dateValue = [event firstRecurrenceStartDate]; // else dateValue = [event startDate]; if ([event isAllDay]) - { - offset = -[timeZone secondsFromGMTForDate: dateValue]; - dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: offset]; - } - [dateValue setTimeZone: utcTZ]; + dateValue = [timeZone shiftedCalendarDateForDate: dateValue]; *data = [dateValue asFileTimeInMemCtx: memCtx]; - + return MAPISTORE_SUCCESS; } @@ -749,22 +753,14 @@ static NSCharacterSet *hexCharacterSet = nil; exceptions, where it is the normal start date for the day of the exception. */ NSCalendarDate *dateValue; - NSInteger offset; dateValue = [event recurrenceId]; if (!dateValue) dateValue = [event startDate]; - [dateValue setTimeZone: timeZone]; if ([event isAllDay]) - { - offset = -[timeZone secondsFromGMTForDate: dateValue]; - dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: offset]; - } - [dateValue setTimeZone: utcTZ]; + dateValue = [timeZone shiftedCalendarDateForDate: dateValue]; *data = [dateValue asFileTimeInMemCtx: memCtx]; - + return MAPISTORE_SUCCESS; } @@ -772,19 +768,12 @@ static NSCharacterSet *hexCharacterSet = nil; inMemCtx: (TALLOC_CTX *) memCtx { NSCalendarDate *dateValue; - NSInteger offset; dateValue = [firstEvent startDate]; if ([firstEvent isAllDay]) - { - offset = -[timeZone secondsFromGMTForDate: dateValue]; - dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: offset]; - } - [dateValue setTimeZone: utcTZ]; + dateValue = [timeZone shiftedCalendarDateForDate: dateValue]; *data = [dateValue asFileTimeInMemCtx: memCtx]; - + return MAPISTORE_SUCCESS; } @@ -804,8 +793,8 @@ static NSCharacterSet *hexCharacterSet = nil; month: [start monthOfYear] day: [start dayOfMonth] hour: 0 minute: 0 second: 0 - timeZone: timeZone]; - [dateValue setTimeZone: utcTZ]; + timeZone: utcTZ]; + dateValue = [timeZone shiftedCalendarDateForDate: dateValue]; *data = [dateValue asFileTimeInMemCtx: memCtx]; rc = MAPISTORE_SUCCESS; } @@ -829,7 +818,7 @@ static NSCharacterSet *hexCharacterSet = nil; dateValue = [event startDate]; offset = [event durationAsTimeInterval]; if ([event isAllDay]) - offset -= [timeZone secondsFromGMTForDate: dateValue]; + offset -= [[timeZone periodForDate: dateValue] secondsOffsetFromGMT]; dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: offset]; @@ -847,15 +836,14 @@ static NSCharacterSet *hexCharacterSet = nil; dateValue = [event recurrenceId]; if (!dateValue) dateValue = [event startDate]; - [dateValue setTimeZone: timeZone]; offset = [firstEvent durationAsTimeInterval]; if ([firstEvent isAllDay]) - offset -= [timeZone secondsFromGMTForDate: dateValue]; + offset -= [[timeZone periodForDate: dateValue] secondsOffsetFromGMT]; dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: offset]; *data = [dateValue asFileTimeInMemCtx: memCtx]; - + return MAPISTORE_SUCCESS; } @@ -871,7 +859,7 @@ static NSCharacterSet *hexCharacterSet = nil; dateValue = [firstEvent startDate]; offset = [firstEvent durationAsTimeInterval]; if ([event isAllDay]) - offset -= [timeZone secondsFromGMTForDate: dateValue]; + offset -= [[timeZone periodForDate: dateValue] secondsOffsetFromGMT]; dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: offset]; @@ -885,23 +873,14 @@ static NSCharacterSet *hexCharacterSet = nil; { enum mapistore_error rc; NSCalendarDate *dateValue; - NSInteger offset; iCalRecurrenceRule *rrule; if ([event isRecurrent]) { rrule = [[event recurrenceRules] objectAtIndex: 0]; dateValue = [rrule untilDate]; - if (dateValue) - { - if ([event isAllDay]) - offset = -[timeZone secondsFromGMTForDate: dateValue]; - else - offset = 0; - dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: offset]; - } + if (dateValue && [event isAllDay]) + dateValue = [timeZone shiftedCalendarDateForDate: dateValue]; else dateValue = [NSCalendarDate dateWithYear: 4500 month: 8 day: 31 hour: 23 minute: 59 second: 00 @@ -932,7 +911,7 @@ static NSCharacterSet *hexCharacterSet = nil; if (contactInfos) { username = [contactInfos objectForKey: @"sAMAccountName"]; - entryId = MAPIStoreInternalEntryId (connInfo->sam_ctx, username); + entryId = MAPIStoreInternalEntryId (connInfo, username); } else entryId = MAPIStoreExternalEntryId (cn, email); @@ -1338,21 +1317,14 @@ static NSCharacterSet *hexCharacterSet = nil; { enum mapistore_error rc; NSCalendarDate *dateValue; - NSInteger offset; dateValue = [event recurrenceId]; if (dateValue) { rc = MAPISTORE_SUCCESS; - + if ([event isAllDay]) - { - offset = -[timeZone secondsFromGMTForDate: dateValue]; - dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: offset]; - } - [dateValue setTimeZone: utcTZ]; + dateValue = [timeZone shiftedCalendarDateForDate: dateValue]; *data = [dateValue asFileTimeInMemCtx: memCtx]; } else @@ -1377,7 +1349,6 @@ static NSCharacterSet *hexCharacterSet = nil; iCalEventChanges *changes; NSArray *changedProperties; NSCalendarDate *dateValue; - NSInteger offset; changes = [iCalEventChanges changesFromEvent: event toEvent: exceptionEvent]; @@ -1385,28 +1356,17 @@ static NSCharacterSet *hexCharacterSet = nil; memset (extendedException, 0, sizeof (struct ExtendedException)); extendedException->ChangeHighlight.Size = sizeof (uint32_t); - dateValue = [exceptionEvent startDate]; - offset = [timeZone secondsFromGMTForDate: dateValue]; - dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: offset]; + dateValue = [timeZone computedDateForDate: [exceptionEvent startDate]]; exceptionInfo->StartDateTime = [dateValue asMinutesSince1601]; extendedException->ChangeHighlight.Value = BIT_CH_START; extendedException->StartDateTime = exceptionInfo->StartDateTime; - dateValue = [exceptionEvent endDate]; - offset = [timeZone secondsFromGMTForDate: dateValue]; - dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: offset]; + dateValue = [timeZone computedDateForDate: [exceptionEvent endDate]]; exceptionInfo->EndDateTime = [dateValue asMinutesSince1601]; extendedException->ChangeHighlight.Value |= BIT_CH_END; extendedException->EndDateTime = exceptionInfo->EndDateTime; - dateValue = [[exceptionEvent recurrenceId] - dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: offset]; + dateValue = [timeZone computedDateForDate: [exceptionEvent recurrenceId]]; exceptionInfo->OriginalStartDate = [dateValue asMinutesSince1601]; extendedException->OriginalStartDate = exceptionInfo->OriginalStartDate; @@ -1464,7 +1424,6 @@ static NSCharacterSet *hexCharacterSet = nil; arp = talloc_zero (NULL, struct AppointmentRecurrencePattern); [rule fillRecurrencePattern: &arp->RecurrencePattern withEvent: event - inTimeZone: timeZone inMemCtx: arp]; arp->ReaderVersion2 = 0x00003006; arp->WriterVersion2 = 0x00003008; /* 0x3008 for compatibility with @@ -1475,7 +1434,7 @@ static NSCharacterSet *hexCharacterSet = nil; fields are relative to midnight of those days ([MS-OXOCAL] 2.2.1.44.5), so no time zone adjustment is needed */ if (![event isAllDay]) - [firstStartDate setTimeZone: timeZone]; + firstStartDate = [timeZone computedDateForDate: firstStartDate]; startMinutes = ([firstStartDate hourOfDay] * 60 + [firstStartDate minuteOfHour]); arp->StartTimeOffset = startMinutes; @@ -1701,15 +1660,16 @@ ReservedBlockEE2Size: 00 00 00 00 fromDate: (NSCalendarDate *) instanceDate; { uint16_t year; + NSCalendarDate *dateValue; if (instanceDate) { - [instanceDate setTimeZone: timeZone]; - year = [instanceDate yearOfCommonEra]; + dateValue = [timeZone computedDateForDate: instanceDate]; + year = [dateValue yearOfCommonEra]; newGlobalId->YH = year >> 8; newGlobalId->YL = year & 0xff; - newGlobalId->Month = [instanceDate monthOfYear]; - newGlobalId->D = [instanceDate dayOfMonth]; + newGlobalId->Month = [dateValue monthOfYear]; + newGlobalId->D = [dateValue dayOfMonth]; } } @@ -1974,7 +1934,6 @@ ReservedBlockEE2Size: 00 00 00 00 if (alarm) { alarmDate = [alarm nextAlarmDate]; - [alarmDate setTimeZone: utcTZ]; *data = [alarmDate asFileTimeInMemCtx: memCtx]; } else @@ -2036,8 +1995,7 @@ ReservedBlockEE2Size: 00 00 00 00 enum mapistore_error rc; NSString *tzid; - tzid = [(iCalDateTime *) [event firstChildWithTag: @"dtstart"] - value: 0 ofAttribute: @"tzid"]; + tzid = [timeZone tzId]; if ([tzid length] > 0) { *data = [tzid asUnicodeInMemCtx: memCtx]; @@ -2053,16 +2011,9 @@ ReservedBlockEE2Size: 00 00 00 00 inMemCtx: (TALLOC_CTX *) memCtx { enum mapistore_error rc; - iCalTimeZone *icalTZ; - icalTZ = [(iCalDateTime *) [event firstChildWithTag: @"dtstart"] timeZone]; - if (icalTZ) - { - *data = [icalTZ asTimeZoneStructInMemCtx: memCtx]; - rc = MAPISTORE_SUCCESS; - } - else - rc = MAPISTORE_ERR_NOT_FOUND; + *data = [timeZone asTimeZoneStructInMemCtx: memCtx]; + rc = MAPISTORE_SUCCESS; return rc; } @@ -2071,24 +2022,16 @@ ReservedBlockEE2Size: 00 00 00 00 inMemCtx: (TALLOC_CTX *) memCtx { enum mapistore_error rc; - iCalTimeZone *icalTZ; /* [MS-OXOCAL] 3.1.5.5.1: This property is used in floating (all-day) events, specified in floating time, to convert the start date from UTC to the user's time zone */ - if ([event isAllDay]) - icalTZ = [iCalTimeZone timeZoneForName: [timeZone timeZoneName]]; - else if ([event isRecurrent]) - icalTZ = [(iCalDateTime *) [event firstChildWithTag: @"dtstart"] timeZone]; - else - icalTZ = nil; - - if (icalTZ) + if ([event isAllDay] | [event isRecurrent]) { /* [MS-OXOCAL] 2.2.1.42: This property can only have the E flag set in the TimeZoneDefinition struct */ - *data = [icalTZ asZoneTimeDefinitionWithFlags: TZRULE_FLAG_EFFECTIVE_TZREG - inMemCtx: memCtx]; + *data = [timeZone asZoneTimeDefinitionWithFlags: TZRULE_FLAG_EFFECTIVE_TZREG + inMemCtx: memCtx]; rc = MAPISTORE_SUCCESS; } else diff --git a/OpenChange/MAPIStoreCalendarEmbeddedMessage.m b/OpenChange/MAPIStoreCalendarEmbeddedMessage.m index ca3ba12d6..986b106d3 100644 --- a/OpenChange/MAPIStoreCalendarEmbeddedMessage.m +++ b/OpenChange/MAPIStoreCalendarEmbeddedMessage.m @@ -58,7 +58,6 @@ wrapperWithICalEvent: [newContainer event] andUser: [userContext sogoUser] andSenderEmail: nil - inTimeZone: [userContext timeZone] withConnectionInfo: [context connectionInfo]]; [self addProxy: appointmentWrapper]; } diff --git a/OpenChange/MAPIStoreCalendarFolder.m b/OpenChange/MAPIStoreCalendarFolder.m index 2e131387b..5c9d70b39 100644 --- a/OpenChange/MAPIStoreCalendarFolder.m +++ b/OpenChange/MAPIStoreCalendarFolder.m @@ -117,11 +117,11 @@ if ([roles containsObject: SOGoRole_ObjectCreator]) rights |= RightsCreateItems; if ([roles containsObject: SOGoRole_ObjectEraser]) - rights |= RightsDeleteAll; + rights |= RightsDeleteAll | RightsDeleteOwn; if ([roles containsObject: SOGoCalendarRole_PublicModifier] && [roles containsObject: SOGoCalendarRole_PrivateModifier] && [roles containsObject: SOGoCalendarRole_ConfidentialModifier]) - rights |= RightsReadItems | RightsEditAll; + rights |= RightsReadItems | RightsEditAll | RightsEditOwn; else if ([roles containsObject: SOGoCalendarRole_PublicViewer] && [roles containsObject: SOGoCalendarRole_PrivateViewer] && [roles containsObject: SOGoCalendarRole_ConfidentialViewer]) diff --git a/OpenChange/MAPIStoreCalendarMessage.m b/OpenChange/MAPIStoreCalendarMessage.m index 7f025e400..108471eae 100644 --- a/OpenChange/MAPIStoreCalendarMessage.m +++ b/OpenChange/MAPIStoreCalendarMessage.m @@ -197,7 +197,6 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK; = [MAPIStoreAppointmentWrapper wrapperWithICalEvent: masterEvent andUser: [userContext sogoUser] andSenderEmail: nil - inTimeZone: [userContext timeZone] withConnectionInfo: [context connectionInfo]]; [self addProxy: appointmentWrapper]; } diff --git a/OpenChange/MAPIStoreContactsFolder.m b/OpenChange/MAPIStoreContactsFolder.m index 823f467cd..a04110d0f 100644 --- a/OpenChange/MAPIStoreContactsFolder.m +++ b/OpenChange/MAPIStoreContactsFolder.m @@ -96,9 +96,9 @@ if ([roles containsObject: SOGoRole_ObjectCreator]) rights |= RightsCreateItems; if ([roles containsObject: SOGoRole_ObjectEraser]) - rights |= RightsDeleteAll; + rights |= RightsDeleteAll | RightsDeleteOwn; if ([roles containsObject: SOGoRole_ObjectEditor]) - rights |= RightsEditAll; + rights |= RightsEditAll | RightsEditOwn; if ([roles containsObject: SOGoRole_ObjectViewer]) rights |= RightsReadItems; if (rights != 0) diff --git a/OpenChange/MAPIStoreContactsMessage.m b/OpenChange/MAPIStoreContactsMessage.m index cccb495e1..b183d445e 100644 --- a/OpenChange/MAPIStoreContactsMessage.m +++ b/OpenChange/MAPIStoreContactsMessage.m @@ -34,11 +34,14 @@ #import #import #import +#import #import "MAPIStoreAttachment.h" #import "MAPIStoreContactsAttachment.h" #import "MAPIStoreContactsFolder.h" +#import "MAPIStoreContext.h" #import "MAPIStorePropertySelectors.h" +#import "MAPIStoreSamDBUtils.h" #import "MAPIStoreTypes.h" #import "NSArray+MAPIStore.h" #import "NSDate+MAPIStore.h" @@ -140,75 +143,6 @@ return MAPISTORE_SUCCESS; } -// - (int) getPidTagOfflineAddressBookName: (void **) data -// inMemCtx: (TALLOC_CTX *) memCtx -// { -// *data = talloc_strdup (memCtx, "PR_OAB_NAME_UNICODE"); - -// return MAPISTORE_SUCCESS; -// } - -// - (int) getPidTagOfflineAddressBookLanguageId: (void **) data -// inMemCtx: (TALLOC_CTX *) memCtx -// { -// /* see http://msdn.microsoft.com/en-us/goglobal/bb895996.asxp */ -// /* English US */ -// *data = MAPILongValue (memCtx, 0x0409); - -// return MAPISTORE_SUCCESS; -// } - - -- (int) getPidTagTitle: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [[sogoObject vCard] title]; - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagProfession: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - int rc = MAPISTORE_SUCCESS; - - stringValue = [[sogoObject vCard] role]; - if (stringValue) - *data = [stringValue asUnicodeInMemCtx: memCtx]; - else - rc = MAPISTORE_ERR_NOT_FOUND; - - return rc; -} - -- (int) getPidTagCompanyName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - CardElement *org; - - org = [[sogoObject vCard] org]; - *data = [[org flattenedValueAtIndex: 0 forKey: @""] - asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagDepartmentName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - CardElement *org; - - org = [[sogoObject vCard] org]; - *data = [[org flattenedValueAtIndex: 1 forKey: @""] - asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - - (int) getPidTagSendInternetEncoding: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -258,39 +192,6 @@ return MAPISTORE_SUCCESS; } -- (int) getPidLidEmail1DisplayName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NGVCard *vCard; - NSString *fn, *email; - - vCard = [sogoObject vCard]; - fn = [vCard fn]; - email = [vCard preferredEMail]; - *data = [[NSString stringWithFormat: @"%@ (%@)", fn, email] - asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidLidEmail1OriginalDisplayName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getPidLidEmail1EmailAddress: data - inMemCtx: memCtx]; -} - -- (int) getPidLidEmail1EmailAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [[sogoObject vCard] preferredEMail]; - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - - (int) getPidTagAccount: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -341,46 +242,6 @@ return [self getYes: data inMemCtx: memCtx]; } -- (int) getPidLidEmail2EmailAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSMutableArray *emails; - NSString *email, *stringValue; - NGVCard *card; - NSUInteger count, max; - - emails = [NSMutableArray array]; - stringValue = nil; - - card = [sogoObject vCard]; - [emails addObjectsFromArray: [card childrenWithTag: @"email"]]; - [emails removeObjectsInArray: [card childrenWithTag: @"email" - andAttribute: @"type" - havingValue: @"pref"]]; - - max = [emails count]; - for (count = 0; !stringValue && count < max; count++) - { - email = [[emails objectAtIndex: count] flattenedValuesForKey: @""]; - - if ([email caseInsensitiveCompare: [card preferredEMail]] != NSOrderedSame) - stringValue = email; - } - - if (!stringValue) - stringValue = @""; - - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidLidEmail2OriginalDisplayName: (void **) data // Other email - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getPidLidEmail2EmailAddress: data inMemCtx: memCtx]; -} - - (int) getPidTagBody: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -396,6 +257,331 @@ return rc; } +- (int) getPidTagSensitivity: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getLongZero: data inMemCtx: memCtx]; +} + +// --------------------------------------------------------- +// Contact Name Properties [MS-OXOCNTC 2.2.1.1] +// --------------------------------------------------------- + +- (int) getPidTagNickname: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [[sogoObject vCard] nickname]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagGeneration: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] + flattenedValueAtIndex: 4 + forKey: @""]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagSurname: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] + flattenedValueAtIndex: 0 + forKey: @""]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagMiddleName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] + flattenedValueAtIndex: 2 + forKey: @""]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagGivenName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] + flattenedValueAtIndex: 1 + forKey: @""]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagDisplayNamePrefix: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] + flattenedValueAtIndex: 3 + forKey: @""]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +// ------------------------------------------------------- +// Electronic Address Properties [MS-OXOCNTC 2.2.1.2] +// ------------------------------------------------------- + +enum { // [MS-OXOCNTC] 2.2.1.2.11 + AddressBookProviderEmailValueEmail1 = 0, + AddressBookProviderEmailValueEmail2, + AddressBookProviderEmailValueEmail3, + AddressBookProviderEmailValueBusinessFax, + AddressBookProviderEmailValueHomeFax, + AddressBookProviderEmailValuePrimaryFax +}; + +/* + Fetch Email1, Email2 and Email3 values from the vcard. + Returns nil if not found +*/ +- (NSString *) _fetchEmailAddress: (NSUInteger) position +{ + NSMutableArray *emails; + NSString *email, *stringValue = nil; + NGVCard *card; + NSUInteger count, max; + + card = [sogoObject vCard]; + + if (position == 1) + // Predefined email + return [card preferredEMail]; + + emails = [NSMutableArray array]; + [emails addObjectsFromArray: [card childrenWithTag: @"email"]]; + [emails removeObjectsInArray: [card childrenWithTag: @"email" + andAttribute: @"type" + havingValue: @"pref"]]; + max = [emails count]; + for (count = 0; !stringValue && count < max; count++) + { + email = [[emails objectAtIndex: count] flattenedValuesForKey: @""]; + + if ([email caseInsensitiveCompare: [card preferredEMail]] != NSOrderedSame) + { + position--; + if (position == 1) + stringValue = email; + } + } + + return stringValue; +} + +/* + Store on *data the given email (position) + It can return MAPISTORE_ERR_NOT_FOUND if the email doesn't exist +*/ +- (int) _getPidLidEmailAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx + atPosition: (NSUInteger) position +{ + NSString *email; + + email = [self _fetchEmailAddress: position]; + if (!email) return MAPISTORE_ERR_NOT_FOUND; + + *data = [email asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidLidEmail1EmailAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailAddress: data inMemCtx: memCtx atPosition: 1]; +} + +- (int) getPidLidEmail2EmailAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailAddress: data inMemCtx: memCtx atPosition: 2]; +} + +- (int) getPidLidEmail3EmailAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailAddress: data inMemCtx: memCtx atPosition: 3]; +} + +- (int) getPidLidEmail1OriginalDisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPidLidEmail1EmailAddress: data inMemCtx: memCtx]; +} + +- (int) getPidLidEmail2OriginalDisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPidLidEmail2EmailAddress: data inMemCtx: memCtx]; +} + +- (int) getPidLidEmail3OriginalDisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPidLidEmail3EmailAddress: data inMemCtx: memCtx]; +} + +- (int) getPidLidEmail1AddressType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + if (![self _fetchEmailAddress: 1]) return MAPISTORE_ERR_NOT_FOUND; + + return [self getSMTPAddrType: data inMemCtx: memCtx]; +} + +- (int) getPidLidEmail2AddressType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + if (![self _fetchEmailAddress: 2]) return MAPISTORE_ERR_NOT_FOUND; + + return [self getSMTPAddrType: data inMemCtx: memCtx]; +} + +- (int) getPidLidEmail3AddressType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + if (![self _fetchEmailAddress: 3]) return MAPISTORE_ERR_NOT_FOUND; + + return [self getSMTPAddrType: data inMemCtx: memCtx]; +} + +/* + Store on *data a string with the display name for the given email (position) + It can return MAPISTORE_ERR_NOT_FOUND if the email doesn't exist +*/ +- (int) _getPidLidEmailDisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx + atPosition: (NSUInteger) position +{ + NGVCard *vCard; + NSString *fn, *email; + + email = [self _fetchEmailAddress: position]; + if (!email) return MAPISTORE_ERR_NOT_FOUND; + + vCard = [sogoObject vCard]; + fn = [vCard fn]; + + *data = [[NSString stringWithFormat: @"%@ (%@)", fn, email] + asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidLidEmail1DisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailDisplayName: data inMemCtx: memCtx atPosition: 1]; +} + +- (int) getPidLidEmail2DisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailDisplayName: data inMemCtx: memCtx atPosition: 2]; +} + +- (int) getPidLidEmail3DisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailDisplayName: data inMemCtx: memCtx atPosition: 3]; +} + +/* + Return an entry id (either one-off or addressbook entry) for the given email + It can return nil if the email doesn't exist +*/ +- (NSData *) _buildEntryIdForEmail: (NSUInteger) position +{ + NSString *email, *username; + NSData *data; + SOGoUserManager *mgr; + NSDictionary *contactInfos; + + email = [self _fetchEmailAddress: position]; + if (!email) return nil; + + // Try to figure out if this email is from local domain + mgr = [SOGoUserManager sharedUserManager]; + contactInfos = [mgr contactInfosForUserWithUIDorEmail: email]; + if (contactInfos) + { + username = [contactInfos objectForKey: @"sAMAccountName"]; + data = MAPIStoreInternalEntryId ([[self context] connectionInfo], username); + } + else + data = MAPIStoreExternalEntryId ([[sogoObject vCard] fn], email); + + return data; +} +/* + Store on *data an entryId for the given email (position) + It can return MAPISTORE_ERR_NOT_FOUND if the email doesn't exist +*/ +- (int) _getPidLidEmailOriginalEntryId: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx + atPosition: (NSUInteger) position +{ + NSData *value; + + value = [self _buildEntryIdForEmail: position]; + if (!value) return MAPISTORE_ERR_NOT_FOUND; + + *data = [value asBinaryInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidLidEmail1OriginalEntryId: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailOriginalEntryId: data + inMemCtx: memCtx + atPosition: 1]; +} + +- (int) getPidLidEmail2OriginalEntryId: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailOriginalEntryId: data + inMemCtx: memCtx + atPosition: 2]; +} + +- (int) getPidLidEmail3OriginalEntryId: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getPidLidEmailOriginalEntryId: data + inMemCtx: memCtx + atPosition: 3]; +} + - (int) _getElement: (NSString *) elementTag ofType: (NSString *) aType excluding: (NSString *) aTypeToExclude @@ -413,14 +599,14 @@ vCard = [sogoObject vCard]; elements = [[vCard childrenWithTag: elementTag] - cardElementsWithAttribute: @"type" - havingValue: aType]; + cardElementsWithAttribute: @"type" + havingValue: aType]; max = [elements count]; for (count = 0; !stringValue && count < max; count++) { ce = [elements objectAtIndex: count]; if (!aTypeToExclude - || ![ce hasAttribute: @"type" havingValue: aTypeToExclude]) + || ![ce hasAttribute: @"type" havingValue: aTypeToExclude]) stringValue = [ce flattenedValueAtIndex: pos forKey: @""]; } @@ -432,41 +618,6 @@ return MAPISTORE_SUCCESS; } -- (int) getPidTagBusinessTelephoneNumber: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"tel" ofType: @"work" excluding: @"fax" - atPos: 0 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagHomeTelephoneNumber: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"tel" ofType: @"home" excluding: @"fax" - atPos: 0 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagMobileTelephoneNumber: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"tel" ofType: @"cell" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagPagerTelephoneNumber: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"tel" ofType: @"pager" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagPrimaryTelephoneNumber: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"tel" ofType: @"pref" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; -} - - (int) getPidTagBusinessFaxNumber: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -474,148 +625,68 @@ atPos: 0 inData: data inMemCtx: memCtx]; } -- (int) getPidTagBusinessHomePage: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"url" ofType: @"work" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagPersonalHomePage: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"url" ofType: @"home" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; -} - -- (int) getPidLidEmail1AddressType: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getSMTPAddrType: data inMemCtx: memCtx]; -} - -- (int) getPidLidEmail2AddressType: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getSMTPAddrType: data inMemCtx: memCtx]; -} - -- (int) getPidLidEmail3AddressType: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getSMTPAddrType: data inMemCtx: memCtx]; -} - -- (int) getPidLidInstantMessagingAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-aim"] - flattenedValuesForKey: @""]; - if (!stringValue) - stringValue = @""; - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidLidPostalAddressId: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx +/* + [MS-OXOCNTC] 2.2.1.2.11 + https://msdn.microsoft.com/en-us/library/ee158427%28v=exchg.80%29.aspx +*/ +- (NSArray *) _buildAddressBookProviderEmailList { + NSMutableArray *list = [[NSMutableArray alloc] init]; NSArray *elements; - CardElement *element; - uint32_t longValue = 0; - NGVCard *vCard; - vCard = [sogoObject vCard]; - elements = [[vCard childrenWithTag: @"adr"] - cardElementsWithAttribute: @"type" - havingValue: @"pref"]; + // Is there a fax number? + elements = [[[sogoObject vCard] childrenWithTag: @"tel"] + cardElementsWithAttribute: @"type" + havingValue: @"fax"]; if ([elements count] > 0) - { - element = [elements objectAtIndex: 0]; - if ([element hasAttribute: @"type" - havingValue: @"home"]) - longValue = 1; // The Home Address is the mailing address. - else if ([element hasAttribute: @"type" - havingValue: @"work"]) - longValue = 2; // The Work Address is the mailing address. - } - *data = MAPILongValue (memCtx, longValue); - + [list addObject: [NSNumber numberWithInteger: AddressBookProviderEmailValueBusinessFax]]; + + // How many different email addresses? + if ([self _fetchEmailAddress: 1]) + [list addObject: [NSNumber numberWithInteger: AddressBookProviderEmailValueEmail1]]; + else + return list; + if ([self _fetchEmailAddress: 2]) + [list addObject: [NSNumber numberWithInteger: AddressBookProviderEmailValueEmail2]]; + else + return list; + if ([self _fetchEmailAddress: 3]) + [list addObject: [NSNumber numberWithInteger: AddressBookProviderEmailValueEmail3]]; + + return list; +} + +- (int) getPidLidAddressBookProviderArrayType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + // [MS-OXOCNTC] 2.2.1.2.12 + // https://msdn.microsoft.com/en-us/library/ee218011%28v=exchg.80%29.aspx + uint32_t value = 0; + NSArray *emailList = [self _buildAddressBookProviderEmailList]; + + for (NSNumber *maskValue in emailList) + value |= 1 << [maskValue intValue]; + + *data = MAPILongValue (memCtx, value); + return MAPISTORE_SUCCESS; } - -// -// getters when no address is selected as the Mailing Address -// -- (int) getPidTagPostalAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx +- (int) getPidLidAddressBookProviderEmailList: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx { - return [self _getElement: @"label" ofType: @"pref" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; + NSArray *emailList = [self _buildAddressBookProviderEmailList]; + + *data = [emailList asMVLongInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; } -- (int) getPidTagPostOfficeBox: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"adr" ofType: @"pref" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; -} +// --------------------------------------------------------- +// Physical Address Properties [MS-OXOCNTC 2.2.1.3] +// --------------------------------------------------------- -- (int) getPidTagStreetAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"adr" ofType: @"pref" excluding: nil - atPos: 2 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagLocality: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"adr" ofType: @"pref" excluding: nil - atPos: 3 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagStateOrProvince: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"adr" ofType: @"pref" excluding: nil - atPos: 4 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagPostalCode: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"adr" ofType: @"pref" excluding: nil - atPos: 5 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagCountry: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"adr" ofType: @"pref" excluding: nil - atPos: 6 inData: data inMemCtx: memCtx]; -} - -// -// home address getters -// -- (int) getPidLidHomeAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"label" ofType: @"home" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; -} - -- (int) getPidTagHomeAddressPostOfficeBox: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self _getElement: @"adr" ofType: @"home" excluding: nil - atPos: 0 inData: data inMemCtx: memCtx]; -} +// Home Address - (int) getPidTagHomeAddressStreet: (void **) data inMemCtx: (TALLOC_CTX *) memCtx @@ -651,23 +722,22 @@ atPos: 6 inData: data inMemCtx: memCtx]; } -// -// Work addresss -// -- (int) getPidLidWorkAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx +- (int) getPidTagHomeAddressPostOfficeBox: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx { - return [self _getElement: @"label" ofType: @"work" excluding: nil + return [self _getElement: @"adr" ofType: @"home" excluding: nil atPos: 0 inData: data inMemCtx: memCtx]; } -- (int) getPidLidWorkAddressPostOfficeBox: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx +- (int) getPidLidHomeAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx { - return [self _getElement: @"adr" ofType: @"work" excluding: nil + return [self _getElement: @"label" ofType: @"home" excluding: nil atPos: 0 inData: data inMemCtx: memCtx]; } +// Work Address + - (int) getPidLidWorkAddressStreet: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -703,29 +773,150 @@ atPos: 6 inData: data inMemCtx: memCtx]; } -// -// -// -- (int) getPidTagNickname: (void **) data +- (int) getPidLidWorkAddressPostOfficeBox: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"work" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPidLidWorkAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"label" ofType: @"work" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +// Mailing Address + +- (int) getPidTagStreetAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"pref" excluding: nil + atPos: 2 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagLocality: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - NSString *stringValue; + return [self _getElement: @"adr" ofType: @"pref" excluding: nil + atPos: 3 inData: data inMemCtx: memCtx]; +} - stringValue = [[sogoObject vCard] nickname]; - *data = [stringValue asUnicodeInMemCtx: memCtx]; +- (int) getPidTagStateOrProvince: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"pref" excluding: nil + atPos: 4 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagPostalCode: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"pref" excluding: nil + atPos: 5 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagCountry: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"pref" excluding: nil + atPos: 6 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagPostOfficeBox: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"adr" ofType: @"pref" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagPostalAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"label" ofType: @"pref" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPidLidPostalAddressId: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSArray *elements; + CardElement *element; + uint32_t longValue = 0; + NGVCard *vCard; + + vCard = [sogoObject vCard]; + elements = [[vCard childrenWithTag: @"adr"] + cardElementsWithAttribute: @"type" + havingValue: @"pref"]; + if ([elements count] > 0) + { + element = [elements objectAtIndex: 0]; + if ([element hasAttribute: @"type" + havingValue: @"home"]) + longValue = 1; // The Home Address is the mailing address. + else if ([element hasAttribute: @"type" + havingValue: @"work"]) + longValue = 2; // The Work Address is the mailing address. + } + *data = MAPILongValue (memCtx, longValue); return MAPISTORE_SUCCESS; } +// ------------------------------------------------------- +// Telephone Properties [MS-OXOCNTC 2.2.1.4] +// ------------------------------------------------------- + +- (int) getPidTagPagerTelephoneNumber: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"tel" ofType: @"pager" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagBusinessTelephoneNumber: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"tel" ofType: @"work" excluding: @"fax" + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagHomeTelephoneNumber: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"tel" ofType: @"home" excluding: @"fax" + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagPrimaryTelephoneNumber: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"tel" ofType: @"pref" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagMobileTelephoneNumber: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"tel" ofType: @"cell" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +// --------------------------------------------------------- +// Event Properties [MS-OXOCNTC 2.2.1.5] +// --------------------------------------------------------- + - (int) getPidTagBirthday: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { NSCalendarDate *dateValue; NSString *stringValue; int rc = MAPISTORE_SUCCESS; - + stringValue = [[sogoObject vCard] bday]; - if (stringValue) + if ([stringValue length] != 0) { dateValue = [NSCalendarDate dateWithString: stringValue calendarFormat: @"%Y-%m-%d"]; @@ -734,7 +925,7 @@ } else rc = MAPISTORE_ERR_NOT_FOUND; - + return rc; } @@ -747,7 +938,7 @@ stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-anniversary"] flattenedValuesForKey: @""]; - if (stringValue && ! [stringValue isEqualToString: @""]) + if ([stringValue length] != 0) { dateValue = [NSCalendarDate dateWithString: stringValue calendarFormat: @"%Y-%m-%d"]; @@ -759,15 +950,54 @@ return rc; } -- (int) getPidTagSpouseName: (void **) data +// --------------------------------------------------------- +// Professional Properties [MS-OXOCNTC 2.2.1.6] +// --------------------------------------------------------- + +- (int) getPidTagTitle: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [[sogoObject vCard] title]; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagCompanyName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + CardElement *org; + + org = [[sogoObject vCard] org]; + *data = [[org flattenedValueAtIndex: 0 forKey: @""] + asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagDepartmentName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + CardElement *org; + + org = [[sogoObject vCard] org]; + *data = [[org flattenedValueAtIndex: 1 forKey: @""] + asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagOfficeLocation: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { NSString *stringValue; int rc = MAPISTORE_SUCCESS; - stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-spouse"] - flattenedValuesForKey: @""]; - if (stringValue) + stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-office"] + flattenedValuesForKey: @""]; + if ([stringValue length] != 0) *data = [stringValue asUnicodeInMemCtx: memCtx]; else rc = MAPISTORE_ERR_NOT_FOUND; @@ -782,8 +1012,8 @@ int rc = MAPISTORE_SUCCESS; stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-manager"] - flattenedValuesForKey: @""]; - if (stringValue) + flattenedValuesForKey: @""]; + if ([stringValue length] != 0) *data = [stringValue asUnicodeInMemCtx: memCtx]; else rc = MAPISTORE_ERR_NOT_FOUND; @@ -798,8 +1028,8 @@ int rc = MAPISTORE_SUCCESS; stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-assistant"] - flattenedValuesForKey: @""]; - if (stringValue) + flattenedValuesForKey: @""]; + if ([stringValue length] != 0) *data = [stringValue asUnicodeInMemCtx: memCtx]; else rc = MAPISTORE_ERR_NOT_FOUND; @@ -807,14 +1037,13 @@ return rc; } -- (int) getPidTagOfficeLocation: (void **) data +- (int) getPidTagProfession: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { NSString *stringValue; int rc = MAPISTORE_SUCCESS; - stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-office"] - flattenedValuesForKey: @""]; + stringValue = [[sogoObject vCard] role]; if (stringValue) *data = [stringValue asUnicodeInMemCtx: memCtx]; else @@ -823,97 +1052,10 @@ return rc; } -- (int) getPidLidFreeBusyLocation: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - int rc = MAPISTORE_SUCCESS; +// --------------------------------------------------------- +// Contact Photo Properties [MS-OXOCNTC 2.2.1.8] +// --------------------------------------------------------- - stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"fburl"] - flattenedValuesForKey: @""]; - if (stringValue) - *data = [stringValue asUnicodeInMemCtx: memCtx]; - else - rc = MAPISTORE_ERR_NOT_FOUND; - - return rc; -} - -// -// Decomposed fullname getters -// -- (int) getPidTagSurname: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] - flattenedValueAtIndex: 0 - forKey: @""]; - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagGivenName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] - flattenedValueAtIndex: 1 - forKey: @""]; - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagMiddleName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] - flattenedValueAtIndex: 2 - forKey: @""]; - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagDisplayNamePrefix: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] - flattenedValueAtIndex: 3 - forKey: @""]; - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagGeneration: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [[[sogoObject vCard] firstChildWithTag: @"n"] - flattenedValueAtIndex: 4 - forKey: @""]; - *data = [stringValue asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagSensitivity: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getLongZero: data inMemCtx: memCtx]; -} - -/* attachments (photos) */ - (void) _fetchAttachmentParts { NGVCardPhoto *photo; @@ -968,7 +1110,7 @@ } - (void) _updatePhotoInVCard: (NGVCard *) card -fromProperties: (NSDictionary *) attachmentProps + fromProperties: (NSDictionary *) attachmentProps { NSString *photoExt, *photoType, *content; CardElement *photo; @@ -1008,6 +1150,72 @@ fromProperties: (NSDictionary *) attachmentProps } } +// --------------------------------------------------------- +// Other Properties [MS-OXOCNTC 2.2.1.10] +// --------------------------------------------------------- + +- (int) getPidTagSpouseName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + int rc = MAPISTORE_SUCCESS; + + stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-spouse"] + flattenedValuesForKey: @""]; + if ([stringValue length] != 0) + *data = [stringValue asUnicodeInMemCtx: memCtx]; + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + +- (int) getPidLidInstantMessagingAddress: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-aim"] + flattenedValuesForKey: @""]; + if (!stringValue) + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidLidFreeBusyLocation: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + int rc = MAPISTORE_SUCCESS; + + stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"fburl"] + flattenedValuesForKey: @""]; + if ([stringValue length] != 0) + *data = [stringValue asUnicodeInMemCtx: memCtx]; + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + +- (int) getPidTagPersonalHomePage: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"url" ofType: @"home" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +- (int) getPidTagBusinessHomePage: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getElement: @"url" ofType: @"work" excluding: nil + atPos: 0 inData: data inMemCtx: memCtx]; +} + +// --------------------------------------------------------- + - (BOOL) subscriberCanReadMessage { return [[self activeUserRoles] containsObject: SOGoRole_ObjectViewer]; @@ -1024,10 +1232,12 @@ fromProperties: (NSDictionary *) attachmentProps || (!isNew && [roles containsObject: SOGoRole_ObjectEditor])); } -// -// -// -- (void) save:(TALLOC_CTX *) memCtx +- (void) saveDistList:(TALLOC_CTX *) memCtx +{ + [self warnWithFormat: @"IPM.DistList messages are ignored"]; +} + +- (void) saveContact:(TALLOC_CTX *) memCtx { NSArray *elements, *units; CardElement *element; @@ -1043,7 +1253,7 @@ fromProperties: (NSDictionary *) attachmentProps [newCard setVersion: @"3.0"]; [newCard setProdID: @"-//Inverse inc.//OpenChange+SOGo//EN"]; [newCard setProfile: @"vCard"]; - + // Decomposed fullname [newCard setNWithFamily: [properties objectForKey: MAPIPropertyKey(PR_SURNAME_UNICODE)] given: [properties objectForKey: MAPIPropertyKey(PR_GIVEN_NAME_UNICODE)] @@ -1066,26 +1276,26 @@ fromProperties: (NSDictionary *) attachmentProps if (value) { if ([elements count] > 0) - [[elements objectAtIndex: 0] setSingleValue: value forKey: @""]; + [[elements objectAtIndex: 0] setSingleValue: value forKey: @""]; else - [newCard addEmail: value - types: [NSArray arrayWithObject: @"pref"]]; + [newCard addEmail: value + types: [NSArray arrayWithObject: @"pref"]]; } value = [properties objectForKey: MAPIPropertyKey (PidLidEmail2EmailAddress)]; if (value) { if ([elements count] > 1) - [[elements objectAtIndex: 1] setSingleValue: value forKey: @""]; + [[elements objectAtIndex: 1] setSingleValue: value forKey: @""]; else - [newCard addEmail: value types: nil]; + [newCard addEmail: value types: nil]; } value = [properties objectForKey: MAPIPropertyKey (PidLidEmail3EmailAddress)]; if (value) { if ([elements count] > 2) - [[elements objectAtIndex: 2] setSingleValue: value forKey: @""]; + [[elements objectAtIndex: 2] setSingleValue: value forKey: @""]; else - [newCard addEmail: value types: nil]; + [newCard addEmail: value types: nil]; } // @@ -1096,39 +1306,39 @@ fromProperties: (NSDictionary *) attachmentProps // 0x00000000 - No address is selected as the Mailing Address. // 0x00000001 - The Home Address is the Mailing Address. // 0x00000002 - The Work Address is the Mailing Address - // 0x00000003 - The Other Address is the Mailing Address. + // 0x00000003 - The Other Address is the Mailing Address. // // postalAddressId = [[properties objectForKey: MAPIPropertyKey (PidLidPostalAddressId)] - intValue]; - + intValue]; + value = [properties objectForKey: MAPIPropertyKey(PidLidWorkAddress)]; if ([value length]) { elements = [newCard childrenWithTag: @"label" - andAttribute: @"type" - havingValue: @"work"]; + andAttribute: @"type" + havingValue: @"work"]; if ([elements count] > 0) - element = [elements objectAtIndex: 0]; + element = [elements objectAtIndex: 0]; else - { - element = [CardElement elementWithTag: @"label"]; - [element addAttribute: @"type" value: @"work"]; - [newCard addChild: element]; - } + { + element = [CardElement elementWithTag: @"label"]; + [element addAttribute: @"type" value: @"work"]; + [newCard addChild: element]; + } if (postalAddressId == 2) - { - [element removeValue: @"pref" - fromAttribute: @"type"]; - [element addAttribute: @"type" - value: @"pref"]; - } + { + [element removeValue: @"pref" + fromAttribute: @"type"]; + [element addAttribute: @"type" + value: @"pref"]; + } [element setSingleValue: value forKey: @""]; } elements = [newCard childrenWithTag: @"adr" - andAttribute: @"type" - havingValue: @"work"]; + andAttribute: @"type" + havingValue: @"work"]; if ([elements count] > 0) element = [elements objectAtIndex: 0]; else @@ -1157,7 +1367,7 @@ fromProperties: (NSDictionary *) attachmentProps value = [properties objectForKey: MAPIPropertyKey(PidLidWorkAddressCountry)]; if (value) [element setSingleValue: value atIndex: 6 forKey: @""]; - + // // home postal addresses handling // @@ -1165,29 +1375,29 @@ fromProperties: (NSDictionary *) attachmentProps if ([value length]) { elements = [newCard childrenWithTag: @"label" - andAttribute: @"type" - havingValue: @"home"]; + andAttribute: @"type" + havingValue: @"home"]; if ([elements count] > 0) - element = [elements objectAtIndex: 0]; + element = [elements objectAtIndex: 0]; else - { - element = [CardElement elementWithTag: @"label"]; - [element addAttribute: @"type" value: @"home"]; - [newCard addChild: element]; - } + { + element = [CardElement elementWithTag: @"label"]; + [element addAttribute: @"type" value: @"home"]; + [newCard addChild: element]; + } if (postalAddressId == 1) - { - [element removeValue: @"pref" - fromAttribute: @"type"]; - [element addAttribute: @"type" - value: @"pref"]; - } + { + [element removeValue: @"pref" + fromAttribute: @"type"]; + [element addAttribute: @"type" + value: @"pref"]; + } [element setSingleValue: value forKey: @""]; } - + elements = [newCard childrenWithTag: @"adr" - andAttribute: @"type" - havingValue: @"home"]; + andAttribute: @"type" + havingValue: @"home"]; if ([elements count] > 0) element = [elements objectAtIndex: 0]; else @@ -1222,27 +1432,27 @@ fromProperties: (NSDictionary *) attachmentProps // // telephone numbers: work, home, fax, pager and mobile // - element = [self _elementWithTag: @"tel" ofType: @"work" forCard: newCard]; + element = [self _elementWithTag: @"tel" ofType: @"work" forCard: newCard]; value = [properties objectForKey: MAPIPropertyKey(PR_OFFICE_TELEPHONE_NUMBER_UNICODE)]; if (value) [element setSingleValue: value forKey: @""]; - element = [self _elementWithTag: @"tel" ofType: @"home" forCard: newCard]; + element = [self _elementWithTag: @"tel" ofType: @"home" forCard: newCard]; value = [properties objectForKey: MAPIPropertyKey(PR_HOME_TELEPHONE_NUMBER_UNICODE)]; if (value) [element setSingleValue: value forKey: @""]; - element = [self _elementWithTag: @"tel" ofType: @"fax" forCard: newCard]; + element = [self _elementWithTag: @"tel" ofType: @"fax" forCard: newCard]; value = [properties objectForKey: MAPIPropertyKey(PR_BUSINESS_FAX_NUMBER_UNICODE)]; if (value) [element setSingleValue: value forKey: @""]; - - element = [self _elementWithTag: @"tel" ofType: @"pager" forCard: newCard]; + + element = [self _elementWithTag: @"tel" ofType: @"pager" forCard: newCard]; value = [properties objectForKey: MAPIPropertyKey(PR_PAGER_TELEPHONE_NUMBER_UNICODE)]; if (value) [element setSingleValue: value forKey: @""]; - element = [self _elementWithTag: @"tel" ofType: @"cell" forCard: newCard]; + element = [self _elementWithTag: @"tel" ofType: @"cell" forCard: newCard]; value = [properties objectForKey: MAPIPropertyKey(PR_MOBILE_TELEPHONE_NUMBER_UNICODE)]; if (value) [element setSingleValue: value forKey: @""]; @@ -1264,7 +1474,7 @@ fromProperties: (NSDictionary *) attachmentProps value = [properties objectForKey: MAPIPropertyKey(PR_NICKNAME_UNICODE)]; if (value) [newCard setNickname: value]; - + value = [properties objectForKey: MAPIPropertyKey(PR_DEPARTMENT_NAME_UNICODE)]; if (value) units = [NSArray arrayWithObject: value]; @@ -1274,19 +1484,19 @@ fromProperties: (NSDictionary *) attachmentProps value = [properties objectForKey: MAPIPropertyKey(PR_COMPANY_NAME_UNICODE)]; if (value) [newCard setOrg: value units: units]; - + value = [properties objectForKey: MAPIPropertyKey(PR_BUSINESS_HOME_PAGE_UNICODE)]; if (value) { [[self _elementWithTag: @"url" ofType: @"work" forCard: newCard] - setSingleValue: value forKey: @""]; + setSingleValue: value forKey: @""]; } value = [properties objectForKey: MAPIPropertyKey(PidLidInstantMessagingAddress)]; if (value) { [[newCard uniqueChildWithTag: @"x-aim"] - setSingleValue: value forKey: @""]; + setSingleValue: value forKey: @""]; } value = [properties objectForKey: MAPIPropertyKey(PR_BIRTHDAY)]; @@ -1310,35 +1520,35 @@ fromProperties: (NSDictionary *) attachmentProps if (value) { [[newCard uniqueChildWithTag: @"x-ms-spouse"] - setSingleValue: value forKey: @""]; + setSingleValue: value forKey: @""]; } value = [properties objectForKey: MAPIPropertyKey(PR_MANAGER_NAME_UNICODE)]; if (value) { [[newCard uniqueChildWithTag: @"x-ms-manager"] - setSingleValue: value forKey: @""]; + setSingleValue: value forKey: @""]; } value = [properties objectForKey: MAPIPropertyKey(PR_ASSISTANT_UNICODE)]; if (value) { [[newCard uniqueChildWithTag: @"x-ms-assistant"] - setSingleValue: value forKey: @""]; + setSingleValue: value forKey: @""]; } value = [properties objectForKey: MAPIPropertyKey(PR_OFFICE_LOCATION_UNICODE)]; if (value) { [[newCard uniqueChildWithTag: @"x-ms-office"] - setSingleValue: value forKey: @""]; + setSingleValue: value forKey: @""]; } value = [properties objectForKey: MAPIPropertyKey(PidLidFreeBusyLocation)]; if (value) { [[newCard uniqueChildWithTag: @"fburl"] - setSingleValue: value forKey: @""]; + setSingleValue: value forKey: @""]; } /* photo */ @@ -1373,4 +1583,13 @@ fromProperties: (NSDictionary *) attachmentProps [self updateVersions]; } +- (void) save:(TALLOC_CTX *) memCtx +{ + NSString *messageClass = [properties objectForKey: MAPIPropertyKey(PR_MESSAGE_CLASS_UNICODE)]; + if ([messageClass isEqualToString: @"IPM.DistList"]) + [self saveDistList: memCtx]; + else + [self saveContact: memCtx]; +} + @end diff --git a/OpenChange/MAPIStoreDBFolder.m b/OpenChange/MAPIStoreDBFolder.m index d1734dc7c..a7c49b72d 100644 --- a/OpenChange/MAPIStoreDBFolder.m +++ b/OpenChange/MAPIStoreDBFolder.m @@ -102,7 +102,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; { value = get_SPropValue_SRow (aRow, PidTagDisplayName_string8); if (value) - folderName = [NSString stringWithUTF8String: value->value.lpszA]; + folderName = [NSString stringWithUTF8String: (const char *) value->value.lpszA]; else folderName = nil; } @@ -321,9 +321,9 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; if ([roles containsObject: MAPIStoreRightDeleteOwn]) rights |= RightsDeleteOwn; if ([roles containsObject: MAPIStoreRightEditAll]) - rights |= RightsEditAll; + rights |= RightsEditAll | RightsEditOwn; if ([roles containsObject: MAPIStoreRightDeleteAll]) - rights |= RightsDeleteAll; + rights |= RightsDeleteAll | RightsDeleteOwn; if ([roles containsObject: MAPIStoreRightCreateSubfolders]) rights |= RightsCreateSubfolders; if ([roles containsObject: MAPIStoreRightFolderOwner]) diff --git a/OpenChange/MAPIStoreDBMessage.m b/OpenChange/MAPIStoreDBMessage.m index bb5acbb57..98d357bf7 100644 --- a/OpenChange/MAPIStoreDBMessage.m +++ b/OpenChange/MAPIStoreDBMessage.m @@ -109,18 +109,70 @@ return self; } +- (NSString *) description +{ + id key, value; + NSEnumerator *propEnumerator; + NSMutableString *description; + + description = [NSMutableString stringWithFormat: @"%@ %@. Properties: {", NSStringFromClass ([self class]), + [self url]]; + + propEnumerator = [properties keyEnumerator]; + while ((key = [propEnumerator nextObject])) + { + uint32_t proptag = 0; + if ([key isKindOfClass: [NSString class]] && [(NSString *)key intValue] > 0) + proptag = [(NSString *)key intValue]; + else if ([key isKindOfClass: [NSNumber class]]) + proptag = [key unsignedLongValue]; + + if (proptag > 0) + { + const char *propTagName = get_proptag_name ([key unsignedLongValue]); + NSString *propName; + + if (propTagName) + propName = [NSString stringWithCString: propTagName + encoding: NSUTF8StringEncoding]; + else + propName = [NSString stringWithFormat: @"0x%.4x", [key unsignedLongValue]]; + + [description appendFormat: @"'%@': ", propName]; + } + else + [description appendFormat: @"'%@': ", key]; + + value = [properties objectForKey: key]; + [description appendFormat: @"%@ (%@), ", value, NSStringFromClass ([value class])]; + } + + [description appendString: @"}\n"]; + + return description; +} + - (uint64_t) objectVersion { - NSNumber *versionNbr; + /* Return the global counter from CN structure. + See [MS-OXCFXICS] Section 2.2.2.1 */ + NSNumber *versionNbr, *cn; uint64_t objectVersion; [(SOGoMAPIDBMessage *) sogoObject reloadIfNeeded]; - versionNbr = [properties objectForKey: @"version"]; + versionNbr = [properties objectForKey: @"version_number"]; if (versionNbr) - objectVersion = (([versionNbr unsignedLongLongValue] >> 16) - & 0x0000ffffffffffffLL); + objectVersion = exchange_globcnt ([versionNbr unsignedLongLongValue]); else - objectVersion = ULLONG_MAX; + { + /* Old version which stored the CN structure not useful for searching */ + cn = [properties objectForKey: @"version"]; + if (cn) + objectVersion = (([cn unsignedLongLongValue] >> 16) + & 0x0000ffffffffffffLL); + else + objectVersion = ULLONG_MAX; + } return objectVersion; } @@ -283,13 +335,19 @@ [properties setObject: attachmentParts forKey: @"attachments"]; newVersion = [[self context] getNewChangeNumber]; + newVersion = exchange_globcnt ((newVersion >> 16) & 0x0000ffffffffffffLL); + [properties setObject: [NSNumber numberWithUnsignedLongLong: newVersion] - forKey: @"version"]; + forKey: @"version_number"]; + + /* Remove old version */ + [properties removeObjectForKey: @"version"]; /* Update PredecessorChangeList accordingly */ [self _updatePredecessorChangeList]; - [self logWithFormat: @"%d props in dict", [properties count]]; + // [self logWithFormat: @"Saving %@", [self description]]; + // [self logWithFormat: @"%d props in dict", [properties count]]; [sogoObject save]; } diff --git a/OpenChange/MAPIStoreDBMessageTable.m b/OpenChange/MAPIStoreDBMessageTable.m index de22e238e..a8c68abd1 100644 --- a/OpenChange/MAPIStoreDBMessageTable.m +++ b/OpenChange/MAPIStoreDBMessageTable.m @@ -61,14 +61,15 @@ static Class MAPIStoreDBMessageK = Nil; if ((uint32_t) res->ulPropTag == PidTagChangeNumber) { + SEL operator; + value = NSObjectFromMAPISPropValue (&res->lpProp); cVersion = exchange_globcnt (([value unsignedLongLongValue] >> 16) & 0x0000ffffffffffffLL); version = [NSNumber numberWithUnsignedLongLong: cVersion]; - //[self logWithFormat: @"change number from oxcfxics: %.16lx", [value unsignedLongLongValue]]; - [self logWithFormat: @" version: %.16lx", cVersion]; - *qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"version" - operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo + operator = [self operatorFromRestrictionOperator: res->relop]; + *qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"version_number" + operatorSelector: operator value: version]; [*qualifier autorelease]; rc = MAPIRestrictionStateNeedsEval; diff --git a/OpenChange/MAPIStoreFolder.m b/OpenChange/MAPIStoreFolder.m index 735c04cd6..c111f2305 100644 --- a/OpenChange/MAPIStoreFolder.m +++ b/OpenChange/MAPIStoreFolder.m @@ -1549,7 +1549,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe struct Binary_r bin32; struct AddressBookEntryId *entryId; NSString *username; - struct ldb_context *samCtx; if (bin && bin->cb) { @@ -1559,8 +1558,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe entryId = get_AddressBookEntryId (NULL, &bin32); if (entryId) { - samCtx = [[self context] connectionInfo]->sam_ctx; - username = MAPIStoreSamDBUserAttribute (samCtx, @"legacyExchangeDN", + username = MAPIStoreSamDBUserAttribute ([[self context] connectionInfo], + @"legacyExchangeDN", [NSString stringWithUTF8String: entryId->X500DN], @"sAMAccountName"); } diff --git a/OpenChange/MAPIStoreGCSFolder.m b/OpenChange/MAPIStoreGCSFolder.m index 2dacdb4cf..336474675 100644 --- a/OpenChange/MAPIStoreGCSFolder.m +++ b/OpenChange/MAPIStoreGCSFolder.m @@ -681,12 +681,12 @@ static Class NSNumberK; - (NSNumber *) lastModifiedFromMessageChangeNumber: (NSString *) changeNumber { NSDictionary *mapping; - NSNumber *modseq; + NSNumber *lastModified; mapping = [[versionsMessage properties] objectForKey: @"VersionMapping"]; - modseq = [mapping objectForKey: changeNumber]; + lastModified = [mapping objectForKey: changeNumber]; - return modseq; + return lastModified; } - (NSString *) changeNumberForMessageWithKey: (NSString *) messageKey diff --git a/OpenChange/MAPIStoreGCSMessage.m b/OpenChange/MAPIStoreGCSMessage.m index 2f7afeada..58b4a9ca2 100644 --- a/OpenChange/MAPIStoreGCSMessage.m +++ b/OpenChange/MAPIStoreGCSMessage.m @@ -140,9 +140,16 @@ [parentFolder synchroniseCache]; changeKey = [parentFolder changeKeyForMessageWithKey: nameInContainer]; } - if (!changeKey) - abort (); - *data = [changeKey asBinaryInMemCtx: memCtx]; + if (changeKey) + *data = [changeKey asBinaryInMemCtx: memCtx]; + else + { + [self warnWithFormat: @"No change key for %@ in folder %@", + nameInContainer, + [parentFolder url] + ]; + rc = MAPISTORE_ERR_NOT_FOUND; + } } return rc; diff --git a/OpenChange/MAPIStoreGCSMessageTable.m b/OpenChange/MAPIStoreGCSMessageTable.m index e6da41039..0dac1fff9 100644 --- a/OpenChange/MAPIStoreGCSMessageTable.m +++ b/OpenChange/MAPIStoreGCSMessageTable.m @@ -27,6 +27,7 @@ #import #import +#import #import #import @@ -38,7 +39,6 @@ #import "MAPIStoreTypes.h" #import "MAPIStoreGCSFolder.h" - #import "MAPIStoreGCSMessageTable.h" #undef DEBUG @@ -89,21 +89,31 @@ if (res->ulPropTag == PidTagChangeNumber) { + NSString *changeNumber; + value = NSObjectFromMAPISPropValue (&res->lpProp); + changeNumber = [NSString stringWithUnsignedLongLong: [(NSNumber *)value unsignedLongLongValue]]; lastModified = [(MAPIStoreGCSFolder *) - container lastModifiedFromMessageChangeNumber: value]; + container lastModifiedFromMessageChangeNumber: changeNumber]; //[self logWithFormat: @"change number from oxcfxics: %.16lx", [value unsignedLongLongValue]]; //[self logWithFormat: @" c_lastmodified: %@", lastModified]; if (lastModified) { + SEL operator; + + operator = [self operatorFromRestrictionOperator: res->relop]; *qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"c_lastmodified" - operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo + operatorSelector: operator value: lastModified]; [*qualifier autorelease]; rc = MAPIRestrictionStateNeedsEval; } else - rc = MAPIRestrictionStateAlwaysTrue; + { + [self logWithFormat: @"No last modified found for: 0x%.16"PRIx64". Then no restriction applied", + [value unsignedLongLongValue]]; + rc = MAPIRestrictionStateAlwaysTrue; + } } else { diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index 7c8e4f8ac..6e87d4ecd 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -74,6 +74,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK; #include #include #include +#include #include #include @@ -181,7 +182,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK; if (aRow->lpProps[i].ulPropTag == PR_DISPLAY_NAME_UNICODE) folderName = [NSString stringWithUTF8String: aRow->lpProps[i].value.lpszW]; else if (aRow->lpProps[i].ulPropTag == PR_DISPLAY_NAME) - folderName = [NSString stringWithUTF8String: aRow->lpProps[i].value.lpszA]; + folderName = [NSString stringWithUTF8String: (const char *) aRow->lpProps[i].value.lpszA]; } if (folderName) @@ -257,6 +258,67 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK; return MAPISTORE_SUCCESS; } +- (EOQualifier *) simplifyQualifier: (EOQualifier *) qualifier +{ + /* Hack: Reduce the number of MODSEQ constraints to a single one as + we assume the difference among MODSEQs will be small enough to + return a small number of UIDs. + + This is the only case we do simplify: + MODSEQ >= x | MODSEQ >= y | MODSEQ >= z => MODSEQ >= min(x,y,z) + */ + if (qualifier && [qualifier isKindOfClass: [EOOrQualifier class]]) + { + EOQualifier *simplifiedQualifier; + NSArray *quals; + NSNumber *minModseq; + NSUInteger i, count; + + quals = [(EOOrQualifier *)qualifier qualifiers]; + count = [quals count]; + if (count < 2) + return qualifier; + + minModseq = [NSNumber numberWithUnsignedLongLong: ULLONG_MAX]; + + for (i = 0; i < count; i++) + { + EOQualifier *subQualifier; + + subQualifier = [quals objectAtIndex: i]; + if ([subQualifier isKindOfClass: [EOAndQualifier class]] + && [[(EOAndQualifier *)subQualifier qualifiers] count] == 1) + subQualifier = [[(EOAndQualifier *)subQualifier qualifiers] objectAtIndex: 0]; + + if ([subQualifier isKindOfClass: [EOKeyValueQualifier class]] + && [[(EOKeyValueQualifier *)subQualifier key] isEqualToString: @"MODSEQ"]) + { + NSNumber *value; + + value = (NSNumber *)[(EOKeyValueQualifier *)subQualifier value]; + if ([minModseq compare: value] == NSOrderedDescending + && [value unsignedLongLongValue] > 0) + minModseq = (NSNumber *)[(EOKeyValueQualifier *)subQualifier value]; + + } + else + return qualifier; + } + + if ([minModseq unsignedLongLongValue] > 0 && [minModseq unsignedLongLongValue] < ULLONG_MAX) + { + simplifiedQualifier = [[EOKeyValueQualifier alloc] + initWithKey: @"MODSEQ" + operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo + value: minModseq]; + [simplifiedQualifier autorelease]; + return simplifiedQualifier; + } + } + + return qualifier; +} + - (EOQualifier *) nonDeletedQualifier { static EOQualifier *nonDeletedQualifier = nil; @@ -281,7 +343,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK; andSortOrderings: (NSArray *) sortOrderings { NSArray *uidKeys; - EOQualifier *fetchQualifier; + EOQualifier *fetchQualifier, *simplifiedQualifier; if ([self ensureFolderExists]) { @@ -290,9 +352,10 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK; if (qualifier) { + simplifiedQualifier = [self simplifyQualifier: qualifier]; fetchQualifier = [[EOAndQualifier alloc] initWithQualifiers: - [self nonDeletedQualifier], qualifier, + [self nonDeletedQualifier], simplifiedQualifier, nil]; [fetchQualifier autorelease]; } @@ -599,7 +662,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) uint64_t lastModseqNbr; EOQualifier *searchQualifier; NSArray *uids, *changeNumbers; - NSUInteger count, max; + NSUInteger count, max, nFetched; NSArray *fetchResults; NSDictionary *result; NSData *changeKey; @@ -678,6 +741,13 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) = [fetchResults sortedArrayUsingFunction: _compareFetchResultsByMODSEQ context: NULL]; + nFetched = [fetchResults count]; + if (nFetched != max) { + [self errorWithFormat: @"Error fetching UIDs. Asked: %d Received: %d." + @"Check the IMAP conversation for details", max, nFetched]; + return NO; + } + for (count = 0; count < max; count++) { result = [fetchResults objectAtIndex: count]; @@ -1641,10 +1711,10 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP) rights |= RightsCreateItems; if ([roles containsObject: SOGoRole_ObjectEraser] && [roles containsObject: SOGoRole_FolderEraser]) - rights |= RightsDeleteAll; + rights |= RightsDeleteAll | RightsDeleteOwn; if ([roles containsObject: SOGoRole_ObjectEditor]) - rights |= RightsEditAll; + rights |= RightsEditAll | RightsEditOwn; if ([roles containsObject: SOGoRole_ObjectViewer]) rights |= RightsReadItems; if ([roles containsObject: SOGoRole_FolderCreator]) diff --git a/OpenChange/MAPIStoreMailMessage.m b/OpenChange/MAPIStoreMailMessage.m index 55ad92bd0..f79511eb6 100644 --- a/OpenChange/MAPIStoreMailMessage.m +++ b/OpenChange/MAPIStoreMailMessage.m @@ -340,7 +340,6 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) wrapperWithICalEvent: event andUser: [context activeUser] andSenderEmail: senderEmail - inTimeZone: [[self userContext] timeZone] withConnectionInfo: [context connectionInfo]]; [appointmentWrapper retain]; } @@ -522,6 +521,9 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) NSUInteger colIdx; NSString *stringValue; + /* As specified in [MS-OXCMAIL] 2.2.3.2.6.1, if there are three + or less characters followed by a colon at the beginning of + the subject, we can assume that's the subject prefix */ subject = [self subject]; colIdx = [subject rangeOfString: @":"].location; if (colIdx != NSNotFound && colIdx < 4) @@ -537,17 +539,45 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) - (int) getPidTagNormalizedSubject: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - NSString *subject; - NSUInteger colIdx; - NSString *stringValue; + NSString *stringValue, *subject; + NSUInteger quoteStartIdx, quoteEndIdx, colIdx; + NSRange quoteRange; + + if (!headerSetup) + [self _fetchHeaderData]; subject = [self subject]; - colIdx = [subject rangeOfString: @":"].location; - if (colIdx != NSNotFound && colIdx < 4) - stringValue = [[subject substringFromIndex: colIdx + 1] - stringByTrimmingLeadSpaces]; + if (mailIsMeetingRequest) + { + + /* SOGo "spices up" the invitation/update mail's subject, but + the client uses it to name the attendee's event, so we keep + only what's inside the quotes */ + quoteStartIdx = [subject rangeOfString: @"\""].location; + quoteEndIdx = [subject rangeOfString: @"\"" + options: NSBackwardsSearch].location; + if (quoteStartIdx != NSNotFound + && quoteEndIdx != NSNotFound + && quoteStartIdx != quoteEndIdx) + { + quoteRange = NSMakeRange(quoteStartIdx + 1, quoteEndIdx - quoteStartIdx - 1); + stringValue = [subject substringWithRange: quoteRange]; + } + else stringValue = subject; + } else - stringValue = subject; + { + + /* As specified in [MS-OXCMAIL] 2.2.3.2.6.1, if there are three + or less characters followed by a colon at the beginning of + the subject, we can assume that's the subject prefix */ + colIdx = [subject rangeOfString: @":"].location; + if (colIdx != NSNotFound && colIdx < 4) + stringValue = [[subject substringFromIndex: colIdx + 1] + stringByTrimmingLeadSpaces]; + else + stringValue = subject; + } if (!stringValue) stringValue = @""; *data = [stringValue asUnicodeInMemCtx: memCtx]; @@ -780,7 +810,6 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) NSDictionary *contactInfos; NGMailAddress *ngAddress; NSData *entryId; - struct ldb_context *samCtx; int rc; if (fullMail) @@ -803,8 +832,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) if (contactInfos) { username = [contactInfos objectForKey: @"sAMAccountName"]; - samCtx = [[self context] connectionInfo]->sam_ctx; - entryId = MAPIStoreInternalEntryId (samCtx, username); + entryId = MAPIStoreInternalEntryId([[self context] connectionInfo], username); } else entryId = MAPIStoreExternalEntryId (cn, email); @@ -1447,11 +1475,9 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) NSData *entryId; NSDictionary *contactInfos; SOGoUserManager *mgr; - struct ldb_context *samCtx; struct mapistore_message *msgData; struct mapistore_message_recipient *recipient; - samCtx = [[self context] connectionInfo]->sam_ctx; [super getMessageData: &msgData inMemCtx: memCtx]; if (!headerSetup) @@ -1505,7 +1531,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) { username = [contactInfos objectForKey: @"sAMAccountName"]; recipient->username = [username asUnicodeInMemCtx: msgData]; - entryId = MAPIStoreInternalEntryId (samCtx, username); + entryId = MAPIStoreInternalEntryId ([[self context] connectionInfo], username); } else { diff --git a/OpenChange/MAPIStoreMailMessageTable.m b/OpenChange/MAPIStoreMailMessageTable.m index c71097d63..d315f6527 100644 --- a/OpenChange/MAPIStoreMailMessageTable.m +++ b/OpenChange/MAPIStoreMailMessageTable.m @@ -181,8 +181,8 @@ static Class MAPIStoreMailMessageK, NSDataK, NSStringK; else { /* Ignore other operations as IMAP only support MODSEQ >= X */ - [self warnWithFormat: @"Ignoring %@ as only supported operators are > and >=", - [self operatorFromRestrictionOperator: res->relop]]; + [self warnWithFormat: @"Ignoring '%@' as only supported operators are > and >=", + NSStringFromSelector ([self operatorFromRestrictionOperator: res->relop])]; rc = MAPIRestrictionStateAlwaysTrue; } } diff --git a/OpenChange/MAPIStoreMailVolatileMessage.m b/OpenChange/MAPIStoreMailVolatileMessage.m index d6154c8e8..1db930ef8 100644 --- a/OpenChange/MAPIStoreMailVolatileMessage.m +++ b/OpenChange/MAPIStoreMailVolatileMessage.m @@ -338,13 +338,10 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" }; NSData *entryId; NSDictionary *allRecipients, *dict, *contactInfos; SOGoUserManager *mgr; - struct ldb_context *samCtx; struct mapistore_message *msgData; struct mapistore_message_recipient *recipient; enum ulRecipClass type; - samCtx = [[self context] connectionInfo]->sam_ctx; - // [super getMessageData: &msgData inMemCtx: memCtx]; msgData = talloc_zero (memCtx, struct mapistore_message); @@ -389,7 +386,7 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" }; { username = [contactInfos objectForKey: @"sAMAccountName"]; recipient->username = [username asUnicodeInMemCtx: msgData]; - entryId = MAPIStoreInternalEntryId (samCtx, username); + entryId = MAPIStoreInternalEntryId ([[self context] connectionInfo], username); } else { @@ -682,6 +679,14 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers, if (!fromResolved) { + TALLOC_CTX *local_mem_ctx; + local_mem_ctx = talloc_new(NULL); + if (!local_mem_ctx) + { + NSLog (@"%s: Out of memory", __PRETTY_FUNCTION__); + return; + } + NSLog (@"Message without an orig from, try to guess it from PidTagSenderEntryId"); senderEntryId = [mailProperties objectForKey: MAPIPropertyKey (PR_SENDER_ENTRYID)]; if (senderEntryId) @@ -695,12 +700,12 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers, bin32.cb = [senderEntryId length]; bin32.lpb = (uint8_t *) [senderEntryId bytes]; - addrBookEntryId = get_AddressBookEntryId (connInfo->sam_ctx, &bin32); + addrBookEntryId = get_AddressBookEntryId (local_mem_ctx, &bin32); if (addrBookEntryId && [[NSString stringWithGUID: &addrBookEntryId->ProviderUID] hasSuffix: @"08002b2fe182"]) { /* TODO: better way to distinguish local and other ones */ - username = MAPIStoreSamDBUserAttribute (connInfo->sam_ctx, @"legacyExchangeDN", + username = MAPIStoreSamDBUserAttribute (connInfo, @"legacyExchangeDN", [NSString stringWithUTF8String: addrBookEntryId->X500DN], @"sAMAccountName"); if (username) { @@ -721,7 +726,7 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers, /* Try with One-Off EntryId */ struct OneOffEntryId *oneOffEntryId; - oneOffEntryId = get_OneOffEntryId (connInfo->sam_ctx, &bin32); + oneOffEntryId = get_OneOffEntryId (local_mem_ctx, &bin32); if (oneOffEntryId && [[NSString stringWithGUID: &oneOffEntryId->ProviderUID] hasSuffix: @"00dd010f5402"]) { @@ -730,9 +735,7 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers, [fromRecipient setObject: [NSString stringWithUTF8String: oneOffEntryId->EmailAddress.lpszW] forKey: @"email"]; } - talloc_free (oneOffEntryId); } - talloc_free (addrBookEntryId); if ([[fromRecipient allKeys] count] > 0) { @@ -742,6 +745,8 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers, } } + /* Free entryId */ + talloc_free(local_mem_ctx); } if (!recipients) @@ -1008,7 +1013,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS - (NGMimeMessage *) _generateMessageWithBcc: (BOOL) withBcc { NSString *contentType; - NGMimeMessage *message; + NGMimeMessage *message; NGMutableHashMap *headers; id messageBody; @@ -1048,6 +1053,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS - (int) submitWithFlags: (enum SubmitFlags) flags { + enum mapistore_error rc = MAPISTORE_SUCCESS; NSDictionary *recipients; NSData *messageData; NSMutableArray *recipientEmails; @@ -1094,7 +1100,10 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS withAuthenticator: authenticator inContext: woContext]; if (error) - [self logWithFormat: @"an error occurred: '%@'", error]; + { + [self errorWithFormat: @"an error occurred: '%@'", error]; + rc = MAPISTORE_ERR_MSG_SEND; + } // mapping = [self mapping]; // [mapping unregisterURLWithID: [self objectId]]; @@ -1106,7 +1115,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS [self logWithFormat: @"skipping submit of message with class '%@'", msgClass]; - return MAPISTORE_SUCCESS; + return rc; } - (void) save: (TALLOC_CTX *) memCtx diff --git a/OpenChange/MAPIStorePermissionsTable.m b/OpenChange/MAPIStorePermissionsTable.m index 3dba53d79..76896836f 100644 --- a/OpenChange/MAPIStorePermissionsTable.m +++ b/OpenChange/MAPIStorePermissionsTable.m @@ -103,7 +103,7 @@ else { connInfo = [(MAPIStoreContext *) [container context] connectionInfo]; - entryId = MAPIStoreInternalEntryId (connInfo->sam_ctx, userId); + entryId = MAPIStoreInternalEntryId (connInfo, userId); } *data = [entryId asBinaryInMemCtx: memCtx]; diff --git a/OpenChange/MAPIStoreRecurrenceUtils.h b/OpenChange/MAPIStoreRecurrenceUtils.h index df3700575..dbb2f0c19 100644 --- a/OpenChange/MAPIStoreRecurrenceUtils.h +++ b/OpenChange/MAPIStoreRecurrenceUtils.h @@ -25,12 +25,9 @@ #include -#import - #import #import - -@class NSTimeZone; +#import @class iCalEvent; @class iCalRepeatableEntityObject; @@ -46,7 +43,7 @@ fromRecurrencePattern: (struct RecurrencePattern *) rp withExceptions: (struct ExceptionInfo *) exInfos andExceptionCount: (uint16_t) exInfoCount - inTimeZone: (NSTimeZone *) tz; + inTimeZone: (iCalTimeZone *) tz; @end @@ -54,7 +51,6 @@ - (void) fillRecurrencePattern: (struct RecurrencePattern *) rp withEvent: (iCalEvent *) event - inTimeZone: (NSTimeZone *) timeZone inMemCtx: (TALLOC_CTX *) memCtx; @end diff --git a/OpenChange/MAPIStoreRecurrenceUtils.m b/OpenChange/MAPIStoreRecurrenceUtils.m index 9e0bf29c3..90052474a 100644 --- a/OpenChange/MAPIStoreRecurrenceUtils.m +++ b/OpenChange/MAPIStoreRecurrenceUtils.m @@ -24,7 +24,6 @@ #import #import #import -#import #import #import @@ -35,6 +34,7 @@ #import #import #import +#import #import "NSDate+MAPIStore.h" #import "MAPIStoreRecurrenceUtils.h" @@ -51,7 +51,7 @@ fromRecurrencePattern: (struct RecurrencePattern *) rp withExceptions: (struct ExceptionInfo *) exInfos andExceptionCount: (uint16_t) exInfoCount - inTimeZone: (NSTimeZone *) tz + inTimeZone: (iCalTimeZone *) tz { NSCalendarDate *startDate, *olEndDate, *untilDate, *exDate; @@ -63,7 +63,7 @@ iCalWeekOccurrence weekOccurrence; iCalWeekOccurrences dayMaskDays; NSUInteger count, max; - NSInteger bySetPos; + NSInteger bySetPos, tzOffset; unsigned char maskValue; [entity removeAllRecurrenceRules]; @@ -242,9 +242,10 @@ { /* The OriginalStartDate is in local time */ exDate = [NSDate dateFromMinutesSince1601: exInfos[count].OriginalStartDate]; + tzOffset = -[[tz periodForDate: exDate] secondsOffsetFromGMT]; exDate = [exDate dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 - seconds: - [tz secondsFromGMT]]; + seconds: tzOffset]; [exceptionDates removeObject: exDate]; } } @@ -263,7 +264,6 @@ - (void) fillRecurrencePattern: (struct RecurrencePattern *) rp withEvent: (iCalEvent *) event - inTimeZone: (NSTimeZone *) timeZone inMemCtx: (TALLOC_CTX *) memCtx { iCalRecurrenceFrequency freq; @@ -279,10 +279,8 @@ NSMutableArray *deletedDates, *modifiedDates; startDate = [event firstRecurrenceStartDate]; - [startDate setTimeZone: timeZone]; endDate = [event lastPossibleRecurrenceStartDate]; - [endDate setTimeZone: timeZone]; - + rp->ReaderVersion = 0x3004; rp->WriterVersion = 0x3004; diff --git a/OpenChange/MAPIStoreSOGoObject.m b/OpenChange/MAPIStoreSOGoObject.m index 2d7b6ad99..7f1cc4b6a 100644 --- a/OpenChange/MAPIStoreSOGoObject.m +++ b/OpenChange/MAPIStoreSOGoObject.m @@ -197,6 +197,7 @@ static Class MAPIStoreFolderK; inMemCtx: (TALLOC_CTX *) memCtx { int rc; + struct mapistore_connection_info *connInfo; uint64_t obVersion; obVersion = [self objectVersion]; @@ -204,8 +205,9 @@ static Class MAPIStoreFolderK; rc = MAPISTORE_ERR_NOT_FOUND; else { + connInfo = [[self context] connectionInfo]; *data = MAPILongLongValue (memCtx, ((obVersion << 16) - | 0x0001)); + | connInfo->repl_id)); rc = MAPISTORE_SUCCESS; } diff --git a/OpenChange/MAPIStoreSamDBUtils.h b/OpenChange/MAPIStoreSamDBUtils.h index 25ee5e4eb..84825332b 100644 --- a/OpenChange/MAPIStoreSamDBUtils.h +++ b/OpenChange/MAPIStoreSamDBUtils.h @@ -25,13 +25,13 @@ @class NSString; -struct ldb_context; +#include -NSString *MAPIStoreSamDBUserAttribute (struct ldb_context *samCtx, +NSString *MAPIStoreSamDBUserAttribute (struct mapistore_connection_info *connInfo, NSString *userKey, NSString *value, NSString *attributeName); -NSData *MAPIStoreInternalEntryId (struct ldb_context *, NSString *username); +NSData *MAPIStoreInternalEntryId (struct mapistore_connection_info *connInfo, NSString *username); NSData *MAPIStoreExternalEntryId (NSString *cn, NSString *email); #endif /* MAPISTORESAMDBUTILS_H */ diff --git a/OpenChange/MAPIStoreSamDBUtils.m b/OpenChange/MAPIStoreSamDBUtils.m index c210deb64..f26d173c2 100644 --- a/OpenChange/MAPIStoreSamDBUtils.m +++ b/OpenChange/MAPIStoreSamDBUtils.m @@ -24,13 +24,18 @@ #import #include #include +#include +#include #import "NSData+MAPIStore.h" #import "MAPIStoreSamDBUtils.h" + + + NSString * -MAPIStoreSamDBUserAttribute (struct ldb_context *samCtx, +MAPIStoreSamDBUserAttribute (struct mapistore_connection_info *connInfo, NSString *userKey, NSString *value, NSString *attributeName) @@ -48,10 +53,20 @@ MAPIStoreSamDBUserAttribute (struct ldb_context *samCtx, attrs[0] = [attributeName UTF8String]; searchFormat = [NSString stringWithFormat: @"(&(objectClass=user)(%@=%%s))", userKey]; - ret = ldb_search (samCtx, memCtx, &res, ldb_get_default_basedn(samCtx), +#if SAMBA_VERSION_MAJOR <= 4 && SAMBA_VERSION_MINOR < 3 + ret = ldb_search (connInfo->sam_ctx, memCtx, &res, + ldb_get_default_basedn(connInfo->sam_ctx), LDB_SCOPE_SUBTREE, attrs, [searchFormat UTF8String], [value UTF8String]); +#else + ret = safe_ldb_search (&connInfo->sam_ctx, memCtx, &res, + ldb_get_default_basedn(connInfo->sam_ctx), + LDB_SCOPE_SUBTREE, attrs, + [searchFormat UTF8String], + [value UTF8String]); +#endif + if (ret == LDB_SUCCESS && res->count == 1) { result = ldb_msg_find_attr_as_string (res->msgs[0], attrs[0], NULL); @@ -65,7 +80,7 @@ MAPIStoreSamDBUserAttribute (struct ldb_context *samCtx, } NSData * -MAPIStoreInternalEntryId (struct ldb_context *samCtx, NSString *username) +MAPIStoreInternalEntryId (struct mapistore_connection_info *connInfo, NSString *username) { static const uint8_t const providerUid[] = { 0xdc, 0xa7, 0x40, 0xc8, 0xc0, 0x42, 0x10, 0x1a, @@ -82,7 +97,7 @@ MAPIStoreInternalEntryId (struct ldb_context *samCtx, NSString *username) type: 32 X500DN: variable */ - legacyDN = MAPIStoreSamDBUserAttribute (samCtx, @"sAMAccountName", username, + legacyDN = MAPIStoreSamDBUserAttribute (connInfo, @"sAMAccountName", username, @"legacyExchangeDN"); if (legacyDN) { diff --git a/OpenChange/MAPIStoreTasksFolder.m b/OpenChange/MAPIStoreTasksFolder.m index d777b8e8b..9bc76def1 100644 --- a/OpenChange/MAPIStoreTasksFolder.m +++ b/OpenChange/MAPIStoreTasksFolder.m @@ -107,11 +107,11 @@ if ([roles containsObject: SOGoRole_ObjectCreator]) rights |= RightsCreateItems; if ([roles containsObject: SOGoRole_ObjectEraser]) - rights |= RightsDeleteAll; + rights |= RightsDeleteAll | RightsDeleteOwn; if ([roles containsObject: SOGoCalendarRole_PublicModifier] && [roles containsObject: SOGoCalendarRole_PrivateModifier] && [roles containsObject: SOGoCalendarRole_ConfidentialModifier]) - rights |= RightsReadItems | RightsEditAll; + rights |= RightsReadItems | RightsEditAll | RightsEditOwn; else if ([roles containsObject: SOGoCalendarRole_PublicViewer] && [roles containsObject: SOGoCalendarRole_PrivateViewer] && [roles containsObject: SOGoCalendarRole_ConfidentialViewer]) diff --git a/OpenChange/MAPIStoreTypes.m b/OpenChange/MAPIStoreTypes.m index 76e72c5d6..eb3b499b3 100644 --- a/OpenChange/MAPIStoreTypes.m +++ b/OpenChange/MAPIStoreTypes.m @@ -198,7 +198,7 @@ NSObjectFromSPropValue (const struct SPropValue *value) break; case PT_STRING8: result = (value->value.lpszA - ? [NSString stringWithUTF8String: value->value.lpszA] + ? [NSString stringWithUTF8String: (const char *) value->value.lpszA] : (id) @""); break; case PT_SYSTIME: diff --git a/OpenChange/MAPIStoreUserContext.m b/OpenChange/MAPIStoreUserContext.m index bc91f56b0..de3dfadf1 100644 --- a/OpenChange/MAPIStoreUserContext.m +++ b/OpenChange/MAPIStoreUserContext.m @@ -73,6 +73,8 @@ static NSMapTable *contextsTable = nil; [userContext autorelease]; [contextsTable setObject: userContext forKey: username]; } + else + [userContext activate]; return userContext; } diff --git a/OpenChange/NSArray+MAPIStore.m b/OpenChange/NSArray+MAPIStore.m index fcb7d3896..be54e9259 100644 --- a/OpenChange/NSArray+MAPIStore.m +++ b/OpenChange/NSArray+MAPIStore.m @@ -228,7 +228,7 @@ mvResult = [NSMutableArray arrayWithCapacity: mvString->cValues]; for (count = 0; count < mvString->cValues; count++) { - subObject = [NSString stringWithUTF8String: mvString->lppszA[count]]; + subObject = [NSString stringWithUTF8String: (const char *) mvString->lppszA[count]]; [mvResult addObject: subObject]; } diff --git a/OpenChange/NSDate+MAPIStore.h b/OpenChange/NSDate+MAPIStore.h index 0e417d21f..d61df7b11 100644 --- a/OpenChange/NSDate+MAPIStore.h +++ b/OpenChange/NSDate+MAPIStore.h @@ -37,6 +37,8 @@ - (BOOL) isNever; /* occurs on 4500-12-31 */ ++ (NSCalendarDate *) dateFromSystemTime: (struct SYSTEMTIME) date + andRuleYear: (uint16_t) rYear; @end NSComparisonResult NSDateCompare (id date1, id date2, void *); diff --git a/OpenChange/NSDate+MAPIStore.m b/OpenChange/NSDate+MAPIStore.m index b96812adf..b8f09dcc6 100644 --- a/OpenChange/NSDate+MAPIStore.m +++ b/OpenChange/NSDate+MAPIStore.m @@ -24,6 +24,7 @@ #import #import +#import "MAPIStoreTypes.h" #import "NSDate+MAPIStore.h" #undef DEBUG @@ -48,7 +49,7 @@ _setupRefDate () refDate = [[NSCalendarDate alloc] initWithYear: 1601 month: 1 day: 1 hour: 0 minute: 0 second: 0 - timeZone: [NSTimeZone timeZoneWithName: @"UTC"]]; + timeZone: utcTZ]; } + (NSCalendarDate *) dateFromMinutesSince1601: (uint32_t) minutes @@ -128,6 +129,64 @@ _setupRefDate () return [calDate yearOfCommonEra] == 4500; } ++ (NSCalendarDate *) dateFromSystemTime: (struct SYSTEMTIME) date + andRuleYear: (uint16_t) rYear +{ + NSCalendarDate *result; + NSInteger daysToDate; + NSUInteger firstDayOfWeek, year; + + /* ([MS-OXOCAL] 2.2.1.41.1) When we're provided an absolute date (i.e., it + happens once), the SYSTEMTIME structure is enough to fill the date. + When we're parsing a SYSTEMTIME field from a time zone rule, however, a + relative date can be provided for the peroidicity of its periods. In this + scenario, the wYear field is empty and we have to use the wYear field in + the parent rule */ + if (date.wYear != 0) + year = date.wYear; + else + year = rYear; + + /* The wDay field indicates the occurrence of the wDayOfWeek within the month. + The 5th occurrence means the last one, even if it is the 4th. */ + if (date.wDay < 5) + { + result = [[NSCalendarDate alloc] initWithYear: year month: date.wMonth day: 1 + hour: date.wHour minute: date.wMinute second: date.wSecond + timeZone: utcTZ]; + [result autorelease]; + + firstDayOfWeek = [result dayOfWeek]; + + daysToDate = 7 * (date.wDay - 1) + date.wDayOfWeek - firstDayOfWeek; + if (date.wDayOfWeek < firstDayOfWeek) + daysToDate += 7; + + result = [result dateByAddingYears: 0 months: 0 days: daysToDate + hours: 0 minutes: 0 + seconds: 0]; + } + else + { + result = [[NSCalendarDate alloc] initWithYear: year month: date.wMonth + 1 day: 1 + hour: date.wHour minute: date.wMinute second: date.wSecond + timeZone: utcTZ]; + [result autorelease]; + + firstDayOfWeek = [result dayOfWeek]; + + daysToDate = date.wDayOfWeek - firstDayOfWeek; + if (date.wDayOfWeek >= firstDayOfWeek) + daysToDate -= 7; + + result = [result dateByAddingYears: 0 months: 0 days: daysToDate + hours: 0 minutes: 0 + seconds: 0]; + } + + return result; +} + @end NSComparisonResult diff --git a/OpenChange/RTFHandler.h b/OpenChange/RTFHandler.h index 52567cc29..b0ea0b684 100644 --- a/OpenChange/RTFHandler.h +++ b/OpenChange/RTFHandler.h @@ -67,6 +67,7 @@ int font_index; int color_index; int start_pos; + const unsigned short *charset; } @end @@ -77,7 +78,7 @@ { @public NSString *family; - NSString *charset; + unsigned char charset; NSString *name; unsigned int pitch; unsigned int index; diff --git a/OpenChange/RTFHandler.m b/OpenChange/RTFHandler.m index 0df3f1fd3..b18754e51 100644 --- a/OpenChange/RTFHandler.m +++ b/OpenChange/RTFHandler.m @@ -25,11 +25,13 @@ // // Useful macros // -#define ADVANCE _bytes++; _current_pos++; -#define ADVANCE_N(N) _bytes += (N); _current_pos += (N); -#define REWIND _bytes--; _current_pos--; - +#define ADVANCE self->_bytes++; self->_current_pos++; +#define ADVANCE_N(N) self->_bytes += (N); self->_current_pos += (N); +#define REWIND self->_bytes--; self->_current_pos--; +#define DEFAULT_CHARSET 1 +#define FONTNAME_LEN_MAX 100 +#define UTF8_FIRST_BYTE_LAST_CODEPOINT 0x7F // // Charset definitions. See http://msdn.microsoft.com/en-us/goglobal/bb964654 for all details. // @@ -229,7 +231,7 @@ const unsigned short ansicpg874[256] = { - (void) dealloc { - RELEASE(a); + [a release]; [super dealloc]; } @@ -244,7 +246,7 @@ const unsigned short ansicpg874[256] = { if ([a count]) { - o = AUTORELEASE([[a lastObject] retain]); + o = [[[a lastObject] retain] autorelease]; [a removeLastObject]; } @@ -257,7 +259,7 @@ const unsigned short ansicpg874[256] = { if ([a count]) { - o = AUTORELEASE([[a lastObject] retain]); + o = [[[a lastObject] retain] autorelease]; } return o; @@ -282,17 +284,28 @@ const unsigned short ansicpg874[256] = { { } + + charset = DEFAULT_CHARSET; return self; } - (void) dealloc { - RELEASE(family); - RELEASE(charset); - RELEASE(name); + [family release]; + [name release]; [super dealloc]; } +- (NSString *) description +{ + NSString *description; + description = [NSString stringWithFormat: + @"%u name=%@ family=%@ charset=%u pitch=%u", + index, name, family, charset, pitch + ]; + return description; +} + @end // @@ -332,6 +345,23 @@ const unsigned short ansicpg874[256] = { return NSMapGet(fontInfos, key); } +- (NSString *) description +{ + NSMutableString *description; + NSEnumerator *enumerator; + RTFFontInfo *fontInfo; + + description = [NSMutableString stringWithFormat: @"Number of fonts: %u\n", [fontInfos count]]; + enumerator = [fontInfos objectEnumerator]; + while ((fontInfo = [enumerator nextObject])) + { + [description appendString: [fontInfo description]]; + [description appendString: @"\n"]; + } + + return description; +} + @end // @@ -357,7 +387,7 @@ const unsigned short ansicpg874[256] = { - (void) dealloc { - RELEASE(colorDefs); + [colorDefs release]; [super dealloc]; } @@ -378,6 +408,135 @@ const unsigned short ansicpg874[256] = { // @implementation RTFHandler +static NSMapTable *_charsets = nil; +static NSMapTable *_cws = nil; +typedef enum { + CW_UNKNOWN = 0, + CW_ANSICPG, + CW_B, + CW_CF, + CW_COLORTBL, + CW_F, + CW_FONTTBL, + CW_I, + CW_PAR, + CW_PICT, + CW_SOFTLINE, + CW_STRIKE, + CW_STYLESHEET, + CW_TAB, + CW_U, + CW_UL, + CW_ULNONE +} commandWordId; + +static NSMapTable *_fontCws = nil; +typedef enum { + FONTCW_UNKNOWN = 0, + FONTCW_F, + FONTCW_FBIDI, + FONTCW_FCHARSET, + FONTCW_FDECOR, + FONTCW_FMODERN, + FONTCW_FNIL, + FONTCW_FPRQ, + FONTCW_FROMAN, + FONTCW_FSCRIPT, + FONTCW_FSWISS, + FONTCW_FTECH +} fontCommandWordId; + +static void _init_charsets_table() +{ + _charsets = NSCreateMapTable(NSObjectMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 23); + // 238 — Eastern European - cpg1250 + NSMapInsert(_charsets, @"ansicpg1250", ansicpg1250); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 238], ansicpg1250); + // 204 — Russian - cpg1251 + NSMapInsert(_charsets, @"ansicpg1251", ansicpg1251); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 204], ansicpg1251); + // 0 - Latin 1 - cpg1252 - also know as ANSI + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 0], ansicpg1252); + NSMapInsert(_charsets, @"ansicpg1252", ansicpg1252); + // 161 - Greek cpg1253 + NSMapInsert(_charsets, @"ansicpg1253", ansicpg1253); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 161], ansicpg1253); + // 162 — Turkish - cpg1254 + NSMapInsert(_charsets, @"ansicpg1254", ansicpg1254); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 162], ansicpg1254); + // 177 — Hebrew Traditional - cpg1255 + // also 181 - Hebrew user + NSMapInsert(_charsets, @"ansicpg1255", ansicpg1255); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 177], ansicpg1255); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 181], ansicpg1255); + // 178 — Arabic - cpg1256 + // also 179 - Arabic traditional + // also 180 - Arabic User + NSMapInsert(_charsets, @"ansicpg1256", ansicpg1256); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 178], ansicpg1256); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 179], ansicpg1256); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 180], ansicpg1256); + // 186 — Baltic - pg 1257 + NSMapInsert(_charsets, @"ansicpg1257", ansicpg1257); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 186], ansicpg1257); + // 163 — Vietnamese - pg1259 + NSMapInsert(_charsets, @"ansicpg1258", ansicpg1258); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 163], ansicpg1258); + // 222 — Thai - cpg874 + NSMapInsert(_charsets, @"ansicpg874", ansicpg874); + NSMapInsert(_charsets, [NSNumber numberWithUnsignedChar: 222], ansicpg874); + + // TODO: check differences between traditional/user/no-qualified for Arabic and Hebrew + // TODO: missing codepage for the following codes: + // 2 — Symbol + // 3 — Invalid + // 77 — Mac + // 128 — Shift Jis + // 129 — Hangul + // 130 — Johab + // 134 — GB2312 + // 136 — Big5 + // 254 — PC 437 + // 255 — OEM +} + +static void _init_cws_table() +{ + _cws = NSCreateMapTable(NSObjectMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 16); + NSMapInsert(_cws, @"ansicpg", (void *) CW_ANSICPG); + NSMapInsert(_cws, @"b", (void *) CW_B); + NSMapInsert(_cws, @"cf", (void *) CW_CF); + NSMapInsert(_cws, @"colortbl", (void *) CW_COLORTBL); + NSMapInsert(_cws, @"f", (void *) CW_F); + NSMapInsert(_cws, @"fonttbl", (void *) CW_FONTTBL); + NSMapInsert(_cws, @"i", (void *) CW_I); + NSMapInsert(_cws, @"par", (void *) CW_PAR); + NSMapInsert(_cws, @"pict", (void *) CW_PICT); + NSMapInsert(_cws, @"softline", (void *) CW_SOFTLINE); + NSMapInsert(_cws, @"strike", (void *) CW_STRIKE); + NSMapInsert(_cws, @"stylesheet", (void *) CW_STYLESHEET); + NSMapInsert(_cws, @"tab", (void *) CW_TAB); + NSMapInsert(_cws, @"u", (void *) CW_U); + NSMapInsert(_cws, @"ul", (void *) CW_UL); + NSMapInsert(_cws, @"ulnone", (void *) CW_ULNONE); +} + +static void _init_fontCws_table() +{ + _fontCws = NSCreateMapTable(NSObjectMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 23); + NSMapInsert(_fontCws, @"f", (void *) FONTCW_F); + NSMapInsert(_fontCws, @"fbidi", (void *) FONTCW_FBIDI); + NSMapInsert(_fontCws, @"fcharset", (void *) FONTCW_FCHARSET); + NSMapInsert(_fontCws, @"fdecor", (void *) FONTCW_FDECOR); + NSMapInsert(_fontCws, @"fmodern", (void *) FONTCW_FMODERN); + NSMapInsert(_fontCws, @"fnil", (void *) FONTCW_FNIL); + NSMapInsert(_fontCws, @"fprq", (void *) FONTCW_FPRQ); + NSMapInsert(_fontCws, @"froman", (void *) FONTCW_FROMAN); + NSMapInsert(_fontCws, @"fscript", (void *) FONTCW_FSCRIPT); + NSMapInsert(_fontCws, @"fswiss", (void *) FONTCW_FSWISS); + NSMapInsert(_fontCws, @"ftech", (void *) FONTCW_FTECH); +} + - (id) initWithData: (NSData *) theData { if ((self = [super init])) @@ -386,18 +545,12 @@ const unsigned short ansicpg874[256] = { _bytes = (char *)[_data bytes]; _len = [_data length]; _current_pos = 0; - - _charsets = NSCreateMapTable(NSObjectMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 10); - NSMapInsert(_charsets, @"ansicpg1250", ansicpg1250); - NSMapInsert(_charsets, @"ansicpg1251", ansicpg1251); - NSMapInsert(_charsets, @"ansicpg1252", ansicpg1252); - NSMapInsert(_charsets, @"ansicpg1253", ansicpg1253); - NSMapInsert(_charsets, @"ansicpg1254", ansicpg1254); - NSMapInsert(_charsets, @"ansicpg1255", ansicpg1255); - NSMapInsert(_charsets, @"ansicpg1256", ansicpg1256); - NSMapInsert(_charsets, @"ansicpg1257", ansicpg1257); - NSMapInsert(_charsets, @"ansicpg1258", ansicpg1258); - NSMapInsert(_charsets, @"ansicpg874", ansicpg874); + if (_charsets == nil) + _init_charsets_table(); + if (_cws == nil) + _init_cws_table(); + if (_fontCws == nil) + _init_fontCws_table(); } return self; @@ -406,7 +559,7 @@ const unsigned short ansicpg874[256] = { - (void) dealloc { NSFreeMapTable(_charsets); - RELEASE(_data); + [_data release]; [super dealloc]; } @@ -455,6 +608,85 @@ const unsigned short ansicpg874[256] = { return start+1; } + +- (const char *) parseControlWordAndSetLenIn: (unsigned int *) len + setHasIntArgumentIn: (BOOL *) hasArg + setIntArgumentIn: (int *) arg +{ + const char *start; + const char *end = NULL; + const char *startArg = NULL; + const char *endArg = NULL; + + ADVANCE; + start = _bytes; + + /* + A control word is defined by: + + \ + */ + while (isalpha(*_bytes)) + { + end = _bytes; + ADVANCE; + } + + if (end == NULL) + { + return NULL; + } + + /* + The can be one of the following: + + - A space. This serves only to delimit a control word and is + ignored in subsequent processing. + + - A numeric digit or an ASCII minus sign (-), which indicates + that a numeric parameter is associated with the control word. + Only this case requires to include it in the control word. + + - Any character other than a letter or a digit + */ + + if (*_bytes == '-' || isdigit(*_bytes)) + { + startArg = _bytes; + endArg = _bytes; + ADVANCE; + while (isdigit(*_bytes)) + { + endArg = _bytes; + ADVANCE; + } + } + + *hasArg = NO; + *arg = 0; + if (startArg) + { + NSString *s; + unsigned int argLength = endArg - startArg + 1; + // the next guard is to protect against a single '-' + if (argLength > 1 || (*startArg != '-')) + { + s = [[NSString alloc] initWithBytesNoCopy: (void *) startArg + length: argLength + encoding: NSASCIIStringEncoding + freeWhenDone: NO]; + [s autorelease]; + *hasArg = YES; + *arg = [s intValue]; // Warning: it does not detect conversion errors + } + } + + + /* In other cases, the delimiting character terminates the control + word and is not part of the control word. */ + *len = end - start + 1; + return start; +} // // {\colortbl\red0\green0\blue0;\red128\green0\blue0;\red255\green0\blue0;} @@ -531,107 +763,140 @@ const unsigned short ansicpg874[256] = { // - (RTFFontTable *) parseFontTable { - NSMutableString *fontName; RTFFontTable *fontTable; RTFFontInfo *fontInfo; - unsigned int count; + unsigned int level; fontTable = [[[RTFFontTable alloc] init] autorelease]; - fontName = nil; fontInfo = nil; - count = 0; + level = 0; do { if (*_bytes == '{') { - if (fontTable) + if (fontTable && level == 1) { fontInfo = [[[RTFFontInfo alloc] init] autorelease]; - fontName = [[[NSMutableString alloc] init] autorelease]; } ADVANCE; - count++; + level++; } else if (*_bytes == '}') { - if (fontTable) //&& ![NSAllMapTableValues(fontTable->fontInfos) containsObject: fontInfo]) + if (fontTable && level == 2) //&& ![NSAllMapTableValues(fontTable->fontInfos) containsObject: fontInfo]) { - ASSIGN(fontInfo->name, fontName); [fontTable addFontInfo: fontInfo atIndex: fontInfo->index]; } ADVANCE; - count--; + level--; } else if (*_bytes == '\\') { const char *cw; unsigned int len; - NSString *s; + BOOL hasArg; + int arg; + NSString *cwKey; + fontCommandWordId cwId; - cw = [self parseControlWord: &len]; - - // Skip our control word - if (strncmp((const char*)cw, "fonttbl", len) == 0) + cw = [self parseControlWordAndSetLenIn: &len + setHasIntArgumentIn: &hasArg + setIntArgumentIn: &arg]; + if (level != 2) + continue; + else if (cw == NULL) continue; - // We must at least parse - s = [[NSString alloc] initWithBytesNoCopy: (void *)cw+1 - length: len-1 - encoding: NSASCIIStringEncoding - freeWhenDone: NO]; - [s autorelease]; + cwKey= [[NSString alloc] initWithBytesNoCopy: (void *)cw + length: len + encoding: NSASCIIStringEncoding + freeWhenDone: NO]; + [cwKey autorelease]; - // If we got a fontnum, let's parse all three fields at once) - if (isdigit(*(cw+1))) + cwId = (fontCommandWordId) NSMapGet(_fontCws, cwKey); + switch (cwId) { - fontInfo->index = [s intValue]; - - // We now parse - cw = [self parseControlWord: &len]; - if (len == 0) // Possibly parsing a space - cw = [self parseControlWord: &len]; - - fontInfo->family = [[NSString alloc] initWithBytesNoCopy: (void *)cw+1 - length: len-1 - encoding: NSASCIIStringEncoding - freeWhenDone: NO]; - - cw = [self parseControlWord: &len]; - if (len == 0) // Possibly parsing a space - cw = [self parseControlWord: &len]; - - fontInfo->charset = [[NSString alloc] initWithBytesNoCopy: (void *)cw+1 - length: len-1 - encoding: NSASCIIStringEncoding - freeWhenDone: NO]; - - // We now skip everything until we find our final group closer ('}') - int cc = 1; - - do - { - if (*_bytes == '{') - cc++; - if (*_bytes == '}') - cc--; - - ADVANCE; - } - while (cc != 0); - - // move back our buffer; - REWIND; + case FONTCW_F: + if (hasArg) + fontInfo->index = arg; + break; + case FONTCW_FBIDI: + fontInfo->family = @"bidi"; + break; + case FONTCW_FCHARSET: + if (hasArg) + fontInfo->charset = arg; + break; + case FONTCW_FDECOR: + fontInfo->family = @"decor"; + break; + case FONTCW_FMODERN: + fontInfo->family = @"modern"; + break; + case FONTCW_FNIL: + fontInfo->family = @"nil"; + break; + case FONTCW_FPRQ: + if (hasArg) + fontInfo->pitch = arg; + break; + case FONTCW_FROMAN: + fontInfo->family = @"roman"; + break; + case FONTCW_FSCRIPT: + fontInfo->family = @"script"; + break; + case FONTCW_FSWISS: + fontInfo->family = @"swiss"; + break; + case FONTCW_FTECH: + fontInfo->family = @"tech"; + break; + case FONTCW_UNKNOWN: + default: + // do nothing + break; } } - else + else // no char { - if (isalnum(*_bytes)) - [fontName appendFormat: @"%c", *_bytes]; + if (level == 2 && isalnum(*_bytes)) + { + // we assume this is the fontname + unsigned int fontnameLen; + const char *delim = strpbrk(_bytes, ";{}\\"); + if (delim == NULL) + { + // no delimiter found, we skip to next characters + ADVANCE; + continue; + } + fontnameLen = delim - _bytes; + // only valid if the delimiter is a correct ';' + if (*delim == ';') + { + // there is no explicit limit length but we took 100 + // as protection + if (delim && fontnameLen <= FONTNAME_LEN_MAX) + { + fontInfo->name = [[NSString alloc] initWithBytesNoCopy: (char *) _bytes + length: fontnameLen + encoding: NSASCIIStringEncoding + freeWhenDone: NO]; + ADVANCE_N(fontnameLen); + } + } + else { + // advance just before the special character + ADVANCE_N(fontnameLen - 1); + } + } ADVANCE; - } - } while (count != 0); + } + + } while (level > 0); return fontTable; } @@ -700,9 +965,238 @@ const unsigned short ansicpg874[256] = { [self parseIgnoringEverything]; } -// -// -// + +// todo: This keyword is only valid in the RTF header section right after the \ansi, \mac, \pc or \pca keyword. +inline static void parseAnsicpg (BOOL hasArg, int arg, const unsigned short **out_default_char) +{ + NSString *key; + const unsigned short *res; + + if (!hasArg) + return; + key = [NSString stringWithFormat: @"anscicpg%i", arg]; + res = NSMapGet(_charsets, key); + if (res) + *out_default_char = res; +} + +inline static void parseB(RTFHandler *self, BOOL hasArg, int arg, RTFFormattingOptions *formattingOptions) +{ + if (!formattingOptions) + return; + if (hasArg && arg == 0) + { + [self->_html appendBytes: "" length: 4]; + formattingOptions->bold = NO; + } + else + { + [self->_html appendBytes: "" length: 3]; + formattingOptions->bold = YES; + } +} + +inline static void parseCf(RTFHandler *self, BOOL hasArg, int arg, RTFFormattingOptions *formattingOptions, RTFColorTable *colorTable) +{ + RTFColorDef *colorDef; + char *v; + + if (!hasArg) + return; + if (!formattingOptions) + return; + + colorDef = [colorTable colorDefAtIndex: arg]; + if (!colorDef) + return; + + if (formattingOptions->color_index >= 0) + { + [self->_html appendBytes: "" length: 7]; + } + + formattingOptions->color_index = arg; + + v = calloc(23, sizeof(char)); + sprintf(v, "", colorDef->red, colorDef->green, colorDef->blue); + [self->_html appendBytes: v length: strlen(v)]; + free(v); +} + + +inline static void parseColorTableWrapper(RTFHandler *self, RTFColorTable **colorTable) +{ + *colorTable = [self parseColorTable]; +} + +inline static void parseF(RTFHandler *self, BOOL hasArg, int arg, RTFFormattingOptions *formattingOptions, RTFFontTable *fontTable) +{ + RTFFontInfo *fontInfo; + + if (!hasArg) + return; + if (!formattingOptions) + return; + + if (formattingOptions->font_index >= 0 && arg != formattingOptions->font_index) + { + [self->_html appendBytes: "" length: 7]; + } + + formattingOptions->font_index = arg; + + fontInfo = [fontTable fontInfoAtIndex: arg]; + char *v = NULL; + if (fontInfo && fontInfo->name) + { + if ([fontInfo->name length] < 128) + { + int tag_size = 15 + [fontInfo->name length]; + v = calloc(tag_size, sizeof(char)); + snprintf(v, tag_size, "", [fontInfo->name UTF8String]); + } + else + { + NSLog(@"RTFHandler: Font %u has %d chars length, parse error? " + "Ignored", arg, [fontInfo->name length]); + v = calloc(7, sizeof(char)); + sprintf(v, ""); + } + } + else + { + // RTF badformed? We don't know about that font (arg index not found). + // Anyhow, we still open the html tag because in the future + // we will close it (e.g. when new font is used). + v = calloc(7, sizeof(char)); + sprintf(v, ""); + } + + if (fontInfo && fontInfo->charset) + { + if (fontInfo->charset == DEFAULT_CHARSET) + /* charset 1 is default charset */ + formattingOptions->charset = NULL; + else { + NSNumber *key = [NSNumber numberWithUnsignedChar: fontInfo->charset]; + formattingOptions->charset = NSMapGet(_charsets, key); + } + } + + [self->_html appendBytes: v length: strlen(v)]; + free(v); +} + +inline static void parseFontTableWrapper(RTFHandler *self, const char * cw, RTFFontTable **fontTable) +{ + // We rewind our buffer so we start at the beginning of {\fonttbl... + self->_bytes = cw-2; + self->_current_pos -= 9; // Length: {\fonttbl + *fontTable = [self parseFontTable]; + + // We go back 1 byte in order to end our section properly ('}' character) + REWIND; +} + +inline static void parseI(RTFHandler *self, BOOL hasArg, int arg, RTFFormattingOptions *formattingOptions) +{ + if (!formattingOptions) + return; + if (hasArg && arg == 0) + { + [self->_html appendBytes: "" length: 4]; + formattingOptions->italic = NO; + } + else + { + [self->_html appendBytes: "" length: 3]; + formattingOptions->italic = YES; + } +} + +inline static void parsePar(RTFHandler *self) +{ + [self->_html appendBytes: "
" length: 4]; +} + +inline static void parsePictureWrapper(RTFHandler *self, const char * cw) +{ + self->_bytes = cw-2; + self->_current_pos -= 6; // Length: {\pict + [self parsePicture]; + REWIND; +} + +// same implementation that /par +inline static void parseSoftline(RTFHandler *self) +{ + [self->_html appendBytes: "
" length: 4]; +} + +inline static void parseStrike(RTFHandler *self, BOOL hasArg, int arg, RTFFormattingOptions *formattingOptions) +{ + if (!formattingOptions) + return; + if (hasArg && arg == 0) + { + [self->_html appendBytes: "" length: 9]; + formattingOptions->strikethrough = NO; + } + else + { + [self->_html appendBytes: "" length: 8]; + formattingOptions->strikethrough = YES; + } +} + +inline static void parseStyleSheetWrapper(RTFHandler *self, const char * cw) +{ + self->_bytes = cw-2; + self->_current_pos -= 12; // Length: {\stylesheet + [self parseStyleSheet]; + REWIND; +} + +inline static void parseTab(RTFHandler *self) +{ + [self->_html appendBytes: "  " length: 12]; +} + +inline static void parseU(RTFHandler *self, BOOL hasArg, int arg) +{ + unichar uch; + NSString *s; + NSData *d; + + if (!hasArg) + return; + if (arg < 0) + // a negative value means a value greater than 32767 + arg = 32767 - arg; + + uch = (unichar) arg; + s = [NSString stringWithCharacters: &uch length: 1]; + d = [s dataUsingEncoding: NSUTF8StringEncoding]; + [self->_html appendData: d]; +} + +inline static void parseUl(RTFHandler *self, BOOL hasArg, int arg, RTFFormattingOptions *formattingOptions) +{ + if (!formattingOptions) + return; + if (hasArg && arg ==0) + { + [self->_html appendBytes: "" length: 4]; + formattingOptions->underline = NO; + } + else + { + [self->_html appendBytes: "" length: 3]; + formattingOptions->underline = YES; + } +} + + - (NSMutableData *) parse { RTFFormattingOptions *formattingOptions; @@ -710,23 +1204,28 @@ const unsigned short ansicpg874[256] = { RTFFontTable *fontTable; RTFStack *stack; - unsigned short *charset; - char c; - + const unsigned short *defaultCharset; + + // convenience variables for parsing + unsigned char c; + NSData *d; + NSString *s; + unichar uch; + stack = [[RTFStack alloc] init]; fontTable = nil; colorTable = nil; - charset = NULL; + defaultCharset = ansicpg1252; formattingOptions = nil; _html = [[NSMutableData alloc] init]; [_html appendBytes: "" length: 34]; - // Check if we got RTF data + // this does not allow \s\n before '}' neither newline before control command if (_len > 4 && strncmp((const char*)_bytes, "{\\rtf", 4) != 0) - return NO; + return nil; while (_current_pos < _len) { @@ -737,16 +1236,25 @@ const unsigned short ansicpg874[256] = { { unsigned int len; const char *cw; - NSString *s; + BOOL hasArg; + int arg; + NSString *cwKey; + commandWordId cwId; + char nextByte = *(_bytes+1); - if (*(_bytes+1) == '\'' && charset) + if (nextByte == '\'') { // A hexadecimal value, based on the specified character set (may be used to identify 8-bit values). - NSString *s; - NSData *d; - const char *b1, *b2; unsigned short index; + + const unsigned short * active_charset; + + if (formattingOptions && formattingOptions->charset) + active_charset = formattingOptions->charset; + else + active_charset = defaultCharset; + ADVANCE; ADVANCE; @@ -757,211 +1265,108 @@ const unsigned short ansicpg874[256] = { index = (isdigit(*b1) ? *b1 - 48 : toupper(*b1) - 55) * 16; index += (isdigit(*b2) ? *b2 - 48 : toupper(*b2) - 55); - s = [NSString stringWithCharacters: &(charset[index]) length: 1]; + s = [NSString stringWithCharacters: &(active_charset[index]) length: 1]; d = [s dataUsingEncoding: NSUTF8StringEncoding]; [_html appendData: d]; continue; } - else if (*(_bytes+1) == '*') + else if (nextByte == '*') { [self parseIgnoringEverything]; continue; } - - cw = [self parseControlWord: &len]; - - s = [[NSString alloc] initWithBytesNoCopy: (void *)cw - length: len - encoding: NSASCIIStringEncoding - freeWhenDone: NO]; - [s autorelease]; - - if (strncmp(cw, "ansicpg", 7) == 0) + else if (!isalpha(nextByte)) { - charset = NSMapGet(_charsets, s); - } - else if (strncmp(cw, "fonttbl", 7) == 0) - { - // We rewind our buffer so we start at the beginning of {\fonttbl... - _bytes = cw-2; - _current_pos -= 9; // Length: {\fonttbl - fontTable = [self parseFontTable]; - - // We go back 1 byte in order to end our section properly ('}' character) - REWIND; - } - else if (strncmp(cw, "stylesheet", 10) == 0) - { - _bytes = cw-2; - _current_pos -= 12; // Length: {\stylesheet - [self parseStyleSheet]; - REWIND; - } - else if (strncmp(cw, "colortbl", 8) == 0) - { - colorTable = [self parseColorTable]; - } - else if (strncmp(cw, "pict", 4) == 0) - { - _bytes = cw-2; - _current_pos -= 6; // Length: {\pict - [self parsePicture]; - REWIND; - } - else if ([s isEqualToString: @"b"] && formattingOptions) - { - [_html appendBytes: "" length: 3]; - formattingOptions->bold = YES; - } - else if ([s isEqualToString: @"b0"] && formattingOptions) - { - [_html appendBytes: "" length: 4]; - formattingOptions->bold = NO; - } - else if ([s hasPrefix: @"cf"] && [s length] > 2) - { - RTFColorDef *colorDef; - int color_index; - char *v; - - if (!formattingOptions) continue; - - color_index = [[s substringFromIndex: 2] intValue]; - colorDef = [colorTable colorDefAtIndex: color_index]; - if (!colorDef) continue; - - if (formattingOptions->color_index >= 0) - { - [_html appendBytes: "
" length: 7]; - } - - formattingOptions->color_index = color_index; - - v = malloc(23*sizeof(char)); - memset(v, 0, 23); - sprintf(v, "", colorDef->red, colorDef->green, colorDef->blue); - [_html appendBytes: v length: strlen(v)]; - free(v); - } - else if ([s hasPrefix: @"fcs"]) - { - // ignore - } - else if ([s hasPrefix: @"fs"]) - { - // ignore - } - else if ([s hasPrefix: @"fbidis"]) - { - // ignore - } - else if ([s hasPrefix: @"fromhtml"]) - { - // ignore - } - else if ([s hasPrefix: @"fromtext"]) - { - // ignore - } - else if ([s hasPrefix: @"f"] && [s length] > 1) - { - RTFFontInfo *fontInfo; - int font_index; - - font_index = [[s substringFromIndex: 1] intValue]; - - if (!formattingOptions) + // escape + character + ADVANCE_N(2); + // check for special escapes for the no-implemented features + // for control of word breaking + if (nextByte == '~') + // no breaking space + nextByte = ' '; + else if (nextByte == '-') + // optional hyphen; we skip it continue; + else if (nextByte == '_') + // no breaking hyphen, treat it as a normal hyphen + nextByte = '-'; - if (formattingOptions->font_index >= 0 && - font_index != formattingOptions->font_index) - { - [_html appendBytes: "" length: 7]; - } + [_html appendBytes: &nextByte length: 1]; + continue; + } - formattingOptions->font_index = font_index; - fontInfo = [fontTable fontInfoAtIndex: font_index]; - char *v = NULL; - if (fontInfo && fontInfo->name) - { - if ([fontInfo->name length] < 128) - { - int tag_size = 15 + [fontInfo->name length]; - v = calloc(tag_size, sizeof(char)); - snprintf(v, tag_size, "", [fontInfo->name UTF8String]); - } - else - { - NSLog(@"RTFHandler: Font %u has %d chars length, parse error? " - "Ignored", font_index, [fontInfo->name length]); - v = calloc(7, sizeof(char)); - sprintf(v, ""); - } - } - else - { - // RTF badformed? We don't know about that font (font_index). - // Anyhow, we still open the html tag because in the future - // we will close it (e.g. when new font is used). - v = calloc(7, sizeof(char)); - sprintf(v, ""); - } - [_html appendBytes: v length: strlen(v)]; - free(v); - } - else if ([s isEqualToString: @"i"] && formattingOptions) - { - [_html appendBytes: "" length: 3]; - formattingOptions->italic = YES; - } - else if ([s isEqualToString: @"i0"] && formattingOptions) - { - [_html appendBytes: "" length: 4]; - formattingOptions->italic = NO; - } - else if ([s isEqualToString: @"tab"]) - { - [_html appendBytes: "  " length: 12]; - } - else if ([s isEqualToString: @"softline"] || [s isEqualToString: @"par"]) - { - [_html appendBytes: "
" length: 4]; - } - else if ([s isEqualToString: @"strike"] && formattingOptions) - { - [_html appendBytes: "" length: 8]; - formattingOptions->strikethrough = YES; - } - else if ([s isEqualToString: @"strike0"] && formattingOptions) - { - [_html appendBytes: "" length: 9]; - formattingOptions->strikethrough = NO; - } - else if ([s hasPrefix: @"u"] && [s length] > 1 && isdigit([s characterAtIndex: 1])) - { - NSData *d; - unichar ch; + cw = [self parseControlWordAndSetLenIn: &len + setHasIntArgumentIn: &hasArg + setIntArgumentIn: &arg]; + if (cw == NULL) + continue; + + cwKey= [[NSString alloc] initWithBytesNoCopy: (void *)cw + length: len + encoding: NSASCIIStringEncoding + freeWhenDone: NO]; + [cwKey autorelease]; - ch = (unichar)[[s substringFromIndex: 1] intValue]; - s = [NSString stringWithCharacters: &ch length: 1]; - d = [s dataUsingEncoding: NSUTF8StringEncoding]; - [_html appendData: d]; - } - else if ([s isEqualToString: @"ul"] && formattingOptions) + cwId = (commandWordId) NSMapGet(_cws, cwKey); + switch (cwId) { - [_html appendBytes: "" length: 3]; - formattingOptions->underline = YES; - } - else if (([s isEqualToString: @"ul0"] || [s isEqualToString: @"ulnone"]) - && formattingOptions) - { - [_html appendBytes: "" length: 4]; - formattingOptions->underline = NO; + case CW_ANSICPG: + parseAnsicpg(hasArg, arg, &defaultCharset); + break; + case CW_B: + parseB(self, hasArg, arg, formattingOptions); + break; + case CW_CF: + parseCf(self, hasArg, arg, formattingOptions, colorTable); + break; + case CW_COLORTBL: + parseColorTableWrapper(self, &colorTable); + break; + case CW_F: + parseF(self, hasArg, arg, formattingOptions, fontTable); + break; + case CW_FONTTBL: + parseFontTableWrapper(self, cw, &fontTable); + break; + case CW_I: + parseI(self, hasArg, arg, formattingOptions); + break; + case CW_PAR: + parsePar(self); + break; + case CW_PICT: + parsePictureWrapper(self, cw); + break; + case CW_SOFTLINE: + parseSoftline(self); + break; + case CW_STRIKE: + parseStrike(self, hasArg, arg, formattingOptions); + break; + case CW_STYLESHEET: + parseStyleSheetWrapper(self, cw); + break; + case CW_TAB: + parseTab(self); + break; + case CW_U: + parseU(self, hasArg, arg); + break; + case CW_UL: + parseUl(self, hasArg, arg, formattingOptions); + break; + case CW_ULNONE: + parseUl(self, YES, 0, formattingOptions); + break; + case CW_UNKNOWN: + default: + // do nothing + break; } // If a space delimits the control word, the space does not appear in the document. - // Any characters following the delimiter, including spaces, will appear in the document. + // Any characters following the delimiter, including spaces, will appear in the document. (except newline!) if (*_bytes == ' ') { ADVANCE; @@ -978,6 +1383,7 @@ const unsigned short ansicpg874[256] = { formattingOptions->font_index = -1; formattingOptions->color_index = -1; formattingOptions->start_pos = [_html length]; + formattingOptions->charset = defaultCharset; [stack push: formattingOptions]; ADVANCE; } @@ -1024,17 +1430,31 @@ const unsigned short ansicpg874[256] = { } else { - // We avoid appending NULL bytes - if (*_bytes) - [_html appendBytes: _bytes length: 1]; + c = *_bytes; + // We avoid appending NULL bytes or endlines + if (c && (c != '\n')) + { + if (c <= UTF8_FIRST_BYTE_LAST_CODEPOINT) + { + // in this case utf8 and ascii encoding are the same + [_html appendBytes: &c length: 1]; + } + else + { + uch = c; + s = [NSString stringWithCharacters: &uch length: 1]; + d = [s dataUsingEncoding: NSUTF8StringEncoding]; + [_html appendData: d]; + } + } ADVANCE; } } [_html appendBytes: "" length: 14]; - RELEASE(stack); - return AUTORELEASE(_html); + [stack release]; + return [_html autorelease]; } @end diff --git a/OpenChange/dbmsgreader.m b/OpenChange/dbmsgreader.m index 3d892ca34..5ad74b82b 100644 --- a/OpenChange/dbmsgreader.m +++ b/OpenChange/dbmsgreader.m @@ -1,8 +1,8 @@ /* dbmsgreader.m - this file is part of SOGo * * Copyright (C) 2011-2012 Inverse inc + * 2015 Enrique J. Hernandez * - * Author: Wolfgang Sourdeau * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,21 +33,58 @@ #import #import +#import + #import "MAPIStoreUserContext.h" #import #import #import "NSObject+PropertyList.h" -Class MAPIStoreUserContextK, SOGoCacheGCSObjectK; + +Class MAPIStoreUserContextK, SOGoCacheGCSObjectK, NSStringK; static void DumpBSONData(NSData *data) { + id key, value; + NSEnumerator *dictEnum; NSDictionary *dvalue; + NSMutableString *outStr; + NSUInteger max; + dvalue = [data BSONValue]; - [dvalue displayWithIndentation:0]; - printf("\n"); + max = [dvalue count]; + dictEnum = [dvalue keyEnumerator]; + NSStringK = [NSString class]; + outStr = [NSMutableString stringWithFormat: @"{ %d items\n", max]; + while ((key = [dictEnum nextObject])) + { + uint32_t proptag = 0; + if ([key isKindOfClass: NSStringK] && [(NSString *)key intValue] > 0) + proptag = [(NSString *)key intValue]; + + if (proptag > 0) + { + const char *propTagName = get_proptag_name (proptag); + NSString *propName; + + if (propTagName) + propName = [NSString stringWithCString: propTagName + encoding: NSUTF8StringEncoding]; + else + propName = [NSString stringWithFormat: @"0x%.4x", [key unsignedLongValue]]; + + [outStr appendFormat: @" %@ = ", propName]; + } + else + [outStr appendFormat: @" %@ = ", key]; + + value = [dvalue objectForKey: key]; + [outStr appendFormat: @"(%@) %@,\n", NSStringFromClass ([value class]), value]; + } + [outStr appendFormat: @"}\n"]; + printf ("%s\n", [outStr UTF8String]); } static void @@ -67,7 +104,7 @@ DbDumpObject (NSString *username, NSString *path) { printf("record found: %p\n", record); content = [[record objectForKey: @"c_content"] dataByDecodingBase64]; - DumpBSONData(content); + DumpBSONData (content); } else NSLog (@"record not found"); diff --git a/OpenChange/iCalEvent+MAPIStore.m b/OpenChange/iCalEvent+MAPIStore.m index 90bf4e8bd..25877029b 100644 --- a/OpenChange/iCalEvent+MAPIStore.m +++ b/OpenChange/iCalEvent+MAPIStore.m @@ -36,6 +36,7 @@ #import #import #import +#import #import #import #import @@ -70,11 +71,12 @@ #include #import "iCalEvent+MAPIStore.h" +#import "iCalTimeZone+MAPIStore.h" @implementation iCalEvent (MAPIStoreProperties) - (void) _setupEventRecurrence: (NSData *) mapiRecurrenceData - inTimeZone: (NSTimeZone *) tz + inTimeZone: (iCalTimeZone *) tz inMemCtx: (TALLOC_CTX *) memCtx { struct Binary_r *blob; @@ -250,10 +252,8 @@ BOOL isAllDay; iCalDateTime *start, *end; iCalTimeZone *tz; - NSTimeZone *userTimeZone; - NSString *priority, *class = nil; + NSString *priority, *class = nil, *tzDescription = nil; NSUInteger responseStatus = 0; - NSInteger tzOffset; SOGoUser *ownerUser; id value; @@ -274,7 +274,31 @@ [self setAccessClass: @"PUBLIC"]; } - userTimeZone = [userContext timeZone]; + /* Time zone = PidLidAppointmentTimeZoneDefinitionRecur + or PidLidAppointmentTimeZoneDefinition[Start|End]Display */ + value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentTimeZoneDefinitionStartDisplay)]; + if (!value) + { + value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentTimeZoneDefinitionEndDisplay)]; + if (!value) + { + /* If PidLidtimeZoneStruct, TZID SHOULD come from PidLidTimeZoneDescription, + if PidLidAppointmentTimeZoneDefinition[Start|End]Display it MUST be derived from KeyName + (MS-OXCICAL] 2.1.3.1.1.19.1) */ + value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentTimeZoneDefinitionRecur)]; + tzDescription = [properties objectForKey: MAPIPropertyKey (PidLidTimeZoneDescription)]; + } + } + if (value) + { + tz = [[iCalTimeZone alloc] iCalTimeZoneFromDefinition: value + withDescription: tzDescription + inMemCtx: memCtx]; + } + else + /* The client is more likely to have the webmail's time zone than any other */ + tz = [iCalTimeZone timeZoneForName: [[userContext timeZone] name]]; + [(iCalCalendar *) parent addTimeZone: tz]; /* CREATED */ value = [properties objectForKey: MAPIPropertyKey (PidTagCreationTime)]; @@ -306,20 +330,13 @@ objectForKey: MAPIPropertyKey (PidLidAppointmentSubType)]; if (value) isAllDay = [value boolValue]; - if (!isAllDay) - { - tz = [iCalTimeZone timeZoneForName: [userTimeZone name]]; - [(iCalCalendar *) parent addTimeZone: tz]; - } - else - tz = nil; // recurrence-id value = [properties objectForKey: MAPIPropertyKey (PidLidExceptionReplaceTime)]; if (value) [self setRecurrenceId: value]; - + // start value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentStartWhole)]; if (!value) @@ -330,15 +347,7 @@ [start setTimeZone: tz]; if (isAllDay) { - /* when user TZ is positive (East) all-day events were not - shown properly in SOGo UI. This day delay fixes it */ - tzOffset = [userTimeZone secondsFromGMTForDate: value]; - if (tzOffset > 0) - { - value = [value dateByAddingYears: 0 months: 0 days: 1 - hours: 0 minutes: 0 - seconds: 0]; - } + /* All-day events are set in floating time ([MS-OXCICAL] 2.1.3.1.1.20.8) */ [start setDate: value]; [start setTimeZone: nil]; } @@ -356,15 +365,7 @@ [end setTimeZone: tz]; if (isAllDay) { - /* when user TZ is positive (East) all-day events were not - shown properly in SOGo UI. This day delay fixes it */ - tzOffset = [userTimeZone secondsFromGMTForDate: value]; - if (tzOffset > 0) - { - value = [value dateByAddingYears: 0 months: 0 days: 1 - hours: 0 minutes: 0 - seconds: 0]; - } + /* All-day events are set in floating time ([MS-OXCICAL] 2.1.3.1.1.20.8) */ [end setDate: value]; [end setTimeZone: nil]; } @@ -467,7 +468,7 @@ value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentRecur)]; if (value) - [self _setupEventRecurrence: value inTimeZone: userTimeZone inMemCtx: memCtx]; + [self _setupEventRecurrence: value inTimeZone: tz inMemCtx: memCtx]; /* alarm */ [self _setupEventAlarmFromProperties: properties]; diff --git a/OpenChange/iCalTimeZone+MAPIStore.h b/OpenChange/iCalTimeZone+MAPIStore.h index 76b0a00a2..25817392a 100644 --- a/OpenChange/iCalTimeZone+MAPIStore.h +++ b/OpenChange/iCalTimeZone+MAPIStore.h @@ -30,6 +30,11 @@ - (struct Binary_r *) asTimeZoneStructInMemCtx: (TALLOC_CTX *) memCtx; - (struct Binary_r *) asZoneTimeDefinitionWithFlags: (enum TZRuleFlag) flags inMemCtx: (TALLOC_CTX *) memCtx; +- (iCalTimeZone *) iCalTimeZoneFromDefinition: (NSData *) value + withDescription: (NSString *) description + inMemCtx: (TALLOC_CTX *) memCtx; +- (NSCalendarDate *) shiftedCalendarDateForDate: (NSCalendarDate *) date; + @end diff --git a/OpenChange/iCalTimeZone+MAPIStore.m b/OpenChange/iCalTimeZone+MAPIStore.m index f692272db..7cf380fdf 100644 --- a/OpenChange/iCalTimeZone+MAPIStore.m +++ b/OpenChange/iCalTimeZone+MAPIStore.m @@ -23,11 +23,15 @@ #import #import #import +#import #import +#import #import #import #import "NSString+MAPIStore.h" +#import "NSData+MAPIStore.h" +#import "NSDate+MAPIStore.h" #include #include @@ -36,6 +40,7 @@ #include #import "iCalTimeZone+MAPIStore.h" +#import "MAPIStoreTypes.h" @interface iCalTimeZonePeriod (MAPIStorePropertiesPrivate) @@ -103,18 +108,18 @@ { iCalTimeZonePeriod *period; struct TimeZoneStruct tz; - int lBias, dlBias; + int32_t lBias, dlBias; memset (&tz, 0, sizeof (struct TimeZoneStruct)); period = [self _mostRecentPeriodWithName: @"STANDARD"]; lBias = -[period secondsOffsetFromGMT] / 60; - tz.lBias = (uint32_t) lBias; + tz.lBias = lBias; [period _fillTZDate: &tz.stStandardDate]; period = [self _mostRecentPeriodWithName: @"DAYLIGHT"]; if (!period) tz.stStandardDate.wMonth = 0; dlBias = -([period secondsOffsetFromGMT] / 60) - lBias; - tz.lDaylightBias = (uint32_t) (dlBias); + tz.lDaylightBias = dlBias; [period _fillTZDate: &tz.stDaylightDate]; tz.wStandardYear = tz.stStandardDate.wYear; tz.wDaylightYear = tz.stDaylightDate.wYear; @@ -153,18 +158,220 @@ period = [self _mostRecentPeriodWithName: @"STANDARD"]; rule.wYear = [[period startDate] yearOfCommonEra]; lBias = -[period secondsOffsetFromGMT] / 60; - rule.lBias = (uint32_t) lBias; + rule.lBias = lBias; [period _fillTZDate: &rule.stStandardDate]; period = [self _mostRecentPeriodWithName: @"DAYLIGHT"]; if (!period) rule.stStandardDate.wMonth = 0; dlBias = -([period secondsOffsetFromGMT] / 60) - lBias; - rule.lDaylightBias = (uint32_t) (dlBias); + rule.lDaylightBias = dlBias; [period _fillTZDate: &rule.stDaylightDate]; return set_TimeZoneDefinition (memCtx, &definition); } +- (NSString *) _offsetStringFromOffset: (NSInteger) offset +{ + NSInteger offsetHours, offsetMins; + NSString *offsetSign; + + /* The offset format is, eg, "+0200" for 2 hours 0 minutes ahead */ + if (offset < 0) + offsetSign = @"-"; + else + offsetSign = @"+"; + offsetHours = abs (offset) / 60; + offsetMins = abs (offset) % 60; + + return [NSString stringWithFormat: @"%@%d%d%d%d", + offsetSign, offsetHours / 10, offsetHours % 10, + offsetMins / 10, offsetMins % 10]; + +} + +- (NSString *) _rRuleStringFromSystemTime: (struct SYSTEMTIME) date +{ + NSString *result, *byDay; + + /* The conversion tables between the SYSTEMTIME fields and the RRULE ones + can be found at [MS-OXCICAL] 2.1.3.2.1 */ + if (date.wDay == 5) + byDay = @"-1"; + else + byDay = [NSString stringWithFormat: @"%d", date.wDay]; + + switch (date.wDayOfWeek) + { + case iCalWeekDaySunday: + byDay = [byDay stringByAppendingString: @"SU"]; + break; + case iCalWeekDayMonday: + byDay = [byDay stringByAppendingString: @"MO"]; + break; + case iCalWeekDayTuesday: + byDay = [byDay stringByAppendingString: @"TU"]; + break; + case iCalWeekDayWednesday: + byDay = [byDay stringByAppendingString: @"WE"]; + break; + case iCalWeekDayThursday: + byDay = [byDay stringByAppendingString: @"TH"]; + break; + case iCalWeekDayFriday: + byDay = [byDay stringByAppendingString: @"FR"]; + break; + case iCalWeekDaySaturday: + byDay = [byDay stringByAppendingString: @"SA"]; + break; + } + + result = [NSString stringWithFormat: @"FREQ=YEARLY;BYDAY=%@;BYMONTH=%d", byDay, date.wMonth]; + + return result; +} + +- (iCalTimeZone *) iCalTimeZoneFromDefinition: (NSData *) value + withDescription: (NSString *) description + inMemCtx: (TALLOC_CTX *) memCtx +{ + BOOL daylightDefined = NO, ruleFound = NO; + iCalDateTime *daylightStart, *standardStart; + iCalRecurrenceRule *daylightRRule, *standardRRule; + iCalTimeZone *tz = nil; + iCalTimeZonePeriod *daylight, *standard; + NSCalendarDate *dlStartValue, *stStartValue; + NSString *strOffsetFrom, *strOffsetTo, *tzID; + char *keyName; + struct Binary_r *binValue; + struct SYSTEMTIME initDate; + struct TimeZoneDefinition *definition; + struct TZRule rule; + uint16_t count; + + binValue = [value asBinaryInMemCtx: memCtx]; + definition = get_TimeZoneDefinition (memCtx, binValue); + + if (!definition) + return nil; + + if (!definition->cRules) + goto end; + + for (count = 0; count < definition->cRules; count++) + { + /* ([MS-OXCICAL] 2.1.3.1.1.19) The TZRule with the + TZRULE_FLAG_EFFECTIVE_TZREG bit set in the TZRule flags field + is the one that MUST be exported */ + if (definition->TZRules[count].flags & TZRULE_FLAG_EFFECTIVE_TZREG) + { + rule = definition->TZRules[count]; + ruleFound = YES; + break; + } + } + + if (!ruleFound) + goto end; + + if (!description) + { + /* The cbHeader field contains the size, in bytes of the Reserved (2b), + cchKeyName (2b) keyName (variable Unicode string) and cRules (2b) + ([MS-OXOCAL] 2.2.1.41). The keyName field is a non-NULL-terminated + char array. */ + keyName = talloc_strndup (memCtx, definition->keyName, (definition->cbHeader - 6) / 2); + tzID = [NSString stringWithCString: keyName + encoding: [NSString defaultCStringEncoding]]; + talloc_free (keyName); + } + else + tzID = [NSString stringWithString: description]; + + tz = [iCalTimeZone groupWithTag: @"vtimezone"]; + [tz addChild: [CardElement simpleElementWithTag: @"tzid" + value: tzID]]; + + if (rule.stStandardDate.wMonth != 0) + daylightDefined = YES; + + /* STANDARD TIME ([MS-OXCICAL] 2.1.3.1.1.19.2) */ + standard = [iCalTimeZonePeriod groupWithTag: @"standard"]; + + /* TZOFFSETFROM = -1 * (PidLidTimeZoneStruct.lBias + PidLidTimeZoneStruct.lDaylightBias) */ + strOffsetFrom = [self _offsetStringFromOffset: -1 * (rule.lBias + rule.lDaylightBias)]; + [standard addChild: [CardElement simpleElementWithTag: @"tzoffsetfrom" + value: strOffsetFrom]]; + + /* TZOFFSETTO = -1 * (PidLidTimeZoneStruct.lBias + PidLidTimeZoneStruct.lStandardBias) */ + strOffsetTo = [self _offsetStringFromOffset: -1 * (rule.lBias + rule.lStandardBias)]; + [standard addChild: [CardElement simpleElementWithTag: @"tzoffsetto" + value: strOffsetTo]]; + + /* DTSTART & RRULE are derived from the stStandardDate and wYear properties */ + standardStart = [iCalDateTime elementWithTag: @"dtstart"]; + + initDate = rule.stStandardDate; + stStartValue = [NSCalendarDate dateFromSystemTime: initDate + andRuleYear: rule.wYear]; + + [standardStart setDateTime: stStartValue]; + [standard addChild: standardStart]; + + if (daylightDefined) + { + standardRRule = [[iCalRecurrenceRule alloc] initWithString: [self _rRuleStringFromSystemTime: initDate]]; + [standard addChild: standardRRule]; + + /* DAYLIGHT SAVING TIME ([MS-OXCICAL] 2.1.3.1.1.19.3) */ + daylight = [iCalTimeZonePeriod groupWithTag: @"daylight"]; + /* TZOFFSETFROM = -1 * (PidLidTimeZoneStruct.lBias + PidLidTimeZoneStruct.lStandardBias) */ + [daylight addChild: [CardElement simpleElementWithTag: @"tzoffsetfrom" + value: strOffsetTo]]; + /* TZOFFSETTO = -1 * (PidLidTimeZoneStruct.lBias + PidLidTimeZoneStruct.lDaylightBias) */ + [daylight addChild: [CardElement simpleElementWithTag: @"tzoffsetto" + value: strOffsetFrom]]; + + /* DTSTART & RRULE are derived from the stDaylightDate and wYear properties */ + daylightStart = [iCalDateTime elementWithTag: @"dtstart"]; + initDate = rule.stDaylightDate; + dlStartValue = [NSCalendarDate dateFromSystemTime: initDate + andRuleYear: rule.wYear]; + + [daylightStart setDateTime: dlStartValue]; + [daylight addChild: daylightStart]; + + daylightRRule = [[iCalRecurrenceRule alloc] initWithString: [self _rRuleStringFromSystemTime: initDate]]; + [daylight addChild: daylightRRule]; + [tz addChild: daylight]; + } + [tz addChild: standard]; + +end: + + talloc_free (definition); + return tz; +} + +/** + * Adjust a date in this vTimeZone to its representation in UTC + * Example: Timezone is +0001, the date is 2015-12-15 00:00:00 +0000 + * it returns 2015-12-14 23:00:00 +0000 + * @param date the date to adjust to the timezone. + * @return a new GMT date adjusted with the offset of the timezone. + */ +- (NSCalendarDate *) shiftedCalendarDateForDate: (NSCalendarDate *) date +{ + NSCalendarDate *tmpDate; + + tmpDate = [date copy]; + [tmpDate autorelease]; + + [tmpDate setTimeZone: utcTZ]; + + return [tmpDate addYear: 0 month: 0 day: 0 + hour: 0 minute: 0 + second: -[[self periodForDate: tmpDate] secondsOffsetFromGMT]]; +} @end diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 32ceb0fee..7fc6c6a56 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -609,8 +609,16 @@ static Class iCalEventK = nil; NSNumber *classNumber; unsigned int grantedCount; iCalAccessClass currentClass; + WOContext *localContext; - [self initializeQuickTablesAclsInContext: context]; + /* FIXME: The stored context from initialisation may have changed + by setContext by other operations in OpenChange library, + so we keep tighly to use the current session one. Without + this, the login is set to nil and a NSException is raised + at [SOGoAppointmentFolder:roleForComponentsWithAccessClass:forUser] + inside [SOGoAppointmentFolder:initializeQuickTablesAclsInContext]. */ + localContext = [[WOApplication application] context]; + [self initializeQuickTablesAclsInContext: localContext]; grantedClasses = [NSMutableArray arrayWithCapacity: 3]; deniedClasses = [NSMutableArray arrayWithCapacity: 3]; for (currentClass = 0; diff --git a/SoObjects/Contacts/SOGoContactSourceFolder.m b/SoObjects/Contacts/SOGoContactSourceFolder.m index abf71093e..e1e39a910 100644 --- a/SoObjects/Contacts/SOGoContactSourceFolder.m +++ b/SoObjects/Contacts/SOGoContactSourceFolder.m @@ -166,9 +166,10 @@ ldifEntry = [childRecords objectForKey: objectName]; if (!ldifEntry) { - ldifEntry = [source lookupContactEntry: objectName]; - if (ldifEntry) - [childRecords setObject: ldifEntry forKey: objectName]; + ldifEntry = [source lookupContactEntry: objectName + inDomain: [[context activeUser] domain]]; + if (ldifEntry) + [childRecords setObject: ldifEntry forKey: objectName]; else if ([self isValidContentName: objectName]) { url = [[[lookupContext request] uri] urlWithoutParameters]; @@ -373,10 +374,14 @@ NSDictionary *record; if (aName && [aName length] > 0) - record = [self _flattenedRecord: [source lookupContactEntry: aName]]; + { + record = [source lookupContactEntry: aName + inDomain: [[context activeUser] domain]]; + record = [self _flattenedRecord: record]; + } else record = nil; - + return record; } @@ -611,7 +616,7 @@ toResponse: (WOResponse *) response { NSObject *element; - NSString *url, *baseURL, *cname; + NSString *url, *baseURL, *cname, *domain; NSString **propertiesArray; NSMutableString *buffer; NSDictionary *object; @@ -628,13 +633,13 @@ max = [refs length]; buffer = [NSMutableString stringWithCapacity: max*512]; - + domain = [[context activeUser] domain]; for (count = 0; count < max; count++) { element = [refs objectAtIndex: count]; url = [[[element firstChild] nodeValue] stringByUnescapingURL]; cname = [self _deduceObjectNameFromURL: url fromBaseURL: baseURL]; - object = [source lookupContactEntry: cname]; + object = [source lookupContactEntry: cname inDomain: domain]; if (object) [self appendObject: object properties: propertiesArray diff --git a/SoObjects/Mailer/NSDictionary+Mail.m b/SoObjects/Mailer/NSDictionary+Mail.m index 177ad38ac..3ed67ff39 100644 --- a/SoObjects/Mailer/NSDictionary+Mail.m +++ b/SoObjects/Mailer/NSDictionary+Mail.m @@ -30,14 +30,13 @@ { NSDictionary *parameters; NSString *filename; - - filename = [[self objectForKey: @"parameterList"] - objectForKey: @"name"]; - - if (!filename) + + filename = nil; + parameters = [[self objectForKey: @"disposition"] + objectForKey: @"parameterList"]; + + if (parameters) { - parameters = [[self objectForKey: @"disposition"] - objectForKey: @"parameterList"]; filename = [parameters objectForKey: @"filename"]; @@ -45,29 +44,33 @@ // See RFC2231 for details. If it was folded before, it will // be unfolded when we get here. if (!filename) - { - filename = [parameters objectForKey: @"filename*"]; - - if (filename) - { - NSRange r; - - filename = [filename stringByUnescapingURL]; - - // We skip up to the language - r = [filename rangeOfString: @"'"]; - - if (r.length) - { - r = [filename rangeOfString: @"'" options: 0 range: NSMakeRange(r.location+1, [filename length]-r.location-1)]; - - if (r.length) - filename = [filename substringFromIndex: r.location+1]; - } - } - } + { + filename = [parameters objectForKey: @"filename*"]; + + if (filename) + { + NSRange r; + + filename = [filename stringByUnescapingURL]; + + // We skip up to the language + r = [filename rangeOfString: @"'"]; + + if (r.length) + { + r = [filename rangeOfString: @"'" options: 0 range: NSMakeRange(r.location+1, [filename length]-r.location-1)]; + + if (r.length) + filename = [filename substringFromIndex: r.location+1]; + } + } + } } + if (!filename) + filename = [[self objectForKey: @"parameterList"] + objectForKey: @"name"]; + return filename; } diff --git a/SoObjects/SOGo/GNUmakefile b/SoObjects/SOGo/GNUmakefile index e4a528f57..783c00d3e 100644 --- a/SoObjects/SOGo/GNUmakefile +++ b/SoObjects/SOGo/GNUmakefile @@ -32,6 +32,12 @@ SOGo_HEADER_FILES = \ SOGoGCSFolder.h \ SOGoParentFolder.h \ SOGoUserFolder.h \ + SOGoSource.h \ + SOGoSystemDefaults.h \ + SOGoDomainDefaults.h \ + SOGoLDAPDefaults.h \ + SOGoDefaultsSource.h \ + SOGoUserDefaults.h \ \ SOGoSieveManager.h \ \ diff --git a/SoObjects/SOGo/LDAPSource.h b/SoObjects/SOGo/LDAPSource.h index 3877ec4ee..9081196be 100644 --- a/SoObjects/SOGo/LDAPSource.h +++ b/SoObjects/SOGo/LDAPSource.h @@ -120,8 +120,10 @@ andMultipleBookingsField: (NSString *) newMultipleBookingsField; - (void) setContactMapping: (NSDictionary *) newMapping andObjectClasses: (NSArray *) newObjectClasses; -- (NGLdapEntry *) lookupGroupEntryByUID: (NSString *) theUID; -- (NGLdapEntry *) lookupGroupEntryByEmail: (NSString *) theEmail; +- (NGLdapEntry *) lookupGroupEntryByUID: (NSString *) theUID + inDomain: (NSString *) domain; +- (NGLdapEntry *) lookupGroupEntryByEmail: (NSString *) theEmail + inDomain: (NSString *) domain; @end diff --git a/SoObjects/SOGo/LDAPSource.m b/SoObjects/SOGo/LDAPSource.m index 55115efdd..2fa8f3773 100644 --- a/SoObjects/SOGo/LDAPSource.m +++ b/SoObjects/SOGo/LDAPSource.m @@ -162,6 +162,7 @@ static Class NSStringK; [multipleBookingsField release]; [MSExchangeHostname release]; [modifiers release]; + [displayName release]; [super dealloc]; } @@ -1242,6 +1243,7 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses } - (NSDictionary *) lookupContactEntry: (NSString *) theID + inDomain: (NSString *) domain { NGLdapEntry *ldapEntry; EOQualifier *qualifier; @@ -1339,12 +1341,14 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses } - (NGLdapEntry *) lookupGroupEntryByUID: (NSString *) theUID + inDomain: (NSString *) domain { return [self _lookupGroupEntryByAttributes: [NSArray arrayWithObject: UIDField] andValue: theUID]; } - (NGLdapEntry *) lookupGroupEntryByEmail: (NSString *) theEmail + inDomain: (NSString *) domain { return [self _lookupGroupEntryByAttributes: mailFields andValue: theEmail]; diff --git a/SoObjects/SOGo/SOGoDomainDefaults.m b/SoObjects/SOGo/SOGoDomainDefaults.m index b55cc0851..4c7541b19 100644 --- a/SoObjects/SOGo/SOGoDomainDefaults.m +++ b/SoObjects/SOGo/SOGoDomainDefaults.m @@ -50,6 +50,9 @@ andParentSource: systemDefaults]; } + if (!domainDefaults) + domainDefaults = [SOGoSystemDefaults sharedSystemDefaults]; + return domainDefaults; } diff --git a/SoObjects/SOGo/SOGoGroup.m b/SoObjects/SOGo/SOGoGroup.m index c72095fb6..431b39028 100644 --- a/SoObjects/SOGo/SOGoGroup.m +++ b/SoObjects/SOGo/SOGoGroup.m @@ -113,7 +113,7 @@ uid = [theID hasPrefix: @"@"] ? [theID substringFromIndex: 1] : theID; return [SOGoGroup groupWithValue: uid - andSourceSelector: @selector (lookupGroupEntryByUID:) + andSourceSelector: @selector (lookupGroupEntryByUID:inDomain:) inDomain: domain]; } @@ -121,7 +121,7 @@ inDomain: (NSString *) domain { return [SOGoGroup groupWithValue: theEmail - andSourceSelector: @selector (lookupGroupEntryByEmail:) + andSourceSelector: @selector (lookupGroupEntryByEmail:inDomain:) inDomain: domain]; } @@ -158,9 +158,9 @@ // Our different sources might not all implements groups support if ([source respondsToSelector: theSelector]) - entry = [source performSelector: theSelector - withObject: theValue]; - + entry = [source performSelector: theSelector + withObject: theValue + withObject: domain]; if (entry) break; diff --git a/SoObjects/SOGo/SOGoObject.m b/SoObjects/SOGo/SOGoObject.m index c54c0b76c..a6c2e16b0 100644 --- a/SoObjects/SOGo/SOGoObject.m +++ b/SoObjects/SOGo/SOGoObject.m @@ -180,6 +180,9 @@ [NSException raise: NSInvalidArgumentException format: @"'_name' must not be an empty string"]; context = [[WOApplication application] context]; + if (!context) + [self errorWithFormat: @"Error: initializing a SOGoObject (named %@) " + @"without wocontext", _name]; nameInContainer = [_name copy]; container = _container; if ([self doesRetainContainer]) diff --git a/SoObjects/SOGo/SOGoSession.m b/SoObjects/SOGo/SOGoSession.m index 9af654b90..73bbbfa5d 100644 --- a/SoObjects/SOGo/SOGoSession.m +++ b/SoObjects/SOGo/SOGoSession.m @@ -41,6 +41,7 @@ #include #import "SOGoSystemDefaults.h" +#import "SOGoUserManager.h" @implementation SOGoSession @@ -262,7 +263,7 @@ // The domain is probably appended to the username; // make sure it is defined as a domain in the configuration. *theDomain = [*theLogin substringFromIndex: (r.location + r.length)]; - if (![[sd domainIds] containsObject: *theDomain]) + if (![[SOGoUserManager sharedUserManager] isDomainDefined: *theDomain]) *theDomain = nil; } } diff --git a/SoObjects/SOGo/SOGoSource.h b/SoObjects/SOGo/SOGoSource.h index b55a5a14a..d59a81086 100644 --- a/SoObjects/SOGo/SOGoSource.h +++ b/SoObjects/SOGo/SOGoSource.h @@ -56,7 +56,8 @@ newPassword: (NSString *) newPassword perr: (SOGoPasswordPolicyError *) perr; -- (NSDictionary *) lookupContactEntry: (NSString *) theID; +- (NSDictionary *) lookupContactEntry: (NSString *) theID + inDomain: (NSString *) domain; - (NSDictionary *) lookupContactEntryWithUIDorEmail: (NSString *) entryID inDomain: (NSString *) domain; diff --git a/SoObjects/SOGo/SOGoSystemDefaults.m b/SoObjects/SOGo/SOGoSystemDefaults.m index 8c9181665..9447b1da3 100644 --- a/SoObjects/SOGo/SOGoSystemDefaults.m +++ b/SoObjects/SOGo/SOGoSystemDefaults.m @@ -261,7 +261,7 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict, - (BOOL) enableDomainBasedUID { - return ([[self domainIds] count] > 0 && [self boolForKey: @"SOGoEnableDomainBasedUID"]); + return [self boolForKey: @"SOGoEnableDomainBasedUID"]; } - (NSArray *) loginDomains diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index 4a57a1575..26588b411 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -165,7 +165,7 @@ // The domain is probably appended to the username; // make sure it is defined as a domain in the configuration. domain = [newLogin substringFromIndex: (r.location + r.length)]; - if ([[sd domainIds] containsObject: domain] && + if ([[SOGoUserManager sharedUserManager] isDomainDefined: domain] && ![sd enableDomainBasedUID]) newLogin = [newLogin substringToIndex: r.location]; diff --git a/SoObjects/SOGo/SOGoUserManager.h b/SoObjects/SOGo/SOGoUserManager.h index d30821108..5cf246d3a 100644 --- a/SoObjects/SOGo/SOGoUserManager.h +++ b/SoObjects/SOGo/SOGoUserManager.h @@ -58,6 +58,7 @@ - (NSArray *) sourceIDsInDomain: (NSString *) domain; - (NSArray *) authenticationSourceIDsInDomain: (NSString *) domain; - (NSArray *) addressBookSourceIDsInDomain: (NSString *) domain; +- (BOOL) isDomainDefined: (NSString *) domain; - (NSObject *) sourceWithID: (NSString *) sourceID; - (NSDictionary *) metadataForSourceID: (NSString *) sourceID; diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index 9765076fd..3a1b542da 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -67,10 +67,12 @@ static Class NSNullK; if (type) { - if ([type isEqualToString: @"ldap"]) + if ([type caseInsensitiveCompare: @"ldap"] == NSOrderedSame) sourceClass = @"LDAPSource"; - else if ([type isEqualToString: @"sql"]) + else if ([type caseInsensitiveCompare: @"sql"] == NSOrderedSame) sourceClass = @"SQLSource"; + else if (NSClassFromString(type)) + sourceClass = type; else { [NSException raise: @"SOGoUserManagerRegistryException" @@ -104,65 +106,58 @@ static Class NSNullK; NSString *sourceID, *value, *type; NSMutableDictionary *metadata; NSObject *sogoSource; - BOOL isAddressBook, result; + BOOL isAddressBook; Class c; - result = NO; sourceID = [udSource objectForKey: @"id"]; - if ([sourceID length] > 0) + if (!sourceID || [sourceID length] == 0) { - if ([_sourcesMetadata objectForKey: sourceID]) - [self errorWithFormat: @"attempted to register a contact/user source" - @" with duplicated id (%@)", sourceID]; - else - { - type = [[udSource objectForKey: @"type"] lowercaseString]; - c = NSClassFromString([_registry sourceClassForType: type]); - sogoSource = [c sourceFromUDSource: udSource inDomain: domain]; - if (sourceID) - [_sources setObject: sogoSource forKey: sourceID]; - else - [self errorWithFormat: @"id field missing in an user source," - @" check the SOGoUserSources defaults"]; - metadata = [NSMutableDictionary dictionary]; - if (domain) - [metadata setObject: domain forKey: @"domain"]; - value = [udSource objectForKey: @"canAuthenticate"]; - if (value) - [metadata setObject: value forKey: @"canAuthenticate"]; - value = [udSource objectForKey: @"isAddressBook"]; - if (value) - { - [metadata setObject: value forKey: @"isAddressBook"]; - isAddressBook = [value boolValue]; - } - else - isAddressBook = NO; - value = [udSource objectForKey: @"displayName"]; - if (value) - [metadata setObject: value forKey: @"displayName"]; - else - { - if (isAddressBook) - [self errorWithFormat: @"addressbook source '%@' has" - @" no displayname", sourceID]; - } - value = [udSource objectForKey: @"MailFieldNames"]; - if (value) - [metadata setObject: value forKey: @"MailFieldNames"]; - value = [udSource objectForKey: @"SearchFieldNames"]; - if (value) - [metadata setObject: value forKey: @"SearchFieldNames"]; - - [_sourcesMetadata setObject: metadata forKey: sourceID]; - result = YES; - } + [self errorWithFormat: @"attempted to register a contact/user source " + @"without id (skipped)"]; + return NO; + } + if ([_sourcesMetadata objectForKey: sourceID]) + { + [self errorWithFormat: @"attempted to register a contact/user source " + @"with duplicated id (%@)", sourceID]; + return NO; + } + + type = [udSource objectForKey: @"type"]; + c = NSClassFromString([_registry sourceClassForType: type]); + sogoSource = [c sourceFromUDSource: udSource inDomain: domain]; + [_sources setObject: sogoSource forKey: sourceID]; + + metadata = [NSMutableDictionary dictionary]; + if (domain) + [metadata setObject: domain forKey: @"domain"]; + value = [udSource objectForKey: @"canAuthenticate"]; + if (value) + [metadata setObject: value forKey: @"canAuthenticate"]; + value = [udSource objectForKey: @"isAddressBook"]; + if (value) + { + [metadata setObject: value forKey: @"isAddressBook"]; + isAddressBook = [value boolValue]; } else - [self errorWithFormat: @"attempted to register a contact/user source" - @" without id (skipped)"]; + isAddressBook = NO; + value = [udSource objectForKey: @"displayName"]; + if (value) + [metadata setObject: value forKey: @"displayName"]; + else if (isAddressBook) + [self errorWithFormat: @"addressbook source '%@' has no displayname", sourceID]; - return result; + value = [udSource objectForKey: @"MailFieldNames"]; + if (value) + [metadata setObject: value forKey: @"MailFieldNames"]; + value = [udSource objectForKey: @"SearchFieldNames"]; + if (value) + [metadata setObject: value forKey: @"SearchFieldNames"]; + + [_sourcesMetadata setObject: metadata forKey: sourceID]; + + return YES; } - (int) _registerSourcesInDomain: (NSString *) domain @@ -171,11 +166,7 @@ static Class NSNullK; unsigned int count, max, total; SOGoDomainDefaults *dd; - if (domain) - dd = [SOGoDomainDefaults defaultsForDomain: domain]; - else - dd = [SOGoSystemDefaults sharedSystemDefaults]; - + dd = [SOGoDomainDefaults defaultsForDomain: domain]; userSources = [dd userSources]; max = [userSources count]; total = 0; @@ -305,6 +296,35 @@ static Class NSNullK; return sourceIDs; } +- (BOOL) isDomainDefined: (NSString *) domain +{ + NSEnumerator *allIDs; + NSArray *ids; + NSString *currentID, *sourceDomain; + SOGoSystemDefaults *sd; + + if (!domain) return NO; + + ids = [_sources allKeys]; + if ([ids containsObject: domain]) + // FIXME check SOGoMailDomain? + // Now source id is being considered as the domain + return YES; + + sd = [SOGoSystemDefaults sharedSystemDefaults]; + if ([sd enableDomainBasedUID]) + { + allIDs = [ids objectEnumerator]; + while ((currentID = [allIDs nextObject])) + { + sourceDomain = [[_sources objectForKey: currentID] domain]; + if (!sourceDomain) // source that can identify any domain + return YES; + } + } + + return NO; +} - (NSString *) displayNameForSourceWithID: (NSString *) sourceID { NSDictionary *metadata; @@ -328,7 +348,6 @@ static Class NSNullK; { NSDictionary *contactInfos; -// NSLog (@"getEmailForUID: %@", uid); contactInfos = [self contactInfosForUserWithUIDorEmail: uid]; return [contactInfos objectForKey: @"c_email"]; @@ -345,8 +364,7 @@ static Class NSNullK; if ([cn length] > 0) { if ([email length] > 0) - fullEmail = [NSString stringWithFormat: @"%@ <%@>", - cn, email]; + fullEmail = [NSString stringWithFormat: @"%@ <%@>", cn, email]; else fullEmail = cn; } @@ -369,11 +387,7 @@ static Class NSNullK; login = [contactInfos objectForKey: @"c_imaplogin"]; if (login == nil) { - if ([domain length]) - dd = [SOGoDomainDefaults defaultsForDomain: domain]; - else - dd = [SOGoSystemDefaults sharedSystemDefaults]; - + dd = [SOGoDomainDefaults defaultsForDomain: domain]; if ([dd forceExternalLoginWithEmail]) { sd = [SOGoSystemDefaults sharedSystemDefaults]; @@ -415,16 +429,16 @@ static Class NSNullK; - (BOOL) _sourceChangePasswordForLogin: (NSString *) login inDomain: (NSString *) domain oldPassword: (NSString *) oldPassword - newPassword: (NSString *) newPassword - perr: (SOGoPasswordPolicyError *) perr + newPassword: (NSString *) newPassword + perr: (SOGoPasswordPolicyError *) perr { NSObject *sogoSource; NSEnumerator *authIDs; NSString *currentID; BOOL didChange; - + didChange = NO; - + authIDs = [[self authenticationSourceIDsInDomain: domain] objectEnumerator]; while (!didChange && (currentID = [authIDs nextObject])) { @@ -465,7 +479,24 @@ static Class NSNullK; } if (checkOK && *domain == nil) - *domain = [sogoSource domain]; + { + SOGoSystemDefaults *sd = [SOGoSystemDefaults sharedSystemDefaults]; + BOOL multidomainSource = [sd enableDomainBasedUID] && + [sogoSource domain] == nil; + if (multidomainSource) + { + NSArray *parts = [login componentsSeparatedByString: @"@"]; + if ([parts count] != 2) + { + [self errorWithFormat: @"Authenticated with multidomain source " + @"but login is not an email (%@).", login]; + return NO; + } + *domain = [parts objectAtIndex: 1]; + } + else + *domain = [sogoSource domain]; + } return checkOK; } @@ -546,7 +577,7 @@ static Class NSNullK; delta = current_time - start_time; block_time = [sd failedLoginBlockInterval]; - + if ([[failedCount objectForKey: @"FailedCount"] intValue] >= [sd maximumFailedLoginCount] && delta >= [sd maximumFailedLoginInterval] && delta <= block_time ) @@ -554,7 +585,7 @@ static Class NSNullK; *_perr = PolicyAccountLocked; return NO; } - + if (delta > block_time) { [[SOGoCache sharedCache] setFailedCount: 0 @@ -638,7 +669,7 @@ static Class NSNullK; [[SOGoCache sharedCache] setFailedCount: ([[failedCount objectForKey: @"FailedCount"] intValue] + 1) forLogin: username]; } - + checkOK = NO; } @@ -649,7 +680,7 @@ static Class NSNullK; { NSObject *currentSource; NSEnumerator *sources; - + sources = [[_sources allValues] objectEnumerator]; while ((currentSource = [sources nextObject])) if ([currentSource conformsToProtocol: @protocol(SOGoDNSource)] && @@ -669,9 +700,9 @@ static Class NSNullK; // - (BOOL) changePasswordForLogin: (NSString *) login inDomain: (NSString *) domain - oldPassword: (NSString *) oldPassword - newPassword: (NSString *) newPassword - perr: (SOGoPasswordPolicyError *) perr + oldPassword: (NSString *) oldPassword + newPassword: (NSString *) newPassword + perr: (SOGoPasswordPolicyError *) perr { NSString *jsonUser, *userLogin; NSMutableDictionary *currentUser; @@ -720,21 +751,19 @@ static Class NSNullK; SOGoDomainDefaults *dd; domain = [contact objectForKey: @"c_domain"]; - if ([domain length]) - dd = [SOGoDomainDefaults defaultsForDomain: domain]; - else - dd = [SOGoSystemDefaults sharedSystemDefaults]; + dd = [SOGoDomainDefaults defaultsForDomain: domain]; emails = [contact objectForKey: @"emails"]; - uid = [contact objectForKey: @"c_uid"]; - if ([uid rangeOfString: @"@"].location == NSNotFound) - systemEmail - = [NSString stringWithFormat: @"%@@%@", uid, [dd mailDomain]]; - else - systemEmail = uid; - - // We always add the system email, which will always be returned - // by SOGoUser -systemEmail. - [emails addObject: systemEmail]; + if ([emails count] == 0) + { + uid = [contact objectForKey: @"c_uid"]; + if ([uid rangeOfString: @"@"].location == NSNotFound) + systemEmail = [NSString stringWithFormat: @"%@@%@", uid, [dd mailDomain]]; + else + systemEmail = uid; + // We always add the system email, which will always be returned + // by SOGoUser -systemEmail. + [emails addObject: systemEmail]; + } [contact setObject: [emails objectAtIndex: 0] forKey: @"c_email"]; } @@ -789,7 +818,7 @@ static Class NSNullK; while (!userEntry && (sourceID = [sogoSources nextObject])) { currentSource = [_sources objectForKey: sourceID]; - + userEntry = [currentSource lookupContactEntryWithUIDorEmail: theUID inDomain: theDomain]; if (userEntry) @@ -904,7 +933,7 @@ static Class NSNullK; [user setObject: [NSNumber numberWithBool: YES] forKey: @"CalendarAccess"]; [user setObject: [NSNumber numberWithBool: NO] - forKey: @"MailAccess"]; + forKey: @"MailAccess"]; } return user; @@ -933,7 +962,7 @@ static Class NSNullK; // The domain is probably appended to the username; // make sure it is a defined domain in the configuration. domain = [uid substringFromIndex: (r.location + r.length)]; - if ([[sd domainIds] containsObject: domain]) + if ([self isDomainDefined: domain]) username = [uid substringToIndex: r.location]; else domain = nil; @@ -948,7 +977,7 @@ static Class NSNullK; // search using the original uid. infos = [self contactInfosForUserWithUIDorEmail: uid inDomain: nil]; - + return infos; } @@ -977,29 +1006,29 @@ static Class NSNullK; if ([currentUser isKindOfClass: NSNullK]) currentUser = nil; else if (!([currentUser objectForKey: @"emails"] - && [currentUser objectForKey: @"cn"])) - { - // We make sure that we either have no occurence of a cache entry or - // that we have an occurence with only a cached password. In the - // latter case, we update the entry with the remaining information - // and recache the value. - if (!currentUser || + && [currentUser objectForKey: @"cn"])) + { + // We make sure that we either have no occurence of a cache entry or + // that we have an occurence with only a cached password. In the + // latter case, we update the entry with the remaining information + // and recache the value. + if (!currentUser || ([currentUser count] == 1 && [currentUser objectForKey: @"password"]) || ([currentUser count] == 2 && [currentUser objectForKey: @"password"] && [currentUser objectForKey: @"DomainLessLogin"])) { - newUser = YES; + newUser = YES; - if (!currentUser) - currentUser = [NSMutableDictionary dictionary]; - } - else - newUser = NO; - [self _fillContactInfosForUser: currentUser + if (!currentUser) + currentUser = [NSMutableDictionary dictionary]; + } + else + newUser = NO; + [self _fillContactInfosForUser: currentUser withUIDorEmail: aUID inDomain: domain]; - if (newUser) - { - if ([[currentUser objectForKey: @"c_uid"] length] == 0) + if (newUser) + { + if ([[currentUser objectForKey: @"c_uid"] length] == 0) { [self _retainUser: (NSDictionary *) [NSNull null] withLogin: cacheUid]; @@ -1022,7 +1051,7 @@ static Class NSNullK; [self _retainUser: currentUser withLogin: cacheUid]; } } - } + } } else currentUser = nil; @@ -1048,7 +1077,7 @@ static Class NSNullK; while ((sourceID = [sources nextObject])) { currentSource = [_sources objectForKey: sourceID]; - contact = [currentSource lookupContactEntry: uid]; + contact = [currentSource lookupContactEntry: uid inDomain: domain]; if (contact) [contacts addObject: contact]; } @@ -1077,30 +1106,30 @@ static Class NSNullK; { uid = [userEntry objectForKey: @"c_uid"]; if ([uid length]) - { - returnContact = [compactContacts objectForKey: uid]; - if (!returnContact) - { - returnContact = [NSMutableDictionary dictionary]; - [returnContact setObject: uid forKey: @"c_uid"]; + { + returnContact = [compactContacts objectForKey: uid]; + if (!returnContact) + { + returnContact = [NSMutableDictionary dictionary]; + [returnContact setObject: uid forKey: @"c_uid"]; source = [userEntry objectForKey: @"source"]; if (source) [returnContact setObject: source forKey: @"source"]; [compactContacts setObject: returnContact forKey: uid]; } - if (![[returnContact objectForKey: @"c_name"] length]) - [returnContact setObject: [userEntry objectForKey: @"c_name"] - forKey: @"c_name"]; - if (![[returnContact objectForKey: @"cn"] length]) - [returnContact setObject: [userEntry objectForKey: @"c_cn"] - forKey: @"cn"]; - emails = [returnContact objectForKey: @"emails"]; - if (!emails) - { - emails = [NSMutableArray array]; - [returnContact setObject: emails forKey: @"emails"]; - } - email = [userEntry objectForKey: @"mail"]; + if (![[returnContact objectForKey: @"c_name"] length]) + [returnContact setObject: [userEntry objectForKey: @"c_name"] + forKey: @"c_name"]; + if (![[returnContact objectForKey: @"cn"] length]) + [returnContact setObject: [userEntry objectForKey: @"c_cn"] + forKey: @"cn"]; + emails = [returnContact objectForKey: @"emails"]; + if (!emails) + { + emails = [NSMutableArray array]; + [returnContact setObject: emails forKey: @"emails"]; + } + email = [userEntry objectForKey: @"mail"]; if ([email isKindOfClass: [NSArray class]]) { allEmails = (NSArray *) email; @@ -1112,22 +1141,22 @@ static Class NSNullK; } } else if (email && ![emails containsObject: email]) - [emails addObject: email]; - email = [userEntry objectForKey: @"mozillasecondemail"]; - if (email && ![emails containsObject: email]) - [emails addObject: email]; - email = [userEntry objectForKey: @"xmozillasecondemail"]; - if (email && ![emails containsObject: email]) - [emails addObject: email]; + [emails addObject: email]; + email = [userEntry objectForKey: @"mozillasecondemail"]; + if (email && ![emails containsObject: email]) + [emails addObject: email]; + email = [userEntry objectForKey: @"xmozillasecondemail"]; + if (email && ![emails containsObject: email]) + [emails addObject: email]; info = [userEntry objectForKey: @"c_info"]; if ([info length] > 0 && ![[returnContact objectForKey: @"c_info"] length]) [returnContact setObject: info forKey: @"c_info"]; - [self _fillContactMailRecords: returnContact]; + [self _fillContactMailRecords: returnContact]; isGroup = [userEntry objectForKey: @"isGroup"]; if (isGroup) [returnContact setObject: isGroup forKey: @"isGroup"]; - } + } } newContacts = [compactContacts allValues]; @@ -1136,7 +1165,7 @@ static Class NSNullK; } - (NSArray *) _fetchEntriesInSources: (NSArray *) sourcesList - matching: (NSString *) filter + matching: (NSString *) filter inDomain: (NSString *) domain { NSMutableArray *contacts; @@ -1150,7 +1179,7 @@ static Class NSNullK; { currentSource = [_sources objectForKey: sourceID]; [contacts addObjectsFromArray: - [currentSource fetchContactsMatching: filter + [currentSource fetchContactsMatching: filter inDomain: domain]]; } diff --git a/SoObjects/SOGo/SQLSource.m b/SoObjects/SOGo/SQLSource.m index 2e117f54d..307348f80 100644 --- a/SoObjects/SOGo/SQLSource.m +++ b/SoObjects/SOGo/SQLSource.m @@ -620,8 +620,9 @@ - (NSDictionary *) lookupContactEntry: (NSString *) theID + inDomain: (NSString *) domain { - return [self _lookupContactEntry: theID considerEmail: NO inDomain: nil]; + return [self _lookupContactEntry: theID considerEmail: NO inDomain: domain]; } - (NSDictionary *) lookupContactEntryWithUIDorEmail: (NSString *) entryID diff --git a/Tools/SOGoToolBackup.m b/Tools/SOGoToolBackup.m index fb2b4d016..3f2cb27bc 100644 --- a/Tools/SOGoToolBackup.m +++ b/Tools/SOGoToolBackup.m @@ -363,6 +363,7 @@ } - (BOOL) extractUserLDIFRecord: (NSString *) uid + inDomain: (NSString *) domain intoRecord: (NSMutableDictionary *) userRecord { NSEnumerator *ldapSources; @@ -375,11 +376,11 @@ lm = [SOGoUserManager sharedUserManager]; done = NO; - ldapSources = [[lm authenticationSourceIDsInDomain: nil] objectEnumerator]; + ldapSources = [[lm authenticationSourceIDsInDomain: domain] objectEnumerator]; while (!done && (sourceID = [ldapSources nextObject])) { currentSource = [lm sourceWithID: sourceID]; - userEntry = [currentSource lookupContactEntry: uid]; + userEntry = [currentSource lookupContactEntry: uid inDomain: domain]; if (userEntry) { [userRecord setObject: [userEntry ldifRecordAsString] @@ -411,25 +412,26 @@ - (BOOL) exportUser: (NSDictionary *) theUser { - NSString *exportPath, *gcsUID, *ldapUID; + NSString *exportPath, *gcsUID, *ldapUID, *domain; NSMutableDictionary *userRecord; SOGoSystemDefaults *sd; - + sd = [SOGoSystemDefaults sharedSystemDefaults]; userRecord = [NSMutableDictionary dictionary]; ldapUID = [theUser objectForKey: @"c_uid"]; exportPath = [directory stringByAppendingPathComponent: ldapUID]; - + domain = [theUser objectForKey: @"c_domain"]; gcsUID = [theUser objectForKey: @"c_uid"]; if ([sd enableDomainBasedUID] && [gcsUID rangeOfString: @"@"].location == NSNotFound) gcsUID = [NSString stringWithFormat: @"%@@%@", gcsUID, [theUser objectForKey: @"c_domain"]]; - + return ([self extractUserFolders: gcsUID intoRecord: userRecord] && [self extractUserLDIFRecord: ldapUID + inDomain: domain intoRecord: userRecord] && [self extractUserPreferences: gcsUID intoRecord: userRecord] diff --git a/UI/WebServerResources/angular-material b/UI/WebServerResources/angular-material index 843d63ec5..80a8929f5 160000 --- a/UI/WebServerResources/angular-material +++ b/UI/WebServerResources/angular-material @@ -1 +1 @@ -Subproject commit 843d63ec5520d313eb327d2d99606e9f6177912e +Subproject commit 80a8929f513c508da49a347a2a52bef48c15fb2a