initial commit

This commit is contained in:
Stefan Reitshamer 2010-03-31 08:57:26 -04:00
commit 4a346a274c
187 changed files with 16525 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
build

43
ArqFolder.h Normal file
View file

@ -0,0 +1,43 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "DictNode.h"
@interface ArqFolder : NSObject {
NSString *s3Path;
NSString *localPath;
}
- (id)initWithS3Path:(NSString *)theS3Path plist:(DictNode *)plist;
- (NSString *)s3Path;
- (NSString *)localPath;
@end

55
ArqFolder.m Normal file
View file

@ -0,0 +1,55 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "ArqFolder.h"
#import "DictNode.h"
@implementation ArqFolder
- (id)initWithS3Path:(NSString *)theS3Path plist:(DictNode *)plist {
if (self = [super init]) {
s3Path = [theS3Path copy];
localPath = [[[plist stringNodeForKey:@"LocalPath"] stringValue] copy];
}
return self;
}
- (void)dealloc {
[s3Path release];
[localPath release];
[super dealloc];
}
- (NSString *)s3Path {
return s3Path;
}
- (NSString *)localPath {
return localPath;
}
@end

44
ArqRestoreCommand.h Normal file
View file

@ -0,0 +1,44 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
@class S3Service;
@interface ArqRestoreCommand : NSObject {
NSString *accessKey;
NSString *secretKey;
NSString *encryptionPassword;
S3Service *s3;
}
- (BOOL)printArqFolders:(NSError **)error;
- (BOOL)restorePath:(NSString *)path error:(NSError **)error;
@end

191
ArqRestoreCommand.m Normal file
View file

@ -0,0 +1,191 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "ArqRestoreCommand.h"
#import "SetNSError.h"
#import "S3AuthorizationProvider.h"
#import "S3Service.h"
#import "RegexKitLite.h"
#import "DictNode.h"
#import "ArqFolder.h"
#import "HTTP.h"
#import "Restorer.h"
@interface ArqRestoreCommand (internal)
- (BOOL)validateS3Keys:(NSError **)error;
- (NSArray *)s3BucketNames:(NSError **)error;
- (NSString *)s3BucketNameForRegion:(int)s3BucketRegion;
@end
@implementation ArqRestoreCommand
- (id)init {
if (self = [super init]) {
char *theAccessKey = getenv("ARQ_ACCESS_KEY");
if (theAccessKey != NULL) {
accessKey = [[NSString alloc] initWithUTF8String:theAccessKey];
}
char *theSecretKey = getenv("ARQ_SECRET_KEY");
if (theSecretKey != NULL) {
secretKey = [[NSString alloc] initWithUTF8String:theSecretKey];
}
char *theEncryptionPassword = getenv("ARQ_ENCRYPTION_PASSWORD");
if (theEncryptionPassword != NULL) {
encryptionPassword = [[NSString alloc] initWithUTF8String:theEncryptionPassword];
}
if (accessKey != nil && secretKey != nil) {
S3AuthorizationProvider *sap = [[S3AuthorizationProvider alloc] initWithAccessKey:accessKey secretKey:secretKey];
s3 = [[S3Service alloc] initWithS3AuthorizationProvider:sap useSSL:NO retryOnNetworkError:YES];
[sap release];
}
}
return self;
}
- (void)dealloc {
[accessKey release];
[secretKey release];
[encryptionPassword release];
[s3 release];
[super dealloc];
}
- (BOOL)printArqFolders:(NSError **)error {
if (![self validateS3Keys:error]) {
return NO;
}
NSArray *s3BucketNames = [self s3BucketNames:error];
if (s3BucketNames == nil) {
return NO;
}
NSMutableArray *computerUUIDPaths = [NSMutableArray array];
for (NSString *s3BucketName in s3BucketNames) {
NSString *computerUUIDPrefix = [NSString stringWithFormat:@"/%@/", s3BucketName];
NSError *myError = nil;
NSArray *computerUUIDs = [s3 commonPrefixesForPathPrefix:computerUUIDPrefix delimiter:@"/" error:&myError];
if (computerUUIDs == nil) {
if ([[myError domain] isEqualToString:[S3Service serverErrorDomain]] && [myError code] == HTTP_NOT_FOUND) {
// Skip.
} else {
if (error != NULL) {
*error = myError;
}
return NO;
}
}
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) {
return NO;
}
DictNode *plist = [DictNode dictNodeWithXMLData:data error:error];
if (plist == nil) {
return NO;
}
printf("s3 path=%s\tlocal path=%s\n", [uuidPath UTF8String], [[[plist stringNodeForKey:@"LocalPath"] stringValue] UTF8String]);
}
}
return YES;
}
- (BOOL)restorePath:(NSString *)path error:(NSError **)error {
if (![self validateS3Keys:error]) {
return NO;
}
if (encryptionPassword == nil) {
SETNSERROR(@"ArqErrorDomain", -1, @"missing ARQ_ENCRYPTION_PASSWORD environment variable");
return NO;
}
NSString *pattern = @"^/([^/]+)/([^/]+)/buckets/([^/]+)";
NSRange s3BucketNameRange = [path rangeOfRegex:pattern capture:1];
NSRange computerUUIDRange = [path rangeOfRegex:pattern capture:2];
NSRange bucketUUIDRange = [path rangeOfRegex:pattern capture:3];
if (s3BucketNameRange.location == NSNotFound || computerUUIDRange.location == NSNotFound || bucketUUIDRange.location == NSNotFound) {
SETNSERROR(@"ArqErrorDomain", -1, @"invalid S3 path");
return NO;
}
NSData *data = [s3 dataAtPath:path error:error];
if (data == nil) {
return NO;
}
DictNode *plist = [DictNode dictNodeWithXMLData:data error:error];
if (plist == nil) {
return NO;
}
NSString *s3BucketName = [path substringWithRange:s3BucketNameRange];
NSString *computerUUID = [path substringWithRange:computerUUIDRange];
NSString *bucketUUID = [path substringWithRange:bucketUUIDRange];
NSString *bucketName = [[plist stringNodeForKey:@"BucketName"] stringValue];
Restorer *restorer = [[[Restorer alloc] initWithS3Service:s3 s3BucketName:s3BucketName computerUUID:computerUUID bucketUUID:bucketUUID bucketName:bucketName encryptionKey:encryptionPassword] autorelease];
if (![restorer restore:error]) {
return NO;
}
printf("restored files are in %s\n", [bucketName fileSystemRepresentation]);
return YES;
}
@end
@implementation ArqRestoreCommand (internal)
- (BOOL)validateS3Keys:(NSError **)error {
if (accessKey == nil) {
SETNSERROR(@"ArqErrorDomain", -1, @"missing ARQ_ACCESS_KEY environment variable");
return NO;
}
if (secretKey == nil) {
SETNSERROR(@"ArqErrorDomain", -1, @"missing ARQ_SECRET_KEY environment variable");
return NO;
}
return YES;
}
- (NSArray *)s3BucketNames:(NSError **)error {
return [NSArray arrayWithObjects:
[self s3BucketNameForRegion:BUCKET_REGION_US_STANDARD],
[self s3BucketNameForRegion:BUCKET_REGION_US_WEST],
[self s3BucketNameForRegion:BUCKET_REGION_EU],
nil];
}
- (NSString *)s3BucketNameForRegion:(int)s3BucketRegion {
NSString *regionSuffix = @"";
if (s3BucketRegion == BUCKET_REGION_US_WEST) {
regionSuffix = @".us-west-1";
} else if (s3BucketRegion == BUCKET_REGION_EU) {
regionSuffix = @".eu";
}
return [NSString stringWithFormat:@"%@.com.haystacksoftware.arq%@", [accessKey lowercaseString], regionSuffix];
}
@end

40
ArqUserLibrary.h Normal file
View file

@ -0,0 +1,40 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
@interface ArqUserLibrary : NSObject {
}
+ (NSString *)arqCachesPath;
@end

40
ArqUserLibrary.m Normal file
View file

@ -0,0 +1,40 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "ArqUserLibrary.h"
@implementation ArqUserLibrary
+ (NSString *)arqCachesPath {
return [NSHomeDirectory() stringByAppendingPathComponent:@"/Library/Arq/Caches.noindex"];
}
@end

62
Commit.h Normal file
View file

@ -0,0 +1,62 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "Blob.h"
#import "BufferedInputStream.h"
#define CURRENT_COMMIT_VERSION 3
@interface Commit : NSObject {
int commitVersion;
NSString *_author;
NSString *_comment;
NSMutableSet *_parentCommitSHA1s;
NSString *_treeSHA1;
NSString *_location;
NSString *_computer;
NSString *_mergeCommonAncestorCommitSHA1;
NSDate *_creationDate;
NSArray *_commitFailedFiles;
}
- (id)initWithBufferedInputStream:(id <BufferedInputStream>)is error:(NSError **)error;
@property(readonly,copy) NSString *author;
@property(readonly,copy) NSString *comment;
@property(readonly,copy) NSString *treeSHA1;
@property(readonly,retain) NSSet *parentCommitSHA1s;
@property(readonly,copy) NSString *location;
@property(readonly,copy) NSString *computer;
@property(readonly,copy) NSString *mergeCommonAncestorCommitSHA1;
@property(readonly,retain) NSDate *creationDate;
@property(readonly,retain) NSArray *commitFailedFiles;
@end

183
Commit.m Normal file
View file

@ -0,0 +1,183 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "IntegerIO.h"
#import "DateIO.h"
#import "StringIO.h"
#import "Commit.h"
#import "Blob.h"
#import "DataInputStream.h"
#import "RegexKitLite.h"
#import "SetNSError.h"
#import "NSErrorCodes.h"
#import "CommitFailedFile.h"
#define HEADER_LENGTH (10)
@interface Commit (internal)
- (BOOL)readHeader:(id <BufferedInputStream>)is error:(NSError **)error;
@end
@implementation Commit
@synthesize author = _author,
comment = _comment,
treeSHA1 = _treeSHA1,
parentCommitSHA1s = _parentCommitSHA1s,
location = _location,
computer = _computer,
mergeCommonAncestorCommitSHA1 = _mergeCommonAncestorCommitSHA1,
creationDate = _creationDate,
commitFailedFiles = _commitFailedFiles;
- (id)initWithBufferedInputStream:(id <BufferedInputStream>)is error:(NSError **)error {
if (self = [super init]) {
_parentCommitSHA1s = [[NSMutableSet alloc] init];
if (![self readHeader:is error:error]) {
[self release];
return nil;
}
BOOL ret = NO;
do {
if (![StringIO read:&_author from:is error:error]) {
break;
}
[_author retain];
if (![StringIO read:&_comment from:is error:error]) {
break;
}
[_comment retain];
uint64_t parentCommitKeyCount = 0;
if (![IntegerIO readUInt64:&parentCommitKeyCount from:is error:error]) {
break;
}
for (uint64_t i = 0; i < parentCommitKeyCount; i++) {
NSString *key;
if (![StringIO read:&key from:is error:error]) {
break;
}
[_parentCommitSHA1s addObject:key];
}
if (![StringIO read:&_treeSHA1 from:is error:error]) {
break;
}
[_treeSHA1 retain];
if (![StringIO read:&_location from:is error:error]) {
break;
}
[_location retain];
NSRange computerRange = [_location rangeOfRegex:@"^file://([^/]+)/" capture:1];
if (computerRange.location != NSNotFound) {
_computer = [[_location substringWithRange:computerRange] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
} else {
_computer = @"";
}
[_computer retain];
if (![StringIO read:&_mergeCommonAncestorCommitSHA1 from:is error:error]) {
break;
}
[_mergeCommonAncestorCommitSHA1 retain];
if (![DateIO read:&_creationDate from:is error:error]) {
break;
}
[_creationDate retain];
if (commitVersion >= 3) {
uint64_t commitFailedFileCount = 0;
if (![IntegerIO readUInt64:&commitFailedFileCount from:is error:error]) {
break;
}
NSMutableArray *commitFailedFiles = [NSMutableArray array];
for (uint64_t index = 0; index < commitFailedFileCount; index++) {
CommitFailedFile *cff = [[CommitFailedFile alloc] initWithInputStream:is error:error];
if (cff == nil) {
break;
}
[commitFailedFiles addObject:cff];
[cff release];
}
_commitFailedFiles = [commitFailedFiles retain];
}
ret = YES;
} while (0);
if (!ret) {
[self release];
self = nil;
}
}
return self;
}
- (void)release {
[super release];
}
- (void)dealloc {
[_author release];
[_comment release];
[_parentCommitSHA1s release];
[_treeSHA1 release];
[_location release];
[_computer release];
[_mergeCommonAncestorCommitSHA1 release];
[_creationDate release];
[_commitFailedFiles release];
[super dealloc];
}
@end
@implementation Commit (internal)
- (BOOL)readHeader:(id <BufferedInputStream>)is error:(NSError **)error {
unsigned char *headerBytes = [is readExactly:HEADER_LENGTH error:error];
if (headerBytes == NULL) {
return NO;
}
NSString *header = [[[NSString alloc] initWithBytes:headerBytes length:HEADER_LENGTH encoding:NSASCIIStringEncoding] autorelease];
NSRange versionRange = [header rangeOfRegex:@"^CommitV(\\d{3})$" capture:1];
commitVersion = 0;
if (versionRange.location != NSNotFound) {
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
NSNumber *number = [nf numberFromString:[header substringWithRange:versionRange]];
commitVersion = [number intValue];
[nf release];
}
if (commitVersion > CURRENT_COMMIT_VERSION || commitVersion < 2) {
SETNSERROR(@"TreeErrorDomain", ERROR_INVALID_OBJECT_VERSION, @"invalid Commit header");
return NO;
}
return YES;
}
@end

44
CommitFailedFile.h Normal file
View file

@ -0,0 +1,44 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
@protocol BufferedInputStream;
@interface CommitFailedFile : NSObject {
NSString *relativePath;
NSString *errorMessage;
}
- (id)initWithInputStream:(id <BufferedInputStream>)is error:(NSError **)error;
- (NSString *)relativePath;
- (NSString *)errorMessage;
- (void)writeTo:(NSMutableData *)data;
@end

65
CommitFailedFile.m Normal file
View file

@ -0,0 +1,65 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "CommitFailedFile.h"
#import "StringIO.h"
#import "BufferedInputStream.h"
@implementation CommitFailedFile
- (id)initWithInputStream:(id <BufferedInputStream>)is error:(NSError **)error {
if (self = [super init]) {
if (![StringIO read:&relativePath from:is error:error]
|| ![StringIO read:&errorMessage from:is error:error]) {
[self release];
self = nil;
}
[relativePath retain];
[errorMessage retain];
}
return self;
}
- (void)dealloc {
[relativePath release];
[errorMessage release];
[super dealloc];
}
- (NSString *)relativePath {
return [[relativePath retain] autorelease];
}
- (NSString *)errorMessage {
return [[errorMessage retain] autorelease];
}
- (void)writeTo:(NSMutableData *)data {
[StringIO write:relativePath to:data];
[StringIO write:errorMessage to:data];
}
@end

53
DiskPack.h Normal file
View file

@ -0,0 +1,53 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
@class S3Service;
@class ServerBlob;
@interface DiskPack : NSObject {
S3Service *s3;
NSString *s3BucketName;
NSString *computerUUID;
NSString *packSetName;
NSString *packSHA1;
NSString *s3Path;
NSString *localPath;
}
+ (NSString *)s3PathWithS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1;
+ (NSString *)localPathWithComputerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1;
- (id)initWithS3Service:(S3Service *)theS3 s3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1;
- (BOOL)makeLocal:(NSError **)error;
- (ServerBlob *)newServerBlobForObjectAtOffset:(unsigned long long)offset error:(NSError **)error;
- (BOOL)fileLength:(unsigned long long *)length error:(NSError **)error;
- (NSArray *)sortedPackIndexEntries:(NSError **)error;
@end

232
DiskPack.m Normal file
View file

@ -0,0 +1,232 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/stat.h>
#import "DiskPack.h"
#import "SetNSError.h"
#import "FDInputStream.h"
#import "StringIO.h"
#import "IntegerIO.h"
#import "PackSetSet.h"
#import "ServerBlob.h"
#import "NSFileManager_extra.h"
#import "S3Service.h"
#import "ServerBlob.h"
#import "FileInputStream.h"
#import "FileOutputStream.h"
#import "Streams.h"
#import "PackSet.h"
#import "S3ObjectReceiver.h"
#import "S3ObjectMetadata.h"
#import "PackIndexEntry.h"
#import "SHA1Hash.h"
@interface DiskPack (internal)
- (BOOL)savePack:(ServerBlob *)sb bytesWritten:(unsigned long long *)written error:(NSError **)error;
- (NSArray *)sortedPackIndexEntriesFromStream:(id <BufferedInputStream>)fis error:(NSError **)error;
@end
@implementation DiskPack
+ (NSString *)s3PathWithS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1 {
return [NSString stringWithFormat:@"%@/%@.pack", [PackSet s3PathWithS3BucketName:theS3BucketName computerUUID:theComputerUUID packSetName:thePackSetName], thePackSHA1];
}
+ (NSString *)localPathWithComputerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1 {
return [NSString stringWithFormat:@"%@/%@/%@.pack", [PackSet localPathWithComputerUUID:theComputerUUID packSetName:thePackSetName], [thePackSHA1 substringToIndex:2], [thePackSHA1 substringFromIndex:2]];
}
- (id)initWithS3Service:(S3Service *)theS3 s3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1 {
if (self = [super init]) {
s3 = [theS3 retain];
s3BucketName = [theS3BucketName retain];
computerUUID = [theComputerUUID retain];
packSetName = [thePackSetName retain];
packSHA1 = [thePackSHA1 retain];
s3Path = [[DiskPack s3PathWithS3BucketName:s3BucketName computerUUID:computerUUID packSetName:packSetName packSHA1:packSHA1] retain];
localPath = [[DiskPack localPathWithComputerUUID:computerUUID packSetName:packSetName packSHA1:packSHA1] retain];
}
return self;
}
- (void)dealloc {
[s3 release];
[s3BucketName release];
[computerUUID release];
[packSetName release];
[packSHA1 release];
[s3Path release];
[localPath release];
[super dealloc];
}
- (BOOL)makeLocal:(NSError **)error {
NSFileManager *fm = [NSFileManager defaultManager];
BOOL ret = NO;
if (![fm fileExistsAtPath:localPath]) {
HSLogDebug(@"packset %@: making pack %@ local", packSetName, packSHA1);
ServerBlob *sb = [s3 newServerBlobAtPath:s3Path error:error];
if (sb != nil) {
unsigned long long bytesWritten;
ret = [self savePack:sb bytesWritten:&bytesWritten error:error];
[sb release];
}
} else {
ret = YES;
}
return ret;
}
- (ServerBlob *)newServerBlobForObjectAtOffset:(unsigned long long)offset error:(NSError **)error {
int fd = open([localPath fileSystemRepresentation], O_RDONLY);
if (fd == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"%s: %@", strerror(errno), localPath);
return nil;
}
ServerBlob *ret = nil;
FDInputStream *fdis = [[FDInputStream alloc] initWithFD:fd];
do {
if (lseek(fd, offset, SEEK_SET) == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"lseek(%@, %qu): %s", localPath, offset, strerror(errno));
break;
}
NSString *mimeType;
NSString *downloadName;
if (![StringIO read:&mimeType from:fdis error:error] || ![StringIO read:&downloadName from:fdis error:error]) {
break;
}
uint64_t dataLen = 0;
if (![IntegerIO readUInt64:&dataLen from:fdis error:error]) {
break;
}
NSData *data = nil;
if (dataLen > 0) {
const unsigned char *bytes = [fdis readExactly:dataLen error:error];
if (bytes == NULL) {
break;
}
data = [NSData dataWithBytes:bytes length:dataLen];
} else {
data = [NSData data];
}
ret = [[ServerBlob alloc] initWithData:data mimeType:mimeType downloadName:downloadName];
} while (0);
close(fd);
[fdis release];
return ret;
}
- (BOOL)fileLength:(unsigned long long *)length error:(NSError **)error {
struct stat st;
if (lstat([localPath fileSystemRepresentation], &st) == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"lstat(%@): %s", localPath, strerror(errno));
return NO;
}
*length = st.st_size;
return YES;
}
- (BOOL)copyToPath:(NSString *)dest error:(NSError **)error {
NSFileManager *fm = [NSFileManager defaultManager];
if ([fm fileExistsAtPath:dest] && ![fm removeItemAtPath:dest error:error]) {
HSLogError(@"error removing old mutable pack at %@", dest);
return NO;
}
if (![fm ensureParentPathExistsForPath:dest error:error] || ![fm copyItemAtPath:localPath toPath:dest error:error]) {
HSLogError(@"error copying pack %@ to %@", localPath, dest);
return NO;
}
HSLogDebug(@"copied %@ to %@", localPath, dest);
return YES;
}
- (NSArray *)sortedPackIndexEntries:(NSError **)error {
unsigned long long length;
if (![self fileLength:&length error:error]) {
return NO;
}
FileInputStream *fis = [[FileInputStream alloc] initWithPath:localPath length:length];
NSArray *ret = [self sortedPackIndexEntriesFromStream:fis error:error];
[fis release];
return ret;
}
@end
@implementation DiskPack (internal)
- (BOOL)savePack:(ServerBlob *)sb bytesWritten:(unsigned long long *)written error:(NSError **)error {
if (![[NSFileManager defaultManager] ensureParentPathExistsForPath:localPath error:error]) {
return NO;
}
id <InputStream> is = [sb newInputStream];
NSError *myError;
BOOL ret = [Streams transferFrom:is atomicallyToFile:localPath bytesWritten:written error:&myError];
if (ret) {
HSLogDebug(@"wrote %qu bytes to %@", *written, localPath);
} else {
if (error != NULL) {
*error = myError;
}
HSLogError(@"error making pack %@ local at %@: %@", packSHA1, localPath, [myError localizedDescription]);
}
[is release];
return ret;
}
- (NSArray *)sortedPackIndexEntriesFromStream:(id <BufferedInputStream>)is error:(NSError **)error {
uint32_t packSig;
uint32_t packVersion;
if (![IntegerIO readUInt32:&packSig from:is error:error] || ![IntegerIO readUInt32:&packVersion from:is error:error]) {
return nil;
}
if (packSig != 0x5041434b) { // "PACK"
SETNSERROR(@"PackErrorDomain", -1, @"invalid pack signature");
return nil;
}
if (packVersion != 2) {
SETNSERROR(@"PackErrorDomain", -1, @"invalid pack version");
}
uint32_t objectCount;
if (![IntegerIO readUInt32:&objectCount from:is error:error]) {
return nil;
}
NSMutableArray *ret = [NSMutableArray array];
for (uint32_t index = 0; index < objectCount; index++) {
uint64_t offset = [is bytesReceived];
NSString *mimeType;
NSString *name;
uint64_t length;
if (![StringIO read:&mimeType from:is error:error] || ![StringIO read:&name from:is error:error] || ![IntegerIO readUInt64:&length from:is error:error]) {
return NO;
}
NSString *objectSHA1 = [SHA1Hash hashStream:is withlength:length error:error];
if (objectSHA1 == nil) {
return NO;
}
PackIndexEntry *pie = [[PackIndexEntry alloc] initWithPackSHA1:packSHA1 offset:offset dataLength:length objectSHA1:objectSHA1];
[ret addObject:pie];
[pie release];
}
return ret;
}
@end

52
DiskPackIndex.h Normal file
View file

@ -0,0 +1,52 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
@class PackIndexEntry;
@class S3Service;
@interface DiskPackIndex : NSObject {
S3Service *s3;
NSString *s3BucketName;
NSString *computerUUID;
NSString *packSetName;
NSString *packSHA1;
NSString *s3Path;
NSString *localPath;
}
+ (NSString *)s3PathWithS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1;
+ (NSString *)localPathWithComputerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1;
- (id)initWithS3Service:(S3Service *)theS3 s3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1;
- (BOOL)makeLocal:(NSError **)error;
- (NSArray *)allPackIndexEntries:(NSError **)error;
- (PackIndexEntry *)entryForSHA1:(NSString *)sha1 error:(NSError **)error;
@end

300
DiskPackIndex.m Normal file
View file

@ -0,0 +1,300 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/stat.h>
#include <sys/mman.h>
#include <libkern/OSByteOrder.h>
#import "DiskPackIndex.h"
#import "NSString_extra.h"
#import "SetNSError.h"
#import "BinarySHA1.h"
#import "PackIndexEntry.h"
#import "NSErrorCodes.h"
#import "S3Service.h"
#import "PackSetSet.h"
#import "FileOutputStream.h"
#import "Streams.h"
#import "NSFileManager_extra.h"
#import "ServerBlob.h"
#import "PackSet.h"
#import "S3ObjectReceiver.h"
#import "DiskPack.h"
#import "BlobACL.h"
#import "FileInputStreamFactory.h"
#import "ArqUserLibrary.h"
#import "Blob.h"
typedef struct index_object {
uint64_t nbo_offset;
uint64_t nbo_datalength;
unsigned char sha1[20];
unsigned char filler[4];
} index_object;
typedef struct pack_index {
uint32_t magic_number;
uint32_t nbo_version;
uint32_t nbo_fanout[256];
index_object first_index_object;
} pack_index;
@interface DiskPackIndex (internal)
- (BOOL)savePackIndex:(ServerBlob *)sb bytesWritten:(unsigned long long *)written error:(NSError **)error;
- (PackIndexEntry *)doEntryForSHA1:(NSString *)sha1 error:(NSError **)error;
- (PackIndexEntry *)findEntryForSHA1:(NSString *)sha1 fd:(int)fd betweenStartIndex:(uint32_t)startIndex andEndIndex:(uint32_t)endIndex error:(NSError **)error;
- (BOOL)readFanoutStartIndex:(uint32_t *)start fanoutEndIndex:(uint32_t *)end fromFD:(int)fd forSHA1FirstByte:(unsigned int)firstByte error:(NSError **)error;
@end
@implementation DiskPackIndex
+ (NSString *)s3PathWithS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1 {
return [NSString stringWithFormat:@"%@/%@.index", [PackSet s3PathWithS3BucketName:theS3BucketName computerUUID:theComputerUUID packSetName:thePackSetName], thePackSHA1];
}
+ (NSString *)localPathWithComputerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1 {
return [NSString stringWithFormat:@"%@/%@/%@.index", [PackSet localPathWithComputerUUID:theComputerUUID packSetName:thePackSetName], [thePackSHA1 substringToIndex:2], [thePackSHA1 substringFromIndex:2]];
}
- (id)initWithS3Service:(S3Service *)theS3 s3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1 {
if (self = [super init]) {
s3 = [theS3 retain];
s3BucketName = [theS3BucketName retain];
computerUUID = [theComputerUUID retain];
packSetName = [thePackSetName retain];
packSHA1 = [thePackSHA1 retain];
s3Path = [[DiskPackIndex s3PathWithS3BucketName:s3BucketName computerUUID:computerUUID packSetName:packSetName packSHA1:packSHA1] retain];
localPath = [[DiskPackIndex localPathWithComputerUUID:computerUUID packSetName:packSetName packSHA1:packSHA1] retain];
}
return self;
}
- (void)dealloc {
[s3 release];
[s3BucketName release];
[computerUUID release];
[packSetName release];
[packSHA1 release];
[s3Path release];
[localPath release];
[super dealloc];
}
- (BOOL)makeLocal:(NSError **)error {
NSFileManager *fm = [NSFileManager defaultManager];
BOOL ret = NO;
if (![fm fileExistsAtPath:localPath]) {
HSLogDebug(@"packset %@: making pack index %@ local", packSetName, packSHA1);
ServerBlob *sb = [s3 newServerBlobAtPath:s3Path error:error];
if (sb != nil) {
unsigned long long bytesWritten;
ret = [self savePackIndex:sb bytesWritten:&bytesWritten error:error];
[sb release];
} else {
HSLogError(@"failed to read pack index from S3 path %@", s3Path);
}
} else {
ret = YES;
}
return ret;
}
- (NSArray *)allPackIndexEntries:(NSError **)error {
int fd = open([localPath fileSystemRepresentation], O_RDONLY);
if (fd == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"%s: %@", strerror(errno), localPath);
return nil;
}
struct stat st;
if (fstat(fd, &st) == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"fstat(%@): %s", localPath, strerror(errno));
close(fd);
return nil;
}
pack_index *the_pack_index = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (the_pack_index == MAP_FAILED) {
SETNSERROR(@"UnixErrorDomain", errno, @"mmap: %s", strerror(errno));
close(fd);
return NO;
}
NSMutableArray *ret = [NSMutableArray array];
uint32_t count = OSSwapBigToHostInt32(the_pack_index->nbo_fanout[255]);
index_object *indexObjects = &(the_pack_index->first_index_object);
for (uint32_t i = 0; i < count; i++) {
uint64_t offset = OSSwapBigToHostInt64(indexObjects[i].nbo_offset);
uint64_t dataLength = OSSwapBigToHostInt64(indexObjects[i].nbo_datalength);
NSString *objectSHA1 = [NSString hexStringWithBytes:indexObjects[i].sha1 length:20];
PackIndexEntry *pie = [[PackIndexEntry alloc] initWithPackSHA1:packSHA1 offset:offset dataLength:dataLength objectSHA1:objectSHA1];
[ret addObject:pie];
[pie release];
}
if (munmap(the_pack_index, st.st_size) == -1) {
HSLogError(@"munmap: %s", strerror(errno));
}
close(fd);
return ret;
}
- (PackIndexEntry *)entryForSHA1:(NSString *)sha1 error:(NSError **)error {
if (error != NULL) {
*error = nil;
}
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
PackIndexEntry *ret = [self doEntryForSHA1:sha1 error:error];
[ret retain];
if (ret == nil && error != NULL) {
[*error retain];
}
[pool drain];
[ret autorelease];
if (ret == nil && error != NULL) {
[*error autorelease];
}
return ret;
}
@end
@implementation DiskPackIndex (internal)
- (BOOL)savePackIndex:(ServerBlob *)sb bytesWritten:(unsigned long long *)written error:(NSError **)error {
if (![[NSFileManager defaultManager] ensureParentPathExistsForPath:localPath error:error]) {
return NO;
}
id <InputStream> is = [sb newInputStream];
NSError *myError = nil;
BOOL ret = [Streams transferFrom:is atomicallyToFile:localPath bytesWritten:written error:&myError];
if (ret) {
HSLogDebug(@"wrote %qu bytes to %@", *written, localPath);
} else {
if (error != NULL) {
*error = myError;
}
HSLogError(@"error making pack %@ local at %@: %@", packSHA1, localPath, [myError localizedDescription]);
}
[is release];
return ret;
}
- (PackIndexEntry *)doEntryForSHA1:(NSString *)sha1 error:(NSError **)error {
NSData *sha1Hex = [sha1 hexStringToData];
unsigned char *sha1Bytes = (unsigned char *)[sha1Hex bytes];
HSLogTrace(@"looking for sha1 %@ in packindex %@", sha1, packSHA1);
int fd = open([localPath fileSystemRepresentation], O_RDONLY);
if (fd == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"%s: %@", strerror(errno), localPath);
return nil;
}
uint32_t startIndex;
uint32_t endIndex;
if (![self readFanoutStartIndex:&startIndex fanoutEndIndex:&endIndex fromFD:fd forSHA1FirstByte:(unsigned int)sha1Bytes[0] error:error]) {
close(fd);
return nil;
}
close(fd);
if (endIndex == 0) {
SETNSERROR(@"PacksErrorDomain", ERROR_NOT_FOUND, @"sha1 %@ not found in pack", sha1);
return NO;
}
fd = open([localPath fileSystemRepresentation], O_RDONLY);
if (fd == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"%s: %@", strerror(errno), localPath);
return nil;
}
PackIndexEntry *ret = [self findEntryForSHA1:sha1 fd:fd betweenStartIndex:startIndex andEndIndex:endIndex error:error];
close(fd);
if (ret != nil) {
HSLogTrace(@"found sha1 %@ in packindex %@", sha1, packSHA1);
}
return ret;
}
- (PackIndexEntry *)findEntryForSHA1:(NSString *)sha1 fd:(int)fd betweenStartIndex:(uint32_t)startIndex andEndIndex:(uint32_t)endIndex error:(NSError **)error {
NSData *sha1Data = [sha1 hexStringToData];
const void *sha1Bytes = [sha1Data bytes];
uint32_t lengthToMap = 4 + 4 + 256*4 + endIndex * sizeof(index_object);
pack_index *the_pack_index = mmap(0, lengthToMap, PROT_READ, MAP_SHARED, fd, 0);
if (the_pack_index == MAP_FAILED) {
SETNSERROR(@"UnixErrorDomain", errno, @"mmap: %s", strerror(errno));
return NO;
}
int64_t left = startIndex;
int64_t right = endIndex - 1;
int64_t middle;
int64_t offset;
int64_t dataLength;
PackIndexEntry *pie = nil;
while (left <= right) {
middle = (left + right)/2;
index_object *firstIndexObject = &(the_pack_index->first_index_object);
index_object *middleIndexObject = &firstIndexObject[middle];
void *middleSHA1 = middleIndexObject->sha1;
NSComparisonResult result = [BinarySHA1 compare:middleSHA1 to:sha1Bytes];
switch (result) {
case NSOrderedAscending:
left = middle + 1;
break;
case NSOrderedDescending:
right = middle - 1;
break;
default:
offset = OSSwapBigToHostInt64(middleIndexObject->nbo_offset);
dataLength = OSSwapBigToHostInt64(middleIndexObject->nbo_datalength);
pie = [[[PackIndexEntry alloc] initWithPackSHA1:packSHA1 offset:offset dataLength:dataLength objectSHA1:sha1] autorelease];
}
if (pie != nil) {
break;
}
}
if (munmap(the_pack_index, lengthToMap) == -1) {
HSLogError(@"munmap: %s", strerror(errno));
}
if (pie == nil) {
SETNSERROR(@"PackErrorDomain", ERROR_NOT_FOUND, @"sha1 %@ not found in pack %@", sha1, packSHA1);
}
return pie;
}
- (BOOL)readFanoutStartIndex:(uint32_t *)start fanoutEndIndex:(uint32_t *)end fromFD:(int)fd forSHA1FirstByte:(unsigned int)firstByte error:(NSError **)error {
size_t len = 4 + 4 + 4*256;
uint32_t *map = mmap(0, len, PROT_READ, MAP_SHARED, fd, 0);
if (map == MAP_FAILED) {
SETNSERROR(@"UnixErrorDomain", errno, @"mmap: %s", strerror(errno));
return NO;
}
BOOL ret = YES;
uint32_t magicNumber = OSSwapBigToHostInt32(map[0]);
uint32_t version = OSSwapBigToHostInt32(map[1]);
if (magicNumber != 0xff744f63 || version != 2) {
SETNSERROR(@"PackErrorDomain", -1, @"invalid pack index header");
ret = NO;
} else {
uint32_t *fanoutTable = map + 2;
*start = 0;
if (firstByte > 0) {
*start = OSSwapBigToHostInt32(fanoutTable[firstByte - 1]);
}
*end = OSSwapBigToHostInt32(fanoutTable[firstByte]);
}
if (munmap(map, len) == -1) {
HSLogError(@"munmap: %s", strerror(errno));
}
return ret;
}
@end

