Sync with "shared to me" selected accounts.
authorMiltiadis Vasilakis <mvasilak@gmail.com>
Thu, 5 Apr 2012 16:14:25 +0000 (19:14 +0300)
committerMiltiadis Vasilakis <mvasilak@gmail.com>
Thu, 5 Apr 2012 17:14:09 +0000 (20:14 +0300)
pithos-macos/PithosSyncDaemon.h
pithos-macos/PithosSyncDaemon.m
pithos-macos/PithosUtilities.h
pithos-macos/PithosUtilities.m

index 04f5f71..49a2a2d 100644 (file)
     PithosAccount *pithosAccount;
     NSDictionary *accountsDictionary;
     ASIPithos *pithos;
-    
-    NSDictionary *containersDictionary;
-    NSUInteger containersCount;
-    NSMutableArray *pithosContainers;
+
+    NSUInteger accountsCount;
+    NSMutableArray *accountsNames;
+    NSUInteger accountsIndex;
+    NSMutableDictionary *accountsPithosContainers;
     NSUInteger containersIndex;
     
     NSMutableArray *objects;
@@ -80,8 +81,8 @@
 @property (nonatomic, retain) NSDictionary *accountsDictionary;
 @property (nonatomic, retain) ASIPithos *pithos;
 
-@property (nonatomic, retain) NSDictionary *containersDictionary;
-@property (nonatomic, retain) NSMutableArray *pithosContainers;
+@property (nonatomic, retain) NSMutableArray *accountsNames;
+@property (nonatomic, retain) NSMutableDictionary *accountsPithosContainers;
 
 @property (nonatomic, retain) NSMutableDictionary *remoteObjects;
 @property (nonatomic, retain) NSMutableDictionary *previousRemoteObjects;
index 073d61c..a61b81d 100644 (file)
 - (void)resetLocalStateWithAll:(BOOL)all;
 - (void)saveLocalState;
 
-- (BOOL)moveToTempTrashFile:(NSString *)filePath pithosContainer:(ASIPithosContainer *)pithosContainer;
+- (BOOL)createSyncDirectory:(NSString *)dirPath;
+- (NSString *)dirPathForAccount:(NSString *)accountName container:(NSString *)containerName;
+- (NSString *)relativeDirPathForAccount:(NSString *)accountName container:(NSString *)containerName;
+
+- (BOOL)moveToTempTrashFile:(NSString *)filePath 
+                accountName:(NSString *)accountName 
+            pithosContainer:(ASIPithosContainer *)pithosContainer;
 - (void)emptyTempTrash;
 - (BOOL)findLocalCopyForObjectWithHash:(NSString *)hash forFile:(NSString *)filePath;
 
 - (void)updateLocalStateWithObject:(ASIPithosObject *)object 
                      localFilePath:(NSString *)filePath 
+                       accountName:(NSString *)accountName 
                    pithosContainer:(ASIPithosContainer *)pithosContainer;
 - (void)updateServerStateWithCurrentState:(PithosLocalObjectState *)currentState 
                                    object:(ASIPithosObject *)object 
                             localFilePath:(NSString *)filePath 
+                              accountName:(NSString *)accountName 
                           pithosContainer:(ASIPithosContainer *)pithosContainer;
 - (void)listRequestFailed:(ASIPithosContainerRequest *)containerRequest;
 - (void)requestFailed:(ASIPithosRequest *)request;
@@ -74,7 +82,7 @@
 
 @implementation PithosSyncDaemon
 @synthesize directoryPath, accountsDictionary, pithos;
