2009-08-14 14:23:35 +02:00
|
|
|
/* SOGoToolRestore.m - this file is part of SOGo
|
|
|
|
*
|
2015-07-28 12:37:33 +02:00
|
|
|
* Copyright (C) 2009-2014 Inverse inc.
|
2009-08-14 14:23:35 +02:00
|
|
|
*
|
|
|
|
* 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>
|
2012-11-27 21:53:39 +01:00
|
|
|
#import <Foundation/NSAutoreleasePool.h>
|
2009-08-14 14:23:35 +02:00
|
|
|
#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>
|
|
|
|
|
2010-01-22 18:32:06 +01:00
|
|
|
#import <Appointments/iCalEntityObject+SOGo.h>
|
2009-08-14 14:23:35 +02:00
|
|
|
#import <SOGo/NSArray+Utilities.h>
|
2010-02-18 23:36:00 +01:00
|
|
|
#import <SOGo/SOGoProductLoader.h>
|
2009-08-14 14:23:35 +02:00
|
|
|
#import <SOGo/SOGoUser.h>
|
2009-11-29 22:48:00 +01:00
|
|
|
#import <SOGo/SOGoUserDefaults.h>
|
2010-02-18 23:36:00 +01:00
|
|
|
#import <SOGo/SOGoUserManager.h>
|
2009-11-29 05:19:32 +01:00
|
|
|
#import <SOGo/SOGoUserProfile.h>
|
2009-11-29 22:48:00 +01:00
|
|
|
#import <SOGo/SOGoUserSettings.h>
|
2015-05-28 17:28:52 +02:00
|
|
|
#import <SOGo/SOGoSystemDefaults.h>
|
2009-08-14 14:23:35 +02:00
|
|
|
|
2014-08-01 15:40:45 +02:00
|
|
|
#import <NGCards/iCalCalendar.h>
|
|
|
|
#import <NGCards/NGVCard.h>
|
|
|
|
#import <NGCards/NGVList.h>
|
|
|
|
|
2015-05-27 16:41:22 +02:00
|
|
|
#import "SOGoToolRestore.h"
|
2009-08-14 14:23:35 +02:00
|
|
|
|
|
|
|
/* 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
|
2015-05-27 16:41:22 +02:00
|
|
|
and storing roles */
|
2009-08-14 16:57:05 +02:00
|
|
|
|
2009-08-14 14:23:35 +02:00
|
|
|
@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
|
|
|
|
{
|
2010-06-22 21:37:56 +02:00
|
|
|
fprintf (stderr, "restore [-l|-p|-f/-F folder/ALL|-p] directory user\n\n"
|
2010-06-22 21:33:12 +02:00
|
|
|
" directory the directory where backup files were initially stored\n"
|
|
|
|
" user the user of whom to restore the data\n"
|
|
|
|
" -l flag used to list folders to restore\n"
|
2010-06-22 21:37:56 +02:00
|
|
|
" -p flag used to restore only the user's preferences\n"
|
2010-06-22 21:33:12 +02:00
|
|
|
" -f/-F flag used to specify which folder to restore, ALL for everything\n\n"
|
2010-06-23 14:08:09 +02:00
|
|
|
"Examples: sogo-tool restore -l /tmp/foo bob\n"
|
|
|
|
" sogo-tool restore -f Contacts/personal /tmp/foo bob\n"
|
|
|
|
" sogo-tool restore -p /tmp/foo bob\n");
|
2009-08-14 14:23:35 +02:00
|
|
|
}
|
|
|
|
|
2014-08-01 15:40:45 +02:00
|
|
|
- (Class) parsingClassForContent: (NSString *) theContent
|
|
|
|
{
|
|
|
|
CardGroup *cardEntry;
|
|
|
|
NSString *firstTag;
|
|
|
|
Class objectClass;
|
|
|
|
|
|
|
|
objectClass = Nil;
|
|
|
|
|
|
|
|
cardEntry = [CardGroup parseSingleFromSource: theContent];
|
|
|
|
if (cardEntry)
|
|
|
|
{
|
|
|
|
firstTag = [[cardEntry tag] uppercaseString];
|
|
|
|
if ([firstTag isEqualToString: @"VCARD"])
|
|
|
|
objectClass = [NGVCard class];
|
|
|
|
else if ([firstTag isEqualToString: @"VLIST"])
|
|
|
|
objectClass = [NGVList class];
|
|
|
|
else
|
|
|
|
objectClass = [iCalCalendar class];
|
|
|
|
}
|
|
|
|
|
|
|
|
return objectClass;
|
|
|
|
}
|
|
|
|
|
2009-08-14 14:23:35 +02:00
|
|
|
- (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
|
|
|
|
{
|
2015-07-28 12:37:33 +02:00
|
|
|
BOOL rc;
|
2009-09-25 16:42:33 +02:00
|
|
|
SOGoUserManager *lm;
|
2009-08-14 14:23:35 +02:00
|
|
|
NSDictionary *infos;
|
2015-07-28 12:37:33 +02:00
|
|
|
SOGoSystemDefaults *sd;
|
2015-05-28 17:28:52 +02:00
|
|
|
NSString *uid = nil;
|
2009-08-14 14:23:35 +02:00
|
|
|
|
2009-09-25 16:42:33 +02:00
|
|
|
lm = [SOGoUserManager sharedUserManager];
|
2009-08-14 14:23:35 +02:00
|
|
|
infos = [lm contactInfosForUserWithUIDorEmail: identifier];
|
2015-05-28 17:28:52 +02:00
|
|
|
if (infos)
|
|
|
|
{
|
|
|
|
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
2015-07-28 12:37:33 +02:00
|
|
|
if ([sd enableDomainBasedUID])
|
2015-05-28 17:28:52 +02:00
|
|
|
uid = [NSString stringWithFormat: @"%@@%@",
|
2015-07-28 12:37:33 +02:00
|
|
|
[infos objectForKey: @"c_uid"],
|
|
|
|
[infos objectForKey: @"c_domain"]];
|
2015-05-28 17:28:52 +02:00
|
|
|
else
|
2015-07-28 12:37:33 +02:00
|
|
|
uid = [infos objectForKey: @"c_uid"];
|
2015-05-28 17:28:52 +02:00
|
|
|
}
|
|
|
|
ASSIGN (userID, uid);
|
2009-08-14 14:23:35 +02:00
|
|
|
if (userID)
|
|
|
|
rc = YES;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rc = NO;
|
|
|
|
NSLog (@"user '%@' not found", identifier);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-08-14 16:57:05 +02:00
|
|
|
- (int) parseModeArguments
|
2009-08-14 14:23:35 +02:00
|
|
|
{
|
|
|
|
NSString *mode;
|
2009-08-14 16:57:05 +02:00
|
|
|
int count, max;
|
2009-08-14 14:23:35 +02:00
|
|
|
|
2009-08-14 16:57:05 +02:00
|
|
|
max = [arguments count];
|
|
|
|
if (max > 0)
|
2009-08-14 14:23:35 +02:00
|
|
|
{
|
2009-08-14 16:57:05 +02:00
|
|
|
mode = [arguments objectAtIndex: 0];
|
|
|
|
count = 1;
|
|
|
|
if ([mode isEqualToString: @"-f"]
|
|
|
|
|| [mode isEqualToString: @"-F"])
|
2009-08-14 14:23:35 +02:00
|
|
|
{
|
2009-08-14 16:57:05 +02:00
|
|
|
if ([mode hasSuffix: @"f"])
|
|
|
|
restoreMode = SOGoToolRestoreFolderMode;
|
|
|
|
else
|
|
|
|
restoreMode = SOGoToolRestoreFolderDestructiveMode;
|
|
|
|
if (max > 1)
|
|
|
|
{
|
|
|
|
count++;
|
|
|
|
ASSIGN (restoreFolder, [arguments objectAtIndex: 1]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
count = 0;
|
|
|
|
NSLog (@"missing 'folder' parameter");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ([mode isEqualToString: @"-l"])
|
|
|
|
restoreMode = SOGoToolRestoreListFoldersMode;
|
|
|
|
else if ([mode isEqualToString: @"-p"])
|
|
|
|
restoreMode = SOGoToolRestorePreferencesMode;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
count = 0;
|
|
|
|
if ([mode hasPrefix: @"-"])
|
|
|
|
NSLog (@"specified mode is invalid");
|
|
|
|
else
|
|
|
|
NSLog (@"missing 'mode' parameter");
|
2009-08-14 14:23:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2009-08-14 16:57:05 +02:00
|
|
|
count = 0;
|
2009-08-14 14:23:35 +02:00
|
|
|
|
2009-08-14 16:57:05 +02:00
|
|
|
return count;
|
2009-08-14 14:23:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) parseArguments
|
|
|
|
{
|
|
|
|
BOOL rc;
|
|
|
|
NSString *identifier;
|
2009-08-14 16:57:05 +02:00
|
|
|
NSArray *newArguments;
|
|
|
|
int count, max;
|
2009-08-14 14:23:35 +02:00
|
|
|
|
2009-08-14 16:57:05 +02:00
|
|
|
count = [self parseModeArguments];
|
|
|
|
max = [arguments count] - count;
|
|
|
|
if (max == 2)
|
2009-08-14 14:23:35 +02:00
|
|
|
{
|
2009-08-14 16:57:05 +02:00
|
|
|
newArguments
|
|
|
|
= [arguments subarrayWithRange: NSMakeRange (count, max)];
|
|
|
|
ASSIGN (directory, [newArguments objectAtIndex: 0]);
|
|
|
|
identifier = [newArguments objectAtIndex: 1];
|
2009-08-14 14:23:35 +02:00
|
|
|
rc = ([self checkDirectory]
|
2009-08-14 16:57:05 +02:00
|
|
|
&& [self fetchUserID: identifier]);
|
2009-08-14 14:23:35 +02:00
|
|
|
}
|
|
|
|
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
|
|
|
|
{
|
|
|
|
NSDictionary *existingRecords, *currentRecord;
|
|
|
|
NSString *cName, *cContent;
|
2014-08-01 15:40:45 +02:00
|
|
|
NSAutoreleasePool *pool;
|
|
|
|
|
2012-10-22 16:09:13 +02:00
|
|
|
unsigned int version;
|
2014-08-01 15:40:45 +02:00
|
|
|
int count, max;
|
2012-04-24 13:47:32 +02:00
|
|
|
BOOL rc;
|
2009-08-14 14:23:35 +02:00
|
|
|
|
|
|
|
if (records)
|
|
|
|
{
|
2012-11-27 21:53:39 +01:00
|
|
|
existingRecords = [self fetchExistingRecordsFromFolder: gcsFolder];
|
|
|
|
pool = [[NSAutoreleasePool alloc] init];
|
2012-04-24 13:47:32 +02:00
|
|
|
version = 0;
|
2009-08-14 14:23:35 +02:00
|
|
|
rc = YES;
|
|
|
|
max = [records count];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
2012-11-27 21:53:39 +01:00
|
|
|
if (count > 0 && count%100 == 0)
|
|
|
|
{
|
|
|
|
DESTROY(pool);
|
|
|
|
pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
}
|
2009-08-14 14:23:35 +02:00
|
|
|
currentRecord = [records objectAtIndex: count];
|
|
|
|
cName = [currentRecord objectForKey: @"c_name"];
|
|
|
|
if (![existingRecords objectForKey: cName])
|
|
|
|
{
|
|
|
|
NSLog (@"restoring record '%@'", cName);
|
|
|
|
cContent = [currentRecord objectForKey: @"c_content"];
|
2014-07-30 20:51:00 +02:00
|
|
|
[gcsFolder writeContent: cContent
|
2014-08-01 15:40:45 +02:00
|
|
|
fromComponent: [[self parsingClassForContent: cContent] parseSingleFromSource: cContent]
|
|
|
|
container: nil
|
2014-07-30 20:51:00 +02:00
|
|
|
toName: cName
|
2012-10-22 16:09:13 +02:00
|
|
|
baseVersion: &version];
|
2009-08-14 14:23:35 +02:00
|
|
|
}
|
|
|
|
}
|
2012-11-27 21:53:39 +01:00
|
|
|
DESTROY(pool);
|
2009-08-14 14:23:35 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rc = NO;
|
|
|
|
NSLog (@"no records found (abort)");
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-08-14 16:57:05 +02:00
|
|
|
- (BOOL) createFolder: (NSString *) folder
|
|
|
|
withFM: (GCSFolderManager *) fm
|
|
|
|
{
|
|
|
|
NSArray *pathElements;
|
|
|
|
NSException *error;
|
2011-04-04 15:01:20 +02:00
|
|
|
NSString *folderType;
|
2009-08-14 16:57:05 +02:00
|
|
|
BOOL rc;
|
|
|
|
|
|
|
|
pathElements = [folder componentsSeparatedByString: @"/"];
|
|
|
|
if ([[pathElements objectAtIndex: 3] isEqualToString: @"Contacts"])
|
|
|
|
folderType = @"Contact";
|
|
|
|
else
|
|
|
|
folderType = @"Appointment";
|
|
|
|
|
|
|
|
error = [fm createFolderOfType: folderType
|
2011-04-04 15:01:20 +02:00
|
|
|
withName: [pathElements objectAtIndex: 4]
|
|
|
|
atPath: folder];
|
2009-08-14 16:57:05 +02:00
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
rc = NO;
|
|
|
|
NSLog (@"an error occured during folder creation: %@", error);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
rc = YES;
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-08-14 14:23:35 +02:00
|
|
|
- (BOOL) restoreFolder: (NSString *) folder
|
|
|
|
withContent: (NSDictionary *) content
|
2009-08-14 16:57:05 +02:00
|
|
|
destructive: (BOOL) isDestructive
|
2009-08-14 14:23:35 +02:00
|
|
|
{
|
|
|
|
GCSFolderManager *fm;
|
|
|
|
GCSFolder *gcsFolder;
|
2009-08-14 16:57:05 +02:00
|
|
|
NSException *error;
|
|
|
|
BOOL rc;
|
|
|
|
|
|
|
|
rc = YES;
|
2009-08-14 14:23:35 +02:00
|
|
|
|
|
|
|
fm = [GCSFolderManager defaultFolderManager];
|
|
|
|
gcsFolder = [fm folderAtPath: folder];
|
2009-08-14 16:57:05 +02:00
|
|
|
if (gcsFolder && isDestructive)
|
|
|
|
{
|
|
|
|
error = [fm deleteFolderAtPath: folder];
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
rc = NO;
|
|
|
|
NSLog (@"an error occured during folder deletion: %@", error);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
gcsFolder = nil;
|
|
|
|
}
|
|
|
|
if (rc)
|
|
|
|
{
|
|
|
|
if (!gcsFolder)
|
|
|
|
{
|
|
|
|
rc = [self createFolder: folder withFM: fm];
|
|
|
|
if (rc)
|
|
|
|
{
|
|
|
|
gcsFolder = [fm folderAtPath: folder];
|
|
|
|
if (!gcsFolder)
|
|
|
|
{
|
|
|
|
rc = NO;
|
|
|
|
NSLog (@"missing folder '%@' could not be recreated",
|
|
|
|
folder);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rc &= ([self restoreDisplayName: [content objectForKey: @"displayname"]
|
|
|
|
ofFolder: gcsFolder
|
|
|
|
withFM: fm]
|
|
|
|
&& [self restoreACL: [content objectForKey: @"acl"]
|
|
|
|
ofFolder: gcsFolder]
|
|
|
|
&& [self restoreRecords: [content objectForKey: @"records"]
|
|
|
|
ofFolder: gcsFolder]);
|
|
|
|
}
|
2009-08-14 14:23:35 +02:00
|
|
|
|
2009-08-14 16:57:05 +02:00
|
|
|
return rc;
|
2009-08-14 14:23:35 +02:00
|
|
|
}
|
|
|
|
|
2009-08-14 16:57:05 +02:00
|
|
|
- (BOOL) restoreUserFolderFromUserRecord: (NSDictionary *) userRecord
|
|
|
|
destructive: (BOOL) isDestructive
|
2009-08-14 14:23:35 +02:00
|
|
|
{
|
|
|
|
NSDictionary *tables, *content;
|
|
|
|
NSArray *restoreFolders;
|
2009-08-14 16:57:05 +02:00
|
|
|
NSString *currentFolder, *folderPath;
|
2009-08-14 14:23:35 +02:00
|
|
|
int count, max;
|
|
|
|
BOOL rc;
|
|
|
|
|
|
|
|
rc = YES;
|
|
|
|
|
|
|
|
tables = [userRecord objectForKey: @"tables"];
|
|
|
|
if (tables)
|
|
|
|
{
|
2009-08-14 16:57:05 +02:00
|
|
|
if ([restoreFolder isEqualToString: @"ALL"])
|
2009-08-14 14:23:35 +02:00
|
|
|
restoreFolders = [tables allKeys];
|
2009-08-14 16:57:05 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
folderPath = [NSString stringWithFormat: @"/Users/%@/%@",
|
|
|
|
userID, restoreFolder];
|
|
|
|
restoreFolders = [NSArray arrayWithObject: folderPath];
|
|
|
|
}
|
2009-08-14 14:23:35 +02:00
|
|
|
max = [restoreFolders count];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
currentFolder = [restoreFolders objectAtIndex: count];
|
|
|
|
content = [tables objectForKey: currentFolder];
|
|
|
|
if (content)
|
|
|
|
rc &= [self restoreFolder: currentFolder
|
2009-08-14 16:57:05 +02:00
|
|
|
withContent: content
|
|
|
|
destructive: isDestructive];
|
2009-08-14 14:23:35 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
rc = NO;
|
2012-05-03 16:07:55 +02:00
|
|
|
NSLog (@"no user table '%@' found", currentFolder);
|
2009-08-14 14:23:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rc = NO;
|
|
|
|
NSLog (@"no table information found in backup file");
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-08-14 16:57:05 +02:00
|
|
|
- (BOOL) listRestorableFolders: (NSDictionary *) userRecord
|
|
|
|
{
|
|
|
|
BOOL rc;
|
|
|
|
NSDictionary *tables, *currentFolder;
|
|
|
|
NSEnumerator *tableKeys;
|
|
|
|
NSString *key, *folderKey;
|
|
|
|
int folderPrefixLen;
|
|
|
|
|
|
|
|
tables = [userRecord objectForKey: @"tables"];
|
|
|
|
if (tables)
|
|
|
|
{
|
|
|
|
NSLog (@"Restorable folders:");
|
2014-08-26 15:00:13 +02:00
|
|
|
folderPrefixLen = 8 + [userID length]; // tables keys start with /Users/<userID>
|
|
|
|
tableKeys = [[[tables allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] objectEnumerator];
|
2009-08-14 16:57:05 +02:00
|
|
|
while ((key = [tableKeys nextObject]))
|
|
|
|
{
|
|
|
|
currentFolder = [tables objectForKey: key];
|
|
|
|
folderKey = [key substringFromIndex: folderPrefixLen];
|
2014-08-26 15:00:13 +02:00
|
|
|
NSLog (@" %@ (%@)",
|
|
|
|
folderKey, [currentFolder objectForKey: @"displayname"]);
|
2009-08-14 16:57:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rc = NO;
|
|
|
|
NSLog (@"no table information found in backup file");
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-08-14 14:23:35 +02:00
|
|
|
- (BOOL) restoreUserPreferencesFromUserRecord: (NSDictionary *) userRecord
|
|
|
|
{
|
|
|
|
SOGoUser *sogoUser;
|
2009-11-29 05:19:32 +01:00
|
|
|
SOGoUserProfile *up;
|
2009-08-14 14:23:35 +02:00
|
|
|
NSArray *preferences;
|
|
|
|
BOOL rc;
|
|
|
|
|
|
|
|
preferences = [userRecord objectForKey: @"preferences"];
|
|
|
|
if (preferences)
|
|
|
|
{
|
|
|
|
rc = YES;
|
|
|
|
sogoUser = [SOGoUser userWithLogin: userID roles: nil];
|
|
|
|
|
2009-11-29 05:19:32 +01:00
|
|
|
up = [[sogoUser userDefaults] source];
|
|
|
|
[up setValues: [preferences objectAtIndex: 0]];
|
|
|
|
[up synchronize];
|
2009-08-14 14:23:35 +02:00
|
|
|
|
2009-11-29 05:19:32 +01:00
|
|
|
up = [[sogoUser userSettings] source];
|
|
|
|
[up setValues: [preferences objectAtIndex: 1]];
|
|
|
|
[up synchronize];
|
2009-08-14 14:23:35 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rc = NO;
|
|
|
|
NSLog (@"no preferences found (abort)");
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) proceed
|
|
|
|
{
|
|
|
|
NSDictionary *userRecord;
|
|
|
|
NSString *importPath;
|
|
|
|
BOOL rc;
|
|
|
|
|
2015-07-28 12:37:33 +02:00
|
|
|
importPath = [directory stringByAppendingPathComponent: userID];
|
2009-08-14 14:23:35 +02:00
|
|
|
userRecord = [NSDictionary dictionaryWithContentsOfFile: importPath];
|
|
|
|
if (userRecord)
|
|
|
|
{
|
|
|
|
if (restoreMode == SOGoToolRestoreFolderMode)
|
2009-08-14 16:57:05 +02:00
|
|
|
rc = [self restoreUserFolderFromUserRecord: userRecord
|
|
|
|
destructive: NO];
|
|
|
|
else if (restoreMode == SOGoToolRestoreFolderDestructiveMode)
|
|
|
|
rc = [self restoreUserFolderFromUserRecord: userRecord
|
|
|
|
destructive: YES];
|
|
|
|
else if (restoreMode == SOGoToolRestoreListFoldersMode)
|
|
|
|
rc = [self listRestorableFolders: userRecord];
|
2009-08-14 14:23:35 +02:00
|
|
|
else
|
|
|
|
rc = [self restoreUserPreferencesFromUserRecord: userRecord];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rc = NO;
|
2015-07-28 12:37:33 +02:00
|
|
|
NSLog (@"user backup file could not be loaded");
|
2009-08-14 14:23:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) run
|
|
|
|
{
|
2010-02-18 23:36:00 +01:00
|
|
|
[[SOGoProductLoader productLoader]
|
|
|
|
loadProducts: [NSArray arrayWithObjects: @"Contacts.SOGo",
|
|
|
|
@"Appointments.SOGo",
|
|
|
|
nil]];
|
|
|
|
[iCalEntityObject initializeSOGoExtensions];
|
|
|
|
|
2009-08-14 14:23:35 +02:00
|
|
|
return ([self parseArguments] && [self proceed]);
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|