Revision 02b6ea19

b/pithos-macos/PithosAccount.h
47 47
    
48 48
    BOOL syncActive;
49 49
    NSString *syncDirectoryPath;
50
    NSString *syncContainerName;
50
    NSMutableDictionary *syncContainersDictionary;
51 51
    NSDate *syncLastCompleted;
52 52
    PithosSyncDaemon *syncDaemon;
53 53
        
54
    // Base for all necessary URLs, default "https://plus.pithos.grnet.gr"
54
    // Base for all necessary URLs, default "https://pithos.okeanos.grnet.gr"
55 55
    NSString *serverURL;
56 56
    // Appended to serverURL to get storageURLPrefix and authURL, default "v1"
57 57
    // Can be overriden by setting directly storageURLPrefix and authURL
......
83 83

  
84 84
@property (nonatomic, assign) BOOL syncActive;
85 85
@property (nonatomic, retain) NSString *syncDirectoryPath;
86
@property (nonatomic, retain) NSString *syncContainerName;
86
@property (nonatomic, retain) NSMutableDictionary *syncContainersDictionary;
87 87
@property (nonatomic, retain) NSDate *syncLastCompleted;
88 88
@property (nonatomic, retain) PithosSyncDaemon *syncDaemon;
89 89

  
b/pithos-macos/PithosAccount.m
47 47

  
48 48
@implementation PithosAccount
49 49
@synthesize uniqueName, active, name;
50
@synthesize syncActive, syncDirectoryPath, syncContainerName, syncLastCompleted, syncDaemon;
50
@synthesize syncActive, syncDirectoryPath, syncContainersDictionary, syncLastCompleted, syncDaemon;
51 51
@synthesize serverURL, versionResource, loginResource, publicResource;
52 52
@synthesize authUser, authToken, storageURLPrefix, authURL, loginURLPrefix, publicURLPrefix;
53 53
@synthesize pithos, accountNode;
......
78 78
    [serverURL release];
79 79
    [syncDaemon release];
80 80
    [syncLastCompleted release];
81
    [syncContainerName release];
81
    [syncContainersDictionary release];
82 82
    [syncDirectoryPath release];
83 83
    [name release];
84 84
    [uniqueName release];
......
86 86
}
87 87

  
88 88
- (NSString *)description {
89
    return [NSString stringWithFormat:@"uniqueName: %@, active: %d, name: %@, syncActive: %d, syncDirectoryPath: %@, syncContainerName: %@, syncLastCompleted: %@, serverURL: %@, versionResource: %@, loginResource: %@, publicResource: %@, authUser: %@, authToken: %@, storageURLPrefix: %@, authURL: %@, loginURLPrefix: %@, publicURLPrefix: %@", 
90
            uniqueName, active, name, syncActive, syncDirectoryPath, syncContainerName, syncLastCompleted, serverURL, versionResource, loginResource, publicResource, authUser, authToken, storageURLPrefix, authURL, loginURLPrefix, publicURLPrefix];
89
    return [NSString stringWithFormat:@"uniqueName: %@, active: %d, name: %@, syncActive: %d, syncDirectoryPath: %@, syncContainersDictionary: %@, syncLastCompleted: %@, serverURL: %@, versionResource: %@, loginResource: %@, publicResource: %@, authUser: %@, authToken: %@, storageURLPrefix: %@, authURL: %@, loginURLPrefix: %@, publicURLPrefix: %@", 
90
            uniqueName, active, name, syncActive, syncDirectoryPath, syncContainersDictionary, syncLastCompleted, serverURL, versionResource, loginResource, publicResource, authUser, authToken, storageURLPrefix, authURL, loginURLPrefix, publicURLPrefix];
91 91
}
92 92

  
93 93
#pragma mark -
......
173 173
    }
174 174
}
175 175

  
176
- (NSString *)syncContainerName {
177
    if (![syncContainerName length]) {
178
        [syncContainerName release];
179
        syncContainerName = [[NSString stringWithString:@"pithos"] retain];
176
- (NSMutableDictionary *)syncContainersDictionary {
177
    if (!syncContainersDictionary) {
178
        syncContainersDictionary = [[NSMutableDictionary dictionaryWithObject:[NSMutableArray array] 
179
                                                                       forKey:@"pithos"] retain];
180 180
    }        
181
    return syncContainerName;
181
    return syncContainersDictionary;
182 182
}
183 183

  
184
- (void)setSyncContainerName:(NSString *)aSyncContainerName {
185
    if (![self.syncContainerName isEqualToString:aSyncContainerName] && [aSyncContainerName length]) {
186
        [syncContainerName release];
187
        syncContainerName = [aSyncContainerName retain];
184
- (void)setSyncContainersDictionary:(NSMutableDictionary *)aSyncContainersDictionary {
185
    if (![self.syncContainersDictionary isEqualTo:syncContainersDictionary]) {
186
        [syncContainersDictionary release];
187
        syncContainersDictionary = [aSyncContainersDictionary retain];
188
        // XXX check for proper dictionary here
188 189
        
189 190
        @synchronized(self) {
190 191
            resetSyncDaemonLocalState = YES;
......
206 207
    @synchronized(self) {
207 208
        if (self.syncActive && !syncDaemon)
208 209
            syncDaemon = [[PithosSyncDaemon alloc] initWithDirectoryPath:self.syncDirectoryPath 
209
                                                           pithosAccount:self 
210
                                                           containerName:self.syncContainerName 
210
                                                           pithosAccount:self
211
                                                    containersDictionary:self.syncContainersDictionary
211 212
                                                         resetLocalState:resetSyncDaemonLocalState];
212 213
        resetSyncDaemonLocalState = NO;
213 214
    }
......
217 218
- (NSString *)serverURL {
218 219
    if (![self urlIsValid:serverURL]) {
219 220
        [serverURL release];
220
        serverURL = [[NSString stringWithString:@"https://plus.pithos.grnet.gr"] retain];
221
        serverURL = [[NSString stringWithString:@"https://pithos.okeanos.grnet.gr"] retain];
221 222
    }
222 223
    return serverURL;
223 224
}
......
424 425

  
425 426
        self.syncActive = [decoder decodeBoolForKey:@"syncActive"];
426 427
        self.syncDirectoryPath = [decoder decodeObjectForKey:@"syncDirectoryPath"];
427
        self.syncContainerName = [decoder decodeObjectForKey:@"syncContainerName"];
428
        self.syncContainersDictionary = [decoder decodeObjectForKey:@"syncContainersDictionary"];
428 429
        self.syncLastCompleted = [decoder decodeObjectForKey:@"syncLastCompleted"];
429 430
        
430 431
        self.serverURL = [decoder decodeObjectForKey:@"serverURL"];
......
454 455
    
455 456
    [encoder encodeBool:syncActive forKey:@"syncActive"];
456 457
    [encoder encodeObject:syncDirectoryPath forKey:@"syncDirectoryPath"];
457
    [encoder encodeObject:syncContainerName forKey:@"syncContainerName"];
458
    [encoder encodeObject:syncContainersDictionary forKey:@"syncContainersDictionary"];
458 459
    [encoder encodeObject:self.syncLastCompleted forKey:@"syncLastCompleted"];
459 460

  
460 461
    [encoder encodeObject:serverURL forKey:@"serverURL"];
b/pithos-macos/PithosSyncDaemon.h
44 44
@interface PithosSyncDaemon : NSObject {
45 45
    NSString *directoryPath;
46 46
    PithosAccount *pithosAccount;
47
    NSString *containerName;
47
    NSDictionary *containersDictionary;
48 48
    ASIPithos *pithos;
49

  
50
    NSString *blockHash;
51
    NSUInteger blockSize;
52
    NSDate *lastModified;
49
    
50
    NSUInteger containersCount;
51
    NSMutableArray *pithosContainers;
52
    NSUInteger containersIndex;
53
    
53 54
    NSMutableArray *objects;
54 55
    NSMutableDictionary *remoteObjects;
56
    NSMutableDictionary *previousRemoteObjects;
55 57
    NSMutableDictionary *storedLocalObjectStates;
56 58
    NSMutableDictionary *currentLocalObjectStates;
57 59
    
58
    NSString *containerDirectoryPath;
59 60
    NSString *pithosStateFilePath;
60 61
    NSString *tempDownloadsDirPath;
61 62
    NSString *tempTrashDirPath;
......
76 77

  
77 78
@property (nonatomic, retain) NSString *directoryPath;
78 79
@property (nonatomic, retain) PithosAccount *pithosAccount;
79
@property (nonatomic, retain) NSString *containerName;
80
@property (nonatomic, retain) NSDictionary *containersDictionary;
80 81
@property (nonatomic, retain) ASIPithos *pithos;
81 82

  
82
@property (nonatomic, retain) NSString *blockHash;
83
@property (nonatomic, assign) NSUInteger blockSize;
84
@property (nonatomic, retain) NSDate *lastModified;
83
@property (nonatomic, retain) NSMutableArray *pithosContainers;
84

  
85 85
@property (nonatomic, retain) NSMutableDictionary *remoteObjects;
86
@property (nonatomic, retain) NSMutableDictionary *previousRemoteObjects;
86 87
@property (nonatomic, retain) NSMutableDictionary *storedLocalObjectStates;
87 88
@property (nonatomic, retain) NSMutableDictionary *currentLocalObjectStates;
88 89

  
89
@property (nonatomic, retain) NSString *containerDirectoryPath;
90 90
@property (nonatomic, readonly) NSString *pithosStateFilePath;
91 91
@property (nonatomic, readonly) NSString *tempDownloadsDirPath;
92 92
@property (nonatomic, readonly) NSString *tempTrashDirPath;
93 93

  
94 94
@property (nonatomic, retain) NSDate *lastCompletedSync;
95 95

  
96
// containersDictionary contains entries with keys the container names that should be included
97
// and values NSArrays that contain the directory names that should be excluded
98
// An empty string denotes that file objects in the container root should be excluded
96 99
- (id)initWithDirectoryPath:(NSString *)aDirectoryPath 
97 100
              pithosAccount:(PithosAccount *)aPithosAccount 
98
              containerName:(NSString *)aContainerName 
101
       containersDictionary:(NSMutableDictionary *)aContainersDictionary 
99 102
            resetLocalState:(BOOL)resetLocalState;
100 103

  
101 104
- (void)resetDaemon;
b/pithos-macos/PithosSyncDaemon.m
43 43
#import "ASINetworkQueue.h"
44 44
#import "ASIPithosRequest.h"
45 45
#import "ASIPithos.h"
46
#import "ASIPithosContainer.h"
46 47
#import "ASIPithosContainerRequest.h"
47 48
#import "ASIPithosObjectRequest.h"
48 49
#import "ASIPithosObject.h"
49 50

  
50 51
@interface PithosSyncDaemon (Private)
51
- (void)resetLocalState;
52
- (NSString *)pithosStateFilePath;
52
- (void)loadLocalState;
53
- (void)resetLocalStateWithAll:(BOOL)all;
53 54
- (void)saveLocalState;
54 55

  
55
- (BOOL)moveToTempTrashFile:(NSString *)filePath;
56
- (BOOL)moveToTempTrashFile:(NSString *)filePath pithosContainer:(ASIPithosContainer *)pithosContainer;
56 57
- (void)emptyTempTrash;
57 58
- (BOOL)findLocalCopyForObjectWithHash:(NSString *)hash forFile:(NSString *)filePath;
58 59

  
59
- (void)updateLocalStateWithObject:(ASIPithosObject *)object localFilePath:(NSString *)filePath;
60
- (void)updateLocalStateWithObject:(ASIPithosObject *)object 
61
                     localFilePath:(NSString *)filePath 
62
                   pithosContainer:(ASIPithosContainer *)pithosContainer;
60 63
- (void)updateServerStateWithCurrentState:(PithosLocalObjectState *)currentState 
61 64
                                   object:(ASIPithosObject *)object 
62
                            localFilePath:(NSString *)filePath;
65
                            localFilePath:(NSString *)filePath 
66
                          pithosContainer:(ASIPithosContainer *)pithosContainer;
63 67
- (void)listRequestFailed:(ASIPithosContainerRequest *)containerRequest;
64 68
- (void)requestFailed:(ASIPithosRequest *)request;
65 69

  
......
69 73
@end
70 74

  
71 75
@implementation PithosSyncDaemon
72
@synthesize directoryPath, pithosAccount, containerName, pithos;
73
@synthesize blockHash, blockSize, lastModified, lastCompletedSync, remoteObjects, storedLocalObjectStates, currentLocalObjectStates;
74
@synthesize containerDirectoryPath, pithosStateFilePath, tempDownloadsDirPath, tempTrashDirPath;
76
@synthesize directoryPath, pithosAccount, containersDictionary, pithos;
77
@synthesize pithosContainers;
78
@synthesize lastCompletedSync, remoteObjects, previousRemoteObjects, storedLocalObjectStates, currentLocalObjectStates;
79
@synthesize pithosStateFilePath, tempDownloadsDirPath, tempTrashDirPath;
75 80

  
76 81
#pragma mark -
77 82
#pragma Object Lifecycle
78 83

  
79 84
- (id)initWithDirectoryPath:(NSString *)aDirectoryPath 
80 85
              pithosAccount:(PithosAccount *)aPithosAccount 
81
              containerName:(NSString *)aContainerName 
86
       containersDictionary:(NSMutableDictionary *)aContainersDictionary 
82 87
            resetLocalState:(BOOL)resetLocalState {
83 88
    if ((self = [super init])) {
84 89
        directoryPath = [aDirectoryPath copy];
85 90
        pithosAccount = [aPithosAccount retain];
86
        containerName = [aContainerName copy];
91
        containersDictionary = [aContainersDictionary retain];
87 92
        self.pithos = pithosAccount.pithos;
88 93
        
94
        containersCount = [containersDictionary count];
95
        self.pithosContainers = [NSMutableArray arrayWithCapacity:containersCount];
96
        for (NSString *containerName in [containersDictionary allKeys]) {
97
            ASIPithosContainer *pithosContainer = [ASIPithosContainer container];
98
            pithosContainer.name = containerName;
99
            [pithosContainers addObject:pithosContainer];
100
        }
101
        
89 102
        activityFacility = [PithosActivityFacility defaultPithosActivityFacility];
90 103
        
91 104
        if (resetLocalState)
92
            [self resetLocalState];
105
            [self resetLocalStateWithAll:YES];
106
        else
107
            [self resetLocalStateWithAll:NO];
93 108
        
94 109
        networkQueue = [[ASINetworkQueue alloc] init];
95 110
        networkQueue.showAccurateProgress = YES;
......
105 120
                                                 selector:@selector(applicationWillTerminate:)
106 121
                                                     name:NSApplicationWillTerminateNotification
107 122
                                                   object:[NSApplication sharedApplication]];
108
        
109
        [self startDaemon];
110 123
    }
111 124
    return self;
112 125
}
113 126

  
114
- (void)resetLocalState {
127
- (void)loadLocalState {
128
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
129
    if ([[NSFileManager defaultManager] fileExistsAtPath:self.pithosStateFilePath])
130
        self.storedLocalObjectStates = [NSKeyedUnarchiver unarchiveObjectWithFile:self.pithosStateFilePath];
131
    else
132
        self.storedLocalObjectStates = [NSMutableDictionary dictionary];
133
    if (!storedLocalObjectStates)
134
        self.storedLocalObjectStates = [NSMutableDictionary dictionary];
135
    for (ASIPithosContainer *pithosContainer in pithosContainers) {
136
        if (![storedLocalObjectStates objectForKey:pithosContainer.name]) {
137
            [storedLocalObjectStates setObject:[NSMutableDictionary dictionary] forKey:pithosContainer.name];
138
        }
139
    }
140
    [pool drain];
141
}
142

  
143
- (void)resetLocalStateWithAll:(BOOL)all {
144
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
115 145
    self.lastCompletedSync = nil;
116 146
    NSFileManager *fileManager = [NSFileManager defaultManager];
117
    NSError *error = nil;
118
    if ([fileManager fileExistsAtPath:self.pithosStateFilePath] && 
119
        (![fileManager removeItemAtPath:self.pithosStateFilePath error:&error] || error))
120
        [PithosUtilities fileActionFailedAlertWithTitle:@"Remove File Error" 
121
                                                message:[NSString stringWithFormat:@"Cannot remove file at '%@'", self.pithosStateFilePath] 
122
                                                  error:error];
123
    if (self.tempDownloadsDirPath) {
124
        error = nil;
125
        for (NSString *subPath in [fileManager contentsOfDirectoryAtPath:self.tempDownloadsDirPath error:&error]) {
126
            if (error) {
127
                [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" 
128
                                                        message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", self.tempDownloadsDirPath] 
129
                                                          error:error];
130
                break;
147
    NSError *error;
148
    if (all) {
149
        self.storedLocalObjectStates = [NSMutableDictionary dictionary];
150
        [self saveLocalState]; // Save an empty dictionary
151
        [self loadLocalState]; // Load to populate with containers
152
        [self saveLocalState]; // Save again
153
        if (self.tempDownloadsDirPath) {
154
            error = nil;
155
            for (NSString *subPath in [fileManager contentsOfDirectoryAtPath:self.tempDownloadsDirPath error:&error]) {
156
                if (error) {
157
                    [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" 
158
                                                            message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", self.tempDownloadsDirPath] 
159
                                                              error:error];
160
                    break;
161
                }
162
                NSString *subFilePath = [self.tempDownloadsDirPath stringByAppendingPathComponent:subPath];
163
                if (![fileManager removeItemAtPath:subFilePath error:&error] || error) {
164
                    [PithosUtilities fileActionFailedAlertWithTitle:@"Remove File Error" 
165
                                                            message:[NSString stringWithFormat:@"Cannot remove file at '%@'", subFilePath] 
166
                                                              error:error];
167
                }
168
                error = nil;
131 169
            }
132
            NSString *subFilePath = [self.tempDownloadsDirPath stringByAppendingPathComponent:subPath];
133
            if (![fileManager removeItemAtPath:subFilePath error:&error] || error) {
134
                [PithosUtilities fileActionFailedAlertWithTitle:@"Remove File Error" 
135
                                                        message:[NSString stringWithFormat:@"Cannot remove file at '%@'", subFilePath] 
136
                                                          error:error];
170
        }
171
    } else {
172
        // Remove containers that don't interest us anymore and save
173
        if (!storedLocalObjectStates)
174
            [self loadLocalState];
175
        for (NSString *containerName in [storedLocalObjectStates allKeys]) {
176
            if (![containersDictionary objectForKey:containerName]) {
177
                [storedLocalObjectStates removeObjectForKey:containerName];
178
                if (self.tempDownloadsDirPath) {
179
                    NSString *containerTempDownloadsDirPath = [self.tempDownloadsDirPath stringByAppendingPathComponent:containerName];
180
                    BOOL isDirectory;
181
                    BOOL fileExists = [fileManager fileExistsAtPath:containerTempDownloadsDirPath isDirectory:&isDirectory];
182
                    if (fileExists && isDirectory) {
183
                        error = nil;
184
                        for (NSString *subPath in [fileManager contentsOfDirectoryAtPath:containerTempDownloadsDirPath error:&error]) {
185
                            if (error) {
186
                                [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" 
187
                                                                        message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", 
188
                                                                                 containerTempDownloadsDirPath] 
189
                                                                          error:error];
190
                                break;
191
                            }
192
                            NSString *subFilePath = [containerTempDownloadsDirPath stringByAppendingPathComponent:subPath];
193
                            if (![fileManager removeItemAtPath:subFilePath error:&error] || error) {
194
                                [PithosUtilities fileActionFailedAlertWithTitle:@"Remove File Error" 
195
                                                                        message:[NSString stringWithFormat:@"Cannot remove file at '%@'", 
196
                                                                                 subFilePath] 
197
                                                                          error:error];
198
                            }
199
                            error = nil;
200
                        }
201
                    } else if (fileExists && !isDirectory) {
202
                        error = nil;
203
                        if (![fileManager removeItemAtPath:containerTempDownloadsDirPath error:&error] || error) {
204
                            [PithosUtilities fileActionFailedAlertWithTitle:@"Remove File Error" 
205
                                                                    message:[NSString stringWithFormat:@"Cannot remove file at '%@'", 
206
                                                                             containerTempDownloadsDirPath] 
207
                                                                      error:error];
208
                        }
209
                    }
210
                }
137 211
            }
138
            error = nil;
139 212
        }
213
        [self saveLocalState];
140 214
    }
215
    [pool drain];
216
}
217

  
218
- (void)saveLocalState {
219
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
220
    [NSKeyedArchiver archiveRootObject:storedLocalObjectStates toFile:self.pithosStateFilePath];
221
    [pool drain];
141 222
}
142 223

  
143 224
- (void)resetDaemon {
......
151 232
    [callbackQueue setSuspended:YES];
152 233
    [self emptyTempTrash];
153 234
    
235
    syncOperationCount = 0;
236
    
154 237
    @synchronized(self) {            
155 238
        daemonActive = NO;
156 239
    }
......
161 244
        if (daemonActive)
162 245
            return;
163 246
    }
247

  
248
    // In the improbable case of leftover operations
249
    [networkQueue reset];
250
    [callbackQueue cancelAllOperations];
164 251
    
165 252
    syncOperationCount = 0;
166 253
    newSyncRequested = NO;
167 254
    syncIncomplete = NO;
168 255
    syncLate = NO;
169

  
170
    self.containerDirectoryPath = [directoryPath stringByAppendingPathComponent:containerName];
171
            
172
    if ([[NSFileManager defaultManager] fileExistsAtPath:self.pithosStateFilePath]) {
173
        NSData *data = [NSData dataWithContentsOfFile:self.pithosStateFilePath];
174
        NSKeyedUnarchiver *unarchiver = [[[NSKeyedUnarchiver alloc] initForReadingWithData:data] autorelease];
175
        self.storedLocalObjectStates = [unarchiver decodeObjectForKey:containerName];
176
        [unarchiver finishDecoding];
177
    } else {
178
        self.storedLocalObjectStates = [NSMutableDictionary dictionary];
179
    }
180

  
181
    // In the improbable case of leftover operations
182
    [networkQueue reset];
183
    [callbackQueue cancelAllOperations];
256
    
257
    [self loadLocalState];
184 258
    
185 259
    [networkQueue go];
186 260
    [callbackQueue setSuspended:NO];
......
198 272
    [tempTrashDirPath release];
199 273
    [tempDownloadsDirPath release];
200 274
    [pithosStateFilePath release];
201
    [containerDirectoryPath release];
202 275
    [currentLocalObjectStates release];
203 276
    [storedLocalObjectStates release];
277
    [previousRemoteObjects release];
204 278
    [remoteObjects release];
205 279
    [objects release];
206 280
    [lastCompletedSync release];
207
    [lastModified release];
208
    [blockHash release];
281
    [pithosContainers release];
209 282
    [pithos release];
210
    [containerName release];
283
    [containersDictionary release];
211 284
    [pithosAccount release];
212 285
    [directoryPath release];
213 286
    [super dealloc];
......
224 297
#pragma mark Properties
225 298

  
226 299
- (NSString *)pithosStateFilePath {
227
    if (!pithosStateFilePath)
300
    if (!pithosStateFilePath) {
228 301
        pithosStateFilePath = [[[[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0] 
229 302
                                 stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]] 
230 303
                                stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-PithosLocalObjectStates.archive", 
231 304
                                                                pithosAccount.uniqueName]] retain];
305
        NSString *pithosStateFileDirPath = [pithosStateFilePath stringByDeletingLastPathComponent];
306
        NSFileManager *fileManager = [NSFileManager defaultManager];
307
        BOOL isDirectory;
308
        BOOL fileExists = [fileManager fileExistsAtPath:pithosStateFileDirPath isDirectory:&isDirectory];
309
        NSError *error = nil;
310
        if (fileExists && !isDirectory)
311
            [fileManager removeItemAtPath:pithosStateFileDirPath error:&error];
312
        if (!error && !fileExists)
313
            [fileManager createDirectoryAtPath:pithosStateFileDirPath withIntermediateDirectories:YES attributes:nil error:&error];
314
        //if (error)
315
        //  pithosStateFilePath = nil;
316
        // XXX create a dir using mktmps?
317
    }
232 318
    return [[pithosStateFilePath copy] autorelease];
233 319
}
234 320

  
......
244 330
        NSError *error = nil;
245 331
        if (fileExists && !isDirectory)
246 332
            [fileManager removeItemAtPath:tempDownloadsDirPath error:&error];
247
        if (!error & !fileExists)
333
        if (!error && !fileExists)
248 334
            [fileManager createDirectoryAtPath:tempDownloadsDirPath withIntermediateDirectories:YES attributes:nil error:&error];
249 335
        //if (error)
250 336
        //    tempDownloadsDirPath = nil;
......
265 351
        NSError *error = nil;
266 352
        if (fileExists && !isDirectory)
267 353
            [fileManager removeItemAtPath:tempTrashDirPath error:&error];
268
        if (!error & !fileExists)
354
        if (!error && !fileExists)
269 355
            [fileManager createDirectoryAtPath:tempTrashDirPath withIntermediateDirectories:YES attributes:nil error:&error];
270 356
        //if (error)
271 357
        //    tempTrashDirPath = nil;
......
277 363
- (void)setDirectoryPath:(NSString *)aDirectoryPath {
278 364
    if (aDirectoryPath && ![aDirectoryPath isEqualToString:directoryPath]) {
279 365
        [self resetDaemon];
280
        [self resetLocalState];
366
        [self resetLocalStateWithAll:YES];
281 367
        [directoryPath release];
282 368
        directoryPath = [aDirectoryPath copy];
283 369
    }
284 370
}
285 371

  
286
- (void)setContainerName:(NSString *)aContainerName {
287
    if (aContainerName && ![aContainerName isEqualToString:containerName]) {
372
- (void)setContainersDictionary:(NSDictionary *)aContainersDictionary {
373
    if (aContainersDictionary && ![aContainersDictionary isEqualToDictionary:containersDictionary]) {
288 374
        [self resetDaemon];
289
        [self resetLocalState];
290
        [containerName release];
291
        containerName = [aContainerName copy];
375
        [containersDictionary release];
376
        containersDictionary = [aContainersDictionary retain];
377
        containersCount = [containersDictionary count];
378
        self.pithosContainers = [NSMutableArray arrayWithCapacity:containersCount];
379
        for (NSString *containerName in [containersDictionary allKeys]) {
380
            ASIPithosContainer *pithosContainer = [ASIPithosContainer container];
381
            pithosContainer.name = containerName;
382
            [pithosContainers addObject:pithosContainer];
383
        }
384
        [self resetLocalStateWithAll:NO];
292 385
    }    
293 386
}
294 387

  
......
308 401
        [self resetDaemon];
309 402
        if (![aPithos.authUser isEqualToString:pithos.authUser] || 
310 403
            ![aPithos.storageURLPrefix isEqualToString:pithos.storageURLPrefix])
311
            [self resetLocalState];
404
            [self resetLocalStateWithAll:YES];
312 405
        pithos.authUser = [[aPithos.authUser copy] autorelease];
313 406
        pithos.authToken = [[aPithos.authToken copy] autorelease];
314 407
        pithos.storageURLPrefix = [[aPithos.storageURLPrefix copy] autorelease];
......
320 413
#pragma mark -
321 414
#pragma mark Sync
322 415

  
323
- (void)saveLocalState {
324
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
325
    NSMutableData *data = [NSMutableData data];
326
    NSKeyedArchiver *archiver = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease];
327
    [archiver encodeObject:storedLocalObjectStates forKey:containerName];
328
    [archiver finishEncoding];
329
    [data writeToFile:self.pithosStateFilePath atomically:YES];
330
    [pool drain];
331
}
332

  
333 416
- (void)syncOperationStarted {
334 417
    @synchronized(self) {
335 418
        syncOperationCount++;
......
340 423
    @synchronized(self) {
341 424
        if (!operationSuccessfull)
342 425
            syncIncomplete = YES;
343
        if (syncOperationCount == 0)
426
        if (syncOperationCount == 0) {
344 427
            // XXX This shouldn't happen, maybe change operationCount to a BOOL as operationsPending in browser
428
            NSLog(@"Sync::WARNING: tried to decrease syncOperationCount when 0");
345 429
            return;
430
        }
346 431
        syncOperationCount--;
347 432
        if (syncOperationCount == 0) {
348 433
            if (!syncIncomplete) {
......
380 465
            // If at least one operation is running return
381 466
            newSyncRequested = YES;
382 467
            return;
383
        } else if (daemonActive) {
468
        } else if (daemonActive && containersCount) {
384 469
            // The first operation is the server listing
385 470
            [self syncOperationStarted];
386 471
            newSyncRequested = NO;
......
394 479
    NSFileManager *fileManager = [NSFileManager defaultManager];
395 480
    BOOL isDirectory;
396 481
    NSError *error = nil;
397
    if (![fileManager fileExistsAtPath:containerDirectoryPath isDirectory:&isDirectory]) {
398
        if (![fileManager createDirectoryAtPath:containerDirectoryPath withIntermediateDirectories:YES attributes:nil error:&error] || 
482
    if (![fileManager fileExistsAtPath:directoryPath isDirectory:&isDirectory]) {
483
        if (![fileManager createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error] || 
399 484
            error) {
400 485
            [PithosUtilities fileActionFailedAlertWithTitle:@"Local Sync Directory Error" 
401
                                                    message:[NSString stringWithFormat:@"Cannot create local sync directory at '%@'", containerDirectoryPath] 
486
                                                    message:[NSString stringWithFormat:@"Cannot create local sync directory at '%@'", directoryPath] 
402 487
                                                      error:error];
403 488
            [self syncOperationFinishedWithSuccess:NO];
404 489
            return;
405 490
        }
406 491
    } else if (!isDirectory) {
407 492
        [PithosUtilities fileActionFailedAlertWithTitle:@"Local Sync Directory Error" 
408
                                                message:[NSString stringWithFormat:@"File already exists at the local sync directory path at '%@'", containerDirectoryPath] 
493
                                                message:[NSString stringWithFormat:@"File already exists at the local sync directory path at '%@'", directoryPath] 
409 494
                                                  error:nil];
410 495
        [self syncOperationFinishedWithSuccess:NO];
411 496
        return;
412 497
    }
413
    
498
    NSString *containerDirectoryPath;
499
    for (ASIPithosContainer *pithosContainer in pithosContainers) {
500
        containerDirectoryPath = [directoryPath stringByAppendingPathComponent:pithosContainer.name];
501
        error = nil;
502
        if (![fileManager fileExistsAtPath:containerDirectoryPath isDirectory:&isDirectory]) {
503
            if (![fileManager createDirectoryAtPath:containerDirectoryPath withIntermediateDirectories:YES attributes:nil error:&error] || 
504
                error) {
505
                [PithosUtilities fileActionFailedAlertWithTitle:@"Local Sync Directory Error" 
506
                                                        message:[NSString stringWithFormat:@"Cannot create local sync directory at '%@'", 
507
                                                                 containerDirectoryPath] 
508
                                                          error:error];
509
                [self syncOperationFinishedWithSuccess:NO];
510
                return;
511
            }
512
        } else if (!isDirectory) {
513
            [PithosUtilities fileActionFailedAlertWithTitle:@"Local Sync Directory Error" 
514
                                                    message:[NSString stringWithFormat:@"File already exists at the local sync directory path at '%@'", 
515
                                                             containerDirectoryPath] 
516
                                                      error:nil];
517
            [self syncOperationFinishedWithSuccess:NO];
518
            return;
519
        }
520
    }
521
    containersIndex = 0;
522
    self.remoteObjects = [NSMutableDictionary dictionaryWithCapacity:containersCount];
414 523
    ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest listObjectsRequestWithPithos:pithos 
415
                                                                                            containerName:containerName 
524
                                                                                            containerName:[[pithosContainers objectAtIndex:containersIndex] name] 
416 525
                                                                                                    limit:0 
417 526
                                                                                                   marker:nil 
418 527
                                                                                                   prefix:nil 
......
421 530
                                                                                                     meta:nil 
422 531
                                                                                                   shared:NO 
423 532
                                                                                                    until:nil 
424
                                                                                          ifModifiedSince:lastModified];
533
                                                                                          ifModifiedSince:[[pithosContainers objectAtIndex:containersIndex] lastModified]];
425 534
    containerRequest.delegate = self;
426 535
    containerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
427 536
    containerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
......
452 561
        if (error) {
453 562
            dispatch_async(dispatch_get_main_queue(), ^{
454 563
                [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" 
455
                                                        message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", containerDirectoryPath] 
564
                                                        message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", trashDirPath] 
456 565
                                                          error:error];
457 566
            });
458 567
            [pool drain];
......
489 598
    [pool drain];
490 599
}
491 600

  
492
- (BOOL)moveToTempTrashFile:(NSString *)filePath {
601
- (BOOL)moveToTempTrashFile:(NSString *)filePath pithosContainer:(ASIPithosContainer *)pithosContainer {
493 602
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
494
    NSString *trashDirPath = self.tempTrashDirPath;
495
    if (!tempTrashDirPath) {
603
    if (!self.tempTrashDirPath) {
496 604
        [pool drain];
497 605
        return NO;
498 606
    }
......
500 608
    BOOL isDirectory;
501 609
    BOOL fileExists = [fileManager fileExistsAtPath:filePath isDirectory:&isDirectory];
502 610
    NSError *error = nil;
503
    NSString *newFilePath = [filePath stringByReplacingOccurrencesOfString:containerDirectoryPath 
504
                                                                withString:trashDirPath];
611
    NSString *containerDirectoryPath = [directoryPath stringByAppendingPathComponent:pithosContainer.name];
612
    NSString *newFilePath = [filePath stringByReplacingOccurrencesOfString:containerDirectoryPath withString:self.tempTrashDirPath];
505 613
    NSString *newDirPath = [newFilePath stringByDeletingLastPathComponent];
506 614
    if (fileExists && isDirectory) {
507 615
        NSArray *subPaths = [fileManager subpathsOfDirectoryAtPath:filePath error:&error];
508 616
        if (error) {
509 617
            dispatch_async(dispatch_get_main_queue(), ^{
510 618
                [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" 
511
                                                        message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", containerDirectoryPath] 
619
                                                        message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", filePath] 
512 620
                                                          error:error];
513 621
            });
514 622
            [pool drain];
......
540 648
            [currentLocalObjectStates removeObjectForKey:filePath];        
541 649
        } else {
542 650
            [currentLocalObjectStates setObject:[PithosLocalObjectState localObjectStateWithFile:newFilePath 
543
                                                                                       blockHash:blockHash 
544
                                                                                       blockSize:blockSize] 
651
                                                                                       blockHash:pithosContainer.blockHash 
652
                                                                                       blockSize:pithosContainer.blockSize] 
545 653
                                         forKey:newFilePath];
546 654
        }
547 655
        for (NSString *subPath in subPaths) {
548 656
            NSString *subFilePath = [filePath stringByAppendingPathComponent:subPath];
549 657
            NSString *newSubFilePath = [subFilePath stringByReplacingOccurrencesOfString:containerDirectoryPath 
550
                                                                              withString:trashDirPath];
658
                                                                              withString:self.tempTrashDirPath];
551 659
            currentState = [currentLocalObjectStates objectForKey:subFilePath];
552 660
            if (currentState) {
553 661
                currentState.filePath = newSubFilePath;
......
555 663
                [currentLocalObjectStates removeObjectForKey:subFilePath];
556 664
            } else {
557 665
                [currentLocalObjectStates setObject:[PithosLocalObjectState localObjectStateWithFile:newSubFilePath 
558
                                                                                           blockHash:blockHash 
559
                                                                                           blockSize:blockSize] 
666
                                                                                           blockHash:pithosContainer.blockHash 
667
                                                                                           blockSize:pithosContainer.blockSize] 
560 668
                                             forKey:newSubFilePath];
561 669
            }        
562 670
        }
......
587 695
            [currentLocalObjectStates removeObjectForKey:filePath];        
588 696
        } else {
589 697
            [currentLocalObjectStates setObject:[PithosLocalObjectState localObjectStateWithFile:newFilePath 
590
                                                                                       blockHash:blockHash 
591
                                                                                       blockSize:blockSize] 
698
                                                                                       blockHash:pithosContainer.blockHash 
699
                                                                                       blockSize:pithosContainer.blockSize] 
592 700
                                         forKey:newFilePath];
593 701
        }
594 702
    }
......
608 716
        localState = [currentLocalObjectStates objectForKey:localFilePath];
609 717
        if (!localState.isDirectory && [hash isEqualToString:localState.hash] && 
610 718
            [fileManager fileExistsAtPath:localFilePath isDirectory:&isDirectory] && !isDirectory) {
611
            if ([localFilePath hasPrefix:containerDirectoryPath]) {
719
            if ([localFilePath hasPrefix:directoryPath]) {
612 720
                if (![fileManager copyItemAtPath:localFilePath toPath:filePath error:&error] || error) {
613 721
                    dispatch_async(dispatch_get_main_queue(), ^{
614 722
                        [PithosUtilities fileActionFailedAlertWithTitle:@"Copy File Error" 
......
619 727
                } else {
620 728
                    [pool drain];
621 729
                    return YES;
622
                }            
730
                }
623 731
            } else if (self.tempTrashDirPath && [localFilePath hasPrefix:self.tempTrashDirPath]) {
624 732
                if (![fileManager moveItemAtPath:localFilePath toPath:filePath error:&error] || error) {
625 733
                    dispatch_async(dispatch_get_main_queue(), ^{
......
643 751
}
644 752

  
645 753
- (void)updateLocalStateWithObject:(ASIPithosObject *)object
646
                     localFilePath:(NSString *)filePath {
754
                     localFilePath:(NSString *)filePath 
755
                   pithosContainer:(ASIPithosContainer *)pithosContainer {
647 756
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
648 757
    NSFileManager *fileManager = [NSFileManager defaultManager];
649 758
    NSError *error;
650 759
    BOOL isDirectory;
651 760
    BOOL fileExists = [fileManager fileExistsAtPath:filePath isDirectory:&isDirectory];
652 761
    NSString *fileDirectoryPath = [filePath stringByDeletingLastPathComponent];
653
    PithosLocalObjectState *storedState = [storedLocalObjectStates objectForKey:object.name];
762
    NSMutableDictionary *containerStoredLocalObjectStates = [storedLocalObjectStates objectForKey:pithosContainer.name];
763
    PithosLocalObjectState *storedState = [containerStoredLocalObjectStates objectForKey:object.name];
654 764
    // Remote updated info
655 765
    NSError *remoteError;
656 766
    BOOL remoteIsDirectory;
657 767
    BOOL remoteObjectExists = [PithosUtilities objectExistsAtPithos:pithos 
658
                                                      containerName:containerName 
768
                                                      containerName:pithosContainer.name 
659 769
                                                         objectName:object.name 
660 770
                                                              error:&remoteError 
661 771
                                                        isDirectory:&remoteIsDirectory 
......
667 777
            syncIncomplete = YES;
668 778
        }
669 779
        NSLog(@"Sync::delete local object: %@", filePath);
670
        if (!fileExists || [self moveToTempTrashFile:filePath]) {
780
        if (!fileExists || [self moveToTempTrashFile:filePath pithosContainer:pithosContainer]) {
671 781
            dispatch_async(dispatch_get_main_queue(), ^{
672 782
                [activityFacility startAndEndActivityWithType:PithosActivityOther 
673
                                                      message:[NSString stringWithFormat:@"Sync: Deleting '%@' locally (finished)", object.name] 
783
                                                      message:[NSString stringWithFormat:@"Sync: Deleting '%@/%@' locally (finished)", 
784
                                                               pithosContainer.name, object.name] 
674 785
                                                pithosAccount:pithosAccount];
675 786
            });
676
            [storedLocalObjectStates removeObjectForKey:object.name];
787
            [containerStoredLocalObjectStates removeObjectForKey:object.name];
677 788
            [self saveLocalState];
678 789
        }
679 790
    } else if ([PithosUtilities isContentTypeDirectory:object.contentType]) {
......
686 797
        }
687 798
        NSLog(@"Sync::create local directory object: %@", filePath);
688 799
        BOOL directoryCreated = NO;
689
        if (!fileExists || (!isDirectory && [self moveToTempTrashFile:filePath])) {
800
        if (!fileExists || (!isDirectory && [self moveToTempTrashFile:filePath pithosContainer:pithosContainer])) {
690 801
            NSLog(@"Sync::local directory object doesn't exist: %@", filePath);
691 802
            error = nil;
692 803
            if (![fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:&error] || error) {
......
708 819
        if (directoryCreated)
709 820
            dispatch_async(dispatch_get_main_queue(), ^{
710 821
                [activityFacility startAndEndActivityWithType:PithosActivityOther 
711
                                                      message:[NSString stringWithFormat:@"Sync: Creating directory '%@' locally (finished)", object.name] 
822
                                                      message:[NSString stringWithFormat:@"Sync: Creating directory '%@/%@' locally (finished)", 
823
                                                               pithosContainer.name, object.name] 
712 824
                                                pithosAccount:pithosAccount];
713 825
            });
714 826
    } else if (object.bytes == 0) {
......
721 833
        }
722 834
        NSLog(@"Sync::create local zero length object: %@", filePath);
723 835
        BOOL fileCreated = NO;
724
        if (!fileExists || ((isDirectory || [PithosUtilities bytesOfFile:filePath]) && [self moveToTempTrashFile:filePath])) {
836
        if (!fileExists || ((isDirectory || [PithosUtilities bytesOfFile:filePath]) && [self moveToTempTrashFile:filePath pithosContainer:pithosContainer])) {
725 837
            NSLog(@"Sync::local zero length object doesn't exist: %@", filePath);
726 838
            // Create directory of the file, if it doesn't exist
727 839
            error = nil;
......
753 865
        if (fileCreated)
754 866
            dispatch_async(dispatch_get_main_queue(), ^{
755 867
                [activityFacility startAndEndActivityWithType:PithosActivityOther 
756
                                                      message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name] 
868
                                                      message:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (100%%)", 
869
                                                               pithosContainer.name, object.name] 
757 870
                                                pithosAccount:pithosAccount];
758 871
            });
759 872
    } else if (storedState.tmpFilePath == nil) {
......
780 893
            [self saveLocalState];
781 894
            dispatch_async(dispatch_get_main_queue(), ^{
782 895
                [activityFacility startAndEndActivityWithType:PithosActivityOther 
783
                                                      message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name] 
896
                                                      message:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (100%%)", 
897
                                                               pithosContainer.name, object.name] 
784 898
                                                pithosAccount:pithosAccount];
785 899
            });
786 900
        } else {
787 901
            [self syncOperationStarted];
788 902
            __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectBlockDataRequestWithPithos:pithos 
789
                                                                                                containerName:containerName 
903
                                                                                                containerName:pithosContainer.name 
790 904
                                                                                                       object:object 
791 905
                                                                                                   blockIndex:0 
792
                                                                                                    blockSize:blockSize];
906
                                                                                                    blockSize:pithosContainer.blockSize];
793 907
            objectRequest.delegate = self;
794 908
            objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
795 909
            objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
796 910
            PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload 
797
                                                                       message:[NSString stringWithFormat:@"Sync: Downloading '%@' (0%%)", object.name] 
911
                                                                       message:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (0%%)", 
912
                                                                                pithosContainer.name, object.name] 
798 913
                                                                    totalBytes:object.bytes 
799 914
                                                                  currentBytes:0 
800 915
                                                                 pithosAccount:pithosAccount];
......
802 917
                [activityFacility updateActivity:activity withMessage:activity.message];
803 918
            });
804 919
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
920
                                      pithosContainer, @"pithosContainer", 
805 921
                                      object, @"pithosObject", 
806
                                      [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, (NSUInteger)ceil((object.bytes +0.0)/(blockSize + 0.0)))], @"missingBlocks", 
922
                                      [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, (NSUInteger)ceil((object.bytes +0.0)/(pithosContainer.blockSize + 0.0)))], @"missingBlocks", 
807 923
                                      [NSNumber numberWithUnsignedInteger:0], @"missingBlockIndex", 
808 924
                                      filePath, @"filePath", 
809 925
                                      activity, @"activity", 
810
                                      [NSString stringWithFormat:@"Sync: Downloading '%@' (stopped)", object.name], @"stoppedActivityMessage", 
811
                                      [NSString stringWithFormat:@"Sync: Downloading '%@' (failed)", object.name], @"failedActivityMessage", 
812
                                      [NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name], @"finishedActivityMessage", 
926
                                      [NSString stringWithFormat:@"Sync: Downloading '%@/%@' (stopped)", pithosContainer.name, object.name], @"stoppedActivityMessage", 
927
                                      [NSString stringWithFormat:@"Sync: Downloading '%@/%@' (failed)", pithosContainer.name, object.name], @"failedActivityMessage", 
928
                                      [NSString stringWithFormat:@"Sync: Downloading '%@/%@' (100%%)", pithosContainer.name, object.name], @"finishedActivityMessage", 
813 929
                                      [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
814 930
                                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
815 931
                                      NSStringFromSelector(@selector(downloadObjectBlockFinished:)), @"didFinishSelector", 
......
817 933
                                      nil];
818 934
            [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
819 935
                [activityFacility updateActivity:activity 
820
                                     withMessage:[NSString stringWithFormat:@"Sync: Downloading '%@' (%.0f%%)", 
936
                                     withMessage:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (%.0f%%)", 
937
                                                  objectRequest.containerName, 
821 938
                                                  [[objectRequest.userInfo objectForKey:@"pithosObject"] name], 
822 939
                                                  (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))] 
823 940
                                      totalBytes:activity.totalBytes 
......
851 968
            [self saveLocalState];
852 969
            dispatch_async(dispatch_get_main_queue(), ^{
853 970
                [activityFacility startAndEndActivityWithType:PithosActivityOther 
854
                                                      message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name] 
971
                                                      message:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (100%%)", 
972
                                                               pithosContainer.name, object.name] 
855 973
                                                pithosAccount:pithosAccount];
856 974
            });
857 975
        } else {
858 976
            [self syncOperationStarted];
859 977
            ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectHashmapRequestWithPithos:pithos 
860
                                                                                             containerName:containerName 
978
                                                                                             containerName:pithosContainer.name 
861 979
                                                                                                objectName:object.name];
862 980
            objectRequest.delegate = self;
863 981
            objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
864 982
            objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
865 983
            PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload 
866
                                                                       message:[NSString stringWithFormat:@"Sync: Downloading '%@' (0%%)", object.name] 
984
                                                                       message:[NSString stringWithFormat:@"Sync: Downloading '%@/%@' (0%%)", 
985
                                                                                pithosContainer.name, object.name] 
867 986
                                                                    totalBytes:object.bytes 
868 987
                                                                  currentBytes:0 
869 988
                                                                 pithosAccount:pithosAccount];
......
871 990
                [activityFacility updateActivity:activity withMessage:activity.message];
872 991
            });
873 992
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
993
                                      pithosContainer, @"pithosContainer", 
874 994
                                      object, @"pithosObject", 
875 995
                                      filePath, @"filePath", 
876 996
                                      activity, @"activity", 
877
                                      [NSString stringWithFormat:@"Sync: Downloading '%@' (stopped)", object.name], @"stoppedActivityMessage", 
878
                                      [NSString stringWithFormat:@"Sync: Downloading '%@' (failed)", object.name], @"failedActivityMessage", 
879
                                      [NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name], @"finishedActivityMessage", 
997
                                      [NSString stringWithFormat:@"Sync: Downloading '%@/%@' (stopped)", pithosContainer.name, object.name], @"stoppedActivityMessage", 
998
                                      [NSString stringWithFormat:@"Sync: Downloading '%@/%@' (failed)", pithosContainer.name, object.name], @"failedActivityMessage", 
999
                                      [NSString stringWithFormat:@"Sync: Downloading '%@/%@' (100%%)", pithosContainer.name, object.name], @"finishedActivityMessage", 
880 1000
                                      [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
881 1001
                                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
882 1002
                                      NSStringFromSelector(@selector(downloadObjectHashMapFinished:)), @"didFinishSelector", 
......
890 1010

  
891 1011
-(void)updateServerStateWithCurrentState:(PithosLocalObjectState *)currentState 
892 1012
                                  object:(ASIPithosObject *)object 
893
                           localFilePath:(NSString *)filePath {
1013
                           localFilePath:(NSString *)filePath 
1014
                         pithosContainer:(ASIPithosContainer *)pithosContainer {
894 1015
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
895 1016
    [self syncOperationStarted];
896 1017
    NSFileManager *fileManager = [NSFileManager defaultManager];
......
905 1026
            return;
906 1027
        }
907 1028
        ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos 
908
                                                                                           containerName:containerName 
1029
                                                                                           containerName:pithosContainer.name 
909 1030
                                                                                              objectName:object.name 
910 1031
                                                                                                    eTag:nil 
911 1032
                                                                                             contentType:@"application/directory" 
......
920 1041
        objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
921 1042
        objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
922 1043
        PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory 
923
                                                                   message:[NSString stringWithFormat:@"Sync: Creating directory '%@'", object.name] 
1044
                                                                   message:[NSString stringWithFormat:@"Sync: Creating directory '%@/%@'", 
1045
                                                                            pithosContainer.name, object.name] 
924 1046
                                                             pithosAccount:pithosAccount];
925 1047
        dispatch_async(dispatch_get_main_queue(), ^{
926 1048
            [activityFacility updateActivity:activity withMessage:activity.message];
927 1049
        });
928 1050
        objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1051
                                  pithosContainer, @"pithosContainer", 
929 1052
                                  object, @"pithosObject", 
930 1053
                                  activity, @"activity", 
931
                                  [NSString stringWithFormat:@"Sync: Creating directory '%@' (stopped)", object.name], @"stoppedActivityMessage", 
932
                                  [NSString stringWithFormat:@"Sync: Creating directory '%@' (failed)", object.name], @"failedActivityMessage", 
933
                                  [NSString stringWithFormat:@"Sync: Creating directory '%@' (finished)", object.name], @"finishedActivityMessage", 
1054
                                  [NSString stringWithFormat:@"Sync: Creating directory '%@/%@' (stopped)", pithosContainer.name, object.name], @"stoppedActivityMessage", 
1055
                                  [NSString stringWithFormat:@"Sync: Creating directory '%@/%@' (failed)", pithosContainer.name, object.name], @"failedActivityMessage", 
1056
                                  [NSString stringWithFormat:@"Sync: Creating directory '%@/%@' (finished)", pithosContainer.name, object.name], @"finishedActivityMessage", 
934 1057
                                  [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
935 1058
                                  [NSNumber numberWithUnsignedInteger:10], @"retries", 
936 1059
                                  NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", 
......
943 1066
            // Local object created in the meantime, just mark the sync cycle as incomplete, but do delete the server object
944 1067
            syncIncomplete = YES;
945 1068
        }
946
        NSString *safeName;
947
        if ([PithosUtilities isContentTypeDirectory:object.contentType])
948
            safeName = [PithosUtilities safeSubdirNameForPithos:pithos containerName:@"trash" subdirName:object.name];
949
        else
950
            safeName = [PithosUtilities safeObjectNameForPithos:pithos containerName:@"trash" objectName:object.name];
951
        if (safeName) {
952
            ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos 
953
                                                                                   containerName:containerName 
954
                                                                                      objectName:object.name 
955
                                                                        destinationContainerName:@"trash" 
956
                                                                           destinationObjectName:safeName 
957
                                                                                   checkIfExists:NO];
958
            if (objectRequest) {
959
                objectRequest.delegate = self;
960
                objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
961
                objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
962
                PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete 
963
                                                                           message:[NSString stringWithFormat:@"Sync: Moving to trash '%@'", object.name] 
964
                                                                     pithosAccount:pithosAccount];
965
                dispatch_async(dispatch_get_main_queue(), ^{
966
                    [activityFacility updateActivity:activity withMessage:activity.message];
967
                });
968
                objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
969
                                          object, @"pithosObject", 
970
                                          activity, @"activity", 
971
                                          [NSString stringWithFormat:@"Sync: Moving to trash '%@' (stopped)", object.name], @"stoppedActivityMessage", 
972
                                          [NSString stringWithFormat:@"Sync: Moving to trash '%@' (failed)", object.name], @"failedActivityMessage", 
973
                                          [NSString stringWithFormat:@"Sync: Moving to trash '%@' (finished)", object.name], @"finishedActivityMessage", 
974
                                          [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
975
                                          [NSNumber numberWithUnsignedInteger:10], @"retries", 
976
                                          NSStringFromSelector(@selector(moveObjectToTrashFinished:)), @"didFinishSelector", 
977
                                          NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
978
                                          nil];
979
                [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
1069
        if ([pithosContainer.name isEqualToString:@"trash"]) {
1070
            // Delete
1071
            ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos 
1072
                                                                                            containerName:pithosContainer.name 
1073
                                                                                               objectName:object.name];
1074
            objectRequest.delegate = self;
1075
            objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1076
            objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
1077
            PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete 
1078
                                                                       message:[NSString stringWithFormat:@"Sync: Deleting '%@/%@'", 
1079
                                                                                pithosContainer.name, object.name] 
1080
                                                                 pithosAccount:pithosAccount];
1081
            dispatch_async(dispatch_get_main_queue(), ^{
1082
                [activityFacility updateActivity:activity withMessage:activity.message];
1083
            });
1084
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1085
                                      pithosContainer, @"pithosContainer", 
1086
                                      object, @"pithosObject", 
1087
                                      activity, @"activity", 
1088
                                      [NSString stringWithFormat:@"Sync: Deleting '%@/%@' (stopped)", pithosContainer.name, object.name], @"stoppedActivityMessage", 
1089
                                      [NSString stringWithFormat:@"Sync: Deleting '%@/%@' (failed)", pithosContainer.name, object.name], @"failedActivityMessage", 
1090
                                      [NSString stringWithFormat:@"Sync: Deleting '%@/%@' (finished)", pithosContainer.name, object.name], @"finishedActivityMessage", 
1091
                                      [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
1092
                                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
1093
                                      NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", 
1094
                                      NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
1095
                                      nil];
1096
            [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
1097
        } else {
1098
            // Move to container trash
1099
            NSString *safeName;
1100
            if ([PithosUtilities isContentTypeDirectory:object.contentType])
1101
                safeName = [PithosUtilities safeSubdirNameForPithos:pithos containerName:@"trash" subdirName:object.name];
1102
            else
1103
                safeName = [PithosUtilities safeObjectNameForPithos:pithos containerName:@"trash" objectName:object.name];
1104
            if (safeName) {
1105
                ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos 
1106
                                                                                       containerName:pithosContainer.name 
1107
                                                                                          objectName:object.name 
1108
                                                                            destinationContainerName:@"trash" 
1109
                                                                               destinationObjectName:safeName 
1110
                                                                                       checkIfExists:NO];
1111
                if (objectRequest) {
1112
                    objectRequest.delegate = self;
1113
                    objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1114
                    objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
1115
                    PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete 
1116
                                                                               message:[NSString stringWithFormat:@"Sync: Moving to trash '%@/%@'", 
1117
                                                                                        pithosContainer.name, object.name] 
1118
                                                                         pithosAccount:pithosAccount];
1119
                    dispatch_async(dispatch_get_main_queue(), ^{
1120
                        [activityFacility updateActivity:activity withMessage:activity.message];
1121
                    });
1122
                    objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1123
                                              pithosContainer, @"pithosContainer", 
1124
                                              object, @"pithosObject", 
1125
                                              activity, @"activity", 
1126
                                              [NSString stringWithFormat:@"Sync: Moving to trash '%@/%@' (stopped)", pithosContainer.name, object.name], @"stoppedActivityMessage", 
1127
                                              [NSString stringWithFormat:@"Sync: Moving to trash '%@/%@' (failed)", pithosContainer.name, object.name], @"failedActivityMessage", 
1128
                                              [NSString stringWithFormat:@"Sync: Moving to trash '%@/%@' (finished)", pithosContainer.name, object.name], @"finishedActivityMessage", 
1129
                                              [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
1130
                                              [NSNumber numberWithUnsignedInteger:10], @"retries", 
1131
                                              NSStringFromSelector(@selector(moveObjectToTrashFinished:)), @"didFinishSelector", 
1132
                                              NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
1133
                                              nil];
1134
                    [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
1135
                } else {
1136
                    [self syncOperationFinishedWithSuccess:NO];
1137
                }
980 1138
            } else {
981 1139
                [self syncOperationFinishedWithSuccess:NO];
982 1140
            }
983
        } else {
984
            [self syncOperationFinishedWithSuccess:NO];
985 1141
        }
986 1142
    } else {
987 1143
        // Upload file to remote object
......
999 1155
            NSLog(@"contentType detection error: %@", error);
1000 1156
        NSArray *hashes = nil;
1001 1157
        ASIPithosObjectRequest *objectRequest = [PithosUtilities writeObjectDataRequestWithPithos:pithos 
1002
                                                                                    containerName:containerName 
1158
                                                                                    containerName:pithosContainer.name 
1003 1159
                                                                                       objectName:object.name 
1004 1160
                                                                                      contentType:object.contentType 
1005
                                                                                        blockSize:blockSize 
1006
                                                                                        blockHash:blockHash 
1161
                                                                                        blockSize:pithosContainer.blockSize 
1162
                                                                                        blockHash:pithosContainer.blockHash 
1007 1163
                                                                                          forFile:filePath 
1008 1164
                                                                                    checkIfExists:NO 
1009 1165
                                                                                           hashes:&hashes 
......
1013 1169
            objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1014 1170
            objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
1015 1171
            PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload 
1016
                                                                       message:[NSString stringWithFormat:@"Sync: Uploading '%@' (0%%)", object.name]
1172
                                                                       message:[NSString stringWithFormat:@"Sync: Uploading '%@/%@' (0%%)", 
1173
                                                                                pithosContainer.name, object.name]
1017 1174
                                                                    totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue]
1018 1175
                                                                  currentBytes:0 
1019 1176
                                                                 pithosAccount:pithosAccount];
......
1022 1179
            });
1023 1180
            [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
1024 1181
             [NSDictionary dictionaryWithObjectsAndKeys:
1182
              pithosContainer, @"pithosContainer", 
1025 1183
              object, @"pithosObject", 
1026 1184
              filePath, @"filePath", 
1027 1185
              hashes, @"hashes", 
......
1108 1266
                [objects addObjectsFromArray:someObjects];
1109 1267
            }
1110 1268
            if ([someObjects count] < 10000) {
1111
                self.blockHash = [containerRequest blockHash];
1112
                self.blockSize = [containerRequest blockSize];
1113
                self.lastModified = [containerRequest lastModified];
1114
                self.remoteObjects = [NSMutableDictionary dictionaryWithCapacity:[objects count]];
1269
                ASIPithosContainer *pithosContainer = [pithosContainers objectAtIndex:containersIndex];
1270
                pithosContainer.blockHash = [containerRequest blockHash];
1271
                pithosContainer.blockSize = [containerRequest blockSize];
1272
                pithosContainer.lastModified = [containerRequest lastModified];
1273
                NSMutableDictionary *containerRemoteObjects = [NSMutableDictionary dictionaryWithCapacity:[objects count]];
1115 1274
                for (ASIPithosObject *object in objects) {
1116
                    [remoteObjects setObject:object forKey:object.name];
1275
                    [containerRemoteObjects setObject:object forKey:object.name];
1117 1276
                }
1277
                [remoteObjects setObject:containerRemoteObjects forKey:pithosContainer.name];
1118 1278
                [objects release];
1119 1279
                objects = nil;
1120 1280
            } else {
1121 1281
                // Do an additional request to fetch more objects
1122 1282
                ASIPithosContainerRequest *newContainerRequest = [ASIPithosContainerRequest listObjectsRequestWithPithos:pithos 
1123
                                                                                                           containerName:containerName 
1283
                                                                                                           containerName:[[pithosContainers objectAtIndex:containersIndex] name] 
1124 1284
                                                                                                                   limit:0 
1125 1285
                                                                                                                  marker:[[someObjects lastObject] name] 
1126 1286
                                                                                                                  prefix:nil 
......
1129 1289
                                                                                                                    meta:nil 
1130 1290
                                                                                                                  shared:NO 
1131 1291
                                                                                                                   until:nil 
1132
                                                                                                         ifModifiedSince:lastModified];
1292
                                                                                                         ifModifiedSince:[[pithosContainers objectAtIndex:containersIndex] lastModified]];
1133 1293
                newContainerRequest.delegate = self;
1134 1294
                newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1135 1295
                newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
......
1139 1299
                [pool drain];
1140 1300
                return;
1141 1301
            }
1302
        } else {
1303
            ASIPithosContainer *pithosContainer = [pithosContainers objectAtIndex:containersIndex];
1304
            NSMutableDictionary *containerRemoteObjects = [previousRemoteObjects objectForKey:pithosContainer.name];
1305
            if (containerRemoteObjects) 
1306
                [remoteObjects setObject:containerRemoteObjects forKey:pithosContainer.name];
1307
        }
1308
        containersIndex++;
1309
        if (containersIndex < containersCount) {
1310
            // Do a request for the next container
1311
            ASIPithosContainerRequest *newContainerRequest = [ASIPithosContainerRequest listObjectsRequestWithPithos:pithos 
1312
                                                                                                       containerName:[[pithosContainers objectAtIndex:containersIndex] name] 
1313
                                                                                                               limit:0 
1314
                                                                                                              marker:nil 
1315
                                                                                                              prefix:nil 
1316
                                                                                                           delimiter:nil 
1317
                                                                                                                path:nil 
1318
                                                                                                                meta:nil 
1319
                                                                                                              shared:NO 
1320
                                                                                                               until:nil 
1321
                                                                                                     ifModifiedSince:[[pithosContainers objectAtIndex:containersIndex] lastModified]];
1322
            newContainerRequest.delegate = self;
1323
            newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1324
            newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
1325
            newContainerRequest.userInfo = containerRequest.userInfo;
1326
            [(NSMutableDictionary *)newContainerRequest.userInfo setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
1327
            [networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
1328
            [pool drain];
1329
            return;
1142 1330
        }
1331
        self.previousRemoteObjects = remoteObjects;
1143 1332
        
1144 1333
        if (operation.isCancelled) {
1145 1334
            [self listRequestFailed:containerRequest];
......
1152 1341
                              withMessage:[containerRequest.userInfo objectForKey:@"finishedActivityMessage"]];
1153 1342
        });
1154 1343
        NSFileManager *fileManager = [NSFileManager defaultManager];
1155
        NSError *error = nil;
1156
        NSArray *subPaths = [fileManager subpathsOfDirectoryAtPath:containerDirectoryPath error:&error];
1157
        if (error) {
1158
            dispatch_async(dispatch_get_main_queue(), ^{
1159
                [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" 
1160
                                                        message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", containerDirectoryPath] 
1161
                                                          error:error];
1162
                [activityFacility startAndEndActivityWithType:PithosActivityOther 
1163
                                                      message:@"Sync: Failed to read contents of sync directory" 
1164
                                                pithosAccount:pithosAccount];
1165
            });
1166
            // Since the local listing failed, the operation finished and the sync cycle is completeted unsuccessfully
1167
            [self syncOperationFinishedWithSuccess:NO];
1168
            [pool drain];
1169
            return;
1170
        }
1344
        NSError *error;
1345
        NSMutableDictionary *subPaths = [NSMutableDictionary dictionaryWithCapacity:containersCount];
1346
        NSUInteger subPathsCount = 0;
1347
        NSString *containerDirectoryPath;
1348
        NSArray *containerSubPaths;
1349
        for (ASIPithosContainer *pithosContainer in pithosContainers) {
1350
            error = nil;
1351
            containerDirectoryPath = [directoryPath stringByAppendingPathComponent:pithosContainer.name];
1352
            containerSubPaths = [fileManager subpathsOfDirectoryAtPath:containerDirectoryPath error:&error];
1353
            if (error) {
1354
                dispatch_async(dispatch_get_main_queue(), ^{
1355
                    [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" 
1356
                                                            message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", containerDirectoryPath] 
1357
                                                              error:error];
1358
                    [activityFacility startAndEndActivityWithType:PithosActivityOther 
1359
                                                          message:@"Sync: Failed to read contents of sync directory" 
1360
                                                    pithosAccount:pithosAccount];
1361
                });
1362
                // Since the local listing failed, the operation finished and the sync cycle is completeted unsuccessfully
1363
                [self syncOperationFinishedWithSuccess:NO];
1364
                [pool drain];
1365
                return;
1366
            }
1367
            [subPaths setObject:containerSubPaths forKey:pithosContainer.name];
1368
            subPathsCount += [containerSubPaths count];
1369
        }        
1171 1370
        
1172 1371
        if (operation.isCancelled) {
1173 1372
            operation.completionBlock = nil;
......
1176 1375
            return;
1177 1376
        }
1178 1377

  
1179
        self.currentLocalObjectStates = [NSMutableDictionary dictionaryWithCapacity:[subPaths count]];
1180
        for (NSString *objectName in subPaths) {
1181
            if (operation.isCancelled) {
1182
                operation.completionBlock = nil;
1183
                [self saveLocalState];
1184
                [self syncOperationFinishedWithSuccess:NO];
1185
                [pool drain];
1186
                return;
1187
            }
1378
        self.currentLocalObjectStates = [NSMutableDictionary dictionaryWithCapacity:subPathsCount];
1379
        NSMutableDictionary *containerStoredLocalObjectStates;
1380
        for (ASIPithosContainer *pithosContainer in pithosContainers) {
1381
            containerDirectoryPath = [directoryPath stringByAppendingPathComponent:pithosContainer.name];
1382
            containerStoredLocalObjectStates = [storedLocalObjectStates objectForKey:pithosContainer.name];
1383
            for (NSString *objectName in [subPaths objectForKey:pithosContainer.name]) {
1384
                if (operation.isCancelled) {
1385
                    operation.completionBlock = nil;
1386
                    [self saveLocalState];
1387
                    [self syncOperationFinishedWithSuccess:NO];
1388
                    [pool drain];
1389
                    return;
1390
                }
1188 1391

  
1189
            PithosLocalObjectState *storedLocalObjectState = [storedLocalObjectStates objectForKey:objectName];
1190
            NSString *filePath = [containerDirectoryPath stringByAppendingPathComponent:objectName];
1191
            if (!storedLocalObjectState || [storedLocalObjectState isModified]) {
1192
                // New or modified existing local object, compute current state
1193
                if (!storedLocalObjectState)
1194
                    // For new local object, also create empty stored state
1195
                    [storedLocalObjectStates setObject:[PithosLocalObjectState localObjectState] forKey:objectName];
1196
                [currentLocalObjectStates setObject:[PithosLocalObjectState localObjectStateWithFile:filePath 
1197
                                                                                           blockHash:blockHash 
1198
                                                                                           blockSize:blockSize] 
1199
                                             forKey:filePath];
1200
            } else {
1201
                // Local object hasn't changed, set stored state also to current
1202
                [currentLocalObjectStates setObject:storedLocalObjectState forKey:filePath];
1392
                PithosLocalObjectState *storedLocalObjectState = [containerStoredLocalObjectStates objectForKey:objectName];
1393
                NSString *filePath = [containerDirectoryPath stringByAppendingPathComponent:objectName];
1394
                if (!storedLocalObjectState || [storedLocalObjectState isModified]) {
1395
                    // New or modified existing local object, compute current state
1396
                    if (!storedLocalObjectState)
1397
                        // For new local object, also create empty stored state
1398
                        [containerStoredLocalObjectStates setObject:[PithosLocalObjectState localObjectState] forKey:objectName];
1399
                    [currentLocalObjectStates setObject:[PithosLocalObjectState localObjectStateWithFile:filePath 
1400
                                                                                               blockHash:pithosContainer.blockHash 
1401
                                                                                               blockSize:pithosContainer.blockSize] 
1402
                                                          forKey:filePath];
1403
                } else {
1404
                    // Local object hasn't changed, set stored state also to current
1405
                    [currentLocalObjectStates setObject:storedLocalObjectState forKey:filePath];
1406
                }
1203 1407
            }
1408
            [self saveLocalState];
1204 1409
        }
1205
        [self saveLocalState];
1206

  
1410
        
1207 1411
        if (operation.isCancelled) {
1208 1412
            operation.completionBlock = nil;
1209 1413
            [self syncOperationFinishedWithSuccess:NO];
......
1211 1415
            return;
1212 1416
        }
1213 1417

  
1214
        for (NSString *objectName in remoteObjects) {
1215
            if (operation.isCancelled) {
1216
                operation.completionBlock = nil;
1217
                [self saveLocalState];
1218
                [self syncOperationFinishedWithSuccess:NO];
1219
                [pool drain];
1220
                return;
1221
            }
1418
        for (ASIPithosContainer *pithosContainer in pithosContainers) {
1419
            containerStoredLocalObjectStates = [storedLocalObjectStates objectForKey:pithosContainer.name];
1420
            for (NSString *objectName in [remoteObjects objectForKey:pithosContainer.name]) {
1421
                if (operation.isCancelled) {
1422
                    operation.completionBlock = nil;
1423
                    [self saveLocalState];
1424
                    [self syncOperationFinishedWithSuccess:NO];
1425
                    [pool drain];
1426
                    return;
1427
                }
1222 1428

  
1223
            if (![storedLocalObjectStates objectForKey:objectName])
1224
                [storedLocalObjectStates setObject:[PithosLocalObjectState localObjectState] forKey:objectName];
1429
                if (![containerStoredLocalObjectStates objectForKey:objectName])
1430
                    [containerStoredLocalObjectStates setObject:[PithosLocalObjectState localObjectState] forKey:objectName];
1431
            }
1432
            [self saveLocalState];
1225 1433
        }
1226
        [self saveLocalState];
1227 1434

  
1228 1435
        if (operation.isCancelled) {
1229 1436
            operation.completionBlock = nil;
......
1232 1439
            return;
1233 1440
        }
1234 1441

  
1235
        for (NSString *objectName in [[storedLocalObjectStates allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
1236
            if (operation.isCancelled) {
1237
                operation.completionBlock = nil;
1238
                [self syncOperationFinishedWithSuccess:NO];
1239
                [pool drain];
1240
                return;
1241
            }
1442
        NSMutableDictionary *containerRemoteObjects;
1443
        for (ASIPithosContainer *pithosContainer in pithosContainers) {
1444
            containerDirectoryPath = [directoryPath stringByAppendingPathComponent:pithosContainer.name];
1445
            containerStoredLocalObjectStates = [storedLocalObjectStates objectForKey:pithosContainer.name];
1446
            containerRemoteObjects = [remoteObjects objectForKey:pithosContainer.name];
1447
            for (NSString *objectName in [[containerStoredLocalObjectStates allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
1448
                if (operation.isCancelled) {
1449
                    operation.completionBlock = nil;
1450
                    [self syncOperationFinishedWithSuccess:NO];
1451
                    [pool drain];
1452
                    return;
1453
                }
1242 1454

  
1243
            NSString *filePath = [containerDirectoryPath stringByAppendingPathComponent:objectName];
1244
            if ([objectName hasSuffix:@"/"])
1245
                filePath = [filePath stringByAppendingString:@":"];
1246
            ASIPithosObject *object = [ASIPithosObject object];
1247
            object.name = objectName;
1248
            NSLog(@"Sync::object name: %@", objectName);
1455
                NSString *filePath = [containerDirectoryPath stringByAppendingPathComponent:objectName];
1456
                if ([objectName hasSuffix:@"/"])
1457
                    filePath = [filePath stringByAppendingString:@":"];
1458
                ASIPithosObject *object = [ASIPithosObject object];
1459
                object.name = objectName;
1460
                NSLog(@"Sync::object name: %@", object.name);
1249 1461
            
1250
            PithosLocalObjectState *storedLocalObjectState = [storedLocalObjectStates objectForKey:object.name];
1251
            PithosLocalObjectState *currentLocalObjectState = [currentLocalObjectStates objectForKey:filePath];
1252
            if (!currentLocalObjectState)
1253
                currentLocalObjectState = [PithosLocalObjectState localObjectStateWithFile:filePath 
1254
                                                                                 blockHash:blockHash 
1255
                                                                                 blockSize:blockSize];
1256
            if (currentLocalObjectState.isDirectory)
1257
                object.contentType = @"application/directory";
1462
                PithosLocalObjectState *storedLocalObjectState = [containerStoredLocalObjectStates objectForKey:object.name];
1463
                PithosLocalObjectState *currentLocalObjectState = [currentLocalObjectStates objectForKey:filePath];
1464
                if (!currentLocalObjectState)
1465
                    currentLocalObjectState = [PithosLocalObjectState localObjectStateWithFile:filePath 
1466
                                                                                     blockHash:pithosContainer.blockHash 
1467
                                                                                     blockSize:pithosContainer.blockSize];
1468
//                if (currentLocalObjectState.isDirectory)
1469
//                    object.contentType = @"application/directory";
1258 1470
            
1259
            PithosLocalObjectState *remoteObjectState = [PithosLocalObjectState localObjectState];
1260
            ASIPithosObject *remoteObject = [remoteObjects objectForKey:objectName];
1261
            if (remoteObject) {
1262
                if ([PithosUtilities isContentTypeDirectory:remoteObject.contentType]) {
1263
                    remoteObjectState.isDirectory = YES;
1264
                    object.contentType = @"application/directory";
1265
                } else {
1266
                    remoteObjectState.hash = remoteObject.objectHash;
1471
                PithosLocalObjectState *remoteObjectState = [PithosLocalObjectState localObjectState];
1472
                ASIPithosObject *remoteObject = [containerRemoteObjects objectForKey:object.name];
1473
                if (remoteObject) {
1474
                    if ([PithosUtilities isContentTypeDirectory:remoteObject.contentType]) {
1475
                        remoteObjectState.isDirectory = YES;
1476
//                        object.contentType = @"application/directory";
1477
                    } else {
1478
                        remoteObjectState.hash = remoteObject.objectHash;
1479
                    }
1267 1480
                }
1268
            }
1269 1481

  
1270
            BOOL localStateHasChanged = ![currentLocalObjectState isEqualToState:storedLocalObjectState];
1271
            BOOL serverStateHasChanged = ![remoteObjectState isEqualToState:storedLocalObjectState];
1272
            NSLog(@"Sync::localStateHasChanged: %d, serverStateHasChanged: %d", localStateHasChanged, serverStateHasChanged);
1273
            if (!localStateHasChanged) {
1274
                // Local state hasn't changed
1275
                if (serverStateHasChanged) {
1276
                    // Server state has changed
1277
                    // Update local state to match that of the server 
1278
                    object.bytes = remoteObject.bytes;
1279
                    object.version = remoteObject.version;
1280
                    object.contentType = remoteObject.contentType;
1281
                    object.objectHash = remoteObject.objectHash;
1282
                    [self updateLocalStateWithObject:object localFilePath:filePath];
1283
                } else if (!remoteObject && ![currentLocalObjectState exists]) {
1284
                    // Server state hasn't changed
1285
                    // If the object doesn't exist neither in the server or locally, it should be removed from the stored local objects
1286
                    [storedLocalObjectStates removeObjectForKey:objectName];
1287
                    [self saveLocalState];
1288
                }
1289
            } else {
1290
                // Local state has changed
1291
                if (!serverStateHasChanged) {
1292
                    // Server state hasn't changed
1293
                    [self updateServerStateWithCurrentState:currentLocalObjectState 
1294
                                                     object:object 
1295
                                              localFilePath:filePath];
1296
                } else {
1297
                    // Server state has also changed
1298
                    if (remoteObjectState.isDirectory && currentLocalObjectState.isDirectory) {
1299
                        // Both did the same change (directory)
1300
                        storedLocalObjectState.filePath = filePath;
1301
                        storedLocalObjectState.isDirectory = YES;
1302
                        [self saveLocalState];
1303
                    } else if ([remoteObjectState isEqualToState:currentLocalObjectState]) {
1304
                        // Both did the same change (object edit or delete)
1305
                        if (![remoteObjectState exists]) {
1306
                            [storedLocalObjectStates removeObjectForKey:object.name];
1307
                        } else {
1308
                            storedLocalObjectState.filePath = filePath;
1309
                            storedLocalObjectState.hash = remoteObjectState.hash;
1310
                        }
1482
                BOOL localStateHasChanged = ![currentLocalObjectState isEqualToState:storedLocalObjectState];
1483
                BOOL serverStateHasChanged = ![remoteObjectState isEqualToState:storedLocalObjectState];
1484
                NSLog(@"Sync::localStateHasChanged: %d, serverStateHasChanged: %d", localStateHasChanged, serverStateHasChanged);
1485
                if (!localStateHasChanged) {
1486
                    // Local state hasn't changed
1487
                    if (serverStateHasChanged) {
1488
                        // Server state has changed
1489
                        // Update local state to match that of the server 
1490
                        object.bytes = remoteObject.bytes;
1491
                        object.version = remoteObject.version;
1492
                        object.contentType = remoteObject.contentType;
1493
                        object.objectHash = remoteObject.objectHash;
1494
                        [self updateLocalStateWithObject:object localFilePath:filePath pithosContainer:pithosContainer];
1495
                    } else if (!remoteObject && ![currentLocalObjectState exists]) {
1496
                        // Server state hasn't changed
1497
                        // If the object doesn't exist neither in the server or locally, it should be removed from the stored local objects
1498
                        [containerStoredLocalObjectStates removeObjectForKey:objectName];
1311 1499
                        [self saveLocalState];
1500
                    }
1501
                } else {
1502
                    // Local state has changed
1503
                    if (!serverStateHasChanged) {
1504
                        // Server state hasn't changed
1505
                        if (currentLocalObjectState.isDirectory)
1506
                            object.contentType = @"application/directory";
1507
                        else
1508
                            object.objectHash = currentLocalObjectState.hash;
1509
                        [self updateServerStateWithCurrentState:currentLocalObjectState 
1510
                                                         object:object 
1511
                                                  localFilePath:filePath 
1512
                                                pithosContainer:pithosContainer];
1312 1513
                    } else {
1313
                        // Conflict, we ask the user which change to keep
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff