Improve code and fix bugs
[pithos-macos] / pithos-macos / PithosAccountNode.m
index 64a4930..42a210b 100644 (file)
 #import "ASIPithosAccount.h"
 #import "ASIPithosContainer.h"
 #import "ASIDownloadCache.h"
+#import "PithosAccount.h"
 #import "PithosUtilities.h"
 #import "PithosActivityFacility.h"
 
 static NSImage *sharedIcon = nil;
 
 @implementation PithosAccountNode
-@synthesize pithos, pithosAccount;
+@synthesize pithos, pithosAccount, translatedGroups;
 
 + (void)initialize {
        if (self == [PithosAccountNode class])
-        sharedIcon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kUserIcon)] retain];
+        sharedIcon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kUserIcon)];
 }
 
 #pragma mark -
@@ -60,22 +61,46 @@ static NSImage *sharedIcon = nil;
 
 - (id)initWithPithos:(ASIPithos *)aPithos {
     if ((self = [super init])) {
-        self.pithos = aPithos;
+        pithos = aPithos;
     }
     return self;
 }
 
 - (void)dealloc {
     [accountRequest clearDelegatesAndCancel];
-    [accountRequest release];
     [refreshMetadataAccountRequest clearDelegatesAndCancel];
-    [refreshMetadataAccountRequest release];
     [applyMetadataAccountRequest clearDelegatesAndCancel];
-    [applyMetadataAccountRequest release];
-    [containers release];
-    [pithosAccount release];
-    [pithos release];
-    [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Internal
+
+- (void)updateGroups {
+    if (!pithosAccount) {
+        self.translatedGroups = [NSMutableDictionary dictionary];
+    } else if (pithosAccountManager) {
+        NSMutableSet *UUIDs = [NSMutableSet set];
+        for (NSString *groupName in pithosAccount.groups) {
+            [UUIDs addObjectsFromArray:[pithosAccount.groups objectForKey:groupName]];
+        }
+        [UUIDs removeObject:@""];
+        [UUIDs removeObject:@"*"];
+        if (UUIDs.count) {
+            [pithosAccountManager updateUserCatalogForForDisplaynames:nil UUIDs:[UUIDs allObjects]];
+        }
+        
+        NSMutableDictionary *newTranslatedGroups = [NSMutableDictionary dictionaryWithCapacity:pithosAccount.groups.count];
+        for (NSString *groupName in pithosAccount.groups) {
+            NSMutableArray *groupUsers = [NSMutableArray array];
+            for (NSString *UUID in [pithosAccount.groups objectForKey:groupName]) {
+                [groupUsers addObject:[pithosAccountManager displaynameForUUID:UUID safe:YES]];
+            }
+            [newTranslatedGroups setObject:groupUsers forKey:groupName];
+        }
+        self.translatedGroups = newTranslatedGroups;
+    } else {
+        self.translatedGroups = [pithosAccount.groups copy];
+    }
 }
 
 #pragma mark -
@@ -83,10 +108,22 @@ static NSImage *sharedIcon = nil;
 
 - (void)setPithos:(ASIPithos *)aPithos {
     if (aPithos && ![aPithos isEqualTo:pithos]) {
-        [pithos release];
-        pithos = [aPithos retain];
-        [url release];
+        pithos = aPithos;
         url = nil;
+        [accountRequest clearDelegatesAndCancel];
+        accountRequest = nil;
+        [refreshMetadataAccountRequest clearDelegatesAndCancel];
+        refreshMetadataAccountRequest = nil;
+        [applyMetadataAccountRequest clearDelegatesAndCancel];
+        applyMetadataAccountRequest = nil;
+        reset = YES;
+    }
+}
+
+- (void)setPithosAccount:(ASIPithosAccount *)aPithosAccount {
+    if (![pithosAccount isEqualTo:aPithosAccount]) {
+        pithosAccount = aPithosAccount;
+        [self updateGroups];
     }
 }
 
@@ -100,18 +137,35 @@ static NSImage *sharedIcon = nil;
 
 - (NSArray *)children {
     @synchronized(self) {
+        if (reset) {
+            [accountRequest clearDelegatesAndCancel];
+            accountRequest = nil;
+            [refreshMetadataAccountRequest clearDelegatesAndCancel];
+            refreshMetadataAccountRequest = nil;
+            [applyMetadataAccountRequest clearDelegatesAndCancel];
+            applyMetadataAccountRequest = nil;
+            children = nil;
+            newChildren = nil;
+            self.pithosAccount = nil;
+            freshness = PithosNodeStateRefreshNeeded;
+            forcedRefresh = YES;
+            reset = NO;
+            [self postChildrenUpdatedNotificationName];
+        }
         switch (freshness) {
             case PithosNodeStateFresh:
                 break;
             case PithosNodeStateRefreshNeeded:
                 freshness = PithosNodeStateRefreshing;
-                accountRequest = [[ASIPithosAccountRequest listContainersRequestWithPithos:pithos 
+                accountRequest = [ASIPithosAccountRequest listContainersRequestWithPithos:pithos 
                                                                                      limit:0 
                                                                                     marker:nil 
                                                                                     shared:shared 
-                                                                                     until:nil] retain];
+                                                                                     until:nil];
                 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:);
@@ -121,15 +175,12 @@ static NSImage *sharedIcon = nil;
                                            NSStringFromSelector(@selector(accountRequestFinished:)), @"didFinishSelector", 
                                            NSStringFromSelector(@selector(accountRequestFailed:)), @"didFailSelector", 
                                            nil];
-                if (!forcedRefresh)
-                    accountRequest.downloadCache = [ASIDownloadCache sharedCache];
                 [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
                 break;
             case PithosNodeStateRefreshing:
                 break;
             case PithosNodeStateRefreshFinished:
                 if (newChildren) {
-                    [children release];
                     children = newChildren;
                     newChildren = nil;
                 }
@@ -142,14 +193,21 @@ static NSImage *sharedIcon = nil;
 }
 
 - (NSString *)displayName {
-    if (displayName == nil)
-        return [NSString stringWithString:(sharingAccount ? sharingAccount : @"account")];
-    return [[displayName copy] autorelease];
+    if (displayName == nil) {
+        if (!sharingAccount) {
+            return @"account";
+        } else if (pithosAccountManager) {
+            return [pithosAccountManager displaynameForUUID:sharingAccount safe:YES];
+        } else {
+            return [sharingAccount copy];
+        }
+    }
+    return [displayName copy];
 }
 
 - (NSImage *)icon {
     if (icon == nil)
-        icon = [sharedIcon retain];
+        icon = sharedIcon;
     return icon;
 }
 
@@ -157,188 +215,173 @@ static NSImage *sharedIcon = nil;
 #pragma mark ASIHTTPRequestDelegate
 
 - (void)accountRequestFailed:(ASIPithosAccountRequest *)request {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-    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 {
+    @autoreleasepool {
         NSString *message;
         NSError *error = [accountRequest error];
         if (error)
-            message = [NSString stringWithFormat:@"Account listing failed: %@", error];
+            message = [NSString stringWithFormat:@"Account listing %@ failed: %@", accountRequest.url, [error localizedDescription]];
         else
-            message = [NSString stringWithFormat:@"Account listing failed: (%d) %@", 
-                       accountRequest.responseStatusCode, accountRequest.responseStatusMessage];
+            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];
         });
-        [newChildren release];
-        newChildren = nil;
-        [accountRequest release];
-        accountRequest = nil;
-        [containers release];
-        containers = nil;
-        forcedRefresh = NO;
-        @synchronized(self) {
-            freshness = PithosNodeStateRefreshNeeded;
+        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 = newAccountRequest;
+            [[PithosUtilities prepareRequest:accountRequest priority:[[accountRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
+        } else {
+            newChildren = nil;
+            accountRequest = nil;
+            forcedRefresh = NO;
+            @synchronized(self) {
+                freshness = PithosNodeStateRefreshNeeded;
+            }
         }
     }
-    [pool drain];
 }
 
 - (void)accountRequestFinished:(ASIPithosAccountRequest *)request {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-    NSLog(@"List account finished: %@", [accountRequest url]);
-    NSLog(@"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
-                NSLog(@"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];
+            
+            NSMutableArray *containers = [accountRequest.userInfo objectForKey:@"containers"];
+            NSArray *someContainers = [accountRequest containers];
+            if (containers == nil) {
+                containers = [NSMutableArray arrayWithArray: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];
+                        node.parent = self;
+                        node.shared = shared;
+                        node.sharingAccount = sharingAccount;
+                        node.inheritChildrenUpdatedNotificationName = inheritChildrenUpdatedNotificationName;
+                        node.pithosAccountManager = pithosAccountManager;
+                        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)];
                 }
-                [[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 = nil;
+                forcedRefresh = NO;
+                @synchronized(self) {
+                    freshness = PithosNodeStateRefreshFinished;
+                }
+                [self postChildrenUpdatedNotificationName];
+            } else {
+                // Do an additional request to fetch more objects
+                accountRequest = [ASIPithosAccountRequest listContainersRequestWithPithos:pithos 
+                                                                                     limit:0 
+                                                                                    marker:[[someContainers lastObject] name] 
+                                                                                    shared:shared 
+                                                                                     until:nil];
+                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",
+                                           containers, @"containers",
+                                           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
-            [accountRequest release];
+        } else if (accountRequest.responseStatusCode == 304) {
+            // Account is not modified, so existing children can be reused
             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];
-            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];
-            if (!forcedRefresh)
-                accountRequest.downloadCache = [ASIDownloadCache sharedCache];
-            [[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 accountRequestFailed:accountRequest];
         }
-        [self postChildrenUpdatedNotificationName];
-    } else {
-        [self accountRequestFailed:accountRequest];
     }
-    [pool drain];
 }
 
 - (void)accountMetadataRequestFinished:(ASIPithosAccountRequest *)request {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-    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;
-        }
-    }
-    [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 = newRequest;
+                }
+            } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
+                @synchronized(self) {
+                    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 = nil;
+                }
+            } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
+                [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataAccountRequest];
+                @synchronized(self) {
+                    refreshMetadataAccountRequest = nil;
+                }
             }
         }
     }
