(feat) applied all changes as a patch coming from PR #180
parent
d05fd407bd
commit
ae6ed0c055
|
@ -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)
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#define MAPISTORECALENDARWRAPPER_H
|
||||
|
||||
#import <NGCards/iCalPerson.h>
|
||||
#import <NGCards/iCalTimeZone.h>
|
||||
#import <Appointments/iCalEntityObject+SOGo.h>
|
||||
|
||||
#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 */
|
||||
|
|
|
@ -37,7 +37,9 @@
|
|||
#import <NGCards/iCalEventChanges.h>
|
||||
#import <NGCards/iCalPerson.h>
|
||||
#import <NGCards/iCalTrigger.h>
|
||||
#import <NGCards/iCalTimeZonePeriod.h>
|
||||
#import <NGCards/NSString+NGCards.h>
|
||||
#import <SOGo/SOGoDomainDefaults.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserManager.h>
|
||||
|
||||
|
@ -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,20 +732,13 @@ 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,20 +753,12 @@ 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,17 +768,10 @@ 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,10 +836,9 @@ 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];
|
||||
|
@ -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,7 +1317,6 @@ static NSCharacterSet *hexCharacterSet = nil;
|
|||
{
|
||||
enum mapistore_error rc;
|
||||
NSCalendarDate *dateValue;
|
||||
NSInteger offset;
|
||||
|
||||
dateValue = [event recurrenceId];
|
||||
if (dateValue)
|
||||
|
@ -1346,13 +1324,7 @@ static NSCharacterSet *hexCharacterSet = nil;
|
|||
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];
|
||||
*data = [timeZone asTimeZoneStructInMemCtx: memCtx];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
else
|
||||
rc = MAPISTORE_ERR_NOT_FOUND;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
@ -2071,23 +2022,15 @@ 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
|
||||
*data = [timeZone asZoneTimeDefinitionWithFlags: TZRULE_FLAG_EFFECTIVE_TZREG
|
||||
inMemCtx: memCtx];
|
||||
rc = MAPISTORE_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,6 @@
|
|||
wrapperWithICalEvent: [newContainer event]
|
||||
andUser: [userContext sogoUser]
|
||||
andSenderEmail: nil
|
||||
inTimeZone: [userContext timeZone]
|
||||
withConnectionInfo: [context connectionInfo]];
|
||||
[self addProxy: appointmentWrapper];
|
||||
}
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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])
|
||||
|
|
|
@ -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)
|
||||
objectVersion = exchange_globcnt ([versionNbr unsignedLongLongValue]);
|
||||
else
|
||||
{
|
||||
/* 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];
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -140,9 +140,16 @@
|
|||
[parentFolder synchroniseCache];
|
||||
changeKey = [parentFolder changeKeyForMessageWithKey: nameInContainer];
|
||||
}
|
||||
if (!changeKey)
|
||||
abort ();
|
||||
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;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#import <Foundation/NSString.h>
|
||||
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSObject+Values.h>
|
||||
|
||||
#import <EOControl/EOFetchSpecification.h>
|
||||
#import <EOControl/EOQualifier.h>
|
||||
|
@ -38,7 +39,6 @@
|
|||
|
||||
#import "MAPIStoreTypes.h"
|
||||
#import "MAPIStoreGCSFolder.h"
|
||||
|
||||
#import "MAPIStoreGCSMessageTable.h"
|
||||
|
||||
#undef DEBUG
|
||||
|
@ -89,22 +89,32 @@
|
|||
|
||||
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
|
||||
{
|
||||
[self logWithFormat: @"No last modified found for: 0x%.16"PRIx64". Then no restriction applied",
|
||||
[value unsignedLongLongValue]];
|
||||
rc = MAPIRestrictionStateAlwaysTrue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
property = [self backendIdentifierForProperty: res->ulPropTag];
|
||||
|
|
|
@ -74,6 +74,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
|
|||
#include <util/attr.h>
|
||||
#include <libmapi/libmapi.h>
|
||||
#include <libmapiproxy.h>
|
||||
#include <limits.h>
|
||||
#include <mapistore/mapistore.h>
|
||||
#include <mapistore/mapistore_errors.h>
|
||||
|
||||
|
@ -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])
|
||||
|
|
|
@ -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];
|
||||
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
|
||||
{
|
||||
|
||||
/* 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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
@ -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
|
||||
|
|
|
@ -103,7 +103,7 @@
|
|||
else
|
||||
{
|
||||
connInfo = [(MAPIStoreContext *) [container context] connectionInfo];
|
||||
entryId = MAPIStoreInternalEntryId (connInfo->sam_ctx, userId);
|
||||
entryId = MAPIStoreInternalEntryId (connInfo, userId);
|
||||
}
|
||||
*data = [entryId asBinaryInMemCtx: memCtx];
|
||||
|
||||
|
|
|
@ -25,12 +25,9 @@
|
|||
|
||||
#include <talloc.h>
|
||||
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
|
||||
#import <NGCards/iCalCalendar.h>
|
||||
#import <NGCards/iCalRecurrenceRule.h>
|
||||
|
||||
@class NSTimeZone;
|
||||
#import <NGCards/iCalTimeZone.h>
|
||||
|
||||
@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
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSSet.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
|
||||
#import <NGExtensions/NSCalendarDate+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
@ -35,6 +34,7 @@
|
|||
#import <NGCards/iCalRepeatableEntityObject.h>
|
||||
#import <NGCards/iCalRecurrenceRule.h>
|
||||
#import <NGCards/iCalTimeZone.h>
|
||||
#import <NGCards/iCalTimeZonePeriod.h>
|
||||
|
||||
#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,9 +279,7 @@
|
|||
NSMutableArray *deletedDates, *modifiedDates;
|
||||
|
||||
startDate = [event firstRecurrenceStartDate];
|
||||
[startDate setTimeZone: timeZone];
|
||||
endDate = [event lastPossibleRecurrenceStartDate];
|
||||
[endDate setTimeZone: timeZone];
|
||||
|
||||
rp->ReaderVersion = 0x3004;
|
||||
rp->WriterVersion = 0x3004;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,13 +25,13 @@
|
|||
|
||||
@class NSString;
|
||||
|
||||
struct ldb_context;
|
||||
#include <mapistore/mapistore.h>
|
||||
|
||||
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 */
|
||||
|
|
|
@ -24,13 +24,18 @@
|
|||
#import <Foundation/NSString.h>
|
||||
#include <talloc.h>
|
||||
#include <ldb.h>
|
||||
#include <libmapiproxy.h>
|
||||
#include <samba/version.h>
|
||||
|
||||
#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)
|
||||
{
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -73,6 +73,8 @@ static NSMapTable *contextsTable = nil;
|
|||
[userContext autorelease];
|
||||
[contextsTable setObject: userContext forKey: username];
|
||||
}
|
||||
else
|
||||
[userContext activate];
|
||||
|
||||
return userContext;
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
|
||||
#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
|
||||
|
|
|
@ -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;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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 <wsourdeau@inverse.ca>
|
||||
*
|
||||
* 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 <SOGo/SOGoProductLoader.h>
|
||||
#import <SOGo/SOGoSystemDefaults.h>
|
||||
|
||||
#import <libmapi/libmapi.h>
|
||||
|
||||
#import "MAPIStoreUserContext.h"
|
||||
#import <SOGo/SOGoCacheGCSObject.h>
|
||||
|
||||
#import <SOGo/BSONCodec.h>
|
||||
#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");
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#import <NGCards/iCalDateTime.h>
|
||||
#import <NGCards/iCalPerson.h>
|
||||
#import <NGCards/iCalTimeZone.h>
|
||||
#import <NGCards/iCalTimeZonePeriod.h>
|
||||
#import <NGCards/iCalTrigger.h>
|
||||
#import <SOGo/SOGoPermissions.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
|
@ -70,11 +71,12 @@
|
|||
#include <mapistore/mapistore_nameid.h>
|
||||
|
||||
#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,13 +330,6 @@
|
|||
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
|
||||
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -23,11 +23,15 @@
|
|||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
#import <NGCards/iCalByDayMask.h>
|
||||
#import <NGCards/iCalDateTime.h>
|
||||
#import <NGCards/iCalTimeZonePeriod.h>
|
||||
#import <NGCards/iCalRecurrenceRule.h>
|
||||
|
||||
#import "NSString+MAPIStore.h"
|
||||
#import "NSData+MAPIStore.h"
|
||||
#import "NSDate+MAPIStore.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
@ -36,6 +40,7 @@
|
|||
#include <libmapi/libmapi.h>
|
||||
|
||||
#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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -166,7 +166,8 @@
|
|||
ldifEntry = [childRecords objectForKey: objectName];
|
||||
if (!ldifEntry)
|
||||
{
|
||||
ldifEntry = [source lookupContactEntry: objectName];
|
||||
ldifEntry = [source lookupContactEntry: objectName
|
||||
inDomain: [[context activeUser] domain]];
|
||||
if (ldifEntry)
|
||||
[childRecords setObject: ldifEntry forKey: objectName];
|
||||
else if ([self isValidContentName: objectName])
|
||||
|
@ -373,7 +374,11 @@
|
|||
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;
|
||||
|
||||
|
@ -611,7 +616,7 @@
|
|||
toResponse: (WOResponse *) response
|
||||
{
|
||||
NSObject <DOMElement> *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
|
||||
|
|
|
@ -31,13 +31,12 @@
|
|||
NSDictionary *parameters;
|
||||
NSString *filename;
|
||||
|
||||
filename = [[self objectForKey: @"parameterList"]
|
||||
objectForKey: @"name"];
|
||||
|
||||
if (!filename)
|
||||
{
|
||||
filename = nil;
|
||||
parameters = [[self objectForKey: @"disposition"]
|
||||
objectForKey: @"parameterList"];
|
||||
|
||||
if (parameters)
|
||||
{
|
||||
filename = [parameters objectForKey: @"filename"];
|
||||
|
||||
|
||||
|
@ -68,6 +67,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
if (!filename)
|
||||
filename = [[self objectForKey: @"parameterList"]
|
||||
objectForKey: @"name"];
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 \
|
||||
\
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -50,6 +50,9 @@
|
|||
andParentSource: systemDefaults];
|
||||
}
|
||||
|
||||
if (!domainDefaults)
|
||||
domainDefaults = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
|
||||
return domainDefaults;
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
@ -159,8 +159,8 @@
|
|||
// Our different sources might not all implements groups support
|
||||
if ([source respondsToSelector: theSelector])
|
||||
entry = [source performSelector: theSelector
|
||||
withObject: theValue];
|
||||
|
||||
withObject: theValue
|
||||
withObject: domain];
|
||||
if (entry)
|
||||
break;
|
||||
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -261,7 +261,7 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
|
|||
|
||||
- (BOOL) enableDomainBasedUID
|
||||
{
|
||||
return ([[self domainIds] count] > 0 && [self boolForKey: @"SOGoEnableDomainBasedUID"]);
|
||||
return [self boolForKey: @"SOGoEnableDomainBasedUID"];
|
||||
}
|
||||
|
||||
- (NSArray *) loginDomains
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
- (NSArray *) sourceIDsInDomain: (NSString *) domain;
|
||||
- (NSArray *) authenticationSourceIDsInDomain: (NSString *) domain;
|
||||
- (NSArray *) addressBookSourceIDsInDomain: (NSString *) domain;
|
||||
- (BOOL) isDomainDefined: (NSString *) domain;
|
||||
|
||||
- (NSObject <SOGoSource> *) sourceWithID: (NSString *) sourceID;
|
||||
- (NSDictionary *) metadataForSourceID: (NSString *) sourceID;
|
||||
|
|
|
@ -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,26 +106,28 @@ static Class NSNullK;
|
|||
NSString *sourceID, *value, *type;
|
||||
NSMutableDictionary *metadata;
|
||||
NSObject <SOGoSource> *sogoSource;
|
||||
BOOL isAddressBook, result;
|
||||
BOOL isAddressBook;
|
||||
Class c;
|
||||
|
||||
result = NO;
|
||||
sourceID = [udSource objectForKey: @"id"];
|
||||
if ([sourceID length] > 0)
|
||||
if (!sourceID || [sourceID length] == 0)
|
||||
{
|
||||
[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];
|
||||
else
|
||||
{
|
||||
type = [[udSource objectForKey: @"type"] lowercaseString];
|
||||
[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];
|
||||
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"];
|
||||
|
@ -141,12 +145,9 @@ static Class NSNullK;
|
|||
value = [udSource objectForKey: @"displayName"];
|
||||
if (value)
|
||||
[metadata setObject: value forKey: @"displayName"];
|
||||
else
|
||||
{
|
||||
if (isAddressBook)
|
||||
[self errorWithFormat: @"addressbook source '%@' has"
|
||||
@" no displayname", sourceID];
|
||||
}
|
||||
else if (isAddressBook)
|
||||
[self errorWithFormat: @"addressbook source '%@' has no displayname", sourceID];
|
||||
|
||||
value = [udSource objectForKey: @"MailFieldNames"];
|
||||
if (value)
|
||||
[metadata setObject: value forKey: @"MailFieldNames"];
|
||||
|
@ -155,14 +156,8 @@ static Class NSNullK;
|
|||
[metadata setObject: value forKey: @"SearchFieldNames"];
|
||||
|
||||
[_sourcesMetadata setObject: metadata forKey: sourceID];
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"attempted to register a contact/user source"
|
||||
@" without id (skipped)"];
|
||||
|
||||
return result;
|
||||
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];
|
||||
|
||||
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];
|
||||
|
||||
if ([dd forceExternalLoginWithEmail])
|
||||
{
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
|
@ -465,7 +479,24 @@ static Class NSNullK;
|
|||
}
|
||||
|
||||
if (checkOK && *domain == nil)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -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];
|
||||
emails = [contact objectForKey: @"emails"];
|
||||
if ([emails count] == 0)
|
||||
{
|
||||
uid = [contact objectForKey: @"c_uid"];
|
||||
if ([uid rangeOfString: @"@"].location == NSNotFound)
|
||||
systemEmail
|
||||
= [NSString stringWithFormat: @"%@@%@", uid, [dd mailDomain]];
|
||||
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"];
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,7 +412,7 @@
|
|||
|
||||
- (BOOL) exportUser: (NSDictionary *) theUser
|
||||
{
|
||||
NSString *exportPath, *gcsUID, *ldapUID;
|
||||
NSString *exportPath, *gcsUID, *ldapUID, *domain;
|
||||
NSMutableDictionary *userRecord;
|
||||
SOGoSystemDefaults *sd;
|
||||
|
||||
|
@ -420,7 +421,7 @@
|
|||
|
||||
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)
|
||||
|
@ -430,6 +431,7 @@
|
|||
return ([self extractUserFolders: gcsUID
|
||||
intoRecord: userRecord]
|
||||
&& [self extractUserLDIFRecord: ldapUID
|
||||
inDomain: domain
|
||||
intoRecord: userRecord]
|
||||
&& [self extractUserPreferences: gcsUID
|
||||
intoRecord: userRecord]
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 843d63ec5520d313eb327d2d99606e9f6177912e
|
||||
Subproject commit 80a8929f513c508da49a347a2a52bef48c15fb2a
|
Loading…
Reference in New Issue