Monotone-Parent: 8c4a5e6241dde8edac2d02e1b2dda9528b69ddb6
Monotone-Revision: b4cd488f57719689e0d2bed481d82df8e68fcf46 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2011-03-22T21:28:16 Monotone-Branch: ca.inverse.sogomaint-2.0.2
parent
7981507271
commit
8c26a74bf5
11
ChangeLog
11
ChangeLog
|
@ -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.
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue