// // ArchiveSource.m // ArchiveSource // // Created by Christopher Snowhill on 10/4/13. // Copyright 2013 __NoWork, Inc__. All rights reserved. // #import "ArchiveSource.h" #import "SandboxBroker.h" #import "Logging.h" static NSString *path_unpack_string(NSString *src, NSRange *remainder) { NSRange bar = [src rangeOfString:@"|"]; if(bar.location != 0 || bar.length != 1) return nil; NSRange next = { .location = 1, .length = [src length] - 1 }; bar = [src rangeOfString:@"|" options:0 range:next]; if(bar.length != 1) return nil; NSRange lengthRange = { .location = 1, .length = bar.location - 1 }; NSUInteger length = [[src substringWithRange:lengthRange] integerValue]; if(length >= ([src length] - bar.location - 1)) return nil; NSRange pathRange = { .location = bar.location + 1, .length = length }; NSString *ret = [src substringWithRange:pathRange]; remainder->location = bar.location + length + 1; remainder->length = [src length] - remainder->location; return ret; } static BOOL g_parse_unpack_path(NSString *src, NSString **archive, NSString **file, NSString **type) { NSRange typeRange; NSRange range; range = [src rangeOfString:@"|"]; if(range.length != 1) return NO; range.length = [src length] - range.location; typeRange.location = 9; typeRange.length = range.location - 9; *type = [src substringWithRange:typeRange]; NSString *url = [src substringWithRange:range]; *archive = path_unpack_string(url, &range); if(!*archive) return NO; range.location++; range.length--; *file = [url substringWithRange:range]; return YES; } @implementation ArchiveSource - (id)init { self = [super init]; if(self) { fex = NULL; } return self; } - (BOOL)open:(NSURL *)url { [self setURL:url]; NSString *urlDecoded = [[url absoluteString] stringByRemovingPercentEncoding]; NSString *type; NSString *archive; NSString *file; if(!g_parse_unpack_path(urlDecoded, &archive, &file, &type)) return NO; if(![type isEqualToString:@"fex"]) return NO; id sandboxBrokerClass = NSClassFromString(@"SandboxBroker"); id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker]; sbHandle = [sandboxBroker beginFolderAccess:[NSURL fileURLWithPath:archive]]; fex_err_t error; error = fex_open(&fex, [archive UTF8String]); if(error) { ALog(@"Error opening archive: %s", error); return NO; } while(!fex_done(fex)) { if([file isEqualToString:guess_encoding_of_string(fex_name(fex))]) break; fex_next(fex); } if(fex_done(fex)) return NO; error = fex_data(fex, &data); if(error) { ALog(@"Error unpacking file from archive: %s", error); return NO; } offset = 0; size = fex_size(fex); return YES; } - (BOOL)seekable { return YES; } - (BOOL)seek:(long)position whence:(int)whence { switch(whence) { case SEEK_CUR: position += offset; break; case SEEK_END: position += size; break; } offset = position; return (offset <= size); } - (long)tell { return offset; } - (long)read:(void *)buffer amount:(long)amount { if(offset >= size) return 0; if(size - offset < amount) amount = size - offset; memcpy(buffer, (const uint8_t *)data + offset, amount); offset += amount; return amount; } - (void)close { if(fex) { fex_close(fex); fex = NULL; } if(sbHandle) { id sandboxBrokerClass = NSClassFromString(@"SandboxBroker"); id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker]; [sandboxBroker endFolderAccess:sbHandle]; sbHandle = NULL; } } - (NSURL *)url { return _url; } - (NSString *)mimeType { return nil; } - (void)setURL:(NSURL *)url { _url = url; } + (NSArray *)schemes { return @[@"unpack"]; } - (void)dealloc { [self close]; } @end