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 "PithosUtilities.h"
39 #import "ASIPithosContainerRequest.h"
40 #import "ASIPithosObjectRequest.h"
41 #import "ASIPithosObject.h"
42 #import "HashMapHash.h"
44 @implementation PithosUtilities
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.allowResumeForFileDownloads = YES;
97 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
98 fileName, @"fileName",
99 destinationPath, @"filePath",
101 return objectRequest;
104 + (NSArray *)objectDataRequestsForSubdirWithContainerName:(NSString *)containerName
105 objectName:(NSString *)objectName
106 toDirectory:(NSString *)directoryPath
107 checkIfExists:(BOOL)ifExists
108 sharingAccount:(NSString *)sharingAccount {
109 NSString *subdirName = [objectName lastPathComponent];
110 NSString *destinationPath = [directoryPath stringByAppendingPathComponent:subdirName];
111 if (ifExists && [[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) {
112 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
113 [alert setMessageText:@"File exists"];
114 [alert setInformativeText:[NSString stringWithFormat:@"A file or directory named '%@' already exists, do you want to replace it?", subdirName]];
115 [alert addButtonWithTitle:@"OK"];
116 [alert addButtonWithTitle:@"Cancel"];
117 NSInteger choice = [alert runModal];
118 if (choice == NSAlertSecondButtonReturn)
122 NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName
123 delimiter:nil sharingAccount:sharingAccount];
127 NSFileManager *defaultManager = [NSFileManager defaultManager];
128 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:[objects count]];
129 NSUInteger subdirPrefixLength = [objectName length];
131 NSError *error = nil;
132 [defaultManager createDirectoryAtPath:[directoryPath stringByAppendingPathComponent:subdirName] withIntermediateDirectories:YES attributes:nil error:&error];
134 NSLog(@"Cannot create directory at '%@': %@", directoryPath, error);
135 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
136 [alert setMessageText:@"Create Directory Error"];
137 [alert setInformativeText:[NSString stringWithFormat:@"Cannot create directory at '%@': %@",
138 directoryPath, error]];
139 [alert addButtonWithTitle:@"OK"];
143 for (ASIPithosObject *object in objects) {
144 if ([object.contentType isEqualToString:@"application/directory"]) {
145 NSString *subdirDirectoryPath = [directoryPath stringByAppendingPathComponent:subdirName];
146 subdirDirectoryPath = [subdirDirectoryPath stringByAppendingPathComponent:[object.name substringFromIndex:subdirPrefixLength]];
148 BOOL directoryIsDirectory;
149 BOOL directoryExists = [defaultManager fileExistsAtPath:subdirDirectoryPath isDirectory:&directoryIsDirectory];
150 NSError *error = nil;
151 if (!directoryExists) {
152 [defaultManager createDirectoryAtPath:subdirDirectoryPath withIntermediateDirectories:YES attributes:nil error:&error];
154 NSLog(@"Cannot create directory at '%@': %@", subdirDirectoryPath, error);
155 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
156 [alert setMessageText:@"Create Directory Error"];
157 [alert setInformativeText:[NSString stringWithFormat:@"Cannot create directory at '%@': %@",
158 subdirDirectoryPath, error]];
159 [alert addButtonWithTitle:@"OK"];
162 } else if (!directoryIsDirectory) {
163 [defaultManager removeItemAtPath:subdirDirectoryPath error:&error];
165 NSLog(@"Cannot remove existing file at '%@': %@", subdirDirectoryPath, error);
166 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
167 [alert setMessageText:@"Remove File Error"];
168 [alert setInformativeText:[NSString stringWithFormat:@"Cannot remove existing file at '%@': %@",
169 subdirDirectoryPath, error]];
170 [alert addButtonWithTitle:@"OK"];
175 NSString *fileName = [object.name lastPathComponent];
176 if([object.name hasSuffix:@"/"])
177 fileName = [fileName stringByAppendingString:@"/"];
179 NSString *objectDirectoryPath = [directoryPath stringByAppendingPathComponent:subdirName];
180 objectDirectoryPath = [objectDirectoryPath stringByAppendingPathComponent:[object.name substringWithRange:NSMakeRange(subdirPrefixLength, [object.name length] - subdirPrefixLength - [fileName length])]];
182 ASIPithosObjectRequest *objectRequest = [self objectDataRequestWithContainerName:containerName
183 objectName:object.name
184 toDirectory:objectDirectoryPath
186 sharingAccount:sharingAccount];
187 [(NSMutableDictionary *)objectRequest.userInfo setObject:[NSNumber numberWithUnsignedInteger:object.bytes] forKey:@"bytes"];
188 [objectRequests addObject:objectRequest];
192 return objectRequests;
198 + (ASIPithosObjectRequest *)writeObjectDataRequestWithContainerName:(NSString *)containerName
199 objectName:(NSString *)objectName
200 contentType:(NSString *)contentType
201 blockSize:(NSUInteger)blockSize
202 blockHash:(NSString *)blockHash
203 forFile:(NSString *)filePath
204 checkIfExists:(BOOL)ifExists
205 hashes:(NSArray **)hashes
206 sharingAccount:(NSString *)sharingAccount {
207 if (ifExists && ![self proceedIfObjectExistsAtContainerName:containerName objectName:objectName
208 sharingAccount:(NSString *)sharingAccount])
212 *hashes = [HashMapHash calculateObjectHashMap:filePath withBlockHash:blockHash andBlockSize:blockSize];
215 NSString *fileName = [filePath lastPathComponent];
216 if ([filePath hasSuffix:@"/"])
217 fileName = [fileName stringByAppendingString:@"/"];
218 NSUInteger bytes = [self bytesOfFile:filePath];
219 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
220 objectName:objectName
221 contentType:contentType
223 contentDisposition:nil
226 isPublic:ASIPithosObjectRequestPublicIgnore
233 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
234 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
235 fileName, @"fileName",
236 [NSNumber numberWithUnsignedInteger:bytes], @"bytes",
238 return objectRequest;
241 + (NSIndexSet *)missingBlocksForHashes:(NSArray *)hashes withMissingHashesResponse:(NSString *)missingHashesResponse {
242 NSArray *responseLines = [missingHashesResponse componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
243 NSMutableIndexSet *missingBlocks = [NSMutableIndexSet indexSet];
244 for (NSString *line in responseLines) {
247 NSUInteger missingBlock = [hashes indexOfObject:line];
248 if (missingBlock != NSNotFound)
249 [missingBlocks addIndex:missingBlock];
251 return missingBlocks;
254 + (ASIPithosContainerRequest *)updateContainerDataRequestWithContainerName:(NSString *)containerName
255 blockSize:(NSUInteger)blockSize
256 forFile:(NSString *)filePath
257 hashes:(NSArray *)hashes
258 missingHashesResponse:(NSString *)missingHashesResponse
259 sharingAccount:(NSString *)sharingAccount {
260 NSIndexSet *missingBlocks = [self missingBlocksForHashes:hashes withMissingHashesResponse:missingHashesResponse];
262 NSFileManager *defaultManager = [NSFileManager defaultManager];
263 NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
265 // http://cocoawithlove.com/2009/07/temporary-files-and-folders-in-cocoa.html
266 NSString *tempFileTemplate = NSTemporaryDirectory();
267 if (tempFileTemplate == nil)
268 tempFileTemplate = @"/tmp";
269 tempFileTemplate = [tempFileTemplate stringByAppendingPathComponent:@"pithos-macos.XXXXXX"];
270 const char *tempFileTemplateCString = [tempFileTemplate fileSystemRepresentation];
271 char *tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1);
272 strcpy(tempFileNameCString, tempFileTemplateCString);
273 int fileDescriptor = mkstemp(tempFileNameCString);
274 NSString *tempFilePath = [defaultManager stringWithFileSystemRepresentation:tempFileNameCString length:strlen(tempFileNameCString)];
275 if (fileDescriptor == -1) {
276 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
277 [alert setMessageText:@"Create Temporary File Error"];
278 [alert setInformativeText:[NSString stringWithFormat:@"Cannot create temporary file at '%@'", tempFilePath]];
279 [alert addButtonWithTitle:@"OK"];
283 free(tempFileNameCString);
284 NSFileHandle *tempFileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fileDescriptor closeOnDealloc:YES];
286 [missingBlocks enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
287 [fileHandle seekToFileOffset:(idx*blockSize)];
288 [tempFileHandle writeData:[fileHandle readDataOfLength:blockSize]];
290 [tempFileHandle closeFile];
291 [fileHandle closeFile];
293 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest updateContainerDataRequestWithContainerName:containerName
299 [containerRequest setRequestUserFromDefaultTo:sharingAccount];
300 return containerRequest;
303 + (ASIPithosContainerRequest *)updateContainerDataRequestWithContainerName:(NSString *)containerName
304 blockSize:(NSUInteger)blockSize
305 forFile:(NSString *)filePath
306 missingBlockIndex:(NSUInteger)missingBlockIndex
307 sharingAccount:(NSString *)sharingAccount {
308 NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
309 [fileHandle seekToFileOffset:(missingBlockIndex *blockSize)];
310 NSData *blockData = [fileHandle readDataOfLength:blockSize];
311 [fileHandle closeFile];
312 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest updateContainerDataRequestWithContainerName:containerName
318 [containerRequest setRequestUserFromDefaultTo:sharingAccount];
319 return containerRequest;
322 + (NSArray *)writeObjectDataRequestsWithContainerName:(NSString *)containerName
323 objectName:(NSString *)objectName
324 blockSize:(NSUInteger)blockSize
325 blockHash:(NSString *)blockHash
326 forDirectory:(NSString *)directoryPath
327 checkIfExists:(BOOL)ifExists
328 objectNames:(NSMutableArray **)objectNames
329 contentTypes:(NSMutableArray **)contentTypes
330 filePaths:(NSMutableArray **)filePaths
331 hashesArrays:(NSMutableArray **)hashesArrays
332 directoryObjectRequests:(NSMutableArray **) directoryObjectRequests
333 sharingAccount:(NSString *)sharingAccount {
334 if (ifExists && ![self proceedIfObjectExistsAtContainerName:containerName objectName:objectName sharingAccount:sharingAccount])
337 NSFileManager *defaultManager = [NSFileManager defaultManager];
338 NSError *error = nil;
339 NSArray *subPaths = [defaultManager subpathsOfDirectoryAtPath:directoryPath error:&error];
341 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
342 [alert setMessageText:@"Directory Read Error"];
343 [alert setInformativeText:[NSString stringWithFormat:@"Cannot read contents of directory '%@': %@",
344 [directoryPath lastPathComponent], error]];
345 [alert addButtonWithTitle:@"OK"];
350 *directoryObjectRequests = [NSMutableArray arrayWithCapacity:[subPaths count]];
351 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
352 objectName:objectName
354 contentType:@"application/directory"
356 contentDisposition:nil
359 isPublic:ASIPithosObjectRequestPublicIgnore
363 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
364 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
365 [directoryPath lastPathComponent], @"fileName",
367 [*directoryObjectRequests addObject:objectRequest];
369 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:[subPaths count]];
370 *objectNames = [NSMutableArray arrayWithCapacity:[subPaths count]];
371 *contentTypes = [NSMutableArray arrayWithCapacity:[subPaths count]];
372 *filePaths = [NSMutableArray arrayWithCapacity:[subPaths count]];
373 *hashesArrays = [NSMutableArray arrayWithCapacity:[subPaths count]];
375 NSString *subObjectName;
378 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 fileName = [filePath lastPathComponent];
389 if ([filePath hasSuffix:@"/"])
390 fileName = [fileName stringByAppendingString:@"/"];
391 bytes = [self bytesOfFile:filePath];
393 contentType = [self contentTypeOfFile:filePath error:&error];
394 if (contentType == nil)
395 contentType = @"application/octet-stream";
397 NSLog(@"contentType detection error: %@", error);
398 objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
399 objectName:subObjectName
400 contentType:contentType
402 contentDisposition:nil
405 isPublic:ASIPithosObjectRequestPublicIgnore
412 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
413 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
414 fileName, @"fileName",
415 [NSNumber numberWithUnsignedInteger:bytes], @"bytes",
417 [objectRequests addObject:objectRequest];
418 [*objectNames addObject:subObjectName];
419 [*contentTypes addObject:contentType];
420 [*filePaths addObject:filePath];
421 [*hashesArrays addObject:hashes];
425 subObjectName = [objectName stringByAppendingPathComponent:objectNameSuffix];
426 fileName = [filePath lastPathComponent];
427 objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName
428 objectName:subObjectName
430 contentType:@"application/directory"
432 contentDisposition:nil
435 isPublic:ASIPithosObjectRequestPublicIgnore
439 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
440 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
441 fileName, @"fileName",
443 [*directoryObjectRequests addObject:objectRequest];
448 return objectRequests;
454 + (NSArray *)deleteObjectRequestsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName {
455 NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil sharingAccount:nil];
459 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
460 ASIPithosObjectRequest *objectRequest;
461 if (![objectName hasSuffix:@"/"]) {
462 objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithContainerName:containerName objectName:objectName];
463 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
464 [objectName lastPathComponent], @"fileName",
466 [objectRequests addObject:objectRequest];
469 for (ASIPithosObject *object in objects) {
470 fileName = [object.name lastPathComponent];
471 if ([object.name hasSuffix:@"/"])
472 fileName = [fileName stringByAppendingString:@"/"];
473 objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithContainerName:containerName objectName:object.name];
474 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
475 fileName, @"fileName",
477 [objectRequests addObject:objectRequest];
480 if ([objectRequests count] == 0)
482 return objectRequests;
488 + (ASIPithosObjectRequest *)copyObjectRequestWithContainerName:(NSString *)containerName
489 objectName:(NSString *)objectName
490 destinationContainerName:(NSString *)destinationContainerName
491 destinationObjectName:(NSString *)destinationObjectName
492 checkIfExists:(BOOL)ifExists
493 sharingAccount:(NSString *)sharingAccount {
494 if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
497 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
498 objectName:objectName
501 contentDisposition:nil
504 isPublic:ASIPithosObjectRequestPublicIgnore
506 destinationContainerName:destinationContainerName
507 destinationObjectName:destinationObjectName
508 destinationAccount:nil
510 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
511 containerName, @"sourceContainerName",
512 objectName, @"sourceObjectName",
513 destinationContainerName, @"destinationContainerName",
514 destinationObjectName, @"destinationObjectName",
517 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
518 return objectRequest;
521 + (NSArray *)copyObjectRequestsForSubdirWithContainerName:(NSString *)containerName
522 objectName:(NSString *)objectName
523 destinationContainerName:(NSString *)destinationContainerName
524 destinationObjectName:(NSString *)destinationObjectName
525 checkIfExists:(BOOL)ifExists
526 sharingAccount:(NSString *)sharingAccount {
527 if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
530 NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName
531 delimiter:nil sharingAccount:sharingAccount];
535 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
536 ASIPithosObjectRequest *objectRequest;
537 if ([objectName isEqualToString:destinationObjectName]) {
538 if (![objectName hasSuffix:@"/"]) {
539 objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
540 objectName:objectName
543 contentDisposition:nil
546 isPublic:ASIPithosObjectRequestPublicIgnore
548 destinationContainerName:destinationContainerName
549 destinationObjectName:objectName
550 destinationAccount:nil
552 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
553 containerName, @"sourceContainerName",
554 objectName, @"sourceObjectName",
555 destinationContainerName, @"destinationContainerName",
556 objectName, @"destinationObjectName",
559 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
560 [objectRequests addObject:objectRequest];
562 for (ASIPithosObject *object in objects) {
563 objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
564 objectName:object.name
567 contentDisposition:nil
570 isPublic:ASIPithosObjectRequestPublicIgnore
572 destinationContainerName:destinationContainerName
573 destinationObjectName:object.name
574 destinationAccount:nil
576 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
577 containerName, @"sourceContainerName",
578 object.name, @"sourceObjectName",
579 destinationContainerName, @"destinationContainerName",
580 object.name, @"destinationObjectName",
583 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
584 [objectRequests addObject:objectRequest];
587 if (![objectName hasSuffix:@"/"]) {
588 objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
589 objectName:objectName
592 contentDisposition:nil
595 isPublic:ASIPithosObjectRequestPublicIgnore
597 destinationContainerName:destinationContainerName
598 destinationObjectName:destinationObjectName
599 destinationAccount:nil
601 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
602 containerName, @"sourceContainerName",
603 objectName, @"sourceObjectName",
604 destinationContainerName, @"destinationContainerName",
605 destinationObjectName, @"destinationObjectName",
608 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
609 [objectRequests addObject:objectRequest];
611 NSRange prefixRange = NSMakeRange(0, [objectName length]);
612 NSString *newObjectName;
613 for (ASIPithosObject *object in objects) {
614 newObjectName = [object.name stringByReplacingOccurrencesOfString:objectName
615 withString:destinationObjectName
616 options:NSAnchoredSearch
618 objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName
619 objectName:object.name
622 contentDisposition:nil
625 isPublic:ASIPithosObjectRequestPublicIgnore
627 destinationContainerName:destinationContainerName
628 destinationObjectName:newObjectName
629 destinationAccount:nil
631 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
632 containerName, @"sourceContainerName",
633 object.name, @"sourceObjectName",
634 destinationContainerName, @"destinationContainerName",
635 newObjectName, @"destinationObjectName",
638 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
639 [objectRequests addObject:objectRequest];
643 if ([objectRequests count] == 0)
645 return objectRequests;
651 + (ASIPithosObjectRequest *)moveObjectRequestWithContainerName:(NSString *)containerName
652 objectName:(NSString *)objectName
653 destinationContainerName:(NSString *)destinationContainerName
654 destinationObjectName:(NSString *)destinationObjectName
655 checkIfExists:(BOOL)ifExists {
656 if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
659 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
660 objectName:objectName
663 contentDisposition:nil
666 isPublic:ASIPithosObjectRequestPublicIgnore
668 destinationContainerName:destinationContainerName
669 destinationObjectName:destinationObjectName
670 destinationAccount:nil];
671 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
672 containerName, @"sourceContainerName",
673 objectName, @"sourceObjectName",
674 destinationContainerName, @"destinationContainerName",
675 destinationObjectName, @"destinationObjectName",
677 return objectRequest;
680 + (NSArray *)moveObjectRequestsForSubdirWithContainerName:(NSString *)containerName
681 objectName:(NSString *)objectName
682 destinationContainerName:(NSString *)destinationContainerName
683 destinationObjectName:(NSString *)destinationObjectName
684 checkIfExists:(BOOL)ifExists {
685 if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
688 NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil sharingAccount:nil];
692 ASIPithosObjectRequest *objectRequest;
693 NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
694 if ([objectName isEqualToString:destinationObjectName]) {
695 if (![objectName hasSuffix:@"/"]) {
696 objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
697 objectName:objectName
700 contentDisposition:nil
703 isPublic:ASIPithosObjectRequestPublicIgnore
705 destinationContainerName:destinationContainerName
706 destinationObjectName:objectName
707 destinationAccount:nil];
708 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
709 containerName, @"sourceContainerName",
710 objectName, @"sourceObjectName",
711 destinationContainerName, @"destinationContainerName",
712 objectName, @"destinationObjectName",
714 [objectRequests addObject:objectRequest];
716 for (ASIPithosObject *object in objects) {
717 objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
718 objectName:object.name
721 contentDisposition:nil
724 isPublic:ASIPithosObjectRequestPublicIgnore
726 destinationContainerName:destinationContainerName
727 destinationObjectName:object.name
728 destinationAccount:nil];
729 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
730 containerName, @"sourceContainerName",
731 object.name, @"sourceObjectName",
732 destinationContainerName, @"destinationContainerName",
733 object.name, @"destinationObjectName",
735 [objectRequests addObject:objectRequest];
738 if (![objectName hasSuffix:@"/"]) {
739 objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
740 objectName:objectName
743 contentDisposition:nil
746 isPublic:ASIPithosObjectRequestPublicIgnore
748 destinationContainerName:destinationContainerName
749 destinationObjectName:destinationObjectName
750 destinationAccount:nil];
751 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
752 containerName, @"sourceContainerName",
753 objectName, @"sourceObjectName",
754 destinationContainerName, @"destinationContainerName",
755 destinationObjectName, @"destinationObjectName",
757 [objectRequests addObject:objectRequest];
759 NSRange prefixRange = NSMakeRange(0, [objectName length]);
760 NSString *newObjectName;
761 for (ASIPithosObject *object in objects) {
762 newObjectName = [object.name stringByReplacingOccurrencesOfString:objectName
763 withString:destinationObjectName
764 options:NSAnchoredSearch
766 objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName
767 objectName:object.name
770 contentDisposition:nil
773 isPublic:ASIPithosObjectRequestPublicIgnore
775 destinationContainerName:destinationContainerName
776 destinationObjectName:newObjectName
777 destinationAccount:nil];
778 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
779 containerName, @"sourceContainerName",
780 object.name, @"sourceObjectName",
781 destinationContainerName, @"destinationContainerName",
782 newObjectName, @"destinationObjectName",
784 [objectRequests addObject:objectRequest];
788 if ([objectRequests count] == 0)
790 return objectRequests;
794 #pragma mark Helper Methods
796 // Size of the file in bytes
797 + (NSUInteger)bytesOfFile:(NSString *)filePath {
798 NSFileManager *defaultManager = [NSFileManager defaultManager];
799 NSDictionary *attributes = [defaultManager attributesOfItemAtPath:filePath error:nil];
800 return [[attributes objectForKey:NSFileSize] intValue];
803 // Content type of the file or nil if it cannot be determined
804 + (NSString *)contentTypeOfFile:(NSString *)filePath error:(NSError **)error {
805 NSURLResponse *response = nil;
806 [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath]
807 cachePolicy:NSURLCacheStorageNotAllowed
809 returningResponse:&response
811 return [response MIMEType];
814 // Returns if an object exists at the given container/object path and if this object is an application/directory
815 // If an error occured an alert is shown and it is returned so the caller won't proceed
816 + (BOOL)objectExistsAtContainerName:(NSString *)containerName objectName:(NSString *)objectName
817 error:(NSError **)error isDirectory:(BOOL *)isDirectory
818 sharingAccount:(NSString *)sharingAccount {
819 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectMetadataRequestWithContainerName:containerName
820 objectName:objectName];
822 [objectRequest setRequestUserFromDefaultTo:sharingAccount];
823 [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
824 while (![objectRequest isFinished]) {
827 *error = [objectRequest error];
829 [self httpRequestErrorAlertWithRequest:objectRequest];
831 } else if (objectRequest.responseStatusCode == 200) {
832 *isDirectory = [[objectRequest contentType] isEqualToString:@"application/directory"];
838 // Returns if the called should proceed, after an interactive check if an object exists
839 // at the given container/object path is performed
840 + (BOOL)proceedIfObjectExistsAtContainerName:(NSString *)containerName objectName:(NSString *)objectName
841 sharingAccount:(NSString *)sharingAccount {
842 NSError *error = nil;
844 BOOL objectExists = [self objectExistsAtContainerName:containerName
845 objectName:objectName
847 isDirectory:&isDirectory
848 sharingAccount:sharingAccount];
851 } else if (objectExists) {
852 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
854 [alert setMessageText:@"Directory Exists"];
856 [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
858 [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
860 [alert setMessageText:@"Object Exists"];
862 [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
864 [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
866 [alert addButtonWithTitle:@"OK"];
867 [alert addButtonWithTitle:@"Cancel"];
868 NSInteger choice = [alert runModal];
869 if (choice == NSAlertSecondButtonReturn)
876 // List of objects at the given container/object path, with prefix and or delimiter
877 + (NSArray *)objectsWithContainerName:(NSString *)containerName objectNamePrefix:(NSString *)objectNamePrefix
878 delimiter:(NSString *)delimiter sharingAccount:(NSString *)sharingAccount {
879 NSMutableArray *objects = [NSMutableArray array];
880 NSString *marker = nil;
882 ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest listObjectsRequestWithContainerName:containerName
885 prefix:objectNamePrefix
892 [containerRequest setRequestUserFromDefaultTo:sharingAccount];
893 [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
894 while (![containerRequest isFinished]) {
897 if ([containerRequest error]) {
898 [self httpRequestErrorAlertWithRequest:containerRequest];
901 NSArray *someObjects = [containerRequest objects];
902 [objects addObjectsFromArray:someObjects];
903 if ([someObjects count] < 10000)
906 marker = [[someObjects lastObject] name];
911 // List of objects at the given container/object path, that may be a subdir or an application/directory,
912 // with prefix and or delimiter
913 + (NSArray *)objectsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName
914 delimiter:(NSString *)delimiter sharingAccount:(NSString *)sharingAccount {
915 NSString *subdirNamePrefix = [NSString stringWithString:objectName];
916 if (![subdirNamePrefix hasSuffix:@"/"])
917 subdirNamePrefix = [subdirNamePrefix stringByAppendingString:@"/"];
918 return [self objectsWithContainerName:containerName objectNamePrefix:subdirNamePrefix
919 delimiter:delimiter sharingAccount:sharingAccount];
922 // A safe object name at the given container/object path
923 // The original name has " %d" appended to it before any ".*" suffix, for the first integer that produces a name that is free to use
924 // If the original name hasn't got a "." but has a "/" suffix, then it is replaced with " %d/" instead
925 // Subdirs are taken into consideration
926 + (NSString *)safeObjectNameForContainerName:(NSString *)containerName objectName:(NSString *)objectName {
927 NSString *objectNamePrefix;
928 NSString *objectNameExtraSuffix;
929 NSRange lastDotRange = [objectName rangeOfString:@"." options:NSBackwardsSearch];
930 if (lastDotRange.length == 1) {
931 objectNamePrefix = [objectName substringToIndex:lastDotRange.location];
932 objectNameExtraSuffix = [objectName substringFromIndex:lastDotRange.location];
933 } else if ([objectName hasSuffix:@"/"]) {
934 objectNamePrefix = [objectName substringToIndex:([objectName length] - 1)];
935 objectNameExtraSuffix = [NSString stringWithString:@"/"];
937 objectNamePrefix = [NSString stringWithString:objectName];
938 objectNameExtraSuffix = [NSString string];
940 NSArray *objects = [self objectsWithContainerName:containerName
941 objectNamePrefix:[objectNamePrefix stringByDeletingLastPathComponent]
946 if ([objects count] == 0)
948 NSMutableArray *objectNames = [NSMutableArray arrayWithArray:
949 [[objects objectsAtIndexes:
950 [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
951 if (pithosObject.subdir)
954 }]] valueForKey:@"name"]];
955 for (NSString *name in [[objects objectsAtIndexes:
956 [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
957 if (pithosObject.subdir)
960 }]] valueForKey:@"name"]) {
961 [objectNames addObject:[name substringToIndex:([name length] - 1)]];
963 if (![objectNames containsObject:objectName])
965 NSUInteger objectNameSuffix = 2;
966 NSString *safeObjectName;
968 safeObjectName = [objectNamePrefix stringByAppendingFormat:@" %lu%@", objectNameSuffix, objectNameExtraSuffix];
970 } while ([objectNames containsObject:safeObjectName]);
971 return safeObjectName;
974 // A safe object name at the given container/object path that may be a subdir or application/directory
975 // The original name has " %d" appended to it, for the first integer that produces a name that is free to use
976 // If the original name has a "/" suffix, then it is replaced with " %d/" instead
977 // Subdirs are taken into consideration
978 + (NSString *)safeSubdirNameForContainerName:(NSString *)containerName subdirName:(NSString *)subdirName {
979 NSString *subdirNamePrefix;
980 NSString *subdirNameExtraSuffix;
981 if ([subdirName hasSuffix:@"/"]) {
982 subdirNamePrefix = [subdirName substringToIndex:([subdirName length] - 1)];
983 subdirNameExtraSuffix = [NSString stringWithString:@"/"];
985 subdirNamePrefix = [NSString stringWithString:subdirName];
986 subdirNameExtraSuffix = [NSString string];
988 NSArray *objects = [self objectsWithContainerName:containerName
989 objectNamePrefix:[subdirNamePrefix stringByDeletingLastPathComponent]
994 if ([objects count] == 0)
996 NSMutableArray *objectNames = [NSMutableArray arrayWithArray:
997 [[objects objectsAtIndexes:
998 [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
999 if (pithosObject.subdir)
1002 }]] valueForKey:@"name"]];
1003 for (NSString *name in [[objects objectsAtIndexes:
1004 [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
1005 if (pithosObject.subdir)
1008 }]] valueForKey:@"name"]) {
1009 [objectNames addObject:[name substringToIndex:([name length] - 1)]];
1011 if (![objectNames containsObject:subdirNamePrefix])
1013 NSUInteger subdirNameSuffix = 2;
1014 NSString *safeSubdirName;
1016 safeSubdirName = [subdirNamePrefix stringByAppendingFormat:@" %lu", subdirNameSuffix];
1018 } while ([objectNames containsObject:safeSubdirName]);
1019 return [safeSubdirName stringByAppendingString:subdirNameExtraSuffix];
1025 + (NSInteger)httpRequestErrorAlertWithRequest:(ASIPithosRequest *)request {
1026 NSString *message = [NSString stringWithFormat:
1027 @"HTTP request error: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@",
1029 request.requestMethod,
1031 [request requestHeaders],
1032 [request responseHeaders],
1033 [request responseString]];
1034 NSLog(@"%@", message);
1035 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1036 [alert setMessageText:@"HTTP Request Error"];
1037 [alert setInformativeText:message];
1038 [alert addButtonWithTitle:@"OK"];
1039 return [alert runModal];
1042 + (NSInteger)unexpectedResponseStatusAlertWithRequest:(ASIPithosRequest *)request {
1043 NSString *message = [NSString stringWithFormat:
1044 @"Unexpected response status %d: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@",
1045 request.responseStatusCode,
1046 request.responseStatusMessage,
1047 request.requestMethod,
1049 [request requestHeaders],
1050 [request responseHeaders],
1051 [request responseString]];
1052 NSLog(@"%@", message);
1053 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1054 [alert setMessageText:@"Unexpected Response Status"];
1055 [alert setInformativeText:message];
1056 [alert addButtonWithTitle:@"OK"];
1057 return [alert runModal];
1061 #pragma mark Request Helper Methods
1063 + (ASIPithosRequest *)prepareRequest:(ASIPithosRequest *)request priority:(NSOperationQueuePriority)priority {
1064 [request setTimeOutSeconds:60];
1065 request.numberOfTimesToRetryOnTimeout = 10;
1066 [request setQueuePriority:priority];
1070 + (ASIPithosRequest *)prepareRequest:(ASIPithosRequest *)request {
1071 return [self prepareRequest:request priority:NSOperationQueuePriorityNormal];
1074 + (ASIPithosRequest *)copyRequest:(ASIPithosRequest *)request {
1075 NSMutableDictionary *userInfo = [[request.userInfo retain] autorelease];
1076 request.userInfo = nil;
1077 ASIPithosRequest *newRequest = [request copy];
1078 newRequest.userInfo = userInfo;