2 // PithosFileUtilities.m
5 // Copyright 2011 GRNET S.A. All rights reserved.
7 // Redistribution and use in source and binary forms, with or
8 // without modification, are permitted provided that the following
11 // 1. Redistributions of source code must retain the above
12 // copyright notice, this list of conditions and the following
15 // 2. Redistributions in binary form must reproduce the above
16 // copyright notice, this list of conditions and the following
17 // disclaimer in the documentation and/or other materials
18 // provided with the distribution.
20 // THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
21 // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
24 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27 // USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
33 // The views and conclusions contained in the software and
34 // documentation are those of the authors and should not be
35 // interpreted as representing official policies, either expressed
36 // or implied, of GRNET S.A.
38 #import "PithosFileUtilities.h"
39 #import "ASIPithosContainerRequest.h"
40 #import "ASIPithosObjectRequest.h"
41 #import "ASIPithosObject.h"
42 #import "HashMapHash.h"
44 @implementation PithosFileUtilities
49 + (ASIPithosObjectRequest *)objectDataRequestWithContainerName:(NSString *)containerName
50 objectName:(NSString *)objectName
51 toDirectory:(NSString *)directoryPath
52 checkIfExists:(BOOL)ifExists
53 sharingAccount:(NSString *)sharingAccount {
54 NSString *fileName = [objectName lastPathComponent];
55 if([objectName hasSuffix:@"/"])
56 fileName = [fileName stringByAppendingString:@"/"];
57 fileName = [fileName stringByReplacingOccurrencesOfString:@"/" withString:@":"];
59 NSFileManager *defaultManager = [NSFileManager defaultManager];
61 NSString *destinationPath = [directoryPath stringByAppendingPathComponent:fileName];
62 if (ifExists && [defaultManager fileExistsAtPath:destinationPath]) {
63 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
64 [alert setMessageText:@"File Exists"];
65 [alert setInformativeText:[NSString stringWithFormat:@"A file or directory named '%@' already exists, do you want to replace it?", fileName]];
66 [alert addButtonWithTitle:@"OK"];
67 [alert addButtonWithTitle:@"Cancel"];
68 NSInteger choice = [alert runModal];
69 if (choice == NSAlertSecondButtonReturn)
73 BOOL directoryIsDirectory;
74 BOOL directoryExists = [defaultManager fileExistsAtPath:directoryPath isDirectory:&directoryIsDirectory];
76 if (!directoryExists) {
77 [defaultManager createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error];
78 } else if (!directoryIsDirectory) {
79 [defaultManager removeItemAtPath:directoryPath error:&error];
82 NSLog(@"Cannot remove existing file '%@': %@", fileName, error);
83 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
84 [alert setMessageText:@"Removal Error"];
85 [alert setInformativeText:[NSString stringWithFormat:@"Cannot remove existing file '%@': %@", fileName, error]];
86 [alert addButtonWithTitle:@"OK"];
91 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectDataRequestWithContainerName:containerName
92 objectName:objectName];
94 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
95 objectRequest.downloadDestinationPath = destinationPath;
96 objectRequest.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
97 fileName, @"fileName",
98 destinationPath, @"filePath",
100 return objectRequest;
103 + (NSArray *)objectDataRequestsForSubdirWithContainerName:(NSString *)containerName
104 objectName:(NSString *)objectName
105 toDirectory:(NSString *)directoryPath
106 checkIfExists:(BOOL)ifExists
107 sharingAccount:(NSString *)sharingAccount {
108 NSString *subdirName = [objectName lastPathComponent];
109 NSString *destinationPath = [directoryPath stringByAppendingPathComponent:subdirName];
110 if (ifExists && [[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) {
111 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
112 [alert setMessageText:@"File exists"];
113 [alert setInformativeText:[NSString stringWithFormat:@"A file or directory named '%@' already exists, do you want to replace it?", subdirName]];
114 [alert addButtonWithTitle:@"OK"];
115 [alert addButtonWithTitle:@"Cancel"];
116 NSInteger choice = [alert runModal];
117 if (choice == NSAlertSecondButtonReturn)
121 NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName
122 delimiter:nil sharingAccount:sharingAccount];
126 NSFileManager *defaultManager = [NSFileManager defaultManager];
127 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:[objects count]];
128 NSUInteger subdirPrefixLength = [objectName length];
130 NSError *error = nil;
131 [defaultManager createDirectoryAtPath:[directoryPath stringByAppendingPathComponent:subdirName] withIntermediateDirectories:YES attributes:nil error:&error];
133 NSLog(@"Cannot create directory at '%@': %@", directoryPath, error);
134 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
135 [alert setMessageText:@"Create Directory Error"];
136 [alert setInformativeText:[NSString stringWithFormat:@"Cannot create directory at '%@': %@",
137 directoryPath, error]];
138 [alert addButtonWithTitle:@"OK"];
142 for (ASIPithosObject *object in objects) {
143 if ([object.contentType isEqualToString:@"application/directory"]) {
144 NSString *subdirDirectoryPath = [directoryPath stringByAppendingPathComponent:subdirName];
145 subdirDirectoryPath = [subdirDirectoryPath stringByAppendingPathComponent:[object.name substringFromIndex:subdirPrefixLength]];
147 BOOL directoryIsDirectory;
148 BOOL directoryExists = [defaultManager fileExistsAtPath:subdirDirectoryPath isDirectory:&directoryIsDirectory];
149 NSError *error = nil;
150 if (!directoryExists) {
151 [defaultManager createDirectoryAtPath:subdirDirectoryPath withIntermediateDirectories:YES attributes:nil error:&error];
153 NSLog(@"Cannot create directory at '%@': %@", subdirDirectoryPath, error);
154 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
155 [alert setMessageText:@"Create Directory Error"];
156 [alert setInformativeText:[NSString stringWithFormat:@"Cannot create directory at '%@': %@",
157 subdirDirectoryPath, error]];
158 [alert addButtonWithTitle:@"OK"];
161 } else if (!directoryIsDirectory) {
162 [defaultManager removeItemAtPath:subdirDirectoryPath error:&error];
164 NSLog(@"Cannot remove existing file at '%@': %@", subdirDirectoryPath, error);
165 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
166 [alert setMessageText:@"Remove File Error"];
167 [alert setInformativeText:[NSString stringWithFormat:@"Cannot remove existing file at '%@': %@",
168 subdirDirectoryPath, error]];
169 [alert addButtonWithTitle:@"OK"];
174 NSString *fileName = [object.name lastPathComponent];
175 if([object.name hasSuffix:@"/"])
176 fileName = [fileName stringByAppendingString:@"/"];
178 NSString *objectDirectoryPath = [directoryPath stringByAppendingPathComponent:subdirName];
179 objectDirectoryPath = [objectDirectoryPath stringByAppendingPathComponent:[object.name substringWithRange:NSMakeRange(subdirPrefixLength, [object.name length] - subdirPrefixLength - [fileName length])]];
181 [objectRequests addObject:[self objectDataRequestWithContainerName:containerName
182 objectName:object.name
183 toDirectory:objectDirectoryPath
185 sharingAccount:sharingAccount]];
189 return objectRequests;
195 + (ASIPithosObjectRequest *)writeObjectDataRequestWithContainerName:(NSString *)containerName
196 objectName:(NSString *)objectName
197 contentType:(NSString *)contentType
198 blockSize:(NSUInteger)blockSize
199 blockHash:(NSString *)blockHash
200 forFile:(NSString *)filePath
201 checkIfExists:(BOOL)ifExists
202 hashes:(NSArray **)hashes
203 sharingAccount:(NSString *)sharingAccount {
204 if (ifExists && ![self proceedIfObjectExistsAtContainerName:containerName objectName:objectName
205 sharingAccount:(NSString *)sharingAccount])
209 *hashes = [HashMapHash calculateObjectHashMap:filePath withBlockHash:blockHash andBlockSize:blockSize];
212 NSUInteger bytes = [self bytesOfFile:filePath];
213 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
214 objectName:objectName
215 contentType:contentType
217 contentDisposition:nil
220 isPublic:ASIPithosObjectRequestPublicIgnore
227 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
228 return objectRequest;
231 + (NSIndexSet *)missingBlocksForHashes:(NSArray *)hashes withMissingHashesResponse:(NSString *)missingHashesResponse {
232 NSArray *responseLines = [missingHashesResponse componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
233 NSMutableIndexSet *missingBlocks = [NSMutableIndexSet indexSet];
234 for (NSString *line in responseLines) {
237 NSUInteger missingBlock = [hashes indexOfObject:line];
238 if (missingBlock != NSNotFound)
239 [missingBlocks addIndex:missingBlock];
241 return missingBlocks;
244 + (ASIPithosObjectRequest *)updateObjectDataRequestWithContainerName:(NSString *)containerName
245 objectName:(NSString *)objectName
246 blockSize:(NSUInteger)blockSize
247 forFile:(NSString *)filePath
248 hashes:(NSArray *)hashes
249 missingHashesResponse:(NSString *)missingHashesResponse
250 sharingAccount:(NSString *)sharingAccount {
251 NSIndexSet *missingBlocks = [self missingBlocksForHashes:hashes withMissingHashesResponse:missingHashesResponse];
253 NSFileManager *defaultManager = [NSFileManager defaultManager];
254 NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
256 // http://cocoawithlove.com/2009/07/temporary-files-and-folders-in-cocoa.html
257 NSString *tempFileTemplate = NSTemporaryDirectory();
258 if (tempFileTemplate == nil)
259 tempFileTemplate = @"/tmp";
260 tempFileTemplate = [tempFileTemplate stringByAppendingPathComponent:@"pithos-macos.XXXXXX"];
261 const char *tempFileTemplateCString = [tempFileTemplate fileSystemRepresentation];
262 char *tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1);
263 strcpy(tempFileNameCString, tempFileTemplateCString);
264 int fileDescriptor = mkstemp(tempFileNameCString);
265 NSString *tempFilePath = [defaultManager stringWithFileSystemRepresentation:tempFileNameCString length:strlen(tempFileNameCString)];
266 if (fileDescriptor == -1) {
267 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
268 [alert setMessageText:@"Create Temporary File Error"];
269 [alert setInformativeText:[NSString stringWithFormat:@"Cannot create temporary file at '%@'", tempFilePath]];
270 [alert addButtonWithTitle:@"OK"];
274 free(tempFileNameCString);
275 NSFileHandle *tempFileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fileDescriptor closeOnDealloc:YES];
277 [missingBlocks enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
278 [fileHandle seekToFileOffset:(idx*blockSize)];
279 [tempFileHandle writeData:[fileHandle readDataOfLength:blockSize]];
281 [tempFileHandle closeFile];
282 [fileHandle closeFile];
284 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
285 objectName:objectName
287 contentType:@"application/octet-stream"
289 contentDisposition:nil
292 isPublic:ASIPithosObjectRequestPublicIgnore
296 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
297 return objectRequest;
300 + (ASIPithosObjectRequest *)updateObjectDataRequestWithContainerName:(NSString *)containerName
301 objectName:(NSString *)objectName
302 blockSize:(NSUInteger)blockSize
303 forFile:(NSString *)filePath
304 missingBlockIndex:(NSUInteger)missingBlockIndex
305 sharingAccount:(NSString *)sharingAccount {
306 NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
307 [fileHandle seekToFileOffset:(missingBlockIndex *blockSize)];
308 NSData *blockData = [fileHandle readDataOfLength:blockSize];
309 [fileHandle closeFile];
310 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
311 objectName:objectName
313 contentType:@"application/octet-stream"
315 contentDisposition:nil
318 isPublic:ASIPithosObjectRequestPublicIgnore
322 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
323 return objectRequest;
326 + (NSArray *)writeObjectDataRequestsWithContainerName:(NSString *)containerName
327 objectName:(NSString *)objectName
328 blockSize:(NSUInteger)blockSize
329 blockHash:(NSString *)blockHash
330 forDirectory:(NSString *)directoryPath
331 checkIfExists:(BOOL)ifExists
332 objectNames:(NSMutableArray **)objectNames
333 contentTypes:(NSMutableArray **)contentTypes
334 filePaths:(NSMutableArray **)filePaths
335 hashesArrays:(NSMutableArray **)hashesArrays
336 directoryObjectRequests:(NSMutableArray **) directoryObjectRequests
337 sharingAccount:(NSString *)sharingAccount {
338 if (ifExists && ![self proceedIfObjectExistsAtContainerName:containerName objectName:objectName sharingAccount:sharingAccount])
341 NSFileManager *defaultManager = [NSFileManager defaultManager];
342 NSError *error = nil;
343 NSArray *subPaths = [defaultManager subpathsOfDirectoryAtPath:directoryPath error:&error];
345 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
346 [alert setMessageText:@"Directory Read Error"];
347 [alert setInformativeText:[NSString stringWithFormat:@"Cannot read contents of directory '%@': %@",
348 [directoryPath lastPathComponent], error]];
349 [alert addButtonWithTitle:@"OK"];
354 *directoryObjectRequests = [NSMutableArray arrayWithCapacity:[subPaths count]];
355 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
356 objectName:objectName
358 contentType:@"application/directory"
360 contentDisposition:nil
363 isPublic:ASIPithosObjectRequestPublicIgnore
367 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
368 [*directoryObjectRequests addObject:objectRequest];
370 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:[subPaths count]];
371 *objectNames = [NSMutableArray arrayWithCapacity:[subPaths count]];
372 *contentTypes = [NSMutableArray arrayWithCapacity:[subPaths count]];
373 *filePaths = [NSMutableArray arrayWithCapacity:[subPaths count]];
374 *hashesArrays = [NSMutableArray arrayWithCapacity:[subPaths count]];
376 NSString *subObjectName;
379 NSString *contentType;
381 for (NSString *objectNameSuffix in subPaths) {
382 filePath = [directoryPath stringByAppendingPathComponent:objectNameSuffix];
383 if ([defaultManager fileExistsAtPath:filePath isDirectory:&isDirectory]) {
385 hashes = [HashMapHash calculateObjectHashMap:filePath withBlockHash:blockHash andBlockSize:blockSize];
387 subObjectName = [objectName stringByAppendingPathComponent:objectNameSuffix];
388 bytes = [self bytesOfFile:filePath];
390 contentType = [self contentTypeOfFile:filePath error:&error];
391 if (contentType == nil)
392 contentType = @"application/octet-stream";
394 NSLog(@"contentType detection error: %@", error);
395 objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
396 objectName:subObjectName
397 contentType:contentType
399 contentDisposition:nil
402 isPublic:ASIPithosObjectRequestPublicIgnore
409 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
410 [objectRequests addObject:objectRequest];
411 [*objectNames addObject:subObjectName];
412 [*contentTypes addObject:contentType];
413 [*filePaths addObject:filePath];
414 [*hashesArrays addObject:hashes];
418 subObjectName = [objectName stringByAppendingPathComponent:objectNameSuffix];
419 objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
420 objectName:subObjectName
422 contentType:@"application/directory"
424 contentDisposition:nil
427 isPublic:ASIPithosObjectRequestPublicIgnore
431 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
432 [*directoryObjectRequests addObject:objectRequest];
437 return objectRequests;
443 + (NSArray *)deleteObjectRequestsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName {
444 NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil sharingAccount:nil];
448 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
450 if (![objectName hasSuffix:@"/"])
451 [objectRequests addObject:[ASIPithosObjectRequest deleteObjectRequestWithContainerName:containerName objectName:objectName]];
452 for (ASIPithosObject *object in objects) {
453 [objectRequests addObject:[ASIPithosObjectRequest deleteObjectRequestWithContainerName:containerName objectName:object.name]];
456 if ([objectRequests count] == 0)
458 return objectRequests;
464 + (ASIPithosObjectRequest *)copyObjectRequestWithContainerName:(NSString *)containerName
465 objectName:(NSString *)objectName
466 destinationContainerName:(NSString *)destinationContainerName
467 destinationObjectName:(NSString *)destinationObjectName
468 checkIfExists:(BOOL)ifExists
469 sharingAccount:(NSString *)sharingAccount {
470 if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
473 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
474 objectName:objectName
477 contentDisposition:nil
480 isPublic:ASIPithosObjectRequestPublicIgnore
482 destinationContainerName:destinationContainerName
483 destinationObjectName:destinationObjectName
486 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
487 return objectRequest;
490 + (NSArray *)copyObjectRequestsForSubdirWithContainerName:(NSString *)containerName
491 objectName:(NSString *)objectName
492 destinationContainerName:(NSString *)destinationContainerName
493 destinationObjectName:(NSString *)destinationObjectName
494 checkIfExists:(BOOL)ifExists
495 sharingAccount:(NSString *)sharingAccount {
496 if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
499 NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName
500 delimiter:nil sharingAccount:sharingAccount];
504 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
505 ASIPithosObjectRequest *objectRequest;
506 if ([objectName isEqualToString:destinationObjectName]) {
507 if (![objectName hasSuffix:@"/"]) {
508 objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
509 objectName:objectName
512 contentDisposition:nil
515 isPublic:ASIPithosObjectRequestPublicIgnore
517 destinationContainerName:destinationContainerName
518 destinationObjectName:objectName
521 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
522 [objectRequests addObject:objectRequest];
524 for (ASIPithosObject *object in objects) {
525 objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
526 objectName:object.name
529 contentDisposition:nil
532 isPublic:ASIPithosObjectRequestPublicIgnore
534 destinationContainerName:destinationContainerName
535 destinationObjectName:object.name
538 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
539 [objectRequests addObject:objectRequest];
542 if (![objectName hasSuffix:@"/"]) {
543 objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
544 objectName:objectName
547 contentDisposition:nil
550 isPublic:ASIPithosObjectRequestPublicIgnore
552 destinationContainerName:destinationContainerName
553 destinationObjectName:destinationObjectName
556 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
557 [objectRequests addObject:objectRequest];
559 NSRange prefixRange = NSMakeRange(0, [objectName length]);
560 NSString *newObjectName;
561 for (ASIPithosObject *object in objects) {
562 newObjectName = [object.name stringByReplacingOccurrencesOfString:objectName
563 withString:destinationObjectName
564 options:NSAnchoredSearch
566 objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
567 objectName:object.name
570 contentDisposition:nil
573 isPublic:ASIPithosObjectRequestPublicIgnore
575 destinationContainerName:destinationContainerName
576 destinationObjectName:newObjectName
579 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
580 [objectRequests addObject:objectRequest];
584 if ([objectRequests count] == 0)
586 return objectRequests;
592 + (ASIPithosObjectRequest *)moveObjectRequestWithContainerName:(NSString *)containerName
593 objectName:(NSString *)objectName
594 destinationContainerName:(NSString *)destinationContainerName
595 destinationObjectName:(NSString *)destinationObjectName
596 checkIfExists:(BOOL)ifExists {
597 if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
600 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
601 objectName:objectName
604 contentDisposition:nil
607 isPublic:ASIPithosObjectRequestPublicIgnore
609 destinationContainerName:destinationContainerName
610 destinationObjectName:destinationObjectName];
611 return objectRequest;
614 + (NSArray *)moveObjectRequestsForSubdirWithContainerName:(NSString *)containerName
615 objectName:(NSString *)objectName
616 destinationContainerName:(NSString *)destinationContainerName
617 destinationObjectName:(NSString *)destinationObjectName
618 checkIfExists:(BOOL)ifExists {
619 if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
622 NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil sharingAccount:nil];
626 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
627 if ([objectName isEqualToString:destinationObjectName]) {
628 if (![objectName hasSuffix:@"/"])
629 [objectRequests addObject:[ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
630 objectName:objectName
633 contentDisposition:nil
636 isPublic:ASIPithosObjectRequestPublicIgnore
638 destinationContainerName:destinationContainerName
639 destinationObjectName:objectName]];
640 for (ASIPithosObject *object in objects) {
641 [objectRequests addObject:[ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
642 objectName:object.name
645 contentDisposition:nil
648 isPublic:ASIPithosObjectRequestPublicIgnore
650 destinationContainerName:destinationContainerName
651 destinationObjectName:object.name]];
654 if (![objectName hasSuffix:@"/"]) {
655 [objectRequests addObject:[ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
656 objectName:objectName
659 contentDisposition:nil
662 isPublic:ASIPithosObjectRequestPublicIgnore
664 destinationContainerName:destinationContainerName
665 destinationObjectName:destinationObjectName]];
667 NSRange prefixRange = NSMakeRange(0, [objectName length]);
668 NSString *newObjectName;
669 for (ASIPithosObject *object in objects) {
670 newObjectName = [object.name stringByReplacingOccurrencesOfString:objectName
671 withString:destinationObjectName
672 options:NSAnchoredSearch
674 [objectRequests addObject:[ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
675 objectName:object.name
678 contentDisposition:nil
681 isPublic:ASIPithosObjectRequestPublicIgnore
683 destinationContainerName:destinationContainerName
684 destinationObjectName:newObjectName]];
688 if ([objectRequests count] == 0)
690 return objectRequests;
694 #pragma mark Helper Methods
696 // Size of the file in bytes
697 + (NSUInteger)bytesOfFile:(NSString *)filePath {
698 NSFileManager *defaultManager = [NSFileManager defaultManager];
699 NSDictionary *attributes = [defaultManager attributesOfItemAtPath:filePath error:nil];
700 return [[attributes objectForKey:NSFileSize] intValue];
703 // Content type of the file or nil if it cannot be determined
704 + (NSString *)contentTypeOfFile:(NSString *)filePath error:(NSError **)error {
705 NSURLResponse *response = nil;
706 [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath]
707 cachePolicy:NSURLCacheStorageNotAllowed
709 returningResponse:&response
711 return [response MIMEType];
714 // Returns if an object exists at the given container/object path and if this object is an application/directory
715 // If an error occured an alert is shown and it is returned so the caller won't proceed
716 + (BOOL)objectExistsAtContainerName:(NSString *)containerName objectName:(NSString *)objectName
717 error:(NSError **)error isDirectory:(BOOL *)isDirectory
718 sharingAccount:(NSString *)sharingAccount {
719 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectMetadataRequestWithContainerName:containerName
720 objectName:objectName];
722 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
723 [objectRequest startSynchronous];
724 *error = [objectRequest error];
726 [self httpRequestErrorAlertWithRequest:objectRequest];
728 } else if (objectRequest.responseStatusCode == 200) {
729 *isDirectory = [[objectRequest contentType] isEqualToString:@"application/directory"];
735 // Returns if the called should proceed, after an interactive check if an object exists
736 // at the given container/object path is performed
737 + (BOOL)proceedIfObjectExistsAtContainerName:(NSString *)containerName objectName:(NSString *)objectName
738 sharingAccount:(NSString *)sharingAccount {
739 NSError *error = nil;
741 BOOL objectExists = [self objectExistsAtContainerName:containerName
742 objectName:objectName
744 isDirectory:&isDirectory
745 sharingAccount:sharingAccount];
748 } else if (objectExists) {
749 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
751 [alert setMessageText:@"Directory Exists"];
753 [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
755 [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
757 [alert setMessageText:@"Object Exists"];
759 [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
761 [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
763 [alert addButtonWithTitle:@"OK"];
764 [alert addButtonWithTitle:@"Cancel"];
765 NSInteger choice = [alert runModal];
766 if (choice == NSAlertSecondButtonReturn)
773 // List of objects at the given container/object path, with prefix and or delimiter
774 + (NSArray *)objectsWithContainerName:(NSString *)containerName objectNamePrefix:(NSString *)objectNamePrefix
775 delimiter:(NSString *)delimiter sharingAccount:(NSString *)sharingAccount {
776 NSMutableArray *objects = [NSMutableArray array];
777 NSString *marker = nil;
779 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest listObjectsRequestWithContainerName:containerName
782 prefix:objectNamePrefix
789 [containerRequest setRequestUserFromDefaultTo:sharingAccount];
790 [containerRequest startSynchronous];
791 if ([containerRequest error]) {
792 [self httpRequestErrorAlertWithRequest:containerRequest];
795 NSArray *someObjects = [containerRequest objects];
796 [objects addObjectsFromArray:someObjects];
797 if ([someObjects count] < 10000)
800 marker = [[someObjects lastObject] name];
805 // List of objects at the given container/object path, that may be a subdir or an application/directory,
806 // with prefix and or delimiter
807 + (NSArray *)objectsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName
808 delimiter:(NSString *)delimiter sharingAccount:(NSString *)sharingAccount {
809 NSString *subdirNamePrefix = [NSString stringWithString:objectName];
810 if (![subdirNamePrefix hasSuffix:@"/"])
811 subdirNamePrefix = [subdirNamePrefix stringByAppendingString:@"/"];
812 return [self objectsWithContainerName:containerName objectNamePrefix:subdirNamePrefix
813 delimiter:delimiter sharingAccount:sharingAccount];
816 // A safe object name at the given container/object path
817 // The original name has " %d" appended to it before any ".*" suffix, for the first integer that produces a name that is free to use
818 // If the original name hasn't got a "." but has a "/" suffix, then it is replaced with " %d/" instead
819 // Subdirs are taken into consideration
820 + (NSString *)safeObjectNameForContainerName:(NSString *)containerName objectName:(NSString *)objectName {
821 NSString *objectNamePrefix;
822 NSString *objectNameExtraSuffix;
823 NSRange lastDotRange = [objectName rangeOfString:@"." options:NSBackwardsSearch];
824 if (lastDotRange.length == 1) {
825 objectNamePrefix = [objectName substringToIndex:lastDotRange.location];
826 objectNameExtraSuffix = [objectName substringFromIndex:lastDotRange.location];
827 } else if ([objectName hasSuffix:@"/"]) {
828 objectNamePrefix = [objectName substringToIndex:([objectName length] - 1)];
829 objectNameExtraSuffix = [NSString stringWithString:@"/"];
831 objectNamePrefix = [NSString stringWithString:objectName];
832 objectNameExtraSuffix = [NSString string];
834 NSArray *objects = [self objectsWithContainerName:containerName
835 objectNamePrefix:[objectNamePrefix stringByDeletingLastPathComponent]
840 if ([objects count] == 0)
842 NSMutableArray *objectNames = [NSMutableArray arrayWithArray:
843 [[objects objectsAtIndexes:
844 [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
845 if (pithosObject.subdir)
848 }]] valueForKey:@"name"]];
849 for (NSString *name in [[objects objectsAtIndexes:
850 [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
851 if (pithosObject.subdir)
854 }]] valueForKey:@"name"]) {
855 [objectNames addObject:[name substringToIndex:([name length] - 1)]];
857 if (![objectNames containsObject:objectName])
859 NSUInteger objectNameSuffix = 2;
860 NSString *safeObjectName;
862 safeObjectName = [objectNamePrefix stringByAppendingFormat:@" %lu%@", objectNameSuffix, objectNameExtraSuffix];
864 } while ([objectNames containsObject:safeObjectName]);
865 return safeObjectName;
868 // A safe object name at the given container/object path that may be a subdir or application/directory
869 // The original name has " %d" appended to it, for the first integer that produces a name that is free to use
870 // If the original name has a "/" suffix, then it is replaced with " %d/" instead
871 // Subdirs are taken into consideration
872 + (NSString *)safeSubdirNameForContainerName:(NSString *)containerName subdirName:(NSString *)subdirName {
873 NSString *subdirNamePrefix;
874 NSString *subdirNameExtraSuffix;
875 if ([subdirName hasSuffix:@"/"]) {
876 subdirNamePrefix = [subdirName substringToIndex:([subdirName length] - 1)];
877 subdirNameExtraSuffix = [NSString stringWithString:@"/"];
879 subdirNamePrefix = [NSString stringWithString:subdirName];
880 subdirNameExtraSuffix = [NSString string];
882 NSArray *objects = [self objectsWithContainerName:containerName
883 objectNamePrefix:[subdirNamePrefix stringByDeletingLastPathComponent]
888 if ([objects count] == 0)
890 NSMutableArray *objectNames = [NSMutableArray arrayWithArray:
891 [[objects objectsAtIndexes:
892 [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
893 if (pithosObject.subdir)
896 }]] valueForKey:@"name"]];
897 for (NSString *name in [[objects objectsAtIndexes:
898 [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
899 if (pithosObject.subdir)
902 }]] valueForKey:@"name"]) {
903 [objectNames addObject:[name substringToIndex:([name length] - 1)]];
905 if (![objectNames containsObject:subdirNamePrefix])
907 NSUInteger subdirNameSuffix = 2;
908 NSString *safeSubdirName;
910 safeSubdirName = [subdirNamePrefix stringByAppendingFormat:@" %lu", subdirNameSuffix];
912 } while ([objectNames containsObject:safeSubdirName]);
913 return [safeSubdirName stringByAppendingString:subdirNameExtraSuffix];
919 + (NSInteger)httpRequestErrorAlertWithRequest:(ASIPithosRequest *)request {
920 NSString *message = [NSString stringWithFormat:
921 @"HTTP request error: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@",
923 request.requestMethod,
925 [request requestHeaders],
926 [request responseHeaders],
927 [request responseString]];
928 NSLog(@"%@", message);
929 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
930 [alert setMessageText:@"HTTP Request Error"];
931 [alert setInformativeText:message];
932 [alert addButtonWithTitle:@"OK"];
933 return [alert runModal];
936 + (NSInteger)unexpectedResponseStatusAlertWithRequest:(ASIPithosRequest *)request {
937 NSString *message = [NSString stringWithFormat:
938 @"Unexpected response status %d: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@",
939 request.responseStatusCode,
940 request.responseStatusMessage,
941 request.requestMethod,
943 [request requestHeaders],
944 [request responseHeaders],
945 [request responseString]];
946 NSLog(@"%@", message);
947 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
948 [alert setMessageText:@"Unexpected Response Status"];
949 [alert setInformativeText:message];
950 [alert addButtonWithTitle:@"OK"];
951 return [alert runModal];