91
FileAttributes.h Normal file
View file

@ -0,0 +1,91 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/stat.h>
#import <Cocoa/Cocoa.h>
@interface FileAttributes : NSObject {
BOOL targetExists;
NSString *path;
const char *cPath;
struct stat st;
struct timespec createTime;
NSString *aclString;
int finderFlags;
int extendedFinderFlags;
NSString *finderFileType;
NSString *finderFileCreator;
}
- (id)initWithPath:(NSString *)thePath error:(NSError **)error;
- (id)initWithPath:(NSString *)thePath stat:(struct stat *)st error:(NSError **)error;
- (unsigned long long)fileSize;
- (NSString *)aclString;
- (NSString *)aclSHA1;
- (int)uid;
- (int)gid;
- (int)mode;
- (long)mtime_sec;
- (long)mtime_nsec;
- (long)flags;
- (int)finderFlags;
- (int)extendedFinderFlags;
- (NSString *)finderFileType;
- (NSString *)finderFileCreator;
- (BOOL)isExtensionHidden;
- (BOOL)isFifo;
- (BOOL)isDevice;
- (BOOL)isSymbolicLink;
- (BOOL)isRegularFile;
- (BOOL)isSocket;
- (int)st_dev;
- (int)st_ino;
- (uint32_t)st_nlink;
- (int)st_rdev;
- (int64_t)ctime_sec;
- (int64_t)ctime_nsec;
- (int64_t)createTime_sec;
- (int64_t)createTime_nsec;
- (int64_t)st_blocks;
- (uint32_t)st_blksize;
- (BOOL)applyFinderFileType:(NSString *)finderFileType finderFileCreator:(NSString *)finderFileCreator error:(NSError **)error;
- (BOOL)applyFlags:(int)flags error:(NSError **)error;
- (BOOL)applyAcl:(NSString *)aclString error:(NSError **)error;
- (BOOL)applyFinderFlags:(int)finderFlags error:(NSError **)error;
- (BOOL)applyExtendedFinderFlags:(int)extendedFinderFlags error:(NSError **)error;
- (BOOL)applyExtensionHidden:(BOOL)isExtensionHidden error:(NSError **)error;
- (BOOL)applyUID:(int)uid gid:(int)gid error:(NSError **)error;
- (BOOL)applyMode:(int)mode error:(NSError **)error;
- (BOOL)applyMTimeSec:(int64_t)mtime_sec mTimeNSec:(int64_t)mtime_nsec error:(NSError **)error;
- (BOOL)applyCreateTimeSec:(int64_t)createTime_sec createTimeNSec:(int64_t)createTime_sec error:(NSError **)error;
@end

559
FileAttributes.m Normal file
View file

@ -0,0 +1,559 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <CoreServices/CoreServices.h>
#include <sys/attr.h>
#include <unistd.h>
#include <sys/time.h>
#include <stdio.h>
#import "FileAttributes.h"
#import "SHA1Hash.h"
#import "FileACL.h"
#import "SetNSError.h"
#import "OSStatusDescription.h"
#define kCouldNotCreateCFString 4
#define kCouldNotGetStringData 5
#define MAX_PATH 1024
struct createDateBuf {
u_int32_t length;
struct timespec createTime;
};
static OSStatus ConvertCStringToHFSUniStr(const char* cStr, HFSUniStr255 *uniStr) {
OSStatus oss = noErr;
CFStringRef tmpStringRef = CFStringCreateWithCString(kCFAllocatorDefault, cStr, kCFStringEncodingMacRoman);
if (tmpStringRef != NULL) {
if (CFStringGetCString(tmpStringRef, (char*)uniStr->unicode, sizeof(uniStr->unicode), kCFStringEncodingUnicode)) {
uniStr->length = CFStringGetLength(tmpStringRef);
} else {
oss = kCouldNotGetStringData;
}
CFRelease(tmpStringRef);
} else {
oss = kCouldNotCreateCFString;
}
return oss;
}
static OSStatus SymlinkPathMakeRef(const UInt8 *path, FSRef *ref, Boolean *isDirectory) {
FSRef tmpFSRef;
char tmpPath[MAX_PATH];
char *tmpNamePtr;
OSStatus oss;
strcpy(tmpPath, (char *)path);
tmpNamePtr = strrchr(tmpPath, '/');
if (*(tmpNamePtr + 1) == '\0') {
// Last character in the path is a '/'.
*tmpNamePtr = '\0';
tmpNamePtr = strrchr(tmpPath, '/');
}
*tmpNamePtr = '\0';
tmpNamePtr++;
// Get FSRef for parent directory.
oss = FSPathMakeRef((const UInt8 *)tmpPath, &tmpFSRef, NULL);
if (oss == noErr) {
HFSUniStr255 uniName;
oss = ConvertCStringToHFSUniStr(tmpNamePtr, &uniName);
if (oss == noErr) {
FSRef newFSRef;
oss = FSMakeFSRefUnicode(&tmpFSRef, uniName.length, uniName.unicode, kTextEncodingUnknown, &newFSRef);
tmpFSRef = newFSRef;
}
}
if (oss == noErr) {
*ref = tmpFSRef;
}
return oss;
}
@implementation FileAttributes
- (id)initWithPath:(NSString *)thePath error:(NSError **)error {
struct stat theStat;
int ret = lstat([thePath fileSystemRepresentation], &theStat);
if (ret == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"%s", strerror(errno));
return nil;
}
return [self initWithPath:thePath stat:&theStat error:error];
}
- (id)initWithPath:(NSString *)thePath stat:(struct stat *)theStat error:(NSError **)error {
if (self = [super init]) {
path = [thePath copy];
cPath = [path fileSystemRepresentation];
memcpy(&st, theStat, sizeof(st));
targetExists = YES;
if ((st.st_mode & S_IFLNK) == S_IFLNK) {
struct stat targetSt;
int ret = stat(cPath, &targetSt);
if (ret == -1 && errno == ENOENT) {
targetExists = NO;
}
}
if (targetExists) {
FSRef fsRef;
Boolean isDirectory;
OSStatus oss = 0;
if ((st.st_mode & S_IFLNK) == S_IFLNK) {
oss = SymlinkPathMakeRef((UInt8*)cPath, &fsRef, &isDirectory);
} else {
oss = FSPathMakeRef((UInt8*)cPath, &fsRef, &isDirectory);
}
if (oss == bdNamErr) {
HSLogInfo(@"skipping finder flags for %s: %@", cPath, [OSStatusDescription descriptionForMacOSStatus:oss]);
}else if (oss != noErr) {
SETNSERROR(@"MacFilesErrorDomain", oss, @"%@", [OSStatusDescription descriptionForMacOSStatus:oss]);
[self release];
self = nil;
return self;
} else {
FSCatalogInfo catalogInfo;
OSErr oserr = FSGetCatalogInfo(&fsRef, kFSCatInfoCreateDate | kFSCatInfoFinderInfo | kFSCatInfoFinderXInfo, &catalogInfo, NULL, NULL, NULL);
if (oserr) {
SETNSERROR(@"MacFilesErrorDomain", oss, @"%@", [OSStatusDescription descriptionForMacOSStatus:(OSStatus)oserr]);
[self release];
self = nil;
return self;
}
finderFlags = 0;
extendedFinderFlags = 0;
if (isDirectory) {
FolderInfo *folderInfo = (FolderInfo *)&catalogInfo.finderInfo;
finderFlags = folderInfo->finderFlags;
ExtendedFolderInfo *extFolderInfo = (ExtendedFolderInfo *)&catalogInfo.extFinderInfo;
extendedFinderFlags = extFolderInfo->extendedFinderFlags;
finderFileType = [[NSString alloc] initWithString:@""];
finderFileCreator = [[NSString alloc] initWithString:@""];
} else {
FileInfo *fileInfo = (FileInfo *)&catalogInfo.finderInfo;
finderFlags = fileInfo->finderFlags;
ExtendedFileInfo *extFileInfo = (ExtendedFileInfo *)&catalogInfo.extFinderInfo;
extendedFinderFlags = extFileInfo->extendedFinderFlags;
char fileType[5];
fileType[0] = *((const char *)&fileInfo->fileType + 3);
fileType[1] = *((const char *)&fileInfo->fileType + 2);
fileType[2] = *((const char *)&fileInfo->fileType + 1);
fileType[3] = *((const char *)&fileInfo->fileType);
fileType[4] = 0;
finderFileType = [[NSString alloc] initWithCString:fileType encoding:NSUTF8StringEncoding];
char fileCreator[5];
fileCreator[0] = *((const char *)&fileInfo->fileCreator + 3);
fileCreator[1] = *((const char *)&fileInfo->fileCreator + 2);
fileCreator[2] = *((const char *)&fileInfo->fileCreator + 1);
fileCreator[3] = *((const char *)&fileInfo->fileCreator);
fileCreator[4] = 0;
finderFileCreator = [[NSString alloc] initWithCString:fileCreator encoding:NSUTF8StringEncoding];
}
}
}
if (![FileACL aclText:&aclString forFile:path error:error]) {
[self release];
self = nil;
return self;
}
[aclString retain];
struct attrlist attrList;
memset(&attrList, 0, sizeof(attrList));
attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
attrList.commonattr = ATTR_CMN_CRTIME;
struct createDateBuf createDateBuf;
if (getattrlist(cPath, &attrList, &createDateBuf, sizeof(createDateBuf), FSOPT_NOFOLLOW) == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"Error getting create date for %@: %s", thePath, strerror(errno));
[self release];
self = nil;
return self;
}
createTime.tv_sec = createDateBuf.createTime.tv_sec;
createTime.tv_nsec = createDateBuf.createTime.tv_nsec;
}
return self;
}
- (void)dealloc {
[path release];
[aclString release];
[finderFileType release];
[finderFileCreator release];
[super dealloc];
}
- (unsigned long long)fileSize {
return (unsigned long long)st.st_size;
}
- (NSString *)aclString {
return aclString;
}
- (NSString *)aclSHA1 {
NSString *sha1 = nil;
if (aclString) {
sha1 = [SHA1Hash hashData:[aclString dataUsingEncoding:NSUTF8StringEncoding]];
}
return sha1;
}
- (int)uid {
return st.st_uid;
}
- (int)gid {
return st.st_gid;
}
- (int)mode {
return st.st_mode;
}
- (long)mtime_sec {
return st.st_mtimespec.tv_sec;
}
- (long)mtime_nsec {
return st.st_mtimespec.tv_nsec;
}
- (long)flags {
return st.st_flags;
}
- (int)finderFlags {
return finderFlags;
}
- (int)extendedFinderFlags {
return extendedFinderFlags;
}
- (NSString *)finderFileType {
return finderFileType;
}
- (NSString *)finderFileCreator {
return finderFileCreator;
}
- (BOOL)isExtensionHidden {
return (st.st_flags & UF_HIDDEN) == UF_HIDDEN;
}
- (BOOL)isFifo {
return (st.st_mode & S_IFIFO) == S_IFIFO;
}
- (BOOL)isDevice {
return (st.st_mode & S_IFCHR) == S_IFCHR || (st.st_mode & S_IFBLK) == S_IFBLK;
}
- (BOOL)isSymbolicLink {
return (st.st_mode & S_IFLNK) == S_IFLNK;
}
- (BOOL)isRegularFile {
return (st.st_mode & S_IFREG) == S_IFREG;
}
- (BOOL)isSocket {
return (st.st_mode & S_IFSOCK) == S_IFSOCK;
}
- (int)st_dev {
return st.st_dev;
}
- (int)st_ino {
return st.st_ino;
}
- (uint32_t)st_nlink {
return st.st_nlink;
}
- (int)st_rdev {
return st.st_rdev;
}
- (int64_t)ctime_sec {
return st.st_ctimespec.tv_sec;
}
- (int64_t)ctime_nsec {
return st.st_ctimespec.tv_nsec;
}
- (int64_t)createTime_sec {
return createTime.tv_sec;
}
- (int64_t)createTime_nsec {
return createTime.tv_nsec;
}
- (int64_t)st_blocks {
return st.st_blocks;
}
- (uint32_t)st_blksize {
return st.st_blksize;
}
- (BOOL)applyFinderFileType:(NSString *)fft finderFileCreator:(NSString *)ffc error:(NSError **)error {
if (targetExists && (![fft isEqualToString:finderFileType] || ![ffc isEqualToString:finderFileCreator])) {
if ([fft length] != 4) {
HSLogWarn(@"not applying finder file type '%@' to %@: invalid length (must be 4 characters)", fft, path);
} else if ([ffc length] != 4) {
HSLogWarn(@"not applying finder file type '%@' to %@: invalid length (must be 4 characters)", ffc, path);
} else {
FSRef fsRef;
Boolean isDirectory;
OSStatus oss = 0;
if ((st.st_mode & S_IFLNK) == S_IFLNK) {
oss = SymlinkPathMakeRef((UInt8*)cPath, &fsRef, &isDirectory);
} else {
oss = FSPathMakeRef((UInt8*)cPath, &fsRef, &isDirectory);
}
if (oss != noErr) {
if (oss == bdNamErr) {
HSLogInfo(@"not setting finder file type/creator on %s: bad name", cPath);
return YES;
} else {
SETNSERROR(@"FileManagerErrorDomain", -1, @"%@", [OSStatusDescription descriptionForMacOSStatus:oss]);
return NO;
}
}
if (isDirectory) {
SETNSERROR(@"FileManagerErrorDomain", -1, @"cannot apply finderFileType to a directory");
return NO;
}
FSCatalogInfo catalogInfo;
OSErr oserr = FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, NULL);
if (oserr) {
SETNSERROR(@"FileManagerErrorDomain", -1, @"%@", [OSStatusDescription descriptionForMacOSStatus:(OSStatus)oserr]);
return NO;
}
FileInfo *fileInfo = (FileInfo *)&catalogInfo.finderInfo;
const char *fileType = [fft UTF8String];
char *destFileType = (char *)&fileInfo->fileType;
destFileType[3] = fileType[0];
destFileType[2] = fileType[1];
destFileType[1] = fileType[2];
destFileType[0] = fileType[3];
const char *fileCreator = [ffc UTF8String];
char *destFileCreator = (char *)&fileInfo->fileCreator;
destFileCreator[3] = fileCreator[0];
destFileCreator[2] = fileCreator[1];
destFileCreator[1] = fileCreator[2];
destFileCreator[0] = fileCreator[3];
oserr = FSSetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catalogInfo);
if (oserr) {
SETNSERROR(@"FileManagerErrorDomain", -1, @"%@", [OSStatusDescription descriptionForMacOSStatus:(OSStatus)oserr]);
return NO;
}
[finderFileType release];
finderFileType = [fft copy];
[finderFileCreator release];
finderFileCreator = [ffc copy];
}
}
return YES;
}
- (BOOL)applyFlags:(int)flags error:(NSError **)error {
if (targetExists && flags != st.st_flags) {
HSLogTrace(@"chflags(%s, %d)", cPath, flags);
if (chflags(cPath, flags) == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"chflags: %s", strerror(errno));
return NO;
}
st.st_flags = flags;
}
return YES;
}
- (BOOL)applyAcl:(NSString *)theACLString error:(NSError **)error {
BOOL ret = YES;
if (![theACLString isEqualToString:aclString]) {
ret = [FileACL writeACLText:theACLString toFile:path error:error];
if (ret) {
[aclString release];
aclString = [theACLString retain];
}
}
return ret;
}
- (BOOL)applyFinderFlags:(int)ff error:(NSError **)error {
if (targetExists && ff != finderFlags) {
FSRef fsRef;
Boolean isDirectory;
OSStatus oss = 0;
if ((st.st_mode & S_IFLNK) == S_IFLNK) {
oss = SymlinkPathMakeRef((UInt8*)cPath, &fsRef, &isDirectory);
} else {
oss = FSPathMakeRef((UInt8*)cPath, &fsRef, &isDirectory);
}
if (oss != noErr) {
if (oss == bdNamErr) {
HSLogInfo(@"not setting finder file type/creator on %s: bad name", cPath);
return YES;
} else {
SETNSERROR(@"FileManagerErrorDomain", -1, @"%@", [OSStatusDescription descriptionForMacOSStatus:oss]);
return NO;
}
}
FSCatalogInfo catalogInfo;
OSErr oserr = FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, NULL);
if (oserr) {
SETNSERROR(@"FileManagerErrorDomain", -1, @"%@", [OSStatusDescription descriptionForMacOSStatus:(OSStatus)oserr]);
return NO;
}
if (isDirectory) {
FolderInfo *folderInfo = (FolderInfo *)&catalogInfo.finderInfo;
folderInfo->finderFlags = ff;
} else {
FileInfo *fileInfo = (FileInfo *)&catalogInfo.finderInfo;
fileInfo->finderFlags = ff;
}
oserr = FSSetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catalogInfo);
if (oserr) {
SETNSERROR(@"FileManagerErrorDomain", -1, @"%@", [OSStatusDescription descriptionForMacOSStatus:(OSStatus)oserr]);
return NO;
}
finderFlags = ff;
}
return YES;
}
- (BOOL)applyExtendedFinderFlags:(int)eff error:(NSError **)error {
if (targetExists && extendedFinderFlags != eff) {
FSRef fsRef;
Boolean isDirectory;
OSStatus oss = 0;
if ((st.st_mode & S_IFLNK) == S_IFLNK) {
oss = SymlinkPathMakeRef((UInt8*)cPath, &fsRef, &isDirectory);
} else {
oss = FSPathMakeRef((UInt8*)cPath, &fsRef, &isDirectory);
}
if (oss != noErr) {
if (oss == bdNamErr) {
HSLogInfo(@"not setting finder file type/creator on %s: bad name", cPath);
return YES;
} else {
SETNSERROR(@"FileManagerErrorDomain", -1, @"%@", [OSStatusDescription descriptionForMacOSStatus:oss]);
return NO;
}
}
FSCatalogInfo catalogInfo;
OSErr oserr = FSGetCatalogInfo(&fsRef, kFSCatInfoFinderXInfo, &catalogInfo, NULL, NULL, NULL);
if (oserr) {
SETNSERROR(@"FileManagerErrorDomain", -1, @"%@", [OSStatusDescription descriptionForMacOSStatus:(OSStatus)oserr]);
return NO;
}
if (isDirectory) {
ExtendedFolderInfo *extFolderInfo = (ExtendedFolderInfo *)&catalogInfo.extFinderInfo;
extFolderInfo->extendedFinderFlags = eff;
} else {
ExtendedFileInfo *extFileInfo = (ExtendedFileInfo *)&catalogInfo.extFinderInfo;
extFileInfo->extendedFinderFlags = eff;
}
oserr = FSSetCatalogInfo(&fsRef, kFSCatInfoFinderXInfo, &catalogInfo);
if (oserr) {
SETNSERROR(@"FileManagerErrorDomain", -1, @"%@", [OSStatusDescription descriptionForMacOSStatus:(OSStatus)oserr]);
return NO;
}
extendedFinderFlags = eff;
}
return YES;
}
- (BOOL)applyExtensionHidden:(BOOL)hidden error:(NSError **)error {
BOOL ret = YES;
if (hidden != [self isExtensionHidden]) {
NSDictionary *attribs = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:hidden], NSFileExtensionHidden, nil];
ret = [[NSFileManager defaultManager] setAttributes:attribs ofItemAtPath:path error:error];
if (ret) {
if (hidden) {
st.st_flags = st.st_flags & UF_HIDDEN;
} else {
st.st_flags = st.st_flags & (0xffffffff ^ UF_HIDDEN);
}
}
}
return ret;
}
- (BOOL)applyUID:(int)uid gid:(int)gid error:(NSError **)error {
if (uid != st.st_uid || gid != st.st_gid) {
HSLogTrace(@"lchown(%s, %d, %d)", cPath, uid, gid);
if (lchown(cPath, uid, gid) == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"lchown: %s", strerror(errno));
return NO;
}
st.st_uid = uid;
st.st_gid = gid;
}
return YES;
}
- (BOOL)applyMode:(int)mode error:(NSError **)error {
if (mode != st.st_mode) {
if ((st.st_mode & S_IFDIR) == S_IFDIR) {
HSLogTrace(@"chmod(%s, %d)", cPath, mode);
int ret = chmod(cPath, mode);
if (ret == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"Setting permissions on %@: %s", path, strerror(errno));
return NO;
}
} else {
int fd = open(cPath, O_RDWR|O_SYMLINK);
if (fd == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"%s", strerror(errno));
return NO;
}
HSLogTrace(@"fchmod symlink (%s, %d)", cPath, mode);
int ret = fchmod(fd, mode);
close(fd);
if (ret == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"Setting permissions on %@: %s", path, strerror(errno));
return NO;
}
}
st.st_mode = mode;
}
return YES;
}
- (BOOL)applyMTimeSec:(int64_t)mtime_sec mTimeNSec:(int64_t)mtime_nsec error:(NSError **)error {
if (st.st_mtimespec.tv_sec != mtime_sec
|| st.st_mtimespec.tv_nsec != mtime_nsec) {
struct timespec mtimeSpec = { mtime_sec, mtime_nsec };
struct timeval atimeVal;
struct timeval mtimeVal;
TIMESPEC_TO_TIMEVAL(&atimeVal, &mtimeSpec); // Just use mtime because we don't have atime, nor do we care about atime.
TIMESPEC_TO_TIMEVAL(&mtimeVal, &mtimeSpec);
struct timeval timevals[2];
timevals[0] = atimeVal;
timevals[1] = mtimeVal;
if (utimes(cPath, timevals) == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"utimes(%@): %s", path, strerror(errno));
return NO;
}
}
return YES;
}
- (BOOL)applyCreateTimeSec:(int64_t)theCreateTime_sec createTimeNSec:(int64_t)theCreateTime_nsec error:(NSError **)error {
if (createTime.tv_sec != theCreateTime_sec || createTime.tv_nsec != theCreateTime_nsec) {
createTime.tv_sec = theCreateTime_sec;
createTime.tv_nsec = theCreateTime_nsec;
struct attrlist attrList;
memset(&attrList, 0, sizeof(attrList));
attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
attrList.commonattr = ATTR_CMN_CRTIME;
if (setattrlist(cPath, &attrList, &createTime, sizeof(createTime), FSOPT_NOFOLLOW) == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"Error setting create date on %@: %s", path, strerror(errno));
return NO;
}
}
return YES;
}
@end

53
HSLog.h Normal file
View file

@ -0,0 +1,53 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <pthread.h>
extern unsigned int global_hslog_level;
extern void setHSLogLevel(int level);
extern int hsLogLevelForName(NSString *levelName);
extern NSString *nameForHSLogLevel(int level);
#define HSLOG_LEVEL_TRACE (5)
#define HSLOG_LEVEL_DEBUG (4)
#define HSLOG_LEVEL_INFO (3)
#define HSLOG_LEVEL_WARN (2)
#define HSLOG_LEVEL_ERROR (1)
#define HSLOG_LEVEL_NONE (0)
#define HSLogTrace( s, ... ) { if (global_hslog_level >= HSLOG_LEVEL_TRACE) { NSLog(@"TRACE [thread %x] %p %@:%d %@", pthread_mach_thread_np(pthread_self()), self, [@__FILE__ lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__]); } }
#define HSLogDebug( s, ... ) { if (global_hslog_level >= HSLOG_LEVEL_DEBUG) { NSLog(@"DEBUG [thread %x] %p %@:%d %@", pthread_mach_thread_np(pthread_self()), self, [@__FILE__ lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__]); } }
#define HSLogDebugStatic( s, ... ) { if (global_hslog_level >= HSLOG_LEVEL_DEBUG) { NSLog(@"DEBUG [thread %x] %@:%d %@", pthread_mach_thread_np(pthread_self()), [@__FILE__ lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__]); } }
#define HSLogInfo( s, ... ) { if (global_hslog_level >= HSLOG_LEVEL_INFO) { NSLog(@"INFO [thread %x] %@", pthread_mach_thread_np(pthread_self()), [NSString stringWithFormat:(s), ##__VA_ARGS__]); } }
#define HSLogWarn( s, ... ) { if (global_hslog_level >= HSLOG_LEVEL_WARN) { NSLog(@"WARN [thread %x] %@", pthread_mach_thread_np(pthread_self()), [NSString stringWithFormat:(s), ##__VA_ARGS__]); } }
#define HSLogError( s, ... ) { if (global_hslog_level >= HSLOG_LEVEL_ERROR) { NSLog(@"ERROR [thread %x] %@", pthread_mach_thread_np(pthread_self()), [NSString stringWithFormat:(s), ##__VA_ARGS__]); } }
#define HSLog( s, ... ) NSLog(@"%@", [NSString stringWithFormat:(s), ##__VA_ARGS__])

68
HSLog.m Normal file
View file

@ -0,0 +1,68 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "HSLog.h"
unsigned int global_hslog_level = HSLOG_LEVEL_WARN;
void setHSLogLevel(int level) {
global_hslog_level = level;
}
extern int hsLogLevelForName(NSString *levelName) {
if ([[levelName lowercaseString] isEqualToString:@"error"]) {
return HSLOG_LEVEL_ERROR;
} else if ([[levelName lowercaseString] isEqualToString:@"warn"]) {
return HSLOG_LEVEL_WARN;
} else if ([[levelName lowercaseString] isEqualToString:@"info"]) {
return HSLOG_LEVEL_INFO;
} else if ([[levelName lowercaseString] isEqualToString:@"debug"]) {
return HSLOG_LEVEL_DEBUG;
} else if ([[levelName lowercaseString] isEqualToString:@"trace"]) {
return HSLOG_LEVEL_TRACE;
}
return HSLOG_LEVEL_NONE;
}
extern NSString *nameForHSLogLevel(int level) {
switch (level) {
case HSLOG_LEVEL_ERROR:
return @"Error";
case HSLOG_LEVEL_WARN:
return @"Warn";
case HSLOG_LEVEL_INFO:
return @"Info";
case HSLOG_LEVEL_DEBUG:
return @"Debug";
case HSLOG_LEVEL_TRACE:
return @"Trace";
}
return @"none";
}

104
Node.h Normal file
View file

@ -0,0 +1,104 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
@class MutableS3Repo;
#import "InputStream.h"
#import "InputStreamFactory.h"
#import "OutputStream.h"
@interface Node : NSObject {
int treeVersion;
BOOL isTree;
unsigned long long dataSize;
NSMutableArray *dataSHA1s;
NSString *thumbnailSHA1;
NSString *previewSHA1;
NSString *xattrsSHA1;
unsigned long long xattrsSize;
NSString *aclSHA1;
int uid;
int gid;
int mode;
int64_t mtime_sec;
int64_t mtime_nsec;
long long flags;
int finderFlags;
int extendedFinderFlags;
NSString *finderFileType;
NSString *finderFileCreator;
BOOL isFileExtensionHidden;
int st_dev;
int st_ino;
uint32_t st_nlink; // in struct stat, it's only 16 bits.
int st_rdev;
int64_t ctime_sec;
int64_t ctime_nsec;
int64_t createTime_sec;
int64_t createTime_nsec;
int64_t st_blocks;
uint32_t st_blksize;
}
- (id)initWithInputStream:(id <InputStream>)is treeVersion:(int)theTreeVersion error:(NSError **)error;
- (void)writeToData:(NSMutableData *)data;
- (BOOL)dataMatchesStatData:(struct stat *)st;
@property(readonly) BOOL isTree;
@property(readonly,copy) NSString *treeSHA1;
@property(readonly,copy) NSArray *dataSHA1s;
@property(readonly) unsigned long long dataSize;
@property(readonly,copy) NSString *thumbnailSHA1;
@property(readonly,copy) NSString *previewSHA1;
@property(readonly,copy) NSString *xattrsSHA1;
@property(readonly) unsigned long long xattrsSize;
@property(readonly,copy) NSString *aclSHA1;
@property(readonly) int uid;
@property(readonly) int gid;
@property(readonly) int mode;
@property(readonly) long long mtime_sec;
@property(readonly) long long mtime_nsec;
@property(readonly) long long flags;
@property(readonly) int finderFlags;
@property(readonly) int extendedFinderFlags;
@property(readonly,copy) NSString *finderFileType;
@property(readonly,copy) NSString *finderFileCreator;
@property(readonly) BOOL isFileExtensionHidden;
@property(readonly) int treeVersion;
@property(readonly) int st_rdev;
@property(readonly) long long ctime_sec;
@property(readonly) long long ctime_nsec;
@property(readonly) long long createTime_sec;
@property(readonly) long long createTime_nsec;
@property(readonly) uint32_t st_nlink;
@property(readonly) int st_ino;
@end

165
Node.m Normal file
View file

@ -0,0 +1,165 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/stat.h>
#import "BooleanIO.h"
#import "IntegerIO.h"
#import "StringIO.h"
#import "Node.h"
#import "InputStream.h"
#import "Blob.h"
#import "SetNSError.h"
#import "Tree.h"
@implementation Node
@synthesize isTree, dataSize, thumbnailSHA1, previewSHA1, xattrsSHA1, xattrsSize, aclSHA1, uid, gid, mode, mtime_sec, mtime_nsec, flags, finderFlags, extendedFinderFlags, finderFileType, finderFileCreator, isFileExtensionHidden, treeVersion, st_rdev;
@synthesize ctime_sec, ctime_nsec, createTime_sec, createTime_nsec, st_nlink, st_ino;
@dynamic treeSHA1, dataSHA1s;
- (id)initWithInputStream:(id <BufferedInputStream>)is treeVersion:(int)theTreeVersion error:(NSError **)error {
if (self = [super init]) {
treeVersion = theTreeVersion;
dataSHA1s = [[NSMutableArray alloc] init];
BOOL ret = NO;
do {
if (![BooleanIO read:&isTree from:is error:error]) {
break;
}
int dataSHA1sCount;
if (![IntegerIO readInt32:&dataSHA1sCount from:is error:error]) {
break;
}
for (int i = 0; i < dataSHA1sCount; i++) {
NSString *dataSHA1;
if (![StringIO read:&dataSHA1 from:is error:error]) {
break;
}
[dataSHA1s addObject:dataSHA1];
}
ret = [IntegerIO readUInt64:&dataSize from:is error:error]
&& [StringIO read:&thumbnailSHA1 from:is error:error]
&& [StringIO read:&previewSHA1 from:is error:error]
&& [StringIO read:&xattrsSHA1 from:is error:error]
&& [IntegerIO readUInt64:&xattrsSize from:is error:error]
&& [StringIO read:&aclSHA1 from:is error:error]
&& [IntegerIO readInt32:&uid from:is error:error]
&& [IntegerIO readInt32:&gid from:is error:error]
&& [IntegerIO readInt32:&mode from:is error:error]
&& [IntegerIO readInt64:&mtime_sec from:is error:error]
&& [IntegerIO readInt64:&mtime_nsec from:is error:error]
&& [IntegerIO readInt64:&flags from:is error:error]
&& [IntegerIO readInt32:&finderFlags from:is error:error]
&& [IntegerIO readInt32:&extendedFinderFlags from:is error:error]
&& [StringIO read:&finderFileType from:is error:error]
&& [StringIO read:&finderFileCreator from:is error:error]
&& [BooleanIO read:&isFileExtensionHidden from:is error:error]
&& [IntegerIO readInt32:&st_dev from:is error:error]
&& [IntegerIO readInt32:&st_ino from:is error:error]
&& [IntegerIO readUInt32:&st_nlink from:is error:error]
&& [IntegerIO readInt32:&st_rdev from:is error:error]
&& [IntegerIO readInt64:&ctime_sec from:is error:error]
&& [IntegerIO readInt64:&ctime_nsec from:is error:error]
&& [IntegerIO readInt64:&createTime_sec from:is error:error]
&& [IntegerIO readInt64:&createTime_nsec from:is error:error]
&& [IntegerIO readInt64:&st_blocks from:is error:error]
&& [IntegerIO readUInt32:&st_blksize from:is error:error];
[thumbnailSHA1 retain];
[previewSHA1 retain];
[xattrsSHA1 retain];
[aclSHA1 retain];
[finderFileType retain];
[finderFileCreator retain];
} while(0);
if (!ret) {
[self release];
self = nil;
return nil;
}
}
return self;
}
- (void)dealloc {
[dataSHA1s release];
[thumbnailSHA1 release];
[previewSHA1 release];
[xattrsSHA1 release];
[aclSHA1 release];
[finderFileType release];
[finderFileCreator release];
[super dealloc];
}
- (void)writeToData:(NSMutableData *)data {
[BooleanIO write:isTree to:data];
[IntegerIO writeInt32:(int32_t)[dataSHA1s count] to:data];
for (NSString *dataSHA1 in dataSHA1s) {
[StringIO write:dataSHA1 to:data];
}
[IntegerIO writeUInt64:dataSize to:data];
[StringIO write:thumbnailSHA1 to:data];
[StringIO write:previewSHA1 to:data];
[StringIO write:xattrsSHA1 to:data];
[IntegerIO writeUInt64:xattrsSize to:data];
[StringIO write:aclSHA1 to:data];
[IntegerIO writeInt32:uid to:data];
[IntegerIO writeInt32:gid to:data];
[IntegerIO writeInt32:mode to:data];
[IntegerIO writeInt64:mtime_sec to:data];
[IntegerIO writeInt64:mtime_nsec to:data];
[IntegerIO writeInt64:flags to:data];
[IntegerIO writeInt32:finderFlags to:data];
[IntegerIO writeInt32:extendedFinderFlags to:data];
[StringIO write:finderFileType to:data];
[StringIO write:finderFileCreator to:data];
[BooleanIO write:isFileExtensionHidden to:data];
[IntegerIO writeInt32:st_dev to:data];
[IntegerIO writeInt32:st_ino to:data];
[IntegerIO writeUInt32:st_nlink to:data];
[IntegerIO writeInt32:st_rdev to:data];
[IntegerIO writeInt64:ctime_sec to:data];
[IntegerIO writeInt64:ctime_nsec to:data];
[IntegerIO writeInt64:createTime_sec to:data];
[IntegerIO writeInt64:createTime_nsec to:data];
[IntegerIO writeInt64:st_blocks to:data];
[IntegerIO writeUInt32:st_blksize to:data];
}
- (BOOL)dataMatchesStatData:(struct stat *)st {
return (st->st_mtimespec.tv_sec == mtime_sec && st->st_mtimespec.tv_nsec == mtime_nsec & st->st_size == dataSize);
}
- (NSString *)treeSHA1 {
NSAssert(isTree, @"must be a Tree");
return [dataSHA1s objectAtIndex:0];
}
- (NSArray *)dataSHA1s {
return dataSHA1s;
}
@end

47
PackIndexEntry.h Normal file
View file

@ -0,0 +1,47 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
@interface PackIndexEntry : NSObject {
NSString *packSHA1;
unsigned long long offset;
unsigned long long dataLength;
NSString *objectSHA1;
}
- (id)initWithPackSHA1:(NSString *)thePackSHA1 offset:(unsigned long long)theOffset dataLength:(unsigned long long)theDataLength objectSHA1:(NSString *)theObjectSHA1;
- (NSString *)packSHA1;
- (unsigned long long)offset;
- (unsigned long long)dataLength;
- (NSString *)objectSHA1;
@end

63
PackIndexEntry.m Normal file
View file

@ -0,0 +1,63 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "PackIndexEntry.h"
@implementation PackIndexEntry
- (id)initWithPackSHA1:(NSString *)thePackSHA1 offset:(unsigned long long)theOffset dataLength:(unsigned long long)theDataLength objectSHA1:(NSString *)theObjectSHA1 {
if (self = [super init]) {
packSHA1 = [thePackSHA1 copy];
offset = theOffset;
dataLength = theDataLength;
objectSHA1 = [theObjectSHA1 copy];
}
return self;
}
- (void)dealloc {
[packSHA1 release];
[objectSHA1 release];
[super dealloc];
}
- (NSString *)packSHA1 {
return packSHA1;
}
- (unsigned long long)offset {
return offset;
}
- (unsigned long long)dataLength {
return dataLength;
}
- (NSString *)objectSHA1 {
return objectSHA1;
}
@end

66
PackSet.h Normal file
View file

@ -0,0 +1,66 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
@class Blob;
@class S3Service;
@class ServerBlob;
@interface PackSet : NSObject {
NSString *packSetName;
NSString *escapedPackSetName;
NSString *packSetDir;
S3Service *s3;
NSString *s3BucketName;
NSString *computerUUID;
BOOL keepPacksLocal;
NSMutableSet *packSHA1s;
NSString *currentPackSHA1;
NSMutableDictionary *packIndexEntries;
}
+ (NSString *)errorDomain;
+ (unsigned long long)maxPackFileSizeMB;
+ (unsigned long long)maxPackItemSizeBytes;
+ (NSString *)s3PathWithS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName;
+ (NSString *)localPathWithComputerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName;
- (id)initWithName:(NSString *)thePackSetName
s3Service:(S3Service *)theS3
s3BucketName:(NSString *)theS3BucketName
computerUUID:(NSString *)theComputerUUID
keepPacksLocal:(BOOL)isKeepPacksLocal
packSHA1s:(NSArray *)thePackSHA1s
error:(NSError **)error;
- (NSString *)name;
- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error;
- (BOOL)containsBlobForSHA1:(NSString *)sha1;
@end

200
PackSet.m Normal file
View file

@ -0,0 +1,200 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "PackSet.h"
#import "S3Service.h"
#import "SHA1Hash.h"
#import "S3ObjectReceiver.h"
#import "SetNSError.h"
#import "DataInputStream.h"
#import "PackSetSet.h"
#import "NSData-InputStream.h"
#import "ServerBlob.h"
#import "NSErrorCodes.h"
#import "DiskPackIndex.h"
#import "PackIndexEntry.h"
#import "DiskPack.h"
#import "RegexKitLite.h"
#import "HTTP.h"
static unsigned long long DEFAULT_MAX_PACK_FILE_SIZE_MB = 10;
static unsigned long long DEFAULT_MAX_PACK_ITEM_SIZE_BYTES = 65536;
static double DEFAULT_MAX_REUSABLE_PACK_FILE_SIZE_FRACTION = 0.6;
@interface PackSet (internal)
+ (unsigned long long)maxReusablePackFileSizeBytes;
- (BOOL)loadPackIndexEntries:(NSString *)packSHA1 totalDataSize:(unsigned long long *)totalDataSize error:(NSError **)error;
- (PackIndexEntry *)packIndexEntryForObjectSHA1:(NSString *)objectSHA1 error:(NSError **)error;
- (PackIndexEntry *)packIndexEntryForObjectSHA1:(NSString *)objectSHA1 inPackSHA1:(NSString *)packSHA1 error:(NSError **)error;
@end
@implementation PackSet
+ (NSString *)errorDomain {
return @"PackSetErrorDomain";
}
+ (unsigned long long)maxPackFileSizeMB {
return DEFAULT_MAX_PACK_FILE_SIZE_MB;
}
+ (unsigned long long)maxPackItemSizeBytes {
return DEFAULT_MAX_PACK_ITEM_SIZE_BYTES;
}
+ (NSString *)s3PathWithS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName {
return [NSString stringWithFormat:@"%@/%@", [PackSetSet s3PathWithS3BucketName:theS3BucketName computerUUID:theComputerUUID], [thePackSetName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
}
+ (NSString *)localPathWithComputerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName {
return [NSString stringWithFormat:@"%@/%@", [PackSetSet localPathWithComputerUUID:theComputerUUID], [thePackSetName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
}
- (id)initWithName:(NSString *)thePackSetName
s3Service:(S3Service *)theS3
s3BucketName:(NSString *)theS3BucketName
computerUUID:(NSString *)theComputerUUID
keepPacksLocal:(BOOL)isKeepPacksLocal
packSHA1s:(NSArray *)thePackSHA1s
error:(NSError **)error {
if (self = [super init]) {
packSetName = [thePackSetName copy];
s3 = [theS3 retain];
s3BucketName = [theS3BucketName copy];
computerUUID = [theComputerUUID copy];
keepPacksLocal = isKeepPacksLocal;
escapedPackSetName = [[thePackSetName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] copy];
packSetDir = [[PackSet localPathWithComputerUUID:theComputerUUID packSetName:packSetName] retain];
packSHA1s = [[NSMutableSet alloc] initWithArray:thePackSHA1s];
packIndexEntries = [[NSMutableDictionary alloc] init];
for (NSString *packSHA1 in packSHA1s) {
unsigned long long totalDataSize = 0;
if (![self loadPackIndexEntries:packSHA1 totalDataSize:&totalDataSize error:error]) {
[self release];
return nil;
}
if (totalDataSize < [PackSet maxReusablePackFileSizeBytes] && currentPackSHA1 == nil) {
currentPackSHA1 = [packSHA1 copy];
}
}
}
return self;
}
- (void)dealloc {
[packSetName release];
[escapedPackSetName release];
[packSetDir release];
[s3 release];
[s3BucketName release];
[computerUUID release];
[packSHA1s release];
[packIndexEntries release];
[currentPackSHA1 release];
[super dealloc];
}
- (NSString *)name {
return packSetName;
}
- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error {
HSLogTrace(@"packset %@ looking for SHA1 %@", packSetName, sha1);
NSError *myError = nil;
PackIndexEntry *entry = [self packIndexEntryForObjectSHA1:sha1 error:&myError];
if (entry == nil && [myError code] != ERROR_NOT_FOUND) {
if (error != NULL) {
*error = myError;
}
HSLogError(@"error reading pack index entry for %@ from pack set %@: %@", sha1, packSetName, [myError localizedDescription]);
return nil;
}
if (entry != nil) {
NSError *myError = nil;
DiskPack *diskPack = [[DiskPack alloc] initWithS3Service:s3 s3BucketName:s3BucketName computerUUID:computerUUID packSetName:packSetName packSHA1:[entry packSHA1]];
if (![diskPack makeLocal:&myError]) {
[diskPack release];
if ([[myError domain] isEqualToString:[S3Service errorDomain]] && [myError code] == HTTP_NOT_FOUND) {
SETNSERROR(@"PackSetErrorDomain", ERROR_NOT_FOUND, @"pack %@ not found in S3: %@", [entry packSHA1], [myError localizedDescription]);
} else if (error != NULL) {
*error = myError;
}
return nil;
}
ServerBlob *sb = [diskPack newServerBlobForObjectAtOffset:[entry offset] error:error];
[diskPack release];
return sb;
}
SETNSERROR(@"PackErrorDomain", ERROR_NOT_FOUND, @"sha1 %@ not found", sha1);
return nil;
}
- (BOOL)containsBlobForSHA1:(NSString *)sha1 {
return [packIndexEntries objectForKey:sha1] != nil;
}
@end
@implementation PackSet (internal)
+ (unsigned long long)maxReusablePackFileSizeBytes {
return (unsigned long long)((double)([PackSet maxPackFileSizeMB] * 1000000) * DEFAULT_MAX_REUSABLE_PACK_FILE_SIZE_FRACTION);
}
- (PackIndexEntry *)packIndexEntryForObjectSHA1:(NSString *)objectSHA1 error:(NSError **)error {
PackIndexEntry *pie = [packIndexEntries objectForKey:objectSHA1];
if (pie == nil) {
SETNSERROR(@"PackErrorDomain", ERROR_NOT_FOUND, @"pie not found for %@", objectSHA1);
}
return pie;
}
- (PackIndexEntry *)packIndexEntryForObjectSHA1:(NSString *)objectSHA1 inPackSHA1:(NSString *)packSHA1 error:(NSError **)error {
PackIndexEntry *pie = [packIndexEntries objectForKey:objectSHA1];
if (pie == nil) {
SETNSERROR(@"PackErrorDomain", ERROR_NOT_FOUND, @"pie not found for %@", objectSHA1);
}
return pie;
}
- (BOOL)loadPackIndexEntries:(NSString *)packSHA1 totalDataSize:(unsigned long long *)totalDataSize error:(NSError **)error {
*totalDataSize = 0;
DiskPackIndex *index = [[DiskPackIndex alloc] initWithS3Service:s3 s3BucketName:s3BucketName computerUUID:computerUUID packSetName:packSetName packSHA1:packSHA1];
BOOL ret = NO;
do {
if (![index makeLocal:error]) {
break;
}
NSArray *pies = [index allPackIndexEntries:error];
if (pies == nil) {
break;
}
for (PackIndexEntry *pie in pies) {
[packIndexEntries setObject:pie forKey:[pie objectSHA1]];
unsigned long long dataEndOffset = [pie offset] + [pie dataLength];
if (dataEndOffset > *totalDataSize) {
*totalDataSize = dataEndOffset;
}
}
ret = YES;
} while (0);
[index release];
return ret;
}
@end

54
PackSetSet.h Normal file
View file

@ -0,0 +1,54 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
@class ServerBlob;
@class S3Service;
@class Blob;
@interface PackSetSet : NSObject {
S3Service *s3;
NSString *s3BucketName;
NSString *computerUUID;
NSMutableDictionary *packSets;
}
+ (NSString *)s3PathWithS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID;
+ (NSString *)localPathWithComputerUUID:(NSString *)computerUUID;
- (id)initWithS3Service:(S3Service *)theS3
s3BucketName:(NSString *)theS3BucketName
computerUUID:(NSString *)theComputerUUID;
- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName error:(NSError **)error;
- (BOOL)containsBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName;
// Sync local cache files to S3 data; reload PackIndexEntries from local cache files.
- (BOOL)resetFromS3:(NSError **)error;
@end

224
PackSetSet.m Normal file
View file

@ -0,0 +1,224 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "PackSetSet.h"
#import "PackSet.h"
#import "SetNSError.h"
#import "NSErrorCodes.h"
#import "S3Service.h"
#import "ArqUserLibrary.h"
#import "RegexKitLite.h"
@interface PackSetSet (internal)
- (NSDictionary *)packSHA1sByPackSetNameFromS3:(NSError **)error;
- (NSMutableSet *)diskPackSetNames:(NSError **)error;
- (PackSet *)packSetForName:(NSString *)packSetName error:(NSError **)error;
- (NSArray *)cachedPackSHA1sForPackSet:(NSString *)packSetName error:(NSError **)error;
@end
@implementation PackSetSet
+ (NSString *)s3PathWithS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID {
return [NSString stringWithFormat:@"/%@/%@/packsets", theS3BucketName, theComputerUUID];
}
+ (NSString *)localPathWithComputerUUID:(NSString *)computerUUID {
return [NSString stringWithFormat:@"%@/%@/packsets", [ArqUserLibrary arqCachesPath], computerUUID];
}
- (id)initWithS3Service:(S3Service *)theS3
s3BucketName:(NSString *)theS3BucketName
computerUUID:(NSString *)theComputerUUID {
if (self = [super init]) {
s3 = [theS3 retain];
s3BucketName = [theS3BucketName copy];
computerUUID = [theComputerUUID copy];
packSets = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)dealloc {
[s3 release];
[s3BucketName release];
[computerUUID release];
[packSets release];
[super dealloc];
}
- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName error:(NSError **)error {
PackSet *packSet = [self packSetForName:packSetName error:error];
if (packSet == nil) {
return nil;
}
return [packSet newServerBlobForSHA1:sha1 error:error];
}
- (BOOL)containsBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName {
BOOL contains = [[packSets objectForKey:packSetName] containsBlobForSHA1:sha1];
return contains;
}
- (BOOL)resetFromS3:(NSError **)error {
HSLogDebug(@"resetting pack sets from S3");
[packSets removeAllObjects];
NSDictionary *s3PackSHA1sByPackSetName = [self packSHA1sByPackSetNameFromS3:error];
if (s3PackSHA1sByPackSetName == nil) {
return NO;
}
//
// Remove disk pack sets that don't exist in S3.
//
NSMutableSet *diskPackSetNames = [self diskPackSetNames:error];
if (diskPackSetNames == nil) {
return NO;
}
NSMutableSet *s3PackSetNames = [NSMutableSet setWithArray:[s3PackSHA1sByPackSetName allKeys]];
[diskPackSetNames minusSet:s3PackSetNames];
for (NSString *bogusDiskPackSetName in diskPackSetNames) {
NSString *packSetPath = [PackSet localPathWithComputerUUID:computerUUID packSetName:bogusDiskPackSetName];
HSLogDebug(@"removing local pack set that doesn't exist in S3: %@", packSetPath);
if (![[NSFileManager defaultManager] removeItemAtPath:packSetPath error:error]) {
return NO;
}
}
//
// Create PackSets, make index files local, and load PackIndexEntries into memory.
//
for (NSString *s3PackSetName in [s3PackSHA1sByPackSetName allKeys]) {
NSArray *packSHA1s = [s3PackSHA1sByPackSetName objectForKey:s3PackSetName];
PackSet *packSet = [[[PackSet alloc] initWithName:s3PackSetName
s3Service:s3
s3BucketName:s3BucketName
computerUUID:computerUUID
keepPacksLocal:[s3PackSetName hasSuffix:@"-trees"]
packSHA1s:packSHA1s error:error] autorelease];
if (packSet == nil) {
return NO;
}
[packSets setObject:packSet forKey:s3PackSetName];
}
return YES;
}
@end
@implementation PackSetSet (internal)
- (NSDictionary *)packSHA1sByPackSetNameFromS3:(NSError **)error {
NSMutableDictionary *packSHA1sByPackSetName = [NSMutableDictionary dictionary];
NSString *packSetPrefix = [PackSet s3PathWithS3BucketName:s3BucketName computerUUID:computerUUID packSetName:@""];
NSArray *s3Paths = [s3 pathsWithPrefix:packSetPrefix error:error];
if (s3Paths == nil) {
return nil;
}
// Format: /<s3bucketname>/<computeruuid>/packsets/<packsetname>/<sha1>.pack
NSString *pattern = [NSString stringWithFormat:@"^%@([^/]+)/(.+)\\.pack$", packSetPrefix];
for (NSString *s3Path in s3Paths) {
NSRange packSetNameRange = [s3Path rangeOfRegex:pattern capture:1];
NSRange sha1Range = [s3Path rangeOfRegex:pattern capture:2];
if (packSetNameRange.location != NSNotFound && sha1Range.location != NSNotFound) {
NSString *escapedPackSetName = [s3Path substringWithRange:packSetNameRange];
NSString *packSetName = [escapedPackSetName stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *packSHA1 = [s3Path substringWithRange:sha1Range];
NSMutableArray *packSHA1s = [packSHA1sByPackSetName objectForKey:packSetName];
if (packSHA1s == nil) {
packSHA1s = [NSMutableArray array];
[packSHA1sByPackSetName setObject:packSHA1s forKey:packSetName];
}
[packSHA1s addObject:packSHA1];
}
}
return packSHA1sByPackSetName;
}
- (NSMutableSet *)diskPackSetNames:(NSError **)error {
NSMutableSet *diskPackSetNames = [NSMutableSet set];
NSString *packSetsDir = [PackSetSet localPathWithComputerUUID:computerUUID];
if ([[NSFileManager defaultManager] fileExistsAtPath:packSetsDir]) {
NSArray *packSetNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:packSetsDir error:error];
if (packSetNames == nil) {
return nil;
}
for (NSString *packSetName in packSetNames) {
if (![packSetName hasPrefix:@"."]) {
[diskPackSetNames addObject:packSetName];
}
}
}
return diskPackSetNames;
}
- (PackSet *)packSetForName:(NSString *)packSetName error:(NSError **)error {
PackSet *packSet = [packSets objectForKey:packSetName];
if (packSet == nil) {
NSError *myError;
NSArray *packSHA1s = [self cachedPackSHA1sForPackSet:packSetName error:&myError];
if (packSHA1s == nil) {
HSLogError(@"error reading cached pack sets: %@", [myError localizedDescription]);
packSHA1s = [NSArray array];
}
packSet = [[PackSet alloc] initWithName:packSetName
s3Service:s3
s3BucketName:s3BucketName
computerUUID:computerUUID
keepPacksLocal:[packSetName hasSuffix:@"-trees"]
packSHA1s:packSHA1s
error:error];
if (packSet == nil) {
return nil;
}
[packSets setObject:packSet forKey:packSetName];
[packSet release];
}
return packSet;
}
- (NSArray *)cachedPackSHA1sForPackSet:(NSString *)packSetName error:(NSError **)error {
NSString *packSetDir = [PackSet localPathWithComputerUUID:computerUUID packSetName:packSetName];
NSMutableArray *ret = [NSMutableArray array];
BOOL isDir = NO;
if ([[NSFileManager defaultManager] fileExistsAtPath:packSetDir isDirectory:&isDir] && isDir) {
NSArray *dirNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:packSetDir error:error];
if (dirNames == nil) {
return nil;
}
for (NSString *dirName in dirNames) {
NSString *dir = [packSetDir stringByAppendingPathComponent:dirName];
if ([[NSFileManager defaultManager] fileExistsAtPath:dir isDirectory:&isDir] && isDir) {
NSArray *fileNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dir error:error];
if (fileNames == nil) {
return nil;
}
for (NSString *fileName in fileNames) {
NSRange sha1Range = [fileName rangeOfRegex:@"^(.+)\\.index$" capture:1];
if (sha1Range.location != NSNotFound) {
NSString *sha1 = [dirName stringByAppendingString:[fileName substringWithRange:sha1Range]];
[ret addObject:sha1];
}
}
}
}
}
return ret;
}
@end

48
RestoreNode.h Normal file
View file

@ -0,0 +1,48 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "InputStream.h"
#import "OutputStream.h"
@class Tree;
@class Node;
@interface RestoreNode : NSObject {
Tree *tree;
NSString *nodeName;
NSString *relativePath;
}
- (id)initWithTree:(Tree *)theTree nodeName:(NSString *)theNodeName relativePath:(NSString *)theRelativePath;
- (Tree *)tree;
- (Node *)node;
- (NSString *)relativePath;
@end

63
RestoreNode.m Normal file
View file

@ -0,0 +1,63 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "RestoreNode.h"
#import "Tree.h"
#import "Node.h"
#import "StringIO.h"
@implementation RestoreNode
- (id)initWithTree:(Tree *)theTree nodeName:(NSString *)theNodeName relativePath:(NSString *)theRelativePath {
if (self = [super init]) {
tree = [theTree retain];
nodeName = [theNodeName copy];
NSAssert((nodeName == nil) || ([tree childNodeWithName:nodeName] != nil), @"node doesn't exist in Tree!");
relativePath = [theRelativePath copy];
}
return self;
}
- (void)dealloc {
[tree release];
[nodeName release];
[relativePath release];
[super dealloc];
}
- (Tree *)tree {
return tree;
}
- (Node *)node {
return [tree childNodeWithName:nodeName];
}
- (NSString *)relativePath {
return relativePath;
}
@end

48
Restorer.h Normal file
View file

@ -0,0 +1,48 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
@class S3Service;
@class S3Fark;
@class S3Repo;
@interface Restorer : NSObject {
S3Fark *fark;
S3Repo *repo;
NSString *bucketName;
NSString *rootPath;
NSMutableArray *restoreNodes;
NSMutableDictionary *hardlinks;
}
- (id)initWithS3Service:(S3Service *)theS3 s3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID bucketUUID:(NSString *)theBucketUUID bucketName:(NSString *)theBucketName encryptionKey:(NSString *)theEncryptionKey;
- (BOOL)restore:(NSError **)error;
@end

467
Restorer.m Normal file
View file

@ -0,0 +1,467 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/stat.h>
#import "Restorer.h"
#import "S3Fark.h"
#import "S3Repo.h"
#import "SetNSError.h"
#import "Tree.h"
#import "Node.h"
#import "RestoreNode.h"
#import "FileAttributes.h"
#import "NSData-InputStream.h"
#import "DataInputStream.h"
#import "XAttrSet.h"
#import "FileOutputStream.h"
#import "NSFileManager_extra.h"
#import "CFStreamPair.h"
#import "NSErrorCodes.h"
@interface Restorer (internal)
- (BOOL)addRestoreNodesForTreeSHA1:(NSString *)treeSHA1 relativePath:(NSString *)relativePath error:(NSError **)error;
- (BOOL)restoreRestoreNode:(RestoreNode *)rn error:(NSError **)error;
- (BOOL)createFile:(Node *)node atPath:(NSString *)path error:(NSError **)error;
- (BOOL)createFileAtPath:(NSString *)path fromSHA1s:(NSArray *)dataSHA1s error:(NSError **)error;
- (BOOL)createFileOnceAtPath:(NSString *)path fromSHA1s:(NSArray *)dataSHA1s error:(NSError **)error;
- (BOOL)appendBlobForSHA1:(NSString *)sha1 toFile:(FileOutputStream *)fos error:(NSError **)error;
- (BOOL)applyTree:(Tree *)tree toPath:(NSString *)restorePath error:(NSError **)error;
- (BOOL)applyNode:(Node *)node toPath:(NSString *)restorePath error:(NSError **)error;
- (BOOL)applyACLSHA1:(NSString *)aclSHA1 toFileAttributes:(FileAttributes *)fa error:(NSError **)error;
- (BOOL)applyXAttrsSHA1:(NSString *)xattrsSHA1 toFile:(NSString *)path error:(NSError **)error;
- (BOOL)createSymLink:(Node *)node path:(NSString *)symLinkFile target:(NSString *)target error:(NSError **)error;
@end
@implementation Restorer
- (id)initWithS3Service:(S3Service *)theS3 s3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID bucketUUID:(NSString *)theBucketUUID bucketName:(NSString *)theBucketName encryptionKey:(NSString *)theEncryptionKey {
if (self = [super init]) {
fark = [[S3Fark alloc] initWithS3Service:theS3 s3BucketName:theS3BucketName computerUUID:theComputerUUID];
repo = [[S3Repo alloc] initWithS3Service:theS3 s3BucketName:theS3BucketName computerUUID:theComputerUUID bucketUUID:theBucketUUID encrypted:YES encryptionKey:theEncryptionKey fark:fark ensureCacheIntegrity:NO];
bucketName = [theBucketName copy];
rootPath = [[[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:theBucketName] copy];
restoreNodes = [[NSMutableArray alloc] init];
hardlinks = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)dealloc {
[fark release];
[repo release];
[bucketName release];
[rootPath release];
[restoreNodes release];
[hardlinks release];
[super dealloc];
}
- (BOOL)restore:(NSError **)error {
if ([[NSFileManager defaultManager] fileExistsAtPath:rootPath]) {
SETNSERROR(@"RestorerErrorDomain", -1, @"%@ already exists", rootPath);
return NO;
}
if (![fark reloadPacksFromS3:error]) {
return NO;
}
if (![[NSFileManager defaultManager] createDirectoryAtPath:rootPath withIntermediateDirectories:YES attributes:nil error:error]) {
HSLogError(@"failed to create directory %@", rootPath);
return NO;
}
NSString *headSHA1 = nil;
if (![repo localHeadSHA1:&headSHA1 error:error]) {
return NO;
}
if (headSHA1 == nil) {
SETNSERROR(@"RestorerErrorDomain", -1, @"no backup found");
return NO;
}
Commit *head = nil;
if (![repo commit:&head forSHA1:headSHA1 error:error]) {
return NO;
}
if (![self addRestoreNodesForTreeSHA1:[head treeSHA1] relativePath:@"" error:error]) {
return NO;
}
for (RestoreNode *rn in restoreNodes) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSError *myError = nil;
BOOL ret = [self restoreRestoreNode:rn error:&myError];
[myError retain];
[pool drain];
[myError autorelease];
if (!ret) {
if (error != NULL) {
*error = myError;
}
return NO;
}
}
return YES;
}
@end
@implementation Restorer (internal)
- (BOOL)addRestoreNodesForTreeSHA1:(NSString *)treeSHA1 relativePath:(NSString *)relativePath error:(NSError **)error {
Tree *tree = nil;
if (![repo tree:&tree forSHA1:treeSHA1 error:error]) {
return NO;
}
RestoreNode *treeRN = [[RestoreNode alloc] initWithTree:tree nodeName:nil relativePath:relativePath];
[restoreNodes addObject:treeRN];
[treeRN release];
for (NSString *childNodeName in [tree childNodeNames]) {
Node *childNode = [tree childNodeWithName:childNodeName];
NSString *childRelativePath = [NSString stringWithFormat:@"%@/%@", relativePath, childNodeName];
if ([childNode isTree]) {
if (![self addRestoreNodesForTreeSHA1:[childNode treeSHA1] relativePath:childRelativePath error:error]) {
return NO;
}
} else {
RestoreNode *childRN = [[RestoreNode alloc] initWithTree:tree nodeName:childNodeName relativePath:childRelativePath];
[restoreNodes addObject:childRN];
[childRN release];
}
}
return YES;
}
- (BOOL)restoreRestoreNode:(RestoreNode *)rn error:(NSError **)error {
printf("restoring %s%s\n", [bucketName UTF8String], [[rn relativePath] UTF8String]);
NSString *restorePath = [rootPath stringByAppendingPathComponent:[rn relativePath]];
NSString *parentPath = [restorePath stringByDeletingLastPathComponent];
if (![[NSFileManager defaultManager] fileExistsAtPath:parentPath]
&& ![[NSFileManager defaultManager] createDirectoryAtPath:parentPath withIntermediateDirectories:YES attributes:nil error:error]) {
HSLogError(@"failed to create directory %@", parentPath);
return NO;
}
BOOL createdFile = NO;
int nlink = [rn node] == nil ? [[rn tree] st_nlink] : [[rn node] st_nlink];
if (nlink > 1) {
int ino = [rn node] == nil ? [[rn tree] st_ino] : [[rn node] st_ino];
NSNumber *inode = [NSNumber numberWithInt:ino];
RestoreNode *existing = [hardlinks objectForKey:inode];
if (existing != nil) {
// Link.
NSString *existingPath = [rootPath stringByAppendingPathComponent:[existing relativePath]];
if (([existing node] == nil) != ([rn node] == nil)) {
SETNSERROR(@"RestoreErrorDomain", -1, @"cannot link a directory to a file");
HSLogError(@"can't link directory to a file");
return NO;
}
if (link([existingPath fileSystemRepresentation], [restorePath fileSystemRepresentation]) == -1) {
SETNSERROR(@"RestoreErrorDomain", -1, @"link(%@,%@): %s", existingPath, restorePath, strerror(errno));
HSLogError(@"link() failed");
return NO;
}
createdFile = YES;
} else {
[hardlinks setObject:rn forKey:inode];
}
}
if (!createdFile) {
Node *node = [rn node];
if (node == nil) {
Tree *tree = [rn tree];
if (![[NSFileManager defaultManager] fileExistsAtPath:restorePath] && ![[NSFileManager defaultManager] createDirectoryAtPath:restorePath withIntermediateDirectories:NO attributes:nil error:error]) {
HSLogError(@"error creating %@", restorePath);
return NO;
}
if (![self applyTree:tree toPath:restorePath error:error]) {
HSLogError(@"applyTree error");
return NO;
}
} else {
int mode = [node mode];
BOOL isFifo = (mode & S_IFIFO) == S_IFIFO;
if (isFifo) {
if (mkfifo([restorePath fileSystemRepresentation], mode) == -1) {
SETNSERROR(@"RestoreErrorDomain", errno, @"mkfifo(%@): %s", restorePath, strerror(errno));
return NO;
}
if (![self applyNode:node toPath:restorePath error:error]) {
HSLogError(@"applyNode error");
return NO;
}
} else if ((mode & S_IFSOCK) == S_IFSOCK) {
// Skip socket -- restoring it doesn't make any sense.
} else if ((mode & S_IFREG) == 0 && ((mode & S_IFCHR) == S_IFCHR || (mode & S_IFBLK) == S_IFBLK)) {
if (![[NSFileManager defaultManager] ensureParentPathExistsForPath:restorePath error:error]) {
return NO;
}
if (mknod([restorePath fileSystemRepresentation], mode, [node st_rdev]) == -1) {
SETNSERROR(@"RestorerErrorDomain", -1, @"mknod(%@): %s", restorePath, strerror(errno));
return NO;
}
} else {
if (![self createFile:node atPath:restorePath error:error]) {
HSLogError(@"createFile error");
return NO;
}
if (![self applyNode:node toPath:restorePath error:error]) {
HSLogError(@"applyNode error");
return NO;
}
}
}
FileAttributes *fa = [[[FileAttributes alloc] initWithPath:restorePath error:error] autorelease];
if (fa == nil) {
return NO;
}
int flags = [fa flags];
if (flags) {
// Clear the flags temporarily so we can change ownership of the file.
if (![fa applyFlags:0 error:error]) {
return NO;
}
}
int uid = [rn node] == nil ? [[rn tree] uid] : [[rn node] uid];
int gid = [rn node] == nil ? [[rn tree] gid] : [[rn node] gid];
NSError *chownError;
if (![fa applyUID:uid gid:gid error:&chownError]) {
fprintf(stderr, "error applying UID and GID to %s: %s\n", [restorePath fileSystemRepresentation], [[chownError localizedDescription] UTF8String]);
}
if (flags) {
if (![fa applyFlags:flags error:error]) {
return NO;
}
}
}
return YES;
}
- (BOOL)applyTree:(Tree *)tree toPath:(NSString *)path error:(NSError **)error {
FileAttributes *fa = [[[FileAttributes alloc] initWithPath:path error:error] autorelease];
if (!fa) {
return NO;
}
if (![fa applyFinderFlags:[tree finderFlags] error:error]
|| ![fa applyExtendedFinderFlags:[tree extendedFinderFlags] error:error]) {
return NO;
}
if (![self applyACLSHA1:[tree aclSHA1] toFileAttributes:fa error:error]) {
return NO;
}
if (![self applyXAttrsSHA1:[tree xattrsSHA1] toFile:path error:error]) {
return NO;
}
if (([tree mode] & (S_ISUID|S_ISGID|S_ISVTX) != 0) && ![fa applyMode:[tree mode] error:error]) {
return NO;
}
if (([tree mode] & S_IFLNK) != S_IFLNK && [tree treeVersion] >= 7 && ![fa applyMTimeSec:tree.mtime_sec mTimeNSec:tree.mtime_nsec error:error]) {
return NO;
}
if (([tree treeVersion] >= 7) && ![fa applyCreateTimeSec:tree.createTime_sec createTimeNSec:tree.createTime_nsec error:error]) {
return NO;
}
if (![fa applyFlags:[tree flags] error:error]) {
return NO;
}
return YES;
}
- (BOOL)applyNode:(Node *)node toPath:(NSString *)path error:(NSError **)error {
FileAttributes *fa = [[[FileAttributes alloc] initWithPath:path error:error] autorelease];
if (!fa) {
return NO;
}
if (![self applyACLSHA1:[node aclSHA1] toFileAttributes:fa error:error]) {
return NO;
}
BOOL isFifo = ([node mode] & S_IFIFO) == S_IFIFO;
if (!isFifo) {
if (![fa applyFinderFlags:[node finderFlags] error:error]
|| ![fa applyExtendedFinderFlags:[node extendedFinderFlags] error:error]
|| ![self applyXAttrsSHA1:[node xattrsSHA1] toFile:path error:error]
|| ![fa applyFinderFileType:[node finderFileType] finderFileCreator:[node finderFileCreator] error:error]) {
return NO;
}
}
if (([node mode] & (S_ISUID|S_ISGID|S_ISVTX) != 0) && ![fa applyMode:[node mode] error:error]) {
return NO;
}
if (([node mode] & S_IFLNK) != S_IFLNK && [node treeVersion] >= 7 && ![fa applyMTimeSec:node.mtime_sec mTimeNSec:node.mtime_nsec error:error]) {
return NO;
}
if (([node treeVersion] >= 7) && ![fa applyCreateTimeSec:node.createTime_sec createTimeNSec:node.createTime_nsec error:error]) {
return NO;
}
if (!isFifo) {
if (![fa applyFlags:[node flags] error:error]) {
return NO;
}
}
return YES;
}
- (BOOL)createFile:(Node *)node atPath:(NSString *)path error:(NSError **)error {
if (![[NSFileManager defaultManager] ensureParentPathExistsForPath:path error:error]) {
HSLogError(@"error ensuring path %@ exists", path);
return NO;
}
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
if (![[NSFileManager defaultManager] removeItemAtPath:path error:error]) {
HSLogError(@"error removing existing file %@", path);
return NO;
}
}
HSLogTrace(@"%qu bytes -> %@", [node dataSize], path);
if (([node mode] & S_IFLNK) == S_IFLNK) {
NSData *data = [repo dataForSHA1s:[node dataSHA1s] error:error];
if (data == nil) {
HSLogError(@"error getting data for %@", [node dataSHA1s]);
return NO;
}
NSString *target = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
if (![self createSymLink:node path:path target:target error:error]) {
HSLogError(@"error creating sym link %@", path);
return NO;
}
} else if ([node dataSize] > 0) {
if (![self createFileAtPath:path fromSHA1s:[node dataSHA1s] error:error]) {
return NO;
}
} else {
// It's a zero-byte file.
int fd = open([path fileSystemRepresentation], O_CREAT|O_EXCL, [node mode]);
if (fd == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"%s: %@", strerror(errno), path);
HSLogError(@"error opening %@", path);
return NO;
}
close(fd);
}
return YES;
}
- (BOOL)createFileAtPath:(NSString *)path fromSHA1s:(NSArray *)dataSHA1s error:(NSError **)error {
BOOL ret = YES;
for (;;) {
NSError *myError = nil;
if (![self createFileOnceAtPath:path fromSHA1s:dataSHA1s error:&myError]) {
if ([[myError domain] isEqualToString:[CFStreamPair errorDomain]]) {
HSLogDebug(@"network error restoring %@ (retrying): %@", path, [myError localizedDescription]);
} else {
if (error != NULL) {
*error = myError;
}
ret = NO;
break;
}
} else {
break;
}
}
return ret;
}
- (BOOL)createFileOnceAtPath:(NSString *)path fromSHA1s:(NSArray *)dataSHA1s error:(NSError **)error {
FileOutputStream *fos = [[FileOutputStream alloc] initWithPath:path append:NO];
BOOL ret = YES;
for (NSString *sha1 in dataSHA1s) {
if (![self appendBlobForSHA1:sha1 toFile:fos error:error]) {
ret = NO;
break;
}
}
[fos release];
return ret;
}
- (BOOL)appendBlobForSHA1:(NSString *)sha1 toFile:(FileOutputStream *)fos error:(NSError **)error {
ServerBlob *dataBlob = [repo newServerBlobForSHA1:sha1 error:error];
if (dataBlob == nil) {
HSLogError(@"error getting server blob for %@", sha1);
return NO;
}
id <InputStream> is = [dataBlob newInputStream];
[dataBlob release];
BOOL ret = YES;
for (;;) {
NSUInteger received = 0;
NSError *myError = nil;
unsigned char *buf = [is read:&received error:&myError];
if (buf == nil) {
if ([myError code] != ERROR_EOF) {
ret = NO;
HSLogError(@"error reading from stream for blob %@: %@", sha1, [myError localizedDescription]);
if (error != NULL) {
*error = myError;
}
}
break;
}
if (![fos write:buf length:received error:error]) {
ret = NO;
break;
}
[NSThread sleepForTimeInterval:0.01];
}
[is release];
return ret;
}
- (BOOL)createSymLink:(Node *)node path:(NSString *)symLinkFile target:(NSString *)target error:(NSError **)error {
struct stat st;
if (lstat([symLinkFile fileSystemRepresentation], &st) == 0) {
if (![[NSFileManager defaultManager] removeItemAtPath:symLinkFile error:error]) {
return NO;
}
}
if (symlink([target fileSystemRepresentation], [symLinkFile fileSystemRepresentation]) == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"symlink(%@): %s", symLinkFile, strerror(errno));
return NO;
}
return YES;
}
- (BOOL)applyACLSHA1:(NSString *)aclSHA1 toFileAttributes:(FileAttributes *)fa error:(NSError **)error {
if (aclSHA1 != nil) {
NSData *data = [repo dataForSHA1:aclSHA1 error:error];
if (data == nil) {
return NO;
}
NSString *aclString = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
if (![fa applyAcl:aclString error:error]) {
return NO;
}
}
return YES;
}
- (BOOL)applyXAttrsSHA1:(NSString *)xattrsSHA1 toFile:(NSString *)path error:(NSError **)error {
if (xattrsSHA1 != nil) {
NSData *xattrsData = [repo dataForSHA1:xattrsSHA1 error:error];
if (xattrsData == nil) {
return NO;
}
DataInputStream *is = [xattrsData newInputStream];
XAttrSet *set = [[[XAttrSet alloc] initWithBufferedInputStream:is error:error] autorelease];
[is release];
if (!set) {
return NO;
}
if (![set applyToFile:path error:error]) {
return NO;
}
}
return YES;
}
@end

53
S3Fark.h Normal file
View file

@ -0,0 +1,53 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
@class S3Service;
@class Blob;
@class ServerBlob;
@class PackSetSet;
@interface S3Fark : NSObject {
S3Service *s3;
NSString *s3BucketName;
NSString *computerUUID;
NSThread *creatorThread;
PackSetSet *packSetSet;
}
- (id)initWithS3Service:(S3Service *)theS3
s3BucketName:(NSString *)theS3BucketName
computerUUID:(NSString *)theComputerUUID;
- (NSData *)dataForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName searchPackOnly:(BOOL)searchPackOnly error:(NSError **)error;
- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName searchPackOnly:(BOOL)searchPackOnly error:(NSError **)error;
- (BOOL)containsBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName searchPackOnly:(BOOL)searchPackOnly;
- (BOOL)reloadPacksFromS3:(NSError **)error;
@end

109
S3Fark.m Normal file
View file

@ -0,0 +1,109 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "S3Service.h"
#import "S3Fark.h"
#import "SetNSError.h"
#import "NSErrorCodes.h"
#import "PackSetSet.h"
#import "ServerBlob.h"
@interface S3Fark (internal)
- (NSString *)pathForSHA1:(NSString *)sha1;
@end
@implementation S3Fark
- (id)initWithS3Service:(S3Service *)theS3
s3BucketName:(NSString *)theS3BucketName
computerUUID:(NSString *)theComputerUUID {
if (self = [super init]) {
s3 = [theS3 retain];
s3BucketName = [theS3BucketName copy];
computerUUID = [theComputerUUID copy];
creatorThread = [[NSThread currentThread] retain];
packSetSet = [[PackSetSet alloc] initWithS3Service:s3 s3BucketName:s3BucketName computerUUID:computerUUID];
}
return self;
}
- (void)dealloc {
[s3 release];
[s3BucketName release];
[computerUUID release];
[creatorThread release];
[packSetSet release];
[super dealloc];
}
- (NSData *)dataForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName searchPackOnly:(BOOL)searchPackOnly error:(NSError **)error {
ServerBlob *sb = [self newServerBlobForSHA1:sha1 packSetName:packSetName searchPackOnly:searchPackOnly error:error];
if (sb == nil) {
return nil;
}
NSData *data = [sb slurp:error];
[sb release];
return data;
}
- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName searchPackOnly:(BOOL)searchPackOnly error:(NSError **)error {
NSAssert([NSThread currentThread] == creatorThread, @"must be on same thread!");
NSError *myError = nil;
ServerBlob *sb = [packSetSet newServerBlobForSHA1:sha1 packSetName:packSetName error:&myError];
if (sb == nil) {
if ([myError code] != ERROR_NOT_FOUND) {
HSLogError(@"error reading sha1 %@ from packSetSet: %@", sha1, [myError localizedDescription]);
}
if (error != NULL) {
*error = myError;
}
if (!searchPackOnly) {
sb = [s3 newServerBlobAtPath:[self pathForSHA1:sha1] error:error];
}
}
return sb;
}
- (BOOL)containsBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName searchPackOnly:(BOOL)searchPackOnly {
NSAssert([NSThread currentThread] == creatorThread, @"must be on same thread!");
BOOL contains = [packSetSet containsBlobForSHA1:sha1 packSetName:packSetName];
if (!contains && !searchPackOnly) {
contains = [s3 containsBlobAtPath:[self pathForSHA1:sha1]];
}
return contains;
}
- (BOOL)reloadPacksFromS3:(NSError **)error {
NSAssert([NSThread currentThread] == creatorThread, @"must be on same thread!");
return [packSetSet resetFromS3:error];
}
@end
@implementation S3Fark (internal)
- (NSString *)pathForSHA1:(NSString *)sha1 {
return [NSString stringWithFormat:@"/%@/%@/objects/%@", s3BucketName, computerUUID, sha1];
}
@end

83
S3Repo.h Normal file
View file

@ -0,0 +1,83 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
@class S3Service;
@class S3Fark;
@class Commit;
@class Tree;
@class Blob;
@class ServerBlob;
@interface S3Repo : NSObject {
S3Service *s3;
NSString *s3BucketName;
NSString *computerUUID;
NSString *bucketUUID;
S3Fark *fark;
BOOL encrypted;
NSString *encryptionKey;
BOOL ensureCacheIntegrity;
NSString *treesPackSetName;
NSString *blobsPackSetName;
}
+ (NSString *)errorDomain;
- (id)initWithS3Service:(S3Service *)theS3
s3BucketName:(NSString *)theS3BucketName
computerUUID:(NSString *)theComputerUUID
bucketUUID:(NSString *)theBucketUUID
encrypted:(BOOL)isEncrypted
encryptionKey:(NSString *)theEncryptionKey
fark:(S3Fark *)theFark
ensureCacheIntegrity:(BOOL)ensure;
- (BOOL)localHeadSHA1:(NSString **)localHeadSHA1 error:(NSError **)error;
// Returns NO if commit not found:
- (BOOL)commit:(Commit **)commit forSHA1:(NSString *)theSHA1 error:(NSError **)error;
// Returns NO if commit not found:
- (BOOL)tree:(Tree **)tree forSHA1:(NSString *)theSHA1 error:(NSError **)error;
- (BOOL)containsBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName searchPackOnly:(BOOL)searchPackOnly;
- (NSData *)dataForSHA1:(NSString *)sha1 error:(NSError **)error;
- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error;
- (NSData *)dataForSHA1s:(NSArray *)sha1s error:(NSError **)error;
- (BOOL)commonAncestorCommitSHA1:(NSString **)ancestorSHA1 forCommitSHA1:(NSString *)commit0SHA1 andCommitSHA1:(NSString *)commit1SHA1 error:(NSError **)error;
- (BOOL)is:(BOOL *)isAncestor commitSHA1:(NSString *)descendantSHA1 ancestorOfCommitSHA1:(NSString *)sha1 error:(NSError **)error;
- (NSString *)localHeadS3Path;
- (BOOL)isEncrypted;
- (NSString *)blobsPackSetName;
- (NSSet *)packSetNames;
@end

291
S3Repo.m Normal file
View file

@ -0,0 +1,291 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "ServerBlob.h"
#import "S3Repo.h"
#import "S3Service.h"
#import "S3Fark.h"
#import "SetNSError.h"
#import "Tree.h"
#import "Commit.h"
#import "NSData-Encrypt.h"
#import "DecryptedInputStream.h"
#import "InputStreams.h"
#import "NSErrorCodes.h"
#import "NSData-InputStream.h"
#import "DataInputStream.h"
#import "HTTP.h"
@interface S3Repo (internal)
- (NSData *)dataForSHA1:(NSString *)sha1 packSetName:(NSString *)thePackSetName error:(NSError **)error;
- (NSData *)dataForSHA1:(NSString *)sha1 packSetName:(NSString *)thePackSetName searchPackOnly:(BOOL)packOnly error:(NSError **)error;
- (BOOL)ancestorCommitSHA1sForCommitSHA1:(NSString *)commitSHA1 outArray:(NSMutableArray *)arr error:(NSError **)error;
- (BOOL)commitSHA1:(NSString **)sha1 fromSHA1:(NSString *)fromSHA1 before:(NSDate *)date error:(NSError **)error;
@end
static NSString *ERROR_DOMAIN = @"S3RepoErrorDomain";
@implementation S3Repo
+ (NSString *)errorDomain {
return ERROR_DOMAIN;
}
- (id)initWithS3Service:(S3Service *)theS3
s3BucketName:(NSString *)theS3BucketName
computerUUID:(NSString *)theComputerUUID
bucketUUID:(NSString *)theBucketUUID
encrypted:(BOOL)isEncrypted
encryptionKey:(NSString *)theEncryptionKey
fark:(S3Fark *)theFark
ensureCacheIntegrity:(BOOL)ensure {
if (self = [super init]) {
s3 = [theS3 retain];
s3BucketName = [theS3BucketName copy];
computerUUID = [theComputerUUID copy];
bucketUUID = [theBucketUUID copy];
encrypted = isEncrypted;
encryptionKey = [theEncryptionKey copy];
fark = [theFark retain];
ensureCacheIntegrity = ensure;
treesPackSetName = [[NSString alloc] initWithFormat:@"%@-trees", bucketUUID];
blobsPackSetName = [[NSString alloc] initWithFormat:@"%@-blobs", bucketUUID];
}
return self;
}
- (void)dealloc {
[s3 release];
[s3BucketName release];
[computerUUID release];
[bucketUUID release];
[encryptionKey release];
[fark release];
[treesPackSetName release];
[blobsPackSetName release];
[super dealloc];
}
- (BOOL)localHeadSHA1:(NSString **)headSHA1 error:(NSError **)error {
*headSHA1 = nil;
NSError *myError;
NSData *headSHA1Data = [s3 dataAtPath:[self localHeadS3Path] error:&myError];
if (headSHA1Data == nil && !([[myError domain] isEqualToString:[S3Service serverErrorDomain]] && [myError code] == HTTP_NOT_FOUND)) {
if (error != NULL) {
*error = myError;
}
return NO;
}
if (headSHA1Data != nil) {
*headSHA1 = [[[NSString alloc] initWithData:headSHA1Data encoding:NSUTF8StringEncoding] autorelease];
}
return YES;
}
- (BOOL)commit:(Commit **)commit forSHA1:(NSString *)theSHA1 error:(NSError **)error {
*commit = nil;
NSData *data = [self dataForSHA1:theSHA1 packSetName:treesPackSetName searchPackOnly:YES error:error];
if (data == nil) {
HSLogDebug(@"commit data not found for %@", theSHA1);
return NO;
}
id <BufferedInputStream> is = [data newInputStream];
*commit = [[[Commit alloc] initWithBufferedInputStream:is error:error] autorelease];
[is release];
if (*commit == nil) {
return NO;
}
return YES;
}
- (BOOL)tree:(Tree **)tree forSHA1:(NSString *)theSHA1 error:(NSError **)error {
*tree = nil;
NSData *data = [self dataForSHA1:theSHA1 packSetName:treesPackSetName searchPackOnly:YES error:error];
if (data == nil) {
HSLogDebug(@"tree data not found for %@", theSHA1);
return NO;
}
id <BufferedInputStream> is = [data newInputStream];
*tree = [[[Tree alloc] initWithBufferedInputStream:is error:error] autorelease];
[is release];
if (*tree == nil) {
return NO;
}
return YES;
}
- (BOOL)containsBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName searchPackOnly:(BOOL)searchPackOnly {
return [fark containsBlobForSHA1:sha1 packSetName:packSetName searchPackOnly:searchPackOnly];
}
- (NSData *)dataForSHA1:(NSString *)sha1 error:(NSError **)error {
NSData *data = [fark dataForSHA1:sha1 packSetName:treesPackSetName searchPackOnly:YES error:error];
if (data == nil) {
data = [fark dataForSHA1:sha1 packSetName:blobsPackSetName searchPackOnly:NO error:error];
}
if (data != nil && encrypted) {
data = [data decryptWithCipher:ARQ_DEFAULT_CIPHER_NAME key:encryptionKey error:error];
}
return data;
}
- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error {
ServerBlob *sb = [fark newServerBlobForSHA1:sha1 packSetName:treesPackSetName searchPackOnly:NO error:error];
if (sb == nil) {
sb = [fark newServerBlobForSHA1:sha1 packSetName:blobsPackSetName searchPackOnly:NO error:error];
}
if (sb != nil && encrypted) {
id <InputStream> is = [sb newInputStream];
NSString *mimeType = [sb mimeType];
NSString *downloadName = [sb downloadName];
[sb release];
DecryptedInputStream *dis = [[DecryptedInputStream alloc] initWithInputStream:is cipherName:ARQ_DEFAULT_CIPHER_NAME key:encryptionKey error:error];
[is release];
if (dis == nil) {
return NO;
}
sb = [[ServerBlob alloc] initWithInputStream:dis mimeType:mimeType downloadName:downloadName];
[dis release];
}
return sb;
}
- (NSData *)dataForSHA1s:(NSArray *)sha1s error:(NSError **)error {
NSMutableData *data = [NSMutableData data];
for (NSString *sha1 in sha1s) {
ServerBlob *sb = [self newServerBlobForSHA1:sha1 error:error];
if (sb == nil) {
return NO;
}
NSData *blobData = [sb slurp:error];
[sb release];
if (blobData == nil) {
return NO;
}
//FIXME: Get rid of this extra copying of data.
[data appendData:blobData];
}
return data;
}
- (BOOL)commonAncestorCommitSHA1:(NSString **)ancestorSHA1 forCommitSHA1:(NSString *)commit0SHA1 andCommitSHA1:(NSString *)commit1SHA1 error:(NSError **)error {
//FIXME: This is slow and memory-intensive!
NSMutableArray *commit0ParentSHA1s = [[[NSMutableArray alloc] initWithObjects:commit0SHA1, nil] autorelease];
NSMutableArray *commit1ParentSHA1s = [[[NSMutableArray alloc] initWithObjects:commit1SHA1, nil] autorelease];
if (![self ancestorCommitSHA1sForCommitSHA1:commit0SHA1 outArray:commit0ParentSHA1s error:error]) {
return NO;
}
if (![self ancestorCommitSHA1sForCommitSHA1:commit1SHA1 outArray:commit1ParentSHA1s error:error]) {
return NO;
}
for (NSString *parent in commit1ParentSHA1s) {
if ([commit0ParentSHA1s containsObject:parent]) {
*ancestorSHA1 = parent;
break;
}
}
return YES;
}
- (BOOL)is:(BOOL *)isAncestor commitSHA1:(NSString *)ancestorCommitSHA1 ancestorOfCommitSHA1:(NSString *)sha1 error:(NSError **)error {
*isAncestor = NO;
if ([ancestorCommitSHA1 isEqualToString:sha1]) {
return YES;
}
//TODO: Get rid of recursion in this method:
Commit *commit = nil;
if (![self commit:&commit forSHA1:sha1 error:error]) {
return NO;
}
for (NSString *parentCommitSHA1 in [commit parentCommitSHA1s]) {
if (![self is:isAncestor commitSHA1:parentCommitSHA1 ancestorOfCommitSHA1:ancestorCommitSHA1 error:error]) {
return NO;
}
if (*isAncestor) {
return YES;
}
}
return YES;
}
- (NSString *)localHeadS3Path {
return [NSString stringWithFormat:@"/%@/%@/bucketdata/%@/refs/heads/master", s3BucketName, computerUUID, bucketUUID];
}
- (BOOL)isEncrypted {
return encrypted;
}
- (NSString *)blobsPackSetName {
return blobsPackSetName;
}
- (NSSet *)packSetNames {
return [NSSet setWithObjects:blobsPackSetName, treesPackSetName, nil];
}
#pragma mark NSObject protocol
- (NSString *)description {
return [NSString stringWithFormat:@"<S3Repo %p bucket %@>", self, bucketUUID];
}
@end
@implementation S3Repo (internal)
- (NSData *)dataForSHA1:(NSString *)sha1 packSetName:(NSString *)thePackSetName error:(NSError **)error {
return [self dataForSHA1:sha1 packSetName:thePackSetName searchPackOnly:NO error:error];
}
- (NSData *)dataForSHA1:(NSString *)sha1 packSetName:(NSString *)thePackSetName searchPackOnly:(BOOL)packOnly error:(NSError **)error {
NSData *data = [fark dataForSHA1:sha1 packSetName:thePackSetName searchPackOnly:packOnly error:error];
if (data != nil && encrypted) {
data = [data decryptWithCipher:ARQ_DEFAULT_CIPHER_NAME key:encryptionKey error:error];
}
return data;
}
- (BOOL)ancestorCommitSHA1sForCommitSHA1:(NSString *)commitSHA1 outArray:(NSMutableArray *)arr error:(NSError **)error {
Commit *commit = nil;
if (![self commit:&commit forSHA1:commitSHA1 error:error]) {
return NO;
}
for (NSString *parentCommitSHA1 in [commit parentCommitSHA1s]) {
[arr addObject:parentCommitSHA1];
if (![self ancestorCommitSHA1sForCommitSHA1:parentCommitSHA1 outArray:arr error:error]) {
return NO;
}
}
return YES;
}
- (BOOL)commitSHA1:(NSString **)sha1 fromSHA1:(NSString *)fromSHA1 before:(NSDate *)date error:(NSError **)error {
HSLogDebug(@"looking for Commit before %@", [date description]);
*sha1 = nil;
for (;;) {
Commit *commit = nil;
if (![self commit:&commit forSHA1:fromSHA1 error:error]) {
return NO;
}
NSDate *creationDate = [commit creationDate];
if ([date earlierDate:creationDate] == creationDate) {
*sha1 = [[fromSHA1 retain] autorelease];
HSLogDebug(@"returning Commit SHA1 %@: creationDate=%@", *sha1, creationDate);
break;
}
if ([[commit parentCommitSHA1s] count] == 0) {
break;
}
fromSHA1 = [[[commit parentCommitSHA1s] allObjects] objectAtIndex:0];
}
return YES;
}
@end

92
Tree.h Normal file
View file

@ -0,0 +1,92 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "Blob.h"
#import "BufferedInputStream.h"
#import "OutputStream.h"
@class Node;
@class MutableS3Repo;
#define CURRENT_TREE_VERSION 10
@interface Tree : NSObject {
int treeVersion;
NSString *xattrsSHA1;
unsigned long long xattrsSize;
NSString *aclSHA1;
int uid;
int gid;
int mode;
long long mtime_sec;
long long mtime_nsec;
long long flags;
int finderFlags;
int extendedFinderFlags;
int st_dev;
int st_ino;
uint32_t st_nlink; // in struct stat, it's only 16 bits.
int st_rdev;
int64_t ctime_sec;
int64_t ctime_nsec;
int64_t createTime_sec;
int64_t createTime_nsec;
int64_t st_blocks;
uint32_t st_blksize;
NSMutableDictionary *nodes;
}
- (id)init;
- (id)initWithBufferedInputStream:(id <BufferedInputStream>)is error:(NSError **)error;
- (NSArray *)childNodeNames;
- (Node *)childNodeWithName:(NSString *)name;
- (BOOL)containsNodeNamed:(NSString *)name;
@property(readonly,copy) NSString *xattrsSHA1;
@property(readonly) unsigned long long xattrsSize;
@property(readonly,copy) NSString *aclSHA1;
@property(readonly) int uid;
@property(readonly) int gid;
@property(readonly) int mode;
@property(readonly) long long mtime_sec;
@property(readonly) long long mtime_nsec;
@property(readonly) long long flags;
@property(readonly) int finderFlags;
@property(readonly) int extendedFinderFlags;
@property(readonly) int treeVersion;
@property(readonly) int st_rdev;
@property(readonly) long long ctime_sec;
@property(readonly) long long ctime_nsec;
@property(readonly) long long createTime_sec;
@property(readonly) long long createTime_nsec;
@property(readonly) uint32_t st_nlink;
@property(readonly) int st_ino;
@end

156
Tree.m Normal file
View file

@ -0,0 +1,156 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "StringIO.h"
#import "IntegerIO.h"
#import "BooleanIO.h"
#import "Node.h"
#import "Tree.h"
#import "Blob.h"
#import "DataInputStream.h"
#import "SetNSError.h"
#import "RegexKitLite.h"
#import "NSErrorCodes.h"
#import "Streams.h"
#define HEADER_LENGTH (8)
@interface Tree (internal)
- (BOOL)readHeader:(id <BufferedInputStream>)is error:(NSError **)error;
@end
@implementation Tree
@synthesize xattrsSHA1, xattrsSize, aclSHA1, uid, gid, mode, mtime_sec, mtime_nsec, flags, finderFlags, extendedFinderFlags, treeVersion, st_rdev;
@synthesize ctime_sec, ctime_nsec, createTime_sec, createTime_nsec, st_nlink, st_ino;
- (id)init {
if (self = [super init]) {
nodes = [[NSMutableDictionary alloc] init];
}
return self;
}
- (id)initWithBufferedInputStream:(id <BufferedInputStream>)is error:(NSError **)error {
if (self = [super init]) {
if (![self readHeader:is error:error]) {
[self release];
return nil;
}
BOOL ret = [StringIO read:&xattrsSHA1 from:is error:error]
&& [IntegerIO readUInt64:&xattrsSize from:is error:error]
&&[StringIO read:&aclSHA1 from:is error:error]
&& [IntegerIO readInt32:&uid from:is error:error]
&& [IntegerIO readInt32:&gid from:is error:error]
&& [IntegerIO readInt32:&mode from:is error:error]
&& [IntegerIO readInt64:&mtime_sec from:is error:error]
&& [IntegerIO readInt64:&mtime_nsec from:is error:error]
&& [IntegerIO readInt64:&flags from:is error:error]
&& [IntegerIO readInt32:&finderFlags from:is error:error]
&& [IntegerIO readInt32:&extendedFinderFlags from:is error:error]
&& [IntegerIO readInt32:&st_dev from:is error:error]
&& [IntegerIO readInt32:&st_ino from:is error:error]
&& [IntegerIO readUInt32:&st_nlink from:is error:error]
&& [IntegerIO readInt32:&st_rdev from:is error:error]
&& [IntegerIO readInt64:&ctime_sec from:is error:error]
&& [IntegerIO readInt64:&ctime_nsec from:is error:error]
&& [IntegerIO readInt64:&st_blocks from:is error:error]
&& [IntegerIO readUInt32:&st_blksize from:is error:error];
[xattrsSHA1 retain];
[aclSHA1 retain];
if (!ret) {
goto initError;
}
unsigned int nodeCount;
if (![IntegerIO readUInt32:&nodeCount from:is error:error]) {
goto initError;
}
nodes = [[NSMutableDictionary alloc] init];
for (unsigned int i = 0; i < nodeCount; i++) {
NSString *nodeName;
if (![StringIO read:&nodeName from:is error:error]) {
goto initError;
}
Node *node = [[Node alloc] initWithInputStream:is treeVersion:treeVersion error:error];
if (!node) {
goto initError;
}
[nodes setObject:node forKey:nodeName];
[node release];
}
goto initDone;
initError:
[self release];
self = nil;
}
initDone:
return self;
}
- (void)dealloc {
[xattrsSHA1 release];
[aclSHA1 release];
[nodes release];
[super dealloc];
}
- (NSArray *)childNodeNames {
return [nodes allKeys];
}
- (Node *)childNodeWithName:(NSString *)name {
return [nodes objectForKey:name];
}
- (BOOL)containsNodeNamed:(NSString *)name {
return [nodes objectForKey:name] != nil;
}
@end
@implementation Tree (internal)
- (BOOL)readHeader:(id <BufferedInputStream>)is error:(NSError **)error {
unsigned char *headerBytes = [is readExactly:HEADER_LENGTH error:error];
if (headerBytes == NULL) {
return NO;
}
NSString *header = [[[NSString alloc] initWithBytes:headerBytes length:HEADER_LENGTH encoding:NSASCIIStringEncoding] autorelease];
NSRange versionRange = [header rangeOfRegex:@"^TreeV(\\d{3})$" capture:1];
treeVersion = 0;
if (versionRange.location != NSNotFound) {
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
NSNumber *number = [nf numberFromString:[header substringWithRange:versionRange]];
treeVersion = [number intValue];
[nf release];
}
if (treeVersion != CURRENT_TREE_VERSION) {
SETNSERROR(@"TreeErrorDomain", ERROR_INVALID_OBJECT_VERSION, @"invalid Tree header: %@", header);
return NO;
}
return YES;
}
@end

45
XAttrSet.h Normal file
View file

@ -0,0 +1,45 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "Blob.h"
#import "BufferedInputStream.h"
@interface XAttrSet : NSObject {
NSMutableDictionary *xattrs;
}
- (id)initWithPath:(NSString *)thePath error:(NSError **)error;
- (id)initWithBufferedInputStream:(id <BufferedInputStream>)is error:(NSError **)error;
- (NSUInteger)count;
- (NSArray *)names;
- (BOOL)applyToFile:(NSString *)path error:(NSError **)error;
@end

190
XAttrSet.m Normal file
View file

@ -0,0 +1,190 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/xattr.h>
#import "XAttrSet.h"
#import "StringIO.h"
#import "DataIO.h"
#import "IntegerIO.h"
#import "Blob.h"
#import "DataInputStream.h"
#import "SetNSError.h"
#import "NSErrorCodes.h"
#import "Streams.h"
#define HEADER_LENGTH (12)
@interface XAttrSet (internal)
- (BOOL)loadFromPath:(NSString *)thePath error:(NSError **)error;
- (BOOL)loadFromInputStream:(id <BufferedInputStream>)is error:(NSError **)error;
@end
@implementation XAttrSet
- (id)initWithPath:(NSString *)thePath error:(NSError **)error {
if (self = [super init]) {
xattrs = [[NSMutableDictionary alloc] init];
if (![self loadFromPath:thePath error:error]) {
[self release];
self = nil;
}
}
return self;
}
- (id)initWithBufferedInputStream:(id <BufferedInputStream>)is error:(NSError **)error {
if (self = [super init]) {
xattrs = [[NSMutableDictionary alloc] init];
if (![self loadFromInputStream:is error:error]) {
[self release];
self = nil;
}
}
return self;
}
- (void)dealloc {
[xattrs release];
[super dealloc];
}
- (NSUInteger)count {
return [xattrs count];
}
- (NSArray *)names {
return [xattrs allKeys];
}
- (BOOL)applyToFile:(NSString *)path error:(NSError **)error {
XAttrSet *current = [[[XAttrSet alloc] initWithPath:path error:error] autorelease];
if (!current) {
return NO;
}
const char *pathChars = [path fileSystemRepresentation];
for (NSString *name in [current names]) {
if (removexattr(pathChars, [name UTF8String], XATTR_NOFOLLOW) == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"removexattr: %s", strerror(errno));
return NO;
}
}
for (NSString *key in [xattrs allKeys]) {
NSData *value = [xattrs objectForKey:key];
if (setxattr(pathChars,
[key UTF8String],
[value bytes],
[value length],
0,
XATTR_NOFOLLOW) == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"setxattr: %s", strerror(errno));
return NO;
}
}
return YES;
}
@end
@implementation XAttrSet (internal)
- (BOOL)loadFromPath:(NSString *)thePath error:(NSError **)error {
NSDictionary *attribs = [[NSFileManager defaultManager] attributesOfItemAtPath:thePath error:error];
if (attribs == nil) {
return NO;
}
NSString *fileType = [attribs objectForKey:NSFileType];
if (![fileType isEqualToString:NSFileTypeSocket]
&& ![fileType isEqualToString:NSFileTypeBlockSpecial]
&& ![fileType isEqualToString:NSFileTypeCharacterSpecial]
&& ![fileType isEqualToString:NSFileTypeUnknown]) {
const char *path = [thePath fileSystemRepresentation];
ssize_t xattrsize = listxattr(path, NULL, 0, XATTR_NOFOLLOW);
if (xattrsize == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"%s", strerror(errno));
return NO;
}
if (xattrsize > 0) {
char *xattrbuf = (char *)malloc(xattrsize);
xattrsize = listxattr(path, xattrbuf, xattrsize, XATTR_NOFOLLOW);
if (xattrsize == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"%s", strerror(errno));
free(xattrbuf);
return NO;
}
for (char *name = xattrbuf; name < (xattrbuf + xattrsize); name += strlen(name) + 1) {
NSString *theName = [NSString stringWithUTF8String:name];
ssize_t valuesize = getxattr(path, name, NULL, 0, 0, XATTR_NOFOLLOW);
NSData *xattrData = nil;
if (valuesize == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"Error reading extended attribute %s: %s", name, strerror(errno));
free(xattrbuf);
return NO;
}
if (valuesize > 0) {
void *value = malloc(valuesize);
if (getxattr(path, name, value, valuesize, 0, XATTR_NOFOLLOW) == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"getxattr: %s", strerror(errno));
free(value);
free(xattrbuf);
return NO;
}
xattrData = [NSData dataWithBytes:value length:valuesize];
free(value);
} else {
xattrData = [NSData data];
}
[xattrs setObject:xattrData forKey:theName];
}
free(xattrbuf);
}
}
return YES;
}
- (BOOL)loadFromInputStream:(id <BufferedInputStream>)is error:(NSError **)error {
unsigned char *headerBytes = [is readExactly:HEADER_LENGTH error:error];
if (headerBytes == NULL) {
return NO;
}
if (strncmp((const char *)headerBytes, "XAttrSetV002", HEADER_LENGTH)) {
SETNSERROR(@"XAttrSetErrorDomain", ERROR_INVALID_OBJECT_VERSION, @"invalid XAttrSet header");
return NO;
}
uint64_t count;
if (![IntegerIO readUInt64:&count from:is error:error]) {
return NO;
}
for (uint64_t i = 0; i < count; i++) {
NSString *name;
if (![StringIO read:&name from:is error:error]) {
return NO;
}
NSData *value;
if (![DataIO read:&value from:is error:error]) {
return NO;
}
[xattrs setObject:value forKey:name];
}
return YES;
}
@end

69
arq_restore.m Normal file
View file

@ -0,0 +1,69 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <libgen.h>
#import <Foundation/Foundation.h>
#import "ArqRestoreCommand.h"
#import "ArqFolder.h"
static void printUsage(const char *exeName) {
fprintf(stderr, "\t%s\n", exeName);
fprintf(stderr, "\t%s /s3bucket/computerUUID/folderUUID\n", exeName);
}
int main (int argc, const char * argv[]) {
setHSLogLevel(HSLOG_LEVEL_ERROR);
char *exePath = strdup(argv[0]);
char *exeName = basename(exePath);
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
ArqRestoreCommand *cmd = [[[ArqRestoreCommand alloc] init] autorelease];
int ret = 0;
NSError *error = nil;
if (argc == 1) {
if (![cmd printArqFolders:&error]) {
NSLog(@"%@", [error localizedDescription]);
ret = 1;
} else {
printf("\nType %s <s3 path> to restore\n", exeName);
}
} else if (argc == 2) {
if (![cmd restorePath:[NSString stringWithUTF8String:argv[1]] error:&error]) {
NSLog(@"%@", [error localizedDescription]);
ret = 1;
}
} else {
printUsage(exeName);
ret = 1;
}
[pool drain];
free(exePath);
return ret;
}

2
arq_restore.xcodeproj/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
stefan.mode1v3
stefan.pbxuser

View file

