Fix analyze errors
[pithos-macos] / pithos-macos / PithosAccountNode.m
index e45b6d7..10fa3a4 100644 (file)
@@ -2,7 +2,7 @@
 //  PithosAccountNode.m
 //  pithos-macos
 //
-// Copyright 2011 GRNET S.A. All rights reserved.
+// Copyright 2011-2012 GRNET S.A. All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or
 // without modification, are permitted provided that the following
 
 #import "PithosAccountNode.h"
 #import "PithosContainerNode.h"
+#import "ASIPithos.h"
 #import "ASIPithosAccountRequest.h"
 #import "ASIPithosAccount.h"
 #import "ASIPithosContainer.h"
 #import "ASIDownloadCache.h"
 #import "PithosUtilities.h"
+#import "PithosActivityFacility.h"
 
 static NSImage *sharedIcon = nil;
 
 @implementation PithosAccountNode
-@synthesize pithosAccount;
+@synthesize pithos, pithosAccount;
 
 + (void)initialize {
        if (self == [PithosAccountNode class])
@@ -56,6 +58,13 @@ static NSImage *sharedIcon = nil;
 #pragma mark -
 #pragma mark Object Lifecycle
 
+- (id)initWithPithos:(ASIPithos *)aPithos {
+    if ((self = [super init])) {
+        pithos = [aPithos retain];
+    }
+    return self;
+}
+
 - (void)dealloc {
     [accountRequest clearDelegatesAndCancel];
     [accountRequest release];
@@ -65,38 +74,85 @@ static NSImage *sharedIcon = nil;
     [applyMetadataAccountRequest release];
     [containers release];
     [pithosAccount release];
+    [pithos release];
     [super dealloc];
 }
 
 #pragma mark -
 #pragma mark Properties
 
+- (void)setPithos:(ASIPithos *)aPithos {
+    if (aPithos && ![aPithos isEqualTo:pithos]) {
+        [pithos release];
+        pithos = [aPithos retain];
+        [url release];
+        url = nil;
+        [accountRequest clearDelegatesAndCancel];
+        [accountRequest release];
+        accountRequest = nil;
+        [refreshMetadataAccountRequest clearDelegatesAndCancel];
+        [refreshMetadataAccountRequest release];
+        refreshMetadataAccountRequest = nil;
+        [applyMetadataAccountRequest clearDelegatesAndCancel];
+        [applyMetadataAccountRequest release];
+        applyMetadataAccountRequest = nil;
+        reset = YES;
+    }
+}
+
 - (NSString *)url {
     if (url == nil)
         url = [[NSString alloc] initWithFormat:@"%@%@", 
-               (sharingAccount ? [ASIPithosRequest storageURLWithAuthUser:sharingAccount] : [ASIPithosRequest storageURL]), 
+               (sharingAccount ? [pithos storageURLWithAuthUser:sharingAccount] : pithos.storageURL), 
                (shared ? @"?shared" : @"")];
     return url;
 }
 
 - (NSArray *)children {
     @synchronized(self) {
+        if (reset) {
+            [accountRequest clearDelegatesAndCancel];
+            [accountRequest release];
+            accountRequest = nil;
+            [refreshMetadataAccountRequest clearDelegatesAndCancel];
+            [refreshMetadataAccountRequest release];
+            refreshMetadataAccountRequest = nil;
+            [applyMetadataAccountRequest clearDelegatesAndCancel];
+            [applyMetadataAccountRequest release];
+            applyMetadataAccountRequest = nil;
+            [children release];
+            children = nil;
+            [newChildren release];
+            newChildren = nil;
+            self.pithosAccount = nil;
+            freshness = PithosNodeStateRefreshNeeded;
+            forcedRefresh = YES;
+            reset = NO;
+            [self postChildrenUpdatedNotificationName];
+        }
         switch (freshness) {
             case PithosNodeStateFresh:
                 break;
             case PithosNodeStateRefreshNeeded:
                 freshness = PithosNodeStateRefreshing;
-                accountRequest = [[ASIPithosAccountRequest listContainersRequestWithLimit:0 
-                                                                                   marker:nil 
-                                                                                   shared:shared 
-                                                                                    until:nil] retain];
+                accountRequest = [[ASIPithosAccountRequest listContainersRequestWithPithos:pithos 
+                                                                                     limit:0 
+                                                                                    marker:nil 
+                                                                                    shared:shared 
+                                                                                     until:nil] retain];
                 if (sharingAccount)
-                    [accountRequest setRequestUserFromDefaultTo:sharingAccount];
-                accountRequest.delegate = self;
-                accountRequest.didFinishSelector = @selector(accountRequestFinished:);
-                accountRequest.didFailSelector = @selector(accountRequestFailed:);
-                if (!forcedRefresh)
+                    [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];
                 break;
             case PithosNodeStateRefreshing:
@@ -130,119 +186,188 @@ static NSImage *sharedIcon = nil;
 #pragma mark -
 #pragma mark ASIHTTPRequestDelegate
 
-- (void)accountRequestFinished:(ASIPithosAccountRequest *)request {
-    NSLog(@"URL: %@", [accountRequest url]);
-    NSLog(@"cached: %d", [accountRequest didUseCachedResponse]);
-
-    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
-            NSLog(@"using newChildren");
-            newChildren = [[NSMutableArray alloc] init];
-            NSMutableIndexSet *keptNodes = [NSMutableIndexSet indexSet];
-            for (ASIPithosContainer *container in containers) {
-                PithosContainerNode *node = [[[PithosContainerNode alloc] initWithPithosContainer:container] autorelease];
-                node.parent = self;
-                node.shared = shared;
-                node.sharingAccount = sharingAccount;
-                if (children) {
-                    NSUInteger oldIndex = [children indexOfObject:node];
-                    if (oldIndex != NSNotFound) {
-                        // Use the same pointer value, if possible
-                        node = [children objectAtIndex:oldIndex];
-                        node.pithosContainer = container;
-                        [keptNodes addIndex:oldIndex];
-                    }
-                }
-                [newChildren addObject:node];
+- (void)accountRequestFailed:(ASIPithosAccountRequest *)request {
+    @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;
             }
-            [[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;
-        }
-        // Notify observers that children are updated
-        [[NSNotificationCenter defaultCenter] postNotificationName:@"PithosAccountNodeChildrenUpdated" object:self];
-    } else {
-        [accountRequest release];
-        // Do an additional request to fetch more objects
-        accountRequest = [[ASIPithosAccountRequest listContainersRequestWithLimit:0 
-                                                                           marker:[[someContainers lastObject] name] 
-                                                                           shared:shared 
-                                                                            until:nil] retain];
-        if (sharingAccount)
-            [accountRequest setRequestUserFromDefaultTo:sharingAccount];
-        accountRequest.delegate = self;
-        if (!forcedRefresh)
-            accountRequest.downloadCache = [ASIDownloadCache sharedCache];
-        [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
     }
 }
 
-- (void)accountRequestFailed:(ASIPithosAccountRequest *)request {
-    [PithosUtilities httpRequestErrorAlertWithRequest:request];
-    [newChildren release];
-    newChildren = nil;
-    [accountRequest release];
-    accountRequest = nil;
-    [containers release];
-    containers = nil;
-    forcedRefresh = NO;
-    @synchronized(self) {
-        freshness = PithosNodeStateRefreshNeeded;
+- (void)accountRequestFinished:(ASIPithosAccountRequest *)request {
+    @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];
+                    }
+                    [[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;
+                }
+                [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 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];
+        }
     }
 }
 
 - (void)accountMetadataRequestFinished:(ASIPithosAccountRequest *)request {
-    NSLog(@"URL: %@", [request url]);
-    NSLog(@"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;
+    @autoreleasepool {
+        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;
+            }
         }
     }
 }
 
 - (void)accountMetadataRequestFailed:(ASIPithosAccountRequest *)request {
-    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;
+    @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;
+                }
+            }
+            [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
+            [newRequest release];
+        } 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;
+                }
+            }
         }
     }
 }
