diff --git a/ArqRestoreCommand.m b/ArqRestoreCommand.m index f295bde..9db45fa 100644 --- a/ArqRestoreCommand.m +++ b/ArqRestoreCommand.m @@ -41,6 +41,7 @@ #import "Restorer.h" #import "NSErrorCodes.h" #import "NSError_extra.h" +#import "UserAndComputer.h" @interface ArqRestoreCommand (internal) - (BOOL)printArqFolders:(NSError **)error; @@ -117,42 +118,55 @@ if (![self validateS3Keys:error]) { return NO; } - NSArray *s3BucketNames = [S3Service s3BucketNamesForAccessKeyID:accessKey]; - NSMutableArray *computerUUIDPaths = [NSMutableArray array]; - for (NSString *s3BucketName in s3BucketNames) { + for (NSString *s3BucketName in [S3Service s3BucketNamesForAccessKeyID:accessKey]) { + printf("S3 bucket: %s", [s3BucketName UTF8String]); NSString *computerUUIDPrefix = [NSString stringWithFormat:@"/%@/", s3BucketName]; NSError *myError = nil; NSArray *computerUUIDs = [s3 commonPrefixesForPathPrefix:computerUUIDPrefix delimiter:@"/" error:&myError]; - if (computerUUIDs == nil) { - if ([myError isErrorWithDomain:[S3Service errorDomain] code:ERROR_NOT_FOUND]) { - // Skip. - } else { - if (error != NULL) { - *error = myError; - } - return NO; + if (computerUUIDs == nil && ![myError isErrorWithDomain:[S3Service errorDomain] code:ERROR_NOT_FOUND]) { + if (error != NULL) { + *error = myError; } - } - for (NSString *computerUUID in computerUUIDs) { - [computerUUIDPaths addObject:[computerUUIDPrefix stringByAppendingPathComponent:computerUUID]]; - } - } - for (NSString *computerUUIDPath in computerUUIDPaths) { - NSString *computerBucketsPrefix = [computerUUIDPath stringByAppendingPathComponent:@"buckets"]; - NSArray *s3BucketUUIDPaths = [s3 pathsWithPrefix:computerBucketsPrefix error:error]; - if (s3BucketUUIDPaths == nil) { return NO; } - for (NSString *uuidPath in s3BucketUUIDPaths) { - NSData *data = [s3 dataAtPath:uuidPath error:error]; - if (data == nil) { + if ([computerUUIDs count] == 0) { + printf(" (no computers found)"); + } + printf("\n"); + for (NSString *computerUUID in computerUUIDs) { + NSString *computerInfoPath = [NSString stringWithFormat:@"/%@/%@/computerinfo", s3BucketName, computerUUID]; + NSError *uacError = nil; + NSData *uacData = [s3 dataAtPath:computerInfoPath error:&uacError]; + UserAndComputer *uac = nil; + if (uacData != nil) { + uac = [[[UserAndComputer alloc] initWithXMLData:uacData error:&uacError] autorelease]; + printf(" %s (%s)", [[uac computerName] UTF8String], [[uac userName] UTF8String]); + } else { + printf(" (unknown computer)"); + } + NSString *computerUUIDPath = [computerUUIDPrefix stringByAppendingPathComponent:computerUUID]; + NSString *computerBucketsPrefix = [computerUUIDPath stringByAppendingPathComponent:@"buckets"]; + NSArray *s3BucketUUIDPaths = [s3 pathsWithPrefix:computerBucketsPrefix error:error]; + if (s3BucketUUIDPaths == nil) { return NO; } - DictNode *plist = [DictNode dictNodeWithXMLData:data error:error]; - if (plist == nil) { - return NO; + if ([s3BucketUUIDPaths count] == 0) { + printf(" (no folders found)"); + } + printf("\n"); + for (NSString *uuidPath in s3BucketUUIDPaths) { + NSData *data = [s3 dataAtPath:uuidPath error:error]; + if (data == nil) { + return NO; + } + DictNode *plist = [DictNode dictNodeWithXMLData:data error:error]; + if (plist == nil) { + return NO; + } + printf(" %s\n", [[[plist stringNodeForKey:@"LocalPath"] stringValue] UTF8String]); + printf(" UUID: %s\n", [[uuidPath lastPathComponent] UTF8String]); + printf(" restore command: arq_restore %s\n", [uuidPath UTF8String]); } - printf("s3 path=%s\tlocal path=%s\n", [uuidPath UTF8String], [[[plist stringNodeForKey:@"LocalPath"] stringValue] UTF8String]); } } return YES; @@ -185,6 +199,20 @@ NSString *computerUUID = [path substringWithRange:computerUUIDRange]; NSString *bucketUUID = [path substringWithRange:bucketUUIDRange]; NSString *bucketName = [[plist stringNodeForKey:@"BucketName"] stringValue]; + + printf("restoring %s from ", [bucketName UTF8String]); + + NSError *uacError = nil; + NSData *uacData = [s3 dataAtPath:[NSString stringWithFormat:@"/%@/%@/computerinfo", s3BucketName, computerUUID] error:&uacError]; + UserAndComputer *uac = nil; + if (uacData != nil) { + uac = [[[UserAndComputer alloc] initWithXMLData:uacData error:&uacError] autorelease]; + printf("%s (%s)", [[uac computerName] UTF8String], [[uac userName] UTF8String]); + } else { + printf("(unknown computer)"); + } + printf(" to %s/%s\n", [[[NSFileManager defaultManager] currentDirectoryPath] UTF8String], [bucketName UTF8String]); + Restorer *restorer = [[[Restorer alloc] initWithS3Service:s3 s3BucketName:s3BucketName computerUUID:computerUUID bucketUUID:bucketUUID bucketName:bucketName encryptionKey:encryptionPassword] autorelease]; if (![restorer restore:error]) { return NO; diff --git a/UserAndComputer.h b/UserAndComputer.h new file mode 100644 index 0000000..b2d1bf2 --- /dev/null +++ b/UserAndComputer.h @@ -0,0 +1,20 @@ +// +// UserAndComputer.h +// Arq +// +// Created by Stefan Reitshamer on 7/9/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import + + +@interface UserAndComputer : NSObject { + NSString *userName; + NSString *computerName; +} +- (id)initWithXMLData:(NSData *)theXMLData error:(NSError **)error; +- (NSString *)userName; +- (NSString *)computerName; +- (NSData *)toXMLData; +@end diff --git a/UserAndComputer.m b/UserAndComputer.m new file mode 100644 index 0000000..12349a0 --- /dev/null +++ b/UserAndComputer.m @@ -0,0 +1,47 @@ +// +// UserAndComputer.m +// Arq +// +// Created by Stefan Reitshamer on 7/9/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import "UserAndComputer.h" +#import "DictNode.h" + +@implementation UserAndComputer +- (id)initWithXMLData:(NSData *)theXMLData error:(NSError **)error { + if (self = [super init]) { + DictNode *plist = [DictNode dictNodeWithXMLData:theXMLData error:error]; + if (plist == nil) { + [self release]; + return nil; + } + userName = [[[plist stringNodeForKey:@"userName"] stringValue] copy]; + computerName = [[[plist stringNodeForKey:@"computerName"] stringValue] copy]; + } + return self; +} +- (void)dealloc { + [userName release]; + [computerName release]; + [super dealloc]; +} +- (NSString *)userName { + return userName; +} +- (NSString *)computerName { + return computerName; +} +- (NSData *)toXMLData { + DictNode *plist = [[[DictNode alloc] init] autorelease]; + [plist putString:userName forKey:@"userName"]; + [plist putString:computerName forKey:@"computerName"]; + return [plist XMLData]; +} + +#pragma mark NSObject +- (NSString *)description { + return [NSString stringWithFormat:@"", userName, computerName]; +} +@end diff --git a/arq_restore.m b/arq_restore.m index 1e5c0c2..eec91e9 100644 --- a/arq_restore.m +++ b/arq_restore.m @@ -53,7 +53,7 @@ int main (int argc, const char **argv) { } else { NSError *myError = nil; if (![cmd execute:&myError]) { - NSLog(@"%@", [myError localizedDescription]); + fprintf(stderr, "restore error: %s\n", [[myError localizedDescription] UTF8String]); ret = 1; } } diff --git a/arq_restore.xcodeproj/project.pbxproj b/arq_restore.xcodeproj/project.pbxproj index f7c4efe..c99bdcf 100644 --- a/arq_restore.xcodeproj/project.pbxproj +++ b/arq_restore.xcodeproj/project.pbxproj @@ -205,6 +205,7 @@ F8F4D5A1121D9FC2002D09C1 /* AppKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D1E1121D7BC3002D09C1 /* AppKeychain.m */; }; F8F4D5A2121D9FC9002D09C1 /* FarkPath.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D1E6121D7DA2002D09C1 /* FarkPath.m */; }; F8F4D5A3121D9FCF002D09C1 /* PackIndexWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D1FD121D8409002D09C1 /* PackIndexWriter.m */; }; + F8F4D67E121DA542002D09C1 /* UserAndComputer.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D67D121DA542002D09C1 /* UserAndComputer.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -438,6 +439,8 @@ F8F4D1FD121D8409002D09C1 /* PackIndexWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PackIndexWriter.m; sourceTree = ""; }; F8F4D205121D8696002D09C1 /* ArqRepo_Verifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArqRepo_Verifier.h; sourceTree = ""; }; F8F4D206121D8696002D09C1 /* ArqRepo_Verifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArqRepo_Verifier.m; sourceTree = ""; }; + F8F4D67C121DA542002D09C1 /* UserAndComputer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserAndComputer.h; sourceTree = ""; }; + F8F4D67D121DA542002D09C1 /* UserAndComputer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UserAndComputer.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -477,8 +480,6 @@ 08FB7794FE84155DC02AAC07 /* arq_restore */ = { isa = PBXGroup; children = ( - F8F4D1FC121D8409002D09C1 /* PackIndexWriter.h */, - F8F4D1FD121D8409002D09C1 /* PackIndexWriter.m */, 08FB7795FE84155DC02AAC07 /* Source */, 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */, 1AB674ADFE9D54B511CA2CBB /* Products */, @@ -495,6 +496,10 @@ F805B8081160E7A1007EC01E /* io */, F805B7C91160E445007EC01E /* http */, F805B7651160DD60007EC01E /* s3 */, + F8F4D1FC121D8409002D09C1 /* PackIndexWriter.h */, + F8F4D1FD121D8409002D09C1 /* PackIndexWriter.m */, + F8F4D67C121DA542002D09C1 /* UserAndComputer.h */, + F8F4D67D121DA542002D09C1 /* UserAndComputer.m */, 32A70AAB03705E1F00C91783 /* arq_restore_Prefix.pch */, 08FB7796FE84155DC02AAC07 /* arq_restore.m */, F8F4D1C1121D79AC002D09C1 /* ArqFark.h */, @@ -915,6 +920,7 @@ F8F4D1E7121D7DA2002D09C1 /* FarkPath.m in Sources */, F8F4D1FE121D8409002D09C1 /* PackIndexWriter.m in Sources */, F8F4D207121D8696002D09C1 /* ArqRepo_Verifier.m in Sources */, + F8F4D67E121DA542002D09C1 /* UserAndComputer.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };