diff --git a/ChangeLog b/ChangeLog index 5d0c1dff7..2bccbdf35 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,20 @@ * SoObjects/Appointments/SOGoAppointmentFolder.m (-_flattenCycleRecord:forRange:intoArray:): idem. +2011-01-14 Ludovic Marcotte + + * Work on the OpenChange backend: added more property + support such as priority for tasks, events and mails, + secondary email address support for contacts, nickname + and birthday support for contacts and a few more or + some fixes there and there. + * OpenChange/SOGoMAPIFSMessage.m: -MAPISave added a hack + to AVOID saving informations on the fs when dealing + with the "inbox" folder - as it'll crash Outlook upon + next restarts + * Added OpenChange/MAPIStoreNotesMessageTable.{h,m} to + later extend and improve Notes support. + 2011-01-13 Francis Lachapelle * SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m diff --git a/OpenChange/GNUmakefile b/OpenChange/GNUmakefile index 31b06655a..df96e7243 100644 --- a/OpenChange/GNUmakefile +++ b/OpenChange/GNUmakefile @@ -64,6 +64,7 @@ $(SOGOBACKEND)_OBJC_FILES += \ MAPIStoreContactsMessageTable.m \ MAPIStoreMailMessageTable.m \ MAPIStoreMailFolderTable.m \ + MAPIStoreNotesMessageTable.m \ MAPIStoreTasksMessageTable.m \ \ SOGoAppointmentObject+MAPIStore.m \ diff --git a/OpenChange/MAPIStoreCalendarMessageTable.m b/OpenChange/MAPIStoreCalendarMessageTable.m index ebdc4d774..50aa07aa5 100644 --- a/OpenChange/MAPIStoreCalendarMessageTable.m +++ b/OpenChange/MAPIStoreCalendarMessageTable.m @@ -124,9 +124,24 @@ *data = [[event created] asFileTimeInMemCtx: memCtx]; break; - case PidLidTimeZoneStruct: + case PR_IMPORTANCE: + { + unsigned int v; + event = [[self lookupChild: childKey] component: NO secure: NO]; + if ([[event priority] isEqualToString: @"9"]) + v = 0x0; + else if ([[event priority] isEqualToString: @"1"]) + v = 0x2; + else + v = 0x1; + + *data = MAPILongValue (memCtx, v); + } + break; + + // case PidLidTimeZoneStruct: // case PR_VD_NAME_UNICODE: // *data = talloc_strdup(memCtx, "PR_VD_NAME_UNICODE"); // break; diff --git a/OpenChange/MAPIStoreContactsMessageTable.m b/OpenChange/MAPIStoreContactsMessageTable.m index c4dd5ffe6..16f265c9d 100644 --- a/OpenChange/MAPIStoreContactsMessageTable.m +++ b/OpenChange/MAPIStoreContactsMessageTable.m @@ -31,6 +31,7 @@ #import #import "MAPIStoreTypes.h" +#import "NSCalendarDate+MAPIStore.h" #import "NSString+MAPIStore.h" #import "MAPIStoreContactsMessageTable.h" @@ -152,6 +153,52 @@ stringValue = [[child vCard] preferredEMail]; *data = [stringValue asUnicodeInMemCtx: memCtx]; break; + + // + // TODO - same logic as -secondaryEmail in UI/Contacts/UIxContactView.m + // We should eventually merge that in order to not duplicate the code. + // We should also eventually handle PidLidEmail3OriginalDisplayName in + // SOGo, Thunderbird, etc. + // + case PidLidEmail2OriginalDisplayName: // Other email + { + NSMutableArray *emails; + NSString *email; + NGVCard *card; + + emails = [NSMutableArray array]; + stringValue = nil; + + card = [[self lookupChild: childKey] vCard]; + [emails addObjectsFromArray: [card childrenWithTag: @"email"]]; + [emails removeObjectsInArray: [card childrenWithTag: @"email" + andAttribute: @"type" + havingValue: @"pref"]]; + + if ([emails count] > 0) + { + int i; + + for (i = 0; i < [emails count]; i++) + { + email = [[emails objectAtIndex: i] value: 0]; + + if ([email caseInsensitiveCompare: [card preferredEMail]] != NSOrderedSame) + { + stringValue = email; + break; + } + } + } + + if (!stringValue) + stringValue = @""; + + *data = [stringValue asUnicodeInMemCtx: memCtx]; + } + break; + + // FIXME: this property does NOT work case PR_BODY_UNICODE: child = [self lookupChild: childKey]; stringValue = [[child vCard] note]; @@ -160,7 +207,7 @@ case PR_OFFICE_TELEPHONE_NUMBER_UNICODE: child = [self lookupChild: childKey]; - element = [self _element: @"phone" ofType: @"work" + element = [self _element: @"tel" ofType: @"work" excluding: @"fax" inCard: [child vCard]]; if (element) @@ -171,7 +218,7 @@ break; case PR_HOME_TELEPHONE_NUMBER_UNICODE: child = [self lookupChild: childKey]; - element = [self _element: @"phone" ofType: @"home" + element = [self _element: @"tel" ofType: @"home" excluding: @"fax" inCard: [child vCard]]; if (element) @@ -182,7 +229,7 @@ break; case PR_MOBILE_TELEPHONE_NUMBER_UNICODE: child = [self lookupChild: childKey]; - element = [self _element: @"phone" ofType: @"cell" + element = [self _element: @"tel" ofType: @"cell" excluding: nil inCard: [child vCard]]; if (element) @@ -193,7 +240,7 @@ break; case PR_PRIMARY_TELEPHONE_NUMBER_UNICODE: child = [self lookupChild: childKey]; - element = [self _element: @"phone" ofType: @"pref" + element = [self _element: @"tel" ofType: @"pref" excluding: nil inCard: [child vCard]]; if (element) @@ -228,7 +275,20 @@ case PidLidEmail3AddressType: *data = [@"SMTP" asUnicodeInMemCtx: memCtx]; break; + + case PidLidInstantMessagingAddress: + child = [self lookupChild: childKey]; + stringValue = [[[child vCard] uniqueChildWithTag: @"x-aim"] value: 0]; + + if (!stringValue) + stringValue = @""; + *data = [stringValue asUnicodeInMemCtx: memCtx]; + break; + // + // We don't handle 0x00000003 - The Other Address is the mailing address. + // See: http://msdn.microsoft.com/en-us/library/cc815430.aspx + // case PidLidPostalAddressId: child = [self lookupChild: childKey]; element = [self _element: @"adr" ofType: @"pref" @@ -236,12 +296,12 @@ inCard: [child vCard]]; if ([element hasAttribute: @"type" havingValue: @"home"]) - longValue = 1; + longValue = 1; // The Home Address is the mailing address. else if ([element hasAttribute: @"type" havingValue: @"work"]) - longValue = 2; + longValue = 2; // The Work Address is the mailing address. else - longValue = 0; + longValue = 0; // No address is selected as the mailing address. *data = MAPILongValue (memCtx, longValue); break; @@ -405,13 +465,34 @@ *data = [stringValue asUnicodeInMemCtx: memCtx]; break; - // PidTagNickname + // PidTagNickname case PR_NICKNAME_UNICODE: child = [self lookupChild: childKey]; stringValue = [[child vCard] nickname]; *data = [stringValue asUnicodeInMemCtx: memCtx]; break; + case PR_BIRTHDAY: + { + NSCalendarDate *dateValue; + + child = [self lookupChild: childKey]; + + stringValue = [[child vCard] bday]; + + if (stringValue) + { + dateValue = [NSCalendarDate dateWithString: stringValue + calendarFormat: @"%Y-%m-%d"]; + // FIXME: We add a day, otherwise Outlook 2003 will display at day earlier + dateValue = [dateValue addYear: 0 month: 0 day: 1 hour: 0 minute: 0 second: 0]; + *data = [dateValue asFileTimeInMemCtx: memCtx]; + } + else + rc = MAPI_E_NOT_FOUND; + } + break; + default: rc = [super getChildProperty: data forKey: childKey diff --git a/OpenChange/MAPIStoreContext.m b/OpenChange/MAPIStoreContext.m index e4dd38f7c..016a0dca5 100644 --- a/OpenChange/MAPIStoreContext.m +++ b/OpenChange/MAPIStoreContext.m @@ -1127,15 +1127,15 @@ _prepareContextClass (struct mapistore_context *newMemCtx, { *path = [[objectURL substringFromIndex: 7] asUnicodeInMemCtx: memCtx]; - [self logWithFormat: @"found path for fmid %.16x: '%s'", - fmid, *path]; + [self logWithFormat: @"found path '%s' for fmid %.16x", + *path, fmid]; rc = MAPISTORE_SUCCESS; } else { - [self warnWithFormat: @"fmid 0x%.16x was found but is not" - @" part of this context (%@, %@)", - fmid, objectURL, uri]; + [self logWithFormat: @"context (%@, %@) does not contain" + @" found fmid: 0x%.16x", + objectURL, uri, fmid]; *path = NULL; rc = MAPI_E_NOT_FOUND; } diff --git a/OpenChange/MAPIStoreMailMessageTable.m b/OpenChange/MAPIStoreMailMessageTable.m index 0e0ec104e..bfd06c677 100644 --- a/OpenChange/MAPIStoreMailMessageTable.m +++ b/OpenChange/MAPIStoreMailMessageTable.m @@ -21,6 +21,7 @@ */ #import +#import #import #import @@ -132,12 +133,22 @@ static Class NSDataK, NSStringK; *data = [[child date] asFileTimeInMemCtx: memCtx]; break; case PR_MESSAGE_FLAGS: // TODO - // NSDictionary *coreInfos; - // NSArray *flags; - // child = [self lookupChild: childKey]; - // coreInfos = [child fetchCoreInfos]; - // flags = [coreInfos objectForKey: @"flags"]; - *data = MAPILongValue (memCtx, 0x02 | 0x20); // fromme + unmodified + { + NSDictionary *coreInfos; + NSArray *flags; + unsigned int v; + + child = [self lookupChild: childKey]; + coreInfos = [child fetchCoreInfos]; + + flags = [coreInfos objectForKey: @"flags"]; + v = MSGFLAG_FROMME; + + if ([flags containsObject: @"seen"]) + v |= MSGFLAG_READ; + + *data = MAPILongValue (memCtx, v); + } break; case PR_FLAG_STATUS: // TODO @@ -191,6 +202,30 @@ static Class NSDataK, NSStringK; case PidNameContentType: *data = [@"message/rfc822" asUnicodeInMemCtx: memCtx]; break; + + + // + // TODO: Merge with the code in UI/MailerUI/UIxMailListActions.m: -messagePriority + // to avoid the duplication of the logic + // + case PR_IMPORTANCE: + { + unsigned int v; + NSString *s; + + child = [self lookupChild: childKey]; + s = [[child mailHeaders] objectForKey: @"x-priority"]; + v = 0x1; + + + if ([s hasPrefix: @"1"]) v = 0x2; + else if ([s hasPrefix: @"2"]) v = 0x2; + else if ([s hasPrefix: @"4"]) v = 0x0; + else if ([s hasPrefix: @"5"]) v = 0x0; + + *data = MAPILongValue (memCtx, v); + } + break; case PR_BODY: case PR_BODY_UNICODE: diff --git a/OpenChange/MAPIStoreNotesContext.m b/OpenChange/MAPIStoreNotesContext.m index c8685fb38..62aa89567 100644 --- a/OpenChange/MAPIStoreNotesContext.m +++ b/OpenChange/MAPIStoreNotesContext.m @@ -1,6 +1,6 @@ /* MAPIStoreNotesContext.m - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. + * Copyright (C) 2010-2011 Inverse inc. * * Author: Wolfgang Sourdeau * @@ -25,6 +25,7 @@ #import "MAPIStoreMapping.h" #import "MAPIStoreNotesContext.h" +#import "MAPIStoreNotesMessageTable.h" @implementation MAPIStoreNotesContext @@ -39,4 +40,9 @@ withID: 0x1c0001]; } +- (Class) messageTableClass +{ + return [MAPIStoreNotesMessageTable class]; +} + @end diff --git a/OpenChange/MAPIStoreNotesMessageTable.h b/OpenChange/MAPIStoreNotesMessageTable.h new file mode 100644 index 000000000..c577759cb --- /dev/null +++ b/OpenChange/MAPIStoreNotesMessageTable.h @@ -0,0 +1,31 @@ +/* MAPIStoreNotesMessageTable.h - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Ludovic Marcotte + * + * 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. + */ + +#ifndef MAPISTORENOTESMESSAGETABLE_H +#define MAPISTORENOTESMESSAGETABLE_H + +#import "MAPIStoreFSMessageTable.h" + +@interface MAPIStoreNotesMessageTable : MAPIStoreFSMessageTable +@end + +#endif /* MAPISTORENOTESMESSAGETABLE_H */ diff --git a/OpenChange/MAPIStoreNotesMessageTable.m b/OpenChange/MAPIStoreNotesMessageTable.m new file mode 100644 index 000000000..a6fece914 --- /dev/null +++ b/OpenChange/MAPIStoreNotesMessageTable.m @@ -0,0 +1,72 @@ +/* MAPIStoreNotesMessageTable.m - this file is part of SOGo + * + * Copyright (C) 2010 Inverse inc + * + * Author: Ludovic Marcotte + * + * 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 "MAPIStoreMapping.h" +#import "MAPIStoreTypes.h" +#import "NSData+MAPIStore.h" +#import "NSString+MAPIStore.h" + +#import "MAPIStoreNotesMessageTable.h" + +#undef DEBUG +#include +#include +#include +#include +#include +#include + + +@implementation MAPIStoreNotesMessageTable + +- (enum MAPISTATUS) getChildProperty: (void **) data + forKey: (NSString *) childKey + withTag: (enum MAPITAGS) propTag +{ + int rc; + + rc = MAPI_E_SUCCESS; + switch (propTag) + { + case PR_ICON_INDEX: // TODO + /* see http://msdn.microsoft.com/en-us/library/cc815472.aspx */ + // *longValue = 0x00000300 for blue + // *longValue = 0x00000301 for green + // *longValue = 0x00000302 for pink + // *longValue = 0x00000303 for yellow + // *longValue = 0x00000304 for white + *data = MAPILongValue (memCtx, 0x00000303); + break; + + default: + rc = [super getChildProperty: data + forKey: childKey + withTag: propTag]; + } + + return rc; +} + +@end diff --git a/OpenChange/MAPIStoreTable.m b/OpenChange/MAPIStoreTable.m index bc46f9496..df3a38b83 100644 --- a/OpenChange/MAPIStoreTable.m +++ b/OpenChange/MAPIStoreTable.m @@ -664,6 +664,7 @@ static Class NSDataK, NSStringK; } value = NSObjectFromMAPISPropValue (&res->lpProp); + *qualifier = [[EOKeyValueQualifier alloc] initWithKey: property operatorSelector: operator value: value]; diff --git a/OpenChange/MAPIStoreTasksMessageTable.m b/OpenChange/MAPIStoreTasksMessageTable.m index 0c7547a39..d0ef67353 100644 --- a/OpenChange/MAPIStoreTasksMessageTable.m +++ b/OpenChange/MAPIStoreTasksMessageTable.m @@ -30,6 +30,7 @@ #import "MAPIStoreTypes.h" #import "NSCalendarDate+MAPIStore.h" +#import "NSData+MAPIStore.h" #import "NSString+MAPIStore.h" #import "MAPIStoreTasksMessageTable.h" @@ -72,6 +73,28 @@ task = [[self lookupChild: childKey] component: NO secure: NO]; *data = [[task summary] asUnicodeInMemCtx: memCtx]; break; + + // + // Not to be confused with PR_PRIORITY + // + // IMPORTANCE_LOW = 0x0, IMPORTANCE_NORMAL = 0x1 and IMPORTANCE_HIGH = 0x2 + // + case PR_IMPORTANCE: + { + unsigned int v; + + task = [[self lookupChild: childKey] component: NO secure: NO]; + + if ([[task priority] isEqualToString: @"9"]) + v = 0x0; + else if ([[task priority] isEqualToString: @"1"]) + v = 0x2; + else + v = 0x1; + + *data = MAPILongValue (memCtx, v); + } + break; case PidLidTaskComplete: task = [[self lookupChild: childKey] component: NO secure: NO]; *data = MAPIBoolValue (memCtx, @@ -90,8 +113,12 @@ else rc = MAPI_E_NOT_FOUND; break; - case PidLidTaskState: // TODO (check) - *data = MAPILongValue (memCtx, 0x02 | 0x03); + // http://msdn.microsoft.com/en-us/library/cc765590.aspx + // It's important to have a proper value for PidLidTaskState + // as it'll affect the UI options of Outlook - making the + // task non-editable in some cases. + case PidLidTaskState: + *data = MAPILongValue (memCtx, 0x1); // not assigned break; case PidLidTaskMode: // TODO *data = MAPILongValue (memCtx, 0x0); @@ -151,9 +178,19 @@ case 0x68340003: case 0x683a0003: case 0x68410003: - *data = MAPILongValue (memCtx, 0); + *data = MAPILongValue (memCtx, 0); + break; + + // FIXME - use the current user + case PidLidTaskOwner: + *data = [@"openchange@example.com" asUnicodeInMemCtx: memCtx]; break; + // See http://msdn.microsoft.com/en-us/library/cc842113.aspx + case PidLidTaskOwnership: + *data = MAPILongValue (memCtx, 0x0); // not assigned + break; + // #define PidLidFlagRequest 0x9027001f // #define PidNameContentType 0x905a001f // #define PidLidBillingInformation 0x908b001f diff --git a/OpenChange/MAPIStoreTypes.m b/OpenChange/MAPIStoreTypes.m index 4ffa6eb38..98c56dc9c 100644 --- a/OpenChange/MAPIStoreTypes.m +++ b/OpenChange/MAPIStoreTypes.m @@ -121,7 +121,7 @@ NSObjectFromMAPISPropValue (const struct mapi_SPropValue *value) case PT_CLSID: result = [NSData dataWithGUID: &value->value.lpguid]; break; - + default: // #define PT_UNSPECIFIED 0x0 // #define PT_I2 0x2 diff --git a/OpenChange/SOGoMAPIFSMessage.m b/OpenChange/SOGoMAPIFSMessage.m index 19daa6bdb..182b00e1a 100644 --- a/OpenChange/SOGoMAPIFSMessage.m +++ b/OpenChange/SOGoMAPIFSMessage.m @@ -20,6 +20,7 @@ * Boston, MA 02111-1307, USA. */ +#import #import #import #import @@ -70,6 +71,7 @@ - (void) MAPISave { + NSArray *pathComponents; NSString *filePath; [self logWithFormat: @"-MAPISave"]; @@ -78,6 +80,18 @@ filePath = [[container directory] stringByAppendingPathComponent: nameInContainer]; + + // FIXME + // We do NOT save the FAI data for the Inbox, as upon the + // next Outlook restart, when restoring those saved properties, + // Outlook will crash. + pathComponents = [filePath pathComponents]; + if ([[pathComponents objectAtIndex: [pathComponents count]-2] isEqualToString: @"inbox"]) + { + [self logWithFormat: @"-MAPISave - skipping FAI at path %@", filePath]; + return; + } + if (![properties writeToFile: filePath atomically: YES]) [NSException raise: @"MAPIStoreIOException" format: @"could not save message"]; diff --git a/OpenChange/SOGoTaskObject+MAPIStore.m b/OpenChange/SOGoTaskObject+MAPIStore.m index 4a37b4b75..6930ca738 100644 --- a/OpenChange/SOGoTaskObject+MAPIStore.m +++ b/OpenChange/SOGoTaskObject+MAPIStore.m @@ -133,13 +133,13 @@ { switch ([value intValue]) { - case 0: // Low + case 0: // IMPORTANCE_LOW priority = @"9"; break; - case 2: // High + case 2: // IMPORTANCE_HIGH priority = @"1"; break; - default: // Normal + default: // IMPORTANCE_NORMAL priority = @"5"; } } diff --git a/UI/WebServerResources/UIxContactEditor.js b/UI/WebServerResources/UIxContactEditor.js index 13fe0a444..7b79a3bee 100644 --- a/UI/WebServerResources/UIxContactEditor.js +++ b/UI/WebServerResources/UIxContactEditor.js @@ -25,7 +25,7 @@ var uixEmailUsr = "([a-zA-Z0-9][a-zA-Z0-9_.-]*|\"([^\\\\\x80-\xff\015\012\"]|\\\\[^\x80-\xff])+\")"; var uixEmailDomain = - "([a-zA-Z0-9][a-zA-Z0-9._-]*\\.)*[a-zA-Z0-9][a-zA-Z0-9._-]*\\.[a-zA-Z]{2,5}"; + "([a-zA-Z0-9][a-zA-Z0-9._-]*\\.)*[a-zA-Z0-9][a-zA-Z0-9._-]*\\.[a-zA-Z]{2,6}"; var uixEmailRegex = new RegExp("^"+uixEmailUsr+"\@"+uixEmailDomain+"$"); var dateRegex = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/;