2009-08-13 20:34:54 +02:00
|
|
|
/* SOGoToolBackup.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>
|
2009-12-25 23:52:52 +01:00
|
|
|
#import <Foundation/NSAutoreleasePool.h>
|
2009-08-13 20:34:54 +02:00
|
|
|
#import <Foundation/NSError.h>
|
|
|
|
#import <Foundation/NSFileManager.h>
|
|
|
|
#import <Foundation/NSString.h>
|
|
|
|
|
|
|
|
#import <GDLAccess/EOAdaptorChannel.h>
|
|
|
|
|
|
|
|
#import <GDLContentStore/GCSChannelManager.h>
|
|
|
|
#import <GDLContentStore/GCSFolderManager.h>
|
|
|
|
#import <GDLContentStore/GCSFolder.h>
|
|
|
|
#import <GDLContentStore/NSURL+GCS.h>
|
|
|
|
|
2009-09-25 16:42:33 +02:00
|
|
|
#import <SOGo/SOGoUserManager.h>
|
2009-08-13 20:34:54 +02:00
|
|
|
#import <SOGo/LDAPSource.h>
|
|
|
|
#import <SOGo/NSArray+Utilities.h>
|
2009-09-09 15:31:45 +02:00
|
|
|
#import <SOGo/NSDictionary+Utilities.h>
|
2009-08-13 20:34:54 +02:00
|
|
|
#import <SOGo/SOGoUser.h>
|
2010-01-14 23:57:44 +01:00
|
|
|
#import <SOGo/SOGoUserDefaults.h>
|
2009-11-29 05:19:32 +01:00
|
|
|
#import <SOGo/SOGoUserProfile.h>
|
2010-01-14 23:57:44 +01:00
|
|
|
#import <SOGo/SOGoUserSettings.h>
|
2009-08-13 20:34:54 +02:00
|
|
|
|
2010-04-15 23:15:10 +02:00
|
|
|
#import "SOGoTool.h"
|
2009-08-13 20:34:54 +02:00
|
|
|
|
2009-08-13 21:48:03 +02:00
|
|
|
/* TODO:
|
2009-08-14 14:22:33 +02:00
|
|
|
- handle database connectivity errors
|
2009-08-13 21:48:03 +02:00
|
|
|
- respond to "--help backup"
|
|
|
|
- allow more than one user specifier on the command-line
|
|
|
|
*/
|
|
|
|
|
2010-04-15 23:15:10 +02:00
|
|
|
@interface SOGoToolBackup : SOGoTool
|
|
|
|
{
|
|
|
|
NSString *directory;
|
|
|
|
NSArray *userIDs;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
2009-08-13 20:34:54 +02:00
|
|
|
@implementation SOGoToolBackup
|
|
|
|
|
|
|
|
+ (NSString *) command
|
|
|
|
{
|
|
|
|
return @"backup";
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSString *) description
|
|
|
|
{
|
|
|
|
return @"backup user folders";
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]))
|
|
|
|
{
|
|
|
|
directory = nil;
|
|
|
|
userIDs = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
[directory release];
|
|
|
|
[userIDs release];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) usage
|
|
|
|
{
|
2009-08-14 21:17:53 +02:00
|
|
|
fprintf (stderr, "backup directory ALL|user1 [user2] ...\n\n"
|
2009-08-13 20:34:54 +02:00
|
|
|
" 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 = [fm createDirectoryAtPath: directory
|
2010-04-20 23:30:17 +02:00
|
|
|
attributes: nil];
|
2009-08-13 20:34:54 +02:00
|
|
|
if (!rc)
|
2010-04-20 23:30:17 +02:00
|
|
|
NSLog (@"an error occured during directory creation");
|
2009-08-13 20:34:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-08-14 21:17:53 +02:00
|
|
|
- (BOOL) fetchUserIDs: (NSArray *) users
|
2009-08-13 20:34:54 +02:00
|
|
|
{
|
2009-12-25 23:52:52 +01:00
|
|
|
NSAutoreleasePool *pool;
|
2009-09-25 16:42:33 +02:00
|
|
|
SOGoUserManager *lm;
|
2009-08-13 20:34:54 +02:00
|
|
|
NSDictionary *infos;
|
2009-08-14 21:17:53 +02:00
|
|
|
NSString *user;
|
|
|
|
id allUsers;
|
|
|
|
int count, max;
|
2009-08-13 20:34:54 +02:00
|
|
|
|
2009-09-25 16:42:33 +02:00
|
|
|
lm = [SOGoUserManager sharedUserManager];
|
2009-12-25 23:52:52 +01:00
|
|
|
pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
|
2009-08-14 21:17:53 +02:00
|
|
|
max = [users count];
|
|
|
|
user = [users objectAtIndex: 0];
|
|
|
|
if (max == 1 && [user isEqualToString: @"ALL"])
|
2009-11-29 22:48:00 +01:00
|
|
|
allUsers = [lm fetchUsersMatching: @"." inDomain: nil];
|
2009-08-13 20:34:54 +02:00
|
|
|
else
|
|
|
|
{
|
2010-01-27 20:27:58 +01:00
|
|
|
allUsers = [NSMutableArray new];
|
2009-08-14 21:17:53 +02:00
|
|
|
for (count = 0; count < max; count++)
|
2009-08-13 20:34:54 +02:00
|
|
|
{
|
2009-12-25 23:52:52 +01:00
|
|
|
if (count > 0 && count%100 == 0)
|
|
|
|
{
|
|
|
|
DESTROY(pool);
|
|
|
|
pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
}
|
|
|
|
|
2009-08-14 21:17:53 +02:00
|
|
|
user = [users objectAtIndex: count];
|
|
|
|
infos = [lm contactInfosForUserWithUIDorEmail: user];
|
|
|
|
if (infos)
|
|
|
|
[allUsers addObject: infos];
|
|
|
|
else
|
|
|
|
NSLog (@"user '%@' unknown", user);
|
2009-08-13 20:34:54 +02:00
|
|
|
}
|
2010-01-27 20:27:58 +01:00
|
|
|
[allUsers autorelease];
|
2009-08-13 20:34:54 +02:00
|
|
|
}
|
|
|
|
|
2009-08-14 21:17:53 +02:00
|
|
|
ASSIGN (userIDs, [allUsers objectsForKey: @"c_uid" notFoundMarker: nil]);
|
2009-12-25 23:52:52 +01:00
|
|
|
DESTROY(pool);
|
2009-08-14 21:17:53 +02:00
|
|
|
|
|
|
|
return ([userIDs count] > 0);
|
2009-08-13 20:34:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) parseArguments
|
|
|
|
{
|
|
|
|
BOOL rc;
|
2009-08-14 21:17:53 +02:00
|
|
|
NSRange rest;
|
|
|
|
int max;
|
2009-08-13 20:34:54 +02:00
|
|
|
|
2009-08-14 21:17:53 +02:00
|
|
|
max = [arguments count];
|
|
|
|
if (max > 1)
|
2009-08-13 20:34:54 +02:00
|
|
|
{
|
|
|
|
ASSIGN (directory, [arguments objectAtIndex: 0]);
|
2009-08-14 21:17:53 +02:00
|
|
|
rest = NSMakeRange (1, max - 1);
|
|
|
|
rc = ([self checkDirectory]
|
|
|
|
&& [self fetchUserIDs: [arguments subarrayWithRange: rest]]);
|
2009-08-13 20:34:54 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[self usage];
|
|
|
|
rc = NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) fetchFolderDisplayName: (NSString *) folder
|
|
|
|
withFM: (GCSFolderManager *) fm
|
|
|
|
{
|
|
|
|
GCSChannelManager *cm;
|
|
|
|
EOAdaptorChannel *fc;
|
|
|
|
NSURL *folderLocation;
|
|
|
|
NSString *sql, *displayName;
|
|
|
|
NSArray *attrs;
|
|
|
|
NSDictionary *row;
|
|
|
|
|
|
|
|
displayName = nil;
|
|
|
|
|
|
|
|
cm = [fm channelManager];
|
|
|
|
folderLocation = [fm folderInfoLocation];
|
|
|
|
fc = [cm acquireOpenChannelForURL: folderLocation];
|
|
|
|
if (fc)
|
|
|
|
{
|
|
|
|
sql
|
|
|
|
= [NSString stringWithFormat: (@"SELECT c_foldername FROM %@"
|
|
|
|
@" WHERE c_path = '%@'"),
|
|
|
|
[folderLocation gcsTableName], folder];
|
|
|
|
[fc evaluateExpressionX: sql];
|
|
|
|
attrs = [fc describeResults: NO];
|
|
|
|
row = [fc fetchAttributes: attrs withZone: NULL];
|
|
|
|
displayName = [row objectForKey: @"c_foldername"];
|
|
|
|
[fc cancelFetch];
|
|
|
|
[cm releaseChannel: fc];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!displayName)
|
|
|
|
displayName = @"";
|
|
|
|
|
|
|
|
return displayName;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary *) fetchFolderACL: (GCSFolder *) gcsFolder
|
|
|
|
{
|
|
|
|
NSMutableDictionary *acl;
|
|
|
|
NSEnumerator *aclRecords;
|
|
|
|
NSDictionary *currentRecord;
|
|
|
|
NSMutableArray *userRoles;
|
|
|
|
NSString *user, *folderPath;
|
|
|
|
|
|
|
|
acl = [NSMutableDictionary dictionary];
|
|
|
|
|
|
|
|
folderPath = [gcsFolder path];
|
|
|
|
aclRecords = [[gcsFolder fetchAclMatchingQualifier: nil] objectEnumerator];
|
|
|
|
while ((currentRecord = [aclRecords nextObject]))
|
|
|
|
{
|
|
|
|
user = [currentRecord objectForKey: @"c_uid"];
|
|
|
|
if ([folderPath hasSuffix: [currentRecord objectForKey: @"c_object"]])
|
|
|
|
{
|
|
|
|
userRoles = [acl objectForKey: user];
|
|
|
|
if (!userRoles)
|
|
|
|
{
|
|
|
|
userRoles = [NSMutableArray array];
|
|
|
|
[acl setObject: userRoles forKey: user];
|
|
|
|
}
|
|
|
|
[userRoles addObject: [currentRecord objectForKey: @"c_role"]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return acl;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) extractFolder: (NSString *) folder
|
|
|
|
withFM: (GCSFolderManager *) fm
|
|
|
|
intoRecord: (NSMutableDictionary *) folderRecord
|
|
|
|
{
|
|
|
|
GCSFolder *gcsFolder;
|
|
|
|
NSArray *records;
|
|
|
|
static NSArray *fields = nil;
|
|
|
|
NSMutableDictionary *tableRecord;
|
|
|
|
|
|
|
|
if (!fields)
|
|
|
|
{
|
|
|
|
fields = [NSArray arrayWithObjects: @"c_name", @"c_content", nil];
|
|
|
|
[fields retain];
|
|
|
|
}
|
|
|
|
|
|
|
|
gcsFolder = [fm folderAtPath: folder];
|
|
|
|
|
|
|
|
tableRecord = [NSMutableDictionary dictionary];
|
|
|
|
// [tableRecord setObject:
|
|
|
|
// forKey: @"displayname"];
|
|
|
|
records = [gcsFolder fetchFields: fields
|
|
|
|
fetchSpecification: nil];
|
|
|
|
[tableRecord setObject: records forKey: @"records"];
|
|
|
|
[tableRecord setObject: [self fetchFolderDisplayName: folder
|
|
|
|
withFM: fm]
|
|
|
|
forKey: @"displayname"];
|
|
|
|
[tableRecord setObject: [self fetchFolderACL: gcsFolder]
|
|
|
|
forKey: @"acl"];
|
|
|
|
[folderRecord setObject: tableRecord forKey: folder];
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) extractUserFolders: (NSString *) uid
|
|
|
|
intoRecord: (NSMutableDictionary *) userRecord
|
|
|
|
{
|
|
|
|
GCSFolderManager *fm;
|
|
|
|
NSArray *folders;
|
|
|
|
NSMutableDictionary *tables;
|
|
|
|
int count, max;
|
|
|
|
NSString *basePath, *folder;
|
|
|
|
|
|
|
|
fm = [GCSFolderManager defaultFolderManager];
|
|
|
|
basePath = [NSString stringWithFormat: @"/Users/%@", uid];
|
|
|
|
folders = [fm listSubFoldersAtPath: basePath recursive: YES];
|
|
|
|
max = [folders count];
|
|
|
|
tables = [NSMutableDictionary dictionaryWithCapacity: max];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
folder = [NSString stringWithFormat: @"%@/%@",
|
|
|
|
basePath, [folders objectAtIndex: count]];
|
2009-08-14 21:17:53 +02:00
|
|
|
// NSLog (@"folder %d: %@", count, folder);
|
2009-08-13 20:34:54 +02:00
|
|
|
[self extractFolder: folder withFM: fm
|
|
|
|
intoRecord: tables];
|
|
|
|
}
|
|
|
|
[userRecord setObject: tables forKey: @"tables"];
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) extractUserLDIFRecord: (NSString *) uid
|
|
|
|
intoRecord: (NSMutableDictionary *) userRecord
|
|
|
|
{
|
|
|
|
NSEnumerator *ldapSources;
|
|
|
|
NSString *sourceID;
|
2009-11-29 05:19:32 +01:00
|
|
|
NSObject <SOGoSource> *currentSource;
|
2009-09-25 16:42:33 +02:00
|
|
|
SOGoUserManager *lm;
|
2009-08-13 20:34:54 +02:00
|
|
|
NSDictionary *userEntry;
|
|
|
|
BOOL done;
|
|
|
|
|
2009-09-25 16:42:33 +02:00
|
|
|
lm = [SOGoUserManager sharedUserManager];
|
2009-08-13 20:34:54 +02:00
|
|
|
|
|
|
|
done = NO;
|
2009-11-29 22:48:00 +01:00
|
|
|
ldapSources = [[lm authenticationSourceIDsInDomain: nil] objectEnumerator];
|
2009-08-13 20:34:54 +02:00
|
|
|
while (!done && (sourceID = [ldapSources nextObject]))
|
|
|
|
{
|
|
|
|
currentSource = [lm sourceWithID: sourceID];
|
|
|
|
userEntry = [currentSource lookupContactEntry: uid];
|
|
|
|
if (userEntry)
|
|
|
|
{
|
|
|
|
[userRecord setObject: [userEntry userRecordAsLDIFEntry]
|
|
|
|
forKey: @"ldif_record"];
|
|
|
|
done = YES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) extractUserPreferences: (NSString *) uid
|
|
|
|
intoRecord: (NSMutableDictionary *) userRecord
|
|
|
|
{
|
|
|
|
SOGoUser *sogoUser;
|
|
|
|
NSArray *preferences;
|
2010-01-14 23:57:44 +01:00
|
|
|
SOGoUserProfile *defaultsSource, *profileSource;
|
2009-08-13 20:34:54 +02:00
|
|
|
|
|
|
|
sogoUser = [SOGoUser userWithLogin: uid roles: nil];
|
2010-01-14 23:57:44 +01:00
|
|
|
defaultsSource = [[sogoUser userDefaults] source];
|
|
|
|
profileSource = [[sogoUser userSettings] source];
|
|
|
|
preferences = [NSArray arrayWithObjects:
|
|
|
|
[defaultsSource values], [profileSource values],
|
2009-08-14 14:22:33 +02:00
|
|
|
nil];
|
2009-08-13 20:34:54 +02:00
|
|
|
[userRecord setObject: preferences forKey: @"preferences"];
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) exportUser: (NSString *) uid
|
|
|
|
{
|
|
|
|
NSMutableDictionary *userRecord;
|
|
|
|
NSString *exportPath;
|
|
|
|
|
|
|
|
userRecord = [NSMutableDictionary dictionary];
|
|
|
|
exportPath = [directory stringByAppendingPathComponent: uid];
|
|
|
|
|
|
|
|
return ([self extractUserFolders: uid
|
|
|
|
intoRecord: userRecord]
|
|
|
|
&& [self extractUserLDIFRecord: uid
|
|
|
|
intoRecord: userRecord]
|
|
|
|
&& [self extractUserPreferences: uid
|
|
|
|
intoRecord: userRecord]
|
|
|
|
&& [userRecord writeToFile: exportPath
|
|
|
|
atomically: NO]);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) proceed
|
|
|
|
{
|
2010-04-28 19:57:02 +02:00
|
|
|
NSAutoreleasePool *pool;
|
2009-08-13 20:34:54 +02:00
|
|
|
int count, max;
|
|
|
|
BOOL rc;
|
|
|
|
|
|
|
|
rc = YES;
|
|
|
|
|
2010-04-28 19:57:02 +02:00
|
|
|
pool = [NSAutoreleasePool new];
|
|
|
|
|
2009-08-13 20:34:54 +02:00
|
|
|
max = [userIDs count];
|
|
|
|
for (count = 0; rc && count < max; count++)
|
2010-04-28 19:57:02 +02:00
|
|
|
{
|
|
|
|
rc = [self exportUser: [userIDs objectAtIndex: count]];
|
|
|
|
if ((count % 10) == 0)
|
|
|
|
[pool emptyPool];
|
|
|
|
}
|
|
|
|
|
|
|
|
[pool release];
|
2009-08-13 20:34:54 +02:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) run
|
|
|
|
{
|
|
|
|
return ([self parseArguments] && [self proceed]);
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|