@@ -256,12 +381,19 @@ static NSImage *sharedIcon = nil;
             NSMutableDictionary *groups = pithosAccount.groups;
             if ([groups count] == 0)
                 groups = [NSMutableDictionary dictionaryWithObject:@"" forKey:@"group"];
-            applyMetadataAccountRequest = [[ASIPithosAccountRequest updateAccountMetadataRequestWithGroups:groups 
+            applyMetadataAccountRequest = [[ASIPithosAccountRequest updateAccountMetadataRequestWithPithos:pithos 
+                                                                                                    groups:groups 
                                                                                                   metadata:pithosAccount.metadata 
                                                                                                     update:NO] retain];
             applyMetadataAccountRequest.delegate = self;
-            applyMetadataAccountRequest.didFinishSelector = @selector(accountMetadataRequestFinished:);
-            applyMetadataAccountRequest.didFailSelector = @selector(accountMetadataRequestFailed:);
+            applyMetadataAccountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+            applyMetadataAccountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+            applyMetadataAccountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                                                    [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
+                                                    [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                                                    NSStringFromSelector(@selector(accountMetadataRequestFinished:)), @"didFinishSelector", 
+                                                    NSStringFromSelector(@selector(accountMetadataRequestFailed:)), @"didFailSelector", 
+                                                    nil];
             [[PithosUtilities prepareRequest:applyMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
         }
     }
@@ -270,11 +402,18 @@ static NSImage *sharedIcon = nil;
 - (void)refreshInfo {
     @synchronized(self) {
         if (refreshMetadataAccountRequest == nil) {
-            refreshMetadataAccountRequest = [[ASIPithosAccountRequest accountMetadataRequest] retain];
+            refreshMetadataAccountRequest = [[ASIPithosAccountRequest accountMetadataRequestWithPithos:pithos] retain];
             refreshMetadataAccountRequest.delegate = self;
-            refreshMetadataAccountRequest.didFinishSelector = @selector(accountMetadataRequestFinished:);
-            refreshMetadataAccountRequest.didFailSelector = @selector(accountMetadataRequestFailed:);
-            refreshMetadataAccountRequest.downloadCache = [ASIDownloadCache sharedCache];
+            refreshMetadataAccountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+            refreshMetadataAccountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+            refreshMetadataAccountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                                                      [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
+                                                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                                                      NSStringFromSelector(@selector(accountMetadataRequestFinished:)), @"didFinishSelector", 
+                                                      NSStringFromSelector(@selector(accountMetadataRequestFailed:)), @"didFailSelector", 
+                                                      nil];
+            if (!sharingAccount)
+                refreshMetadataAccountRequest.downloadCache = [ASIDownloadCache sharedCache];
             [[PithosUtilities prepareRequest:refreshMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
         }
     }