@ -0,0 +1,825 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 45;
objects = {
/* Begin PBXBuildFile section */
8DD76F9A0486AA7600D96B5E /* arq_restore.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* arq_restore.m */; settings = {ATTRIBUTES = (); }; };
8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; };
F805B54D1160D3E6007EC01E /* ArqRestoreCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B54C1160D3E6007EC01E /* ArqRestoreCommand.m */; };
F805B7211160D9C2007EC01E /* HSLog.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7201160D9C2007EC01E /* HSLog.m */; };
F805B7301160DBE9007EC01E /* ArqFolder.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B72F1160DBE9007EC01E /* ArqFolder.m */; };
F805B7531160DCFE007EC01E /* ArrayNode.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7421160DCFE007EC01E /* ArrayNode.m */; };
F805B7541160DCFE007EC01E /* BooleanNode.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7441160DCFE007EC01E /* BooleanNode.m */; };
F805B7551160DCFE007EC01E /* DictNode.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7461160DCFE007EC01E /* DictNode.m */; };
F805B7561160DCFE007EC01E /* IntegerNode.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7481160DCFE007EC01E /* IntegerNode.m */; };
F805B7571160DCFE007EC01E /* RealNode.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B74C1160DCFE007EC01E /* RealNode.m */; };
F805B7581160DCFE007EC01E /* StringNode.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B74E1160DCFE007EC01E /* StringNode.m */; };
F805B7591160DCFE007EC01E /* XMLPListReader.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7501160DCFE007EC01E /* XMLPListReader.m */; };
F805B75A1160DCFE007EC01E /* XMLPListWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7521160DCFE007EC01E /* XMLPListWriter.m */; };
F805B7821160DD60007EC01E /* HTTPConnection_S3.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7691160DD60007EC01E /* HTTPConnection_S3.m */; };
F805B7831160DD60007EC01E /* NSError_S3.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B76B1160DD60007EC01E /* NSError_S3.m */; };
F805B7841160DD60007EC01E /* PathReceiver.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B76D1160DD60007EC01E /* PathReceiver.m */; };
F805B7851160DD60007EC01E /* S3AuthorizationParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B76F1160DD60007EC01E /* S3AuthorizationParameters.m */; };
F805B7861160DD60007EC01E /* S3AuthorizationProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7711160DD60007EC01E /* S3AuthorizationProvider.m */; };
F805B7871160DD60007EC01E /* S3Lister.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7731160DD60007EC01E /* S3Lister.m */; };
F805B7881160DD60007EC01E /* S3ObjectMetadata.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7751160DD60007EC01E /* S3ObjectMetadata.m */; };
F805B7891160DD60007EC01E /* S3ObjectReceiver.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7771160DD60007EC01E /* S3ObjectReceiver.m */; };
F805B78B1160DD60007EC01E /* S3Request.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B77C1160DD60007EC01E /* S3Request.m */; };
F805B78C1160DD60007EC01E /* S3Service.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B77E1160DD60007EC01E /* S3Service.m */; };
F805B78D1160DD60007EC01E /* S3Signature.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7801160DD60007EC01E /* S3Signature.m */; };
F805B7A91160DEF2007EC01E /* RegexKitLite.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7A81160DEF2007EC01E /* RegexKitLite.m */; };
F805B7BA1160E3AF007EC01E /* Blob.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7B91160E3AF007EC01E /* Blob.m */; };
F805B7D11160E445007EC01E /* HTTPConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7CC1160E445007EC01E /* HTTPConnection.m */; };
F805B7D21160E445007EC01E /* HTTPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7CE1160E445007EC01E /* HTTPRequest.m */; };
F805B7D31160E445007EC01E /* HTTPResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7D01160E445007EC01E /* HTTPResponse.m */; };
F805B7D81160E456007EC01E /* BlobACL.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7D71160E456007EC01E /* BlobACL.m */; };
F805B7E11160E48B007EC01E /* ServerBlob.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7E01160E48B007EC01E /* ServerBlob.m */; };
F805B7FB1160E73D007EC01E /* RFC2616DateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7FA1160E73D007EC01E /* RFC2616DateFormatter.m */; };
F805B7FE1160E764007EC01E /* RFC822.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7FD1160E764007EC01E /* RFC822.m */; };
F805B81B1160E838007EC01E /* InputStreams.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B81A1160E838007EC01E /* InputStreams.m */; };
F805B8251160E857007EC01E /* ChunkedInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8221160E857007EC01E /* ChunkedInputStream.m */; };
F805B8261160E857007EC01E /* FixedLengthInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8241160E857007EC01E /* FixedLengthInputStream.m */; };
F805B82B1160E861007EC01E /* FDInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8281160E861007EC01E /* FDInputStream.m */; };
F805B82C1160E861007EC01E /* FDOutputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B82A1160E861007EC01E /* FDOutputStream.m */; };
F805B82F1160E86E007EC01E /* DataInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B82E1160E86E007EC01E /* DataInputStream.m */; };
F805B8321160E878007EC01E /* Writer.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8311160E878007EC01E /* Writer.m */; };
F805B83B1160E8DD007EC01E /* NSData-InputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B83A1160E8DD007EC01E /* NSData-InputStream.m */; };
F805B83F1160E900007EC01E /* StreamPairFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B83E1160E900007EC01E /* StreamPairFactory.m */; };
F805B8421160E90F007EC01E /* Streams.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8411160E90F007EC01E /* Streams.m */; };
F805B8531160E9B0007EC01E /* CFStreamPair.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8521160E9B0007EC01E /* CFStreamPair.m */; };
F805B85A1160E9C9007EC01E /* CFStreamInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8571160E9C9007EC01E /* CFStreamInputStream.m */; };
F805B85B1160E9C9007EC01E /* CFStreamOutputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8591160E9C9007EC01E /* CFStreamOutputStream.m */; };
F805B8601160E9F0007EC01E /* DNS_SDErrors.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B85F1160E9F0007EC01E /* DNS_SDErrors.m */; };
F805B8651160EA15007EC01E /* NSXMLNode_extra.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8641160EA15007EC01E /* NSXMLNode_extra.m */; };
F805B86A1160EA83007EC01E /* NSData-Base64Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8691160EA83007EC01E /* NSData-Base64Extensions.m */; };
F805B86F1160EAC1007EC01E /* DataInputStreamFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B86E1160EAC1007EC01E /* DataInputStreamFactory.m */; };
F805B8891160EB39007EC01E /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8881160EB39007EC01E /* libcrypto.dylib */; };
F805B88B1160EB3A007EC01E /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B88A1160EB39007EC01E /* libicucore.dylib */; };
F805B88F1160EB45007EC01E /* libssl.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B88E1160EB45007EC01E /* libssl.dylib */; };
F805B8931160EB4E007EC01E /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8921160EB4E007EC01E /* SystemConfiguration.framework */; };
F805B8A11160EBAA007EC01E /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8A01160EBAA007EC01E /* CoreFoundation.framework */; };
F805B8C21160EC41007EC01E /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8C11160EC41007EC01E /* Security.framework */; };
F805B8CE1160ECD7007EC01E /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8CD1160ECD7007EC01E /* CoreServices.framework */; };
F8D678001160F26A00CC270E /* Restorer.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D677FF1160F26A00CC270E /* Restorer.m */; };
F8D678171160F4E300CC270E /* S3Fark.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678141160F4E300CC270E /* S3Fark.m */; };
F8D678181160F4E300CC270E /* S3Repo.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678161160F4E300CC270E /* S3Repo.m */; };
F8D6781D1160F4FD00CC270E /* SHA1Hash.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6781C1160F4FD00CC270E /* SHA1Hash.m */; };
F8D6782D1160F5D000CC270E /* PackSetSet.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6782A1160F5D000CC270E /* PackSetSet.m */; };
F8D6782E1160F5D000CC270E /* PackSet.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6782C1160F5D000CC270E /* PackSet.m */; };
F8D678331160F62E00CC270E /* ArqUserLibrary.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678321160F62E00CC270E /* ArqUserLibrary.m */; };
F8D6783C1160F70100CC270E /* PackIndexEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6783B1160F70100CC270E /* PackIndexEntry.m */; };
F8D678451160F74A00CC270E /* DiskPack.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678421160F74A00CC270E /* DiskPack.m */; };
F8D678461160F74A00CC270E /* DiskPackIndex.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678441160F74A00CC270E /* DiskPackIndex.m */; };
F8D6785F1160F7CF00CC270E /* Commit.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6785C1160F7CF00CC270E /* Commit.m */; };
F8D678601160F7CF00CC270E /* Tree.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6785E1160F7CF00CC270E /* Tree.m */; };
F8D678651160F7FE00CC270E /* NSData-Encrypt.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678641160F7FE00CC270E /* NSData-Encrypt.m */; };
F8D6786A1160F81100CC270E /* OpenSSL.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678691160F81100CC270E /* OpenSSL.m */; };
F8D6786F1160F84600CC270E /* DecryptedInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6786E1160F84600CC270E /* DecryptedInputStream.m */; };
F8D678741160F85D00CC270E /* CryptInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678731160F85D00CC270E /* CryptInputStream.m */; };
F8D678771160F86E00CC270E /* FileInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678761160F86E00CC270E /* FileInputStream.m */; };
F8D678821160F8A000CC270E /* DataIO.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678791160F8A000CC270E /* DataIO.m */; };
F8D678831160F8A000CC270E /* DateIO.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6787B1160F8A000CC270E /* DateIO.m */; };
F8D678841160F8A000CC270E /* DoubleIO.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6787D1160F8A000CC270E /* DoubleIO.m */; };
F8D678851160F8A000CC270E /* IntegerIO.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6787F1160F8A000CC270E /* IntegerIO.m */; };
F8D678861160F8A000CC270E /* StringIO.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678811160F8A000CC270E /* StringIO.m */; };
F8D678891160F8CD00CC270E /* NSString_extra.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678881160F8CD00CC270E /* NSString_extra.m */; };
F8D6788C1160F8E500CC270E /* BinarySHA1.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6788B1160F8E500CC270E /* BinarySHA1.m */; };
F8D6789A1160FA2A00CC270E /* NSFileManager_extra.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678991160FA2A00CC270E /* NSFileManager_extra.m */; };
F8D6789D1160FA3900CC270E /* FileOutputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6789C1160FA3900CC270E /* FileOutputStream.m */; };
F8D678A01160FA4800CC270E /* FileInputStreamFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6789F1160FA4800CC270E /* FileInputStreamFactory.m */; };
F8D678A51160FA5F00CC270E /* BooleanIO.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678A41160FA5F00CC270E /* BooleanIO.m */; };
F8D678A81160FA6A00CC270E /* EncryptedInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678A71160FA6A00CC270E /* EncryptedInputStream.m */; };
F8D678AF1160FAD900CC270E /* CommitFailedFile.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678AE1160FAD900CC270E /* CommitFailedFile.m */; };
F8D678B81160FB2100CC270E /* Node.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678B71160FB2100CC270E /* Node.m */; };
F8D67CEB1161363A00CC270E /* FileAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D67CEA1161363A00CC270E /* FileAttributes.m */; };
F8D67CF21161366100CC270E /* XAttrSet.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D67CF11161366100CC270E /* XAttrSet.m */; };
F8D67D071161384100CC270E /* FileACL.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D67D041161384100CC270E /* FileACL.m */; };
F8D67D081161384100CC270E /* OSStatusDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D67D061161384100CC270E /* OSStatusDescription.m */; };
F8D67F701161443600CC270E /* RestoreNode.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D67F6F1161443600CC270E /* RestoreNode.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
8DD76F9E0486AA7600D96B5E /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
08FB7796FE84155DC02AAC07 /* arq_restore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = arq_restore.m; sourceTree = "<group>"; };
08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
32A70AAB03705E1F00C91783 /* arq_restore_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = arq_restore_Prefix.pch; sourceTree = "<group>"; };
8DD76FA10486AA7600D96B5E /* arq_restore */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = arq_restore; sourceTree = BUILT_PRODUCTS_DIR; };
F805B54B1160D3E6007EC01E /* ArqRestoreCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArqRestoreCommand.h; sourceTree = "<group>"; };
F805B54C1160D3E6007EC01E /* ArqRestoreCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArqRestoreCommand.m; sourceTree = "<group>"; };
F805B71F1160D9C2007EC01E /* HSLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSLog.h; sourceTree = "<group>"; };
F805B7201160D9C2007EC01E /* HSLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSLog.m; sourceTree = "<group>"; };
F805B72E1160DBE9007EC01E /* ArqFolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArqFolder.h; sourceTree = "<group>"; };
F805B72F1160DBE9007EC01E /* ArqFolder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArqFolder.m; sourceTree = "<group>"; };
F805B7411160DCFE007EC01E /* ArrayNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrayNode.h; sourceTree = "<group>"; };
F805B7421160DCFE007EC01E /* ArrayNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArrayNode.m; sourceTree = "<group>"; };
F805B7431160DCFE007EC01E /* BooleanNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BooleanNode.h; sourceTree = "<group>"; };
F805B7441160DCFE007EC01E /* BooleanNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BooleanNode.m; sourceTree = "<group>"; };
F805B7451160DCFE007EC01E /* DictNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DictNode.h; sourceTree = "<group>"; };
F805B7461160DCFE007EC01E /* DictNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DictNode.m; sourceTree = "<group>"; };
F805B7471160DCFE007EC01E /* IntegerNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntegerNode.h; sourceTree = "<group>"; };
F805B7481160DCFE007EC01E /* IntegerNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IntegerNode.m; sourceTree = "<group>"; };
F805B7491160DCFE007EC01E /* PListNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PListNode.h; sourceTree = "<group>"; };
F805B74A1160DCFE007EC01E /* PListNodeType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PListNodeType.h; sourceTree = "<group>"; };
F805B74B1160DCFE007EC01E /* RealNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RealNode.h; sourceTree = "<group>"; };
F805B74C1160DCFE007EC01E /* RealNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RealNode.m; sourceTree = "<group>"; };
F805B74D1160DCFE007EC01E /* StringNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringNode.h; sourceTree = "<group>"; };
F805B74E1160DCFE007EC01E /* StringNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StringNode.m; sourceTree = "<group>"; };
F805B74F1160DCFE007EC01E /* XMLPListReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XMLPListReader.h; sourceTree = "<group>"; };
F805B7501160DCFE007EC01E /* XMLPListReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XMLPListReader.m; sourceTree = "<group>"; };
F805B7511160DCFE007EC01E /* XMLPListWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XMLPListWriter.h; sourceTree = "<group>"; };
F805B7521160DCFE007EC01E /* XMLPListWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XMLPListWriter.m; sourceTree = "<group>"; };
F805B7681160DD60007EC01E /* HTTPConnection_S3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPConnection_S3.h; sourceTree = "<group>"; };
F805B7691160DD60007EC01E /* HTTPConnection_S3.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTTPConnection_S3.m; sourceTree = "<group>"; };
F805B76A1160DD60007EC01E /* NSError_S3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSError_S3.h; sourceTree = "<group>"; };
F805B76B1160DD60007EC01E /* NSError_S3.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSError_S3.m; sourceTree = "<group>"; };
F805B76C1160DD60007EC01E /* PathReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PathReceiver.h; sourceTree = "<group>"; };
F805B76D1160DD60007EC01E /* PathReceiver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PathReceiver.m; sourceTree = "<group>"; };
F805B76E1160DD60007EC01E /* S3AuthorizationParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S3AuthorizationParameters.h; sourceTree = "<group>"; };
F805B76F1160DD60007EC01E /* S3AuthorizationParameters.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = S3AuthorizationParameters.m; sourceTree = "<group>"; };
F805B7701160DD60007EC01E /* S3AuthorizationProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S3AuthorizationProvider.h; sourceTree = "<group>"; };
F805B7711160DD60007EC01E /* S3AuthorizationProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = S3AuthorizationProvider.m; sourceTree = "<group>"; };
F805B7721160DD60007EC01E /* S3Lister.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S3Lister.h; sourceTree = "<group>"; };
F805B7731160DD60007EC01E /* S3Lister.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = S3Lister.m; sourceTree = "<group>"; };
F805B7741160DD60007EC01E /* S3ObjectMetadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S3ObjectMetadata.h; sourceTree = "<group>"; };
F805B7751160DD60007EC01E /* S3ObjectMetadata.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = S3ObjectMetadata.m; sourceTree = "<group>"; };
F805B7761160DD60007EC01E /* S3ObjectReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S3ObjectReceiver.h; sourceTree = "<group>"; };
F805B7771160DD60007EC01E /* S3ObjectReceiver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = S3ObjectReceiver.m; sourceTree = "<group>"; };
F805B77A1160DD60007EC01E /* S3Receiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S3Receiver.h; sourceTree = "<group>"; };
F805B77B1160DD60007EC01E /* S3Request.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S3Request.h; sourceTree = "<group>"; };
F805B77C1160DD60007EC01E /* S3Request.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = S3Request.m; sourceTree = "<group>"; };
F805B77D1160DD60007EC01E /* S3Service.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S3Service.h; sourceTree = "<group>"; };
F805B77E1160DD60007EC01E /* S3Service.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = S3Service.m; sourceTree = "<group>"; };
F805B77F1160DD60007EC01E /* S3Signature.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S3Signature.h; sourceTree = "<group>"; };
F805B7801160DD60007EC01E /* S3Signature.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = S3Signature.m; sourceTree = "<group>"; };
F805B7A71160DEF2007EC01E /* RegexKitLite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegexKitLite.h; sourceTree = "<group>"; };
F805B7A81160DEF2007EC01E /* RegexKitLite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RegexKitLite.m; sourceTree = "<group>"; };
F805B7B81160E3AF007EC01E /* Blob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Blob.h; sourceTree = "<group>"; };
F805B7B91160E3AF007EC01E /* Blob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Blob.m; sourceTree = "<group>"; };
F805B7CA1160E445007EC01E /* HTTP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTP.h; sourceTree = "<group>"; };
F805B7CB1160E445007EC01E /* HTTPConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPConnection.h; sourceTree = "<group>"; };
F805B7CC1160E445007EC01E /* HTTPConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTTPConnection.m; sourceTree = "<group>"; };
F805B7CD1160E445007EC01E /* HTTPRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPRequest.h; sourceTree = "<group>"; };
F805B7CE1160E445007EC01E /* HTTPRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTTPRequest.m; sourceTree = "<group>"; };
F805B7CF1160E445007EC01E /* HTTPResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPResponse.h; sourceTree = "<group>"; };
F805B7D01160E445007EC01E /* HTTPResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTTPResponse.m; sourceTree = "<group>"; };
F805B7D61160E456007EC01E /* BlobACL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlobACL.h; sourceTree = "<group>"; };
F805B7D71160E456007EC01E /* BlobACL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BlobACL.m; sourceTree = "<group>"; };
F805B7DE1160E48B007EC01E /* NSErrorCodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSErrorCodes.h; sourceTree = "<group>"; };
F805B7DF1160E48B007EC01E /* ServerBlob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ServerBlob.h; sourceTree = "<group>"; };
F805B7E01160E48B007EC01E /* ServerBlob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ServerBlob.m; sourceTree = "<group>"; };
F805B7F91160E73D007EC01E /* RFC2616DateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RFC2616DateFormatter.h; sourceTree = "<group>"; };
F805B7FA1160E73D007EC01E /* RFC2616DateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RFC2616DateFormatter.m; sourceTree = "<group>"; };
F805B7FC1160E764007EC01E /* RFC822.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RFC822.h; sourceTree = "<group>"; };
F805B7FD1160E764007EC01E /* RFC822.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RFC822.m; sourceTree = "<group>"; };
F805B80B1160E7C3007EC01E /* InputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InputStream.h; sourceTree = "<group>"; };
F805B8191160E838007EC01E /* InputStreams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InputStreams.h; sourceTree = "<group>"; };
F805B81A1160E838007EC01E /* InputStreams.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InputStreams.m; sourceTree = "<group>"; };
F805B8211160E857007EC01E /* ChunkedInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChunkedInputStream.h; sourceTree = "<group>"; };
F805B8221160E857007EC01E /* ChunkedInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ChunkedInputStream.m; sourceTree = "<group>"; };
F805B8231160E857007EC01E /* FixedLengthInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FixedLengthInputStream.h; sourceTree = "<group>"; };
F805B8241160E857007EC01E /* FixedLengthInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FixedLengthInputStream.m; sourceTree = "<group>"; };
F805B8271160E861007EC01E /* FDInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FDInputStream.h; sourceTree = "<group>"; };
F805B8281160E861007EC01E /* FDInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FDInputStream.m; sourceTree = "<group>"; };
F805B8291160E861007EC01E /* FDOutputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FDOutputStream.h; sourceTree = "<group>"; };
F805B82A1160E861007EC01E /* FDOutputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FDOutputStream.m; sourceTree = "<group>"; };
F805B82D1160E86E007EC01E /* DataInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataInputStream.h; sourceTree = "<group>"; };
F805B82E1160E86E007EC01E /* DataInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DataInputStream.m; sourceTree = "<group>"; };
F805B8301160E878007EC01E /* Writer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Writer.h; sourceTree = "<group>"; };
F805B8311160E878007EC01E /* Writer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Writer.m; sourceTree = "<group>"; };
F805B8331160E882007EC01E /* BufferedInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BufferedInputStream.h; sourceTree = "<group>"; };
F805B8361160E8A0007EC01E /* OutputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OutputStream.h; sourceTree = "<group>"; };
F805B8391160E8DD007EC01E /* NSData-InputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData-InputStream.h"; sourceTree = "<group>"; };
F805B83A1160E8DD007EC01E /* NSData-InputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData-InputStream.m"; sourceTree = "<group>"; };
F805B83C1160E900007EC01E /* StreamPair.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StreamPair.h; sourceTree = "<group>"; };
F805B83D1160E900007EC01E /* StreamPairFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StreamPairFactory.h; sourceTree = "<group>"; };
F805B83E1160E900007EC01E /* StreamPairFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StreamPairFactory.m; sourceTree = "<group>"; };
F805B8401160E90F007EC01E /* Streams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Streams.h; sourceTree = "<group>"; };
F805B8411160E90F007EC01E /* Streams.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Streams.m; sourceTree = "<group>"; };
F805B8511160E9B0007EC01E /* CFStreamPair.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFStreamPair.h; sourceTree = "<group>"; };
F805B8521160E9B0007EC01E /* CFStreamPair.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CFStreamPair.m; sourceTree = "<group>"; };
F805B8561160E9C9007EC01E /* CFStreamInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFStreamInputStream.h; sourceTree = "<group>"; };
F805B8571160E9C9007EC01E /* CFStreamInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CFStreamInputStream.m; sourceTree = "<group>"; };
F805B8581160E9C9007EC01E /* CFStreamOutputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFStreamOutputStream.h; sourceTree = "<group>"; };
F805B8591160E9C9007EC01E /* CFStreamOutputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CFStreamOutputStream.m; sourceTree = "<group>"; };
F805B85E1160E9F0007EC01E /* DNS_SDErrors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNS_SDErrors.h; sourceTree = "<group>"; };
F805B85F1160E9F0007EC01E /* DNS_SDErrors.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DNS_SDErrors.m; sourceTree = "<group>"; };
F805B8631160EA15007EC01E /* NSXMLNode_extra.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSXMLNode_extra.h; sourceTree = "<group>"; };
F805B8641160EA15007EC01E /* NSXMLNode_extra.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSXMLNode_extra.m; sourceTree = "<group>"; };
F805B8661160EA36007EC01E /* InputStreamFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InputStreamFactory.h; sourceTree = "<group>"; };
F805B8681160EA83007EC01E /* NSData-Base64Extensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData-Base64Extensions.h"; sourceTree = "<group>"; };
F805B8691160EA83007EC01E /* NSData-Base64Extensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData-Base64Extensions.m"; sourceTree = "<group>"; };
F805B86D1160EAC1007EC01E /* DataInputStreamFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataInputStreamFactory.h; sourceTree = "<group>"; };
F805B86E1160EAC1007EC01E /* DataInputStreamFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DataInputStreamFactory.m; sourceTree = "<group>"; };
F805B8881160EB39007EC01E /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = usr/lib/libcrypto.dylib; sourceTree = SDKROOT; };
F805B88A1160EB39007EC01E /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; };
F805B88E1160EB45007EC01E /* libssl.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libssl.dylib; path = usr/lib/libssl.dylib; sourceTree = SDKROOT; };
F805B8921160EB4E007EC01E /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
F805B8981160EB71007EC01E /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
F805B8A01160EBAA007EC01E /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
F805B8BB1160EC38007EC01E /* SecurityFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SecurityFoundation.framework; path = System/Library/Frameworks/SecurityFoundation.framework; sourceTree = SDKROOT; };
F805B8C11160EC41007EC01E /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
F805B8C51160EC4E007EC01E /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
F805B8CD1160ECD7007EC01E /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
F8D6763E1160F22800CC270E /* SetNSError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SetNSError.h; sourceTree = "<group>"; };
F8D677FE1160F26A00CC270E /* Restorer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Restorer.h; sourceTree = "<group>"; };
F8D677FF1160F26A00CC270E /* Restorer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Restorer.m; sourceTree = "<group>"; };
F8D678131160F4E300CC270E /* S3Fark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S3Fark.h; sourceTree = "<group>"; };
F8D678141160F4E300CC270E /* S3Fark.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = S3Fark.m; sourceTree = "<group>"; };
F8D678151160F4E300CC270E /* S3Repo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S3Repo.h; sourceTree = "<group>"; };
F8D678161160F4E300CC270E /* S3Repo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = S3Repo.m; sourceTree = "<group>"; };
F8D6781B1160F4FD00CC270E /* SHA1Hash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHA1Hash.h; sourceTree = "<group>"; };
F8D6781C1160F4FD00CC270E /* SHA1Hash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHA1Hash.m; sourceTree = "<group>"; };
F8D678291160F5D000CC270E /* PackSetSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PackSetSet.h; sourceTree = "<group>"; };
F8D6782A1160F5D000CC270E /* PackSetSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PackSetSet.m; sourceTree = "<group>"; };
F8D6782B1160F5D000CC270E /* PackSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PackSet.h; sourceTree = "<group>"; };
F8D6782C1160F5D000CC270E /* PackSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PackSet.m; sourceTree = "<group>"; };
F8D678311160F62E00CC270E /* ArqUserLibrary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArqUserLibrary.h; sourceTree = "<group>"; };
F8D678321160F62E00CC270E /* ArqUserLibrary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArqUserLibrary.m; sourceTree = "<group>"; };
F8D6783A1160F70100CC270E /* PackIndexEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PackIndexEntry.h; sourceTree = "<group>"; };
F8D6783B1160F70100CC270E /* PackIndexEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PackIndexEntry.m; sourceTree = "<group>"; };
F8D678411160F74A00CC270E /* DiskPack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiskPack.h; sourceTree = "<group>"; };
F8D678421160F74A00CC270E /* DiskPack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DiskPack.m; sourceTree = "<group>"; };
F8D678431160F74A00CC270E /* DiskPackIndex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiskPackIndex.h; sourceTree = "<group>"; };
F8D678441160F74A00CC270E /* DiskPackIndex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DiskPackIndex.m; sourceTree = "<group>"; };
F8D6785B1160F7CE00CC270E /* Commit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Commit.h; sourceTree = "<group>"; };
F8D6785C1160F7CF00CC270E /* Commit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Commit.m; sourceTree = "<group>"; };
F8D6785D1160F7CF00CC270E /* Tree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tree.h; sourceTree = "<group>"; };
F8D6785E1160F7CF00CC270E /* Tree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Tree.m; sourceTree = "<group>"; };
F8D678631160F7FE00CC270E /* NSData-Encrypt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData-Encrypt.h"; sourceTree = "<group>"; };
F8D678641160F7FE00CC270E /* NSData-Encrypt.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData-Encrypt.m"; sourceTree = "<group>"; };
F8D678681160F81100CC270E /* OpenSSL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenSSL.h; sourceTree = "<group>"; };
F8D678691160F81100CC270E /* OpenSSL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OpenSSL.m; sourceTree = "<group>"; };
F8D6786D1160F84600CC270E /* DecryptedInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DecryptedInputStream.h; sourceTree = "<group>"; };
F8D6786E1160F84600CC270E /* DecryptedInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DecryptedInputStream.m; sourceTree = "<group>"; };
F8D678721160F85D00CC270E /* CryptInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptInputStream.h; sourceTree = "<group>"; };
F8D678731160F85D00CC270E /* CryptInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CryptInputStream.m; sourceTree = "<group>"; };
F8D678751160F86E00CC270E /* FileInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileInputStream.h; sourceTree = "<group>"; };
F8D678761160F86E00CC270E /* FileInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FileInputStream.m; sourceTree = "<group>"; };
F8D678781160F8A000CC270E /* DataIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataIO.h; sourceTree = "<group>"; };
F8D678791160F8A000CC270E /* DataIO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DataIO.m; sourceTree = "<group>"; };
F8D6787A1160F8A000CC270E /* DateIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DateIO.h; sourceTree = "<group>"; };
F8D6787B1160F8A000CC270E /* DateIO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DateIO.m; sourceTree = "<group>"; };
F8D6787C1160F8A000CC270E /* DoubleIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DoubleIO.h; sourceTree = "<group>"; };
F8D6787D1160F8A000CC270E /* DoubleIO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DoubleIO.m; sourceTree = "<group>"; };
F8D6787E1160F8A000CC270E /* IntegerIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntegerIO.h; sourceTree = "<group>"; };
F8D6787F1160F8A000CC270E /* IntegerIO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IntegerIO.m; sourceTree = "<group>"; };
F8D678801160F8A000CC270E /* StringIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringIO.h; sourceTree = "<group>"; };
F8D678811160F8A000CC270E /* StringIO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StringIO.m; sourceTree = "<group>"; };
F8D678871160F8CD00CC270E /* NSString_extra.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSString_extra.h; sourceTree = "<group>"; };
F8D678881160F8CD00CC270E /* NSString_extra.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSString_extra.m; sourceTree = "<group>"; };
F8D6788A1160F8E500CC270E /* BinarySHA1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BinarySHA1.h; sourceTree = "<group>"; };
F8D6788B1160F8E500CC270E /* BinarySHA1.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BinarySHA1.m; sourceTree = "<group>"; };
F8D678981160FA2A00CC270E /* NSFileManager_extra.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSFileManager_extra.h; sourceTree = "<group>"; };
F8D678991160FA2A00CC270E /* NSFileManager_extra.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSFileManager_extra.m; sourceTree = "<group>"; };
F8D6789B1160FA3900CC270E /* FileOutputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileOutputStream.h; sourceTree = "<group>"; };
F8D6789C1160FA3900CC270E /* FileOutputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FileOutputStream.m; sourceTree = "<group>"; };
F8D6789E1160FA4800CC270E /* FileInputStreamFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileInputStreamFactory.h; sourceTree = "<group>"; };
F8D6789F1160FA4800CC270E /* FileInputStreamFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FileInputStreamFactory.m; sourceTree = "<group>"; };
F8D678A31160FA5F00CC270E /* BooleanIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BooleanIO.h; sourceTree = "<group>"; };
F8D678A41160FA5F00CC270E /* BooleanIO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BooleanIO.m; sourceTree = "<group>"; };
F8D678A61160FA6A00CC270E /* EncryptedInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EncryptedInputStream.h; sourceTree = "<group>"; };
F8D678A71160FA6A00CC270E /* EncryptedInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EncryptedInputStream.m; sourceTree = "<group>"; };
F8D678AD1160FAD900CC270E /* CommitFailedFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommitFailedFile.h; sourceTree = "<group>"; };
F8D678AE1160FAD900CC270E /* CommitFailedFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommitFailedFile.m; sourceTree = "<group>"; };
F8D678B61160FB2100CC270E /* Node.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Node.h; sourceTree = "<group>"; };
F8D678B71160FB2100CC270E /* Node.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Node.m; sourceTree = "<group>"; };
F8D67CE91161363A00CC270E /* FileAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileAttributes.h; sourceTree = "<group>"; };
F8D67CEA1161363A00CC270E /* FileAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FileAttributes.m; sourceTree = "<group>"; };
F8D67CF01161366100CC270E /* XAttrSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XAttrSet.h; sourceTree = "<group>"; };
F8D67CF11161366100CC270E /* XAttrSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XAttrSet.m; sourceTree = "<group>"; };
F8D67D031161384100CC270E /* FileACL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileACL.h; sourceTree = "<group>"; };
F8D67D041161384100CC270E /* FileACL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FileACL.m; sourceTree = "<group>"; };
F8D67D051161384100CC270E /* OSStatusDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSStatusDescription.h; sourceTree = "<group>"; };
F8D67D061161384100CC270E /* OSStatusDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OSStatusDescription.m; sourceTree = "<group>"; };
F8D67F6E1161443600CC270E /* RestoreNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RestoreNode.h; sourceTree = "<group>"; };
F8D67F6F1161443600CC270E /* RestoreNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RestoreNode.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
8DD76F9B0486AA7600D96B5E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */,
F805B8891160EB39007EC01E /* libcrypto.dylib in Frameworks */,
F805B88B1160EB3A007EC01E /* libicucore.dylib in Frameworks */,
F805B88F1160EB45007EC01E /* libssl.dylib in Frameworks */,
F805B8931160EB4E007EC01E /* SystemConfiguration.framework in Frameworks */,
F805B8A11160EBAA007EC01E /* CoreFoundation.framework in Frameworks */,
F805B8C21160EC41007EC01E /* Security.framework in Frameworks */,
F805B8CE1160ECD7007EC01E /* CoreServices.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
08FB7794FE84155DC02AAC07 /* arq_restore */ = {
isa = PBXGroup;
children = (
08FB7795FE84155DC02AAC07 /* Source */,
08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */,
1AB674ADFE9D54B511CA2CBB /* Products */,
);
name = arq_restore;
sourceTree = "<group>";
};
08FB7795FE84155DC02AAC07 /* Source */ = {
isa = PBXGroup;
children = (
F805B8671160EA7C007EC01E /* crypto */,
F805B7A61160DEF2007EC01E /* shared */,
F805B7401160DCFE007EC01E /* plist */,
F805B8081160E7A1007EC01E /* io */,
F805B7C91160E445007EC01E /* http */,
F805B7651160DD60007EC01E /* s3 */,
32A70AAB03705E1F00C91783 /* arq_restore_Prefix.pch */,
08FB7796FE84155DC02AAC07 /* arq_restore.m */,
F8D678311160F62E00CC270E /* ArqUserLibrary.h */,
F8D678321160F62E00CC270E /* ArqUserLibrary.m */,
F805B54B1160D3E6007EC01E /* ArqRestoreCommand.h */,
F805B54C1160D3E6007EC01E /* ArqRestoreCommand.m */,
F805B72E1160DBE9007EC01E /* ArqFolder.h */,
F805B72F1160DBE9007EC01E /* ArqFolder.m */,
F8D6785B1160F7CE00CC270E /* Commit.h */,
F8D6785C1160F7CF00CC270E /* Commit.m */,
F8D678AD1160FAD900CC270E /* CommitFailedFile.h */,
F8D678AE1160FAD900CC270E /* CommitFailedFile.m */,
F8D678411160F74A00CC270E /* DiskPack.h */,
F8D678421160F74A00CC270E /* DiskPack.m */,
F8D678431160F74A00CC270E /* DiskPackIndex.h */,
F8D678441160F74A00CC270E /* DiskPackIndex.m */,
F805B71F1160D9C2007EC01E /* HSLog.h */,
F805B7201160D9C2007EC01E /* HSLog.m */,
F8D678B61160FB2100CC270E /* Node.h */,
F8D678B71160FB2100CC270E /* Node.m */,
F8D6783A1160F70100CC270E /* PackIndexEntry.h */,
F8D6783B1160F70100CC270E /* PackIndexEntry.m */,
F8D678291160F5D000CC270E /* PackSetSet.h */,
F8D6782A1160F5D000CC270E /* PackSetSet.m */,
F8D6782B1160F5D000CC270E /* PackSet.h */,
F8D6782C1160F5D000CC270E /* PackSet.m */,
F8D677FE1160F26A00CC270E /* Restorer.h */,
F8D677FF1160F26A00CC270E /* Restorer.m */,
F8D67F6E1161443600CC270E /* RestoreNode.h */,
F8D67F6F1161443600CC270E /* RestoreNode.m */,
F8D678131160F4E300CC270E /* S3Fark.h */,
F8D678141160F4E300CC270E /* S3Fark.m */,
F8D678151160F4E300CC270E /* S3Repo.h */,
F8D678161160F4E300CC270E /* S3Repo.m */,
F8D6785D1160F7CF00CC270E /* Tree.h */,
F8D6785E1160F7CF00CC270E /* Tree.m */,
F8D67CE91161363A00CC270E /* FileAttributes.h */,
F8D67CEA1161363A00CC270E /* FileAttributes.m */,
F8D67CF01161366100CC270E /* XAttrSet.h */,
F8D67CF11161366100CC270E /* XAttrSet.m */,
);
name = Source;
sourceTree = "<group>";
};
08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = {
isa = PBXGroup;
children = (
08FB779EFE84155DC02AAC07 /* Foundation.framework */,
F805B8921160EB4E007EC01E /* SystemConfiguration.framework */,
F805B8981160EB71007EC01E /* Security.framework */,
F805B8A01160EBAA007EC01E /* CoreFoundation.framework */,
F805B8BB1160EC38007EC01E /* SecurityFoundation.framework */,
F805B8C11160EC41007EC01E /* Security.framework */,
F805B8C51160EC4E007EC01E /* Cocoa.framework */,
F805B8CD1160ECD7007EC01E /* CoreServices.framework */,
F805B8881160EB39007EC01E /* libcrypto.dylib */,
F805B88A1160EB39007EC01E /* libicucore.dylib */,
F805B88E1160EB45007EC01E /* libssl.dylib */,
);
name = "External Frameworks and Libraries";
sourceTree = "<group>";
};
1AB674ADFE9D54B511CA2CBB /* Products */ = {
isa = PBXGroup;
children = (
8DD76FA10486AA7600D96B5E /* arq_restore */,
);
name = Products;
sourceTree = "<group>";
};
F805B7401160DCFE007EC01E /* plist */ = {
isa = PBXGroup;
children = (
F805B7411160DCFE007EC01E /* ArrayNode.h */,
F805B7421160DCFE007EC01E /* ArrayNode.m */,
F805B7431160DCFE007EC01E /* BooleanNode.h */,
F805B7441160DCFE007EC01E /* BooleanNode.m */,
F805B7451160DCFE007EC01E /* DictNode.h */,
F805B7461160DCFE007EC01E /* DictNode.m */,
F805B7471160DCFE007EC01E /* IntegerNode.h */,
F805B7481160DCFE007EC01E /* IntegerNode.m */,
F805B7491160DCFE007EC01E /* PListNode.h */,
F805B74A1160DCFE007EC01E /* PListNodeType.h */,
F805B74B1160DCFE007EC01E /* RealNode.h */,
F805B74C1160DCFE007EC01E /* RealNode.m */,
F805B74D1160DCFE007EC01E /* StringNode.h */,
F805B74E1160DCFE007EC01E /* StringNode.m */,
F805B74F1160DCFE007EC01E /* XMLPListReader.h */,
F805B7501160DCFE007EC01E /* XMLPListReader.m */,
F805B7511160DCFE007EC01E /* XMLPListWriter.h */,
F805B7521160DCFE007EC01E /* XMLPListWriter.m */,
);
path = plist;
sourceTree = "<group>";
};
F805B7651160DD60007EC01E /* s3 */ = {
isa = PBXGroup;
children = (
F805B7681160DD60007EC01E /* HTTPConnection_S3.h */,
F805B7691160DD60007EC01E /* HTTPConnection_S3.m */,
F805B76A1160DD60007EC01E /* NSError_S3.h */,
F805B76B1160DD60007EC01E /* NSError_S3.m */,
F805B76C1160DD60007EC01E /* PathReceiver.h */,
F805B76D1160DD60007EC01E /* PathReceiver.m */,
F805B76E1160DD60007EC01E /* S3AuthorizationParameters.h */,
F805B76F1160DD60007EC01E /* S3AuthorizationParameters.m */,
F805B7701160DD60007EC01E /* S3AuthorizationProvider.h */,
F805B7711160DD60007EC01E /* S3AuthorizationProvider.m */,
F805B7721160DD60007EC01E /* S3Lister.h */,
F805B7731160DD60007EC01E /* S3Lister.m */,
F805B7741160DD60007EC01E /* S3ObjectMetadata.h */,
F805B7751160DD60007EC01E /* S3ObjectMetadata.m */,
F805B7761160DD60007EC01E /* S3ObjectReceiver.h */,
F805B7771160DD60007EC01E /* S3ObjectReceiver.m */,
F805B77A1160DD60007EC01E /* S3Receiver.h */,
F805B77B1160DD60007EC01E /* S3Request.h */,
F805B77C1160DD60007EC01E /* S3Request.m */,
F805B77D1160DD60007EC01E /* S3Service.h */,
F805B77E1160DD60007EC01E /* S3Service.m */,
F805B77F1160DD60007EC01E /* S3Signature.h */,
F805B7801160DD60007EC01E /* S3Signature.m */,
);
path = s3;
sourceTree = "<group>";
};
F805B7A61160DEF2007EC01E /* shared */ = {
isa = PBXGroup;
children = (
F8D6788A1160F8E500CC270E /* BinarySHA1.h */,
F8D6788B1160F8E500CC270E /* BinarySHA1.m */,
F805B7D61160E456007EC01E /* BlobACL.h */,
F805B7D71160E456007EC01E /* BlobACL.m */,
F805B7B81160E3AF007EC01E /* Blob.h */,
F805B7B91160E3AF007EC01E /* Blob.m */,
F805B85E1160E9F0007EC01E /* DNS_SDErrors.h */,
F805B85F1160E9F0007EC01E /* DNS_SDErrors.m */,
F8D67D031161384100CC270E /* FileACL.h */,
F8D67D041161384100CC270E /* FileACL.m */,
F805B7DE1160E48B007EC01E /* NSErrorCodes.h */,
F8D678871160F8CD00CC270E /* NSString_extra.h */,
F8D678881160F8CD00CC270E /* NSString_extra.m */,
F805B8631160EA15007EC01E /* NSXMLNode_extra.h */,
F805B8641160EA15007EC01E /* NSXMLNode_extra.m */,
F8D67D051161384100CC270E /* OSStatusDescription.h */,
F8D67D061161384100CC270E /* OSStatusDescription.m */,
F805B7FC1160E764007EC01E /* RFC822.h */,
F805B7FD1160E764007EC01E /* RFC822.m */,
F805B7A71160DEF2007EC01E /* RegexKitLite.h */,
F805B7A81160DEF2007EC01E /* RegexKitLite.m */,
F805B7DF1160E48B007EC01E /* ServerBlob.h */,
F805B7E01160E48B007EC01E /* ServerBlob.m */,
F8D6763E1160F22800CC270E /* SetNSError.h */,
);
path = shared;
sourceTree = "<group>";
};
F805B7C91160E445007EC01E /* http */ = {
isa = PBXGroup;
children = (
F805B7CA1160E445007EC01E /* HTTP.h */,
F805B7CB1160E445007EC01E /* HTTPConnection.h */,
F805B7CC1160E445007EC01E /* HTTPConnection.m */,
F805B7CD1160E445007EC01E /* HTTPRequest.h */,
F805B7CE1160E445007EC01E /* HTTPRequest.m */,
F805B7CF1160E445007EC01E /* HTTPResponse.h */,
F805B7D01160E445007EC01E /* HTTPResponse.m */,
F805B7F91160E73D007EC01E /* RFC2616DateFormatter.h */,
F805B7FA1160E73D007EC01E /* RFC2616DateFormatter.m */,
);
path = http;
sourceTree = "<group>";
};
F805B8081160E7A1007EC01E /* io */ = {
isa = PBXGroup;
children = (
F8D678A31160FA5F00CC270E /* BooleanIO.h */,
F8D678A41160FA5F00CC270E /* BooleanIO.m */,
F805B8331160E882007EC01E /* BufferedInputStream.h */,
F805B8561160E9C9007EC01E /* CFStreamInputStream.h */,
F805B8571160E9C9007EC01E /* CFStreamInputStream.m */,
F805B8581160E9C9007EC01E /* CFStreamOutputStream.h */,
F805B8591160E9C9007EC01E /* CFStreamOutputStream.m */,
F805B8511160E9B0007EC01E /* CFStreamPair.h */,
F805B8521160E9B0007EC01E /* CFStreamPair.m */,
F805B8211160E857007EC01E /* ChunkedInputStream.h */,
F805B8221160E857007EC01E /* ChunkedInputStream.m */,
F8D678721160F85D00CC270E /* CryptInputStream.h */,
F8D678731160F85D00CC270E /* CryptInputStream.m */,
F8D678781160F8A000CC270E /* DataIO.h */,
F8D678791160F8A000CC270E /* DataIO.m */,
F805B86D1160EAC1007EC01E /* DataInputStreamFactory.h */,
F805B86E1160EAC1007EC01E /* DataInputStreamFactory.m */,
F805B82D1160E86E007EC01E /* DataInputStream.h */,
F805B82E1160E86E007EC01E /* DataInputStream.m */,
F8D6787A1160F8A000CC270E /* DateIO.h */,
F8D6787B1160F8A000CC270E /* DateIO.m */,
F8D6786D1160F84600CC270E /* DecryptedInputStream.h */,
F8D6786E1160F84600CC270E /* DecryptedInputStream.m */,
F8D6787C1160F8A000CC270E /* DoubleIO.h */,
F8D6787D1160F8A000CC270E /* DoubleIO.m */,
F8D678A61160FA6A00CC270E /* EncryptedInputStream.h */,
F8D678A71160FA6A00CC270E /* EncryptedInputStream.m */,
F805B8271160E861007EC01E /* FDInputStream.h */,
F805B8281160E861007EC01E /* FDInputStream.m */,
F805B8291160E861007EC01E /* FDOutputStream.h */,
F805B82A1160E861007EC01E /* FDOutputStream.m */,
F8D678751160F86E00CC270E /* FileInputStream.h */,
F8D678761160F86E00CC270E /* FileInputStream.m */,
F8D6789E1160FA4800CC270E /* FileInputStreamFactory.h */,
F8D6789F1160FA4800CC270E /* FileInputStreamFactory.m */,
F8D6789B1160FA3900CC270E /* FileOutputStream.h */,
F8D6789C1160FA3900CC270E /* FileOutputStream.m */,
F805B8231160E857007EC01E /* FixedLengthInputStream.h */,
F805B8241160E857007EC01E /* FixedLengthInputStream.m */,
F805B8661160EA36007EC01E /* InputStreamFactory.h */,
F805B8191160E838007EC01E /* InputStreams.h */,
F805B81A1160E838007EC01E /* InputStreams.m */,
F805B80B1160E7C3007EC01E /* InputStream.h */,
F8D6787E1160F8A000CC270E /* IntegerIO.h */,
F8D6787F1160F8A000CC270E /* IntegerIO.m */,
F805B8391160E8DD007EC01E /* NSData-InputStream.h */,
F805B83A1160E8DD007EC01E /* NSData-InputStream.m */,
F8D678981160FA2A00CC270E /* NSFileManager_extra.h */,
F8D678991160FA2A00CC270E /* NSFileManager_extra.m */,
F805B8361160E8A0007EC01E /* OutputStream.h */,
F805B83C1160E900007EC01E /* StreamPair.h */,
F805B83D1160E900007EC01E /* StreamPairFactory.h */,
F805B83E1160E900007EC01E /* StreamPairFactory.m */,
F805B8401160E90F007EC01E /* Streams.h */,
F805B8411160E90F007EC01E /* Streams.m */,
F8D678801160F8A000CC270E /* StringIO.h */,
F8D678811160F8A000CC270E /* StringIO.m */,
F805B8301160E878007EC01E /* Writer.h */,
F805B8311160E878007EC01E /* Writer.m */,
);
path = io;
sourceTree = "<group>";
};
F805B8671160EA7C007EC01E /* crypto */ = {
isa = PBXGroup;
children = (
F8D6781B1160F4FD00CC270E /* SHA1Hash.h */,
F8D6781C1160F4FD00CC270E /* SHA1Hash.m */,
F805B8681160EA83007EC01E /* NSData-Base64Extensions.h */,
F805B8691160EA83007EC01E /* NSData-Base64Extensions.m */,
F8D678631160F7FE00CC270E /* NSData-Encrypt.h */,
F8D678641160F7FE00CC270E /* NSData-Encrypt.m */,
F8D678681160F81100CC270E /* OpenSSL.h */,
F8D678691160F81100CC270E /* OpenSSL.m */,
);
path = crypto;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
8DD76F960486AA7600D96B5E /* arq_restore */ = {
isa = PBXNativeTarget;
buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "arq_restore" */;
buildPhases = (
8DD76F990486AA7600D96B5E /* Sources */,
8DD76F9B0486AA7600D96B5E /* Frameworks */,
8DD76F9E0486AA7600D96B5E /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = arq_restore;
productInstallPath = "$(HOME)/bin";
productName = arq_restore;
productReference = 8DD76FA10486AA7600D96B5E /* arq_restore */;
productType = "com.apple.product-type.tool";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "arq_restore" */;
compatibilityVersion = "Xcode 3.1";
hasScannedForEncodings = 1;
mainGroup = 08FB7794FE84155DC02AAC07 /* arq_restore */;
projectDirPath = "";
projectRoot = "";
targets = (
8DD76F960486AA7600D96B5E /* arq_restore */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
8DD76F990486AA7600D96B5E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8DD76F9A0486AA7600D96B5E /* arq_restore.m in Sources */,
F805B54D1160D3E6007EC01E /* ArqRestoreCommand.m in Sources */,
F805B7211160D9C2007EC01E /* HSLog.m in Sources */,
F805B7301160DBE9007EC01E /* ArqFolder.m in Sources */,
F805B7531160DCFE007EC01E /* ArrayNode.m in Sources */,
F805B7541160DCFE007EC01E /* BooleanNode.m in Sources */,
F805B7551160DCFE007EC01E /* DictNode.m in Sources */,
F805B7561160DCFE007EC01E /* IntegerNode.m in Sources */,
F805B7571160DCFE007EC01E /* RealNode.m in Sources */,
F805B7581160DCFE007EC01E /* StringNode.m in Sources */,
F805B7591160DCFE007EC01E /* XMLPListReader.m in Sources */,
F805B75A1160DCFE007EC01E /* XMLPListWriter.m in Sources */,
F805B7821160DD60007EC01E /* HTTPConnection_S3.m in Sources */,
F805B7831160DD60007EC01E /* NSError_S3.m in Sources */,
F805B7841160DD60007EC01E /* PathReceiver.m in Sources */,
F805B7851160DD60007EC01E /* S3AuthorizationParameters.m in Sources */,
F805B7861160DD60007EC01E /* S3AuthorizationProvider.m in Sources */,
F805B7871160DD60007EC01E /* S3Lister.m in Sources */,
F805B7881160DD60007EC01E /* S3ObjectMetadata.m in Sources */,
F805B7891160DD60007EC01E /* S3ObjectReceiver.m in Sources */,
F805B78B1160DD60007EC01E /* S3Request.m in Sources */,
F805B78C1160DD60007EC01E /* S3Service.m in Sources */,
F805B78D1160DD60007EC01E /* S3Signature.m in Sources */,
F805B7A91160DEF2007EC01E /* RegexKitLite.m in Sources */,
F805B7BA1160E3AF007EC01E /* Blob.m in Sources */,
F805B7D11160E445007EC01E /* HTTPConnection.m in Sources */,
F805B7D21160E445007EC01E /* HTTPRequest.m in Sources */,
F805B7D31160E445007EC01E /* HTTPResponse.m in Sources */,
F805B7D81160E456007EC01E /* BlobACL.m in Sources */,
F805B7E11160E48B007EC01E /* ServerBlob.m in Sources */,
F805B7FB1160E73D007EC01E /* RFC2616DateFormatter.m in Sources */,
F805B7FE1160E764007EC01E /* RFC822.m in Sources */,
F805B81B1160E838007EC01E /* InputStreams.m in Sources */,
F805B8251160E857007EC01E /* ChunkedInputStream.m in Sources */,
F805B8261160E857007EC01E /* FixedLengthInputStream.m in Sources */,
F805B82B1160E861007EC01E /* FDInputStream.m in Sources */,
F805B82C1160E861007EC01E /* FDOutputStream.m in Sources */,
F805B82F1160E86E007EC01E /* DataInputStream.m in Sources */,
F805B8321160E878007EC01E /* Writer.m in Sources */,
F805B83B1160E8DD007EC01E /* NSData-InputStream.m in Sources */,
F805B83F1160E900007EC01E /* StreamPairFactory.m in Sources */,
F805B8421160E90F007EC01E /* Streams.m in Sources */,
F805B8531160E9B0007EC01E /* CFStreamPair.m in Sources */,
F805B85A1160E9C9007EC01E /* CFStreamInputStream.m in Sources */,
F805B85B1160E9C9007EC01E /* CFStreamOutputStream.m in Sources */,
F805B8601160E9F0007EC01E /* DNS_SDErrors.m in Sources */,
F805B8651160EA15007EC01E /* NSXMLNode_extra.m in Sources */,
F805B86A1160EA83007EC01E /* NSData-Base64Extensions.m in Sources */,
F805B86F1160EAC1007EC01E /* DataInputStreamFactory.m in Sources */,
F8D678001160F26A00CC270E /* Restorer.m in Sources */,
F8D678171160F4E300CC270E /* S3Fark.m in Sources */,
F8D678181160F4E300CC270E /* S3Repo.m in Sources */,
F8D6781D1160F4FD00CC270E /* SHA1Hash.m in Sources */,
F8D6782D1160F5D000CC270E /* PackSetSet.m in Sources */,
F8D6782E1160F5D000CC270E /* PackSet.m in Sources */,
F8D678331160F62E00CC270E /* ArqUserLibrary.m in Sources */,
F8D6783C1160F70100CC270E /* PackIndexEntry.m in Sources */,
F8D678451160F74A00CC270E /* DiskPack.m in Sources */,
F8D678461160F74A00CC270E /* DiskPackIndex.m in Sources */,
F8D6785F1160F7CF00CC270E /* Commit.m in Sources */,
F8D678601160F7CF00CC270E /* Tree.m in Sources */,
F8D678651160F7FE00CC270E /* NSData-Encrypt.m in Sources */,
F8D6786A1160F81100CC270E /* OpenSSL.m in Sources */,
F8D6786F1160F84600CC270E /* DecryptedInputStream.m in Sources */,
F8D678741160F85D00CC270E /* CryptInputStream.m in Sources */,
F8D678771160F86E00CC270E /* FileInputStream.m in Sources */,
F8D678821160F8A000CC270E /* DataIO.m in Sources */,
F8D678831160F8A000CC270E /* DateIO.m in Sources */,
F8D678841160F8A000CC270E /* DoubleIO.m in Sources */,
F8D678851160F8A000CC270E /* IntegerIO.m in Sources */,
F8D678861160F8A000CC270E /* StringIO.m in Sources */,
F8D678891160F8CD00CC270E /* NSString_extra.m in Sources */,
F8D6788C1160F8E500CC270E /* BinarySHA1.m in Sources */,
F8D6789A1160FA2A00CC270E /* NSFileManager_extra.m in Sources */,
F8D6789D1160FA3900CC270E /* FileOutputStream.m in Sources */,
F8D678A01160FA4800CC270E /* FileInputStreamFactory.m in Sources */,
F8D678A51160FA5F00CC270E /* BooleanIO.m in Sources */,
F8D678A81160FA6A00CC270E /* EncryptedInputStream.m in Sources */,
F8D678AF1160FAD900CC270E /* CommitFailedFile.m in Sources */,
F8D678B81160FB2100CC270E /* Node.m in Sources */,
F8D67CEB1161363A00CC270E /* FileAttributes.m in Sources */,
F8D67CF21161366100CC270E /* XAttrSet.m in Sources */,
F8D67D071161384100CC270E /* FileACL.m in Sources */,
F8D67D081161384100CC270E /* OSStatusDescription.m in Sources */,
F8D67F701161443600CC270E /* RestoreNode.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
1DEB927508733DD40010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = arq_restore_Prefix.pch;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
HEADER_SEARCH_PATHS = "";
INSTALL_PATH = /usr/local/bin;
PRODUCT_NAME = arq_restore;
SDKROOT = macosx10.5;
};
name = Debug;
};
1DEB927608733DD40010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = arq_restore_Prefix.pch;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
HEADER_SEARCH_PATHS = "";
INSTALL_PATH = /usr/local/bin;
PRODUCT_NAME = arq_restore;
SDKROOT = macosx10.5;
};
name = Release;
};
1DEB927908733DD40010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
ONLY_ACTIVE_ARCH = YES;
PREBINDING = NO;
SDKROOT = macosx10.6;
};
name = Debug;
};
1DEB927A08733DD40010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
PREBINDING = NO;
SDKROOT = macosx10.6;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "arq_restore" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB927508733DD40010E9CD /* Debug */,
1DEB927608733DD40010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "arq_restore" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB927908733DD40010E9CD /* Debug */,
1DEB927A08733DD40010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
}

4
arq_restore_Prefix.pch Normal file
View file

@ -0,0 +1,4 @@
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#import "HSLog.h"
#endif

8
arq_restore_usage Normal file
View file

@ -0,0 +1,8 @@
usage:
- set ARQ_ACCESS_KEY and ARQ_SECRET_KEY
- arq_restore
- /akiaiyuk3n3tme6l4hfa.com.haystacksoftware.arq.eu/1C493DC6-FB2C-4EEC-8356-838DABE3AE2C/3AA39F05-4C47-4CE5-839A-3A28255DD91E path=/Users/stefan
- set ARQ_ENCRYPTION_PASSWORD
- arq_restore 3AA39F05-4C47-4CE5-839A-3A28255DD91E
- restores 'stefan' folder to local directory, unless 'stefan' folder already exists

View file

@ -0,0 +1,28 @@
// Copyright (c) 2006 Dave Dribin (http://www.dribin.org/dave/)
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
@interface NSData (Base64)
- (NSString *) encodeBase64;
- (NSString *) encodeBase64WithNewlines: (BOOL) encodeWithNewlines;
@end

View file

@ -0,0 +1,57 @@
// Copyright (c) 2006 Dave Dribin (http://www.dribin.org/dave/)
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "NSData-Base64Extensions.h"
#include <openssl/bio.h>
#include <openssl/evp.h>
@implementation NSData (Base64)
- (NSString *) encodeBase64;
{
return [self encodeBase64WithNewlines: YES];
}
- (NSString *) encodeBase64WithNewlines: (BOOL) encodeWithNewlines;
{
// Create a memory buffer which will contain the Base64 encoded string
BIO * mem = BIO_new(BIO_s_mem());
// Push on a Base64 filter so that writing to the buffer encodes the data
BIO * b64 = BIO_new(BIO_f_base64());
if (!encodeWithNewlines)
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
mem = BIO_push(b64, mem);
// Encode all the data
BIO_write(mem, [self bytes], [self length]);
BIO_flush(mem);
// Create a new string from the data in the memory buffer
char * base64Pointer;
long base64Length = BIO_get_mem_data(mem, &base64Pointer);
NSString *base64String = [[[NSString alloc] initWithCString:base64Pointer length:base64Length] autorelease];
// Clean up and go home
BIO_free_all(mem);
return base64String;
}
@end

41
crypto/NSData-Encrypt.h Normal file
View file

@ -0,0 +1,41 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#define ARQ_DEFAULT_CIPHER_NAME @"aes256"
@interface NSData (Encrypt)
- (NSData *)encryptWithCipher:(NSString *)cipherName key:(NSString *)key error:(NSError **)error;
- (NSData *)decryptWithCipher:(NSString *)cipherName key:(NSString *)key error:(NSError **)error;
@end

190
crypto/NSData-Encrypt.m Normal file
View file

@ -0,0 +1,190 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <openssl/evp.h>
#import "NSData-Encrypt.h"
#import "OpenSSL.h"
#import "SetNSError.h"
@interface Crypter : NSObject {
NSData *data;
const EVP_CIPHER *cipher;
EVP_CIPHER_CTX cipherContext;
unsigned char evp_key[EVP_MAX_KEY_LENGTH];
unsigned char iv[EVP_MAX_IV_LENGTH];
}
- (id)initWithCipher:(NSString *)cipherName key:(NSString *)key data:(NSData *)theData error:(NSError **)error;
- (NSData *)encrypt:(NSError **)error;
- (NSData *)decrypt:(NSError **)error;
@end
@implementation Crypter
- (id)initWithCipher:(NSString *)cipherName key:(NSString *)key data:(NSData *)theData error:(NSError **)error {
if (self = [super init]) {
BOOL ret = NO;
do {
data = [theData retain];
if ([data length] > 0) {
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
if ([keyData length] > EVP_MAX_KEY_LENGTH) {
SETNSERROR(@"NSDataEncryptErrorDomain", -1, @"encryption key must be less than or equal to %d bytes", EVP_MAX_KEY_LENGTH);
break;
}
if (![OpenSSL initializeSSL:error]) {
break;
}
cipher = EVP_get_cipherbyname([cipherName UTF8String]);
if (!cipher) {
SETNSERROR(@"NSDataEncryptErrorDomain", -1, @"failed to load %@ cipher: %@", cipherName, [OpenSSL errorMessage]);
break;
}
evp_key[0] = 0;
EVP_BytesToKey(cipher, EVP_md5(), NULL, [keyData bytes], [keyData length], 1, evp_key, iv);
EVP_CIPHER_CTX_init(&cipherContext);
}
ret = YES;
} while(0);
if (!ret) {
[self release];
self = nil;
}
}
return self;
}
- (void)dealloc {
if ([data length] > 0) {
EVP_CIPHER_CTX_cleanup(&cipherContext);
}
[data release];
[super dealloc];
}
- (NSData *)encrypt:(NSError **)error {
if ([data length] == 0) {
return [NSData data];
}
if (!EVP_EncryptInit(&cipherContext, cipher, evp_key, iv)) {
SETNSERROR(@"NSDataEncryptErrorDomain", -1, @"EVP_EncryptInit: %@", [OpenSSL errorMessage]);
return nil;
}
EVP_CIPHER_CTX_set_key_length(&cipherContext, EVP_MAX_KEY_LENGTH);
// Need room for data + cipher block size - 1.
unsigned char *outbuf = (unsigned char *)calloc([data length] + EVP_CIPHER_CTX_block_size(&cipherContext) - 1, sizeof(unsigned char));
int outlen;
if (!EVP_EncryptUpdate(&cipherContext, outbuf, &outlen, [data bytes], [data length])) {
SETNSERROR(@"NSDataEncryptErrorDomain", -1, @"EVP_EncryptUpdate: %@", [OpenSSL errorMessage]);
return nil;
}
int templen;
if (!EVP_EncryptFinal(&cipherContext, outbuf + outlen, &templen)) {
SETNSERROR(@"NSDataEncryptErrorDomain", -1, @"EVP_EncryptFinal: %@", [OpenSSL errorMessage]);
return nil;
}
outlen += templen;
NSData *ret = [[[NSData alloc] initWithBytes:outbuf length:outlen] autorelease];
free(outbuf);
return ret;
}
- (NSData *)decrypt:(NSError **)error {
if ([data length] == 0) {
return [NSData data];
}
int inlen = [data length];
unsigned char *input = (unsigned char *)[data bytes];
// Check for 8-byte salt in encrypted data and skip it.
if (inlen > 8+8 && strncmp((const char *)input, "Salted__", 8) == 0) {
input += 16;
inlen -= 16;
}
if (!EVP_DecryptInit(&cipherContext, cipher, evp_key, iv)) {
SETNSERROR(@"NSDataDecryptErrorDomain", -1, @"EVP_DecryptInit: %@", [OpenSSL errorMessage]);
return nil;
}
EVP_CIPHER_CTX_set_key_length(&cipherContext, EVP_MAX_KEY_LENGTH);
// The data buffer passed to EVP_DecryptUpdate() should have sufficient room for
// (input_length + cipher_block_size) bytes unless the cipher block size is 1 in which
// case input_length bytes is sufficient.
unsigned char *outbuf;
if(EVP_CIPHER_CTX_block_size(&cipherContext) > 1) {
outbuf = (unsigned char *)calloc(inlen + EVP_CIPHER_CTX_block_size(&cipherContext), sizeof(unsigned char));
} else {
outbuf = (unsigned char *)calloc(inlen, sizeof(unsigned char));
}
int outlen;
if (!EVP_DecryptUpdate(&cipherContext, outbuf, &outlen, input, inlen)) {
SETNSERROR(@"NSDataEncryptErrorDomain", -1, @"EVP_DecryptUpdate: %@", [OpenSSL errorMessage]);
return nil;
}
int templen;
if (!EVP_DecryptFinal(&cipherContext, outbuf + outlen, &templen)) {
SETNSERROR(@"NSDataEncryptErrorDomain", -1, @"EVP_DecryptFinal: %@", [OpenSSL errorMessage]);
return nil;
}
outlen += templen;
NSData *ret = [[[NSData alloc] initWithBytes:outbuf length:outlen] autorelease];
free(outbuf);
return ret;
}
@end
@implementation NSData (Encrypt)
- (NSData *)encryptWithCipher:(NSString *)cipherName key:(NSString *)key error:(NSError **)error {
NSData *ret = nil;
Crypter *crypter = [[Crypter alloc] initWithCipher:cipherName key:key data:self error:error];
if (crypter != nil) {
ret = [crypter encrypt:error];
[crypter release];
}
return ret;
}
- (NSData *)decryptWithCipher:(NSString *)cipherName key:(NSString *)key error:(NSError **)error {
NSData *ret = nil;
Crypter *crypter = [[Crypter alloc] initWithCipher:cipherName key:key data:self error:error];
if (crypter != nil) {
ret = [crypter decrypt:error];
[crypter release];
}
return ret;
}
@end

43
crypto/OpenSSL.h Normal file
View file

@ -0,0 +1,43 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import <openssl/ssl.h>
@interface OpenSSL : NSObject {
}
+ (BOOL)initializeSSL:(NSError **)error;
+ (SSL_CTX *)context;
+ (NSString *)errorMessage;
@end

76
crypto/OpenSSL.m Normal file
View file

@ -0,0 +1,76 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <openssl/err.h>
#import <openssl/ssl.h>
#import "OpenSSL.h"
#import "SetNSError.h"
static BOOL initialized = NO;
static SSL_CTX *ctx;
@implementation OpenSSL
+ (BOOL)initializeSSL:(NSError **)error {
if (!initialized) {
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
ERR_load_crypto_strings();
ctx = SSL_CTX_new(SSLv23_method());
if (ctx == NULL) {
SETNSERROR(@"SSLErrorDomain", -1, @"SSL_CTX_new: %@", [OpenSSL errorMessage]);
return NO;
}
initialized = YES;
}
return YES;
}
+ (SSL_CTX *)context {
return ctx;
}
+ (NSString *)errorMessage {
NSMutableString *msg = [NSMutableString string];
for (;;) {
unsigned long err = ERR_get_error();
if (err == 0) {
break;
}
if ([msg length] > 0) {
[msg appendString:@"; "];
}
[msg appendFormat:@"%s", ERR_error_string(err, NULL)];
}
if ([msg length] == 0) {
[msg appendString:@"(no error)"];
}
return msg;
}
@end

45
crypto/SHA1Hash.h Normal file
View file

@ -0,0 +1,45 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "InputStream.h"
@class Blob;
@interface SHA1Hash : NSObject {
}
+ (NSString *)hashData:(NSData *)data;
+ (NSString *)hashBlob:(Blob *)blob blobLength:(unsigned long long *)blobLength error:(NSError **)error;
+ (NSString *)hashStream:(id <InputStream>)is withlength:(uint64_t)length error:(NSError **)error;
+ (NSString *)hashFile:(NSString *)path error:(NSError **)error;
@end

134
crypto/SHA1Hash.m Normal file
View file

@ -0,0 +1,134 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "SHA1Hash.h"
#include <openssl/sha.h>
#import "FileInputStream.h"
#import "NSErrorCodes.h"
#import "Blob.h"
#import "BufferedInputStream.h"
@interface SHA1Hash (internal)
+ (NSString *)hashStream:(id <InputStream>)is error:(NSError **)error;
+ (NSString *)hashStream:(id <InputStream>)is streamLength:(unsigned long long *)streamLength error:(NSError **)error;
@end
static NSString *digest2String(unsigned char *digest) {
char *str = (char *)calloc(SHA_DIGEST_LENGTH, 2);
for (int i = 0; i < SHA_DIGEST_LENGTH; i++) {
sprintf(&(str[i*2]), "%02x", digest[i]);
}
NSString *ret = [[[NSString alloc] initWithCString:str length:SHA_DIGEST_LENGTH*2] autorelease];
free(str);
return ret;
}
@implementation SHA1Hash
+ (NSString *)hashData:(NSData *)data {
SHA_CTX ctx;
SHA1_Init(&ctx);
SHA1_Update(&ctx, [data bytes], (unsigned long)[data length]);
unsigned char md[SHA_DIGEST_LENGTH];
SHA1_Final(md, &ctx);
return digest2String(md);
}
+ (NSString *)hashBlob:(Blob *)blob blobLength:(unsigned long long *)blobLength error:(NSError **)error {
id <InputStream> is = [blob newInputStream:self];
if (is == nil) {
return nil;
}
NSString *sha1 = [SHA1Hash hashStream:is streamLength:blobLength error:error];
[is release];
return sha1;
}
+ (NSString *)hashFile:(NSString *)path error:(NSError **)error {
NSDictionary *attribs = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:error];
if (attribs == nil) {
return NO;
}
FileInputStream *fis = [[FileInputStream alloc] initWithPath:path length:[[attribs objectForKey:NSFileSize] unsignedLongLongValue]];
NSString *sha1 = [SHA1Hash hashStream:fis error:error];
[fis release];
return sha1;
}
+ (NSString *)hashStream:(id <BufferedInputStream>)bis withlength:(uint64_t)length error:(NSError **)error {
SHA_CTX ctx;
SHA1_Init(&ctx);
uint64_t received = 0;
while (received < length) {
uint64_t toRead = length - received;
NSUInteger thisLength = 0;
unsigned char *buf = [bis readMaximum:toRead length:&thisLength error:error];
if (buf == NULL) {
return nil;
}
NSAssert(thisLength > 0, @"expected more than 0 bytes");
SHA1_Update(&ctx, buf, (unsigned long)thisLength);
received += (uint64_t)thisLength;
}
unsigned char md[SHA_DIGEST_LENGTH];
SHA1_Final(md, &ctx);
return digest2String(md);
}
@end
@implementation SHA1Hash (internal)
+ (NSString *)hashStream:(id <InputStream>)is error:(NSError **)error {
unsigned long long length;
return [SHA1Hash hashStream:is streamLength:&length error:error];
}
+ (NSString *)hashStream:(id <InputStream>)is streamLength:(unsigned long long *)streamLength error:(NSError **)error {
SHA_CTX ctx;
SHA1_Init(&ctx);
*streamLength = 0;
for (;;) {
NSUInteger length = 0;
NSError *myError;
unsigned char *buf = [is read:&length error:&myError];
if (buf == NULL) {
if ([myError code] != ERROR_EOF) {
if (error != NULL) {
*error = myError;
}
return nil;
}
break; // EOF.
}
NSAssert(length > 0, @"expected more than 0 bytes");
SHA1_Update(&ctx, buf, (unsigned long)length);
*streamLength += (unsigned long long)length;
}
unsigned char md[SHA_DIGEST_LENGTH];
SHA1_Final(md, &ctx);
return digest2String(md);
}
@end

44
http/HTTP.h Normal file
View file

@ -0,0 +1,44 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define HTTP_1_1 @"1.1"
#define HTTP_INTERNAL_SERVER_ERROR (500)
#define HTTP_FORBIDDEN (403)
#define HTTP_BAD_REQUEST (400)
#define HTTP_CONFLICT (409)
#define HTTP_REQUESTED_RANGE_NOT_SATISFIABLE (416)
#define HTTP_LENGTH_REQUIRED (411)
#define HTTP_NOT_FOUND (404)
#define HTTP_MOVED_PERMANENTLY (301)
#define HTTP_MOVED_TEMPORARILY (307)
#define HTTP_SERVICE_NOT_AVAILABLE (503)

59
http/HTTPConnection.h Normal file
View file

@ -0,0 +1,59 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
@protocol InputStream;
@protocol StreamPair;
@class HTTPRequest;
@class HTTPResponse;
@interface HTTPConnection : NSObject {
id <StreamPair> streamPair;
HTTPRequest *request;
HTTPResponse *response;
}
- (id)initWithHost:(NSString *)theHost useSSL:(BOOL)isUseSSL error:(NSError **)error;
- (void)setRequestMethod:(NSString *)theRequestMethod pathInfo:(NSString *)thePathInfo queryString:(NSString *)theQueryString protocol:(NSString *)theProtocol;
- (void)setRequestHeader:(NSString *)value forKey:(NSString *)key;
- (void)setRequestHostHeader;
- (void)setRequestKeepAliveHeader;
- (void)setRequestContentDispositionHeader:(NSString *)downloadName;
- (void)setRFC822DateRequestHeader;
- (BOOL)executeRequest:(NSError **)error;
- (int)responseCode;
- (NSString *)responseHeaderForKey:(NSString *)key;
- (NSString *)responseMimeType;
- (NSString *)responseDownloadName;
- (id <InputStream>)newResponseBodyStream:(NSError **)error;
- (NSData *)slurpResponseBody:(NSError **)error;
- (void)setCloseRequested;
@end

135
http/HTTPConnection.m Normal file
View file

@ -0,0 +1,135 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "HTTPConnection.h"
#import "HTTPRequest.h"
#import "HTTPResponse.h"
#import "StreamPairFactory.h"
#import "StreamPair.h"
#import "Streams.h"
#import "RegexKitLite.h"
#import "FDOutputStream.h"
#import "FDInputStream.h"
@implementation HTTPConnection
- (id)initWithHost:(NSString *)theHost useSSL:(BOOL)isUseSSL error:(NSError **)error {
if (self = [super init]) {
streamPair = [[StreamPairFactory theFactory] newStreamPairToHost:theHost useSSL:isUseSSL error:error];
if (streamPair == nil) {
[self release];
return nil;
}
request = [[HTTPRequest alloc] initWithHost:theHost];
response = [[HTTPResponse alloc] init];
}
return self;
}
- (void)dealloc {
[streamPair release];
[request release];
[response release];
[super dealloc];
}
- (void)setRequestMethod:(NSString *)theRequestMethod pathInfo:(NSString *)thePathInfo queryString:(NSString *)theQueryString protocol:(NSString *)theProtocol {
[request setMethod:theRequestMethod];
[request setPathInfo:thePathInfo];
[request setQueryString:theQueryString];
[request setProtocol:theProtocol];
}
- (void)setRequestHeader:(NSString *)value forKey:(NSString *)key {
[request setHeader:value forKey:key];
}
- (void)setRequestHostHeader {
[request setHostHeader];
}
- (void)setRequestKeepAliveHeader {
[request setKeepAliveHeader];
}
- (void)setRequestContentDispositionHeader:(NSString *)downloadName {
[request setContentDispositionHeader:downloadName];
}
- (void)setRFC822DateRequestHeader {
[request setRFC822DateHeader];
}
- (BOOL)executeRequest:(NSError **)error {
if (![request write:streamPair error:error]) {
return NO;
}
if (![response readHead:streamPair requestMethod:[request method] error:error]) {
return NO;
}
if ([[response headerForKey:@"Connection"] isEqualToString:@"Close"]) {
HSLogDebug(@"Connection: Close header received; requesting close on %@", streamPair);
[streamPair setCloseRequested];
}
if (![[response protocol] isEqualToString:@"1.1"]) {
HSLogDebug(@"protocol %@ HTTP response; requesting close on %@", [response protocol], streamPair);
[streamPair setCloseRequested];
}
return YES;
}
- (int)responseCode {
return [response code];
}
- (NSString *)responseHeaderForKey:(NSString *)key {
return [response headerForKey:key];
}
- (NSString *)responseMimeType {
return [response headerForKey:@"Content-Type"];
}
- (NSString *)responseDownloadName {
NSString *downloadName = nil;
NSString *contentDisposition = [response headerForKey:@"Content-Disposition"];
if (contentDisposition != nil) {
NSRange filenameRange = [contentDisposition rangeOfRegex:@"attachment;filename=(.+)" capture:1];
if (filenameRange.location != NSNotFound) {
downloadName = [contentDisposition substringWithRange:filenameRange];
}
}
return downloadName;
}
- (id <InputStream>)newResponseBodyStream:(NSError **)error {
return [response newResponseInputStream:streamPair error:error];
}
- (NSData *)slurpResponseBody:(NSError **)error {
id <InputStream> is = [self newResponseBodyStream:error];
if (is == nil) {
return nil;
}
NSData *data = [is slurp:error];
[is release];
return data;
}
- (void)setCloseRequested {
[streamPair setCloseRequested];
}
@end

61
http/HTTPRequest.h Normal file
View file

@ -0,0 +1,61 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
@protocol OutputStream;
@class RFC2616DateFormatter;
@interface HTTPRequest : NSObject {
NSString *host;
NSString *method;
NSString *pathInfo;
NSString *queryString;
NSString *protocol;
NSMutableDictionary *headers;
RFC2616DateFormatter *dateFormatter;
}
@property (copy, readonly) NSString *host;
@property (copy) NSString *method;
@property (copy) NSString *pathInfo;
@property (copy) NSString *queryString;
@property (copy) NSString *protocol;
- (id)initWithHost:(NSString *)host;
- (void)setHeader:(NSString *)value forKey:(NSString *)key;
- (void)setHostHeader;
- (void)setKeepAliveHeader;
- (void)setRFC822DateHeader;
- (void)setContentDispositionHeader:(NSString *)downloadName;
- (NSString *)headerForKey:(NSString *)key;
- (NSArray *)allHeaderKeys;
- (BOOL)write:(id <OutputStream>)os error:(NSError **)error;
@end

128
http/HTTPRequest.m Normal file
View file

@ -0,0 +1,128 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "HTTPRequest.h"
#import "Writer.h"
#import "RFC2616DateFormatter.h"
#define DEFAULT_HTTP_PORT (80)
@implementation HTTPRequest
@synthesize host, method, pathInfo, queryString, protocol;
- (id)initWithHost:(NSString *)theHost {
if (self = [super init]) {
host = [theHost retain];
headers = [[NSMutableDictionary alloc] init];
dateFormatter = [[RFC2616DateFormatter alloc] init];
}
return self;
}
- (void)dealloc {
[host release];
[method release];
[pathInfo release];
[queryString release];
[protocol release];
[headers release];
[dateFormatter release];
[super dealloc];
}
- (void)setHeader:(NSString *)value forKey:(NSString *)key {
[headers setValue:value forKey:key];
}
- (void)setHostHeader {
[headers setValue:host forKey:@"Host"];
}
- (void)setKeepAliveHeader {
[headers setValue:@"keep-alive" forKey:@"Connection"];
}
- (void)setRFC822DateHeader {
[headers setValue:[dateFormatter rfc2616StringFromDate:[NSDate date]] forKey:@"Date"];
}
- (void)setContentDispositionHeader:(NSString *)downloadName {
if (downloadName != nil) {
NSString *encodedFilename = [NSString stringWithFormat:@"\"%@\"", [downloadName stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\\\""]];
NSString *contentDisposition = [NSString stringWithFormat:@"attachment;filename=%@", encodedFilename];
[self setHeader:contentDisposition forKey:@"Content-Disposition"];
}
}
- (NSString *)headerForKey:(NSString *)key {
return [headers objectForKey:key];
}
- (NSArray *)allHeaderKeys {
return [headers allKeys];
}
- (BOOL)write:(id <OutputStream>)os error:(NSError **)error {
HSLogTrace(@"writing %@", self);
Writer *writer = [[Writer alloc] initWithOutputStream:os];
BOOL ret = NO;
do {
if (![writer write:method error:error]
|| ![writer write:@" " error:error]
|| ![writer write:pathInfo error:error]) {
break;
}
if (queryString != nil) {
if (![writer write:queryString error:error]) {
break;
}
}
if (![writer write:@" HTTP/" error:error]
|| ![writer write:protocol error:error]
|| ![writer write:@"\r\n" error:error]) {
break;
}
for (NSString *key in [headers allKeys]) {
HSLogTrace(@"header: %@ = %@", key, [headers objectForKey:key]);
if (![writer write:key error:error]
|| ![writer write:@": " error:error]
|| ![writer write:[headers objectForKey:key] error:error]
|| ![writer write:@"\r\n" error:error]) {
break;
}
}
if (![writer write:@"\r\n" error:error]) {
break;
}
ret = YES;
} while(0);
[writer release];
return ret;
}
#pragma mark NSObject protocol
- (NSString *)description {
return [NSString stringWithFormat:@"<HTTPRequest: %@ %@%@ HTTP/%@>", method, pathInfo, (queryString != nil ? queryString : @""), protocol];
}
@end

51
http/HTTPResponse.h Normal file
View file

@ -0,0 +1,51 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
@protocol BufferedInputStream;
@protocol InputStream;
@class FDInputStream;
@interface HTTPResponse : NSObject {
int code;
NSString *protocol;
NSMutableDictionary *headers;
NSString *requestMethod;
}
- (id)init;
- (BOOL)readHead:(id <BufferedInputStream>)is requestMethod:(NSString *)requestMethod error:(NSError **)error;
- (int)code;
- (NSString *)protocol;
- (NSString *)headerForKey:(NSString *)key;
- (unsigned long long)contentLength;
- (id <InputStream>)newResponseInputStream:(id <BufferedInputStream>)underlyingStream error:(NSError **)error;
@end

155
http/HTTPResponse.m Normal file
View file

@ -0,0 +1,155 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "HTTPResponse.h"
#import "RegexKitLite.h"
#import "SetNSError.h"
#import "BufferedInputStream.h"
#import "DataInputStream.h"
#import "ChunkedInputStream.h"
#import "FixedLengthInputStream.h"
#import "InputStream.h"
#import "InputStreams.h"
#import "NSData-InputStream.h"
#import "FDInputStream.h"
#define MAX_HTTP_STATUS_LINE_LENGTH (8192)
#define MAX_HTTP_HEADER_LINE_LENGTH (8192)
@implementation HTTPResponse
- (id)init {
if (self = [super init]) {
headers = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)dealloc {
[requestMethod release];
[protocol release];
[headers release];
[super dealloc];
}
- (int)code {
return code;
}
- (NSString *)protocol {
return protocol;
}
- (NSString *)headerForKey:(NSString *)key {
return [headers objectForKey:key];
}
- (unsigned long long)contentLength {
long long contentLength = 0;
NSString *str = [self headerForKey:@"Content-Length"];
if (str != nil) {
NSScanner *scanner = [NSScanner scannerWithString:str];
if (![scanner scanLongLong:&contentLength]) {
HSLogWarn(@"unable to scan Content-Length %@", str);
}
}
return (unsigned long long)contentLength;
}
- (id <InputStream>)newResponseInputStream:(id <BufferedInputStream>)underlyingStream error:(NSError **)error {
id <InputStream> ret = nil;
if ([requestMethod isEqualToString:@"HEAD"] || code == 204) {
ret = [[NSData data] newInputStream];
} else {
NSString *transferEncoding = [self headerForKey:@"Transfer-Encoding"];
NSString *contentLength = [self headerForKey:@"Content-Length"];
if (transferEncoding != nil) {
if ([[transferEncoding lowercaseString] isEqualToString:@"chunked"]) {
ret = [[ChunkedInputStream alloc] initWithUnderlyingStream:underlyingStream];
} else {
SETNSERROR(@"StreamErrorDomain", -1, @"unknown Transfer-Encoding: %@", transferEncoding);
}
} else if (contentLength != nil) {
int length = [contentLength intValue];
ret = [[FixedLengthInputStream alloc] initWithUnderlyingStream:underlyingStream length:(NSUInteger)length];
} else {
/*
* FIXME: handle multipart/byteranges media type.
* See rfc2616 section 4.4 ("message length").
*/
HSLogWarn(@"response body with no content-length");
ret = [underlyingStream retain];
}
}
return ret;
}
- (BOOL)readHead:(id <BufferedInputStream>)inputStream requestMethod:(NSString *)theRequestMethod error:(NSError **)error {
[headers removeAllObjects];
[requestMethod release];
requestMethod = [theRequestMethod copy];
NSString *line = [InputStreams readLineWithCRLF:inputStream maxLength:MAX_HTTP_STATUS_LINE_LENGTH error:error];
if (line == nil) {
return NO;
}
NSString *pattern = @"^HTTP/(1.\\d)\\s+(\\d+)\\s+(.+)\r\n$";
NSRange protoRange = [line rangeOfRegex:pattern capture:1];
NSRange codeRange = [line rangeOfRegex:pattern capture:2];
if (protoRange.location == NSNotFound || codeRange.location == NSNotFound) {
SETNSERROR(@"HTTPResponseErrorDomain", -1, @"unexpected response status line: %@", line);
return NO;
}
protocol = [[line substringWithRange:protoRange] retain];
code = [[line substringWithRange:codeRange] intValue];
NSString *headerPattern = @"^([^:]+):\\s+(.+)\r\n$";
for(;;) {
line = [InputStreams readLineWithCRLF:inputStream maxLength:MAX_HTTP_HEADER_LINE_LENGTH error:error];
if (line == nil) {
return NO;
}
if ([line isEqualToString:@"\r\n"]) {
break;
}
NSRange nameRange = [line rangeOfRegex:headerPattern capture:1];
NSRange valueRange = [line rangeOfRegex:headerPattern capture:2];
if (nameRange.location == NSNotFound || valueRange.location == NSNotFound) {
SETNSERROR(@"HTTPResponseErrorDomain", -1, @"invalid response header: %@", line);
return NO;
}
NSString *name = [line substringWithRange:nameRange];
NSString *value = [line substringWithRange:valueRange];
if ([headers objectForKey:name] != nil) {
HSLogWarn(@"dumping response header %@ = %@", name, [headers objectForKey:name]);
}
[headers setObject:value forKey:name];
}
return YES;
}
#pragma mark NSObject protocol
- (NSString *)description {
return [NSString stringWithFormat:@"<HTTPResponse %p: code=%d headers=%@>", self, code, headers];
}
@end

View file

@ -0,0 +1,41 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
@interface RFC2616DateFormatter : NSObject {
NSLocale *usLocale;
NSDateFormatter *formatter;
}
- (NSString *)rfc2616StringFromDate:(NSDate *)date;
@end

View file

@ -0,0 +1,60 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "RFC2616DateFormatter.h"
@implementation RFC2616DateFormatter
- (id)init {
if (self = [super init]) {
formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss z"];
[formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
if (usLocale != nil) {
[formatter setLocale:usLocale];
} else {
HSLogWarn(@"no en_US locale installed");
}
}
return self;
}
- (void)dealloc {
[formatter release];
[usLocale release];
[super dealloc];
}
- (NSString *)rfc2616StringFromDate:(NSDate *)date {
//FIXME: If US locale isn't available, put the English words into the date yourself, according to http://www.ietf.org/rfc/rfc2616.txt
return [formatter stringFromDate:date];
}
@end

43
io/BooleanIO.h Normal file
View file

@ -0,0 +1,43 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "BufferedInputStream.h"
#import "OutputStream.h"
@interface BooleanIO : NSObject {
}
+ (void)write:(BOOL)b to:(NSMutableData *)data;
+ (BOOL)write:(BOOL)b to:(id <OutputStream>)os error:(NSError **)error;
+ (BOOL)read:(BOOL *)value from:(id <BufferedInputStream>)is error:(NSError **)error;
@end

55
io/BooleanIO.m Normal file
View file

@ -0,0 +1,55 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "BooleanIO.h"
#import "Streams.h"
@implementation BooleanIO
+ (void)write:(BOOL)b to:(NSMutableData *)data {
char c = b ? 1 : 0;
[data appendBytes:&c length:1];
}
+ (BOOL)write:(BOOL)b to:(id <OutputStream>)os error:(NSError **)error {
unsigned char c = b ? 1 : 0;
return [os write:&c length:1 error:error];
}
+ (BOOL)read:(BOOL *)value from:(id <BufferedInputStream>)is error:(NSError **)error {
*value = NO;
unsigned char *bytes = [is readExactly:1 error:error];
if (!bytes) {
return NO;
}
*value = bytes[0] != 0;
return YES;
}
@end

40
io/BufferedInputStream.h Normal file
View file

@ -0,0 +1,40 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "InputStream.h"
@protocol BufferedInputStream <InputStream>
- (unsigned char *)readExactly:(NSUInteger)exactLength error:(NSError **)error;
- (unsigned char *)readMaximum:(NSUInteger)maximum length:(NSUInteger *)length error:(NSError **)error;
- (uint64_t)bytesReceived;
@end

42
io/CFStreamInputStream.h Normal file
View file

@ -0,0 +1,42 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "BufferedInputStream.h"
@interface CFStreamInputStream : NSObject <BufferedInputStream> {
CFReadStreamRef readStream;
BOOL isOpen;
uint64_t bytesReceived;
}
- (id)initWithCFReadStream:(CFReadStreamRef)streamRef;
@end

156
io/CFStreamInputStream.m Normal file
View file

@ -0,0 +1,156 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "CFStreamInputStream.h"
#import "InputStreams.h"
#import "SetNSError.h"
#import "NSErrorCodes.h"
#import "CFStreamPair.h"
#define MY_BUF_SIZE (4096)
#define DEFAULT_READ_TIMEOUT_SECONDS (60)
@interface CFStreamInputStream (internal)
- (BOOL)open:(NSError **)error;
@end
@implementation CFStreamInputStream
- (id)initWithCFReadStream:(CFReadStreamRef)streamRef {
if (self = [super init]) {
readStream = streamRef;
CFRetain(readStream);
}
return self;
}
- (void)dealloc {
CFRelease(readStream);
[super dealloc];
}
#pragma mark InputStream
- (unsigned char *)read:(NSUInteger *)length error:(NSError **)error {
if (![self open:error]) {
return NULL;
}
unsigned char *ret = [self readMaximum:MY_BUF_SIZE length:length error:error];
if (ret != NULL) {
bytesReceived += (uint64_t)*length;
}
return ret;
}
- (NSData *)slurp:(NSError **)error {
if (![self open:error]) {
return nil;
}
return [InputStreams slurp:self error:error];
}
#pragma mark BufferedInputStream
- (unsigned char *)readExactly:(NSUInteger)exactLength error:(NSError **)error {
if (![self open:error]) {
return NULL;
}
if (exactLength > 2147483648) {
SETNSERROR(@"InputStreamErrorDomain", -1, @"absurd length %u requested", exactLength);
return NULL;
}
NSMutableData *data = [NSMutableData dataWithLength:exactLength];
unsigned char *dataBuf = [data mutableBytes];
NSUInteger total = 0;
while (total < exactLength) {
NSUInteger maximum = exactLength - total;
NSUInteger length;
unsigned char *ibuf = [self readMaximum:maximum length:&length error:error];
if (ibuf == NULL) {
return NULL;
}
NSAssert(length > 0, @"expected more than 0 bytes");
memcpy(dataBuf + total, ibuf, length);
total += length;
}
bytesReceived += (uint64_t)exactLength;
return dataBuf;
}
- (unsigned char *)readMaximum:(NSUInteger)maximum length:(NSUInteger *)length error:(NSError **)error {
if (![self open:error]) {
return NULL;
}
NSUInteger toRead = (MY_BUF_SIZE > maximum) ? maximum : MY_BUF_SIZE;
NSMutableData *data = [NSMutableData dataWithLength:toRead];
unsigned char *buf = (unsigned char *)[data mutableBytes];
CFIndex index = CFReadStreamRead(readStream, buf, toRead);
if (index == -1) {
if (error != NULL) {
CFErrorRef err = CFReadStreamCopyError(readStream);
if (err == NULL) {
SETNSERROR(@"CFStreamPairErrorDomain", -1, @"unknown network error");
} else {
*error = [CFStreamPair NSErrorWithNetworkError:err];
CFRelease(err);
}
}
return NULL;
}
if (index == 0) {
SETNSERROR(@"StreamErrorDomain", ERROR_EOF, @"EOF");
return NULL;
}
*length = (NSUInteger)index;
bytesReceived += (uint64_t)index;
return buf;
}
- (void)bytesWereNotUsed {
}
- (uint64_t)bytesReceived {
return bytesReceived;
}
@end
@implementation CFStreamInputStream (internal)
- (BOOL)open:(NSError **)error {
if (!isOpen) {
if (!CFReadStreamOpen(readStream)) {
if (error != NULL) {
CFErrorRef err = CFReadStreamCopyError(readStream);
if (err == NULL) {
SETNSERROR(@"CFStreamPairErrorDomain", -1, @"unknown network error");
} else {
*error = [CFStreamPair NSErrorWithNetworkError:err];
CFRelease(err);
}
}
return NO;
}
isOpen = YES;
}
return YES;
}
@end

42
io/CFStreamOutputStream.h Normal file
View file

@ -0,0 +1,42 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "OutputStream.h"
@interface CFStreamOutputStream : NSObject <OutputStream> {
CFWriteStreamRef writeStream;
BOOL isOpen;
unsigned long long bytesWritten;
}
- (id)initWithCFWriteStream:(CFWriteStreamRef)streamRef;
@end

86
io/CFStreamOutputStream.m Normal file
View file

@ -0,0 +1,86 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "CFStreamOutputStream.h"
#import "SetNSError.h"
#import "CFStreamPair.h"
@implementation CFStreamOutputStream
- (id)initWithCFWriteStream:(CFWriteStreamRef)streamRef {
if (self = [super init]) {
writeStream = streamRef;
CFRetain(streamRef);
}
return self;
}
- (void)dealloc {
CFWriteStreamClose(writeStream);
CFRelease(writeStream);
[super dealloc];
}
#pragma mark OutputStream
- (BOOL)write:(const unsigned char *)buf length:(NSUInteger)len error:(NSError **)error {
if (!isOpen) {
Boolean ret = CFWriteStreamOpen(writeStream);
if (ret == false) {
SETNSERROR(@"CFStreamErrorDomain", -1, @"error opening write stream");
return NO;
}
isOpen = YES;
}
CFIndex index = 0;
NSUInteger written = 0;
while ((len - written) > 0) {
write_again:
index = CFWriteStreamWrite(writeStream, &(buf[written]), len - written);
if (index == -1) {
if (error != NULL) {
CFErrorRef err = CFWriteStreamCopyError(writeStream);
if (err == NULL) {
SETNSERROR(@"CFStreamPairErrorDomain", -1, @"unknown network error");
} else {
*error = [CFStreamPair NSErrorWithNetworkError:err];
CFRelease(err);
}
}
return NO;
}
written += (unsigned long long)index;
bytesWritten += (unsigned long long)index;
}
return YES;
}
- (unsigned long long)bytesWritten {
return bytesWritten;
}
@end

51
io/CFStreamPair.h Normal file
View file

@ -0,0 +1,51 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "StreamPair.h"
@class CFStreamInputStream;
@class CFStreamOutputStream;
@interface CFStreamPair : NSObject <StreamPair> {
NSString *description;
CFStreamInputStream *is;
CFStreamOutputStream *os;
NSTimeInterval createTime;
NSTimeInterval maxLifetime;
BOOL closeRequested;
}
+ (NSString *)errorDomain;
+ (NSError *)NSErrorWithNetworkError:(CFErrorRef)err;
- (id)initWithHost:(NSString *)theHost useSSL:(BOOL)isUseSSL maxLifetime:(NSTimeInterval)theMaxLifetime;
@end

235
io/CFStreamPair.m Normal file
View file

@ -0,0 +1,235 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#import <SystemConfiguration/SystemConfiguration.h>
#import "CFStreamPair.h"
#import "CFStreamInputStream.h"
#import "CFStreamOutputStream.h"
#import "DNS_SDErrors.h"
static uint32_t HTTP_PORT = 80;
static uint32_t HTTPS_PORT = 443;
@implementation CFStreamPair
+ (NSString *)errorDomain {
return @"CFStreamPairErrorDomain";
}
+ (NSError *)NSErrorWithNetworkError:(CFErrorRef)err {
NSString *localizedDescription = @"Network error";
NSString *domain = (NSString *)CFErrorGetDomain(err);
CFIndex code = CFErrorGetCode(err);
CFDictionaryRef userInfo = CFErrorCopyUserInfo(err);
if ([domain isEqualToString:(NSString *)kCFErrorDomainCFNetwork]) {
if (code == kCFHostErrorHostNotFound) {
localizedDescription = @"host not found";
} else if (code == kCFHostErrorUnknown) {
int gaiCode = 0;
if (CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(userInfo, kCFGetAddrInfoFailureKey), kCFNumberIntType, &gaiCode)) {
HSLogDebug(@"Host lookup error: %s", gai_strerror(gaiCode));
localizedDescription = @"Could not connect to the Internet";
}
} else if (code == kCFSOCKSErrorUnknownClientVersion) {
localizedDescription = @"Unknown SOCKS client version";
} else if (code == kCFSOCKSErrorUnsupportedServerVersion) {
localizedDescription = [NSString stringWithFormat:@"Unsupported SOCKS server version (server requested version %@)", (NSString *)CFDictionaryGetValue(userInfo, kCFSOCKSVersionKey)];
} else if (code == kCFSOCKS4ErrorRequestFailed) {
localizedDescription = @"SOCKS4 request rejected or failed";
} else if (code == kCFSOCKS4ErrorIdentdFailed) {
localizedDescription = @"SOCKS4 server cannot connect to identd on the client";
} else if (code == kCFSOCKS4ErrorIdConflict) {
localizedDescription = @"SOCKS4 client and identd report different user IDs";
} else if (code == kCFSOCKS4ErrorUnknownStatusCode) {
localizedDescription = [NSString stringWithFormat:@"SOCKS4 error %@", (NSString *)CFDictionaryGetValue(userInfo, kCFSOCKSStatusCodeKey)];
} else if (code == kCFSOCKS5ErrorBadState) {
localizedDescription = @"SOCKS5 bad state";
} else if (code == kCFSOCKS5ErrorBadResponseAddr) {
localizedDescription = @"SOCKS5 bad credentials";
} else if (code == kCFSOCKS5ErrorBadCredentials) {
localizedDescription = @"SOCKS5 unsupported negotiation method";
} else if (code == kCFSOCKS5ErrorUnsupportedNegotiationMethod) {
localizedDescription = @"SOCKS5 unsupported negotiation method";
} else if (code == kCFSOCKS5ErrorNoAcceptableMethod) {
localizedDescription = @"SOCKS5 no acceptable method";
} else if (code == kCFNetServiceErrorUnknown) {
localizedDescription = @"Unknown Net Services error";
} else if (code == kCFNetServiceErrorCollision) {
localizedDescription = @"Net Services: collision";
} else if (code == kCFNetServiceErrorNotFound) {
localizedDescription = @"Net Services: not found";
} else if (code == kCFNetServiceErrorInProgress) {
localizedDescription = @"Net Services: in progress";
} else if (code == kCFNetServiceErrorBadArgument) {
localizedDescription = @"Net Services: bad argument";
} else if (code == kCFNetServiceErrorCancel) {
localizedDescription = @"Net Services: cancelled";
} else if (code == kCFNetServiceErrorInvalid) {
localizedDescription = @"Net Services: invalid";
} else if (code == kCFNetServiceErrorTimeout) {
localizedDescription = @"Net Services timeout";
} else if (code == kCFNetServiceErrorDNSServiceFailure) {
localizedDescription = @"Net Services DNS failure";
int dns_sdCode = 0;
if (CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(userInfo, kCFDNSServiceFailureKey), kCFNumberIntType, &dns_sdCode)) {
localizedDescription = [NSString stringWithFormat:@"Net Services DNS failure: %@", [DNS_SDErrors descriptionForDNS_SDError:dns_sdCode]];
}
} else if (code == kCFFTPErrorUnexpectedStatusCode) {
localizedDescription = [NSString stringWithFormat:@"FTP error %@", (NSString *)CFDictionaryGetValue(userInfo, kCFFTPStatusCodeKey)];
} else if (code == kCFErrorHTTPAuthenticationTypeUnsupported) {
localizedDescription = @"HTTP authentication type unsupported";
} else if (code == kCFErrorHTTPBadCredentials) {
localizedDescription = @"bad HTTP credentials";
} else if (code == kCFErrorHTTPConnectionLost) {
localizedDescription = @"HTTP connection lost";
} else if (code == kCFErrorHTTPParseFailure) {
localizedDescription = @"HTTP parse failure";
} else if (code == kCFErrorHTTPRedirectionLoopDetected) {
localizedDescription = @"HTTP redirection loop detected";
} else if (code == kCFErrorHTTPBadURL) {
localizedDescription = @"bad HTTP URL";
} else if (code == kCFErrorHTTPProxyConnectionFailure) {
localizedDescription = @"HTTP proxy connection failure";
} else if (code == kCFErrorHTTPBadProxyCredentials) {
localizedDescription = @"bad HTTP proxy credentials";
} else if (code == kCFErrorPACFileError) {
localizedDescription = @"HTTP PAC file error";
}
} else if ([domain isEqualToString:@"NSPOSIXErrorDomain"] && code == ENOTCONN) {
localizedDescription = @"Lost connection to the Internet";
} else {
localizedDescription = [(NSString *)CFErrorCopyDescription(err) autorelease];
}
CFRelease(userInfo);
return [NSError errorWithDomain:[CFStreamPair errorDomain] code:code userInfo:[NSDictionary dictionaryWithObjectsAndKeys:localizedDescription, NSLocalizedDescriptionKey, nil]];
}
- (id)initWithHost:(NSString *)theHost useSSL:(BOOL)isUseSSL maxLifetime:(NSTimeInterval)theMaxLifetime {
if (self = [super init]) {
description = [[NSString alloc] initWithFormat:@"<CFStreamPair host=%@ ssl=%@>", theHost, (isUseSSL ? @"YES" : @"NO")];
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)theHost, (isUseSSL ? HTTPS_PORT : HTTP_PORT), &readStream, &writeStream);
if (isUseSSL) {
NSDictionary *sslProperties = [NSDictionary dictionaryWithObjectsAndKeys:
(NSString *)kCFStreamSocketSecurityLevelNegotiatedSSL, kCFStreamSSLLevel,
kCFBooleanTrue, kCFStreamSSLAllowsExpiredCertificates,
kCFBooleanTrue, kCFStreamSSLAllowsExpiredRoots,
kCFBooleanTrue, kCFStreamSSLAllowsAnyRoot,
kCFBooleanFalse, kCFStreamSSLValidatesCertificateChain,
kCFNull, kCFStreamSSLPeerName,
nil];
CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, sslProperties);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, sslProperties);
}
NSDictionary *proxyDict = (NSDictionary *)SCDynamicStoreCopyProxies(NULL);
if ([proxyDict objectForKey:(NSString *)kCFStreamPropertyHTTPProxyHost] != nil) {
CFReadStreamSetProperty(readStream, kCFStreamPropertyHTTPProxy, proxyDict);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertyHTTPProxy, proxyDict);
}
if ([proxyDict objectForKey:(NSString *)kCFStreamPropertySOCKSProxyHost] != nil && [proxyDict objectForKey:(NSString *)kCFStreamPropertySOCKSProxyPort] != nil) {
CFReadStreamSetProperty(readStream, kCFStreamPropertySOCKSProxy, proxyDict);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertySOCKSProxy, proxyDict);
}
[proxyDict release];
is = [[CFStreamInputStream alloc] initWithCFReadStream:readStream];
os = [[CFStreamOutputStream alloc] initWithCFWriteStream:writeStream];
CFRelease(readStream);
CFRelease(writeStream);
createTime = [NSDate timeIntervalSinceReferenceDate];
maxLifetime = theMaxLifetime;
}
return self;
}
- (void)dealloc {
[description release];
[is release];
[os release];
[super dealloc];
}
- (void)setCloseRequested {
closeRequested = YES;
}
- (BOOL)isUsable {
if (closeRequested) {
HSLogDebug(@"%@ close requested; not reusing", self);
return NO;
}
if (([NSDate timeIntervalSinceReferenceDate] - createTime) > maxLifetime) {
HSLogDebug(@"%@ > %f seconds old; not reusing", self, maxLifetime);
return NO;
}
return YES;
}
#pragma mark BufferedInputStream
- (unsigned char *)readExactly:(NSUInteger)exactLength error:(NSError **)error {
return [is readExactly:exactLength error:error];
}
- (unsigned char *)readMaximum:(NSUInteger)maximum length:(NSUInteger *)length error:(NSError **)error {
return [is readMaximum:maximum length:length error:error];
}
- (uint64_t)bytesReceived {
return [is bytesReceived];
}
- (unsigned char *)read:(NSUInteger *)length error:(NSError **)error {
return [is read:length error:error];
}
- (NSData *)slurp:(NSError **)error {
return [is slurp:error];
}
- (void)bytesWereNotUsed {
}
#pragma mark OutputStream
- (BOOL)write:(const unsigned char *)buf length:(NSUInteger)len error:(NSError **)error {
NSError *myError = nil;
BOOL ret = [os write:buf length:len error:&myError];
if (error != NULL) {
*error = myError;
}
if (!ret && [[myError domain] isEqualToString:@"UnixErrorDomain"] && [myError code] == EPIPE) {
HSLogError(@"broken pipe"); //FIXME: This may not work with CFStream stuff.
}
return ret;
}
- (unsigned long long)bytesWritten {
return [os bytesWritten];
}
#pragma mark NSObject
- (NSString *)description {
return description;
}
@end

