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
|