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 NSString *fileName = [objectName lastPathComponent];
54 if([objectName hasSuffix:@"/"])
55 fileName = [fileName stringByAppendingString:@"/"];
56 fileName = [fileName stringByReplacingOccurrencesOfString:@"/" withString:@":"];
58 NSFileManager *defaultManager = [NSFileManager defaultManager];
60 NSString *destinationPath = [directoryPath stringByAppendingPathComponent:fileName];
61 if (ifExists && [defaultManager fileExistsAtPath:destinationPath]) {
62 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
63 [alert setMessageText:@"File Exists"];
64 [alert setInformativeText:[NSString stringWithFormat:@"A file or directory named '%@' already exists, do you want to replace it?", fileName]];
65 [alert addButtonWithTitle:@"OK"];
66 [alert addButtonWithTitle:@"Cancel"];
67 NSInteger choice = [alert runModal];
68 if (choice == NSAlertSecondButtonReturn)
72 BOOL directoryIsDirectory;
73 BOOL directoryExists = [defaultManager fileExistsAtPath:directoryPath isDirectory:&directoryIsDirectory];
75 if (!directoryExists) {
76 [defaultManager createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error];
77 } else if (!directoryIsDirectory) {
78 [defaultManager removeItemAtPath:directoryPath error:&error];
81 NSLog(@"Cannot remove existing file '%@': %@", fileName, error);
82 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
83 [alert setMessageText:@"Removal Error"];
84 [alert setInformativeText:[NSString stringWithFormat:@"Cannot remove existing file '%@': %@", fileName, error]];
85 [alert addButtonWithTitle:@"OK"];
90 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectDataRequestWithContainerName:containerName
91 objectName:objectName];
92 objectRequest.downloadDestinationPath = destinationPath;
93 objectRequest.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
94 fileName, @"fileName",
95 destinationPath, @"filePath",
100 + (NSArray *)objectDataRequestsForSubdirWithContainerName:(NSString *)containerName
101 objectName:(NSString *)objectName
102 toDirectory:(NSString *)directoryPath
103 checkIfExists:(BOOL)ifExists {
104 NSString *subdirName = [objectName lastPathComponent];
105 NSString *destinationPath = [directoryPath stringByAppendingPathComponent:subdirName];
106 if (ifExists && [[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) {
107 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
108 [alert setMessageText:@"File exists"];
109 [alert setInformativeText:[NSString stringWithFormat:@"A file or directory named '%@' already exists, do you want to replace it?", subdirName]];
110 [alert addButtonWithTitle:@"OK"];
111 [alert addButtonWithTitle:@"Cancel"];
112 NSInteger choice = [alert runModal];
113 if (choice == NSAlertSecondButtonReturn)
117 NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil];
121 NSFileManager *defaultManager = [NSFileManager defaultManager];
122 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:[objects count]];
123 NSUInteger subdirPrefixLength = [objectName length];
125 NSError *error = nil;
126 [defaultManager createDirectoryAtPath:[directoryPath stringByAppendingPathComponent:subdirName] withIntermediateDirectories:YES attributes:nil error:&error];
128 NSLog(@"Cannot create directory at '%@': %@", directoryPath, error);
129 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
130 [alert setMessageText:@"Create Directory Error"];
131 [alert setInformativeText:[NSString stringWithFormat:@"Cannot create directory at '%@': %@",
132 directoryPath, error]];
133 [alert addButtonWithTitle:@"OK"];
137 for (ASIPithosObject *object in objects) {
138 if ([object.contentType isEqualToString:@"application/directory"]) {
139 NSString *subdirDirectoryPath = [directoryPath stringByAppendingPathComponent:subdirName];
140 subdirDirectoryPath = [subdirDirectoryPath stringByAppendingPathComponent:[object.name substringFromIndex:subdirPrefixLength]];
142 BOOL directoryIsDirectory;
143 BOOL directoryExists = [defaultManager fileExistsAtPath:subdirDirectoryPath isDirectory:&directoryIsDirectory];
144 NSError *error = nil;
145 if (!directoryExists) {
146 [defaultManager createDirectoryAtPath:subdirDirectoryPath withIntermediateDirectories:YES attributes:nil error:&error];
148 NSLog(@"Cannot create directory at '%@': %@", subdirDirectoryPath, error);
149 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
150 [alert setMessageText:@"Create Directory Error"];
151 [alert setInformativeText:[NSString stringWithFormat:@"Cannot create directory at '%@': %@",
152 subdirDirectoryPath, error]];
153 [alert addButtonWithTitle:@"OK"];
156 } else if (!directoryIsDirectory) {
157 [defaultManager removeItemAtPath:subdirDirectoryPath error:&error];
159 NSLog(@"Cannot remove existing file at '%@': %@", subdirDirectoryPath, error);
160 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
161 [alert setMessageText:@"Remove File Error"];
162 [alert setInformativeText:[NSString stringWithFormat:@"Cannot remove existing file at '%@': %@",
163 subdirDirectoryPath, error]];
164 [alert addButtonWithTitle:@"OK"];
169 NSString *fileName = [object.name lastPathComponent];
170 if([object.name hasSuffix:@"/"])
171 fileName = [fileName stringByAppendingString:@"/"];
173 NSString *objectDirectoryPath = [directoryPath stringByAppendingPathComponent:subdirName];
174 objectDirectoryPath = [objectDirectoryPath stringByAppendingPathComponent:[object.name substringWithRange:NSMakeRange(subdirPrefixLength, [object.name length] - subdirPrefixLength - [fileName length])]];
176 [objectRequests addObject:[self objectDataRequestWithContainerName:containerName
177 objectName:object.name
178 toDirectory:objectDirectoryPath
183 return objectRequests;
189 + (ASIPithosObjectRequest *)writeObjectDataRequestWithContainerName:(NSString *)containerName
190 objectName:(NSString *)objectName
191 contentType:(NSString *)contentType
192 blockSize:(NSUInteger)blockSize
193 blockHash:(NSString *)blockHash
194 forFile:(NSString *)filePath
195 checkIfExists:(BOOL)ifExists
196 hashes:(NSArray **)hashes {
197 if (ifExists && ![self proceedIfObjectExistsAtContainerName:containerName objectName:objectName])
201 *hashes = [HashMapHash calculateObjectHashMap:filePath withBlockHash:blockHash andBlockSize:blockSize];
204 NSUInteger bytes = [self bytesOfFile:filePath];
205 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
206 objectName:objectName
207 contentType:contentType
209 contentDisposition:nil
212 isPublic:ASIPithosObjectRequestPublicIgnore
218 return objectRequest;
221 + (NSIndexSet *)missingBlocksForHashes:(NSArray *)hashes withMissingHashesResponse:(NSString *)missingHashesResponse {
222 NSArray *responseLines = [missingHashesResponse componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
223 NSMutableIndexSet *missingBlocks = [NSMutableIndexSet indexSet];
224 for (NSString *line in responseLines) {
227 NSUInteger missingBlock = [hashes indexOfObject:line];
228 if (missingBlock != NSNotFound)
229 [missingBlocks addIndex:missingBlock];
231 return missingBlocks;
234 + (ASIPithosObjectRequest *)updateObjectDataRequestWithContainerName:(NSString *)containerName
235 objectName:(NSString *)objectName
236 blockSize:(NSUInteger)blockSize
237 forFile:(NSString *)filePath
238 hashes:(NSArray *)hashes
239 missingHashesResponse:(NSString *)missingHashesResponse {
240 NSIndexSet *missingBlocks = [self missingBlocksForHashes:hashes withMissingHashesResponse:missingHashesResponse];
242 NSFileManager *defaultManager = [NSFileManager defaultManager];
243 NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
245 // http://cocoawithlove.com/2009/07/temporary-files-and-folders-in-cocoa.html
246 NSString *tempFileTemplate = NSTemporaryDirectory();
247 if (tempFileTemplate == nil)
248 tempFileTemplate = @"/tmp";
249 tempFileTemplate = [tempFileTemplate stringByAppendingPathComponent:@"pithos-macos.XXXXXX"];
250 const char *tempFileTemplateCString = [tempFileTemplate fileSystemRepresentation];
251 char *tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1);
252 strcpy(tempFileNameCString, tempFileTemplateCString);
253 int fileDescriptor = mkstemp(tempFileNameCString);
254 NSString *tempFilePath = [defaultManager stringWithFileSystemRepresentation:tempFileNameCString length:strlen(tempFileNameCString)];
255 if (fileDescriptor == -1) {
256 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
257 [alert setMessageText:@"Create Temporary File Error"];
258 [alert setInformativeText:[NSString stringWithFormat:@"Cannot create temporary file at '%@'", tempFilePath]];
259 [alert addButtonWithTitle:@"OK"];
263 free(tempFileNameCString);
264 NSFileHandle *tempFileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fileDescriptor closeOnDealloc:YES];
266 [missingBlocks enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
267 [fileHandle seekToFileOffset:(idx*blockSize)];
268 [tempFileHandle writeData:[fileHandle readDataOfLength:blockSize]];
270 [tempFileHandle closeFile];
271 [fileHandle closeFile];
273 return [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
274 objectName:objectName
276 contentType:@"application/octet-stream"
278 contentDisposition:nil
281 isPublic:ASIPithosObjectRequestPublicIgnore
286 + (ASIPithosObjectRequest *)updateObjectDataRequestWithContainerName:(NSString *)containerName
287 objectName:(NSString *)objectName
288 blockSize:(NSUInteger)blockSize
289 forFile:(NSString *)filePath
290 missingBlockIndex:(NSUInteger)missingBlockIndex {
291 NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
292 [fileHandle seekToFileOffset:(missingBlockIndex *blockSize)];
293 NSData *blockData = [fileHandle readDataOfLength:blockSize];
294 [fileHandle closeFile];
295 return [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
296 objectName:objectName
298 contentType:@"application/octet-stream"
300 contentDisposition:nil
303 isPublic:ASIPithosObjectRequestPublicIgnore
308 + (NSArray *)writeObjectDataRequestsWithContainerName:(NSString *)containerName
309 objectName:(NSString *)objectName
310 blockSize:(NSUInteger)blockSize
311 blockHash:(NSString *)blockHash
312 forDirectory:(NSString *)directoryPath
313 checkIfExists:(BOOL)ifExists
314 objectNames:(NSMutableArray **)objectNames
315 contentTypes:(NSMutableArray **)contentTypes
316 filePaths:(NSMutableArray **)filePaths
317 hashesArrays:(NSMutableArray **)hashesArrays
318 directoryObjectRequests:(NSMutableArray **) directoryObjectRequests {
319 if (ifExists && ![self proceedIfObjectExistsAtContainerName:containerName objectName:objectName])
322 NSFileManager *defaultManager = [NSFileManager defaultManager];
323 NSError *error = nil;
324 NSArray *subPaths = [defaultManager subpathsOfDirectoryAtPath:directoryPath error:&error];
326 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
327 [alert setMessageText:@"Directory Read Error"];
328 [alert setInformativeText:[NSString stringWithFormat:@"Cannot read contents of directory '%@': %@",
329 [directoryPath lastPathComponent], error]];
330 [alert addButtonWithTitle:@"OK"];
335 *directoryObjectRequests = [NSMutableArray arrayWithCapacity:[subPaths count]];
336 [*directoryObjectRequests addObject:[ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
337 objectName:objectName
339 contentType:@"application/directory"
341 contentDisposition:nil
344 isPublic:ASIPithosObjectRequestPublicIgnore
346 data:[NSData data]]];
349 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:[subPaths count]];
350 *objectNames = [NSMutableArray arrayWithCapacity:[subPaths count]];
351 *contentTypes = [NSMutableArray arrayWithCapacity:[subPaths count]];
352 *filePaths = [NSMutableArray arrayWithCapacity:[subPaths count]];
353 *hashesArrays = [NSMutableArray arrayWithCapacity:[subPaths count]];
355 NSString *subObjectName;
358 NSString *contentType;
360 for (NSString *objectNameSuffix in subPaths) {
361 filePath = [directoryPath stringByAppendingPathComponent:objectNameSuffix];
362 if ([defaultManager fileExistsAtPath:filePath isDirectory:&isDirectory]) {
364 hashes = [HashMapHash calculateObjectHashMap:filePath withBlockHash:blockHash andBlockSize:blockSize];
366 subObjectName = [objectName stringByAppendingPathComponent:objectNameSuffix];
367 bytes = [self bytesOfFile:filePath];
369 contentType = [self contentTypeOfFile:filePath error:&error];
370 if (contentType == nil)
371 contentType = @"application/octet-stream";
373 NSLog(@"contentType detection error: %@", error);
374 [objectRequests addObject:[ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
375 objectName:subObjectName
376 contentType:contentType
378 contentDisposition:nil
381 isPublic:ASIPithosObjectRequestPublicIgnore
387 [*objectNames addObject:subObjectName];
388 [*contentTypes addObject:contentType];
389 [*filePaths addObject:filePath];
390 [*hashesArrays addObject:hashes];
394 subObjectName = [objectName stringByAppendingPathComponent:objectNameSuffix];
395 [*directoryObjectRequests addObject:[ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
396 objectName:subObjectName
398 contentType:@"application/directory"
400 contentDisposition:nil
403 isPublic:ASIPithosObjectRequestPublicIgnore
405 data:[NSData data]]];
410 return objectRequests;
416 + (NSArray *)deleteObjectRequestsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName {
417 NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil];
421 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
423 if (![objectName hasSuffix:@"/"])
424 [objectRequests addObject:[ASIPithosObjectRequest deleteObjectRequestWithContainerName:containerName objectName:objectName]];
425 for (ASIPithosObject *object in objects) {
426 [objectRequests addObject:[ASIPithosObjectRequest deleteObjectRequestWithContainerName:containerName objectName:object.name]];
429 if ([objectRequests count] == 0)
431 return objectRequests;
437 + (ASIPithosObjectRequest *)copyObjectRequestWithContainerName:(NSString *)containerName
438 objectName:(NSString *)objectName
439 destinationContainerName:(NSString *)destinationContainerName
440 destinationObjectName:(NSString *)destinationObjectName
441 checkIfExists:(BOOL)ifExists {
442 if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName])
445 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
446 objectName:objectName
449 contentDisposition:nil
452 isPublic:ASIPithosObjectRequestPublicIgnore
454 destinationContainerName:destinationContainerName
455 destinationObjectName:destinationObjectName
457 return objectRequest;
460 + (NSArray *)copyObjectRequestsForSubdirWithContainerName:(NSString *)containerName
461 objectName:(NSString *)objectName
462 destinationContainerName:(NSString *)destinationContainerName
463 destinationObjectName:(NSString *)destinationObjectName
464 checkIfExists:(BOOL)ifExists {
465 if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName])
468 NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil];
472 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
473 if ([objectName isEqualToString:destinationObjectName]) {
474 if (![objectName hasSuffix:@"/"])
475 [objectRequests addObject:[ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
476 objectName:objectName
479 contentDisposition:nil
482 isPublic:ASIPithosObjectRequestPublicIgnore
484 destinationContainerName:destinationContainerName
485 destinationObjectName:objectName
487 for (ASIPithosObject *object in objects) {
488 [objectRequests addObject:[ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
489 objectName:object.name
492 contentDisposition:nil
495 isPublic:ASIPithosObjectRequestPublicIgnore
497 destinationContainerName:destinationContainerName
498 destinationObjectName:object.name
502 if (![objectName hasSuffix:@"/"]) {
503 [objectRequests addObject:[ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
504 objectName:objectName
507 contentDisposition:nil
510 isPublic:ASIPithosObjectRequestPublicIgnore
512 destinationContainerName:destinationContainerName
513 destinationObjectName:destinationObjectName
516 NSRange prefixRange = NSMakeRange(0, [objectName length]);
517 NSString *newObjectName;
518 for (ASIPithosObject *object in objects) {
519 newObjectName = [object.name stringByReplacingOccurrencesOfString:objectName
520 withString:destinationObjectName
521 options:NSAnchoredSearch
523 [objectRequests addObject:[ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
524 objectName:object.name
527 contentDisposition:nil
530 isPublic:ASIPithosObjectRequestPublicIgnore
532 destinationContainerName:destinationContainerName
533 destinationObjectName:newObjectName
538 if ([objectRequests count] == 0)
540 return objectRequests;
546 + (ASIPithosObjectRequest *)moveObjectRequestWithContainerName:(NSString *)containerName
547 objectName:(NSString *)objectName
548 destinationContainerName:(NSString *)destinationContainerName
549 destinationObjectName:(NSString *)destinationObjectName
550 checkIfExists:(BOOL)ifExists {
551 if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName])
554 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
555 objectName:objectName
558 contentDisposition:nil
561 isPublic:ASIPithosObjectRequestPublicIgnore
563 destinationContainerName:destinationContainerName
564 destinationObjectName:destinationObjectName];
565 return objectRequest;
568 + (NSArray *)moveObjectRequestsForSubdirWithContainerName:(NSString *)containerName
569 objectName:(NSString *)objectName
570 destinationContainerName:(NSString *)destinationContainerName
571 destinationObjectName:(NSString *)destinationObjectName
572 checkIfExists:(BOOL)ifExists {
573 if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName])
576 NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil];
580 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
581 if ([objectName isEqualToString:destinationObjectName]) {
582 if (![objectName hasSuffix:@"/"])
583 [objectRequests addObject:[ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
584 objectName:objectName
587 contentDisposition:nil
590 isPublic:ASIPithosObjectRequestPublicIgnore
592 destinationContainerName:destinationContainerName
593 destinationObjectName:objectName]];
594 for (ASIPithosObject *object in objects) {
595 [objectRequests addObject:[ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
596 objectName:object.name
599 contentDisposition:nil
602 isPublic:ASIPithosObjectRequestPublicIgnore
604 destinationContainerName:destinationContainerName
605 destinationObjectName:object.name]];
608 if (![objectName hasSuffix:@"/"]) {
609 [objectRequests addObject:[ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
610 objectName:objectName
613 contentDisposition:nil
616 isPublic:ASIPithosObjectRequestPublicIgnore
618 destinationContainerName:destinationContainerName
619 destinationObjectName:destinationObjectName]];
621 NSRange prefixRange = NSMakeRange(0, [objectName length]);
622 NSString *newObjectName;
623 for (ASIPithosObject *object in objects) {
624 newObjectName = [object.name stringByReplacingOccurrencesOfString:objectName
625 withString:destinationObjectName
626 options:NSAnchoredSearch
628 [objectRequests addObject:[ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
629 objectName:object.name
632 contentDisposition:nil
635 isPublic:ASIPithosObjectRequestPublicIgnore
637 destinationContainerName:destinationContainerName
638 destinationObjectName:newObjectName]];
642 if ([objectRequests count] == 0)
644 return objectRequests;
648 #pragma mark Helper Methods
650 // Size of the file in bytes
651 + (NSUInteger)bytesOfFile:(NSString *)filePath {
652 NSFileManager *defaultManager = [NSFileManager defaultManager];
653 NSDictionary *attributes = [defaultManager attributesOfItemAtPath:filePath error:nil];
654 return [[attributes objectForKey:NSFileSize] intValue];
657 // Content type of the file or nil if it cannot be determined
658 + (NSString *)contentTypeOfFile:(NSString *)filePath error:(NSError **)error {
659 NSURLResponse *response = nil;
660 [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath]
661 cachePolicy:NSURLCacheStorageNotAllowed
663 returningResponse:&response
665 return [response MIMEType];
668 // Returns if an object exists at the given container/object path and if this object is an application/directory
669 // If an error occured an alert is shown and it is returned so the caller won't proceed
670 + (BOOL)objectExistsAtContainerName:(NSString *)containerName objectName:(NSString *)objectName
671 error:(NSError **)error isDirectory:(BOOL *)isDirectory {
672 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectMetadataRequestWithContainerName:containerName
673 objectName:objectName];
674 [objectRequest startSynchronous];
675 *error = [objectRequest error];
677 [self httpRequestErrorAlertWithRequest:objectRequest];
679 } else if (objectRequest.responseStatusCode == 200) {
680 *isDirectory = [[objectRequest contentType] isEqualToString:@"application/directory"];
686 // Returns if the called should proceed, after an interactive check if an object exists
687 // at the given container/object path is performed
688 + (BOOL)proceedIfObjectExistsAtContainerName:(NSString *)containerName objectName:(NSString *)objectName {
689 NSError *error = nil;
691 BOOL objectExists = [self objectExistsAtContainerName:containerName
692 objectName:objectName
694 isDirectory:&isDirectory];
697 } else if (objectExists) {
698 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
700 [alert setMessageText:@"Directory Exists"];
701 [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
703 [alert setMessageText:@"Object Exists"];
704 [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
706 [alert addButtonWithTitle:@"OK"];
707 [alert addButtonWithTitle:@"Cancel"];
708 NSInteger choice = [alert runModal];
709 if (choice == NSAlertSecondButtonReturn)
716 // List of objects at the given container/object path, with prefix and or delimiter
717 + (NSArray *)objectsWithContainerName:(NSString *)containerName objectNamePrefix:(NSString *)objectNamePrefix
718 delimiter:(NSString *)delimiter {
719 NSMutableArray *objects = [NSMutableArray array];
720 NSString *marker = nil;
722 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest listObjectsRequestWithContainerName:containerName
725 prefix:objectNamePrefix
731 [containerRequest startSynchronous];
732 if ([containerRequest error]) {
733 [self httpRequestErrorAlertWithRequest:containerRequest];
736 NSArray *someObjects = [containerRequest objects];
737 [objects addObjectsFromArray:someObjects];
738 if ([someObjects count] < 10000)
741 marker = [[someObjects lastObject] name];
746 // List of objects at the given container/object path, that may be a subdir or an application/directory,
747 // with prefix and or delimiter
748 + (NSArray *)objectsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName
749 delimiter:(NSString *)delimiter {
750 NSString *subdirNamePrefix = [NSString stringWithString:objectName];
751 if (![subdirNamePrefix hasSuffix:@"/"])
752 subdirNamePrefix = [subdirNamePrefix stringByAppendingString:@"/"];
753 return [self objectsWithContainerName:containerName objectNamePrefix:subdirNamePrefix delimiter:delimiter];
756 // A safe object name at the given container/object path
757 // The original name has " %d" appended to it before any ".*" suffix, for the first integer that produces a name that is free to use
758 // If the original name hasn't got a "." but has a "/" suffix, then it is replaced with " %d/" instead
759 // Subdirs are taken into consideration
760 + (NSString *)safeObjectNameForContainerName:(NSString *)containerName objectName:(NSString *)objectName {
761 NSString *objectNamePrefix;
762 NSString *objectNameExtraSuffix;
763 NSRange lastDotRange = [objectName rangeOfString:@"." options:NSBackwardsSearch];
764 if (lastDotRange.length == 1) {
765 objectNamePrefix = [objectName substringToIndex:lastDotRange.location];
766 objectNameExtraSuffix = [objectName substringFromIndex:lastDotRange.location];
767 } else if ([objectName hasSuffix:@"/"]) {
768 objectNamePrefix = [objectName substringToIndex:([objectName length] - 1)];
769 objectNameExtraSuffix = [NSString stringWithString:@"/"];
771 objectNamePrefix = [NSString stringWithString:objectName];
772 objectNameExtraSuffix = [NSString string];
774 NSArray *objects = [self objectsWithContainerName:containerName
775 objectNamePrefix:[objectNamePrefix stringByDeletingLastPathComponent]
779 if ([objects count] == 0)
781 NSMutableArray *objectNames = [NSMutableArray arrayWithArray:
782 [[objects objectsAtIndexes:
783 [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
784 if (pithosObject.subdir)
787 }]] valueForKey:@"name"]];
788 for (NSString *name in [[objects objectsAtIndexes:
789 [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
790 if (pithosObject.subdir)
793 }]] valueForKey:@"name"]) {
794 [objectNames addObject:[name substringToIndex:([name length] - 1)]];
796 if (![objectNames containsObject:objectName])
798 NSUInteger objectNameSuffix = 2;
799 NSString *safeObjectName;
801 safeObjectName = [objectNamePrefix stringByAppendingFormat:@" %lu%@", objectNameSuffix, objectNameExtraSuffix];
803 } while ([objectNames containsObject:safeObjectName]);
804 return safeObjectName;
807 // A safe object name at the given container/object path that may be a subdir or application/directory
808 // The original name has " %d" appended to it, for the first integer that produces a name that is free to use
809 // If the original name has a "/" suffix, then it is replaced with " %d/" instead
810 // Subdirs are taken into consideration
811 + (NSString *)safeSubdirNameForContainerName:(NSString *)containerName subdirName:(NSString *)subdirName {
812 NSString *subdirNamePrefix;
813 NSString *subdirNameExtraSuffix;
814 if ([subdirName hasSuffix:@"/"]) {
815 subdirNamePrefix = [subdirName substringToIndex:([subdirName length] - 1)];
816 subdirNameExtraSuffix = [NSString stringWithString:@"/"];
818 subdirNamePrefix = [NSString stringWithString:subdirName];
819 subdirNameExtraSuffix = [NSString string];
821 NSArray *objects = [self objectsWithContainerName:containerName
822 objectNamePrefix:[subdirNamePrefix stringByDeletingLastPathComponent]
826 if ([objects count] == 0)
828 NSMutableArray *objectNames = [NSMutableArray arrayWithArray:
829 [[objects objectsAtIndexes:
830 [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
831 if (pithosObject.subdir)
834 }]] valueForKey:@"name"]];
835 for (NSString *name in [[objects objectsAtIndexes:
836 [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
837 if (pithosObject.subdir)
840 }]] valueForKey:@"name"]) {
841 [objectNames addObject:[name substringToIndex:([name length] - 1)]];
843 if (![objectNames containsObject:subdirNamePrefix])
845 NSUInteger subdirNameSuffix = 2;
846 NSString *safeSubdirName;
848 safeSubdirName = [subdirNamePrefix stringByAppendingFormat:@" %lu", subdirNameSuffix];
850 } while ([objectNames containsObject:safeSubdirName]);
851 return [safeSubdirName stringByAppendingString:subdirNameExtraSuffix];
857 + (NSInteger)httpRequestErrorAlertWithRequest:(ASIPithosRequest *)request {
858 NSString *message = [NSString stringWithFormat:
859 @"HTTP request error: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@",
861 request.requestMethod,
863 [request requestHeaders],
864 [request responseHeaders],
865 [request responseString]];
866 NSLog(@"%@", message);
867 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
868 [alert setMessageText:@"HTTP Request Error"];
869 [alert setInformativeText:message];
870 [alert addButtonWithTitle:@"OK"];
871 return [alert runModal];
874 + (NSInteger)unexpectedResponseStatusAlertWithRequest:(ASIPithosRequest *)request {
875 NSString *message = [NSString stringWithFormat:
876 @"Unexpected response status %d: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@",
877 request.responseStatusCode,
878 request.responseStatusMessage,
879 request.requestMethod,
881 [request requestHeaders],
882 [request responseHeaders],
883 [request responseString]];
884 NSLog(@"%@", message);
885 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
886 [alert setMessageText:@"Unexpected Response Status"];
887 [alert setInformativeText:message];
888 [alert addButtonWithTitle:@"OK"];
889 return [alert runModal];