-@synthesize containersDictionary, pithosContainers;
+@synthesize accountsNames, accountsPithosContainers;
 @synthesize lastCompletedSync, remoteObjects, previousRemoteObjects, storedLocalObjectStates, currentLocalObjectStates;
 @synthesize pithosStateFilePath, tempDownloadsDirPath, tempTrashDirPath;
 
     if ((self = [super init])) {
         directoryPath = [aDirectoryPath copy];
         pithosAccount = [aPithosAccount retain];
-        accountsDictionary = [anAccountsDictionary copy];
-        self.pithos = pithosAccount.pithos;
-        
-        self.containersDictionary = [accountsDictionary objectForKey:@""];
-        if (!containersDictionary)
-            self.containersDictionary = [NSDictionary dictionary];
-        containersCount = [containersDictionary count];
-        self.pithosContainers = [NSMutableArray arrayWithCapacity:containersCount];
-        for (NSString *containerName in containersDictionary) {
-            ASIPithosContainer *pithosContainer = [ASIPithosContainer container];
-            pithosContainer.name = containerName;
-            [pithosContainers addObject:pithosContainer];
-        }
+        self.accountsDictionary = anAccountsDictionary;
+        self.pithos = pithosAccount.pithos;        
         
         activityFacility = [PithosActivityFacility defaultPithosActivityFacility];
         
         self.storedLocalObjectStates = [NSMutableDictionary dictionary];
     if (!storedLocalObjectStates)
         self.storedLocalObjectStates = [NSMutableDictionary dictionary];
-    for (ASIPithosContainer *pithosContainer in pithosContainers) {
-        if (![storedLocalObjectStates objectForKey:pithosContainer.name]) {
-            [storedLocalObjectStates setObject:[NSMutableDictionary dictionary] forKey:pithosContainer.name];
+    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;
-    NSFileManager *fileManager = [NSFileManager defaultManager];
-    NSError *error;
     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) {
-            error = nil;
-            for (NSString *subPath in [fileManager contentsOfDirectoryAtPath:self.tempDownloadsDirPath error:&error]) {
-                if (error) {
-                    [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" 
-                                                            message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", self.tempDownloadsDirPath] 
-                                                              error:error];
-                    break;
-                }
-                NSString *subFilePath = [self.tempDownloadsDirPath stringByAppendingPathComponent:subPath];
-                if (![fileManager removeItemAtPath:subFilePath error:&error] || error) {
-                    [PithosUtilities fileActionFailedAlertWithTitle:@"Remove File Error" 
-                                                            message:[NSString stringWithFormat:@"Cannot remove file at '%@'", subFilePath] 
-                                                              error:error];
-                }
-                error = nil;
-            }
-        }
+        if (self.tempDownloadsDirPath)
+            [PithosUtilities removeContentsAtPath:self.tempDownloadsDirPath];
     } else {
         // Remove containers that don't interest us anymore and save
         if (!storedLocalObjectStates)
             [self loadLocalState];
-        for (NSString *containerName in storedLocalObjectStates) {
-            if (![containersDictionary objectForKey:containerName]) {
-                [storedLocalObjectStates removeObjectForKey:containerName];
+        for (NSString *accountName in storedLocalObjectStates) {
+            NSMutableDictionary *accountStoredLocalObjectStates = [storedLocalObjectStates objectForKey:accountName];
+            NSDictionary *containersDictionary = [accountsDictionary objectForKey:accountName];
+            if (!containersDictionary) {
                 if (self.tempDownloadsDirPath) {
-                    NSString *containerTempDownloadsDirPath = [self.tempDownloadsDirPath stringByAppendingPathComponent:containerName];
-                    BOOL isDirectory;
-                    BOOL fileExists = [fileManager fileExistsAtPath:containerTempDownloadsDirPath isDirectory:&isDirectory];
-                    if (fileExists && isDirectory) {
-                        error = nil;
-                        for (NSString *subPath in [fileManager contentsOfDirectoryAtPath:containerTempDownloadsDirPath error:&error]) {
-                            if (error) {
-                                [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" 
-                                                                        message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", 
-                                                                                 containerTempDownloadsDirPath] 
-                                                                          error:error];
-                                break;
-                            }
-                            NSString *subFilePath = [containerTempDownloadsDirPath stringByAppendingPathComponent:subPath];
-                            if (![fileManager removeItemAtPath:subFilePath error:&error] || error) {
-                                [PithosUtilities fileActionFailedAlertWithTitle:@"Remove File Error" 
-                                                                        message:[NSString stringWithFormat:@"Cannot remove file at '%@'", 
-                                                                                 subFilePath] 
-                                                                          error:error];
-                            }
-                            error = nil;
-                        }
-                    } else if (fileExists && !isDirectory) {
-                        error = nil;
-                        if (![fileManager removeItemAtPath:containerTempDownloadsDirPath error:&error] || error) {
-                            [PithosUtilities fileActionFailedAlertWithTitle:@"Remove File Error" 
-                                                                    message:[NSString stringWithFormat:@"Cannot remove file at '%@'", 
-                                                                             containerTempDownloadsDirPath] 
-                                                                      error:error];
+                    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]];
+                    }
+                }
+                [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];
                     }
                 }
             }
     [remoteObjects release];
     [objects release];
     [lastCompletedSync release];
-    [pithosContainers release];
-    [containersDictionary release];
+    [accountsPithosContainers release];
+    [accountsNames release];
     [pithos release];
     [accountsDictionary release];
     [pithosAccount release];
 
 - (void)setAccountsDictionary:(NSDictionary *)anAccountsDictionary {
     if (anAccountsDictionary && ![anAccountsDictionary isEqualToDictionary:accountsDictionary]) {
-        [self resetDaemon];
+        BOOL reset = (accountsDictionary != nil);
+        if (reset)
+            [self resetDaemon];
+        
         [accountsDictionary release];
         accountsDictionary = [anAccountsDictionary copy];
         
-        self.containersDictionary = [accountsDictionary objectForKey:@""];
-        if (!containersDictionary)
-            self.containersDictionary = [NSDictionary dictionary];
-        containersCount = [containersDictionary count];
-        self.pithosContainers = [NSMutableArray arrayWithCapacity:containersCount];
-        for (NSString *containerName in containersDictionary) {
-            ASIPithosContainer *pithosContainer = [ASIPithosContainer container];
-            pithosContainer.name = containerName;
-            [pithosContainers addObject:pithosContainer];
+        accountsCount = [accountsDictionary count];
+        self.accountsNames = [NSMutableArray arrayWithCapacity:accountsCount];
+        self.accountsPithosContainers = [NSMutableDictionary dictionaryWithCapacity:accountsCount];
+        NSDictionary *containersDictionary = [accountsDictionary objectForKey:@""];
+        if (containersDictionary && [containersDictionary count]) {
+            NSMutableArray *pithosContainers = [NSMutableArray arrayWithCapacity:[containersDictionary count]];
+            for (NSString *containerName in containersDictionary) {
+                ASIPithosContainer *pithosContainer = [ASIPithosContainer container];
+                pithosContainer.name = containerName;
+                [pithosContainers addObject:pithosContainer];
+            }
+            [accountsNames addObject:@""];
+            [accountsPithosContainers setObject:pithosContainers forKey:@""];
+        }
+        for (NSString *accountName in accountsDictionary) {
+            NSDictionary *containersDictionary = [accountsDictionary objectForKey:accountName];
+            if (![accountsNames containsObject:accountName] && [containersDictionary count]) {
+                NSMutableArray *pithosContainers = [NSMutableArray arrayWithCapacity:[containersDictionary count]];
+                for (NSString *containerName in containersDictionary) {
+                    ASIPithosContainer *pithosContainer = [ASIPithosContainer container];
+                    pithosContainer.name = containerName;
+                    [pithosContainers addObject:pithosContainer];
+                }
+                [accountsNames addObject:accountName];
+                [accountsPithosContainers setObject:pithosContainers forKey:accountName];
+            }
         }
-        [self resetLocalStateWithAll:NO];
+
+        if (reset)
+            [self resetLocalStateWithAll:NO];
     }
 }
 
 }
 
 #pragma mark -
+#pragma mark Helper Methods
+
+- (BOOL)createSyncDirectory:(NSString *)dirPath {
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+    BOOL isDirectory;
+    NSError *error = nil;
+    if (![fileManager fileExistsAtPath:dirPath isDirectory:&isDirectory]) {
+        if (![fileManager createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:nil error:&error] || error) {
+            [PithosUtilities fileActionFailedAlertWithTitle:@"Local Sync Directory Error" 
+                                                    message:[NSString stringWithFormat:@"Cannot create local sync directory at '%@'", 
+                                                             dirPath] 
+                                                      error:error];
+            return NO;
+        }
+    } else if (!isDirectory) {
+        [PithosUtilities fileActionFailedAlertWithTitle:@"Local Sync Directory Error" 
+                                                message:[NSString stringWithFormat:@"File already exists at the local sync directory path at '%@'", 
+                                                         dirPath] 
+                                                  error:nil];
+        return NO;
+    }
+    return YES;
+}
+
+- (NSString *)dirPathForAccount:(NSString *)accountName container:(NSString *)containerName {
+    if ([accountName isEqualToString:@""])
+        return [directoryPath stringByAppendingPathComponent:containerName];
+    else
+        return [[[directoryPath stringByAppendingPathComponent:@"shared to me"] 
+                 stringByAppendingPathComponent:accountName] 
+                stringByAppendingPathComponent:containerName];
+}
+
+- (NSString *)relativeDirPathForAccount:(NSString *)accountName container:(NSString *)containerName {
+    if ([accountName isEqualToString:@""])
+        return containerName;
+    else
+        return [[[NSString stringWithString:@"shared to me"] 
+                 stringByAppendingPathComponent:accountName] 
+                stringByAppendingPathComponent:containerName];    
+}
+
+#pragma mark -
 #pragma mark Sync
 
 - (void)syncOperationStarted {
             // If at least one operation is running return
             newSyncRequested = YES;
             return;
-        } else if (daemonActive && containersCount) {
+        } else if (daemonActive && accountsCount) {
             // The first operation is the server listing
             [self syncOperationStarted];
             newSyncRequested = NO;
         }
     }
 
