Monotone-Parent: 9755daf6ba583e833c7bab387fc5e777c6272bd1

Monotone-Revision: 98e35c999db27e9159edf91d2c17f8b5bc742330

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2009-08-14T12:23:35
Monotone-Branch: ca.inverse.sogo
This commit is contained in:
Wolfgang Sourdeau 2009-08-14 12:23:35 +00:00
parent 47e6a827ee
commit 70898b25f1
4 changed files with 506 additions and 0 deletions

View file

@ -1,5 +1,9 @@
2009-08-14 Wolfgang Sourdeau <wsourdeau@inverse.ca> 2009-08-14 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Tools/SOGoToolRestore.m: new "sogo-tool" utility that restores
the specified user data previously created by the "backup"
command.
* Tools/SOGoToolBackup.m (-extractUserPreferences:intoRecord:): we * Tools/SOGoToolBackup.m (-extractUserPreferences:intoRecord:): we
must extract the values of the user defaults in order to put them must extract the values of the user defaults in order to put them
in the record dictionary. Putting the defaults themselves will in the record dictionary. Putting the defaults themselves will

View file

@ -11,6 +11,7 @@ $(SOGO_TOOL)_OBJC_FILES += \
\ \
SOGoTool.m \ SOGoTool.m \
SOGoToolBackup.m \ SOGoToolBackup.m \
SOGoToolRestore.m \
SOGoToolCheckDoubles.m \ SOGoToolCheckDoubles.m \
SOGoToolRemoveDoubles.m \ SOGoToolRemoveDoubles.m \
\ \

44
Tools/SOGoToolRestore.h Normal file
View file

@ -0,0 +1,44 @@
/* SOGoToolRestore.h - this file is part of SOGo
*
* Copyright (C) 2009 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* 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 SOGOTOOLRESTORE_H
#define SOGOTOOLRESTORE_H
#import "SOGoTool.h"
typedef enum SOGoToolRestoreMode {
SOGoToolRestoreFolderMode,
SOGoToolRestorePreferencesMode
} SOGoToolRestoreMode;
@interface SOGoToolRestore : SOGoTool
{
NSString *directory;
NSString *userID;
NSString *restoreFolder;
BOOL destructive; /* destructive mode not handled */
SOGoToolRestoreMode restoreMode;
}
@end
#endif /* SOGOTOOLRESTORE_H */

457
Tools/SOGoToolRestore.m Normal file
View file

