From b58ff732dffb8ea6ee24d335863056f852dbd405 Mon Sep 17 00:00:00 2001 From: Miltiadis Vasilakis Date: Mon, 7 Jan 2013 16:32:19 +0200 Subject: [PATCH] Use @autoreleasepool directive --- pithos-apple-common | 2 +- pithos-macos.xcodeproj/project.pbxproj | 10 +- pithos-macos/AllowedToBoolTransformer.m | 2 +- pithos-macos/BytesExtendedSizeTransformer.m | 2 +- pithos-macos/BytesSizeTransformer.m | 2 +- pithos-macos/CountTransformer.m | 2 +- pithos-macos/DirPathFileURLTransformer.m | 2 +- pithos-macos/GroupMembersDictionaryTransformer.m | 2 +- pithos-macos/HashMapHash.m | 47 +- pithos-macos/LastCompletedSyncTransformer.m | 2 +- pithos-macos/LastModifiedDateTransformer.m | 2 +- pithos-macos/MetadataKeyTransformer.m | 2 +- pithos-macos/PithosAccount.m | 12 +- pithos-macos/PithosAccountNode.m | 290 +-- pithos-macos/PithosActivity.m | 4 +- pithos-macos/PithosBrowserController.m | 2092 ++++++++------- pithos-macos/PithosContainerNode.m | 384 +-- pithos-macos/PithosObjectNode.m | 120 +- pithos-macos/PithosObjectNodeInfoController.m | 2 +- pithos-macos/PithosSharingAccountsNode.m | 194 +- pithos-macos/PithosSubdirNode.m | 132 +- pithos-macos/PithosSyncDaemon.m | 2935 +++++++++++----------- pithos-macos/PithosUtilities.m | 70 +- pithos-macos/PolicyVersioningTransformer.m | 2 +- pithos-macos/PublicURLTransformer.m | 2 +- pithos-macos/SharingAccountBoolTransformer.m | 2 +- pithos-macos/SharingDictionaryTransformer.m | 2 +- pithos-macos/UsingSizeTransformer.m | 2 +- 28 files changed, 3104 insertions(+), 3218 deletions(-) diff --git a/pithos-apple-common b/pithos-apple-common index 7ac1bf1..cc176fe 160000 --- a/pithos-apple-common +++ b/pithos-apple-common @@ -1 +1 @@ -Subproject commit 7ac1bf11f8e6267e6146230f2dce93765542bf05 +Subproject commit cc176feb28e66fac71ccdcfffa96b86b2855de68 diff --git a/pithos-macos.xcodeproj/project.pbxproj b/pithos-macos.xcodeproj/project.pbxproj index 12d5551..3ba0ad6 100644 --- a/pithos-macos.xcodeproj/project.pbxproj +++ b/pithos-macos.xcodeproj/project.pbxproj @@ -89,7 +89,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 610DD2F113E6BB2000ED982F /* pithos-macos.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; name = "pithos-macos.app"; path = "Pithos+.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 610DD2F113E6BB2000ED982F /* Pithos+.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Pithos+.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 610DD2F513E6BB2000ED982F /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 610DD2F813E6BB2000ED982F /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 610DD2F913E6BB2000ED982F /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; @@ -274,7 +274,7 @@ 610DD2F213E6BB2000ED982F /* Products */ = { isa = PBXGroup; children = ( - 610DD2F113E6BB2000ED982F /* pithos-macos.app */, + 610DD2F113E6BB2000ED982F /* Pithos+.app */, ); name = Products; sourceTree = ""; @@ -595,7 +595,7 @@ ); name = "Pithos+"; productName = "pithos-macos"; - productReference = 610DD2F113E6BB2000ED982F /* pithos-macos.app */; + productReference = 610DD2F113E6BB2000ED982F /* Pithos+.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -604,7 +604,7 @@ 610DD2E813E6BB2000ED982F /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0430; + LastUpgradeCheck = 0450; ORGANIZATIONNAME = koomasi; }; buildConfigurationList = 610DD2EB13E6BB2000ED982F /* Build configuration list for PBXProject "pithos-macos" */; @@ -782,6 +782,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; @@ -797,6 +798,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_ENABLE_OBJC_EXCEPTIONS = YES; diff --git a/pithos-macos/AllowedToBoolTransformer.m b/pithos-macos/AllowedToBoolTransformer.m index 7cd0c8d..9a07ccd 100644 --- a/pithos-macos/AllowedToBoolTransformer.m +++ b/pithos-macos/AllowedToBoolTransformer.m @@ -54,7 +54,7 @@ } + (void)initialize { - [[NSValueTransformer class] setValueTransformer:[self new] forName:@"AllowedToBoolTransformer"]; + [[self class] setValueTransformer:[[self new] autorelease] forName:@"AllowedToBoolTransformer"]; } @end diff --git a/pithos-macos/BytesExtendedSizeTransformer.m b/pithos-macos/BytesExtendedSizeTransformer.m index 726e460..74717e4 100644 --- a/pithos-macos/BytesExtendedSizeTransformer.m +++ b/pithos-macos/BytesExtendedSizeTransformer.m @@ -88,7 +88,7 @@ } + (void)initialize { - [[NSValueTransformer class] setValueTransformer:[self new] forName:@"BytesExtendedSizeTransformer"]; + [[self class] setValueTransformer:[[self new] autorelease] forName:@"BytesExtendedSizeTransformer"]; } @end diff --git a/pithos-macos/BytesSizeTransformer.m b/pithos-macos/BytesSizeTransformer.m index 026b643..54093db 100644 --- a/pithos-macos/BytesSizeTransformer.m +++ b/pithos-macos/BytesSizeTransformer.m @@ -83,7 +83,7 @@ } + (void)initialize { - [[NSValueTransformer class] setValueTransformer:[self new] forName:@"BytesSizeTransformer"]; + [[self class] setValueTransformer:[[self new] autorelease] forName:@"BytesSizeTransformer"]; } @end diff --git a/pithos-macos/CountTransformer.m b/pithos-macos/CountTransformer.m index b8a0502..fbce720 100644 --- a/pithos-macos/CountTransformer.m +++ b/pithos-macos/CountTransformer.m @@ -54,7 +54,7 @@ } + (void)initialize { - [[NSValueTransformer class] setValueTransformer:[self new] forName:@"CountTransformer"]; + [[self class] setValueTransformer:[[self new] autorelease] forName:@"CountTransformer"]; } @end diff --git a/pithos-macos/DirPathFileURLTransformer.m b/pithos-macos/DirPathFileURLTransformer.m index b8e4cab..1186fde 100644 --- a/pithos-macos/DirPathFileURLTransformer.m +++ b/pithos-macos/DirPathFileURLTransformer.m @@ -60,7 +60,7 @@ } + (void)initialize { - [[NSValueTransformer class] setValueTransformer:[self new] forName:@"DirPathFileURLTransformer"]; + [[self class] setValueTransformer:[[self new] autorelease] forName:@"DirPathFileURLTransformer"]; } @end diff --git a/pithos-macos/GroupMembersDictionaryTransformer.m b/pithos-macos/GroupMembersDictionaryTransformer.m index 5eba1a1..70b7aab 100644 --- a/pithos-macos/GroupMembersDictionaryTransformer.m +++ b/pithos-macos/GroupMembersDictionaryTransformer.m @@ -71,7 +71,7 @@ } + (void)initialize { - [[NSValueTransformer class] setValueTransformer:[self new] forName:@"GroupMembersDictionaryTransformer"]; + [[self class] setValueTransformer:[[self new] autorelease] forName:@"GroupMembersDictionaryTransformer"]; } @end diff --git a/pithos-macos/HashMapHash.m b/pithos-macos/HashMapHash.m index 6f2bd18..0116eb8 100644 --- a/pithos-macos/HashMapHash.m +++ b/pithos-macos/HashMapHash.m @@ -101,33 +101,32 @@ NSUInteger fileSize = [attributes fileSize]; NSMutableArray *hashes = [NSMutableArray arrayWithCapacity:(fileSize/blockSize + 1)]; - BOOL done; + BOOL done = NO; while (!done) { - NSAutoreleasePool * pool = [NSAutoreleasePool new]; - NSData *data = [fileHandle readDataOfLength:blockSize]; - done = ([data length] == 0); - - if (!done) { - // Strip trailing slashes. - const char *bytes = [data bytes]; - NSInteger i; - for (i = [data length] - 1; i >= 0; i--) { - if (bytes[i] != '\0') - break; - } - if (i != [data length] - 1) { - if (i >= 0) - data = [data subdataWithRange:NSMakeRange(0, i + 1)]; - else - data = [NSData data]; - } + @autoreleasepool { + NSData *data = [fileHandle readDataOfLength:blockSize]; + done = ([data length] == 0); - unsigned char hash[CC_SHA256_DIGEST_LENGTH]; - if (CC_SHA256([data bytes], (unsigned int)[data length], hash)) - [hashes addObject:[NSMutableData dataWithBytes:hash length:CC_SHA256_DIGEST_LENGTH]]; + if (!done) { + // Strip trailing slashes. + const char *bytes = [data bytes]; + NSInteger i; + for (i = [data length] - 1; i >= 0; i--) { + if (bytes[i] != '\0') + break; + } + if (i != [data length] - 1) { + if (i >= 0) + data = [data subdataWithRange:NSMakeRange(0, i + 1)]; + else + data = [NSData data]; + } + + unsigned char hash[CC_SHA256_DIGEST_LENGTH]; + if (CC_SHA256([data bytes], (unsigned int)[data length], hash)) + [hashes addObject:[NSMutableData dataWithBytes:hash length:CC_SHA256_DIGEST_LENGTH]]; + } } - - [pool drain]; } [fileHandle closeFile]; diff --git a/pithos-macos/LastCompletedSyncTransformer.m b/pithos-macos/LastCompletedSyncTransformer.m index 8f6b471..76e59d0 100644 --- a/pithos-macos/LastCompletedSyncTransformer.m +++ b/pithos-macos/LastCompletedSyncTransformer.m @@ -75,7 +75,7 @@ } + (void)initialize { - [[NSValueTransformer class] setValueTransformer:[self new] forName:@"LastCompletedSyncTransformer"]; + [[self class] setValueTransformer:[[self new] autorelease] forName:@"LastCompletedSyncTransformer"]; } @end diff --git a/pithos-macos/LastModifiedDateTransformer.m b/pithos-macos/LastModifiedDateTransformer.m index b14fda6..7f31f4a 100644 --- a/pithos-macos/LastModifiedDateTransformer.m +++ b/pithos-macos/LastModifiedDateTransformer.m @@ -76,7 +76,7 @@ } + (void)initialize { - [[NSValueTransformer class] setValueTransformer:[self new] forName:@"LastModifiedDateTransformer"]; + [[self class] setValueTransformer:[[self new] autorelease] forName:@"LastModifiedDateTransformer"]; } @end diff --git a/pithos-macos/MetadataKeyTransformer.m b/pithos-macos/MetadataKeyTransformer.m index 1bb8b20..3521121 100644 --- a/pithos-macos/MetadataKeyTransformer.m +++ b/pithos-macos/MetadataKeyTransformer.m @@ -58,7 +58,7 @@ } + (void)initialize { - [[NSValueTransformer class] setValueTransformer:[self new] forName:@"MetadataKeyTransformer"]; + [[self class] setValueTransformer:[[self new] autorelease] forName:@"MetadataKeyTransformer"]; } @end diff --git a/pithos-macos/PithosAccount.m b/pithos-macos/PithosAccount.m index 90c1293..f3ee062 100644 --- a/pithos-macos/PithosAccount.m +++ b/pithos-macos/PithosAccount.m @@ -60,8 +60,8 @@ PithosAccount *pithosAccount = [[[self alloc] init] autorelease]; pithosAccount.uniqueName = [NSString stringWithFormat:@"pithosAccount-%f", [NSDate timeIntervalSinceReferenceDate]]; pithosAccount.syncSkipHidden = YES; - pithosAccount.versionResource = [NSString stringWithString:@"v1"]; - pithosAccount.loginResource = [NSString stringWithString:@"login"]; + pithosAccount.versionResource = @"v1"; + pithosAccount.loginResource = @"login"; return pithosAccount; } @@ -112,14 +112,14 @@ if (![name length]) { [name release]; NSDictionary *pithosAccountsDictionary = [(pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate] pithosAccountsDictionary]; - NSString *namePrefix = [NSString stringWithString:@"okeanos"]; + NSString *namePrefix = @"okeanos"; NSUInteger nameSuffix = 1; - name = [NSString stringWithString:@"okeanos"]; + name = @"okeanos"; NSString *documentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSFileManager *fileManager = [NSFileManager defaultManager]; while ([pithosAccountsDictionary objectForKey:name] || [fileManager fileExistsAtPath:[documentsDirectoryPath stringByAppendingPathComponent:name]]) { - name = [NSString stringWithFormat:@"%@%d", namePrefix, ++nameSuffix]; + name = [NSString stringWithFormat:@"%@%ld", namePrefix, ++nameSuffix]; } [name retain]; } @@ -233,7 +233,7 @@ - (NSString *)serverURL { if (![self urlIsValid:serverURL]) { [serverURL release]; - serverURL = [[NSString stringWithString:@"https://pithos.okeanos.grnet.gr"] retain]; + serverURL = [@"https://pithos.okeanos.grnet.gr" retain]; } return serverURL; } diff --git a/pithos-macos/PithosAccountNode.m b/pithos-macos/PithosAccountNode.m index 1b85934..4c3a9ce 100644 --- a/pithos-macos/PithosAccountNode.m +++ b/pithos-macos/PithosAccountNode.m @@ -187,84 +187,118 @@ static NSImage *sharedIcon = nil; #pragma mark ASIHTTPRequestDelegate - (void)accountRequestFailed:(ASIPithosAccountRequest *)request { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSString *message; - NSError *error = [accountRequest error]; - if (error) - message = [NSString stringWithFormat:@"Account listing %@ failed: %@", accountRequest.url, [error localizedDescription]]; - else - message = [NSString stringWithFormat:@"Account listing %@ failed: (%d) %@", - accountRequest.url, accountRequest.responseStatusCode, accountRequest.responseStatusMessage]; - dispatch_async(dispatch_get_main_queue(), ^{ - [[PithosActivityFacility defaultPithosActivityFacility] startAndEndActivityWithType:PithosActivityOther message:message]; - }); - NSUInteger retries = [[accountRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue]; - if (retries > 0) { - ASIPithosAccountRequest *newAccountRequest = (ASIPithosAccountRequest *)[PithosUtilities copyRequest:accountRequest]; - [(NSMutableDictionary *)(newAccountRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; - [accountRequest release]; - accountRequest = newAccountRequest; - [[PithosUtilities prepareRequest:accountRequest priority:[[accountRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous]; - } else { - [newChildren release]; - newChildren = nil; - [accountRequest release]; - accountRequest = nil; - [containers release]; - containers = nil; - forcedRefresh = NO; - @synchronized(self) { - freshness = PithosNodeStateRefreshNeeded; + @autoreleasepool { + NSString *message; + NSError *error = [accountRequest error]; + if (error) + message = [NSString stringWithFormat:@"Account listing %@ failed: %@", accountRequest.url, [error localizedDescription]]; + else + message = [NSString stringWithFormat:@"Account listing %@ failed: (%d) %@", + accountRequest.url, accountRequest.responseStatusCode, accountRequest.responseStatusMessage]; + dispatch_async(dispatch_get_main_queue(), ^{ + [[PithosActivityFacility defaultPithosActivityFacility] startAndEndActivityWithType:PithosActivityOther message:message]; + }); + NSUInteger retries = [[accountRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue]; + if (retries > 0) { + ASIPithosAccountRequest *newAccountRequest = (ASIPithosAccountRequest *)[PithosUtilities copyRequest:accountRequest]; + [(NSMutableDictionary *)(newAccountRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; + [accountRequest release]; + accountRequest = newAccountRequest; + [[PithosUtilities prepareRequest:accountRequest priority:[[accountRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous]; + } else { + [newChildren release]; + newChildren = nil; + [accountRequest release]; + accountRequest = nil; + [containers release]; + containers = nil; + forcedRefresh = NO; + @synchronized(self) { + freshness = PithosNodeStateRefreshNeeded; + } } } - [pool drain]; } - (void)accountRequestFinished:(ASIPithosAccountRequest *)request { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - DLog(@"List account finished: %@", [accountRequest url]); - DLog(@"Cached: %d", [accountRequest didUseCachedResponse]); - if (accountRequest.responseStatusCode == 200) { - self.pithosAccount = [accountRequest account]; - - NSArray *someContainers = [accountRequest containers]; - if (containers == nil) { - containers = [[NSMutableArray alloc] initWithArray:someContainers]; - } else { - [containers addObjectsFromArray:someContainers]; - } - if ([someContainers count] < 10000) { - if (!accountRequest.didUseCachedResponse || ([containers count] != [someContainers count]) || !children) { - // Save new children - DLog(@"using newChildren"); - newChildren = [[NSMutableArray alloc] init]; - NSMutableIndexSet *keptNodes = [NSMutableIndexSet indexSet]; - for (ASIPithosContainer *container in containers) { - PithosContainerNode *node = [[[PithosContainerNode alloc] initWithPithos:pithos pithosContainer:container] autorelease]; - node.parent = self; - node.shared = shared; - node.sharingAccount = sharingAccount; - node.inheritChildrenUpdatedNotificationName = inheritChildrenUpdatedNotificationName; - if (children) { - NSUInteger oldIndex = [children indexOfObject:node]; - if (oldIndex != NSNotFound) { - // Use the same pointer value, if possible - node = [children objectAtIndex:oldIndex]; -// node.pithosContainer = container; - [node setLimitedPithosContainer:container]; - [keptNodes addIndex:oldIndex]; + @autoreleasepool { + DLog(@"List account finished: %@", [accountRequest url]); + DLog(@"Cached: %d", [accountRequest didUseCachedResponse]); + if (accountRequest.responseStatusCode == 200) { + self.pithosAccount = [accountRequest account]; + + NSArray *someContainers = [accountRequest containers]; + if (containers == nil) { + containers = [[NSMutableArray alloc] initWithArray:someContainers]; + } else { + [containers addObjectsFromArray:someContainers]; + } + if ([someContainers count] < 10000) { + if (!accountRequest.didUseCachedResponse || ([containers count] != [someContainers count]) || !children) { + // Save new children + DLog(@"using newChildren"); + newChildren = [[NSMutableArray alloc] init]; + NSMutableIndexSet *keptNodes = [NSMutableIndexSet indexSet]; + for (ASIPithosContainer *container in containers) { + PithosContainerNode *node = [[[PithosContainerNode alloc] initWithPithos:pithos pithosContainer:container] autorelease]; + node.parent = self; + node.shared = shared; + node.sharingAccount = sharingAccount; + node.inheritChildrenUpdatedNotificationName = inheritChildrenUpdatedNotificationName; + if (children) { + NSUInteger oldIndex = [children indexOfObject:node]; + if (oldIndex != NSNotFound) { + // Use the same pointer value, if possible + node = [children objectAtIndex:oldIndex]; + // node.pithosContainer = container; + [node setLimitedPithosContainer:container]; + [keptNodes addIndex:oldIndex]; + } } + [newChildren addObject:node]; } - [newChildren addObject:node]; + [[children objectsAtIndexes: + [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])] indexesPassingTest:^(NSUInteger idx, BOOL *stop){ + if ([keptNodes containsIndex:idx]) + return NO; + return YES; + }]] makeObjectsPerformSelector:@selector(pithosNodeWillBeRemoved)]; + } + // Else cache was used and all results were fetched during this request, so existing children can be reused + [accountRequest release]; + accountRequest = nil; + [containers release]; + containers = nil; + forcedRefresh = NO; + @synchronized(self) { + freshness = PithosNodeStateRefreshFinished; } - [[children objectsAtIndexes: - [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])] indexesPassingTest:^(NSUInteger idx, BOOL *stop){ - if ([keptNodes containsIndex:idx]) - return NO; - return YES; - }]] makeObjectsPerformSelector:@selector(pithosNodeWillBeRemoved)]; + [self postChildrenUpdatedNotificationName]; + } else { + [accountRequest release]; + // Do an additional request to fetch more objects + accountRequest = [[ASIPithosAccountRequest listContainersRequestWithPithos:pithos + limit:0 + marker:[[someContainers lastObject] name] + shared:shared + until:nil] retain]; + if (sharingAccount) + [accountRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos]; + else if (!forcedRefresh) + accountRequest.downloadCache = [ASIDownloadCache sharedCache]; + accountRequest.delegate = self; + accountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + accountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + accountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(accountRequestFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(accountRequestFailed:)), @"didFailSelector", + nil]; + [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous]; } - // Else cache was used and all results were fetched during this request, so existing children can be reused + } else if (accountRequest.responseStatusCode == 304) { + // Account is not modified, so existing children can be reused [accountRequest release]; accountRequest = nil; [containers release]; @@ -275,100 +309,66 @@ static NSImage *sharedIcon = nil; } [self postChildrenUpdatedNotificationName]; } else { - [accountRequest release]; - // Do an additional request to fetch more objects - accountRequest = [[ASIPithosAccountRequest listContainersRequestWithPithos:pithos - limit:0 - marker:[[someContainers lastObject] name] - shared:shared - until:nil] retain]; - if (sharingAccount) - [accountRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos]; - else if (!forcedRefresh) - accountRequest.downloadCache = [ASIDownloadCache sharedCache]; - accountRequest.delegate = self; - accountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - accountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - accountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(accountRequestFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(accountRequestFailed:)), @"didFailSelector", - nil]; - [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous]; + [self accountRequestFailed:accountRequest]; } - } else if (accountRequest.responseStatusCode == 304) { - // Account is not modified, so existing children can be reused - [accountRequest release]; - accountRequest = nil; - [containers release]; - containers = nil; - forcedRefresh = NO; - @synchronized(self) { - freshness = PithosNodeStateRefreshFinished; - } - [self postChildrenUpdatedNotificationName]; - } else { - [self accountRequestFailed:accountRequest]; } - [pool drain]; } - (void)accountMetadataRequestFinished:(ASIPithosAccountRequest *)request { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - DLog(@"URL: %@", [request url]); - DLog(@"cached: %d", [request didUseCachedResponse]); - - if ([request isEqualTo:applyMetadataAccountRequest]) { - @synchronized(self) { - [applyMetadataAccountRequest release]; - applyMetadataAccountRequest = nil; - } - [self refreshInfo]; - } else if ([request isEqualTo:refreshMetadataAccountRequest]) { - self.pithosAccount = [refreshMetadataAccountRequest account]; - @synchronized(self) { - [refreshMetadataAccountRequest release]; - refreshMetadataAccountRequest = nil; - } - } - [pool drain]; -} - -- (void)accountMetadataRequestFailed:(ASIPithosAccountRequest *)request { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue]; - if (retries > 0) { - ASIPithosAccountRequest *newRequest = (ASIPithosAccountRequest *)[PithosUtilities copyRequest:request]; - [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; + @autoreleasepool { + DLog(@"URL: %@", [request url]); + DLog(@"cached: %d", [request didUseCachedResponse]); + if ([request isEqualTo:applyMetadataAccountRequest]) { @synchronized(self) { [applyMetadataAccountRequest release]; - applyMetadataAccountRequest = newRequest; + applyMetadataAccountRequest = nil; } + [self refreshInfo]; } else if ([request isEqualTo:refreshMetadataAccountRequest]) { + self.pithosAccount = [refreshMetadataAccountRequest account]; @synchronized(self) { [refreshMetadataAccountRequest release]; - refreshMetadataAccountRequest = newRequest; + refreshMetadataAccountRequest = nil; } } - [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous]; - } else { - if ([request isEqualTo:applyMetadataAccountRequest]) { - [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataAccountRequest]; - @synchronized(self) { - [applyMetadataAccountRequest release]; - applyMetadataAccountRequest = nil; + } +} + +- (void)accountMetadataRequestFailed:(ASIPithosAccountRequest *)request { + @autoreleasepool { + NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue]; + if (retries > 0) { + ASIPithosAccountRequest *newRequest = (ASIPithosAccountRequest *)[PithosUtilities copyRequest:request]; + [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; + if ([request isEqualTo:applyMetadataAccountRequest]) { + @synchronized(self) { + [applyMetadataAccountRequest release]; + applyMetadataAccountRequest = newRequest; + } + } else if ([request isEqualTo:refreshMetadataAccountRequest]) { + @synchronized(self) { + [refreshMetadataAccountRequest release]; + refreshMetadataAccountRequest = newRequest; + } } - } else if ([request isEqualTo:refreshMetadataAccountRequest]) { - [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataAccountRequest]; - @synchronized(self) { - [refreshMetadataAccountRequest release]; - refreshMetadataAccountRequest = nil; + [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous]; + } else { + if ([request isEqualTo:applyMetadataAccountRequest]) { + [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataAccountRequest]; + @synchronized(self) { + [applyMetadataAccountRequest release]; + applyMetadataAccountRequest = nil; + } + } else if ([request isEqualTo:refreshMetadataAccountRequest]) { + [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataAccountRequest]; + @synchronized(self) { + [refreshMetadataAccountRequest release]; + refreshMetadataAccountRequest = nil; + } } } } - [pool drain]; } #pragma mark - diff --git a/pithos-macos/PithosActivity.m b/pithos-macos/PithosActivity.m index 1b4c16b..6555dac 100644 --- a/pithos-macos/PithosActivity.m +++ b/pithos-macos/PithosActivity.m @@ -61,10 +61,10 @@ - (NSString *)description { if (pithosAccount) - return [NSString stringWithFormat:@"type: %lu, message: %@, totalBytes: %lu, currentBytes: %lu, pithosAccount: %@", + return [NSString stringWithFormat:@"type: %u, message: %@, totalBytes: %lu, currentBytes: %lu, pithosAccount: %@", type, message, totalBytes, currentBytes, pithosAccount.name]; else - return [NSString stringWithFormat:@"type: %lu, message: %@, totalBytes: %lu, currentBytes: %lu", + return [NSString stringWithFormat:@"type: %u, message: %@, totalBytes: %lu, currentBytes: %lu", type, message, totalBytes, currentBytes]; } diff --git a/pithos-macos/PithosBrowserController.m b/pithos-macos/PithosBrowserController.m index 0368f77..d9e8aa2 100644 --- a/pithos-macos/PithosBrowserController.m +++ b/pithos-macos/PithosBrowserController.m @@ -615,128 +615,40 @@ (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) { // Operation: Rename (move) an object or subdir/ node __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (operation.isCancelled) { - [pool drain]; - return; - } - NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName]; - if ([newName hasSuffix:@"/"]) - destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; - NSError *error = nil; - BOOL isDirectory; - if ([PithosUtilities objectExistsAtPithos:pithos - containerName:node.pithosContainer.name - objectName:destinationObjectName - error:&error - isDirectory:&isDirectory - sharingAccount:nil]) { - dispatch_async(dispatch_get_main_queue(), ^{ - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - [alert setMessageText:@"Name Taken"]; - [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]]; - [alert addButtonWithTitle:@"OK"]; - [alert runModal]; - }); - [pool drain]; - return; - } else if (error) { - [pool drain]; - return; - } - if (operation.isCancelled) { - [pool drain]; - return; - } - ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos - containerName:node.pithosContainer.name - objectName:node.pithosObject.name - destinationContainerName:node.pithosContainer.name - destinationObjectName:destinationObjectName - checkIfExists:NO]; - if (!operation.isCancelled && objectRequest) { - objectRequest.delegate = self; - objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", - [objectRequest.userInfo objectForKey:@"sourceContainerName"], - [objectRequest.userInfo objectForKey:@"sourceObjectName"], - [objectRequest.userInfo objectForKey:@"destinationContainerName"], - [objectRequest.userInfo objectForKey:@"destinationObjectName"]]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove - message:messagePrefix]; - [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: - [NSDictionary dictionaryWithObjectsAndKeys: - [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", - [NSNumber numberWithBool:YES], @"refresh", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - moveNetworkQueue, @"networkQueue", - @"move", @"operationType", - nil]]; - [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; - } - [pool drain]; - }]; - [moveQueue addOperation:operation]; - } else if ([node class] == [PithosSubdirNode class]) { - if (firstSlashRange.length == 1) - return; - // Operation: Rename (move) a subdir node and its descendants - // The resulting ASIPithosObjectRequests are chained through dependencies - __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (operation.isCancelled) { - [pool drain]; - return; - } - NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName]; - NSError *error = nil; - BOOL isDirectory; - if ([PithosUtilities objectExistsAtPithos:pithos - containerName:node.pithosContainer.name - objectName:destinationObjectName - error:&error - isDirectory:&isDirectory - sharingAccount:nil]) { - dispatch_async(dispatch_get_main_queue(), ^{ - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - [alert setMessageText:@"Name Taken"]; - [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]]; - [alert addButtonWithTitle:@"OK"]; - [alert runModal]; - }); - [pool drain]; - return; - } else if (error) { - [pool drain]; - return; - } - if (operation.isCancelled) { - [pool drain]; - return; - } - if (node.pithosObject.subdir) - destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; - NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithPithos:pithos - containerName:node.pithosContainer.name - objectName:node.pithosObject.name - destinationContainerName:node.pithosContainer.name - destinationObjectName:destinationObjectName - checkIfExists:NO]; - if (!operation.isCancelled && objectRequests) { - ASIPithosObjectRequest *previousObjectRequest = nil; - for (ASIPithosObjectRequest *objectRequest in objectRequests) { - if (operation.isCancelled) { - [pool drain]; - return; - } + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName]; + if ([newName hasSuffix:@"/"]) + destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; + NSError *error = nil; + BOOL isDirectory; + if ([PithosUtilities objectExistsAtPithos:pithos + containerName:node.pithosContainer.name + objectName:destinationObjectName + error:&error + isDirectory:&isDirectory + sharingAccount:nil]) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + [alert setMessageText:@"Name Taken"]; + [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + }); + return; + } else if (error) { + return; + } + if (operation.isCancelled) + return; + ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos + containerName:node.pithosContainer.name + objectName:node.pithosObject.name + destinationContainerName:node.pithosContainer.name + destinationObjectName:destinationObjectName + checkIfExists:NO]; + if (!operation.isCancelled && objectRequest) { objectRequest.delegate = self; objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); @@ -762,13 +674,87 @@ moveNetworkQueue, @"networkQueue", @"move", @"operationType", nil]]; - if (previousObjectRequest) - [objectRequest addDependency:previousObjectRequest]; - previousObjectRequest = objectRequest; [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } } - [pool drain]; + }]; + [moveQueue addOperation:operation]; + } else if ([node class] == [PithosSubdirNode class]) { + if (firstSlashRange.length == 1) + return; + // Operation: Rename (move) a subdir node and its descendants + // The resulting ASIPithosObjectRequests are chained through dependencies + __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName]; + NSError *error = nil; + BOOL isDirectory; + if ([PithosUtilities objectExistsAtPithos:pithos + containerName:node.pithosContainer.name + objectName:destinationObjectName + error:&error + isDirectory:&isDirectory + sharingAccount:nil]) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + [alert setMessageText:@"Name Taken"]; + [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + }); + return; + } else if (error) { + return; + } + if (operation.isCancelled) + return; + if (node.pithosObject.subdir) + destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; + NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithPithos:pithos + containerName:node.pithosContainer.name + objectName:node.pithosObject.name + destinationContainerName:node.pithosContainer.name + destinationObjectName:destinationObjectName + checkIfExists:NO]; + if (!operation.isCancelled && objectRequests) { + ASIPithosObjectRequest *previousObjectRequest = nil; + for (ASIPithosObjectRequest *objectRequest in objectRequests) { + if (operation.isCancelled) + return; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", + [objectRequest.userInfo objectForKey:@"sourceContainerName"], + [objectRequest.userInfo objectForKey:@"sourceObjectName"], + [objectRequest.userInfo objectForKey:@"destinationContainerName"], + [objectRequest.userInfo objectForKey:@"destinationObjectName"]]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove + message:messagePrefix]; + [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: + [NSDictionary dictionaryWithObjectsAndKeys: + [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", + [NSNumber numberWithBool:YES], @"refresh", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + moveNetworkQueue, @"networkQueue", + @"move", @"operationType", + nil]]; + if (previousObjectRequest) + [objectRequest addDependency:previousObjectRequest]; + previousObjectRequest = objectRequest; + [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; + } + } + } }]; [moveQueue addOperation:operation]; } @@ -971,31 +957,81 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { // Operation: Download a subdir node and its descendants // The resulting ASIPithosObjectRequests are chained through dependencies __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (operation.isCancelled) { - [pool drain]; - return; - } - NSArray *objectRequests = [PithosUtilities objectDataRequestsForSubdirWithPithos:pithos - containerName:node.pithosContainer.name - objectName:node.pithosObject.name - toDirectory:dirPath - checkIfExists:checkIfExists - sharingAccount:node.sharingAccount]; - if (!operation.isCancelled && objectRequests) { - ASIPithosObjectRequest *previousObjectRequest = nil; - for (__block ASIPithosObjectRequest *objectRequest in objectRequests) { - if (operation.isCancelled) { - [pool drain]; - return; + @autoreleasepool { + if (operation.isCancelled) + return; + NSArray *objectRequests = [PithosUtilities objectDataRequestsForSubdirWithPithos:pithos + containerName:node.pithosContainer.name + objectName:node.pithosObject.name + toDirectory:dirPath + checkIfExists:checkIfExists + sharingAccount:node.sharingAccount]; + if (!operation.isCancelled && objectRequests) { + ASIPithosObjectRequest *previousObjectRequest = nil; + for (__block ASIPithosObjectRequest *objectRequest in objectRequests) { + if (operation.isCancelled) + return; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Downloading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload + message:[messagePrefix stringByAppendingString:@" (0%)"] + totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue] + currentBytes:0]; + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility updateActivity:activity withMessage:activity.message]; + }); + [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary: + [NSDictionary dictionaryWithObjectsAndKeys: + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(downloadObjectFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + downloadNetworkQueue, @"networkQueue", + @"download", @"operationType", + nil]]; + [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){ + [activityFacility updateActivity:activity + withMessage:[messagePrefix stringByAppendingFormat:@" (%.0f%%)", (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)] + totalBytes:activity.totalBytes + currentBytes:(activity.currentBytes + size)]; + }]; + if (previousObjectRequest) + [objectRequest addDependency:previousObjectRequest]; + previousObjectRequest = objectRequest; + [downloadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]]; } + } + } + }]; + [downloadQueue addOperation:operation]; + } else if ([node class] == [PithosObjectNode class]) { + // Operation: Download an object node + __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + @autoreleasepool { + if (operation.isCancelled) + return; + __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectDataRequestWithPithos:pithos + containerName:node.pithosContainer.name + objectName:node.pithosObject.name + version:version + toDirectory:dirPath + withNewFileName:newFileName + checkIfExists:checkIfExists + sharingAccount:node.sharingAccount]; + if (!operation.isCancelled && objectRequest) { objectRequest.delegate = self; objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); NSString *messagePrefix = [NSString stringWithFormat:@"Downloading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload message:[messagePrefix stringByAppendingString:@" (0%)"] - totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue] + totalBytes:node.pithosObject.bytes currentBytes:0]; dispatch_async(dispatch_get_main_queue(), ^{ [activityFacility updateActivity:activity withMessage:activity.message]; @@ -1019,65 +1055,9 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { totalBytes:activity.totalBytes currentBytes:(activity.currentBytes + size)]; }]; - if (previousObjectRequest) - [objectRequest addDependency:previousObjectRequest]; - previousObjectRequest = objectRequest; [downloadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]]; } } - [pool drain]; - }]; - [downloadQueue addOperation:operation]; - } else if ([node class] == [PithosObjectNode class]) { - // Operation: Download an object node - __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (operation.isCancelled) { - [pool drain]; - return; - } - __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectDataRequestWithPithos:pithos - containerName:node.pithosContainer.name - objectName:node.pithosObject.name - version:version - toDirectory:dirPath - withNewFileName:newFileName - checkIfExists:checkIfExists - sharingAccount:node.sharingAccount]; - if (!operation.isCancelled && objectRequest) { - objectRequest.delegate = self; - objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Downloading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload - message:[messagePrefix stringByAppendingString:@" (0%)"] - totalBytes:node.pithosObject.bytes - currentBytes:0]; - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility updateActivity:activity withMessage:activity.message]; - }); - [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary: - [NSDictionary dictionaryWithObjectsAndKeys: - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(downloadObjectFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - downloadNetworkQueue, @"networkQueue", - @"download", @"operationType", - nil]]; - [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){ - [activityFacility updateActivity:activity - withMessage:[messagePrefix stringByAppendingFormat:@" (%.0f%%)", (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)] - totalBytes:activity.totalBytes - currentBytes:(activity.currentBytes + size)]; - }]; - [downloadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]]; - [pool drain]; - } }]; [downloadQueue addOperation:operation]; } @@ -1121,195 +1101,183 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { precomposedStringWithCanonicalMapping]; // Operation: Upload a local file __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (operation.isCancelled) { - [pool drain]; - return; - } - NSError *error = nil; - NSString *contentType = [PithosUtilities contentTypeOfFile:filePath error:&error]; - if (contentType == nil) - contentType = @"application/octet-stream"; - #if DEBUG_PITHOS - if (error) - DLog(@"contentType detection error: %@", error); - #endif - NSArray *hashes = nil; - if (operation.isCancelled) { - [pool drain]; - return; - } - ASIPithosObjectRequest *objectRequest = [PithosUtilities writeObjectDataRequestWithPithos:pithos - containerName:containerName - objectName:objectName - contentType:contentType - blockSize:blockSize - blockHash:blockHash - forFile:filePath - checkIfExists:YES - hashes:&hashes - sharingAccount:destinationNode.sharingAccount]; - if (!operation.isCancelled && objectRequest) { - objectRequest.delegate = self; - objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Uploading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload - message:[messagePrefix stringByAppendingString:@" (0%)"] - totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue] - currentBytes:0]; - [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary: - [NSDictionary dictionaryWithObjectsAndKeys: - containerName, @"containerName", - objectName, @"objectName", - contentType, @"contentType", - [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize", - blockHash, @"blockHash", - filePath, @"filePath", - hashes, @"hashes", - [NSArray arrayWithObject:destinationNode], @"refreshNodes", - [NSNumber numberWithBool:YES], @"refresh", - [NSNumber numberWithUnsignedInteger:10], @"iteration", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - uploadNetworkQueue, @"networkQueue", - @"upload", @"operationType", - nil]]; - if (destinationNode.sharingAccount) - [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"]; - [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]]; - } - [pool drain]; - }]; - [uploadQueue addOperation:operation]; - } else { - // Upload directory, confirm first - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - [alert setMessageText:@"Upload directory"]; - [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to upload it and its contents?", filePath]]; - [alert addButtonWithTitle:@"OK"]; - [alert addButtonWithTitle:@"Cancel"]; - NSInteger choice = [alert runModal]; - if (choice == NSAlertFirstButtonReturn) { - NSString *objectName = [[objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]] - precomposedStringWithCanonicalMapping]; - // Operation: Upload a local directory and its descendants - // The resulting ASIPithosObjectRequests are chained through dependencies - __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (operation.isCancelled) { - [pool drain]; + @autoreleasepool { + if (operation.isCancelled) return; - } - NSMutableArray *objectNames = nil; - NSMutableArray *contentTypes = nil; - NSMutableArray *filePaths = nil; - NSMutableArray *hashesArrays = nil; - NSMutableArray *directoryObjectRequests = nil; - NSArray *objectRequests = [PithosUtilities writeObjectDataRequestsWithPithos:pithos - containerName:containerName - objectName:objectName - blockSize:blockSize - blockHash:blockHash - forDirectory:filePath - checkIfExists:YES - objectNames:&objectNames - contentTypes:&contentTypes - filePaths:&filePaths - hashesArrays:&hashesArrays - directoryObjectRequests:&directoryObjectRequests - sharingAccount:destinationNode.sharingAccount]; - if (operation.isCancelled) { - [pool drain]; + NSError *error = nil; + NSString *contentType = [PithosUtilities contentTypeOfFile:filePath error:&error]; + if (contentType == nil) + contentType = @"application/octet-stream"; + #if DEBUG_PITHOS + if (error) + DLog(@"contentType detection error: %@", error); + #endif + NSArray *hashes = nil; + if (operation.isCancelled) return; - } - ASIPithosObjectRequest *previousObjectRequest = nil; - for (ASIPithosObjectRequest *objectRequest in directoryObjectRequests) { - if (operation.isCancelled) { - [pool drain]; - return; - } + ASIPithosObjectRequest *objectRequest = [PithosUtilities writeObjectDataRequestWithPithos:pithos + containerName:containerName + objectName:objectName + contentType:contentType + blockSize:blockSize + blockHash:blockHash + forFile:filePath + checkIfExists:YES + hashes:&hashes + sharingAccount:destinationNode.sharingAccount]; + if (!operation.isCancelled && objectRequest) { objectRequest.delegate = self; objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory - message:messagePrefix]; + NSString *messagePrefix = [NSString stringWithFormat:@"Uploading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload + message:[messagePrefix stringByAppendingString:@" (0%)"] + totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue] + currentBytes:0]; [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary: [NSDictionary dictionaryWithObjectsAndKeys: + containerName, @"containerName", + objectName, @"objectName", + contentType, @"contentType", + [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize", + blockHash, @"blockHash", + filePath, @"filePath", + hashes, @"hashes", + [NSArray arrayWithObject:destinationNode], @"refreshNodes", [NSNumber numberWithBool:YES], @"refresh", + [NSNumber numberWithUnsignedInteger:10], @"iteration", activity, @"activity", [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", uploadNetworkQueue, @"networkQueue", @"upload", @"operationType", nil]]; - if (previousObjectRequest) - [objectRequest addDependency:previousObjectRequest]; - previousObjectRequest = objectRequest; + if (destinationNode.sharingAccount) + [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"]; [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]]; } - if (!operation.isCancelled && objectRequests) { - for (NSUInteger i = 0 ; i < [objectRequests count] ; i++) { - if (operation.isCancelled) { - [pool drain]; + } + }]; + [uploadQueue addOperation:operation]; + } else { + // Upload directory, confirm first + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + [alert setMessageText:@"Upload directory"]; + [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to upload it and its contents?", filePath]]; + [alert addButtonWithTitle:@"OK"]; + [alert addButtonWithTitle:@"Cancel"]; + NSInteger choice = [alert runModal]; + if (choice == NSAlertFirstButtonReturn) { + NSString *objectName = [[objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]] + precomposedStringWithCanonicalMapping]; + // Operation: Upload a local directory and its descendants + // The resulting ASIPithosObjectRequests are chained through dependencies + __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + @autoreleasepool { + if (operation.isCancelled) + return; + NSMutableArray *objectNames = nil; + NSMutableArray *contentTypes = nil; + NSMutableArray *filePaths = nil; + NSMutableArray *hashesArrays = nil; + NSMutableArray *directoryObjectRequests = nil; + NSArray *objectRequests = [PithosUtilities writeObjectDataRequestsWithPithos:pithos + containerName:containerName + objectName:objectName + blockSize:blockSize + blockHash:blockHash + forDirectory:filePath + checkIfExists:YES + objectNames:&objectNames + contentTypes:&contentTypes + filePaths:&filePaths + hashesArrays:&hashesArrays + directoryObjectRequests:&directoryObjectRequests + sharingAccount:destinationNode.sharingAccount]; + if (operation.isCancelled) + return; + ASIPithosObjectRequest *previousObjectRequest = nil; + for (ASIPithosObjectRequest *objectRequest in directoryObjectRequests) { + if (operation.isCancelled) return; - } - ASIPithosObjectRequest *objectRequest = [objectRequests objectAtIndex:i]; objectRequest.delegate = self; objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Uploading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload - message:[messagePrefix stringByAppendingString:@" (0%)"] - totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue] - currentBytes:0]; + NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory + message:messagePrefix]; [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary: [NSDictionary dictionaryWithObjectsAndKeys: - containerName, @"containerName", - [objectNames objectAtIndex:i], @"objectName", - [contentTypes objectAtIndex:i], @"contentType", - [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize", - blockHash, @"blockHash", - [filePaths objectAtIndex:i], @"filePath", - [hashesArrays objectAtIndex:i], @"hashes", [NSNumber numberWithBool:YES], @"refresh", - [NSNumber numberWithUnsignedInteger:10], @"iteration", activity, @"activity", [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", uploadNetworkQueue, @"networkQueue", @"upload", @"operationType", nil]]; - if (destinationNode.sharingAccount) - [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"]; if (previousObjectRequest) [objectRequest addDependency:previousObjectRequest]; previousObjectRequest = objectRequest; [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]]; } - } - [pool drain]; - }]; - [uploadQueue addOperation:operation]; - } - } + if (!operation.isCancelled && objectRequests) { + for (NSUInteger i = 0 ; i < [objectRequests count] ; i++) { + if (operation.isCancelled) + return; + ASIPithosObjectRequest *objectRequest = [objectRequests objectAtIndex:i]; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Uploading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload + message:[messagePrefix stringByAppendingString:@" (0%)"] + totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue] + currentBytes:0]; + [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary: + [NSDictionary dictionaryWithObjectsAndKeys: + containerName, @"containerName", + [objectNames objectAtIndex:i], @"objectName", + [contentTypes objectAtIndex:i], @"contentType", + [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize", + blockHash, @"blockHash", + [filePaths objectAtIndex:i], @"filePath", + [hashesArrays objectAtIndex:i], @"hashes", + [NSNumber numberWithBool:YES], @"refresh", + [NSNumber numberWithUnsignedInteger:10], @"iteration", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + uploadNetworkQueue, @"networkQueue", + @"upload", @"operationType", + nil]]; + if (destinationNode.sharingAccount) + [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"]; + if (previousObjectRequest) + [objectRequest addDependency:previousObjectRequest]; + previousObjectRequest = objectRequest; + [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]]; + } + } + } + }]; + [uploadQueue addOperation:operation]; + } + } } } return YES; @@ -1331,75 +1299,19 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) { // Operation: Move an object or subdir/ node __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (operation.isCancelled) { - [pool drain]; - return; - } - NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName]; - if ([node.pithosObject.name hasSuffix:@"/"]) - destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; - ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos - containerName:node.pithosContainer.name - objectName:node.pithosObject.name - destinationContainerName:containerName - destinationObjectName:destinationObjectName - checkIfExists:YES]; - if (!operation.isCancelled && objectRequest) { - objectRequest.delegate = self; - objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", - [objectRequest.userInfo objectForKey:@"sourceContainerName"], - [objectRequest.userInfo objectForKey:@"sourceObjectName"], - [objectRequest.userInfo objectForKey:@"destinationContainerName"], - [objectRequest.userInfo objectForKey:@"destinationObjectName"]]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove - message:messagePrefix]; - [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: - [NSDictionary dictionaryWithObjectsAndKeys: - [NSArray arrayWithObjects:node.parent, destinationNode, nil], @"forceRefreshNodes", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - moveNetworkQueue, @"networkQueue", - @"move", @"operationType", - nil]]; - [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; - } - [pool drain]; - }]; - [moveQueue addOperation:operation]; - } else if ([node class] == [PithosSubdirNode class]) { - // Operation: Move a subdir node and its descendants - // The resulting ASIPithosObjectRequests are chained through dependencies - __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (operation.isCancelled) { - [pool drain]; - return; - } - NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName]; - if (node.pithosObject.subdir) - destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; - NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithPithos:pithos - containerName:node.pithosContainer.name - objectName:node.pithosObject.name - destinationContainerName:containerName - destinationObjectName:destinationObjectName - checkIfExists:YES]; - if (!operation.isCancelled && objectRequests) { - ASIPithosObjectRequest *previousObjectRequest = nil; - for (ASIPithosObjectRequest *objectRequest in objectRequests) { - if (operation.isCancelled) { - [pool drain]; - return; - } + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName]; + if ([node.pithosObject.name hasSuffix:@"/"]) + destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; + ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos + containerName:node.pithosContainer.name + objectName:node.pithosObject.name + destinationContainerName:containerName + destinationObjectName:destinationObjectName + checkIfExists:YES]; + if (!operation.isCancelled && objectRequest) { objectRequest.delegate = self; objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); @@ -1413,7 +1325,6 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: [NSDictionary dictionaryWithObjectsAndKeys: [NSArray arrayWithObjects:node.parent, destinationNode, nil], @"forceRefreshNodes", - [NSNumber numberWithBool:YES], @"refresh", activity, @"activity", [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", @@ -1425,13 +1336,64 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { moveNetworkQueue, @"networkQueue", @"move", @"operationType", nil]]; - if (previousObjectRequest) - [objectRequest addDependency:previousObjectRequest]; - previousObjectRequest = objectRequest; [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } } - [pool drain]; + }]; + [moveQueue addOperation:operation]; + } else if ([node class] == [PithosSubdirNode class]) { + // Operation: Move a subdir node and its descendants + // The resulting ASIPithosObjectRequests are chained through dependencies + __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName]; + if (node.pithosObject.subdir) + destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; + NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithPithos:pithos + containerName:node.pithosContainer.name + objectName:node.pithosObject.name + destinationContainerName:containerName + destinationObjectName:destinationObjectName + checkIfExists:YES]; + if (!operation.isCancelled && objectRequests) { + ASIPithosObjectRequest *previousObjectRequest = nil; + for (ASIPithosObjectRequest *objectRequest in objectRequests) { + if (operation.isCancelled) + return; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", + [objectRequest.userInfo objectForKey:@"sourceContainerName"], + [objectRequest.userInfo objectForKey:@"sourceObjectName"], + [objectRequest.userInfo objectForKey:@"destinationContainerName"], + [objectRequest.userInfo objectForKey:@"destinationObjectName"]]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove + message:messagePrefix]; + [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: + [NSDictionary dictionaryWithObjectsAndKeys: + [NSArray arrayWithObjects:node.parent, destinationNode, nil], @"forceRefreshNodes", + [NSNumber numberWithBool:YES], @"refresh", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + moveNetworkQueue, @"networkQueue", + @"move", @"operationType", + nil]]; + if (previousObjectRequest) + [objectRequest addDependency:previousObjectRequest]; + previousObjectRequest = objectRequest; + [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; + } + } + } }]; [moveQueue addOperation:operation]; } @@ -1455,99 +1417,29 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) { // Operation: Copy an object or subdir/ node __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (operation.isCancelled) { - [pool drain]; - return; - } - NSString *destinationObjectName; - if (![destinationNode isEqualTo:node.parent]) { - destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName]; - if ([node.pithosObject.name hasSuffix:@"/"]) - destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; - } else { - destinationObjectName = [PithosUtilities safeObjectNameForPithos:pithos - containerName:containerName - objectName:node.pithosObject.name]; - } - if (operation.isCancelled) { - [pool drain]; - return; - } - ASIPithosObjectRequest *objectRequest = [PithosUtilities copyObjectRequestWithPithos:pithos - containerName:node.pithosContainer.name - objectName:node.pithosObject.name - destinationContainerName:containerName - destinationObjectName:destinationObjectName - checkIfExists:YES - sharingAccount:node.sharingAccount]; - if (!operation.isCancelled && objectRequest) { - objectRequest.delegate = self; - objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@'", - [objectRequest.userInfo objectForKey:@"sourceContainerName"], - [objectRequest.userInfo objectForKey:@"sourceObjectName"], - [objectRequest.userInfo objectForKey:@"destinationContainerName"], - [objectRequest.userInfo objectForKey:@"destinationObjectName"]]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCopy - message:messagePrefix]; - [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: - [NSDictionary dictionaryWithObjectsAndKeys: - [NSArray arrayWithObject:destinationNode], @"forceRefreshNodes", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(copyFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - copyNetworkQueue, @"networkQueue", - @"copy", @"operationType", - nil]]; - [copyNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; - } - [pool drain]; - }]; - [copyQueue addOperation:operation]; - } else if ([node class] == [PithosSubdirNode class]) { - // Operation: Copy a subdir node and its descendants - // The resulting ASIPithosObjectRequests are chained through dependencies - __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (operation.isCancelled) { - [pool drain]; - return; - } - NSString *destinationObjectName; - if (![destinationNode isEqualTo:node.parent]) { - destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName]; - if (node.pithosObject.subdir) - destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; - } else { - destinationObjectName = [PithosUtilities safeSubdirNameForPithos:pithos - containerName:containerName - subdirName:node.pithosObject.name]; - } - if (operation.isCancelled) { - [pool drain]; - return; - } - NSArray *objectRequests = [PithosUtilities copyObjectRequestsForSubdirWithPithos:pithos - containerName:node.pithosContainer.name - objectName:node.pithosObject.name - destinationContainerName:containerName - destinationObjectName:destinationObjectName - checkIfExists:YES - sharingAccount:node.sharingAccount]; - if (!operation.isCancelled && objectRequests) { - ASIPithosObjectRequest *previousObjectRequest = nil; - for (ASIPithosObjectRequest *objectRequest in objectRequests) { - if (operation.isCancelled) { - [pool drain]; - return; - } + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *destinationObjectName; + if (![destinationNode isEqualTo:node.parent]) { + destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName]; + if ([node.pithosObject.name hasSuffix:@"/"]) + destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; + } else { + destinationObjectName = [PithosUtilities safeObjectNameForPithos:pithos + containerName:containerName + objectName:node.pithosObject.name]; + } + if (operation.isCancelled) + return; + ASIPithosObjectRequest *objectRequest = [PithosUtilities copyObjectRequestWithPithos:pithos + containerName:node.pithosContainer.name + objectName:node.pithosObject.name + destinationContainerName:containerName + destinationObjectName:destinationObjectName + checkIfExists:YES + sharingAccount:node.sharingAccount]; + if (!operation.isCancelled && objectRequest) { objectRequest.delegate = self; objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); @@ -1572,13 +1464,73 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { copyNetworkQueue, @"networkQueue", @"copy", @"operationType", nil]]; - if (previousObjectRequest) - [objectRequest addDependency:previousObjectRequest]; - previousObjectRequest = objectRequest; [copyNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } } - [pool drain]; + }]; + [copyQueue addOperation:operation]; + } else if ([node class] == [PithosSubdirNode class]) { + // Operation: Copy a subdir node and its descendants + // The resulting ASIPithosObjectRequests are chained through dependencies + __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *destinationObjectName; + if (![destinationNode isEqualTo:node.parent]) { + destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName]; + if (node.pithosObject.subdir) + destinationObjectName = [destinationObjectName stringByAppendingString:@"/"]; + } else { + destinationObjectName = [PithosUtilities safeSubdirNameForPithos:pithos + containerName:containerName + subdirName:node.pithosObject.name]; + } + if (operation.isCancelled) + return; + NSArray *objectRequests = [PithosUtilities copyObjectRequestsForSubdirWithPithos:pithos + containerName:node.pithosContainer.name + objectName:node.pithosObject.name + destinationContainerName:containerName + destinationObjectName:destinationObjectName + checkIfExists:YES + sharingAccount:node.sharingAccount]; + if (!operation.isCancelled && objectRequests) { + ASIPithosObjectRequest *previousObjectRequest = nil; + for (ASIPithosObjectRequest *objectRequest in objectRequests) { + if (operation.isCancelled) + return; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@'", + [objectRequest.userInfo objectForKey:@"sourceContainerName"], + [objectRequest.userInfo objectForKey:@"sourceObjectName"], + [objectRequest.userInfo objectForKey:@"destinationContainerName"], + [objectRequest.userInfo objectForKey:@"destinationObjectName"]]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCopy + message:messagePrefix]; + [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: + [NSDictionary dictionaryWithObjectsAndKeys: + [NSArray arrayWithObject:destinationNode], @"forceRefreshNodes", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(copyFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + copyNetworkQueue, @"networkQueue", + @"copy", @"operationType", + nil]]; + if (previousObjectRequest) + [objectRequest addDependency:previousObjectRequest]; + previousObjectRequest = objectRequest; + [copyNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; + } + } + } }]; [copyQueue addOperation:operation]; } @@ -1614,14 +1566,14 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { selector:NSSelectorFromString([request.userInfo objectForKey:@"didFinishSelector"]) object:request] autorelease]; operation.completionBlock = ^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if ([[request.userInfo objectForKey:@"operation"] isCancelled]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] - withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]]; - }); + @autoreleasepool { + if ([[request.userInfo objectForKey:@"operation"] isCancelled]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] + withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]]; + }); + } } - [pool drain]; }; [(NSMutableDictionary *)request.userInfo setObject:operation forKey:@"operation"]; [callbackQueue addOperation:operation]; @@ -1658,14 +1610,14 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { selector:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"]) object:request] autorelease]; operation.completionBlock = ^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if ([[request.userInfo objectForKey:@"operation"] isCancelled]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] - withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]]; - }); + @autoreleasepool { + if ([[request.userInfo objectForKey:@"operation"] isCancelled]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] + withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]]; + }); + } } - [pool drain]; }; [(NSMutableDictionary *)request.userInfo setObject:operation forKey:@"operation"]; [callbackQueue addOperation:operation]; @@ -1673,253 +1625,188 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { } - (void)requestFailed:(ASIPithosRequest *)request { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [request.userInfo objectForKey:@"operation"]; - DLog(@"Request failed: %@", request.url); - if (operation.isCancelled) { - [pool drain]; - return; - } - if (request.isCancelled) { - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] - withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]]; - }); - [pool drain]; - return; - } - NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue]; - if (retries > 0) { - ASIPithosRequest *newRequest = (ASIPithosRequest *)[[PithosUtilities copyRequest:request] autorelease]; - [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; - [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithBool:NO] forKey:@"unexpectedResponseStatus"]; - [[newRequest.userInfo objectForKey:@"networkQueue"] addOperation:[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]]]; - } else { - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] - withMessage:[request.userInfo objectForKey:@"failedActivityMessage"]]; - }); - if ([[request.userInfo objectForKey:@"unexpectedResponseStatus"] boolValue]) - [PithosUtilities unexpectedResponseStatusAlertWithRequest:request]; - else - [PithosUtilities httpRequestErrorAlertWithRequest:request]; + @autoreleasepool { + NSOperation *operation = [request.userInfo objectForKey:@"operation"]; + DLog(@"Request failed: %@", request.url); + if (operation.isCancelled) + return; + if (request.isCancelled) { + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] + withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]]; + }); + return; + } + NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue]; + if (retries > 0) { + ASIPithosRequest *newRequest = (ASIPithosRequest *)[[PithosUtilities copyRequest:request] autorelease]; + [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; + [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithBool:NO] forKey:@"unexpectedResponseStatus"]; + [[newRequest.userInfo objectForKey:@"networkQueue"] addOperation:[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]]]; + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] + withMessage:[request.userInfo objectForKey:@"failedActivityMessage"]]; + }); + if ([[request.userInfo objectForKey:@"unexpectedResponseStatus"] boolValue]) + [PithosUtilities unexpectedResponseStatusAlertWithRequest:request]; + else + [PithosUtilities httpRequestErrorAlertWithRequest:request]; + } } - [pool drain]; } - (void)downloadObjectFinished:(ASIPithosObjectRequest *)objectRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; - DLog(@"Download finished: %@", objectRequest.url); - if (operation.isCancelled) { - [self requestFailed:objectRequest]; - } else if (objectRequest.responseStatusCode == 200) { - NSString *filePath = [objectRequest.userInfo objectForKey:@"filePath"]; - PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"]; - NSUInteger totalBytes = activity.totalBytes; - - // XXX change contentLength to objectContentLength if it is fixed in the server - if ([objectRequest contentLength] == 0) { - // The check above was: - // if (([objectRequest contentLength] == 0) && ![PithosUtilities isContentTypeDirectory:[objectRequest contentType]]) { - // I checked for directory content types in order not to create a file in place of a directory, - // but this callback method is not called in the case of a directory download. - // It maybe the case though, when downloading an old version of an object, is of a directory content type. - // In this case, a file should be created. This is actually a feature that allows you to hide data in a directory object. - DLog(@"Downloaded 0 bytes"); - NSFileManager *fileManager = [NSFileManager defaultManager]; - if (![fileManager fileExistsAtPath:filePath]) { - if (![fileManager createFileAtPath:filePath contents:nil attributes:nil]) { - dispatch_async(dispatch_get_main_queue(), ^{ - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - [alert setMessageText:@"Create File Error"]; - [alert setInformativeText:[NSString stringWithFormat:@"Cannot create zero length file at %@", filePath]]; - [alert addButtonWithTitle:@"OK"]; - [alert runModal]; - }); + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"Download finished: %@", objectRequest.url); + if (operation.isCancelled) { + [self requestFailed:objectRequest]; + } else if (objectRequest.responseStatusCode == 200) { + NSString *filePath = [objectRequest.userInfo objectForKey:@"filePath"]; + PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"]; + NSUInteger totalBytes = activity.totalBytes; + + // XXX change contentLength to objectContentLength if it is fixed in the server + if ([objectRequest contentLength] == 0) { + // The check above was: + // if (([objectRequest contentLength] == 0) && ![PithosUtilities isContentTypeDirectory:[objectRequest contentType]]) { + // I checked for directory content types in order not to create a file in place of a directory, + // but this callback method is not called in the case of a directory download. + // It maybe the case though, when downloading an old version of an object, is of a directory content type. + // In this case, a file should be created. This is actually a feature that allows you to hide data in a directory object. + DLog(@"Downloaded 0 bytes"); + NSFileManager *fileManager = [NSFileManager defaultManager]; + if (![fileManager fileExistsAtPath:filePath]) { + if (![fileManager createFileAtPath:filePath contents:nil attributes:nil]) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + [alert setMessageText:@"Create File Error"]; + [alert setInformativeText:[NSString stringWithFormat:@"Cannot create zero length file at %@", filePath]]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + }); + } } } + + NSUInteger currentBytes = [objectRequest objectContentLength]; + if (currentBytes == 0) + currentBytes = totalBytes; + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:activity + withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] + totalBytes:totalBytes + currentBytes:currentBytes]; + }); + } else { + [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; + [self requestFailed:objectRequest]; } - - NSUInteger currentBytes = [objectRequest objectContentLength]; - if (currentBytes == 0) - currentBytes = totalBytes; - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:activity - withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] - totalBytes:totalBytes - currentBytes:currentBytes]; - }); - } else { - [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; - [self requestFailed:objectRequest]; } - [pool drain]; } - (void)uploadDirectoryObjectFinished:(ASIPithosObjectRequest *)objectRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; - DLog(@"Upload directory object finished: %@", objectRequest.url); - if (operation.isCancelled) { - [self requestFailed:objectRequest]; - } else if (objectRequest.responseStatusCode == 201) { - DLog(@"Directory object created: %@", objectRequest.url); - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] - withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]]; - for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) { - [node forceRefresh]; - } - for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) { - [node refresh]; - } - if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue]) - [self forceRefresh:self]; - else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue]) - [self refresh:self]; - }); - } else { - [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; - [self requestFailed:objectRequest]; + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"Upload directory object finished: %@", objectRequest.url); + if (operation.isCancelled) { + [self requestFailed:objectRequest]; + } else if (objectRequest.responseStatusCode == 201) { + DLog(@"Directory object created: %@", objectRequest.url); + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] + withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]]; + for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) { + [node forceRefresh]; + } + for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) { + [node refresh]; + } + if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue]) + [self forceRefresh:self]; + else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue]) + [self refresh:self]; + }); + } else { + [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; + [self requestFailed:objectRequest]; + } } - [pool drain]; } - (void)uploadObjectUsingHashMapFinished:(ASIPithosObjectRequest *)objectRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; - DLog(@"Upload using hashmap finished: %@", objectRequest.url); - NSString *fileName = [objectRequest.userInfo objectForKey:@"fileName"]; - PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"]; - NSUInteger totalBytes = activity.totalBytes; - NSUInteger currentBytes = activity.currentBytes; - if (operation.isCancelled) { - [self requestFailed:objectRequest]; - } else if (objectRequest.responseStatusCode == 201) { - DLog(@"Object created: %@", objectRequest.url); - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:activity - withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] - totalBytes:totalBytes - currentBytes:totalBytes]; - for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) { - [node forceRefresh]; - } - for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) { - [node refresh]; - } - if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue]) - [self forceRefresh:self]; - else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue]) - [self refresh:self]; - }); - } else if (objectRequest.responseStatusCode == 409) { - NSUInteger iteration = [[objectRequest.userInfo objectForKey:@"iteration"] unsignedIntegerValue]; - if (iteration == 0) { - DLog(@"Upload iteration limit reached: %@", objectRequest.url); + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"Upload using hashmap finished: %@", objectRequest.url); + NSString *fileName = [objectRequest.userInfo objectForKey:@"fileName"]; + PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"]; + NSUInteger totalBytes = activity.totalBytes; + NSUInteger currentBytes = activity.currentBytes; + if (operation.isCancelled) { + [self requestFailed:objectRequest]; + } else if (objectRequest.responseStatusCode == 201) { + DLog(@"Object created: %@", objectRequest.url); dispatch_async(dispatch_get_main_queue(), ^{ [activityFacility endActivity:activity - withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]]; - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - [alert setMessageText:@"Upload Timeout"]; - [alert setInformativeText:[NSString stringWithFormat:@"Upload iteration limit reached for object with path '%@'", - [objectRequest.userInfo objectForKey:@"objectName"]]]; - [alert addButtonWithTitle:@"OK"]; - [alert runModal]; + withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] + totalBytes:totalBytes + currentBytes:totalBytes]; + for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) { + [node forceRefresh]; + } + for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) { + [node refresh]; + } + if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue]) + [self forceRefresh:self]; + else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue]) + [self refresh:self]; }); - [pool drain]; - return; - } - DLog(@"object is missing hashes: %@", objectRequest.url); - NSIndexSet *missingBlocks = [PithosUtilities missingBlocksForHashes:[objectRequest.userInfo objectForKey:@"hashes"] - withMissingHashes:[objectRequest hashes]]; - NSUInteger blockSize = [[objectRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue]; - if (totalBytes >= [missingBlocks count]*blockSize) - currentBytes = totalBytes - [missingBlocks count]*blockSize; - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility updateActivity:activity - withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (totalBytes ? (100*(currentBytes + 0.0)/(totalBytes + 0.0)) : 100)] - totalBytes:totalBytes - currentBytes:currentBytes]; - }); - NSUInteger missingBlockIndex = [missingBlocks firstIndex]; - __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithPithos:pithos - containerName:[objectRequest.userInfo objectForKey:@"containerName"] - blockSize:blockSize - forFile:[objectRequest.userInfo objectForKey:@"filePath"] - missingBlockIndex:missingBlockIndex - sharingAccount:[objectRequest.userInfo objectForKey:@"sharingAccount"]]; - newContainerRequest.delegate = self; - newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - newContainerRequest.userInfo = objectRequest.userInfo; - [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:(--iteration)] forKey:@"iteration"]; - [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; - [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:missingBlocks forKey:@"missingBlocks"]; - [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"]; - [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadMissingBlockFinished:)) forKey:@"didFinishSelector"]; - [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){ - [activityFacility updateActivity:activity - withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)] - totalBytes:activity.totalBytes - currentBytes:(activity.currentBytes + size)]; - }]; - [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]]; - } else { - [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; - [self requestFailed:objectRequest]; - } - [pool drain]; -} - -- (void)uploadMissingBlockFinished:(ASIPithosContainerRequest *)containerRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [containerRequest.userInfo objectForKey:@"operation"]; - DLog(@"Upload of missing block finished: %@", containerRequest.url); - NSUInteger blockSize = [[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue]; - NSString *fileName = [containerRequest.userInfo objectForKey:@"fileName"]; - PithosActivity *activity = [containerRequest.userInfo objectForKey:@"activity"]; - if (operation.isCancelled) { - [self requestFailed:containerRequest]; - } else if (containerRequest.responseStatusCode == 202) { - NSIndexSet *missingBlocks = [containerRequest.userInfo objectForKey:@"missingBlocks"]; - NSUInteger missingBlockIndex = [[containerRequest.userInfo objectForKey:@"missingBlockIndex"] unsignedIntegerValue]; - missingBlockIndex = [missingBlocks indexGreaterThanIndex:missingBlockIndex]; - if (missingBlockIndex == NSNotFound) { - NSArray *hashes = [containerRequest.userInfo objectForKey:@"hashes"]; - ASIPithosObjectRequest *newObjectRequest = [PithosUtilities writeObjectDataRequestWithPithos:pithos - containerName:[containerRequest.userInfo objectForKey:@"containerName"] - objectName:[containerRequest.userInfo objectForKey:@"objectName"] - contentType:[containerRequest.userInfo objectForKey:@"contentType"] - blockSize:blockSize - blockHash:[containerRequest.userInfo objectForKey:@"blockHash"] - forFile:[containerRequest.userInfo objectForKey:@"filePath"] - checkIfExists:NO - hashes:&hashes - sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]]; - newObjectRequest.delegate = self; - newObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - newObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - newObjectRequest.userInfo = containerRequest.userInfo; - [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; - [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlocks"]; - [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlockIndex"]; - [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)) forKey:@"didFinishSelector"]; - [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]]; - } else { + } else if (objectRequest.responseStatusCode == 409) { + NSUInteger iteration = [[objectRequest.userInfo objectForKey:@"iteration"] unsignedIntegerValue]; + if (iteration == 0) { + DLog(@"Upload iteration limit reached: %@", objectRequest.url); + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:activity + withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]]; + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + [alert setMessageText:@"Upload Timeout"]; + [alert setInformativeText:[NSString stringWithFormat:@"Upload iteration limit reached for object with path '%@'", + [objectRequest.userInfo objectForKey:@"objectName"]]]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + }); + return; + } + DLog(@"object is missing hashes: %@", objectRequest.url); + NSIndexSet *missingBlocks = [PithosUtilities missingBlocksForHashes:[objectRequest.userInfo objectForKey:@"hashes"] + withMissingHashes:[objectRequest hashes]]; + NSUInteger blockSize = [[objectRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue]; + if (totalBytes >= [missingBlocks count]*blockSize) + currentBytes = totalBytes - [missingBlocks count]*blockSize; + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility updateActivity:activity + withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (totalBytes ? (100*(currentBytes + 0.0)/(totalBytes + 0.0)) : 100)] + totalBytes:totalBytes + currentBytes:currentBytes]; + }); + NSUInteger missingBlockIndex = [missingBlocks firstIndex]; __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithPithos:pithos - containerName:[containerRequest.userInfo objectForKey:@"containerName"] - blockSize:[[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue] - forFile:[containerRequest.userInfo objectForKey:@"filePath"] + containerName:[objectRequest.userInfo objectForKey:@"containerName"] + blockSize:blockSize + forFile:[objectRequest.userInfo objectForKey:@"filePath"] missingBlockIndex:missingBlockIndex - sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]]; + sharingAccount:[objectRequest.userInfo objectForKey:@"sharingAccount"]]; newContainerRequest.delegate = self; newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - newContainerRequest.userInfo = containerRequest.userInfo; + newContainerRequest.userInfo = objectRequest.userInfo; + [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:(--iteration)] forKey:@"iteration"]; [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; + [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:missingBlocks forKey:@"missingBlocks"]; [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"]; + [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadMissingBlockFinished:)) forKey:@"didFinishSelector"]; [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){ [activityFacility updateActivity:activity withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)] @@ -1927,96 +1814,157 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { currentBytes:(activity.currentBytes + size)]; }]; [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]]; + } else { + [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; + [self requestFailed:objectRequest]; + } + } +} + +- (void)uploadMissingBlockFinished:(ASIPithosContainerRequest *)containerRequest { + @autoreleasepool { + NSOperation *operation = [containerRequest.userInfo objectForKey:@"operation"]; + DLog(@"Upload of missing block finished: %@", containerRequest.url); + NSUInteger blockSize = [[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue]; + NSString *fileName = [containerRequest.userInfo objectForKey:@"fileName"]; + PithosActivity *activity = [containerRequest.userInfo objectForKey:@"activity"]; + if (operation.isCancelled) { + [self requestFailed:containerRequest]; + } else if (containerRequest.responseStatusCode == 202) { + NSIndexSet *missingBlocks = [containerRequest.userInfo objectForKey:@"missingBlocks"]; + NSUInteger missingBlockIndex = [[containerRequest.userInfo objectForKey:@"missingBlockIndex"] unsignedIntegerValue]; + missingBlockIndex = [missingBlocks indexGreaterThanIndex:missingBlockIndex]; + if (missingBlockIndex == NSNotFound) { + NSArray *hashes = [containerRequest.userInfo objectForKey:@"hashes"]; + ASIPithosObjectRequest *newObjectRequest = [PithosUtilities writeObjectDataRequestWithPithos:pithos + containerName:[containerRequest.userInfo objectForKey:@"containerName"] + objectName:[containerRequest.userInfo objectForKey:@"objectName"] + contentType:[containerRequest.userInfo objectForKey:@"contentType"] + blockSize:blockSize + blockHash:[containerRequest.userInfo objectForKey:@"blockHash"] + forFile:[containerRequest.userInfo objectForKey:@"filePath"] + checkIfExists:NO + hashes:&hashes + sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]]; + newObjectRequest.delegate = self; + newObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + newObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + newObjectRequest.userInfo = containerRequest.userInfo; + [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; + [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlocks"]; + [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlockIndex"]; + [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)) forKey:@"didFinishSelector"]; + [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]]; + } else { + __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithPithos:pithos + containerName:[containerRequest.userInfo objectForKey:@"containerName"] + blockSize:[[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue] + forFile:[containerRequest.userInfo objectForKey:@"filePath"] + missingBlockIndex:missingBlockIndex + sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]]; + newContainerRequest.delegate = self; + newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + newContainerRequest.userInfo = containerRequest.userInfo; + [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; + [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"]; + [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){ + [activityFacility updateActivity:activity + withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)] + totalBytes:activity.totalBytes + currentBytes:(activity.currentBytes + size)]; + }]; + [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]]; + } + } else { + [(NSMutableDictionary *)(containerRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; + [self requestFailed:containerRequest]; } - } else { - [(NSMutableDictionary *)(containerRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; - [self requestFailed:containerRequest]; } - [pool drain]; } - (void)moveFinished:(ASIPithosObjectRequest *)objectRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; - DLog(@"Move object finished: %@", objectRequest.url); - if (operation.isCancelled) { - [self requestFailed:objectRequest]; - } else if (objectRequest.responseStatusCode == 201) { - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] - withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]]; - for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) { - [node forceRefresh]; - } - for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) { - [node refresh]; - } - if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue]) - [self forceRefresh:self]; - else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue]) - [self refresh:self]; - }); - } else { - [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; - [self requestFailed:objectRequest]; + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"Move object finished: %@", objectRequest.url); + if (operation.isCancelled) { + [self requestFailed:objectRequest]; + } else if (objectRequest.responseStatusCode == 201) { + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] + withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]]; + for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) { + [node forceRefresh]; + } + for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) { + [node refresh]; + } + if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue]) + [self forceRefresh:self]; + else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue]) + [self refresh:self]; + }); + } else { + [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; + [self requestFailed:objectRequest]; + } } - [pool drain]; } - (void)copyFinished:(ASIPithosObjectRequest *)objectRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; - DLog(@"Copy object finished: %@", objectRequest.url); - if (operation.isCancelled) { - [self requestFailed:objectRequest]; - } else if (objectRequest.responseStatusCode == 201) { - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] - withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]]; - for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) { - [node forceRefresh]; - } - for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) { - [node refresh]; - } - if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue]) - [self forceRefresh:self]; - else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue]) - [self refresh:self]; - }); - } else { - [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; - [self requestFailed:objectRequest]; + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"Copy object finished: %@", objectRequest.url); + if (operation.isCancelled) { + [self requestFailed:objectRequest]; + } else if (objectRequest.responseStatusCode == 201) { + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] + withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]]; + for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) { + [node forceRefresh]; + } + for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) { + [node refresh]; + } + if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue]) + [self forceRefresh:self]; + else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue]) + [self refresh:self]; + }); + } else { + [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; + [self requestFailed:objectRequest]; + } } - [pool drain]; } - (void)deleteObjectFinished:(ASIPithosObjectRequest *)objectRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; - DLog(@"Delete object finished: %@", objectRequest.url); - if (operation.isCancelled) { - [self requestFailed:objectRequest]; - } else if (objectRequest.responseStatusCode == 204) { - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] - withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]]; - for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) { - [node forceRefresh]; - } - for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) { - [node refresh]; - } - if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue]) - [self forceRefresh:self]; - else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue]) - [self refresh:self]; - }); - } else { - [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; - [self requestFailed:objectRequest]; + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"Delete object finished: %@", objectRequest.url); + if (operation.isCancelled) { + [self requestFailed:objectRequest]; + } else if (objectRequest.responseStatusCode == 204) { + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] + withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]]; + for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) { + [node forceRefresh]; + } + for (PithosNode *node in [objectRequest.userInfo objectForKey:@"refreshNodes"]) { + [node refresh]; + } + if ([[objectRequest.userInfo objectForKey:@"forceRefresh"] boolValue]) + [self forceRefresh:self]; + else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue]) + [self refresh:self]; + }); + } else { + [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"]; + [self requestFailed:objectRequest]; + } } - [pool drain]; } #pragma mark - @@ -2252,9 +2200,9 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { self.clipboardNodes = nil; } else if (clipboardCopy || ![menuNode isEqualTo:clipboardParentNode]) { if (clipboardNodesCount == 1) - menuItemTitle = [NSString stringWithString:@"Paste Item"]; + menuItemTitle = @"Paste Item"; else - menuItemTitle = [NSString stringWithString:@"Paste Items"]; + menuItemTitle = @"Paste Items"; [menu addItem:[NSMenuItem separatorItem]]; menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuPaste:) keyEquivalent:@""] autorelease]; [menuItem setRepresentedObject:menuNode]; @@ -2332,9 +2280,9 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { self.clipboardNodes = nil; } else if (clipboardCopy || ![firstMenuNode isEqualTo:clipboardParentNode]) { if (clipboardNodesCount == 1) - menuItemTitle = [NSString stringWithString:@"Paste Item"]; + menuItemTitle = @"Paste Item"; else - menuItemTitle = [NSString stringWithString:@"Paste Items"]; + menuItemTitle = @"Paste Items"; menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:@selector(menuPaste:) keyEquivalent:@""] autorelease]; [menuItem setRepresentedObject:firstMenuNode]; [menu addItem:menuItem]; @@ -2419,108 +2367,100 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { if ([node class] == [PithosContainerNode class]) { // Operation: Create (upload) a new root application/directory object __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (operation.isCancelled) { - [pool drain]; - return; - } - NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos - containerName:node.pithosContainer.name - subdirName:@"untitled folder"]; - NSString *fileName = [safeObjectName lastPathComponent]; - if (operation.isCancelled) { - [pool drain]; - return; + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos + containerName:node.pithosContainer.name + subdirName:@"untitled folder"]; + NSString *fileName = [safeObjectName lastPathComponent]; + if (operation.isCancelled) + return; + ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos + containerName:node.pithosContainer.name + objectName:safeObjectName + eTag:nil + contentType:@"application/directory" + contentEncoding:nil + contentDisposition:nil + manifest:nil + sharing:nil + isPublic:ASIPithosObjectRequestPublicIgnore + metadata:nil + data:[NSData data]]; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", fileName]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory + message:messagePrefix]; + objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + fileName, @"fileName", + [NSArray arrayWithObject:node], @"refreshNodes", + [NSNumber numberWithBool:YES], @"refresh", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + uploadNetworkQueue, @"networkQueue", + @"upload", @"operationType", + nil]; + [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } - ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos - containerName:node.pithosContainer.name - objectName:safeObjectName - eTag:nil - contentType:@"application/directory" - contentEncoding:nil - contentDisposition:nil - manifest:nil - sharing:nil - isPublic:ASIPithosObjectRequestPublicIgnore - metadata:nil - data:[NSData data]]; - objectRequest.delegate = self; - objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", fileName]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory - message:messagePrefix]; - objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: - fileName, @"fileName", - [NSArray arrayWithObject:node], @"refreshNodes", - [NSNumber numberWithBool:YES], @"refresh", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - uploadNetworkQueue, @"networkQueue", - @"upload", @"operationType", - nil]; - [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; - [pool drain]; }]; [uploadQueue addOperation:operation]; } else if (([node class] == [PithosSubdirNode class]) && (node.pithosObject.subdir || ![node.pithosObject.name hasSuffix:@"/"])) { // Operation: Create (upload) a new aplication/directory object __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (operation.isCancelled) { - [pool drain]; - return; - } - NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos - containerName:node.pithosContainer.name - subdirName:[node.pithosObject.name stringByAppendingPathComponent:@"untitled folder"]]; - NSString *fileName = [safeObjectName lastPathComponent]; - if (operation.isCancelled) { - [pool drain]; - return; + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos + containerName:node.pithosContainer.name + subdirName:[node.pithosObject.name stringByAppendingPathComponent:@"untitled folder"]]; + NSString *fileName = [safeObjectName lastPathComponent]; + if (operation.isCancelled) + return; + ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos + containerName:node.pithosContainer.name + objectName:safeObjectName + eTag:nil + contentType:@"application/directory" + contentEncoding:nil + contentDisposition:nil + manifest:nil + sharing:nil + isPublic:ASIPithosObjectRequestPublicIgnore + metadata:nil + data:[NSData data]]; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", fileName]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory + message:messagePrefix]; + objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + fileName, @"fileName", + [NSArray arrayWithObject:node], @"refreshNodes", + [NSNumber numberWithBool:YES], @"refresh", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + uploadNetworkQueue, @"networkQueue", + @"upload", @"operationType", + nil]; + [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } - ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos - containerName:node.pithosContainer.name - objectName:safeObjectName - eTag:nil - contentType:@"application/directory" - contentEncoding:nil - contentDisposition:nil - manifest:nil - sharing:nil - isPublic:ASIPithosObjectRequestPublicIgnore - metadata:nil - data:[NSData data]]; - objectRequest.delegate = self; - objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", fileName]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory - message:messagePrefix]; - objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: - fileName, @"fileName", - [NSArray arrayWithObject:node], @"refreshNodes", - [NSNumber numberWithBool:YES], @"refresh", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - uploadNetworkQueue, @"networkQueue", - @"upload", @"operationType", - nil]; - [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; - [pool drain]; }]; [uploadQueue addOperation:operation]; } @@ -2538,7 +2478,7 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { if (([nodes count] == 1) && ([firstNode class] == [PithosObjectNode class])) { NSSavePanel *save = [NSSavePanel savePanel]; save.nameFieldStringValue = firstNode.displayName; - int result = [save runModal]; + NSInteger result = [save runModal]; if (result == NSOKButton) { NSString *destinationPath = save.URL.path; NSString *directoryPath = [destinationPath stringByDeletingLastPathComponent]; @@ -2554,7 +2494,7 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { open.canChooseFiles = NO; open.canChooseDirectories = YES; open.canCreateDirectories = YES; - int result = [open runModal]; + NSInteger result = [open runModal]; if (result == NSOKButton) { NSString *directoryPath = open.URL.path; for (PithosNode *node in nodes) { @@ -2570,91 +2510,83 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) { // Operation: Delete an object or subdir/ node __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (operation.isCancelled) { - [pool drain]; - return; - } - NSString *fileName = [node.pithosObject.name lastPathComponent]; - if ([node.pithosObject.name hasSuffix:@"/"]) - fileName = [fileName stringByAppendingString:@"/"]; - ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos - containerName:node.pithosContainer.name - objectName:node.pithosObject.name]; - if (operation.isCancelled) { - [pool drain]; - return; + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *fileName = [node.pithosObject.name lastPathComponent]; + if ([node.pithosObject.name hasSuffix:@"/"]) + fileName = [fileName stringByAppendingString:@"/"]; + ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos + containerName:node.pithosContainer.name + objectName:node.pithosObject.name]; + if (operation.isCancelled) + return; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Deleting '%@'", fileName]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete + message:messagePrefix]; + objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + fileName, @"fileName", + [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + deleteNetworkQueue, @"networkQueue", + @"delete", @"operationType", + nil]; + [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } - objectRequest.delegate = self; - objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Deleting '%@'", fileName]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete - message:messagePrefix]; - objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: - fileName, @"fileName", - [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - deleteNetworkQueue, @"networkQueue", - @"delete", @"operationType", - nil]; - [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; - [pool drain]; }]; [deleteQueue addOperation:operation]; } else if ([node class] == [PithosSubdirNode class]) { // Operation: Delete a subdir node and its descendants // The resulting ASIPithosObjectRequests are chained through dependencies __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (operation.isCancelled) { - [pool drain]; - return; - } - NSArray *objectRequests = [PithosUtilities deleteObjectRequestsForSubdirWithPithos:pithos - containerName:node.pithosContainer.name - objectName:node.pithosObject.name]; - if (!operation.isCancelled && objectRequests) { - ASIPithosObjectRequest *previousObjectRequest = nil; - for (ASIPithosObjectRequest *objectRequest in objectRequests) { - if (operation.isCancelled) { - [pool drain]; - return; + @autoreleasepool { + if (operation.isCancelled) + return; + NSArray *objectRequests = [PithosUtilities deleteObjectRequestsForSubdirWithPithos:pithos + containerName:node.pithosContainer.name + objectName:node.pithosObject.name]; + if (!operation.isCancelled && objectRequests) { + ASIPithosObjectRequest *previousObjectRequest = nil; + for (ASIPithosObjectRequest *objectRequest in objectRequests) { + if (operation.isCancelled) + return; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Deleting '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete + message:messagePrefix]; + [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: + [NSDictionary dictionaryWithObjectsAndKeys: + [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + deleteNetworkQueue, @"networkQueue", + @"delete", @"operationType", + nil]]; + if (previousObjectRequest) + [objectRequest addDependency:previousObjectRequest]; + previousObjectRequest = objectRequest; + [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } - objectRequest.delegate = self; - objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Deleting '%@'", [objectRequest.userInfo objectForKey:@"fileName"]]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete - message:messagePrefix]; - [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: - [NSDictionary dictionaryWithObjectsAndKeys: - [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - deleteNetworkQueue, @"networkQueue", - @"delete", @"operationType", - nil]]; - if (previousObjectRequest) - [objectRequest addDependency:previousObjectRequest]; - previousObjectRequest = objectRequest; - [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } } - [pool drain]; }]; [deleteQueue addOperation:operation]; } @@ -2669,78 +2601,20 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { [node.pithosObject.name hasSuffix:@"/"])) { // Operation: Move to trash an object or subdir/ node __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (operation.isCancelled) { - [pool drain]; - return; - } - NSString *safeObjectName = [PithosUtilities safeObjectNameForPithos:pithos - containerName:@"trash" - objectName:node.pithosObject.name]; - if (!operation.isCancelled && safeObjectName) { - ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos - containerName:node.pithosContainer.name - objectName:node.pithosObject.name - destinationContainerName:@"trash" - destinationObjectName:safeObjectName - checkIfExists:NO]; - if (!operation.isCancelled && objectRequest) { - objectRequest.delegate = self; - objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", - [objectRequest.userInfo objectForKey:@"sourceContainerName"], - [objectRequest.userInfo objectForKey:@"sourceObjectName"], - [objectRequest.userInfo objectForKey:@"destinationContainerName"], - [objectRequest.userInfo objectForKey:@"destinationObjectName"]]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove - message:messagePrefix]; - [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: - [NSDictionary dictionaryWithObjectsAndKeys: - [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - moveNetworkQueue, @"networkQueue", - @"move", @"operationType", - nil]]; - [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; - } - } - [pool drain]; - }]; - [moveQueue addOperation:operation]; - } else if ([node class] == [PithosSubdirNode class]) { - // Operation: Move to trash a subdir node and its descendants - // The resulting ASIPithosObjectRequests are chained through dependencies - __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (operation.isCancelled) { - [pool drain]; - return; - } - NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos - containerName:@"trash" - subdirName:node.pithosObject.name]; - if (!operation.isCancelled && safeObjectName) { - NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithPithos:pithos - containerName:node.pithosContainer.name - objectName:node.pithosObject.name - destinationContainerName:@"trash" - destinationObjectName:safeObjectName - checkIfExists:NO]; - if (!operation.isCancelled && objectRequests) { - ASIPithosObjectRequest *previousObjectRequest = nil; - for (ASIPithosObjectRequest *objectRequest in objectRequests) { - if (operation.isCancelled) { - [pool drain]; - return; - } + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *safeObjectName = [PithosUtilities safeObjectNameForPithos:pithos + containerName:@"trash" + objectName:node.pithosObject.name]; + if (!operation.isCancelled && safeObjectName) { + ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos + containerName:node.pithosContainer.name + objectName:node.pithosObject.name + destinationContainerName:@"trash" + destinationObjectName:safeObjectName + checkIfExists:NO]; + if (!operation.isCancelled && objectRequest) { objectRequest.delegate = self; objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); @@ -2765,14 +2639,66 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column { moveNetworkQueue, @"networkQueue", @"move", @"operationType", nil]]; - if (previousObjectRequest) - [objectRequest addDependency:previousObjectRequest]; - previousObjectRequest = objectRequest; [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; } } } - [pool drain]; + }]; + [moveQueue addOperation:operation]; + } else if ([node class] == [PithosSubdirNode class]) { + // Operation: Move to trash a subdir node and its descendants + // The resulting ASIPithosObjectRequests are chained through dependencies + __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + @autoreleasepool { + if (operation.isCancelled) + return; + NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos + containerName:@"trash" + subdirName:node.pithosObject.name]; + if (!operation.isCancelled && safeObjectName) { + NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithPithos:pithos + containerName:node.pithosContainer.name + objectName:node.pithosObject.name + destinationContainerName:@"trash" + destinationObjectName:safeObjectName + checkIfExists:NO]; + if (!operation.isCancelled && objectRequests) { + ASIPithosObjectRequest *previousObjectRequest = nil; + for (ASIPithosObjectRequest *objectRequest in objectRequests) { + if (operation.isCancelled) + return; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", + [objectRequest.userInfo objectForKey:@"sourceContainerName"], + [objectRequest.userInfo objectForKey:@"sourceObjectName"], + [objectRequest.userInfo objectForKey:@"destinationContainerName"], + [objectRequest.userInfo objectForKey:@"destinationObjectName"]]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove + message:messagePrefix]; + [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary: + [NSDictionary dictionaryWithObjectsAndKeys: + [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + moveNetworkQueue, @"networkQueue", + @"move", @"operationType", + nil]]; + if (previousObjectRequest) + [objectRequest addDependency:previousObjectRequest]; + previousObjectRequest = objectRequest; + [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; + } + } + } + } }]; [moveQueue addOperation:operation]; } diff --git a/pithos-macos/PithosContainerNode.m b/pithos-macos/PithosContainerNode.m index 347663f..d3a0890 100644 --- a/pithos-macos/PithosContainerNode.m +++ b/pithos-macos/PithosContainerNode.m @@ -221,72 +221,95 @@ static NSImage *sharedIcon = nil; #pragma mark ASIHTTPRequestDelegate - (void)containerRequestFailed:(ASIPithosContainerRequest *)request { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSString *message; - NSError *error = [containerRequest error]; - if (error) - message = [NSString stringWithFormat:@"Container listing %@ failed: %@", containerRequest.url, [error localizedDescription]]; - else - message = [NSString stringWithFormat:@"Container listing %@ failed: (%d) %@", - containerRequest.url, containerRequest.responseStatusCode, containerRequest.responseStatusMessage]; - dispatch_async(dispatch_get_main_queue(), ^{ - [[PithosActivityFacility defaultPithosActivityFacility] startAndEndActivityWithType:PithosActivityOther message:message]; - }); - NSUInteger retries = [[containerRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue]; - if (retries > 0) { - ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[PithosUtilities copyRequest:containerRequest]; - [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; - [containerRequest release]; - containerRequest = newContainerRequest; - [[PithosUtilities prepareRequest:containerRequest priority:[[containerRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous]; - } else { - [newChildren release]; - newChildren = nil; - [containerRequest release]; - containerRequest = nil; - [objects release]; - objects = nil; - forcedRefresh = NO; - @synchronized(self) { - freshness = PithosNodeStateRefreshNeeded; + @autoreleasepool { + NSString *message; + NSError *error = [containerRequest error]; + if (error) + message = [NSString stringWithFormat:@"Container listing %@ failed: %@", containerRequest.url, [error localizedDescription]]; + else + message = [NSString stringWithFormat:@"Container listing %@ failed: (%d) %@", + containerRequest.url, containerRequest.responseStatusCode, containerRequest.responseStatusMessage]; + dispatch_async(dispatch_get_main_queue(), ^{ + [[PithosActivityFacility defaultPithosActivityFacility] startAndEndActivityWithType:PithosActivityOther message:message]; + }); + NSUInteger retries = [[containerRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue]; + if (retries > 0) { + ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[PithosUtilities copyRequest:containerRequest]; + [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; + [containerRequest release]; + containerRequest = newContainerRequest; + [[PithosUtilities prepareRequest:containerRequest priority:[[containerRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous]; + } else { + [newChildren release]; + newChildren = nil; + [containerRequest release]; + containerRequest = nil; + [objects release]; + objects = nil; + forcedRefresh = NO; + @synchronized(self) { + freshness = PithosNodeStateRefreshNeeded; + } } } - [pool drain]; } - (void)containerRequestFinished:(ASIPithosContainerRequest *)request { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - DLog(@"List container finished: %@", [containerRequest url]); - DLog(@"Cached: %d", [containerRequest didUseCachedResponse]); - if (containerRequest.responseStatusCode == 200) { - if ((pithosContainer.blockHash == nil) || (pithosContainer.blockSize == 0)) { - pithosContainer.blockHash = [containerRequest blockHash]; - pithosContainer.blockSize = [containerRequest blockSize]; - } - - NSArray *someObjects = [containerRequest objects]; - if (objects == nil) { - objects = [[NSMutableArray alloc] initWithArray:someObjects]; - } else { - [objects addObjectsFromArray:someObjects]; - } - if ([someObjects count] < 10000) { - if (!containerRequest.didUseCachedResponse || ([objects count] != [someObjects count]) || !children) { - // Save new children - DLog(@"using newChildren"); - newChildren = [[NSMutableArray alloc] init]; - NSArray *objectNames = [objects valueForKey:@"name"]; - NSMutableIndexSet *keptNodes = [NSMutableIndexSet indexSet]; - BOOL isSubdirNode = ([self class] == [PithosSubdirNode class]); - for (ASIPithosObject *object in objects) { - if (!isSubdirNode || - ([object.name hasPrefix:[((PithosSubdirNode *)self).prefix stringByAppendingString:@"/"]] && - ([object.name length] > [((PithosSubdirNode *)self).prefix length] + 1))) { - // The check above removes false objects due to trailing slash or same prefix - if (object.subdir) { - NSUInteger sameNameObjectIndex = [objectNames indexOfObject:[object.name substringToIndex:([object.name length] - 1)]]; - if ((sameNameObjectIndex == NSNotFound) || - ![PithosUtilities isContentTypeDirectory:[[objects objectAtIndex:sameNameObjectIndex] contentType]]) { + @autoreleasepool { + DLog(@"List container finished: %@", [containerRequest url]); + DLog(@"Cached: %d", [containerRequest didUseCachedResponse]); + if (containerRequest.responseStatusCode == 200) { + if ((pithosContainer.blockHash == nil) || (pithosContainer.blockSize == 0)) { + pithosContainer.blockHash = [containerRequest blockHash]; + pithosContainer.blockSize = [containerRequest blockSize]; + } + + NSArray *someObjects = [containerRequest objects]; + if (objects == nil) { + objects = [[NSMutableArray alloc] initWithArray:someObjects]; + } else { + [objects addObjectsFromArray:someObjects]; + } + if ([someObjects count] < 10000) { + if (!containerRequest.didUseCachedResponse || ([objects count] != [someObjects count]) || !children) { + // Save new children + DLog(@"using newChildren"); + newChildren = [[NSMutableArray alloc] init]; + NSArray *objectNames = [objects valueForKey:@"name"]; + NSMutableIndexSet *keptNodes = [NSMutableIndexSet indexSet]; + BOOL isSubdirNode = ([self class] == [PithosSubdirNode class]); + for (ASIPithosObject *object in objects) { + if (!isSubdirNode || + ([object.name hasPrefix:[((PithosSubdirNode *)self).prefix stringByAppendingString:@"/"]] && + ([object.name length] > [((PithosSubdirNode *)self).prefix length] + 1))) { + // The check above removes false objects due to trailing slash or same prefix + if (object.subdir) { + NSUInteger sameNameObjectIndex = [objectNames indexOfObject:[object.name substringToIndex:([object.name length] - 1)]]; + if ((sameNameObjectIndex == NSNotFound) || + ![PithosUtilities isContentTypeDirectory:[[objects objectAtIndex:sameNameObjectIndex] contentType]]) { + PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithos:pithos + pithosContainer:pithosContainer + pithosObject:object] autorelease]; + node.parent = self; + node.shared = shared; + node.sharingAccount = sharingAccount; + node.inheritChildrenUpdatedNotificationName = inheritChildrenUpdatedNotificationName; + if (children) { + NSUInteger oldIndex = [children indexOfObject:node]; + if (oldIndex != NSNotFound) { + // Use the same pointer value, if possible + node = [children objectAtIndex:oldIndex]; + node.pithosContainer = pithosContainer; +// node.pithosObject = object; + [node setLimitedPithosObject:object]; + [keptNodes addIndex:oldIndex]; + } + } + if (sharingAccount) + node.pithosObject.allowedTo = @"read"; + [newChildren addObject:node]; + } + } else if ([PithosUtilities isContentTypeDirectory:object.contentType]) { PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithos:pithos pithosContainer:pithosContainer pithosObject:object] autorelease]; @@ -300,66 +323,82 @@ static NSImage *sharedIcon = nil; // Use the same pointer value, if possible node = [children objectAtIndex:oldIndex]; node.pithosContainer = pithosContainer; -// node.pithosObject = object; +// node.pithosObject = object; [node setLimitedPithosObject:object]; [keptNodes addIndex:oldIndex]; } } - if (sharingAccount) - node.pithosObject.allowedTo = [NSString stringWithString:@"read"]; [newChildren addObject:node]; - } - } else if ([PithosUtilities isContentTypeDirectory:object.contentType]) { - PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithos:pithos - pithosContainer:pithosContainer - pithosObject:object] autorelease]; - node.parent = self; - node.shared = shared; - node.sharingAccount = sharingAccount; - node.inheritChildrenUpdatedNotificationName = inheritChildrenUpdatedNotificationName; - if (children) { - NSUInteger oldIndex = [children indexOfObject:node]; - if (oldIndex != NSNotFound) { - // Use the same pointer value, if possible - node = [children objectAtIndex:oldIndex]; - node.pithosContainer = pithosContainer; -// node.pithosObject = object; - [node setLimitedPithosObject:object]; - [keptNodes addIndex:oldIndex]; - } - } - [newChildren addObject:node]; - } else { - PithosObjectNode *node = [[[PithosObjectNode alloc] initWithPithos:pithos - pithosContainer:pithosContainer - pithosObject:object] autorelease]; - node.parent = self; - node.shared = shared; - node.sharingAccount = sharingAccount; - node.inheritChildrenUpdatedNotificationName = inheritChildrenUpdatedNotificationName; - if (children) { - NSUInteger oldIndex = [children indexOfObject:node]; - if (oldIndex != NSNotFound) { - // Use the same pointer value, if possible - node = [children objectAtIndex:oldIndex]; - node.pithosContainer = pithosContainer; -// node.pithosObject = object; - [node setLimitedPithosObject:object]; - [keptNodes addIndex:oldIndex]; + } else { + PithosObjectNode *node = [[[PithosObjectNode alloc] initWithPithos:pithos + pithosContainer:pithosContainer + pithosObject:object] autorelease]; + node.parent = self; + node.shared = shared; + node.sharingAccount = sharingAccount; + node.inheritChildrenUpdatedNotificationName = inheritChildrenUpdatedNotificationName; + if (children) { + NSUInteger oldIndex = [children indexOfObject:node]; + if (oldIndex != NSNotFound) { + // Use the same pointer value, if possible + node = [children objectAtIndex:oldIndex]; + node.pithosContainer = pithosContainer; +// node.pithosObject = object; + [node setLimitedPithosObject:object]; + [keptNodes addIndex:oldIndex]; + } } + [newChildren addObject:node]; } - [newChildren addObject:node]; } } + [[children objectsAtIndexes: + [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])] indexesPassingTest:^(NSUInteger idx, BOOL *stop){ + if ([keptNodes containsIndex:idx]) + return NO; + return YES; + }]] makeObjectsPerformSelector:@selector(pithosNodeWillBeRemoved)]; + } + // Else cache was used and all results were fetched during this request, so existing children can be reused + [containerRequest release]; + containerRequest = nil; + [objects release]; + objects = nil; + forcedRefresh = NO; + @synchronized(self) { + freshness = PithosNodeStateRefreshFinished; } - [[children objectsAtIndexes: - [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])] indexesPassingTest:^(NSUInteger idx, BOOL *stop){ - if ([keptNodes containsIndex:idx]) - return NO; - return YES; - }]] makeObjectsPerformSelector:@selector(pithosNodeWillBeRemoved)]; + [self postChildrenUpdatedNotificationName]; + } else { + [containerRequest release]; + // Do an additional request to fetch more objects + containerRequest = [[ASIPithosContainerRequest listObjectsRequestWithPithos:pithos + containerName:pithosContainer.name + limit:0 + marker:[[someObjects lastObject] name] + prefix:prefix + delimiter:@"/" + path:nil + meta:nil + shared:shared + until:nil] retain]; + if (sharingAccount) + [containerRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos]; + else if (!forcedRefresh) + containerRequest.downloadCache = [ASIDownloadCache sharedCache]; + containerRequest.delegate = self; + containerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + containerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + containerRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(containerRequestFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(containerRequestFailed:)), @"didFailSelector", + nil]; + [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous]; } - // Else cache was used and all results were fetched during this request, so existing children can be reused + } else if (containerRequest.responseStatusCode == 304) { + // Container is not modified, so existing children can be reused [containerRequest release]; containerRequest = nil; [objects release]; @@ -370,106 +409,67 @@ static NSImage *sharedIcon = nil; } [self postChildrenUpdatedNotificationName]; } else { - [containerRequest release]; - // Do an additional request to fetch more objects - containerRequest = [[ASIPithosContainerRequest listObjectsRequestWithPithos:pithos - containerName:pithosContainer.name - limit:0 - marker:[[someObjects lastObject] name] - prefix:prefix - delimiter:@"/" - path:nil - meta:nil - shared:shared - until:nil] retain]; - if (sharingAccount) - [containerRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos]; - else if (!forcedRefresh) - containerRequest.downloadCache = [ASIDownloadCache sharedCache]; - containerRequest.delegate = self; - containerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - containerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - containerRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(containerRequestFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(containerRequestFailed:)), @"didFailSelector", - nil]; - [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous]; + [self containerRequestFailed:containerRequest]; } - } else if (containerRequest.responseStatusCode == 304) { - // Container is not modified, so existing children can be reused - [containerRequest release]; - containerRequest = nil; - [objects release]; - objects = nil; - forcedRefresh = NO; - @synchronized(self) { - freshness = PithosNodeStateRefreshFinished; - } - [self postChildrenUpdatedNotificationName]; - } else { - [self containerRequestFailed:containerRequest]; } - [pool drain]; } - (void)containerMetadataRequestFinished:(ASIPithosContainerRequest *)request { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - DLog(@"URL: %@", [request url]); - DLog(@"cached: %d", [request didUseCachedResponse]); - - if ([request isEqualTo:applyMetadataContainerRequest]) { - @synchronized(self) { - [applyMetadataContainerRequest release]; - applyMetadataContainerRequest = nil; - } - [self refreshInfo]; - } else if ([request isEqualTo:refreshMetadataContainerRequest]) { - [[pithosNodeInfoController window] makeFirstResponder:nil]; - self.pithosContainer = [refreshMetadataContainerRequest container]; - @synchronized(self) { - [refreshMetadataContainerRequest release]; - refreshMetadataContainerRequest = nil; - } - } - [pool drain]; -} - -- (void)containerMetadataRequestFailed:(ASIPithosContainerRequest *)request { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue]; - if (retries > 0) { - ASIPithosContainerRequest *newRequest = (ASIPithosContainerRequest *)[PithosUtilities copyRequest:request]; - [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; + @autoreleasepool { + DLog(@"URL: %@", [request url]); + DLog(@"cached: %d", [request didUseCachedResponse]); + if ([request isEqualTo:applyMetadataContainerRequest]) { @synchronized(self) { [applyMetadataContainerRequest release]; - applyMetadataContainerRequest = newRequest; + applyMetadataContainerRequest = nil; } + [self refreshInfo]; } else if ([request isEqualTo:refreshMetadataContainerRequest]) { + [[pithosNodeInfoController window] makeFirstResponder:nil]; + self.pithosContainer = [refreshMetadataContainerRequest container]; @synchronized(self) { [refreshMetadataContainerRequest release]; - refreshMetadataContainerRequest = newRequest; + refreshMetadataContainerRequest = nil; } } - [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous]; - } else { - if ([request isEqualTo:applyMetadataContainerRequest]) { - [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataContainerRequest]; - @synchronized(self) { - [applyMetadataContainerRequest release]; - applyMetadataContainerRequest = nil; + } +} + +- (void)containerMetadataRequestFailed:(ASIPithosContainerRequest *)request { + @autoreleasepool { + NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue]; + if (retries > 0) { + ASIPithosContainerRequest *newRequest = (ASIPithosContainerRequest *)[PithosUtilities copyRequest:request]; + [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; + if ([request isEqualTo:applyMetadataContainerRequest]) { + @synchronized(self) { + [applyMetadataContainerRequest release]; + applyMetadataContainerRequest = newRequest; + } + } else if ([request isEqualTo:refreshMetadataContainerRequest]) { + @synchronized(self) { + [refreshMetadataContainerRequest release]; + refreshMetadataContainerRequest = newRequest; + } } - } else if ([request isEqualTo:refreshMetadataContainerRequest]) { - [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataContainerRequest]; - @synchronized(self) { - [refreshMetadataContainerRequest release]; - refreshMetadataContainerRequest = nil; + [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous]; + } else { + if ([request isEqualTo:applyMetadataContainerRequest]) { + [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataContainerRequest]; + @synchronized(self) { + [applyMetadataContainerRequest release]; + applyMetadataContainerRequest = nil; + } + } else if ([request isEqualTo:refreshMetadataContainerRequest]) { + [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataContainerRequest]; + @synchronized(self) { + [refreshMetadataContainerRequest release]; + refreshMetadataContainerRequest = nil; + } } } } - [pool drain]; } #pragma mark - diff --git a/pithos-macos/PithosObjectNode.m b/pithos-macos/PithosObjectNode.m index a8fb309..4cbf638 100644 --- a/pithos-macos/PithosObjectNode.m +++ b/pithos-macos/PithosObjectNode.m @@ -161,83 +161,83 @@ #pragma mark ASIHTTPRequestDelegate - (void)objectRequestFinished:(ASIPithosObjectRequest *)request { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - DLog(@"URL: %@", [request url]); - DLog(@"cached: %d", [request didUseCachedResponse]); - - if ([request isEqualTo:applyMetadataObjectRequest]) { - int responseStatusCode = applyMetadataObjectRequest.responseStatusCode; - if (responseStatusCode != 202) - [PithosUtilities unexpectedResponseStatusAlertWithRequest:applyMetadataObjectRequest]; - @synchronized(self) { - [applyMetadataObjectRequest release]; - applyMetadataObjectRequest = nil; - } - if (responseStatusCode == 202) - [self refreshInfo]; - } else if ([request isEqualTo:refreshMetadataObjectRequest]) { - [[pithosNodeInfoController window] makeFirstResponder:nil]; - self.pithosObject = [refreshMetadataObjectRequest object]; - @synchronized(self) { - [refreshMetadataObjectRequest release]; - refreshMetadataObjectRequest = nil; - } - } else if ([request isEqualTo:refreshVersionsObjectRequest]) { - [[pithosNodeInfoController window] makeFirstResponder:nil]; - self.versions = [refreshVersionsObjectRequest versions]; - @synchronized(self) { - [refreshVersionsObjectRequest release]; - refreshVersionsObjectRequest = nil; - } - } - [pool drain]; -} - -- (void)objectRequestFailed:(ASIPithosObjectRequest *)request { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue]; - if (retries > 0) { - ASIPithosObjectRequest *newRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:request]; - [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; + @autoreleasepool { + DLog(@"URL: %@", [request url]); + DLog(@"cached: %d", [request didUseCachedResponse]); + if ([request isEqualTo:applyMetadataObjectRequest]) { - @synchronized(self) { - [applyMetadataObjectRequest release]; - applyMetadataObjectRequest = newRequest; - } - } else if ([request isEqualTo:refreshMetadataObjectRequest]) { - @synchronized(self) { - [refreshMetadataObjectRequest release]; - refreshMetadataObjectRequest = newRequest; - } - } else if ([request isEqualTo:refreshVersionsObjectRequest]) { - @synchronized(self) { - [refreshVersionsObjectRequest release]; - refreshVersionsObjectRequest = newRequest; - } - } - [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous]; - } else { - if ([request isEqualTo:applyMetadataObjectRequest]) { - [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataObjectRequest]; + int responseStatusCode = applyMetadataObjectRequest.responseStatusCode; + if (responseStatusCode != 202) + [PithosUtilities unexpectedResponseStatusAlertWithRequest:applyMetadataObjectRequest]; @synchronized(self) { [applyMetadataObjectRequest release]; applyMetadataObjectRequest = nil; } + if (responseStatusCode == 202) + [self refreshInfo]; } else if ([request isEqualTo:refreshMetadataObjectRequest]) { - [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataObjectRequest]; + [[pithosNodeInfoController window] makeFirstResponder:nil]; + self.pithosObject = [refreshMetadataObjectRequest object]; @synchronized(self) { [refreshMetadataObjectRequest release]; refreshMetadataObjectRequest = nil; } } else if ([request isEqualTo:refreshVersionsObjectRequest]) { - [PithosUtilities httpRequestErrorAlertWithRequest:refreshVersionsObjectRequest]; + [[pithosNodeInfoController window] makeFirstResponder:nil]; + self.versions = [refreshVersionsObjectRequest versions]; @synchronized(self) { [refreshVersionsObjectRequest release]; refreshVersionsObjectRequest = nil; } } } - [pool drain]; +} + +- (void)objectRequestFailed:(ASIPithosObjectRequest *)request { + @autoreleasepool { + NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue]; + if (retries > 0) { + ASIPithosObjectRequest *newRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:request]; + [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; + if ([request isEqualTo:applyMetadataObjectRequest]) { + @synchronized(self) { + [applyMetadataObjectRequest release]; + applyMetadataObjectRequest = newRequest; + } + } else if ([request isEqualTo:refreshMetadataObjectRequest]) { + @synchronized(self) { + [refreshMetadataObjectRequest release]; + refreshMetadataObjectRequest = newRequest; + } + } else if ([request isEqualTo:refreshVersionsObjectRequest]) { + @synchronized(self) { + [refreshVersionsObjectRequest release]; + refreshVersionsObjectRequest = newRequest; + } + } + [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous]; + } else { + if ([request isEqualTo:applyMetadataObjectRequest]) { + [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataObjectRequest]; + @synchronized(self) { + [applyMetadataObjectRequest release]; + applyMetadataObjectRequest = nil; + } + } else if ([request isEqualTo:refreshMetadataObjectRequest]) { + [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataObjectRequest]; + @synchronized(self) { + [refreshMetadataObjectRequest release]; + refreshMetadataObjectRequest = nil; + } + } else if ([request isEqualTo:refreshVersionsObjectRequest]) { + [PithosUtilities httpRequestErrorAlertWithRequest:refreshVersionsObjectRequest]; + @synchronized(self) { + [refreshVersionsObjectRequest release]; + refreshVersionsObjectRequest = nil; + } + } + } + } } #pragma mark - diff --git a/pithos-macos/PithosObjectNodeInfoController.m b/pithos-macos/PithosObjectNodeInfoController.m index 422fde4..b9e11bc 100644 --- a/pithos-macos/PithosObjectNodeInfoController.m +++ b/pithos-macos/PithosObjectNodeInfoController.m @@ -80,7 +80,7 @@ - (IBAction)downloadVersion:(id)sender { NSSavePanel *save = [NSSavePanel savePanel]; [save setNameFieldStringValue:node.displayName]; - int result = [save runModal]; + NSInteger result = [save runModal]; if (result == NSOKButton) { NSString *destinationPath = [[save URL] path]; NSString *directoryPath = [destinationPath stringByDeletingLastPathComponent]; diff --git a/pithos-macos/PithosSharingAccountsNode.m b/pithos-macos/PithosSharingAccountsNode.m index 5eb6984..6cc668c 100644 --- a/pithos-macos/PithosSharingAccountsNode.m +++ b/pithos-macos/PithosSharingAccountsNode.m @@ -140,7 +140,7 @@ - (NSString *)displayName { if (displayName == nil) - return [NSString stringWithString:@"sharing accounts"]; + return @"sharing accounts"; return [[displayName copy] autorelease]; } @@ -148,115 +148,115 @@ #pragma mark ASIHTTPRequestDelegate - (void)sharingAccountsRequestFailed:(ASIPithosRequest *)request { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSString *message; - NSError *error = [sharingAccountsRequest error]; - if (error) - message = [NSString stringWithFormat:@"Sharing accounts listing %@ failed: %@", - sharingAccountsRequest.url, [error localizedDescription]]; - else - message = [NSString stringWithFormat:@"Sharing accounts listing %@ failed: (%d) %@", - sharingAccountsRequest.url, sharingAccountsRequest.responseStatusCode, sharingAccountsRequest.responseStatusMessage]; - dispatch_async(dispatch_get_main_queue(), ^{ - [[PithosActivityFacility defaultPithosActivityFacility] startAndEndActivityWithType:PithosActivityOther message:message]; - }); - NSUInteger retries = [[sharingAccountsRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue]; - if (retries > 0) { - ASIPithosRequest *newSharingAccountsRequest = (ASIPithosRequest *)[PithosUtilities copyRequest:sharingAccountsRequest]; - [(NSMutableDictionary *)(newSharingAccountsRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; - [sharingAccountsRequest release]; - sharingAccountsRequest = newSharingAccountsRequest; - [[PithosUtilities prepareRequest:sharingAccountsRequest priority:[[sharingAccountsRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous]; - } else { - [newChildren release]; - newChildren = nil; - [sharingAccountsRequest release]; - sharingAccountsRequest = nil; - [sharingAccounts release]; - sharingAccounts = nil; - forcedRefresh = NO; - @synchronized(self) { - freshness = PithosNodeStateRefreshNeeded; + @autoreleasepool { + NSString *message; + NSError *error = [sharingAccountsRequest error]; + if (error) + message = [NSString stringWithFormat:@"Sharing accounts listing %@ failed: %@", + sharingAccountsRequest.url, [error localizedDescription]]; + else + message = [NSString stringWithFormat:@"Sharing accounts listing %@ failed: (%d) %@", + sharingAccountsRequest.url, sharingAccountsRequest.responseStatusCode, sharingAccountsRequest.responseStatusMessage]; + dispatch_async(dispatch_get_main_queue(), ^{ + [[PithosActivityFacility defaultPithosActivityFacility] startAndEndActivityWithType:PithosActivityOther message:message]; + }); + NSUInteger retries = [[sharingAccountsRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue]; + if (retries > 0) { + ASIPithosRequest *newSharingAccountsRequest = (ASIPithosRequest *)[PithosUtilities copyRequest:sharingAccountsRequest]; + [(NSMutableDictionary *)(newSharingAccountsRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; + [sharingAccountsRequest release]; + sharingAccountsRequest = newSharingAccountsRequest; + [[PithosUtilities prepareRequest:sharingAccountsRequest priority:[[sharingAccountsRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous]; + } else { + [newChildren release]; + newChildren = nil; + [sharingAccountsRequest release]; + sharingAccountsRequest = nil; + [sharingAccounts release]; + sharingAccounts = nil; + forcedRefresh = NO; + @synchronized(self) { + freshness = PithosNodeStateRefreshNeeded; + } } } - [pool drain]; } - (void)sharingAccountsRequestFinished:(ASIPithosRequest *)request { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - DLog(@"List sharing accounts finished: %@", [sharingAccountsRequest url]); - DLog(@"Cached: %d", [sharingAccountsRequest didUseCachedResponse]); - if (sharingAccountsRequest.responseStatusCode == 200) { - NSArray *someSharingAccounts = [sharingAccountsRequest sharingAccounts]; - if (sharingAccounts == nil) { - sharingAccounts = [[NSMutableArray alloc] initWithArray:someSharingAccounts]; - } else { - [sharingAccounts addObjectsFromArray:someSharingAccounts]; - } - if ([someSharingAccounts count] < 10000) { - if (!sharingAccountsRequest.didUseCachedResponse || ([sharingAccounts count] != [someSharingAccounts count]) || !children) { - // Save new children - DLog(@"using newChildren"); - newChildren = [[NSMutableArray alloc] init]; - NSMutableIndexSet *keptNodes = [NSMutableIndexSet indexSet]; - for (ASIPithosAccount *account in sharingAccounts) { - if (![account.name isEqualToString:pithos.authUser]) { - PithosAccountNode *node = [[[PithosAccountNode alloc] initWithPithos:pithos] autorelease]; - node.parent = self; - node.shared = shared; - node.sharingAccount = account.name; - node.inheritChildrenUpdatedNotificationName = inheritChildrenUpdatedNotificationName; - if (children) { - NSUInteger oldIndex = [children indexOfObject:node]; - if (oldIndex != NSNotFound) { - // Use the same pointer value, if possible - node = [children objectAtIndex:oldIndex]; - [keptNodes addIndex:oldIndex]; + @autoreleasepool { + DLog(@"List sharing accounts finished: %@", [sharingAccountsRequest url]); + DLog(@"Cached: %d", [sharingAccountsRequest didUseCachedResponse]); + if (sharingAccountsRequest.responseStatusCode == 200) { + NSArray *someSharingAccounts = [sharingAccountsRequest sharingAccounts]; + if (sharingAccounts == nil) { + sharingAccounts = [[NSMutableArray alloc] initWithArray:someSharingAccounts]; + } else { + [sharingAccounts addObjectsFromArray:someSharingAccounts]; + } + if ([someSharingAccounts count] < 10000) { + if (!sharingAccountsRequest.didUseCachedResponse || ([sharingAccounts count] != [someSharingAccounts count]) || !children) { + // Save new children + DLog(@"using newChildren"); + newChildren = [[NSMutableArray alloc] init]; + NSMutableIndexSet *keptNodes = [NSMutableIndexSet indexSet]; + for (ASIPithosAccount *account in sharingAccounts) { + if (![account.name isEqualToString:pithos.authUser]) { + PithosAccountNode *node = [[[PithosAccountNode alloc] initWithPithos:pithos] autorelease]; + node.parent = self; + node.shared = shared; + node.sharingAccount = account.name; + node.inheritChildrenUpdatedNotificationName = inheritChildrenUpdatedNotificationName; + if (children) { + NSUInteger oldIndex = [children indexOfObject:node]; + if (oldIndex != NSNotFound) { + // Use the same pointer value, if possible + node = [children objectAtIndex:oldIndex]; + [keptNodes addIndex:oldIndex]; + } } + [newChildren addObject:node]; } - [newChildren addObject:node]; } + [[children objectsAtIndexes: + [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])] indexesPassingTest:^(NSUInteger idx, BOOL *stop){ + if ([keptNodes containsIndex:idx]) + return NO; + return YES; + }]] makeObjectsPerformSelector:@selector(pithosNodeWillBeRemoved)]; } - [[children objectsAtIndexes: - [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])] indexesPassingTest:^(NSUInteger idx, BOOL *stop){ - if ([keptNodes containsIndex:idx]) - return NO; - return YES; - }]] makeObjectsPerformSelector:@selector(pithosNodeWillBeRemoved)]; - } - // Else cache was used and all results were fetched during this request, so existing children can be reused - [sharingAccountsRequest release]; - sharingAccountsRequest = nil; - [sharingAccounts release]; - sharingAccounts = nil; - forcedRefresh = NO; - @synchronized(self) { - freshness = PithosNodeStateRefreshFinished; + // Else cache was used and all results were fetched during this request, so existing children can be reused + [sharingAccountsRequest release]; + sharingAccountsRequest = nil; + [sharingAccounts release]; + sharingAccounts = nil; + forcedRefresh = NO; + @synchronized(self) { + freshness = PithosNodeStateRefreshFinished; + } + [self postChildrenUpdatedNotificationName]; + } else { + [sharingAccountsRequest release]; + // Do an additional request to fetch more objects + sharingAccountsRequest = [[ASIPithosRequest listSharingAccountsRequestWithPithos:pithos + limit:0 + marker:[[someSharingAccounts lastObject] name]] retain]; + sharingAccountsRequest.delegate = self; + sharingAccountsRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + sharingAccountsRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + sharingAccountsRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(sharingAccountsRequestFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(sharingAccountsRequestFailed:)), @"didFailSelector", + nil]; +// if (!forcedRefresh) +// sharingAccountsRequest.downloadCache = [ASIDownloadCache sharedCache]; + [[PithosUtilities prepareRequest:sharingAccountsRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous]; } - [self postChildrenUpdatedNotificationName]; } else { - [sharingAccountsRequest release]; - // Do an additional request to fetch more objects - sharingAccountsRequest = [[ASIPithosRequest listSharingAccountsRequestWithPithos:pithos - limit:0 - marker:[[someSharingAccounts lastObject] name]] retain]; - sharingAccountsRequest.delegate = self; - sharingAccountsRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - sharingAccountsRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - sharingAccountsRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(sharingAccountsRequestFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(sharingAccountsRequestFailed:)), @"didFailSelector", - nil]; -// if (!forcedRefresh) -// sharingAccountsRequest.downloadCache = [ASIDownloadCache sharedCache]; - [[PithosUtilities prepareRequest:sharingAccountsRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous]; + [self sharingAccountsRequestFailed:sharingAccountsRequest]; } - } else { - [self sharingAccountsRequestFailed:sharingAccountsRequest]; } - [pool drain]; } @end diff --git a/pithos-macos/PithosSubdirNode.m b/pithos-macos/PithosSubdirNode.m index bcec8fe..371324c 100644 --- a/pithos-macos/PithosSubdirNode.m +++ b/pithos-macos/PithosSubdirNode.m @@ -169,89 +169,89 @@ static NSImage *sharedIcon = nil; #pragma mark ASIHTTPRequestDelegate - (void)objectRequestFinished:(ASIPithosObjectRequest *)request { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - DLog(@"URL: %@", [request url]); - DLog(@"cached: %d", [request didUseCachedResponse]); - - if ([request isEqualTo:applyMetadataObjectRequest]) { - int responseStatusCode = applyMetadataObjectRequest.responseStatusCode; - if ((responseStatusCode != 201) && (responseStatusCode != 202)) - [PithosUtilities unexpectedResponseStatusAlertWithRequest:applyMetadataObjectRequest]; - @synchronized(self) { - [applyMetadataObjectRequest release]; - applyMetadataObjectRequest = nil; - } - if ((responseStatusCode == 201) || (responseStatusCode == 202)) - [self refreshInfo]; - } else if ([request isEqualTo:refreshMetadataObjectRequest]) { - [[pithosNodeInfoController window] makeFirstResponder:nil]; - self.pithosObject = [refreshMetadataObjectRequest object]; - if (refreshParent) { - // Ask the parent for refresh for the case where an object was removed - // It is done here so that it doesn't affect the info window refresh - [parent refresh]; - refreshParent = NO; - } - @synchronized(self) { - [refreshMetadataObjectRequest release]; - refreshMetadataObjectRequest = nil; - } - } else if ([request isEqualTo:refreshVersionsObjectRequest]) { - [[pithosNodeInfoController window] makeFirstResponder:nil]; - self.versions = [refreshVersionsObjectRequest versions]; - @synchronized(self) { - [refreshVersionsObjectRequest release]; - refreshVersionsObjectRequest = nil; - } - } - [pool drain]; -} - -- (void)objectRequestFailed:(ASIPithosObjectRequest *)request { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue]; - if (retries > 0) { - ASIPithosObjectRequest *newRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:request]; - [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; - if ([request isEqualTo:applyMetadataObjectRequest]) { - @synchronized(self) { - [applyMetadataObjectRequest release]; - applyMetadataObjectRequest = newRequest; - } - } else if ([request isEqualTo:refreshMetadataObjectRequest]) { - @synchronized(self) { - [refreshMetadataObjectRequest release]; - refreshMetadataObjectRequest = newRequest; - } - } else if ([request isEqualTo:refreshVersionsObjectRequest]) { - @synchronized(self) { - [refreshVersionsObjectRequest release]; - refreshVersionsObjectRequest = newRequest; - } - } - [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous]; - } else { + @autoreleasepool { + DLog(@"URL: %@", [request url]); + DLog(@"cached: %d", [request didUseCachedResponse]); + if ([request isEqualTo:applyMetadataObjectRequest]) { - [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataObjectRequest]; + int responseStatusCode = applyMetadataObjectRequest.responseStatusCode; + if ((responseStatusCode != 201) && (responseStatusCode != 202)) + [PithosUtilities unexpectedResponseStatusAlertWithRequest:applyMetadataObjectRequest]; @synchronized(self) { [applyMetadataObjectRequest release]; applyMetadataObjectRequest = nil; } + if ((responseStatusCode == 201) || (responseStatusCode == 202)) + [self refreshInfo]; } else if ([request isEqualTo:refreshMetadataObjectRequest]) { - [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataObjectRequest]; + [[pithosNodeInfoController window] makeFirstResponder:nil]; + self.pithosObject = [refreshMetadataObjectRequest object]; + if (refreshParent) { + // Ask the parent for refresh for the case where an object was removed + // It is done here so that it doesn't affect the info window refresh + [parent refresh]; + refreshParent = NO; + } @synchronized(self) { [refreshMetadataObjectRequest release]; refreshMetadataObjectRequest = nil; } } else if ([request isEqualTo:refreshVersionsObjectRequest]) { - [PithosUtilities httpRequestErrorAlertWithRequest:refreshVersionsObjectRequest]; + [[pithosNodeInfoController window] makeFirstResponder:nil]; + self.versions = [refreshVersionsObjectRequest versions]; @synchronized(self) { [refreshVersionsObjectRequest release]; refreshVersionsObjectRequest = nil; } } } - [pool drain]; +} + +- (void)objectRequestFailed:(ASIPithosObjectRequest *)request { + @autoreleasepool { + NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue]; + if (retries > 0) { + ASIPithosObjectRequest *newRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:request]; + [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; + if ([request isEqualTo:applyMetadataObjectRequest]) { + @synchronized(self) { + [applyMetadataObjectRequest release]; + applyMetadataObjectRequest = newRequest; + } + } else if ([request isEqualTo:refreshMetadataObjectRequest]) { + @synchronized(self) { + [refreshMetadataObjectRequest release]; + refreshMetadataObjectRequest = newRequest; + } + } else if ([request isEqualTo:refreshVersionsObjectRequest]) { + @synchronized(self) { + [refreshVersionsObjectRequest release]; + refreshVersionsObjectRequest = newRequest; + } + } + [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous]; + } else { + if ([request isEqualTo:applyMetadataObjectRequest]) { + [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataObjectRequest]; + @synchronized(self) { + [applyMetadataObjectRequest release]; + applyMetadataObjectRequest = nil; + } + } else if ([request isEqualTo:refreshMetadataObjectRequest]) { + [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataObjectRequest]; + @synchronized(self) { + [refreshMetadataObjectRequest release]; + refreshMetadataObjectRequest = nil; + } + } else if ([request isEqualTo:refreshVersionsObjectRequest]) { + [PithosUtilities httpRequestErrorAlertWithRequest:refreshVersionsObjectRequest]; + @synchronized(self) { + [refreshVersionsObjectRequest release]; + refreshVersionsObjectRequest = nil; + } + } + } + } } #pragma mark - diff --git a/pithos-macos/PithosSyncDaemon.m b/pithos-macos/PithosSyncDaemon.m index 841f6f9..26a4a2c 100644 --- a/pithos-macos/PithosSyncDaemon.m +++ b/pithos-macos/PithosSyncDaemon.m @@ -127,80 +127,80 @@ } - (void)loadLocalState { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if ([[NSFileManager defaultManager] fileExistsAtPath:self.pithosStateFilePath]) - self.storedLocalObjectStates = [NSKeyedUnarchiver unarchiveObjectWithFile:self.pithosStateFilePath]; - else - self.storedLocalObjectStates = [NSMutableDictionary dictionary]; - if (!storedLocalObjectStates) - self.storedLocalObjectStates = [NSMutableDictionary dictionary]; - for (NSString *accountName in accountsNames) { - NSMutableDictionary *accountStoredLocalObjectStates = [storedLocalObjectStates objectForKey:accountName]; - if (!accountStoredLocalObjectStates) { - accountStoredLocalObjectStates = [NSMutableDictionary dictionary]; - [storedLocalObjectStates setObject:accountStoredLocalObjectStates forKey:accountName]; - } - for (ASIPithosContainer *pithosContainer in [accountsPithosContainers objectForKey:accountName]) { - if (![accountStoredLocalObjectStates objectForKey:pithosContainer.name]) - [accountStoredLocalObjectStates setObject:[NSMutableDictionary dictionary] forKey:pithosContainer.name]; + @autoreleasepool { + if ([[NSFileManager defaultManager] fileExistsAtPath:self.pithosStateFilePath]) + self.storedLocalObjectStates = [NSKeyedUnarchiver unarchiveObjectWithFile:self.pithosStateFilePath]; + else + self.storedLocalObjectStates = [NSMutableDictionary dictionary]; + if (!storedLocalObjectStates) + self.storedLocalObjectStates = [NSMutableDictionary dictionary]; + for (NSString *accountName in accountsNames) { + NSMutableDictionary *accountStoredLocalObjectStates = [storedLocalObjectStates objectForKey:accountName]; + if (!accountStoredLocalObjectStates) { + accountStoredLocalObjectStates = [NSMutableDictionary dictionary]; + [storedLocalObjectStates setObject:accountStoredLocalObjectStates forKey:accountName]; + } + for (ASIPithosContainer *pithosContainer in [accountsPithosContainers objectForKey:accountName]) { + if (![accountStoredLocalObjectStates objectForKey:pithosContainer.name]) + [accountStoredLocalObjectStates setObject:[NSMutableDictionary dictionary] forKey:pithosContainer.name]; + } } } - [pool drain]; } - (void)resetLocalStateWithAll:(BOOL)all { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - self.lastCompletedSync = nil; - if (all) { - self.storedLocalObjectStates = [NSMutableDictionary dictionary]; - [self saveLocalState]; // Save an empty dictionary - [self loadLocalState]; // Load to populate with containers - [self saveLocalState]; // Save again - if (self.tempDownloadsDirPath) - [PithosUtilities removeContentsAtPath:self.tempDownloadsDirPath]; - } else { - // Remove containers that don't interest us anymore and save - if (!storedLocalObjectStates) - [self loadLocalState]; - for (NSString *accountName in storedLocalObjectStates) { - NSMutableDictionary *accountStoredLocalObjectStates = [storedLocalObjectStates objectForKey:accountName]; - NSDictionary *containersDictionary = [accountsDictionary objectForKey:accountName]; - if (!containersDictionary) { - if (self.tempDownloadsDirPath) { - if ([accountName isEqualToString:@""]) { - for (NSString *containerName in accountStoredLocalObjectStates) { - [PithosUtilities removeContentsAtPath:[self.tempDownloadsDirPath stringByAppendingPathComponent:containerName]]; + @autoreleasepool { + self.lastCompletedSync = nil; + if (all) { + self.storedLocalObjectStates = [NSMutableDictionary dictionary]; + [self saveLocalState]; // Save an empty dictionary + [self loadLocalState]; // Load to populate with containers + [self saveLocalState]; // Save again + if (self.tempDownloadsDirPath) + [PithosUtilities removeContentsAtPath:self.tempDownloadsDirPath]; + } else { + // Remove containers that don't interest us anymore and save + if (!storedLocalObjectStates) + [self loadLocalState]; + for (NSString *accountName in storedLocalObjectStates) { + NSMutableDictionary *accountStoredLocalObjectStates = [storedLocalObjectStates objectForKey:accountName]; + NSDictionary *containersDictionary = [accountsDictionary objectForKey:accountName]; + if (!containersDictionary) { + if (self.tempDownloadsDirPath) { + if ([accountName isEqualToString:@""]) { + for (NSString *containerName in accountStoredLocalObjectStates) { + [PithosUtilities removeContentsAtPath:[self.tempDownloadsDirPath stringByAppendingPathComponent:containerName]]; + } + } else { + [PithosUtilities removeContentsAtPath:[[self.tempDownloadsDirPath stringByAppendingPathComponent:@"shared to me"] + stringByAppendingPathComponent:accountName]]; } - } else { - [PithosUtilities removeContentsAtPath:[[self.tempDownloadsDirPath stringByAppendingPathComponent:@"shared to me"] - stringByAppendingPathComponent:accountName]]; } - } - [storedLocalObjectStates removeObjectForKey:accountName]; - } else { - // Check the account's containers - for (NSString *containerName in accountStoredLocalObjectStates) { - if (![containersDictionary objectForKey:containerName]) { - if ([accountName isEqualToString:@""]) - [PithosUtilities removeContentsAtPath:[self.tempDownloadsDirPath stringByAppendingPathComponent:containerName]]; - else - [PithosUtilities removeContentsAtPath:[[[self.tempDownloadsDirPath stringByAppendingPathComponent:@"shared to me"] - stringByAppendingPathComponent:accountName] - stringByAppendingPathComponent:containerName]]; - [accountStoredLocalObjectStates removeObjectForKey:containerName]; + [storedLocalObjectStates removeObjectForKey:accountName]; + } else { + // Check the account's containers + for (NSString *containerName in accountStoredLocalObjectStates) { + if (![containersDictionary objectForKey:containerName]) { + if ([accountName isEqualToString:@""]) + [PithosUtilities removeContentsAtPath:[self.tempDownloadsDirPath stringByAppendingPathComponent:containerName]]; + else + [PithosUtilities removeContentsAtPath:[[[self.tempDownloadsDirPath stringByAppendingPathComponent:@"shared to me"] + stringByAppendingPathComponent:accountName] + stringByAppendingPathComponent:containerName]]; + [accountStoredLocalObjectStates removeObjectForKey:containerName]; + } } } } + [self saveLocalState]; } - [self saveLocalState]; } - [pool drain]; } - (void)saveLocalState { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - [NSKeyedArchiver archiveRootObject:storedLocalObjectStates toFile:self.pithosStateFilePath]; - [pool drain]; + @autoreleasepool { + [NSKeyedArchiver archiveRootObject:storedLocalObjectStates toFile:self.pithosStateFilePath]; + } } - (void)resetDaemon { @@ -457,9 +457,7 @@ if ([accountName isEqualToString:@""]) return containerName; else - return [[[NSString stringWithString:@"shared to me"] - stringByAppendingPathComponent:accountName] - stringByAppendingPathComponent:containerName]; + return [[@"shared to me" stringByAppendingPathComponent:accountName] stringByAppendingPathComponent:containerName]; } #pragma mark - @@ -582,184 +580,174 @@ } - (void)emptyTempTrash { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSString *trashDirPath = self.tempTrashDirPath; - if (trashDirPath) { - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSError *error = nil; -// NSArray *subPaths = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:trashDirPath error:&error]; - NSArray *subPaths = [fileManager contentsOfDirectoryAtPath:trashDirPath error:&error]; - if (error) { - [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" - message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", trashDirPath] - error:error]; - [pool drain]; - return; - } - if ([subPaths count]) { -// NSMutableArray *subURLs = [NSMutableArray arrayWithCapacity:[subPaths count]]; -// for (NSString *subPath in subPaths) { -// [subURLs addObject:[NSURL fileURLWithPath:[trashDirPath stringByAppendingPathComponent:subPath]]]; -// } -// [self syncOperationStarted]; -// [[NSWorkspace sharedWorkspace] recycleURLs:subURLs completionHandler:^(NSDictionary *newURLs, NSError *error) { -// if (error) { -// [PithosUtilities fileActionFailedAlertWithTitle:@"Move to Trash Error" -// message:@"Cannot move files to Trash" -// error:error]; -// } -// [self syncOperationFinishedWithSuccess:YES]; -// }]; - for (NSString *subPath in subPaths) { - NSString *subFilePath = [trashDirPath stringByAppendingPathComponent:subPath]; - error = nil; - if (![fileManager removeItemAtPath:subFilePath error:&error] || error) - [PithosUtilities fileActionFailedAlertWithTitle:@"Remove File Error" - message:[NSString stringWithFormat:@"Cannot remove file at '%@'", subFilePath] - error:error]; + @autoreleasepool { + NSString *trashDirPath = self.tempTrashDirPath; + if (trashDirPath) { + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSError *error = nil; +// NSArray *subPaths = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:trashDirPath error:&error]; + NSArray *subPaths = [fileManager contentsOfDirectoryAtPath:trashDirPath error:&error]; + if (error) { + [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" + message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", trashDirPath] + error:error]; + return; + } + if ([subPaths count]) { +// NSMutableArray *subURLs = [NSMutableArray arrayWithCapacity:[subPaths count]]; +// for (NSString *subPath in subPaths) { +// [subURLs addObject:[NSURL fileURLWithPath:[trashDirPath stringByAppendingPathComponent:subPath]]]; +// } +// [self syncOperationStarted]; +// [[NSWorkspace sharedWorkspace] recycleURLs:subURLs completionHandler:^(NSDictionary *newURLs, NSError *error) { +// if (error) { +// [PithosUtilities fileActionFailedAlertWithTitle:@"Move to Trash Error" +// message:@"Cannot move files to Trash" +// error:error]; +// } +// [self syncOperationFinishedWithSuccess:YES]; +// }]; + for (NSString *subPath in subPaths) { + NSString *subFilePath = [trashDirPath stringByAppendingPathComponent:subPath]; + error = nil; + if (![fileManager removeItemAtPath:subFilePath error:&error] || error) + [PithosUtilities fileActionFailedAlertWithTitle:@"Remove File Error" + message:[NSString stringWithFormat:@"Cannot remove file at '%@'", subFilePath] + error:error]; + } } } } - [pool drain]; } - (BOOL)moveToTempTrashFile:(NSString *)filePath accountName:(NSString *)accountName pithosContainer:(ASIPithosContainer *)pithosContainer { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (!self.tempTrashDirPath) { - [pool drain]; - return NO; - } - NSFileManager *fileManager = [NSFileManager defaultManager]; - BOOL isDirectory; - BOOL fileExists = [fileManager fileExistsAtPath:filePath isDirectory:&isDirectory]; - NSError *error = nil; - NSString *containerDirectoryPath = [self dirPathForAccount:accountName container:pithosContainer.name]; - NSString *newFilePath = [filePath stringByReplacingOccurrencesOfString:containerDirectoryPath withString:self.tempTrashDirPath]; - NSString *newDirPath = [newFilePath stringByDeletingLastPathComponent]; - if (fileExists && isDirectory) { - NSArray *subPaths = [fileManager subpathsOfDirectoryAtPath:filePath error:&error]; - if (error) { - [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" - message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", filePath] - error:error]; - [pool drain]; - return NO; - } - if (![fileManager createDirectoryAtPath:newDirPath withIntermediateDirectories:YES attributes:nil error:&error] || error) { - [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" - message:[NSString stringWithFormat:@"Cannot create directory at '%@'", newDirPath] - error:error]; - [pool drain]; - return NO; - } - if (![fileManager moveItemAtPath:filePath toPath:newFilePath error:&error] || error) { - [PithosUtilities fileActionFailedAlertWithTitle:@"Move File Error" - message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", - filePath, newFilePath] - error:error]; - [pool drain]; + @autoreleasepool { + if (!self.tempTrashDirPath) return NO; - } - PithosLocalObjectState *currentState = [currentLocalObjectStates objectForKey:filePath]; - if (currentState) { - currentState.filePath = newFilePath; - [currentLocalObjectStates setObject:currentState forKey:newFilePath]; - [currentLocalObjectStates removeObjectForKey:filePath]; - } else { - [currentLocalObjectStates setObject:[PithosLocalObjectState localObjectStateWithFile:newFilePath - blockHash:pithosContainer.blockHash - blockSize:pithosContainer.blockSize] - forKey:newFilePath]; - } - for (NSString *subPath in subPaths) { - NSString *subFilePath = [filePath stringByAppendingPathComponent:subPath]; - NSString *newSubFilePath = [subFilePath stringByReplacingOccurrencesOfString:containerDirectoryPath - withString:self.tempTrashDirPath]; - currentState = [currentLocalObjectStates objectForKey:subFilePath]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + BOOL isDirectory; + BOOL fileExists = [fileManager fileExistsAtPath:filePath isDirectory:&isDirectory]; + NSError *error = nil; + NSString *containerDirectoryPath = [self dirPathForAccount:accountName container:pithosContainer.name]; + NSString *newFilePath = [filePath stringByReplacingOccurrencesOfString:containerDirectoryPath withString:self.tempTrashDirPath]; + NSString *newDirPath = [newFilePath stringByDeletingLastPathComponent]; + if (fileExists && isDirectory) { + NSArray *subPaths = [fileManager subpathsOfDirectoryAtPath:filePath error:&error]; + if (error) { + [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" + message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", filePath] + error:error]; + return NO; + } + if (![fileManager createDirectoryAtPath:newDirPath withIntermediateDirectories:YES attributes:nil error:&error] || error) { + [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" + message:[NSString stringWithFormat:@"Cannot create directory at '%@'", newDirPath] + error:error]; + return NO; + } + if (![fileManager moveItemAtPath:filePath toPath:newFilePath error:&error] || error) { + [PithosUtilities fileActionFailedAlertWithTitle:@"Move File Error" + message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", + filePath, newFilePath] + error:error]; + return NO; + } + PithosLocalObjectState *currentState = [currentLocalObjectStates objectForKey:filePath]; if (currentState) { - currentState.filePath = newSubFilePath; - [currentLocalObjectStates setObject:currentState forKey:newSubFilePath]; - [currentLocalObjectStates removeObjectForKey:subFilePath]; + currentState.filePath = newFilePath; + [currentLocalObjectStates setObject:currentState forKey:newFilePath]; + [currentLocalObjectStates removeObjectForKey:filePath]; } else { - [currentLocalObjectStates setObject:[PithosLocalObjectState localObjectStateWithFile:newSubFilePath + [currentLocalObjectStates setObject:[PithosLocalObjectState localObjectStateWithFile:newFilePath blockHash:pithosContainer.blockHash blockSize:pithosContainer.blockSize] - forKey:newSubFilePath]; - } - } - } else if (fileExists && !isDirectory) { - if (![fileManager createDirectoryAtPath:newDirPath withIntermediateDirectories:YES attributes:nil error:&error] || error) { - [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" - message:[NSString stringWithFormat:@"Cannot create directory at '%@'", newDirPath] - error:error]; - [pool drain]; - return NO; - } - if (![fileManager moveItemAtPath:filePath toPath:newFilePath error:&error] || error) { - [PithosUtilities fileActionFailedAlertWithTitle:@"Move File Error" - message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", - filePath, newFilePath] - error:error]; - [pool drain]; - return NO; - } - PithosLocalObjectState *currentState = [currentLocalObjectStates objectForKey:filePath]; - if (currentState) { - currentState.filePath = newFilePath; - [currentLocalObjectStates setObject:currentState forKey:newFilePath]; - [currentLocalObjectStates removeObjectForKey:filePath]; - } else { - [currentLocalObjectStates setObject:[PithosLocalObjectState localObjectStateWithFile:newFilePath - blockHash:pithosContainer.blockHash - blockSize:pithosContainer.blockSize] - forKey:newFilePath]; + forKey:newFilePath]; + } + for (NSString *subPath in subPaths) { + NSString *subFilePath = [filePath stringByAppendingPathComponent:subPath]; + NSString *newSubFilePath = [subFilePath stringByReplacingOccurrencesOfString:containerDirectoryPath + withString:self.tempTrashDirPath]; + currentState = [currentLocalObjectStates objectForKey:subFilePath]; + if (currentState) { + currentState.filePath = newSubFilePath; + [currentLocalObjectStates setObject:currentState forKey:newSubFilePath]; + [currentLocalObjectStates removeObjectForKey:subFilePath]; + } else { + [currentLocalObjectStates setObject:[PithosLocalObjectState localObjectStateWithFile:newSubFilePath + blockHash:pithosContainer.blockHash + blockSize:pithosContainer.blockSize] + forKey:newSubFilePath]; + } + } + } else if (fileExists && !isDirectory) { + if (![fileManager createDirectoryAtPath:newDirPath withIntermediateDirectories:YES attributes:nil error:&error] || error) { + [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" + message:[NSString stringWithFormat:@"Cannot create directory at '%@'", newDirPath] + error:error]; + return NO; + } + if (![fileManager moveItemAtPath:filePath toPath:newFilePath error:&error] || error) { + [PithosUtilities fileActionFailedAlertWithTitle:@"Move File Error" + message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", + filePath, newFilePath] + error:error]; + return NO; + } + PithosLocalObjectState *currentState = [currentLocalObjectStates objectForKey:filePath]; + if (currentState) { + currentState.filePath = newFilePath; + [currentLocalObjectStates setObject:currentState forKey:newFilePath]; + [currentLocalObjectStates removeObjectForKey:filePath]; + } else { + [currentLocalObjectStates setObject:[PithosLocalObjectState localObjectStateWithFile:newFilePath + blockHash:pithosContainer.blockHash + blockSize:pithosContainer.blockSize] + forKey:newFilePath]; + } } } - [pool drain]; return YES; } - (BOOL)findLocalCopyForObjectWithHash:(NSString *)hash forFile:(NSString *)filePath { if ([hash length] != 64) return NO; - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - PithosLocalObjectState *localState; - NSFileManager *fileManager = [NSFileManager defaultManager]; - BOOL isDirectory; - NSError *error = nil; - for (NSString *localFilePath in currentLocalObjectStates) { - localState = [currentLocalObjectStates objectForKey:localFilePath]; - if (!localState.isDirectory && [hash isEqualToString:localState.hash] && - [fileManager fileExistsAtPath:localFilePath isDirectory:&isDirectory] && !isDirectory) { - if ([localFilePath hasPrefix:directoryPath]) { - if (![fileManager copyItemAtPath:localFilePath toPath:filePath error:&error] || error) { - [PithosUtilities fileActionFailedAlertWithTitle:@"Copy File Error" - message:[NSString stringWithFormat:@"Cannot copy file at '%@' to '%@'", - localFilePath, filePath] - error:error]; - } else { - [pool drain]; - return YES; - } - } else if (self.tempTrashDirPath && [localFilePath hasPrefix:self.tempTrashDirPath]) { - if (![fileManager moveItemAtPath:localFilePath toPath:filePath error:&error] || error) { - [PithosUtilities fileActionFailedAlertWithTitle:@"Move File Error" - message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", - localFilePath, filePath] - error:error]; - } else { - localState.filePath = filePath; - [currentLocalObjectStates setObject:localState forKey:filePath]; - [currentLocalObjectStates removeObjectForKey:localFilePath]; - [pool drain]; - return YES; + @autoreleasepool { + PithosLocalObjectState *localState; + NSFileManager *fileManager = [NSFileManager defaultManager]; + BOOL isDirectory; + NSError *error = nil; + for (NSString *localFilePath in currentLocalObjectStates) { + localState = [currentLocalObjectStates objectForKey:localFilePath]; + if (!localState.isDirectory && [hash isEqualToString:localState.hash] && + [fileManager fileExistsAtPath:localFilePath isDirectory:&isDirectory] && !isDirectory) { + if ([localFilePath hasPrefix:directoryPath]) { + if (![fileManager copyItemAtPath:localFilePath toPath:filePath error:&error] || error) { + [PithosUtilities fileActionFailedAlertWithTitle:@"Copy File Error" + message:[NSString stringWithFormat:@"Cannot copy file at '%@' to '%@'", + localFilePath, filePath] + error:error]; + } else { + return YES; + } + } else if (self.tempTrashDirPath && [localFilePath hasPrefix:self.tempTrashDirPath]) { + if (![fileManager moveItemAtPath:localFilePath toPath:filePath error:&error] || error) { + [PithosUtilities fileActionFailedAlertWithTitle:@"Move File Error" + message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", + localFilePath, filePath] + error:error]; + } else { + localState.filePath = filePath; + [currentLocalObjectStates setObject:localState forKey:filePath]; + [currentLocalObjectStates removeObjectForKey:localFilePath]; + return YES; + } } } } } - [pool drain]; return NO; } @@ -767,173 +755,320 @@ localFilePath:(NSString *)filePath accountName:(NSString *)accountName pithosContainer:(ASIPithosContainer *)pithosContainer { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSError *error; - BOOL isDirectory; - BOOL fileExists = [fileManager fileExistsAtPath:filePath isDirectory:&isDirectory]; - NSString *fileDirectoryPath = [filePath stringByDeletingLastPathComponent]; - NSMutableDictionary *containerStoredLocalObjectStates = [[storedLocalObjectStates objectForKey:accountName] - objectForKey:pithosContainer.name]; - PithosLocalObjectState *storedState = [containerStoredLocalObjectStates objectForKey:object.name]; - // Remote updated info - NSError *remoteError; - BOOL remoteIsDirectory; - BOOL remoteObjectExists = [PithosUtilities objectExistsAtPithos:pithos - containerName:pithosContainer.name - objectName:object.name - error:&remoteError - isDirectory:&remoteIsDirectory - sharingAccount:([accountName isEqualToString:@""] ? nil : accountName)]; - if (!object || !object.objectHash) { - // Delete local object - if (![accountName isEqualToString:@""]) { - // If "shared to me" skip - [pool drain]; - return; - } - if (remoteObjectExists) { - // Remote object created in the meantime, just mark the sync cycle as incomplete, but do delete the local object - syncIncomplete = YES; - } - DLog(@"Sync::delete local object: %@", filePath); - if (!fileExists || [self moveToTempTrashFile:filePath accountName:accountName pithosContainer:pithosContainer]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility startAndEndActivityWithType:PithosActivityOther - message:[NSString stringWithFormat:@"Sync: Deleting '%@/%@' locally (finished)", - pithosContainer.name, object.name] - pithosAccount:pithosAccount]; - }); - [containerStoredLocalObjectStates removeObjectForKey:object.name]; - [self saveLocalState]; - } - } else if ([PithosUtilities isContentTypeDirectory:object.contentType]) { - // Create local directory object - if (!remoteObjectExists || !remoteIsDirectory) { - // Remote directory object deleted or changed to a file object in the meantime, mark the sync cycle as incomplete and skip - syncIncomplete = YES; - [pool drain]; - return; - } - DLog(@"Sync::create local directory object: %@", filePath); - BOOL directoryCreated = NO; - if (!fileExists || (!isDirectory && [self moveToTempTrashFile:filePath accountName:accountName pithosContainer:pithosContainer])) { - DLog(@"Sync::local directory object doesn't exist: %@", filePath); - error = nil; - if (![fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:&error] || error) { - [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" - message:[NSString stringWithFormat:@"Cannot create directory at '%@'", filePath] - error:error]; + @autoreleasepool { + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSError *error; + BOOL isDirectory; + BOOL fileExists = [fileManager fileExistsAtPath:filePath isDirectory:&isDirectory]; + NSString *fileDirectoryPath = [filePath stringByDeletingLastPathComponent]; + NSMutableDictionary *containerStoredLocalObjectStates = [[storedLocalObjectStates objectForKey:accountName] + objectForKey:pithosContainer.name]; + PithosLocalObjectState *storedState = [containerStoredLocalObjectStates objectForKey:object.name]; + // Remote updated info + NSError *remoteError; + BOOL remoteIsDirectory; + BOOL remoteObjectExists = [PithosUtilities objectExistsAtPithos:pithos + containerName:pithosContainer.name + objectName:object.name + error:&remoteError + isDirectory:&remoteIsDirectory + sharingAccount:([accountName isEqualToString:@""] ? nil : accountName)]; + if (!object || !object.objectHash) { + // Delete local object + if (![accountName isEqualToString:@""]) + // If "shared to me" skip + return; + if (remoteObjectExists) { + // Remote object created in the meantime, just mark the sync cycle as incomplete, but do delete the local object + syncIncomplete = YES; + } + DLog(@"Sync::delete local object: %@", filePath); + if (!fileExists || [self moveToTempTrashFile:filePath accountName:accountName pithosContainer:pithosContainer]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility startAndEndActivityWithType:PithosActivityOther + message:[NSString stringWithFormat:@"Sync: Deleting '%@/%@' locally (finished)", + pithosContainer.name, object.name] + pithosAccount:pithosAccount]; + }); + [containerStoredLocalObjectStates removeObjectForKey:object.name]; + [self saveLocalState]; + } + } else if ([PithosUtilities isContentTypeDirectory:object.contentType]) { + // Create local directory object + if (!remoteObjectExists || !remoteIsDirectory) { + // Remote directory object deleted or changed to a file object in the meantime, mark the sync cycle as incomplete and skip + syncIncomplete = YES; + return; + } + DLog(@"Sync::create local directory object: %@", filePath); + BOOL directoryCreated = NO; + if (!fileExists || (!isDirectory && [self moveToTempTrashFile:filePath accountName:accountName pithosContainer:pithosContainer])) { + DLog(@"Sync::local directory object doesn't exist: %@", filePath); + error = nil; + if (![fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:&error] || error) { + [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" + message:[NSString stringWithFormat:@"Cannot create directory at '%@'", filePath] + error:error]; + } else { + directoryCreated = YES; + storedState.filePath = filePath; + storedState.isDirectory = YES; + [self saveLocalState]; + } } else { + DLog(@"Sync::local directory object exists: %@", filePath); directoryCreated = YES; - storedState.filePath = filePath; - storedState.isDirectory = YES; - [self saveLocalState]; } - } else { - DLog(@"Sync::local directory object exists: %@", filePath); - directoryCreated = YES; - } - if (directoryCreated) - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility startAndEndActivityWithType:PithosActivityOther - message:[NSString stringWithFormat:@"Sync: Creating directory '%@/%@' locally (finished)", - [self relativeDirPathForAccount:accountName container:pithosContainer.name], - object.name] - pithosAccount:pithosAccount]; - }); - } else if (object.bytes == 0) { - // Create local object with zero length - if (!remoteObjectExists || remoteIsDirectory) { - // Remote file object deleted or changed to a directory object in the meantime, mark the sync cycle as incomplete and skip - syncIncomplete = YES; - [pool drain]; - return; - } - DLog(@"Sync::create local zero length object: %@", filePath); - BOOL fileCreated = NO; - if (!fileExists || - ((isDirectory || [PithosUtilities bytesOfFile:filePath]) && - [self moveToTempTrashFile:filePath accountName:accountName pithosContainer:pithosContainer])) { - DLog(@"Sync::local zero length object doesn't exist: %@", filePath); + if (directoryCreated) + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility startAndEndActivityWithType:PithosActivityOther + message:[NSString stringWithFormat:@"Sync: Creating directory '%@/%@' locally (finished)", + [self relativeDirPathForAccount:accountName container:pithosContainer.name], + object.name] + pithosAccount:pithosAccount]; + }); + } else if (object.bytes == 0) { + // Create local object with zero length + if (!remoteObjectExists || remoteIsDirectory) { + // Remote file object deleted or changed to a directory object in the meantime, mark the sync cycle as incomplete and skip + syncIncomplete = YES; + return; + } + DLog(@"Sync::create local zero length object: %@", filePath); + BOOL fileCreated = NO; + if (!fileExists || + ((isDirectory || [PithosUtilities bytesOfFile:filePath]) && + [self moveToTempTrashFile:filePath accountName:accountName pithosContainer:pithosContainer])) { + DLog(@"Sync::local zero length object doesn't exist: %@", filePath); + // Create directory of the file, if it doesn't exist + error = nil; + if (![PithosUtilities safeCreateDirectory:fileDirectoryPath error:&error]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" + message:[NSString stringWithFormat:@"Cannot create directory at '%@'", fileDirectoryPath] + error:error]; + }); + } + error = nil; + if (![fileManager createFileAtPath:filePath contents:nil attributes:nil]) { + [PithosUtilities fileActionFailedAlertWithTitle:@"Create File Error" + message:[NSString stringWithFormat:@"Cannot create file at '%@'", filePath] + error:error]; + } else { + fileCreated = YES; + storedState.filePath = filePath; + storedState.hash = object.objectHash; + storedState.tmpFilePath = nil; + [self saveLocalState]; + } + } else { + DLog(@"Sync::local zero length object exists: %@", filePath); + fileCreated = YES; + } + if (fileCreated) + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility startAndEndActivityWithType:PithosActivityOther + message:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (100%%)", + [self relativeDirPathForAccount:accountName container:pithosContainer.name], + object.name] + pithosAccount:pithosAccount]; + }); + } else if (storedState.tmpFilePath == nil) { + // Create new local object + if (!remoteObjectExists || remoteIsDirectory) { + // Remote file object deleted or changed to a directory object in the meantime, mark the sync cycle as incomplete and skip + syncIncomplete = YES; + return; + } // Create directory of the file, if it doesn't exist error = nil; if (![PithosUtilities safeCreateDirectory:fileDirectoryPath error:&error]) { + [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" + message:[NSString stringWithFormat:@"Cannot create directory at '%@'", fileDirectoryPath] + error:error]; + } + // Check first if a local copy exists + if ([self findLocalCopyForObjectWithHash:object.objectHash forFile:filePath]) { + storedState.filePath = filePath; + storedState.hash = object.objectHash; + [self saveLocalState]; dispatch_async(dispatch_get_main_queue(), ^{ - [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" - message:[NSString stringWithFormat:@"Cannot create directory at '%@'", fileDirectoryPath] - error:error]; + [activityFacility startAndEndActivityWithType:PithosActivityOther + message:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (100%%)", + [self relativeDirPathForAccount:accountName container:pithosContainer.name], + object.name] + pithosAccount:pithosAccount]; + }); + } else { + [self syncOperationStarted]; + __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectBlockDataRequestWithPithos:pithos + containerName:pithosContainer.name + object:object + blockIndex:0 + blockSize:pithosContainer.blockSize]; + if (![accountName isEqualToString:@""]) + [objectRequest setRequestUserFromDefaultTo:accountName withPithos:pithos]; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Sync: Downloading '%@/%@'", + [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload + message:[messagePrefix stringByAppendingString:@" (0%%)"] + totalBytes:object.bytes + currentBytes:0 + pithosAccount:pithosAccount]; + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility updateActivity:activity withMessage:activity.message]; }); + objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + accountName, @"accountName", + pithosContainer, @"pithosContainer", + object, @"pithosObject", + messagePrefix, @"messagePrefix", + [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, (NSUInteger)ceil((object.bytes +0.0)/(pithosContainer.blockSize + 0.0)))], @"missingBlocks", + [NSNumber numberWithUnsignedInteger:0], @"missingBlockIndex", + filePath, @"filePath", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (100%%)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(downloadObjectBlockFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + nil]; + [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){ + [activityFacility updateActivity:activity + withMessage:[NSString stringWithFormat:@"%@ (%.0f%%)", + messagePrefix, (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))] + totalBytes:activity.totalBytes + currentBytes:(activity.currentBytes + size)]; + }]; + [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityNormal]]; + } + } else { + // Resume local object download + if (!remoteObjectExists || remoteIsDirectory) { + // Remote file object deleted or changed to a directory object in the meantime, mark the sync cycle as incomplete and skip + syncIncomplete = YES; + return; } + // Create directory of the file, if it doesn't exist error = nil; - if (![fileManager createFileAtPath:filePath contents:nil attributes:nil]) { - [PithosUtilities fileActionFailedAlertWithTitle:@"Create File Error" - message:[NSString stringWithFormat:@"Cannot create file at '%@'", filePath] + if (![PithosUtilities safeCreateDirectory:fileDirectoryPath error:&error]) { + [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" + message:[NSString stringWithFormat:@"Cannot create directory at '%@'", fileDirectoryPath] error:error]; - } else { - fileCreated = YES; + } + // Check first if a local copy exists + if ([self findLocalCopyForObjectWithHash:object.objectHash forFile:filePath]) { storedState.filePath = filePath; storedState.hash = object.objectHash; + // Delete incomplete temp download storedState.tmpFilePath = nil; [self saveLocalState]; + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility startAndEndActivityWithType:PithosActivityOther + message:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (100%%)", + [self relativeDirPathForAccount:accountName container:pithosContainer.name], + object.name] + pithosAccount:pithosAccount]; + }); + } else { + [self syncOperationStarted]; + ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectHashmapRequestWithPithos:pithos + containerName:pithosContainer.name + objectName:object.name]; + if (![accountName isEqualToString:@""]) + [objectRequest setRequestUserFromDefaultTo:accountName withPithos:pithos]; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Sync: Downloading '%@/%@'", + [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload + message:[messagePrefix stringByAppendingString:@" (0%%)"] + totalBytes:object.bytes + currentBytes:0 + pithosAccount:pithosAccount]; + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility updateActivity:activity withMessage:activity.message]; + }); + objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + accountName, @"accountName", + pithosContainer, @"pithosContainer", + object, @"pithosObject", + messagePrefix, @"messagePrefix", + filePath, @"filePath", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (100%%)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(downloadObjectHashMapFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + nil]; + [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityNormal]]; } - } else { - DLog(@"Sync::local zero length object exists: %@", filePath); - fileCreated = YES; - } - if (fileCreated) - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility startAndEndActivityWithType:PithosActivityOther - message:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (100%%)", - [self relativeDirPathForAccount:accountName container:pithosContainer.name], - object.name] - pithosAccount:pithosAccount]; - }); - } else if (storedState.tmpFilePath == nil) { - // Create new local object - if (!remoteObjectExists || remoteIsDirectory) { - // Remote file object deleted or changed to a directory object in the meantime, mark the sync cycle as incomplete and skip - syncIncomplete = YES; - [pool drain]; - return; - } - // Create directory of the file, if it doesn't exist - error = nil; - if (![PithosUtilities safeCreateDirectory:fileDirectoryPath error:&error]) { - [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" - message:[NSString stringWithFormat:@"Cannot create directory at '%@'", fileDirectoryPath] - error:error]; } - // Check first if a local copy exists - if ([self findLocalCopyForObjectWithHash:object.objectHash forFile:filePath]) { - storedState.filePath = filePath; - storedState.hash = object.objectHash; - [self saveLocalState]; - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility startAndEndActivityWithType:PithosActivityOther - message:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (100%%)", - [self relativeDirPathForAccount:accountName container:pithosContainer.name], - object.name] - pithosAccount:pithosAccount]; - }); - } else { - [self syncOperationStarted]; - __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectBlockDataRequestWithPithos:pithos - containerName:pithosContainer.name - object:object - blockIndex:0 - blockSize:pithosContainer.blockSize]; + } +} + +-(void)updateServerStateWithCurrentState:(PithosLocalObjectState *)currentState + object:(ASIPithosObject *)object + localFilePath:(NSString *)filePath + accountName:(NSString *)accountName + pithosContainer:(ASIPithosContainer *)pithosContainer { + @autoreleasepool { + [self syncOperationStarted]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + BOOL isDirectory; + BOOL fileExists = [fileManager fileExistsAtPath:filePath isDirectory:&isDirectory]; + if (currentState.isDirectory) { + // Create remote directory object + if (![accountName isEqualToString:@""]) { + if (!object.allowedTo) { + NSMutableDictionary *containerRemoteObjects = [[remoteObjects objectForKey:accountName] objectForKey:pithosContainer.name]; + NSString *objectAncestorName = [object.name stringByDeletingLastPathComponent]; + while ([objectAncestorName length] && !object.allowedTo) { + object.allowedTo = [[containerRemoteObjects objectForKey:objectAncestorName] allowedTo]; + objectAncestorName = [objectAncestorName stringByDeletingLastPathComponent]; + } + } + if (![object.allowedTo isEqualToString:@"write"]) { + // If read-only "shared to me" skip + [self syncOperationFinishedWithSuccess:YES]; + return; + } + } + if (!fileExists || !isDirectory) { + // Local directory object deleted or changed to a file in the meantime, mark the sync cycle as incomplete and skip + [self syncOperationFinishedWithSuccess:NO]; + return; + } + ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos + containerName:pithosContainer.name + objectName:object.name + eTag:nil + contentType:@"application/directory" + contentEncoding:nil + contentDisposition:nil + manifest:nil + sharing:nil + isPublic:ASIPithosObjectRequestPublicIgnore + metadata:nil + data:[NSData data]]; if (![accountName isEqualToString:@""]) [objectRequest setRequestUserFromDefaultTo:accountName withPithos:pithos]; objectRequest.delegate = self; objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Sync: Downloading '%@/%@'", + NSString *messagePrefix = [NSString stringWithFormat:@"Sync: Creating directory '%@/%@'", [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload - message:[messagePrefix stringByAppendingString:@" (0%%)"] - totalBytes:object.bytes - currentBytes:0 + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory + message:messagePrefix pithosAccount:pithosAccount]; dispatch_async(dispatch_get_main_queue(), ^{ [activityFacility updateActivity:activity withMessage:activity.message]; @@ -943,343 +1078,185 @@ pithosContainer, @"pithosContainer", object, @"pithosObject", messagePrefix, @"messagePrefix", - [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, (NSUInteger)ceil((object.bytes +0.0)/(pithosContainer.blockSize + 0.0)))], @"missingBlocks", - [NSNumber numberWithUnsignedInteger:0], @"missingBlockIndex", - filePath, @"filePath", activity, @"activity", [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (100%%)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(downloadObjectBlockFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", nil]; - [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){ - [activityFacility updateActivity:activity - withMessage:[NSString stringWithFormat:@"%@ (%.0f%%)", - messagePrefix, (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))] - totalBytes:activity.totalBytes - currentBytes:(activity.currentBytes + size)]; - }]; - [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityNormal]]; - } - } else { - // Resume local object download - if (!remoteObjectExists || remoteIsDirectory) { - // Remote file object deleted or changed to a directory object in the meantime, mark the sync cycle as incomplete and skip - syncIncomplete = YES; - [pool drain]; - return; - } - // Create directory of the file, if it doesn't exist - error = nil; - if (![PithosUtilities safeCreateDirectory:fileDirectoryPath error:&error]) { - [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" - message:[NSString stringWithFormat:@"Cannot create directory at '%@'", fileDirectoryPath] - error:error]; - } - // Check first if a local copy exists - if ([self findLocalCopyForObjectWithHash:object.objectHash forFile:filePath]) { - storedState.filePath = filePath; - storedState.hash = object.objectHash; - // Delete incomplete temp download - storedState.tmpFilePath = nil; - [self saveLocalState]; - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility startAndEndActivityWithType:PithosActivityOther - message:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (100%%)", - [self relativeDirPathForAccount:accountName container:pithosContainer.name], - object.name] - pithosAccount:pithosAccount]; - }); - } else { - [self syncOperationStarted]; - ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectHashmapRequestWithPithos:pithos - containerName:pithosContainer.name - objectName:object.name]; - if (![accountName isEqualToString:@""]) - [objectRequest setRequestUserFromDefaultTo:accountName withPithos:pithos]; - objectRequest.delegate = self; - objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Sync: Downloading '%@/%@'", - [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload - message:[messagePrefix stringByAppendingString:@" (0%%)"] - totalBytes:object.bytes - currentBytes:0 - pithosAccount:pithosAccount]; - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility updateActivity:activity withMessage:activity.message]; - }); - objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: - accountName, @"accountName", - pithosContainer, @"pithosContainer", - object, @"pithosObject", - messagePrefix, @"messagePrefix", - filePath, @"filePath", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (100%%)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(downloadObjectHashMapFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - nil]; - [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityNormal]]; - } - } - [pool drain]; -} - --(void)updateServerStateWithCurrentState:(PithosLocalObjectState *)currentState - object:(ASIPithosObject *)object - localFilePath:(NSString *)filePath - accountName:(NSString *)accountName - pithosContainer:(ASIPithosContainer *)pithosContainer { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - [self syncOperationStarted]; - NSFileManager *fileManager = [NSFileManager defaultManager]; - BOOL isDirectory; - BOOL fileExists = [fileManager fileExistsAtPath:filePath isDirectory:&isDirectory]; - if (currentState.isDirectory) { - // Create remote directory object - if (![accountName isEqualToString:@""]) { - if (!object.allowedTo) { - NSMutableDictionary *containerRemoteObjects = [[remoteObjects objectForKey:accountName] objectForKey:pithosContainer.name]; - NSString *objectAncestorName = [object.name stringByDeletingLastPathComponent]; - while ([objectAncestorName length] && !object.allowedTo) { - object.allowedTo = [[containerRemoteObjects objectForKey:objectAncestorName] allowedTo]; - objectAncestorName = [objectAncestorName stringByDeletingLastPathComponent]; - } - } - if (![object.allowedTo isEqualToString:@"write"]) { - // If read-only "shared to me" skip + [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; + } else if (![currentState exists]) { + // Delete remote object + if (![accountName isEqualToString:@""]) { + // If "shared to me" skip [self syncOperationFinishedWithSuccess:YES]; - [pool drain]; return; } - } - if (!fileExists || !isDirectory) { - // Local directory object deleted or changed to a file in the meantime, mark the sync cycle as incomplete and skip - [self syncOperationFinishedWithSuccess:NO]; - [pool drain]; - return; - } - ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos + if (fileExists) { + // Local object created in the meantime, just mark the sync cycle as incomplete, but do delete the server object + syncIncomplete = YES; + } + if ([pithosContainer.name isEqualToString:@"trash"]) { + // Delete + ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos + containerName:pithosContainer.name + objectName:object.name]; + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Sync: Deleting '%@/%@'", + [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete + message:messagePrefix + pithosAccount:pithosAccount]; + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility updateActivity:activity withMessage:activity.message]; + }); + objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + accountName, @"accountName", + pithosContainer, @"pithosContainer", + object, @"pithosObject", + messagePrefix, @"messagePrefix", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + nil]; + [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; + } else { + // Move to container trash + NSString *safeName; + if ([PithosUtilities isContentTypeDirectory:object.contentType]) + safeName = [PithosUtilities safeSubdirNameForPithos:pithos containerName:@"trash" subdirName:object.name]; + else + safeName = [PithosUtilities safeObjectNameForPithos:pithos containerName:@"trash" objectName:object.name]; + if (safeName) { + ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos containerName:pithosContainer.name objectName:object.name - eTag:nil - contentType:@"application/directory" - contentEncoding:nil - contentDisposition:nil - manifest:nil - sharing:nil - isPublic:ASIPithosObjectRequestPublicIgnore - metadata:nil - data:[NSData data]]; - if (![accountName isEqualToString:@""]) - [objectRequest setRequestUserFromDefaultTo:accountName withPithos:pithos]; - objectRequest.delegate = self; - objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Sync: Creating directory '%@/%@'", - [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory - message:messagePrefix - pithosAccount:pithosAccount]; - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility updateActivity:activity withMessage:activity.message]; - }); - objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: - accountName, @"accountName", - pithosContainer, @"pithosContainer", - object, @"pithosObject", - messagePrefix, @"messagePrefix", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - nil]; - [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; - } else if (![currentState exists]) { - // Delete remote object - if (![accountName isEqualToString:@""]) { - // If "shared to me" skip - [self syncOperationFinishedWithSuccess:YES]; - [pool drain]; - return; - } - if (fileExists) { - // Local object created in the meantime, just mark the sync cycle as incomplete, but do delete the server object - syncIncomplete = YES; - } - if ([pithosContainer.name isEqualToString:@"trash"]) { - // Delete - ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos - containerName:pithosContainer.name - objectName:object.name]; - objectRequest.delegate = self; - objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Sync: Deleting '%@/%@'", - [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete - message:messagePrefix - pithosAccount:pithosAccount]; - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility updateActivity:activity withMessage:activity.message]; - }); - objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: - accountName, @"accountName", - pithosContainer, @"pithosContainer", - object, @"pithosObject", - messagePrefix, @"messagePrefix", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - nil]; - [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; - } else { - // Move to container trash - NSString *safeName; - if ([PithosUtilities isContentTypeDirectory:object.contentType]) - safeName = [PithosUtilities safeSubdirNameForPithos:pithos containerName:@"trash" subdirName:object.name]; - else - safeName = [PithosUtilities safeObjectNameForPithos:pithos containerName:@"trash" objectName:object.name]; - if (safeName) { - ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos - containerName:pithosContainer.name - objectName:object.name - destinationContainerName:@"trash" - destinationObjectName:safeName - checkIfExists:NO]; - if (objectRequest) { - objectRequest.delegate = self; - objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Sync: Moving to trash '%@/%@'", - [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete - message:messagePrefix - pithosAccount:pithosAccount]; - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility updateActivity:activity withMessage:activity.message]; - }); - objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: - accountName, @"accountName", - pithosContainer, @"pithosContainer", - object, @"pithosObject", - messagePrefix, @"messagePrefix", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(moveObjectToTrashFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - nil]; - [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; + destinationContainerName:@"trash" + destinationObjectName:safeName + checkIfExists:NO]; + if (objectRequest) { + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Sync: Moving to trash '%@/%@'", + [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete + message:messagePrefix + pithosAccount:pithosAccount]; + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility updateActivity:activity withMessage:activity.message]; + }); + objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + accountName, @"accountName", + pithosContainer, @"pithosContainer", + object, @"pithosObject", + messagePrefix, @"messagePrefix", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(moveObjectToTrashFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + nil]; + [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]]; + } else { + [self syncOperationFinishedWithSuccess:NO]; + } } else { [self syncOperationFinishedWithSuccess:NO]; } - } else { - [self syncOperationFinishedWithSuccess:NO]; } - } - } else { - // Upload file to remote object - if (![accountName isEqualToString:@""]) { - if (!object.allowedTo) { - NSMutableDictionary *containerRemoteObjects = [[remoteObjects objectForKey:accountName] objectForKey:pithosContainer.name]; - NSString *objectAncestorName = [object.name stringByDeletingLastPathComponent]; - while ([objectAncestorName length] && !object.allowedTo) { - object.allowedTo = [[containerRemoteObjects objectForKey:objectAncestorName] allowedTo]; - objectAncestorName = [objectAncestorName stringByDeletingLastPathComponent]; + } else { + // Upload file to remote object + if (![accountName isEqualToString:@""]) { + if (!object.allowedTo) { + NSMutableDictionary *containerRemoteObjects = [[remoteObjects objectForKey:accountName] objectForKey:pithosContainer.name]; + NSString *objectAncestorName = [object.name stringByDeletingLastPathComponent]; + while ([objectAncestorName length] && !object.allowedTo) { + object.allowedTo = [[containerRemoteObjects objectForKey:objectAncestorName] allowedTo]; + objectAncestorName = [objectAncestorName stringByDeletingLastPathComponent]; + } + } + if (![object.allowedTo isEqualToString:@"write"]) { + // If read-only "shared to me" skip + [self syncOperationFinishedWithSuccess:YES]; + return; } } - if (![object.allowedTo isEqualToString:@"write"]) { - // If read-only "shared to me" skip - [self syncOperationFinishedWithSuccess:YES]; - [pool drain]; + if (!fileExists || isDirectory) { + // Local file object deleted or changed to a directory in the meantime, mark the sync cycle as incomplete and skip + [self syncOperationFinishedWithSuccess:NO]; return; } - } - if (!fileExists || isDirectory) { - // Local file object deleted or changed to a directory in the meantime, mark the sync cycle as incomplete and skip - [self syncOperationFinishedWithSuccess:NO]; - [pool drain]; - return; - } - NSError *error = nil; - object.contentType = [PithosUtilities contentTypeOfFile:filePath error:&error]; - if (object.contentType == nil) - object.contentType = @"application/octet-stream"; - #if DEBUG_PITHOS - if (error) - DLog(@"contentType detection error: %@", error); - #endif - NSArray *hashes = nil; - ASIPithosObjectRequest *objectRequest = [PithosUtilities writeObjectDataRequestWithPithos:pithos - containerName:pithosContainer.name - objectName:object.name - contentType:object.contentType - blockSize:pithosContainer.blockSize - blockHash:pithosContainer.blockHash - forFile:filePath - checkIfExists:NO - hashes:&hashes - sharingAccount:([accountName isEqualToString:@""] ? nil : accountName)]; - if (objectRequest) { - objectRequest.delegate = self; - objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - NSString *messagePrefix = [NSString stringWithFormat:@"Sync: Uploading '%@/%@'", - [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name]; - PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload - message:[messagePrefix stringByAppendingString:@" (0%%)"] - totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue] - currentBytes:0 - pithosAccount:pithosAccount]; - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility updateActivity:activity withMessage:activity.message]; - }); - [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary: - [NSDictionary dictionaryWithObjectsAndKeys: - accountName, @"accountName", - pithosContainer, @"pithosContainer", - object, @"pithosObject", - messagePrefix, @"messagePrefix", - filePath, @"filePath", - hashes, @"hashes", - [NSNumber numberWithUnsignedInteger:10], @"iteration", - activity, @"activity", - [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", - [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", - [messagePrefix stringByAppendingString:@" (100%%)"], @"finishedActivityMessage", - [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", - [NSNumber numberWithUnsignedInteger:10], @"retries", - NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", - NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", - nil]]; - [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityNormal]]; - } else { - [self syncOperationFinishedWithSuccess:NO]; + NSError *error = nil; + object.contentType = [PithosUtilities contentTypeOfFile:filePath error:&error]; + if (object.contentType == nil) + object.contentType = @"application/octet-stream"; + #if DEBUG_PITHOS + if (error) + DLog(@"contentType detection error: %@", error); + #endif + NSArray *hashes = nil; + ASIPithosObjectRequest *objectRequest = [PithosUtilities writeObjectDataRequestWithPithos:pithos + containerName:pithosContainer.name + objectName:object.name + contentType:object.contentType + blockSize:pithosContainer.blockSize + blockHash:pithosContainer.blockHash + forFile:filePath + checkIfExists:NO + hashes:&hashes + sharingAccount:([accountName isEqualToString:@""] ? nil : accountName)]; + if (objectRequest) { + objectRequest.delegate = self; + objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + NSString *messagePrefix = [NSString stringWithFormat:@"Sync: Uploading '%@/%@'", + [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name]; + PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload + message:[messagePrefix stringByAppendingString:@" (0%%)"] + totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue] + currentBytes:0 + pithosAccount:pithosAccount]; + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility updateActivity:activity withMessage:activity.message]; + }); + [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary: + [NSDictionary dictionaryWithObjectsAndKeys: + accountName, @"accountName", + pithosContainer, @"pithosContainer", + object, @"pithosObject", + messagePrefix, @"messagePrefix", + filePath, @"filePath", + hashes, @"hashes", + [NSNumber numberWithUnsignedInteger:10], @"iteration", + activity, @"activity", + [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", + [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", + [messagePrefix stringByAppendingString:@" (100%%)"], @"finishedActivityMessage", + [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", + nil]]; + [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityNormal]]; + } else { + [self syncOperationFinishedWithSuccess:NO]; + } } } - [pool drain]; } #pragma mark - @@ -1291,15 +1268,15 @@ selector:NSSelectorFromString([request.userInfo objectForKey:@"didFinishSelector"]) object:request] autorelease]; operation.completionBlock = ^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if ([[request.userInfo objectForKey:@"operation"] isCancelled]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] - withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]]; - }); - [self syncOperationFinishedWithSuccess:NO]; + @autoreleasepool { + if ([[request.userInfo objectForKey:@"operation"] isCancelled]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] + withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]]; + }); + [self syncOperationFinishedWithSuccess:NO]; + } } - [pool drain]; }; [(NSMutableDictionary *)request.userInfo setObject:operation forKey:@"operation"]; [callbackQueue addOperation:operation]; @@ -1317,15 +1294,15 @@ selector:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"]) object:request] autorelease]; operation.completionBlock = ^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if ([[request.userInfo objectForKey:@"operation"] isCancelled]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] - withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]]; - }); - [self syncOperationFinishedWithSuccess:NO]; + @autoreleasepool { + if ([[request.userInfo objectForKey:@"operation"] isCancelled]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] + withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]]; + }); + [self syncOperationFinishedWithSuccess:NO]; + } } - [pool drain]; }; [(NSMutableDictionary *)request.userInfo setObject:operation forKey:@"operation"]; [callbackQueue addOperation:operation]; @@ -1333,40 +1310,79 @@ } - (void)listRequestFinished:(ASIPithosContainerRequest *)containerRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [containerRequest.userInfo objectForKey:@"operation"]; - DLog(@"Sync::list request finished: %@", containerRequest.url); - if (operation.isCancelled) { - [self listRequestFailed:containerRequest]; - } else if ((containerRequest.responseStatusCode == 200) || - (containerRequest.responseStatusCode == 304) || - (containerRequest.responseStatusCode == 403)) { - NSString *accountName = [accountsNames objectAtIndex:accountsIndex]; - ASIPithosContainer *pithosContainer = [[accountsPithosContainers objectForKey:accountName] objectAtIndex:containersIndex]; - if (containerRequest.responseStatusCode == 200) { - NSArray *someObjects = [containerRequest objects]; - if (objects == nil) { - objects = [[NSMutableArray alloc] initWithArray:someObjects]; - } else { - [objects addObjectsFromArray:someObjects]; - } - if ([someObjects count] < 10000) { - pithosContainer.blockHash = [containerRequest blockHash]; - pithosContainer.blockSize = [containerRequest blockSize]; - pithosContainer.lastModified = [containerRequest lastModified]; - NSMutableDictionary *containerRemoteObjects = [NSMutableDictionary dictionaryWithCapacity:[objects count]]; - for (ASIPithosObject *object in objects) { - [containerRemoteObjects setObject:object forKey:object.name]; + @autoreleasepool { + NSOperation *operation = [containerRequest.userInfo objectForKey:@"operation"]; + DLog(@"Sync::list request finished: %@", containerRequest.url); + if (operation.isCancelled) { + [self listRequestFailed:containerRequest]; + } else if ((containerRequest.responseStatusCode == 200) || + (containerRequest.responseStatusCode == 304) || + (containerRequest.responseStatusCode == 403)) { + NSString *accountName = [accountsNames objectAtIndex:accountsIndex]; + ASIPithosContainer *pithosContainer = [[accountsPithosContainers objectForKey:accountName] objectAtIndex:containersIndex]; + if (containerRequest.responseStatusCode == 200) { + NSArray *someObjects = [containerRequest objects]; + if (objects == nil) { + objects = [[NSMutableArray alloc] initWithArray:someObjects]; + } else { + [objects addObjectsFromArray:someObjects]; } - [[remoteObjects objectForKey:accountName] setObject:containerRemoteObjects forKey:pithosContainer.name]; - [objects release]; - objects = nil; - } else { - // Do an additional request to fetch more objects + if ([someObjects count] < 10000) { + pithosContainer.blockHash = [containerRequest blockHash]; + pithosContainer.blockSize = [containerRequest blockSize]; + pithosContainer.lastModified = [containerRequest lastModified]; + NSMutableDictionary *containerRemoteObjects = [NSMutableDictionary dictionaryWithCapacity:[objects count]]; + for (ASIPithosObject *object in objects) { + [containerRemoteObjects setObject:object forKey:object.name]; + } + [[remoteObjects objectForKey:accountName] setObject:containerRemoteObjects forKey:pithosContainer.name]; + [objects release]; + objects = nil; + } else { + // Do an additional request to fetch more objects + ASIPithosContainerRequest *newContainerRequest = [ASIPithosContainerRequest listObjectsRequestWithPithos:pithos + containerName:pithosContainer.name + limit:0 + marker:[[someObjects lastObject] name] + prefix:nil + delimiter:nil + path:nil + meta:nil + shared:NO + until:nil + ifModifiedSince:pithosContainer.lastModified]; + if (![accountName isEqualToString:@""]) + [newContainerRequest setRequestUserFromDefaultTo:accountName withPithos:pithos]; + newContainerRequest.delegate = self; + newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + newContainerRequest.userInfo = containerRequest.userInfo; + [(NSMutableDictionary *)newContainerRequest.userInfo setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; + [networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]]; + return; + } + } else if (containerRequest.responseStatusCode == 304) { + NSMutableDictionary *containerRemoteObjects = [[previousRemoteObjects objectForKey:accountName] + objectForKey:pithosContainer.name]; + if (containerRemoteObjects) + [[remoteObjects objectForKey:accountName] setObject:containerRemoteObjects forKey:pithosContainer.name]; + } else if (containerRequest.responseStatusCode == 403) { + [[remoteObjects objectForKey:accountName] setObject:[NSMutableDictionary dictionary] forKey:pithosContainer.name]; + } + + containersIndex++; + if (containersIndex == [[accountsPithosContainers objectForKey:accountName] count]) { + accountsIndex++; + containersIndex = 0; + } + if (accountsIndex < accountsCount) { + accountName = [accountsNames objectAtIndex:accountsIndex]; + pithosContainer = [[accountsPithosContainers objectForKey:accountName] objectAtIndex:containersIndex]; + // Do a request for the next container ASIPithosContainerRequest *newContainerRequest = [ASIPithosContainerRequest listObjectsRequestWithPithos:pithos containerName:pithosContainer.name limit:0 - marker:[[someObjects lastObject] name] + marker:nil prefix:nil delimiter:nil path:nil @@ -1382,571 +1398,586 @@ newContainerRequest.userInfo = containerRequest.userInfo; [(NSMutableDictionary *)newContainerRequest.userInfo setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; [networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]]; - [pool drain]; return; } - } else if (containerRequest.responseStatusCode == 304) { - NSMutableDictionary *containerRemoteObjects = [[previousRemoteObjects objectForKey:accountName] - objectForKey:pithosContainer.name]; - if (containerRemoteObjects) - [[remoteObjects objectForKey:accountName] setObject:containerRemoteObjects forKey:pithosContainer.name]; - } else if (containerRequest.responseStatusCode == 403) { - [[remoteObjects objectForKey:accountName] setObject:[NSMutableDictionary dictionary] forKey:pithosContainer.name]; - } - - containersIndex++; - if (containersIndex == [[accountsPithosContainers objectForKey:accountName] count]) { - accountsIndex++; - containersIndex = 0; - } - if (accountsIndex < accountsCount) { - accountName = [accountsNames objectAtIndex:accountsIndex]; - pithosContainer = [[accountsPithosContainers objectForKey:accountName] objectAtIndex:containersIndex]; - // Do a request for the next container - ASIPithosContainerRequest *newContainerRequest = [ASIPithosContainerRequest listObjectsRequestWithPithos:pithos - containerName:pithosContainer.name - limit:0 - marker:nil - prefix:nil - delimiter:nil - path:nil - meta:nil - shared:NO - until:nil - ifModifiedSince:pithosContainer.lastModified]; - if (![accountName isEqualToString:@""]) - [newContainerRequest setRequestUserFromDefaultTo:accountName withPithos:pithos]; - newContainerRequest.delegate = self; - newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - newContainerRequest.userInfo = containerRequest.userInfo; - [(NSMutableDictionary *)newContainerRequest.userInfo setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; - [networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]]; - [pool drain]; - return; - } - self.previousRemoteObjects = remoteObjects; - // remoteObjects contains all remote objects for the legal containers, without enforcing directory exclusions - - if (operation.isCancelled) { - [self listRequestFailed:containerRequest]; - [pool drain]; - return; - } + self.previousRemoteObjects = remoteObjects; + // remoteObjects contains all remote objects for the legal containers, without enforcing directory exclusions + + if (operation.isCancelled) { + [self listRequestFailed:containerRequest]; + return; + } - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] - withMessage:[containerRequest.userInfo objectForKey:@"finishedActivityMessage"]]; - }); - NSFileManager *fileManager = [NSFileManager defaultManager]; - - // Compute current state of legal existing local objects - // and add an empty stored state for legal new local objects since last sync - self.currentLocalObjectStates = [NSMutableDictionary dictionary]; - for (NSString *accountName in accountsNames) { - for (ASIPithosContainer *pithosContainer in [accountsPithosContainers objectForKey:accountName]) { - NSString *containerDirectoryPath = [self dirPathForAccount:accountName container:pithosContainer.name]; - NSSet *containerExcludedDirectories = [[accountsDictionary objectForKey:accountName] objectForKey:pithosContainer.name]; - BOOL containerExcludeRootFiles = [containerExcludedDirectories containsObject:@""]; - NSMutableDictionary *containerStoredLocalObjectStates = [[storedLocalObjectStates objectForKey:accountName] - objectForKey:pithosContainer.name]; - NSDirectoryEnumerator *dirEnumerator = [fileManager enumeratorAtPath:containerDirectoryPath]; - for (NSString *objectName in dirEnumerator) { - objectName = [objectName precomposedStringWithCanonicalMapping]; - if (operation.isCancelled) { - operation.completionBlock = nil; - [self saveLocalState]; - [self syncOperationFinishedWithSuccess:NO]; - [pool drain]; - return; - } + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] + withMessage:[containerRequest.userInfo objectForKey:@"finishedActivityMessage"]]; + }); + NSFileManager *fileManager = [NSFileManager defaultManager]; + + // Compute current state of legal existing local objects + // and add an empty stored state for legal new local objects since last sync + self.currentLocalObjectStates = [NSMutableDictionary dictionary]; + for (NSString *accountName in accountsNames) { + for (ASIPithosContainer *pithosContainer in [accountsPithosContainers objectForKey:accountName]) { + NSString *containerDirectoryPath = [self dirPathForAccount:accountName container:pithosContainer.name]; + NSSet *containerExcludedDirectories = [[accountsDictionary objectForKey:accountName] objectForKey:pithosContainer.name]; + BOOL containerExcludeRootFiles = [containerExcludedDirectories containsObject:@""]; + NSMutableDictionary *containerStoredLocalObjectStates = [[storedLocalObjectStates objectForKey:accountName] + objectForKey:pithosContainer.name]; + NSDirectoryEnumerator *dirEnumerator = [fileManager enumeratorAtPath:containerDirectoryPath]; + for (NSString *objectName in dirEnumerator) { + objectName = [objectName precomposedStringWithCanonicalMapping]; + if (operation.isCancelled) { + operation.completionBlock = nil; + [self saveLocalState]; + [self syncOperationFinishedWithSuccess:NO]; + return; + } - NSString *filePath = [containerDirectoryPath stringByAppendingPathComponent:objectName]; - NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil]; - BOOL isDirectory; - BOOL fileExists = [fileManager fileExistsAtPath:filePath isDirectory:&isDirectory]; - if ([[attributes fileType] isEqualToString:NSFileTypeSymbolicLink]) { - [PithosUtilities fileActionFailedAlertWithTitle:@"Sync Error" - message:[NSString stringWithFormat:@"Sync directory at '%@' contains symbolic links. Remove them and re-activate sync for account '%@'.", - containerDirectoryPath, pithosAccount.name] - error:nil]; - pithosAccount.syncActive = NO; - return; - } else if (fileExists) { - if (skipHidden && [[objectName lastPathComponent] hasPrefix:@"."]) { - // Skip hidden directory and its descendants, or hidden file - if (isDirectory) - [dirEnumerator skipDescendants]; - // Remove stored state if any - [containerStoredLocalObjectStates removeObjectForKey:objectName]; - continue; - } else if ([[objectName pathComponents] count] == 1) { - if ([containerExcludedDirectories containsObject:[objectName lowercaseString]]) { - // Skip excluded directory and its descendants, or root file with same name + NSString *filePath = [containerDirectoryPath stringByAppendingPathComponent:objectName]; + NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil]; + BOOL isDirectory; + BOOL fileExists = [fileManager fileExistsAtPath:filePath isDirectory:&isDirectory]; + if ([[attributes fileType] isEqualToString:NSFileTypeSymbolicLink]) { + [PithosUtilities fileActionFailedAlertWithTitle:@"Sync Error" + message:[NSString stringWithFormat:@"Sync directory at '%@' contains symbolic links. Remove them and re-activate sync for account '%@'.", + containerDirectoryPath, pithosAccount.name] + error:nil]; + pithosAccount.syncActive = NO; + return; + } else if (fileExists) { + if (skipHidden && [[objectName lastPathComponent] hasPrefix:@"."]) { + // Skip hidden directory and its descendants, or hidden file if (isDirectory) [dirEnumerator skipDescendants]; // Remove stored state if any [containerStoredLocalObjectStates removeObjectForKey:objectName]; continue; - } else if (!isDirectory && containerExcludeRootFiles) { - // Skip excluded root file - // Remove stored state if any - [containerStoredLocalObjectStates removeObjectForKey:objectName]; - continue; + } else if ([[objectName pathComponents] count] == 1) { + if ([containerExcludedDirectories containsObject:[objectName lowercaseString]]) { + // Skip excluded directory and its descendants, or root file with same name + if (isDirectory) + [dirEnumerator skipDescendants]; + // Remove stored state if any + [containerStoredLocalObjectStates removeObjectForKey:objectName]; + continue; + } else if (!isDirectory && containerExcludeRootFiles) { + // Skip excluded root file + // Remove stored state if any + [containerStoredLocalObjectStates removeObjectForKey:objectName]; + continue; + } + } + // Include local object + PithosLocalObjectState *storedLocalObjectState = [containerStoredLocalObjectStates objectForKey:objectName]; + if (!storedLocalObjectState || [storedLocalObjectState isModified]) { + // New or modified existing local object, compute current state + if (!storedLocalObjectState) + // For new local object, also create empty stored state + [containerStoredLocalObjectStates setObject:[PithosLocalObjectState localObjectState] forKey:objectName]; + [currentLocalObjectStates setObject:[PithosLocalObjectState localObjectStateWithFile:filePath + blockHash:pithosContainer.blockHash + blockSize:pithosContainer.blockSize] + forKey:filePath]; + } else { + // Local object hasn't changed, set stored state also to current + [currentLocalObjectStates setObject:storedLocalObjectState forKey:filePath]; } - } - // Include local object - PithosLocalObjectState *storedLocalObjectState = [containerStoredLocalObjectStates objectForKey:objectName]; - if (!storedLocalObjectState || [storedLocalObjectState isModified]) { - // New or modified existing local object, compute current state - if (!storedLocalObjectState) - // For new local object, also create empty stored state - [containerStoredLocalObjectStates setObject:[PithosLocalObjectState localObjectState] forKey:objectName]; - [currentLocalObjectStates setObject:[PithosLocalObjectState localObjectStateWithFile:filePath - blockHash:pithosContainer.blockHash - blockSize:pithosContainer.blockSize] - forKey:filePath]; - } else { - // Local object hasn't changed, set stored state also to current - [currentLocalObjectStates setObject:storedLocalObjectState forKey:filePath]; } } + [self saveLocalState]; } - [self saveLocalState]; } - } - - if (operation.isCancelled) { - operation.completionBlock = nil; - [self syncOperationFinishedWithSuccess:NO]; - [pool drain]; - return; - } + + if (operation.isCancelled) { + operation.completionBlock = nil; + [self syncOperationFinishedWithSuccess:NO]; + return; + } - // Add an empty stored state for legal new remote objects since last sync - for (NSString *accountName in accountsNames) { - for (ASIPithosContainer *pithosContainer in [accountsPithosContainers objectForKey:accountName]) { - NSSet *containerExcludedDirectories = [[accountsDictionary objectForKey:accountName] objectForKey:pithosContainer.name]; - BOOL containerExcludeRootFiles = [containerExcludedDirectories containsObject:@""]; - NSMutableDictionary *containerStoredLocalObjectStates = [[storedLocalObjectStates objectForKey:accountName] - objectForKey:pithosContainer.name]; - NSMutableDictionary *containerRemoteObjects = [[remoteObjects objectForKey:accountName] objectForKey:pithosContainer.name]; - for (NSString *objectName in containerRemoteObjects) { - if (operation.isCancelled) { - operation.completionBlock = nil; - [self saveLocalState]; - [self syncOperationFinishedWithSuccess:NO]; - [pool drain]; - return; - } + // Add an empty stored state for legal new remote objects since last sync + for (NSString *accountName in accountsNames) { + for (ASIPithosContainer *pithosContainer in [accountsPithosContainers objectForKey:accountName]) { + NSSet *containerExcludedDirectories = [[accountsDictionary objectForKey:accountName] objectForKey:pithosContainer.name]; + BOOL containerExcludeRootFiles = [containerExcludedDirectories containsObject:@""]; + NSMutableDictionary *containerStoredLocalObjectStates = [[storedLocalObjectStates objectForKey:accountName] + objectForKey:pithosContainer.name]; + NSMutableDictionary *containerRemoteObjects = [[remoteObjects objectForKey:accountName] objectForKey:pithosContainer.name]; + for (NSString *objectName in containerRemoteObjects) { + if (operation.isCancelled) { + operation.completionBlock = nil; + [self saveLocalState]; + [self syncOperationFinishedWithSuccess:NO]; + return; + } - ASIPithosObject *object = [containerRemoteObjects objectForKey:objectName]; - NSString *localObjectName; - if ([object.name hasSuffix:@"/"]) - localObjectName = [NSString stringWithFormat:@"%@:", [object.name substringToIndex:([object.name length] -1)]]; - else - localObjectName = [NSString stringWithString:object.name]; - NSArray *pathComponents = [localObjectName pathComponents]; - if (skipHidden) { - BOOL skipObject = NO; - for (NSString *pathComponent in pathComponents) { - if ([pathComponent hasPrefix:@"."]) { - // Skip hidden directory and its descendants, or hidden file - // Remove stored state if any - [containerStoredLocalObjectStates removeObjectForKey:objectName]; - skipObject = YES; - break; + ASIPithosObject *object = [containerRemoteObjects objectForKey:objectName]; + NSString *localObjectName; + if ([object.name hasSuffix:@"/"]) + localObjectName = [NSString stringWithFormat:@"%@:", [object.name substringToIndex:([object.name length] -1)]]; + else + localObjectName = [NSString stringWithString:object.name]; + NSArray *pathComponents = [localObjectName pathComponents]; + if (skipHidden) { + BOOL skipObject = NO; + for (NSString *pathComponent in pathComponents) { + if ([pathComponent hasPrefix:@"."]) { + // Skip hidden directory and its descendants, or hidden file + // Remove stored state if any + [containerStoredLocalObjectStates removeObjectForKey:objectName]; + skipObject = YES; + break; + } } + if (skipObject) + continue; } - if (skipObject) + if ([containerExcludedDirectories containsObject:[[pathComponents objectAtIndex:0] lowercaseString]]) { + // Skip excluded directory object and its descendants, or root file object with same name + // Remove stored state if any + [containerStoredLocalObjectStates removeObjectForKey:object.name]; continue; + } else if (containerExcludeRootFiles && + ([pathComponents count] == 1) && + ![PithosUtilities isContentTypeDirectory:object.contentType]) { + // Skip root file object + // Remove stored state if any + [containerStoredLocalObjectStates removeObjectForKey:object.name]; + continue; + } + if (![containerStoredLocalObjectStates objectForKey:object.name]) + [containerStoredLocalObjectStates setObject:[PithosLocalObjectState localObjectState] forKey:object.name]; } - if ([containerExcludedDirectories containsObject:[[pathComponents objectAtIndex:0] lowercaseString]]) { - // Skip excluded directory object and its descendants, or root file object with same name - // Remove stored state if any - [containerStoredLocalObjectStates removeObjectForKey:object.name]; - continue; - } else if (containerExcludeRootFiles && - ([pathComponents count] == 1) && - ![PithosUtilities isContentTypeDirectory:object.contentType]) { - // Skip root file object - // Remove stored state if any - [containerStoredLocalObjectStates removeObjectForKey:object.name]; - continue; - } - if (![containerStoredLocalObjectStates objectForKey:object.name]) - [containerStoredLocalObjectStates setObject:[PithosLocalObjectState localObjectState] forKey:object.name]; + [self saveLocalState]; } - [self saveLocalState]; } - } - if (operation.isCancelled) { - operation.completionBlock = nil; - [self syncOperationFinishedWithSuccess:NO]; - [pool drain]; - return; - } + if (operation.isCancelled) { + operation.completionBlock = nil; + [self syncOperationFinishedWithSuccess:NO]; + return; + } - // For each stored state compare with current and remote state - // Stored states of local objects that have been deleted, - // haven't been checked for legality (only existing local remote objects) - // These should be identified and skipped - for (NSString *accountName in accountsNames) { - for (ASIPithosContainer *pithosContainer in [accountsPithosContainers objectForKey:accountName]) { - NSString *containerDirectoryPath = [self dirPathForAccount:accountName container:pithosContainer.name]; - NSSet *containerExcludedDirectories = [[accountsDictionary objectForKey:accountName] objectForKey:pithosContainer.name]; - BOOL containerExcludeRootFiles = [containerExcludedDirectories containsObject:@""]; - NSMutableDictionary *containerStoredLocalObjectStates = [[storedLocalObjectStates objectForKey:accountName] - objectForKey:pithosContainer.name]; - NSMutableDictionary *containerRemoteObjects = [[remoteObjects objectForKey:accountName] objectForKey:pithosContainer.name]; - for (NSString *objectName in [[containerStoredLocalObjectStates allKeys] sortedArrayUsingSelector:@selector(compare:)]) { - if (operation.isCancelled) { - operation.completionBlock = nil; - [self syncOperationFinishedWithSuccess:NO]; - [pool drain]; - return; - } + // For each stored state compare with current and remote state + // Stored states of local objects that have been deleted, + // haven't been checked for legality (only existing local remote objects) + // These should be identified and skipped + for (NSString *accountName in accountsNames) { + for (ASIPithosContainer *pithosContainer in [accountsPithosContainers objectForKey:accountName]) { + NSString *containerDirectoryPath = [self dirPathForAccount:accountName container:pithosContainer.name]; + NSSet *containerExcludedDirectories = [[accountsDictionary objectForKey:accountName] objectForKey:pithosContainer.name]; + BOOL containerExcludeRootFiles = [containerExcludedDirectories containsObject:@""]; + NSMutableDictionary *containerStoredLocalObjectStates = [[storedLocalObjectStates objectForKey:accountName] + objectForKey:pithosContainer.name]; + NSMutableDictionary *containerRemoteObjects = [[remoteObjects objectForKey:accountName] objectForKey:pithosContainer.name]; + for (NSString *objectName in [[containerStoredLocalObjectStates allKeys] sortedArrayUsingSelector:@selector(compare:)]) { + if (operation.isCancelled) { + operation.completionBlock = nil; + [self syncOperationFinishedWithSuccess:NO]; + return; + } - NSString *filePath = [containerDirectoryPath stringByAppendingPathComponent:objectName]; - if ([objectName hasSuffix:@"/"]) - filePath = [filePath stringByAppendingString:@":"]; - ASIPithosObject *object = [ASIPithosObject object]; - object.name = objectName; - DLog(@"Sync::object name: %@", object.name); - - PithosLocalObjectState *storedLocalObjectState = [containerStoredLocalObjectStates objectForKey:object.name]; - PithosLocalObjectState *currentLocalObjectState = [currentLocalObjectStates objectForKey:filePath]; - if (!currentLocalObjectState) { - // The stored state corresponds to a remote or deleted local object, that's why there is no current state - // In the latter case it must be checked for legality, which can be determined by its stored state - // If it existed locally, but was deleted, state.exists is true, - // else if the stored state is an empty state that was created due to the server object, state.exists is false - if (storedLocalObjectState.exists) { - NSString *localObjectName; - if ([object.name hasSuffix:@"/"]) - localObjectName = [NSString stringWithFormat:@"%@:", [object.name substringToIndex:([object.name length] -1)]]; - else - localObjectName = [NSString stringWithString:object.name]; - NSArray *pathComponents = [localObjectName pathComponents]; - if (skipHidden) { - BOOL skipObject = NO; - for (NSString *pathComponent in pathComponents) { - if ([pathComponent hasPrefix:@"."]) { - // Skip hidden directory and its descendants, or hidden file - // Remove stored state if any - [containerStoredLocalObjectStates removeObjectForKey:objectName]; - [self saveLocalState]; - skipObject = YES; - break; + NSString *filePath = [containerDirectoryPath stringByAppendingPathComponent:objectName]; + if ([objectName hasSuffix:@"/"]) + filePath = [filePath stringByAppendingString:@":"]; + ASIPithosObject *object = [ASIPithosObject object]; + object.name = objectName; + DLog(@"Sync::object name: %@", object.name); + + PithosLocalObjectState *storedLocalObjectState = [containerStoredLocalObjectStates objectForKey:object.name]; + PithosLocalObjectState *currentLocalObjectState = [currentLocalObjectStates objectForKey:filePath]; + if (!currentLocalObjectState) { + // The stored state corresponds to a remote or deleted local object, that's why there is no current state + // In the latter case it must be checked for legality, which can be determined by its stored state + // If it existed locally, but was deleted, state.exists is true, + // else if the stored state is an empty state that was created due to the server object, state.exists is false + if (storedLocalObjectState.exists) { + NSString *localObjectName; + if ([object.name hasSuffix:@"/"]) + localObjectName = [NSString stringWithFormat:@"%@:", [object.name substringToIndex:([object.name length] -1)]]; + else + localObjectName = [NSString stringWithString:object.name]; + NSArray *pathComponents = [localObjectName pathComponents]; + if (skipHidden) { + BOOL skipObject = NO; + for (NSString *pathComponent in pathComponents) { + if ([pathComponent hasPrefix:@"."]) { + // Skip hidden directory and its descendants, or hidden file + // Remove stored state if any + [containerStoredLocalObjectStates removeObjectForKey:objectName]; + [self saveLocalState]; + skipObject = YES; + break; + } } + if (skipObject) + continue; } - if (skipObject) + if ([containerExcludedDirectories containsObject:[[pathComponents objectAtIndex:0] lowercaseString]]) { + // Skip excluded directory object and its descendants, or root file object with same name + // Remove stored state + [containerStoredLocalObjectStates removeObjectForKey:object.name]; + [self saveLocalState]; continue; + } else if (containerExcludeRootFiles && ([pathComponents count] == 1) && !storedLocalObjectState.isDirectory) { + // Skip root file object + // Remove stored state + [containerStoredLocalObjectStates removeObjectForKey:object.name]; + [self saveLocalState]; + continue; + } } - if ([containerExcludedDirectories containsObject:[[pathComponents objectAtIndex:0] lowercaseString]]) { - // Skip excluded directory object and its descendants, or root file object with same name - // Remove stored state - [containerStoredLocalObjectStates removeObjectForKey:object.name]; - [self saveLocalState]; - continue; - } else if (containerExcludeRootFiles && ([pathComponents count] == 1) && !storedLocalObjectState.isDirectory) { - // Skip root file object - // Remove stored state - [containerStoredLocalObjectStates removeObjectForKey:object.name]; - [self saveLocalState]; - continue; - } + // There is also the off case that a local object has been created in the meantime + // This call works in any case, existent or non-existent local object + currentLocalObjectState = [PithosLocalObjectState localObjectStateWithFile:filePath + blockHash:pithosContainer.blockHash + blockSize:pithosContainer.blockSize]; } - // There is also the off case that a local object has been created in the meantime - // This call works in any case, existent or non-existent local object - currentLocalObjectState = [PithosLocalObjectState localObjectStateWithFile:filePath - blockHash:pithosContainer.blockHash - blockSize:pithosContainer.blockSize]; - } - - PithosLocalObjectState *remoteObjectState = [PithosLocalObjectState localObjectState]; - ASIPithosObject *remoteObject = [containerRemoteObjects objectForKey:object.name]; - if (remoteObject) { - if ([PithosUtilities isContentTypeDirectory:remoteObject.contentType]) { - remoteObjectState.isDirectory = YES; - } else { - remoteObjectState.hash = remoteObject.objectHash; + + PithosLocalObjectState *remoteObjectState = [PithosLocalObjectState localObjectState]; + ASIPithosObject *remoteObject = [containerRemoteObjects objectForKey:object.name]; + if (remoteObject) { + if ([PithosUtilities isContentTypeDirectory:remoteObject.contentType]) { + remoteObjectState.isDirectory = YES; + } else { + remoteObjectState.hash = remoteObject.objectHash; + } } - } - BOOL localStateHasChanged = ![currentLocalObjectState isEqualToState:storedLocalObjectState]; - BOOL serverStateHasChanged = ![remoteObjectState isEqualToState:storedLocalObjectState]; - DLog(@"Sync::localStateHasChanged: %d, serverStateHasChanged: %d", localStateHasChanged, serverStateHasChanged); - if (!localStateHasChanged) { - // Local state hasn't changed - if (serverStateHasChanged) { - // Server state has changed - // Update local state to match that of the server - object.bytes = remoteObject.bytes; - object.version = remoteObject.version; - object.contentType = remoteObject.contentType; - object.objectHash = remoteObject.objectHash; - [self updateLocalStateWithObject:object localFilePath:filePath - accountName:accountName pithosContainer:pithosContainer]; - } else if (!remoteObject && ![currentLocalObjectState exists]) { - // Server state hasn't changed - // If the object doesn't exist neither in the server or locally, it should be removed from the stored local objects - [containerStoredLocalObjectStates removeObjectForKey:objectName]; - [self saveLocalState]; - } - } else { - // Local state has changed - if (!serverStateHasChanged) { - // Server state hasn't changed - if (currentLocalObjectState.isDirectory) - object.contentType = @"application/directory"; - else - object.objectHash = currentLocalObjectState.hash; - [self updateServerStateWithCurrentState:currentLocalObjectState object:object localFilePath:filePath - accountName:accountName pithosContainer:pithosContainer]; - } else { - // Server state has also changed - if (remoteObjectState.isDirectory && currentLocalObjectState.isDirectory) { - // Both did the same change (directory) - storedLocalObjectState.filePath = filePath; - storedLocalObjectState.isDirectory = YES; - [self saveLocalState]; - } else if ([remoteObjectState isEqualToState:currentLocalObjectState]) { - // Both did the same change (object edit or delete) - if (![remoteObjectState exists]) { - [containerStoredLocalObjectStates removeObjectForKey:object.name]; - } else { - storedLocalObjectState.filePath = filePath; - storedLocalObjectState.hash = remoteObjectState.hash; + BOOL localStateHasChanged = ![currentLocalObjectState isEqualToState:storedLocalObjectState]; + BOOL serverStateHasChanged = ![remoteObjectState isEqualToState:storedLocalObjectState]; + DLog(@"Sync::localStateHasChanged: %d, serverStateHasChanged: %d", localStateHasChanged, serverStateHasChanged); + if (!localStateHasChanged) { + // Local state hasn't changed + if (serverStateHasChanged) { + // Server state has changed + // Update local state to match that of the server + object.bytes = remoteObject.bytes; + object.version = remoteObject.version; + object.contentType = remoteObject.contentType; + object.objectHash = remoteObject.objectHash; + [self updateLocalStateWithObject:object localFilePath:filePath + accountName:accountName pithosContainer:pithosContainer]; + } else if (!remoteObject && ![currentLocalObjectState exists]) { + // Server state hasn't changed + // If the object doesn't exist neither in the server or locally, it should be removed from the stored local objects + [containerStoredLocalObjectStates removeObjectForKey:objectName]; + [self saveLocalState]; } - [self saveLocalState]; + } else { + // Local state has changed + if (!serverStateHasChanged) { + // Server state hasn't changed + if (currentLocalObjectState.isDirectory) + object.contentType = @"application/directory"; + else + object.objectHash = currentLocalObjectState.hash; + [self updateServerStateWithCurrentState:currentLocalObjectState object:object localFilePath:filePath + accountName:accountName pithosContainer:pithosContainer]; } else { - // Conflict, we ask the user which change to keep - NSString *informativeText; - NSString *firstButtonText; - NSString *secondButtonText; - - if (![remoteObjectState exists]) { - // Remote object has been deleted - informativeText = [NSString stringWithFormat:@"'%@/%@' has been modified locally, while it has been deleted from server.", - [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name ]; - firstButtonText = @"Delete local file"; - secondButtonText = @"Upload file to server"; - } else if (![currentLocalObjectState exists]) { - informativeText = [NSString stringWithFormat:@"'%@/%@' has been modified on the server, while it has been deleted locally.", - [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name]; - firstButtonText = @"Download file from server"; - secondButtonText = @"Delete file on server"; + // Server state has also changed + if (remoteObjectState.isDirectory && currentLocalObjectState.isDirectory) { + // Both did the same change (directory) + storedLocalObjectState.filePath = filePath; + storedLocalObjectState.isDirectory = YES; + [self saveLocalState]; + } else if ([remoteObjectState isEqualToState:currentLocalObjectState]) { + // Both did the same change (object edit or delete) + if (![remoteObjectState exists]) { + [containerStoredLocalObjectStates removeObjectForKey:object.name]; + } else { + storedLocalObjectState.filePath = filePath; + storedLocalObjectState.hash = remoteObjectState.hash; + } + [self saveLocalState]; } else { - informativeText = [NSString stringWithFormat:@"'%@/%@' has been modified both locally and on the server.", - [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name]; - firstButtonText = @"Keep server version"; - secondButtonText = @"Keep local version"; - } - __block NSInteger choice; - dispatch_sync(dispatch_get_main_queue(), ^{ - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - [alert setMessageText:@"Conflict"]; - [alert setInformativeText:informativeText]; - [alert addButtonWithTitle:firstButtonText]; - [alert addButtonWithTitle:secondButtonText]; - [alert addButtonWithTitle:@"Do nothing"]; - choice = [alert runModal]; - }); - if (choice == NSAlertFirstButtonReturn) { - object.bytes = remoteObject.bytes; - object.version = remoteObject.version; - object.contentType = remoteObject.contentType; - object.objectHash = remoteObject.objectHash; - [self updateLocalStateWithObject:object localFilePath:filePath - accountName:accountName pithosContainer:pithosContainer]; - } if (choice == NSAlertSecondButtonReturn) { - if (currentLocalObjectState.isDirectory) - object.contentType = @"application/directory"; - else - object.objectHash = currentLocalObjectState.hash; - [self updateServerStateWithCurrentState:currentLocalObjectState object:object localFilePath:filePath - accountName:accountName pithosContainer:pithosContainer]; + // Conflict, we ask the user which change to keep + NSString *informativeText; + NSString *firstButtonText; + NSString *secondButtonText; + + if (![remoteObjectState exists]) { + // Remote object has been deleted + informativeText = [NSString stringWithFormat:@"'%@/%@' has been modified locally, while it has been deleted from server.", + [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name ]; + firstButtonText = @"Delete local file"; + secondButtonText = @"Upload file to server"; + } else if (![currentLocalObjectState exists]) { + informativeText = [NSString stringWithFormat:@"'%@/%@' has been modified on the server, while it has been deleted locally.", + [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name]; + firstButtonText = @"Download file from server"; + secondButtonText = @"Delete file on server"; + } else { + informativeText = [NSString stringWithFormat:@"'%@/%@' has been modified both locally and on the server.", + [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name]; + firstButtonText = @"Keep server version"; + secondButtonText = @"Keep local version"; + } + __block NSInteger choice; + dispatch_sync(dispatch_get_main_queue(), ^{ + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + [alert setMessageText:@"Conflict"]; + [alert setInformativeText:informativeText]; + [alert addButtonWithTitle:firstButtonText]; + [alert addButtonWithTitle:secondButtonText]; + [alert addButtonWithTitle:@"Do nothing"]; + choice = [alert runModal]; + }); + if (choice == NSAlertFirstButtonReturn) { + object.bytes = remoteObject.bytes; + object.version = remoteObject.version; + object.contentType = remoteObject.contentType; + object.objectHash = remoteObject.objectHash; + [self updateLocalStateWithObject:object localFilePath:filePath + accountName:accountName pithosContainer:pithosContainer]; + } if (choice == NSAlertSecondButtonReturn) { + if (currentLocalObjectState.isDirectory) + object.contentType = @"application/directory"; + else + object.objectHash = currentLocalObjectState.hash; + [self updateServerStateWithCurrentState:currentLocalObjectState object:object localFilePath:filePath + accountName:accountName pithosContainer:pithosContainer]; + } } } } } } + } + [self syncOperationFinishedWithSuccess:YES]; + } else { + [self listRequestFailed:containerRequest]; } - } - [self syncOperationFinishedWithSuccess:YES]; - } else { - [self listRequestFailed:containerRequest]; } - [pool drain]; } - (void)listRequestFailed:(ASIPithosContainerRequest *)containerRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [containerRequest.userInfo objectForKey:@"operation"]; - if (operation.isCancelled) { - [objects release]; - objects = nil; - [pool drain]; - return; - } - if (containerRequest.isCancelled) { - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] - withMessage:[containerRequest.userInfo objectForKey:@"stoppedActivityMessage"]]; - }); - [objects release]; - objects = nil; - [self syncOperationFinishedWithSuccess:NO]; - [pool drain]; - return; - } - // If the server listing fails, the sync should start over, so just retrying is enough - NSUInteger retries = [[containerRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue]; - if (retries > 0) { - ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[[PithosUtilities copyRequest:containerRequest] autorelease]; - [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; - [networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]]; - } else { - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] - withMessage:[containerRequest.userInfo objectForKey:@"failedActivityMessage"]]; - }); - [objects release]; - objects = nil; - // Since the server listing failed in all retries, the operation finished and the sync cycle is completeted unsuccesfully - [self syncOperationFinishedWithSuccess:NO]; - } - [pool drain]; -} - -- (void)downloadObjectBlockFinished:(ASIPithosObjectRequest *)objectRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; - DLog(@"Sync::download object block finished: %@", objectRequest.url); - if (operation.isCancelled) { - [self requestFailed:objectRequest]; - } else if (objectRequest.responseStatusCode == 206) { - NSString *accountName = [objectRequest.userInfo objectForKey:@"accountName"]; - ASIPithosContainer *pithosContainer = [objectRequest.userInfo objectForKey:@"pithosContainer"]; - ASIPithosObject *object = [objectRequest.userInfo objectForKey:@"pithosObject"]; - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSError *error; - PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"]; - - NSString *downloadsDirPath = self.tempDownloadsDirPath; - if (!downloadsDirPath) { + @autoreleasepool { + NSOperation *operation = [containerRequest.userInfo objectForKey:@"operation"]; + if (operation.isCancelled) { + [objects release]; + objects = nil; + return; + } + if (containerRequest.isCancelled) { dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:activity - withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]]; + [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] + withMessage:[containerRequest.userInfo objectForKey:@"stoppedActivityMessage"]]; }); + [objects release]; + objects = nil; [self syncOperationFinishedWithSuccess:NO]; - [pool drain]; return; } - - PithosLocalObjectState *storedState = [[[storedLocalObjectStates objectForKey:accountName] - objectForKey:pithosContainer.name] - objectForKey:object.name]; - if ((storedState.tmpFilePath == nil) || ![fileManager fileExistsAtPath:storedState.tmpFilePath]) { - NSString *tempFileTemplate = [downloadsDirPath stringByAppendingPathComponent:@"download.XXXXXX"]; - const char *tempFileTemplateCString = [tempFileTemplate fileSystemRepresentation]; - char *tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1); - strcpy(tempFileNameCString, tempFileTemplateCString); - int fileDescriptor = mkstemp(tempFileNameCString); - NSString *tempFilePath = [fileManager stringWithFileSystemRepresentation:tempFileNameCString length:strlen(tempFileNameCString)]; - free(tempFileNameCString); - if (fileDescriptor == -1) { - [PithosUtilities fileActionFailedAlertWithTitle:@"Create Temporary File Error" - message:[NSString stringWithFormat:@"Cannot create temporary file at '%@'", tempFilePath] - error:nil]; - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:activity - withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]]; - }); - [self syncOperationFinishedWithSuccess:NO]; - [pool drain]; - return; - } - close(fileDescriptor); - storedState.tmpFilePath = tempFilePath; - [self saveLocalState]; + // If the server listing fails, the sync should start over, so just retrying is enough + NSUInteger retries = [[containerRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue]; + if (retries > 0) { + ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[[PithosUtilities copyRequest:containerRequest] autorelease]; + [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; + [networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]]; + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] + withMessage:[containerRequest.userInfo objectForKey:@"failedActivityMessage"]]; + }); + [objects release]; + objects = nil; + // Since the server listing failed in all retries, the operation finished and the sync cycle is completeted unsuccesfully + [self syncOperationFinishedWithSuccess:NO]; } + } +} - NSUInteger missingBlockIndex = [[objectRequest.userInfo objectForKey:@"missingBlockIndex"] unsignedIntegerValue]; - NSFileHandle *tempFileHandle = [NSFileHandle fileHandleForWritingAtPath:storedState.tmpFilePath]; - [tempFileHandle seekToFileOffset:missingBlockIndex*pithosContainer.blockSize]; - [tempFileHandle writeData:[objectRequest responseData]]; - [tempFileHandle closeFile]; - - NSIndexSet *missingBlocks = [objectRequest.userInfo objectForKey:@"missingBlocks"]; - missingBlockIndex = [missingBlocks indexGreaterThanIndex:missingBlockIndex]; - if (missingBlockIndex == NSNotFound) { - NSString *filePath = [objectRequest.userInfo objectForKey:@"filePath"]; - NSString *dirPath = [filePath stringByDeletingLastPathComponent]; - if ([fileManager fileExistsAtPath:filePath] && ![self moveToTempTrashFile:filePath - accountName:accountName - pithosContainer:pithosContainer]) { +- (void)downloadObjectBlockFinished:(ASIPithosObjectRequest *)objectRequest { + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"Sync::download object block finished: %@", objectRequest.url); + if (operation.isCancelled) { + [self requestFailed:objectRequest]; + } else if (objectRequest.responseStatusCode == 206) { + NSString *accountName = [objectRequest.userInfo objectForKey:@"accountName"]; + ASIPithosContainer *pithosContainer = [objectRequest.userInfo objectForKey:@"pithosContainer"]; + ASIPithosObject *object = [objectRequest.userInfo objectForKey:@"pithosObject"]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSError *error; + PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"]; + + NSString *downloadsDirPath = self.tempDownloadsDirPath; + if (!downloadsDirPath) { dispatch_async(dispatch_get_main_queue(), ^{ [activityFacility endActivity:activity withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]]; }); [self syncOperationFinishedWithSuccess:NO]; - [pool drain]; return; - } else if (![fileManager fileExistsAtPath:dirPath]) { - // File doesn't exist but also the containing directory doesn't exist - // In most cases this should have been resolved as an update of the corresponding local object, - // but it never hurts to check + } + + PithosLocalObjectState *storedState = [[[storedLocalObjectStates objectForKey:accountName] + objectForKey:pithosContainer.name] + objectForKey:object.name]; + if ((storedState.tmpFilePath == nil) || ![fileManager fileExistsAtPath:storedState.tmpFilePath]) { + NSString *tempFileTemplate = [downloadsDirPath stringByAppendingPathComponent:@"download.XXXXXX"]; + const char *tempFileTemplateCString = [tempFileTemplate fileSystemRepresentation]; + char *tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1); + strcpy(tempFileNameCString, tempFileTemplateCString); + int fileDescriptor = mkstemp(tempFileNameCString); + NSString *tempFilePath = [fileManager stringWithFileSystemRepresentation:tempFileNameCString length:strlen(tempFileNameCString)]; + free(tempFileNameCString); + if (fileDescriptor == -1) { + [PithosUtilities fileActionFailedAlertWithTitle:@"Create Temporary File Error" + message:[NSString stringWithFormat:@"Cannot create temporary file at '%@'", tempFilePath] + error:nil]; + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:activity + withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]]; + }); + [self syncOperationFinishedWithSuccess:NO]; + return; + } + close(fileDescriptor); + storedState.tmpFilePath = tempFilePath; + [self saveLocalState]; + } + + NSUInteger missingBlockIndex = [[objectRequest.userInfo objectForKey:@"missingBlockIndex"] unsignedIntegerValue]; + NSFileHandle *tempFileHandle = [NSFileHandle fileHandleForWritingAtPath:storedState.tmpFilePath]; + [tempFileHandle seekToFileOffset:missingBlockIndex*pithosContainer.blockSize]; + [tempFileHandle writeData:[objectRequest responseData]]; + [tempFileHandle closeFile]; + + NSIndexSet *missingBlocks = [objectRequest.userInfo objectForKey:@"missingBlocks"]; + missingBlockIndex = [missingBlocks indexGreaterThanIndex:missingBlockIndex]; + if (missingBlockIndex == NSNotFound) { + NSString *filePath = [objectRequest.userInfo objectForKey:@"filePath"]; + NSString *dirPath = [filePath stringByDeletingLastPathComponent]; + if ([fileManager fileExistsAtPath:filePath] && ![self moveToTempTrashFile:filePath + accountName:accountName + pithosContainer:pithosContainer]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:activity + withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]]; + }); + [self syncOperationFinishedWithSuccess:NO]; + return; + } else if (![fileManager fileExistsAtPath:dirPath]) { + // File doesn't exist but also the containing directory doesn't exist + // In most cases this should have been resolved as an update of the corresponding local object, + // but it never hurts to check + error = nil; + [fileManager createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:nil error:nil]; + if (error != nil) { + [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" + message:[NSString stringWithFormat:@"Cannot create directory at '%@'", dirPath] + error:error]; + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] + withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]]; + }); + [self syncOperationFinishedWithSuccess:NO]; + return; + } + } + // Move file from tmp download error = nil; - [fileManager createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:nil error:nil]; + [fileManager moveItemAtPath:storedState.tmpFilePath toPath:filePath error:&error]; if (error != nil) { - [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" - message:[NSString stringWithFormat:@"Cannot create directory at '%@'", dirPath] + [PithosUtilities fileActionFailedAlertWithTitle:@"Move File Error" + message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", storedState.tmpFilePath, filePath] error:error]; dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] + [activityFacility endActivity:activity withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]]; }); [self syncOperationFinishedWithSuccess:NO]; - [pool drain]; return; } - } - // Move file from tmp download - error = nil; - [fileManager moveItemAtPath:storedState.tmpFilePath toPath:filePath error:&error]; - if (error != nil) { - [PithosUtilities fileActionFailedAlertWithTitle:@"Move File Error" - message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", storedState.tmpFilePath, filePath] - error:error]; dispatch_async(dispatch_get_main_queue(), ^{ [activityFacility endActivity:activity - withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]]; + withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] + totalBytes:activity.totalBytes + currentBytes:activity.totalBytes]; }); - [self syncOperationFinishedWithSuccess:NO]; - [pool drain]; + + storedState.filePath = filePath; + storedState.hash = object.objectHash; + storedState.tmpFilePath = nil; + [self saveLocalState]; + [self syncOperationFinishedWithSuccess:YES]; return; + } else { + if (newSyncRequested || syncLate || operation.isCancelled) { + [self requestFailed:objectRequest]; + } else { + __block ASIPithosObjectRequest *newObjectRequest = [PithosUtilities objectBlockDataRequestWithPithos:pithos + containerName:pithosContainer.name + object:object + blockIndex:missingBlockIndex + blockSize:pithosContainer.blockSize]; + if (![accountName isEqualToString:@""]) + [newObjectRequest setRequestUserFromDefaultTo:accountName withPithos:pithos]; + newObjectRequest.delegate = self; + newObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + newObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + newObjectRequest.userInfo = objectRequest.userInfo; + [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"]; + [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; + [newObjectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){ + [activityFacility updateActivity:activity + withMessage:[NSString stringWithFormat:@"%@ (%.0f%%)", + [newObjectRequest.userInfo objectForKey:@"messagePrefix"], + (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))] + totalBytes:activity.totalBytes + currentBytes:(activity.currentBytes + size)]; + }]; + [networkQueue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]]; + } } + } else if (objectRequest.responseStatusCode == 412) { + // The object has changed on the server dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:activity - withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] - totalBytes:activity.totalBytes - currentBytes:activity.totalBytes]; + [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] + withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]]; }); - - storedState.filePath = filePath; - storedState.hash = object.objectHash; - storedState.tmpFilePath = nil; - [self saveLocalState]; - [self syncOperationFinishedWithSuccess:YES]; - [pool drain]; - return; + [self syncOperationFinishedWithSuccess:NO]; } else { + [self requestFailed:objectRequest]; + } + } +} + +- (void)downloadObjectHashMapFinished:(ASIPithosObjectRequest *)objectRequest { + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"Sync::download object hashmap finished: %@", objectRequest.url); + if (operation.isCancelled) { + [self requestFailed:objectRequest]; + } else if (objectRequest.responseStatusCode == 200) { if (newSyncRequested || syncLate || operation.isCancelled) { [self requestFailed:objectRequest]; } else { + NSString *accountName = [objectRequest.userInfo objectForKey:@"accountName"]; + ASIPithosContainer *pithosContainer = [objectRequest.userInfo objectForKey:@"pithosContainer"]; + ASIPithosObject *object = [objectRequest.userInfo objectForKey:@"pithosObject"]; + PithosLocalObjectState *storedState = [[[storedLocalObjectStates objectForKey:accountName] + objectForKey:pithosContainer.name] + objectForKey:object.name]; + if ([PithosUtilities bytesOfFile:storedState.tmpFilePath] > object.bytes) + [[NSFileHandle fileHandleForWritingAtPath:storedState.tmpFilePath] truncateFileAtOffset:object.bytes]; + PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"]; + NSIndexSet *missingBlocks = [PithosUtilities missingBlocksForFile:storedState.tmpFilePath + blockSize:pithosContainer.blockSize + blockHash:pithosContainer.blockHash + withHashes:[objectRequest hashes]]; + NSUInteger missingBlockIndex = [missingBlocks firstIndex]; + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility updateActivity:activity + withMessage:[NSString stringWithFormat:@"%@ (%.0f%%)", + [objectRequest.userInfo objectForKey:@"messagePrefix"], + (100*(activity.totalBytes - [missingBlocks count]*pithosContainer.blockSize + 0.0)/(activity.totalBytes + 0.0))] + totalBytes:activity.totalBytes + currentBytes:(activity.totalBytes - [missingBlocks count]*pithosContainer.blockSize)]; + }); + __block ASIPithosObjectRequest *newObjectRequest = [PithosUtilities objectBlockDataRequestWithPithos:pithos containerName:pithosContainer.name object:object @@ -1958,8 +1989,10 @@ newObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); newObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); newObjectRequest.userInfo = objectRequest.userInfo; + [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:missingBlocks forKey:@"missingBlocks"]; [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"]; [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; + [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:NSStringFromSelector(@selector(downloadObjectBlockFinished:)) forKey:@"didFinishSelector"]; [newObjectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){ [activityFacility updateActivity:activity withMessage:[NSString stringWithFormat:@"%@ (%.0f%%)", @@ -1970,290 +2003,148 @@ }]; [networkQueue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]]; } + } else { + [self requestFailed:objectRequest]; } - } else if (objectRequest.responseStatusCode == 412) { - // The object has changed on the server - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] - withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]]; - }); - [self syncOperationFinishedWithSuccess:NO]; - } else { - [self requestFailed:objectRequest]; } - [pool drain]; } -- (void)downloadObjectHashMapFinished:(ASIPithosObjectRequest *)objectRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; - DLog(@"Sync::download object hashmap finished: %@", objectRequest.url); - if (operation.isCancelled) { - [self requestFailed:objectRequest]; - } else if (objectRequest.responseStatusCode == 200) { - if (newSyncRequested || syncLate || operation.isCancelled) { +- (void)uploadDirectoryObjectFinished:(ASIPithosObjectRequest *)objectRequest { + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"Sync::upload directory object finished: %@", objectRequest.url); + if (operation.isCancelled) { [self requestFailed:objectRequest]; - } else { - NSString *accountName = [objectRequest.userInfo objectForKey:@"accountName"]; - ASIPithosContainer *pithosContainer = [objectRequest.userInfo objectForKey:@"pithosContainer"]; - ASIPithosObject *object = [objectRequest.userInfo objectForKey:@"pithosObject"]; - PithosLocalObjectState *storedState = [[[storedLocalObjectStates objectForKey:accountName] - objectForKey:pithosContainer.name] - objectForKey:object.name]; - if ([PithosUtilities bytesOfFile:storedState.tmpFilePath] > object.bytes) - [[NSFileHandle fileHandleForWritingAtPath:storedState.tmpFilePath] truncateFileAtOffset:object.bytes]; - PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"]; - NSIndexSet *missingBlocks = [PithosUtilities missingBlocksForFile:storedState.tmpFilePath - blockSize:pithosContainer.blockSize - blockHash:pithosContainer.blockHash - withHashes:[objectRequest hashes]]; - NSUInteger missingBlockIndex = [missingBlocks firstIndex]; + } else if (objectRequest.responseStatusCode == 201) { + PithosLocalObjectState *storedState = [[[storedLocalObjectStates objectForKey:[objectRequest.userInfo objectForKey:@"accountName"]] + objectForKey:[[objectRequest.userInfo objectForKey:@"pithosContainer"] name]] + objectForKey:[[objectRequest.userInfo objectForKey:@"pithosObject"] name]]; + storedState.isDirectory = YES; + [self saveLocalState]; dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility updateActivity:activity - withMessage:[NSString stringWithFormat:@"%@ (%.0f%%)", - [objectRequest.userInfo objectForKey:@"messagePrefix"], - (100*(activity.totalBytes - [missingBlocks count]*pithosContainer.blockSize + 0.0)/(activity.totalBytes + 0.0))] - totalBytes:activity.totalBytes - currentBytes:(activity.totalBytes - [missingBlocks count]*pithosContainer.blockSize)]; + [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] + withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]]; }); - - __block ASIPithosObjectRequest *newObjectRequest = [PithosUtilities objectBlockDataRequestWithPithos:pithos - containerName:pithosContainer.name - object:object - blockIndex:missingBlockIndex - blockSize:pithosContainer.blockSize]; - if (![accountName isEqualToString:@""]) - [newObjectRequest setRequestUserFromDefaultTo:accountName withPithos:pithos]; - newObjectRequest.delegate = self; - newObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - newObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - newObjectRequest.userInfo = objectRequest.userInfo; - [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:missingBlocks forKey:@"missingBlocks"]; - [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"]; - [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; - [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:NSStringFromSelector(@selector(downloadObjectBlockFinished:)) forKey:@"didFinishSelector"]; - [newObjectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){ - [activityFacility updateActivity:activity - withMessage:[NSString stringWithFormat:@"%@ (%.0f%%)", - [newObjectRequest.userInfo objectForKey:@"messagePrefix"], - (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))] - totalBytes:activity.totalBytes - currentBytes:(activity.currentBytes + size)]; - }]; - [networkQueue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]]; + [self syncOperationFinishedWithSuccess:YES]; + } else { + [self requestFailed:objectRequest]; } - } else { - [self requestFailed:objectRequest]; - } - [pool drain]; -} - -- (void)uploadDirectoryObjectFinished:(ASIPithosObjectRequest *)objectRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; - DLog(@"Sync::upload directory object finished: %@", objectRequest.url); - if (operation.isCancelled) { - [self requestFailed:objectRequest]; - } else if (objectRequest.responseStatusCode == 201) { - PithosLocalObjectState *storedState = [[[storedLocalObjectStates objectForKey:[objectRequest.userInfo objectForKey:@"accountName"]] - objectForKey:[[objectRequest.userInfo objectForKey:@"pithosContainer"] name]] - objectForKey:[[objectRequest.userInfo objectForKey:@"pithosObject"] name]]; - storedState.isDirectory = YES; - [self saveLocalState]; - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] - withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]]; - }); - [self syncOperationFinishedWithSuccess:YES]; - } else { - [self requestFailed:objectRequest]; } - [pool drain]; } - (void)moveObjectToTrashFinished:(ASIPithosObjectRequest *)objectRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; - DLog(@"Sync::move object to trash finished: %@", objectRequest.url); - if (operation.isCancelled) { - [self requestFailed:objectRequest]; - } else if (objectRequest.responseStatusCode == 201) { - [[[storedLocalObjectStates objectForKey:[objectRequest.userInfo objectForKey:@"accountName"]] - objectForKey:[[objectRequest.userInfo objectForKey:@"pithosContainer"] name]] - removeObjectForKey:[[objectRequest.userInfo objectForKey:@"pithosObject"] name]]; - [self saveLocalState]; - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] - withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]]; - }); - [self syncOperationFinishedWithSuccess:YES]; - } else { - [self requestFailed:objectRequest]; + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"Sync::move object to trash finished: %@", objectRequest.url); + if (operation.isCancelled) { + [self requestFailed:objectRequest]; + } else if (objectRequest.responseStatusCode == 201) { + [[[storedLocalObjectStates objectForKey:[objectRequest.userInfo objectForKey:@"accountName"]] + objectForKey:[[objectRequest.userInfo objectForKey:@"pithosContainer"] name]] + removeObjectForKey:[[objectRequest.userInfo objectForKey:@"pithosObject"] name]]; + [self saveLocalState]; + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] + withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]]; + }); + [self syncOperationFinishedWithSuccess:YES]; + } else { + [self requestFailed:objectRequest]; + } } - [pool drain]; } - (void)deleteObjectFinished:(ASIPithosObjectRequest *)objectRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; - DLog(@"Sync::delete object finished: %@", objectRequest.url); - if (operation.isCancelled) { - [self requestFailed:objectRequest]; - } else if (objectRequest.responseStatusCode == 204) { - [[[storedLocalObjectStates objectForKey:[objectRequest.userInfo objectForKey:@"accountName"]] - objectForKey:[[objectRequest.userInfo objectForKey:@"pithosContainer"] name]] - removeObjectForKey:[[objectRequest.userInfo objectForKey:@"pithosObject"] name]]; - [self saveLocalState]; - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] - withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]]; - }); - [self syncOperationFinishedWithSuccess:YES]; - } else { - [self requestFailed:objectRequest]; - } - [pool drain]; -} - -- (void)uploadObjectUsingHashMapFinished:(ASIPithosObjectRequest *)objectRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; - DLog(@"Sync::upload using hashmap finished: %@", objectRequest.url); - NSString *accountName = [objectRequest.userInfo objectForKey:@"accountName"]; - ASIPithosContainer *pithosContainer = [objectRequest.userInfo objectForKey:@"pithosContainer"]; - ASIPithosObject *object = [objectRequest.userInfo objectForKey:@"pithosObject"]; - PithosLocalObjectState *storedState = [[[storedLocalObjectStates objectForKey:accountName] - objectForKey:pithosContainer.name] - objectForKey:object.name]; - PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"]; - NSUInteger totalBytes = activity.totalBytes; - NSUInteger currentBytes = activity.currentBytes; - if (operation.isCancelled) { - [self requestFailed:objectRequest]; - } else if (objectRequest.responseStatusCode == 201) { - DLog(@"Sync::object created: %@", objectRequest.url); - storedState.filePath = [objectRequest.userInfo objectForKey:@"filePath"]; - storedState.hash = object.objectHash; - [self saveLocalState]; - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:activity - withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] - totalBytes:totalBytes - currentBytes:totalBytes]; - }); - [self syncOperationFinishedWithSuccess:YES]; - } else if (objectRequest.responseStatusCode == 409) { - if (newSyncRequested || syncLate || operation.isCancelled) { + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"Sync::delete object finished: %@", objectRequest.url); + if (operation.isCancelled) { [self requestFailed:objectRequest]; - } else { - NSUInteger iteration = [[objectRequest.userInfo objectForKey:@"iteration"] unsignedIntegerValue]; - if (iteration == 0) { - DLog(@"Sync::upload iteration limit reached: %@", objectRequest.url); - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:activity withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]]; - }); - [self syncOperationFinishedWithSuccess:NO]; - [pool drain]; - return; - } - DLog(@"Sync::object is missing hashes: %@", objectRequest.url); - NSIndexSet *missingBlocks = [PithosUtilities missingBlocksForHashes:[objectRequest.userInfo objectForKey:@"hashes"] - withMissingHashes:[objectRequest hashes]]; - if (totalBytes >= [missingBlocks count]*pithosContainer.blockSize) - currentBytes = totalBytes - [missingBlocks count]*pithosContainer.blockSize; + } else if (objectRequest.responseStatusCode == 204) { + [[[storedLocalObjectStates objectForKey:[objectRequest.userInfo objectForKey:@"accountName"]] + objectForKey:[[objectRequest.userInfo objectForKey:@"pithosContainer"] name]] + removeObjectForKey:[[objectRequest.userInfo objectForKey:@"pithosObject"] name]]; + [self saveLocalState]; dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility updateActivity:activity - withMessage:[NSString stringWithFormat:@"%@ (%.0f%%)", - [objectRequest.userInfo objectForKey:@"messagePrefix"], - (100*(currentBytes + 0.0)/(totalBytes + 0.0))] - totalBytes:totalBytes - currentBytes:currentBytes]; + [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] + withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]]; }); - NSUInteger missingBlockIndex = [missingBlocks firstIndex]; - __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithPithos:pithos - containerName:pithosContainer.name - blockSize:pithosContainer.blockSize - forFile:[objectRequest.userInfo objectForKey:@"filePath"] - missingBlockIndex:missingBlockIndex - sharingAccount:([accountName isEqualToString:@""] ? nil : accountName)]; - newContainerRequest.delegate = self; - newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - newContainerRequest.userInfo = objectRequest.userInfo; - [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:(--iteration)] forKey:@"iteration"]; - [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; - [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:missingBlocks forKey:@"missingBlocks"]; - [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"]; - [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadMissingBlockFinished:)) forKey:@"didFinishSelector"]; - [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){ - [activityFacility updateActivity:activity - withMessage:[NSString stringWithFormat:@"%@ (%.0f%%)", - [newContainerRequest.userInfo objectForKey:@"messagePrefix"], - (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))] - totalBytes:activity.totalBytes - currentBytes:(activity.currentBytes + size)]; - }]; - [networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]]; + [self syncOperationFinishedWithSuccess:YES]; + } else { + [self requestFailed:objectRequest]; } - } else { - [self requestFailed:objectRequest]; } - [pool drain]; } -- (void)uploadMissingBlockFinished:(ASIPithosContainerRequest *)containerRequest { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [containerRequest.userInfo objectForKey:@"operation"]; - DLog(@"Sync::upload of missing block finished: %@", containerRequest.url); - if (operation.isCancelled) { - [self requestFailed:containerRequest]; - } else if (containerRequest.responseStatusCode == 202) { - NSString *accountName = [containerRequest.userInfo objectForKey:@"accountName"]; - ASIPithosContainer *pithosContainer = [containerRequest.userInfo objectForKey:@"pithosContainer"]; - ASIPithosObject *object = [containerRequest.userInfo objectForKey:@"pithosObject"]; - PithosActivity *activity = [containerRequest.userInfo objectForKey:@"activity"]; - NSIndexSet *missingBlocks = [containerRequest.userInfo objectForKey:@"missingBlocks"]; - NSUInteger missingBlockIndex = [[containerRequest.userInfo objectForKey:@"missingBlockIndex"] unsignedIntegerValue]; - missingBlockIndex = [missingBlocks indexGreaterThanIndex:missingBlockIndex]; +- (void)uploadObjectUsingHashMapFinished:(ASIPithosObjectRequest *)objectRequest { + @autoreleasepool { + NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"]; + DLog(@"Sync::upload using hashmap finished: %@", objectRequest.url); + NSString *accountName = [objectRequest.userInfo objectForKey:@"accountName"]; + ASIPithosContainer *pithosContainer = [objectRequest.userInfo objectForKey:@"pithosContainer"]; + ASIPithosObject *object = [objectRequest.userInfo objectForKey:@"pithosObject"]; + PithosLocalObjectState *storedState = [[[storedLocalObjectStates objectForKey:accountName] + objectForKey:pithosContainer.name] + objectForKey:object.name]; + PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"]; + NSUInteger totalBytes = activity.totalBytes; + NSUInteger currentBytes = activity.currentBytes; if (operation.isCancelled) { - [self requestFailed:containerRequest]; - } else if (missingBlockIndex == NSNotFound) { - NSArray *hashes = [containerRequest.userInfo objectForKey:@"hashes"]; - ASIPithosObjectRequest *newObjectRequest = [PithosUtilities writeObjectDataRequestWithPithos:pithos - containerName:pithosContainer.name - objectName:object.name - contentType:object.contentType - blockSize:pithosContainer.blockSize - blockHash:pithosContainer.blockHash - forFile:[containerRequest.userInfo objectForKey:@"filePath"] - checkIfExists:NO - hashes:&hashes - sharingAccount:([accountName isEqualToString:@""] ? nil : accountName)]; - newObjectRequest.delegate = self; - newObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); - newObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - newObjectRequest.userInfo = containerRequest.userInfo; - [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; - [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlocks"]; - [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlockIndex"]; - [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)) forKey:@"didFinishSelector"]; - [networkQueue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]]; - } else { + [self requestFailed:objectRequest]; + } else if (objectRequest.responseStatusCode == 201) { + DLog(@"Sync::object created: %@", objectRequest.url); + storedState.filePath = [objectRequest.userInfo objectForKey:@"filePath"]; + storedState.hash = object.objectHash; + [self saveLocalState]; + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:activity + withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] + totalBytes:totalBytes + currentBytes:totalBytes]; + }); + [self syncOperationFinishedWithSuccess:YES]; + } else if (objectRequest.responseStatusCode == 409) { if (newSyncRequested || syncLate || operation.isCancelled) { - [self requestFailed:containerRequest]; + [self requestFailed:objectRequest]; } else { + NSUInteger iteration = [[objectRequest.userInfo objectForKey:@"iteration"] unsignedIntegerValue]; + if (iteration == 0) { + DLog(@"Sync::upload iteration limit reached: %@", objectRequest.url); + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:activity withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]]; + }); + [self syncOperationFinishedWithSuccess:NO]; + return; + } + DLog(@"Sync::object is missing hashes: %@", objectRequest.url); + NSIndexSet *missingBlocks = [PithosUtilities missingBlocksForHashes:[objectRequest.userInfo objectForKey:@"hashes"] + withMissingHashes:[objectRequest hashes]]; + if (totalBytes >= [missingBlocks count]*pithosContainer.blockSize) + currentBytes = totalBytes - [missingBlocks count]*pithosContainer.blockSize; + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility updateActivity:activity + withMessage:[NSString stringWithFormat:@"%@ (%.0f%%)", + [objectRequest.userInfo objectForKey:@"messagePrefix"], + (100*(currentBytes + 0.0)/(totalBytes + 0.0))] + totalBytes:totalBytes + currentBytes:currentBytes]; + }); + NSUInteger missingBlockIndex = [missingBlocks firstIndex]; __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithPithos:pithos - containerName:pithosContainer.name - blockSize:pithosContainer.blockSize - forFile:[containerRequest.userInfo objectForKey:@"filePath"] + containerName:pithosContainer.name + blockSize:pithosContainer.blockSize + forFile:[objectRequest.userInfo objectForKey:@"filePath"] missingBlockIndex:missingBlockIndex sharingAccount:([accountName isEqualToString:@""] ? nil : accountName)]; newContainerRequest.delegate = self; newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); - newContainerRequest.userInfo = containerRequest.userInfo; + newContainerRequest.userInfo = objectRequest.userInfo; + [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:(--iteration)] forKey:@"iteration"]; [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; + [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:missingBlocks forKey:@"missingBlocks"]; [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"]; + [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadMissingBlockFinished:)) forKey:@"didFinishSelector"]; [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){ [activityFacility updateActivity:activity withMessage:[NSString stringWithFormat:@"%@ (%.0f%%)", @@ -2264,43 +2155,109 @@ }]; [networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]]; } + } else { + [self requestFailed:objectRequest]; } - } else { - [self requestFailed:containerRequest]; } - [pool drain]; } -- (void)requestFailed:(ASIPithosRequest *)request { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSOperation *operation = [request.userInfo objectForKey:@"operation"]; - DLog(@"Sync::request failed: %@", request.url); - if (operation.isCancelled) { - [pool drain]; - return; - } - if (request.isCancelled || newSyncRequested || syncLate) { - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] - withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]]; - }); - [self syncOperationFinishedWithSuccess:NO]; - [pool drain]; - return; +- (void)uploadMissingBlockFinished:(ASIPithosContainerRequest *)containerRequest { + @autoreleasepool { + NSOperation *operation = [containerRequest.userInfo objectForKey:@"operation"]; + DLog(@"Sync::upload of missing block finished: %@", containerRequest.url); + if (operation.isCancelled) { + [self requestFailed:containerRequest]; + } else if (containerRequest.responseStatusCode == 202) { + NSString *accountName = [containerRequest.userInfo objectForKey:@"accountName"]; + ASIPithosContainer *pithosContainer = [containerRequest.userInfo objectForKey:@"pithosContainer"]; + ASIPithosObject *object = [containerRequest.userInfo objectForKey:@"pithosObject"]; + PithosActivity *activity = [containerRequest.userInfo objectForKey:@"activity"]; + NSIndexSet *missingBlocks = [containerRequest.userInfo objectForKey:@"missingBlocks"]; + NSUInteger missingBlockIndex = [[containerRequest.userInfo objectForKey:@"missingBlockIndex"] unsignedIntegerValue]; + missingBlockIndex = [missingBlocks indexGreaterThanIndex:missingBlockIndex]; + if (operation.isCancelled) { + [self requestFailed:containerRequest]; + } else if (missingBlockIndex == NSNotFound) { + NSArray *hashes = [containerRequest.userInfo objectForKey:@"hashes"]; + ASIPithosObjectRequest *newObjectRequest = [PithosUtilities writeObjectDataRequestWithPithos:pithos + containerName:pithosContainer.name + objectName:object.name + contentType:object.contentType + blockSize:pithosContainer.blockSize + blockHash:pithosContainer.blockHash + forFile:[containerRequest.userInfo objectForKey:@"filePath"] + checkIfExists:NO + hashes:&hashes + sharingAccount:([accountName isEqualToString:@""] ? nil : accountName)]; + newObjectRequest.delegate = self; + newObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + newObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + newObjectRequest.userInfo = containerRequest.userInfo; + [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; + [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlocks"]; + [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlockIndex"]; + [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)) forKey:@"didFinishSelector"]; + [networkQueue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]]; + } else { + if (newSyncRequested || syncLate || operation.isCancelled) { + [self requestFailed:containerRequest]; + } else { + __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithPithos:pithos + containerName:pithosContainer.name + blockSize:pithosContainer.blockSize + forFile:[containerRequest.userInfo objectForKey:@"filePath"] + missingBlockIndex:missingBlockIndex + sharingAccount:([accountName isEqualToString:@""] ? nil : accountName)]; + newContainerRequest.delegate = self; + newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + newContainerRequest.userInfo = containerRequest.userInfo; + [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"]; + [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"]; + [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){ + [activityFacility updateActivity:activity + withMessage:[NSString stringWithFormat:@"%@ (%.0f%%)", + [newContainerRequest.userInfo objectForKey:@"messagePrefix"], + (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))] + totalBytes:activity.totalBytes + currentBytes:(activity.currentBytes + size)]; + }]; + [networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]]; + } + } + } else { + [self requestFailed:containerRequest]; + } } - NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue]; - if (retries > 0) { - ASIPithosRequest *newRequest = (ASIPithosRequest *)[[PithosUtilities copyRequest:request] autorelease]; - [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; - [networkQueue addOperation:[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]]]; - } else { - dispatch_async(dispatch_get_main_queue(), ^{ - [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] - withMessage:[request.userInfo objectForKey:@"failedActivityMessage"]]; - }); - [self syncOperationFinishedWithSuccess:NO]; +} + +- (void)requestFailed:(ASIPithosRequest *)request { + @autoreleasepool { + NSOperation *operation = [request.userInfo objectForKey:@"operation"]; + DLog(@"Sync::request failed: %@", request.url); + if (operation.isCancelled) + return; + if (request.isCancelled || newSyncRequested || syncLate) { + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] + withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]]; + }); + [self syncOperationFinishedWithSuccess:NO]; + return; + } + NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue]; + if (retries > 0) { + ASIPithosRequest *newRequest = (ASIPithosRequest *)[[PithosUtilities copyRequest:request] autorelease]; + [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; + [networkQueue addOperation:[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]]]; + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] + withMessage:[request.userInfo objectForKey:@"failedActivityMessage"]]; + }); + [self syncOperationFinishedWithSuccess:NO]; + } } - [pool drain]; } @end diff --git a/pithos-macos/PithosUtilities.m b/pithos-macos/PithosUtilities.m index 273bbe2..c3641fe 100644 --- a/pithos-macos/PithosUtilities.m +++ b/pithos-macos/PithosUtilities.m @@ -926,9 +926,11 @@ if (uti != NULL) { CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType); CFRelease(uti); - return (NSString *)MIMEType; + CFRelease(url); + return [(NSString *)MIMEType autorelease]; } } + CFRelease(url); return nil; } @@ -1101,7 +1103,7 @@ 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]; @@ -1148,7 +1150,7 @@ NSString *subdirNameExtraSuffix; if ([subdirName hasSuffix:@"/"]) { subdirNamePrefix = [subdirName substringToIndex:([subdirName length] - 1)]; - subdirNameExtraSuffix = [NSString stringWithString:@"/"]; + subdirNameExtraSuffix = @"/"; } else { subdirNamePrefix = [NSString stringWithString:subdirName]; subdirNameExtraSuffix = [NSString string]; @@ -1204,13 +1206,13 @@ [request responseString]]; DLog(@"%@", message); dispatch_async(dispatch_get_main_queue(), ^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - [alert setMessageText:@"HTTP Request Error"]; - [alert setInformativeText:message]; - [alert addButtonWithTitle:@"OK"]; - [alert runModal]; - [pool drain]; + @autoreleasepool { + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + [alert setMessageText:@"HTTP Request Error"]; + [alert setInformativeText:message]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + } }); } @@ -1230,40 +1232,40 @@ [request responseString]]; DLog(@"%@", message); dispatch_async(dispatch_get_main_queue(), ^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - [alert setMessageText:@"Unexpected Response Status"]; - [alert setInformativeText:message]; - [alert addButtonWithTitle:@"OK"]; - [alert runModal]; - [pool drain]; + @autoreleasepool { + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + [alert setMessageText:@"Unexpected Response Status"]; + [alert setInformativeText:message]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + } }); } + (void)httpAuthenticationError { dispatch_async(dispatch_get_main_queue(), ^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - [alert setMessageText:@"Authentication Error"]; - [alert setInformativeText:@"Authentication error, please check your token or login again"]; - [alert addButtonWithTitle:@"OK"]; - [alert runModal]; - [pool drain]; + @autoreleasepool { + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + [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(), ^{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - [alert setMessageText:title]; - if (error) - [alert setInformativeText:[NSString stringWithFormat:@"%@: %@", message, [error localizedDescription]]]; - else - [alert setInformativeText:message]; - [alert addButtonWithTitle:@"OK"]; - [alert runModal]; - [pool drain]; + @autoreleasepool { + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + [alert setMessageText:title]; + if (error) + [alert setInformativeText:[NSString stringWithFormat:@"%@: %@", message, [error localizedDescription]]]; + else + [alert setInformativeText:message]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + } }); } diff --git a/pithos-macos/PolicyVersioningTransformer.m b/pithos-macos/PolicyVersioningTransformer.m index fba205c..402a82c 100644 --- a/pithos-macos/PolicyVersioningTransformer.m +++ b/pithos-macos/PolicyVersioningTransformer.m @@ -79,7 +79,7 @@ } + (void)initialize { - [[NSValueTransformer class] setValueTransformer:[self new] forName:@"SharingDictionaryTransformer"]; + [[self class] setValueTransformer:[[self new] autorelease] forName:@"SharingDictionaryTransformer"]; } @end diff --git a/pithos-macos/PublicURLTransformer.m b/pithos-macos/PublicURLTransformer.m index 928f0f7..32c55bb 100644 --- a/pithos-macos/PublicURLTransformer.m +++ b/pithos-macos/PublicURLTransformer.m @@ -59,7 +59,7 @@ } + (void)initialize { - [[NSValueTransformer class] setValueTransformer:[self new] forName:@"PublicURLTransformer"]; + [[self class] setValueTransformer:[[self new] autorelease] forName:@"PublicURLTransformer"]; } @end diff --git a/pithos-macos/SharingAccountBoolTransformer.m b/pithos-macos/SharingAccountBoolTransformer.m index 873c60d..ebc9779 100644 --- a/pithos-macos/SharingAccountBoolTransformer.m +++ b/pithos-macos/SharingAccountBoolTransformer.m @@ -54,7 +54,7 @@ } + (void)initialize { - [[NSValueTransformer class] setValueTransformer:[self new] forName:@"SharingAccountBoolTransformer"]; + [[self class] setValueTransformer:[[self new] autorelease] forName:@"SharingAccountBoolTransformer"]; } @end diff --git a/pithos-macos/SharingDictionaryTransformer.m b/pithos-macos/SharingDictionaryTransformer.m index a0f354e..d47ee7c 100644 --- a/pithos-macos/SharingDictionaryTransformer.m +++ b/pithos-macos/SharingDictionaryTransformer.m @@ -115,7 +115,7 @@ } + (void)initialize { - [[NSValueTransformer class] setValueTransformer:[self new] forName:@"SharingDictionaryTransformer"]; + [[self class] setValueTransformer:[[self new] autorelease] forName:@"SharingDictionaryTransformer"]; } @end diff --git a/pithos-macos/UsingSizeTransformer.m b/pithos-macos/UsingSizeTransformer.m index 7c9547c..356773f 100644 --- a/pithos-macos/UsingSizeTransformer.m +++ b/pithos-macos/UsingSizeTransformer.m @@ -65,7 +65,7 @@ } + (void)initialize { - [[NSValueTransformer class] setValueTransformer:[self new] forName:@"UsingSizeTransformer"]; + [[self class] setValueTransformer:[[self new] autorelease] forName:@"UsingSizeTransformer"]; } @end -- 1.7.10.4