Monotone-Parent: 9755daf6ba583e833c7bab387fc5e777c6272bd1

Monotone-Revision: 98e35c999db27e9159edf91d2c17f8b5bc742330

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2009-08-14T12:23:35
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
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>
* 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

View File

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

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 */

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