@ -0,0 +1,457 @@
/* SOGoToolRestore.m - this file is part of SOGo
*
* Copyright (C) 2009 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* 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 <Foundation/NSArray.h>
#import <Foundation/NSError.h>
#import <Foundation/NSFileManager.h>
#import <Foundation/NSString.h>
#import <GDLAccess/EOAdaptorChannel.h>
#import <GDLAccess/EOAdaptorContext.h>
#import <GDLContentStore/GCSChannelManager.h>
#import <GDLContentStore/GCSFolderManager.h>
#import <GDLContentStore/GCSFolder.h>
#import <GDLContentStore/NSURL+GCS.h>
#import <SOGo/LDAPUserManager.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import "SOGoToolRestore.h"
/* TODO:
- respond to "--help restore"
- handle database connectivity errors
- handle the case where the restored folder has been deleted
- write methods in GDLContentStore to get/update displayname
and storing roles */
@implementation SOGoToolRestore
+ (NSString *) command
{
return @"restore";
}
+ (NSString *) description
{
return @"restore user folders";
}
- (id) init
{
if ((self = [super init]))
{
directory = nil;
userID = nil;
restoreFolder = nil;
destructive = NO;
}
return self;
}
- (void) dealloc
{
[directory release];
[userID release];
[restoreFolder release];
[super dealloc];
}
- (void) usage
{
fprintf (stderr, "restore directory user [-f folder|-p]\n\n"
" folder the folder where backup files will be stored\n"
" user the user of whom to save the data\n");
}
- (BOOL) checkDirectory
{
NSFileManager *fm;
BOOL exists, isDir, rc;
fm = [NSFileManager defaultManager];
exists = [fm fileExistsAtPath: directory isDirectory: &isDir];
if (exists)
{
if (isDir)
rc = YES;
else
{
rc = NO;
NSLog (@"specified directory is a regular file");
}
}
else
{
rc = NO;
NSLog (@"specified directory does not exist");
}
return rc;
}
- (BOOL) fetchUserID: (NSString *) identifier
{
BOOL rc;
LDAPUserManager *lm;
NSDictionary *infos;
lm = [LDAPUserManager sharedUserManager];
infos = [lm contactInfosForUserWithUIDorEmail: identifier];
ASSIGN (userID, [infos objectForKey: @"c_uid"]);
if (userID)
rc = YES;
else
{
rc = NO;
NSLog (@"user '%@' not found", identifier);
}
return rc;
}
- (BOOL) parseModeArguments: (NSArray *) modeArguments
{
NSString *mode;
BOOL rc;
rc = NO;
mode = [modeArguments objectAtIndex: 0];
if ([mode isEqualToString: @"-f"])
{
rc = YES;
restoreMode = SOGoToolRestoreFolderMode;
if ([modeArguments count] == 2)
{
restoreFolder = [NSString stringWithFormat: @"/Users/%@/%@",
userID,
[modeArguments objectAtIndex: 1]];
[restoreFolder retain];
}
}
else if ([mode isEqualToString: @"-p"])
{
rc = YES;
restoreMode = SOGoToolRestorePreferencesMode;
}
else
[self usage];
return rc;
}
- (BOOL) parseArguments
{
BOOL rc;
NSString *identifier;
NSArray *modeArguments;
int max;
max = [arguments count];
if ([arguments count] > 2)
{
ASSIGN (directory, [arguments objectAtIndex: 0]);
identifier = [arguments objectAtIndex: 1];
modeArguments
= [arguments subarrayWithRange: NSMakeRange (2, max - 2)];
rc = ([self checkDirectory]
&& [self fetchUserID: identifier]
&& [self parseModeArguments: modeArguments]);
}
else
{
[self usage];
rc = NO;
}
return rc;
}
- (BOOL) restoreDisplayName: (NSString *) newDisplayName
ofFolder: (GCSFolder *) gcsFolder
withFM: (GCSFolderManager *) fm
{
BOOL rc;
GCSChannelManager *cm;
EOAdaptorChannel *fc;
NSURL *folderLocation;
NSString *sql;
if (newDisplayName)
{
rc = YES;
cm = [fm channelManager];
folderLocation = [fm folderInfoLocation];
fc = [cm acquireOpenChannelForURL: folderLocation];
if (fc)
{
sql
= [NSString stringWithFormat: (@"UPDATE %@"
@" SET c_foldername = '%@'"
@" WHERE c_path = '%@'"),
[folderLocation gcsTableName],
[newDisplayName stringByReplacingString: @"'"
withString: @"''"],
[gcsFolder path]];
[fc evaluateExpressionX: sql];
[cm releaseChannel: fc];
}
}
else
{
rc = NO;
NSLog (@"no display name found (abort)");
}
return rc;
}
- (BOOL) restoreACL: (NSDictionary *) acl
ofFolder: (GCSFolder *) gcsFolder
{
EOAdaptorChannel *channel;
NSEnumerator *users, *userRoles;
NSString *SQL, *folderPath, *aclTableName, *currentUser, *currentRole;
BOOL rc;
if (acl)
{
aclTableName = [gcsFolder aclTableName];
folderPath = [[gcsFolder path] substringFromIndex: 6];
[gcsFolder deleteAclWithSpecification: nil];
channel = [gcsFolder acquireAclChannel];
[[channel adaptorContext] beginTransaction];
users = [[acl allKeys] objectEnumerator];
while ((currentUser = [users nextObject]))
{
userRoles = [[acl objectForKey: currentUser] objectEnumerator];
while ((currentRole = [userRoles nextObject]))
{
SQL = [NSString stringWithFormat: @"INSERT INTO %@"
@" (c_object, c_uid, c_role)"
@" VALUES ('%@', '%@', '%@')",
aclTableName,
folderPath, currentUser, currentRole];
[channel evaluateExpressionX: SQL];
}
}
[[channel adaptorContext] commitTransaction];
[gcsFolder releaseChannel: channel];
rc = YES;
}
else
{
rc = NO;
NSLog (@"no acl found (abort)");
}
return rc;
}
- (NSDictionary *) fetchExistingRecordsFromFolder: (GCSFolder *) gcsFolder
{
NSArray *records;
int count, max;
NSDictionary *row;
NSMutableDictionary *existingRecords;
records = [gcsFolder fetchFields: [NSArray arrayWithObject: @"c_name"]
fetchSpecification: nil];
max = [records count];
existingRecords = [NSMutableDictionary dictionaryWithCapacity: max];
for (count = 0; count < max; count++)
{
row = [records objectAtIndex: count];
[existingRecords setObject: @""
forKey: [row objectForKey: @"c_name"]];
}
return existingRecords;
}
- (BOOL) restoreRecords: (NSArray *) records
ofFolder: (GCSFolder *) gcsFolder
{
BOOL rc;
NSDictionary *existingRecords, *currentRecord;
NSString *cName, *cContent;
NSException *ex;
int count, max;
if (records)
{
rc = YES;
existingRecords = [self fetchExistingRecordsFromFolder: gcsFolder];
max = [records count];
for (count = 0; count < max; count++)
{
currentRecord = [records objectAtIndex: count];
cName = [currentRecord objectForKey: @"c_name"];
if (![existingRecords objectForKey: cName])
{
NSLog (@"restoring record '%@'", cName);
cContent = [currentRecord objectForKey: @"c_content"];
ex = [gcsFolder writeContent: cContent toName: cName
baseVersion: 0];
}
}
}
else
{
rc = NO;
NSLog (@"no records found (abort)");
}
return rc;
}
- (BOOL) restoreFolder: (NSString *) folder
withContent: (NSDictionary *) content
{
GCSFolderManager *fm;
GCSFolder *gcsFolder;
fm = [GCSFolderManager defaultFolderManager];
gcsFolder = [fm folderAtPath: folder];
return ([self restoreDisplayName: [content objectForKey: @"displayname"]
ofFolder: gcsFolder
withFM: fm]
&& [self restoreACL: [content objectForKey: @"acl"]
ofFolder: gcsFolder]
&& [self restoreRecords: [content objectForKey: @"records"]
ofFolder: gcsFolder]);
}
- (BOOL)
restoreSpecifiedUserFolderFromUserRecord: (NSDictionary *) userRecord
{
NSDictionary *tables, *content;
NSArray *restoreFolders;
NSString *currentFolder;
int count, max;
BOOL rc;
rc = YES;
tables = [userRecord objectForKey: @"tables"];
if (tables)
{
if (restoreFolder)
restoreFolders = [NSArray arrayWithObject: restoreFolder];
else
restoreFolders = [tables allKeys];
max = [restoreFolders count];
for (count = 0; count < max; count++)
{
currentFolder = [restoreFolders objectAtIndex: count];
content = [tables objectForKey: currentFolder];
if (content)
rc &= [self restoreFolder: currentFolder
withContent: content];
else
{
rc = NO;
NSLog (@"no user table with that name");
}
}
}
else
{
rc = NO;
NSLog (@"no table information found in backup file");
}
return rc;
}
- (BOOL) restoreUserPreferencesFromUserRecord: (NSDictionary *) userRecord
{
SOGoUser *sogoUser;
NSUserDefaults *storedPreferences;
NSArray *preferences;
BOOL rc;
preferences = [userRecord objectForKey: @"preferences"];
if (preferences)
{
rc = YES;
sogoUser = [SOGoUser userWithLogin: userID roles: nil];
storedPreferences = [sogoUser userDefaults];
[storedPreferences setValues: [preferences objectAtIndex: 0]];
[storedPreferences synchronize];
storedPreferences = [sogoUser userSettings];
[storedPreferences setValues: [preferences objectAtIndex: 1]];
[storedPreferences synchronize];
}
else
{
rc = NO;
NSLog (@"no preferences found (abort)");
}
return rc;
}
- (BOOL) proceed
{
NSDictionary *userRecord;
NSString *importPath;
BOOL rc;
importPath = [directory stringByAppendingPathComponent: userID];
userRecord = [NSDictionary dictionaryWithContentsOfFile: importPath];
if (userRecord)
{
if (restoreMode == SOGoToolRestoreFolderMode)
rc = [self restoreSpecifiedUserFolderFromUserRecord: userRecord];
else
rc = [self restoreUserPreferencesFromUserRecord: userRecord];
}
else
{
rc = NO;
NSLog (@"user backup file could not be loaded");
}
return rc;
}
- (BOOL) run
{
return ([self parseArguments] && [self proceed]);
}
@end