From 12788c847d5ba3d609848bf52589b4c7f3907595 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Mon, 8 Dec 2014 10:45:34 -0500 Subject: [PATCH] New sogo-tool feature to manage EAS data --- ActiveSync/SOGoActiveSyncDispatcher+Sync.m | 2 +- ActiveSync/SOGoActiveSyncDispatcher.m | 37 ++- NEWS | 3 + Tools/GNUmakefile | 3 +- Tools/SOGoToolManageEAS.m | 282 +++++++++++++++++++++ 5 files changed, 321 insertions(+), 6 deletions(-) create mode 100644 Tools/SOGoToolManageEAS.m diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m index d59102eb5..db70bde32 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m @@ -1118,7 +1118,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *changeDetected = YES; if (!([[self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]] objectForKey: @"displayName"])) - status = 13; // need folderSync + status = 12; // need folderSync else status = 3; // do a complete resync } diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 2c05a1fae..8becc4adb 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -908,6 +908,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [o reloadIfNeeded]; [[o properties ] setObject: [[folderMetadata objectForKey: @"path"] substringFromIndex: 1] forKey: @"displayName"]; + + // clean cache content to avoid stale data + [[o properties] removeObjectForKey: @"SyncKey"]; + [[o properties] removeObjectForKey: @"SyncCache"]; + [[o properties] removeObjectForKey: @"DateCache"]; + [[o properties] removeObjectForKey: @"MoreAvailable"]; + [[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"]; [o save]; command_count++; @@ -951,11 +958,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Decide between add and change if (![[o properties ] objectForKey: @"displayName"] || first_sync) - operation = @"Add"; + operation = @"Add"; else if (![[[o properties ] objectForKey: @"displayName"] isEqualToString: [[folders objectAtIndex:fi] displayName]]) - operation = @"Update"; - else - operation = nil; + operation = @"Update"; + else + operation = nil; if (operation) { @@ -983,6 +990,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [o setTableUrl: [self folderTableURL]]; [o reloadIfNeeded]; [[o properties ] setObject: [[folders objectAtIndex:fi] displayName] forKey: @"displayName"]; + + if ([operation isEqualToString: @"Add"]) + { + // clean cache content to avoid stale data + [[o properties] removeObjectForKey: @"SyncKey"]; + [[o properties] removeObjectForKey: @"SyncCache"]; + [[o properties] removeObjectForKey: @"DateCache"]; + [[o properties] removeObjectForKey: @"MoreAvailable"]; + [[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"]; + } + [o save]; } else @@ -994,6 +1012,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. command_count++; [[o properties ] setObject: [[folders objectAtIndex:fi] displayName] forKey: @"displayName"]; + + if ([operation isEqualToString: @"Add"]) + { + // clean cache content to avoid stale data + [[o properties] removeObjectForKey: @"SyncKey"]; + [[o properties] removeObjectForKey: @"SyncCache"]; + [[o properties] removeObjectForKey: @"DateCache"]; + [[o properties] removeObjectForKey: @"MoreAvailable"]; + [[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"]; + } + [o save]; } } diff --git a/NEWS b/NEWS index d0282ba99..a62f77dba 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ 2.2.11 (2014-xx-xx) ------------------- +New features + - sogo-tool can now be used to manage EAS metadata for all devices + Enhancements - Improved the SAML2 documentation - Radically reduced AES memory usage diff --git a/Tools/GNUmakefile b/Tools/GNUmakefile index bb580edc9..5b1eed2e9 100644 --- a/Tools/GNUmakefile +++ b/Tools/GNUmakefile @@ -22,7 +22,8 @@ $(SOGO_TOOL)_OBJC_FILES += \ SOGoToolRemoveDoubles.m \ SOGoToolRenameUser.m \ SOGoToolRestore.m \ - SOGoToolUserPreferences.m + SOGoToolUserPreferences.m \ + SOGoToolManageEAS.m TOOL_NAME += $(SOGO_TOOL) ### diff --git a/Tools/SOGoToolManageEAS.m b/Tools/SOGoToolManageEAS.m new file mode 100644 index 000000000..e001c67f8 --- /dev/null +++ b/Tools/SOGoToolManageEAS.m @@ -0,0 +1,282 @@ +/* SOGoToolManageEAS.m - this file is part of SOGo + * + * Copyright (C) 2014 Inverse inc. + * + * 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 +#import +#import + +#import +#import + +#import +#import +#import "SOGo/SOGoCredentialsFile.h" +#import +#import +#import +#import +#import +#import +#import + +#import + + +#import "SOGoTool.h" + +typedef enum +{ + ManageEASUnknown = -1, + ManageEASListDevices = 0, + ManageEASListFolders = 2, + ManageEASResetDevice = 3, + ManageEASRestFolder = 4, +} SOGoManageEASCommand; + +@interface SOGoToolManageEAS : SOGoTool +@end + +@implementation SOGoToolManageEAS + ++ (void) initialize +{ +} + ++ (NSString *) command +{ + return @"manage-eas"; +} + ++ (NSString *) description +{ + return @"manage EAS folders"; +} + +- (void) usage +{ + fprintf (stderr, "manage-eas listdevices|resetdevice|resetfolder user \n\n" + " user the user of whom to reset the whole device or a single folder\n" + " Examples:\n" + " sogo-tool manage-eas listdevices janedoe\n" + " sogo-tool manage-eas listfolders janedoe androidc316986417\n" + " sogo-tool manage-eas resetdevice janedoe androidc316986417\n" + " sogo-tool manage-eas resetfolder janedow androidc316986417+folderlala-dada-sasa_7a13_1a2386e0_e\n") ; +} + + +- (SOGoManageEASCommand) _cmdFromString: (NSString *) theString +{ + if ([theString length] > 2) + { + if ([theString caseInsensitiveCompare: @"listdevices"] == NSOrderedSame) + return ManageEASListDevices; + else if ([theString caseInsensitiveCompare: @"listfolders"] == NSOrderedSame) + return ManageEASListFolders; + else if ([theString caseInsensitiveCompare: @"resetdevice"] == NSOrderedSame) + return ManageEASResetDevice; + else if ([theString caseInsensitiveCompare: @"resetfolder"] == NSOrderedSame) + return ManageEASRestFolder; + } + + return ManageEASUnknown; +} + +- (BOOL) run +{ + NSString *userId; + SOGoManageEASCommand cmd; + SOGoCacheGCSObject *oc, *foc; + NSString *urlString, *ocFSTableName, *deviceId; + NSURL *folderTableURL; + NSMutableArray *parts; + + BOOL rc; + int max; + + max = [sanitizedArguments count]; + rc = NO; + + if (max > 1) + { + SOGoUser *user; + + cmd = [self _cmdFromString: [sanitizedArguments objectAtIndex: 0]]; + + userId = [sanitizedArguments objectAtIndex: 1]; + + user = [SOGoUser userWithLogin: userId]; + + if (![user loginInDomain]) + return NO; + + urlString = [[user domainDefaults] folderInfoURL]; + parts = [[urlString componentsSeparatedByString: @"/"] + mutableCopy]; + [parts autorelease]; + if ([parts count] == 5) + { + /* If "OCSFolderInfoURL" is properly configured, we must have 5 + parts in this url. We strip the '-' character in case we have + this in the domain part - like foo@bar-zot.com */ + ocFSTableName = [NSString stringWithFormat: @"sogo_cache_folder_%@", + [[[user loginInDomain] asCSSIdentifier] + stringByReplacingOccurrencesOfString: @"-" + withString: @"_"]]; + [parts replaceObjectAtIndex: 4 withObject: ocFSTableName]; + folderTableURL + = [NSURL URLWithString: [parts componentsJoinedByString: @"/"]]; + [folderTableURL retain]; + } + + switch (cmd) + { + case ManageEASListDevices: + oc = [SOGoCacheGCSObject objectWithName: @"0" inContainer: nil]; + [oc setObjectType: ActiveSyncGlobalCacheObject]; + + [oc setTableUrl: folderTableURL]; + + for (id cacheEntry in [oc cacheEntriesForDeviceId: nil newerThanVersion: -1]) + fprintf(stdout,"%s\n", [[cacheEntry substringFromIndex: 1] UTF8String]); + + rc = YES; + break; + + case ManageEASListFolders: + if (max > 2) + { + /* value specified on command line */ + deviceId = [sanitizedArguments objectAtIndex: 2]; + + oc = [SOGoCacheGCSObject objectWithName: @"0" inContainer: nil]; + [oc setObjectType: ActiveSyncFolderCacheObject]; + + [oc setTableUrl: folderTableURL]; + + for (id cacheEntry in [oc cacheEntriesForDeviceId: deviceId newerThanVersion: -1]) { + fprintf(stdout,"Folder Key: %s\n", [[cacheEntry substringFromIndex: 1] UTF8String]); + + foc = [SOGoCacheGCSObject objectWithName: [cacheEntry substringFromIndex: 1] inContainer: nil]; + [foc setObjectType: ActiveSyncFolderCacheObject]; + [foc setTableUrl: folderTableURL]; + + [foc reloadIfNeeded]; + + fprintf(stdout, " Folder Name: %s\n\n", [[[foc properties] objectForKey: @"displayName"] UTF8String]); + + if (verbose) + fprintf(stdout, " metadata Name: %s\n\n", [[[foc properties] description] UTF8String]); + + } + + rc = YES; + } + else + { + fprintf(stderr, "\nERROR: deviceId not specified\n\n"); + } + + break; + + + case ManageEASResetDevice: + if (max > 2) + { + /* value specified on command line */ + deviceId = [sanitizedArguments objectAtIndex: 2]; + oc = [SOGoCacheGCSObject objectWithName: deviceId inContainer: nil]; + [oc setObjectType: ActiveSyncGlobalCacheObject]; + [oc setTableUrl: folderTableURL]; + + [oc reloadIfNeeded]; + if ([oc isNew]) { + fprintf(stderr, "ERROR: Device with ID '%s' not found\n", [deviceId UTF8String]); + return rc; + } + + NSMutableString *sql; + + sql = [NSMutableString stringWithFormat: @"DELETE FROM %@" @" WHERE c_path like '/%@'", [oc tableName], deviceId]; + + [oc performBatchSQLQueries: [NSArray arrayWithObject: sql]]; + rc = YES; + } + else + { + fprintf(stderr, "\nERROR: deviceId not specified\n\n"); + } + + break; + + case ManageEASRestFolder: + if (max > 2) + { + /* value specified on command line */ + deviceId = [sanitizedArguments objectAtIndex: 2]; + + //if ([deviceId rangeOfString: @"+"].location == NSNotFound) { + // fprintf(stderr, "ERROR: Deviceid invalid folder \"%@\" not found\n", deviceId); + // return rc; + //} + + oc = [SOGoCacheGCSObject objectWithName: deviceId inContainer: nil]; + [oc setObjectType: ActiveSyncFolderCacheObject]; + [oc setTableUrl: folderTableURL]; + + [oc reloadIfNeeded]; + + if ([oc isNew]) { + fprintf(stderr, "ERROR: Folder with ID \"%s\" not found\n", [deviceId UTF8String]); + return rc; + } + [[oc properties] removeObjectForKey: @"SyncKey"]; + [[oc properties] removeObjectForKey: @"SyncCache"]; + [[oc properties] removeObjectForKey: @"DateCache"]; + [[oc properties] removeObjectForKey: @"MoreAvailable"]; + + [oc save]; + rc = YES; + + } + else + { + fprintf(stderr, "\nERROR: folderId not specified\n\n"); + } + + break; + + case ManageEASUnknown: + break; + } + } + + if (!rc) + { + [self usage]; + } + + return rc; +} + +@end