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 = [NSMutableDictionary 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 ASIPithosObjectRequest *objectRequest = [self objectDataRequestWithContainerName:containerName
182 objectName:object.name
183 toDirectory:objectDirectoryPath
185 sharingAccount:sharingAccount];
186 [(NSMutableDictionary *)objectRequest.userInfo setObject:[NSNumber numberWithUnsignedInteger:object.bytes] forKey:@"bytes"];
187 [objectRequests addObject:objectRequest];
191 return objectRequests;
197 + (ASIPithosObjectRequest *)writeObjectDataRequestWithContainerName:(NSString *)containerName
198 objectName:(NSString *)objectName
199 contentType:(NSString *)contentType
200 blockSize:(NSUInteger)blockSize
201 blockHash:(NSString *)blockHash
202 forFile:(NSString *)filePath
203 checkIfExists:(BOOL)ifExists
204 hashes:(NSArray **)hashes
205 sharingAccount:(NSString *)sharingAccount {
206 if (ifExists && ![self proceedIfObjectExistsAtContainerName:containerName objectName:objectName
207 sharingAccount:(NSString *)sharingAccount])
211 *hashes = [HashMapHash calculateObjectHashMap:filePath withBlockHash:blockHash andBlockSize:blockSize];
214 NSString *fileName = [filePath lastPathComponent];
215 if ([filePath hasSuffix:@"/"])
216 fileName = [fileName stringByAppendingString:@"/"];
217 NSUInteger bytes = [self bytesOfFile:filePath];
218 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
219 objectName:objectName
220 contentType:contentType
222 contentDisposition:nil
225 isPublic:ASIPithosObjectRequestPublicIgnore
232 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
233 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
234 fileName, @"fileName",
235 [NSNumber numberWithUnsignedInteger:bytes], @"bytes",
237 return objectRequest;
240 + (NSIndexSet *)missingBlocksForHashes:(NSArray *)hashes withMissingHashesResponse:(NSString *)missingHashesResponse {
241 NSArray *responseLines = [missingHashesResponse componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
242 NSMutableIndexSet *missingBlocks = [NSMutableIndexSet indexSet];
243 for (NSString *line in responseLines) {
246 NSUInteger missingBlock = [hashes indexOfObject:line];
247 if (missingBlock != NSNotFound)
248 [missingBlocks addIndex:missingBlock];
250 return missingBlocks;
253 + (ASIPithosContainerRequest *)updateContainerDataRequestWithContainerName:(NSString *)containerName
254 blockSize:(NSUInteger)blockSize
255 forFile:(NSString *)filePath
256 hashes:(NSArray *)hashes
257 missingHashesResponse:(NSString *)missingHashesResponse
258 sharingAccount:(NSString *)sharingAccount {
259 NSIndexSet *missingBlocks = [self missingBlocksForHashes:hashes withMissingHashesResponse:missingHashesResponse];
261 NSFileManager *defaultManager = [NSFileManager defaultManager];
262 NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
264 // http://cocoawithlove.com/2009/07/temporary-files-and-folders-in-cocoa.html
265 NSString *tempFileTemplate = NSTemporaryDirectory();
266 if (tempFileTemplate == nil)
267 tempFileTemplate = @"/tmp";
268 tempFileTemplate = [tempFileTemplate stringByAppendingPathComponent:@"pithos-macos.XXXXXX"];
269 const char *tempFileTemplateCString = [tempFileTemplate fileSystemRepresentation];
270 char *tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1);
271 strcpy(tempFileNameCString, tempFileTemplateCString);
272 int fileDescriptor = mkstemp(tempFileNameCString);
273 NSString *tempFilePath = [defaultManager stringWithFileSystemRepresentation:tempFileNameCString length:strlen(tempFileNameCString)];
274 if (fileDescriptor == -1) {
275 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
276 [alert setMessageText:@"Create Temporary File Error"];
277 [alert setInformativeText:[NSString stringWithFormat:@"Cannot create temporary file at '%@'", tempFilePath]];
278 [alert addButtonWithTitle:@"OK"];
282 free(tempFileNameCString);
283 NSFileHandle *tempFileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fileDescriptor closeOnDealloc:YES];
285 [missingBlocks enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
286 [fileHandle seekToFileOffset:(idx*blockSize)];
287 [tempFileHandle writeData:[fileHandle readDataOfLength:blockSize]];
289 [tempFileHandle closeFile];
290 [fileHandle closeFile];
292 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest updateContainerDataRequestWithContainerName:containerName
298 [containerRequest setRequestUserFromDefaultTo:sharingAccount];
299 return containerRequest;
302 + (ASIPithosContainerRequest *)updateContainerDataRequestWithContainerName:(NSString *)containerName
303 blockSize:(NSUInteger)blockSize
304 forFile:(NSString *)filePath
305 missingBlockIndex:(NSUInteger)missingBlockIndex
306 sharingAccount:(NSString *)sharingAccount {
307 NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
308 [fileHandle seekToFileOffset:(missingBlockIndex *blockSize)];
309 NSData *blockData = [fileHandle readDataOfLength:blockSize];
310 [fileHandle closeFile];
311 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest updateContainerDataRequestWithContainerName:containerName
317 [containerRequest setRequestUserFromDefaultTo:sharingAccount];
318 return containerRequest;
321 + (NSArray *)writeObjectDataRequestsWithContainerName:(NSString *)containerName
322 objectName:(NSString *)objectName
323 blockSize:(NSUInteger)blockSize
324 blockHash:(NSString *)blockHash
325 forDirectory:(NSString *)directoryPath
326 checkIfExists:(BOOL)ifExists
327 objectNames:(NSMutableArray **)objectNames
328 contentTypes:(NSMutableArray **)contentTypes
329 filePaths:(NSMutableArray **)filePaths
330 hashesArrays:(NSMutableArray **)hashesArrays
331 directoryObjectRequests:(NSMutableArray **) directoryObjectRequests
332 sharingAccount:(NSString *)sharingAccount {
333 if (ifExists && ![self proceedIfObjectExistsAtContainerName:containerName objectName:objectName sharingAccount:sharingAccount])
336 NSFileManager *defaultManager = [NSFileManager defaultManager];
337 NSError *error = nil;
338 NSArray *subPaths = [defaultManager subpathsOfDirectoryAtPath:directoryPath error:&error];
340 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
341 [alert setMessageText:@"Directory Read Error"];
342 [alert setInformativeText:[NSString stringWithFormat:@"Cannot read contents of directory '%@': %@",
343 [directoryPath lastPathComponent], error]];
344 [alert addButtonWithTitle:@"OK"];
349 *directoryObjectRequests = [NSMutableArray arrayWithCapacity:[subPaths count]];
350 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
351 objectName:objectName
353 contentType:@"application/directory"
355 contentDisposition:nil
358 isPublic:ASIPithosObjectRequestPublicIgnore
362 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
363 [*directoryObjectRequests addObject:objectRequest];
365 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:[subPaths count]];
366 *objectNames = [NSMutableArray arrayWithCapacity:[subPaths count]];
367 *contentTypes = [NSMutableArray arrayWithCapacity:[subPaths count]];
368 *filePaths = [NSMutableArray arrayWithCapacity:[subPaths count]];
369 *hashesArrays = [NSMutableArray arrayWithCapacity:[subPaths count]];
371 NSString *subObjectName;
374 NSString *contentType;
377 for (NSString *objectNameSuffix in subPaths) {
378 filePath = [directoryPath stringByAppendingPathComponent:objectNameSuffix];
379 if ([defaultManager fileExistsAtPath:filePath isDirectory:&isDirectory]) {
381 hashes = [HashMapHash calculateObjectHashMap:filePath withBlockHash:blockHash andBlockSize:blockSize];
383 subObjectName = [objectName stringByAppendingPathComponent:objectNameSuffix];
384 fileName = [filePath lastPathComponent];
385 if ([filePath hasSuffix:@"/"])
386 fileName = [fileName stringByAppendingString:@"/"];
387 bytes = [self bytesOfFile:filePath];
389 contentType = [self contentTypeOfFile:filePath error:&error];
390 if (contentType == nil)
391 contentType = @"application/octet-stream";
393 NSLog(@"contentType detection error: %@", error);
394 objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
395 objectName:subObjectName
396 contentType:contentType
398 contentDisposition:nil
401 isPublic:ASIPithosObjectRequestPublicIgnore
408 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
409 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
410 fileName, @"fileName",
411 [NSNumber numberWithUnsignedInteger:bytes], @"bytes",
413 [objectRequests addObject:objectRequest];
414 [*objectNames addObject:subObjectName];
415 [*contentTypes addObject:contentType];
416 [*filePaths addObject:filePath];
417 [*hashesArrays addObject:hashes];
421 subObjectName = [objectName stringByAppendingPathComponent:objectNameSuffix];
422 objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
423 objectName:subObjectName
425 contentType:@"application/directory"
427 contentDisposition:nil
430 isPublic:ASIPithosObjectRequestPublicIgnore
434 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
435 [*directoryObjectRequests addObject:objectRequest];
440 return objectRequests;
446 + (NSArray *)deleteObjectRequestsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName {
447 NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil sharingAccount:nil];
451 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
453 if (![objectName hasSuffix:@"/"])
454 [objectRequests addObject:[ASIPithosObjectRequest deleteObjectRequestWithContainerName:containerName objectName:objectName]];
455 for (ASIPithosObject *object in objects) {
456 [objectRequests addObject:[ASIPithosObjectRequest deleteObjectRequestWithContainerName:containerName objectName:object.name]];
459 if ([objectRequests count] == 0)
461 return objectRequests;
467 + (ASIPithosObjectRequest *)copyObjectRequestWithContainerName:(NSString *)containerName
468 objectName:(NSString *)objectName
469 destinationContainerName:(NSString *)destinationContainerName
470 destinationObjectName:(NSString *)destinationObjectName
471 checkIfExists:(BOOL)ifExists
472 sharingAccount:(NSString *)sharingAccount {
473 if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
476 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
477 objectName:objectName
480 contentDisposition:nil
483 isPublic:ASIPithosObjectRequestPublicIgnore
485 destinationContainerName:destinationContainerName
486 destinationObjectName:destinationObjectName
487 destinationAccount:nil
490 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
491 return objectRequest;
494 + (NSArray *)copyObjectRequestsForSubdirWithContainerName:(NSString *)containerName
495 objectName:(NSString *)objectName
496 destinationContainerName:(NSString *)destinationContainerName
497 destinationObjectName:(NSString *)destinationObjectName
498 checkIfExists:(BOOL)ifExists
499 sharingAccount:(NSString *)sharingAccount {
500 if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
503 NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName
504 delimiter:nil sharingAccount:sharingAccount];
508 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
509 ASIPithosObjectRequest *objectRequest;
510 if ([objectName isEqualToString:destinationObjectName]) {
511 if (![objectName hasSuffix:@"/"]) {
512 objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
513 objectName:objectName
516 contentDisposition:nil
519 isPublic:ASIPithosObjectRequestPublicIgnore
521 destinationContainerName:destinationContainerName
522 destinationObjectName:objectName
523 destinationAccount:nil
526 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
527 [objectRequests addObject:objectRequest];
529 for (ASIPithosObject *object in objects) {
530 objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
531 objectName:object.name
534 contentDisposition:nil
537 isPublic:ASIPithosObjectRequestPublicIgnore
539 destinationContainerName:destinationContainerName
540 destinationObjectName:object.name
541 destinationAccount:nil
544 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
545 [objectRequests addObject:objectRequest];
548 if (![objectName hasSuffix:@"/"]) {
549 objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
550 objectName:objectName
553 contentDisposition:nil
556 isPublic:ASIPithosObjectRequestPublicIgnore
558 destinationContainerName:destinationContainerName
559 destinationObjectName:destinationObjectName
560 destinationAccount:nil
563 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
564 [objectRequests addObject:objectRequest];
566 NSRange prefixRange = NSMakeRange(0, [objectName length]);
567 NSString *newObjectName;
568 for (ASIPithosObject *object in objects) {
569 newObjectName = [object.name stringByReplacingOccurrencesOfString:objectName
570 withString:destinationObjectName
571 options:NSAnchoredSearch
573 objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
574 objectName:object.name
577 contentDisposition:nil
580 isPublic:ASIPithosObjectRequestPublicIgnore
582 destinationContainerName:destinationContainerName
583 destinationObjectName:newObjectName
584 destinationAccount:nil
587 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
588 [objectRequests addObject:objectRequest];
592 if ([objectRequests count] == 0)
594 return objectRequests;
600 + (ASIPithosObjectRequest *)moveObjectRequestWithContainerName:(NSString *)containerName
601 objectName:(NSString *)objectName
602 destinationContainerName:(NSString *)destinationContainerName
603 destinationObjectName:(NSString *)destinationObjectName
604 checkIfExists:(BOOL)ifExists {
605 if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
608 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
609 objectName:objectName
612 contentDisposition:nil
615 isPublic:ASIPithosObjectRequestPublicIgnore
617 destinationContainerName:destinationContainerName
618 destinationObjectName:destinationObjectName
619 destinationAccount:nil];
620 return objectRequest;
623 + (NSArray *)moveObjectRequestsForSubdirWithContainerName:(NSString *)containerName
624 objectName:(NSString *)objectName
625 destinationContainerName:(NSString *)destinationContainerName
626 destinationObjectName:(NSString *)destinationObjectName
627 checkIfExists:(BOOL)ifExists {
628 if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
631 NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil sharingAccount:nil];
635 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
636 if ([objectName isEqualToString:destinationObjectName]) {
637 if (![objectName hasSuffix:@"/"])
638 [objectRequests addObject:[ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
639 objectName:objectName
642 contentDisposition:nil
645 isPublic:ASIPithosObjectRequestPublicIgnore
647 destinationContainerName:destinationContainerName
648 destinationObjectName:objectName
649 destinationAccount:nil]];
650 for (ASIPithosObject *object in objects) {
651 [objectRequests addObject:[ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
652 objectName:object.name
655 contentDisposition:nil
658 isPublic:ASIPithosObjectRequestPublicIgnore
660 destinationContainerName:destinationContainerName
661 destinationObjectName:object.name
662 destinationAccount:nil]];
665 if (![objectName hasSuffix:@"/"]) {
666 [objectRequests addObject:[ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
667 objectName:objectName
670 contentDisposition:nil
673 isPublic:ASIPithosObjectRequestPublicIgnore
675 destinationContainerName:destinationContainerName
676 destinationObjectName:destinationObjectName
677 destinationAccount:nil]];
679 NSRange prefixRange = NSMakeRange(0, [objectName length]);
680 NSString *newObjectName;
681 for (ASIPithosObject *object in objects) {
682 newObjectName = [object.name stringByReplacingOccurrencesOfString:objectName
683 withString:destinationObjectName
684 options:NSAnchoredSearch
686 [objectRequests addObject:[ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
687 objectName:object.name
690 contentDisposition:nil
693 isPublic:ASIPithosObjectRequestPublicIgnore
695 destinationContainerName:destinationContainerName
696 destinationObjectName:newObjectName
697 destinationAccount:nil]];
701 if ([objectRequests count] == 0)
703 return objectRequests;
707 #pragma mark Helper Methods
709 // Size of the file in bytes
710 + (NSUInteger)bytesOfFile:(NSString *)filePath {
711 NSFileManager *defaultManager = [NSFileManager defaultManager];
712 NSDictionary *attributes = [defaultManager attributesOfItemAtPath:filePath error:nil];
713 return [[attributes objectForKey:NSFileSize] intValue];
716 // Content type of the file or nil if it cannot be determined
717 + (NSString *)contentTypeOfFile:(NSString *)filePath error:(NSError **)error {
718 NSURLResponse *response = nil;
719 [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath]
720 cachePolicy:NSURLCacheStorageNotAllowed
722 returningResponse:&response
724 return [response MIMEType];
727 // Returns if an object exists at the given container/object path and if this object is an application/directory
728 // If an error occured an alert is shown and it is returned so the caller won't proceed
729 + (BOOL)objectExistsAtContainerName:(NSString *)containerName objectName:(NSString *)objectName
730 error:(NSError **)error isDirectory:(BOOL *)isDirectory
731 sharingAccount:(NSString *)sharingAccount {
732 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectMetadataRequestWithContainerName:containerName
733 objectName:objectName];
735 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
736 [objectRequest startSynchronous];
737 *error = [objectRequest error];
739 [self httpRequestErrorAlertWithRequest:objectRequest];
741 } else if (objectRequest.responseStatusCode == 200) {
742 *isDirectory = [[objectRequest contentType] isEqualToString:@"application/directory"];
748 // Returns if the called should proceed, after an interactive check if an object exists
749 // at the given container/object path is performed
750 + (BOOL)proceedIfObjectExistsAtContainerName:(NSString *)containerName objectName:(NSString *)objectName
751 sharingAccount:(NSString *)sharingAccount {
752 NSError *error = nil;
754 BOOL objectExists = [self objectExistsAtContainerName:containerName
755 objectName:objectName
757 isDirectory:&isDirectory
758 sharingAccount:sharingAccount];
761 } else if (objectExists) {
762 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
764 [alert setMessageText:@"Directory Exists"];
766 [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
768 [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
770 [alert setMessageText:@"Object Exists"];
772 [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
774 [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
776 [alert addButtonWithTitle:@"OK"];
777 [alert addButtonWithTitle:@"Cancel"];
778 NSInteger choice = [alert runModal];
779 if (choice == NSAlertSecondButtonReturn)
786 // List of objects at the given container/object path, with prefix and or delimiter
787 + (NSArray *)objectsWithContainerName:(NSString *)containerName objectNamePrefix:(NSString *)objectNamePrefix
788 delimiter:(NSString *)delimiter sharingAccount:(NSString *)sharingAccount {
789 NSMutableArray *objects = [NSMutableArray array];
790 NSString *marker = nil;
792 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest listObjectsRequestWithContainerName:containerName
795 prefix:objectNamePrefix
802 [containerRequest setRequestUserFromDefaultTo:sharingAccount];
803 [containerRequest startSynchronous];
804 if ([containerRequest error]) {
805 [self httpRequestErrorAlertWithRequest:containerRequest];
808 NSArray *someObjects = [containerRequest objects];
809 [objects addObjectsFromArray:someObjects];
810 if ([someObjects count] < 10000)
813 marker = [[someObjects lastObject] name];
818 // List of objects at the given container/object path, that may be a subdir or an application/directory,
819 // with prefix and or delimiter
820 + (NSArray *)objectsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName
821 delimiter:(NSString *)delimiter sharingAccount:(NSString *)sharingAccount {
822 NSString *subdirNamePrefix = [NSString stringWithString:objectName];
823 if (![subdirNamePrefix hasSuffix:@"/"])
824 subdirNamePrefix = [subdirNamePrefix stringByAppendingString:@"/"];
825 return [self objectsWithContainerName:containerName objectNamePrefix:subdirNamePrefix
826 delimiter:delimiter sharingAccount:sharingAccount];
829 // A safe object name at the given container/object path
830 // The original name has " %d" appended to it before any ".*" suffix, for the first integer that produces a name that is free to use
831 // If the original name hasn't got a "." but has a "/" suffix, then it is replaced with " %d/" instead
832 // Subdirs are taken into consideration
833 + (NSString *)safeObjectNameForContainerName:(NSString *)containerName objectName:(NSString *)objectName {
834 NSString *objectNamePrefix;
835 NSString *objectNameExtraSuffix;
836 NSRange lastDotRange = [objectName rangeOfString:@"." options:NSBackwardsSearch];
837 if (lastDotRange.length == 1) {
838 objectNamePrefix = [objectName substringToIndex:lastDotRange.location];
839 objectNameExtraSuffix = [objectName substringFromIndex:lastDotRange.location];
840 } else if ([objectName hasSuffix:@"/"]) {
841 objectNamePrefix = [objectName substringToIndex:([objectName length] - 1)];
842 objectNameExtraSuffix = [NSString stringWithString:@"/"];
844 objectNamePrefix = [NSString stringWithString:objectName];
845 objectNameExtraSuffix = [NSString string];
847 NSArray *objects = [self objectsWithContainerName:containerName
848 objectNamePrefix:[objectNamePrefix stringByDeletingLastPathComponent]
853 if ([objects count] == 0)
855 NSMutableArray *objectNames = [NSMutableArray arrayWithArray:
856 [[objects objectsAtIndexes:
857 [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
858 if (pithosObject.subdir)
861 }]] valueForKey:@"name"]];
862 for (NSString *name in [[objects objectsAtIndexes:
863 [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
864 if (pithosObject.subdir)
867 }]] valueForKey:@"name"]) {
868 [objectNames addObject:[name substringToIndex:([name length] - 1)]];
870 if (![objectNames containsObject:objectName])
872 NSUInteger objectNameSuffix = 2;
873 NSString *safeObjectName;
875 safeObjectName = [objectNamePrefix stringByAppendingFormat:@" %lu%@", objectNameSuffix, objectNameExtraSuffix];
877 } while ([objectNames containsObject:safeObjectName]);
878 return safeObjectName;
881 // A safe object name at the given container/object path that may be a subdir or application/directory
882 // The original name has " %d" appended to it, for the first integer that produces a name that is free to use
883 // If the original name has a "/" suffix, then it is replaced with " %d/" instead
884 // Subdirs are taken into consideration
885 + (NSString *)safeSubdirNameForContainerName:(NSString *)containerName subdirName:(NSString *)subdirName {
886 NSString *subdirNamePrefix;
887 NSString *subdirNameExtraSuffix;
888 if ([subdirName hasSuffix:@"/"]) {
889 subdirNamePrefix = [subdirName substringToIndex:([subdirName length] - 1)];
890 subdirNameExtraSuffix = [NSString stringWithString:@"/"];
892 subdirNamePrefix = [NSString stringWithString:subdirName];
893 subdirNameExtraSuffix = [NSString string];
895 NSArray *objects = [self objectsWithContainerName:containerName
896 objectNamePrefix:[subdirNamePrefix stringByDeletingLastPathComponent]
901 if ([objects count] == 0)
903 NSMutableArray *objectNames = [NSMutableArray arrayWithArray:
904 [[objects objectsAtIndexes:
905 [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
906 if (pithosObject.subdir)
909 }]] valueForKey:@"name"]];
910 for (NSString *name in [[objects objectsAtIndexes:
911 [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
912 if (pithosObject.subdir)
915 }]] valueForKey:@"name"]) {
916 [objectNames addObject:[name substringToIndex:([name length] - 1)]];
918 if (![objectNames containsObject:subdirNamePrefix])
920 NSUInteger subdirNameSuffix = 2;
921 NSString *safeSubdirName;
923 safeSubdirName = [subdirNamePrefix stringByAppendingFormat:@" %lu", subdirNameSuffix];
925 } while ([objectNames containsObject:safeSubdirName]);
926 return [safeSubdirName stringByAppendingString:subdirNameExtraSuffix];
932 + (NSInteger)httpRequestErrorAlertWithRequest:(ASIPithosRequest *)request {
933 NSString *message = [NSString stringWithFormat:
934 @"HTTP request error: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@",
936 request.requestMethod,
938 [request requestHeaders],
939 [request responseHeaders],
940 [request responseString]];
941 NSLog(@"%@", message);
942 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
943 [alert setMessageText:@"HTTP Request Error"];
944 [alert setInformativeText:message];
945 [alert addButtonWithTitle:@"OK"];
946 return [alert runModal];
949 + (NSInteger)unexpectedResponseStatusAlertWithRequest:(ASIPithosRequest *)request {
950 NSString *message = [NSString stringWithFormat:
951 @"Unexpected response status %d: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@",
952 request.responseStatusCode,
953 request.responseStatusMessage,
954 request.requestMethod,
956 [request requestHeaders],
957 [request responseHeaders],
958 [request responseString]];
959 NSLog(@"%@", message);
960 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
961 [alert setMessageText:@"Unexpected Response Status"];
962 [alert setInformativeText:message];
963 [alert addButtonWithTitle:@"OK"];
964 return [alert runModal];