From 724538a4b780425584347b4f68038ce2b7fd540f Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 30 Dec 2010 14:18:42 +0000 Subject: [PATCH] Monotone-Parent: dd136334b53404f4fb419f4960bacd50ae38a47c Monotone-Revision: f5f2ee70c2acc45239a32d38bf99d6078e2dbe39 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2010-12-30T14:18:42 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 24 + OpenChange/MAPIStoreContext.h | 108 +- OpenChange/MAPIStoreContext.m | 1542 ++++++++-------------------- OpenChange/MAPIStoreFolderTable.h | 31 + OpenChange/MAPIStoreFolderTable.m | 129 +++ OpenChange/MAPIStoreMessageTable.h | 31 + OpenChange/MAPIStoreMessageTable.m | 201 ++++ OpenChange/MAPIStoreTable.h | 106 ++ OpenChange/MAPIStoreTable.m | 792 ++++++++++++++ 9 files changed, 1746 insertions(+), 1218 deletions(-) create mode 100644 OpenChange/MAPIStoreFolderTable.h create mode 100644 OpenChange/MAPIStoreFolderTable.m create mode 100644 OpenChange/MAPIStoreMessageTable.h create mode 100644 OpenChange/MAPIStoreMessageTable.m create mode 100644 OpenChange/MAPIStoreTable.h create mode 100644 OpenChange/MAPIStoreTable.m diff --git a/ChangeLog b/ChangeLog index 286e00a6b..0039b49b4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,29 @@ 2010-12-30 Wolfgang Sourdeau + * OpenChange/MAPIStoreContext.m: refactored to extract the table + handling code into a separate class. This enables the introduction + of real classes for contents tables, folder tables and FAI tables, + as well as any other type that could be handled later. Changes: + - new messageTableClass and folderTableClass methods for + determining the class that is appropriate to the current datatype. + - new messageTable, folderTable ivars. + - new faiTable ivar, always an instance of + MAPIStoreFAIMessageTable. + - created MAPIStoreTable, subclassed into MAPIStoreMessageTable + and MAPIStoreFolderTable from which appropriate subclasses are + derived for handling mails, tasks or calendar items. + - renamed + "getMessageTableChildproperty:atURL:withTag:inFolder:withFID:" and + "getFolderTableChildproperty:atURL:withTag:inFolder:withFID:" to + the simpler "getChildProperty:forKey:withTag:" form and moved them + to the MAPIStoreTable class. + - moved "-setRestrictions:" to MAPIStoreTable. + - renamed "getFolderMessageKeys:matchingQualifier:" to + "cachedChildKeys" and "cachedRestrictedChildKeys" and moved them + to MAPIStoreTable. + - moved all the "evaluateRestriction:intoQualifier:" methods to + MAPIStoreTable. + * OpenChange/EOBitmaskQualifier.m (-description): new method. 2010-12-29 Ludovic Marcotte diff --git a/OpenChange/MAPIStoreContext.h b/OpenChange/MAPIStoreContext.h index 972d6071d..b3b0f9517 100644 --- a/OpenChange/MAPIStoreContext.h +++ b/OpenChange/MAPIStoreContext.h @@ -25,16 +25,6 @@ #import -#define SENSITIVITY_NONE 0 -#define SENSITIVITY_PERSONAL 1 -#define SENSITIVITY_PRIVATE 2 -#define SENSITIVITY_COMPANY_CONFIDENTIAL 3 - -#define TBL_LEAF_ROW 0x00000001 -#define TBL_EMPTY_CATEGORY 0x00000002 -#define TBL_EXPANDED_CATEGORY 0x00000003 -#define TBL_COLLAPSED_CATEGORY 0x00000004 - @class NSArray; @class NSFileHandle; @class NSMutableArray; @@ -46,40 +36,33 @@ @class WOContext; @class SOGoFolder; +@class SOGoMAPIFSFolder; @class SOGoObject; @class MAPIStoreAuthenticator; @class MAPIStoreMapping; - -typedef enum { - MAPIRestrictionStateAlwaysFalse = NO, - MAPIRestrictionStateAlwaysTrue = YES, - MAPIRestrictionStateNeedsEval, /* needs passing of qualifier to underlying - database */ -} MAPIRestrictionState; +@class MAPIStoreTable; @interface MAPIStoreContext : NSObject { struct mapistore_context *memCtx; - void *ldbCtx; - - BOOL baseContextSet; NSString *uri; NSMutableArray *parentFoldersBag; - NSMutableDictionary *objectCache; - NSMutableDictionary *messages; MAPIStoreAuthenticator *authenticator; WOContext *woContext; - NSMutableDictionary *messageCache; - NSMutableDictionary *subfolderCache; - id moduleFolder; - NSMutableDictionary *restrictedMessageCache; - MAPIRestrictionState restrictionState; - EOQualifier *restriction; + id moduleFolder; + SOGoMAPIFSFolder *faiModuleFolder; + + MAPIStoreTable *folderTable; + MAPIStoreTable *messageTable; + MAPIStoreTable *faiTable; + + /* for active messages (NSDictionary instances) */ + NSMutableDictionary *messages; } + (id) contextFromURI: (const char *) newUri @@ -107,9 +90,9 @@ typedef enum { byName: (const char *) foldername inParentFID: (uint64_t) parent_fid; -- (int) setRestrictions: (struct mapi_SRestriction *) res +- (int) setRestrictions: (const struct mapi_SRestriction *) res withFID: (uint64_t) fid - andTableType: (uint8_t) type + andTableType: (uint8_t) tableType getTableStatus: (uint8_t *) tableStatus; - (enum MAPISTATUS) getTableProperty: (void **) data @@ -134,7 +117,8 @@ typedef enum { withMID: (uint64_t) mid inFID: (uint64_t) fid; - (int) createMessagePropertiesWithMID: (uint64_t) mid - inFID: (uint64_t) fid; + inFID: (uint64_t) fid + isAssociated: (BOOL) isAssociated; - (int) saveChangesInMessageWithMID: (uint64_t) mid andFlags: (uint8_t) flags; - (int) submitMessageWithMID: (uint64_t) mid @@ -161,71 +145,31 @@ typedef enum { withFlags: (uint8_t) flags; - (int) releaseRecordWithFMID: (uint64_t) fmid ofTableType: (uint8_t) tableType; +- (int) getFoldersList: (struct indexing_folders_list **) folders_list + withFMID: (uint64_t) fmid; /* util methods */ +- (NSString *) extractChildNameFromURL: (NSString *) childURL + andFolderURLAt: (NSString **) folderURL; + - (void) registerValue: (id) value asProperty: (enum MAPITAGS) property forURL: (NSString *) url; - -/* restrictions */ - -- (MAPIRestrictionState) evaluateRestriction: (struct mapi_SRestriction *) res - intoQualifier: (EOQualifier **) qualifier; -- (MAPIRestrictionState) evaluateNotRestriction: (struct mapi_SNotRestriction *) res - intoQualifier: (EOQualifier **) qualifierPtr; -- (MAPIRestrictionState) evaluateAndRestriction: (struct mapi_SAndRestriction *) res - intoQualifier: (EOQualifier **) qualifierPtr; -- (MAPIRestrictionState) evaluateOrRestriction: (struct mapi_SOrRestriction *) res - intoQualifier: (EOQualifier **) qualifierPtr; - /* subclass methods */ + (NSString *) MAPIModuleName; + (void) registerFixedMappings: (MAPIStoreMapping *) storeMapping; -- (NSArray *) getFolderMessageKeys: (SOGoFolder *) folder - matchingQualifier: (EOQualifier *) qualifier; +- (Class) messageTableClass; +- (Class) folderTableClass; -- (enum MAPISTATUS) getCommonTableChildproperty: (void **) data - atURL: (NSString *) childURL - withTag: (enum MAPITAGS) proptag - inFolder: (SOGoFolder *) folder - withFID: (uint64_t) fid; - -- (enum MAPISTATUS) getMessageTableChildproperty: (void **) data - atURL: (NSString *) childURL - withTag: (enum MAPITAGS) proptag - inFolder: (SOGoFolder *) folder - withFID: (uint64_t) fid; - -- (enum MAPISTATUS) getFolderTableChildproperty: (void **) data - atURL: (NSString *) childURL - withTag: (enum MAPITAGS) proptag - inFolder: (SOGoFolder *) folder - withFID: (uint64_t) fid; - -- (int) getFoldersList: (struct indexing_folders_list **) folders_list - withFMID: (uint64_t) fmid; +- (id) createMessageOfClass: (NSString *) messageClass + inFolderAtURL: (NSString *) folderURL; - (int) openMessage: (struct mapistore_message *) msg - atURL: (NSString *) childURL; - -- (int) getMessageProperties: (struct SPropTagArray *) sPropTagArray - inRow: (struct SRow *) aRow - atURL: (NSString *) childURL; - -- (NSString *) backendIdentifierForProperty: (enum MAPITAGS) property; - -/* restrictions */ -- (MAPIRestrictionState) evaluateContentRestriction: (struct mapi_SContentRestriction *) res - intoQualifier: (EOQualifier **) qualifier; -- (MAPIRestrictionState) evaluatePropertyRestriction: (struct mapi_SPropertyRestriction *) res - intoQualifier: (EOQualifier **) qualifier; -- (MAPIRestrictionState) evaluateBitmaskRestriction: (struct mapi_SBitmaskRestriction *) res - intoQualifier: (EOQualifier **) qualifier; -- (MAPIRestrictionState) evaluateExistRestriction: (struct mapi_SExistRestriction *) res - intoQualifier: (EOQualifier **) qualifier; + forKey: (NSString *) childKey + inTable: (MAPIStoreTable *) table; @end diff --git a/OpenChange/MAPIStoreContext.m b/OpenChange/MAPIStoreContext.m index 4f10f7da6..560b0941e 100644 --- a/OpenChange/MAPIStoreContext.m +++ b/OpenChange/MAPIStoreContext.m @@ -25,9 +25,6 @@ #import #import #import -#import - -#import #import #import @@ -36,29 +33,26 @@ #import #import -#import #import #import -#import "EOBitmaskQualifier.h" - -#import "NSArray+MAPIStore.h" -#import "NSCalendarDate+MAPIStore.h" -#import "NSData+MAPIStore.h" +#import "SOGoMAPIFSFolder.h" +#import "SOGoMAPIFSMessage.h" #import "MAPIApplication.h" #import "MAPIStoreAuthenticator.h" +#import "MAPIStoreFolderTable.h" +#import "MAPIStoreFAIMessageTable.h" #import "MAPIStoreMapping.h" #import "MAPIStoreTypes.h" +#import "NSArray+MAPIStore.h" +#import "NSString+MAPIStore.h" #import "MAPIStoreContext.h" -#import "NSString+MAPIStore.h" - #undef DEBUG #include #include -#include /* TODO: homogenize method names and order of parameters */ @@ -71,210 +65,12 @@ @interface SOGoObject (MAPIStoreProtocol) -- (NSString *) davEntityTag; -- (NSString *) davContentLength; - (void) setMAPIProperties: (NSDictionary *) properties; - (void) MAPISave; - (void) MAPISubmit; @end -/* restriction helpers */ -static NSString * -MAPIStringForRestrictionState (MAPIRestrictionState state) -{ - NSString *stateStr; - - if (state == MAPIRestrictionStateAlwaysTrue) - stateStr = @"always true"; - else if (state == MAPIRestrictionStateAlwaysFalse) - stateStr = @"always false"; - else - stateStr = @"needs eval"; - - return stateStr; -} - -static NSString * -MAPIStringForRestriction (struct mapi_SRestriction *resPtr); - -// static NSString * -// _MAPIIndentString(int indent) -// { -// NSString *spaces; -// char *buffer; - -// if (indent > 0) -// { -// buffer = malloc (indent + 1); -// memset (buffer, 32, indent); -// *(buffer+indent) = 0; -// spaces = [NSString stringWithFormat: @"%s", buffer]; -// free (buffer); -// } -// else -// spaces = @""; - -// return spaces; -// } - -static NSString * -MAPIStringForAndRestriction (struct mapi_SAndRestriction *resAnd) -{ - NSMutableArray *restrictions; - uint16_t count; - - restrictions = [NSMutableArray arrayWithCapacity: 8]; - for (count = 0; count < resAnd->cRes; count++) - [restrictions addObject: MAPIStringForRestriction ((struct mapi_SRestriction *) resAnd->res + count)]; - - return [NSString stringWithFormat: @"(%@)", [restrictions componentsJoinedByString: @" && "]]; -} - -static NSString * -MAPIStringForOrRestriction (struct mapi_SOrRestriction *resOr) -{ - NSMutableArray *restrictions; - uint16_t count; - - restrictions = [NSMutableArray arrayWithCapacity: 8]; - for (count = 0; count < resOr->cRes; count++) - [restrictions addObject: MAPIStringForRestriction ((struct mapi_SRestriction *) resOr->res + count)]; - - return [NSString stringWithFormat: @"(%@)", [restrictions componentsJoinedByString: @" || "]]; -} - -static NSString * -MAPIStringForNotRestriction (struct mapi_SNotRestriction *resNot) -{ - return [NSString stringWithFormat: @"!(%@)", - MAPIStringForRestriction ((struct mapi_SRestriction *) &resNot->res)]; -} - -static NSString * -MAPIStringForContentRestriction (struct mapi_SContentRestriction *resContent) -{ - NSString *eqMatch, *caseMatch; - id value; - const char *propName; - - switch (resContent->fuzzy & 0xf) - { - case 0: eqMatch = @"eq"; break; - case 1: eqMatch = @"substring"; break; - case 2: eqMatch = @"prefix"; break; - default: eqMatch = @"[unknown]"; - } - - switch (((resContent->fuzzy) >> 16) & 0xf) - { - case 0: caseMatch = @"fl"; break; - case 1: caseMatch = @"nc"; break; - case 2: caseMatch = @"ns"; break; - case 4: caseMatch = @"lo"; break; - default: caseMatch = @"[unknown]"; - } - - propName = get_proptag_name (resContent->ulPropTag); - if (!propName) - propName = ""; - - value = NSObjectFromMAPISPropValue (&resContent->lpProp); - - return [NSString stringWithFormat: @"%s(0x%.8x) %@,%@ %@", - propName, resContent->ulPropTag, eqMatch, caseMatch, value]; -} - -static NSString * -MAPIStringForExistRestriction (struct mapi_SExistRestriction *resExist) -{ - const char *propName; - - propName = get_proptag_name (resExist->ulPropTag); - if (!propName) - propName = ""; - - return [NSString stringWithFormat: @"%s(0x%.8x) IS NOT NULL", propName, resExist->ulPropTag]; -} - -static NSString * -MAPIStringForPropertyRestriction (struct mapi_SPropertyRestriction *resProperty) -{ - static NSString *operators[] = { @"<", @"<=", @">", @">=", @"==", @"!=", - @"=~" }; - NSString *operator; - id value; - const char *propName; - - propName = get_proptag_name (resProperty->ulPropTag); - if (!propName) - propName = ""; - - if (resProperty->relop >= 0 && resProperty->relop < 7) - operator = operators[resProperty->relop]; - else - operator = [NSString stringWithFormat: @"", resProperty->relop]; - value = NSObjectFromMAPISPropValue (&resProperty->lpProp); - - return [NSString stringWithFormat: @"%s(0x%.8x) %@ %@", - propName, resProperty->ulPropTag, operator, value]; -} - -static NSString * -MAPIStringForBitmaskRestriction (struct mapi_SBitmaskRestriction *resBitmask) -{ - NSString *format; - const char *propName; - - propName = get_proptag_name (resBitmask->ulPropTag); - if (!propName) - propName = ""; - - if (resBitmask->relMBR == 0) - format = @"((%s(0x%.8x) & 0x%.8x) == 0)"; - else - format = @"((%s(0x%.8x) & 0x%.8x) != 0)"; - - return [NSString stringWithFormat: format, - propName, resBitmask->ulPropTag, resBitmask->ulMask]; -} - -static NSString * -MAPIStringForRestriction (struct mapi_SRestriction *resPtr) -{ - NSString *restrictionStr; - - if (resPtr) - { - switch (resPtr->rt) - { - // RES_CONTENT=(int)(0x3), - // RES_BITMASK=(int)(0x6), - // RES_EXIST=(int)(0x8), - - case 0: restrictionStr = MAPIStringForAndRestriction(&resPtr->res.resAnd); break; - case 1: restrictionStr = MAPIStringForOrRestriction(&resPtr->res.resOr); break; - case 2: restrictionStr = MAPIStringForNotRestriction(&resPtr->res.resNot); break; - case 3: restrictionStr = MAPIStringForContentRestriction(&resPtr->res.resContent); break; - case 4: restrictionStr = MAPIStringForPropertyRestriction(&resPtr->res.resProperty); break; - case 6: restrictionStr = MAPIStringForBitmaskRestriction(&resPtr->res.resBitmask); break; - case 8: restrictionStr = MAPIStringForExistRestriction(&resPtr->res.resExist); break; - // case 5: MAPIStringForComparePropsRestriction(&resPtr->res.resCompareProps); break; - // case 7: MAPIStringForPropertyRestriction(&resPtr->res.resProperty); break; - // case 9: MAPIStringForPropertyRestriction(&resPtr->res.resProperty); break; - // case 10: MAPIStringForPropertyRestriction(&resPtr->res.resProperty); break; - default: - restrictionStr - = [NSString stringWithFormat: @"[unhandled restriction type: %d]", - resPtr->rt]; - } - } - else - restrictionStr = @"[unrestricted]"; - - return restrictionStr; -} - @implementation MAPIStoreContext : NSObject /* sogo://username:password@{contacts,calendar,tasks,journal,notes,mail}/dossier/id */ @@ -329,6 +125,18 @@ static NSMutableDictionary *contextClassMapping; { } +- (Class) messageTableClass +{ + [self subclassResponsibility: _cmd]; + + return Nil; +} + +- (Class) folderTableClass +{ + return [MAPIStoreFolderTable class]; +} + static inline MAPIStoreContext * _prepareContextClass (struct mapistore_context *newMemCtx, Class contextClass, NSString *completeURLString, @@ -413,19 +221,16 @@ _prepareContextClass (struct mapistore_context *newMemCtx, { if ((self = [super init])) { - messageCache = [NSMutableDictionary new]; - subfolderCache = [NSMutableDictionary new]; messages = [NSMutableDictionary new]; woContext = [WOContext contextWithRequest: nil]; [woContext retain]; parentFoldersBag = [NSMutableArray new]; moduleFolder = nil; + faiModuleFolder = nil; uri = nil; - baseContextSet = NO; - - restrictedMessageCache = [NSMutableDictionary new]; - restrictionState = MAPIRestrictionStateAlwaysTrue; - restriction = nil; + messageTable = nil; + faiTable = nil; + folderTable = nil; } [self logWithFormat: @"-init"]; @@ -438,14 +243,15 @@ _prepareContextClass (struct mapistore_context *newMemCtx, [self logWithFormat: @"-dealloc"]; [parentFoldersBag release]; - [restriction release]; - [restrictedMessageCache release]; - [messageCache release]; - [subfolderCache release]; + [messageTable release]; + [faiTable release]; + [folderTable release]; + [messages release]; [moduleFolder release]; + [faiModuleFolder release]; [woContext release]; [authenticator release]; @@ -457,13 +263,12 @@ _prepareContextClass (struct mapistore_context *newMemCtx, - (void) setURI: (NSString *) newUri andMemCtx: (struct mapistore_context *) newMemCtx { - struct loadparm_context *lpCtx; - ASSIGN (uri, newUri); memCtx = newMemCtx; - lpCtx = loadparm_init (newMemCtx); - ldbCtx = mapiproxy_server_openchange_ldb_init (lpCtx); + faiModuleFolder = [SOGoMAPIFSFolder folderWithURL: [NSURL URLWithString: newUri] + andTableType: MAPISTORE_FAI_TABLE]; + [faiModuleFolder retain]; } - (void) setAuthenticator: (MAPIStoreAuthenticator *) newAuthenticator @@ -482,7 +287,7 @@ _prepareContextClass (struct mapistore_context *newMemCtx, [MAPIApp setMAPIStoreContext: self]; info = [[NSThread currentThread] threadDictionary]; - [info setObject: woContext forKey:@"WOContext"]; + [info setObject: woContext forKey: @"WOContext"]; } - (void) tearDownRequest @@ -490,7 +295,7 @@ _prepareContextClass (struct mapistore_context *newMemCtx, NSMutableDictionary *info; info = [[NSThread currentThread] threadDictionary]; - [info removeObjectForKey:@"WOContext"]; + [info removeObjectForKey: @"WOContext"]; [MAPIApp setMAPIStoreContext: nil]; } @@ -524,7 +329,9 @@ _prepareContextClass (struct mapistore_context *newMemCtx, // } // } -- (id) lookupObject: (NSString *) objectURLString + +- (id) _lookupObject: (NSString *) objectURLString + fromBaseFolder: (SOGoFolder *) baseFolder { id object; NSURL *objectURL; @@ -536,11 +343,7 @@ _prepareContextClass (struct mapistore_context *newMemCtx, objectURL = [NSURL URLWithString: objectURLString]; if (objectURL) { - object = moduleFolder; - if (!object) - [NSException raise: @"MAPIStoreIOException" - format: @"no moduleFolder set for context"]; - + object = baseFolder; pathString = [objectURL path]; if ([pathString hasPrefix: @"/"]) pathString = [pathString substringFromIndex: 1]; @@ -577,10 +380,30 @@ _prepareContextClass (struct mapistore_context *newMemCtx, } [woContext setClientObject: object]; - + return object; } +- (id) lookupObject: (NSString *) objectURLString +{ + if (!moduleFolder) + [NSException raise: @"MAPIStoreIOException" + format: @"no moduleFolder set for context"]; + + return [self _lookupObject: objectURLString + fromBaseFolder: moduleFolder]; +} + +- (id) lookupFAIObject: (NSString *) objectURLString +{ + if (!faiModuleFolder) + [NSException raise: @"MAPIStoreIOException" + format: @"no moduleFolder set for context"]; + + return [self _lookupObject: objectURLString + fromBaseFolder: faiModuleFolder]; +} + - (NSString *) _createFolder: (struct SRow *) aRow inParentURL: (NSString *) parentFolderURL { @@ -723,80 +546,59 @@ _prepareContextClass (struct mapistore_context *newMemCtx, return MAPISTORE_SUCCESS; } -- (NSArray *) _messageKeysForFolderURL: (NSString *) folderURL +/* TODO: should handle folder hierarchies */ +- (MAPIStoreTable *) _tableForFID: (uint64_t) fid + andTableType: (uint8_t) tableType { - NSArray *keys; - SOGoFolder *folder; + MAPIStoreTable *table; - keys = [messageCache objectForKey: folderURL]; - if (!keys) + if (tableType == MAPISTORE_MESSAGE_TABLE) { - folder = [self lookupObject: folderURL]; - if (folder) - keys = [self getFolderMessageKeys: folder - matchingQualifier: nil]; - else - keys = [NSArray array]; - [messageCache setObject: keys forKey: folderURL]; - [self logWithFormat: @"message keys for '%@': %@", folderURL, keys]; + if (!messageTable) + { + messageTable = [[self messageTableClass] new]; + [messageTable setContext: self + withMemCtx: memCtx]; + [messageTable setFolder: moduleFolder + withURL: uri + andFID: fid]; + } + table = messageTable; + } + else if (tableType == MAPISTORE_FAI_TABLE) + { + if (!faiTable) + { + faiTable = [MAPIStoreFAIMessageTable new]; + [faiTable setContext: self + withMemCtx: memCtx]; + [faiTable setFolder: faiModuleFolder + withURL: uri + andFID: fid]; + } + table = faiTable; + } + else if (tableType == MAPISTORE_FOLDER_TABLE) + { + if (!folderTable) + { + folderTable = [[self folderTableClass] new]; + [folderTable setContext: self + withMemCtx: memCtx]; + [folderTable setFolder: moduleFolder + withURL: uri + andFID: fid]; + } + table = folderTable; + } + else + { + table = nil; + [NSException raise: @"MAPIStoreIOException" + format: @"unsupported table type: %d", tableType]; } - return keys; -} - -- (NSArray *) _restrictedMessageKeysForFolderURL: (NSString *) folderURL -{ - NSArray *keys; - SOGoFolder *folder; - - keys = [restrictedMessageCache objectForKey: folderURL]; - if (!keys) - { - folder = [self lookupObject: folderURL]; - if (folder) - keys = [self getFolderMessageKeys: folder - matchingQualifier: restriction]; - else - keys = [NSArray array]; - [restrictedMessageCache setObject: keys forKey: folderURL]; - [self logWithFormat: @"restricted message keys for '%@': %@", folderURL, keys]; - } - - return keys; -} - -- (NSArray *) getFolderMessageKeys: (SOGoFolder *) folder - matchingQualifier: (EOQualifier *) qualifier -{ - [self subclassResponsibility: _cmd]; - - return nil; -} - -- (NSArray *) _subfolderKeysForFolderURL: (NSString *) folderURL -{ - // NSArray *keys; - // SOGoFolder *folder; - - return [NSArray array]; - // keys = [subfolderCache objectForKey: folderURL]; - // if (!keys) - // { - // folder = [self lookupObject: folderURL]; - // if (folder) - // { - // keys = [folder toManyRelationshipKeys]; - // if (!keys) - // keys = [NSArray array]; - // } - // else - // keys = [NSArray array]; - // [subfolderCache setObject: keys forKey: folderURL]; - // } - - // [self logWithFormat: @"folder keys for '%@': %@", folderURL, keys]; - - // return keys; + return table; } /** @@ -810,660 +612,81 @@ _prepareContextClass (struct mapistore_context *newMemCtx, ofTableType: (uint8_t) tableType inFID: (uint64_t) fid { - NSArray *ids; + MAPIStoreTable *table; + NSArray *keys; NSString *url; int rc; [self logWithFormat: @"METHOD '%s' (%d)", __FUNCTION__, __LINE__]; - [self logWithFormat: @"context restriction state is: %@", - MAPIStringForRestrictionState (restrictionState)]; - if (restriction) - [self logWithFormat: @" active qualifier: %@", restriction]; + // [self logWithFormat: @"context restriction state is: %@", + // MAPIStringForRestrictionState (restrictionState)]; + // if (restriction) + // [self logWithFormat: @" active qualifier: %@", restriction]; - if (restrictionState == MAPIRestrictionStateAlwaysFalse) + // if (restrictionState == MAPIRestrictionStateAlwaysFalse) + // { + // *rowCount = 0; + // rc = MAPI_E_SUCCESS; + // } + // else + // { + url = [mapping urlFromID: fid]; + if (url) { - *rowCount = 0; + table = [self _tableForFID: fid andTableType: tableType]; + keys = [table cachedChildKeys]; + *rowCount = [keys count]; rc = MAPI_E_SUCCESS; } else { - url = [mapping urlFromID: fid]; - if (url) - { - if (tableType == MAPISTORE_FOLDER_TABLE) - ids = [self _subfolderKeysForFolderURL: url]; - else - ids = [self _messageKeysForFolderURL: url]; - - if ([ids isKindOfClass: NSArrayK]) - *rowCount = [ids count]; - else - *rowCount = 0; - rc = MAPI_E_SUCCESS; - } - else - { - [self errorWithFormat: @"No url found for FID: %lld", fid]; - rc = MAPISTORE_ERR_NOT_FOUND; - } + [self errorWithFormat: @"No url found for FID: %lld", fid]; + rc = MAPISTORE_ERR_NOT_FOUND; } + // } [self logWithFormat: @"result: count = %d, rc = %d", *rowCount, rc]; return rc; } -- (enum MAPISTATUS) getCommonTableChildproperty: (void **) data - atURL: (NSString *) childURL - withTag: (enum MAPITAGS) proptag - inFolder: (SOGoFolder *) folder - withFID: (uint64_t) fid -{ - NSString *stringValue; - id child; - // uint64_t *llongValue; - // uint32_t *longValue; - int rc; - const char *propName; +// - (void) logRestriction: (struct mapi_SRestriction *) res +// withState: (MAPIRestrictionState) state +// { +// NSString *resStr; - rc = MAPI_E_SUCCESS; - switch (proptag) - { - case PR_DISPLAY_NAME_UNICODE: - child = [self lookupObject: childURL]; - *data = [[child displayName] asUnicodeInMemCtx: memCtx]; - break; - case PR_SEARCH_KEY: - child = [self lookupObject: childURL]; - stringValue = [child nameInContainer]; - *data = [[stringValue dataUsingEncoding: NSASCIIStringEncoding] - asBinaryInMemCtx: memCtx]; - break; - default: - // rc = MAPI_E_NOT_FOUND; - // if ((proptag & 0x001F) == 0x001F) - // { - propName = get_proptag_name (proptag); - if (!propName) - propName = ""; - [self errorWithFormat: @"Unhandled value: %s (0x%.8x), childURL: %@", - propName, proptag, childURL]; - *data = NULL; - // *data = [stringValue asUnicodeInMemCtx: memCtx]; - // rc = MAPI_E_SUCCESS; - // [self errorWithFormat: @"Unknown proptag (returned): %.8x for child '%@'", - // proptag, childURL]; - // } - // } - // else - // { - // *data = NULL; - rc = MAPI_E_NOT_FOUND; - break; - } +// resStr = MAPIStringForRestriction (res); - return rc; -} +// [self logWithFormat: @"%@ --> %@", resStr, MAPIStringForRestrictionState (state)]; +// } -- (enum MAPISTATUS) getMessageTableChildproperty: (void **) data - atURL: (NSString *) childURL - withTag: (enum MAPITAGS) proptag - inFolder: (SOGoFolder *) folder - withFID: (uint64_t) fid -{ - int rc; - uint32_t contextId; - uint64_t mappingId; - NSString *folderURL, *stringValue; - id child; - - rc = MAPI_E_SUCCESS; - switch (proptag) - { - case PR_INST_ID: // TODO: DOUBT - /* we return a unique id based on the url */ - *data = MAPILongLongValue (memCtx, [childURL hash]); - break; - case PR_INSTANCE_NUM: // TODO: DOUBT - *data = MAPILongValue (memCtx, 0); - break; - case PR_ROW_TYPE: // TODO: DOUBT - *data = MAPILongValue (memCtx, TBL_LEAF_ROW); - break; - case PR_DEPTH: // TODO: DOUBT - *data = MAPILongLongValue (memCtx, 0); - break; - case PR_ACCESS: // TODO - *data = MAPILongValue (memCtx, 0x02); - break; - case PR_ACCESS_LEVEL: // TODO - *data = MAPILongValue (memCtx, 0x00000000); - break; - case PR_VD_VERSION: - /* mandatory value... wtf? */ - *data = MAPILongValue (memCtx, 8); - break; - case PR_FID: - *data = MAPILongLongValue (memCtx, fid); - break; - case PR_MID: - mappingId = [mapping idFromURL: childURL]; - if (mappingId == NSNotFound) - { - openchangedb_get_new_folderID (ldbCtx, &mappingId); - [mapping registerURL: childURL withID: mappingId]; - folderURL = [mapping urlFromID: fid]; - NSAssert (folderURL != nil, - @"folder URL is expected to be known here"); - contextId = 0; - mapistore_search_context_by_uri (memCtx, [uri UTF8String] + 7, - &contextId); - NSAssert (contextId > 0, @"no matching context found"); - mapistore_indexing_record_add_mid (memCtx, contextId, mappingId); - } - *data = MAPILongLongValue (memCtx, mappingId); - break; - case PR_MESSAGE_CODEPAGE: - *data = MAPILongValue (memCtx, 0x0000); // use folder object codepage - break; - case PR_MESSAGE_LOCALE_ID: - *data = MAPILongValue (memCtx, 0x0409); - break; - case PR_MESSAGE_FLAGS: // TODO - *data = MAPILongValue (memCtx, 0x02 | 0x20); // fromme + unmodified - break; - case PR_MESSAGE_SIZE: // TODO - child = [self lookupObject: childURL]; - /* TODO: choose another name in SOGo for that method */ - *data = MAPILongValue (memCtx, [[child davContentLength] intValue]); - break; - case PR_MSG_STATUS: // TODO - *data = MAPILongValue (memCtx, 0); - break; - case PR_SUBJECT_PREFIX_UNICODE: // TODO - *data = [@"" asUnicodeInMemCtx: memCtx]; - break; - case PR_IMPORTANCE: // TODO -> subclass? - *data = MAPILongValue (memCtx, 1); - break; - case PR_PRIORITY: // TODO -> subclass? - *data = MAPILongValue (memCtx, 0); - break; - case PR_SENSITIVITY: // TODO -> subclass in calendar - *data = MAPILongValue (memCtx, 0); - break; - case PR_CHANGE_KEY: - child = [self lookupObject: childURL]; - stringValue = [child davEntityTag]; - *data = [[stringValue dataUsingEncoding: NSASCIIStringEncoding] - asShortBinaryInMemCtx: memCtx]; - break; - - /* those are queried while they really pertain to the - addressbook module */ - // #define PR_OAB_LANGID PROP_TAG(PT_LONG , 0x6807) /* 0x68070003 */ - // case PR_OAB_NAME_UNICODE: - // case PR_OAB_CONTAINER_GUID_UNICODE: - - // 0x68420102 PidTagScheduleInfoDelegatorWantsCopy (BOOL) - - default: - rc = [self getCommonTableChildproperty: data - atURL: childURL - withTag: proptag - inFolder: folder - withFID: fid]; - } - - return rc; -} - -- (NSString *) _parentURLFromURL: (NSString *) urlString -{ - NSString *newURL; - NSArray *parts; - NSMutableArray *newParts; - - parts = [urlString componentsSeparatedByString: @"/"]; - if ([parts count] > 3) - { - newParts = [parts mutableCopy]; - [newParts autorelease]; - [newParts removeLastObject]; - [newParts addObject: @""]; - newURL = [newParts componentsJoinedByString: @"/"]; - } - else - newURL = nil; - - return newURL; -} - -- (enum MAPISTATUS) getFolderTableChildproperty: (void **) data - atURL: (NSString *) childURL - withTag: (enum MAPITAGS) proptag - inFolder: (SOGoFolder *) folder - withFID: (uint64_t) fid -{ - // id child; - // struct Binary_r *binaryValue; - uint32_t contextId; - uint64_t mappingId; - int rc; - NSString *folderURL; - - rc = MAPI_E_SUCCESS; - switch (proptag) - { - case PR_FID: - mappingId = [mapping idFromURL: childURL]; - if (mappingId == NSNotFound) - { - openchangedb_get_new_folderID (ldbCtx, &mappingId); - [mapping registerURL: childURL withID: mappingId]; - folderURL = [mapping urlFromID: fid]; - NSAssert (folderURL != nil, - @"folder URL is expected to be known here"); - contextId = 0; - mapistore_search_context_by_uri (memCtx, [uri UTF8String] + 7, - &contextId); - NSAssert (contextId > 0, @"no matching context found"); - mapistore_indexing_record_add_fid (memCtx, contextId, mappingId); - } - // mappingId = [mapping idFromURL: childURL]; - // } - *data = MAPILongLongValue (memCtx, mappingId); - break; - case PR_PARENT_FID: - *data = MAPILongLongValue (memCtx, fid); - break; - case PR_ATTR_HIDDEN: - case PR_ATTR_SYSTEM: - case PR_ATTR_READONLY: - *data = MAPIBoolValue (memCtx, NO); - break; - case PR_SUBFOLDERS: - *data = MAPIBoolValue (memCtx, - [[self _subfolderKeysForFolderURL: childURL] - count] > 0); - break; - case PR_CONTENT_COUNT: - *data = MAPILongValue (memCtx, - [[self _messageKeysForFolderURL: childURL] - count]); - break; - // case PR_EXTENDED_FOLDER_FLAGS: // TODO: DOUBT: how to indicate the - // // number of subresponses ? - // binaryValue = talloc_zero(memCtx, struct Binary_r); - // *data = binaryValue; - // break; - default: - rc = [self getCommonTableChildproperty: data - atURL: childURL - withTag: proptag - inFolder: folder - withFID: fid]; - } - - return rc; -} - -- (void) logRestriction: (struct mapi_SRestriction *) res - withState: (MAPIRestrictionState) state -{ - NSString *resStr; - - resStr = MAPIStringForRestriction (res); - - [self logWithFormat: @"%@ --> %@", resStr, MAPIStringForRestrictionState (state)]; -} - -- (MAPIRestrictionState) evaluateRestriction: (struct mapi_SRestriction *) res - intoQualifier: (EOQualifier **) qualifier -{ - MAPIRestrictionState state; - - switch (res->rt) - { - /* basic operators */ - case 0: state = [self evaluateAndRestriction: &res->res.resAnd - intoQualifier: qualifier]; - break; - case 1: state = [self evaluateOrRestriction: &res->res.resOr - intoQualifier: qualifier]; - break; - case 2: state = [self evaluateNotRestriction: &res->res.resNot - intoQualifier: qualifier]; - break; - - /* content restrictions */ - case 3: state = [self evaluateContentRestriction: &res->res.resContent - intoQualifier: qualifier]; - break; - case 4: state = [self evaluatePropertyRestriction: &res->res.resProperty - intoQualifier: qualifier]; - break; - case 6: state = [self evaluateBitmaskRestriction: &res->res.resBitmask - intoQualifier: qualifier]; - break; - case 8: state = [self evaluateExistRestriction: &res->res.resExist - intoQualifier: qualifier]; - break; - // case 5: MAPIStringForComparePropsRestriction(&resPtr->res.resCompareProps); break; - // case 7: MAPIStringForPropertyRestriction(&resPtr->res.resProperty); break; - // case 9: MAPIStringForPropertyRestriction(&resPtr->res.resProperty); break; - // case 10: MAPIStringForPropertyRestriction(&resPtr->res.resProperty); break; - default: - [NSException raise: @"MAPIStoreRestrictionException" - format: @"unhandled restriction type"]; - state = MAPIRestrictionStateAlwaysTrue; - } - - [self logRestriction: res withState: state]; - - return state; -} - -- (MAPIRestrictionState) evaluateNotRestriction: (struct mapi_SNotRestriction *) res - intoQualifier: (EOQualifier **) qualifierPtr -{ - MAPIRestrictionState state, subState; - EONotQualifier *qualifier; - EOQualifier *subQualifier; - - subState = [self evaluateRestriction: (struct mapi_SRestriction *)&res->res - intoQualifier: &subQualifier]; - if (subState == MAPIRestrictionStateAlwaysTrue) - state = MAPIRestrictionStateAlwaysFalse; - else if (subState == MAPIRestrictionStateAlwaysFalse) - state = MAPIRestrictionStateAlwaysTrue; - else - { - state = MAPIRestrictionStateNeedsEval; - qualifier = [[EONotQualifier alloc] initWithQualifier: subQualifier]; - [qualifier autorelease]; - *qualifierPtr = qualifier; - } - - return state; -} - -- (MAPIRestrictionState) evaluateAndRestriction: (struct mapi_SAndRestriction *) res - intoQualifier: (EOQualifier **) qualifierPtr -{ - MAPIRestrictionState state, subState; - EOAndQualifier *qualifier; - EOQualifier *subQualifier; - NSMutableArray *subQualifiers; - uint16_t count; - - state = MAPIRestrictionStateNeedsEval; - - subQualifiers = [NSMutableArray arrayWithCapacity: 8]; - for (count = 0; - state == MAPIRestrictionStateNeedsEval && count < res->cRes; - count++) - { - subState = [self evaluateRestriction: (struct mapi_SRestriction *) res->res + count - intoQualifier: &subQualifier]; - if (subState == MAPIRestrictionStateNeedsEval) - [subQualifiers addObject: subQualifier]; - else if (subState == MAPIRestrictionStateAlwaysFalse) - state = MAPIRestrictionStateAlwaysFalse; - } - - if (state == MAPIRestrictionStateNeedsEval) - { - if ([subQualifiers count] == 0) - state = MAPIRestrictionStateAlwaysTrue; - else - { - qualifier = [[EOAndQualifier alloc] - initWithQualifierArray: subQualifiers]; - [qualifier autorelease]; - *qualifierPtr = qualifier; - } - } - - return state; -} - -- (MAPIRestrictionState) evaluateOrRestriction: (struct mapi_SOrRestriction *) res - intoQualifier: (EOQualifier **) qualifierPtr -{ - MAPIRestrictionState state, subState; - EOOrQualifier *qualifier; - EOQualifier *subQualifier; - NSMutableArray *subQualifiers; - uint16_t count, falseCount; - - state = MAPIRestrictionStateNeedsEval; - - falseCount = 0; - subQualifiers = [NSMutableArray arrayWithCapacity: 8]; - for (count = 0; - state == MAPIRestrictionStateNeedsEval && count < res->cRes; - count++) - { - subState = [self evaluateRestriction: (struct mapi_SRestriction *) res->res + count - intoQualifier: &subQualifier]; - if (subState == MAPIRestrictionStateNeedsEval) - [subQualifiers addObject: subQualifier]; - else if (subState == MAPIRestrictionStateAlwaysTrue) - state = MAPIRestrictionStateAlwaysTrue; - else - falseCount++; - } - - if (falseCount == res->cRes) - state = MAPIRestrictionStateAlwaysFalse; - else if ([subQualifiers count] == 0) - state = MAPIRestrictionStateAlwaysTrue; - - if (state == MAPIRestrictionStateNeedsEval) - { - qualifier = [[EOOrQualifier alloc] - initWithQualifierArray: subQualifiers]; - [qualifier autorelease]; - *qualifierPtr = qualifier; - } - - return state; -} - -- (NSString *) backendIdentifierForProperty: (enum MAPITAGS) property -{ - [self subclassResponsibility: _cmd]; - - return nil; -} - -- (void) _raiseUnhandledPropertyException: (enum MAPITAGS) property - inFunction: (const char *) function -{ - const char *propName; - - propName = get_proptag_name (property); - if (!propName) - propName = ""; - [NSException raise: @"MAPIStoreUnhandledPropertyException" - format: @"property %s (%.8x) has no matching field name (%@)" - @" in '%s'", - propName, property, self, function]; -} - -- (MAPIRestrictionState) evaluateContentRestriction: (struct mapi_SContentRestriction *) res - intoQualifier: (EOQualifier **) qualifier -{ - NSString *property; - SEL operator; - id value; - - property = [self backendIdentifierForProperty: res->ulPropTag]; - if (!property) - [self _raiseUnhandledPropertyException: res->ulPropTag - inFunction: __FUNCTION__]; - - value = NSObjectFromMAPISPropValue (&res->lpProp); - if ([value isKindOfClass: NSDataK]) - { - value = [[NSString alloc] initWithData: value - encoding: NSUTF8StringEncoding]; - [value autorelease]; - } - else if (![value isKindOfClass: NSStringK]) - [NSException raise: @"MAPIStoreTypeConversionException" - format: @"unhandled content restriction for class '%@'", - NSStringFromClass ([value class])]; - - switch (res->fuzzy & 0xf) - { - case 0: - operator = EOQualifierOperatorEqual; - break; - case 1: - operator = EOQualifierOperatorLike; - value = [NSString stringWithFormat: @"%%%@%%", value]; - break; - case 2: - operator = EOQualifierOperatorEqual; - value = [NSString stringWithFormat: @"%@%%", value]; - break; - default: [NSException raise: @"MAPIStoreInvalidOperatorException" - format: @"fuzzy operator value '%.4x' is invalid", - res->fuzzy]; - } - - *qualifier = [[EOKeyValueQualifier alloc] initWithKey: property - operatorSelector: EOQualifierOperatorCaseInsensitiveLike - value: value]; - [*qualifier autorelease]; - - [self logWithFormat: @"%s: resulting qualifier: %@", - __PRETTY_FUNCTION__, *qualifier]; - - return MAPIRestrictionStateNeedsEval; -} - -- (MAPIRestrictionState) evaluatePropertyRestriction: (struct mapi_SPropertyRestriction *) res - intoQualifier: (EOQualifier **) qualifier -{ - static SEL operators[] = { EOQualifierOperatorLessThan, - EOQualifierOperatorLessThanOrEqualTo, - EOQualifierOperatorGreaterThan, - EOQualifierOperatorGreaterThanOrEqualTo, - EOQualifierOperatorEqual, - EOQualifierOperatorNotEqual, - EOQualifierOperatorContains }; - SEL operator; - id value; - NSString *property; - - property = [self backendIdentifierForProperty: res->ulPropTag]; - if (!property) - [self _raiseUnhandledPropertyException: res->ulPropTag - inFunction: __FUNCTION__]; - - if (res->relop >= 0 && res->relop < 7) - operator = operators[res->relop]; - else - { - operator = NULL; - [NSException raise: @"MAPIStoreRestrictionException" - format: @"unhandled operator type number %d", res->relop]; - } - - value = NSObjectFromMAPISPropValue (&res->lpProp); - *qualifier = [[EOKeyValueQualifier alloc] initWithKey: property - operatorSelector: operator - value: value]; - [*qualifier autorelease]; - - return MAPIRestrictionStateNeedsEval; -} - -- (MAPIRestrictionState) evaluateBitmaskRestriction: (struct mapi_SBitmaskRestriction *) res - intoQualifier: (EOQualifier **) qualifier -{ - NSString *property; - - property = [self backendIdentifierForProperty: res->ulPropTag]; - if (!property) - [self _raiseUnhandledPropertyException: res->ulPropTag - inFunction: __FUNCTION__]; - - *qualifier = [[EOBitmaskQualifier alloc] initWithKey: property - mask: res->ulMask - isZero: (res->relMBR == BMR_EQZ)]; - [*qualifier autorelease]; - - return MAPIRestrictionStateNeedsEval; -} - -- (MAPIRestrictionState) evaluateExistRestriction: (struct mapi_SExistRestriction *) res - intoQualifier: (EOQualifier **) qualifier -{ - NSString *property; - - property = [self backendIdentifierForProperty: res->ulPropTag]; - if (!property) - [self _raiseUnhandledPropertyException: res->ulPropTag - inFunction: __FUNCTION__]; - - *qualifier = [[EOKeyValueQualifier alloc] initWithKey: property - operatorSelector: EOQualifierOperatorNotEqual - value: nil]; - [*qualifier autorelease]; - - return MAPIRestrictionStateNeedsEval; -} - -- (int) setRestrictions: (struct mapi_SRestriction *) res +- (int) setRestrictions: (const struct mapi_SRestriction *) res withFID: (uint64_t) fid - andTableType: (uint8_t) type + andTableType: (uint8_t) tableType getTableStatus: (uint8_t *) tableStatus { - NSString *folderURL; - - NSLog (@"set restriction to (table type: %d): %@", - type, MAPIStringForRestriction (res)); - - [restriction release]; - if (res) - restrictionState = [self evaluateRestriction: res - intoQualifier: &restriction]; - else - restrictionState = MAPIRestrictionStateAlwaysTrue; - - if (restrictionState == MAPIRestrictionStateNeedsEval) - [restriction retain]; - else - restriction = nil; - - folderURL = [mapping urlFromID: fid]; - if (folderURL) - [restrictedMessageCache removeObjectForKey: folderURL]; - - if (restriction) - [self logWithFormat: @" resulting EOQualifier: %@", restriction]; + MAPIStoreTable *table; + table = [self _tableForFID: fid andTableType: tableType]; + [table setRestrictions: res]; + return MAPISTORE_SUCCESS; } - (enum MAPISTATUS) getTableProperty: (void **) data - withTag: (enum MAPITAGS) proptag + withTag: (enum MAPITAGS) propTag atPosition: (uint32_t) pos withTableType: (uint8_t) tableType andQueryType: (enum table_query_type) queryType inFID: (uint64_t) fid { NSArray *children, *restrictedChildren; - NSString *folderURL, *childURL, *childName; - SOGoFolder *folder; + NSString *folderURL, *childKey; + MAPIStoreTable *table; const char *propName; int rc; - propName = get_proptag_name (proptag); + propName = get_proptag_name (propTag); if (!propName) propName = ""; // [self logWithFormat: @"METHOD '%s' (%d) -- proptag: %s (0x%.8x), pos: %.8x," @@ -1475,111 +698,146 @@ _prepareContextClass (struct mapistore_context *newMemCtx, // if (restriction) // [self logWithFormat: @" active qualifier: %@", restriction]; - if (restrictionState == MAPIRestrictionStateAlwaysFalse) - rc = MAPI_E_INVALID_OBJECT; - else + folderURL = [mapping urlFromID: fid]; + if (folderURL) { - folderURL = [mapping urlFromID: fid]; - if (folderURL) + restrictedChildren = nil; + table = [self _tableForFID: fid andTableType: tableType]; + if (queryType == MAPISTORE_PREFILTERED_QUERY) { - folder = [self lookupObject: folderURL]; + children = [table cachedRestrictedChildKeys]; restrictedChildren = nil; - if (tableType == MAPISTORE_FOLDER_TABLE) + } + else + { + children = [table cachedChildKeys]; + restrictedChildren = [table cachedRestrictedChildKeys]; + } + + if ([children count] > pos) + { + childKey = [children objectAtIndex: pos]; + + // TODO: the use of restrictedChildren might be optimized by + // making it a dictionary (hash versus linear search) + if (queryType == MAPISTORE_PREFILTERED_QUERY + || [restrictedChildren containsObject: childKey]) { - if (queryType != MAPISTORE_PREFILTERED_QUERY) - [NSException raise: @"MAPIStoreIOException" - format: @"filtering is not supported for folder tables"]; - children = [self _subfolderKeysForFolderURL: folderURL]; - } - else - { // MAPISTORE_MESSAGE_TABLE: - if (queryType == MAPISTORE_PREFILTERED_QUERY) - children = [self _restrictedMessageKeysForFolderURL: folderURL]; - else - { - children = [self _messageKeysForFolderURL: folderURL]; - restrictedChildren = [self _restrictedMessageKeysForFolderURL: folderURL]; - } - } - - if ([children count] > pos) - { - childName = [[children objectAtIndex: pos] stringByEscapingURL]; - if ([folderURL hasSuffix: @"/"]) - childURL = [folderURL stringByAppendingString: childName]; - else - childURL = [folderURL stringByAppendingFormat: @"/%@", - childName]; - - if (tableType == MAPISTORE_FOLDER_TABLE) - { - // [self logWithFormat: @" querying child folder at URL: %@", childURL]; - rc = [self getFolderTableChildproperty: data - atURL: childURL - withTag: proptag - inFolder: folder - withFID: fid]; - } - else - { - // TODO: the use of restrictedChildren might be optimized by - // making it a dictionary (hash versus linear search) - if (queryType == MAPISTORE_PREFILTERED_QUERY - || [restrictedChildren containsObject: childName]) - { - // [self logWithFormat: @" querying child message at URL: %@", childURL]; - rc = [self getMessageTableChildproperty: data - atURL: childURL - withTag: proptag - inFolder: folder - withFID: fid]; - } - else - { - // [self logWithFormat: - // @"child '%@' does not match active restriction", - // childURL]; - rc = MAPI_E_INVALID_OBJECT; - } - } + rc = [table getChildProperty: data + forKey: childKey + withTag: propTag]; if (rc == MAPI_E_SUCCESS && *data == NULL) { [self errorWithFormat: @"both 'success' and NULL data" @" returned for proptag %s(0x%.8x)", - propName, proptag]; + propName, propTag]; rc = MAPI_E_NOT_FOUND; } } else - { - // [self errorWithFormat: - // @"Invalid row position %d for table type %d" - // @" in FID: %lld", - // pos, tableType, fid]; - rc = MAPI_E_INVALID_OBJECT; - } + // [self logWithFormat: + // @"child '%@' does not match active restriction", + // childURL]; + rc = MAPI_E_INVALID_OBJECT; } else { - [self errorWithFormat: @"No url found for FID: %lld", fid]; + // [self errorWithFormat: + // @"Invalid row position %d for table type %d" + // @" in FID: %lld", + // pos, tableType, fid]; rc = MAPI_E_INVALID_OBJECT; } } + else + { + [self errorWithFormat: @"No url found for FID: %lld", fid]; + rc = MAPI_E_INVALID_OBJECT; + } return rc; } +- (int) openMessage: (struct mapistore_message *) msg + forKey: (NSString *) childKey + inTable: (MAPIStoreTable *) table +{ + static enum MAPITAGS tags[] = { PR_SUBJECT_UNICODE, PR_HASATTACH, + PR_MESSAGE_DELIVERY_TIME, PR_MESSAGE_FLAGS, + PR_FLAG_STATUS, PR_SENSITIVITY, + PR_SENT_REPRESENTING_NAME_UNICODE, + PR_INTERNET_MESSAGE_ID_UNICODE, + PR_READ_RECEIPT_REQUESTED }; + struct SRowSet *recipients; + struct SRow *properties; + NSInteger count, max; + const char *propName; + void *propValue; + + [self logWithFormat: @"INCOMPLETE METHOD '%s' (%d): no recipient handling", + __FUNCTION__, __LINE__]; + recipients = talloc_zero (memCtx, struct SRowSet); + recipients->cRows = 0; + recipients->aRow = NULL; + msg->recipients = recipients; + + max = 9; + properties = talloc_zero (memCtx, struct SRow); + properties->cValues = 0; + properties->ulAdrEntryPad = 0; + properties->lpProps = talloc_array (properties, struct SPropValue, max); + for (count = 0; count < max; count++) + { + if ([table getChildProperty: &propValue + forKey: childKey + withTag: tags[count]] + == MAPI_E_SUCCESS) + { + if (propValue == NULL) + { + propName = get_proptag_name (tags[count]); + if (!propName) + propName = ""; + [self errorWithFormat: @"both 'success' and NULL data" + @" returned for proptag %s(0x%.8x)", + propName, tags[count]]; + } + + set_SPropValue_proptag (properties->lpProps + properties->cValues, + tags[count], + propValue); + properties->cValues++; + } + } + msg->properties = properties; + + return MAPI_E_SUCCESS; +} + - (int) openMessage: (struct mapistore_message *) msg withMID: (uint64_t) mid inFID: (uint64_t) fid { - NSString *childURL; + NSString *childURL, *childKey, *folderURL; + MAPIStoreTable *table; int rc; childURL = [mapping urlFromID: mid]; if (childURL) { - rc = [self openMessage: msg atURL: childURL]; + childKey = [self extractChildNameFromURL: childURL + andFolderURLAt: &folderURL]; + table = [self _tableForFID: fid andTableType: MAPISTORE_FAI_TABLE]; + if ([[table cachedChildKeys] containsObject: childKey]) + rc = [self openMessage: msg forKey: childKey inTable: table]; + else + { + table = [self _tableForFID: fid andTableType: MAPISTORE_MESSAGE_TABLE]; + if ([[table cachedChildKeys] containsObject: childKey]) + rc = [self openMessage: msg forKey: childKey inTable: table]; + else + rc = MAPI_E_INVALID_OBJECT; + } } else rc = MAPISTORE_ERR_NOT_FOUND; @@ -1587,16 +845,9 @@ _prepareContextClass (struct mapistore_context *newMemCtx, return rc; } -- (int) openMessage: (struct mapistore_message *) msg - atURL: (NSString *) childURL -{ - [self logWithFormat: @"UNIMPLEMENTED METHOD '%s' (%d)", __FUNCTION__, __LINE__]; - - return MAPISTORE_ERROR; -} - - (int) createMessagePropertiesWithMID: (uint64_t) mid inFID: (uint64_t) fid + isAssociated: (BOOL) isAssociated { NSMutableDictionary *newMessage; NSNumber *midNbr; @@ -1608,24 +859,28 @@ _prepareContextClass (struct mapistore_context *newMemCtx, forKey: @"fid"]; midNbr = [NSNumber numberWithUnsignedLongLong: mid]; [newMessage setObject: midNbr forKey: @"mid"]; + [newMessage setObject: [NSNumber numberWithBool: isAssociated] + forKey: @"associated"]; [messages setObject: newMessage forKey: midNbr]; [newMessage release]; return MAPISTORE_SUCCESS; } -- (id) createMessageInFolder: (id) parentFolder +- (id) createMessageOfClass: (NSString *) messageClass + inFolderAtURL: (NSString *) folderURL { [self subclassResponsibility: _cmd]; return nil; } -- (id) _createMessageWithMID: (uint64_t) mid - inFID: (uint64_t) fid +- (id) _createMessageOfClass: (NSString *) messageClass + associated: (BOOL) associated + withMID: (uint64_t) mid + inFID: (uint64_t) fid { NSString *folderURL, *messageURL; - SOGoFolder *parentFolder; id message; message = nil; @@ -1633,28 +888,29 @@ _prepareContextClass (struct mapistore_context *newMemCtx, folderURL = [mapping urlFromID: fid]; if (folderURL) { - parentFolder = [self lookupObject: folderURL]; - if (parentFolder) - { - [self logWithFormat: @"%s: instantiate message in folder: %@", - __PRETTY_FUNCTION__, folderURL]; - message = [self createMessageInFolder: parentFolder]; - if (message) - { - if (![folderURL hasSuffix: @"/"]) - folderURL = [NSString stringWithFormat: @"%@/", folderURL]; - messageURL = [NSString stringWithFormat: @"%@%@", folderURL, - [message nameInContainer]]; - [mapping registerURL: messageURL withID: mid]; - - [messageCache removeObjectForKey: folderURL]; - [restrictedMessageCache removeObjectForKey: folderURL]; - } - else - [self errorWithFormat: - @"no message created in folder '%.16x' with mid '%.16x'", - fid, mid]; - } + if (associated) + { + message = [[self lookupFAIObject: folderURL] newMessage]; + [faiTable cleanupCaches]; + } + else + { + message = [self createMessageOfClass: messageClass + inFolderAtURL: folderURL]; + [messageTable cleanupCaches]; + } + if (message) + { + if (![folderURL hasSuffix: @"/"]) + folderURL = [NSString stringWithFormat: @"%@/", folderURL]; + messageURL = [NSString stringWithFormat: @"%@%@", folderURL, + [message nameInContainer]]; + [mapping registerURL: messageURL withID: mid]; + } + else + [self errorWithFormat: + @"no message created in folder '%.16x' with mid '%.16x'", + fid, mid]; } else [self errorWithFormat: @"registered message without a valid fid (%.16x)", fid]; @@ -1678,37 +934,44 @@ _prepareContextClass (struct mapistore_context *newMemCtx, [NSNumber numberWithUnsignedLongLong: mid]]; if (messageProperties) { - if ([[messageProperties - objectForKey: MAPIPropertyKey (PR_MESSAGE_CLASS_UNICODE)] - isEqualToString: @"IPM.Microsoft.FolderDesign.NamedView"]) + messageURL = [mapping urlFromID: mid]; + if ([[messageProperties objectForKey: @"associated"] boolValue]) { - [self logWithFormat: @"ignored message with view data:"]; - MAPIStoreDumpMessageProperties (messageProperties); - rc = MAPI_E_NO_SUPPORT; + if (messageURL) + message = [self lookupFAIObject: messageURL]; + else + { + fid = [[messageProperties objectForKey: @"fid"] + unsignedLongLongValue]; + message = [self _createMessageOfClass: [messageProperties objectForKey: MAPIPropertyKey (PR_MESSAGE_CLASS_UNICODE)] + associated: YES + withMID: mid inFID: fid]; + } } else { - messageURL = [mapping urlFromID: mid]; if (messageURL) message = [self lookupObject: messageURL]; else { fid = [[messageProperties objectForKey: @"fid"] unsignedLongLongValue]; - message = [self _createMessageWithMID: mid inFID: fid]; + message = [self _createMessageOfClass: [messageProperties objectForKey: MAPIPropertyKey (PR_MESSAGE_CLASS_UNICODE)] + associated: NO + withMID: mid inFID: fid]; } - if (message) - { - [message setMAPIProperties: messageProperties]; - if (isSave) - [message MAPISave]; - else - [message MAPISubmit]; - rc = MAPISTORE_SUCCESS; - } - else - rc = MAPISTORE_ERROR; } + if (message) + { + [message setMAPIProperties: messageProperties]; + if (isSave) + [message MAPISave]; + else + [message MAPISubmit]; + rc = MAPISTORE_SUCCESS; + } + else + rc = MAPISTORE_ERROR; } else rc = MAPISTORE_ERR_NOT_FOUND; @@ -1743,7 +1006,15 @@ _prepareContextClass (struct mapistore_context *newMemCtx, inRow: (struct SRow *) aRow withMID: (uint64_t) fmid { - NSString *childURL; + MAPIStoreTable *table; + NSArray *children; + NSString *childURL, *folderURL, *childKey; + NSInteger count; + void *propValue; + uint64_t fid; + const char *propName; + enum MAPITAGS tag; + enum MAPISTATUS propRc; int rc; [self logWithFormat: @"METHOD '%s' -- fmid: 0x%.16x, tableType: %d", @@ -1752,99 +1023,69 @@ _prepareContextClass (struct mapistore_context *newMemCtx, childURL = [mapping urlFromID: fmid]; if (childURL) { - switch (tableType) - { - case MAPISTORE_MESSAGE: - rc = [self getMessageProperties: sPropTagArray inRow: aRow - atURL: childURL]; - break; - case MAPISTORE_FOLDER: - default: - [self errorWithFormat: @"%s: value of tableType not handled: %d", - __FUNCTION__, tableType]; - rc = MAPISTORE_ERROR; - break; - } + childKey = [self extractChildNameFromURL: childURL + andFolderURLAt: &folderURL]; + fid = [mapping idFromURL: folderURL]; + if (fid == NSNotFound) + [NSException raise: @"MAPIStoreIOException" + format: @"no fid found for url '%@'", folderURL]; + + table = [self _tableForFID: fid andTableType: tableType]; + children = [table cachedChildKeys]; + if ([children containsObject: childKey]) + { + aRow->lpProps = talloc_array (aRow, struct SPropValue, + sPropTagArray->cValues); + for (count = 0; count < sPropTagArray->cValues; count++) + { + tag = sPropTagArray->aulPropTag[count]; + + propValue = NULL; + propRc = [table getChildProperty: &propValue + forKey: childKey + withTag: tag]; + // propName = get_proptag_name (tag); + // if (!propName) + // propName = ""; + // [self logWithFormat: @" lookup of property %s (%.8x) returned %d", + // propName, tag, propRc]; + + if (propRc == MAPI_E_SUCCESS && !propValue) + { + propName = get_proptag_name (tag); + if (!propName) + propName = ""; + [self errorWithFormat: @"both 'success' and NULL data" + @" returned for proptag %s(0x%.8x)", + propName, tag]; + propRc = MAPI_E_NOT_FOUND; + } + + if (propRc != MAPI_E_SUCCESS) + { + if (propValue) + talloc_free (propValue); + propValue = MAPILongValue (memCtx, propRc); + tag = (tag & 0xffff0000) | 0x000a; + } + set_SPropValue_proptag (&(aRow->lpProps[aRow->cValues]), + tag, propValue); + aRow->cValues++; + } + rc = MAPI_E_SUCCESS; + } + else + rc = MAPI_E_INVALID_OBJECT; } else { [self errorWithFormat: @"No url found for FMID: %lld", fmid]; - rc = MAPISTORE_ERR_NOT_FOUND; + rc = MAPI_E_INVALID_OBJECT; } return rc; } -- (int) getMessageProperties: (struct SPropTagArray *) sPropTagArray - inRow: (struct SRow *) aRow - atURL: (NSString *) childURL -{ - id child; - NSInteger count; - void *propValue; - const char *propName; - enum MAPITAGS tag; - enum MAPISTATUS propRc; - int rc; - - child = [self lookupObject: childURL]; - if (child) - { - aRow->lpProps = talloc_array (aRow, struct SPropValue, - sPropTagArray->cValues); - for (count = 0; count < sPropTagArray->cValues; count++) - { - tag = sPropTagArray->aulPropTag[count]; - - propValue = NULL; - propRc = [self getMessageTableChildproperty: &propValue - atURL: childURL - withTag: tag - inFolder: nil - withFID: 0]; - propName = get_proptag_name (tag); - if (!propName) - propName = ""; - [self logWithFormat: @" lookup of property %s (%.8x) returned %d", - propName, tag, propRc]; - - if (propRc == MAPI_E_SUCCESS && !propValue) - [self errorWithFormat: @"both 'success' and NULL data returned"]; - - if (propRc != MAPI_E_SUCCESS) - { - if (propValue) - talloc_free (propValue); - propValue = MAPILongValue (memCtx, propRc); - tag = (tag & 0xffff0000) | 0x000a; - } - set_SPropValue_proptag (&(aRow->lpProps[aRow->cValues]), - tag, propValue); - aRow->cValues++; - } - rc = MAPI_E_SUCCESS; - } - else - rc = MAPI_E_NOT_FOUND; - - return rc; -} - -// struct indexing_context_list { -// struct tdb_wrap *index_ctx; -// char *username; -// uint32_t ref_count; -// struct indexing_context_list *prev; -// struct indexing_context_list *next; -// }; - -// struct tdb_wrap { -// struct tdb_context *tdb; -// const char *name; -// struct tdb_wrap *prev; -// struct tdb_wrap *next; -// }; - - (int) getPath: (char **) path ofFMID: (uint64_t) fmid withTableType: (uint8_t) tableType @@ -1934,8 +1175,9 @@ _prepareContextClass (struct mapistore_context *newMemCtx, } else { - [self errorWithFormat: @"fmid 0x%.16x *not* found", fmid]; - rc = MAPISTORE_ERR_NOT_FOUND; + [self errorWithFormat: @"fmid 0x%.16x *not* found (faking success)", + fmid]; + rc = MAPISTORE_SUCCESS; } break; case MAPISTORE_FOLDER: @@ -2209,7 +1451,8 @@ _prepareContextClass (struct mapistore_context *newMemCtx, && [currentURL hasPrefix: uri]) { nsFolderList = [NSMutableArray arrayWithCapacity: 32]; - currentURL = [self _parentURLFromURL: currentURL]; + [self extractChildNameFromURL: currentURL + andFolderURLAt: ¤tURL]; while (currentURL && rc == MAPI_E_SUCCESS && ![currentURL isEqualToString: uri]) { @@ -2222,7 +1465,8 @@ _prepareContextClass (struct mapistore_context *newMemCtx, else { [nsFolderList addObject: [NSNumber numberWithUnsignedLongLong: fid]]; - currentURL = [self _parentURLFromURL: currentURL]; + [self extractChildNameFromURL: currentURL + andFolderURLAt: ¤tURL]; } } @@ -2269,4 +1513,30 @@ _prepareContextClass (struct mapistore_context *newMemCtx, } } +- (NSString *) extractChildNameFromURL: (NSString *) objectURL + andFolderURLAt: (NSString **) folderURL; +{ + NSString *childKey; + NSRange lastSlash; + NSUInteger slashPtr; + + if ([objectURL hasSuffix: @"/"]) + objectURL = [objectURL substringToIndex: [objectURL length] - 2]; + lastSlash = [objectURL rangeOfString: @"/" + options: NSBackwardsSearch]; + if (lastSlash.location != NSNotFound) + { + slashPtr = NSMaxRange (lastSlash); + childKey = [objectURL substringFromIndex: slashPtr]; + if ([childKey length] == 0) + childKey = nil; + if (folderURL) + *folderURL = [objectURL substringToIndex: slashPtr]; + } + else + childKey = nil; + + return childKey; +} + @end diff --git a/OpenChange/MAPIStoreFolderTable.h b/OpenChange/MAPIStoreFolderTable.h new file mode 100644 index 000000000..f8c2b24d0 --- /dev/null +++ b/OpenChange/MAPIStoreFolderTable.h @@ -0,0 +1,31 @@ +/* MAPIStoreFolderTable.h - this file is part of SOGo + * + * Copyright (C) 2010 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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 MAPISTOREFOLDERTABLE_H +#define MAPISTOREFOLDERTABLE_H + +#import "MAPIStoreTable.h" + +@interface MAPIStoreFolderTable : MAPIStoreTable +@end + +#endif /* MAPISTOREFOLDERTABLE_H */ diff --git a/OpenChange/MAPIStoreFolderTable.m b/OpenChange/MAPIStoreFolderTable.m new file mode 100644 index 000000000..5cf265ede --- /dev/null +++ b/OpenChange/MAPIStoreFolderTable.m @@ -0,0 +1,129 @@ +/* MAPIStoreFolderTable.m - this file is part of SOGo + * + * Copyright (C) 2010 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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 + +#import "MAPIStoreMapping.h" +#import "MAPIStoreTypes.h" + +#import "MAPIStoreFolderTable.h" + +#undef DEBUG +#include +#include +#include + +@implementation MAPIStoreFolderTable + +static MAPIStoreMapping *mapping; + ++ (void) initialize +{ + mapping = [MAPIStoreMapping sharedMapping]; +} + +- (NSArray *) childKeys +{ + return [NSArray array]; + // return [folder toManyRelationshipKeys]; +} + +- (NSArray *) restrictedChildKeys +{ + [self errorWithFormat: @"restrictions are ignored on mail folder tables"]; + return [folder toManyRelationshipKeys]; +} + +- (enum MAPISTATUS) getChildProperty: (void **) data + forKey: (NSString *) childKey + withTag: (enum MAPITAGS) propTag +{ + SOGoFolder *child; + // id child; + // struct Binary_r *binaryValue; + NSString *childURL; + uint32_t contextId; + uint64_t mappingId; + int rc; + + rc = MAPI_E_SUCCESS; + switch (propTag) + { + case PR_FID: + childURL = [NSString stringWithFormat: @"%@%@", folderURL, childKey]; + mappingId = [mapping idFromURL: childURL]; + if (mappingId == NSNotFound) + { + openchangedb_get_new_folderID (ldbCtx, &mappingId); + [mapping registerURL: childURL withID: mappingId]; + contextId = 0; + mapistore_search_context_by_uri (memCtx, [folderURL UTF8String] + 7, + &contextId); + mapistore_indexing_record_add_mid (memCtx, contextId, mappingId); + } + *data = MAPILongLongValue (memCtx, mappingId); + break; + case PR_ACCESS: // TODO + *data = MAPILongValue (memCtx, 0x63); + break; + case PR_ACCESS_LEVEL: // TODO + *data = MAPILongValue (memCtx, 0x01); + break; + case PR_PARENT_FID: + *data = MAPILongLongValue (memCtx, fid); + break; + case PR_ATTR_HIDDEN: + case PR_ATTR_SYSTEM: + case PR_ATTR_READONLY: + *data = MAPIBoolValue (memCtx, NO); + break; + case PR_SUBFOLDERS: + child = [self lookupChild: childKey]; + *data = MAPIBoolValue (memCtx, 0); + // [[child toManyRelationshipKeys] count] > 0); + // *data = MAPIBoolValue (memCtx, + // [[child toManyRelationshipKeys] count] > 0); + break; + case PR_CONTENT_COUNT: + child = [self lookupChild: childKey]; + *data = MAPILongValue (memCtx, + [[child toOneRelationshipKeys] count]); + break; + // case PR_EXTENDED_FOLDER_FLAGS: // TODO: DOUBT: how to indicate the + // // number of subresponses ? + // binaryValue = talloc_zero(memCtx, struct Binary_r); + // *data = binaryValue; + // break; + default: + rc = [super getChildProperty: data + forKey: childKey + withTag: propTag]; + } + + return rc; +} + +@end diff --git a/OpenChange/MAPIStoreMessageTable.h b/OpenChange/MAPIStoreMessageTable.h new file mode 100644 index 000000000..6ba737949 --- /dev/null +++ b/OpenChange/MAPIStoreMessageTable.h @@ -0,0 +1,31 @@ +/* MAPIStoreMessageTable.h - this file is part of SOGo + * + * Copyright (C) 2010 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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 MAPISTOREMESSAGETABLE_H +#define MAPISTOREMESSAGETABLE_H + +#import "MAPIStoreTable.h" + +@interface MAPIStoreMessageTable : MAPIStoreTable +@end + +#endif /* MAPISTOREMESSAGETABLE_H */ diff --git a/OpenChange/MAPIStoreMessageTable.m b/OpenChange/MAPIStoreMessageTable.m new file mode 100644 index 000000000..8f5249120 --- /dev/null +++ b/OpenChange/MAPIStoreMessageTable.m @@ -0,0 +1,201 @@ +/* MAPIStoreMessageTable.m - this file is part of SOGo + * + * Copyright (C) 2010 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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 "MAPIStoreMessageTable.h" + +#undef DEBUG +#include +#include +#include +#include +#include +#include + +@interface SOGoObject (MAPIStoreProtocol) + +- (NSString *) davEntityTag; +- (NSString *) davContentLength; + +@end + +@implementation MAPIStoreMessageTable + +static MAPIStoreMapping *mapping; + ++ (void) initialize +{ + mapping = [MAPIStoreMapping sharedMapping]; +} + +- (NSArray *) childKeys +{ + return [folder toOneRelationshipKeys]; +} + +- (enum MAPISTATUS) getChildProperty: (void **) data + forKey: (NSString *) childKey + withTag: (enum MAPITAGS) propTag +{ + int rc; + uint32_t contextId; + uint64_t mappingId; + NSString *stringValue, *childURL; + NSUInteger length; + id child; + + rc = MAPI_E_SUCCESS; + switch (propTag) + { + case PR_INST_ID: // TODO: DOUBT + /* we return a unique id based on the key */ + *data = MAPILongLongValue (memCtx, [childKey hash]); + break; + case PR_INSTANCE_NUM: // TODO: DOUBT + *data = MAPILongValue (memCtx, 0); + break; + case PR_ROW_TYPE: // TODO: DOUBT + *data = MAPILongValue (memCtx, TBL_LEAF_ROW); + break; + case PR_DEPTH: // TODO: DOUBT + *data = MAPILongLongValue (memCtx, 0); + break; + case PR_ACCESS: // TODO + *data = MAPILongValue (memCtx, 0x02); + break; + case PR_ACCESS_LEVEL: // TODO + *data = MAPILongValue (memCtx, 0x00000000); + break; + case PR_VIEW_STYLE: + case PR_VIEW_MAJORVERSION: + *data = MAPILongValue (memCtx, 0); + break; + case PidLidSideEffects: // TODO + *data = MAPILongValue (memCtx, 0x00000000); + break; + case PidLidCurrentVersion: + *data = MAPILongValue (memCtx, 115608); // Outlook 11.5608 + break; + case PidLidCurrentVersionName: + *data = [@"11.0" asUnicodeInMemCtx: memCtx]; + break; + case PidLidAutoProcessState: + *data = MAPILongValue (memCtx, 0x00000000); + break; + case PidNameContentClass: + *data = [@"Sharing" asUnicodeInMemCtx: memCtx]; + break; + + // case PR_VD_NAME_UNICODE: + // *data = [@"PR_VD_NAME_UNICODE fake value" asUnicodeInMemCtx: memCtx]; + // break; + // case PR_VD_VERSION: + // /* mandatory value... wtf? */g + // *data = MAPILongValue (memCtx, 8); + // break; + case PR_FID: + *data = MAPILongLongValue (memCtx, fid); + break; + case PR_MID: + childURL = [NSString stringWithFormat: @"%@%@", folderURL, childKey]; + mappingId = [mapping idFromURL: childURL]; + if (mappingId == NSNotFound) + { + openchangedb_get_new_folderID (ldbCtx, &mappingId); + [mapping registerURL: childURL withID: mappingId]; + contextId = 0; + mapistore_search_context_by_uri (memCtx, [folderURL UTF8String] + 7, + &contextId); + mapistore_indexing_record_add_mid (memCtx, contextId, mappingId); + } + *data = MAPILongLongValue (memCtx, mappingId); + break; + case PR_MESSAGE_CODEPAGE: + case PR_INTERNET_CPID: + *data = MAPILongValue (memCtx, 1200); + break; + case PR_MESSAGE_LOCALE_ID: + *data = MAPILongValue (memCtx, 0x0409); + break; + case PR_MESSAGE_FLAGS: // TODO + *data = MAPILongValue (memCtx, MSGFLAG_FROMME | MSGFLAG_READ | MSGFLAG_UNMODIFIED); + break; + case PR_MESSAGE_SIZE: // TODO + child = [self lookupChild: childKey]; + /* TODO: choose another name in SOGo for that method */ + *data = MAPILongValue (memCtx, [[child davContentLength] intValue]); + break; + case PR_MSG_STATUS: // TODO + *data = MAPILongValue (memCtx, 0); + break; + case PR_SUBJECT_PREFIX_UNICODE: // TODO + *data = [@"" asUnicodeInMemCtx: memCtx]; + break; + case PR_IMPORTANCE: // TODO -> subclass? + *data = MAPILongValue (memCtx, 1); + break; + case PR_PRIORITY: // TODO -> subclass? + *data = MAPILongValue (memCtx, 0); + break; + case PR_SENSITIVITY: // TODO -> subclass in calendar + *data = MAPILongValue (memCtx, 0); + break; + case PR_CHANGE_KEY: + child = [self lookupChild: childKey]; + stringValue = [child davEntityTag]; + length = [stringValue length]; + if (length < 6) /* guid = 16 bytes */ + { + length += 6; + stringValue = [NSString stringWithFormat: @"000000%@", + stringValue]; + } + if (length > 6) + stringValue = [stringValue substringFromIndex: length - 7]; + stringValue = [NSString stringWithFormat: @"SOGo%@%@%@", + stringValue, stringValue, stringValue]; + *data = [[stringValue dataUsingEncoding: NSASCIIStringEncoding] + asShortBinaryInMemCtx: memCtx]; + break; + + case PR_SUBJECT_UNICODE: + rc = [self getChildProperty: data forKey: childKey + withTag: PR_NORMALIZED_SUBJECT_UNICODE]; + break; + + default: + rc = [super getChildProperty: data + forKey: childKey + withTag: propTag]; + } + + return rc; +} + +@end diff --git a/OpenChange/MAPIStoreTable.h b/OpenChange/MAPIStoreTable.h new file mode 100644 index 000000000..5ccc7e643 --- /dev/null +++ b/OpenChange/MAPIStoreTable.h @@ -0,0 +1,106 @@ +/* MAPIStoreTable.h - this file is part of SOGo + * + * Copyright (C) 2010 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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 MAPISTORETABLE_H +#define MAPISTORETABLE_H + +#import + +#define SENSITIVITY_NONE 0 +#define SENSITIVITY_PERSONAL 1 +#define SENSITIVITY_PRIVATE 2 +#define SENSITIVITY_COMPANY_CONFIDENTIAL 3 + +#define TBL_LEAF_ROW 0x00000001 +#define TBL_EMPTY_CATEGORY 0x00000002 +#define TBL_EXPANDED_CATEGORY 0x00000003 +#define TBL_COLLAPSED_CATEGORY 0x00000004 + +typedef enum { + MAPIRestrictionStateAlwaysFalse = NO, + MAPIRestrictionStateAlwaysTrue = YES, + MAPIRestrictionStateNeedsEval, /* needs passing of qualifier to underlying + database */ +} MAPIRestrictionState; + +@class NSArray; +@class NSString; +@class EOQualifier; + +@interface MAPIStoreTable : NSObject +{ + id context; + struct mapistore_context *memCtx; + void *ldbCtx; + + id folder; + NSString *folderURL; + uint64_t fid; + + id lastChild; + NSString *lastChildKey; + + NSArray *cachedKeys; + NSArray *cachedRestrictedKeys; + EOQualifier *restriction; /* should probably be a dictionary too */ + MAPIRestrictionState restrictionState; +} + +- (id) folder; + +- (void) setContext: (id) newContext + withMemCtx: (struct mapistore_context *) newMemCtx; +- (void) setFolder: (id) newFolder + withURL: (NSString *) newFolderURL + andFID: (uint64_t) newFid; + +- (void) setRestrictions: (const struct mapi_SRestriction *) res; + +- (NSArray *) cachedChildKeys; +- (NSArray *) cachedRestrictedChildKeys; + +- (id) lookupChild: (NSString *) childKey; + +- (void) cleanupCaches; + +- (enum MAPISTATUS) getChildProperty: (void **) data + forKey: (NSString *) childKey + withTag: (enum MAPITAGS) proptag; + +/* subclasses */ + +- (NSArray *) childKeys; +- (NSArray *) restrictedChildKeys; + +- (NSString *) backendIdentifierForProperty: (enum MAPITAGS) property; +- (MAPIRestrictionState) evaluateContentRestriction: (const struct mapi_SContentRestriction *) res + intoQualifier: (EOQualifier **) qualifier; +- (MAPIRestrictionState) evaluatePropertyRestriction: (const struct mapi_SPropertyRestriction *) res + intoQualifier: (EOQualifier **) qualifier; +- (MAPIRestrictionState) evaluateBitmaskRestriction: (const struct mapi_SBitmaskRestriction *) res + intoQualifier: (EOQualifier **) qualifier; +- (MAPIRestrictionState) evaluateExistRestriction: (const struct mapi_SExistRestriction *) res + intoQualifier: (EOQualifier **) qualifier; + +@end + +#endif /* MAPISTORETABLE_H */ diff --git a/OpenChange/MAPIStoreTable.m b/OpenChange/MAPIStoreTable.m new file mode 100644 index 000000000..3bff46999 --- /dev/null +++ b/OpenChange/MAPIStoreTable.m @@ -0,0 +1,792 @@ +/* MAPIStoreTable.m - this file is part of SOGo + * + * Copyright (C) 2010 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * 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. + */ + +#import + +#import +#import + +#import + +#import "EOBitmaskQualifier.h" +#import "MAPIStoreTypes.h" +#import "NSData+MAPIStore.h" +#import "NSString+MAPIStore.h" + +#import "MAPIStoreTable.h" + +#undef DEBUG +#include +#include + +@interface MAPIStoreTable (Private) + +- (MAPIRestrictionState) evaluateRestriction: (const struct mapi_SRestriction *) res + intoQualifier: (EOQualifier **) qualifier; +@end + +static Class NSDataK, NSStringK; + +// static NSString * +// MAPIStringForRestrictionState (MAPIRestrictionState state) +// { +// NSString *stateStr; + +// if (state == MAPIRestrictionStateAlwaysTrue) +// stateStr = @"always true"; +// else if (state == MAPIRestrictionStateAlwaysFalse) +// stateStr = @"always false"; +// else +// stateStr = @"needs eval"; + +// return stateStr; +// } + +// static NSString * +// MAPIStringForRestriction (const struct mapi_SRestriction *resPtr); + +// static NSString * +// _MAPIIndentString(int indent) +// { +// NSString *spaces; +// char *buffer; + +// if (indent > 0) +// { +// buffer = malloc (indent + 1); +// memset (buffer, 32, indent); +// *(buffer+indent) = 0; +// spaces = [NSString stringWithFormat: @"%s", buffer]; +// free (buffer); +// } +// else +// spaces = @""; + +// return spaces; +// } + +// static NSString * +// MAPIStringForAndRestriction (const struct mapi_SAndRestriction *resAnd) +// { +// NSMutableArray *restrictions; +// uint16_t count; + +// restrictions = [NSMutableArray arrayWithCapacity: 8]; +// for (count = 0; count < resAnd->cRes; count++) +// [restrictions addObject: MAPIStringForRestriction ((struct mapi_SRestriction *) resAnd->res + count)]; + +// return [NSString stringWithFormat: @"(%@)", [restrictions componentsJoinedByString: @" && "]]; +// } + +// static NSString * +// MAPIStringForOrRestriction (const struct mapi_SOrRestriction *resOr) +// { +// NSMutableArray *restrictions; +// uint16_t count; + +// restrictions = [NSMutableArray arrayWithCapacity: 8]; +// for (count = 0; count < resOr->cRes; count++) +// [restrictions addObject: MAPIStringForRestriction ((struct mapi_SRestriction *) resOr->res + count)]; + +// return [NSString stringWithFormat: @"(%@)", [restrictions componentsJoinedByString: @" || "]]; +// } + +// static NSString * +// MAPIStringForNotRestriction (const struct mapi_SNotRestriction *resNot) +// { +// return [NSString stringWithFormat: @"!(%@)", +// MAPIStringForRestriction ((struct mapi_SRestriction *) &resNot->res)]; +// } + +// static NSString * +// MAPIStringForContentRestriction (const struct mapi_SContentRestriction *resContent) +// { +// NSString *eqMatch, *caseMatch; +// id value; +// const char *propName; + +// switch (resContent->fuzzy & 0xf) +// { +// case 0: eqMatch = @"eq"; break; +// case 1: eqMatch = @"substring"; break; +// case 2: eqMatch = @"prefix"; break; +// default: eqMatch = @"[unknown]"; +// } + +// switch (((resContent->fuzzy) >> 16) & 0xf) +// { +// case 0: caseMatch = @"fl"; break; +// case 1: caseMatch = @"nc"; break; +// case 2: caseMatch = @"ns"; break; +// case 4: caseMatch = @"lo"; break; +// default: caseMatch = @"[unknown]"; +// } + +// propName = get_proptag_name (resContent->ulPropTag); +// if (!propName) +// propName = ""; + +// value = NSObjectFromMAPISPropValue (&resContent->lpProp); + +// return [NSString stringWithFormat: @"%s(0x%.8x) %@,%@ %@", +// propName, resContent->ulPropTag, eqMatch, caseMatch, value]; +// } + +// static NSString * +// MAPIStringForExistRestriction (const struct mapi_SExistRestriction *resExist) +// { +// const char *propName; + +// propName = get_proptag_name (resExist->ulPropTag); +// if (!propName) +// propName = ""; + +// return [NSString stringWithFormat: @"%s(0x%.8x) IS NOT NULL", propName, resExist->ulPropTag]; +// } + +// static NSString * +// MAPIStringForPropertyRestriction (const struct mapi_SPropertyRestriction *resProperty) +// { +// static NSString *operators[] = { @"<", @"<=", @">", @">=", @"==", @"!=", +// @"=~" }; +// NSString *operator; +// id value; +// const char *propName; + +// propName = get_proptag_name (resProperty->ulPropTag); +// if (!propName) +// propName = ""; + +// if (resProperty->relop >= 0 && resProperty->relop < 7) +// operator = operators[resProperty->relop]; +// else +// operator = [NSString stringWithFormat: @"", resProperty->relop]; +// value = NSObjectFromMAPISPropValue (&resProperty->lpProp); + +// return [NSString stringWithFormat: @"%s(0x%.8x) %@ %@", +// propName, resProperty->ulPropTag, operator, value]; +// } + +// static NSString * +// MAPIStringForBitmaskRestriction (const struct mapi_SBitmaskRestriction *resBitmask) +// { +// NSString *format; +// const char *propName; + +// propName = get_proptag_name (resBitmask->ulPropTag); +// if (!propName) +// propName = ""; + +// if (resBitmask->relMBR == 0) +// format = @"((%s(0x%.8x) & 0x%.8x) == 0)"; +// else +// format = @"((%s(0x%.8x) & 0x%.8x) != 0)"; + +// return [NSString stringWithFormat: format, +// propName, resBitmask->ulPropTag, resBitmask->ulMask]; +// } + +// static NSString * +// MAPIStringForRestriction (const struct mapi_SRestriction *resPtr) +// { +// NSString *restrictionStr; + +// if (resPtr) +// { +// switch (resPtr->rt) +// { +// // RES_CONTENT=(int)(0x3), +// // RES_BITMASK=(int)(0x6), +// // RES_EXIST=(int)(0x8), + +// case 0: restrictionStr = MAPIStringForAndRestriction(&resPtr->res.resAnd); break; +// case 1: restrictionStr = MAPIStringForOrRestriction(&resPtr->res.resOr); break; +// case 2: restrictionStr = MAPIStringForNotRestriction(&resPtr->res.resNot); break; +// case 3: restrictionStr = MAPIStringForContentRestriction(&resPtr->res.resContent); break; +// case 4: restrictionStr = MAPIStringForPropertyRestriction(&resPtr->res.resProperty); break; +// case 6: restrictionStr = MAPIStringForBitmaskRestriction(&resPtr->res.resBitmask); break; +// case 8: restrictionStr = MAPIStringForExistRestriction(&resPtr->res.resExist); break; +// // case 5: MAPIStringForComparePropsRestriction(&resPtr->res.resCompareProps); break; +// // case 7: MAPIStringForPropertyRestriction(&resPtr->res.resProperty); break; +// // case 9: MAPIStringForPropertyRestriction(&resPtr->res.resProperty); break; +// // case 10: MAPIStringForPropertyRestriction(&resPtr->res.resProperty); break; +// default: +// restrictionStr +// = [NSString stringWithFormat: @"[unhandled restriction type: %d]", +// resPtr->rt]; +// } +// } +// else +// restrictionStr = @"[unrestricted]"; + +// return restrictionStr; +// } + +@implementation MAPIStoreTable + ++ (void) initialize +{ + NSDataK = [NSData class]; + NSStringK = [NSString class]; +} + +- (id) init +{ + if ((self = [super init])) + { + context = nil; + memCtx = NULL; + + folder = nil; + folderURL = nil; + + lastChild = nil; + lastChildKey = nil; + + cachedKeys = nil; + cachedRestrictedKeys = nil; + restriction = nil; + restrictionState = MAPIRestrictionStateAlwaysTrue; + } + + return self; +} + +- (void) dealloc +{ + [folder release]; + [folderURL release]; + [lastChildKey release]; + [lastChild release]; + [cachedKeys release]; + [cachedRestrictedKeys release]; + [restriction release]; + [super dealloc]; +} + +- (void) setContext: (id) newContext + withMemCtx: (struct mapistore_context *) newMemCtx +{ + struct loadparm_context *lpCtx; + + context = newContext; + + memCtx = newMemCtx; + lpCtx = loadparm_init (newMemCtx); + ldbCtx = mapiproxy_server_openchange_ldb_init (lpCtx); +} + +- (void) setFolder: (id) newFolder + withURL: (NSString *) newFolderURL + andFID: (uint64_t) newFid +{ + ASSIGN (folder, newFolder); + ASSIGN (folderURL, newFolderURL); + fid = newFid; +} + +- (id) folder +{ + if (!folder) + [self warnWithFormat: @"returning nil folder"]; + return folder; +} + +- (NSArray *) cachedChildKeys +{ + if (!cachedKeys) + { + cachedKeys = [self childKeys]; + [cachedKeys retain]; + } + + return cachedKeys; +} + +- (NSArray *) cachedRestrictedChildKeys +{ + if (!cachedRestrictedKeys) + { + cachedRestrictedKeys = [self restrictedChildKeys]; + [cachedRestrictedKeys retain]; + } + + return cachedRestrictedKeys; +} + +- (void) cleanupCaches +{ + [cachedRestrictedKeys release]; + cachedRestrictedKeys = nil; + [cachedKeys release]; + cachedKeys = nil; +} + +- (id) lookupChild: (NSString *) childKey +{ + id newChild; + + if ([lastChildKey isEqualToString: childKey]) + newChild = lastChild; + else + { + [self logWithFormat: @"child key is now '%@'", childKey]; + newChild = [folder lookupName: childKey + inContext: nil + acquire: NO]; + ASSIGN (lastChildKey, childKey); + ASSIGN (lastChild, newChild); + } + + return newChild; +} + +- (void) setRestrictions: (const struct mapi_SRestriction *) res +{ + EOQualifier *oldRestriction; + + // [self logWithFormat: @"set restriction to (table type: %d): %@", + // type, MAPIStringForRestriction (res)]; + + oldRestriction = restriction; + [restriction release]; + if (res) + restrictionState = [self evaluateRestriction: res + intoQualifier: &restriction]; + else + restrictionState = MAPIRestrictionStateAlwaysTrue; + + if (restrictionState == MAPIRestrictionStateNeedsEval) + [restriction retain]; + else + restriction = nil; + + [cachedRestrictedKeys release]; + cachedRestrictedKeys = nil; + + if (restriction) + [self logWithFormat: @"restriction set to EOQualifier: %@", + restriction]; + else if (oldRestriction) + [self logWithFormat: @"restriction unset"]; +} + +- (enum MAPISTATUS) getChildProperty: (void **) data + forKey: (NSString *) childKey + withTag: (enum MAPITAGS) propTag +{ + NSString *stringValue; + id child; + // uint64_t *llongValue; + // uint32_t *longValue; + int rc; + const char *propName; + + rc = MAPI_E_SUCCESS; + switch (propTag) + { + case PR_DISPLAY_NAME_UNICODE: + child = [self lookupChild: childKey]; + *data = [[child displayName] asUnicodeInMemCtx: memCtx]; + break; + case PR_SEARCH_KEY: // TODO + child = [self lookupChild: childKey]; + stringValue = [child nameInContainer]; + *data = [[stringValue dataUsingEncoding: NSASCIIStringEncoding] + asBinaryInMemCtx: memCtx]; + break; + case PR_GENERATE_EXCHANGE_VIEWS: // TODO + *data = MAPIBoolValue (memCtx, NO); + break; + + default: + propName = get_proptag_name (propTag); + if (!propName) + propName = ""; + [self warnWithFormat: + @"unhandled or NULL value: %s (0x%.8x), childKey: %@", + propName, propTag, childKey]; + // if ((propTag & 0x001F) == 0x001F) + // { + // stringValue = [NSString stringWithFormat: @"fake %s (0x.8x) value", + // propName, propTag]; + // *data = [stringValue asUnicodeInMemCtx: memCtx]; + // rc = MAPI_E_SUCCESS; + // } + // else + // { + *data = NULL; + rc = MAPI_E_NOT_FOUND; + // } + break; + } + + return rc; +} + +- (MAPIRestrictionState) evaluateNotRestriction: (struct mapi_SNotRestriction *) res + intoQualifier: (EOQualifier **) qualifierPtr +{ + MAPIRestrictionState state, subState; + EONotQualifier *qualifier; + EOQualifier *subQualifier; + + subState = [self evaluateRestriction: (struct mapi_SRestriction *)&res->res + intoQualifier: &subQualifier]; + if (subState == MAPIRestrictionStateAlwaysTrue) + state = MAPIRestrictionStateAlwaysFalse; + else if (subState == MAPIRestrictionStateAlwaysFalse) + state = MAPIRestrictionStateAlwaysTrue; + else + { + state = MAPIRestrictionStateNeedsEval; + qualifier = [[EONotQualifier alloc] initWithQualifier: subQualifier]; + [qualifier autorelease]; + *qualifierPtr = qualifier; + } + + return state; +} + +- (MAPIRestrictionState) evaluateAndRestriction: (struct mapi_SAndRestriction *) res + intoQualifier: (EOQualifier **) qualifierPtr +{ + MAPIRestrictionState state, subState; + EOAndQualifier *qualifier; + EOQualifier *subQualifier; + NSMutableArray *subQualifiers; + uint16_t count; + + state = MAPIRestrictionStateNeedsEval; + + subQualifiers = [NSMutableArray arrayWithCapacity: 8]; + for (count = 0; + state == MAPIRestrictionStateNeedsEval && count < res->cRes; + count++) + { + subState = [self evaluateRestriction: (struct mapi_SRestriction *) res->res + count + intoQualifier: &subQualifier]; + if (subState == MAPIRestrictionStateNeedsEval) + [subQualifiers addObject: subQualifier]; + else if (subState == MAPIRestrictionStateAlwaysFalse) + state = MAPIRestrictionStateAlwaysFalse; + } + + if (state == MAPIRestrictionStateNeedsEval) + { + if ([subQualifiers count] == 0) + state = MAPIRestrictionStateAlwaysTrue; + else + { + qualifier = [[EOAndQualifier alloc] + initWithQualifierArray: subQualifiers]; + [qualifier autorelease]; + *qualifierPtr = qualifier; + } + } + + return state; +} + +- (MAPIRestrictionState) evaluateOrRestriction: (struct mapi_SOrRestriction *) res + intoQualifier: (EOQualifier **) qualifierPtr +{ + MAPIRestrictionState state, subState; + EOOrQualifier *qualifier; + EOQualifier *subQualifier; + NSMutableArray *subQualifiers; + uint16_t count, falseCount; + + state = MAPIRestrictionStateNeedsEval; + + falseCount = 0; + subQualifiers = [NSMutableArray arrayWithCapacity: 8]; + for (count = 0; + state == MAPIRestrictionStateNeedsEval && count < res->cRes; + count++) + { + subState = [self evaluateRestriction: (struct mapi_SRestriction *) res->res + count + intoQualifier: &subQualifier]; + if (subState == MAPIRestrictionStateNeedsEval) + [subQualifiers addObject: subQualifier]; + else if (subState == MAPIRestrictionStateAlwaysTrue) + state = MAPIRestrictionStateAlwaysTrue; + else + falseCount++; + } + + if (falseCount == res->cRes) + state = MAPIRestrictionStateAlwaysFalse; + else if ([subQualifiers count] == 0) + state = MAPIRestrictionStateAlwaysTrue; + + if (state == MAPIRestrictionStateNeedsEval) + { + qualifier = [[EOOrQualifier alloc] + initWithQualifierArray: subQualifiers]; + [qualifier autorelease]; + *qualifierPtr = qualifier; + } + + return state; +} + +- (NSString *) backendIdentifierForProperty: (enum MAPITAGS) property +{ + [self subclassResponsibility: _cmd]; + + return nil; +} + +- (void) _warnUnhandledPropertyException: (enum MAPITAGS) property + inFunction: (const char *) function +{ + const char *propName; + + propName = get_proptag_name (property); + if (!propName) + propName = ""; + [self warnWithFormat: + @"property %s (%.8x) has no matching field name (%@) in '%s'", + propName, property, self, function]; +} + +- (MAPIRestrictionState) evaluateContentRestriction: (struct mapi_SContentRestriction *) res + intoQualifier: (EOQualifier **) qualifier +{ + NSString *property; + SEL operator; + id value; + MAPIRestrictionState rc; + + property = [self backendIdentifierForProperty: res->ulPropTag]; + if (property) + { + value = NSObjectFromMAPISPropValue (&res->lpProp); + if ([value isKindOfClass: NSDataK]) + { + value = [[NSString alloc] initWithData: value + encoding: NSUTF8StringEncoding]; + [value autorelease]; + } + else if (![value isKindOfClass: NSStringK]) + [NSException raise: @"MAPIStoreTypeConversionException" + format: @"unhandled content restriction for class '%@'", + NSStringFromClass ([value class])]; + + switch (res->fuzzy & 0xf) + { + case 0: + operator = EOQualifierOperatorEqual; + break; + case 1: + operator = EOQualifierOperatorLike; + value = [NSString stringWithFormat: @"%%%@%%", value]; + break; + case 2: + operator = EOQualifierOperatorEqual; + value = [NSString stringWithFormat: @"%@%%", value]; + break; + default: [NSException raise: @"MAPIStoreInvalidOperatorException" + format: @"fuzzy operator value '%.4x' is invalid", + res->fuzzy]; + } + + *qualifier = [[EOKeyValueQualifier alloc] initWithKey: property + operatorSelector: EOQualifierOperatorCaseInsensitiveLike + value: value]; + [*qualifier autorelease]; + + [self logWithFormat: @"%s: resulting qualifier: %@", + __PRETTY_FUNCTION__, *qualifier]; + + rc = MAPIRestrictionStateNeedsEval; + } + else + { + [self _warnUnhandledPropertyException: res->ulPropTag + inFunction: __FUNCTION__]; + + rc = MAPIRestrictionStateAlwaysFalse; + } + + return rc; +} + +- (MAPIRestrictionState) evaluatePropertyRestriction: (struct mapi_SPropertyRestriction *) res + intoQualifier: (EOQualifier **) qualifier +{ + static SEL operators[] = { EOQualifierOperatorLessThan, + EOQualifierOperatorLessThanOrEqualTo, + EOQualifierOperatorGreaterThan, + EOQualifierOperatorGreaterThanOrEqualTo, + EOQualifierOperatorEqual, + EOQualifierOperatorNotEqual, + EOQualifierOperatorContains }; + SEL operator; + id value; + NSString *property; + MAPIRestrictionState rc; + + property = [self backendIdentifierForProperty: res->ulPropTag]; + if (property) + { + if (res->relop >= 0 && res->relop < 7) + operator = operators[res->relop]; + else + { + operator = NULL; + [NSException raise: @"MAPIStoreRestrictionException" + format: @"unhandled operator type number %d", res->relop]; + } + + value = NSObjectFromMAPISPropValue (&res->lpProp); + *qualifier = [[EOKeyValueQualifier alloc] initWithKey: property + operatorSelector: operator + value: value]; + [*qualifier autorelease]; + + rc = MAPIRestrictionStateNeedsEval; + } + else + { + [self _warnUnhandledPropertyException: res->ulPropTag + inFunction: __FUNCTION__]; + rc = MAPIRestrictionStateAlwaysFalse; + } + + return rc; +} + +- (MAPIRestrictionState) evaluateBitmaskRestriction: (struct mapi_SBitmaskRestriction *) res + intoQualifier: (EOQualifier **) qualifier +{ + NSString *property; + MAPIRestrictionState rc; + + property = [self backendIdentifierForProperty: res->ulPropTag]; + if (property) + { + *qualifier = [[EOBitmaskQualifier alloc] initWithKey: property + mask: res->ulMask + isZero: (res->relMBR == BMR_EQZ)]; + [*qualifier autorelease]; + + rc = MAPIRestrictionStateNeedsEval; + } + else + { + [self _warnUnhandledPropertyException: res->ulPropTag + inFunction: __FUNCTION__]; + rc = MAPIRestrictionStateAlwaysFalse; + } + + return rc; +} + +- (MAPIRestrictionState) evaluateExistRestriction: (struct mapi_SExistRestriction *) res + intoQualifier: (EOQualifier **) qualifier +{ + NSString *property; + MAPIRestrictionState rc; + + property = [self backendIdentifierForProperty: res->ulPropTag]; + if (property) + { + *qualifier = [[EOKeyValueQualifier alloc] initWithKey: property + operatorSelector: EOQualifierOperatorNotEqual + value: nil]; + [*qualifier autorelease]; + + rc = MAPIRestrictionStateNeedsEval; + } + else + { + [self _warnUnhandledPropertyException: res->ulPropTag + inFunction: __FUNCTION__]; + rc = MAPIRestrictionStateAlwaysFalse; + } + + return rc; +} + +- (MAPIRestrictionState) evaluateRestriction: (struct mapi_SRestriction *) res + intoQualifier: (EOQualifier **) qualifier +{ + MAPIRestrictionState state; + + switch (res->rt) + { + /* basic operators */ + case 0: state = [self evaluateAndRestriction: &res->res.resAnd + intoQualifier: qualifier]; + break; + case 1: state = [self evaluateOrRestriction: &res->res.resOr + intoQualifier: qualifier]; + break; + case 2: state = [self evaluateNotRestriction: &res->res.resNot + intoQualifier: qualifier]; + break; + + /* content restrictions */ + case 3: state = [self evaluateContentRestriction: &res->res.resContent + intoQualifier: qualifier]; + break; + case 4: state = [self evaluatePropertyRestriction: &res->res.resProperty + intoQualifier: qualifier]; + break; + case 6: state = [self evaluateBitmaskRestriction: &res->res.resBitmask + intoQualifier: qualifier]; + break; + case 8: state = [self evaluateExistRestriction: &res->res.resExist + intoQualifier: qualifier]; + break; + // case 5: MAPIStringForComparePropsRestriction(&resPtr->res.resCompareProps); break; + // case 7: MAPIStringForPropertyRestriction(&resPtr->res.resProperty); break; + // case 9: MAPIStringForPropertyRestriction(&resPtr->res.resProperty); break; + // case 10: MAPIStringForPropertyRestriction(&resPtr->res.resProperty); break; + default: + [NSException raise: @"MAPIStoreRestrictionException" + format: @"unhandled restriction type"]; + state = MAPIRestrictionStateAlwaysTrue; + } + + // [self logRestriction: res withState: state]; + + return state; +} + +- (NSArray *) childKeys +{ + [self subclassResponsibility: _cmd]; + + return nil; +} + +- (NSArray *) restrictedChildKeys +{ + [self subclassResponsibility: _cmd]; + + return nil; +} + +@end