From a663fdd260e0bed5e3f09b3ff1ad709037b4a553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Wed, 18 Mar 2015 23:54:10 +0100 Subject: [PATCH 1/4] oc-mail: Give support to store extra properties in versions message This is handy when you want to store properties once a mail has hit the server and thus cannot be changed. --- OpenChange/MAPIStoreMailFolder.h | 5 +++++ OpenChange/MAPIStoreMailFolder.m | 31 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/OpenChange/MAPIStoreMailFolder.h b/OpenChange/MAPIStoreMailFolder.h index 2263f955b..c56e74862 100644 --- a/OpenChange/MAPIStoreMailFolder.h +++ b/OpenChange/MAPIStoreMailFolder.h @@ -55,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 ba2339586..f0a6c33bc 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -964,6 +964,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 From 514b1c03be190f065f45e5e205e2efd0a2d241d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Wed, 18 Mar 2015 23:57:33 +0100 Subject: [PATCH 2/4] oc-mail: Store request properties Save them in extra properties from folder container. This is required because the client once a request is accepted or denied sets these two properties and save the message again. As we cannot modify an IMAP message, we use this utility. See [MS-OXSHARE] Section 3.1.4.3 for details. --- OpenChange/MAPIStoreMailMessage.m | 26 +++++++++++- OpenChange/MAPIStoreSharingMessage.h | 9 +++- OpenChange/MAPIStoreSharingMessage.m | 63 ++++++++++++++++++++++++++-- 3 files changed, 90 insertions(+), 8 deletions(-) diff --git a/OpenChange/MAPIStoreMailMessage.m b/OpenChange/MAPIStoreMailMessage.m index 8c35f4265..c0ca8f9b6 100644 --- a/OpenChange/MAPIStoreMailMessage.m +++ b/OpenChange/MAPIStoreMailMessage.m @@ -65,7 +65,7 @@ @class iCalCalendar, iCalEvent; -static Class NSExceptionK; +static Class NSExceptionK, MAPIStoreSharingMessageK; @interface NSString (MAPIStoreMIME) @@ -108,6 +108,7 @@ static Class NSExceptionK; + (void) initialize { NSExceptionK = [NSException class]; + MAPIStoreSharingMessageK = [MAPIStoreSharingMessage class]; } - (id) init @@ -267,7 +268,8 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) a sharing object is a proxy in a mail message */ sharingMessage = [[MAPIStoreSharingMessage alloc] initWithMailHeaders: [sogoObject mailHeaders] - andConnectionInfo: [[self context] connectionInfo]]; + andConnectionInfo: [[self context] connectionInfo] + fromMessage: self]; [self addProxy: sharingMessage]; [sharingMessage release]; } @@ -1641,6 +1643,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; @@ -1654,6 +1671,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/MAPIStoreSharingMessage.h b/OpenChange/MAPIStoreSharingMessage.h index 9b62389a0..c3ca14feb 100644 --- a/OpenChange/MAPIStoreSharingMessage.h +++ b/OpenChange/MAPIStoreSharingMessage.h @@ -23,9 +23,9 @@ #ifndef MAPISTORESHARINGMESSAGE_H #define MAPISTORESHARINGMESSAGE_H +#import "MAPIStoreMailMessage.h" #import "MAPIStoreObjectProxy.h" - @interface MAPIStoreSharingMessage : MAPIStoreObjectProxy { struct mapistore_connection_info *connInfo; @@ -33,7 +33,8 @@ } - (id) initWithMailHeaders: (NSDictionary *) mailHeaders - andConnectionInfo: (struct mapistore_connection_info *) newConnInfo; + andConnectionInfo: (struct mapistore_connection_info *) newConnInfo + fromMessage: (MAPIStoreMailMessage *) msg; /* getters */ - (int) getPidLidSharingCapabilities: (void **) data @@ -89,6 +90,10 @@ - (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 index 594863e0e..339ca0750 100644 --- a/OpenChange/MAPIStoreSharingMessage.m +++ b/OpenChange/MAPIStoreSharingMessage.m @@ -22,9 +22,11 @@ #include +#import #import #import +#import #import #import "MAPIStoreTypes.h" @@ -33,9 +35,11 @@ #import "NSString+MAPIStore.h" #import "NSValue+MAPIStore.h" +#import "MAPIStoreMailFolder.h" #import "MAPIStoreSharingMessage.h" #include +#include @implementation MAPIStoreSharingMessage @@ -52,6 +56,7 @@ - (id) initWithMailHeaders: (NSDictionary *) mailHeaders andConnectionInfo: (struct mapistore_connection_info *) newConnInfo + fromMessage: (MAPIStoreMailMessage *) msg { NSEnumerator *enumerator; NSString *key; @@ -67,6 +72,12 @@ [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; @@ -368,7 +379,18 @@ enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND; id value; - value = [properties objectForKey: @"x-ms-sharing-responsetime"]; + 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]; @@ -385,7 +407,10 @@ enum mapistore_error rc = MAPISTORE_ERR_NOT_FOUND; id value; - value = [properties objectForKey: @"x-ms-sharing-responsetype"]; + value = [properties objectForKey: MAPIPropertyKey (PidLidSharingResponseType)]; + if (!value) + value = [properties objectForKey: @"x-ms-sharing-responsetype"]; + if (value) { *data = MAPILongValue (memCtx, [value intValue]); @@ -398,8 +423,38 @@ - (int) getPidNameContentClass: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - *data = talloc_strdup (memCtx, "Sharing"); - return MAPISTORE_SUCCESS; + *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 From 661b6694af3e2dacb1daa560c489b6ac9553db4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Thu, 19 Mar 2015 00:00:55 +0100 Subject: [PATCH 3/4] oc-mail: Fix several sharing properties managing * PidNameXSharingFlavor is used by Outlook 2010 so we have to store it * 0x5100 is used although it is not in spec as Sharing Flavour value when the request is denied from a message where Request + Invitation was sent. * Return properly PidNameXSharingCapabilities and PidNameXSharingFlavor as it is an string representation of a hex number * Try to guess proper sharing flavour value when it is missing --- OpenChange/MAPIStoreMailVolatileMessage.m | 30 +++++++++++++++++++++-- OpenChange/MAPIStoreSharingMessage.m | 14 ++++++----- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/OpenChange/MAPIStoreMailVolatileMessage.m b/OpenChange/MAPIStoreMailVolatileMessage.m index 77d6294ab..7ef2c2c96 100644 --- a/OpenChange/MAPIStoreMailVolatileMessage.m +++ b/OpenChange/MAPIStoreMailVolatileMessage.m @@ -28,6 +28,7 @@ #import #import #import +#import #import #import #import @@ -541,6 +542,7 @@ FillMessageHeadersFromSharingProperties (NGMutableHashMap *headers, NSDictionary about the properties */ id value; + NSNumber *sharingFlavourNum = nil; value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingCapabilities)]; if (value) @@ -549,8 +551,32 @@ FillMessageHeadersFromSharingProperties (NGMutableHashMap *headers, NSDictionary value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingFlavor)]; if (value) - [headers setObject: value - forKey: @"X-MS-Sharing-Flavor"]; + 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: 0x25100]; + } + [headers setObject: sharingFlavourNum + forKey: @"X-MS-Sharing-Flavor"]; + } value = [mailProperties objectForKey: MAPIPropertyKey (PidLidSharingInitiatorEntryId)]; if (value) diff --git a/OpenChange/MAPIStoreSharingMessage.m b/OpenChange/MAPIStoreSharingMessage.m index 339ca0750..67a133bd8 100644 --- a/OpenChange/MAPIStoreSharingMessage.m +++ b/OpenChange/MAPIStoreSharingMessage.m @@ -134,7 +134,8 @@ value = [properties objectForKey: @"x-ms-sharing-capabilities"]; if (value) { - *data = [[value stringValue] asUnicodeInMemCtx: memCtx]; + *data = [[NSString stringWithFormat:@"%X", [value intValue]] + asUnicodeInMemCtx: memCtx]; rc = MAPISTORE_SUCCESS; } @@ -177,7 +178,7 @@ if ([value intValue] == 0x40290) /* Special folder */ { value = [properties objectForKey: @"x-ms-sharing-responsetime"]; - auxValue = [properties objectForKey: @"x-ms-sharing-remotename"]; + auxValue = [properties objectForKey: @"x-ms-sharing-remoteuid"]; if (value) /* A sharing request */ { if (auxValue) @@ -187,10 +188,10 @@ } else { - if (auxValue) /* It SHOULD be an invitation or response acceptance */ + if (auxValue) /* It SHOULD be an invitation or response */ *data = MAPILongValue (memCtx, 0x20310); - else - *data = MAPILongValue (memCtx, 0x23310); + else /* No remote info, then denial */ + *data = MAPILongValue (memCtx, 0x25100); } } else @@ -213,7 +214,8 @@ value = [properties objectForKey: @"x-ms-sharing-flavor"]; if (value) { - *data = [[value stringValue] asUnicodeInMemCtx: memCtx]; + *data = [[NSString stringWithFormat:@"%X", [value intValue]] + asUnicodeInMemCtx: memCtx]; rc = MAPISTORE_SUCCESS; } From f9c8661fe6308e795ac098f85de2407344342869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Wed, 25 Mar 2015 00:03:51 +0100 Subject: [PATCH 4/4] oc-mail: Use constant names in props value This gives to the developer more information about the property values for PidLidSharingFlavor and PidLidSharingCapabilities. --- OpenChange/MAPIStoreMailVolatileMessage.m | 2 +- OpenChange/MAPIStoreSharingMessage.h | 2 ++ OpenChange/MAPIStoreSharingMessage.m | 13 +++++++------ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/OpenChange/MAPIStoreMailVolatileMessage.m b/OpenChange/MAPIStoreMailVolatileMessage.m index 7ef2c2c96..b5239f193 100644 --- a/OpenChange/MAPIStoreMailVolatileMessage.m +++ b/OpenChange/MAPIStoreMailVolatileMessage.m @@ -572,7 +572,7 @@ FillMessageHeadersFromSharingProperties (NGMutableHashMap *headers, NSDictionary /* 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: 0x25100]; + sharingFlavourNum = [NSNumber numberWithUnsignedInt: SHARING_DENY_REQUEST]; } [headers setObject: sharingFlavourNum forKey: @"X-MS-Sharing-Flavor"]; diff --git a/OpenChange/MAPIStoreSharingMessage.h b/OpenChange/MAPIStoreSharingMessage.h index c3ca14feb..7db7f552f 100644 --- a/OpenChange/MAPIStoreSharingMessage.h +++ b/OpenChange/MAPIStoreSharingMessage.h @@ -26,6 +26,8 @@ #import "MAPIStoreMailMessage.h" #import "MAPIStoreObjectProxy.h" +#define SHARING_SPECIAL_FOLDER 0x40290 + @interface MAPIStoreSharingMessage : MAPIStoreObjectProxy { struct mapistore_connection_info *connInfo; diff --git a/OpenChange/MAPIStoreSharingMessage.m b/OpenChange/MAPIStoreSharingMessage.m index 67a133bd8..ba0c74132 100644 --- a/OpenChange/MAPIStoreSharingMessage.m +++ b/OpenChange/MAPIStoreSharingMessage.m @@ -38,6 +38,7 @@ #import "MAPIStoreMailFolder.h" #import "MAPIStoreSharingMessage.h" +#include #include #include @@ -175,28 +176,28 @@ value = [properties objectForKey: @"x-ms-sharing-capabilities"]; if (value) { - if ([value intValue] == 0x40290) /* Special folder */ + 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, 0x20710); + *data = MAPILongValue (memCtx, SHARING_INVITATION_REQUEST_FOLDER); else - *data = MAPILongValue (memCtx, 0x20500); + *data = MAPILongValue (memCtx, SHARING_REQUEST_SPECIAL_FOLDER); } else { if (auxValue) /* It SHOULD be an invitation or response */ - *data = MAPILongValue (memCtx, 0x20310); + *data = MAPILongValue (memCtx, SHARING_INVITATION_SPECIAL_FOLDER); else /* No remote info, then denial */ - *data = MAPILongValue (memCtx, 0x25100); + *data = MAPILongValue (memCtx, SHARING_DENY_REQUEST); } } else { - *data = MAPILongValue (memCtx, 0x310); + *data = MAPILongValue (memCtx, SHARING_INVITATION_FOLDER); } rc = MAPISTORE_SUCCESS; }