Monotone-Parent: 8c4a5e6241dde8edac2d02e1b2dda9528b69ddb6

Monotone-Revision: b4cd488f57719689e0d2bed481d82df8e68fcf46

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2011-03-22T21:28:16
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Wolfgang Sourdeau 2011-03-22 21:28:16 +00:00
parent 7981507271
commit 8c26a74bf5
3 changed files with 327 additions and 27 deletions

View File

@ -1,5 +1,16 @@
2011-03-22 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* OpenChange/MAPIStoreCalendarMessage.m (-init): new method to
initialize the attachment ivars.
(-ownerTimeZone): new method with common code.
(-getProperty:withTag): (PR_ICON_INDEX) return a more precise icon depending on
the recurring nature of the event as well on its number of
attendees. (PidLidAppointmentStartWhole,
PidLidAppointmentEndWhole) removed properties. (PidLidRecurring,
PidLidIsRecurring), returns YES when recurring.
(PidLidAppointmentRecur) property handles property, by using
-_computeAppointmentRecur and other submethods.
* OpenChange/NSCalendarDate+MAPIStore.m (-asMinutesSince1601):
corollary to +dateFromMinutesSince1601.

View File

@ -25,12 +25,11 @@
#import "MAPIStoreGCSMessage.h"
#define minutesPerHour 60
#define hoursPerDay 24
#define minutesPerDay (minutesPerHour * hoursPerDay)
#define SOGoMinutesPerHour 60
#define SOGoHoursPerDay 24
#define SOGoMinutesPerDay (SOGoMinutesPerHour * SOGoHoursPerDay)
@interface MAPIStoreCalendarMessage : MAPIStoreGCSMessage
@end
#endif /* MAPISTORECALENDARMESSAGE_H */

View File