-    [pool drain];
 }
 
 #pragma mark -
@@ -347,13 +390,104 @@ static NSImage *sharedIcon = nil;
 - (void)applyInfo {
     @synchronized(self) {
         if (applyMetadataAccountRequest == nil) {
-            NSMutableDictionary *groups = pithosAccount.groups;
-            if ([groups count] == 0)
-                groups = [NSMutableDictionary dictionaryWithObject:@"" forKey:@"group"];
-            applyMetadataAccountRequest = [[ASIPithosAccountRequest updateAccountMetadataRequestWithPithos:pithos 
+            NSMutableDictionary *groups = [NSMutableDictionary dictionary];
+            if (translatedGroups.count) {
+                for (NSString *groupName in translatedGroups) {
+                    if (!groupName.length ||
+                        [groupName rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@" -_~,;"]].location != NSNotFound) {
+                        NSAlert *alert = [[NSAlert alloc] init];
+                        [alert setMessageText:@"Invalid Input"];
+                        [alert setInformativeText:@"Group names cannot be empty or contain ' ', '-', '_', '~', ',' or ';'."];
+                        [alert addButtonWithTitle:@"OK"];
+                        [alert runModal];
+                        return;
+                    }
+                }
+                if (pithosAccountManager) {
+                    NSMutableSet *allGroupUsers = [NSMutableSet set];
+                    for (NSMutableArray *groupUsers in [translatedGroups objectEnumerator]) {
+                        [allGroupUsers addObjectsFromArray:groupUsers];
+                    }
+                    [allGroupUsers removeObject:@""];
+                    [allGroupUsers removeObject:@"*"];
+                    if (allGroupUsers.count) {
+                        ASIPithosRequest *userCatalogRequest = [pithosAccountManager updateUserCatalogForForDisplaynames:[allGroupUsers allObjects]
+                                                                                                                        UUIDs:nil];
+                        if (userCatalogRequest.error || ((userCatalogRequest.responseStatusCode != 200) && (userCatalogRequest.responseStatusCode != 404))) {
+                            return;
+                        } else if (userCatalogRequest.responseStatusCode == 200) {
+                            // Check if all users exist.
+                            NSDictionary *displaynameCatalog = [userCatalogRequest displaynameCatalog];
+                            NSMutableArray *inexistentGroupUsers = [NSMutableArray array];
+                            for (NSString *groupUser in allGroupUsers) {
+                                if (![displaynameCatalog objectForKey:groupUser]) {
+                                    [inexistentGroupUsers addObject:groupUser];
+                                }
+                            }
+                            if (!inexistentGroupUsers.count) {
+                                // Create groups.
+                                for (NSString *groupName in translatedGroups) {
+                                    NSMutableArray *groupUsers = [NSMutableArray array];
+                                    for (NSString *groupUser in [translatedGroups objectForKey:groupName]) {
+                                        [groupUsers addObject:([groupUser isEqualToString:@"*"] ?
+                                                               @"*" : [displaynameCatalog objectForKey:groupUser])];
+                                    }
+                                    [groups setObject:groupUsers forKey:groupName];
+                                }
+                            } else {
+                                NSAlert *alert = [[NSAlert alloc] init];
+                                if (inexistentGroupUsers.count == 1) {
+                                    [alert setMessageText:@"Invalid User"];
+                                    [alert setInformativeText:[NSString stringWithFormat:@"User '%@' doesn't exist.", [inexistentGroupUsers objectAtIndex:0]]];
+                                } else {
+                                    [alert setMessageText:@"Invalid Users"];
+                                    [alert setInformativeText:[NSString stringWithFormat:@"Users '%@' don't exist.", [inexistentGroupUsers componentsJoinedByString:@"', '"]]];
+                                }
+                                [alert addButtonWithTitle:@"OK"];
+                                [alert runModal];
+                                return;
+                            }
+                        } else {
+                            // 404. Since we don't translate to UUIDs, check for invalid chars.
+                            BOOL valid = YES;
+                            for (NSString *groupUser in allGroupUsers) {
+                                if ([groupUser rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@" ~,;:"]].location != NSNotFound) {
+                                    valid = NO;
+                                    break;
+                                }
+                            }
+                            if (valid) {
+                                [groups addEntriesFromDictionary:translatedGroups];
+                            } else {
+                                NSAlert *alert = [[NSAlert alloc] init];
+                                [alert setMessageText:@"Invalid Input"];
+                                [alert setInformativeText:@"Users cannot contain ' ', '~', ',', ';' or ':'."];
+                                [alert addButtonWithTitle:@"OK"];
+                                [alert runModal];
+                                return;
+                            }
+                        }
+                    } else {
+                        for (NSString *groupName in translatedGroups) {
+                            if ([[translatedGroups objectForKey:groupName] containsObject:@"*"]) {
+                                [groups setObject:[NSMutableArray arrayWithObject:@"*"] forKey:groupName];
+                            }
+                        }
+                        if (!groups.count) {
+                            [groups setObject:[NSArray arrayWithObject:@""] forKey:@"group"];
+                        }
+                    }
+                } else {
+                    [groups addEntriesFromDictionary:translatedGroups];
+                }
+            } else {
+                [groups setObject:[NSArray arrayWithObject:@""] forKey:@"group"];
+            }
+            
+            applyMetadataAccountRequest = [ASIPithosAccountRequest updateAccountMetadataRequestWithPithos:pithos
                                                                                                     groups:groups 
                                                                                                   metadata:pithosAccount.metadata 
-                                                                                                    update:NO] retain];
+                                                                                                    update:NO];
             applyMetadataAccountRequest.delegate = self;
             applyMetadataAccountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
             applyMetadataAccountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -371,7 +505,7 @@ static NSImage *sharedIcon = nil;
 - (void)refreshInfo {
     @synchronized(self) {
         if (refreshMetadataAccountRequest == nil) {
-            refreshMetadataAccountRequest = [[ASIPithosAccountRequest accountMetadataRequestWithPithos:pithos] retain];
+            refreshMetadataAccountRequest = [ASIPithosAccountRequest accountMetadataRequestWithPithos:pithos];
             refreshMetadataAccountRequest.delegate = self;
             refreshMetadataAccountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
             refreshMetadataAccountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -381,7 +515,8 @@ static NSImage *sharedIcon = nil;
                                                       NSStringFromSelector(@selector(accountMetadataRequestFinished:)), @"didFinishSelector", 
                                                       NSStringFromSelector(@selector(accountMetadataRequestFailed:)), @"didFailSelector", 
                                                       nil];
-            refreshMetadataAccountRequest.downloadCache = [ASIDownloadCache sharedCache];
+            if (!sharingAccount)
+                refreshMetadataAccountRequest.downloadCache = [ASIDownloadCache sharedCache];
             [[PithosUtilities prepareRequest:refreshMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
         }
     }