43
io/ChunkedInputStream.h Normal file
View file

@ -0,0 +1,43 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "InputStream.h"
@class FDInputStream;
@interface ChunkedInputStream : NSObject <InputStream> {
FDInputStream *underlyingStream;
NSUInteger chunkLength;
NSUInteger received;
}
- (id)initWithUnderlyingStream:(FDInputStream *)is;
@end

92
io/ChunkedInputStream.m Normal file
View file

@ -0,0 +1,92 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "ChunkedInputStream.h"
#import "SetNSError.h"
#import "InputStreams.h"
#import "NSErrorCodes.h"
#import "FDInputStream.h"
#define MAX_CHUNK_LENGTH_LINE_LENGTH (1024)
@implementation ChunkedInputStream
- (id)initWithUnderlyingStream:(FDInputStream *)is {
if (self = [super init]) {
underlyingStream = [is retain];
}
return self;
}
- (void)dealloc {
[underlyingStream release];
[super dealloc];
}
- (unsigned char *)read:(NSUInteger *)length error:(NSError **)error {
if (received >= chunkLength) {
received = 0;
NSString *line = [InputStreams readLineWithCRLF:underlyingStream maxLength:MAX_CHUNK_LENGTH_LINE_LENGTH error:error];
if (line == nil) {
return NULL;
}
NSScanner *scanner = [NSScanner scannerWithString:line];
if (![scanner scanHexInt:&chunkLength]) {
SETNSERROR(@"StreamErrorDomain", -1, @"invalid chunk length: %@", line);
return NULL;
}
HSLogTrace(@"chunk length = %u", chunkLength);
}
unsigned char *buf = NULL;
if (chunkLength == 0) {
SETNSERROR(@"StreamsErrorDomain", ERROR_EOF, @"EOF (zero chunk length)");
} else {
buf = [underlyingStream readMaximum:(chunkLength - received) length:length error:error];
if (buf) {
received += *length;
}
}
if (received >= chunkLength) {
NSString *line = [InputStreams readLineWithCRLF:underlyingStream maxLength:MAX_CHUNK_LENGTH_LINE_LENGTH error:error];
if (line == nil) {
return NULL;
}
if (![line isEqualToString:@"\r\n"]) {
SETNSERROR(@"StreamErrorDomain", -1, @"missing CRLF at end of chunk!");
return NULL;
}
}
return buf;
}
- (NSData *)slurp:(NSError **)error {
return [InputStreams slurp:self error:error];
}
- (void)bytesWereNotUsed {
}
@end