-    NSFileManager *fileManager = [NSFileManager defaultManager];
-    BOOL isDirectory;
-    NSError *error = nil;
-    if (![fileManager fileExistsAtPath:directoryPath isDirectory:&isDirectory]) {
-        if (![fileManager createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error] || 
-            error) {
-            [PithosUtilities fileActionFailedAlertWithTitle:@"Local Sync Directory Error" 
-                                                    message:[NSString stringWithFormat:@"Cannot create local sync directory at '%@'", directoryPath] 
-                                                      error:error];
-            [self syncOperationFinishedWithSuccess:NO];
-            return;
-        }
-    } else if (!isDirectory) {
-        [PithosUtilities fileActionFailedAlertWithTitle:@"Local Sync Directory Error" 
-                                                message:[NSString stringWithFormat:@"File already exists at the local sync directory path at '%@'", directoryPath] 
-                                                  error:nil];
+    if (![self createSyncDirectory:directoryPath]) {
         [self syncOperationFinishedWithSuccess:NO];
         return;
     }
-    for (ASIPithosContainer *pithosContainer in pithosContainers) {
-        NSString *containerDirectoryPath = [directoryPath stringByAppendingPathComponent:pithosContainer.name];
-        error = nil;
-        if (![fileManager fileExistsAtPath:containerDirectoryPath isDirectory:&isDirectory]) {
-            if (![fileManager createDirectoryAtPath:containerDirectoryPath withIntermediateDirectories:YES attributes:nil error:&error] || 
-                error) {
-                [PithosUtilities fileActionFailedAlertWithTitle:@"Local Sync Directory Error" 
-                                                        message:[NSString stringWithFormat:@"Cannot create local sync directory at '%@'", 
-                                                                 containerDirectoryPath] 
-                                                          error:error];
+    for (NSString *accountName in accountsNames) {
+        for (ASIPithosContainer *pithosContainer in [accountsPithosContainers objectForKey:accountName]) {
+            if (![self createSyncDirectory:[self dirPathForAccount:accountName container:pithosContainer.name]]) {
                 [self syncOperationFinishedWithSuccess:NO];
                 return;
             }
-        } else if (!isDirectory) {
-            [PithosUtilities fileActionFailedAlertWithTitle:@"Local Sync Directory Error" 
-                                                    message:[NSString stringWithFormat:@"File already exists at the local sync directory path at '%@'", 
-                                                             containerDirectoryPath] 
-                                                      error:nil];
-            [self syncOperationFinishedWithSuccess:NO];
-            return;
         }
     }
+
+    self.remoteObjects = [NSMutableDictionary dictionaryWithCapacity:accountsCount];
+    for (NSString *accountName in accountsNames) {
+        [remoteObjects setObject:[NSMutableDictionary dictionary] forKey:accountName];
+    }
+    accountsIndex = 0;
     containersIndex = 0;
-    self.remoteObjects = [NSMutableDictionary dictionaryWithCapacity:containersCount];
+    NSString *accountName = [accountsNames objectAtIndex:accountsIndex];
+    ASIPithosContainer *pithosContainer = [[accountsPithosContainers objectForKey:accountName] objectAtIndex:containersIndex];
     ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest listObjectsRequestWithPithos:pithos 
-                                                                                            containerName:[[pithosContainers objectAtIndex:containersIndex] name] 
+                                                                                            containerName:pithosContainer.name 
                                                                                                     limit:0 
                                                                                                    marker:nil 
                                                                                                    prefix:nil 
                                                                                                      meta:nil 
                                                                                                    shared:NO 
                                                                                                     until:nil 
-                                                                                          ifModifiedSince:[[pithosContainers objectAtIndex:containersIndex] lastModified]];
+                                                                                          ifModifiedSince:pithosContainer.lastModified];
+    if (![accountName isEqualToString:@""])
+        [containerRequest setRequestUserFromDefaultTo:accountName withPithos:pithos];
     containerRequest.delegate = self;
     containerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
     containerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
     [pool drain];
 }
 
-- (BOOL)moveToTempTrashFile:(NSString *)filePath pithosContainer:(ASIPithosContainer *)pithosContainer {
+- (BOOL)moveToTempTrashFile:(NSString *)filePath 
+                accountName:(NSString *)accountName 
+            pithosContainer:(ASIPithosContainer *)pithosContainer {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     if (!self.tempTrashDirPath) {
         [pool drain];
     BOOL isDirectory;
     BOOL fileExists = [fileManager fileExistsAtPath:filePath isDirectory:&isDirectory];
     NSError *error = nil;
-    NSString *containerDirectoryPath = [directoryPath stringByAppendingPathComponent:pithosContainer.name];
+    NSString *containerDirectoryPath = [self dirPathForAccount:accountName container:pithosContainer.name];
     NSString *newFilePath = [filePath stringByReplacingOccurrencesOfString:containerDirectoryPath withString:self.tempTrashDirPath];
     NSString *newDirPath = [newFilePath stringByDeletingLastPathComponent];
     if (fileExists && isDirectory) {
 
 - (void)updateLocalStateWithObject:(ASIPithosObject *)object
                      localFilePath:(NSString *)filePath 
+                       accountName:(NSString *)accountName 
                    pithosContainer:(ASIPithosContainer *)pithosContainer {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSFileManager *fileManager = [NSFileManager defaultManager];
     BOOL isDirectory;
     BOOL fileExists = [fileManager fileExistsAtPath:filePath isDirectory:&isDirectory];
     NSString *fileDirectoryPath = [filePath stringByDeletingLastPathComponent];
-    NSMutableDictionary *containerStoredLocalObjectStates = [storedLocalObjectStates objectForKey:pithosContainer.name];
+    NSMutableDictionary *containerStoredLocalObjectStates = [[storedLocalObjectStates objectForKey:accountName] 
+                                                             objectForKey:pithosContainer.name];
     PithosLocalObjectState *storedState = [containerStoredLocalObjectStates objectForKey:object.name];
     // Remote updated info
     NSError *remoteError;
                                                          objectName:object.name 
                                                               error:&remoteError 
                                                         isDirectory:&remoteIsDirectory 
-                                                     sharingAccount:nil];
+                                                     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;
         }
         NSLog(@"Sync::delete local object: %@", filePath);
-        if (!fileExists || [self moveToTempTrashFile:filePath pithosContainer:pithosContainer]) {
+        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)", 
         }
         NSLog(@"Sync::create local directory object: %@", filePath);
         BOOL directoryCreated = NO;
-        if (!fileExists || (!isDirectory && [self moveToTempTrashFile:filePath pithosContainer:pithosContainer])) {
+        if (!fileExists || (!isDirectory && [self moveToTempTrashFile:filePath accountName:accountName pithosContainer:pithosContainer])) {
             NSLog(@"Sync::local directory object doesn't exist: %@", filePath);
             error = nil;
             if (![fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:&error] || error) {
             dispatch_async(dispatch_get_main_queue(), ^{
                 [activityFacility startAndEndActivityWithType:PithosActivityOther 
                                                       message:[NSString stringWithFormat:@"Sync: Creating directory '%@/%@' locally (finished)", 
-                                                               pithosContainer.name, object.name] 
+                                                               [self relativeDirPathForAccount:accountName container:pithosContainer.name], 
+                                                               object.name] 
                                                 pithosAccount:pithosAccount];
             });
     } else if (object.bytes == 0) {
         }
         NSLog(@"Sync::create local zero length object: %@", filePath);
         BOOL fileCreated = NO;
-        if (!fileExists || ((isDirectory || [PithosUtilities bytesOfFile:filePath]) && [self moveToTempTrashFile:filePath pithosContainer:pithosContainer])) {
+        if (!fileExists || 
+            ((isDirectory || [PithosUtilities bytesOfFile:filePath]) && 
+             [self moveToTempTrashFile:filePath accountName:accountName pithosContainer:pithosContainer])) {
             NSLog(@"Sync::local zero length object doesn't exist: %@", filePath);
             // Create directory of the file, if it doesn't exist
             error = nil;
             dispatch_async(dispatch_get_main_queue(), ^{
                 [activityFacility startAndEndActivityWithType:PithosActivityOther 
                                                       message:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (100%%)", 
-                                                               pithosContainer.name, object.name] 
+                                                               [self relativeDirPathForAccount:accountName container:pithosContainer.name], 
+                                                               object.name] 
                                                 pithosAccount:pithosAccount];
             });
     } else if (storedState.tmpFilePath == nil) {
             dispatch_async(dispatch_get_main_queue(), ^{
                 [activityFacility startAndEndActivityWithType:PithosActivityOther 
                                                       message:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (100%%)", 
-                                                               pithosContainer.name, object.name] 
+                                                               [self relativeDirPathForAccount:accountName container:pithosContainer.name], 
+                                                               object.name] 
                                                 pithosAccount:pithosAccount];
             });
         } else {
                                                                                                        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:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (0%%)", 
-                                                                                pithosContainer.name, object.name] 
+                                                                       message:[messagePrefix stringByAppendingString:@" (0%%)"] 
                                                                     totalBytes:object.bytes 
                                                                   currentBytes:0 
                                                                  pithosAccount:pithosAccount];
                 [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", 
-                                      [NSString stringWithFormat:@"Sync: Downloading '%@/%@' (stopped)", pithosContainer.name, object.name], @"stoppedActivityMessage", 
-                                      [NSString stringWithFormat:@"Sync: Downloading '%@/%@' (failed)", pithosContainer.name, object.name], @"failedActivityMessage", 
-                                      [NSString stringWithFormat:@"Sync: Downloading '%@/%@' (100%%)", pithosContainer.name, object.name], @"finishedActivityMessage", 
+                                      [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                                      [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                                      [messagePrefix stringByAppendingString:@" (100%%)"], @"finishedActivityMessage", 
                                       [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
                                       [NSNumber numberWithUnsignedInteger:10], @"retries", 
                                       NSStringFromSelector(@selector(downloadObjectBlockFinished:)), @"didFinishSelector", 
                                       nil];
             [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
                 [activityFacility updateActivity:activity 
-                                     withMessage:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (%.0f%%)", 
-                                                  objectRequest.containerName, 
-                                                  [[objectRequest.userInfo objectForKey:@"pithosObject"] name], 
-                                                  (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))] 
+                                     withMessage:[NSString stringWithFormat:@"%@ (%.0f%%)", 
+                                                  messagePrefix, (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))] 
                                       totalBytes:activity.totalBytes 
                                     currentBytes:(activity.currentBytes + size)];
             }];
             dispatch_async(dispatch_get_main_queue(), ^{
                 [activityFacility startAndEndActivityWithType:PithosActivityOther 
                                                       message:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (100%%)", 
-                                                               pithosContainer.name, object.name] 
+                                                               [self relativeDirPathForAccount:accountName container:pithosContainer.name], 
+                                                               object.name] 
                                                 pithosAccount:pithosAccount];
             });
         } else {
             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:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (0%%)", 
-                                                                                pithosContainer.name, object.name] 
+                                                                       message:[messagePrefix stringByAppendingString:@" (0%%)"] 
                                                                     totalBytes:object.bytes 
                                                                   currentBytes:0 
                                                                  pithosAccount:pithosAccount];
                 [activityFacility updateActivity:activity withMessage:activity.message];
             });
             objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                                      accountName, @"accountName", 
                                       pithosContainer, @"pithosContainer", 
                                       object, @"pithosObject", 