@ -28,8 +28,10 @@
#import <Foundation/NSData.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <Foundation/NSTimeZone.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSCalendarDate+misc.h>
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalByDayMask.h>
#import <NGCards/iCalDateTime.h>
@ -43,6 +45,8 @@
#import "MAPIStoreContext.h"
#import "MAPIStoreTypes.h"
#import "MAPIStoreAttachment.h"
#import "MAPIStoreAttachmentTable.h"
#import "NSCalendarDate+MAPIStore.h"
#import "NSData+MAPIStore.h"
#import "NSString+MAPIStore.h"
@ -57,8 +61,36 @@
#include <mapistore/mapistore.h>
#include <mapistore/mapistore_nameid.h>
// extern void ndr_print_AppointmentRecurrencePattern(struct ndr_print *ndr, const char *name, const struct AppointmentRecurrencePattern *r);
@implementation MAPIStoreCalendarMessage
- (id) init
{
if ((self = [super init]))
{
attachmentKeys = [NSMutableArray new];
attachmentParts = [NSMutableDictionary new];
}
return self;
}
- (NSTimeZone *) ownerTimeZone
{
NSString *owner;
SOGoUserDefaults *ud;
NSTimeZone *tz;
WOContext *woContext;
woContext = [[self context] woContext];
owner = [sogoObject ownerInContext: woContext];
ud = [[SOGoUser userWithLogin: owner] userDefaults];
tz = [ud timeZone];
return tz;
}
- (void) _setupRecurrenceInCalendar: (iCalCalendar *) calendar
withMasterEvent: (iCalEvent *) vEvent
fromData: (NSData *) mapiRecurrenceData
@ -93,11 +125,14 @@
blob = [mapiRecurrenceData asBinaryInMemCtx: memCtx];
pattern = get_AppointmentRecurrencePattern (memCtx, blob);
// DEBUG(5, ("From client:\n"));
// NDR_PRINT_DEBUG(AppointmentRecurrencePattern, pattern);
memset (&dayMaskDays, 0, sizeof (iCalWeekOccurrences));
if (pattern->RecurrencePattern.PatternType == PatternType_Day)
{
[rule setFrequency: iCalRecurrenceFrequenceDaily];
[rule setRepeatInterval: pattern->RecurrencePattern.Period / minutesPerDay];
[rule setRepeatInterval: pattern->RecurrencePattern.Period / SOGoMinutesPerDay];
}
else if (pattern->RecurrencePattern.PatternType == PatternType_Week)
{
@ -235,11 +270,227 @@
talloc_free (blob);
}
static void
_fillRecurrencePattern (struct RecurrencePattern *rp,
NSCalendarDate *startDate, NSCalendarDate *endDate,
iCalRecurrenceRule *rule)
{
iCalRecurrenceFrequency freq;
iCalByDayMask *byDayMask;
NSString *byMonthDay, *bySetPos;
NSCalendarDate *untilDate, *beginOfWeek, *minimumDate, *moduloDate, *midnight;
iCalWeekOccurrences *days;
NSInteger dayOfWeek, repeatInterval, repeatCount, count, firstOccurrence;
uint32_t nbrMonths, mask;
rp->ReaderVersion = 0x3004;
rp->WriterVersion = 0x3004;
rp->StartDate = [[startDate beginOfDay] asMinutesSince1601];
untilDate = [rule untilDate];
if (untilDate)
{
rp->EndDate = [untilDate asMinutesSince1601];
rp->EndType = END_AFTER_DATE;
}
else
{
repeatCount = [rule repeatCount];
if (repeatCount > 0)
{
rp->EndDate = [endDate asMinutesSince1601];
rp->OccurrenceCount = repeatCount;
rp->EndType = END_AFTER_N_OCCURRENCES;
}
else
{
rp->EndDate = 0x5ae980df;
rp->EndType = END_NEVER_END;
}
}
freq = [rule frequency];
repeatInterval = [rule repeatInterval];
if (freq == iCalRecurrenceFrequenceDaily)
{
rp->RecurFrequency = RecurFrequency_Daily;
rp->PatternType = PatternType_Day;
rp->Period = repeatInterval * SOGoMinutesPerDay;
rp->FirstDateTime = rp->StartDate % rp->Period;
}
else if (freq == iCalRecurrenceFrequenceWeekly)
{
rp->RecurFrequency = RecurFrequency_Weekly;
rp->PatternType = PatternType_Week;
rp->Period = repeatInterval;
mask = 0;
byDayMask = [rule byDayMask];
for (count = 0; count < 7; count++)
if ([byDayMask occursOnDay: count])
mask |= 1 << count;
rp->PatternTypeSpecific.WeekRecurrencePattern = mask;
/* FirstDateTime */
dayOfWeek = [startDate dayOfWeek];
if (dayOfWeek)
beginOfWeek = [startDate dateByAddingYears: 0 months: 0
days: -dayOfWeek
hours: 0 minutes: 0
seconds: 0];
else
beginOfWeek = startDate;
rp->FirstDateTime = ([[beginOfWeek beginOfDay] asMinutesSince1601]
% (repeatInterval * 10080));
}
else
{
if (freq == iCalRecurrenceFrequenceMonthly)
{
rp->RecurFrequency = RecurFrequency_Monthly;
rp->Period = repeatInterval;
}
else if (freq == iCalRecurrenceFrequenceYearly)
{
rp->RecurFrequency = RecurFrequency_Yearly;
rp->Period = 12;
if (repeatInterval != 1)
[rule errorWithFormat:
@"yearly interval '%d' cannot be converted",
repeatInterval];
}
else
[rule errorWithFormat: @"frequency '%d' cannot be converted", freq];
/* FirstDateTime */
midnight = [[startDate firstDayOfMonth] beginOfDay];
minimumDate = [NSCalendarDate dateFromMinutesSince1601: 0];
nbrMonths = (([midnight yearOfCommonEra]
- [minimumDate yearOfCommonEra]) * 12
+ [midnight monthOfYear] - 1);
moduloDate = [minimumDate dateByAddingYears: 0
months: (nbrMonths % rp->Period)
days: 0 hours: 0 minutes: 0
seconds: 0];
rp->FirstDateTime = [moduloDate asMinutesSince1601];
byMonthDay = [[rule byMonthDay] objectAtIndex: 0];
if (byMonthDay)
{
if ([byMonthDay intValue] < 0)
{
/* This means we cannot handle values of BYMONTHDAY that are <
-7. */
rp->PatternType = PatternType_MonthNth;
rp->PatternTypeSpecific.MonthRecurrencePattern.WeekRecurrencePattern = 0x7f;
rp->PatternTypeSpecific.MonthRecurrencePattern.N = RecurrenceN_Last;
}
else
{
rp->PatternType = PatternType_Month;
rp->PatternTypeSpecific.Day = [byMonthDay intValue];
}
}
else
{
rp->PatternType = PatternType_MonthNth;
byDayMask = [rule byDayMask];
days = [byDayMask weekDayOccurrences];
mask = 0;
for (count = 0; count < 7; count++)
if (days[0][count])
mask |= 1 << count;
if (mask)
{
rp->PatternTypeSpecific.MonthRecurrencePattern.WeekRecurrencePattern = mask;
bySetPos = [rule namedValue: @"bysetpos"];
if ([bySetPos length])
rp->PatternTypeSpecific.MonthRecurrencePattern.N
= ([bySetPos hasPrefix: @"-"]
? RecurrenceN_Last : [bySetPos intValue]);
else
{
firstOccurrence = [byDayMask firstOccurrence];
if (firstOccurrence)
rp->PatternTypeSpecific.MonthRecurrencePattern.N
= ((firstOccurrence > -1)
? firstOccurrence : RecurrenceN_Last);
}
}
else
[rule errorWithFormat: @"rule for an event that never occurs"];
}
}
}
static void
_fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp,
NSCalendarDate *startDate, NSTimeInterval duration,
NSCalendarDate * endDate, iCalRecurrenceRule * rule)
{
uint32_t startMinutes;
_fillRecurrencePattern (&arp->RecurrencePattern, startDate, endDate, rule);
arp->ReaderVersion2 = 0x00003006;
arp->WriterVersion2 = 0x00003009;
startMinutes = ([startDate hourOfDay] * 60 + [startDate minuteOfHour]);
arp->StartTimeOffset = startMinutes;
arp->EndTimeOffset = startMinutes + (uint32_t) (duration / 60);
arp->ExceptionCount = 0;
arp->ReservedBlock1Size = 0;
/* Currently ignored in property.idl:
arp->ReservedBlock2Size = 0; */
}
- (struct SBinary_short *) _computeAppointmentRecur
{
struct AppointmentRecurrencePattern *arp;
struct Binary_r *bin;
struct SBinary_short *sBin;
NSCalendarDate *firstStartDate;
iCalEvent *vEvent;
iCalRecurrenceRule *rule;
vEvent = [sogoObject component: NO secure: NO];
rule = [[vEvent recurrenceRules] objectAtIndex: 0];
firstStartDate = [vEvent firstRecurrenceStartDate];
if (firstStartDate)
{
[firstStartDate setTimeZone: [self ownerTimeZone]];
arp = talloc_zero (memCtx, struct AppointmentRecurrencePattern);
_fillAppointmentRecurrencePattern (arp, firstStartDate,
[vEvent durationAsTimeInterval],
[vEvent lastPossibleRecurrenceStartDate],
rule);
sBin = talloc_zero (memCtx, struct SBinary_short);
bin = set_AppointmentRecurrencePattern (sBin, arp);
sBin->cb = bin->cb;
sBin->lpb = bin->lpb;
talloc_free (arp);
// DEBUG(5, ("To client:\n"));
// NDR_PRINT_DEBUG (AppointmentRecurrencePattern, arp);
}
else
{
[self errorWithFormat: @"no first occurrence found in rule: %@", rule];
sBin = NULL;
}
return sBin;
}
- (enum MAPISTATUS) getProperty: (void **) data
withTag: (enum MAPITAGS) propTag
{
NSTimeInterval timeValue;
id event;
uint32_t longValue;
int rc;
rc = MAPI_E_SUCCESS;
@ -251,19 +502,19 @@
// *longValue = 0x00000402 for meeting
// *longValue = 0x00000403 for recurring meeting
// *longValue = 0x00000404 for invitation
*data = MAPILongValue (memCtx, 0x00000400);
event = [sogoObject component: NO secure: NO];
longValue = 0x0400;
if ([event isRecurrent])
longValue |= 0x0001;
if ([[event attendees] count] > 0)
longValue |= 0x0002;
*data = MAPILongValue (memCtx, longValue);
break;
case PR_MESSAGE_CLASS_UNICODE:
*data = talloc_strdup(memCtx, "IPM.Appointment");
break;
case PidLidAppointmentStartWhole: // DTSTART
event = [sogoObject component: NO secure: NO];
*data = [[event startDate] asFileTimeInMemCtx: memCtx];
break;
case PidLidAppointmentEndWhole: // DTEND
event = [sogoObject component: NO secure: NO];
*data = [[event endDate] asFileTimeInMemCtx: memCtx];
break;
case PidLidAppointmentDuration:
event = [sogoObject component: NO secure: NO];
timeValue = [[event endDate] timeIntervalSinceDate: [event startDate]];
@ -276,9 +527,6 @@
case PidLidBusyStatus: // TODO
*data = MAPILongValue (memCtx, 0x02);
break;
case PidLidRecurring: // TODO
*data = MAPIBoolValue (memCtx, NO);
break;
// case 0x82410003: // TODO
// *data = MAPILongValue (memCtx, 0);
@ -320,6 +568,17 @@
}
break;
/* Recurrence */
case PidLidIsRecurring:
case PidLidRecurring:
event = [sogoObject component: NO secure: NO];
*data = MAPIBoolValue (memCtx, [event isRecurrent]);
break;
case PidLidAppointmentRecur:
*data = [self _computeAppointmentRecur];
break;
// case PidLidTimeZoneStruct:
// case PR_VD_NAME_UNICODE:
// *data = talloc_strdup(memCtx, "PR_VD_NAME_UNICODE");
@ -391,15 +650,14 @@
- (void) save
{
WOContext *woContext;
iCalCalendar *vCalendar;
iCalEvent *vEvent;
id value;
SOGoUserDefaults *ud;
NSCalendarDate *now;
iCalTimeZone *tz;
NSString *owner, *content;
iCalDateTime *start, *end;
WOContext *woContext;
iCalTimeZone *tz;
NSCalendarDate *now;
NSString *content, *tzName;
id value;
[self logWithFormat: @"-save, event props:"];
// MAPIStoreDumpMessageProperties (newProperties);
@ -427,10 +685,8 @@
if (value)
[vEvent setLocation: value];
woContext = [[self context] woContext];
owner = [sogoObject ownerInContext: woContext];
ud = [[SOGoUser userWithLogin: owner] userDefaults];
tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]];
tzName = [[self ownerTimeZone] name];
tz = [iCalTimeZone timeZoneForName: tzName];
[vCalendar addTimeZone: tz];
// start
@ -472,6 +728,7 @@
iCalPerson *person;
int i;
woContext = [[self context] woContext];
dict = [[woContext activeUser] primaryIdentity];
person = [iCalPerson new];
[person setCn: [dict objectForKey: @"fullName"]];
@ -511,4 +768,37 @@
[sogoObject saveComponent: vEvent];
}
/* TODO: those are stubs meant to prevent OpenChange from crashing when a
recurring event is open */
- (NSArray *) childKeysMatchingQualifier: (EOQualifier *) qualifier
andSortOrderings: (NSArray *) sortOrderings
{
/* TODO: Here we should return recurrence exceptions */
return attachmentKeys;
}
- (id) lookupChild: (NSString *) childKey
{
return [attachmentParts objectForKey: childKey];
}
- (MAPIStoreAttachmentTable *) attachmentTable
{
return [MAPIStoreAttachmentTable tableForContainer: self];
}
- (MAPIStoreAttachment *) createAttachment
{
MAPIStoreAttachment *newAttachment;
newAttachment = [MAPIStoreAttachment new];
[newAttachment setAID: 0];
[attachmentParts setObject: newAttachment
forKey: @"0"];
[attachmentKeys addObject: @"0"];
[newAttachment release];
return newAttachment;
}
@end