58
io/CryptInputStream.h Normal file
View file

@ -0,0 +1,58 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#include <openssl/evp.h>
#import "InputStream.h"
typedef int (*CryptInitFunc)(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, unsigned char *key, unsigned char *iv);
typedef int (*CryptUpdateFunc)(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, unsigned char *in, int inl);
typedef int (*CryptFinalFunc)(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);
@interface CryptInputStream : NSObject <InputStream> {
CryptInitFunc cryptInit;
CryptUpdateFunc cryptUpdate;
CryptFinalFunc cryptFinal;
id <InputStream> is;
NSUInteger totalInBytesRecvd;
unsigned char *outBuf;
NSUInteger outBufLen;
const EVP_CIPHER *cipher;
EVP_CIPHER_CTX cipherContext;
unsigned char evp_key[EVP_MAX_KEY_LENGTH];
unsigned char iv[EVP_MAX_IV_LENGTH];
size_t blockSize;
BOOL initialized;
BOOL finalized;
}
- (id)initWithCryptInitFunc:(void *)theCryptInit cryptUpdateFunc:(void *)theCryptUpdate cryptFinalFunc:(void *)theCryptFinal inputStream:(id <InputStream>)theIS cipherName:(NSString *)theCipherName key:(NSString *)theKey error:(NSError **)error;
@end

