diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index c684c3ec8..e2e301565 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -2675,7 +2675,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. parts in this url. We strip the '-' character in case we have this in the domain part - like foo@bar-zot.com */ ocFSTableName = [NSMutableString stringWithFormat: @"sogo_cache_folder_%@", - [[user loginInDomain] asCSSIdentifier]]; + [[user login] asCSSIdentifier]]; [ocFSTableName replaceOccurrencesOfString: @"-" withString: @"_" options: 0 diff --git a/OpenChange/Codepages.h b/OpenChange/Codepages.h new file mode 100644 index 000000000..60d8223d8 --- /dev/null +++ b/OpenChange/Codepages.h @@ -0,0 +1,34 @@ +/* Codepages.h - this file is part of SOGo + * + * Copyright (C) 2014 Jesús García Sáez + * + * 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 + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import + + +@interface Codepages + ++ (NSDictionary *) getCodepagesTable; ++ (NSDictionary *) getReverseCodepagesTable; + ++ (NSNumber *) getCodepageFromName: (NSString *) name; ++ (NSString *) getNameFromCodepage: (NSNumber *) codepage; + +@end diff --git a/OpenChange/Codepages.m b/OpenChange/Codepages.m new file mode 100644 index 000000000..8776ffe22 --- /dev/null +++ b/OpenChange/Codepages.m @@ -0,0 +1,214 @@ +/* Codepages.m - this file is part of SOGo + * + * Copyright (C) 2014 Jesús García Sáez + * + * 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 + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import "Codepages.h" +#import + +@implementation Codepages + ++ (NSDictionary *) getCodepagesTable +{ + static NSDictionary *table = nil; + + if (table == nil) + { + /* http://msdn.microsoft.com/en-us/library/dd317756%28v=vs.85%29.aspx */ + table = [[NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt: 37], @"ibm037", + [NSNumber numberWithInt: 437], @"ibm437", + [NSNumber numberWithInt: 500], @"ibm500", + [NSNumber numberWithInt: 708], @"asmo-708", + [NSNumber numberWithInt: 720], @"dos-720", + [NSNumber numberWithInt: 737], @"ibm737", + [NSNumber numberWithInt: 775], @"ibm775", + [NSNumber numberWithInt: 850], @"ibm850", + [NSNumber numberWithInt: 852], @"ibm852", + [NSNumber numberWithInt: 855], @"ibm855", + [NSNumber numberWithInt: 857], @"ibm857", + [NSNumber numberWithInt: 858], @"ibm00858", + [NSNumber numberWithInt: 860], @"ibm860", + [NSNumber numberWithInt: 861], @"ibm861", + [NSNumber numberWithInt: 862], @"dos-862", + [NSNumber numberWithInt: 863], @"ibm863", + [NSNumber numberWithInt: 864], @"ibm864", + [NSNumber numberWithInt: 865], @"ibm865", + [NSNumber numberWithInt: 866], @"cp866", + [NSNumber numberWithInt: 869], @"ibm869", + [NSNumber numberWithInt: 870], @"ibm870", + [NSNumber numberWithInt: 874], @"windows-874", + [NSNumber numberWithInt: 875], @"cp875", + [NSNumber numberWithInt: 932], @"shift_jis", + [NSNumber numberWithInt: 936], @"gb2312", + [NSNumber numberWithInt: 949], @"ks_c_5601-1987", + [NSNumber numberWithInt: 950], @"big5", + [NSNumber numberWithInt: 1026], @"ibm1026", + [NSNumber numberWithInt: 1047], @"ibm01047", + [NSNumber numberWithInt: 1140], @"ibm01140", + [NSNumber numberWithInt: 1141], @"ibm01141", + [NSNumber numberWithInt: 1142], @"ibm01142", + [NSNumber numberWithInt: 1143], @"ibm01143", + [NSNumber numberWithInt: 1144], @"ibm01144", + [NSNumber numberWithInt: 1145], @"ibm01145", + [NSNumber numberWithInt: 1146], @"ibm01146", + [NSNumber numberWithInt: 1147], @"ibm01147", + [NSNumber numberWithInt: 1148], @"ibm01148", + [NSNumber numberWithInt: 1149], @"ibm01149", + [NSNumber numberWithInt: 1200], @"utf-16", + [NSNumber numberWithInt: 1201], @"unicodefffe", + [NSNumber numberWithInt: 1250], @"windows-1250", + [NSNumber numberWithInt: 1251], @"windows-1251", + [NSNumber numberWithInt: 1252], @"windows-1252", + [NSNumber numberWithInt: 1253], @"windows-1253", + [NSNumber numberWithInt: 1254], @"windows-1254", + [NSNumber numberWithInt: 1255], @"windows-1255", + [NSNumber numberWithInt: 1256], @"windows-1256", + [NSNumber numberWithInt: 1257], @"windows-1257", + [NSNumber numberWithInt: 1258], @"windows-1258", + [NSNumber numberWithInt: 1361], @"johab", + [NSNumber numberWithInt: 10000], @"macintosh", + [NSNumber numberWithInt: 10001], @"x-mac-japanese", + [NSNumber numberWithInt: 10002], @"x-mac-chinesetrad", + [NSNumber numberWithInt: 10003], @"x-mac-korean", + [NSNumber numberWithInt: 10004], @"x-mac-arabic", + [NSNumber numberWithInt: 10005], @"x-mac-hebrew", + [NSNumber numberWithInt: 10006], @"x-mac-greek", + [NSNumber numberWithInt: 10007], @"x-mac-cyrillic", + [NSNumber numberWithInt: 10008], @"x-mac-chinesesimp", + [NSNumber numberWithInt: 10010], @"x-mac-romanian", + [NSNumber numberWithInt: 10017], @"x-mac-ukrainian", + [NSNumber numberWithInt: 10021], @"x-mac-thai", + [NSNumber numberWithInt: 10029], @"x-mac-ce", + [NSNumber numberWithInt: 10079], @"x-mac-icelandic", + [NSNumber numberWithInt: 10081], @"x-mac-turkish", + [NSNumber numberWithInt: 10082], @"x-mac-croatian", + [NSNumber numberWithInt: 12000], @"utf-32", + [NSNumber numberWithInt: 12001], @"utf-32be", + [NSNumber numberWithInt: 20000], @"x-chinese-cns", + [NSNumber numberWithInt: 20001], @"x-cp20001", + [NSNumber numberWithInt: 20002], @"x-chinese-eten", + [NSNumber numberWithInt: 20003], @"x-cp20003", + [NSNumber numberWithInt: 20004], @"x-cp20004", + [NSNumber numberWithInt: 20005], @"x-cp20005", + [NSNumber numberWithInt: 20105], @"x-ia5", + [NSNumber numberWithInt: 20106], @"x-ia5-german", + [NSNumber numberWithInt: 20107], @"x-ia5-swedish", + [NSNumber numberWithInt: 20108], @"x-ia5-norwegian", + [NSNumber numberWithInt: 20127], @"us-ascii", + [NSNumber numberWithInt: 20261], @"x-cp20261", + [NSNumber numberWithInt: 20269], @"x-cp20269", + [NSNumber numberWithInt: 20273], @"ibm273", + [NSNumber numberWithInt: 20277], @"ibm277", + [NSNumber numberWithInt: 20278], @"ibm278", + [NSNumber numberWithInt: 20280], @"ibm280", + [NSNumber numberWithInt: 20284], @"ibm284", + [NSNumber numberWithInt: 20285], @"ibm285", + [NSNumber numberWithInt: 20290], @"ibm290", + [NSNumber numberWithInt: 20297], @"ibm297", + [NSNumber numberWithInt: 20420], @"ibm420", + [NSNumber numberWithInt: 20423], @"ibm423", + [NSNumber numberWithInt: 20424], @"ibm424", + [NSNumber numberWithInt: 20833], @"x-ebcdic-koreanextended", + [NSNumber numberWithInt: 20838], @"ibm-thai", + [NSNumber numberWithInt: 20866], @"koi8-r", + [NSNumber numberWithInt: 20871], @"ibm871", + [NSNumber numberWithInt: 20880], @"ibm880", + [NSNumber numberWithInt: 20905], @"ibm905", + [NSNumber numberWithInt: 20924], @"ibm00924", + [NSNumber numberWithInt: 20932], @"euc-jp", + [NSNumber numberWithInt: 20936], @"x-cp20936", + [NSNumber numberWithInt: 20949], @"x-cp20949", + [NSNumber numberWithInt: 21025], @"cp1025", + [NSNumber numberWithInt: 21866], @"koi8-u", + [NSNumber numberWithInt: 28591], @"iso-8859-1", + [NSNumber numberWithInt: 28592], @"iso-8859-2", + [NSNumber numberWithInt: 28593], @"iso-8859-3", + [NSNumber numberWithInt: 28594], @"iso-8859-4", + [NSNumber numberWithInt: 28595], @"iso-8859-5", + [NSNumber numberWithInt: 28596], @"iso-8859-6", + [NSNumber numberWithInt: 28597], @"iso-8859-7", + [NSNumber numberWithInt: 28598], @"iso-8859-8", + [NSNumber numberWithInt: 28599], @"iso-8859-9", + [NSNumber numberWithInt: 28603], @"iso-8859-13", + [NSNumber numberWithInt: 28605], @"iso-8859-15", + [NSNumber numberWithInt: 29001], @"x-europa", + [NSNumber numberWithInt: 38598], @"iso-8859-8-i", + [NSNumber numberWithInt: 50220], @"iso-2022-jp", + [NSNumber numberWithInt: 50221], @"csiso2022jp", + [NSNumber numberWithInt: 50222], @"iso-2022-jp", + [NSNumber numberWithInt: 50225], @"iso-2022-kr", + [NSNumber numberWithInt: 50227], @"x-cp50227", + [NSNumber numberWithInt: 51932], @"euc-jp", + [NSNumber numberWithInt: 51936], @"euc-cn", + [NSNumber numberWithInt: 51949], @"euc-kr", + [NSNumber numberWithInt: 52936], @"hz-gb-2312", + [NSNumber numberWithInt: 54936], @"gb18030", + [NSNumber numberWithInt: 57002], @"x-iscii-de", + [NSNumber numberWithInt: 57003], @"x-iscii-be", + [NSNumber numberWithInt: 57004], @"x-iscii-ta", + [NSNumber numberWithInt: 57005], @"x-iscii-te", + [NSNumber numberWithInt: 57006], @"x-iscii-as", + [NSNumber numberWithInt: 57007], @"x-iscii-or", + [NSNumber numberWithInt: 57008], @"x-iscii-ka", + [NSNumber numberWithInt: 57009], @"x-iscii-ma", + [NSNumber numberWithInt: 57010], @"x-iscii-gu", + [NSNumber numberWithInt: 57011], @"x-iscii-pa", + [NSNumber numberWithInt: 65000], @"utf-7", + [NSNumber numberWithInt: 65001], @"utf-8", + nil] retain]; + } + return table; +} + ++ (NSDictionary *) getReverseCodepagesTable +{ + static NSDictionary *table = nil; + + if (table == nil) + { + NSDictionary *codepages_table; + NSEnumerator *enumerator; + NSMutableArray *codepages, *names; + id key; + // Build reverse table: (NSNumber) codepage -> (NSString) encoding name + codepages_table = [self getCodepagesTable]; + codepages = [NSMutableArray arrayWithCapacity: [codepages_table count]]; + names = [NSMutableArray arrayWithCapacity: [codepages_table count]]; + enumerator = [codepages_table keyEnumerator]; + while ((key = [enumerator nextObject])) + { + [names addObject: key]; + [codepages addObject: [codepages_table objectForKey: key]]; + } + table = [[NSDictionary dictionaryWithObjects: names forKeys: codepages] retain]; + } + return table; +} + ++ (NSNumber *) getCodepageFromName: (NSString *) name +{ + return [[self getCodepagesTable] objectForKey: [name lowercaseString]]; +} + ++ (NSString *) getNameFromCodepage: (NSNumber *) codepage +{ + return [[self getReverseCodepagesTable] objectForKey: codepage]; +} + +@end diff --git a/OpenChange/GNUmakefile b/OpenChange/GNUmakefile index 29939b87d..e443ec3f6 100644 --- a/OpenChange/GNUmakefile +++ b/OpenChange/GNUmakefile @@ -31,7 +31,7 @@ endif all:: @echo " Python executable: ${PYTHON}" -SAMBA_PRIVATE_DIR = $(shell $(PYTHON) ./samba-get-config.py "private dir") +SAMBA_PRIVATE_DIR = $(shell $(PYTHON) ./samba-get-config.py 'private dir' || echo /var/lib/samba/private) $(SOGOBACKEND)_PRINCIPAL_CLASS = MAPIApplication @@ -110,6 +110,8 @@ $(SOGOBACKEND)_OBJC_FILES += \ \ MAPIStoreFallbackContext.m \ \ + MAPIStoreSharingMessage.m \ + \ NSArray+MAPIStore.m \ NSData+MAPIStore.m \ NSDate+MAPIStore.m \ @@ -120,7 +122,9 @@ $(SOGOBACKEND)_OBJC_FILES += \ iCalEvent+MAPIStore.m \ iCalTimeZone+MAPIStore.m \ \ - RTFHandler.m + RTFHandler.m \ + \ + Codepages.m $(SOGOBACKEND)_RESOURCE_FILES += \ diff --git a/OpenChange/MAPIStoreAppointmentWrapper.m b/OpenChange/MAPIStoreAppointmentWrapper.m index 24c6e1ad3..7fa60c5c5 100644 --- a/OpenChange/MAPIStoreAppointmentWrapper.m +++ b/OpenChange/MAPIStoreAppointmentWrapper.m @@ -45,6 +45,7 @@ #import "MAPIStoreRecurrenceUtils.h" #import "MAPIStoreSamDBUtils.h" #import "MAPIStoreTypes.h" +#import "NSArray+MAPIStore.h" #import "NSData+MAPIStore.h" #import "NSDate+MAPIStore.h" #import "NSObject+MAPIStore.h" @@ -791,11 +792,20 @@ static NSCharacterSet *hexCharacterSet = nil; inMemCtx: (TALLOC_CTX *) memCtx { enum mapistore_error rc; - NSCalendarDate *dateValue; + NSCalendarDate *dateValue, *start; if ([event isRecurrent]) { - dateValue = [[event startDate] hour: 0 minute: 0 second: 0]; + /* [MS-OXOCAL] For a recurring series, this property specifies + midnight in the user's machine time zone, on the date of the + first instance, then is persisted in UTC. */ + start = [event startDate]; + dateValue = [NSCalendarDate dateWithYear: [start yearOfCommonEra] + month: [start monthOfYear] + day: [start dayOfMonth] + hour: 0 minute: 0 second: 0 + timeZone: timeZone]; + [dateValue setTimeZone: utcTZ]; *data = [dateValue asFileTimeInMemCtx: memCtx]; rc = MAPISTORE_SUCCESS; } @@ -894,7 +904,7 @@ static NSCharacterSet *hexCharacterSet = nil; } else dateValue = [NSCalendarDate dateWithYear: 4500 month: 8 day: 31 - hour: 23 minute: 59 second: 59 + hour: 23 minute: 59 second: 00 timeZone: utcTZ]; *data = [dateValue asFileTimeInMemCtx: memCtx]; rc = MAPISTORE_SUCCESS; @@ -1191,11 +1201,31 @@ static NSCharacterSet *hexCharacterSet = nil; return [self getYes: data inMemCtx: memCtx]; } -- (int) getPidTagSensitivity: (void **) data // not implemented, depends on CLASS +- (int) getPidTagSensitivity: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - // normal = 0, personal?? = 1, private = 2, confidential = 3 - return [self getLongZero: data inMemCtx: memCtx]; + /* See [MS-OXCICAL] Section 2.1.3.11.20.4 */ + uint32_t v; + NSString *accessClass; + + accessClass = [event accessClass]; + if (accessClass) + { + if ([accessClass isEqualToString: @"X-PERSONAL"]) + v = 0x1; + else if ([accessClass isEqualToString: @"PRIVATE"]) + v = 0x2; + else if ([accessClass isEqualToString: @"CONFIDENTIAL"]) + v = 0x3; + else + v = 0x0; /* PUBLIC */ + } + else + v = 0x0; /* PUBLIC */ + + *data = MAPILongValue (memCtx, v); + + return MAPISTORE_SUCCESS; } - (int) getPidTagImportance: (void **) data @@ -1214,6 +1244,22 @@ static NSCharacterSet *hexCharacterSet = nil; return MAPISTORE_SUCCESS; } +- (int) getPidNameKeywords: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + /* See [MS-OXCICAL] Section 2.1.3.1.1.20.3 */ + NSArray *categories; + + categories = [event categories]; + if (categories) + { + *data = [categories asMVUnicodeInMemCtx: memCtx]; + return MAPISTORE_SUCCESS; + } + else + return MAPISTORE_ERR_NOT_FOUND; +} + - (int) getPidTagBody: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -1231,7 +1277,7 @@ static NSCharacterSet *hexCharacterSet = nil; /* Avoiding those trail weird characters at event description */ range = [stringValue rangeOfString: trimingString options: NSBackwardsSearch]; - if (range.location > 0) + if (range.location != NSNotFound) { stringValue = [stringValue substringToIndex: (NSMaxRange(range) -1)]; } @@ -1826,16 +1872,25 @@ ReservedBlockEE2Size: 00 00 00 00 NSArray *alarms; NSUInteger count, max; iCalAlarm *currentAlarm; - NSString *action; + NSString *action, *webstatus; alarms = [event alarms]; max = [alarms count]; for (count = 0; !alarm && count < max; count++) { currentAlarm = [alarms objectAtIndex: count]; - action = [[currentAlarm action] lowercaseString]; - if (!action || [action isEqualToString: @"display"]) - ASSIGN (alarm, currentAlarm); + + // Only handle 'display' alarms + action = [currentAlarm action]; + if ([action caseInsensitiveCompare: @"display"] != NSOrderedSame) + continue; + + // Ignore alarms already triggered + webstatus = [[currentAlarm trigger] value: 0 ofAttribute: @"x-webstatus"]; + if ([webstatus caseInsensitiveCompare: @"triggered"] == NSOrderedSame) + continue; + + ASSIGN (alarm, currentAlarm); } alarmSet = YES; diff --git a/OpenChange/MAPIStoreCalendarFolder.m b/OpenChange/MAPIStoreCalendarFolder.m index 8c0830683..5f57c0560 100644 --- a/OpenChange/MAPIStoreCalendarFolder.m +++ b/OpenChange/MAPIStoreCalendarFolder.m @@ -100,6 +100,14 @@ [roles addObject: SOGoCalendarRole_PrivateViewer]; [roles addObject: SOGoCalendarRole_ConfidentialViewer]; } + if (rights & RightsFreeBusySimple) + { + [roles addObject: SOGoCalendarRole_PublicDAndTViewer]; + } + if (rights & RightsFreeBusyDetailed) + { + [roles addObject: SOGoCalendarRole_ConfidentialDAndTViewer]; + } // [self logWithFormat: @"roles for rights %.8x = (%@)", rights, roles]; @@ -121,12 +129,21 @@ else if ([roles containsObject: SOGoCalendarRole_PublicViewer] && [roles containsObject: SOGoCalendarRole_PrivateViewer] && [roles containsObject: SOGoCalendarRole_ConfidentialViewer]) - rights |= RightsReadItems | 0x1800; + // We have to set by hand other rights as only the highest role is returned + // See SOGoAppointmentFolder.m:aclsForUser for details + rights |= RightsReadItems | RightsFreeBusySimple | RightsFreeBusyDetailed; + + if ([roles containsObject: SOGoCalendarRole_PublicDAndTViewer]) + rights |= RightsFreeBusySimple; + + if ([roles containsObject: SOGoCalendarRole_ConfidentialDAndTViewer]) + rights |= RightsFreeBusyDetailed; + if (rights != 0) rights |= RoleNone; /* actually "folder visible" */ // [self logWithFormat: @"rights for roles (%@) = %.8x", roles, rights]; - + return rights; } @@ -169,6 +186,14 @@ [(SOGoAppointmentFolder *) sogoObject aclSQLListingFilter]]; } +- (int) getPidTagContainerClass: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + *data = [@"IPF.Appointment" asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + - (int) getPidTagDefaultPostMessageClass: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { diff --git a/OpenChange/MAPIStoreCalendarMessage.m b/OpenChange/MAPIStoreCalendarMessage.m index de89eb487..f5d00e8d4 100644 --- a/OpenChange/MAPIStoreCalendarMessage.m +++ b/OpenChange/MAPIStoreCalendarMessage.m @@ -81,7 +81,7 @@ // extern void ndr_print_AppointmentRecurrencePattern(struct ndr_print *ndr, const char *name, const struct AppointmentRecurrencePattern *r); -static Class NSArrayK; +static Class NSArrayK, MAPIStoreAppointmentWrapperK; @implementation SOGoAppointmentObject (MAPIStoreExtension) @@ -97,6 +97,7 @@ static Class NSArrayK; + (void) initialize { NSArrayK = [NSArray class]; + MAPIStoreAppointmentWrapperK = [MAPIStoreAppointmentWrapper class]; } + (enum mapistore_error) getAvailableProperties: (struct SPropTagArray **) propertiesP @@ -180,6 +181,12 @@ static Class NSArrayK; else { origCalendar = [sogoObject calendar: YES secure: YES]; + if (!origCalendar) + { + [self errorWithFormat: @"Incorrect calendar event %@. Empty message is created", + [self url]]; + return self; + } calendar = [origCalendar mutableCopy]; masterEvent = [[calendar events] objectAtIndex: 0]; [self _setupAttachmentParts]; @@ -205,13 +212,23 @@ static Class NSArrayK; [super dealloc]; } -/* getters */ -- (int) getPidLidFInvited: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx +- (MAPIStoreAppointmentWrapper *) _appointmentWrapper { - return [self getYes: data inMemCtx: memCtx]; + NSUInteger i, max; + id proxy; + max = [proxies count]; + for (i = 0; i < max; i++) { + proxy = [proxies objectAtIndex: i]; + if ([proxy isKindOfClass: MAPIStoreAppointmentWrapperK]) + { + return proxy; + } + } + return nil; } + +/* getters */ - (int) getPidTagMessageClass: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -324,100 +341,63 @@ static Class NSArrayK; } else { - /* HACK: we know the first (and only) proxy is our appointment wrapper - instance, but this might not always be true */ - [[proxies objectAtIndex: 0] fillMessageData: msgData - inMemCtx: memCtx]; + [[self _appointmentWrapper] fillMessageData: msgData + inMemCtx: memCtx]; } *dataPtr = msgData; } -/* sender representing */ -// - (int) getPidTagSentRepresentingEmailAddress: (void **) data -// inMemCtx: (TALLOC_CTX *) memCtx -// { -// return [self getPidTagSenderEmailAddress: data inMemCtx: memCtx]; -// } - -// - (int) getPidTagSentRepresentingAddressType: (void **) data -// inMemCtx: (TALLOC_CTX *) memCtx -// { -// return [self getSMTPAddrType: data inMemCtx: memCtx]; -// } - -// - (int) getPidTagSentRepresentingName: (void **) data -// inMemCtx: (TALLOC_CTX *) memCtx -// { -// return [self getPidTagSenderName: data inMemCtx: memCtx]; -// } - -// - (int) getPidTagSentRepresentingEntryId: (void **) data -// inMemCtx: (TALLOC_CTX *) memCtx -// { -// return [self getPidTagSenderEntryId: data inMemCtx: memCtx]; -// } - -/* attendee */ -// - (int) getPidTagReceivedByAddressType: (void **) data -// inMemCtx: (TALLOC_CTX *) memCtx -// { -// return [appointmentWrapper getPidTagReceivedByAddressType: data -// inMemCtx: memCtx]; -// } - -// - (int) getPidTagReceivedByEmailAddress: (void **) data -// inMemCtx: (TALLOC_CTX *) memCtx -// { -// return [appointmentWrapper getPidTagReceivedByEmailAddress: data -// inMemCtx: memCtx]; -// } - -// - (int) getPidTagReceivedByName: (void **) data -// inMemCtx: (TALLOC_CTX *) memCtx -// { -// return [appointmentWrapper getPidTagReceivedByName: data -// inMemCtx: memCtx]; -// } - -// - (int) getPidTagReceivedByEntryId: (void **) data -// inMemCtx: (TALLOC_CTX *) memCtx -// { -// return [appointmentWrapper getPidTagReceivedByEntryId: data -// inMemCtx: memCtx]; -// } - -// /* attendee representing */ -// - (int) getPidTagReceivedRepresentingEmailAddress: (void **) data -// inMemCtx: (TALLOC_CTX *) memCtx -// { -// return [self getPidTagReceivedByEmailAddress: data inMemCtx: memCtx]; -// } - -// - (int) getPidTagReceivedRepresentingAddressType: (void **) data -// inMemCtx: (TALLOC_CTX *) memCtx -// { -// return [self getSMTPAddrType: data inMemCtx: memCtx]; -// } - -// - (int) getPidTagReceivedRepresentingName: (void **) data -// inMemCtx: (TALLOC_CTX *) memCtx -// { -// return [self getPidTagReceivedByName: data inMemCtx: memCtx]; -// } - -// - (int) getPidTagReceivedRepresentingEntryId: (void **) data -// inMemCtx: (TALLOC_CTX *) memCtx -// { -// return [self getPidTagReceivedByEntryId: data inMemCtx: memCtx]; -// } - - (int) getPidTagResponseRequested: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { return [self getYes: data inMemCtx: memCtx]; } +/* This three methods: getPidTagNormalizedSubject, + getPidTagSensitivity and getPidTagImportance are implemented in + MAPIStoreMessage base class, then the proxy method is not reached + (see MAPIStoreObject). +*/ +- (int) getPidTagNormalizedSubject: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + MAPIStoreAppointmentWrapper *appointmentWrapper; + + appointmentWrapper = [self _appointmentWrapper]; + if (appointmentWrapper) + return [appointmentWrapper getPidTagNormalizedSubject: data inMemCtx: memCtx]; + + return MAPISTORE_ERR_NOT_FOUND; +} + +- (int) getPidTagSensitivity: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + MAPIStoreAppointmentWrapper *appointmentWrapper; + + appointmentWrapper = [self _appointmentWrapper]; + if (appointmentWrapper) + return [appointmentWrapper getPidTagSensitivity: data inMemCtx: memCtx]; + + return [self getLongZero: data inMemCtx: memCtx]; +} + +- (int) getPidTagImportance: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + MAPIStoreAppointmentWrapper *appointmentWrapper; + + appointmentWrapper = [self _appointmentWrapper]; + if (appointmentWrapper) + return [appointmentWrapper getPidTagImportance: data inMemCtx: memCtx]; + + *data = MAPILongValue (memCtx, 1); + + return MAPISTORE_SUCCESS; +} + + - (NSString *) _uidFromGlobalObjectId: (TALLOC_CTX *) memCtx { NSData *objectId; diff --git a/OpenChange/MAPIStoreContactsMessage.m b/OpenChange/MAPIStoreContactsMessage.m index 3b35f9c34..cccb495e1 100644 --- a/OpenChange/MAPIStoreContactsMessage.m +++ b/OpenChange/MAPIStoreContactsMessage.m @@ -170,6 +170,21 @@ return MAPISTORE_SUCCESS; } +- (int) getPidTagProfession: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + int rc = MAPISTORE_SUCCESS; + + stringValue = [[sogoObject vCard] role]; + if (stringValue) + *data = [stringValue asUnicodeInMemCtx: memCtx]; + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + - (int) getPidTagCompanyName: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -723,6 +738,107 @@ return rc; } +- (int) getPidTagWeddingAnniversary: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSCalendarDate *dateValue; + NSString *stringValue; + int rc = MAPISTORE_SUCCESS; + + stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-anniversary"] + flattenedValuesForKey: @""]; + if (stringValue && ! [stringValue isEqualToString: @""]) + { + dateValue = [NSCalendarDate dateWithString: stringValue + calendarFormat: @"%Y-%m-%d"]; + *data = [dateValue asFileTimeInMemCtx: memCtx]; + } + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + +- (int) getPidTagSpouseName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + int rc = MAPISTORE_SUCCESS; + + stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-spouse"] + flattenedValuesForKey: @""]; + if (stringValue) + *data = [stringValue asUnicodeInMemCtx: memCtx]; + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + +- (int) getPidTagManagerName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + int rc = MAPISTORE_SUCCESS; + + stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-manager"] + flattenedValuesForKey: @""]; + if (stringValue) + *data = [stringValue asUnicodeInMemCtx: memCtx]; + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + +- (int) getPidTagAssistant: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + int rc = MAPISTORE_SUCCESS; + + stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-assistant"] + flattenedValuesForKey: @""]; + if (stringValue) + *data = [stringValue asUnicodeInMemCtx: memCtx]; + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + +- (int) getPidTagOfficeLocation: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + int rc = MAPISTORE_SUCCESS; + + stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"x-ms-office"] + flattenedValuesForKey: @""]; + if (stringValue) + *data = [stringValue asUnicodeInMemCtx: memCtx]; + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + +- (int) getPidLidFreeBusyLocation: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + int rc = MAPISTORE_SUCCESS; + + stringValue = [[[sogoObject vCard] uniqueChildWithTag: @"fburl"] + flattenedValuesForKey: @""]; + if (stringValue) + *data = [stringValue asUnicodeInMemCtx: memCtx]; + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + // // Decomposed fullname getters // @@ -1133,12 +1249,18 @@ fromProperties: (NSDictionary *) attachmentProps // - // job title, nickname, company name, deparment, work url, im address/screen name and birthday + // job title, profession, nickname, company name, deparment, work url, im address/screen name and birthday // value = [properties objectForKey: MAPIPropertyKey(PR_TITLE_UNICODE)]; if (value) [newCard setTitle: value]; + value = [properties objectForKey: MAPIPropertyKey(PR_PROFESSION_UNICODE)]; + if (value) + { + [newCard setRole: value]; + } + value = [properties objectForKey: MAPIPropertyKey(PR_NICKNAME_UNICODE)]; if (value) [newCard setNickname: value]; @@ -1173,6 +1295,52 @@ fromProperties: (NSDictionary *) attachmentProps [newCard setBday: [value descriptionWithCalendarFormat: @"%Y-%m-%d"]]; } + // + // wedding anniversary, spouse's name, manager's name, assistant's name, office location, freebusy location + // + value = [properties objectForKey: MAPIPropertyKey(PidTagWeddingAnniversary)]; + if (value) + { + [[newCard uniqueChildWithTag: @"x-ms-anniversary"] + setSingleValue: [value descriptionWithCalendarFormat: @"%Y-%m-%d"] + forKey: @""]; + } + + value = [properties objectForKey: MAPIPropertyKey(PR_SPOUSE_NAME_UNICODE)]; + if (value) + { + [[newCard uniqueChildWithTag: @"x-ms-spouse"] + setSingleValue: value forKey: @""]; + } + + value = [properties objectForKey: MAPIPropertyKey(PR_MANAGER_NAME_UNICODE)]; + if (value) + { + [[newCard uniqueChildWithTag: @"x-ms-manager"] + setSingleValue: value forKey: @""]; + } + + value = [properties objectForKey: MAPIPropertyKey(PR_ASSISTANT_UNICODE)]; + if (value) + { + [[newCard uniqueChildWithTag: @"x-ms-assistant"] + setSingleValue: value forKey: @""]; + } + + value = [properties objectForKey: MAPIPropertyKey(PR_OFFICE_LOCATION_UNICODE)]; + if (value) + { + [[newCard uniqueChildWithTag: @"x-ms-office"] + setSingleValue: value forKey: @""]; + } + + value = [properties objectForKey: MAPIPropertyKey(PidLidFreeBusyLocation)]; + if (value) + { + [[newCard uniqueChildWithTag: @"fburl"] + setSingleValue: value forKey: @""]; + } + /* photo */ if ([attachmentParts count] > 0) { diff --git a/OpenChange/MAPIStoreContext.m b/OpenChange/MAPIStoreContext.m index 5b73173d8..fca045b17 100644 --- a/OpenChange/MAPIStoreContext.m +++ b/OpenChange/MAPIStoreContext.m @@ -51,6 +51,7 @@ #import "MAPIStoreContext.h" #undef DEBUG +#include #include #include #include @@ -276,6 +277,11 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri) { NSString *username; + if (newConnInfo == NULL) + { + return nil; + } + if ((self = [self init])) { ASSIGN (contextUrl, newUrl); @@ -367,8 +373,9 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri) NSString *objectURL, *url; // TDB_DATA key, dbuf; - url = [[contextUrl absoluteString] - stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; + url = [contextUrl absoluteString]; + // FIXME transform percent escapes but not for user part of the url + //stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; objectURL = [[userContext mapping] urlFromID: fmid]; if (objectURL) { @@ -549,7 +556,7 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri) { uint64_t newVersionNumber; - if (openchangedb_get_new_changeNumber (connInfo->oc_ctx, &newVersionNumber) + if (openchangedb_get_new_changeNumber (connInfo->oc_ctx, connInfo->username, &newVersionNumber) != MAPI_E_SUCCESS) abort (); @@ -566,9 +573,9 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri) memCtx = talloc_zero(NULL, TALLOC_CTX); newChangeNumbers = [NSMutableArray arrayWithCapacity: max]; - + if (openchangedb_get_new_changeNumbers (connInfo->oc_ctx, - memCtx, max, &numbers) + memCtx, connInfo->username, max, &numbers) != MAPI_E_SUCCESS || numbers->cValues != max) abort (); for (count = 0; count < max; count++) diff --git a/OpenChange/MAPIStoreDBFolder.m b/OpenChange/MAPIStoreDBFolder.m index f9b2e128d..4dd7fbfd9 100644 --- a/OpenChange/MAPIStoreDBFolder.m +++ b/OpenChange/MAPIStoreDBFolder.m @@ -138,7 +138,9 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; MAPIStoreMapping *mapping; NSRange slashRange; - if (isMove && [targetFolder isKindOfClass: MAPIStoreDBFolderK]) + pathComponent = nil; + + if (isMove && ([targetFolder isKindOfClass: MAPIStoreDBFolderK] || !targetFolder)) { path = [sogoObject path]; slashRange = [path rangeOfString: @"/" options: NSBackwardsSearch]; @@ -147,14 +149,28 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; format: @"db folder path must start with a '/'"]; else pathComponent = [path substringFromIndex: slashRange.location + 1]; - targetPath = [[targetFolder sogoObject] path]; - newPath = [NSString stringWithFormat: @"%@/%@", - targetPath, pathComponent]; - [dbFolder changePathTo: newPath]; + + if (targetFolder) + { + targetPath = [[targetFolder sogoObject] path]; + newPath = [NSString stringWithFormat: @"%@/%@", + targetPath, pathComponent]; + [dbFolder changePathTo: newPath + intoNewContainer: [targetFolder dbFolder]]; + } + else + [dbFolder changePathTo: [NSString stringWithFormat: @"/fallback/%@", pathComponent] + intoNewContainer: nil]; mapping = [self mapping]; - newURL = [NSString stringWithFormat: @"%@%@/", - [targetFolder url], pathComponent]; + + if (targetFolder) + newURL = [NSString stringWithFormat: @"%@%@/", + [targetFolder url], pathComponent]; + else + newURL = [NSString stringWithFormat: @"sogo://%@@fallback/%@/", + [[self userContext] username], pathComponent]; + [mapping updateID: [self objectId] withURL: newURL]; diff --git a/OpenChange/MAPIStoreDBMessage.m b/OpenChange/MAPIStoreDBMessage.m index 074c205ea..077f2c708 100644 --- a/OpenChange/MAPIStoreDBMessage.m +++ b/OpenChange/MAPIStoreDBMessage.m @@ -50,19 +50,22 @@ NSUInteger count; enum MAPITAGS faiProperties[] = { 0x68350102, 0x683c0102, 0x683e0102, 0x683f0102, 0x68410003, 0x68420102, - 0x68450102, 0x68460003 }; + 0x68450102, 0x68460003, + // PR_VD_NAME_W, PR_VD_FLAGS, PR_VD_VERSION, PR_VIEW_CLSID + 0x7006001F, 0x70030003, 0x70070003, 0x68330048 }; + size_t faiSize = sizeof(faiProperties) / sizeof(enum MAPITAGS); properties = talloc_zero (memCtx, struct SPropTagArray); - properties->cValues = MAPIStoreSupportedPropertiesCount + 8; + properties->cValues = MAPIStoreSupportedPropertiesCount + faiSize; properties->aulPropTag = talloc_array (properties, enum MAPITAGS, - MAPIStoreSupportedPropertiesCount + 8); + MAPIStoreSupportedPropertiesCount + faiSize); for (count = 0; count < MAPIStoreSupportedPropertiesCount; count++) properties->aulPropTag[count] = MAPIStoreSupportedProperties[count]; /* FIXME (hack): append a few undocumented properties that can be added to FAI messages */ - for (count = 0; count < 8; count++) + for (count = 0; count < faiSize; count++) properties->aulPropTag[MAPIStoreSupportedPropertiesCount+count] = faiProperties[count]; diff --git a/OpenChange/MAPIStoreFallbackContext.m b/OpenChange/MAPIStoreFallbackContext.m index 41463542a..1555226d5 100644 --- a/OpenChange/MAPIStoreFallbackContext.m +++ b/OpenChange/MAPIStoreFallbackContext.m @@ -30,6 +30,7 @@ #undef DEBUG #include +#include #include @implementation MAPIStoreFallbackContext @@ -55,7 +56,10 @@ NSString *baseURL, *url, *name; MAPIStoreUserContext *userContext; - baseURL = [NSString stringWithFormat: @"sogo://%@@fallback/", userName]; + baseURL = [NSString stringWithFormat: @"sogo://%@@fallback/", + [userName stringByReplacingOccurrencesOfString: @"@" + withString: @"%40"]]; + context = talloc_zero (memCtx, struct mapistore_contexts_list); context->url = [baseURL asUnicodeInMemCtx: context]; @@ -99,7 +103,10 @@ forUser: (NSString *) userName { return [NSString stringWithFormat: @"sogo://%@@fallback/0x%.16"PRIx64"/", - userName, (unsigned long long) fid]; + [userName stringByReplacingOccurrencesOfString: @"@" + withString: @"%40"], + (unsigned long long) fid]; + } @end diff --git a/OpenChange/MAPIStoreFolder.h b/OpenChange/MAPIStoreFolder.h index 94a1cb634..e5440bd95 100644 --- a/OpenChange/MAPIStoreFolder.h +++ b/OpenChange/MAPIStoreFolder.h @@ -150,6 +150,7 @@ /* helpers */ - (uint64_t) idForObjectWithKey: (NSString *) childKey; +- (MAPIStoreFolder *) rootContainer; /* subclasses */ - (MAPIStoreMessage *) createMessage; diff --git a/OpenChange/MAPIStoreFolder.m b/OpenChange/MAPIStoreFolder.m index a0455efb6..0d862b85e 100644 --- a/OpenChange/MAPIStoreFolder.m +++ b/OpenChange/MAPIStoreFolder.m @@ -29,6 +29,7 @@ #import #import #import +#import #import #import #import @@ -52,6 +53,7 @@ #import #import "SOGoMAPIDBMessage.h" #import "SOGoCacheGCSObject+MAPIStore.h" +#import #include @@ -100,6 +102,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe NSUInteger lastPartIdx; MAPIStoreUserContext *userContext; + parts = 0; + lastPartIdx = 0; folderURL = [NSURL URLWithString: [self url]]; /* note: -[NSURL path] returns an unescaped representation */ path = [folderURL path]; @@ -231,9 +235,15 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe SOGoFolder *sogoFolder; WOContext *woContext; + childFolder = nil; if ([[self folderKeys] containsObject: folderKey]) { woContext = [[self userContext] woContext]; + /* We activate the user for the context using the root folder + context as there are times where the active user is not + matching with the one stored in the application context + and SOGo object is storing cached data with the wrong user */ + [[self userContext] activateWithUser: [woContext activeUser]]; sogoFolder = [sogoObject lookupName: folderKey inContext: woContext acquire: NO]; if (sogoFolder && ![sogoFolder isKindOfClass: NSExceptionK]) @@ -278,6 +288,12 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe msgObject = [sogoObject lookupName: messageKey inContext: nil acquire: NO]; + /* If the lookup in the indexing table works, but the IMAP does + not have the message, then the message does not exist in this + folder */ + if (msgObject && [msgObject isKindOfClass: [SOGoMailObject class]] + && ! [(SOGoMailObject *)msgObject doesMailExist]) + return nil; if (msgObject && ![msgObject isKindOfClass: NSExceptionK]) { [msgObject setContext: [[self userContext] woContext]]; @@ -433,16 +449,11 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe return rc; } -- (void) deleteFolderImpl +- (int) deleteFolder { // TODO: raise exception in case underlying delete fails? // [propsMessage delete]; [dbFolder delete]; -} - -- (int) deleteFolder -{ - [self deleteFolderImpl]; [self cleanupCaches]; @@ -503,6 +514,12 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe else rc = MAPISTORE_ERR_DENIED; } + else + { + /* Unregistering from indexing table as the backend says the + object was not found */ + [mapping unregisterURLWithID: mid]; + } } return rc; @@ -568,8 +585,10 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe struct mapistore_object_notification_parameters *notif_parameters; int rc; + /* flags that control the behaviour of the operation + (MAPISTORE_SOFT_DELETE or MAPISTORE_PERMANENT_DELETE) */ [self logWithFormat: @"-deleteMessageWithMID: mid: 0x%.16llx flags: %d", mid, flags]; - + mapping = [self mapping]; childURL = [mapping urlFromID: mid]; if (childURL) @@ -646,7 +665,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe notifyChangesForChild: message]; } [self logWithFormat: @"successfully deleted object at URL: %@", childURL]; - [mapping unregisterURLWithID: mid]; + /* Ensure we are respecting flags parameter */ + [mapping unregisterURLWithID: mid andFlags: flags]; [self cleanupCaches]; rc = MAPISTORE_SUCCESS; } @@ -676,6 +696,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe //TALLOC_CTX *memCtx; struct SRow aRow; struct SPropValue property; + uint8_t deleteFlags; [self logWithFormat: @"-moveCopyMessageWithMID: 0x%.16llx .. withMID: 0x%.16llx .. wantCopy: %d", srcMid, targetMid, wantCopy]; @@ -706,7 +727,9 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe } [destMsg save: memCtx]; if (!wantCopy) - rc = [sourceFolder deleteMessageWithMID: srcMid andFlags: 0]; + /* We want to keep mid for restoring/shared data to work if mids are different. */ + deleteFlags = (srcMid == targetMid) ? MAPISTORE_PERMANENT_DELETE : MAPISTORE_SOFT_DELETE; + rc = [sourceFolder deleteMessageWithMID: srcMid andFlags: deleteFlags]; end: //talloc_free (memCtx); @@ -845,7 +868,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe if (isMove) { fmid = [mapping idFromURL: [message url]]; - [self deleteMessageWithMID: fmid andFlags: 0]; + [self deleteMessageWithMID: fmid andFlags: MAPISTORE_PERMANENT_DELETE]; [mapping registerURL: [targetMessage url] withID: fmid]; } @@ -866,7 +889,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe if (isMove) { fmid = [mapping idFromURL: [message url]]; - [self deleteMessageWithMID: fmid andFlags: 0]; + [self deleteMessageWithMID: fmid andFlags: MAPISTORE_PERMANENT_DELETE]; [mapping registerURL: [targetMessage url] withID: fmid]; } @@ -893,16 +916,23 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe } if (isMove) - { - fmid = [mapping idFromURL: [self url]]; - [mapping unregisterURLWithID: fmid]; - [self deleteFolderImpl]; - [mapping registerURL: [newFolder url] - withID: fmid]; - } + [self deleteFolder]; + [targetFolder cleanupCaches]; } [self cleanupCaches]; + + /* We perform the mapping operations at the + end as objectId is required to be available + until the caches are cleaned up */ + if (isMove && rc == MAPISTORE_SUCCESS) + { + fmid = [mapping idFromURL: [self url]]; + [mapping unregisterURLWithID: fmid]; + [mapping registerURL: [newFolder url] + withID: fmid]; + } + } else rc = MAPISTORE_ERR_DENIED; @@ -946,6 +976,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe NSString *baseURL, *URL, *key; NSArray *newIDs; uint64_t idNbr; + bool softDeleted; baseURL = [self url]; @@ -956,8 +987,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe { key = [keys objectAtIndex: count]; URL = [NSString stringWithFormat: @"%@%@", baseURL, key]; - idNbr = [mapping idFromURL: URL]; - if (idNbr == NSNotFound) + idNbr = [mapping idFromURL: URL isSoftDeleted: &softDeleted]; + if (idNbr == NSNotFound && !softDeleted) [missingURLs addObject: URL]; } @@ -1088,6 +1119,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe MAPIStoreMapping *mapping; struct UI8Array_r *fmids; uint64_t fmid; + bool softDeleted; keys = [self getDeletedKeysFromChangeNumber: changeNum andCN: &cnNbr inTableType: tableType]; @@ -1114,10 +1146,10 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe { url = [NSString stringWithFormat: format, baseURL, [keys objectAtIndex: count]]; - fmid = [mapping idFromURL: url]; + fmid = [mapping idFromURL: url isSoftDeleted: &softDeleted]; if (fmid != NSNotFound) /* if no fmid is returned, then the object "never existed" in the OpenChange - databases */ + databases. Soft-deleted messages are returned back */ { fmids->lpui8[fmids->cValues] = fmid; fmids->cValues++; @@ -1713,10 +1745,12 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe aclFolder = [self aclFolder]; - users = [aclFolder aclUsers]; + users = [[aclFolder aclUsers] copy]; max = [users count]; for (count = 0; count < max; count++) [aclFolder removeUserFromAcls: [users objectAtIndex: count]]; + + [users release]; } - (int) modifyPermissions: (struct PermissionData *) permissions @@ -1864,6 +1898,17 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe inFolderURL: [self url]]; } +- (MAPIStoreFolder *) rootContainer +{ + /* Return the oldest ancestor, which does not have + container. If there is not container, it returns itself. + */ + if (container) + return [container rootContainer]; + else + return self; +} + - (NSDate *) creationTime { return [dbFolder creationDate]; diff --git a/OpenChange/MAPIStoreGCSBaseContext.m b/OpenChange/MAPIStoreGCSBaseContext.m index 216eab01c..384859c5d 100644 --- a/OpenChange/MAPIStoreGCSBaseContext.m +++ b/OpenChange/MAPIStoreGCSBaseContext.m @@ -82,7 +82,9 @@ andTDBIndexing: indexing]; parentFolder = [[userContext rootFolders] objectForKey: moduleName]; baseUrl = [NSString stringWithFormat: @"sogo://%@@%@/", - userName, moduleName]; + [userName stringByReplacingOccurrencesOfString: @"@" + withString: @"%40"], + moduleName]; subfolders = [parentFolder subFolders]; max = [subfolders count]; @@ -127,7 +129,9 @@ if (![parentFolder newFolderWithName: folderName nameInContainer: &nameInContainer]) mapistoreURI = [NSString stringWithFormat: @"sogo://%@@%@/%@/", - userName, moduleName, nameInContainer]; + [userName stringByReplacingOccurrencesOfString: @"@" + withString: @"%40"], + moduleName, nameInContainer]; else mapistoreURI = nil; [MAPIApp setUserContext: nil]; diff --git a/OpenChange/MAPIStoreMailAttachment.m b/OpenChange/MAPIStoreMailAttachment.m index e21fc7925..388ad924d 100644 --- a/OpenChange/MAPIStoreMailAttachment.m +++ b/OpenChange/MAPIStoreMailAttachment.m @@ -163,8 +163,15 @@ - (int) getPidTagDisplayName: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - *data = [[bodyInfo objectForKey: @"description"] - asUnicodeInMemCtx: memCtx]; + NSString *fileName; + + fileName = [self _fileName]; + if ([fileName isEqualToString: @"sharing_metadata.xml"]) + /* Required to disallow user from seeing the attachment by default */ + *data = [@"sharing_metadata.xml" asUnicodeInMemCtx: memCtx]; + else + *data = [[bodyInfo objectForKey: @"description"] + asUnicodeInMemCtx: memCtx]; return MAPISTORE_SUCCESS; } @@ -181,11 +188,17 @@ - (int) getPidTagAttachMimeTag: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - NSString *mimeTag; + NSString *mimeTag, *fileName; + + fileName = [self _fileName]; + if ([fileName isEqualToString: @"sharing_metadata.xml"]) + /* Required by [MS-OXWSMSHR] Section 3.1.1 */ + mimeTag = [NSString stringWithFormat: @"application/x-sharing-metadata-xml"]; + else + mimeTag = [NSString stringWithFormat: @"%@/%@", + [bodyInfo objectForKey: @"type"], + [bodyInfo objectForKey: @"subtype"]]; - mimeTag = [NSString stringWithFormat: @"%@/%@", - [bodyInfo objectForKey: @"type"], - [bodyInfo objectForKey: @"subtype"]]; *data = [[mimeTag lowercaseString] asUnicodeInMemCtx: memCtx]; return MAPISTORE_SUCCESS; diff --git a/OpenChange/MAPIStoreMailContext.m b/OpenChange/MAPIStoreMailContext.m index 24d974e5a..87493ff09 100644 --- a/OpenChange/MAPIStoreMailContext.m +++ b/OpenChange/MAPIStoreMailContext.m @@ -87,7 +87,7 @@ MakeDisplayFolderName (NSString *folderName) inMemCtx: (TALLOC_CTX *) memCtx { struct mapistore_contexts_list *firstContext = NULL, *context; - NSString *urlBase, *stringData, *currentName, *inboxName, *draftsName, *sentName, *trashName; + NSString *urlBase, *stringData, *currentName, *realName, *inboxName, *draftsName, *sentName, *trashName; NSArray *unprefixedFolders; NSMutableArray *secondaryFolders; enum mapistore_context_role role[] = {MAPISTORE_MAIL_ROLE, @@ -126,7 +126,11 @@ MakeDisplayFolderName (NSString *folderName) trashName = [NSString stringWithFormat: @"folder%@", [unprefixedFolders componentsJoinedByString: @"/folder"]]; - urlBase = [NSString stringWithFormat: @"sogo://%@:%@@mail/", userName, userName]; + urlBase = [NSString stringWithFormat: @"sogo://%@:%@@mail/", + [userName stringByReplacingOccurrencesOfString: @"@" + withString: @"%40"], + [userName stringByReplacingOccurrencesOfString: @"@" + withString: @"%40"]]; for (count = 0; count < 3; count++) { context = talloc_zero (memCtx, struct mapistore_contexts_list); @@ -153,12 +157,23 @@ MakeDisplayFolderName (NSString *folderName) for (count = 0; count < max; count++) { context = talloc_zero (memCtx, struct mapistore_contexts_list); + // secondaryFolders has the names (1) Imap4Encoded and (2) asCSSIdentifier + // e.g.: Probl&AOg-mes_SP_de_SP_synchronisation currentName = [secondaryFolders objectAtIndex: count]; - stringData = [NSString stringWithFormat: @"%@%@", - urlBase, [currentName stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]]; - context->url = [stringData asUnicodeInMemCtx: context]; - stringData = [[[currentName substringFromIndex: 6] fromCSSIdentifier] stringByDecodingImap4FolderName]; - context->name = [stringData asUnicodeInMemCtx: context]; + // To get the real name we have to revert that (applying the decode functions) + // in reverse order + // e.g.: Problèmes de synchronisation + realName = [[currentName fromCSSIdentifier] + stringByDecodingImap4FolderName]; + // And finally to represent that as URI we have to (1) asCSSIdentifier, + // (2) Imap4Encode and (3) AddPercentEscapes + // e.g.: Probl&AOg-mes_SP_de_SP_synchronisation + // In the example there are no percent escapes added because is already ok + stringData = [[[realName asCSSIdentifier] + stringByEncodingImap4FolderName] + stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; + context->url = [[NSString stringWithFormat: @"%@%@", urlBase, stringData] asUnicodeInMemCtx: context]; + context->name = [[realName substringFromIndex: 6] asUnicodeInMemCtx: context]; context->main_folder = false; context->role = MAPISTORE_MAIL_ROLE; context->tag = "tag"; @@ -188,7 +203,10 @@ MakeDisplayFolderName (NSString *folderName) inContainer: accountFolder]; if ([newFolder create]) mapistoreURI = [NSString stringWithFormat: @"sogo://%@:%@@mail/%@/", - userName, userName, + [userName stringByReplacingOccurrencesOfString: @"@" + withString: @"%40"], + [userName stringByReplacingOccurrencesOfString: @"@" + withString: @"%40"], [[folderName stringByEncodingImap4FolderName] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]]; else mapistoreURI = nil; @@ -264,8 +282,12 @@ MakeDisplayFolderName (NSString *folderName) componentsSeparatedByString: @"/"]; folderName = [NSString stringWithFormat: @"folder%@", [unprefixedFolders componentsJoinedByString: @"/folder"]]; - url = [NSString stringWithFormat: @"sogo://%@:%@@outbox/%@", userName, - userName, folderName]; + url = [NSString stringWithFormat: @"sogo://%@:%@@outbox/%@", + [userName stringByReplacingOccurrencesOfString: @"@" + withString: @"%40"], + [userName stringByReplacingOccurrencesOfString: @"@" + withString: @"%40"], + folderName]; context = talloc_zero (memCtx, struct mapistore_contexts_list); context->url = [url asUnicodeInMemCtx: context]; diff --git a/OpenChange/MAPIStoreMailFolder.h b/OpenChange/MAPIStoreMailFolder.h index d7ec7aaaf..c56e74862 100644 --- a/OpenChange/MAPIStoreMailFolder.h +++ b/OpenChange/MAPIStoreMailFolder.h @@ -44,6 +44,9 @@ /* synchronisation & versioning */ - (BOOL) synchroniseCache; +- (void) synchronizeUpdatedFolder: (NSNumber *) lastModseq + withMapping: (NSMutableDictionary *) mapping; +- (BOOL) synchroniseCacheForUID: (NSString *) messageUID; - (NSNumber *) modseqFromMessageChangeNumber: (NSString *) changeNum; - (NSString *) messageUIDFromMessageKey: (NSString *) messageKey; - (NSString *) changeNumberForMessageUID: (NSString *) messageUid; @@ -52,6 +55,11 @@ - (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey; - (NSData *) predecessorChangeListForMessageWithKey: (NSString *) messageKey; +/* Extra properties from mail messages that already hit the server */ +- (void) setExtraProperties: (NSDictionary *) props + forMessage: (NSString *) messageKey; +- (NSDictionary *) extraPropertiesForMessage: (NSString *) messageKey; + @end /* MAPIStoreOutboxFolder is a special subclass of MAPIStoreMailFolder where diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index f0912d8f0..07d1debe2 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -70,6 +70,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK; #undef DEBUG #include #include +#include #include #include @@ -163,6 +164,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK; int i; nameInContainer = nil; + rc = MAPISTORE_ERROR; folderName = nil; for (i = 0; !folderName && i < aRow->cValues; i++) @@ -177,6 +179,12 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK; { nameInContainer = [NSString stringWithFormat: @"folder%@", [[folderName stringByEncodingImap4FolderName] asCSSIdentifier]]; + + /* it may be the operation is interleaved with operations + from other users having cached information in the thread + with the other user, so it'd better activate the user again here... */ + [[self userContext] activateWithUser: [[[self userContext] woContext] activeUser]]; + newFolder = [SOGoMailFolderK objectWithName: nameInContainer inContainer: sogoObject]; if ([newFolder create]) @@ -511,7 +519,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) *nextModseq; NSString *changeNumber, *uid, *messageKey; uint64_t lastModseqNbr; - EOQualifier *kvQualifier, *searchQualifier; + EOQualifier *searchQualifier; NSArray *uids, *changeNumbers; NSUInteger count, max; NSArray *fetchResults; @@ -552,14 +560,11 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) { lastModseqNbr = [lastModseq unsignedLongLongValue]; nextModseq = [NSNumber numberWithUnsignedLongLong: lastModseqNbr + 1]; - kvQualifier = [[EOKeyValueQualifier alloc] + searchQualifier = [[EOKeyValueQualifier alloc] initWithKey: @"modseq" operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo value: nextModseq]; - searchQualifier = [[EOAndQualifier alloc] - initWithQualifiers: - kvQualifier, [self nonDeletedQualifier], nil]; - [kvQualifier release]; + [searchQualifier autorelease]; } else @@ -587,7 +592,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) fetchResults = [(NSDictionary *) [sogoObject fetchUIDs: uids - parts: [NSArray arrayWithObject: @"modseq"]] + parts: [NSArray arrayWithObjects: @"modseq", @"flags", nil]] objectForKey: @"fetch"]; /* NOTE: we sort items manually because Cyrus does not properly sort @@ -623,58 +628,230 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) if (!lastModseq || ([lastModseq compare: modseq] == NSOrderedAscending)) lastModseq = modseq; + + if ([[result objectForKey: @"flags"] containsObject: @"deleted"]) + [currentProperties setObject: changeNumber + forKey: @"SyncLastDeleteChangeNumber"]; } [currentProperties setObject: lastModseq forKey: @"SyncLastModseq"]; foundChange = YES; } - /* 2. we synchronise deleted UIDs */ + /* 2. we synchronise expunged UIDs */ if (initialLastModseq) { fetchResults = [(SOGoMailFolder *) sogoObject fetchUIDsOfVanishedItems: lastModseqNbr]; + max = [fetchResults count]; - changeNumbers = [[self context] getNewChangeNumbers: max]; + changeNumber = nil; for (count = 0; count < max; count++) { - uid = [fetchResults objectAtIndex: count]; + uid = [[fetchResults objectAtIndex: count] stringValue]; if ([messages objectForKey: uid]) { - newChangeNum = [[changeNumbers objectAtIndex: count] - unsignedLongLongValue]; - changeNumber = [NSString stringWithUnsignedLongLong: newChangeNum]; + if (!changeNumber) + { + newChangeNum = [[self context] getNewChangeNumber]; + changeNumber = [NSString stringWithUnsignedLongLong: newChangeNum]; + } [messages removeObjectForKey: uid]; - [self logWithFormat: @"removed message entry for uid %@", uid]; + [self logWithFormat: @"Removed message entry for UID %@", uid]; + } + else + { + [self logWithFormat:@"Message entry not found for UID %@", uid]; } } if (changeNumber) { [currentProperties setObject: changeNumber forKey: @"SyncLastDeleteChangeNumber"]; + [mapping setObject: lastModseq forKey: changeNumber]; foundChange = YES; } } if (foundChange) { + [self synchronizeUpdatedFolder: lastModseq + withMapping: mapping]; + ti = [NSNumber numberWithDouble: [now timeIntervalSince1970]]; [currentProperties setObject: ti forKey: @"SyncLastSynchronisationDate"]; [versionsMessage save]; } + return rc; } - + +- (void) synchronizeUpdatedFolder: (NSNumber *) lastModseq + withMapping: (NSMutableDictionary *) mapping +{ + /* This method should be called whenever something has changed on the folder. + Then we will perform two actions: + 1 - Update the PidTagChangeNumber property of the root container. + 2 - Store relationship PidTagChangenumber with lastModseq value on the + mapping given as parameter for this folder */ + uint64_t *current_cn; + struct SRow row; + struct SPropValue prop; + uint64_t fid; + const char *username; + struct openchangedb_context *oc_ctx; + enum MAPISTATUS retval; + TALLOC_CTX *local_mem_ctx = NULL; + + row.cValues = 1; + prop.ulPropTag = PidTagChangeNumber; + prop.value.d = 0; // It doesn't matter, it will be autogenerated + row.lpProps = ∝ + + /* We are doing a "touch" operation to update change number of the root container. + We get the root container as it has the properties in the OpenChange DB */ + username = [[self context] connectionInfo]->username; + oc_ctx = [[self context] connectionInfo]->oc_ctx; + fid = [[self rootContainer] objectId]; + retval = openchangedb_set_folder_properties(oc_ctx, username, fid, &row); + if (retval != MAPI_E_SUCCESS) + { + [self errorWithFormat:@"%s: Error setting change number on %"PRIu64, + __PRETTY_FUNCTION__, fid]; + return; + } + + local_mem_ctx = talloc_named(NULL, 0, __PRETTY_FUNCTION__); + if (local_mem_ctx == NULL) + { + [self errorWithFormat:@"%s: Error with talloc_named, out of memory?", + __PRETTY_FUNCTION__]; + return; + } + retval = openchangedb_get_folder_property(local_mem_ctx, oc_ctx, username, + PidTagChangeNumber, fid, + (void **) ¤t_cn); + if (retval != MAPI_E_SUCCESS) + { + [self errorWithFormat:@"%s: Error getting change number on %"PRIu64, + __PRETTY_FUNCTION__, fid]; + talloc_free(local_mem_ctx); + return; + } + + [mapping setObject: lastModseq + forKey: [NSString stringWithUnsignedLongLong: *current_cn]]; + talloc_free(local_mem_ctx); +} + +- (BOOL) synchroniseCacheForUID: (NSString *) messageUID +{ + /* Try to synchronise old UIDs in versions.plist cache using an + specific UID. It returns a boolean indicating if the + synchronisation were done. + + It should be used as last resort, keeping synchroniseCache to main + sync entry point. + */ + NSMutableDictionary *currentProperties, *messages, *messageEntry, *mapping; + NSArray *fetchResults; + uint64_t changeNumber; + NSDictionary *result; + NSNumber *modseq; + NSString *changeNumberStr; + NSData *changeKey; + + [versionsMessage reloadIfNeeded]; + currentProperties = [versionsMessage properties]; + messages = [currentProperties objectForKey: @"Messages"]; + messageEntry = [messages objectForKey: messageUID]; + if (!messageEntry) + { + fetchResults = [(NSDictionary *) [sogoObject fetchUIDs: [NSArray arrayWithObject: messageUID] + parts: [NSArray arrayWithObjects: @"modseq", @"flags", nil]] + objectForKey: @"fetch"]; + if ([fetchResults count] == 1) + { + result = [fetchResults objectAtIndex: 0]; + modseq = [result objectForKey: @"modseq"]; + changeNumber = [[self context] getNewChangeNumber]; + changeNumberStr = [NSString stringWithUnsignedLongLong: changeNumber]; + + /* Create new message entry in Messages dict */ + messageEntry = [NSMutableDictionary new]; + [messages setObject: messageEntry forKey: messageUID]; + [messageEntry release]; + + /* Store the modseq and change number */ + [messageEntry setObject: modseq forKey: @"modseq"]; + [messageEntry setObject: changeNumberStr forKey: @"version"]; + + /* Store the change key */ + changeKey = [self getReplicaKeyFromGlobCnt: changeNumber >> 16]; + [self _setChangeKey: changeKey forMessageEntry: messageEntry]; + + /* Store the changeNumber -> modseq mapping */ + mapping = [currentProperties objectForKey: @"VersionMapping"]; + [mapping setObject: modseq forKey: changeNumberStr]; + + /* Store the last deleted change number if it is soft-deleted */ + if ([[result objectForKey: @"flags"] containsObject: @"deleted"]) + [currentProperties setObject: changeNumberStr + forKey: @"SyncLastDeleteChangeNumber"]; + + /* Save the message */ + [versionsMessage save]; + return YES; + } + else + { + return NO; + } + } + /* If message entry exists, then synchroniseCache did its job */ + return YES; +} + + - (NSNumber *) modseqFromMessageChangeNumber: (NSString *) changeNum { NSDictionary *mapping; NSNumber *modseq; + NSEnumerator *enumerator; + id key; + uint64_t found, target, current, replica_id, current_cn; + NSString *closestChangeNum; mapping = [[versionsMessage properties] objectForKey: @"VersionMapping"]; modseq = [mapping objectForKey: changeNum]; + if (modseq) return modseq; + + // Not found from stored change numbers for this folder. + // Get the closest modseq for the change number given. + // O(n) cost but will be unusual behaviour. + target = exchange_globcnt([changeNum unsignedLongLongValue] >> 16); + replica_id = [changeNum unsignedLongLongValue] & 0xFFFF; + found = 0; + enumerator = [mapping keyEnumerator]; + while ((key = [enumerator nextObject])) + { + current_cn = [(NSString *)key unsignedLongLongValue]; + if ((current_cn & 0xFFFF) != replica_id) + continue; + current = exchange_globcnt(current_cn >> 16); + if (current < target && current > found) + found = current; + } + + if (found) + { + closestChangeNum = [NSString stringWithUnsignedLongLong: + (exchange_globcnt(found) << 16 | replica_id)]; + modseq = [mapping objectForKey: closestChangeNum]; + } return modseq; } @@ -688,7 +865,11 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) if (dotRange.location != NSNotFound) messageUid = [messageKey substringToIndex: dotRange.location]; else - messageUid = nil; + { + messageUid = nil; + [self errorWithFormat:@"%s: Unexpected messageKey value [%@]", + __PRETTY_FUNCTION__, messageKey]; + } return messageUid; } @@ -710,12 +891,24 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) { NSMutableDictionary *messages, *messageEntry; NSString *messageUid; + BOOL synced; messageUid = [self messageUIDFromMessageKey: messageKey]; messages = [[versionsMessage properties] objectForKey: @"Messages"]; messageEntry = [messages objectForKey: messageUid]; if (!messageEntry) - abort (); + { + [self warnWithFormat: @"attempting to synchronise to set the change key for " + @"this message %@", messageKey]; + synced = [self synchroniseCacheForUID: messageUid]; + if (synced) + messageEntry = [[[versionsMessage properties] objectForKey: @"Messages"] objectForKey: messageUid]; + if (!messageEntry) + { + [self errorWithFormat: @"still nothing. We crash!"]; + abort (); + } + } [self _setChangeKey: changeKey forMessageEntry: messageEntry]; [versionsMessage save]; @@ -784,6 +977,37 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) return list; } +/* Management for extra properties once they already hit the IMAP server */ +- (void) setExtraProperties: (NSDictionary *) props + forMessage: (NSString *) messageKey +{ + NSMutableDictionary *extraProps, *currentProperties; + NSString *messageUid; + + messageUid = [self messageUIDFromMessageKey: messageKey]; + currentProperties = [versionsMessage properties]; + extraProps = [currentProperties objectForKey: @"ExtraMessagesProperties"]; + if (!extraProps) + { + extraProps = [NSMutableDictionary new]; + [currentProperties setObject: extraProps forKey: @"ExtraMessagesProperties"]; + [extraProps release]; + } + + [extraProps setObject: props + forKey: messageUid]; + [versionsMessage save]; +} + +- (NSDictionary *) extraPropertiesForMessage: (NSString *) messageKey +{ + NSString *messageUid; + + messageUid = [self messageUIDFromMessageKey: messageKey]; + return [[[versionsMessage properties] objectForKey: @"ExtraMessagesProperties"] + objectForKey: messageUid]; +} + - (NSArray *) getDeletedKeysFromChangeNumber: (uint64_t) changeNum andCN: (NSNumber **) cnNbr inTableType: (uint8_t) tableType @@ -792,6 +1016,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) NSString *changeNumber; uint64_t modseq; NSDictionary *versionProperties; + EOQualifier *deletedQualifier, *kvQualifier, *searchQualifier; if (tableType == MAPISTORE_MESSAGE_TABLE) { @@ -800,8 +1025,33 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) unsignedLongLongValue]; if (modseq > 0) { + /* Hard deleted items */ deletedUIDs = [(SOGoMailFolder *) sogoObject fetchUIDsOfVanishedItems: modseq]; + + /* Soft deleted items */ + kvQualifier = [[EOKeyValueQualifier alloc] + initWithKey: @"modseq" + operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo + value: [NSNumber numberWithUnsignedLongLong: modseq]]; + deletedQualifier + = [[EOKeyValueQualifier alloc] + initWithKey: @"FLAGS" + operatorSelector: EOQualifierOperatorContains + value: [NSArray arrayWithObject: @"Deleted"]]; + + searchQualifier = [[EOAndQualifier alloc] + initWithQualifiers: + kvQualifier, deletedQualifier, nil]; + + deletedUIDs = [deletedUIDs arrayByAddingObjectsFromArray: + [sogoObject fetchUIDsMatchingQualifier: searchQualifier + sortOrdering: nil]]; + + [deletedQualifier release]; + [kvQualifier release]; + [searchQualifier release]; + deletedKeys = [deletedUIDs stringsWithFormat: @"%@.eml"]; if ([deletedUIDs count] > 0) { @@ -857,6 +1107,8 @@ _parseIMAPRange (const unichar *uniString, NSArray **UIDsP) uint32_t currentUid, rangeMin; BOOL done = NO, inRange = NO; + rangeMin = 0; + currentUid = 0; UIDs = [NSMutableArray array]; while (!done) { @@ -981,13 +1233,20 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP) if (![[result objectForKey: @"result"] boolValue]) return MAPISTORE_ERROR; - /* "Move" treatment: Store \Deleted and unregister urls */ + /* "Move" treatment: Store \Deleted and unregister urls as soft-deleted */ if (!wantCopy) { [client storeFlags: [NSArray arrayWithObject: @"Deleted"] forUIDs: uids addOrRemove: YES]; for (count = 0; count < midCount; count++) - [mapping unregisterURLWithID: srcMids[count]]; + { + /* Using soft-deleted to make deleted fmids to return the + srcMids. + See [MAPIStoreFolder getDeletedFMIDs:andCN:fromChangeNumber:inTableType:inMemCtx] + for details */ + [mapping unregisterURLWithID: srcMids[count] + andFlags: MAPISTORE_SOFT_DELETE]; + } } /* Registration of target messages */ @@ -1061,6 +1320,7 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP) struct SRow folderRow; struct SPropValue nameProperty; MAPIStoreMailFolder *newFolder; + SOGoMailAccount *accountFolder; SOGoMailFolder *targetSOGoFolder; NSMutableArray *uids; NSArray *childKeys; @@ -1068,12 +1328,12 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP) NGImap4Connection *connection; NGImap4Client *client; NSString *newURL, *parentDBFolderPath, *childKey, *folderIMAPName, - *urlNamePart, *newFolderIMAPName; + *urlNamePart, *newFolderIMAPName, *newFolderDBName; NSException *error; MAPIStoreMapping *mapping; NSDictionary *result; - if ([targetFolder isKindOfClass: MAPIStoreMailFolderK]) + if ([targetFolder isKindOfClass: MAPIStoreMailFolderK] || (!targetFolder && isMove)) { folderURL = [sogoObject imap4URL]; if (!newFolderName) @@ -1081,9 +1341,23 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP) targetSOGoFolder = [targetFolder sogoObject]; if (isMove) { - urlNamePart = [newFolderName stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; - newFolderURL = [NSURL URLWithString: urlNamePart - relativeToURL: [targetSOGoFolder imap4URL]]; + newFolderDBName = [[newFolderName stringByEncodingImap4FolderName] asCSSIdentifier]; + if (targetSOGoFolder) + { + /* Mimetise [SOGoMailFolderK imap4URLString] */ + urlNamePart = [[newFolderName stringByEncodingImap4FolderName] stringByEscapingURL]; + newFolderURL = [NSURL URLWithString: urlNamePart + relativeToURL: [targetSOGoFolder imap4URL]]; + } + else + { + /* Mimetise what createRootSecondaryFolderWithFID does */ + accountFolder = [[[self userContext] rootFolders] objectForKey: @"mail"]; + targetSOGoFolder = [SOGoMailFolder objectWithName: [NSString stringWithFormat: @"folder%@", + newFolderDBName] + inContainer: accountFolder]; + newFolderURL = [targetSOGoFolder imap4URL]; + } error = [[sogoObject imap4Connection] moveMailboxAtURL: folderURL toURL: newFolderURL]; @@ -1093,16 +1367,29 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP) { rc = MAPISTORE_SUCCESS; mapping = [self mapping]; - newURL = [NSString stringWithFormat: @"%@folder%@/", - [targetFolder url], urlNamePart]; + if (targetFolder) + newURL = [NSString stringWithFormat: @"%@folder%@/", + [targetFolder url], newFolderDBName]; + else + newURL = [NSString stringWithFormat: @"sogo://%@:%@@mail/folder%@/", + [[self userContext] username], [[self userContext] username], + newFolderDBName]; [mapping updateID: [self objectId] withURL: newURL]; - parentDBFolderPath = [[targetFolder dbFolder] path]; - if (!parentDBFolderPath) - parentDBFolderPath = @""; - [dbFolder changePathTo: [NSString stringWithFormat: - @"%@/folder%@", - parentDBFolderPath, - newFolderName]]; + if (targetFolder) + { + parentDBFolderPath = [[targetFolder dbFolder] path]; + if (!parentDBFolderPath) + parentDBFolderPath = @""; + [dbFolder changePathTo: [NSString stringWithFormat: + @"%@/folder%@", + parentDBFolderPath, + newFolderDBName] + intoNewContainer: [targetFolder dbFolder]]; + } + else + [dbFolder changePathTo: [NSString stringWithFormat: + @"/mail/folder%@", newFolderDBName] + intoNewContainer: nil]; } } else @@ -1290,6 +1577,15 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP) { [bodyPartKeys addObject: bodyPartKey]; messageUid = [self messageUIDFromMessageKey: messageKey]; + /* If the bodyPartKey include peek, remove it as it is not returned + as key in the IMAP server response. + + IMAP conversation example: + a4 UID FETCH 1 (UID BODY.PEEK[text]) + * 1 FETCH (UID 1 BODY[TEXT] {1677} + */ + bodyPartKey = [bodyPartKey stringByReplacingOccurrencesOfString: @"body.peek" + withString: @"body"]; [keyAssoc setObject: bodyPartKey forKey: messageUid]; } } diff --git a/OpenChange/MAPIStoreMailMessage.h b/OpenChange/MAPIStoreMailMessage.h index 22d9e3320..fffd0c422 100644 --- a/OpenChange/MAPIStoreMailMessage.h +++ b/OpenChange/MAPIStoreMailMessage.h @@ -35,6 +35,7 @@ { BOOL headerSetup; BOOL mailIsEvent; + BOOL mailIsSharingObject; NSString *mimeKey; NSString *headerCharset; NSString *headerEncoding; diff --git a/OpenChange/MAPIStoreMailMessage.m b/OpenChange/MAPIStoreMailMessage.m index 0515f0163..11eb24d03 100644 --- a/OpenChange/MAPIStoreMailMessage.m +++ b/OpenChange/MAPIStoreMailMessage.m @@ -32,6 +32,7 @@ #import #import #import +#import #import #import #import @@ -40,6 +41,7 @@ #import #import +#import "Codepages.h" #import "NSData+MAPIStore.h" #import "NSObject+MAPIStore.h" #import "NSString+MAPIStore.h" @@ -50,6 +52,7 @@ #import "MAPIStoreMailFolder.h" #import "MAPIStoreMapping.h" #import "MAPIStoreSamDBUtils.h" +#import "MAPIStoreSharingMessage.h" #import "MAPIStoreTypes.h" #import "MAPIStoreUserContext.h" @@ -63,7 +66,7 @@ @class iCalCalendar, iCalEvent; -static Class NSExceptionK; +static Class NSExceptionK, MAPIStoreSharingMessageK; @interface NSString (MAPIStoreMIME) @@ -106,6 +109,7 @@ static Class NSExceptionK; + (void) initialize { NSExceptionK = [NSException class]; + MAPIStoreSharingMessageK = [MAPIStoreSharingMessage class]; } - (id) init @@ -114,6 +118,7 @@ static Class NSExceptionK; { mimeKey = nil; mailIsEvent = NO; + mailIsSharingObject = NO; headerCharset = nil; headerEncoding = nil; headerMimeType = nil; @@ -132,6 +137,7 @@ static Class NSExceptionK; [bodyContent release]; [headerMimeType release]; [headerCharset release]; + [headerEncoding release]; [appointmentWrapper release]; [super dealloc]; } @@ -151,6 +157,30 @@ static Class NSExceptionK; return [sogoObject date]; } +- (enum mapistore_error) getAvailableProperties: (struct SPropTagArray **) propertiesP + inMemCtx: (TALLOC_CTX *) memCtx +{ + BOOL listedProperties[65536]; + NSUInteger count; + uint16_t propId; + + if (mailIsSharingObject) + { + memset (listedProperties, NO, 65536 * sizeof (BOOL)); + [super getAvailableProperties: propertiesP inMemCtx: memCtx]; + for (count = 0; count < (*propertiesP)->cValues; count++) + { + propId = ((*propertiesP)->aulPropTag[count] >> 16) & 0xffff; + listedProperties[propId] = YES; + } + [MAPIStoreSharingMessage fillAvailableProperties: *propertiesP + withExclusions: listedProperties]; + return MAPISTORE_SUCCESS; + } + else + return [super getAvailableProperties: propertiesP inMemCtx: memCtx]; +} + static NSComparisonResult _compareBodyKeysByPriority (id entry1, id entry2, void *data) { @@ -200,9 +230,11 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) - (void) _fetchHeaderData { + MAPIStoreSharingMessage *sharingMessage; NSMutableArray *keys; NSArray *acceptedTypes; NSDictionary *messageData, *partHeaderData, *parameters; + NSString *sharingHeader; acceptedTypes = [NSArray arrayWithObjects: @"text/calendar", @"application/ics", @@ -227,6 +259,22 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) if ([headerMimeType isEqualToString: @"text/calendar"] || [headerMimeType isEqualToString: @"application/ics"]) mailIsEvent = YES; + else + { + sharingHeader = [[sogoObject mailHeaders] objectForKey: @"x-ms-sharing-localtype"]; + if (sharingHeader) + { + mailIsSharingObject = YES; + /* It is difficult to subclass this in folder class, that's why + a sharing object is a proxy in a mail message */ + sharingMessage = [[MAPIStoreSharingMessage alloc] + initWithMailHeaders: [sogoObject mailHeaders] + andConnectionInfo: [[self context] connectionInfo] + fromMessage: self]; + [self addProxy: sharingMessage]; + [sharingMessage release]; + } + } } headerSetup = YES; @@ -368,6 +416,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) { uint64_t version = ULLONG_MAX; NSString *uid, *changeNumber; + BOOL synced; uid = [(MAPIStoreMailFolder *) container messageUIDFromMessageKey: [self nameInContainer]]; @@ -386,8 +435,19 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) [self logWithFormat: @"got one"]; else { - [self errorWithFormat: @"still nothing. We crash!"]; - abort(); + [self warnWithFormat: @"attempting to get change number" + @" by synchronising this specific message..."]; + synced = [(MAPIStoreMailFolder *) container synchroniseCacheForUID: uid]; + if (synced) + { + changeNumber = [(MAPIStoreMailFolder *) container + changeNumberForMessageUID: uid]; + } + else + { + [self errorWithFormat: @"still nothing. We crash!"]; + abort(); + } } } version = [changeNumber unsignedLongLongValue] >> 16; @@ -504,6 +564,8 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) if (mailIsEvent) [[self _appointmentWrapper] getPidTagMessageClass: data inMemCtx: memCtx]; + else if (mailIsSharingObject) + *data = talloc_strdup (memCtx, "IPM.Sharing"); else *data = talloc_strdup (memCtx, "IPM.Note"); @@ -923,15 +985,17 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) - (int) getPidTagInternetCodepage: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - /* ref: - http://msdn.microsoft.com/en-us/library/dd317756%28v=vs.85%29.aspx - - minimal list that should be handled: - us-ascii: 20127 - iso-8859-1: 28591 - iso-8859-15: 28605 - utf-8: 65001 */ - *data = MAPILongValue(memCtx, 65001); + NSNumber *codepage; + + codepage = [Codepages getCodepageFromName: headerCharset]; + if (!codepage) + { + [self warnWithFormat: @"Couldn't find codepage from `%@`. " + @"Using UTF-8 by default", headerCharset]; + codepage = [Codepages getCodepageFromName: @"utf-8"]; + } + + *data = MAPILongValue(memCtx, [codepage intValue]); return MAPISTORE_SUCCESS; } @@ -1333,6 +1397,41 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) : MAPISTORE_ERR_NOT_FOUND); } +- (int) getPidTagTransportMessageHeaders: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSDictionary *mailHeaders; + NSEnumerator *keyEnumerator; + NSMutableArray *headers; + NGMimeMessageGenerator *g; + NSString *headerKey, *fullHeader, *headerGenerated; + id headerValue; + NSData *headerData; + + /* Let's encode each mail header and put them on 'headers' array */ + mailHeaders = [sogoObject mailHeaders]; + headers = [NSMutableArray arrayWithCapacity: [mailHeaders count]]; + + g = [[NGMimeMessageGenerator alloc] init]; + keyEnumerator = [mailHeaders keyEnumerator]; + while ((headerKey = [keyEnumerator nextObject])) + { + headerValue = [mailHeaders objectForKey: headerKey]; + + headerData = [g generateDataForHeaderField: headerKey value: headerValue]; + headerGenerated = [[NSString alloc] initWithData: headerData encoding:NSUTF8StringEncoding]; + fullHeader = [NSString stringWithFormat:@"%@: %@", headerKey, headerGenerated]; + [headerGenerated release]; + + [headers addObject: fullHeader]; + } + [g release]; + + *data = [[headers componentsJoinedByString:@"\n"] asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + - (void) getMessageData: (struct mapistore_message **) dataPtr inMemCtx: (TALLOC_CTX *) memCtx { @@ -1566,11 +1665,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) if (!headerSetup) [self _fetchHeaderData]; - if ([mimeKey hasPrefix: @"body.peek"]) - bodyPartKey = [NSString stringWithFormat: @"body[%@]", - [mimeKey _strippedBodyKey]]; - else - bodyPartKey = mimeKey; + bodyPartKey = mimeKey; return bodyPartKey; } @@ -1584,6 +1679,21 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) bodySetup = YES; } +- (MAPIStoreSharingMessage *) _sharingObject +{ + /* Get the sharing object if available */ + NSUInteger i, max; + id proxy; + + max = [proxies count]; + for (i = 0; i < max; i++) { + proxy = [proxies objectAtIndex: i]; + if ([proxy isKindOfClass: MAPIStoreSharingMessageK]) + return proxy; + } + return nil; +} + - (void) save: (TALLOC_CTX *) memCtx { NSNumber *value; @@ -1597,6 +1707,11 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) else /* 0: unflagged, 1: follow up complete */ [sogoObject removeFlags: @"\\Flagged"]; } + + if (mailIsSharingObject) + [[self _sharingObject] saveWithMessage: self + andSOGoObject: sogoObject]; + } @end diff --git a/OpenChange/MAPIStoreMailVolatileMessage.m b/OpenChange/MAPIStoreMailVolatileMessage.m index 7df01fdf1..b5239f193 100644 --- a/OpenChange/MAPIStoreMailVolatileMessage.m +++ b/OpenChange/MAPIStoreMailVolatileMessage.m @@ -28,9 +28,10 @@ #import #import #import +#import #import -#import #import +#import #import #import #import @@ -43,6 +44,7 @@ #import #import #import +#import #import #import #import @@ -50,6 +52,7 @@ #import #import +#import "Codepages.h" #import "MAPIStoreAttachment.h" #import "MAPIStoreAttachmentTable.h" #import "MAPIStoreContext.h" @@ -62,13 +65,13 @@ #import "NSData+MAPIStore.h" #import "NSObject+MAPIStore.h" #import "NSString+MAPIStore.h" -#import #import "MAPIStoreMailVolatileMessage.h" #undef DEBUG #include #include +#include static Class NSNumberK = Nil; @@ -531,13 +534,112 @@ QuoteSpecials (NSString *address) return result; } +static inline void +FillMessageHeadersFromSharingProperties (NGMutableHashMap *headers, NSDictionary *mailProperties) +{ + /* Store the *important* properties related with a sharing object as + MIME eXtension headers. See [MS-OXSHARE] Section 2.2 for details + about the properties */ + + id value; + NSNumber *sharingFlavourNum = nil; + + value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingCapabilities)]; + if (value) + [headers setObject: value + forKey: @"X-MS-Sharing-Capabilities"]; + + value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingFlavor)]; + if (value) + sharingFlavourNum = (NSNumber *)value; + else + { + value = [mailProperties objectForKey: MAPIPropertyKey (PidNameXSharingFlavor)]; + if (value) + { + /* Transform the hex string to unsigned int */ + NSScanner *scanner; + unsigned int sharingFlavour; + scanner = [NSScanner scannerWithString:value]; + if ([scanner scanHexInt:&sharingFlavour]) + sharingFlavourNum =[NSNumber numberWithUnsignedInt: sharingFlavour]; + } + } + if (sharingFlavourNum) + { + if ([sharingFlavourNum unsignedIntegerValue] == 0x5100) + { + /* 0x5100 sharing flavour is not in standard but it seems to + be a denial of request + invitation message so we store + deny sharing flavour */ + sharingFlavourNum = [NSNumber numberWithUnsignedInt: SHARING_DENY_REQUEST]; + } + [headers setObject: sharingFlavourNum + forKey: @"X-MS-Sharing-Flavor"]; + } + + value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingInitiatorEntryId)]; + if (value) + [headers setObject: [[value stringByEncodingBase64] stringByReplacingOccurrencesOfString: @"\n" + withString: @""] + forKey: @"X-MS-Sharing-InitiatorEntryId"]; + + value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingInitiatorName)]; + if (value) + [headers setObject: value + forKey: @"X-MS-Sharing-InitiatorName"]; + + value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingInitiatorSmtp)]; + if (value) + [headers setObject: value + forKey: @"X-MS-Sharing-InitiatorSmtp"]; + + value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingLocalType)]; + if (value) + [headers setObject: value + forKey: @"X-MS-Sharing-LocalType"]; + + value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingProviderName)]; + if (value) + [headers setObject: value + forKey: @"X-MS-Sharing-ProviderName"]; + + value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingRemoteName)]; + if (value) + [headers setObject: value + forKey: @"X-MS-Sharing-RemoteName"]; + + value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingRemoteStoreUid)]; + if (value) + [headers setObject: value + forKey: @"X-MS-Sharing-RemoteStoreUid"]; + + value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingRemoteUid)]; + if (value) + [headers setObject: value + forKey: @"X-MS-Sharing-RemoteUid"]; + + value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingResponseTime)]; + if (value) + [headers setObject: value + forKey: @"X-MS-Sharing-ResponseTime"]; + + value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingResponseType)]; + if (value) + [headers setObject: value + forKey: @"X-MS-Sharing-ResponseType"]; + +} + static inline void FillMessageHeadersFromProperties (NGMutableHashMap *headers, NSDictionary *mailProperties, BOOL withBcc, struct mapistore_connection_info *connInfo) { + BOOL fromResolved = NO; + NSData *senderEntryId; NSMutableString *subject; - NSString *from, *recId, *messageId, *subjectData; + NSString *from, *recId, *messageId, *subjectData, *recipientsStr, *msgClass; NSArray *list; NSCalendarDate *date; NSDictionary *recipients; @@ -571,10 +673,96 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers, list = MakeRecipientsList ([recipients objectForKey: @"orig"]); if ([list count]) - [headers setObjects: list forKey: @"from"]; + { + [headers setObjects: list forKey: @"from"]; + fromResolved = YES; + } + } + + if (!fromResolved) + { + NSLog (@"Message without an orig from, try to guess it from PidTagSenderEntryId"); + senderEntryId = [mailProperties objectForKey: MAPIPropertyKey (PR_SENDER_ENTRYID)]; + if (senderEntryId) + { + struct Binary_r bin32; + struct AddressBookEntryId *addrBookEntryId; + NSString *username; + NSMutableDictionary *fromRecipient; + + fromRecipient = [NSMutableDictionary dictionaryWithCapacity: 2]; + + bin32.cb = [senderEntryId length]; + bin32.lpb = (uint8_t *) [senderEntryId bytes]; + addrBookEntryId = get_AddressBookEntryId (connInfo->sam_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", + [NSString stringWithUTF8String: addrBookEntryId->X500DN], @"sAMAccountName"); + if (username) + { + SOGoUser *fromUser; + + fromUser = [SOGoUser userWithLogin: [username lowercaseString]]; + [fromRecipient setObject: [fromUser cn] forKey: @"fullName"]; + [fromRecipient setObject: [[fromUser allEmails] objectAtIndex: 0] + forKey: @"email"]; + } + else + [fromRecipient setObject: [NSString stringWithUTF8String: addrBookEntryId->X500DN] + forKey: @"email"]; + + } + else + { + /* Try with One-Off EntryId */ + struct OneOffEntryId *oneOffEntryId; + + oneOffEntryId = get_OneOffEntryId (connInfo->sam_ctx, &bin32); + if (oneOffEntryId && [[NSString stringWithGUID: &oneOffEntryId->ProviderUID] + hasSuffix: @"00dd010f5402"]) + { + [fromRecipient setObject: [NSString stringWithUTF8String: oneOffEntryId->DisplayName.lpszW] + forKey: @"fullName"]; + [fromRecipient setObject: [NSString stringWithUTF8String: oneOffEntryId->EmailAddress.lpszW] + forKey: @"email"]; + } + talloc_free (oneOffEntryId); + } + talloc_free (addrBookEntryId); + + if ([[fromRecipient allKeys] count] > 0) + { + list = MakeRecipientsList ([NSArray arrayWithObjects: fromRecipient, nil]); + if ([list count]) + [headers setObjects: list forKey: @"from"]; + } + + } + } + + if (!recipients) + { + NSLog (@"Message without recipients." + @"Guessing recipients from PidTagOriginalDisplayTo and PidTagOriginalCc"); + recipientsStr = [mailProperties objectForKey: MAPIPropertyKey (PidTagOriginalDisplayTo)]; + if (recipientsStr) + { + list = [recipientsStr componentsSeparatedByString:@", "]; + if ([list count]) + [headers setObjects: list forKey: @"to"]; + } + + recipientsStr = [mailProperties objectForKey: MAPIPropertyKey (PidTagOriginalDisplayCc)]; + if (recipientsStr) + { + list = [recipientsStr componentsSeparatedByString:@", "]; + if ([list count]) + [headers setObjects: list forKey: @"cc"]; + } } - else - NSLog (@"message without recipients"); subject = [NSMutableString stringWithCapacity: 128]; subjectData = [mailProperties objectForKey: MAPIPropertyKey (PR_SUBJECT_PREFIX_UNICODE)]; @@ -592,8 +780,6 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers, date = [mailProperties objectForKey: MAPIPropertyKey (PR_CLIENT_SUBMIT_TIME)]; if (date) { - date = [date addYear: 0 month: 0 day: 0 - hour: 0 minute: 0 second: [[date timeZone] secondsFromGMT]]; [headers addObject: [date rfc822DateString] forKey: @"date"]; } [headers addObject: @"1.0" forKey: @"MIME-Version"]; @@ -612,6 +798,13 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers, { [headers addObject: @"5 (Lowest)" forKey: @"X-Priority"]; } + + msgClass = [mailProperties objectForKey: MAPIPropertyKey (PidTagMessageClass)]; + if ([msgClass isEqualToString: @"IPM.Sharing"]) + { + FillMessageHeadersFromSharingProperties (headers, mailProperties); + } + } static NSArray * @@ -672,21 +865,9 @@ MakeTextHtmlBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, { /* charset */ codePage = [mailProperties objectForKey: MAPIPropertyKey (PR_INTERNET_CPID)]; - switch ([codePage intValue]) - { - case 20127: - charset = @"us-ascii"; - break; - case 28605: - charset = @"iso-8859-15"; - break; - case 65001: - charset = @"utf-8"; - break; - case 28591: - default: - charset = @"iso-8859-1"; - } + charset = [Codepages getNameFromCodepage: codePage]; + if (!charset) + charset = @"utf-8"; htmlContentType = [NSString stringWithFormat: @"text/html; charset=%@", charset]; @@ -834,7 +1015,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS messageBody = MakeMessageBody (properties, attachmentParts, &contentType); if (messageBody) { - [headers setObject: contentType forKey: @"content-type"]; + [message setHeader: contentType forKey: @"content-type"]; [message setBody: messageBody]; } @@ -873,12 +1054,12 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS id authenticator; msgClass = [properties objectForKey: MAPIPropertyKey (PidTagMessageClass)]; - if ([msgClass isEqualToString: @"IPM.Note"]) /* we skip invitation replies */ + if ([msgClass isEqualToString: @"IPM.Note"] || [msgClass isEqualToString: @"IPM.Sharing"]) /* we skip invitation replies */ { /* send mail */ messageData = [self _generateMailDataWithBcc: NO]; - + recipientEmails = [NSMutableArray arrayWithCapacity: 32]; recipients = [properties objectForKey: @"recipients"]; for (type = MAPI_ORIG; type <= MAPI_BCC; type++) diff --git a/OpenChange/MAPIStoreMapping.h b/OpenChange/MAPIStoreMapping.h index 620112d76..827cddf38 100644 --- a/OpenChange/MAPIStoreMapping.h +++ b/OpenChange/MAPIStoreMapping.h @@ -49,11 +49,15 @@ - (NSString *) urlFromID: (uint64_t) idKey; - (uint64_t) idFromURL: (NSString *) url; +- (uint64_t) idFromURL: (NSString *) url + isSoftDeleted: (bool *) softDeleted; - (BOOL) registerURL: (NSString *) urlString withID: (uint64_t) idNbr; - (void) registerURLs: (NSArray *) urlString withIDs: (NSArray *) idNbrs; - (void) unregisterURLWithID: (uint64_t) idNbr; +- (void) unregisterURLWithID: (uint64_t) idNbr + andFlags: (uint8_t) flags; - (void) updateID: (uint64_t) idNbr withURL: (NSString *) urlString; diff --git a/OpenChange/MAPIStoreMapping.m b/OpenChange/MAPIStoreMapping.m index 194488bc7..6a910ea4c 100644 --- a/OpenChange/MAPIStoreMapping.m +++ b/OpenChange/MAPIStoreMapping.m @@ -114,6 +114,9 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr) { ASSIGN (username, newUsername); indexing = newIndexing; + /* Workaround so all indexing context are valid and won't be freed. */ + // TODO refactor indexing interface + talloc_reference(memCtx, indexing); } return self; @@ -157,6 +160,21 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr) return NSNotFound; } +- (uint64_t) idFromURL: (NSString *) url + isSoftDeleted: (bool *) softDeleted +{ + enum mapistore_error ret; + uint64_t idNbr; + + ret = indexing->get_fmid(indexing, [username UTF8String], [url UTF8String], + false, &idNbr, softDeleted); + + if (ret != MAPISTORE_SUCCESS) + return NSNotFound; + + return idNbr; +} + - (void) _updateFolderWithURL: (NSString *) oldURL withURL: (NSString *) urlString { @@ -214,7 +232,7 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr) { NSString *oldURL; uint64_t oldIdNbr; - bool rc; + bool rc, softDeleted; oldURL = [self urlFromID: idNbr]; if (oldURL != NULL) @@ -225,13 +243,16 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr) return NO; } - oldIdNbr = [self idFromURL: urlString]; + oldIdNbr = [self idFromURL: urlString isSoftDeleted: &softDeleted]; if (oldIdNbr != NSNotFound) { - [self errorWithFormat: - @"attempt to double register an entry with idNbr ('%@', %lld," - @" 0x%.16"PRIx64", oldid=0x%.16"PRIx64")", - urlString, idNbr, idNbr, oldIdNbr]; + if (softDeleted) + [self logWithFormat: @"Attempt to register a soft-deleted %@", urlString]; + else + [self errorWithFormat: + @"attempt to double register an entry with idNbr ('%@', %lld," + @" 0x%.16"PRIx64", oldid=0x%.16"PRIx64")", + urlString, idNbr, idNbr, oldIdNbr]; return NO; } else @@ -275,4 +296,13 @@ MAPIStoreMappingKeyFromId (uint64_t idNbr) idNbr, MAPISTORE_PERMANENT_DELETE); } +- (void) unregisterURLWithID: (uint64_t) idNbr + andFlags: (uint8_t) flags +{ + indexing->del_fmid(indexing, [username UTF8String], + idNbr, + (flags == MAPISTORE_SOFT_DELETE) ? MAPISTORE_SOFT_DELETE : MAPISTORE_PERMANENT_DELETE); +} + + @end diff --git a/OpenChange/MAPIStoreMessage.m b/OpenChange/MAPIStoreMessage.m index 444ecd0fb..e922d4a18 100644 --- a/OpenChange/MAPIStoreMessage.m +++ b/OpenChange/MAPIStoreMessage.m @@ -474,7 +474,9 @@ rtf2html (NSData *compressedRTF) MAPIStoreMessage *mainMessage; //[self logWithFormat: @"METHOD '%s' (%d)", __FUNCTION__, __LINE__]; - + + containerTables = nil; + max = 0; context = [self context]; ownerUser = [[self userContext] sogoUser]; userIsOwner = [[context activeUser] isEqual: ownerUser]; @@ -782,7 +784,7 @@ rtf2html (NSData *compressedRTF) return [self getLongZero: data inMemCtx: memCtx]; } -- (int) getPidTagSensitivity: (void **) data // TODO -> subclass in calendar +- (int) getPidTagSensitivity: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { return [self getLongZero: data inMemCtx: memCtx]; @@ -795,7 +797,7 @@ rtf2html (NSData *compressedRTF) TALLOC_CTX *localMemCtx; char *prefix, *normalizedSubject; - localMemCtx = talloc_zero (NULL, TALLOC_CTX); + localMemCtx = talloc_zero (memCtx, TALLOC_CTX); if ([self getProperty: (void **) &prefix withTag: PidTagSubjectPrefix inMemCtx: localMemCtx] @@ -807,6 +809,8 @@ rtf2html (NSData *compressedRTF) if (rc == MAPISTORE_SUCCESS) *data = talloc_asprintf (memCtx, "%s%s", prefix, normalizedSubject); + talloc_free(localMemCtx); + return rc; } diff --git a/OpenChange/MAPIStoreNotesContext.m b/OpenChange/MAPIStoreNotesContext.m index 594a819b5..725b0d158 100644 --- a/OpenChange/MAPIStoreNotesContext.m +++ b/OpenChange/MAPIStoreNotesContext.m @@ -45,7 +45,8 @@ context = talloc_zero(memCtx, struct mapistore_contexts_list); context->url = talloc_asprintf (context, "sogo://%s@notes/", - [userName UTF8String]); + [[userName stringByReplacingOccurrencesOfString: @"@" + withString: @"%40"] UTF8String]); // context->name = "Notes personnelles"; context->main_folder = true; context->role = MAPISTORE_NOTES_ROLE; diff --git a/OpenChange/MAPIStoreObject.m b/OpenChange/MAPIStoreObject.m index 1d468915c..ecb00dba3 100644 --- a/OpenChange/MAPIStoreObject.m +++ b/OpenChange/MAPIStoreObject.m @@ -23,7 +23,6 @@ #import #import #import -#import #import #import #import @@ -245,17 +244,16 @@ static Class NSExceptionK, MAPIStoreFolderK; struct SPropValue *cValue; NSUInteger counter; NSMutableDictionary *newProperties; - NSTimeZone *tz; - NSInteger tzOffset; id value; - tz = nil; - newProperties = [NSMutableDictionary dictionaryWithCapacity: aRow->cValues]; for (counter = 0; counter < aRow->cValues; counter++) { cValue = aRow->lpProps + counter; value = NSObjectFromSPropValue (cValue); + if (value == nil) + continue; + switch (cValue->ulPropTag & 0xffff) { case PT_STRING8: @@ -264,16 +262,6 @@ static Class NSExceptionK, MAPIStoreFolderK; @"attempting to set string property as PR_STRING8: %.8x", cValue->ulPropTag]; break; - case PT_SYSTIME: - if (!tz) - { - tz = [[self userContext] timeZone]; - tzOffset = -[tz secondsFromGMT]; - } - value = [value addYear: 0 month: 0 day: 0 - hour: 0 minute: 0 second: tzOffset]; - [value setTimeZone: tz]; - break; } [newProperties setObject: value forKey: MAPIPropertyKey (cValue->ulPropTag)]; diff --git a/OpenChange/MAPIStoreRecurrenceUtils.h b/OpenChange/MAPIStoreRecurrenceUtils.h index 68c84361e..df3700575 100644 --- a/OpenChange/MAPIStoreRecurrenceUtils.h +++ b/OpenChange/MAPIStoreRecurrenceUtils.h @@ -25,6 +25,8 @@ #include +#import + #import #import @@ -41,7 +43,10 @@ @interface iCalCalendar (MAPIStoreRecurrence) - (void) setupRecurrenceWithMasterEntity: (iCalRepeatableEntityObject *) entity - fromRecurrencePattern: (struct RecurrencePattern *) rp; + fromRecurrencePattern: (struct RecurrencePattern *) rp + withExceptions: (struct ExceptionInfo *) exInfos + andExceptionCount: (uint16_t) exInfoCount + inTimeZone: (NSTimeZone *) tz; @end diff --git a/OpenChange/MAPIStoreRecurrenceUtils.m b/OpenChange/MAPIStoreRecurrenceUtils.m index ebf588000..9e0bf29c3 100644 --- a/OpenChange/MAPIStoreRecurrenceUtils.m +++ b/OpenChange/MAPIStoreRecurrenceUtils.m @@ -24,6 +24,7 @@ #import #import #import +#import #import #import @@ -48,6 +49,10 @@ - (void) setupRecurrenceWithMasterEntity: (iCalRepeatableEntityObject *) entity fromRecurrencePattern: (struct RecurrencePattern *) rp + withExceptions: (struct ExceptionInfo *) exInfos + andExceptionCount: (uint16_t) exInfoCount + inTimeZone: (NSTimeZone *) tz + { NSCalendarDate *startDate, *olEndDate, *untilDate, *exDate; NSString *monthDay, *month; @@ -214,34 +219,42 @@ /* exception dates: - take all deleted instances - - remove all modified instances from the above set + - remove all modified instances available in ExceptionInfo from the above set - add remaining instances, in chronological order */ - exceptionDates = [NSMutableSet set]; - for (count = 0; count < rp->DeletedInstanceCount; count++) - { - exDate - = [NSDate dateFromMinutesSince1601: rp->DeletedInstanceDates[count]]; - exDate = [exDate hour: [startDate hourOfDay] - minute: [startDate minuteOfHour] - second: [startDate secondOfMinute]]; - [exceptionDates addObject: exDate]; - } - for (count = 0; count < rp->ModifiedInstanceCount; count++) - { - exDate - = [NSDate dateFromMinutesSince1601: rp->ModifiedInstanceDates[count]]; - exDate = [exDate hour: [startDate hourOfDay] - minute: [startDate minuteOfHour] - second: [startDate secondOfMinute]]; - [exceptionDates removeObject: exDate]; - } - realExDates = [[exceptionDates allObjects] - sortedArrayUsingSelector: @selector (compare:)]; - max = [realExDates count]; - for (count = 0; count < max; count++) - [entity addToExceptionDates: [realExDates objectAtIndex: count]]; + /* Heuristic to avoid these loops */ + if (rp->DeletedInstanceCount != rp->ModifiedInstanceCount) { + exceptionDates = [NSMutableSet set]; + for (count = 0; count < rp->DeletedInstanceCount; count++) + { + exDate + = [NSDate dateFromMinutesSince1601: rp->DeletedInstanceDates[count]]; + exDate = [exDate hour: [startDate hourOfDay] + minute: [startDate minuteOfHour] + second: [startDate secondOfMinute]]; + [exceptionDates addObject: exDate]; + } + /* Read the exceptions to remove the instances that are modified and not deleted */ + if (exInfos && exInfoCount > 0) + { + for (count = 0; count < exInfoCount; count++) + { + /* The OriginalStartDate is in local time */ + exDate = [NSDate dateFromMinutesSince1601: exInfos[count].OriginalStartDate]; + exDate = [exDate dateByAddingYears: 0 months: 0 days: 0 + hours: 0 minutes: 0 + seconds: - [tz secondsFromGMT]]; + [exceptionDates removeObject: exDate]; + } + } + + realExDates = [[exceptionDates allObjects] + sortedArrayUsingSelector: @selector (compare:)]; + max = [realExDates count]; + for (count = 0; count < max; count++) + [entity addToExceptionDates: [realExDates objectAtIndex: count]]; + } } @end @@ -312,15 +325,25 @@ rp->RecurFrequency = RecurFrequency_Weekly; rp->PatternType = PatternType_Week; rp->Period = repeatInterval; + + dayOfWeek = [startDate dayOfWeek]; + mask = 0; byDayMask = [self byDayMask]; - for (count = 0; count < 7; count++) - if ([byDayMask occursOnDay: count]) - mask |= 1 << count; + if (byDayMask) + { + for (count = 0; count < 7; count++) + if ([byDayMask occursOnDay: count]) + mask |= 1 << count; + } + else + { + /* Set the recurrence pattern using start date */ + mask |= 1 << dayOfWeek; + } rp->PatternTypeSpecific.WeekRecurrencePattern = mask; /* FirstDateTime */ - dayOfWeek = [startDate dayOfWeek]; if (dayOfWeek) beginOfWeek = [startDate dateByAddingYears: 0 months: 0 days: -dayOfWeek diff --git a/OpenChange/MAPIStoreSOGo.m b/OpenChange/MAPIStoreSOGo.m index 923541764..4ec0ba53a 100644 --- a/OpenChange/MAPIStoreSOGo.m +++ b/OpenChange/MAPIStoreSOGo.m @@ -47,6 +47,7 @@ #import "NSObject+MAPIStore.h" #import "NSString+MAPIStore.h" +#include #include #include #include @@ -58,8 +59,8 @@ static BOOL initialization_done = NO; #define NS_CURRENT_THREAD_REGISTER() \ BOOL __nsrct_thread_registered = GSRegisterCurrentThread(); \ if (!initialization_done) { \ - DEBUG(5, ("[SOGo: %s:%d] You should call sogo_backend_init() first. Current thread: %p, pid: %d\n", \ - __FUNCTION__, __LINE__, GSCurrentThread(), getpid())); \ + OC_DEBUG(5, "[SOGo] You should call sogo_backend_init() first. Current thread: %p, pid: %d\n", \ + GSCurrentThread(), getpid()); \ } #define NS_CURRENT_THREAD_TRY_UNREGISTER() \ if (__nsrct_thread_registered) { \ @@ -144,7 +145,7 @@ sogo_backend_init (void) GSRegisterCurrentThread(); if (initialization_done) { - DEBUG(0, ("SOGo backend already initialized.\n")); + OC_DEBUG(5, "[SOGo] SOGo backend already initialized.\n"); return MAPISTORE_SUCCESS; } @@ -189,7 +190,7 @@ sogo_backend_init (void) [pool release]; - DEBUG(0, ("[SOGo: %s:%d] backend init SUCCESS. Current thread: %p, pid: %d\n", __FUNCTION__, __LINE__, GSCurrentThread(), getpid())); + OC_DEBUG(5, "[SOGo] backend init SUCCESS. Current thread: %p, pid: %d\n", GSCurrentThread(), getpid()); initialization_done = YES; return MAPISTORE_SUCCESS; @@ -213,7 +214,7 @@ sogo_backend_create_context(TALLOC_CTX *mem_ctx, MAPIStoreContext *context; int rc; - DEBUG(0, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); NS_CURRENT_THREAD_REGISTER(); pool = [NSAutoreleasePool new]; @@ -250,7 +251,7 @@ sogo_backend_create_root_folder (const char *username, NSString *mapistoreUri; int rc; - DEBUG(0, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); NS_CURRENT_THREAD_REGISTER(); pool = [NSAutoreleasePool new]; @@ -287,7 +288,7 @@ sogo_backend_list_contexts(const char *username, struct indexing_context *indexi NSString *userName; int rc; - DEBUG(0, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); NS_CURRENT_THREAD_REGISTER(); pool = [NSAutoreleasePool new]; @@ -335,7 +336,7 @@ sogo_context_get_path(void *backend_object, TALLOC_CTX *mem_ctx, MAPIStoreContext *context; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (backend_object) { @@ -369,7 +370,7 @@ sogo_context_get_root_folder(void *backend_object, TALLOC_CTX *mem_ctx, MAPIStoreFolder *folder; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (backend_object) { @@ -412,7 +413,7 @@ sogo_folder_open_folder(void *folder_object, TALLOC_CTX *mem_ctx, uint64_t fid, MAPIStoreFolder *folder, *childFolder; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (folder_object) { @@ -456,7 +457,7 @@ sogo_folder_create_folder(void *folder_object, TALLOC_CTX *mem_ctx, MAPIStoreFolder *folder, *childFolder; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (folder_object) { @@ -499,7 +500,7 @@ sogo_folder_delete(void *folder_object) MAPIStoreFolder *folder; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (folder_object) { @@ -531,7 +532,7 @@ sogo_folder_get_child_count(void *folder_object, enum mapistore_table_type table MAPIStoreFolder *folder; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (folder_object) { @@ -567,7 +568,7 @@ sogo_folder_open_message(void *folder_object, MAPIStoreMessage *message; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (folder_object) { @@ -609,7 +610,7 @@ sogo_folder_create_message(void *folder_object, MAPIStoreMessage *message; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (folder_object) { @@ -645,7 +646,7 @@ sogo_folder_delete_message(void *folder_object, uint64_t mid, uint8_t flags) MAPIStoreFolder *folder; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (folder_object) { @@ -683,7 +684,7 @@ sogo_folder_move_copy_messages(void *folder_object, struct MAPIStoreTallocWrapper *wrapper; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (folder_object) { @@ -727,7 +728,7 @@ sogo_folder_move_folder(void *folder_object, void *target_folder_object, struct MAPIStoreTallocWrapper *wrapper; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (folder_object) { @@ -777,7 +778,7 @@ sogo_folder_copy_folder(void *folder_object, void *target_folder_object, TALLOC_ struct MAPIStoreTallocWrapper *wrapper; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (folder_object) { @@ -821,7 +822,7 @@ sogo_folder_get_deleted_fmids(void *folder_object, TALLOC_CTX *mem_ctx, MAPIStoreFolder *folder; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (folder_object) { @@ -860,7 +861,7 @@ sogo_folder_open_table(void *folder_object, TALLOC_CTX *mem_ctx, MAPIStoreTable *table; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (folder_object) { @@ -899,7 +900,7 @@ sogo_folder_modify_permissions(void *folder_object, uint8_t flags, MAPIStoreFolder *folder; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (folder_object) { @@ -933,7 +934,7 @@ sogo_folder_preload_message_bodies(void *folder_object, enum mapistore_table_typ MAPIStoreFolder *folder; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (folder_object) { @@ -968,7 +969,7 @@ sogo_message_get_message_data(void *message_object, MAPIStoreMessage *message; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (message_object) { @@ -1003,7 +1004,7 @@ sogo_message_create_attachment (void *message_object, TALLOC_CTX *mem_ctx, void MAPIStoreAttachment *attachment; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (message_object) { @@ -1040,7 +1041,7 @@ sogo_message_open_attachment (void *message_object, TALLOC_CTX *mem_ctx, MAPIStoreAttachment *attachment; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (message_object) { @@ -1076,7 +1077,7 @@ sogo_message_get_attachment_table (void *message_object, TALLOC_CTX *mem_ctx, vo MAPIStoreAttachmentTable *table; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (message_object) { @@ -1115,7 +1116,8 @@ sogo_message_modify_recipients (void *message_object, MAPIStoreMessage *message; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); + if (message_object) { @@ -1150,7 +1152,8 @@ sogo_message_set_read_flag (void *message_object, uint8_t flag) MAPIStoreMessage *message; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); + if (message_object) { @@ -1183,7 +1186,8 @@ sogo_message_save (void *message_object, TALLOC_CTX *mem_ctx) MAPIStoreMessage *message; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); + if (message_object) { @@ -1216,7 +1220,8 @@ sogo_message_submit (void *message_object, enum SubmitFlags flags) MAPIStoreMailVolatileMessage *message; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); + if (message_object) { @@ -1254,7 +1259,8 @@ sogo_message_attachment_open_embedded_message (void *attachment_object, MAPIStoreEmbeddedMessage *message; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); + if (attachment_object) { @@ -1295,7 +1301,8 @@ sogo_message_attachment_create_embedded_message (void *attachment_object, MAPIStoreEmbeddedMessage *message; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); + if (attachment_object) { @@ -1331,7 +1338,8 @@ static enum mapistore_error sogo_table_get_available_properties(void *table_obje MAPIStoreTable *table; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); + if (table_object) { @@ -1363,7 +1371,8 @@ sogo_table_set_columns (void *table_object, uint16_t count, enum MAPITAGS *prope MAPIStoreTable *table; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); + if (table_object) { @@ -1396,7 +1405,8 @@ sogo_table_set_restrictions (void *table_object, struct mapi_SRestriction *restr MAPIStoreTable *table; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); + if (table_object) { @@ -1431,7 +1441,8 @@ sogo_table_set_sort_order (void *table_object, struct SSortOrderSet *sort_order, MAPIStoreTable *table; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); + if (table_object) { @@ -1468,7 +1479,8 @@ sogo_table_get_row (void *table_object, TALLOC_CTX *mem_ctx, MAPIStoreTable *table; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); + if (table_object) { @@ -1503,7 +1515,8 @@ sogo_table_get_row_count (void *table_object, MAPIStoreTable *table; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); + if (table_object) { @@ -1536,7 +1549,8 @@ sogo_table_handle_destructor (void *table_object, uint32_t handle_id) MAPIStoreTable *table; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); + if (table_object) { @@ -1570,7 +1584,8 @@ static enum mapistore_error sogo_properties_get_available_properties(void *objec MAPIStoreObject *propObject; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); + if (object) { @@ -1605,7 +1620,8 @@ sogo_properties_get_properties (void *object, MAPIStoreObject *propObject; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); + if (object) { @@ -1639,7 +1655,8 @@ sogo_properties_set_properties (void *object, struct SRow *aRow) MAPIStoreObject *propObject; int rc; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); + if (object) { @@ -1674,7 +1691,7 @@ sogo_manager_generate_uri (TALLOC_CTX *mem_ctx, NSAutoreleasePool *pool; NSString *partialURLString, *username, *directory; - DEBUG (5, ("[SOGo: %s:%d]\n", __FUNCTION__, __LINE__)); + OC_DEBUG(5, "[SOGo]"); if (uri == NULL) return MAPISTORE_ERR_INVALID_PARAMETER; @@ -1693,7 +1710,10 @@ sogo_manager_generate_uri (TALLOC_CTX *mem_ctx, username = [NSString stringWithUTF8String: (user ? user : "*")]; /* Do proper directory lookup here */ directory = [NSString stringWithUTF8String: (folder ? folder : "*")]; - partialURLString = [NSString stringWithFormat: @"sogo://%@:*@%@", username, directory]; + partialURLString = [NSString stringWithFormat: @"sogo://%@:*@%@", + [username stringByReplacingOccurrencesOfString: @"@" + withString: @"%40"], + directory]; } if (![partialURLString hasSuffix: @"/"]) partialURLString = [partialURLString stringByAppendingString: @"/"]; @@ -1776,7 +1796,7 @@ int mapistore_init_backend(void) /* Register ourselves with the MAPISTORE subsystem */ ret = mapistore_backend_register (&backend); if (ret != MAPISTORE_SUCCESS) - DEBUG(0, ("Failed to register the '%s' mapistore backend!\n", backend.backend.name)); + OC_DEBUG(0, "[SOGo] Failed to register the '%s' mapistore backend!\n", backend.backend.name); } return ret; diff --git a/OpenChange/MAPIStoreSharingMessage.h b/OpenChange/MAPIStoreSharingMessage.h new file mode 100644 index 000000000..7db7f552f --- /dev/null +++ b/OpenChange/MAPIStoreSharingMessage.h @@ -0,0 +1,101 @@ +/* MAPIStoreSharingMessage.h - this file is part of SOGo-OpenChange + * + * Copyright (C) 2015 Enrique J. Hernández + * + * Author: Enrique J. Hernández + * + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef MAPISTORESHARINGMESSAGE_H +#define MAPISTORESHARINGMESSAGE_H + +#import "MAPIStoreMailMessage.h" +#import "MAPIStoreObjectProxy.h" + +#define SHARING_SPECIAL_FOLDER 0x40290 + +@interface MAPIStoreSharingMessage : MAPIStoreObjectProxy +{ + struct mapistore_connection_info *connInfo; + NSMutableDictionary *properties; +} + +- (id) initWithMailHeaders: (NSDictionary *) mailHeaders + andConnectionInfo: (struct mapistore_connection_info *) newConnInfo + fromMessage: (MAPIStoreMailMessage *) msg; + +/* getters */ +- (int) getPidLidSharingCapabilities: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidNameXSharingCapabilities: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidLidSharingConfigurationUrl: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidNameXSharingConfigUrl: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidLidSharingFlavor: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidNameXSharingFlavor: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidLidSharingInitiatorEntryId: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidLidSharingInitiatorName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidLidSharingInitiatorSmtp: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidLidSharingLocalType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidNameXSharingLocalType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidLidSharingProviderGuid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidNameXSharingProviderGuid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidLidSharingProviderName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidNameXSharingProviderName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidLidSharingProviderUrl: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidNameXSharingProviderUrl: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidLidSharingRemoteName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidNameXSharingRemoteName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidLidSharingRemoteStoreUid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidNameXSharingRemoteStoreUid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidLidSharingRemoteType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidTypeXSharingRemoteType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidLidSharingResponseTime: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidLidSharingResponseType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidNameContentClass: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; + +/* Save */ +- (void) saveWithMessage: (MAPIStoreMailMessage *) msg + andSOGoObject: (SOGoMailObject *) sogoObject; + +@end + +#endif /* MAPISTORECALENDARWRAPPER_H */ diff --git a/OpenChange/MAPIStoreSharingMessage.m b/OpenChange/MAPIStoreSharingMessage.m new file mode 100644 index 000000000..e1e0e79da --- /dev/null +++ b/OpenChange/MAPIStoreSharingMessage.m @@ -0,0 +1,463 @@ +/* MAPIStoreSharingMessage.m - this file is part of SOGo-OpenChange + * + * Copyright (C) 2015 Enrique J. Hernández + * + * Author: Enrique J. Hernández + * + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#import +#import +#import + +#import +#import + +#import "NSData+MAPIStore.h" +#import "NSDate+MAPIStore.h" +#import "NSString+MAPIStore.h" +#import "NSValue+MAPIStore.h" + +#import "MAPIStoreMailFolder.h" +#import "MAPIStoreSharingMessage.h" +#import "MAPIStoreTypes.h" + +#include +#include +#include + +@implementation MAPIStoreSharingMessage + +- (id) init +{ + if ((self = [super init])) + { + connInfo = NULL; + properties = nil; + } + + return self; +} + +- (id) initWithMailHeaders: (NSDictionary *) mailHeaders + andConnectionInfo: (struct mapistore_connection_info *) newConnInfo + fromMessage: (MAPIStoreMailMessage *) msg +{ + NSEnumerator *enumerator; + NSString *key; + + if ((self = [self init])) + { + connInfo = newConnInfo; + enumerator = [mailHeaders keyEnumerator]; + properties = [NSMutableDictionary new]; + while ((key = [enumerator nextObject])) + { + if ([key hasPrefix: @"x-ms-sharing-"]) + [properties setObject: [mailHeaders objectForKey: key] + forKey: key]; + } + + /* Set request properties from container folder */ + NSDictionary *requestProps = [(MAPIStoreMailFolder *)[msg container] + extraPropertiesForMessage: [msg nameInContainer]]; + if (requestProps) + [properties addEntriesFromDictionary: requestProps]; + } + + return self; +} + +- (void) dealloc +{ + [properties release]; + [super dealloc]; +} + +- (enum mapistore_error) _getStringProperty: (NSString *) propertyName + inData: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND; + id value; + + value = [properties objectForKey: propertyName]; + if (value) + { + *data = [value asUnicodeInMemCtx: memCtx]; + rc = MAPISTORE_SUCCESS; + } + + return rc; + +} + +- (int) getPidLidSharingCapabilities: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + + enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND; + id value; + + value = [properties objectForKey: @"x-ms-sharing-capabilities"]; + if (value) + { + *data = MAPILongValue (memCtx, [value intValue]); + rc = MAPISTORE_SUCCESS; + } + + return rc; +} + +- (int) getPidNameXSharingCapabilities: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + + enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND; + id value; + + value = [properties objectForKey: @"x-ms-sharing-capabilities"]; + if (value) + { + *data = [[NSString stringWithFormat:@"%X", [value intValue]] + asUnicodeInMemCtx: memCtx]; + rc = MAPISTORE_SUCCESS; + } + + return rc; +} + +- (int) getPidLidSharingConfigurationUrl: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + + *data = [@"" asUnicodeInMemCtx: memCtx]; + return MAPISTORE_SUCCESS; +} + +- (int) getPidNameXSharingConfigUrl: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPidLidSharingConfigurationUrl: data inMemCtx: memCtx]; +} + +- (int) getPidLidSharingFlavor: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + /* See [MS-OXSHARE] Section 2.2.2.5 for details */ + enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND; + id value, auxValue; + + value = [properties objectForKey: @"x-ms-sharing-flavor"]; + if (value) + { + *data = MAPILongValue (memCtx, [value intValue]); + rc = MAPISTORE_SUCCESS; + } + else + { + /* Guess its value required by old clients based on other properties */ + value = [properties objectForKey: @"x-ms-sharing-capabilities"]; + if (value) + { + if ([value intValue] == SHARING_SPECIAL_FOLDER) + { + value = [properties objectForKey: @"x-ms-sharing-responsetime"]; + auxValue = [properties objectForKey: @"x-ms-sharing-remoteuid"]; + if (value) /* A sharing request */ + { + if (auxValue) + *data = MAPILongValue (memCtx, SHARING_INVITATION_REQUEST_FOLDER); + else + *data = MAPILongValue (memCtx, SHARING_REQUEST_SPECIAL_FOLDER); + } + else + { + if (auxValue) /* It SHOULD be an invitation or response */ + *data = MAPILongValue (memCtx, SHARING_INVITATION_SPECIAL_FOLDER); + else /* No remote info, then denial */ + *data = MAPILongValue (memCtx, SHARING_DENY_REQUEST); + } + } + else + { + *data = MAPILongValue (memCtx, SHARING_INVITATION_FOLDER); + } + rc = MAPISTORE_SUCCESS; + } + } + + return rc; +} + +- (int) getPidNameXSharingFlavor: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND; + id value; + + value = [properties objectForKey: @"x-ms-sharing-flavor"]; + if (value) + { + *data = [[NSString stringWithFormat:@"%X", [value intValue]] + asUnicodeInMemCtx: memCtx]; + rc = MAPISTORE_SUCCESS; + } + + return rc; +} + +- (int) getPidLidSharingInitiatorEntryId: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND; + id value; + NSData *entryId; + + value = [properties objectForKey: @"x-ms-sharing-initiatorentryid"]; + if (value) + { + entryId = [value dataByDecodingBase64]; + if (entryId) + { + *data = [entryId asBinaryInMemCtx: memCtx]; + rc = MAPISTORE_SUCCESS; + } + } + + return rc; +} + +- (int) getPidLidSharingInitiatorName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getStringProperty: @"x-ms-sharing-initiatorname" + inData: data + inMemCtx: memCtx]; +} + +- (int) getPidLidSharingInitiatorSmtp: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getStringProperty: @"x-ms-sharing-initiatorsmtp" + inData: data + inMemCtx: memCtx]; +} + +- (int) getPidLidSharingLocalType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getStringProperty: @"x-ms-sharing-localtype" + inData: data + inMemCtx: memCtx]; +} + +- (int) getPidNameXSharingLocalType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPidLidSharingLocalType: data inMemCtx: memCtx]; +} + +- (int) getPidLidSharingProviderGuid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + const unsigned char providerGuidBytes[] = {0xAE, 0xF0, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}; + NSData *providerGuid = [NSData dataWithBytes: providerGuidBytes length: 16]; + *data = [providerGuid asBinaryInMemCtx: memCtx]; + return MAPISTORE_SUCCESS; +} + +- (int) getPidNameXSharingProviderGuid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + *data = [@"AEF0060000000000C000000000000046" asUnicodeInMemCtx: memCtx]; + return MAPISTORE_SUCCESS; +} + +- (int) getPidLidSharingProviderName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + + return [self _getStringProperty: @"x-ms-sharing-providername" + inData: data + inMemCtx: memCtx]; +} + +- (int) getPidNameXSharingProviderName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPidLidSharingProviderName: data inMemCtx: memCtx]; +} + +- (int) getPidLidSharingProviderUrl: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getStringProperty: @"x-ms-sharing-providerurl" + inData: data + inMemCtx: memCtx]; +} + +- (int) getPidNameXSharingProviderUrl: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPidLidSharingProviderUrl: data inMemCtx: memCtx]; +} + +- (int) getPidLidSharingRemoteName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getStringProperty: @"x-ms-sharing-remotename" + inData: data + inMemCtx: memCtx]; +} + +- (int) getPidNameXSharingRemoteName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPidLidSharingRemoteName: data inMemCtx: memCtx]; +} + +- (int) getPidLidSharingRemoteStoreUid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getStringProperty: @"x-ms-sharing-remotestoreuid" + inData: data + inMemCtx: memCtx]; +} + +- (int) getPidNameXSharingRemoteStoreUid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPidLidSharingRemoteStoreUid: data inMemCtx: memCtx]; +} + +- (int) getPidLidSharingRemoteType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getStringProperty: @"x-ms-sharing-remotetype" + inData: data + inMemCtx: memCtx]; +} + +- (int) getPidTypeXSharingRemoteType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPidLidSharingRemoteType: data inMemCtx: memCtx]; +} + +- (int) getPidLidSharingRemoteUid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self _getStringProperty: @"x-ms-sharing-remoteuid" + inData: data + inMemCtx: memCtx]; +} + +- (int) getPidUidXSharingRemoteUid: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getPidLidSharingRemoteUid: data inMemCtx: memCtx]; +} + +- (int) getPidLidSharingResponseTime: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND; + id value; + + value = [properties objectForKey: MAPIPropertyKey (PidLidSharingResponseTime)]; + if (!value) + { + value = [properties objectForKey: @"x-ms-sharing-responsetime"]; + if (value) + { + /* Here the value is a GSCBufferString */ + NSString * responseTime = [NSString stringWithUTF8String: [value UTF8String]]; + value = [NSDate dateWithString: responseTime]; + } + } + + if (value) + { + *data = [value asFileTimeInMemCtx: memCtx]; + rc = MAPISTORE_SUCCESS; + } + + return rc; +} + +- (int) getPidLidSharingResponseType: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + + enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND; + id value; + + value = [properties objectForKey: MAPIPropertyKey (PidLidSharingResponseType)]; + if (!value) + value = [properties objectForKey: @"x-ms-sharing-responsetype"]; + + if (value) + { + *data = MAPILongValue (memCtx, [value intValue]); + rc = MAPISTORE_SUCCESS; + } + + return rc; +} + +- (int) getPidNameContentClass: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + *data = talloc_strdup (memCtx, "Sharing"); + return MAPISTORE_SUCCESS; +} + +- (void) saveWithMessage: (MAPIStoreMailMessage *) msg + andSOGoObject: (SOGoMailObject *) sogoObject +{ + /* Store PidLidSharingResponseType and PidLidSharingResponseTime if + required in versions message from the container as I don't see + other straightforward place to put that information */ + id response; + NSDictionary *propsToStore; + + response = [[msg properties] objectForKey: MAPIPropertyKey (PidLidSharingResponseType)]; + if (response) + { + /* FIXME: Is there any better way to increase the modseq? */ + [sogoObject addFlags: @"\\Draft"]; + [sogoObject removeFlags: @"\\Draft"]; + + /* Store this modification in container folder along the property values */ + propsToStore = [NSDictionary dictionaryWithObjects: + [NSArray arrayWithObjects: response, + [[msg properties] objectForKey: MAPIPropertyKey (PidLidSharingResponseTime)], + nil] + forKeys: + [NSArray arrayWithObjects: MAPIPropertyKey (PidLidSharingResponseType), + MAPIPropertyKey (PidLidSharingResponseTime), nil]]; + + [(MAPIStoreMailFolder *)[msg container] setExtraProperties: propsToStore + forMessage: [msg nameInContainer]]; + } +} + +@end diff --git a/OpenChange/MAPIStoreTasksMessage.m b/OpenChange/MAPIStoreTasksMessage.m index 9215bc409..20bf67f8b 100644 --- a/OpenChange/MAPIStoreTasksMessage.m +++ b/OpenChange/MAPIStoreTasksMessage.m @@ -367,13 +367,15 @@ NSString *status, *priority, *tzName; NSCalendarDate *now; NSInteger tzOffset; + NSTimeZone *userTZ; double doubleValue; vToDo = [sogoObject component: YES secure: NO]; vCalendar = [vToDo parent]; [vCalendar setProdID: @"-//Inverse inc.//OpenChange+SOGo//EN"]; - tzName = [[[self userContext] timeZone] name]; + userTZ = [[self userContext] timeZone]; + tzName = [userTZ name]; tz = [iCalTimeZone timeZoneForName: tzName]; [vCalendar addTimeZone: tz]; @@ -428,6 +430,13 @@ { date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"dtstart"]; [date setTimeZone: tz]; + /* The property is set to user's local time zone. + See: [MS-OXOTASK] 2.2.2.2.4 + TODO: Ignore when the PT_SYSTIME is 0x5AE980E0*/ + tzOffset = [userTZ secondsFromGMTForDate: value]; + value = [value dateByAddingYears: 0 months: 0 days: 0 + hours: 0 minutes: 0 + seconds: -tzOffset]; [date setDateTime: value]; } @@ -437,6 +446,13 @@ { date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"due"]; [date setTimeZone: tz]; + /* The property is set to user's local time zone. + See: [MS-OXOTASK] 2.2.2.2.5 + TODO: Ignore when the PT_SYSTIME is 0x5AE980E0*/ + tzOffset = [userTZ secondsFromGMTForDate: value]; + value = [value dateByAddingYears: 0 months: 0 days: 0 + hours: 0 minutes: 0 + seconds: -tzOffset]; [date setDateTime: value]; } @@ -445,7 +461,9 @@ if (value) { date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"completed"]; - tzOffset = [[value timeZone] secondsFromGMTForDate: value]; + /* The property is set to midnight in local time zone converted to UTC: + See: [MS-OXOTASK] 2.2.2.2.9 */ + tzOffset = [userTZ secondsFromGMTForDate: value]; value = [value dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -tzOffset]; diff --git a/OpenChange/MAPIStoreTypes.m b/OpenChange/MAPIStoreTypes.m index f8e32c824..76e72c5d6 100644 --- a/OpenChange/MAPIStoreTypes.m +++ b/OpenChange/MAPIStoreTypes.m @@ -119,6 +119,7 @@ NSObjectFromMAPISPropValue (const struct mapi_SPropValue *value) break; case PT_SYSTIME: result = [NSCalendarDate dateFromFileTime: &(value->value.ft)]; + [result setTimeZone: utcTZ]; break; case PT_BINARY: case PT_SVREID: @@ -154,8 +155,7 @@ NSObjectFromMAPISPropValue (const struct mapi_SPropValue *value) // #define PT_I8 0x14 // #define PT_SRESTRICT 0xFD // #define PT_ACTIONS 0xFE - result = [NSNull null]; - abort(); + result = nil; NSLog (@"%s: object type not handled: %d (0x%.4x)", __PRETTY_FUNCTION__, valueType, valueType); } @@ -250,8 +250,7 @@ NSObjectFromSPropValue (const struct SPropValue *value) // #define PT_I8 0x14 // #define PT_SRESTRICT 0xFD // #define PT_ACTIONS 0xFE - result = [NSNull null]; - abort(); + result = nil; NSLog (@"%s: object type not handled: %d (0x%.4x)", __PRETTY_FUNCTION__, valueType, valueType); } diff --git a/OpenChange/MAPIStoreUserContext.m b/OpenChange/MAPIStoreUserContext.m index 04300fd35..159748519 100644 --- a/OpenChange/MAPIStoreUserContext.m +++ b/OpenChange/MAPIStoreUserContext.m @@ -311,9 +311,9 @@ static NSMapTable *contextsTable = nil; parts in this url. We strip the '-' character in case we have this in the domain part - like foo@bar-zot.com */ ocFSTableName = [NSString stringWithFormat: @"sogo_cache_folder_%@", - [[[user loginInDomain] asCSSIdentifier] - stringByReplacingOccurrencesOfString: @"-" - withString: @"_"]]; + [[[user login] asCSSIdentifier] + stringByReplacingOccurrencesOfString: @"-" + withString: @"_"]]; [parts replaceObjectAtIndex: 4 withObject: ocFSTableName]; folderTableURL = [NSURL URLWithString: [parts componentsJoinedByString: @"/"]]; diff --git a/OpenChange/RTFHandler.m b/OpenChange/RTFHandler.m index 1e15ebaa8..e7be0f14e 100644 --- a/OpenChange/RTFHandler.m +++ b/OpenChange/RTFHandler.m @@ -26,6 +26,7 @@ // Useful macros // #define ADVANCE _bytes++; _current_pos++; +#define ADVANCE_N(N) _bytes += (N); _current_pos += (N); #define REWIND _bytes--; _current_pos--; @@ -412,16 +413,46 @@ const unsigned short ansicpg874[256] = { - (const char *) parseControlWord: (unsigned int *) len { const char *start, *end; - + start = ADVANCE; - - while (isalnum(*_bytes) || *_bytes == '-') + + /* + A control word is defined by: + + \ + */ + while (isalpha(*_bytes)) { ADVANCE; } + + /* + The can be one of the following: + + - A space. This serves only to delimit a control word and is + ignored in subsequent processing. + + - A numeric digit or an ASCII minus sign (-), which indicates + that a numeric parameter is associated with the control word. + Only this case requires to include it in the control word. + + - Any character other than a letter or a digit + */ + if (*_bytes == '-' || isdigit(*_bytes)) + { + ADVANCE; + while (isdigit(*_bytes)) // TODO: Allow up to 10 digits + { + ADVANCE; + } + } + /* In this case, the delimiting character terminates the control + word and is not part of the control word. */ + end = _bytes; - + *len = end-start-1; + return start+1; } @@ -508,6 +539,7 @@ const unsigned short ansicpg874[256] = { fontTable = [[[RTFFontTable alloc] init] autorelease]; fontName = nil; + fontInfo = nil; count = 0; do @@ -543,7 +575,7 @@ const unsigned short ansicpg874[256] = { // Skip our control word if (strncmp((const char*)cw, "fonttbl", len) == 0) continue; - + // We must at least parse s = [[NSString alloc] initWithBytesNoCopy: (void *)cw+1 length: len-1 @@ -558,12 +590,18 @@ const unsigned short ansicpg874[256] = { // We now parse cw = [self parseControlWord: &len]; + if (len == 0) // Possibly parsing a space + cw = [self parseControlWord: &len]; + fontInfo->family = [[NSString alloc] initWithBytesNoCopy: (void *)cw+1 length: len-1 encoding: NSASCIIStringEncoding freeWhenDone: NO]; cw = [self parseControlWord: &len]; + if (len == 0) // Possibly parsing a space + cw = [self parseControlWord: &len]; + fontInfo->charset = [[NSString alloc] initWithBytesNoCopy: (void *)cw+1 length: len-1 encoding: NSASCIIStringEncoding @@ -622,13 +660,44 @@ const unsigned short ansicpg874[256] = { } while (count != 0); } +- (void) parseIgnoringEverything +{ + unsigned int count = 1; + // Ignore everything. But we cannot parse it blindly because it could have + // binary data with '}' and '{' bytes, so disasters can happen and they will + do + { + if (*_bytes == '\\') + { + unsigned int binary_size, len = 0, cw_len; + const char *cw = [self parseControlWord: &len]; + cw_len = strlen("bin"); + if (strncmp(cw, "bin", cw_len) == 0 && len > cw_len) + { + NSString *s; + s = [[NSString alloc] initWithBytesNoCopy: (void *) cw + cw_len + length: len - cw_len + encoding: NSASCIIStringEncoding + freeWhenDone: NO]; + [s autorelease]; + binary_size = [s intValue]; + ADVANCE_N(binary_size); + } + } + + if (*_bytes == '{') count++; + if (*_bytes == '}') count--; + ADVANCE; + } + while (count > 0); +} + // // // - (void) parsePicture { - // Do the same as -parseStyleSheet for now, that is, ignore everything. - [self parseStyleSheet]; + [self parseIgnoringEverything]; } // @@ -648,6 +717,7 @@ const unsigned short ansicpg874[256] = { fontTable = nil; colorTable = nil; charset = NULL; + formattingOptions = nil; _html = [[NSMutableData alloc] init]; [_html appendBytes: "" length: 34]; @@ -694,19 +764,7 @@ const unsigned short ansicpg874[256] = { } else if (*(_bytes+1) == '*') { - int cc = 1; - - do - { - if (*_bytes == '{') - cc++; - if (*_bytes == '}') - cc--; - - ADVANCE; - } - while (cc != 0); - + [self parseIgnoringEverything]; continue; } @@ -726,16 +784,16 @@ const unsigned short ansicpg874[256] = { { // We rewind our buffer so we start at the beginning of {\fonttbl... _bytes = cw-2; - _current_pos -= 10; + _current_pos -= 9; // Length: {\fonttbl fontTable = [self parseFontTable]; - // We go back 1 byte in order to end our section properly ('}' character + // We go back 1 byte in order to end our section properly ('}' character) REWIND; } else if (strncmp(cw, "stylesheet", 10) == 0) { _bytes = cw-2; - _current_pos -= 13; + _current_pos -= 12; // Length: {\stylesheet [self parseStyleSheet]; REWIND; } @@ -746,7 +804,7 @@ const unsigned short ansicpg874[256] = { else if (strncmp(cw, "pict", 4) == 0) { _bytes = cw-2; - _current_pos -= 7; + _current_pos -= 6; // Length: {\pict [self parsePicture]; REWIND; } @@ -766,19 +824,19 @@ const unsigned short ansicpg874[256] = { int color_index; char *v; + if (!formattingOptions) continue; + color_index = [[s substringFromIndex: 2] intValue]; - - if (!formattingOptions) - continue; - - if (formattingOptions->color_index >= 0) // && color_index != formattingOptions->color_index) + colorDef = [colorTable colorDefAtIndex: color_index]; + if (!colorDef) continue; + + if (formattingOptions->color_index >= 0) { [_html appendBytes: "" length: 7]; } - - formattingOptions->color_index = color_index; - colorDef = [colorTable colorDefAtIndex: color_index]; - + + formattingOptions->color_index = color_index; + v = malloc(23*sizeof(char)); memset(v, 0, 23); sprintf(v, "", colorDef->red, colorDef->green, colorDef->blue); @@ -814,20 +872,41 @@ const unsigned short ansicpg874[256] = { if (!formattingOptions) continue; - + if (formattingOptions->font_index >= 0 && font_index != formattingOptions->font_index) { [_html appendBytes: "" length: 7]; } - + formattingOptions->font_index = font_index; fontInfo = [fontTable fontInfoAtIndex: font_index]; - - char *v = malloc(128*sizeof(char)); - memset(v, 0, 128); - sprintf(v, "", [fontInfo->name UTF8String]); + char *v = NULL; + if (fontInfo && fontInfo->name) + { + if (fontInfo->name.length < 128) + { + int tag_size = 15 + fontInfo->name.length; + v = calloc(tag_size, sizeof(char)); + snprintf(v, tag_size, "", [fontInfo->name UTF8String]); + } + else + { + NSLog(@"RTFHandler: Font %u has %d chars length, parse error? " + "Ignored", font_index, fontInfo->name.length); + v = calloc(7, sizeof(char)); + sprintf(v, ""); + } + } + else + { + // RTF badformed? We don't know about that font (font_index). + // Anyhow, we still open the html tag because in the future + // we will close it (e.g. when new font is used). + v = calloc(7, sizeof(char)); + sprintf(v, ""); + } [_html appendBytes: v length: strlen(v)]; free(v); } diff --git a/OpenChange/dbmsgreader.m b/OpenChange/dbmsgreader.m index 8e56655b8..3d892ca34 100644 --- a/OpenChange/dbmsgreader.m +++ b/OpenChange/dbmsgreader.m @@ -39,7 +39,7 @@ #import #import "NSObject+PropertyList.h" -Class MAPIStoreUserContextK, SOGoMAPIDBObjectK; +Class MAPIStoreUserContextK, SOGoCacheGCSObjectK; static void DumpBSONData(NSData *data) @@ -60,7 +60,7 @@ DbDumpObject (NSString *username, NSString *path) ctx = [MAPIStoreUserContextK userContextWithUsername: username andTDBIndexing: NULL]; - dbobject = [SOGoMAPIDBObjectK new]; + dbobject = [SOGoCacheGCSObjectK new]; [dbobject setTableUrl: [ctx folderTableURL]]; record = [dbobject lookupRecord: path newerThanVersion: -1]; if (record) @@ -107,7 +107,7 @@ int main (int argc, char *argv[], char *envp[]) [loader loadProducts: [NSArray arrayWithObject: BACKEND_BUNDLE_NAME]]; MAPIStoreUserContextK = NSClassFromString (@"MAPIStoreUserContext"); - SOGoMAPIDBObjectK = NSClassFromString (@"SOGoMAPIDBObject"); + SOGoCacheGCSObjectK = NSClassFromString (@"SOGoCacheGCSObject"); arguments = [[NSProcessInfo processInfo] arguments]; if ([arguments count] > 2) { diff --git a/OpenChange/iCalEvent+MAPIStore.m b/OpenChange/iCalEvent+MAPIStore.m index 40eb0c0ef..90bf4e8bd 100644 --- a/OpenChange/iCalEvent+MAPIStore.m +++ b/OpenChange/iCalEvent+MAPIStore.m @@ -74,6 +74,7 @@ @implementation iCalEvent (MAPIStoreProperties) - (void) _setupEventRecurrence: (NSData *) mapiRecurrenceData + inTimeZone: (NSTimeZone *) tz inMemCtx: (TALLOC_CTX *) memCtx { struct Binary_r *blob; @@ -81,9 +82,18 @@ blob = [mapiRecurrenceData asBinaryInMemCtx: memCtx]; pattern = get_AppointmentRecurrencePattern (memCtx, blob); - [(iCalCalendar *) parent - setupRecurrenceWithMasterEntity: self - fromRecurrencePattern: &pattern->RecurrencePattern]; + + if (pattern == NULL) { + [self logWithFormat: @"Error parsing recurrence pattern. No changes in event recurrence will be done"]; + return; + } + + [(iCalCalendar *) parent setupRecurrenceWithMasterEntity: self + fromRecurrencePattern: &pattern->RecurrencePattern + withExceptions: pattern->ExceptionInfo + andExceptionCount: pattern->ExceptionCount + inTimeZone: tz + ]; //talloc_free (blob); } @@ -131,6 +141,107 @@ } } +- (int) _updateFromAttendeeMAPIProperties: (NSArray *) recipients + withRole: (NSString *) role + outParam: (BOOL *) organizerIsSet +{ + NSDictionary *dict; + NSString *attEmail; + iCalPerson *person; + iCalPersonPartStat newPartStat; + NSNumber *flags, *trackStatus; + int i, effective; + + effective = 0; + for (i = 0; i < [recipients count]; i++) + { + dict = [recipients objectAtIndex: i]; + person = [iCalPerson new]; + [person setCn: [dict objectForKey: @"fullName"]]; + attEmail = [dict objectForKey: @"email"]; + [person setEmail: attEmail]; + + flags = [dict objectForKey: MAPIPropertyKey (PR_RECIPIENT_FLAGS)]; + if (!flags) + { + [self logWithFormat: + @"no recipient flags specified: skipping recipient"]; + continue; + } + + if (([flags unsignedIntValue] & 0x0002)) /* recipOrganizer */ + { + [self setOrganizer: person]; + *organizerIsSet = YES; + [self logWithFormat: @"organizer set via recipient flags"]; + } + else + { + BOOL isOrganizer = NO; + + // /* Work-around: it happens that Outlook still passes the + // organizer as a recipient, maybe because of a feature + // documented in a pre-mesozoic PDF still buried in a + // cavern... In that case we remove it, and we keep the + // number of effective recipients in "effective". If the + // total is 0, we remove the "ORGANIZER" too. */ + // if ([attEmail isEqualToString: orgEmail]) + // { + // [self logWithFormat: + // @"avoiding setting organizer as recipient"]; + // continue; + // } + + trackStatus = [dict objectForKey: MAPIPropertyKey (PidTagRecipientTrackStatus)]; + if (trackStatus) + { + /* FIXME: we should provide a data converter between OL + partstats and SOGo */ + switch ([trackStatus unsignedIntValue]) + { + case 0x01: /* respOrganized */ + isOrganizer = YES; + break; + case 0x02: /* respTentative */ + newPartStat = iCalPersonPartStatTentative; + break; + case 0x03: /* respAccepted */ + newPartStat = iCalPersonPartStatAccepted; + break; + case 0x04: /* respDeclined */ + newPartStat = iCalPersonPartStatDeclined; + break; + default: + newPartStat = iCalPersonPartStatNeedsAction; + } + + if (isOrganizer) + { + [self setOrganizer: person]; + *organizerIsSet = YES; + [self logWithFormat: @"organizer set via track status"]; + } + else + { + [person setParticipationStatus: newPartStat]; + [person setRsvp: @"TRUE"]; + [person setRole: role]; + [self addToAttendees: person]; + effective++; + } + } + else + [self errorWithFormat: @"skipped recipient due" + @" to missing track status"]; + } + + [person release]; + } + + return effective; +} + + - (void) updateFromMAPIProperties: (NSDictionary *) properties inUserContext: (MAPIStoreUserContext *) userContext withActiveUser: (SOGoUser *) activeUser @@ -140,7 +251,7 @@ iCalDateTime *start, *end; iCalTimeZone *tz; NSTimeZone *userTimeZone; - NSString *priority; + NSString *priority, *class = nil; NSUInteger responseStatus = 0; NSInteger tzOffset; SOGoUser *ownerUser; @@ -207,16 +318,7 @@ value = [properties objectForKey: MAPIPropertyKey (PidLidExceptionReplaceTime)]; if (value) - { - if (!isAllDay) - { - tzOffset = [userTimeZone secondsFromGMTForDate: value]; - value = [value dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: tzOffset]; - } - [self setRecurrenceId: value]; - } + [self setRecurrenceId: value]; // start value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentStartWhole)]; @@ -241,13 +343,7 @@ [start setTimeZone: nil]; } else - { - tzOffset = [userTimeZone secondsFromGMTForDate: value]; - value = [value dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: tzOffset]; [start setDateTime: value]; - } } /* end */ @@ -273,13 +369,7 @@ [end setTimeZone: nil]; } else - { - tzOffset = [[value timeZone] secondsFromGMTForDate: value]; - value = [value dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: 0 - seconds: tzOffset]; [end setDateTime: value]; - } } /* priority */ @@ -302,6 +392,36 @@ priority = @"0"; // None [self setPriority: priority]; + /* class */ + /* See [MS-OXCICAL] Section 2.1.3.11.20.4 */ + value = [properties objectForKey: MAPIPropertyKey(PR_SENSITIVITY)]; + if (value) + { + switch ([value intValue]) + { + case 1: + class = @"X-PERSONAL"; + break; + case 2: + class = @"PRIVATE"; + break; + case 3: + class = @"CONFIDENTIAL"; + break; + default: /* 0 as well */ + class = @"PUBLIC"; + } + } + + if (class) + [self setAccessClass: class]; + + /* Categories */ + /* See [MS-OXCICAL] Section 2.1.3.1.1.20.3 */ + value = [properties objectForKey: MAPIPropertyKey (PidNameKeywords)]; + if (value) + [self setCategories: value]; + /* show time as free/busy/tentative/out of office. Possible values are: 0x00000000 - olFree 0x00000001 - olTentative @@ -347,7 +467,7 @@ value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentRecur)]; if (value) - [self _setupEventRecurrence: value inMemCtx: memCtx]; + [self _setupEventRecurrence: value inTimeZone: userTimeZone inMemCtx: memCtx]; /* alarm */ [self _setupEventAlarmFromProperties: properties]; @@ -356,106 +476,28 @@ value = [properties objectForKey: @"recipients"]; if (value) { - NSArray *recipients; NSDictionary *dict; - NSString *orgEmail, *sentBy, *attEmail; + NSString *orgEmail, *sentBy; iCalPerson *person; iCalPersonPartStat newPartStat; - NSNumber *flags, *trackStatus; - int i, effective; + int effective; BOOL organizerIsSet = NO; [self setOrganizer: nil]; [self removeAllAttendees]; - recipients = [value objectForKey: @"to"]; - effective = 0; - for (i = 0; i < [recipients count]; i++) - { - dict = [recipients objectAtIndex: i]; - person = [iCalPerson new]; - [person setCn: [dict objectForKey: @"fullName"]]; - attEmail = [dict objectForKey: @"email"]; - [person setEmail: attEmail]; - - flags = [dict objectForKey: MAPIPropertyKey (PR_RECIPIENT_FLAGS)]; - if (!flags) - { - [self logWithFormat: - @"no recipient flags specified: skipping recipient"]; - continue; - } + /* In [MS-OXOCAL] Section 2.2.4.10.7 says the recipient type is 0x01 as Required + and 0x02 as Optional and other documents such [MS-OXCMSG] 2.2.3.1.2 indicates + that MAPI_TO is 0x01 and MAPI_CC is 0x02, that's why in SOGo is in 'to' and 'cc' + respectively. */ + effective = [self _updateFromAttendeeMAPIProperties: [value objectForKey: @"to"] + withRole: @"REQ-PARTICIPANT" + outParam: &organizerIsSet]; + effective += [self _updateFromAttendeeMAPIProperties: [value objectForKey: @"cc"] + withRole: @"OPT-PARTICIPANT" + outParam: &organizerIsSet]; - if (([flags unsignedIntValue] & 0x0002)) /* recipOrganizer */ - { - [self setOrganizer: person]; - organizerIsSet = YES; - [self logWithFormat: @"organizer set via recipient flags"]; - } - else - { - BOOL isOrganizer = NO; - - // /* Work-around: it happens that Outlook still passes the - // organizer as a recipient, maybe because of a feature - // documented in a pre-mesozoic PDF still buried in a - // cavern... In that case we remove it, and we keep the - // number of effective recipients in "effective". If the - // total is 0, we remove the "ORGANIZER" too. */ - // if ([attEmail isEqualToString: orgEmail]) - // { - // [self logWithFormat: - // @"avoiding setting organizer as recipient"]; - // continue; - // } - - trackStatus = [dict objectForKey: MAPIPropertyKey (PidTagRecipientTrackStatus)]; - if (trackStatus) - { - /* FIXME: we should provide a data converter between OL - partstats and SOGo */ - switch ([trackStatus unsignedIntValue]) - { - case 0x01: /* respOrganized */ - isOrganizer = YES; - break; - case 0x02: /* respTentative */ - newPartStat = iCalPersonPartStatTentative; - break; - case 0x03: /* respAccepted */ - newPartStat = iCalPersonPartStatAccepted; - break; - case 0x04: /* respDeclined */ - newPartStat = iCalPersonPartStatDeclined; - break; - default: - newPartStat = iCalPersonPartStatNeedsAction; - } - - if (isOrganizer) - { - [self setOrganizer: person]; - organizerIsSet = YES; - [self logWithFormat: @"organizer set via track status"]; - } - else - { - [person setParticipationStatus: newPartStat]; - [person setRsvp: @"TRUE"]; - [person setRole: @"REQ-PARTICIPANT"]; - [self addToAttendees: person]; - effective++; - } - } - else - [self errorWithFormat: @"skipped recipient due" - @" to missing track status"]; - } - - [person release]; - } - - if (effective == 0) /* See work-around above */ + if (effective == 0) /* See work-around inside _updateFromAttendeeMAPIProperties */ [self setOrganizer: nil]; else { @@ -481,7 +523,7 @@ = [properties objectForKey: MAPIPropertyKey (PidLidResponseStatus)]; if (value) responseStatus = [value unsignedLongValue]; - + /* FIXME: we should provide a data converter between OL partstats and SOGo */ switch (responseStatus) @@ -503,23 +545,6 @@ value = [properties objectForKey: MAPIPropertyKey (PidLidAttendeeCriticalChange)]; if (value && ![value isNever]) [self setTimeStampAsDate: value]; - // if (newPartStat // != iCalPersonPartStatUndefined - // ) - // { - // // iCalPerson *participant; - - // // participant = [self userAsAttendee: ownerUser]; - // // [participant setParticipationStatus: newPartStat]; - // // [sogoObject saveComponent: self]; - - // [sogoObject changeParticipationStatus: newPartStat - // withDelegate: nil]; - // // [[self context] tearDownRequest]; - // } - // // }1005 - - // // else - // // { } } else @@ -534,7 +559,7 @@ [person setCn: [dict objectForKey: @"fullName"]]; orgEmail = [dict objectForKey: @"email"]; [person setEmail: orgEmail]; - + if (![activeUser isEqual: ownerUser]) { dict = [activeUser primaryIdentity]; diff --git a/OpenChange/iCalTimeZone+MAPIStore.m b/OpenChange/iCalTimeZone+MAPIStore.m index 8e9bf7c14..f692272db 100644 --- a/OpenChange/iCalTimeZone+MAPIStore.m +++ b/OpenChange/iCalTimeZone+MAPIStore.m @@ -51,6 +51,7 @@ NSArray *byMonth; iCalByDayMask *mask; NSCalendarDate *dateValue; + int16_t wDay; rrule = [self recurrenceRule]; byMonth = [rrule byMonth]; @@ -59,7 +60,14 @@ tzData->wMonth = [[byMonth objectAtIndex: 0] intValue]; mask = [rrule byDayMask]; tzData->wDayOfWeek = [mask firstDay]; - tzData->wDay = [mask firstOccurrence]; + wDay = [mask firstOccurrence]; + if (wDay < 0) + /* [MS-OXOCAL] the wDay field is set to indicate the + occurrence of the day of the week within the month (1 to + 5, where 5 indicates the final occurrence during the + month if that day of the week does not occur 5 times). */ + wDay += 6; + tzData->wDay = (uint16_t) wDay; dateValue = [self startDate]; tzData->wHour = [dateValue hourOfDay]; diff --git a/OpenChange/samba-get-config.py b/OpenChange/samba-get-config.py index c91c3ed44..06311d996 100755 --- a/OpenChange/samba-get-config.py +++ b/OpenChange/samba-get-config.py @@ -4,5 +4,6 @@ import sys import samba.param a = samba.param.LoadParm() +a.set('debug level', '0') a.load_default() print a.get(sys.argv[1]) diff --git a/SOPE/NGCards/NGCardsSaxHandler.m b/SOPE/NGCards/NGCardsSaxHandler.m index fca1a64e6..a70255863 100644 --- a/SOPE/NGCards/NGCardsSaxHandler.m +++ b/SOPE/NGCards/NGCardsSaxHandler.m @@ -55,8 +55,6 @@ static NSArray *privilegedTagNames = nil; - (void) dealloc { - if (content) - free (content); [self reset]; [cards release]; [currentGroup release]; diff --git a/SoObjects/SOGo/NSString+Utilities.m b/SoObjects/SOGo/NSString+Utilities.m index 70fd860ab..46f42af8e 100644 --- a/SoObjects/SOGo/NSString+Utilities.m +++ b/SoObjects/SOGo/NSString+Utilities.m @@ -560,9 +560,9 @@ static int cssEscapingCount; - (id) objectFromJSONString { SBJsonParser *parser; - NSObject *object; + NSArray *object; NSError *error; - NSString *unescaped; + NSString *unescaped, *json; object = nil; @@ -571,13 +571,16 @@ static int cssEscapingCount; parser = [SBJsonParser new]; [parser autorelease]; error = nil; - object = [parser objectWithString: self + + /* Parse it this way so we can parse simple values, like "null" */ + json = [NSString stringWithFormat: @"[%@]", self]; + object = [parser objectWithString: json error: &error]; if (error) { [self errorWithFormat: @"json parser: %@," @" attempting once more after unescaping...", error]; - unescaped = [self stringByReplacingString: @"\\\\" + unescaped = [json stringByReplacingString: @"\\\\" withString: @"\\"]; object = [parser objectWithString: unescaped error: &error]; @@ -591,7 +594,7 @@ static int cssEscapingCount; } } - return object; + return [object objectAtIndex: 0]; } - (NSString *) asSafeSQLString diff --git a/SoObjects/SOGo/SOGoCacheGCSFolder.h b/SoObjects/SOGo/SOGoCacheGCSFolder.h index 1b63ae683..3495dc0d6 100644 --- a/SoObjects/SOGo/SOGoCacheGCSFolder.h +++ b/SoObjects/SOGo/SOGoCacheGCSFolder.h @@ -49,6 +49,8 @@ andSortOrderings: (NSArray *) sortOrderings; - (void) changePathTo: (NSString *) newPath; +- (void) changePathTo: (NSString *) newPath + intoNewContainer: (id) newContainer; @end diff --git a/SoObjects/SOGo/SOGoCacheGCSFolder.m b/SoObjects/SOGo/SOGoCacheGCSFolder.m index f8d25d381..b29287fcb 100644 --- a/SoObjects/SOGo/SOGoCacheGCSFolder.m +++ b/SoObjects/SOGo/SOGoCacheGCSFolder.m @@ -308,6 +308,14 @@ Class SOGoCacheGCSObjectK = Nil; [super changePathTo: newPath]; } +- (void) changePathTo: (NSString *) newPath intoNewContainer: (id) newContainer +{ + [self changePathTo: newPath]; + container = newContainer; + if ([self doesRetainContainer]) + [container retain]; +} + // - (NSArray *) toOneRelationshipKeysMatchingQualifier: (EOQualifier *) qualifier // andSortOrderings: (NSArray *) sortOrderings // { diff --git a/SoObjects/SOGo/SOGoObject.h b/SoObjects/SOGo/SOGoObject.h index e6de44944..8ea1c6b77 100644 --- a/SoObjects/SOGo/SOGoObject.h +++ b/SoObjects/SOGo/SOGoObject.h @@ -83,6 +83,8 @@ - (BOOL) isInPublicZone; +- (BOOL) doesRetainContainer; + /* accessors */ - (void) setContext: (WOContext *) newContext; diff --git a/Tools/SOGoToolManageEAS.m b/Tools/SOGoToolManageEAS.m index 0d8a7d772..0e01da733 100644 --- a/Tools/SOGoToolManageEAS.m +++ b/Tools/SOGoToolManageEAS.m @@ -142,7 +142,7 @@ typedef enum parts in this url. We strip the '-' character in case we have this in the domain part - like foo@bar-zot.com */ ocFSTableName = [NSMutableString stringWithFormat: @"sogo_cache_folder_%@", - [[user loginInDomain] asCSSIdentifier]]; + [[user login] asCSSIdentifier]]; [ocFSTableName replaceOccurrencesOfString: @"-" withString: @"_" options: 0