Monotone-Parent: 9755daf6ba583e833c7bab387fc5e777c6272bd1
Monotone-Revision: 98e35c999db27e9159edf91d2c17f8b5bc742330 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2009-08-14T12:23:35 Monotone-Branch: ca.inverse.sogomaint-2.0.2
parent
47e6a827ee
commit
70898b25f1
|
@ -1,5 +1,9 @@
|
|||
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
|
||||
must extract the values of the user defaults in order to put them
|
||||
in the record dictionary. Putting the defaults themselves will
|
||||
|
|
|
@ -11,6 +11,7 @@ $(SOGO_TOOL)_OBJC_FILES += \
|
|||
\
|
||||
SOGoTool.m \
|
||||
SOGoToolBackup.m \
|
||||
SOGoToolRestore.m \
|
||||
SOGoToolCheckDoubles.m \
|
||||
SOGoToolRemoveDoubles.m \
|
||||
\
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
Loading…
Reference in New Issue