187
io/CryptInputStream.m Normal file
View file

@ -0,0 +1,187 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "CryptInputStream.h"
#import "SetNSError.h"
#import "OpenSSL.h"
#import "InputStreams.h"
#import "NSErrorCodes.h"
@interface CryptInputStream (internal)
- (unsigned char *)readAtLeastBlockSize:(NSUInteger *)length error:(NSError **)error;
@end
@implementation CryptInputStream
- (id)initWithCryptInitFunc:(void *)theCryptInit cryptUpdateFunc:(void *)theCryptUpdate cryptFinalFunc:(void *)theCryptFinal inputStream:(id <InputStream>)theIS cipherName:(NSString *)theCipherName key:(NSString *)theKey error:(NSError **)error {
if (self = [super init]) {
cryptInit = (CryptInitFunc)theCryptInit;
cryptUpdate = (CryptUpdateFunc)theCryptUpdate;
cryptFinal = (CryptFinalFunc)theCryptFinal;
BOOL ret = NO;
do {
is = [theIS retain];
NSData *keyData = [theKey dataUsingEncoding:NSUTF8StringEncoding];
if ([keyData length] > EVP_MAX_KEY_LENGTH) {
SETNSERROR(@"EncryptedInputStreamErrorDomain", -1, @"encryption key must be less than or equal to %d bytes", EVP_MAX_KEY_LENGTH);
break;
}
if (![OpenSSL initializeSSL:error]) {
break;
}
cipher = EVP_get_cipherbyname([theCipherName UTF8String]);
if (!cipher) {
SETNSERROR(@"EncryptedInputStreamErrorDomain", -1, @"failed to load %@ cipher: %@", theCipherName, [OpenSSL errorMessage]);
break;
}
evp_key[0] = 0;
EVP_BytesToKey(cipher, EVP_md5(), NULL, [keyData bytes], [keyData length], 1, evp_key, iv);
EVP_CIPHER_CTX_init(&cipherContext);
if (!(*cryptInit)(&cipherContext, cipher, evp_key, iv)) {
SETNSERROR(@"NSDataEncryptErrorDomain", -1, @"EVP_EncryptInit: %@", [OpenSSL errorMessage]);
break;
}
EVP_CIPHER_CTX_set_key_length(&cipherContext, EVP_MAX_KEY_LENGTH);
blockSize = (unsigned long long)EVP_CIPHER_CTX_block_size(&cipherContext);
initialized = YES;
ret = YES;
} while(0);
if (!ret) {
[self release];
self = nil;
}
}
return self;
}
- (void)dealloc {
if (initialized) {
EVP_CIPHER_CTX_cleanup(&cipherContext);
}
if (outBuf != NULL) {
free(outBuf);
}
[is release];
[super dealloc];
}
- (BOOL)cryptUpdate:(int *)outLen inBuf:(unsigned char *)inBuf inLen:(NSUInteger)inLen {
@throw [NSException exceptionWithName:@"PureVirtualMethod" reason:@"don't call this" userInfo:nil];
}
- (BOOL)cryptFinal:(int *)outLen {
@throw [NSException exceptionWithName:@"PureVirtualMethod" reason:@"don't call this" userInfo:nil];
}
- (unsigned char *)read:(NSUInteger *)length error:(NSError **)error {
if (finalized) {
SETNSERROR(@"StreamsErrorDomain", ERROR_EOF, @"already finalized");
return NULL;
}
NSUInteger inLen = 0;
int outLen = 0;
NSError *myError;
unsigned char *inBuf = [self readAtLeastBlockSize:&inLen error:&myError];
if (inBuf == NULL && [myError code] != ERROR_EOF) {
if (error != NULL) {
*error = myError;
}
return NULL;
}
NSUInteger neededBufLen = inLen + blockSize;
if (outBufLen < neededBufLen) {
if (outBuf == NULL) {
outBuf = (unsigned char *)malloc(neededBufLen);
} else {
outBuf = (unsigned char *)realloc(outBuf, neededBufLen);
}
outBufLen = neededBufLen;
}
if (inBuf != NULL) {
NSAssert(inLen > 0, @"expected more than 0 input bytes");
totalInBytesRecvd += inLen;
if (!(*cryptUpdate)(&cipherContext, outBuf, &outLen, inBuf, inLen)) {
SETNSERROR(@"OpenSSLErrorDomain", -1, @"crypt update: %@", [OpenSSL errorMessage]);
return NULL;
}
NSAssert(outLen < outBufLen, @"can't receive more bytes than outBufLen from EVP_EncryptUpdate");
}
if (outLen == 0) {
finalized = YES;
if (totalInBytesRecvd > 0 && !(*cryptFinal)(&cipherContext, outBuf, &outLen)) {
SETNSERROR(@"OpenSSLErrorDomain", -1, @"crypt final: %@", [OpenSSL errorMessage]);
return NULL;
}
NSAssert(outLen < outBufLen, @"can't receive more bytes than outBufLen from EVP_EncryptFinal");
}
if (outLen == 0) {
SETNSERROR(@"StreamsErrorDomain", ERROR_EOF, @"EOF on encrypted input stream");
return NULL;
}
NSAssert(outLen > 0, @"outLen must be greater than 0");
*length = (NSUInteger)outLen;
return outBuf;
}
- (NSData *)slurp:(NSError **)error {
return [InputStreams slurp:self error:error];
}
- (void)bytesWereNotUsed {
}
@end
@implementation CryptInputStream (internal)
- (unsigned char *)readAtLeastBlockSize:(NSUInteger *)length error:(NSError **)error {
NSMutableData *data = [NSMutableData data];
while ([data length] < blockSize) {
NSUInteger recvd = 0;
NSError *myError = nil;
unsigned char *buf = [is read:&recvd error:&myError];
if (buf == NULL) {
if ([myError code] != ERROR_EOF) {
if (error != NULL) {
*error = myError;
}
return NULL;
}
break;
}
if (recvd > blockSize && [data length] == 0) {
// Short-circuit to avoid a buffer copy.
*length = recvd;
return buf;
}
[data appendBytes:buf length:recvd];
}
if ([data length] == 0) {
SETNSERROR(@"StreamsErrorDomain", ERROR_EOF, @"EOF");
return NULL;
}
NSAssert([data length] > 0, @"must have received some bytes");
*length = [data length];
return [data mutableBytes];
}
@end

42
io/DataIO.h Normal file
View file

@ -0,0 +1,42 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "BufferedInputStream.h"
@interface DataIO : NSObject {
}
+ (void)write:(NSData *)data to:(NSMutableData *)data;
+ (BOOL)read:(NSData **)value from:(id <BufferedInputStream>)is error:(NSError **)error;
@end

56
io/DataIO.m Normal file
View file

@ -0,0 +1,56 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "DataIO.h"
#import "IntegerIO.h"
#import "Streams.h"
@implementation DataIO
+ (void)write:(NSData *)data to:(NSMutableData *)outData {
[IntegerIO writeUInt64:(uint64_t)[data length] to:outData];
[outData appendBytes:[data bytes] length:[data length]];
}
+ (BOOL)read:(NSData **)value from:(id <BufferedInputStream>)is error:(NSError **)error {
*value = NO;
uint64_t length = 0;
if (![IntegerIO readUInt64:&length from:is error:error]) {
return NO;
}
unsigned char *bytes = [is readExactly:length error:error];
if (!bytes) {
return NO;
}
*value = [NSData dataWithBytes:bytes length:length];
return YES;
}
@end

42
io/DataInputStream.h Normal file
View file

@ -0,0 +1,42 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "BufferedInputStream.h"
@interface DataInputStream : NSObject <BufferedInputStream> {
NSData *data;
NSUInteger pos;
}
- (id)initWithData:(NSData *)theData;
- (id)initWithData:(NSData *)theData offset:(unsigned long long)theOffset length:(unsigned long long)theLength;
@end

112
io/DataInputStream.m Normal file
View file

@ -0,0 +1,112 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "DataInputStream.h"
#import "SetNSError.h"
#import "NSErrorCodes.h"
@implementation DataInputStream
- (id)initWithData:(NSData *)theData {
if (self = [super init]) {
data = [theData retain];
}
return self;
}
- (id)initWithData:(NSData *)theData offset:(unsigned long long)theOffset length:(unsigned long long)theLength {
if (self = [super init]) {
data = [theData subdataWithRange:NSMakeRange((NSUInteger)theOffset, (NSUInteger)theLength)];
}
return self;
}
- (void)dealloc {
[data release];
[super dealloc];
}
#pragma mark InputStream protocol
- (unsigned char *)read:(NSUInteger *)length error:(NSError **)error {
if (pos >= [data length]) {
SETNSERROR(@"StreamsErrorDomain", ERROR_EOF, @"EOF on data");
return NULL;
}
NSUInteger remaining = [data length] - pos;
*length = remaining;
unsigned char *ret = (unsigned char *)[data bytes] + pos;
pos = [data length];
return ret;
}
- (unsigned char *)readMaximum:(NSUInteger)maximum length:(NSUInteger *)length error:(NSError **)error {
if (pos >= [data length]) {
SETNSERROR(@"StreamsErrorDomain", ERROR_EOF, @"EOF on data");
return NULL;
}
NSUInteger len = [data length] - pos;
if (len > maximum) {
len = maximum;
}
unsigned char *buf = (unsigned char *)[data bytes] + pos;
pos += len;
return buf;
}
- (NSData *)slurp:(NSError **)error {
if (pos == 0) {
/* This is both a short-circuit and a hack.
* The short-circuit part is avoiding copying 'data'.
* The hack part is if [data length] == 0, we return the empty 'data' instead of an EOF error.
*/
return [[data retain] autorelease];
}
if (pos >= [data length]) {
SETNSERROR(@"StreamsErrorDomain", ERROR_EOF, @"EOF on data");
return NULL;
}
NSData *ret = [data subdataWithRange:NSMakeRange(pos, [data length] - pos)];
pos = [data length];
return ret;
}
- (void)bytesWereNotUsed {
}
- (uint64_t)bytesReceived {
return (uint64_t)pos;
}
#pragma mark BufferedInputStream protocol
- (unsigned char *)readExactly:(NSUInteger)exactLength error:(NSError **)error {
if (([data length] - pos) < exactLength) {
SETNSERROR(@"StreamsErrorDomain", ERROR_EOF, @"EOF");
return NULL;
}
unsigned char *buf = (unsigned char *)[data bytes] + pos;
pos += exactLength;
return buf;
}
@end

View file

@ -0,0 +1,40 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "InputStreamFactory.h"
@interface DataInputStreamFactory : NSObject <InputStreamFactory> {
NSData *data;
}
- (id)initWithData:(NSData *)theData;
@end

View file

@ -0,0 +1,56 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "DataInputStreamFactory.h"
#import "DataInputStream.h"
#import "NSData-InputStream.h"
@implementation DataInputStreamFactory
- (id)initWithData:(NSData *)theData {
if (self = [super init]) {
data = [theData retain];
}
return self;
}
- (void)dealloc {
[data release];
[super dealloc];
}
#pragma mark InputStreamFactory protocol
- (id <InputStream>) newInputStream:(id)sender {
return [data newInputStream];
}
- (id <InputStream>) newInputStream:(id)sender sourceOffset:(unsigned long long)theOffset sourceLength:(unsigned long long)theLength {
return [[DataInputStream alloc] initWithData:data offset:theOffset length:theLength];
}
@end

41
io/DateIO.h Normal file
View file

@ -0,0 +1,41 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "BufferedInputStream.h"
@interface DateIO : NSObject {
}
+ (void)write:(NSDate *)date to:(NSMutableData *)data;
+ (BOOL)read:(NSDate **)date from:(id <BufferedInputStream>)is error:(NSError **)error;
@end

61
io/DateIO.m Normal file
View file

@ -0,0 +1,61 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "BooleanIO.h"
#import "IntegerIO.h"
#import "DateIO.h"
@implementation DateIO
+ (void)write:(NSDate *)date to:(NSMutableData *)data {
BOOL dateNotNil = (date != nil);
[BooleanIO write:dateNotNil to:data];
if (dateNotNil) {
long long millisecondsSince1970 = (long long)([date timeIntervalSince1970] * 1000.0);
[IntegerIO writeInt64:millisecondsSince1970 to:data];
}
}
+ (BOOL)read:(NSDate **)date from:(id <BufferedInputStream>)is error:(NSError **)error {
*date = nil;
BOOL notNil;
if (![BooleanIO read:&notNil from:is error:error]) {
return NO;
}
if (notNil) {
long long millisecondsSince1970;
if (![IntegerIO readInt64:&millisecondsSince1970 from:is error:error]) {
return NO;
}
*date = [NSDate dateWithTimeIntervalSince1970:((double)millisecondsSince1970 / 1000.0)];
}
return YES;
}
@end

39
io/DecryptedInputStream.h Normal file
View file

@ -0,0 +1,39 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "CryptInputStream.h"
@interface DecryptedInputStream : CryptInputStream {
}
- (id)initWithInputStream:(id <InputStream>)theIS cipherName:(NSString *)theCipherName key:(NSString *)theKey error:(NSError **)error;
@end

41
io/DecryptedInputStream.m Normal file
View file

@ -0,0 +1,41 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "DecryptedInputStream.h"
#include <openssl/evp.h>
@implementation DecryptedInputStream
- (id)initWithInputStream:(id <InputStream>)theIS cipherName:(NSString *)theCipherName key:(NSString *)theKey error:(NSError **)error {
self = [super initWithCryptInitFunc:&EVP_DecryptInit cryptUpdateFunc:&EVP_DecryptUpdate cryptFinalFunc:&EVP_DecryptFinal inputStream:theIS cipherName:theCipherName key:theKey error:error];
return self;
}
@end

41
io/DoubleIO.h Normal file
View file

@ -0,0 +1,41 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "BufferedInputStream.h"
@interface DoubleIO : NSObject {
}
+ (void)write:(double)d to:(NSMutableData *)data;
+ (BOOL)read:(double *)value from:(id <BufferedInputStream>)is error:(NSError **)error;
@end

65
io/DoubleIO.m Normal file
View file

@ -0,0 +1,65 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "DoubleIO.h"
#import "StringIO.h"
#import "SetNSError.h"
//FIXME: Delete this class? It's not used anywhere.
@implementation DoubleIO
+ (void)write:(double)d to:(NSMutableData *)data {
NSString *str = [NSString stringWithFormat:@"%f", d];
[StringIO write:str to:data];
}
+ (BOOL)read:(double *)value from:(id <BufferedInputStream>)is error:(NSError **)error {
if (error) {
*error = 0;
}
*value = 0;
NSString *str;
if (![StringIO read:&str from:is error:error]) {
return NO;
}
if (!str) {
SETNSERROR(@"DoubleIOErrorDomain", -1, @"nil string; expected a double");
return NO;
}
BOOL ret = [[NSScanner scannerWithString:str] scanDouble:value];
if (!ret) {
SETNSERROR(@"DoubleIOErrorDomain", -1, @"%@ does not contain a double", str);
}
return ret;
}
@end

39
io/EncryptedInputStream.h Normal file
View file

@ -0,0 +1,39 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "CryptInputStream.h"
@interface EncryptedInputStream : CryptInputStream {
}
- (id)initWithInputStream:(id <InputStream>)theIS cipherName:(NSString *)theCipherName key:(NSString *)theKey error:(NSError **)error;
@end

41
io/EncryptedInputStream.m Normal file
View file

@ -0,0 +1,41 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "EncryptedInputStream.h"
#import <openssl/evp.h>
@implementation EncryptedInputStream
- (id)initWithInputStream:(id <InputStream>)theIS cipherName:(NSString *)theCipherName key:(NSString *)theKey error:(NSError **)error {
self = [super initWithCryptInitFunc:&EVP_EncryptInit cryptUpdateFunc:&EVP_EncryptUpdate cryptFinalFunc:&EVP_EncryptFinal inputStream:theIS cipherName:theCipherName key:theKey error:error];
return self;
}
@end

42
io/FDInputStream.h Normal file
View file

@ -0,0 +1,42 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "BufferedInputStream.h"
@interface FDInputStream : NSObject <BufferedInputStream> {
int fd;
uint64_t bytesReceived;
}
+ (void)setReadTimeoutSeconds:(time_t)timeout;
- (id)initWithFD:(int)theFD;
@end

141
io/FDInputStream.m Normal file
View file

@ -0,0 +1,141 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "FDInputStream.h"
#import "SetNSError.h"
#import "InputStreams.h"
#import "NSErrorCodes.h"
#define MY_BUF_SIZE (4096)
#define DEFAULT_READ_TIMEOUT_SECONDS (60)
static time_t readTimeoutSeconds = DEFAULT_READ_TIMEOUT_SECONDS;
@implementation FDInputStream
+ (void)setReadTimeoutSeconds:(time_t)timeout {
readTimeoutSeconds = timeout;
HSLogInfo(@"network read timeout set to %d seconds", timeout);
}
- (id)initWithFD:(int)theFD {
if (self = [super init]) {
fd = theFD;
}
return self;
}
- (void)dealloc {
[super dealloc];
}
- (unsigned char *)read:(NSUInteger *)length error:(NSError **)error {
return [self readMaximum:MY_BUF_SIZE length:length error:error];
}
- (unsigned char *)readMaximum:(NSUInteger)maximum length:(NSUInteger *)length error:(NSError **)error {
NSUInteger toRead = (MY_BUF_SIZE > maximum) ? maximum : MY_BUF_SIZE;
NSMutableData *data = [NSMutableData dataWithLength:toRead];
unsigned char *buf = (unsigned char *)[data mutableBytes];
int ret = 0;
fd_set readSet;
fd_set exceptSet;
FD_ZERO(&readSet);
FD_SET((unsigned int)fd, &readSet);
FD_ZERO(&exceptSet);
FD_SET((unsigned int)fd, &exceptSet);
struct timeval timeout;
struct timeval *pTimeout;
select_again:
timeout.tv_sec = readTimeoutSeconds;
timeout.tv_usec = 0;
pTimeout = (readTimeoutSeconds > 0) ? &timeout : 0;
ret = select(fd + 1, &readSet, 0, &exceptSet, pTimeout);
if ((ret == -1) && (errno == EINTR)) {
goto select_again;
} else if (ret == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"select: %s", strerror(errno));
return NULL;
} else if (ret == 0) {
SETNSERROR(@"InputStreamErrorDomain", -1, @"read timeout");
return NULL;
}
read_again:
ret = read(fd, buf, toRead);
if ((ret == -1) && (errno == EINTR)) {
goto read_again;
} else if (ret == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"read: %s", strerror(errno));
return NULL;
}
if (ret == 0) {
SETNSERROR(@"StreamErrorDomain", ERROR_EOF, @"EOF on fd %d", fd);
return NULL;
}
*length = (NSUInteger)ret;
bytesReceived += (uint64_t)ret;
return buf;
}
- (NSData *)slurp:(NSError **)error {
return [InputStreams slurp:self error:error];
}
- (void)bytesWereNotUsed {
}
#pragma mark BufferedInputStream protocol
- (unsigned char *)readExactly:(NSUInteger)exactLength error:(NSError **)error {
if (exactLength > 2147483648) {
SETNSERROR(@"InputStreamErrorDomain", -1, @"absurd length %u requested", exactLength);
return NULL;
}
NSMutableData *data = [NSMutableData dataWithLength:exactLength];
unsigned char *dataBuf = [data mutableBytes];
NSUInteger total = 0;
while (total < exactLength) {
NSUInteger maximum = exactLength - total;
NSUInteger length;
unsigned char *ibuf = [self readMaximum:maximum length:&length error:error];
if (ibuf == NULL) {
return NULL;
}
NSAssert(length > 0, @"expected more than 0 bytes");
memcpy(dataBuf + total, ibuf, length);
total += length;
}
bytesReceived += (uint64_t)exactLength;
return dataBuf;
}
- (uint64_t)bytesReceived {
return bytesReceived;
}
#pragma mark NSObject protocol
- (NSString *)description {
return [NSString stringWithFormat:@"<FDInputStream: fd=%d>", fd];
}
@end

42
io/FDOutputStream.h Normal file
View file

@ -0,0 +1,42 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "OutputStream.h"
@interface FDOutputStream : NSObject <OutputStream> {
int fd;
unsigned long long bytesWritten;
}
- (id)initWithFD:(int)theFD;
@end

64
io/FDOutputStream.m Normal file
View file

@ -0,0 +1,64 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "FDOutputStream.h"
#import "SetNSError.h"
@implementation FDOutputStream
- (id)initWithFD:(int)theFD {
if (self = [super init]) {
fd = theFD;
}
return self;
}
- (BOOL)write:(const unsigned char *)buf length:(NSUInteger)len error:(NSError **)error {
int ret = 0;
NSUInteger written = 0;
while ((len - written) > 0) {
write_again:
ret = write(fd, &(buf[written]), len - written);
if ((ret == -1) && (errno == EINTR)) {
goto write_again;
}
if (ret == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"write: %s", strerror(errno));
return NO;
}
written += (NSUInteger)ret;
bytesWritten += (NSUInteger)ret;
}
return YES;
}
- (unsigned long long)bytesWritten {
return bytesWritten;
}
@end

46
io/FileInputStream.h Normal file
View file

@ -0,0 +1,46 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "BufferedInputStream.h"
@interface FileInputStream : NSObject <BufferedInputStream> {
int fd;
NSString *path;
unsigned long long fileLength;
unsigned long long offset;
unsigned char *buf;
uint64_t bytesReceived;
}
- (id)initWithPath:(NSString *)thePath length:(unsigned long long)theLength;
- (id)initWithPath:(NSString *)thePath offset:(unsigned long long)theOffset length:(unsigned long long)theLength;
@end

162
io/FileInputStream.m Normal file
View file

@ -0,0 +1,162 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "FileInputStream.h"
#import "SetNSError.h"
#import "InputStreams.h"
#import "NSErrorCodes.h"
#define MY_BUF_SIZE (4096)
@interface FileInputStream (internal)
- (void)close;
@end
@implementation FileInputStream
- (id)initWithPath:(NSString *)thePath length:(unsigned long long)theLength {
if (self = [super init]) {
fd = -1;
path = [thePath retain];
fileLength = theLength;
buf = (unsigned char *)malloc(MY_BUF_SIZE);
}
return self;
}
- (id)initWithPath:(NSString *)thePath offset:(unsigned long long)theOffset length:(unsigned long long)theLength {
if (self = [super init]) {
fd = -1;
path = [thePath retain];
fileLength = theOffset + theLength;
buf = (unsigned char *)malloc(MY_BUF_SIZE);
offset = theOffset;
}
return self;
}
- (void)dealloc {
[self close];
[path release];
free(buf);
[super dealloc];
}
- (unsigned char *)read:(NSUInteger *)length error:(NSError **)error {
return [self readMaximum:MY_BUF_SIZE length:length error:error];
}
- (unsigned char *)readMaximum:(NSUInteger)maximum length:(NSUInteger *)length error:(NSError **)error {
if (fd == -1) {
fd = open([path fileSystemRepresentation], O_RDONLY|O_NOFOLLOW);
if (fd == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"%s", strerror(errno));
return NO;
}
HSLogTrace(@"opened fd %d (%@)", fd, path);
if (offset > 0) {
if (lseek(fd, (off_t)offset, SEEK_SET) == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"lseek(%@, %qu): %s", path, offset, strerror(errno));
return NO;
}
}
}
int ret;
unsigned long long remaining = fileLength - offset;
unsigned long long toRead = (maximum > remaining) ? remaining : maximum;
if (toRead > MY_BUF_SIZE) {
toRead = MY_BUF_SIZE;
}
if (toRead == 0) {
SETNSERROR(@"StreamsErrorDomain", ERROR_EOF, @"reached EOF");
return NULL;
}
read_again:
ret = read(fd, buf, (size_t)toRead);
if ((ret == -1) && (errno == EINTR)) {
goto read_again;
}
if (ret == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"read: %s", strerror(errno));
return NULL;
}
if (ret == 0) {
SETNSERROR(@"StreamsErrorDomain", ERROR_EOF, @"EOF on %@", path);
return NULL;
}
offset += (unsigned long long)ret;
*length = (NSUInteger)ret;
bytesReceived += (uint64_t)ret;
return buf;
}
- (NSData *)slurp:(NSError **)error {
return [InputStreams slurp:self error:error];
}
#pragma mark BufferedInputStream
- (unsigned char *)readExactly:(NSUInteger)exactLength error:(NSError **)error {
if (exactLength > 2147483648) {
SETNSERROR(@"InputStreamErrorDomain", -1, @"absurd length %u requested", exactLength);
return NULL;
}
NSMutableData *data = [NSMutableData dataWithLength:exactLength];
unsigned char *dataBuf = [data mutableBytes];
NSUInteger total = 0;
while (total < exactLength) {
NSUInteger maximum = exactLength - total;
NSUInteger length;
unsigned char *ibuf = [self readMaximum:maximum length:&length error:error];
if (ibuf == NULL) {
return NULL;
}
NSAssert(length > 0, @"expected more than 0 bytes");
memcpy(dataBuf + total, ibuf, length);
total += length;
}
return dataBuf;
}
- (uint64_t)bytesReceived {
return bytesReceived;
}
- (void)bytesWereNotUsed {
}
#pragma mark NSObject protocol
- (NSString *)description {
return [NSString stringWithFormat:@"<FileInputStream: fd=%d path=%@>", fd, path];
}
@end
@implementation FileInputStream (internal)
- (void)close {
if (fd != -1) {
close(fd);
HSLogTrace(@"closed fd %d (%@)", fd, path);
fd = -1;
}
}
@end

View file

@ -0,0 +1,41 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "InputStreamFactory.h"
@interface FileInputStreamFactory : NSObject <InputStreamFactory> {
NSString *path;
unsigned long long length;
}
- (id)initWithPath:(NSString *)thePath error:(NSError **)error;
@end

View file

@ -0,0 +1,58 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "FileInputStreamFactory.h"
#import "FileInputStream.h"
@implementation FileInputStreamFactory
- (id)initWithPath:(NSString *)thePath error:(NSError **)error {
if (self = [super init]) {
path = [thePath copy];
NSDictionary *attribs = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:error];
if (attribs == nil) {
return nil;
}
length = [[attribs objectForKey:NSFileSize] unsignedLongLongValue];
}
return self;
}
- (void)dealloc {
[path release];
[super dealloc];
}
- (id <InputStream>) newInputStream:(id)sender {
return [[FileInputStream alloc] initWithPath:path length:length];
}
- (id <InputStream>) newInputStream:(id)sender sourceOffset:(unsigned long long)theOffset sourceLength:(unsigned long long)theLength {
return [[FileInputStream alloc] initWithPath:path offset:theOffset length:length];
}
@end

44
io/FileOutputStream.h Normal file
View file

@ -0,0 +1,44 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "OutputStream.h"
@interface FileOutputStream : NSObject <OutputStream> {
BOOL append;
int fd;
NSString *path;
unsigned long long bytesWritten;
}
- (id)initWithPath:(NSString *)thePath append:(BOOL)isAppend;
- (void)close;
@end

92
io/FileOutputStream.m Normal file
View file

@ -0,0 +1,92 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "FileOutputStream.h"
#import "SetNSError.h"
@implementation FileOutputStream
- (id)initWithPath:(NSString *)thePath append:(BOOL)isAppend {
if (self = [super init]) {
path = [thePath copy];
append = isAppend;
fd = -1;
}
return self;
}
- (void)dealloc {
if (fd != -1) {
close(fd);
}
[path release];
[super dealloc];
}
- (void)close {
if (fd != -1) {
close(fd);
fd = -1;
}
}
- (BOOL)write:(const unsigned char *)buf length:(NSUInteger)len error:(NSError **)error {
if (fd == -1) {
int oflag = O_WRONLY|O_CREAT;
if (append) {
oflag |= O_APPEND;
} else {
oflag |= O_TRUNC;
}
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
fd = open([path fileSystemRepresentation], oflag, mode);
if (fd == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"%s", strerror(errno));
return NO;
}
}
int ret = 0;
NSUInteger written = 0;
while ((len - written) > 0) {
write_again:
ret = write(fd, &(buf[written]), len - written);
if ((ret == -1) && (errno == EINTR)) {
goto write_again;
} else if (ret == -1) {
SETNSERROR(@"UnixErrorDomain", errno, @"write: %s", strerror(errno));
return NO;
}
written += (NSUInteger)ret;
bytesWritten += (NSUInteger)ret;
}
return YES;
}
- (unsigned long long)bytesWritten {
return bytesWritten;
}
@end

View file

@ -0,0 +1,43 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "InputStream.h"
@class FDInputStream;
@interface FixedLengthInputStream : NSObject <InputStream> {
FDInputStream *underlyingStream;
unsigned long long fixedLength;
unsigned long long totalReceived;
}
- (id)initWithUnderlyingStream:(FDInputStream *)is length:(unsigned long long)theLength;
@end

View file

@ -0,0 +1,69 @@
/*
Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of PhotoMinds LLC or Haystack Software, nor the names of
their contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "FixedLengthInputStream.h"
#import "InputStreams.h"
#import "SetNSError.h"
#import "NSErrorCodes.h"
#import "FDInputStream.h"
@implementation FixedLengthInputStream
- (id)initWithUnderlyingStream:(FDInputStream *)is length:(unsigned long long)theLength {
if (self = [super init]) {
underlyingStream = [is retain];
fixedLength = theLength;
}
return self;
}
- (void)dealloc {
[underlyingStream release];
[super dealloc];
}
- (unsigned char *)read:(NSUInteger *)length error:(NSError **)error {
unsigned long long maximum = fixedLength - totalReceived;
if (maximum == 0) {
SETNSERROR(@"StreamsErrorDomain", ERROR_EOF, @"EOF on fixed length input stream");
return NULL;
}
unsigned char *buf = [underlyingStream readMaximum:maximum length:length error:error];
if (buf == NULL) {
return NULL;
}
totalReceived += (unsigned long long)(*length);
return buf;
}
- (NSData *)slurp:(NSError **)error {
return [InputStreams slurp:self error:error];
}
- (void)bytesWereNotUsed {
}
@end

Some files were not shown because too many files have changed in this diff Show more