From 9434e3882ec73ed15236b88384819123de809e5c Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 9 Feb 2017 14:39:24 -0500 Subject: [PATCH] (feat) new sogo-tool checkup command to make sure user's data is sane --- Tools/GNUmakefile | 1 + Tools/SOGoSockD.h | 4 +- Tools/SOGoSockD.m | 4 +- Tools/SOGoSockDOperation.h | 4 +- Tools/SOGoSockDOperation.m | 4 +- Tools/SOGoSockDScanner.h | 4 +- Tools/SOGoSockDScanner.m | 4 +- Tools/SOGoToolCheckupUser.m | 323 ++++++++++++++++++++++++++++++++++ Tools/SOGoToolRemove.m | 4 +- Tools/SOGoToolRemoveDoubles.m | 4 +- Tools/SOGoToolRenameUser.m | 4 +- Tools/sogo-ealarms-notify.m | 4 +- Tools/sogo-slapd-sockd.m | 4 +- 13 files changed, 335 insertions(+), 33 deletions(-) create mode 100644 Tools/SOGoToolCheckupUser.m diff --git a/Tools/GNUmakefile b/Tools/GNUmakefile index 60d2a609f..ad7cc9320 100644 --- a/Tools/GNUmakefile +++ b/Tools/GNUmakefile @@ -20,6 +20,7 @@ $(SOGO_TOOL)_OBJC_FILES += \ SOGoToolRemove.m \ SOGoToolRemoveDoubles.m \ SOGoToolRenameUser.m \ + SOGoToolCheckupUser.m \ SOGoToolCleanupUser.m \ SOGoToolRestore.m \ SOGoToolCreateFolder.m \ diff --git a/Tools/SOGoSockD.h b/Tools/SOGoSockD.h index 18508c0e6..4eb8a56d8 100644 --- a/Tools/SOGoSockD.h +++ b/Tools/SOGoSockD.h @@ -1,8 +1,6 @@ /* SOGoSockD.h - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2010-2017 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 diff --git a/Tools/SOGoSockD.m b/Tools/SOGoSockD.m index a3a25f472..dbdb7c5ef 100644 --- a/Tools/SOGoSockD.m +++ b/Tools/SOGoSockD.m @@ -1,8 +1,6 @@ /* SOGoSockD.m - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2010-2017 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 diff --git a/Tools/SOGoSockDOperation.h b/Tools/SOGoSockDOperation.h index b97213fc4..64d3f9256 100644 --- a/Tools/SOGoSockDOperation.h +++ b/Tools/SOGoSockDOperation.h @@ -1,8 +1,6 @@ /* SOGoSockDOperation.h - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2010-2017 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 diff --git a/Tools/SOGoSockDOperation.m b/Tools/SOGoSockDOperation.m index d3aa4f3d4..1aff39b31 100644 --- a/Tools/SOGoSockDOperation.m +++ b/Tools/SOGoSockDOperation.m @@ -1,8 +1,6 @@ /* SOGoSockDOperation.m - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2010-2017 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 diff --git a/Tools/SOGoSockDScanner.h b/Tools/SOGoSockDScanner.h index e9f2ffe4e..989b5742d 100644 --- a/Tools/SOGoSockDScanner.h +++ b/Tools/SOGoSockDScanner.h @@ -1,8 +1,6 @@ /* SOGoSockDScanner.h - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2010-2017 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 diff --git a/Tools/SOGoSockDScanner.m b/Tools/SOGoSockDScanner.m index 5a16cec34..650d7ad5e 100644 --- a/Tools/SOGoSockDScanner.m +++ b/Tools/SOGoSockDScanner.m @@ -1,9 +1,7 @@ /* SOGoSockDScanner.m - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2010-2017 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 diff --git a/Tools/SOGoToolCheckupUser.m b/Tools/SOGoToolCheckupUser.m new file mode 100644 index 000000000..9b879d5de --- /dev/null +++ b/Tools/SOGoToolCheckupUser.m @@ -0,0 +1,323 @@ +/* SOGoToolCheckup.m - this file is part of SOGo + * + * Copyright (C) 2017 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 +#import + +#import +#import + +#import "SOGoTool.h" + +@interface SOGoToolCheckup : SOGoTool +{ + NSArray *usersToCheckup; +} + +@end + +@implementation SOGoToolCheckup + ++ (NSString *) command +{ + return @"checkup"; +} + ++ (NSString *) description +{ + return @"checkup integrity of user(s) data)"; +} + +- (id) init +{ + if ((self = [super init])) + { + usersToCheckup = nil; + } + + return self; +} + +- (void) dealloc +{ + [usersToCheckup release]; + [super dealloc]; +} + +- (void) usage +{ + fprintf (stderr, "checkup [user]...\n\n" + " user the user to purge the records or ALL for everybody\n\n" + "Example: sogo-tool checkup jdoe\n"); +} + +- (BOOL) fetchUserIDs: (NSArray *) users +{ + NSAutoreleasePool *pool; + SOGoUserManager *lm; + NSDictionary *infos; + NSString *user; + id allUsers; + int count, max; + + lm = [SOGoUserManager sharedUserManager]; + + max = [users count]; + user = [users objectAtIndex: 0]; + if (max == 1 && [user isEqualToString: @"ALL"]) + { + GCSFolderManager *fm; + GCSChannelManager *cm; + NSURL *folderLocation; + EOAdaptorChannel *fc; + NSArray *attrs; + NSMutableArray *allSqlUsers; + NSString *sql; + + fm = [GCSFolderManager defaultFolderManager]; + cm = [fm channelManager]; + folderLocation = [fm folderInfoLocation]; + fc = [cm acquireOpenChannelForURL: folderLocation]; + if (fc) + { + allSqlUsers = [NSMutableArray new]; + sql = [NSString stringWithFormat: @"SELECT DISTINCT c_path2 FROM %@", + [folderLocation gcsTableName]]; + [fc evaluateExpressionX: sql]; + attrs = [fc describeResults: NO]; + while ((infos = [fc fetchAttributes: attrs withZone: NULL])) + { + user = [infos objectForKey: @"c_path2"]; + if (user) + [allSqlUsers addObject: user]; + } + [cm releaseChannel: fc]; + + users = allSqlUsers; + max = [users count]; + [allSqlUsers autorelease]; + } + } + + pool = [[NSAutoreleasePool alloc] init]; + allUsers = [NSMutableArray new]; + for (count = 0; count < max; count++) + { + if (count > 0 && count%100 == 0) + { + DESTROY(pool); + pool = [[NSAutoreleasePool alloc] init]; + } + + user = [users objectAtIndex: count]; + infos = [lm contactInfosForUserWithUIDorEmail: user]; + if (infos) + [allUsers addObject: infos]; + else + { + // We haven't found the user based on the GCS table name + // Let's try to strip the domain part and search again. + // This can happen when using SOGoEnableDomainBasedUID (YES) + // but login in SOGo using a UID without domain (DomainLessLogin gets set) + NSRange r; + + r = [user rangeOfString: @"@"]; + + if (r.location != NSNotFound) + { + user = [user substringToIndex: r.location]; + infos = [lm contactInfosForUserWithUIDorEmail: user]; + if (infos) + [allUsers addObject: infos]; + else + NSLog (@"user '%@' unknown", user); + } + else + NSLog (@"user '%@' unknown", user); + } + } + [allUsers autorelease]; + + ASSIGN (usersToCheckup, allUsers); + DESTROY(pool); + + return ([usersToCheckup count] > 0); +} + +- (BOOL) parseArguments +{ + BOOL rc; + int max; + + max = [arguments count]; + if (max == 1) + { + rc = [self fetchUserIDs: arguments]; + } + else + { + [self usage]; + rc = NO; + } + + return rc; +} + +- (BOOL) checkupFolder: (NSString *) folder + withFM: (GCSFolderManager *) fm +{ + NSString *content, *c_name; + GCSFolder *gcsFolder; + NSArray *objects; + + unsigned int i, count; + BOOL rc, is_calendar; + + gcsFolder = [fm folderAtPath: folder]; + is_calendar = ([[gcsFolder folderTypeName] caseInsensitiveCompare: @"Appointment"] == NSOrderedSame); + objects = [gcsFolder fetchFields: [NSArray arrayWithObjects: @"c_name", @"c_content", nil] fetchSpecification: nil]; + count = [objects count]; + + for (i = 0; i < count; i++) + { + content = [[[objects objectAtIndex: i] objectForKey: @"c_content"] stringByTrimmingSpaces]; + c_name = [[objects objectAtIndex: i] objectForKey: @"c_name"]; + if (is_calendar) + { + // We check for + // BEGIN:VCALENDAR + // .. + // END:VCALENDAR + iCalCalendar *calendar; + + if ([content length] < 30 || + [[content substringToIndex: 15] caseInsensitiveCompare: @"BEGIN:VCALENDAR"] != NSOrderedSame || + [[content substringFromIndex: [content length]-13] caseInsensitiveCompare: @"END:VCALENDAR"] != NSOrderedSame) + { + NSLog(@"Corrupted calendar item (missing tags) in path %@ with c_name = %@", folder, c_name); + rc = NO; + } + else + { + calendar = [iCalCalendar parseSingleFromSource: content]; + if (!calendar) + { + NSLog(@"Corrupted calendar item (unparsable) in path %@ with c_name = %@", folder, c_name); + rc = NO; + } + } + } + else + { + NGVCard *card; + + card = [NGVCard parseSingleFromSource: content]; + + if (!card) + { + NSLog(@"Corrupted card item (unparsable) in path %@ with c_name = %@", folder, c_name); + rc = NO; + } + } + } + + return rc; +} + +- (BOOL) checkupUserFolders: (NSString *) uid +{ + GCSFolderManager *fm; + NSArray *folders; + int count, max; + NSString *basePath, *folder; + + fm = [GCSFolderManager defaultFolderManager]; + basePath = [NSString stringWithFormat: @"/Users/%@", uid]; + folders = [fm listSubFoldersAtPath: basePath recursive: YES]; + max = [folders count]; + for (count = 0; count < max; count++) + { + folder = [NSString stringWithFormat: @"%@/%@", basePath, [folders objectAtIndex: count]]; + //NSLog (@"folder %d: %@", count, folder); + [self checkupFolder: folder withFM: fm]; + } + + return YES; +} + +- (BOOL) checkupUser: (NSDictionary *) theUser +{ + NSString *gcsUID, *domain; + SOGoSystemDefaults *sd; + + sd = [SOGoSystemDefaults sharedSystemDefaults]; + + domain = [theUser objectForKey: @"c_domain"]; + gcsUID = [theUser objectForKey: @"c_uid"]; + + if ([sd enableDomainBasedUID] && [gcsUID rangeOfString: @"@"].location == NSNotFound) + gcsUID = [NSString stringWithFormat: @"%@@%@", gcsUID, domain]; + + return [self checkupUserFolders: gcsUID]; +} + +- (BOOL) proceed +{ + NSAutoreleasePool *pool; + int count, max; + BOOL rc; + + rc = YES; + + pool = [NSAutoreleasePool new]; + + max = [usersToCheckup count]; + for (count = 0; rc && count < max; count++) + { + rc = [self checkupUser: [usersToCheckup objectAtIndex: count]]; + if ((count % 10) == 0) + [pool emptyPool]; + } + + [pool release]; + + return rc; +} + +- (BOOL) run +{ + return ([self parseArguments] && [self proceed]); +} + +@end diff --git a/Tools/SOGoToolRemove.m b/Tools/SOGoToolRemove.m index 34ef10bad..43720267a 100644 --- a/Tools/SOGoToolRemove.m +++ b/Tools/SOGoToolRemove.m @@ -1,8 +1,6 @@ /* SOGoToolRemove.m - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2010-2017 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 diff --git a/Tools/SOGoToolRemoveDoubles.m b/Tools/SOGoToolRemoveDoubles.m index 820d6e2d5..5dcb28023 100644 --- a/Tools/SOGoToolRemoveDoubles.m +++ b/Tools/SOGoToolRemoveDoubles.m @@ -1,8 +1,6 @@ /* SOGoToolRemoveDoubles.m - this file is part of SOGo * - * Copyright (C) 2009 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2009-2017 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 diff --git a/Tools/SOGoToolRenameUser.m b/Tools/SOGoToolRenameUser.m index 07b471695..7d054794a 100644 --- a/Tools/SOGoToolRenameUser.m +++ b/Tools/SOGoToolRenameUser.m @@ -1,8 +1,6 @@ /* SOGoToolRenameUser.m - this file is part of SOGo * - * Copyright (C) 2011 Inverse inc - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2011-2017 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 diff --git a/Tools/sogo-ealarms-notify.m b/Tools/sogo-ealarms-notify.m index 47fe3a3b5..b9c5f2021 100644 --- a/Tools/sogo-ealarms-notify.m +++ b/Tools/sogo-ealarms-notify.m @@ -1,8 +1,6 @@ /* sogo-ealarms-notify.m - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2010-2017 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 diff --git a/Tools/sogo-slapd-sockd.m b/Tools/sogo-slapd-sockd.m index 2473ce84e..67b41f6bc 100644 --- a/Tools/sogo-slapd-sockd.m +++ b/Tools/sogo-slapd-sockd.m @@ -1,8 +1,6 @@ /* sogo-slapd-sockd.m - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2010-2017 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