//
-// PithosFileUtilities.m
+// PithosUtilities.m
// pithos-macos
//
-// Copyright 2011 GRNET S.A. All rights reserved.
+// Copyright 2011-2012 GRNET S.A. All rights reserved.
//
// Redistribution and use in source and binary forms, with or
// without modification, are permitted provided that the following
// or implied, of GRNET S.A.
#import "PithosUtilities.h"
+#import "ASINetworkQueue.h"
+#import "ASIPithos.h"
#import "ASIPithosContainerRequest.h"
#import "ASIPithosObjectRequest.h"
#import "ASIPithosObject.h"
#pragma mark -
#pragma mark Download
-+ (ASIPithosObjectRequest *)objectDataRequestWithContainerName:(NSString *)containerName
- objectName:(NSString *)objectName
- toDirectory:(NSString *)directoryPath
- checkIfExists:(BOOL)ifExists
- sharingAccount:(NSString *)sharingAccount {
- NSString *fileName = [objectName lastPathComponent];
- if([objectName hasSuffix:@"/"])
- fileName = [fileName stringByAppendingString:@"/"];
++ (ASIPithosObjectRequest *)objectDataRequestWithPithos:(ASIPithos *)pithos
+ containerName:(NSString *)containerName
+ objectName:(NSString *)objectName
+ version:(NSString *)version
+ toDirectory:(NSString *)directoryPath
+ withNewFileName:(NSString *)newFileName
+ checkIfExists:(BOOL)ifExists
+ sharingAccount:(NSString *)sharingAccount {
+ NSString *fileName;
+ if (newFileName) {
+ fileName = [NSString stringWithString:newFileName];
+ } else {
+ fileName = [objectName lastPathComponent];
+ if ([objectName hasSuffix:@"/"])
+ fileName = [fileName stringByAppendingString:@"/"];
+ }
fileName = [fileName stringByReplacingOccurrencesOfString:@"/" withString:@":"];
- NSFileManager *defaultManager = [NSFileManager defaultManager];
+ NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *destinationPath = [directoryPath stringByAppendingPathComponent:fileName];
- if (ifExists && [defaultManager fileExistsAtPath:destinationPath]) {
- NSAlert *alert = [[[NSAlert alloc] init] autorelease];
- [alert setMessageText:@"File Exists"];
- [alert setInformativeText:[NSString stringWithFormat:@"A file or directory named '%@' already exists, do you want to replace it?", fileName]];
- [alert addButtonWithTitle:@"OK"];
- [alert addButtonWithTitle:@"Cancel"];
- NSInteger choice = [alert runModal];
+ if (ifExists && [fileManager fileExistsAtPath:destinationPath]) {
+ __block NSInteger choice;
+ dispatch_sync(dispatch_get_main_queue(), ^{
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:@"File Exists"];
+ [alert setInformativeText:[NSString stringWithFormat:@"A file or directory named '%@' already exists, do you want to replace it?", fileName]];
+ [alert addButtonWithTitle:@"OK"];
+ [alert addButtonWithTitle:@"Cancel"];
+ choice = [alert runModal];
+ });
if (choice == NSAlertSecondButtonReturn)
return nil;
}
BOOL directoryIsDirectory;
- BOOL directoryExists = [defaultManager fileExistsAtPath:directoryPath isDirectory:&directoryIsDirectory];
+ BOOL directoryExists = [fileManager fileExistsAtPath:directoryPath isDirectory:&directoryIsDirectory];
NSError *error = nil;
if (!directoryExists) {
- [defaultManager createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error];
+ [fileManager createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error];
} else if (!directoryIsDirectory) {
- [defaultManager removeItemAtPath:directoryPath error:&error];
+ [fileManager removeItemAtPath:directoryPath error:&error];
}
if (error) {
- NSLog(@"Cannot remove existing file '%@': %@", fileName, error);
- NSAlert *alert = [[[NSAlert alloc] init] autorelease];
- [alert setMessageText:@"Removal Error"];
- [alert setInformativeText:[NSString stringWithFormat:@"Cannot remove existing file '%@': %@", fileName, error]];
- [alert addButtonWithTitle:@"OK"];
- [alert runModal];
+ DLog(@"Cannot remove existing file '%@': %@", fileName, error);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:@"Removal Error"];
+ [alert setInformativeText:[NSString stringWithFormat:@"Cannot remove existing file '%@': %@",
+ fileName, [error localizedDescription]]];
+ [alert addButtonWithTitle:@"OK"];
+ [alert runModal];
+ });
return nil;
}
- ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectDataRequestWithContainerName:containerName
- objectName:objectName];
+ ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectDataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:objectName
+ version:version];
if (sharingAccount)
- [objectRequest setRequestUserFromDefaultTo:sharingAccount];
+ [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
objectRequest.downloadDestinationPath = destinationPath;
objectRequest.allowResumeForFileDownloads = YES;
objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
return objectRequest;
}
-+ (NSArray *)objectDataRequestsForSubdirWithContainerName:(NSString *)containerName
- objectName:(NSString *)objectName
- toDirectory:(NSString *)directoryPath
- checkIfExists:(BOOL)ifExists
- sharingAccount:(NSString *)sharingAccount {
++ (NSArray *)objectDataRequestsForSubdirWithPithos:(ASIPithos *)pithos
+ containerName:(NSString *)containerName
+ objectName:(NSString *)objectName
+ toDirectory:(NSString *)directoryPath
+ checkIfExists:(BOOL)ifExists
+ sharingAccount:(NSString *)sharingAccount {
NSString *subdirName = [objectName lastPathComponent];
NSString *destinationPath = [directoryPath stringByAppendingPathComponent:subdirName];
if (ifExists && [[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) {
- NSAlert *alert = [[[NSAlert alloc] init] autorelease];
- [alert setMessageText:@"File exists"];
- [alert setInformativeText:[NSString stringWithFormat:@"A file or directory named '%@' already exists, do you want to replace it?", subdirName]];
- [alert addButtonWithTitle:@"OK"];
- [alert addButtonWithTitle:@"Cancel"];
- NSInteger choice = [alert runModal];
+ __block NSInteger choice;
+ dispatch_sync(dispatch_get_main_queue(), ^{
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:@"File exists"];
+ [alert setInformativeText:[NSString stringWithFormat:@"A file or directory named '%@' already exists, do you want to replace it?", subdirName]];
+ [alert addButtonWithTitle:@"OK"];
+ [alert addButtonWithTitle:@"Cancel"];
+ choice = [alert runModal];
+ });
if (choice == NSAlertSecondButtonReturn)
return nil;
}
- NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName
- delimiter:nil sharingAccount:sharingAccount];
+ NSArray *objects = [self objectsForSubdirWithPithos:pithos containerName:containerName objectName:objectName
+ delimiter:nil sharingAccount:sharingAccount];
if (objects == nil)
return nil;
- NSFileManager *defaultManager = [NSFileManager defaultManager];
+ NSFileManager *fileManager = [NSFileManager defaultManager];
NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:[objects count]];
NSUInteger subdirPrefixLength = [objectName length];
NSError *error = nil;
- [defaultManager createDirectoryAtPath:[directoryPath stringByAppendingPathComponent:subdirName] withIntermediateDirectories:YES attributes:nil error:&error];
+ [fileManager createDirectoryAtPath:[directoryPath stringByAppendingPathComponent:subdirName] withIntermediateDirectories:YES attributes:nil error:&error];
if (error) {
- NSLog(@"Cannot create directory at '%@': %@", directoryPath, error);
- NSAlert *alert = [[[NSAlert alloc] init] autorelease];
- [alert setMessageText:@"Create Directory Error"];
- [alert setInformativeText:[NSString stringWithFormat:@"Cannot create directory at '%@': %@",
- directoryPath, error]];
- [alert addButtonWithTitle:@"OK"];
- [alert runModal];
+ DLog(@"Cannot create directory at '%@': %@", directoryPath, error);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:@"Create Directory Error"];
+ [alert setInformativeText:[NSString stringWithFormat:@"Cannot create directory at '%@': %@",
+ directoryPath, [error localizedDescription]]];
+ [alert addButtonWithTitle:@"OK"];
+ [alert runModal];
+ });
}
for (ASIPithosObject *object in objects) {
- if ([object.contentType isEqualToString:@"application/directory"]) {
+ if ([self isContentTypeDirectory:object.contentType]) {
NSString *subdirDirectoryPath = [directoryPath stringByAppendingPathComponent:subdirName];
subdirDirectoryPath = [subdirDirectoryPath stringByAppendingPathComponent:[object.name substringFromIndex:subdirPrefixLength]];
BOOL directoryIsDirectory;
- BOOL directoryExists = [defaultManager fileExistsAtPath:subdirDirectoryPath isDirectory:&directoryIsDirectory];
+ BOOL directoryExists = [fileManager fileExistsAtPath:subdirDirectoryPath isDirectory:&directoryIsDirectory];
NSError *error = nil;
if (!directoryExists) {
- [defaultManager createDirectoryAtPath:subdirDirectoryPath withIntermediateDirectories:YES attributes:nil error:&error];
+ [fileManager createDirectoryAtPath:subdirDirectoryPath withIntermediateDirectories:YES attributes:nil error:&error];
if (error) {
- NSLog(@"Cannot create directory at '%@': %@", subdirDirectoryPath, error);
- NSAlert *alert = [[[NSAlert alloc] init] autorelease];
- [alert setMessageText:@"Create Directory Error"];
- [alert setInformativeText:[NSString stringWithFormat:@"Cannot create directory at '%@': %@",
- subdirDirectoryPath, error]];
- [alert addButtonWithTitle:@"OK"];
- [alert runModal];
+ DLog(@"Cannot create directory at '%@': %@", subdirDirectoryPath, error);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:@"Create Directory Error"];
+ [alert setInformativeText:[NSString stringWithFormat:@"Cannot create directory at '%@': %@",
+ subdirDirectoryPath, [error localizedDescription]]];
+ [alert addButtonWithTitle:@"OK"];
+ [alert runModal];
+ });
}
} else if (!directoryIsDirectory) {
- [defaultManager removeItemAtPath:subdirDirectoryPath error:&error];
+ [fileManager removeItemAtPath:subdirDirectoryPath error:&error];
if (error) {
- NSLog(@"Cannot remove existing file at '%@': %@", subdirDirectoryPath, error);
- NSAlert *alert = [[[NSAlert alloc] init] autorelease];
- [alert setMessageText:@"Remove File Error"];
- [alert setInformativeText:[NSString stringWithFormat:@"Cannot remove existing file at '%@': %@",
- subdirDirectoryPath, error]];
- [alert addButtonWithTitle:@"OK"];
- [alert runModal];
+ DLog(@"Cannot remove existing file at '%@': %@", subdirDirectoryPath, error);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:@"Remove File Error"];
+ [alert setInformativeText:[NSString stringWithFormat:@"Cannot remove existing file at '%@': %@",
+ subdirDirectoryPath, [error localizedDescription]]];
+ [alert addButtonWithTitle:@"OK"];
+ [alert runModal];
+ });
}
}
} else {
NSString *objectDirectoryPath = [directoryPath stringByAppendingPathComponent:subdirName];
objectDirectoryPath = [objectDirectoryPath stringByAppendingPathComponent:[object.name substringWithRange:NSMakeRange(subdirPrefixLength, [object.name length] - subdirPrefixLength - [fileName length])]];
- ASIPithosObjectRequest *objectRequest = [self objectDataRequestWithContainerName:containerName
- objectName:object.name
- toDirectory:objectDirectoryPath
- checkIfExists:NO
- sharingAccount:sharingAccount];
+ ASIPithosObjectRequest *objectRequest = [self objectDataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:object.name
+ version:nil
+ toDirectory:objectDirectoryPath
+ withNewFileName:nil
+ checkIfExists:NO
+ sharingAccount:sharingAccount];
[(NSMutableDictionary *)objectRequest.userInfo setObject:[NSNumber numberWithUnsignedInteger:object.bytes] forKey:@"bytes"];
[objectRequests addObject:objectRequest];
}
}
#pragma mark -
+#pragma mark Download Block
+
++ (ASIPithosObjectRequest *)objectBlockDataRequestWithPithos:(ASIPithos *)pithos
+ containerName:(NSString *)containerName
+ object:(ASIPithosObject *)object
+ blockIndex:(NSUInteger)blockIndex
+ blockSize:(NSUInteger)blockSize {
+ NSUInteger rangeStart = blockIndex * blockSize;
+ NSUInteger rangeEnd = (rangeStart + blockSize <= object.bytes) ? (rangeStart + blockSize - 1) : (object.bytes - 1);
+ ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectDataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:object.name
+ version:nil
+ range:[NSString stringWithFormat:@"bytes=%lu-%lu", rangeStart, rangeEnd]
+ ifMatch:object.hash];
+ return objectRequest;
+}
+
++ (NSIndexSet *)missingBlocksForFile:(NSString *)filePath
+ blockSize:(NSUInteger)blockSize
+ blockHash:(NSString *)blockHash
+ withHashes:(NSArray *)hashes {
+ NSArray *fileHashes = [HashMapHash objectHashMapStrings:filePath withBlockHash:blockHash andBlockSize:blockSize];
+ return [hashes indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
+ if ((idx >= [fileHashes count]) || ![(NSString *)obj isEqualToString:[fileHashes objectAtIndex:idx]])
+ return YES;
+ return NO;
+ }];
+}
+
+#pragma mark -
#pragma mark Upload
-+ (ASIPithosObjectRequest *)writeObjectDataRequestWithContainerName:(NSString *)containerName
- objectName:(NSString *)objectName
- contentType:(NSString *)contentType
- blockSize:(NSUInteger)blockSize
- blockHash:(NSString *)blockHash
- forFile:(NSString *)filePath
- checkIfExists:(BOOL)ifExists
- hashes:(NSArray **)hashes
- sharingAccount:(NSString *)sharingAccount {
- if (ifExists && ![self proceedIfObjectExistsAtContainerName:containerName objectName:objectName
- sharingAccount:(NSString *)sharingAccount])
++ (ASIPithosObjectRequest *)writeObjectDataRequestWithPithos:(ASIPithos *)pithos
+ containerName:(NSString *)containerName
+ objectName:(NSString *)objectName
+ contentType:(NSString *)contentType
+ blockSize:(NSUInteger)blockSize
+ blockHash:(NSString *)blockHash
+ forFile:(NSString *)filePath
+ checkIfExists:(BOOL)ifExists
+ hashes:(NSArray **)hashes
+ sharingAccount:(NSString *)sharingAccount {
+ if (ifExists && ![self proceedIfObjectExistsAtPithos:pithos containerName:containerName objectName:objectName
+ sharingAccount:(NSString *)sharingAccount])
return nil;
if (*hashes == nil)
- *hashes = [HashMapHash calculateObjectHashMap:filePath withBlockHash:blockHash andBlockSize:blockSize];
+ *hashes = [HashMapHash objectHashMapStrings:filePath withBlockHash:blockHash andBlockSize:blockSize];
if (*hashes == nil)
return nil;
NSString *fileName = [filePath lastPathComponent];
if ([filePath hasSuffix:@"/"])
fileName = [fileName stringByAppendingString:@"/"];
NSUInteger bytes = [self bytesOfFile:filePath];
- ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
- objectName:objectName
- contentType:contentType
- contentEncoding:nil
- contentDisposition:nil
- manifest:nil
- sharing:nil
- isPublic:ASIPithosObjectRequestPublicIgnore
- metadata:nil
- blockSize:blockSize
- blockHash:blockHash
- hashes:*hashes
- bytes:bytes];
+ ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:objectName
+ contentType:contentType
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ blockSize:blockSize
+ blockHash:blockHash
+ hashes:*hashes
+ bytes:bytes];
if (sharingAccount)
- [objectRequest setRequestUserFromDefaultTo:sharingAccount];
+ [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
fileName, @"fileName",
[NSNumber numberWithUnsignedInteger:bytes], @"bytes",
return objectRequest;
}
-+ (NSIndexSet *)missingBlocksForHashes:(NSArray *)hashes withMissingHashesResponse:(NSString *)missingHashesResponse {
- NSArray *responseLines = [missingHashesResponse componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
++ (NSIndexSet *)missingBlocksForHashes:(NSArray *)hashes withMissingHashes:(NSArray *)missingHashes {
NSMutableIndexSet *missingBlocks = [NSMutableIndexSet indexSet];
- for (NSString *line in responseLines) {
- if (![line length])
+ for (NSString *missingHash in missingHashes) {
+ if (![missingHash length])
break;
- NSUInteger missingBlock = [hashes indexOfObject:line];
+ NSUInteger missingBlock = [hashes indexOfObject:missingHash];
if (missingBlock != NSNotFound)
[missingBlocks addIndex:missingBlock];
}
return missingBlocks;
}
-+ (ASIPithosContainerRequest *)updateContainerDataRequestWithContainerName:(NSString *)containerName
- blockSize:(NSUInteger)blockSize
- forFile:(NSString *)filePath
- hashes:(NSArray *)hashes
- missingHashesResponse:(NSString *)missingHashesResponse
- sharingAccount:(NSString *)sharingAccount {
- NSIndexSet *missingBlocks = [self missingBlocksForHashes:hashes withMissingHashesResponse:missingHashesResponse];
++ (ASIPithosContainerRequest *)updateContainerDataRequestWithPithos:(ASIPithos *)pithos
+ containerName:(NSString *)containerName
+ blockSize:(NSUInteger)blockSize
+ forFile:(NSString *)filePath
+ hashes:(NSArray *)hashes
+ missingHashes:(NSArray *)missingHashes
+ sharingAccount:(NSString *)sharingAccount {
+ NSIndexSet *missingBlocks = [self missingBlocksForHashes:hashes withMissingHashes:missingHashes];
- NSFileManager *defaultManager = [NSFileManager defaultManager];
+ NSFileManager *fileManager = [NSFileManager defaultManager];
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
// http://cocoawithlove.com/2009/07/temporary-files-and-folders-in-cocoa.html
char *tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1);
strcpy(tempFileNameCString, tempFileTemplateCString);
int fileDescriptor = mkstemp(tempFileNameCString);
- NSString *tempFilePath = [defaultManager stringWithFileSystemRepresentation:tempFileNameCString length:strlen(tempFileNameCString)];
+ NSString *tempFilePath = [fileManager stringWithFileSystemRepresentation:tempFileNameCString length:strlen(tempFileNameCString)];
+ free(tempFileNameCString);
if (fileDescriptor == -1) {
- NSAlert *alert = [[[NSAlert alloc] init] autorelease];
- [alert setMessageText:@"Create Temporary File Error"];
- [alert setInformativeText:[NSString stringWithFormat:@"Cannot create temporary file at '%@'", tempFilePath]];
- [alert addButtonWithTitle:@"OK"];
- [alert runModal];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:@"Create Temporary File Error"];
+ [alert setInformativeText:[NSString stringWithFormat:@"Cannot create temporary file at '%@'", tempFilePath]];
+ [alert addButtonWithTitle:@"OK"];
+ [alert runModal];
+ });
return nil;
}
- free(tempFileNameCString);
NSFileHandle *tempFileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fileDescriptor closeOnDealloc:YES];
[missingBlocks enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
[tempFileHandle closeFile];
[fileHandle closeFile];
- ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest updateContainerDataRequestWithContainerName:containerName
- policy:nil
- metadata:nil
- update:YES
- file:tempFilePath];
+ ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest updateContainerDataRequestWithPithos:pithos
+ containerName:containerName
+ policy:nil
+ metadata:nil
+ update:YES
+ file:tempFilePath];
if (sharingAccount)
- [containerRequest setRequestUserFromDefaultTo:sharingAccount];
+ [containerRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
return containerRequest;
}
-+ (ASIPithosContainerRequest *)updateContainerDataRequestWithContainerName:(NSString *)containerName
- blockSize:(NSUInteger)blockSize
- forFile:(NSString *)filePath
- missingBlockIndex:(NSUInteger)missingBlockIndex
- sharingAccount:(NSString *)sharingAccount {
++ (ASIPithosContainerRequest *)updateContainerDataRequestWithPithos:(ASIPithos *)pithos
+ containerName:(NSString *)containerName
+ blockSize:(NSUInteger)blockSize
+ forFile:(NSString *)filePath
+ missingBlockIndex:(NSUInteger)missingBlockIndex
+ sharingAccount:(NSString *)sharingAccount {
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
[fileHandle seekToFileOffset:(missingBlockIndex *blockSize)];
NSData *blockData = [fileHandle readDataOfLength:blockSize];
[fileHandle closeFile];
- ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest updateContainerDataRequestWithContainerName:containerName
- policy:nil
- metadata:nil
- update:YES
- data:blockData];
+ ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest updateContainerDataRequestWithPithos:pithos
+ containerName:containerName
+ policy:nil
+ metadata:nil
+ update:YES
+ data:blockData];
if (sharingAccount)
- [containerRequest setRequestUserFromDefaultTo:sharingAccount];
+ [containerRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
return containerRequest;
}
-+ (NSArray *)writeObjectDataRequestsWithContainerName:(NSString *)containerName
- objectName:(NSString *)objectName
- blockSize:(NSUInteger)blockSize
- blockHash:(NSString *)blockHash
- forDirectory:(NSString *)directoryPath
- checkIfExists:(BOOL)ifExists
- objectNames:(NSMutableArray **)objectNames
- contentTypes:(NSMutableArray **)contentTypes
- filePaths:(NSMutableArray **)filePaths
- hashesArrays:(NSMutableArray **)hashesArrays
- directoryObjectRequests:(NSMutableArray **) directoryObjectRequests
- sharingAccount:(NSString *)sharingAccount {
- if (ifExists && ![self proceedIfObjectExistsAtContainerName:containerName objectName:objectName sharingAccount:sharingAccount])
++ (NSArray *)writeObjectDataRequestsWithPithos:(ASIPithos *)pithos
+ containerName:(NSString *)containerName
+ objectName:(NSString *)objectName
+ blockSize:(NSUInteger)blockSize
+ blockHash:(NSString *)blockHash
+ forDirectory:(NSString *)directoryPath
+ checkIfExists:(BOOL)ifExists
+ objectNames:(NSMutableArray **)objectNames
+ contentTypes:(NSMutableArray **)contentTypes
+ filePaths:(NSMutableArray **)filePaths
+ hashesArrays:(NSMutableArray **)hashesArrays
+ directoryObjectRequests:(NSMutableArray **) directoryObjectRequests
+ sharingAccount:(NSString *)sharingAccount {
+ if (ifExists && ![self proceedIfObjectExistsAtPithos:pithos containerName:containerName objectName:objectName
+ sharingAccount:sharingAccount])
return nil;
- NSFileManager *defaultManager = [NSFileManager defaultManager];
+ NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
- NSArray *subPaths = [defaultManager subpathsOfDirectoryAtPath:directoryPath error:&error];
+ NSArray *subPaths = [fileManager subpathsOfDirectoryAtPath:directoryPath error:&error];
if (error) {
- NSAlert *alert = [[[NSAlert alloc] init] autorelease];
- [alert setMessageText:@"Directory Read Error"];
- [alert setInformativeText:[NSString stringWithFormat:@"Cannot read contents of directory '%@': %@",
- [directoryPath lastPathComponent], error]];
- [alert addButtonWithTitle:@"OK"];
- [alert runModal];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:@"Directory Read Error"];
+ [alert setInformativeText:[NSString stringWithFormat:@"Cannot read contents of directory '%@': %@",
+ [directoryPath lastPathComponent], [error localizedDescription]]];
+ [alert addButtonWithTitle:@"OK"];
+ [alert runModal];
+ });
return nil;
}
*directoryObjectRequests = [NSMutableArray arrayWithCapacity:[subPaths count]];
- ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
- objectName:objectName
- eTag:nil
- contentType:@"application/directory"
- contentEncoding:nil
- contentDisposition:nil
- manifest:nil
- sharing:nil
- isPublic:ASIPithosObjectRequestPublicIgnore
- metadata:nil
- data:[NSData data]];
+ ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:objectName
+ eTag:nil
+ contentType:@"application/directory"
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ data:[NSData data]];
if (sharingAccount)
- [objectRequest setRequestUserFromDefaultTo:sharingAccount];
+ [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
+ objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ [directoryPath lastPathComponent], @"fileName",
+ nil];
[*directoryObjectRequests addObject:objectRequest];
NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:[subPaths count]];
NSString *fileName;
for (NSString *objectNameSuffix in subPaths) {
filePath = [directoryPath stringByAppendingPathComponent:objectNameSuffix];
- if ([defaultManager fileExistsAtPath:filePath isDirectory:&isDirectory]) {
+ if ([fileManager fileExistsAtPath:filePath isDirectory:&isDirectory]) {
if (!isDirectory) {
- hashes = [HashMapHash calculateObjectHashMap:filePath withBlockHash:blockHash andBlockSize:blockSize];
+ hashes = [HashMapHash objectHashMapStrings:filePath withBlockHash:blockHash andBlockSize:blockSize];
if (hashes) {
- subObjectName = [objectName stringByAppendingPathComponent:objectNameSuffix];
+ subObjectName = [[objectName stringByAppendingPathComponent:objectNameSuffix] precomposedStringWithCanonicalMapping];
fileName = [filePath lastPathComponent];
if ([filePath hasSuffix:@"/"])
fileName = [fileName stringByAppendingString:@"/"];
contentType = [self contentTypeOfFile:filePath error:&error];
if (contentType == nil)
contentType = @"application/octet-stream";
+ #if DEBUG_PITHOS
if (error)
- NSLog(@"contentType detection error: %@", error);
- objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
- objectName:subObjectName
- contentType:contentType
- contentEncoding:nil
- contentDisposition:nil
- manifest:nil
- sharing:nil
- isPublic:ASIPithosObjectRequestPublicIgnore
- metadata:nil
- blockSize:blockSize
- blockHash:blockHash
- hashes:hashes
- bytes:bytes];
+ DLog(@"contentType detection error: %@", error);
+ #endif
+ objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:subObjectName
+ contentType:contentType
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ blockSize:blockSize
+ blockHash:blockHash
+ hashes:hashes
+ bytes:bytes];
if (sharingAccount)
- [objectRequest setRequestUserFromDefaultTo:sharingAccount];
+ [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
fileName, @"fileName",
[NSNumber numberWithUnsignedInteger:bytes], @"bytes",
}
} else {
- subObjectName = [objectName stringByAppendingPathComponent:objectNameSuffix];
- objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
- objectName:subObjectName
- eTag:nil
- contentType:@"application/directory"
- contentEncoding:nil
- contentDisposition:nil
- manifest:nil
- sharing:nil
- isPublic:ASIPithosObjectRequestPublicIgnore
- metadata:nil
- data:[NSData data]];
+ subObjectName = [[objectName stringByAppendingPathComponent:objectNameSuffix] precomposedStringWithCanonicalMapping];
+ fileName = [filePath lastPathComponent];
+ objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:subObjectName
+ eTag:nil
+ contentType:@"application/directory"
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ data:[NSData data]];
if (sharingAccount)
- [objectRequest setRequestUserFromDefaultTo:sharingAccount];
+ [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
+ objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ fileName, @"fileName",
+ nil];
[*directoryObjectRequests addObject:objectRequest];
}
}
#pragma mark -
#pragma mark Delete
-+ (NSArray *)deleteObjectRequestsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName {
- NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil sharingAccount:nil];
++ (NSArray *)deleteObjectRequestsForSubdirWithPithos:(ASIPithos *)pithos
+ containerName:(NSString *)containerName
+ objectName:(NSString *)objectName {
+ NSArray *objects = [self objectsForSubdirWithPithos:pithos containerName:containerName objectName:objectName delimiter:nil
+ sharingAccount:nil];
if (objects == nil)
return nil;
NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
-
- if (![objectName hasSuffix:@"/"])
- [objectRequests addObject:[ASIPithosObjectRequest deleteObjectRequestWithContainerName:containerName objectName:objectName]];
+ ASIPithosObjectRequest *objectRequest;
+ if (![objectName hasSuffix:@"/"]) {
+ objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos containerName:containerName objectName:objectName];
+ objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ [objectName lastPathComponent], @"fileName",
+ nil];
+ [objectRequests addObject:objectRequest];
+ }
+ NSString *fileName;
for (ASIPithosObject *object in objects) {
- [objectRequests addObject:[ASIPithosObjectRequest deleteObjectRequestWithContainerName:containerName objectName:object.name]];
+ fileName = [object.name lastPathComponent];
+ if ([object.name hasSuffix:@"/"])
+ fileName = [fileName stringByAppendingString:@"/"];
+ objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos containerName:containerName objectName:object.name];
+ objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ fileName, @"fileName",
+ nil];
+ [objectRequests addObject:objectRequest];
}
if ([objectRequests count] == 0)
#pragma mark -
#pragma mark Copy
-+ (ASIPithosObjectRequest *)copyObjectRequestWithContainerName:(NSString *)containerName
- objectName:(NSString *)objectName
- destinationContainerName:(NSString *)destinationContainerName
- destinationObjectName:(NSString *)destinationObjectName
- checkIfExists:(BOOL)ifExists
- sharingAccount:(NSString *)sharingAccount {
- if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
++ (ASIPithosObjectRequest *)cpyObjectRequestWithPithos:(ASIPithos *)pithos
+ containerName:(NSString *)containerName
+ objectName:(NSString *)objectName
+ destinationContainerName:(NSString *)destinationContainerName
+ destinationObjectName:(NSString *)destinationObjectName
+ checkIfExists:(BOOL)ifExists
+ sharingAccount:(NSString *)sharingAccount {
+ if (ifExists && ![self proceedIfObjectExistsAtPithos:pithos containerName:destinationContainerName objectName:destinationObjectName
+ sharingAccount:nil])
return nil;
- ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
- objectName:objectName
- contentType:nil
- contentEncoding:nil
- contentDisposition:nil
- manifest:nil
- sharing:nil
- isPublic:ASIPithosObjectRequestPublicIgnore
- metadata:nil
- destinationContainerName:destinationContainerName
- destinationObjectName:destinationObjectName
- destinationAccount:nil
- sourceVersion:nil];
+ ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest cpyObjectDataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:objectName
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:destinationObjectName
+ destinationAccount:nil
+ sourceVersion:nil];
objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
containerName, @"sourceContainerName",
objectName, @"sourceObjectName",
destinationObjectName, @"destinationObjectName",
nil];
if (sharingAccount)
- [objectRequest setRequestUserFromDefaultTo:sharingAccount];
+ [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
return objectRequest;
}
-+ (NSArray *)copyObjectRequestsForSubdirWithContainerName:(NSString *)containerName
- objectName:(NSString *)objectName
- destinationContainerName:(NSString *)destinationContainerName
- destinationObjectName:(NSString *)destinationObjectName
- checkIfExists:(BOOL)ifExists
- sharingAccount:(NSString *)sharingAccount {
- if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
++ (NSArray *)cpyObjectRequestsForSubdirWithPithos:(ASIPithos *)pithos
+ containerName:(NSString *)containerName
+ objectName:(NSString *)objectName
+ destinationContainerName:(NSString *)destinationContainerName
+ destinationObjectName:(NSString *)destinationObjectName
+ checkIfExists:(BOOL)ifExists
+ sharingAccount:(NSString *)sharingAccount {
+ if (ifExists && ![self proceedIfObjectExistsAtPithos:pithos containerName:destinationContainerName objectName:destinationObjectName
+ sharingAccount:nil])
return nil;
- NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName
- delimiter:nil sharingAccount:sharingAccount];
+ NSArray *objects = [self objectsForSubdirWithPithos:pithos containerName:containerName objectName:objectName
+ delimiter:nil sharingAccount:sharingAccount];
if (objects == nil)
return nil;
ASIPithosObjectRequest *objectRequest;
if ([objectName isEqualToString:destinationObjectName]) {
if (![objectName hasSuffix:@"/"]) {
- objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
- objectName:objectName
- contentType:nil
- contentEncoding:nil
- contentDisposition:nil
- manifest:nil
- sharing:nil
- isPublic:ASIPithosObjectRequestPublicIgnore
- metadata:nil
- destinationContainerName:destinationContainerName
- destinationObjectName:objectName
- destinationAccount:nil
- sourceVersion:nil];
+ objectRequest = [ASIPithosObjectRequest cpyObjectDataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:objectName
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:objectName
+ destinationAccount:nil
+ sourceVersion:nil];
objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
containerName, @"sourceContainerName",
objectName, @"sourceObjectName",
objectName, @"destinationObjectName",
nil];
if (sharingAccount)
- [objectRequest setRequestUserFromDefaultTo:sharingAccount];
+ [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
[objectRequests addObject:objectRequest];
}
for (ASIPithosObject *object in objects) {
- objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
- objectName:object.name
- contentType:nil
- contentEncoding:nil
- contentDisposition:nil
- manifest:nil
- sharing:nil
- isPublic:ASIPithosObjectRequestPublicIgnore
- metadata:nil
- destinationContainerName:destinationContainerName
- destinationObjectName:object.name
- destinationAccount:nil
- sourceVersion:nil];
+ objectRequest = [ASIPithosObjectRequest cpyObjectDataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:object.name
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:object.name
+ destinationAccount:nil
+ sourceVersion:nil];
objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
containerName, @"sourceContainerName",
object.name, @"sourceObjectName",
object.name, @"destinationObjectName",
nil];
if (sharingAccount)
- [objectRequest setRequestUserFromDefaultTo:sharingAccount];
+ [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
[objectRequests addObject:objectRequest];
}
} else {
if (![objectName hasSuffix:@"/"]) {
- objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
- objectName:objectName
- contentType:nil
- contentEncoding:nil
- contentDisposition:nil
- manifest:nil
- sharing:nil
- isPublic:ASIPithosObjectRequestPublicIgnore
- metadata:nil
- destinationContainerName:destinationContainerName
- destinationObjectName:destinationObjectName
- destinationAccount:nil
- sourceVersion:nil];
+ objectRequest = [ASIPithosObjectRequest cpyObjectDataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:objectName
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:destinationObjectName
+ destinationAccount:nil
+ sourceVersion:nil];
objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
containerName, @"sourceContainerName",
objectName, @"sourceObjectName",
destinationObjectName, @"destinationObjectName",
nil];
if (sharingAccount)
- [objectRequest setRequestUserFromDefaultTo:sharingAccount];
+ [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
[objectRequests addObject:objectRequest];
}
NSRange prefixRange = NSMakeRange(0, [objectName length]);
withString:destinationObjectName
options:NSAnchoredSearch
range:prefixRange];
- objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
- objectName:object.name
- contentType:nil
- contentEncoding:nil
- contentDisposition:nil
- manifest:nil
- sharing:nil
- isPublic:ASIPithosObjectRequestPublicIgnore
- metadata:nil
- destinationContainerName:destinationContainerName
- destinationObjectName:newObjectName
- destinationAccount:nil
- sourceVersion:nil];
+ objectRequest = [ASIPithosObjectRequest cpyObjectDataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:object.name
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:newObjectName
+ destinationAccount:nil
+ sourceVersion:nil];
objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
containerName, @"sourceContainerName",
object.name, @"sourceObjectName",
newObjectName, @"destinationObjectName",
nil];
if (sharingAccount)
- [objectRequest setRequestUserFromDefaultTo:sharingAccount];
+ [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
[objectRequests addObject:objectRequest];
}
}
#pragma mark -
#pragma mark Move
-+ (ASIPithosObjectRequest *)moveObjectRequestWithContainerName:(NSString *)containerName
- objectName:(NSString *)objectName
- destinationContainerName:(NSString *)destinationContainerName
- destinationObjectName:(NSString *)destinationObjectName
- checkIfExists:(BOOL)ifExists {
- if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
++ (ASIPithosObjectRequest *)moveObjectRequestWithPithos:(ASIPithos *)pithos
+ containerName:(NSString *)containerName
+ objectName:(NSString *)objectName
+ destinationContainerName:(NSString *)destinationContainerName
+ destinationObjectName:(NSString *)destinationObjectName
+ checkIfExists:(BOOL)ifExists {
+ if (ifExists && ![self proceedIfObjectExistsAtPithos:pithos containerName:destinationContainerName objectName:destinationObjectName
+ sharingAccount:nil])
return nil;
- ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
- objectName:objectName
- contentType:nil
- contentEncoding:nil
- contentDisposition:nil
- manifest:nil
- sharing:nil
- isPublic:ASIPithosObjectRequestPublicIgnore
- metadata:nil
- destinationContainerName:destinationContainerName
- destinationObjectName:destinationObjectName
- destinationAccount:nil];
+ ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:objectName
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:destinationObjectName
+ destinationAccount:nil];
objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
containerName, @"sourceContainerName",
objectName, @"sourceObjectName",
return objectRequest;
}
-+ (NSArray *)moveObjectRequestsForSubdirWithContainerName:(NSString *)containerName
- objectName:(NSString *)objectName
- destinationContainerName:(NSString *)destinationContainerName
- destinationObjectName:(NSString *)destinationObjectName
- checkIfExists:(BOOL)ifExists {
- if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
++ (NSArray *)moveObjectRequestsForSubdirWithPithos:(ASIPithos *)pithos
+ containerName:(NSString *)containerName
+ objectName:(NSString *)objectName
+ destinationContainerName:(NSString *)destinationContainerName
+ destinationObjectName:(NSString *)destinationObjectName
+ checkIfExists:(BOOL)ifExists {
+ if (ifExists && ![self proceedIfObjectExistsAtPithos:pithos containerName:destinationContainerName objectName:destinationObjectName
+ sharingAccount:nil])
return nil;
- NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil sharingAccount:nil];
+ NSArray *objects = [self objectsForSubdirWithPithos:pithos containerName:containerName objectName:objectName
+ delimiter:nil sharingAccount:nil];
if (objects == nil)
return nil;
NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
if ([objectName isEqualToString:destinationObjectName]) {
if (![objectName hasSuffix:@"/"]) {
- objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
- objectName:objectName
- contentType:nil
- contentEncoding:nil
- contentDisposition:nil
- manifest:nil
- sharing:nil
- isPublic:ASIPithosObjectRequestPublicIgnore
- metadata:nil
- destinationContainerName:destinationContainerName
- destinationObjectName:objectName
- destinationAccount:nil];
+ objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:objectName
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:objectName
+ destinationAccount:nil];
objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
containerName, @"sourceContainerName",
objectName, @"sourceObjectName",
[objectRequests addObject:objectRequest];
}
for (ASIPithosObject *object in objects) {
- objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
- objectName:object.name
- contentType:nil
- contentEncoding:nil
- contentDisposition:nil
- manifest:nil
- sharing:nil
- isPublic:ASIPithosObjectRequestPublicIgnore
- metadata:nil
- destinationContainerName:destinationContainerName
- destinationObjectName:object.name
- destinationAccount:nil];
+ objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:object.name
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:object.name
+ destinationAccount:nil];
objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
containerName, @"sourceContainerName",
object.name, @"sourceObjectName",
}
} else {
if (![objectName hasSuffix:@"/"]) {
- objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
- objectName:objectName
- contentType:nil
- contentEncoding:nil
- contentDisposition:nil
- manifest:nil
- sharing:nil
- isPublic:ASIPithosObjectRequestPublicIgnore
- metadata:nil
- destinationContainerName:destinationContainerName
- destinationObjectName:destinationObjectName
- destinationAccount:nil];
+ objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:objectName
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:destinationObjectName
+ destinationAccount:nil];
objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
containerName, @"sourceContainerName",
objectName, @"sourceObjectName",
withString:destinationObjectName
options:NSAnchoredSearch
range:prefixRange];
- objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
- objectName:object.name
- contentType:nil
- contentEncoding:nil
- contentDisposition:nil
- manifest:nil
- sharing:nil
- isPublic:ASIPithosObjectRequestPublicIgnore
- metadata:nil
- destinationContainerName:destinationContainerName
- destinationObjectName:newObjectName
- destinationAccount:nil];
+ objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:object.name
+ contentType:nil
+ contentEncoding:nil
+ contentDisposition:nil
+ manifest:nil
+ sharing:nil
+ isPublic:ASIPithosObjectRequestPublicIgnore
+ metadata:nil
+ destinationContainerName:destinationContainerName
+ destinationObjectName:newObjectName
+ destinationAccount:nil];
objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
containerName, @"sourceContainerName",
object.name, @"sourceObjectName",
// Size of the file in bytes
+ (NSUInteger)bytesOfFile:(NSString *)filePath {
- NSFileManager *defaultManager = [NSFileManager defaultManager];
- NSDictionary *attributes = [defaultManager attributesOfItemAtPath:filePath error:nil];
- return [[attributes objectForKey:NSFileSize] intValue];
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ NSDictionary *attributes = [fileManager attributesOfItemAtPath:filePath error:nil];
+ return [[attributes objectForKey:NSFileSize] unsignedIntegerValue];
}
// Content type of the file or nil if it cannot be determined
+ (NSString *)contentTypeOfFile:(NSString *)filePath error:(NSError **)error {
- NSURLResponse *response = nil;
- [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath]
- cachePolicy:NSURLCacheStorageNotAllowed
- timeoutInterval:.1]
- returningResponse:&response
- error:error];
- return [response MIMEType];
+ // Based on http://www.ddeville.me/2011/12/mime-to-UTI-cocoa/
+ // and Apple example ImageBrowserViewAppearance/ImageBrowserController.m
+ LSItemInfoRecord info;
+ CFStringRef uti = NULL;
+ CFURLRef url = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)filePath, kCFURLPOSIXPathStyle, FALSE);
+ if (LSCopyItemInfoForURL(url, kLSRequestExtension | kLSRequestTypeCreator, &info) == noErr) {
+ // Obtain the UTI using the file information.
+ // If there is a file extension, get the UTI.
+ if (info.extension != NULL) {
+ uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, info.extension, kUTTypeData);
+ CFRelease(info.extension);
+ }
+ // No UTI yet
+ if (uti == NULL) {
+ // If there is an OSType, get the UTI.
+ CFStringRef typeString = UTCreateStringForOSType(info.filetype);
+ if ( typeString != NULL) {
+ uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, typeString, kUTTypeData);
+ CFRelease(typeString);
+ }
+ }
+ if (uti != NULL) {
+ CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType);
+ CFRelease(uti);
+ CFRelease(url);
+ return (__bridge_transfer NSString *)MIMEType;
+ }
+ }
+ CFRelease(url);
+ return nil;
+}
+
+// Creates a directory if it doesn't exist and returns if successful
++ (BOOL)safeCreateDirectory:(NSString *)directoryPath error:(NSError **)error {
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ BOOL isDirectory;
+ BOOL fileExists = [fileManager fileExistsAtPath:directoryPath isDirectory:&isDirectory];
+ if (fileExists)
+ return isDirectory;
+ if (![fileManager createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:error] || *error)
+ return NO;
+ return YES;
+}
+
+// Removes contents of a directory and the directory itself if selected
++ (void)removeContentsAtPath:(NSString *)dirPath andDirectory:(BOOL)removeDirectory {
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ NSError *error = nil;
+ BOOL isDirectory;
+ if (![fileManager fileExistsAtPath:dirPath isDirectory:&isDirectory])
+ return;
+ if (isDirectory) {
+ for (NSString *subPath in [fileManager contentsOfDirectoryAtPath:dirPath error:&error]) {
+ if (error) {
+ [self fileActionFailedAlertWithTitle:@"Directory Contents Error"
+ message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", dirPath]
+ error:error];
+ break;
+ }
+ NSString *subFilePath = [dirPath stringByAppendingPathComponent:subPath];
+ if (![fileManager removeItemAtPath:subFilePath error:&error] || error) {
+ [self fileActionFailedAlertWithTitle:@"Remove File Error"
+ message:[NSString stringWithFormat:@"Cannot remove file at '%@'", subFilePath]
+ error:error];
+ }
+ error = nil;
+ }
+ if (removeDirectory && (![fileManager removeItemAtPath:dirPath error:&error] || error)) {
+ [self fileActionFailedAlertWithTitle:@"Remove Directory Error"
+ message:[NSString stringWithFormat:@"Cannot remove directory at '%@'", dirPath]
+ error:error];
+ }
+ } else if (![fileManager removeItemAtPath:dirPath error:&error] || error) {
+ [self fileActionFailedAlertWithTitle:@"Remove File Error"
+ message:[NSString stringWithFormat:@"Cannot remove file at '%@'", dirPath]
+ error:error];
+ }
+}
+
+// Removes contents of a directory
++ (void)removeContentsAtPath:(NSString *)dirPath {
+ [self removeContentsAtPath:dirPath andDirectory:NO];
+}
+
+// Returns if an object is a directory based on its content type
++ (BOOL)isContentTypeDirectory:(NSString *)contentType {
+ return ([contentType isEqualToString:@"application/directory"] ||
+ [contentType hasPrefix:@"application/directory;"] ||
+ [contentType isEqualToString:@"application/folder"] ||
+ [contentType hasPrefix:@"application/folder;"]);
}
// Returns if an object exists at the given container/object path and if this object is an application/directory
// If an error occured an alert is shown and it is returned so the caller won't proceed
-+ (BOOL)objectExistsAtContainerName:(NSString *)containerName objectName:(NSString *)objectName
- error:(NSError **)error isDirectory:(BOOL *)isDirectory
- sharingAccount:(NSString *)sharingAccount {
- ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectMetadataRequestWithContainerName:containerName
- objectName:objectName];
++ (BOOL)objectExistsAtPithos:(ASIPithos *)pithos containerName:(NSString *)containerName objectName:(NSString *)objectName
+ error:(NSError **)error isDirectory:(BOOL *)isDirectory sharingAccount:(NSString *)sharingAccount {
+ ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectMetadataRequestWithPithos:pithos
+ containerName:containerName
+ objectName:objectName];
if (sharingAccount)
- [objectRequest setRequestUserFromDefaultTo:sharingAccount];
- [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
- while (![objectRequest isFinished]) {
- sleep(1);
- }
- *error = [objectRequest error];
- if (*error) {
- [self httpRequestErrorAlertWithRequest:objectRequest];
- return NO;
- } else if (objectRequest.responseStatusCode == 200) {
- *isDirectory = [[objectRequest contentType] isEqualToString:@"application/directory"];
- return YES;
+ [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
+ [self startAndWaitForRequest:objectRequest];
+ if (error != NULL) {
+ *error = [objectRequest error];
+ if (*error) {
+ [self httpRequestErrorAlertWithRequest:objectRequest];
+ return NO;
+ } else if (objectRequest.responseStatusCode == 200) {
+ *isDirectory = [self isContentTypeDirectory:[objectRequest contentType]];
+ return YES;
+ }
}
return NO;
}
-// Returns if the called should proceed, after an interactive check if an object exists
+// Returns if the caller should proceed, after an interactive check if an object exists
// at the given container/object path is performed
-+ (BOOL)proceedIfObjectExistsAtContainerName:(NSString *)containerName objectName:(NSString *)objectName
- sharingAccount:(NSString *)sharingAccount {
++ (BOOL)proceedIfObjectExistsAtPithos:(ASIPithos *)pithos containerName:(NSString *)containerName objectName:(NSString *)objectName
+ sharingAccount:(NSString *)sharingAccount {
NSError *error = nil;
BOOL isDirectory;
- BOOL objectExists = [self objectExistsAtContainerName:containerName
- objectName:objectName
- error:&error
- isDirectory:&isDirectory
- sharingAccount:sharingAccount];
+ BOOL objectExists = [self objectExistsAtPithos:pithos containerName:containerName objectName:objectName
+ error:&error isDirectory:&isDirectory sharingAccount:sharingAccount];
if (error) {
return NO;
} else if (objectExists) {
- NSAlert *alert = [[[NSAlert alloc] init] autorelease];
- if (isDirectory) {
- [alert setMessageText:@"Directory Exists"];
- if (sharingAccount)
- [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
- else
- [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
- } else {
- [alert setMessageText:@"Object Exists"];
- if (sharingAccount)
- [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
- else
- [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
- }
- [alert addButtonWithTitle:@"OK"];
- [alert addButtonWithTitle:@"Cancel"];
- NSInteger choice = [alert runModal];
+ __block NSInteger choice;
+ dispatch_sync(dispatch_get_main_queue(), ^{
+ NSAlert *alert = [[NSAlert alloc] init];
+ if (isDirectory) {
+ [alert setMessageText:@"Directory Exists"];
+ if (sharingAccount)
+ [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
+ else
+ [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
+ } else {
+ [alert setMessageText:@"Object Exists"];
+ if (sharingAccount)
+ [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
+ else
+ [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
+ }
+ [alert addButtonWithTitle:@"OK"];
+ [alert addButtonWithTitle:@"Cancel"];
+ choice = [alert runModal];
+ });
if (choice == NSAlertSecondButtonReturn)
return NO;
}
return YES;
}
-
// List of objects at the given container/object path, with prefix and or delimiter
-+ (NSArray *)objectsWithContainerName:(NSString *)containerName objectNamePrefix:(NSString *)objectNamePrefix
- delimiter:(NSString *)delimiter sharingAccount:(NSString *)sharingAccount {
++ (NSArray *)objectsWithPithos:(ASIPithos *)pithos containerName:(NSString *)containerName objectNamePrefix:(NSString *)objectNamePrefix
+ delimiter:(NSString *)delimiter sharingAccount:(NSString *)sharingAccount {
NSMutableArray *objects = [NSMutableArray array];
NSString *marker = nil;
do {
- ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest listObjectsRequestWithContainerName:containerName
- limit:0
- marker:marker
- prefix:objectNamePrefix
- delimiter:delimiter
- path:nil
- meta:nil
- shared:NO
- until:nil];
+ ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest listObjectsRequestWithPithos:pithos
+ containerName:containerName
+ limit:0
+ marker:marker
+ prefix:objectNamePrefix
+ delimiter:delimiter
+ path:nil
+ meta:nil
+ shared:NO
+ until:nil];
if (sharingAccount)
- [containerRequest setRequestUserFromDefaultTo:sharingAccount];
- [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
- while (![containerRequest isFinished]) {
- sleep(1);
- }
+ [containerRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
+ [self startAndWaitForRequest:containerRequest];
if ([containerRequest error]) {
[self httpRequestErrorAlertWithRequest:containerRequest];
return nil;
// List of objects at the given container/object path, that may be a subdir or an application/directory,
// with prefix and or delimiter
-+ (NSArray *)objectsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName
- delimiter:(NSString *)delimiter sharingAccount:(NSString *)sharingAccount {
++ (NSArray *)objectsForSubdirWithPithos:(ASIPithos *)pithos containerName:(NSString *)containerName objectName:(NSString *)objectName
+ delimiter:(NSString *)delimiter sharingAccount:(NSString *)sharingAccount {
NSString *subdirNamePrefix = [NSString stringWithString:objectName];
if (![subdirNamePrefix hasSuffix:@"/"])
subdirNamePrefix = [subdirNamePrefix stringByAppendingString:@"/"];
- return [self objectsWithContainerName:containerName objectNamePrefix:subdirNamePrefix
- delimiter:delimiter sharingAccount:sharingAccount];
+ return [self objectsWithPithos:pithos containerName:containerName objectNamePrefix:subdirNamePrefix
+ delimiter:delimiter sharingAccount:sharingAccount];
}
// A safe object name at the given container/object path
// The original name has " %d" appended to it before any ".*" suffix, for the first integer that produces a name that is free to use
// If the original name hasn't got a "." but has a "/" suffix, then it is replaced with " %d/" instead
// Subdirs are taken into consideration
-+ (NSString *)safeObjectNameForContainerName:(NSString *)containerName objectName:(NSString *)objectName {
++ (NSString *)safeObjectNameForPithos:(ASIPithos *)pithos containerName:(NSString *)containerName objectName:(NSString *)objectName {
NSString *objectNamePrefix;
NSString *objectNameExtraSuffix;
NSRange lastDotRange = [objectName rangeOfString:@"." options:NSBackwardsSearch];
objectNameExtraSuffix = [objectName substringFromIndex:lastDotRange.location];
} else if ([objectName hasSuffix:@"/"]) {
objectNamePrefix = [objectName substringToIndex:([objectName length] - 1)];
- objectNameExtraSuffix = [NSString stringWithString:@"/"];
+ objectNameExtraSuffix = @"/";
} else {
objectNamePrefix = [NSString stringWithString:objectName];
objectNameExtraSuffix = [NSString string];
}
- NSArray *objects = [self objectsWithContainerName:containerName
- objectNamePrefix:[objectNamePrefix stringByDeletingLastPathComponent]
- delimiter:@"/"
- sharingAccount:nil];
+ NSArray *objects = [self objectsWithPithos:pithos containerName:containerName
+ objectNamePrefix:[objectNamePrefix stringByDeletingLastPathComponent]
+ delimiter:@"/" sharingAccount:nil];
if (objects == nil)
return nil;
if ([objects count] == 0)
// The original name has " %d" appended to it, for the first integer that produces a name that is free to use
// If the original name has a "/" suffix, then it is replaced with " %d/" instead
// Subdirs are taken into consideration
-+ (NSString *)safeSubdirNameForContainerName:(NSString *)containerName subdirName:(NSString *)subdirName {
++ (NSString *)safeSubdirNameForPithos:(ASIPithos *)pithos containerName:(NSString *)containerName subdirName:(NSString *)subdirName {
NSString *subdirNamePrefix;
NSString *subdirNameExtraSuffix;
if ([subdirName hasSuffix:@"/"]) {
subdirNamePrefix = [subdirName substringToIndex:([subdirName length] - 1)];
- subdirNameExtraSuffix = [NSString stringWithString:@"/"];
+ subdirNameExtraSuffix = @"/";
} else {
subdirNamePrefix = [NSString stringWithString:subdirName];
subdirNameExtraSuffix = [NSString string];
}
- NSArray *objects = [self objectsWithContainerName:containerName
- objectNamePrefix:[subdirNamePrefix stringByDeletingLastPathComponent]
- delimiter:@"/"
- sharingAccount:nil];
+ NSArray *objects = [self objectsWithPithos:pithos containerName:containerName
+ objectNamePrefix:[subdirNamePrefix stringByDeletingLastPathComponent]
+ delimiter:@"/" sharingAccount:nil];
if (objects == nil)
return nil;
if ([objects count] == 0)
#pragma mark -
#pragma mark Alerts
-+ (NSInteger)httpRequestErrorAlertWithRequest:(ASIPithosRequest *)request {
++ (void)httpRequestErrorAlertWithRequest:(ASIPithosRequest *)request {
+ if (request.responseStatusCode == 401) {
+ [self httpAuthenticationError];
+ return;
+ }
NSString *message = [NSString stringWithFormat:
@"HTTP request error: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@",
- [request error],
+ [[request error] localizedDescription],
request.requestMethod,
request.url,
[request requestHeaders],
[request responseHeaders],
[request responseString]];
- NSLog(@"%@", message);
- NSAlert *alert = [[[NSAlert alloc] init] autorelease];
- [alert setMessageText:@"HTTP Request Error"];
- [alert setInformativeText:message];
- [alert addButtonWithTitle:@"OK"];
- return [alert runModal];
+ DLog(@"%@", message);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ @autoreleasepool {
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:@"HTTP Request Error"];
+ [alert setInformativeText:message];
+ [alert addButtonWithTitle:@"OK"];
+ [alert runModal];
+ }
+ });
}
-+ (NSInteger)unexpectedResponseStatusAlertWithRequest:(ASIPithosRequest *)request {
++ (void)unexpectedResponseStatusAlertWithRequest:(ASIPithosRequest *)request {
+ if (request.responseStatusCode == 401) {
+ [self httpAuthenticationError];
+ return;
+ }
NSString *message = [NSString stringWithFormat:
@"Unexpected response status %d: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@",
request.responseStatusCode,
[request requestHeaders],
[request responseHeaders],
[request responseString]];
- NSLog(@"%@", message);
- NSAlert *alert = [[[NSAlert alloc] init] autorelease];
- [alert setMessageText:@"Unexpected Response Status"];
- [alert setInformativeText:message];
- [alert addButtonWithTitle:@"OK"];
- return [alert runModal];
+ DLog(@"%@", message);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ @autoreleasepool {
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:@"Unexpected Response Status"];
+ [alert setInformativeText:message];
+ [alert addButtonWithTitle:@"OK"];
+ [alert runModal];
+ }
+ });
+}
+
++ (void)httpAuthenticationError {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ @autoreleasepool {
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:@"Authentication Error"];
+ [alert setInformativeText:@"Authentication error, please check your token or login again"];
+ [alert addButtonWithTitle:@"OK"];
+ [alert runModal];
+ }
+ });
+}
+
++ (void)fileActionFailedAlertWithTitle:(NSString *)title message:(NSString *)message error:(NSError *)error {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ @autoreleasepool {
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:title];
+ if (error)
+ [alert setInformativeText:[NSString stringWithFormat:@"%@: %@", message, [error localizedDescription]]];
+ else
+ [alert setInformativeText:message];
+ [alert addButtonWithTitle:@"OK"];
+ [alert runModal];
+ }
+ });
}
#pragma mark -
-#pragma mark Prepare Requests
+#pragma mark Request Helper Methods
+ (ASIPithosRequest *)prepareRequest:(ASIPithosRequest *)request priority:(NSOperationQueuePriority)priority {
[request setTimeOutSeconds:60];
return [self prepareRequest:request priority:NSOperationQueuePriorityNormal];
}
++ (ASIPithosRequest *)copyRequest:(ASIPithosRequest *)request {
+ NSMutableDictionary *userInfo = (NSMutableDictionary *)request.userInfo;
+ request.userInfo = nil;
+ ASIPithosRequest *newRequest = [request copy];
+ newRequest.userInfo = userInfo;
+ return newRequest;
+}
+
++ (void)startAndWaitForRequest:(ASIPithosRequest *)request {
+ ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
+ [networkQueue go];
+ [networkQueue addOperations:[NSArray arrayWithObject:[self prepareRequest:request]] waitUntilFinished:YES];
+}
@end