diff --git a/ChangeLog b/ChangeLog index acdadb9a3..355c20228 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2011-03-20 Wolfgang Sourdeau + * OpenChange/MAPIStoreCalendarMessage.m + (-_setupRecurrenceInCalendar:withMasterEvent:fromData:): new + method for setting recurrence info from "PidLidAppointmentRecur" + (no exdate, exrule or modified occurrence for now). + * OpenChange/NSCalendarDate+MAPIStore.m (+dateFromMinutesSince1601): new constructor, helpful for MAPI recurrence structures. diff --git a/OpenChange/GNUmakefile b/OpenChange/GNUmakefile index c78e28e27..d55006bd3 100644 --- a/OpenChange/GNUmakefile +++ b/OpenChange/GNUmakefile @@ -112,25 +112,29 @@ $(SOGOBACKEND)_RESOURCE_FILES += \ product.plist ### cflags and libs -LIBMAPI_CFLAGS = $(shell pkg-config libmapistore --cflags) +LIBMAPI_CFLAGS = $(shell pkg-config libmapi --cflags) +LIBMAPISTORE_CFLAGS = $(shell pkg-config libmapistore --cflags) -ifeq ($(LIBMAPI_CFLAGS),) +ifeq ($(LIBMAPISTORE_CFLAGS),) all install:: @echo "Cannot build the OpenChange SOGo backend (empty CFLAGS for libmapistore)" else -LIBMAPI_LIBS = $(shell pkg-config libmapistore --libs) -lmapiproxy +LIBMAPI_LIBS = $(shell pkg-config libmapi --libs) +LIBMAPISTORE_LIBS = $(shell pkg-config libmapistore --libs) -lmapiproxy ADDITIONAL_INCLUDE_DIRS += \ -Werror -Wall \ $(LIBMAPI_CFLAGS) \ + $(LIBMAPISTORE_CFLAGS) \ -I../SoObjects -I../SOPE \ -DBACKEND_BUNDLE_NAME="@\"$(BUNDLE_NAME)$(BUNDLE_EXTENSION)\"" \ -DSOGO_BUNDLES_DIR="@\"$(BUNDLE_INSTALL_DIR)\"" ADDITIONAL_LIB_DIRS += -Wl,--as-needed \ -L../SOGo/SOGo.framework/ -lSOGo \ -L../../OGoContentStore/$(GNUSTEP_OBJ_DIR)/ -lOGoContentStore \ - $(LIBMAPI_LIBS) + $(LIBMAPI_LIBS) \ + $(LIBMAPISTORE_LIBS) SAMBA_LIB_DIR = $(shell pkg-config libmapistore --variable=libdir) diff --git a/OpenChange/MAPIStoreCalendarMessage.h b/OpenChange/MAPIStoreCalendarMessage.h index cfc6b1c5f..32d409722 100644 --- a/OpenChange/MAPIStoreCalendarMessage.h +++ b/OpenChange/MAPIStoreCalendarMessage.h @@ -25,6 +25,10 @@ #import "MAPIStoreGCSMessage.h" +#define minutesPerHour 60 +#define hoursPerDay 24 +#define minutesPerDay (minutesPerHour * hoursPerDay) + @interface MAPIStoreCalendarMessage : MAPIStoreGCSMessage @end diff --git a/OpenChange/MAPIStoreCalendarMessage.m b/OpenChange/MAPIStoreCalendarMessage.m index e499022e0..eb8b101d9 100644 --- a/OpenChange/MAPIStoreCalendarMessage.m +++ b/OpenChange/MAPIStoreCalendarMessage.m @@ -20,16 +20,23 @@ * Boston, MA 02111-1307, USA. */ +/* TODO: + - merge common code with tasks + - take the tz definitions from Outlook */ + #import +#import #import #import #import #import #import +#import #import #import #import #import +#import #import #import #import @@ -37,6 +44,7 @@ #import "MAPIStoreContext.h" #import "MAPIStoreTypes.h" #import "NSCalendarDate+MAPIStore.h" +#import "NSData+MAPIStore.h" #import "NSString+MAPIStore.h" #import "MAPIStoreCalendarMessage.h" @@ -44,11 +52,189 @@ #undef DEBUG #include #include +#include +#include #include #include @implementation MAPIStoreCalendarMessage +- (void) _setupRecurrenceInCalendar: (iCalCalendar *) calendar + withMasterEvent: (iCalEvent *) vEvent + fromData: (NSData *) mapiRecurrenceData +{ + struct Binary_r *blob; + struct AppointmentRecurrencePattern *pattern; + iCalRecurrenceRule *rule; + iCalByDayMask *byDayMask; + iCalWeekOccurrence weekOccurrence; + iCalWeekOccurrences dayMaskDays; + NSString *monthDay, *month; + NSCalendarDate *startDate, *olEndDate, *endDate; + NSUInteger count; + NSInteger bySetPos; + unsigned char maskValue; + NSMutableArray *otherEvents; + + /* cleanup */ + [vEvent removeAllRecurrenceRules]; + [vEvent removeAllExceptionRules]; + [vEvent removeAllExceptionDates]; + otherEvents = [[calendar events] mutableCopy]; + [otherEvents removeObject: vEvent]; + [calendar removeChildren: otherEvents]; + [otherEvents release]; + + startDate = [vEvent startDate]; + + rule = [iCalRecurrenceRule elementWithTag: @"rrule"]; + [vEvent addToRecurrenceRules: rule]; + + blob = [mapiRecurrenceData asBinaryInMemCtx: memCtx]; + pattern = get_AppointmentRecurrencePattern (memCtx, blob); + + memset (&dayMaskDays, 0, sizeof (iCalWeekOccurrences)); + if (pattern->RecurrencePattern.PatternType == PatternType_Day) + { + [rule setFrequency: iCalRecurrenceFrequenceDaily]; + [rule setRepeatInterval: pattern->RecurrencePattern.Period / minutesPerDay]; + } + else if (pattern->RecurrencePattern.PatternType == PatternType_Week) + { + [rule setFrequency: iCalRecurrenceFrequenceWeekly]; + [rule setRepeatInterval: pattern->RecurrencePattern.Period]; + /* MAPI values for days are the same as in NGCards */ + for (count = 0; count < 7; count++) + { + maskValue = 1 << count; + if ((pattern->RecurrencePattern.PatternTypeSpecific.WeekRecurrencePattern & maskValue)) + dayMaskDays[count] = iCalWeekOccurrenceAll; + } + byDayMask = [iCalByDayMask byDayMaskWithDays: dayMaskDays]; + [rule setByDayMask: byDayMask]; + } + else + { + if (pattern->RecurrencePattern.RecurFrequency + == RecurFrequency_Monthly) + { + [rule setFrequency: iCalRecurrenceFrequenceMonthly]; + [rule setRepeatInterval: pattern->RecurrencePattern.Period]; + } + else if (pattern->RecurrencePattern.RecurFrequency + == RecurFrequency_Yearly) + { + [rule setFrequency: iCalRecurrenceFrequenceYearly]; + [rule setRepeatInterval: pattern->RecurrencePattern.Period / 12]; + month = [NSString stringWithFormat: @"%d", [startDate monthOfYear]]; + [rule setNamedValue: @"bymonth" to: month]; + } + else + [self errorWithFormat: + @"unhandled frequency case for Month pattern type: %d", + pattern->RecurrencePattern.RecurFrequency]; + + if ((pattern->RecurrencePattern.PatternType & 3) == 3) + { + /* HjMonthNth and MonthNth */ + if (pattern->RecurrencePattern.PatternTypeSpecific.MonthRecurrencePattern.WeekRecurrencePattern + == 0x7f) + { + /* firsts or last day of month */ + if (pattern->RecurrencePattern.PatternTypeSpecific.MonthRecurrencePattern.N + == RecurrenceN_Last) + monthDay = @"-1"; + else + monthDay = [NSString stringWithFormat: @"%d", + pattern->RecurrencePattern.PatternTypeSpecific.MonthRecurrencePattern.N]; + [rule setNamedValue: @"bymonthday" to: monthDay]; + } + else if ((pattern->RecurrencePattern.PatternTypeSpecific.MonthRecurrencePattern.WeekRecurrencePattern + == 0x3e) /* Nth week day */ + || (pattern->RecurrencePattern.PatternTypeSpecific.MonthRecurrencePattern.WeekRecurrencePattern + == 0x41)) /* Nth week-end day */ + { + for (count = 0; count < 7; count++) + { + maskValue = 1 << count; + if ((pattern->RecurrencePattern.PatternTypeSpecific.MonthRecurrencePattern.WeekRecurrencePattern + & maskValue)) + dayMaskDays[count] = iCalWeekOccurrenceAll; + } + byDayMask = [iCalByDayMask byDayMaskWithDays: dayMaskDays]; + [rule setByDayMask: byDayMask]; + + if (pattern->RecurrencePattern.PatternTypeSpecific.MonthRecurrencePattern.N + == RecurrenceN_Last) + bySetPos = -1; + else + bySetPos = pattern->RecurrencePattern.PatternTypeSpecific.MonthRecurrencePattern.N; + + [rule setNamedValue: @"bysetpos" + to: [NSString stringWithFormat: @"%d", bySetPos]]; + } + else + { + if (pattern->RecurrencePattern.PatternTypeSpecific.MonthRecurrencePattern.N + < RecurrenceN_Last) + weekOccurrence = (1 + << (pattern->RecurrencePattern.PatternTypeSpecific.MonthRecurrencePattern.N + - 1)); + else + weekOccurrence = iCalWeekOccurrenceLast; + + for (count = 0; count < 7; count++) + { + maskValue = 1 << count; + if ((pattern->RecurrencePattern.PatternTypeSpecific.MonthRecurrencePattern.WeekRecurrencePattern + & maskValue)) + dayMaskDays[count] = weekOccurrence; + } + byDayMask = [iCalByDayMask byDayMaskWithDays: dayMaskDays]; + [rule setByDayMask: byDayMask]; + } + } + else if ((pattern->RecurrencePattern.PatternType & 2) == 2 + || (pattern->RecurrencePattern.PatternType & 4) == 4) + { + /* MonthEnd, HjMonth and HjMonthEnd */ + [rule setNamedValue: @"bymonthday" + to: [NSString stringWithFormat: @"%d", + pattern->RecurrencePattern.PatternTypeSpecific.Day]]; + } + else + [self errorWithFormat: @"invalid value for PatternType: %.4x", + pattern->RecurrencePattern.PatternType]; + } + + switch (pattern->RecurrencePattern.EndType) + { + case END_NEVER_END: + case NEVER_END: + break; + case END_AFTER_N_OCCURRENCES: + [rule setRepeatCount: pattern->RecurrencePattern.OccurrenceCount]; + break; + case END_AFTER_DATE: + olEndDate = [NSCalendarDate dateFromMinutesSince1601: pattern->RecurrencePattern.EndDate]; + endDate = [NSCalendarDate dateWithYear: [olEndDate yearOfCommonEra] + month: [olEndDate monthOfYear] + day: [olEndDate dayOfMonth] + hour: [startDate hourOfDay] + minute: [startDate minuteOfHour] + second: [startDate secondOfMinute] + timeZone: [startDate timeZone]]; + [rule setUntilDate: endDate]; + break; + default: + [self errorWithFormat: @"invalid value for EndType: %.4x", + pattern->RecurrencePattern.EndType]; + } + + talloc_free (pattern); + talloc_free (blob); +} + - (enum MAPISTATUS) getProperty: (void **) data withTag: (enum MAPITAGS) propTag { @@ -57,7 +243,7 @@ int rc; rc = MAPI_E_SUCCESS; - switch (propTag) + switch ((uint32_t) propTag) { case PR_ICON_INDEX: // TODO /* see http://msdn.microsoft.com/en-us/library/cc815472.aspx */ @@ -203,7 +389,6 @@ } } -/* TODO: merge with tasks */ - (void) save { iCalCalendar *vCalendar; @@ -315,6 +500,13 @@ } } + /* recurrence */ + value = [newProperties + objectForKey: MAPIPropertyKey (PidLidAppointmentRecur)]; + [self _setupRecurrenceInCalendar: vCalendar + withMasterEvent: vEvent + fromData: value]; + // [sogoObject saveContentString: [vCalendar versitString]]; [sogoObject saveComponent: vEvent]; }