From 846d391120ee819cb32ec28d9cf4f267a25ae118 Mon Sep 17 00:00:00 2001 From: Stefan Reitshamer Date: Fri, 20 Aug 2010 11:51:11 -0400 Subject: [PATCH] Changed BufferedInputStream from a protocol to a class; removed a bunch of redundant code; changed the InputStream interface to work more like the read(2) system call; fixed a bug with restoring large files when network errors occur. --- ArqRepo.m | 8 +- Commit.h | 2 +- Commit.m | 13 +- CommitFailedFile.h | 4 +- CommitFailedFile.m | 2 +- DiskPack.m | 22 +- Node.m | 2 +- Restorer.h | 1 + Restorer.m | 108 +++++--- Tree.h | 4 +- Tree.m | 13 +- XAttrSet.h | 2 +- XAttrSet.m | 13 +- arq_restore.m | 2 +- arq_restore.xcodeproj/project.pbxproj | 62 +++-- crypto/SHA1Hash.h | 3 +- crypto/SHA1Hash.m | 55 ++-- http/CFStreamPair.h | 2 +- http/CFStreamPair.m | 22 +- http/HTTP.h | 1 + http/HTTPConnection.h | 6 +- http/HTTPConnection.m | 21 +- http/HTTPRequest.m | 5 +- http/HTTPResponse.h | 7 +- http/HTTPResponse.m | 9 +- http/StreamPair.h | 4 +- http/StreamPairFactory.h | 2 +- http/StreamPairFactory.m | 12 +- io/BooleanIO.h | 2 +- io/BooleanIO.m | 9 +- io/BufferedInputStream.h | 14 +- io/BufferedInputStream.m | 124 +++++++++ io/CFStreamInputStream.h | 4 +- io/CFStreamInputStream.m | 68 +---- io/CFStreamPair.m | 235 ------------------ io/ChunkedInputStream.h | 6 +- io/ChunkedInputStream.m | 33 +-- io/CryptInputStream.h | 6 +- io/CryptInputStream.m | 105 ++++---- io/DataIO.h | 6 +- io/DataIO.m | 15 +- io/DataInputStream.h | 6 +- io/DataInputStream.m | 63 ++--- io/DataInputStreamFactory.m | 1 + io/DateIO.h | 4 +- io/DateIO.m | 5 +- io/DoubleIO.h | 6 +- io/DoubleIO.m | 10 +- io/EncryptedInputStream.h | 2 +- io/FDInputStream.h | 4 +- io/FDInputStream.m | 58 +---- io/FileInputStream.h | 6 +- io/FileInputStream.m | 68 ++--- io/FileOutputStream.m | 2 - io/FixedLengthInputStream.h | 6 +- io/FixedLengthInputStream.m | 35 ++- io/InputStream.h | 2 +- io/InputStreamFactory.h | 2 + io/InputStreams.h | 6 +- io/InputStreams.m | 87 +++---- io/IntegerIO.h | 10 +- io/IntegerIO.m | 23 +- io/MonitoredInputStream.m | 16 +- io/NSFileManager_extra.h | 1 + io/NSFileManager_extra.m | 12 + io/StreamPair.h | 4 +- io/StreamPairFactory.m | 168 ------------- io/Streams.h | 5 +- io/Streams.m | 29 ++- io/StringIO.h | 5 +- io/StringIO.m | 21 +- plist/ArrayNode.h | 1 + plist/ArrayNode.m | 49 +++- .../BinaryPListReader.h | 23 +- plist/BinaryPListReader.m | 178 +++++++++++++ .../BinaryPListWriter.h | 17 +- plist/BinaryPListWriter.m | 134 ++++++++++ plist/BooleanNode.h | 1 + plist/BooleanNode.m | 27 +- plist/DictNode.h | 8 +- plist/DictNode.m | 105 +++++++- plist/IntegerNode.m | 26 +- plist/RealNode.h | 1 + plist/RealNode.m | 27 +- plist/StringNode.h | 1 + plist/StringNode.m | 28 ++- s3/HTTPConnection_S3.h | 1 - s3/HTTPConnection_S3.m | 1 - s3/S3Request.m | 20 +- s3/S3Service.h | 2 + s3/S3Service.m | 91 +++++++ shared/NSErrorCodes.h | 16 +- shared/RegexKitLite.m | 6 +- 93 files changed, 1394 insertions(+), 1070 deletions(-) create mode 100644 io/BufferedInputStream.m delete mode 100644 io/CFStreamPair.m delete mode 100644 io/StreamPairFactory.m rename io/CFStreamPair.h => plist/BinaryPListReader.h (73%) create mode 100644 plist/BinaryPListReader.m rename io/StreamPairFactory.h => plist/BinaryPListWriter.h (77%) create mode 100644 plist/BinaryPListWriter.m diff --git a/ArqRepo.m b/ArqRepo.m index 8660aca..865921f 100644 --- a/ArqRepo.m +++ b/ArqRepo.m @@ -82,7 +82,9 @@ - (Commit *)commitForSHA1:(NSString *)theSHA1 error:(NSError **)error { return nil; } DataInputStream *dis = [[DataInputStream alloc] initWithData:data]; - Commit *commit = [[[Commit alloc] initWithBufferedInputStream:dis error:error] autorelease]; + BufferedInputStream *bis = [[BufferedInputStream alloc] initWithUnderlyingStream:dis]; + Commit *commit = [[[Commit alloc] initWithBufferedInputStream:bis error:error] autorelease]; + [bis release]; [dis release]; return commit; } @@ -109,7 +111,9 @@ - (Tree *)treeForSHA1:(NSString *)theSHA1 error:(NSError **)error { return nil; } DataInputStream *dis = [[DataInputStream alloc] initWithData:data]; - Tree *tree = [[[Tree alloc] initWithBufferedInputStream:dis error:error] autorelease]; + BufferedInputStream *bis = [[BufferedInputStream alloc] initWithUnderlyingStream:dis]; + Tree *tree = [[[Tree alloc] initWithBufferedInputStream:bis error:error] autorelease]; + [bis release]; [dis release]; return tree; } diff --git a/Commit.h b/Commit.h index 5cee273..8272c6f 100644 --- a/Commit.h +++ b/Commit.h @@ -48,7 +48,7 @@ NSDate *_creationDate; NSArray *_commitFailedFiles; } -- (id)initWithBufferedInputStream:(id )is error:(NSError **)error; +- (id)initWithBufferedInputStream:(BufferedInputStream *)is error:(NSError **)error; @property(readonly,copy) NSString *author; @property(readonly,copy) NSString *comment; diff --git a/Commit.m b/Commit.m index 21f1942..9b58293 100644 --- a/Commit.m +++ b/Commit.m @@ -35,6 +35,7 @@ #import "StringIO.h" #import "Commit.h" #import "DataInputStream.h" +#import "BufferedInputStream.h" #import "RegexKitLite.h" #import "SetNSError.h" #import "NSErrorCodes.h" @@ -43,7 +44,7 @@ #define HEADER_LENGTH (10) @interface Commit (internal) -- (BOOL)readHeader:(id )is error:(NSError **)error; +- (BOOL)readHeader:(BufferedInputStream *)is error:(NSError **)error; @end @implementation Commit @@ -57,7 +58,7 @@ @implementation Commit creationDate = _creationDate, commitFailedFiles = _commitFailedFiles; -- (id)initWithBufferedInputStream:(id )is error:(NSError **)error { +- (id)initWithBufferedInputStream:(BufferedInputStream *)is error:(NSError **)error { if (self = [super init]) { _parentCommitSHA1s = [[NSMutableSet alloc] init]; if (![self readHeader:is error:error]) { @@ -158,12 +159,12 @@ - (void)dealloc { @end @implementation Commit (internal) -- (BOOL)readHeader:(id )is error:(NSError **)error { - unsigned char *headerBytes = [is readExactly:HEADER_LENGTH error:error]; - if (headerBytes == NULL) { +- (BOOL)readHeader:(BufferedInputStream *)is error:(NSError **)error { + NSData *headerData = [is readExactly:HEADER_LENGTH error:error]; + if (headerData == nil) { return NO; } - NSString *header = [[[NSString alloc] initWithBytes:headerBytes length:HEADER_LENGTH encoding:NSASCIIStringEncoding] autorelease]; + NSString *header = [[[NSString alloc] initWithData:headerData encoding:NSUTF8StringEncoding] autorelease]; NSRange versionRange = [header rangeOfRegex:@"^CommitV(\\d{3})$" capture:1]; commitVersion = 0; if (versionRange.location != NSNotFound) { diff --git a/CommitFailedFile.h b/CommitFailedFile.h index 8a5977e..dbd5709 100644 --- a/CommitFailedFile.h +++ b/CommitFailedFile.h @@ -31,13 +31,13 @@ */ #import -@protocol BufferedInputStream; +@class BufferedInputStream; @interface CommitFailedFile : NSObject { NSString *relativePath; NSString *errorMessage; } -- (id)initWithInputStream:(id )is error:(NSError **)error; +- (id)initWithInputStream:(BufferedInputStream *)is error:(NSError **)error; - (NSString *)relativePath; - (NSString *)errorMessage; - (void)writeTo:(NSMutableData *)data; diff --git a/CommitFailedFile.m b/CommitFailedFile.m index 71fee87..4d3029c 100644 --- a/CommitFailedFile.m +++ b/CommitFailedFile.m @@ -35,7 +35,7 @@ #import "BufferedInputStream.h" @implementation CommitFailedFile -- (id)initWithInputStream:(id )is error:(NSError **)error { +- (id)initWithInputStream:(BufferedInputStream *)is error:(NSError **)error { if (self = [super init]) { if (![StringIO read:&relativePath from:is error:error] || ![StringIO read:&errorMessage from:is error:error]) { diff --git a/DiskPack.m b/DiskPack.m index abff2ea..98c6720 100644 --- a/DiskPack.m +++ b/DiskPack.m @@ -34,6 +34,7 @@ #import "DiskPack.h" #import "SetNSError.h" #import "FDInputStream.h" +#import "BufferedInputStream.h" #import "StringIO.h" #import "IntegerIO.h" #import "ServerBlob.h" @@ -51,7 +52,7 @@ @interface DiskPack (internal) - (BOOL)savePack:(ServerBlob *)sb bytesWritten:(unsigned long long *)written error:(NSError **)error; -- (NSArray *)sortedPackIndexEntriesFromStream:(id )fis error:(NSError **)error; +- (NSArray *)sortedPackIndexEntriesFromStream:(BufferedInputStream *)fis error:(NSError **)error; @end @implementation DiskPack @@ -113,6 +114,7 @@ - (ServerBlob *)newServerBlobForObjectAtOffset:(unsigned long long)offset error: } ServerBlob *ret = nil; FDInputStream *fdis = [[FDInputStream alloc] initWithFD:fd]; + BufferedInputStream *bis = [[BufferedInputStream alloc] initWithUnderlyingStream:fdis]; do { if (lseek(fd, offset, SEEK_SET) == -1) { SETNSERROR(@"UnixErrorDomain", errno, @"lseek(%@, %qu): %s", localPath, offset, strerror(errno)); @@ -120,26 +122,26 @@ - (ServerBlob *)newServerBlobForObjectAtOffset:(unsigned long long)offset error: } NSString *mimeType; NSString *downloadName; - if (![StringIO read:&mimeType from:fdis error:error] || ![StringIO read:&downloadName from:fdis error:error]) { + if (![StringIO read:&mimeType from:bis error:error] || ![StringIO read:&downloadName from:bis error:error]) { break; } uint64_t dataLen = 0; - if (![IntegerIO readUInt64:&dataLen from:fdis error:error]) { + if (![IntegerIO readUInt64:&dataLen from:bis error:error]) { break; } NSData *data = nil; if (dataLen > 0) { - const unsigned char *bytes = [fdis readExactly:dataLen error:error]; - if (bytes == NULL) { + data = [bis readExactly:dataLen error:error]; + if (data == nil) { break; } - data = [NSData dataWithBytes:bytes length:dataLen]; } else { data = [NSData data]; } ret = [[ServerBlob alloc] initWithData:data mimeType:mimeType downloadName:downloadName]; } while (0); close(fd); + [bis release]; [fdis release]; return ret; } @@ -158,7 +160,9 @@ - (NSArray *)sortedPackIndexEntries:(NSError **)error { return NO; } FileInputStream *fis = [[FileInputStream alloc] initWithPath:localPath offset:0 length:length]; - NSArray *ret = [self sortedPackIndexEntriesFromStream:fis error:error]; + BufferedInputStream *bis = [[BufferedInputStream alloc] initWithUnderlyingStream:fis]; + NSArray *ret = [self sortedPackIndexEntriesFromStream:bis error:error]; + [bis release]; [fis release]; return ret; } @@ -183,7 +187,7 @@ - (BOOL)savePack:(ServerBlob *)sb bytesWritten:(unsigned long long *)written err [is release]; return ret; } -- (NSArray *)sortedPackIndexEntriesFromStream:(id )is error:(NSError **)error { +- (NSArray *)sortedPackIndexEntriesFromStream:(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]) { @@ -210,7 +214,7 @@ - (NSArray *)sortedPackIndexEntriesFromStream:(id )is error 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]; + NSString *objectSHA1 = [SHA1Hash hashStream:is withLength:length error:error]; if (objectSHA1 == nil) { return NO; } diff --git a/Node.m b/Node.m index 431aa24..b53e5e0 100644 --- a/Node.m +++ b/Node.m @@ -42,7 +42,7 @@ @implementation Node @synthesize ctime_sec, ctime_nsec, createTime_sec, createTime_nsec, st_nlink, st_ino; @dynamic treeSHA1, dataSHA1s; -- (id)initWithInputStream:(id )is treeVersion:(int)theTreeVersion error:(NSError **)error { +- (id)initWithInputStream:(BufferedInputStream *)is treeVersion:(int)theTreeVersion error:(NSError **)error { if (self = [super init]) { treeVersion = theTreeVersion; dataSHA1s = [[NSMutableArray alloc] init]; diff --git a/Restorer.h b/Restorer.h index 95d2136..9b16204 100644 --- a/Restorer.h +++ b/Restorer.h @@ -42,6 +42,7 @@ NSString *rootPath; NSMutableArray *restoreNodes; NSMutableDictionary *hardlinks; + unsigned long long writtenToCurrentFile; } - (id)initWithS3Service:(S3Service *)theS3 s3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID bucketUUID:(NSString *)theBucketUUID bucketName:(NSString *)theBucketName encryptionKey:(NSString *)theEncryptionKey; - (BOOL)restore:(NSError **)error; diff --git a/Restorer.m b/Restorer.m index 090fb3b..39ec214 100644 --- a/Restorer.m +++ b/Restorer.m @@ -47,14 +47,19 @@ #import "NSFileManager_extra.h" #import "CFStreamPair.h" #import "NSErrorCodes.h" +#import "BufferedInputStream.h" +#import "StreamPairFactory.h" + +#define MAX_RETRIES (10) +#define MY_BUF_SIZE (8192) @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)doAppendBlobForSHA1:(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; @@ -354,26 +359,6 @@ - (BOOL)createFile:(Node *)node atPath:(NSString *)path error:(NSError **)error 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) { @@ -386,35 +371,76 @@ - (BOOL)createFileOnceAtPath:(NSString *)path fromSHA1s:(NSArray *)dataSHA1s err 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); + int i = 0; + BOOL ret = NO; + NSError *myError = nil; + NSAutoreleasePool *pool = nil; + for (;;) { + [pool drain]; + pool = [[NSAutoreleasePool alloc] init]; + if ([self doAppendBlobForSHA1:sha1 toFile:fos error:&myError]) { + ret = YES; + break; + } + [[StreamPairFactory theFactory] clear]; + BOOL isNetworkError = [[myError domain] isEqualToString:[CFStreamPair errorDomain]]; + // Retry indefinitely on network errors: + if (!isNetworkError && (++i >= MAX_RETRIES)) { + HSLogError(@"failed to get blob %@ after %d retries: %@", sha1, i, [myError localizedDescription]); + break; + } + HSLogWarn(@"error appending blob %@ to file %@ (retrying): %@", sha1, [fos path], [myError localizedDescription]); + // Seek back to the starting offset for this blob: + if (![fos seekTo:writtenToCurrentFile error:&myError]) { + ret = NO; + break; + } + } + [myError retain]; + [pool drain]; + [myError autorelease]; + if (error != NULL) { + *error = myError; + } + return ret; +} +- (BOOL)doAppendBlobForSHA1:(NSString *)sha1 toFile:(FileOutputStream *)fos error:(NSError **)error { + if (error != NULL) { + *error = nil; + } + ServerBlob *sb = [[repo newServerBlobForSHA1:sha1 error:error] autorelease]; + if (sb == nil) { return NO; } - id is = [dataBlob newInputStream]; - [dataBlob release]; + id is = [[sb newInputStream] autorelease]; BOOL ret = YES; + NSAutoreleasePool *pool = nil; + unsigned char *buf = (unsigned char *)malloc(MY_BUF_SIZE); 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; - } - } + [pool drain]; + pool = [[NSAutoreleasePool alloc] init]; + NSUInteger received = [is read:buf bufferLength:MY_BUF_SIZE error:error]; + if (received < 0) { + ret = NO; + break; + } + if (received == 0) { break; } if (![fos write:buf length:received error:error]) { ret = NO; break; } - [NSThread sleepForTimeInterval:0.01]; + writtenToCurrentFile += received; + } + free(buf); + if (error != NULL) { + [*error retain]; + } + [pool drain]; + if (error != NULL) { + [*error autorelease]; } - [is release]; return ret; } - (BOOL)createSymLink:(Node *)node path:(NSString *)symLinkFile target:(NSString *)target error:(NSError **)error { @@ -450,7 +476,9 @@ - (BOOL)applyXAttrsSHA1:(NSString *)xattrsSHA1 toFile:(NSString *)path error:(NS return NO; } DataInputStream *is = [xattrsData newInputStream]; - XAttrSet *set = [[[XAttrSet alloc] initWithBufferedInputStream:is error:error] autorelease]; + BufferedInputStream *bis = [[BufferedInputStream alloc] initWithUnderlyingStream:is]; + XAttrSet *set = [[[XAttrSet alloc] initWithBufferedInputStream:bis error:error] autorelease]; + [bis release]; [is release]; if (!set) { return NO; diff --git a/Tree.h b/Tree.h index b3a6e79..5ad8918 100644 --- a/Tree.h +++ b/Tree.h @@ -32,7 +32,7 @@ #import #import "Blob.h" -@protocol BufferedInputStream; +@class BufferedInputStream; @class Node; #define CURRENT_TREE_VERSION 10 @@ -64,7 +64,7 @@ NSMutableDictionary *nodes; } + (NSString *)errorDomain; -- (id)initWithBufferedInputStream:(id )is error:(NSError **)error; +- (id)initWithBufferedInputStream:(BufferedInputStream *)is error:(NSError **)error; - (NSArray *)childNodeNames; - (Node *)childNodeWithName:(NSString *)name; - (BOOL)containsNodeNamed:(NSString *)name; diff --git a/Tree.m b/Tree.m index 6e904b0..51abd77 100644 --- a/Tree.m +++ b/Tree.m @@ -37,12 +37,13 @@ #import "Tree.h" #import "Blob.h" #import "DataInputStream.h" +#import "BufferedInputStream.h" #import "SetNSError.h" #import "RegexKitLite.h" #import "NSErrorCodes.h" @interface Tree (internal) -- (BOOL)readHeader:(id )is error:(NSError **)error; +- (BOOL)readHeader:(BufferedInputStream *)is error:(NSError **)error; @end @implementation Tree @@ -52,7 +53,7 @@ @implementation Tree + (NSString *)errorDomain { return @"TreeErrorDomain"; } -- (id)initWithBufferedInputStream:(id )is error:(NSError **)error { +- (id)initWithBufferedInputStream:(BufferedInputStream *)is error:(NSError **)error { if (self = [super init]) { if (![self readHeader:is error:error]) { [self release]; @@ -164,12 +165,12 @@ - (Blob *)toBlob { @end @implementation Tree (internal) -- (BOOL)readHeader:(id )is error:(NSError **)error { - unsigned char *headerBytes = [is readExactly:TREE_HEADER_LENGTH error:error]; - if (headerBytes == NULL) { +- (BOOL)readHeader:(BufferedInputStream *)is error:(NSError **)error { + NSData *headerData = [is readExactly:TREE_HEADER_LENGTH error:error]; + if (headerData == nil) { return NO; } - NSString *header = [[[NSString alloc] initWithBytes:headerBytes length:TREE_HEADER_LENGTH encoding:NSASCIIStringEncoding] autorelease]; + NSString *header = [[[NSString alloc] initWithData:headerData encoding:NSUTF8StringEncoding] autorelease]; NSRange versionRange = [header rangeOfRegex:@"^TreeV(\\d{3})$" capture:1]; treeVersion = 0; if (versionRange.location != NSNotFound) { diff --git a/XAttrSet.h b/XAttrSet.h index 4eb7c90..c7e4e12 100644 --- a/XAttrSet.h +++ b/XAttrSet.h @@ -38,7 +38,7 @@ NSMutableDictionary *xattrs; } - (id)initWithPath:(NSString *)thePath error:(NSError **)error; -- (id)initWithBufferedInputStream:(id )is error:(NSError **)error; +- (id)initWithBufferedInputStream:(BufferedInputStream *)is error:(NSError **)error; - (Blob *)toBlob; - (NSUInteger)count; - (unsigned long long)dataLength; diff --git a/XAttrSet.m b/XAttrSet.m index 0c99ae4..551805c 100644 --- a/XAttrSet.m +++ b/XAttrSet.m @@ -38,6 +38,7 @@ #import "IntegerIO.h" #import "Blob.h" #import "DataInputStream.h" +#import "BufferedInputStream.h" #import "SetNSError.h" #import "NSErrorCodes.h" #import "Streams.h" @@ -47,7 +48,7 @@ @interface XAttrSet (internal) - (BOOL)loadFromPath:(NSString *)thePath error:(NSError **)error; -- (BOOL)loadFromInputStream:(id )is error:(NSError **)error; +- (BOOL)loadFromInputStream:(BufferedInputStream *)is error:(NSError **)error; @end @implementation XAttrSet @@ -69,7 +70,7 @@ - (id)initWithPath:(NSString *)thePath error:(NSError **)error { } return self; } -- (id)initWithBufferedInputStream:(id )is error:(NSError **)error { +- (id)initWithBufferedInputStream:(BufferedInputStream *)is error:(NSError **)error { if (self = [super init]) { xattrs = [[NSMutableDictionary alloc] init]; if (![self loadFromInputStream:is error:error]) { @@ -189,12 +190,12 @@ - (BOOL)loadFromPath:(NSString *)thePath error:(NSError **)error { } return YES; } -- (BOOL)loadFromInputStream:(id )is error:(NSError **)error { - unsigned char *headerBytes = [is readExactly:HEADER_LENGTH error:error]; - if (headerBytes == NULL) { +- (BOOL)loadFromInputStream:(BufferedInputStream *)is error:(NSError **)error { + NSData *headerData = [is readExactly:HEADER_LENGTH error:error]; + if (headerData == nil) { return NO; } - if (strncmp((const char *)headerBytes, "XAttrSetV002", HEADER_LENGTH)) { + if (strncmp((const char *)[headerData bytes], "XAttrSetV002", HEADER_LENGTH)) { SETNSERROR(@"XAttrSetErrorDomain", ERROR_INVALID_OBJECT_VERSION, @"invalid XAttrSet header"); return NO; } diff --git a/arq_restore.m b/arq_restore.m index eec91e9..e4565e8 100644 --- a/arq_restore.m +++ b/arq_restore.m @@ -41,7 +41,7 @@ static void printUsage(const char *exeName) { fprintf(stderr, "\t%s [-l log_level] /s3bucket/computerUUID/folderUUID\n", exeName); } int main (int argc, const char **argv) { - setHSLogLevel(HSLOG_LEVEL_ERROR); + setHSLogLevel(HSLOG_LEVEL_WARN); char *exePath = strdup(argv[0]); char *exeName = basename(exePath); NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; diff --git a/arq_restore.xcodeproj/project.pbxproj b/arq_restore.xcodeproj/project.pbxproj index c99bdcf..fa85ac0 100644 --- a/arq_restore.xcodeproj/project.pbxproj +++ b/arq_restore.xcodeproj/project.pbxproj @@ -47,9 +47,7 @@ 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 */; }; @@ -102,9 +100,7 @@ F83C1A9811CA7C170001958F /* DataInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B82E1160E86E007EC01E /* DataInputStream.m */; }; F83C1A9911CA7C170001958F /* Writer.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8311160E878007EC01E /* Writer.m */; }; F83C1A9A11CA7C170001958F /* NSData-InputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B83A1160E8DD007EC01E /* NSData-InputStream.m */; }; - F83C1A9B11CA7C170001958F /* StreamPairFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B83E1160E900007EC01E /* StreamPairFactory.m */; }; F83C1A9C11CA7C170001958F /* Streams.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8411160E90F007EC01E /* Streams.m */; }; - F83C1A9D11CA7C170001958F /* CFStreamPair.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8521160E9B0007EC01E /* CFStreamPair.m */; }; F83C1A9E11CA7C170001958F /* CFStreamInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8571160E9C9007EC01E /* CFStreamInputStream.m */; }; F83C1A9F11CA7C170001958F /* CFStreamOutputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8591160E9C9007EC01E /* CFStreamOutputStream.m */; }; F83C1AA011CA7C170001958F /* DNS_SDErrors.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B85F1160E9F0007EC01E /* DNS_SDErrors.m */; }; @@ -156,6 +152,17 @@ F83C1AE311CA7C7C0001958F /* arq_restore.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* arq_restore.m */; }; F83C1D0A11CA929D0001958F /* BucketVerifier.m in Sources */ = {isa = PBXBuildFile; fileRef = F83C1D0911CA929D0001958F /* BucketVerifier.m */; }; F83C1D1D11CA95AF0001958F /* BucketVerifier.m in Sources */ = {isa = PBXBuildFile; fileRef = F83C1D0911CA929D0001958F /* BucketVerifier.m */; }; + F8987235121EB68900F07D76 /* BinaryPListReader.m in Sources */ = {isa = PBXBuildFile; fileRef = F8987232121EB68900F07D76 /* BinaryPListReader.m */; }; + F8987236121EB68900F07D76 /* BinaryPListWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = F8987234121EB68900F07D76 /* BinaryPListWriter.m */; }; + F8987527121EB89100F07D76 /* CFStreamPair.m in Sources */ = {isa = PBXBuildFile; fileRef = F8987526121EB89100F07D76 /* CFStreamPair.m */; }; + F8987532121EB94000F07D76 /* StreamPairFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = F8987531121EB94000F07D76 /* StreamPairFactory.m */; }; + F8987560121EBD9600F07D76 /* BufferedInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F898755F121EBD9600F07D76 /* BufferedInputStream.m */; }; + F8987588121EBDF300F07D76 /* BinaryPListReader.m in Sources */ = {isa = PBXBuildFile; fileRef = F8987232121EB68900F07D76 /* BinaryPListReader.m */; }; + F8987589121EBDF400F07D76 /* BinaryPListWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = F8987234121EB68900F07D76 /* BinaryPListWriter.m */; }; + F898758A121EBDFA00F07D76 /* BufferedInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F898755F121EBD9600F07D76 /* BufferedInputStream.m */; }; + F898758B121EBDFD00F07D76 /* CFStreamPair.m in Sources */ = {isa = PBXBuildFile; fileRef = F8987526121EB89100F07D76 /* CFStreamPair.m */; }; + F898758C121EBE0600F07D76 /* UserAndComputer.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D67D121DA542002D09C1 /* UserAndComputer.m */; }; + F898758D121EBE0800F07D76 /* StreamPairFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = F8987531121EB94000F07D76 /* StreamPairFactory.m */; }; F8D678001160F26A00CC270E /* Restorer.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D677FF1160F26A00CC270E /* Restorer.m */; }; F8D6781D1160F4FD00CC270E /* SHA1Hash.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6781C1160F4FD00CC270E /* SHA1Hash.m */; }; F8D678331160F62E00CC270E /* ArqUserLibrary.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678321160F62E00CC270E /* ArqUserLibrary.m */; }; @@ -321,12 +328,8 @@ F805B8391160E8DD007EC01E /* NSData-InputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData-InputStream.h"; sourceTree = ""; }; F805B83A1160E8DD007EC01E /* NSData-InputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData-InputStream.m"; sourceTree = ""; }; F805B83C1160E900007EC01E /* StreamPair.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StreamPair.h; sourceTree = ""; }; - F805B83D1160E900007EC01E /* StreamPairFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StreamPairFactory.h; sourceTree = ""; }; - F805B83E1160E900007EC01E /* StreamPairFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StreamPairFactory.m; sourceTree = ""; }; F805B8401160E90F007EC01E /* Streams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Streams.h; sourceTree = ""; }; F805B8411160E90F007EC01E /* Streams.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Streams.m; sourceTree = ""; }; - F805B8511160E9B0007EC01E /* CFStreamPair.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFStreamPair.h; sourceTree = ""; }; - F805B8521160E9B0007EC01E /* CFStreamPair.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CFStreamPair.m; sourceTree = ""; }; F805B8561160E9C9007EC01E /* CFStreamInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFStreamInputStream.h; sourceTree = ""; }; F805B8571160E9C9007EC01E /* CFStreamInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CFStreamInputStream.m; sourceTree = ""; }; F805B8581160E9C9007EC01E /* CFStreamOutputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFStreamOutputStream.h; sourceTree = ""; }; @@ -356,6 +359,16 @@ F83C1AD711CA7C170001958F /* arq_verify */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = arq_verify; sourceTree = BUILT_PRODUCTS_DIR; }; F83C1D0811CA929D0001958F /* BucketVerifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BucketVerifier.h; sourceTree = ""; }; F83C1D0911CA929D0001958F /* BucketVerifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BucketVerifier.m; sourceTree = ""; }; + F8987231121EB68900F07D76 /* BinaryPListReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BinaryPListReader.h; sourceTree = ""; }; + F8987232121EB68900F07D76 /* BinaryPListReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BinaryPListReader.m; sourceTree = ""; }; + F8987233121EB68900F07D76 /* BinaryPListWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BinaryPListWriter.h; sourceTree = ""; }; + F8987234121EB68900F07D76 /* BinaryPListWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BinaryPListWriter.m; sourceTree = ""; }; + F8987525121EB89100F07D76 /* CFStreamPair.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFStreamPair.h; sourceTree = ""; }; + F8987526121EB89100F07D76 /* CFStreamPair.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CFStreamPair.m; sourceTree = ""; }; + F898752F121EB94000F07D76 /* StreamPair.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StreamPair.h; sourceTree = ""; }; + F8987530121EB94000F07D76 /* StreamPairFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StreamPairFactory.h; sourceTree = ""; }; + F8987531121EB94000F07D76 /* StreamPairFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StreamPairFactory.m; sourceTree = ""; }; + F898755F121EBD9600F07D76 /* BufferedInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BufferedInputStream.m; sourceTree = ""; }; F8D6763E1160F22800CC270E /* SetNSError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SetNSError.h; sourceTree = ""; }; F8D677FE1160F26A00CC270E /* Restorer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Restorer.h; sourceTree = ""; }; F8D677FF1160F26A00CC270E /* Restorer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Restorer.m; sourceTree = ""; }; @@ -581,6 +594,10 @@ F805B7401160DCFE007EC01E /* plist */ = { isa = PBXGroup; children = ( + F8987231121EB68900F07D76 /* BinaryPListReader.h */, + F8987232121EB68900F07D76 /* BinaryPListReader.m */, + F8987233121EB68900F07D76 /* BinaryPListWriter.h */, + F8987234121EB68900F07D76 /* BinaryPListWriter.m */, F805B7411160DCFE007EC01E /* ArrayNode.h */, F805B7421160DCFE007EC01E /* ArrayNode.m */, F805B7431160DCFE007EC01E /* BooleanNode.h */, @@ -671,6 +688,11 @@ F805B7C91160E445007EC01E /* http */ = { isa = PBXGroup; children = ( + F898752F121EB94000F07D76 /* StreamPair.h */, + F8987530121EB94000F07D76 /* StreamPairFactory.h */, + F8987531121EB94000F07D76 /* StreamPairFactory.m */, + F8987525121EB89100F07D76 /* CFStreamPair.h */, + F8987526121EB89100F07D76 /* CFStreamPair.m */, F805B7CA1160E445007EC01E /* HTTP.h */, F805B7CB1160E445007EC01E /* HTTPConnection.h */, F805B7CC1160E445007EC01E /* HTTPConnection.m */, @@ -687,17 +709,14 @@ F805B8081160E7A1007EC01E /* io */ = { isa = PBXGroup; children = ( - F8F4D19E121D78AD002D09C1 /* MonitoredInputStream.h */, - F8F4D19F121D78AD002D09C1 /* MonitoredInputStream.m */, F8D678A31160FA5F00CC270E /* BooleanIO.h */, F8D678A41160FA5F00CC270E /* BooleanIO.m */, F805B8331160E882007EC01E /* BufferedInputStream.h */, + F898755F121EBD9600F07D76 /* BufferedInputStream.m */, 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 */, @@ -734,14 +753,14 @@ F805B80B1160E7C3007EC01E /* InputStream.h */, F8D6787E1160F8A000CC270E /* IntegerIO.h */, F8D6787F1160F8A000CC270E /* IntegerIO.m */, + F8F4D19E121D78AD002D09C1 /* MonitoredInputStream.h */, + F8F4D19F121D78AD002D09C1 /* MonitoredInputStream.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 */, @@ -868,9 +887,7 @@ 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 */, @@ -921,6 +938,11 @@ F8F4D1FE121D8409002D09C1 /* PackIndexWriter.m in Sources */, F8F4D207121D8696002D09C1 /* ArqRepo_Verifier.m in Sources */, F8F4D67E121DA542002D09C1 /* UserAndComputer.m in Sources */, + F8987235121EB68900F07D76 /* BinaryPListReader.m in Sources */, + F8987236121EB68900F07D76 /* BinaryPListWriter.m in Sources */, + F8987527121EB89100F07D76 /* CFStreamPair.m in Sources */, + F8987532121EB94000F07D76 /* StreamPairFactory.m in Sources */, + F8987560121EBD9600F07D76 /* BufferedInputStream.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -968,9 +990,7 @@ F83C1A9811CA7C170001958F /* DataInputStream.m in Sources */, F83C1A9911CA7C170001958F /* Writer.m in Sources */, F83C1A9A11CA7C170001958F /* NSData-InputStream.m in Sources */, - F83C1A9B11CA7C170001958F /* StreamPairFactory.m in Sources */, F83C1A9C11CA7C170001958F /* Streams.m in Sources */, - F83C1A9D11CA7C170001958F /* CFStreamPair.m in Sources */, F83C1A9E11CA7C170001958F /* CFStreamInputStream.m in Sources */, F83C1A9F11CA7C170001958F /* CFStreamOutputStream.m in Sources */, F83C1AA011CA7C170001958F /* DNS_SDErrors.m in Sources */, @@ -1019,6 +1039,12 @@ F8F4D5A1121D9FC2002D09C1 /* AppKeychain.m in Sources */, F8F4D5A2121D9FC9002D09C1 /* FarkPath.m in Sources */, F8F4D5A3121D9FCF002D09C1 /* PackIndexWriter.m in Sources */, + F8987588121EBDF300F07D76 /* BinaryPListReader.m in Sources */, + F8987589121EBDF400F07D76 /* BinaryPListWriter.m in Sources */, + F898758A121EBDFA00F07D76 /* BufferedInputStream.m in Sources */, + F898758B121EBDFD00F07D76 /* CFStreamPair.m in Sources */, + F898758C121EBE0600F07D76 /* UserAndComputer.m in Sources */, + F898758D121EBE0800F07D76 /* StreamPairFactory.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/crypto/SHA1Hash.h b/crypto/SHA1Hash.h index 6a9cd7c..a32df48 100644 --- a/crypto/SHA1Hash.h +++ b/crypto/SHA1Hash.h @@ -37,8 +37,9 @@ @interface SHA1Hash : NSObject { } ++ (NSString *)errorDomain; + (NSString *)hashData:(NSData *)data; + (NSString *)hashBlob:(Blob *)blob blobLength:(unsigned long long *)blobLength error:(NSError **)error; -+ (NSString *)hashStream:(id )is withlength:(uint64_t)length error:(NSError **)error; ++ (NSString *)hashStream:(id )is withLength:(uint64_t)length error:(NSError **)error; + (NSString *)hashFile:(NSString *)path error:(NSError **)error; @end diff --git a/crypto/SHA1Hash.m b/crypto/SHA1Hash.m index f4561d0..a5ba5b9 100644 --- a/crypto/SHA1Hash.m +++ b/crypto/SHA1Hash.m @@ -39,6 +39,8 @@ #import "Blob.h" #import "BufferedInputStream.h" +#define MY_BUF_SIZE (8192) + @interface SHA1Hash (internal) + (NSString *)hashStream:(id )is error:(NSError **)error; + (NSString *)hashStream:(id )is streamLength:(unsigned long long *)streamLength error:(NSError **)error; @@ -55,6 +57,9 @@ + (NSString *)hashStream:(id )is streamLength:(unsigned long long * } @implementation SHA1Hash ++ (NSString *)errorDomain { + return @"SHA1HashErrorDomain"; +} + (NSString *)hashData:(NSData *)data { SHA_CTX ctx; SHA1_Init(&ctx); @@ -84,20 +89,29 @@ + (NSString *)hashFile:(NSString *)path error:(NSError **)error { [fis release]; return sha1; } -+ (NSString *)hashStream:(id )bis withlength:(uint64_t)length error:(NSError **)error { ++ (NSString *)hashStream:(id )is withLength:(uint64_t)length error:(NSError **)error { SHA_CTX ctx; SHA1_Init(&ctx); uint64_t received = 0; + unsigned char *buf = (unsigned char *)malloc(MY_BUF_SIZE); + NSInteger ret = 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; + uint64_t toReadThisTime = toRead > MY_BUF_SIZE ? MY_BUF_SIZE : toRead; + ret = [is read:buf bufferLength:toReadThisTime error:error]; + if (ret < 0) { + break; } - NSAssert(thisLength > 0, @"expected more than 0 bytes"); - SHA1_Update(&ctx, buf, (unsigned long)thisLength); - received += (uint64_t)thisLength; + if (ret == 0) { + SETNSERROR([SHA1Hash errorDomain], -1, @"unexpected EOF after %qu of %qu bytes", received, length); + break; + } + SHA1_Update(&ctx, buf, ret); + received += (uint64_t)ret; + } + free(buf); + if (ret <= 0) { + return nil; } unsigned char md[SHA_DIGEST_LENGTH]; SHA1_Final(md, &ctx); @@ -114,22 +128,19 @@ + (NSString *)hashStream:(id )is streamLength:(unsigned long long * SHA_CTX ctx; SHA1_Init(&ctx); *streamLength = 0; + unsigned char *buf = (unsigned char *)malloc(MY_BUF_SIZE); + NSInteger ret = 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. + ret = [is read:buf bufferLength:MY_BUF_SIZE error:error]; + if (ret <= 0) { + break; } - NSAssert(length > 0, @"expected more than 0 bytes"); - SHA1_Update(&ctx, buf, (unsigned long)length); - *streamLength += (unsigned long long)length; + SHA1_Update(&ctx, buf, (unsigned long)ret); + *streamLength += (unsigned long long)ret; + } + free(buf); + if (ret < 0) { + return nil; } unsigned char md[SHA_DIGEST_LENGTH]; SHA1_Final(md, &ctx); diff --git a/http/CFStreamPair.h b/http/CFStreamPair.h index 032a131..ffdbf67 100644 --- a/http/CFStreamPair.h +++ b/http/CFStreamPair.h @@ -46,6 +46,6 @@ } + (NSString *)errorDomain; + (NSError *)NSErrorWithNetworkError:(CFErrorRef)err; -- (id)initWithHost:(NSString *)theHost useSSL:(BOOL)isUseSSL maxLifetime:(NSTimeInterval)theMaxLifetime; +- (id)initWithHost:(NSString *)theHost port:(int)thePort useSSL:(BOOL)isUseSSL maxLifetime:(NSTimeInterval)theMaxLifetime; @end diff --git a/http/CFStreamPair.m b/http/CFStreamPair.m index 43fcca0..66c7299 100644 --- a/http/CFStreamPair.m +++ b/http/CFStreamPair.m @@ -39,9 +39,6 @@ #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"; @@ -133,12 +130,12 @@ + (NSError *)NSErrorWithNetworkError:(CFErrorRef)err { 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 { +- (id)initWithHost:(NSString *)theHost port:(int)thePort useSSL:(BOOL)isUseSSL maxLifetime:(NSTimeInterval)theMaxLifetime { if (self = [super init]) { description = [[NSString alloc] initWithFormat:@"", theHost, (isUseSSL ? @"YES" : @"NO")]; CFReadStreamRef readStream; CFWriteStreamRef writeStream; - CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)theHost, (isUseSSL ? HTTPS_PORT : HTTP_PORT), &readStream, &writeStream); + CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)theHost, thePort, &readStream, &writeStream); if (isUseSSL) { NSDictionary *sslProperties = [NSDictionary dictionaryWithObjectsAndKeys: (NSString *)kCFStreamSocketSecurityLevelNegotiatedSSL, kCFStreamSSLLevel, @@ -193,18 +190,9 @@ - (BOOL)isUsable { 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]; +#pragma mark InputStream +- (NSInteger)read:(unsigned char *)buf bufferLength:(NSUInteger)bufferLength error:(NSError **)error { + return [is read:buf bufferLength:bufferLength error:error]; } - (NSData *)slurp:(NSError **)error { return [is slurp:error]; diff --git a/http/HTTP.h b/http/HTTP.h index c6a7186..9949fb8 100644 --- a/http/HTTP.h +++ b/http/HTTP.h @@ -32,6 +32,7 @@ #define HTTP_1_1 @"1.1" +#define HTTP_OK (200) #define HTTP_INTERNAL_SERVER_ERROR (500) #define HTTP_FORBIDDEN (403) #define HTTP_BAD_REQUEST (400) diff --git a/http/HTTPConnection.h b/http/HTTPConnection.h index 0977831..6cf09d3 100644 --- a/http/HTTPConnection.h +++ b/http/HTTPConnection.h @@ -33,16 +33,18 @@ #import #import "InputStream.h" @protocol StreamPair; -@protocol BufferedInputStream; +@class BufferedInputStream; @class HTTPRequest; @class HTTPResponse; @interface HTTPConnection : NSObject { id streamPair; + BufferedInputStream *bufferedInputStream; HTTPRequest *request; HTTPResponse *response; } - (id)initWithHost:(NSString *)theHost useSSL:(BOOL)isUseSSL error:(NSError **)error; +- (id)initWithHost:(NSString *)theHost port:(int)thePort 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; @@ -55,7 +57,7 @@ - (NSString *)responseHeaderForKey:(NSString *)key; - (NSString *)responseMimeType; - (NSString *)responseDownloadName; -- (id )newResponseBodyStream:(NSError **)error; +- (id )newResponseBodyStream:(NSError **)error; - (NSData *)slurpResponseBody:(NSError **)error; - (void)setCloseRequested; @end diff --git a/http/HTTPConnection.m b/http/HTTPConnection.m index 610cb90..3e2e724 100644 --- a/http/HTTPConnection.m +++ b/http/HTTPConnection.m @@ -39,15 +39,23 @@ #import "RegexKitLite.h" #import "FDOutputStream.h" #import "FDInputStream.h" +#import "BufferedInputStream.h" + +static int HTTP_DEFAULT_PORT = 80; +static int HTTPS_DEFAULT_PORT = 443; @implementation HTTPConnection - (id)initWithHost:(NSString *)theHost useSSL:(BOOL)isUseSSL error:(NSError **)error { + return [self initWithHost:theHost port:(isUseSSL ? HTTPS_DEFAULT_PORT : HTTP_DEFAULT_PORT) useSSL:isUseSSL error:error]; +} +- (id)initWithHost:(NSString *)theHost port:(int)thePort useSSL:(BOOL)isUseSSL error:(NSError **)error { if (self = [super init]) { - streamPair = [[StreamPairFactory theFactory] newStreamPairToHost:theHost useSSL:isUseSSL error:error]; + streamPair = [[StreamPairFactory theFactory] newStreamPairToHost:theHost port:thePort useSSL:isUseSSL error:error]; if (streamPair == nil) { [self release]; return nil; } + bufferedInputStream = [[BufferedInputStream alloc] initWithUnderlyingStream:streamPair]; request = [[HTTPRequest alloc] initWithHost:theHost]; response = [[HTTPResponse alloc] init]; } @@ -55,6 +63,7 @@ - (id)initWithHost:(NSString *)theHost useSSL:(BOOL)isUseSSL error:(NSError **)e } - (void)dealloc { [streamPair release]; + [bufferedInputStream release]; [request release]; [response release]; [super dealloc]; @@ -87,10 +96,12 @@ - (BOOL)executeRequestWithBody:(id )bodyStream error:(NSError **)er if (![request write:streamPair error:error]) { return NO; } - if (bodyStream != nil && ![Streams transferFrom:bodyStream to:streamPair error:error]) { + unsigned long long written = 0; + if (bodyStream != nil && ![Streams transferFrom:bodyStream to:streamPair bytesWritten:&written error:error]) { return NO; } - if (![response readHead:streamPair requestMethod:[request method] error:error]) { + HSLogTrace(@"wrote %qu-byte request body", written); + if (![response readHead:bufferedInputStream requestMethod:[request method] error:error]) { return NO; } if ([[response headerForKey:@"Connection"] isEqualToString:@"Close"]) { @@ -123,8 +134,8 @@ - (NSString *)responseDownloadName { } return downloadName; } -- (id )newResponseBodyStream:(NSError **)error { - return [response newResponseInputStream:streamPair error:error]; +- (id )newResponseBodyStream:(NSError **)error { + return [response newResponseInputStream:bufferedInputStream error:error]; } - (NSData *)slurpResponseBody:(NSError **)error { id is = [self newResponseBodyStream:error]; diff --git a/http/HTTPRequest.m b/http/HTTPRequest.m index c45bd02..276f33b 100644 --- a/http/HTTPRequest.m +++ b/http/HTTPRequest.m @@ -89,12 +89,14 @@ - (BOOL)write:(id )os error:(NSError **)error { Writer *writer = [[Writer alloc] initWithOutputStream:os]; BOOL ret = NO; do { + HSLogTrace(@"writing %@ %@%@ HTTP/%@\\r\\n", method, pathInfo, (queryString != nil ? queryString : @""), protocol); if (![writer write:method error:error] || ![writer write:@" " error:error] || ![writer write:pathInfo error:error]) { break; } if (queryString != nil) { + HSLogTrace(@"writing %@", queryString); if (![writer write:queryString error:error]) { break; } @@ -105,7 +107,7 @@ - (BOOL)write:(id )os error:(NSError **)error { break; } for (NSString *key in [headers allKeys]) { - HSLogTrace(@"header: %@ = %@", key, [headers objectForKey:key]); + HSLogTrace(@"writing %@: %@\\r\\n", key, [headers objectForKey:key]); if (![writer write:key error:error] || ![writer write:@": " error:error] || ![writer write:[headers objectForKey:key] error:error] @@ -113,6 +115,7 @@ - (BOOL)write:(id )os error:(NSError **)error { break; } } + HSLogTrace(@"writing \\r\\n"); if (![writer write:@"\r\n" error:error]) { break; } diff --git a/http/HTTPResponse.h b/http/HTTPResponse.h index 3bffa15..72df4ec 100644 --- a/http/HTTPResponse.h +++ b/http/HTTPResponse.h @@ -31,8 +31,9 @@ */ #import -@protocol BufferedInputStream; +@protocol InputStream; @class FDInputStream; +@class BufferedInputStream; @interface HTTPResponse : NSObject { int code; @@ -41,10 +42,10 @@ NSString *requestMethod; } - (id)init; -- (BOOL)readHead:(id )is requestMethod:(NSString *)requestMethod error:(NSError **)error; +- (BOOL)readHead:(BufferedInputStream *)is requestMethod:(NSString *)requestMethod error:(NSError **)error; - (int)code; - (NSString *)protocol; - (NSString *)headerForKey:(NSString *)key; - (unsigned long long)contentLength; -- (id )newResponseInputStream:(id )underlyingStream error:(NSError **)error; +- (id )newResponseInputStream:(BufferedInputStream *)underlyingStream error:(NSError **)error; @end diff --git a/http/HTTPResponse.m b/http/HTTPResponse.m index 84074a1..3a51099 100644 --- a/http/HTTPResponse.m +++ b/http/HTTPResponse.m @@ -78,8 +78,8 @@ - (unsigned long long)contentLength { } return (unsigned long long)contentLength; } -- (id )newResponseInputStream:(id )underlyingStream error:(NSError **)error { - id ret = nil; +- (id )newResponseInputStream:(BufferedInputStream *)underlyingStream error:(NSError **)error { + id ret = nil; if ([requestMethod isEqualToString:@"HEAD"] || code == 204) { ret = [[NSData data] newInputStream]; } else { @@ -105,14 +105,16 @@ - (unsigned long long)contentLength { } return ret; } -- (BOOL)readHead:(id )inputStream requestMethod:(NSString *)theRequestMethod error:(NSError **)error { +- (BOOL)readHead:(BufferedInputStream *)inputStream requestMethod:(NSString *)theRequestMethod error:(NSError **)error { [headers removeAllObjects]; [requestMethod release]; requestMethod = [theRequestMethod copy]; + HSLogTrace(@"reading response start line"); NSString *line = [InputStreams readLineWithCRLF:inputStream maxLength:MAX_HTTP_STATUS_LINE_LENGTH error:error]; if (line == nil) { return NO; } + HSLogTrace(@"response start line: %@", line); NSString *pattern = @"^HTTP/(1.\\d)\\s+(\\d+)\\s+(.+)\r\n$"; NSRange protoRange = [line rangeOfRegex:pattern capture:1]; NSRange codeRange = [line rangeOfRegex:pattern capture:2]; @@ -129,6 +131,7 @@ - (BOOL)readHead:(id )inputStream requestMethod:(NSString * if (line == nil) { return NO; } + HSLogTrace(@"response header: %@", line); if ([line isEqualToString:@"\r\n"]) { break; } diff --git a/http/StreamPair.h b/http/StreamPair.h index b0a35e6..d0082a3 100644 --- a/http/StreamPair.h +++ b/http/StreamPair.h @@ -31,10 +31,10 @@ */ #import -#import "BufferedInputStream.h" +#import "InputStream.h" #import "OutputStream.h" -@protocol StreamPair +@protocol StreamPair - (void)setCloseRequested; - (BOOL)isUsable; diff --git a/http/StreamPairFactory.h b/http/StreamPairFactory.h index e9ea308..ee8c824 100644 --- a/http/StreamPairFactory.h +++ b/http/StreamPairFactory.h @@ -41,6 +41,6 @@ } + (StreamPairFactory *)theFactory; - (void)setMaxStreamPairLifetime:(NSTimeInterval)theMaxLifetime; -- (id )newStreamPairToHost:(NSString *)theHost useSSL:(BOOL)isUseSSL error:(NSError **)error; +- (id )newStreamPairToHost:(NSString *)theHost port:(int)thePort useSSL:(BOOL)isUseSSL error:(NSError **)error; - (void)clear; @end diff --git a/http/StreamPairFactory.m b/http/StreamPairFactory.m index 8c2448f..1effbf4 100644 --- a/http/StreamPairFactory.m +++ b/http/StreamPairFactory.m @@ -41,7 +41,7 @@ @interface StreamPairMap : NSObject { NSMutableDictionary *streamPairs; } -- (id )newStreamPairToHost:(NSString *)host useSSL:(BOOL)useSSL maxLifeTimeSeconds:(NSTimeInterval)theMaxLifetime error:(NSError **)error; +- (id )newStreamPairToHost:(NSString *)host port:(int)thePort useSSL:(BOOL)useSSL maxLifeTimeSeconds:(NSTimeInterval)theMaxLifetime error:(NSError **)error; - (void)dropUnusableStreamPairs; @end @@ -56,8 +56,8 @@ - (void)dealloc { [streamPairs release]; [super dealloc]; } -- (id )newStreamPairToHost:(NSString *)host useSSL:(BOOL)useSSL maxLifeTimeSeconds:(NSTimeInterval)theMaxLifetime error:(NSError **)error { - NSString *key = [NSString stringWithFormat:@"%@:%@", [host lowercaseString], (useSSL ? @"SSL" : @"noSSL")]; +- (id )newStreamPairToHost:(NSString *)host port:(int)thePort useSSL:(BOOL)useSSL maxLifeTimeSeconds:(NSTimeInterval)theMaxLifetime error:(NSError **)error { + NSString *key = [NSString stringWithFormat:@"%@:%d:%@", [host lowercaseString], thePort, (useSSL ? @"SSL" : @"noSSL")]; id streamPair = [streamPairs objectForKey:key]; if (streamPair != nil) { if (![streamPair isUsable]) { @@ -68,7 +68,7 @@ - (void)dealloc { } } if (streamPair == nil) { - streamPair = [[CFStreamPair alloc] initWithHost:host useSSL:useSSL maxLifetime:theMaxLifetime]; + streamPair = [[CFStreamPair alloc] initWithHost:host port:thePort useSSL:useSSL maxLifetime:theMaxLifetime]; [streamPairs setObject:streamPair forKey:key]; } return streamPair; @@ -133,7 +133,7 @@ - (void)dealloc { - (void)setMaxStreamPairLifetime:(NSTimeInterval)theMaxLifetime { maxStreamPairLifetime = theMaxLifetime; } -- (id )newStreamPairToHost:(NSString *)theHost useSSL:(BOOL)isUseSSL error:(NSError **)error { +- (id )newStreamPairToHost:(NSString *)theHost port:(int)thePort useSSL:(BOOL)isUseSSL error:(NSError **)error { void *pthreadPtr = pthread_self(); #ifdef __LP64__ NSNumber *threadID = [NSNumber numberWithUnsignedLongLong:(uint64_t)pthreadPtr]; @@ -147,7 +147,7 @@ - (void)setMaxStreamPairLifetime:(NSTimeInterval)theMaxLifetime { [threadMap setObject:map forKey:threadID]; [map release]; } - id streamPair = [map newStreamPairToHost:theHost useSSL:isUseSSL maxLifeTimeSeconds:maxStreamPairLifetime error:error]; + id streamPair = [map newStreamPairToHost:theHost port:thePort useSSL:isUseSSL maxLifeTimeSeconds:maxStreamPairLifetime error:error]; [lock unlock]; return streamPair; } diff --git a/io/BooleanIO.h b/io/BooleanIO.h index 3ac826f..7f8388f 100644 --- a/io/BooleanIO.h +++ b/io/BooleanIO.h @@ -39,5 +39,5 @@ } + (void)write:(BOOL)b to:(NSMutableData *)data; + (BOOL)write:(BOOL)b to:(id )os error:(NSError **)error; -+ (BOOL)read:(BOOL *)value from:(id )is error:(NSError **)error; ++ (BOOL)read:(BOOL *)value from:(BufferedInputStream *)is error:(NSError **)error; @end diff --git a/io/BooleanIO.m b/io/BooleanIO.m index 45c4218..59ed00a 100644 --- a/io/BooleanIO.m +++ b/io/BooleanIO.m @@ -32,6 +32,7 @@ #import "BooleanIO.h" #import "Streams.h" +#import "BufferedInputStream.h" @implementation BooleanIO + (void)write:(BOOL)b to:(NSMutableData *)data { @@ -43,13 +44,13 @@ + (BOOL)write:(BOOL)b to:(id )os error:(NSError **)error { return [os write:&c length:1 error:error]; } -+ (BOOL)read:(BOOL *)value from:(id )is error:(NSError **)error { ++ (BOOL)read:(BOOL *)value from:(BufferedInputStream *)is error:(NSError **)error { *value = NO; - unsigned char *bytes = [is readExactly:1 error:error]; - if (!bytes) { + unsigned char c = 0; + if (![is readExactly:1 into:&c error:error]) { return NO; } - *value = bytes[0] != 0; + *value = (c != 0); return YES; } @end diff --git a/io/BufferedInputStream.h b/io/BufferedInputStream.h index a60650d..f20a723 100644 --- a/io/BufferedInputStream.h +++ b/io/BufferedInputStream.h @@ -33,8 +33,16 @@ #import #import "InputStream.h" -@protocol BufferedInputStream -- (unsigned char *)readExactly:(NSUInteger)exactLength error:(NSError **)error; -- (unsigned char *)readMaximum:(NSUInteger)maximum length:(NSUInteger *)length error:(NSError **)error; +@interface BufferedInputStream : NSObject { + id underlyingStream; + unsigned char *buf; + NSUInteger pos; + NSUInteger len; + uint64_t totalBytesReceived; +} ++ (NSString *)errorDomain; +- (id)initWithUnderlyingStream:(id )theUnderlyingStream; +- (NSData *)readExactly:(NSUInteger)exactLength error:(NSError **)error; +- (BOOL)readExactly:(NSUInteger)exactLength into:(unsigned char *)buf error:(NSError **)error; - (uint64_t)bytesReceived; @end diff --git a/io/BufferedInputStream.m b/io/BufferedInputStream.m new file mode 100644 index 0000000..7aaaa3e --- /dev/null +++ b/io/BufferedInputStream.m @@ -0,0 +1,124 @@ +/* + Copyright (c) 2009-2010, 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 "BufferedInputStream.h" +#import "InputStream.h" +#import "NSErrorCodes.h" +#import "InputStreams.h" +#import "SetNSError.h" + +#define MY_BUF_SIZE (8192) + +@implementation BufferedInputStream ++ (NSString *)errorDomain { + return @"BufInputStreamErrorDomain"; +} +- (id)initWithUnderlyingStream:(id )theUnderlyingStream { + if (self = [super init]) { + underlyingStream = [theUnderlyingStream retain]; + buf = (unsigned char *)malloc(MY_BUF_SIZE); + pos = 0; + len = 0; + } + return self; +} +- (void)dealloc { + [underlyingStream release]; + free(buf); + [super dealloc]; +} +- (NSData *)readExactly:(NSUInteger)exactLength error:(NSError **)error { + NSMutableData *data = [NSMutableData dataWithLength:exactLength]; + unsigned char *dataBuf = [data mutableBytes]; + if (![self readExactly:exactLength into:dataBuf error:error]) { + return nil; + } + return data; +} +- (BOOL)readExactly:(NSUInteger)exactLength into:(unsigned char *)outBuf error:(NSError **)error { + if (exactLength > 2147483648) { + SETNSERROR(@"InputStreamErrorDomain", -1, @"absurd length %u requested", exactLength); + return NO; + } + NSUInteger received = 0; + while (received < exactLength) { + NSInteger ret = [self read:(outBuf + received) bufferLength:(exactLength - received) error:error]; + if (ret == -1) { + return NO; + } + if (ret == 0) { + SETNSERROR([BufferedInputStream errorDomain], ERROR_EOF, @"EOF after %u of %u bytes received", received, exactLength); + return NO; + } + received += ret; + totalBytesReceived += ret; + } + return YES; +} +- (uint64_t)bytesReceived { + return totalBytesReceived; +} + +#pragma mark InputStream +- (NSInteger)read:(unsigned char *)outBuf bufferLength:(NSUInteger)outBufLen error:(NSError **)error { + NSInteger ret = 0; + NSUInteger remaining = len - pos; + if (remaining > 0) { + // Return bytes from my buf: + ret = remaining > outBufLen ? outBufLen : remaining; + memcpy(outBuf, buf + pos, ret); + pos += ret; + } else if (outBufLen > MY_BUF_SIZE) { + // Read direct into outBuf: + ret = [underlyingStream read:outBuf bufferLength:outBufLen error:error]; + } else { + // Read into my buf and return only what's asked for. + NSInteger myRet = [underlyingStream read:buf bufferLength:MY_BUF_SIZE error:error]; + if (myRet < 0) { + return myRet; + } + pos = 0; + len = myRet; + if (len > 0) { + ret = len > outBufLen ? outBufLen : len; + memcpy(outBuf, buf + pos, ret); + pos += ret; + } else { + ret = 0; + } + } + return ret; +} +- (NSData *)slurp:(NSError **)error { + return [InputStreams slurp:self error:error]; +} +@end diff --git a/io/CFStreamInputStream.h b/io/CFStreamInputStream.h index 039c4a3..62398b8 100644 --- a/io/CFStreamInputStream.h +++ b/io/CFStreamInputStream.h @@ -31,9 +31,9 @@ */ #import -#import "BufferedInputStream.h" +#import "InputStream.h" -@interface CFStreamInputStream : NSObject { +@interface CFStreamInputStream : NSObject { CFReadStreamRef readStream; BOOL isOpen; uint64_t bytesReceived; diff --git a/io/CFStreamInputStream.m b/io/CFStreamInputStream.m index 5c8a3a7..be2dca1 100644 --- a/io/CFStreamInputStream.m +++ b/io/CFStreamInputStream.m @@ -36,7 +36,7 @@ #import "NSErrorCodes.h" #import "CFStreamPair.h" -#define MY_BUF_SIZE (4096) +#define MY_BUF_SIZE (8192) #define DEFAULT_READ_TIMEOUT_SECONDS (60) @interface CFStreamInputStream (internal) @@ -57,57 +57,11 @@ - (void)dealloc { } #pragma mark InputStream -- (unsigned char *)read:(NSUInteger *)length error:(NSError **)error { +- (NSInteger)read:(unsigned char *)buf bufferLength:(NSUInteger)bufferLength error:(NSError **)error { if (![self open:error]) { - return NULL; + return -1; } - 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); + CFIndex index = CFReadStreamRead(readStream, buf, bufferLength); if (index == -1) { if (error != NULL) { CFErrorRef err = CFReadStreamCopyError(readStream); @@ -118,18 +72,12 @@ - (unsigned char *)readMaximum:(NSUInteger)maximum length:(NSUInteger *)length e CFRelease(err); } } - return NULL; - } - if (index == 0) { - SETNSERROR(@"StreamErrorDomain", ERROR_EOF, @"EOF"); - return NULL; + return -1; } - *length = (NSUInteger)index; - bytesReceived += (uint64_t)index; - return buf; + return (NSInteger)index; } -- (uint64_t)bytesReceived { - return bytesReceived; +- (NSData *)slurp:(NSError **)error { + return [InputStreams slurp:self error:error]; } @end @implementation CFStreamInputStream (internal) diff --git a/io/CFStreamPair.m b/io/CFStreamPair.m deleted file mode 100644 index 5475d76..0000000 --- a/io/CFStreamPair.m +++ /dev/null @@ -1,235 +0,0 @@ -/* - 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 -#include -#include -#import -#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:@"", 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 diff --git a/io/ChunkedInputStream.h b/io/ChunkedInputStream.h index f075470..80c38a6 100644 --- a/io/ChunkedInputStream.h +++ b/io/ChunkedInputStream.h @@ -32,12 +32,12 @@ #import #import "InputStream.h" -@class FDInputStream; +@class BufferedInputStream; @interface ChunkedInputStream : NSObject { - FDInputStream *underlyingStream; + BufferedInputStream *underlyingStream; NSUInteger chunkLength; NSUInteger received; } -- (id)initWithUnderlyingStream:(FDInputStream *)is; +- (id)initWithUnderlyingStream:(BufferedInputStream *)is; @end diff --git a/io/ChunkedInputStream.m b/io/ChunkedInputStream.m index 4514e8b..1217f57 100644 --- a/io/ChunkedInputStream.m +++ b/io/ChunkedInputStream.m @@ -34,12 +34,12 @@ #import "SetNSError.h" #import "InputStreams.h" #import "NSErrorCodes.h" -#import "FDInputStream.h" +#import "BufferedInputStream.h" #define MAX_CHUNK_LENGTH_LINE_LENGTH (1024) @implementation ChunkedInputStream -- (id)initWithUnderlyingStream:(FDInputStream *)is { +- (id)initWithUnderlyingStream:(BufferedInputStream *)is { if (self = [super init]) { underlyingStream = [is retain]; } @@ -49,40 +49,43 @@ - (void)dealloc { [underlyingStream release]; [super dealloc]; } -- (unsigned char *)read:(NSUInteger *)length error:(NSError **)error { + +#pragma mark InputStream protocol +- (NSInteger)read:(unsigned char *)buf bufferLength:(NSUInteger)bufferLength 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; + return -1; } NSScanner *scanner = [NSScanner scannerWithString:line]; if (![scanner scanHexInt:&chunkLength]) { SETNSERROR(@"StreamErrorDomain", -1, @"invalid chunk length: %@", line); - return NULL; + return -1; } 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; - } + return 0; + } + NSUInteger remaining = chunkLength - received; + NSUInteger toRead = remaining > bufferLength ? bufferLength : remaining; + NSInteger ret = [underlyingStream read:buf bufferLength:toRead error:error]; + if (ret < 0) { + return -1; } + received += ret; if (received >= chunkLength) { NSString *line = [InputStreams readLineWithCRLF:underlyingStream maxLength:MAX_CHUNK_LENGTH_LINE_LENGTH error:error]; if (line == nil) { - return NULL; + return -1; } if (![line isEqualToString:@"\r\n"]) { SETNSERROR(@"StreamErrorDomain", -1, @"missing CRLF at end of chunk!"); - return NULL; + return -1; } } - return buf; + return ret; } - (NSData *)slurp:(NSError **)error { return [InputStreams slurp:self error:error]; diff --git a/io/CryptInputStream.h b/io/CryptInputStream.h index 10ba4b1..62b7b24 100644 --- a/io/CryptInputStream.h +++ b/io/CryptInputStream.h @@ -43,8 +43,12 @@ typedef int (*CryptFinalFunc)(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl CryptUpdateFunc cryptUpdate; CryptFinalFunc cryptFinal; id is; + unsigned char *inBuf; + NSUInteger inBufSize; unsigned char *outBuf; - NSUInteger outBufLen; + NSInteger outBufLen; + NSUInteger outBufSize; + NSUInteger outBufPos; const EVP_CIPHER *cipher; EVP_CIPHER_CTX cipherContext; unsigned char evp_key[EVP_MAX_KEY_LENGTH]; diff --git a/io/CryptInputStream.m b/io/CryptInputStream.m index 3e42ee1..6fb39b7 100644 --- a/io/CryptInputStream.m +++ b/io/CryptInputStream.m @@ -36,7 +36,11 @@ #import "InputStreams.h" #import "NSErrorCodes.h" -#define MY_BUF_SIZE (4096) +#define MY_BUF_SIZE (8192) + +@interface CryptInputStream (internal) +- (BOOL)fillOutBuf:(NSError **)error; +@end @implementation CryptInputStream + (NSString *)errorDomain { @@ -72,8 +76,10 @@ - (id)initWithCryptInitFunc:(void *)theCryptInit cryptUpdateFunc:(void *)theCryp } EVP_CIPHER_CTX_set_key_length(&cipherContext, EVP_MAX_KEY_LENGTH); blockSize = (unsigned long long)EVP_CIPHER_CTX_block_size(&cipherContext); - outBufLen = MY_BUF_SIZE + blockSize - 1; - outBuf = (unsigned char *)malloc(outBufLen); + inBufSize = MY_BUF_SIZE; + inBuf = (unsigned char *)malloc(inBufSize); + outBufSize = MY_BUF_SIZE + blockSize - 1; + outBuf = (unsigned char *)malloc(outBufSize); initialized = YES; ret = YES; } while(0); @@ -88,6 +94,9 @@ - (void)dealloc { if (initialized) { EVP_CIPHER_CTX_cleanup(&cipherContext); } + if (inBuf != NULL) { + free(inBuf); + } if (outBuf != NULL) { free(outBuf); } @@ -100,65 +109,53 @@ - (BOOL)cryptUpdate:(int *)outLen inBuf:(unsigned char *)inBuf inLen:(NSUInteger - (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([CryptInputStream errorDomain], ERROR_EOF, @"EOF"); - return NULL; - } - NSUInteger inLen = 0; - NSError *myError = nil; - unsigned char *inBuf = [is read:&inLen error:&myError]; - int outLen = 0; - if (inBuf == NULL) { - if ([myError code] == ERROR_EOF) { - finalized = YES; - if (!(cryptFinal)(&cipherContext, outBuf, &outLen)) { - SETNSERROR(@"OpenSSLErrorDomain", -1, @"crypt final: %@", [OpenSSL errorMessage]); - return NULL; - } - if (outLen == 0) { - // Don't return 0 bytes and make the caller have to call again. - SETNSERROR([CryptInputStream errorDomain], ERROR_EOF, @"EOF"); - return NULL; - } - *length = (NSUInteger)outLen; - return outBuf; - } else { - if (error != NULL) { - *error = myError; - } - return NULL; + +#pragma mark InputStream +- (NSInteger)read:(unsigned char *)buf bufferLength:(NSUInteger)bufferLength error:(NSError **)error { + while (outBufPos >= outBufLen && !finalized) { + if (![self fillOutBuf:error]) { + return -1; } } - NSUInteger needed = inLen + blockSize - 1; - if (outBufLen < needed) { - outBuf = (unsigned char *)realloc(outBuf, needed); - if (outBuf == NULL) { - SETNSERROR(@"MallocErrorDomain", -1, @"malloc failed"); - return NULL; - } - outBufLen = needed; + NSUInteger available = outBufLen - outBufPos; + NSUInteger ret = 0; + if (available > 0) { + NSUInteger toCopy = available > bufferLength ? bufferLength : available; + memcpy(buf, outBuf + outBufPos, toCopy); + outBufPos += toCopy; + ret = toCopy; } - if (!(*cryptUpdate)(&cipherContext, outBuf, &outLen, inBuf, inLen)) { - SETNSERROR(@"OpenSSLErrorDomain", -1, @"crypt update: %@", [OpenSSL errorMessage]); - return NULL; + return ret; +} +- (NSData *)slurp:(NSError **)error { + return [InputStreams slurp:self error:error]; +} +@end + +@implementation CryptInputStream (internal) +- (BOOL)fillOutBuf:(NSError **)error { + if (finalized) { + SETNSERROR([CryptInputStream errorDomain], ERROR_EOF, @"EOF"); + return NO; + } + outBufLen = 0; + outBufPos = 0; + NSInteger recvd = [is read:inBuf bufferLength:inBufSize error:error]; + if (recvd == -1) { + return NO; } - if (outLen == 0) { + if (recvd == 0) { finalized = YES; - if (!(cryptFinal)(&cipherContext, outBuf, &outLen)) { + if (!(cryptFinal)(&cipherContext, outBuf, &outBufLen)) { SETNSERROR(@"OpenSSLErrorDomain", -1, @"crypt final: %@", [OpenSSL errorMessage]); - return NULL; + return NO; } - if (outLen == 0) { - // Don't return 0 bytes and make the caller have to call again. - SETNSERROR([CryptInputStream errorDomain], ERROR_EOF, @"EOF"); - return NULL; + } else { + if (!(*cryptUpdate)(&cipherContext, outBuf, &outBufLen, inBuf, recvd)) { + SETNSERROR(@"OpenSSLErrorDomain", -1, @"crypt update: %@", [OpenSSL errorMessage]); + return NO; } } - *length = (NSUInteger)outLen; - return outBuf; -} -- (NSData *)slurp:(NSError **)error { - return [InputStreams slurp:self error:error]; + return YES; } @end diff --git a/io/DataIO.h b/io/DataIO.h index f57ddee..ebbdfb3 100644 --- a/io/DataIO.h +++ b/io/DataIO.h @@ -31,12 +31,14 @@ */ #import -#import "BufferedInputStream.h" + +#import +@class BufferedInputStream; @interface DataIO : NSObject { } + (void)write:(NSData *)data to:(NSMutableData *)data; -+ (BOOL)read:(NSData **)value from:(id )is error:(NSError **)error; ++ (BOOL)read:(NSData **)value from:(BufferedInputStream *)is error:(NSError **)error; @end diff --git a/io/DataIO.m b/io/DataIO.m index a763c64..f7a2b63 100644 --- a/io/DataIO.m +++ b/io/DataIO.m @@ -30,27 +30,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#import + #import "DataIO.h" #import "IntegerIO.h" #import "Streams.h" +#import "BufferedInputStream.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 )is error:(NSError **)error { - *value = NO; ++ (BOOL)read:(NSData **)value from:(BufferedInputStream *)is error:(NSError **)error { + *value = nil; 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; + *value = [is readExactly:length error:error]; + return *value != nil; } @end diff --git a/io/DataInputStream.h b/io/DataInputStream.h index 74cec4e..8b2c476 100644 --- a/io/DataInputStream.h +++ b/io/DataInputStream.h @@ -31,9 +31,11 @@ */ #import -#import "BufferedInputStream.h" -@interface DataInputStream : NSObject { +#import +#import "InputStream.h" + +@interface DataInputStream : NSObject { NSData *data; NSUInteger pos; } diff --git a/io/DataInputStream.m b/io/DataInputStream.m index 0b56377..2f3f420 100644 --- a/io/DataInputStream.m +++ b/io/DataInputStream.m @@ -30,6 +30,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#import + #import "DataInputStream.h" #import "SetNSError.h" #import "NSErrorCodes.h" @@ -53,58 +55,27 @@ - (void)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; - } +- (NSInteger)read:(unsigned char *)buf bufferLength:(NSUInteger)bufferLength error:(NSError **)error { + NSInteger ret = 0; 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; + if (remaining > 0) { + ret = remaining > bufferLength ? bufferLength : remaining; + unsigned char *bytes = (unsigned char *)[data bytes]; + memcpy(buf, bytes + pos, ret); + pos += ret; } - unsigned char *buf = (unsigned char *)[data bytes] + pos; - pos += len; - return buf; + return ret; } - (NSData *)slurp:(NSError **)error { + NSData *ret = nil; 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]; + ret = [[data retain] autorelease]; + } else if (pos >= [data length]) { + ret = [NSData data]; + } else { + ret = [data subdataWithRange:NSMakeRange(pos, [data length] - pos)]; + pos = [data length]; } - 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; } -- (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 diff --git a/io/DataInputStreamFactory.m b/io/DataInputStreamFactory.m index cb57f56..f40e8ad 100644 --- a/io/DataInputStreamFactory.m +++ b/io/DataInputStreamFactory.m @@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#import #import "DataInputStreamFactory.h" #import "DataInputStream.h" diff --git a/io/DateIO.h b/io/DateIO.h index bed2cbf..b79a540 100644 --- a/io/DateIO.h +++ b/io/DateIO.h @@ -31,11 +31,11 @@ */ #import -#import "BufferedInputStream.h" +@class BufferedInputStream; @interface DateIO : NSObject { } + (void)write:(NSDate *)date to:(NSMutableData *)data; -+ (BOOL)read:(NSDate **)date from:(id )is error:(NSError **)error; ++ (BOOL)read:(NSDate **)date from:(BufferedInputStream *)is error:(NSError **)error; @end diff --git a/io/DateIO.m b/io/DateIO.m index 6849037..861a45b 100644 --- a/io/DateIO.m +++ b/io/DateIO.m @@ -30,9 +30,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#import + #import "BooleanIO.h" #import "IntegerIO.h" #import "DateIO.h" +#import "BufferedInputStream.h" @implementation DateIO + (void)write:(NSDate *)date to:(NSMutableData *)data { @@ -43,7 +46,7 @@ + (void)write:(NSDate *)date to:(NSMutableData *)data { [IntegerIO writeInt64:millisecondsSince1970 to:data]; } } -+ (BOOL)read:(NSDate **)date from:(id )is error:(NSError **)error { ++ (BOOL)read:(NSDate **)date from:(BufferedInputStream *)is error:(NSError **)error { *date = nil; BOOL notNil; if (![BooleanIO read:¬Nil from:is error:error]) { diff --git a/io/DoubleIO.h b/io/DoubleIO.h index e5732d6..f4845af 100644 --- a/io/DoubleIO.h +++ b/io/DoubleIO.h @@ -31,11 +31,13 @@ */ #import -#import "BufferedInputStream.h" +@class BufferedInputStream; +@protocol OutputStream; @interface DoubleIO : NSObject { } + (void)write:(double)d to:(NSMutableData *)data; -+ (BOOL)read:(double *)value from:(id )is error:(NSError **)error; ++ (BOOL)write:(double)d to:(id )os error:(NSError **)error; ++ (BOOL)read:(double *)value from:(BufferedInputStream *)is error:(NSError **)error; @end diff --git a/io/DoubleIO.m b/io/DoubleIO.m index 836f015..b8cdd89 100644 --- a/io/DoubleIO.m +++ b/io/DoubleIO.m @@ -33,6 +33,7 @@ #import "DoubleIO.h" #import "StringIO.h" #import "SetNSError.h" +#import "BufferedInputStream.h" //FIXME: Delete this class? It's not used anywhere. @@ -43,13 +44,17 @@ + (void)write:(double)d to:(NSMutableData *)data { NSString *str = [NSString stringWithFormat:@"%f", d]; [StringIO write:str to:data]; } -+ (BOOL)read:(double *)value from:(id )is error:(NSError **)error { ++ (BOOL)write:(double)d to:(id )os error:(NSError **)error { + NSString *str = [NSString stringWithFormat:@"%f", d]; + return [StringIO write:str to:os error:error]; +} ++ (BOOL)read:(double *)value from:(BufferedInputStream *)is error:(NSError **)error { if (error) { *error = 0; } *value = 0; NSString *str; - if (![StringIO read:&str from:is error:error]) { + if (![StringIO newString:&str from:is error:error]) { return NO; } if (!str) { @@ -57,6 +62,7 @@ + (BOOL)read:(double *)value from:(id )is error:(NSError ** return NO; } BOOL ret = [[NSScanner scannerWithString:str] scanDouble:value]; + [str release]; if (!ret) { SETNSERROR(@"DoubleIOErrorDomain", -1, @"%@ does not contain a double", str); } diff --git a/io/EncryptedInputStream.h b/io/EncryptedInputStream.h index fb80002..936138e 100644 --- a/io/EncryptedInputStream.h +++ b/io/EncryptedInputStream.h @@ -36,4 +36,4 @@ @interface EncryptedInputStream : CryptInputStream { } - (id)initWithInputStream:(id )theIS cipherName:(NSString *)theCipherName key:(NSString *)theKey error:(NSError **)error; -@end \ No newline at end of file +@end diff --git a/io/FDInputStream.h b/io/FDInputStream.h index eef8848..5bc1ac1 100644 --- a/io/FDInputStream.h +++ b/io/FDInputStream.h @@ -31,9 +31,9 @@ */ #import -#import "BufferedInputStream.h" +#import "InputStream.h" -@interface FDInputStream : NSObject { +@interface FDInputStream : NSObject { int fd; uint64_t bytesReceived; } diff --git a/io/FDInputStream.m b/io/FDInputStream.m index b07c9c2..e53eb57 100644 --- a/io/FDInputStream.m +++ b/io/FDInputStream.m @@ -35,7 +35,7 @@ #import "InputStreams.h" #import "NSErrorCodes.h" -#define MY_BUF_SIZE (4096) +#define MY_BUF_SIZE (8192) #define DEFAULT_READ_TIMEOUT_SECONDS (60) static time_t readTimeoutSeconds = DEFAULT_READ_TIMEOUT_SECONDS; @@ -54,14 +54,10 @@ - (id)initWithFD:(int)theFD { - (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; + +#pragma mark InputStream +- (NSInteger)read:(unsigned char *)buf bufferLength:(NSUInteger)bufferLength error:(NSError **)error { + NSInteger ret = 0; fd_set readSet; fd_set exceptSet; FD_ZERO(&readSet); @@ -80,58 +76,26 @@ - (unsigned char *)readMaximum:(NSUInteger)maximum length:(NSUInteger *)length e goto select_again; } else if (ret == -1) { SETNSERROR(@"UnixErrorDomain", errno, @"select: %s", strerror(errno)); - return NULL; + return -1; } else if (ret == 0) { - SETNSERROR(@"InputStreamErrorDomain", -1, @"read timeout"); - return NULL; + SETNSERROR(@"InputStreamErrorDomain", ERROR_TIMEOUT, @"read timeout"); + return -1; } read_again: - ret = read(fd, buf, toRead); + ret = read(fd, buf, bufferLength); if ((ret == -1) && (errno == EINTR)) { goto read_again; } else if (ret == -1) { SETNSERROR(@"UnixErrorDomain", errno, @"read: %s", strerror(errno)); - return NULL; + return -1; } - if (ret == 0) { - SETNSERROR(@"StreamErrorDomain", ERROR_EOF, @"EOF on fd %d", fd); - return NULL; - } - *length = (NSUInteger)ret; - bytesReceived += (uint64_t)ret; - return buf; + return ret; } - (NSData *)slurp:(NSError **)error { return [InputStreams slurp:self error:error]; } -#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:@"", fd]; diff --git a/io/FileInputStream.h b/io/FileInputStream.h index 899a058..128336e 100644 --- a/io/FileInputStream.h +++ b/io/FileInputStream.h @@ -31,15 +31,13 @@ */ #import -#import "BufferedInputStream.h" +#import "InputStream.h" -@interface FileInputStream : NSObject { +@interface FileInputStream : NSObject { int fd; NSString *path; unsigned long long fileLength; unsigned long long offset; - unsigned char *buf; - uint64_t bytesReceived; } - (id)initWithPath:(NSString *)thePath offset:(unsigned long long)theOffset length:(unsigned long long)theLength; @end diff --git a/io/FileInputStream.m b/io/FileInputStream.m index bfc94ba..ad734d0 100644 --- a/io/FileInputStream.m +++ b/io/FileInputStream.m @@ -35,7 +35,7 @@ #import "InputStreams.h" #import "NSErrorCodes.h" -#define MY_BUF_SIZE (4096) +#define MY_BUF_SIZE (8192) @interface FileInputStream (internal) - (void)close; @@ -47,7 +47,6 @@ - (id)initWithPath:(NSString *)thePath offset:(unsigned long long)theOffset leng fd = -1; path = [thePath retain]; fileLength = theOffset + theLength; - buf = (unsigned char *)malloc(MY_BUF_SIZE); offset = theOffset; } return self; @@ -55,85 +54,48 @@ - (id)initWithPath:(NSString *)thePath offset:(unsigned long long)theOffset leng - (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 { + +#pragma mark InputStream +- (NSInteger)read:(unsigned char *)buf bufferLength:(NSUInteger)bufferLength 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; + return -1; } 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; + return -1; } } } - 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; - } + unsigned long long fileRemaining = fileLength - offset; + unsigned long long toRead = fileRemaining > bufferLength ? bufferLength : fileRemaining; if (toRead == 0) { - SETNSERROR(@"StreamsErrorDomain", ERROR_EOF, @"reached EOF"); - return NULL; + return 0; } + + NSInteger ret = 0; read_again: ret = read(fd, buf, (size_t)toRead); if ((ret == -1) && (errno == EINTR)) { goto read_again; } - if (ret == -1) { + if (ret < 0) { SETNSERROR(@"UnixErrorDomain", errno, @"read: %s", strerror(errno)); - return NULL; + } else { + offset += ret; } - 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; + return ret; } - (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; -} - #pragma mark NSObject protocol - (NSString *)description { return [NSString stringWithFormat:@"", fd, path]; diff --git a/io/FileOutputStream.m b/io/FileOutputStream.m index b8284df..4de3db8 100644 --- a/io/FileOutputStream.m +++ b/io/FileOutputStream.m @@ -73,7 +73,6 @@ - (NSString *)path { return path; } -#pragma mark OutputStream - (BOOL)write:(const unsigned char *)buf length:(NSUInteger)len error:(NSError **)error { if (fd == -1 && ![self open:error]) { return NO; @@ -98,7 +97,6 @@ - (unsigned long long)bytesWritten { return bytesWritten; } @end - @implementation FileOutputStream (internal) - (BOOL)open:(NSError **)error { int oflag = O_WRONLY|O_CREAT; diff --git a/io/FixedLengthInputStream.h b/io/FixedLengthInputStream.h index 81672c1..3b664b1 100644 --- a/io/FixedLengthInputStream.h +++ b/io/FixedLengthInputStream.h @@ -32,12 +32,12 @@ #import #import "InputStream.h" -@protocol BufferedInputStream; +@class BufferedInputStream; @interface FixedLengthInputStream : NSObject { - id underlyingStream; + BufferedInputStream *underlyingStream; unsigned long long fixedLength; unsigned long long totalReceived; } -- (id)initWithUnderlyingStream:(id )is length:(unsigned long long)theLength; +- (id)initWithUnderlyingStream:(BufferedInputStream *)is length:(unsigned long long)theLength; @end diff --git a/io/FixedLengthInputStream.m b/io/FixedLengthInputStream.m index a73e88f..51466df 100644 --- a/io/FixedLengthInputStream.m +++ b/io/FixedLengthInputStream.m @@ -35,9 +35,10 @@ #import "SetNSError.h" #import "NSErrorCodes.h" #import "FDInputStream.h" +#import "BufferedInputStream.h" @implementation FixedLengthInputStream -- (id)initWithUnderlyingStream:(id )is length:(unsigned long long)theLength { +- (id)initWithUnderlyingStream:(BufferedInputStream *)is length:(unsigned long long)theLength { if (self = [super init]) { underlyingStream = [is retain]; fixedLength = theLength; @@ -48,27 +49,23 @@ - (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; - } - NSError *myError = nil; - unsigned char *buf = [underlyingStream readMaximum:maximum length:length error:&myError]; - if (buf == NULL) { - if ([myError code] == ERROR_EOF) { - HSLogError(@"unexpected EOF when only %qu of %qu bytes received", totalReceived, fixedLength); - SETNSERROR(@"StreamsErrorDomain", -1, @"unexpected EOF when only %qu of %qu bytes received", totalReceived, fixedLength); - return NULL; + +#pragma mark InputStream +- (NSInteger)read:(unsigned char *)buf bufferLength:(NSUInteger)bufferLength error:(NSError **)error { + NSInteger ret = 0; + unsigned long long remaining = fixedLength - totalReceived; + if (remaining > 0) { + NSUInteger toRead = remaining; + if (toRead > bufferLength) { + toRead = bufferLength; } - if (error != NULL) { - *error = myError; + ret = [underlyingStream read:buf bufferLength:toRead error:error]; + if (ret < 0) { + return -1; } - return NULL; } - totalReceived += (unsigned long long)(*length); - return buf; + totalReceived += ret; + return ret; } - (NSData *)slurp:(NSError **)error { return [InputStreams slurp:self error:error]; diff --git a/io/InputStream.h b/io/InputStream.h index 5520e49..38fd50b 100644 --- a/io/InputStream.h +++ b/io/InputStream.h @@ -34,6 +34,6 @@ @protocol InputStream -- (unsigned char *)read:(NSUInteger *)length error:(NSError **)error; +- (NSInteger)read:(unsigned char *)buf bufferLength:(NSUInteger)bufferLength error:(NSError **)error; - (NSData *)slurp:(NSError **)error; @end diff --git a/io/InputStreamFactory.h b/io/InputStreamFactory.h index 73c489c..aec4aa2 100644 --- a/io/InputStreamFactory.h +++ b/io/InputStreamFactory.h @@ -30,6 +30,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#import + #import #import "InputStream.h" diff --git a/io/InputStreams.h b/io/InputStreams.h index e8db3c1..970db39 100644 --- a/io/InputStreams.h +++ b/io/InputStreams.h @@ -32,12 +32,12 @@ #import #import "InputStream.h" -@class FDInputStream; +@class BufferedInputStream; @interface InputStreams : NSObject { } + (NSData *)slurp:(id )is error:(NSError **)error; -+ (NSString *)readLineWithCRLF:(FDInputStream *)is maxLength:(NSUInteger)maxLength error:(NSError **)error; -+ (NSString *)readLine:(FDInputStream *)is error:(NSError **)error; ++ (NSString *)readLineWithCRLF:(BufferedInputStream *)bis maxLength:(NSUInteger)maxLength error:(NSError **)error; ++ (NSString *)readLine:(BufferedInputStream *)bis error:(NSError **)error; @end diff --git a/io/InputStreams.m b/io/InputStreams.m index b996049..a081b5a 100644 --- a/io/InputStreams.m +++ b/io/InputStreams.m @@ -33,83 +33,64 @@ #import "InputStreams.h" #import "SetNSError.h" #import "NSErrorCodes.h" -#import "FDInputStream.h" +#import "BufferedInputStream.h" +#define MY_BUF_SIZE (8192) @implementation InputStreams + (NSData *)slurp:(id )is error:(NSError **)error { NSMutableData *data = [[[NSMutableData alloc] init] autorelease]; + unsigned char *buf = (unsigned char *)malloc(MY_BUF_SIZE); + NSInteger ret = 0; for (;;) { - NSError *myError = nil; - NSUInteger received; - unsigned char *buf = [is read:&received error:&myError]; - if (buf == NULL) { - if ([myError code] != ERROR_EOF) { - data = nil; - if (error != NULL) { - *error = myError; - } - } + ret = [is read:buf bufferLength:MY_BUF_SIZE error:error]; + if (ret <= 0) { break; } - [data appendBytes:buf length:received]; + [data appendBytes:buf length:ret]; + } + free(buf); + if (ret == -1) { + return nil; } return data; } -+ (NSString *)readLineWithCRLF:(FDInputStream *)is maxLength:(NSUInteger)maxLength error:(NSError **)error { - NSMutableData *data = [[[NSMutableData alloc] init] autorelease]; ++ (NSString *)readLineWithCRLF:(BufferedInputStream *)bis maxLength:(NSUInteger)maxLength error:(NSError **)error { + unsigned char *buf = (unsigned char *)malloc(maxLength); + NSUInteger received = 0; for (;;) { - if ([data length] > maxLength) { - SETNSERROR(@"InputStreamErrorDomain", -1, @"exceeded maxLength %u", maxLength); + if (received > maxLength) { + SETNSERROR(@"InputStreamErrorDomain", -1, @"exceeded maxLength %u before finding CRLF", maxLength); return nil; } - NSUInteger received = 0; - unsigned char *buf = [is readMaximum:1 length:&received error:error]; - if (buf == NULL) { + if (![bis readExactly:1 into:(buf + received) error:error]) { return nil; } - NSAssert(received == 1, @"expected 1 byte from readMaximum:"); - [data appendBytes:buf length:1]; - char c = buf[0]; - if (c == '\r') { - buf = [is readMaximum:1 length:&received error:error]; - if (buf == NULL) { - return nil; - } - NSAssert(received == 1, @"expected 1 byte from readMaximum:"); - [data appendBytes:buf length:1]; - c = buf[0]; - if (c == '\n') { - break; - } + received++; + if (received >= 2 && buf[received - 1] == '\n' && buf[received - 2] == '\r') { + break; } } - NSString *line = [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSUTF8StringEncoding] autorelease]; - HSLogTrace(@"got line <%@>", [line substringToIndex:[line length] - 2]); - return line; + NSString *ret = [[[NSString alloc] initWithBytes:buf length:received encoding:NSUTF8StringEncoding] autorelease]; + HSLogTrace(@"got line <%@>", ret); + return ret; } -+ (NSString *)readLine:(FDInputStream *)is error:(NSError **)error { - NSMutableData *data = [[[NSMutableData alloc] init] autorelease]; ++ (NSString *)readLine:(BufferedInputStream *)bis error:(NSError **)error { + NSMutableData *data = [NSMutableData data]; + unsigned char buf[1]; + NSUInteger received = 0; for (;;) { - NSUInteger received = 0; - NSError *myError = nil; - unsigned char *buf = [is readMaximum:1 length:&received error:&myError]; - if (buf == NULL) { - if ([myError code] != ERROR_EOF) { - if (error != NULL) { - *error = myError; - } - return nil; - } - //EOF. - break; + if (![bis readExactly:1 into:buf error:error]) { + return nil; } - NSAssert(received == 1, @"expected 1 byte from readMaximum:"); - if (buf[0] == '\n') { + if (*buf == '\n') { break; } [data appendBytes:buf length:1]; + received++; } - return [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSUTF8StringEncoding] autorelease]; + NSString *ret = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; + HSLogTrace(@"got line <%@> followed by \\n", ret); + return ret; } @end diff --git a/io/IntegerIO.h b/io/IntegerIO.h index 326793e..83a3fed 100644 --- a/io/IntegerIO.h +++ b/io/IntegerIO.h @@ -32,7 +32,7 @@ #import #import "OutputStream.h" -#import "BufferedInputStream.h" +@class BufferedInputStream; @interface IntegerIO : NSObject { @@ -49,10 +49,10 @@ + (BOOL)writeInt64:(int64_t)i to:(id )os error:(NSError **)error; + (BOOL)writeUInt64:(uint64_t)i to:(id )os error:(NSError **)error; -+ (BOOL)readInt32:(int32_t *)value from:(id )is error:(NSError **)error; -+ (BOOL)readUInt32:(uint32_t *)value from:(id )is error:(NSError **)error; ++ (BOOL)readInt32:(int32_t *)value from:(BufferedInputStream *)is error:(NSError **)error; ++ (BOOL)readUInt32:(uint32_t *)value from:(BufferedInputStream *)is error:(NSError **)error; -+ (BOOL)readInt64:(int64_t *)value from:(id )is error:(NSError **)error; -+ (BOOL)readUInt64:(uint64_t *)value from:(id )is error:(NSError **)error; ++ (BOOL)readInt64:(int64_t *)value from:(BufferedInputStream *)is error:(NSError **)error; ++ (BOOL)readUInt64:(uint64_t *)value from:(BufferedInputStream *)is error:(NSError **)error; @end diff --git a/io/IntegerIO.m b/io/IntegerIO.m index 3c28006..3218cc3 100644 --- a/io/IntegerIO.m +++ b/io/IntegerIO.m @@ -34,6 +34,7 @@ #import "InputStream.h" #import "OutputStream.h" #import "Streams.h" +#import "BufferedInputStream.h" @implementation IntegerIO // @@ -67,30 +68,28 @@ + (BOOL)writeUInt64:(uint64_t)value to:(id )os error:(NSError **)e uint64_t nboValue = OSSwapHostToBigInt64(value); return [os write:(const unsigned char *)&nboValue length:8 error:error]; } -+ (BOOL)readInt32:(int32_t *)value from:(id )is error:(NSError **)error { ++ (BOOL)readInt32:(int32_t *)value from:(BufferedInputStream *)is error:(NSError **)error { return [IntegerIO readUInt32:(uint32_t *)value from:is error:error]; } -+ (BOOL)readUInt32:(uint32_t *)value from:(id )is error:(NSError **)error { ++ (BOOL)readUInt32:(uint32_t *)value from:(BufferedInputStream *)is error:(NSError **)error { *value = 0; - unsigned char *buf = [is readExactly:4 error:error]; - if (!buf) { + uint32_t nboValue = 0; + if (![is readExactly:sizeof(uint32_t) into:(unsigned char *)&nboValue error:error]) { return NO; } - uint32_t *nboValue = (uint32_t *)buf; - *value = OSSwapBigToHostInt32(*nboValue); + *value = OSSwapBigToHostInt32(nboValue); return YES; } -+ (BOOL)readInt64:(int64_t *)value from:(id )is error:(NSError **)error { ++ (BOOL)readInt64:(int64_t *)value from:(BufferedInputStream *)is error:(NSError **)error { return [IntegerIO readUInt64:(uint64_t *)value from:is error:error]; } -+ (BOOL)readUInt64:(uint64_t *)value from:(id )is error:(NSError **)error { ++ (BOOL)readUInt64:(uint64_t *)value from:(BufferedInputStream *)is error:(NSError **)error { *value = 0; - unsigned char *buf = [is readExactly:8 error:error]; - if (!buf) { + uint64_t nboValue = 0; + if (![is readExactly:sizeof(uint64_t) into:(unsigned char *)&nboValue error:error]) { return NO; } - uint64_t *nboValue = (uint64_t *)buf; - *value = OSSwapBigToHostInt64(*nboValue); + *value = OSSwapBigToHostInt64(nboValue); return YES; } @end diff --git a/io/MonitoredInputStream.m b/io/MonitoredInputStream.m index cdf13f9..5baee8e 100644 --- a/io/MonitoredInputStream.m +++ b/io/MonitoredInputStream.m @@ -48,15 +48,15 @@ - (void)dealloc { [delegate release]; [super dealloc]; } -- (unsigned char *)read:(NSUInteger *)length error:(NSError **)error { - unsigned char *buf = [underlyingStream read:length error:error]; - if (buf != NULL) { - if (![delegate monitoredInputStream:self receivedBytes:*length error:error]) { - return NULL; - } - bytesReceived += (unsigned long long)*length; + +#pragma mark InputStream +- (NSInteger)read:(unsigned char *)buf bufferLength:(NSUInteger)bufferLength error:(NSError **)error { + NSInteger ret = [underlyingStream read:buf bufferLength:bufferLength error:error]; + if (ret < 0) { + return ret; } - return buf; + bytesReceived += (unsigned long long)ret; + return ret; } - (NSData *)slurp:(NSError **)error { NSData *data = [underlyingStream slurp:error]; diff --git a/io/NSFileManager_extra.h b/io/NSFileManager_extra.h index c83fc5f..814e295 100644 --- a/io/NSFileManager_extra.h +++ b/io/NSFileManager_extra.h @@ -36,4 +36,5 @@ @interface NSFileManager (extra) - (BOOL)ensureParentPathExistsForPath:(NSString *)path error:(NSError **)error; - (BOOL)touchFileAtPath:(NSString *)path error:(NSError **)error; +- (BOOL)createUniqueTempDirectoryWithTemplate:(NSString *)pathTemplate createdDirectory:(NSString **)createdPath error:(NSError **)error; @end diff --git a/io/NSFileManager_extra.m b/io/NSFileManager_extra.m index 5f54abf..5e829c7 100644 --- a/io/NSFileManager_extra.m +++ b/io/NSFileManager_extra.m @@ -75,4 +75,16 @@ - (BOOL)touchFileAtPath:(NSString *)path error:(NSError **)error { } return YES; } +- (BOOL)createUniqueTempDirectoryWithTemplate:(NSString *)pathTemplate createdDirectory:(NSString **)createdPath error:(NSError **)error { + *createdPath = nil; + char *cTemplate = strdup([pathTemplate fileSystemRepresentation]); + char *tempDir = mkdtemp(cTemplate); + if (tempDir != NULL) { + *createdPath = [NSString stringWithUTF8String:tempDir]; + } else { + SETNSERROR(@"UnixErrorDomain", errno, @"mkdtemp(%@): %s", pathTemplate, strerror(errno)); + } + free(cTemplate); + return tempDir != NULL; +} @end diff --git a/io/StreamPair.h b/io/StreamPair.h index a3c9bad..e420887 100644 --- a/io/StreamPair.h +++ b/io/StreamPair.h @@ -31,10 +31,10 @@ */ #import -#import "BufferedInputStream.h" +#import "InputStream.h" #import "OutputStream.h" -@protocol StreamPair +@protocol StreamPair - (void)setCloseRequested; - (BOOL)isUsable; diff --git a/io/StreamPairFactory.m b/io/StreamPairFactory.m deleted file mode 100644 index 787a275..0000000 --- a/io/StreamPairFactory.m +++ /dev/null @@ -1,168 +0,0 @@ -/* - 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 "StreamPairFactory.h" -#import "CFStreamPair.h" - -static StreamPairFactory *theFactory = nil; - -#define DEFAULT_MAX_STREAM_PAIR_LIFETIME_SECONDS (60) -#define CLEANUP_THREAD_SLEEP_SECONDS (5) - -@interface StreamPairMap : NSObject { - NSMutableDictionary *streamPairs; -} -- (id )newStreamPairToHost:(NSString *)host useSSL:(BOOL)useSSL maxLifeTimeSeconds:(NSTimeInterval)theMaxLifetime error:(NSError **)error; -- (void)dropUnusableStreamPairs; -@end - -@implementation StreamPairMap -- (id)init { - if (self = [super init]) { - streamPairs = [[NSMutableDictionary alloc] init]; - } - return self; -} -- (void)dealloc { - [streamPairs release]; - [super dealloc]; -} -- (id )newStreamPairToHost:(NSString *)host useSSL:(BOOL)useSSL maxLifeTimeSeconds:(NSTimeInterval)theMaxLifetime error:(NSError **)error { - NSString *key = [NSString stringWithFormat:@"%@:%@", [host lowercaseString], (useSSL ? @"SSL" : @"noSSL")]; - id streamPair = [streamPairs objectForKey:key]; - if (streamPair != nil) { - if (![streamPair isUsable]) { - [streamPairs removeObjectForKey:key]; - streamPair = nil; - } else { - [streamPair retain]; - } - } - if (streamPair == nil) { - streamPair = [[CFStreamPair alloc] initWithHost:host useSSL:useSSL maxLifetime:theMaxLifetime]; - [streamPairs setObject:streamPair forKey:key]; - } - return streamPair; -} -- (void)dropUnusableStreamPairs { - NSMutableArray *keysToDrop = [NSMutableArray array]; - for (NSString *key in streamPairs) { - id streamPair = [streamPairs objectForKey:key]; - if (![streamPair isUsable]) { - [keysToDrop addObject:key]; - } - } - [streamPairs removeObjectsForKeys:keysToDrop]; -} -@end - - - -@implementation StreamPairFactory -+ (StreamPairFactory *)theFactory { - if (theFactory == nil) { - theFactory = [[super allocWithZone:NULL] init]; - } - return theFactory; -} - -/* Singleton recipe: */ -+ (id)allocWithZone:(NSZone *)zone { - return [[StreamPairFactory theFactory] retain]; -} -- (id)copyWithZone:(NSZone *)zone { - return self; -} -- (id)retain { - return self; -} -- (NSUInteger)retainCount { - return NSUIntegerMax; //denotes an object that cannot be released -} -- (void)release { - //do nothing -} -- (id)autorelease { - return self; -} - -- (id)init { - if (self = [super init]) { - lock = [[NSLock alloc] init]; - [lock setName:@"SocketFactory lock"]; - threadMap = [[NSMutableDictionary alloc] init]; - maxStreamPairLifetime = DEFAULT_MAX_STREAM_PAIR_LIFETIME_SECONDS; - [NSThread detachNewThreadSelector:@selector(dropUnusableSockets) toTarget:self withObject:nil]; - } - return self; -} -- (void)dealloc { - [lock release]; - [threadMap release]; - [super dealloc]; -} -- (void)setMaxStreamPairLifetime:(NSTimeInterval)theMaxLifetime { - maxStreamPairLifetime = theMaxLifetime; -} -- (id )newStreamPairToHost:(NSString *)theHost useSSL:(BOOL)isUseSSL error:(NSError **)error { - void *pthreadPtr = pthread_self(); -#ifdef __LP64__ - NSNumber *threadID = [NSNumber numberWithUnsignedLongLong:(uint64_t)pthreadPtr]; -#else - NSNumber *threadID = [NSNumber numberWithUnsignedLong:(uint32_t)pthreadPtr]; -#endif - [lock lock]; - StreamPairMap *map = [threadMap objectForKey:threadID]; - if (map == nil) { - map = [[StreamPairMap alloc] init]; - [threadMap setObject:map forKey:threadID]; - [map release]; - } - id streamPair = [map newStreamPairToHost:theHost useSSL:isUseSSL maxLifeTimeSeconds:maxStreamPairLifetime error:error]; - [lock unlock]; - return streamPair; -} - -#pragma mark cleanup thread -- (void)dropUnusableSockets { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - for (;;) { - [NSThread sleepForTimeInterval:CLEANUP_THREAD_SLEEP_SECONDS]; - [lock lock]; - for (StreamPairMap *map in [threadMap allValues]) { - [map dropUnusableStreamPairs]; - } - [lock unlock]; - } - [pool drain]; -} -@end diff --git a/io/Streams.h b/io/Streams.h index 8cc94e8..5e9f055 100644 --- a/io/Streams.h +++ b/io/Streams.h @@ -31,12 +31,13 @@ */ #import -@protocol InputStream; -@protocol OutputStream; +#import "InputStream.h" +#import "OutputStream.h" @interface Streams : NSObject { } + (BOOL)transferFrom:(id )is to:(id )os error:(NSError **)error; ++ (BOOL)transferFrom:(id )is to:(id )os bytesWritten:(unsigned long long *)written error:(NSError **)error; + (BOOL)transferFrom:(id )is atomicallyToFile:(NSString *)path bytesWritten:(unsigned long long *)written error:(NSError **)error; @end diff --git a/io/Streams.m b/io/Streams.m index 576ad19..8d2b80e 100644 --- a/io/Streams.m +++ b/io/Streams.m @@ -34,31 +34,30 @@ #import "SetNSError.h" #import "FDOutputStream.h" #import "NSErrorCodes.h" -#import "InputStream.h" + +#define MY_BUF_SIZE (8192) @implementation Streams + (BOOL)transferFrom:(id )is to:(id )os error:(NSError **)error { - BOOL ret = YES; + unsigned long long written = 0; + return [Streams transferFrom:is to:os bytesWritten:&written error:error]; +} ++ (BOOL)transferFrom:(id )is to:(id )os bytesWritten:(unsigned long long *)written error:(NSError **)error { + NSInteger received = 0; + unsigned char *buf = (unsigned char *)malloc(MY_BUF_SIZE); for (;;) { - NSUInteger received; - NSError *readError = nil; - unsigned char *buf = [is read:&received error:&readError]; - if (buf == NULL) { - if ([readError code] != ERROR_EOF) { - ret = NO; - if (error != NULL) { - *error = readError; - } - } + received = [is read:buf bufferLength:MY_BUF_SIZE error:error]; + if (received <= 0) { break; } - NSAssert(received > 0, @"expected to receive more than 0 bytes"); if (![os write:buf length:received error:error]) { - ret = NO; + received = -1; break; } + *written += (unsigned long long)received; } - return ret; + free(buf); + return received >= 0; } + (BOOL)transferFrom:(id )is atomicallyToFile:(NSString *)path bytesWritten:(unsigned long long *)written error:(NSError **)error { NSString *tempFileTemplate = [path stringByAppendingString:@".XXXXXX"]; diff --git a/io/StringIO.h b/io/StringIO.h index c030a71..afd9728 100644 --- a/io/StringIO.h +++ b/io/StringIO.h @@ -31,7 +31,7 @@ */ #import -#import "BufferedInputStream.h" +@class BufferedInputStream; #import "OutputStream.h" @interface StringIO : NSObject { @@ -39,5 +39,6 @@ } + (void)write:(NSString *)value to:(NSMutableData *)data; + (BOOL)write:(NSString *)str to:(id )os error:(NSError **)error; -+ (BOOL)read:(NSString **)value from:(id )is error:(NSError **)error; ++ (BOOL)read:(NSString **)value from:(BufferedInputStream *)is error:(NSError **)error; ++ (BOOL)newString:(NSString **)value from:(BufferedInputStream *)is error:(NSError **)error; @end diff --git a/io/StringIO.m b/io/StringIO.m index d730759..f75f17d 100644 --- a/io/StringIO.m +++ b/io/StringIO.m @@ -37,6 +37,7 @@ #import "Streams.h" #import "NSData-InputStream.h" #import "BooleanIO.h" +#import "BufferedInputStream.h" @implementation StringIO + (void)write:(NSString *)str to:(NSMutableData *)data { @@ -56,7 +57,14 @@ + (BOOL)write:(NSString *)str to:(id )os error:(NSError **)error { [data release]; return ret; } -+ (BOOL)read:(NSString **)value from:(id )is error:(NSError **)error { ++ (BOOL)read:(NSString **)value from:(BufferedInputStream *)is error:(NSError **)error { + if (![StringIO newString:value from:is error:error]) { + return NO; + } + [*value autorelease]; + return YES; +} ++ (BOOL)newString:(NSString **)value from:(BufferedInputStream *)is error:(NSError **)error { *value = nil; BOOL isNotNil = NO; if (![BooleanIO read:&isNotNil from:is error:error]) { @@ -67,11 +75,16 @@ + (BOOL)read:(NSString **)value from:(id )is error:(NSError if (![IntegerIO readUInt64:&len from:is error:error]) { return NO; } - unsigned char *utf8 = [is readExactly:len error:error]; - if (!utf8) { + unsigned char *buf = (unsigned char *)malloc(len); + *value = nil; + BOOL ret = [is readExactly:len into:buf error:error]; + if (ret) { + *value = [[NSString alloc] initWithBytes:buf length:len encoding:NSUTF8StringEncoding]; + } + free(buf); + if (!ret) { return NO; } - *value = [[[NSString alloc] initWithBytes:utf8 length:len encoding:NSUTF8StringEncoding] autorelease]; } return YES; } diff --git a/plist/ArrayNode.h b/plist/ArrayNode.h index c5ef4a4..3558a6e 100644 --- a/plist/ArrayNode.h +++ b/plist/ArrayNode.h @@ -55,4 +55,5 @@ - (StringNode *)stringNodeAtIndex:(int)index; - (void)add:(id )node; - (void)add:(id )node atIndex:(int)index; +- (BOOL)isEqualToArrayNode:(ArrayNode *)other; @end diff --git a/plist/ArrayNode.m b/plist/ArrayNode.m index 0f854e3..dd5fcd1 100644 --- a/plist/ArrayNode.m +++ b/plist/ArrayNode.m @@ -34,6 +34,11 @@ #import "ArrayNode.h" #import "PListNodeType.h" +@interface ArrayNode (internal) +- (id)initWithList:(NSMutableArray *)list; +- (NSArray *)list; +@end + @implementation ArrayNode - (id)init { if (self = [super init]) { @@ -47,6 +52,15 @@ - (id)initWithArray:(NSArray *)nodes { } return self; } +- (BOOL)isEqualToArrayNode:(ArrayNode *)other { + if (self == other) { + return YES; + } + if (![list isEqualToArray:[other list]]) { + return NO; + } + return YES; +} - (void)dealloc { [list release]; [super dealloc]; @@ -97,10 +111,43 @@ - (int)type { return PLN_ARRAY; } +#pragma mark NSCopying protocol +- (id)copyWithZone:(NSZone *)zone { + NSMutableArray *listCopy = [[NSMutableArray alloc] initWithArray:list copyItems:YES]; + ArrayNode *ret = [[ArrayNode alloc] initWithList:listCopy]; + [listCopy release]; + return ret; +} #pragma mark NSObject protocol - +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } + if (other == nil || ![other isKindOfClass:[self class]]) { + return NO; + } + return [self isEqualToArrayNode:other]; +} +- (NSUInteger)hash { + NSUInteger prime = 31; + NSUInteger result = 1; + result = prime * result + [list hash]; + return result; +} - (NSString *)description { return [NSString stringWithFormat:@"", self, [list description]]; } @end + +@implementation ArrayNode (internal) +- (id)initWithList:(NSMutableArray *)theList { + if (self = [super init]) { + list = [theList retain]; + } + return self; +} +- (NSArray *)list { + return list; +} +@end diff --git a/io/CFStreamPair.h b/plist/BinaryPListReader.h similarity index 73% rename from io/CFStreamPair.h rename to plist/BinaryPListReader.h index 1877e1f..36d933c 100644 --- a/io/CFStreamPair.h +++ b/plist/BinaryPListReader.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -31,21 +31,12 @@ */ #import -#import "StreamPair.h" -@class CFStreamInputStream; -@class CFStreamOutputStream; +#import "BufferedInputStream.h" +@class DictNode; -@interface CFStreamPair : NSObject { - NSString *description; - CFStreamInputStream *is; - CFStreamOutputStream *os; - NSTimeInterval createTime; - NSTimeInterval maxLifetime; - BOOL closeRequested; - +@interface BinaryPListReader : NSObject { + BufferedInputStream *is; } -+ (NSString *)errorDomain; -+ (NSError *)NSErrorWithNetworkError:(CFErrorRef)err; -- (id)initWithHost:(NSString *)theHost useSSL:(BOOL)isUseSSL maxLifetime:(NSTimeInterval)theMaxLifetime; - +- (id)initWithStream:(BufferedInputStream *)theIS; +- (DictNode *)read:(NSError **)error; @end diff --git a/plist/BinaryPListReader.m b/plist/BinaryPListReader.m new file mode 100644 index 0000000..de21d0c --- /dev/null +++ b/plist/BinaryPListReader.m @@ -0,0 +1,178 @@ +/* + Copyright (c) 2009-2010, 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 "BinaryPListReader.h" +#import "PListNode.h" +#import "ArrayNode.h" +#import "BooleanNode.h" +#import "DictNode.h" +#import "IntegerNode.h" +#import "RealNode.h" +#import "StringNode.h" +#import "IntegerIO.h" +#import "BooleanIO.h" +#import "StringIO.h" +#import "DoubleIO.h" +#import "PListNodeType.h" +#import "DataInputStream.h" +#import "SetNSError.h" +#import "NSData-InputStream.h" + +@interface BinaryPListReader (internal) +- (ArrayNode *)readArray:(NSError **)error; +- (BooleanNode *)readBoolean:(NSError **)error; +- (DictNode *)readDict:(NSError **)error; +- (IntegerNode *)readInteger:(NSError **)error; +- (RealNode *)readReal:(NSError **)error; +- (StringNode *)readString:(NSError **)error; +- (id )readPListNode:(NSError **)error; +@end + +@implementation BinaryPListReader +- (id)initWithStream:(BufferedInputStream *)theIS { + if (self = [super init]) { + is = [theIS retain]; + if (!is) { + [self release]; + self = nil; + } + } + return self; +} +- (void)dealloc { + [is release]; + [super dealloc]; +} + +- (DictNode *)read:(NSError **)error { + return [self readDict:error]; +} +@end + +@implementation BinaryPListReader (internal) +- (ArrayNode *)readArray:(NSError **)error { + uint32_t size; + if (![IntegerIO readUInt32:&size from:is error:error]) { + return nil; + } + NSMutableArray *arr = [[NSMutableArray alloc] init]; + for (uint32_t i = 0; i < size; i++) { + id node = [self readPListNode:error]; + if (!node) { + [arr release]; + return nil; + } + [arr addObject:node]; + } + ArrayNode *an = [[[ArrayNode alloc] initWithArray:arr] autorelease]; + [arr release]; + return an; +} +- (BooleanNode *)readBoolean:(NSError **)error { + BOOL value; + if (![BooleanIO read:&value from:is error:error]) { + return nil; + } + return [[[BooleanNode alloc] initWithBoolean:value] autorelease]; +} +- (DictNode *)readDict:(NSError **)error { + uint32_t size; + if (![IntegerIO readUInt32:&size from:is error:error]) { + return nil; + } + DictNode *dn = [[[DictNode alloc] init] autorelease]; + for (uint32_t i = 0; i < size; i++) { + NSString *key; + if (![StringIO read:&key from:is error:error]) { + return nil; + } + id value = [self readPListNode:error]; + if (!value) { + return nil; + } + [dn put:value forKey:key]; + } + return dn; +} +- (IntegerNode *)readInteger:(NSError **)error { + long long value; + if (![IntegerIO readInt64:&value from:is error:error]) { + return nil; + } + return [[[IntegerNode alloc] initWithLongLong:value] autorelease]; +} +- (RealNode *)readReal:(NSError **)error { + double value; + if (![DoubleIO read:&value from:is error:error]) { + return nil; + } + return [[[RealNode alloc] initWithDouble:value] autorelease]; +} +- (StringNode *)readString:(NSError **)error { + NSString *value; + if (![StringIO read:&value from:is error:error]) { + return nil; + } + return [[[StringNode alloc] initWithString:value] autorelease]; +} +- (id )readPListNode:(NSError **)error { + int nodeType; + if (![IntegerIO readInt32:&nodeType from:is error:error]) { + return nil; + } + id node = nil; + switch (nodeType) { + case PLN_ARRAY: + node = [self readArray:error]; + break; + case PLN_BOOLEAN: + node = [self readBoolean:error]; + break; + case PLN_DICT: + node = [self readDict:error]; + break; + case PLN_INTEGER: + node = [self readInteger:error]; + break; + case PLN_REAL: + node = [self readReal:error]; + break; + case PLN_STRING: + node = [self readString:error]; + break; + default: + SETNSERROR(@"PListErrorDomain", -1, @"invalid node type"); + } + return node; +} + +@end diff --git a/io/StreamPairFactory.h b/plist/BinaryPListWriter.h similarity index 77% rename from io/StreamPairFactory.h rename to plist/BinaryPListWriter.h index 6817f76..3d42b1c 100644 --- a/io/StreamPairFactory.h +++ b/plist/BinaryPListWriter.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -31,16 +31,11 @@ */ #import -@protocol StreamPair; -@interface StreamPairFactory : NSObject { - NSTimeInterval maxStreamPairLifetime; - NSLock *lock; - NSMutableDictionary *threadMap; - -} -+ (StreamPairFactory *)theFactory; -- (void)setMaxStreamPairLifetime:(NSTimeInterval)theMaxLifetime; -- (id )newStreamPairToHost:(NSString *)theHost useSSL:(BOOL)isUseSSL error:(NSError **)error; +@interface BinaryPListWriter : NSObject { + NSMutableData *data; +} +- (id)initWithMutableData:(NSMutableData *)theData; +- (void)write:(DictNode *)plist; @end diff --git a/plist/BinaryPListWriter.m b/plist/BinaryPListWriter.m new file mode 100644 index 0000000..5c2a3ee --- /dev/null +++ b/plist/BinaryPListWriter.m @@ -0,0 +1,134 @@ +/* + Copyright (c) 2009-2010, 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 "PListNode.h" +#import "ArrayNode.h" +#import "BooleanNode.h" +#import "DictNode.h" +#import "IntegerNode.h" +#import "RealNode.h" +#import "StringNode.h" +#import "IntegerIO.h" +#import "BooleanIO.h" +#import "StringIO.h" +#import "DoubleIO.h" +#import "PListNodeType.h" +#import "BinaryPListWriter.h" + +@interface BinaryPListWriter (internal) +- (void)writeArray:(ArrayNode *)node; +- (void)writeBoolean:(BooleanNode *)node; +- (void)writeDict:(DictNode *)node; +- (void)writeInteger:(IntegerNode *)node; +- (void)writeReal:(RealNode *)node; +- (void)writeString:(StringNode *)node; +- (void)writePListNode:(id )node; +@end + +@implementation BinaryPListWriter +- (id)initWithMutableData:(NSMutableData *)theData { + if (self = [super init]) { + data = [theData retain]; + } + return self; +} +- (void)dealloc { + [data release]; + [super dealloc]; +} +- (void)write:(DictNode *)plist { + [self writeDict:plist]; +} +@end + +@implementation BinaryPListWriter (internal) +- (void)writeArray:(ArrayNode *)node { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSUInteger size = [node size]; + NSAssert(size < 0xffffffff, @"size is greater than uint32_t max!"); + [IntegerIO writeUInt32:(uint32_t)size to:data]; //FIXME: Should have written 64 bits! + for (int i = 0; i < size; i++) { + [self writePListNode:[node objectAtIndex:i]]; + } + [pool drain]; +} +- (void)writeBoolean:(BooleanNode *)node { + [BooleanIO write:[node booleanValue] to:data]; +} +- (void)writeDict:(DictNode *)node { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSArray *orderedKeys = [node orderedKeySet]; + NSUInteger count = [orderedKeys count]; + NSAssert(count < 0xffffffff, @"count is greater than uint32_t max!"); + [IntegerIO writeUInt32:(uint32_t)[orderedKeys count] to:data]; //FIXME: Should have written 64 bits! + for (NSString *key in orderedKeys) { + [StringIO write:key to:data]; + [self writePListNode:[node nodeForKey:key]]; + } + [pool drain]; +} +- (void)writeInteger:(IntegerNode *)node { + [IntegerIO writeInt64:[node longlongValue] to:data]; +} +- (void)writeReal:(RealNode *)node { + [DoubleIO write:[node doubleValue] to:data]; +} +- (void)writeString:(StringNode *)node { + [StringIO write:[node stringValue] to:data]; +} +- (void)writePListNode:(id )node { + int type = [node type]; + [IntegerIO writeInt32:type to:data]; + switch (type) { + case PLN_ARRAY: + [self writeArray:(ArrayNode *)node]; + break; + case PLN_BOOLEAN: + [self writeBoolean:(BooleanNode *)node]; + break; + case PLN_DICT: + [self writeDict:(DictNode *)node]; + break; + case PLN_INTEGER: + [self writeInteger:(IntegerNode *)node]; + break; + case PLN_REAL: + [self writeReal:(RealNode *)node]; + break; + case PLN_STRING: + [self writeString:(StringNode *)node]; + break; + default: + @throw [NSException exceptionWithName:@"InvalidPListNodeTypeException" reason:[NSString stringWithFormat:@"invalid type %d", type] userInfo:nil]; // Programming error. + } +} +@end diff --git a/plist/BooleanNode.h b/plist/BooleanNode.h index f341815..21b78f6 100644 --- a/plist/BooleanNode.h +++ b/plist/BooleanNode.h @@ -38,4 +38,5 @@ } - (id)initWithBoolean:(BOOL)value; - (BOOL)booleanValue; +- (BOOL)isEqualToBooleanNode:(BooleanNode *)other; @end diff --git a/plist/BooleanNode.m b/plist/BooleanNode.m index 6971369..869f8c0 100644 --- a/plist/BooleanNode.m +++ b/plist/BooleanNode.m @@ -44,7 +44,12 @@ - (id)initWithBoolean:(BOOL)b { - (BOOL)booleanValue { return value; } - +- (BOOL)isEqualToBooleanNode:(BooleanNode *)other { + if (self == other) { + return YES; + } + return value == [other booleanValue]; +} #pragma mark PListNode protocol @@ -52,9 +57,27 @@ - (int)type { return PLN_BOOLEAN; } +#pragma mark NSCopying protocol +- (id)copyWithZone:(NSZone *)zone { + return [[BooleanNode alloc] initWithBoolean:value]; +} #pragma mark NSObject protocol - +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } + if (other == nil || ![other isKindOfClass:[self class]]) { + return NO; + } + return [self isEqualToBooleanNode:other]; +} +- (NSUInteger)hash { + NSUInteger prime = 31; + NSUInteger result = 1; + result = prime * result + (value ? 1231 : 1237); + return result; +} - (NSString *)description { return [NSString stringWithFormat:@"", self, (value ? @"YES" : @"NO")]; } diff --git a/plist/DictNode.h b/plist/DictNode.h index 2a06b5e..32eaf72 100644 --- a/plist/DictNode.h +++ b/plist/DictNode.h @@ -39,12 +39,14 @@ @class StringNode; #import "PListNode.h" -@interface DictNode : NSObject { +@interface DictNode : NSObject { NSMutableDictionary *dict; NSMutableArray *orderedKeys; } + (DictNode *)dictNodeWithContentsOfXMLFile:(NSString *)path error:(NSError **)error; + (DictNode *)dictNodeWithXMLData:(NSData *)data error:(NSError **)error; ++ (DictNode *)dictNodeWithContentsOfBinaryFile:(NSString *)path error:(NSError **)error; ++ (DictNode *)dictNodeWithBinaryData:(NSData *)data error:(NSError **)error; - (int)size; - (BOOL)containsKey:(NSString *)key; @@ -70,4 +72,8 @@ - (BOOL)writeXMLToFile:(NSString *)path error:(NSError **)error; - (NSData *)XMLData; +- (BOOL)writeAtomicallyToBinaryFile:(NSString *)path error:(NSError **)error; +- (NSData *)binaryData; + +- (BOOL)isEqualToDictNode:(DictNode *)dictNode; @end diff --git a/plist/DictNode.m b/plist/DictNode.m index 3a2f2bd..09b98f8 100644 --- a/plist/DictNode.m +++ b/plist/DictNode.m @@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #import "PListNode.h" #import "PListNodeType.h" #import "ArrayNode.h" @@ -40,6 +41,17 @@ #import "DictNode.h" #import "XMLPListReader.h" #import "XMLPListWriter.h" +#import "BinaryPListWriter.h" +#import "SetNSError.h" +#import "FileInputStream.h" +#import "DataInputStream.h" +#import "BinaryPListReader.h" +#import "BufferedInputStream.h" + +@interface DictNode (internal) +- (id)initWithDict:(NSMutableDictionary *)theDict orderedKeys:(NSMutableArray *)theOrderedKeys; +- (NSDictionary *)dict; +@end @implementation DictNode + (DictNode *)dictNodeWithContentsOfXMLFile:(NSString *)path error:(NSError **)error { @@ -57,6 +69,31 @@ + (DictNode *)dictNodeWithXMLData:(NSData *)data error:(NSError **)error { [reader release]; return dn; } ++ (DictNode *)dictNodeWithContentsOfBinaryFile:(NSString *)path error:(NSError **)error { + struct stat st; + if (stat([path fileSystemRepresentation], &st) == -1) { + SETNSERROR(@"UnixErrorDomain", errno, @"%s", strerror(errno)); + return nil; + } + FileInputStream *fis = [[FileInputStream alloc] initWithPath:path offset:0 length:(unsigned long long)st.st_size]; + BufferedInputStream *bis = [[BufferedInputStream alloc] initWithUnderlyingStream:fis]; + BinaryPListReader *reader = [[BinaryPListReader alloc] initWithStream:bis]; + DictNode *ret = [reader read:error]; + [reader release]; + [bis release]; + [fis release]; + return ret; +} ++ (DictNode *)dictNodeWithBinaryData:(NSData *)data error:(NSError **)error { + DataInputStream *dis = [[DataInputStream alloc] initWithData:data]; + BufferedInputStream *bis = [[BufferedInputStream alloc] initWithUnderlyingStream:dis]; + BinaryPListReader *reader = [[BinaryPListReader alloc] initWithStream:bis]; + [bis release]; + DictNode *ret = [reader read:error]; + [reader release]; + [dis release]; + return ret; +} - (id)init { if (self = [super init]) { @@ -171,7 +208,34 @@ - (NSData *)XMLData { [writer release]; return data; } - +- (BOOL)writeAtomicallyToBinaryFile:(NSString *)path error:(NSError **)error { + NSMutableData *data = [[NSMutableData alloc] init]; + BinaryPListWriter *bplw = [[BinaryPListWriter alloc] initWithMutableData:data]; + [bplw write:self]; + [bplw release]; + BOOL ret = [data writeToFile:path options:NSAtomicWrite error:error]; + [data release]; + return ret; +} +- (NSData *)binaryData { + NSMutableData *data = [[[NSMutableData alloc] init] autorelease]; + BinaryPListWriter *bplw = [[BinaryPListWriter alloc] initWithMutableData:data]; + [bplw write:self]; + [bplw release]; + return data; +} +- (BOOL)isEqualToDictNode:(DictNode *)dictNode { + if (self == dictNode) { + return YES; + } + if (![orderedKeys isEqualToArray:[dictNode orderedKeySet]]) { + return NO; + } + if (![dict isEqualToDictionary:[dictNode dict]]) { + return NO; + } + return YES; +} #pragma mark PListNode protocol @@ -179,10 +243,47 @@ - (int)type { return PLN_DICT; } +#pragma mark NSCopying protocol +- (id)copyWithZone:(NSZone *)zone { + NSMutableDictionary *dictCopy = [[NSMutableDictionary alloc] initWithDictionary:dict copyItems:YES]; + NSMutableArray *orderedKeysCopy = [[NSMutableArray alloc] initWithArray:orderedKeys copyItems:YES]; + DictNode *dictNodeCopy = [[DictNode alloc] initWithDict:dictCopy orderedKeys:orderedKeysCopy]; + [dictCopy release]; + [orderedKeysCopy release]; + return dictNodeCopy; +} #pragma mark NSObject protocol - +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } + if (other == nil || ![other isKindOfClass:[self class]]) { + return NO; + } + return [self isEqualToDictNode:other]; +} +- (NSUInteger)hash { + NSUInteger prime = 31; + NSUInteger result = 1; + result = prime * result + [dict hash]; + result = prime * result + [orderedKeys hash]; + return result; +} - (NSString *)description { return [NSString stringWithFormat:@"", self, [dict description]]; } @end + +@implementation DictNode (internal) +- (id)initWithDict:(NSMutableDictionary *)theDict orderedKeys:(NSMutableArray *)theOrderedKeys { + if (self = [super init]) { + dict = [theDict retain]; + orderedKeys = [theOrderedKeys retain]; + } + return self; +} +- (NSDictionary *)dict { + return dict; +} +@end diff --git a/plist/IntegerNode.m b/plist/IntegerNode.m index 016d918..2b02e5f 100644 --- a/plist/IntegerNode.m +++ b/plist/IntegerNode.m @@ -65,7 +65,17 @@ - (int)intValue { - (long long)longlongValue { return value; } +- (BOOL)isEqualToIntegerNode:(IntegerNode *)other { + if (self == other) { + return YES; + } + return value = [other longlongValue]; +} +#pragma mark NSCopying protocol +- (id)copyWithZone:(NSZone *)zone { + return [[IntegerNode alloc] initWithLongLong:value]; +} #pragma mark PListNode protocol @@ -75,7 +85,21 @@ - (int)type { #pragma mark NSObject protocol - +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } + if (other == nil || ![other isKindOfClass:[self class]]) { + return NO; + } + return [self isEqualToIntegerNode:other]; +} +- (NSUInteger)hash { + NSUInteger prime = 31; + NSUInteger result = 1; + result = prime * result + (NSUInteger)value; + return result; +} - (NSString *)description { return [NSString stringWithFormat:@"", self, value]; } diff --git a/plist/RealNode.h b/plist/RealNode.h index 6eddc86..11e9bcc 100644 --- a/plist/RealNode.h +++ b/plist/RealNode.h @@ -39,4 +39,5 @@ - (id)initWithDouble:(double)value; - (id)initWithString:(NSString *)theValue error:(NSError **)error; - (double)doubleValue; +- (BOOL)isEqualToRealNode:(RealNode *)other; @end diff --git a/plist/RealNode.m b/plist/RealNode.m index 66bc7f1..344230c 100644 --- a/plist/RealNode.m +++ b/plist/RealNode.m @@ -55,7 +55,12 @@ - (id)initWithString:(NSString *)theValue error:(NSError **)error { - (double)doubleValue { return value; } - +- (BOOL)isEqualToRealNode:(RealNode *)other { + if (other == self) { + return YES; + } + return value = [other doubleValue]; +} #pragma mark PListNode protocol @@ -63,9 +68,27 @@ - (int)type { return PLN_REAL; } +#pragma mark NSCopying protocol +- (id)copyWithZone:(NSZone *)zone { + return [[RealNode alloc] initWithDouble:value]; +} #pragma mark NSObject protocol - +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } + if (other == nil || ![other isKindOfClass:[self class]]) { + return NO; + } + return [self isEqualToRealNode:other]; +} +- (NSUInteger)hash { + NSUInteger prime = 31; + NSUInteger result = 1; + result = prime * result + (NSUInteger)value; + return result; +} - (NSString *)description { return [NSString stringWithFormat:@"", self, value]; } diff --git a/plist/StringNode.h b/plist/StringNode.h index 8ccd6ff..502866c 100644 --- a/plist/StringNode.h +++ b/plist/StringNode.h @@ -38,4 +38,5 @@ } - (id)initWithString:(NSString *)theValue; - (NSString *)stringValue; +- (BOOL)isEqualToStringNode:(StringNode *)other; @end diff --git a/plist/StringNode.m b/plist/StringNode.m index 993e7dd..114b608 100644 --- a/plist/StringNode.m +++ b/plist/StringNode.m @@ -48,7 +48,12 @@ - (void)dealloc { - (NSString *)stringValue { return value; } - +- (BOOL)isEqualToStringNode:(StringNode *)other { + if (self == other) { + return YES; + } + return value == [other stringValue]; +} #pragma mark PListNode protocol @@ -56,10 +61,27 @@ - (int)type { return PLN_STRING; } - +#pragma mark NSCopying protocol +- (id)copyWithZone:(NSZone *)zone { + return [[StringNode alloc] initWithString:value]; +} #pragma mark NSObject protocol - +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } + if (other == nil || ![other isKindOfClass:[self class]]) { + return NO; + } + return [self isEqualToStringNode:other]; +} +- (NSUInteger)hash { + NSUInteger prime = 31; + NSUInteger result = 1; + result = prime * result + [value hash]; + return result; +} - (NSString *)description { return [NSString stringWithFormat:@"", self, value]; } diff --git a/s3/HTTPConnection_S3.h b/s3/HTTPConnection_S3.h index de65410..3b24aa6 100644 --- a/s3/HTTPConnection_S3.h +++ b/s3/HTTPConnection_S3.h @@ -29,7 +29,6 @@ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #import #import "HTTPConnection.h" @class S3AuthorizationProvider; diff --git a/s3/HTTPConnection_S3.m b/s3/HTTPConnection_S3.m index 08ce7f3..b65d0f8 100644 --- a/s3/HTTPConnection_S3.m +++ b/s3/HTTPConnection_S3.m @@ -29,7 +29,6 @@ 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_S3.h" #import "S3AuthorizationParameters.h" #import "S3AuthorizationProvider.h" diff --git a/s3/S3Request.m b/s3/S3Request.m index 975fb24..b8d88bd 100644 --- a/s3/S3Request.m +++ b/s3/S3Request.m @@ -50,7 +50,7 @@ @interface S3Request (internal) - (ServerBlob *)newServerBlobOnce:(NSError **)error; -- (void)setError:(NSError **)error withHTTPResponseCode:(int)code responseData:(NSData *)response; +- (BOOL)setError:(NSError **)error withHTTPResponseCode:(int)code responseData:(NSData *)response; @end @implementation S3Request @@ -178,11 +178,8 @@ - (ServerBlob *)newServerBlob:(NSError **)error { [pool drain]; [myError autorelease]; if (sb == nil && error != NULL) { - if (myError != nil) { - *error = myError; - } else { - SETNSERROR([S3Service errorDomain], -1, @"unknown error reading %@", path); - } + NSAssert(myError != nil, @"myError must be set"); + *error = myError; } return sb; } @@ -226,7 +223,7 @@ - (ServerBlob *)newServerBlobOnce:(NSError **)error { ServerBlob *ret = nil; int code = [conn responseCode]; if (code >= 200 && code <= 299) { - id bodyStream = [conn newResponseBodyStream:error]; + id bodyStream = [conn newResponseBodyStream:error]; if (bodyStream == nil) { return nil; } @@ -260,7 +257,7 @@ - (ServerBlob *)newServerBlobOnce:(NSError **)error { } return nil; } -- (void)setError:(NSError **)error withHTTPResponseCode:(int)code responseData:(NSData *)response { +- (BOOL)setError:(NSError **)error withHTTPResponseCode:(int)code responseData:(NSData *)response { NSAssert(error != NULL, @"NSError **error must not be NULL"); NSString *errorXML = [[[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding] autorelease]; HSLogDebug(@"amazon HTTP error code=%d; xml=%@", code, errorXML); @@ -269,7 +266,7 @@ - (void)setError:(NSError **)error withHTTPResponseCode:(int)code responseData:( if (xmlDoc == nil) { HSLogError(@"error parsing Amazon error XML: %@", [xmlError localizedDescription]); SETNSERROR([S3Service errorDomain], code, @"Amazon error (failed to parse XML); xml=%@", errorXML); - return; + return YES; } HSLogTrace(@"error XML: %@", [xmlDoc description]); @@ -279,13 +276,13 @@ - (void)setError:(NSError **)error withHTTPResponseCode:(int)code responseData:( if (errorNodes == nil) { HSLogError(@"error finding Error node in Amazon error XML: %@", [xmlError localizedDescription]); SETNSERROR([S3Service errorDomain], code, @"Amazon error (failed to parse Error node in XML); xml=%@", errorXML); - return; + return YES; } if ([errorNodes count] == 0) { HSLogWarn(@"missing Error node in S3 XML response %@", errorXML); SETNSERROR([S3Service errorDomain], code, @"Amazon error (no Error node found); xml=%@", errorXML); - return; + return YES; } if ([errorNodes count] > 1) { @@ -303,6 +300,7 @@ - (void)setError:(NSError **)error withHTTPResponseCode:(int)code responseData:( nil]; NSError *myError = [NSError errorWithDomain:[S3Service amazonErrorDomain] code:code userInfo:userInfo]; *error = myError; + return YES; } #pragma mark MonitoredInputStream diff --git a/s3/S3Service.h b/s3/S3Service.h index 05527dc..2dd5800 100644 --- a/s3/S3Service.h +++ b/s3/S3Service.h @@ -72,6 +72,8 @@ enum { - (NSData *)dataAtPath:(NSString *)path error:(NSError **)error; - (ServerBlob *)newServerBlobAtPath:(NSString *)path error:(NSError **)error; +- (BOOL)aclXMLData:(NSData **)aclXMLData atPath:(NSString *)path error:(NSError **)error; +- (BOOL)acl:(int *)acl atPath:(NSString *)path error:(NSError **)error; - (NSArray *)commonPrefixesForPathPrefix:(NSString *)prefix delimiter:(NSString *)delimiter error:(NSError **)error; @end diff --git a/s3/S3Service.m b/s3/S3Service.m index e9315da..afcd453 100644 --- a/s3/S3Service.m +++ b/s3/S3Service.m @@ -59,6 +59,7 @@ @interface S3Service (internal) - (NSXMLDocument *)listBuckets:(NSError **)error; +- (BOOL)internalACL:(int *)acl atPath:(NSString *)path error:(NSError **)error; @end @implementation S3Service @@ -199,6 +200,39 @@ - (ServerBlob *)newServerBlobAtPath:(NSString *)path error:(NSError **)error { [s3r release]; return sb; } +- (BOOL)aclXMLData:(NSData **)aclXMLData atPath:(NSString *)path error:(NSError **)error { + *aclXMLData = nil; + HSLogDebug(@"getting %@", path); + S3Request *s3r = [[S3Request alloc] initWithMethod:@"GET" path:path queryString:@"?acl" authorizationProvider:sap useSSL:useSSL retryOnNetworkError:retryOnNetworkError]; + ServerBlob *sb = [s3r newServerBlob:error]; + [s3r release]; + if (sb == nil) { + return NO; + } + NSData *output = [sb slurp:error]; + [sb release]; + if (output == nil) { + return NO; + } + *aclXMLData = output; + return YES; +} +- (BOOL)acl:(int *)acl atPath:(NSString *)path error:(NSError **)error { + if (error != NULL) { + *error = nil; + } + *acl = 0; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + BOOL ret = [self internalACL:acl atPath:path error:error]; + if (!ret && error != NULL) { + [*error retain]; + } + [pool drain]; + if (!ret && error != NULL) { + [*error autorelease]; + } + return ret; +} - (NSArray *)commonPrefixesForPathPrefix:(NSString *)prefix delimiter:(NSString *)delimiter error:(NSError **)error { if (![prefix hasPrefix:@"/"]) { HSLogError(@"invalid prefix %@", prefix); @@ -269,4 +303,61 @@ - (NSXMLDocument *)listBuckets:(NSError **)error { } return [[[NSXMLDocument alloc] initWithData:data options:0 error:error] autorelease]; } +- (BOOL)internalACL:(int *)acl atPath:(NSString *)path error:(NSError **)error { + NSData *aclData; + if (![self aclXMLData:&aclData atPath:path error:error]) { + return NO; + } + NSXMLDocument *xmlDoc = [[[NSXMLDocument alloc] initWithData:aclData options:0 error:error] autorelease]; + if (!xmlDoc) { + return NO; + } + NSArray *grants = [xmlDoc nodesForXPath:@"AccessControlPolicy/AccessControlList/Grant" error:error]; + if (!grants) { + return NO; + } + BOOL publicRead = NO; + BOOL publicWrite = NO; + for (NSXMLElement *grant in grants) { + NSArray *grantees = [grant nodesForXPath:@"Grantee" error:error]; + if (!grantees) { + return NO; + } + for (NSXMLElement *grantee in grantees) { + NSString *xsiType = [[grantee attributeForName:@"xsi:type"] stringValue]; + if ([xsiType isEqualToString:@"Group"]) { + NSArray *uris = [grantee nodesForXPath:@"URI" error:error]; + if (!uris) { + return NO; + } + if ([uris count] > 0) { + if ([[[uris objectAtIndex:0] stringValue] isEqualToString:@"http://acs.amazonaws.com/groups/global/AllUsers"]) { + NSArray *permissions = [grant nodesForXPath:@"Permission" error:error]; + if (!permissions) { + return NO; + } + for (NSXMLElement *permission in permissions) { + if ([[permission stringValue] isEqualToString:@"WRITE"]) { + publicWrite = YES; + } else if ([[permission stringValue] isEqualToString:@"READ"]) { + publicRead = YES; + } else { + SETNSERROR([S3Service errorDomain], -1, @"unexpected permission"); + return NO; + } + } + } + } + } + } + } + if (publicRead && publicWrite) { + *acl = PUBLIC_READ_WRITE; + } else if (publicRead) { + *acl = PUBLIC_READ; + } else { + *acl = PRIVATE; + } + return YES; +} @end diff --git a/shared/NSErrorCodes.h b/shared/NSErrorCodes.h index 3e610f8..0130174 100644 --- a/shared/NSErrorCodes.h +++ b/shared/NSErrorCodes.h @@ -30,12 +30,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define ERROR_NOT_FOUND -2 -#define ERROR_INVALID_OBJECT_VERSION -3 -#define ERROR_EOF -4 -#define ERROR_NOT_LICENSED -5 -#define ERROR_BUCKET_CONFIGURATION_CHANGED -6 -#define ERROR_ABORT_REQUESTED -7 +#define ERROR_NOT_FOUND (-2) +#define ERROR_INVALID_OBJECT_VERSION (-3) +#define ERROR_EOF (-4) +#define ERROR_NOT_LICENSED (-5) +#define ERROR_BUCKET_CONFIGURATION_CHANGED (-6) +#define ERROR_ABORT_REQUESTED (-7) #define ERROR_PACK_INDEX_ENTRY_NOT_RESOLVABLE (-8) #define ERROR_COULD_NOT_CONNECT_TO_AGENT (-9) -#define ERROR_AGENT_COMMUNICATION (-10) \ No newline at end of file +#define ERROR_AGENT_COMMUNICATION (-10) +#define ERROR_TIMEOUT (-11) +#define ERROR_ACCESS_DENIED (-12) diff --git a/shared/RegexKitLite.m b/shared/RegexKitLite.m index 6bf66af..cbd8777 100644 --- a/shared/RegexKitLite.m +++ b/shared/RegexKitLite.m @@ -536,9 +536,11 @@ static void rkl_find(RKLCacheSlot *cacheSlot, NSInteger capture, NSRange searchR cacheSlot->lastFindRange = searchRange; // Cache the successful search/find range. } - +//#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-value" if(capture == 0) { captureRange = cacheSlot->lastMatchRange; } else { RKLGetRangeForCapture(cacheSlot->icu_regex, status, capture, captureRange); } - +//#pragma clang diagnostic pop + exitNow: *resultRange = captureRange; }