+                                      messagePrefix, @"messagePrefix", 
                                       filePath, @"filePath", 
                                       activity, @"activity", 
-                                      [NSString stringWithFormat:@"Sync: Downloading '%@/%@' (stopped)", pithosContainer.name, object.name], @"stoppedActivityMessage", 
-                                      [NSString stringWithFormat:@"Sync: Downloading '%@/%@' (failed)", pithosContainer.name, object.name], @"failedActivityMessage", 
-                                      [NSString stringWithFormat:@"Sync: Downloading '%@/%@' (100%%)", pithosContainer.name, object.name], @"finishedActivityMessage", 
+                                      [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                                      [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                                      [messagePrefix stringByAppendingString:@" (100%%)"], @"finishedActivityMessage", 
                                       [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
                                       [NSNumber numberWithUnsignedInteger:10], @"retries", 
                                       NSStringFromSelector(@selector(downloadObjectHashMapFinished:)), @"didFinishSelector", 
 -(void)updateServerStateWithCurrentState:(PithosLocalObjectState *)currentState 
                                   object:(ASIPithosObject *)object 
                            localFilePath:(NSString *)filePath 
+                             accountName:(NSString *)accountName 
                          pithosContainer:(ASIPithosContainer *)pithosContainer {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     [self syncOperationStarted];
     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];
+                [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];
                                                                                                 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:[NSString stringWithFormat:@"Sync: Creating directory '%@/%@'", 
-                                                                            pithosContainer.name, object.name] 
+                                                                   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", 
-                                  [NSString stringWithFormat:@"Sync: Creating directory '%@/%@' (stopped)", pithosContainer.name, object.name], @"stoppedActivityMessage", 
-                                  [NSString stringWithFormat:@"Sync: Creating directory '%@/%@' (failed)", pithosContainer.name, object.name], @"failedActivityMessage", 
-                                  [NSString stringWithFormat:@"Sync: Creating directory '%@/%@' (finished)", pithosContainer.name, object.name], @"finishedActivityMessage", 
+                                  [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                                  [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                                  [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", 
                                   [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                                   [NSNumber numberWithUnsignedInteger:10], @"retries", 
                                   NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", 
         [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;
             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:[NSString stringWithFormat:@"Sync: Deleting '%@/%@'", 
-                                                                                pithosContainer.name, object.name] 
+                                                                       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", 
-                                      [NSString stringWithFormat:@"Sync: Deleting '%@/%@' (stopped)", pithosContainer.name, object.name], @"stoppedActivityMessage", 
-                                      [NSString stringWithFormat:@"Sync: Deleting '%@/%@' (failed)", pithosContainer.name, object.name], @"failedActivityMessage", 
-                                      [NSString stringWithFormat:@"Sync: Deleting '%@/%@' (finished)", pithosContainer.name, object.name], @"finishedActivityMessage", 
+                                      [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                                      [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                                      [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", 
                                       [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                                       [NSNumber numberWithUnsignedInteger:10], @"retries", 
                                       NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", 
                     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:[NSString stringWithFormat:@"Sync: Moving to trash '%@/%@'", 
-                                                                                        pithosContainer.name, object.name] 
+                                                                               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", 
-                                              [NSString stringWithFormat:@"Sync: Moving to trash '%@/%@' (stopped)", pithosContainer.name, object.name], @"stoppedActivityMessage", 
-                                              [NSString stringWithFormat:@"Sync: Moving to trash '%@/%@' (failed)", pithosContainer.name, object.name], @"failedActivityMessage", 
-                                              [NSString stringWithFormat:@"Sync: Moving to trash '%@/%@' (finished)", pithosContainer.name, object.name], @"finishedActivityMessage", 
+                                              [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                                              [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                                              [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", 
                                               [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                                               [NSNumber numberWithUnsignedInteger:10], @"retries", 
                                               NSStringFromSelector(@selector(moveObjectToTrashFinished:)), @"didFinishSelector", 
         }
     } 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];
+                [pool drain];
+                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];
                                                                                           forFile:filePath 
                                                                                     checkIfExists:NO 
                                                                                            hashes:&hashes 
-                                                                                   sharingAccount:nil];
+                                                                                   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:[NSString stringWithFormat:@"Sync: Uploading '%@/%@' (0%%)", 
-                                                                                pithosContainer.name, object.name]
+                                                                       message:[messagePrefix stringByAppendingString:@" (0%%)"] 
                                                                     totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue]
                                                                   currentBytes:0 
                                                                  pithosAccount:pithosAccount];
             });
             [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
              [NSDictionary dictionaryWithObjectsAndKeys:
+              accountName, @"accountName", 
               pithosContainer, @"pithosContainer", 
               object, @"pithosObject", 
+              messagePrefix, @"messagePrefix", 
               filePath, @"filePath", 
               hashes, @"hashes", 
               [NSNumber numberWithUnsignedInteger:10], @"iteration", 
               activity, @"activity", 
-              [NSString stringWithFormat:@"Sync: Uploading '%@' (stopped)", object.name], @"stoppedActivityMessage", 
-              [NSString stringWithFormat:@"Sync: Uploading '%@' (failed)", object.name], @"failedActivityMessage", 
-              [NSString stringWithFormat:@"Sync: Uploading '%@' (100%%)", object.name], @"finishedActivityMessage", 
+              [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+              [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+              [messagePrefix stringByAppendingString:@" (100%%)"], @"finishedActivityMessage", 
               [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
               [NSNumber numberWithUnsignedInteger:10], @"retries", 
               NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", 
     NSLog(@"Sync::list request finished: %@", containerRequest.url);
     if (operation.isCancelled) {
         [self listRequestFailed:containerRequest];
-    } else if ((containerRequest.responseStatusCode == 200) || (containerRequest.responseStatusCode == 304)) {
+    } 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 addObjectsFromArray:someObjects];
             }
             if ([someObjects count] < 10000) {
-                ASIPithosContainer *pithosContainer = [pithosContainers objectAtIndex:containersIndex];
                 pithosContainer.blockHash = [containerRequest blockHash];
                 pithosContainer.blockSize = [containerRequest blockSize];
                 pithosContainer.lastModified = [containerRequest lastModified];
                 for (ASIPithosObject *object in objects) {
                     [containerRemoteObjects setObject:object forKey:object.name];
                 }
-                [remoteObjects setObject:containerRemoteObjects forKey:pithosContainer.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:[[pithosContainers objectAtIndex:containersIndex] name] 
+                                                                                                           containerName:pithosContainer.name 
                                                                                                                    limit:0 
                                                                                                                   marker:[[someObjects lastObject] name] 
                                                                                                                   prefix:nil 
                                                                                                                     meta:nil 
                                                                                                                   shared:NO 
                                                                                                                    until:nil 
-                                                                                                         ifModifiedSince:[[pithosContainers objectAtIndex:containersIndex] lastModified]];
+                                                                                                         ifModifiedSince:pithosContainer.lastModified];
+                if (![accountName isEqualToString:@""])
+                    [newContainerRequest setRequestUserFromDefaultTo:accountName withPithos:pithos];
                 newContainerRequest.delegate = self;
                 newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                 newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
                 [pool drain];
                 return;
             }
-        } else {
-            ASIPithosContainer *pithosContainer = [pithosContainers objectAtIndex:containersIndex];
-            NSMutableDictionary *containerRemoteObjects = [previousRemoteObjects objectForKey:pithosContainer.name];
+        } else if (containerRequest.responseStatusCode == 304) {
+            NSMutableDictionary *containerRemoteObjects = [[previousRemoteObjects objectForKey:accountName] 
+                                                           objectForKey:pithosContainer.name];
             if (containerRemoteObjects) 
-                [remoteObjects setObject:containerRemoteObjects forKey:pithosContainer.name];
+                [[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 < containersCount) {
+        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:[[pithosContainers objectAtIndex:containersIndex] name] 
+                                                                                                       containerName:pithosContainer.name 
                                                                                                                limit:0 
                                                                                                               marker:nil 
                                                                                                               prefix:nil 
                                                                                                                 meta:nil 
                                                                                                               shared:NO 
                                                                                                                until:nil 
-                                                                                                     ifModifiedSince:[[pithosContainers objectAtIndex:containersIndex] lastModified]];
+                                                                                                     ifModifiedSince:pithosContainer.lastModified];
+            if (![accountName isEqualToString:@""])
+                [newContainerRequest setRequestUserFromDefaultTo:accountName withPithos:pithos];
             newContainerRequest.delegate = self;
             newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
             newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
         // 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 (ASIPithosContainer *pithosContainer in pithosContainers) {
-            NSString *containerDirectoryPath = [directoryPath stringByAppendingPathComponent:pithosContainer.name];
-            NSArray *containerExcludedDirectories = [containersDictionary objectForKey:pithosContainer.name];
-            BOOL containerExludeRootFiles = [containerExcludedDirectories containsObject:@""];
-            NSMutableDictionary *containerStoredLocalObjectStates = [storedLocalObjectStates objectForKey:pithosContainer.name];
-            NSDirectoryEnumerator *dirEnumerator = [fileManager enumeratorAtPath:containerDirectoryPath];
-            for (NSString *objectName in dirEnumerator) {
-                if (operation.isCancelled) {
-                    operation.completionBlock = nil;
-                    [self saveLocalState];
-                    [self syncOperationFinishedWithSuccess:NO];
-                    [pool drain];
-                    return;
-                }
+        for (NSString *accountName in accountsNames) {
+            for (ASIPithosContainer *pithosContainer in [accountsPithosContainers objectForKey:accountName]) {
+                NSString *containerDirectoryPath = [self dirPathForAccount:accountName container:pithosContainer.name];
+                NSArray *containerExcludedDirectories = [[accountsDictionary objectForKey:accountName] objectForKey:pithosContainer.name];
+                BOOL containerExludeRootFiles = [containerExcludedDirectories containsObject:@""];
+                NSMutableDictionary *containerStoredLocalObjectStates = [[storedLocalObjectStates objectForKey:accountName] 
+                                                                         objectForKey:pithosContainer.name];
+                NSDirectoryEnumerator *dirEnumerator = [fileManager enumeratorAtPath:containerDirectoryPath];
+                for (NSString *objectName in dirEnumerator) {
+                    if (operation.isCancelled) {
+                        operation.completionBlock = nil;
+                        [self saveLocalState];
+                        [self syncOperationFinishedWithSuccess:NO];
+                        [pool drain];
+                        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]) {
-                    dispatch_async(dispatch_get_main_queue(), ^{
-                        [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) {
-                    NSArray *pathComponents = [objectName pathComponents];
-                    if ([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 && containerExludeRootFiles) {
-                            // Skip excluded root file
-                            // Remove stored state if any
-                            [containerStoredLocalObjectStates removeObjectForKey:objectName];
-                            continue;
+                    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]) {
+                        dispatch_async(dispatch_get_main_queue(), ^{
+                            [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) {
+                        NSArray *pathComponents = [objectName pathComponents];
+                        if ([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 && containerExludeRootFiles) {
+                                // 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;
         }
 
         // Add an empty stored state for legal new remote objects since last sync
-        for (ASIPithosContainer *pithosContainer in pithosContainers) {
-            NSArray *containerExcludedDirectories = [containersDictionary objectForKey:pithosContainer.name];
-            BOOL containerExludeRootFiles = [containerExcludedDirectories containsObject:@""];
-            NSMutableDictionary *containerStoredLocalObjectStates = [storedLocalObjectStates objectForKey:pithosContainer.name];
-            NSMutableDictionary *containerRemoteObjects = [remoteObjects objectForKey:pithosContainer.name];
-            for (NSString *objectName in containerRemoteObjects) {
-                if (operation.isCancelled) {
-                    operation.completionBlock = nil;
-                    [self saveLocalState];
-                    [self syncOperationFinishedWithSuccess:NO];
-                    [pool drain];
-                    return;
-                }
+        for (NSString *accountName in accountsNames) {
+            for (ASIPithosContainer *pithosContainer in [accountsPithosContainers objectForKey:accountName]) {
+                NSArray *containerExcludedDirectories = [[accountsDictionary objectForKey:accountName] objectForKey:pithosContainer.name];
+                BOOL containerExludeRootFiles = [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;
+                    }
 
-                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 ([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 (containerExludeRootFiles && 
-                           ([pathComponents count] == 1) && 
-                           ![PithosUtilities isContentTypeDirectory:object.contentType]) {
-                    // Skip root file object
-                    // Remove stored state if any
-                    [containerStoredLocalObjectStates removeObjectForKey:object.name];                    
-                    continue;
+                    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 ([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 (containerExludeRootFiles && 
+                               ([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 (![containerStoredLocalObjectStates objectForKey:object.name])
-                    [containerStoredLocalObjectStates setObject:[PithosLocalObjectState localObjectState] forKey:object.name];
+                [self saveLocalState];
             }
-            [self saveLocalState];
         }
 
         if (operation.isCancelled) {
         // 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 (ASIPithosContainer *pithosContainer in pithosContainers) {
-            NSString *containerDirectoryPath = [directoryPath stringByAppendingPathComponent:pithosContainer.name];
-            NSArray *containerExcludedDirectories = [containersDictionary objectForKey:pithosContainer.name];
-            BOOL containerExludeRootFiles = [containerExcludedDirectories containsObject:@""];
-            NSMutableDictionary *containerStoredLocalObjectStates = [storedLocalObjectStates objectForKey:pithosContainer.name];
-            NSMutableDictionary *containerRemoteObjects = [remoteObjects 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 (NSString *accountName in accountsNames) {
+            for (ASIPithosContainer *pithosContainer in [accountsPithosContainers objectForKey:accountName]) {
+                NSString *containerDirectoryPath = [self dirPathForAccount:accountName container:pithosContainer.name];
+                NSArray *containerExcludedDirectories = [[accountsDictionary objectForKey:accountName] objectForKey:pithosContainer.name];
+                BOOL containerExludeRootFiles = [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;
+                    }
 
-                NSString *filePath = [containerDirectoryPath stringByAppendingPathComponent:objectName];
-                if ([objectName hasSuffix:@"/"])
-                    filePath = [filePath stringByAppendingString:@":"];
-                ASIPithosObject *object = [ASIPithosObject object];
-                object.name = objectName;
-                NSLog(@"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 ([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 (containerExludeRootFiles && ([pathComponents count] == 1) && !storedLocalObjectState.isDirectory) {
-                            // Skip root file object
-                            // Remove stored state
-                            [containerStoredLocalObjectStates removeObjectForKey:object.name];                    
-                            [self saveLocalState];
-                            continue;
+                    NSString *filePath = [containerDirectoryPath stringByAppendingPathComponent:objectName];
+                    if ([objectName hasSuffix:@"/"])
+                        filePath = [filePath stringByAppendingString:@":"];
+                    ASIPithosObject *object = [ASIPithosObject object];
+                    object.name = objectName;
+                    NSLog(@"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 ([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 (containerExludeRootFiles && ([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];
-                NSLog(@"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 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];
-                    }
+                    BOOL localStateHasChanged = ![currentLocalObjectState isEqualToState:storedLocalObjectState];
+                    BOOL serverStateHasChanged = ![remoteObjectState isEqualToState:storedLocalObjectState];
+                    NSLog(@"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) {
                             object.contentType = @"application/directory";
                         else
                             object.objectHash = currentLocalObjectState.hash;
-                        [self updateServerStateWithCurrentState:currentLocalObjectState 
-                                                         object:object 
-                                                  localFilePath:filePath 
-                                                pithosContainer:pithosContainer];
+                        [self updateServerStateWithCurrentState:currentLocalObjectState object:object localFilePath:filePath 
+                                                    accountName:accountName pithosContainer:pithosContainer];
                     } else {
                         // Server state has also changed
                         if (remoteObjectState.isDirectory && currentLocalObjectState.isDirectory) {
                             if (![remoteObjectState exists]) {
                                 // Remote object has been deleted
                                 informativeText = [NSString stringWithFormat:@"'%@/%@' has been modified locally, while it has been deleted from server.", 
-                                                   pithosContainer.name, object.name ];
+                                                   [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.", 
-                                                   pithosContainer.name, object.name];
+                                                   [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.", 
-                                                   pithosContainer.name, object.name];
+                                                   [self relativeDirPathForAccount:accountName container:pithosContainer.name], object.name];
                                 firstButtonText = @"Keep server version";
                                 secondButtonText = @"Keep local version";
                             }
                                 object.version = remoteObject.version;
                                 object.contentType = remoteObject.contentType;
                                 object.objectHash = remoteObject.objectHash;
-                                [self updateLocalStateWithObject:object localFilePath:filePath pithosContainer:pithosContainer];
+                                [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 
-                                                        pithosContainer:pithosContainer];
+                                [self updateServerStateWithCurrentState:currentLocalObjectState object:object localFilePath:filePath 
+                                                            accountName:accountName pithosContainer:pithosContainer];
                             }
                         }
                     }
                 }
             }
         }
+        }
         [self syncOperationFinishedWithSuccess:YES];
     } else {
         [self listRequestFailed:containerRequest];
     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];
             return;
         }
         
-        PithosLocalObjectState *storedState = [[storedLocalObjectStates objectForKey:pithosContainer.name] objectForKey:object.name];
+        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];
             storedState.tmpFilePath = tempFilePath;
             [self saveLocalState];
         }
-        
 
         NSUInteger missingBlockIndex = [[objectRequest.userInfo objectForKey:@"missingBlockIndex"] unsignedIntegerValue];
         NSFileHandle *tempFileHandle = [NSFileHandle fileHandleForWritingAtPath:storedState.tmpFilePath];
         if (missingBlockIndex == NSNotFound) {
             NSString *filePath = [objectRequest.userInfo objectForKey:@"filePath"];
             NSString *dirPath = [filePath stringByDeletingLastPathComponent];
-            if ([fileManager fileExistsAtPath:filePath] && ![self moveToTempTrashFile:filePath pithosContainer:pithosContainer]) {
+            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"]];
                                                                                                               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:);
                 [(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:@"Sync: Downloading '%@/%@' (%.0f%%)", 
-                                                      newObjectRequest.containerName, object.name, 
+                                         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)];
         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:pithosContainer.name] objectForKey:object.name];
+            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"];
             NSUInteger missingBlockIndex = [missingBlocks firstIndex];
             dispatch_async(dispatch_get_main_queue(), ^{
                 [activityFacility updateActivity:activity 
-                                     withMessage:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (%.0f%%)", 
-                                                  pithosContainer.name, object.name, 
+                                     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)];
                                                                                                           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:);
             [(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:@"Sync: Downloading '%@/%@' (%.0f%%)", 
-                                                  newObjectRequest.containerName, object.name, 
+                                     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)];
     if (operation.isCancelled) {
         [self requestFailed:objectRequest];
     } else if (objectRequest.responseStatusCode == 201) {
-        PithosLocalObjectState *storedState = [[storedLocalObjectStates objectForKey:[[objectRequest.userInfo objectForKey:@"pithosContainer"] name]] 
+        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];
     if (operation.isCancelled) {
         [self requestFailed:objectRequest];
     } else if (objectRequest.responseStatusCode == 201) {
-        [[storedLocalObjectStates objectForKey:[[objectRequest.userInfo objectForKey:@"pithosContainer"] name]] 
+        [[[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(), ^{
     if (operation.isCancelled) {
         [self requestFailed:objectRequest];
     } else if (objectRequest.responseStatusCode == 204) {
-        [[storedLocalObjectStates objectForKey:[[objectRequest.userInfo objectForKey:@"pithosContainer"] name]] 
+        [[[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(), ^{
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
     NSLog(@"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:pithosContainer.name] objectForKey:object.name];
+    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;
                 currentBytes = totalBytes - [missingBlocks count]*pithosContainer.blockSize;
             dispatch_async(dispatch_get_main_queue(), ^{
                 [activityFacility updateActivity:activity 
-                                     withMessage:[NSString stringWithFormat:@"Sync: Uploading '%@/%@' (%.0f%%)", 
-                                                  pithosContainer.name, object.name, (100*(currentBytes + 0.0)/(totalBytes + 0.0))] 
+                                     withMessage:[NSString stringWithFormat:@"%@ (%.0f%%)", 
+                                                  [objectRequest.userInfo objectForKey:@"messagePrefix"], 
+                                                  (100*(currentBytes + 0.0)/(totalBytes + 0.0))] 
                                       totalBytes:totalBytes 
                                     currentBytes:currentBytes];
             });
                                                                                                                  blockSize:pithosContainer.blockSize 
                                                                                                                    forFile:[objectRequest.userInfo objectForKey:@"filePath"] 
                                                                                                          missingBlockIndex:missingBlockIndex 
-                                                                                                            sharingAccount:nil];
+                                                                                                            sharingAccount:([accountName isEqualToString:@""] ? nil : accountName)];
             newContainerRequest.delegate = self;
             newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
             newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
             [(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:@"Sync: Uploading '%@/%@' (%.0f%%)", 
-                                                  newContainerRequest.containerName, object.name, (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))] 
+                                     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)];
             }];
     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"];
                                                                                                  forFile:[containerRequest.userInfo objectForKey:@"filePath"] 
                                                                                            checkIfExists:NO 
                                                                                                   hashes:&hashes 
-                                                                                          sharingAccount:nil];
+                                                                                          sharingAccount:([accountName isEqualToString:@""] ? nil : accountName)];
             newObjectRequest.delegate = self;
             newObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
             newObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
                                                                                                                      blockSize:pithosContainer.blockSize
                                                                                                                        forFile:[containerRequest.userInfo objectForKey:@"filePath"] 
                                                                                                              missingBlockIndex:missingBlockIndex 
-                                                                                                                sharingAccount:nil];
+                                                                                                                sharingAccount:([accountName isEqualToString:@""] ? nil : accountName)];
                 newContainerRequest.delegate = self;
                 newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                 newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
                 [(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:@"Sync: Uploading '%@/%@' (%.0f%%)", 
-                                                      newContainerRequest.containerName, object.name, (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))] 
+                                         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)];
                 }];
index 8f3de0e..efa2ed4 100644 (file)
 + (NSUInteger)bytesOfFile:(NSString *)filePath;
 + (NSString *)contentTypeOfFile:(NSString *)filePath error:(NSError **)error;
 + (BOOL)safeCreateDirectory:(NSString *)directoryPath error:(NSError **)error;
++ (void)removeContentsAtPath:(NSString *)dirPath;
 + (BOOL)isContentTypeDirectory:(NSString *)contentType;
 + (BOOL)objectExistsAtPithos:(ASIPithos *)pithos containerName:(NSString *)containerName objectName:(NSString *)objectName 
                        error:(NSError **)error isDirectory:(BOOL *)isDirectory sharingAccount:(NSString *)sharingAccount;
index b3a3161..af97bb2 100644 (file)
     }
     
     for (ASIPithosObject *object in objects) {
-        if ([PithosUtilities isContentTypeDirectory:object.contentType]) {
+        if ([self isContentTypeDirectory:object.contentType]) {
             NSString *subdirDirectoryPath = [directoryPath stringByAppendingPathComponent:subdirName];
             subdirDirectoryPath = [subdirDirectoryPath stringByAppendingPathComponent:[object.name substringFromIndex:subdirPrefixLength]];
             
     return YES;
 }
 
+// Removes contents of a directory
++ (void)removeContentsAtPath:(NSString *)dirPath {
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+    NSError *error = nil;
+    BOOL isDirectory;
+    if (![fileManager fileExistsAtPath:dirPath isDirectory:&isDirectory])
+        return;
+    if (isDirectory) {
+        for (NSString *subPath in [fileManager contentsOfDirectoryAtPath:dirPath error:&error]) {
+            if (error) {
+                [self fileActionFailedAlertWithTitle:@"Directory Contents Error" 
+                                             message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", dirPath] 
+                                               error:error];
+                break;
+            }
+            NSString *subFilePath = [dirPath stringByAppendingPathComponent:subPath];
+            if (![fileManager removeItemAtPath:subFilePath error:&error] || error) {
+                [self fileActionFailedAlertWithTitle:@"Remove File Error" 
+                                             message:[NSString stringWithFormat:@"Cannot remove file at '%@'", subFilePath] 
+                                               error:error];
+            }
+            error = nil;
+        }
+    } else if (![fileManager removeItemAtPath:dirPath error:&error] || error) {
+        [self fileActionFailedAlertWithTitle:@"Remove File Error" 
+                                     message:[NSString stringWithFormat:@"Cannot remove file at '%@'", dirPath] 
+                                       error:error];
+    }
+}
+
 // Returns if an object is a directory based on its content type
 + (BOOL)isContentTypeDirectory:(NSString *)contentType {
     return ([contentType isEqualToString:@"application/directory"] ||
         [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
     ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
     [networkQueue go];
-    [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:objectRequest]] waitUntilFinished:YES];
+    [networkQueue addOperations:[NSArray arrayWithObject:[self prepareRequest:objectRequest]] waitUntilFinished:YES];
     *error = [objectRequest error];
     if (*error) {
         [self httpRequestErrorAlertWithRequest:objectRequest];
             [containerRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
         ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
         [networkQueue go];
-        [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:containerRequest]] waitUntilFinished:YES];
+        [networkQueue addOperations:[NSArray arrayWithObject:[self prepareRequest:containerRequest]] waitUntilFinished:YES];
         if ([containerRequest error]) {
             [self httpRequestErrorAlertWithRequest:containerRequest];
             return nil;