Statistics
| Branch: | Tag: | Revision:

root / pithos-macos / PithosUtilities.m @ cfc17d33

History | View | Annotate | Download (72.4 kB)

1
//
2
//  PithosUtilities.m
3
//  pithos-macos
4
//
5
// Copyright 2011-2012 GRNET S.A. All rights reserved.
6
//
7
// Redistribution and use in source and binary forms, with or
8
// without modification, are permitted provided that the following
9
// conditions are met:
10
// 
11
//   1. Redistributions of source code must retain the above
12
//      copyright notice, this list of conditions and the following
13
//      disclaimer.
14
// 
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.
19
// 
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.
32
// 
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.
37

    
38
#import "PithosUtilities.h"
39
#import "ASINetworkQueue.h"
40
#import "ASIPithos.h"
41
#import "ASIPithosContainerRequest.h"
42
#import "ASIPithosObjectRequest.h"
43
#import "ASIPithosObject.h"
44
#import "HashMapHash.h"
45

    
46
@implementation PithosUtilities
47

    
48
#pragma mark -
49
#pragma mark Download
50

    
51
+ (ASIPithosObjectRequest *)objectDataRequestWithPithos:(ASIPithos *)pithos 
52
                                          containerName:(NSString *)containerName 
53
                                             objectName:(NSString *)objectName 
54
                                                version:(NSString *)version 
55
                                            toDirectory:(NSString *)directoryPath 
56
                                        withNewFileName:(NSString *)newFileName 
57
                                          checkIfExists:(BOOL)ifExists 
58
                                         sharingAccount:(NSString *)sharingAccount {
59
    NSString *fileName;
60
    if (newFileName) {
61
        fileName = [NSString stringWithString:newFileName];
62
    } else {
63
        fileName = [objectName lastPathComponent];
64
        if ([objectName hasSuffix:@"/"])
65
            fileName = [fileName stringByAppendingString:@"/"];    
66
    }
67
    fileName = [fileName stringByReplacingOccurrencesOfString:@"/" withString:@":"];
68
    
69
    NSFileManager *fileManager = [NSFileManager defaultManager];
70
    
71
    NSString *destinationPath = [directoryPath stringByAppendingPathComponent:fileName];
72
    if (ifExists && [fileManager fileExistsAtPath:destinationPath]) {
73
        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
74
        [alert setMessageText:@"File Exists"];
75
        [alert setInformativeText:[NSString stringWithFormat:@"A file or directory named '%@' already exists, do you want to replace it?", fileName]];
76
        [alert addButtonWithTitle:@"OK"];
77
        [alert addButtonWithTitle:@"Cancel"];
78
        NSInteger choice = [alert runModal];
79
        if (choice == NSAlertSecondButtonReturn)
80
            return nil;
81
    }
82
    
83
    BOOL directoryIsDirectory;
84
    BOOL directoryExists = [fileManager fileExistsAtPath:directoryPath isDirectory:&directoryIsDirectory];
85
    NSError *error = nil;
86
    if (!directoryExists) {
87
        [fileManager createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error];
88
    } else if (!directoryIsDirectory) {
89
        [fileManager removeItemAtPath:directoryPath error:&error];
90
    }
91
    if (error) {
92
        NSLog(@"Cannot remove existing file '%@': %@", fileName, error);
93
        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
94
        [alert setMessageText:@"Removal Error"];
95
        [alert setInformativeText:[NSString stringWithFormat:@"Cannot remove existing file '%@': %@", fileName, error]];
96
        [alert addButtonWithTitle:@"OK"];
97
        [alert runModal];
98
        return nil;
99
    }
100

    
101
    ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectDataRequestWithPithos:pithos 
102
                                                                                  containerName:containerName 
103
                                                                                     objectName:objectName 
104
                                                                                        version:version];
105
    if (sharingAccount)
106
        [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
107
    objectRequest.downloadDestinationPath = destinationPath;
108
    objectRequest.allowResumeForFileDownloads = YES;
109
    objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
110
                              fileName, @"fileName", 
111
                              destinationPath, @"filePath", 
112
                              nil];
113
    return objectRequest;
114
}
115

    
116
+ (NSArray *)objectDataRequestsForSubdirWithPithos:(ASIPithos *)pithos 
117
                                     containerName:(NSString *)containerName 
118
                                        objectName:(NSString *)objectName 
119
                                       toDirectory:(NSString *)directoryPath 
120
                                     checkIfExists:(BOOL)ifExists 
121
                                    sharingAccount:(NSString *)sharingAccount {
122
    NSString *subdirName = [objectName lastPathComponent];
123
    NSString *destinationPath = [directoryPath stringByAppendingPathComponent:subdirName];
124
    if (ifExists && [[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) {
125
        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
126
        [alert setMessageText:@"File exists"];
127
        [alert setInformativeText:[NSString stringWithFormat:@"A file or directory named '%@' already exists, do you want to replace it?", subdirName]];
128
        [alert addButtonWithTitle:@"OK"];
129
        [alert addButtonWithTitle:@"Cancel"];
130
        NSInteger choice = [alert runModal];
131
        if (choice == NSAlertSecondButtonReturn)
132
            return nil;
133
    }
134
    
135
    NSArray *objects = [self objectsForSubdirWithPithos:pithos containerName:containerName objectName:objectName 
136
                                              delimiter:nil sharingAccount:sharingAccount];
137
    if (objects == nil)
138
        return nil;
139
    
140
    NSFileManager *fileManager = [NSFileManager defaultManager];
141
    NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:[objects count]];
142
    NSUInteger subdirPrefixLength = [objectName length];
143

    
144
    NSError *error = nil;
145
    [fileManager createDirectoryAtPath:[directoryPath stringByAppendingPathComponent:subdirName] withIntermediateDirectories:YES attributes:nil error:&error];
146
    if (error) {
147
        NSLog(@"Cannot create directory at '%@': %@", directoryPath, error);
148
        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
149
        [alert setMessageText:@"Create Directory Error"];
150
        [alert setInformativeText:[NSString stringWithFormat:@"Cannot create directory at '%@': %@", 
151
                                   directoryPath, error]];
152
        [alert addButtonWithTitle:@"OK"];
153
        [alert runModal];
154
    }
155
    
156
    for (ASIPithosObject *object in objects) {
157
        if ([PithosUtilities isContentTypeDirectory:object.contentType]) {
158
            NSString *subdirDirectoryPath = [directoryPath stringByAppendingPathComponent:subdirName];
159
            subdirDirectoryPath = [subdirDirectoryPath stringByAppendingPathComponent:[object.name substringFromIndex:subdirPrefixLength]];
160
            
161
            BOOL directoryIsDirectory;
162
            BOOL directoryExists = [fileManager fileExistsAtPath:subdirDirectoryPath isDirectory:&directoryIsDirectory];
163
            NSError *error = nil;
164
            if (!directoryExists) {
165
                [fileManager createDirectoryAtPath:subdirDirectoryPath withIntermediateDirectories:YES attributes:nil error:&error];
166
                if (error) {
167
                    NSLog(@"Cannot create directory at '%@': %@", subdirDirectoryPath, error);
168
                    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
169
                    [alert setMessageText:@"Create Directory Error"];
170
                    [alert setInformativeText:[NSString stringWithFormat:@"Cannot create directory at '%@': %@", 
171
                                               subdirDirectoryPath, error]];
172
                    [alert addButtonWithTitle:@"OK"];
173
                    [alert runModal];
174
                }
175
            } else if (!directoryIsDirectory) {
176
                [fileManager removeItemAtPath:subdirDirectoryPath error:&error];
177
                if (error) {
178
                    NSLog(@"Cannot remove existing file at '%@': %@", subdirDirectoryPath, error);
179
                    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
180
                    [alert setMessageText:@"Remove File Error"];
181
                    [alert setInformativeText:[NSString stringWithFormat:@"Cannot remove existing file at '%@': %@", 
182
                                               subdirDirectoryPath, error]];
183
                    [alert addButtonWithTitle:@"OK"];
184
                    [alert runModal];
185
                }
186
            }
187
        } else {
188
            NSString *fileName = [object.name lastPathComponent];
189
            if([object.name hasSuffix:@"/"])
190
                fileName = [fileName stringByAppendingString:@"/"];
191
            
192
            NSString *objectDirectoryPath = [directoryPath stringByAppendingPathComponent:subdirName];
193
            objectDirectoryPath = [objectDirectoryPath stringByAppendingPathComponent:[object.name substringWithRange:NSMakeRange(subdirPrefixLength, [object.name length] - subdirPrefixLength - [fileName length])]];
194
            
195
            ASIPithosObjectRequest *objectRequest = [self objectDataRequestWithPithos:pithos 
196
                                                                        containerName:containerName 
197
                                                                           objectName:object.name 
198
                                                                              version:nil 
199
                                                                          toDirectory:objectDirectoryPath 
200
                                                                      withNewFileName:nil 
201
                                                                        checkIfExists:NO 
202
                                                                       sharingAccount:sharingAccount];
203
            [(NSMutableDictionary *)objectRequest.userInfo setObject:[NSNumber numberWithUnsignedInteger:object.bytes] forKey:@"bytes"];
204
            [objectRequests addObject:objectRequest];
205
        }
206
    }
207
    
208
    return objectRequests;
209
}
210

    
211
#pragma mark -
212
#pragma mark Download Block
213

    
214
+ (ASIPithosObjectRequest *)objectBlockDataRequestWithPithos:(ASIPithos *)pithos 
215
                                               containerName:(NSString *)containerName 
216
                                                      object:(ASIPithosObject *)object 
217
                                                  blockIndex:(NSUInteger)blockIndex 
218
                                                   blockSize:(NSUInteger)blockSize {
219
    NSUInteger rangeStart = blockIndex * blockSize;
220
    NSUInteger rangeEnd = (rangeStart + blockSize <= object.bytes) ? (rangeStart + blockSize - 1) : (object.bytes - 1);
221
    ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectDataRequestWithPithos:pithos 
222
                                                                                  containerName:containerName
223
                                                                                     objectName:object.name
224
                                                                                        version:nil
225
                                                                                          range:[NSString stringWithFormat:@"bytes=%lu-%lu", rangeStart, rangeEnd]
226
                                                                                        ifMatch:object.hash];
227
    return objectRequest;
228
}
229

    
230
+ (NSIndexSet *)missingBlocksForFile:(NSString *)filePath
231
                           blockSize:(NSUInteger)blockSize 
232
                           blockHash:(NSString *)blockHash 
233
                          withHashes:(NSArray *)hashes {
234
    NSArray *fileHashes = [HashMapHash objectHashMapStrings:filePath withBlockHash:blockHash andBlockSize:blockSize];
235
    return [hashes indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
236
        if ((idx >= [fileHashes count]) || ![(NSString *)obj isEqualToString:[fileHashes objectAtIndex:idx]])
237
            return YES;
238
        return NO;
239
    }];
240
}
241

    
242
#pragma mark -
243
#pragma mark Upload
244

    
245
+ (ASIPithosObjectRequest *)writeObjectDataRequestWithPithos:(ASIPithos *)pithos 
246
                                               containerName:(NSString *)containerName
247
                                                  objectName:(NSString *)objectName
248
                                                 contentType:(NSString *)contentType 
249
                                                   blockSize:(NSUInteger)blockSize 
250
                                                   blockHash:(NSString *)blockHash 
251
                                                     forFile:(NSString *)filePath 
252
                                               checkIfExists:(BOOL)ifExists 
253
                                                      hashes:(NSArray **)hashes 
254
                                              sharingAccount:(NSString *)sharingAccount {
255
    if (ifExists && ![self proceedIfObjectExistsAtPithos:pithos containerName:containerName objectName:objectName 
256
                                          sharingAccount:(NSString *)sharingAccount])
257
        return nil;
258
    
259
    if (*hashes == nil)
260
        *hashes = [HashMapHash objectHashMapStrings:filePath withBlockHash:blockHash andBlockSize:blockSize];
261
    if (*hashes == nil)
262
        return nil;
263
    NSString *fileName = [filePath lastPathComponent];
264
    if ([filePath hasSuffix:@"/"])
265
        fileName = [fileName stringByAppendingString:@"/"];
266
    NSUInteger bytes = [self bytesOfFile:filePath];
267
    ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos 
268
                                                                                       containerName:containerName 
269
                                                                                          objectName:objectName 
270
                                                                                         contentType:contentType 
271
                                                                                     contentEncoding:nil 
272
                                                                                  contentDisposition:nil 
273
                                                                                            manifest:nil 
274
                                                                                             sharing:nil 
275
                                                                                            isPublic:ASIPithosObjectRequestPublicIgnore 
276
                                                                                            metadata:nil
277
                                                                                           blockSize:blockSize
278
                                                                                           blockHash:blockHash 
279
                                                                                              hashes:*hashes 
280
                                                                                               bytes:bytes];
281
    if (sharingAccount) 
282
        [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
283
    objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
284
                              fileName, @"fileName", 
285
                              [NSNumber numberWithUnsignedInteger:bytes], @"bytes", 
286
                              nil];
287
    return objectRequest;
288
}
289

    
290
+ (NSIndexSet *)missingBlocksForHashes:(NSArray *)hashes withMissingHashes:(NSArray *)missingHashes {
291
    NSMutableIndexSet *missingBlocks = [NSMutableIndexSet indexSet];
292
    for (NSString *missingHash in missingHashes) {
293
        if (![missingHash length])
294
            break;
295
        NSUInteger missingBlock = [hashes indexOfObject:missingHash];
296
        if (missingBlock != NSNotFound)
297
            [missingBlocks addIndex:missingBlock];
298
    }
299
    return missingBlocks;
300
}
301

    
302
+ (ASIPithosContainerRequest *)updateContainerDataRequestWithPithos:(ASIPithos *)pithos 
303
                                                      containerName:(NSString *)containerName 
304
                                                          blockSize:(NSUInteger)blockSize 
305
                                                            forFile:(NSString *)filePath 
306
                                                             hashes:(NSArray *)hashes 
307
                                                      missingHashes:(NSArray *)missingHashes 
308
                                                     sharingAccount:(NSString *)sharingAccount {
309
    NSIndexSet *missingBlocks = [self missingBlocksForHashes:hashes withMissingHashes:missingHashes];
310
    
311
    NSFileManager *fileManager = [NSFileManager defaultManager];
312
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
313
    
314
    // http://cocoawithlove.com/2009/07/temporary-files-and-folders-in-cocoa.html
315
    NSString *tempFileTemplate = NSTemporaryDirectory();
316
    if (tempFileTemplate == nil)
317
        tempFileTemplate = @"/tmp";
318
    tempFileTemplate = [tempFileTemplate stringByAppendingPathComponent:@"pithos-macos.XXXXXX"];
319
    const char *tempFileTemplateCString = [tempFileTemplate fileSystemRepresentation];
320
    char *tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1);
321
    strcpy(tempFileNameCString, tempFileTemplateCString);
322
    int fileDescriptor = mkstemp(tempFileNameCString);
323
    NSString *tempFilePath = [fileManager stringWithFileSystemRepresentation:tempFileNameCString length:strlen(tempFileNameCString)];
324
    if (fileDescriptor == -1) {
325
        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
326
        [alert setMessageText:@"Create Temporary File Error"];
327
        [alert setInformativeText:[NSString stringWithFormat:@"Cannot create temporary file at '%@'", tempFilePath]];
328
        [alert addButtonWithTitle:@"OK"];
329
        [alert runModal];
330
        return nil;
331
    }
332
    free(tempFileNameCString);
333
    NSFileHandle *tempFileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fileDescriptor closeOnDealloc:YES];
334

    
335
    [missingBlocks enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
336
        [fileHandle seekToFileOffset:(idx*blockSize)];
337
        [tempFileHandle writeData:[fileHandle readDataOfLength:blockSize]];
338
    }];
339
    [tempFileHandle closeFile];
340
    [fileHandle closeFile];
341

    
342
    ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest updateContainerDataRequestWithPithos:pithos 
343
                                                                                                    containerName:containerName 
344
                                                                                                           policy:nil 
345
                                                                                                         metadata:nil 
346
                                                                                                           update:YES 
347
                                                                                                             file:tempFilePath];
348
    if (sharingAccount)
349
        [containerRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
350
    return containerRequest;
351
}
352

    
353
+ (ASIPithosContainerRequest *)updateContainerDataRequestWithPithos:(ASIPithos *)pithos 
354
                                                      containerName:(NSString *)containerName 
355
                                                          blockSize:(NSUInteger)blockSize 
356
                                                            forFile:(NSString *)filePath 
357
                                                  missingBlockIndex:(NSUInteger)missingBlockIndex 
358
                                                     sharingAccount:(NSString *)sharingAccount {
359
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
360
    [fileHandle seekToFileOffset:(missingBlockIndex *blockSize)];
361
    NSData *blockData = [fileHandle readDataOfLength:blockSize];
362
    [fileHandle closeFile];
363
    ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest updateContainerDataRequestWithPithos:pithos 
364
                                                                                                    containerName:containerName 
365
                                                                                                           policy:nil 
366
                                                                                                         metadata:nil 
367
                                                                                                           update:YES 
368
                                                                                                             data:blockData];
369
    if (sharingAccount)
370
        [containerRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
371
    return containerRequest;
372
}
373

    
374
+ (NSArray *)writeObjectDataRequestsWithPithos:(ASIPithos *)pithos 
375
                                 containerName:(NSString *)containerName
376
                                    objectName:(NSString *)objectName
377
                                     blockSize:(NSUInteger)blockSize 
378
                                     blockHash:(NSString *)blockHash 
379
                                  forDirectory:(NSString *)directoryPath 
380
                                 checkIfExists:(BOOL)ifExists 
381
                                   objectNames:(NSMutableArray **)objectNames
382
                                  contentTypes:(NSMutableArray **)contentTypes
383
                                     filePaths:(NSMutableArray **)filePaths 
384
                                  hashesArrays:(NSMutableArray **)hashesArrays 
385
                       directoryObjectRequests:(NSMutableArray **) directoryObjectRequests 
386
                                sharingAccount:(NSString *)sharingAccount {
387
    if (ifExists && ![self proceedIfObjectExistsAtPithos:pithos containerName:containerName objectName:objectName 
388
                                          sharingAccount:sharingAccount])
389
        return nil;
390

    
391
    NSFileManager *fileManager = [NSFileManager defaultManager];
392
    NSError *error = nil;
393
    NSArray *subPaths = [fileManager subpathsOfDirectoryAtPath:directoryPath error:&error];
394
    if (error) {
395
        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
396
        [alert setMessageText:@"Directory Read Error"];
397
        [alert setInformativeText:[NSString stringWithFormat:@"Cannot read contents of directory '%@': %@", 
398
                                   [directoryPath lastPathComponent], error]];
399
        [alert addButtonWithTitle:@"OK"];
400
        [alert runModal];
401
        return nil;
402
    }
403
    
404
    *directoryObjectRequests = [NSMutableArray arrayWithCapacity:[subPaths count]];
405
    ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos 
406
                                                                                       containerName:containerName 
407
                                                                                          objectName:objectName 
408
                                                                                                eTag:nil 
409
                                                                                         contentType:@"application/directory" 
410
                                                                                     contentEncoding:nil 
411
                                                                                  contentDisposition:nil 
412
                                                                                            manifest:nil 
413
                                                                                             sharing:nil 
414
                                                                                            isPublic:ASIPithosObjectRequestPublicIgnore 
415
                                                                                            metadata:nil 
416
                                                                                                data:[NSData data]];
417
    if (sharingAccount)
418
        [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
419
    objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
420
                              [directoryPath lastPathComponent], @"fileName", 
421
                              nil];
422
    [*directoryObjectRequests addObject:objectRequest];
423
    
424
    NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:[subPaths count]];
425
    *objectNames = [NSMutableArray arrayWithCapacity:[subPaths count]];
426
    *contentTypes = [NSMutableArray arrayWithCapacity:[subPaths count]];
427
    *filePaths = [NSMutableArray arrayWithCapacity:[subPaths count]];
428
    *hashesArrays = [NSMutableArray arrayWithCapacity:[subPaths count]];
429
    BOOL isDirectory;
430
    NSString *subObjectName;
431
    NSArray *hashes;
432
    NSUInteger bytes;
433
    NSString *contentType;
434
    NSString *filePath;
435
    NSString *fileName;
436
    for (NSString *objectNameSuffix in subPaths) {
437
        filePath = [directoryPath stringByAppendingPathComponent:objectNameSuffix];
438
        if ([fileManager fileExistsAtPath:filePath isDirectory:&isDirectory]) {
439
            if (!isDirectory) {
440
                hashes = [HashMapHash objectHashMapStrings:filePath withBlockHash:blockHash andBlockSize:blockSize];
441
                if (hashes) {
442
                    subObjectName = [objectName stringByAppendingPathComponent:objectNameSuffix];
443
                    fileName = [filePath lastPathComponent];
444
                    if ([filePath hasSuffix:@"/"])
445
                        fileName = [fileName stringByAppendingString:@"/"];
446
                    bytes = [self bytesOfFile:filePath];
447
                    error = nil;
448
                    contentType = [self contentTypeOfFile:filePath error:&error];
449
                    if (contentType == nil)
450
                        contentType = @"application/octet-stream";
451
                    if (error)
452
                        NSLog(@"contentType detection error: %@", error);
453
                    objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos 
454
                                                                               containerName:containerName 
455
                                                                                  objectName:subObjectName 
456
                                                                                 contentType:contentType 
457
                                                                             contentEncoding:nil 
458
                                                                          contentDisposition:nil 
459
                                                                                    manifest:nil 
460
                                                                                     sharing:nil 
461
                                                                                    isPublic:ASIPithosObjectRequestPublicIgnore 
462
                                                                                    metadata:nil
463
                                                                                   blockSize:blockSize
464
                                                                                   blockHash:blockHash 
465
                                                                                      hashes:hashes 
466
                                                                                       bytes:bytes];
467
                    if (sharingAccount)
468
                        [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
469
                    objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
470
                                              fileName, @"fileName", 
471
                                              [NSNumber numberWithUnsignedInteger:bytes], @"bytes", 
472
                                              nil];
473
                    [objectRequests addObject:objectRequest];
474
                    [*objectNames addObject:subObjectName];
475
                    [*contentTypes addObject:contentType];
476
                    [*filePaths addObject:filePath];
477
                    [*hashesArrays addObject:hashes];
478
                }
479
                
480
            } else {
481
                subObjectName = [objectName stringByAppendingPathComponent:objectNameSuffix];
482
                fileName = [filePath lastPathComponent];
483
                objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos 
484
                                                                           containerName:containerName 
485
                                                                              objectName:subObjectName 
486
                                                                                    eTag:nil 
487
                                                                             contentType:@"application/directory" 
488
                                                                         contentEncoding:nil 
489
                                                                      contentDisposition:nil 
490
                                                                                manifest:nil 
491
                                                                                 sharing:nil 
492
                                                                                isPublic:ASIPithosObjectRequestPublicIgnore 
493
                                                                                metadata:nil 
494
                                                                                    data:[NSData data]];
495
                if (sharingAccount)
496
                    [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
497
                objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
498
                                          fileName, @"fileName", 
499
                                          nil];
500
                [*directoryObjectRequests addObject:objectRequest];
501
            }
502
        }
503
    }
504
    
505
    return objectRequests;
506
}
507

    
508
#pragma mark -
509
#pragma mark Delete
510

    
511
+ (NSArray *)deleteObjectRequestsForSubdirWithPithos:(ASIPithos *)pithos 
512
                                       containerName:(NSString *)containerName 
513
                                          objectName:(NSString *)objectName {
514
    NSArray *objects = [self objectsForSubdirWithPithos:pithos containerName:containerName objectName:objectName delimiter:nil 
515
                                         sharingAccount:nil];
516
    if (objects == nil)
517
        return nil;
518

    
519
    NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
520
    ASIPithosObjectRequest *objectRequest;
521
    if (![objectName hasSuffix:@"/"]) {
522
        objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos containerName:containerName objectName:objectName];
523
        objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
524
                                  [objectName lastPathComponent], @"fileName", 
525
                                  nil];
526
        [objectRequests addObject:objectRequest];
527
    }
528
    NSString *fileName;
529
    for (ASIPithosObject *object in objects) {
530
        fileName = [object.name lastPathComponent];
531
        if ([object.name hasSuffix:@"/"])
532
            fileName = [fileName stringByAppendingString:@"/"];
533
        objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos containerName:containerName objectName:object.name];
534
        objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
535
                                  fileName, @"fileName", 
536
                                  nil];
537
        [objectRequests addObject:objectRequest];
538
    }
539
    
540
    if ([objectRequests count] == 0)
541
        return nil;
542
    return objectRequests;
543
}
544

    
545
#pragma mark -
546
#pragma mark Copy
547

    
548
+ (ASIPithosObjectRequest *)copyObjectRequestWithPithos:(ASIPithos *)pithos 
549
                                          containerName:(NSString *)containerName 
550
                                             objectName:(NSString *)objectName 
551
                               destinationContainerName:(NSString *)destinationContainerName 
552
                                  destinationObjectName:(NSString *)destinationObjectName 
553
                                          checkIfExists:(BOOL)ifExists 
554
                                         sharingAccount:(NSString *)sharingAccount {
555
    if (ifExists && ![self proceedIfObjectExistsAtPithos:pithos containerName:destinationContainerName objectName:destinationObjectName 
556
                                          sharingAccount:nil])
557
        return nil;
558
    
559
    ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithPithos:pithos 
560
                                                                                      containerName:containerName 
561
                                                                                         objectName:objectName 
562
                                                                                        contentType:nil 
563
                                                                                    contentEncoding:nil 
564
                                                                                 contentDisposition:nil 
565
                                                                                           manifest:nil 
566
                                                                                            sharing:nil 
567
                                                                                           isPublic:ASIPithosObjectRequestPublicIgnore 
568
                                                                                           metadata:nil 
569
                                                                           destinationContainerName:destinationContainerName 
570
                                                                              destinationObjectName:destinationObjectName 
571
                                                                                 destinationAccount:nil
572
                                                                                      sourceVersion:nil];
573
    objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
574
                              containerName, @"sourceContainerName", 
575
                              objectName, @"sourceObjectName", 
576
                              destinationContainerName, @"destinationContainerName", 
577
                              destinationObjectName, @"destinationObjectName", 
578
                              nil];
579
    if (sharingAccount) 
580
        [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
581
    return objectRequest;
582
}
583

    
584
+ (NSArray *)copyObjectRequestsForSubdirWithPithos:(ASIPithos *)pithos 
585
                                     containerName:(NSString *)containerName 
586
                                        objectName:(NSString *)objectName 
587
                          destinationContainerName:(NSString *)destinationContainerName 
588
                             destinationObjectName:(NSString *)destinationObjectName 
589
                                     checkIfExists:(BOOL)ifExists 
590
                                    sharingAccount:(NSString *)sharingAccount {
591
    if (ifExists && ![self proceedIfObjectExistsAtPithos:pithos containerName:destinationContainerName objectName:destinationObjectName 
592
                                          sharingAccount:nil])
593
        return nil;
594
    
595
    NSArray *objects = [self objectsForSubdirWithPithos:pithos containerName:containerName objectName:objectName 
596
                                              delimiter:nil sharingAccount:sharingAccount];
597
    if (objects == nil)
598
        return nil;
599
    
600
    NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
601
    ASIPithosObjectRequest *objectRequest;
602
    if ([objectName isEqualToString:destinationObjectName]) {
603
        if (![objectName hasSuffix:@"/"]) {
604
            objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithPithos:pithos 
605
                                                                      containerName:containerName 
606
                                                                         objectName:objectName 
607
                                                                        contentType:nil 
608
                                                                    contentEncoding:nil 
609
                                                                 contentDisposition:nil 
610
                                                                           manifest:nil 
611
                                                                            sharing:nil 
612
                                                                           isPublic:ASIPithosObjectRequestPublicIgnore 
613
                                                                           metadata:nil 
614
                                                           destinationContainerName:destinationContainerName 
615
                                                              destinationObjectName:objectName 
616
                                                                 destinationAccount:nil 
617
                                                                      sourceVersion:nil];
618
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
619
                                      containerName, @"sourceContainerName", 
620
                                      objectName, @"sourceObjectName", 
621
                                      destinationContainerName, @"destinationContainerName", 
622
                                      objectName, @"destinationObjectName", 
623
                                      nil];
624
            if (sharingAccount)
625
                [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
626
            [objectRequests addObject:objectRequest];
627
        }
628
        for (ASIPithosObject *object in objects) {
629
            objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithPithos:pithos 
630
                                                                      containerName:containerName 
631
                                                                         objectName:object.name 
632
                                                                        contentType:nil 
633
                                                                    contentEncoding:nil 
634
                                                                 contentDisposition:nil 
635
                                                                           manifest:nil 
636
                                                                            sharing:nil 
637
                                                                           isPublic:ASIPithosObjectRequestPublicIgnore 
638
                                                                           metadata:nil 
639
                                                           destinationContainerName:destinationContainerName 
640
                                                              destinationObjectName:object.name 
641
                                                                 destinationAccount:nil 
642
                                                                      sourceVersion:nil];
643
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
644
                                      containerName, @"sourceContainerName", 
645
                                      object.name, @"sourceObjectName", 
646
                                      destinationContainerName, @"destinationContainerName", 
647
                                      object.name, @"destinationObjectName", 
648
                                      nil];
649
            if (sharingAccount)
650
                [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
651
            [objectRequests addObject:objectRequest];
652
        }
653
    } else {
654
        if (![objectName hasSuffix:@"/"]) {
655
            objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithPithos:pithos 
656
                                                                      containerName:containerName 
657
                                                                         objectName:objectName 
658
                                                                        contentType:nil 
659
                                                                    contentEncoding:nil 
660
                                                                 contentDisposition:nil 
661
                                                                           manifest:nil 
662
                                                                            sharing:nil 
663
                                                                           isPublic:ASIPithosObjectRequestPublicIgnore 
664
                                                                           metadata:nil 
665
                                                           destinationContainerName:destinationContainerName 
666
                                                              destinationObjectName:destinationObjectName 
667
                                                                 destinationAccount:nil 
668
                                                                      sourceVersion:nil];
669
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
670
                                      containerName, @"sourceContainerName", 
671
                                      objectName, @"sourceObjectName", 
672
                                      destinationContainerName, @"destinationContainerName", 
673
                                      destinationObjectName, @"destinationObjectName", 
674
                                      nil];
675
            if (sharingAccount)
676
                [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
677
            [objectRequests addObject:objectRequest];
678
        }
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
685
                                                                        range:prefixRange];
686
            objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithPithos:pithos 
687
                                                                      containerName:containerName 
688
                                                                         objectName:object.name 
689
                                                                        contentType:nil 
690
                                                                    contentEncoding:nil 
691
                                                                 contentDisposition:nil 
692
                                                                           manifest:nil 
693
                                                                            sharing:nil 
694
                                                                           isPublic:ASIPithosObjectRequestPublicIgnore 
695
                                                                           metadata:nil 
696
                                                           destinationContainerName:destinationContainerName 
697
                                                              destinationObjectName:newObjectName 
698
                                                                 destinationAccount:nil 
699
                                                                      sourceVersion:nil];
700
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
701
                                      containerName, @"sourceContainerName", 
702
                                      object.name, @"sourceObjectName", 
703
                                      destinationContainerName, @"destinationContainerName", 
704
                                      newObjectName, @"destinationObjectName", 
705
                                      nil];
706
            if (sharingAccount)
707
                [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
708
            [objectRequests addObject:objectRequest];
709
        }
710
    }
711
    
712
    if ([objectRequests count] == 0)
713
        return nil;
714
    return objectRequests;
715
}
716

    
717
#pragma mark -
718
#pragma mark Move
719

    
720
+ (ASIPithosObjectRequest *)moveObjectRequestWithPithos:(ASIPithos *)pithos 
721
                                          containerName:(NSString *)containerName 
722
                                             objectName:(NSString *)objectName 
723
                               destinationContainerName:(NSString *)destinationContainerName 
724
                                  destinationObjectName:(NSString *)destinationObjectName 
725
                                          checkIfExists:(BOOL)ifExists {
726
    if (ifExists && ![self proceedIfObjectExistsAtPithos:pithos containerName:destinationContainerName objectName:destinationObjectName 
727
                                          sharingAccount:nil])
728
        return nil;
729
    
730
    ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithPithos:pithos 
731
                                                                                      containerName:containerName 
732
                                                                                         objectName:objectName 
733
                                                                                        contentType:nil 
734
                                                                                    contentEncoding:nil 
735
                                                                                 contentDisposition:nil 
736
                                                                                           manifest:nil 
737
                                                                                            sharing:nil 
738
                                                                                           isPublic:ASIPithosObjectRequestPublicIgnore 
739
                                                                                           metadata:nil 
740
                                                                           destinationContainerName:destinationContainerName 
741
                                                                              destinationObjectName:destinationObjectName 
742
                                                                                 destinationAccount:nil];
743
    objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
744
                              containerName, @"sourceContainerName", 
745
                              objectName, @"sourceObjectName", 
746
                              destinationContainerName, @"destinationContainerName", 
747
                              destinationObjectName, @"destinationObjectName", 
748
                              nil];
749
    return objectRequest;
750
}
751

    
752
+ (NSArray *)moveObjectRequestsForSubdirWithPithos:(ASIPithos *)pithos 
753
                                     containerName:(NSString *)containerName 
754
                                        objectName:(NSString *)objectName 
755
                          destinationContainerName:(NSString *)destinationContainerName 
756
                             destinationObjectName:(NSString *)destinationObjectName 
757
                                     checkIfExists:(BOOL)ifExists {
758
    if (ifExists && ![self proceedIfObjectExistsAtPithos:pithos containerName:destinationContainerName objectName:destinationObjectName 
759
                                          sharingAccount:nil])
760
        return nil;
761
    
762
    NSArray *objects = [self objectsForSubdirWithPithos:pithos containerName:containerName objectName:objectName 
763
                                              delimiter:nil sharingAccount:nil];
764
    if (objects == nil)
765
        return nil;
766
    
767
    ASIPithosObjectRequest *objectRequest;
768
    NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
769
    if ([objectName isEqualToString:destinationObjectName]) {
770
        if (![objectName hasSuffix:@"/"]) {
771
            objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithPithos:pithos 
772
                                                                      containerName:containerName 
773
                                                                         objectName:objectName 
774
                                                                        contentType:nil 
775
                                                                    contentEncoding:nil 
776
                                                                 contentDisposition:nil 
777
                                                                           manifest:nil 
778
                                                                            sharing:nil 
779
                                                                           isPublic:ASIPithosObjectRequestPublicIgnore 
780
                                                                           metadata:nil 
781
                                                           destinationContainerName:destinationContainerName 
782
                                                              destinationObjectName:objectName 
783
                                                                 destinationAccount:nil];
784
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
785
                                      containerName, @"sourceContainerName", 
786
                                      objectName, @"sourceObjectName", 
787
                                      destinationContainerName, @"destinationContainerName", 
788
                                      objectName, @"destinationObjectName", 
789
                                      nil];
790
            [objectRequests addObject:objectRequest];
791
        }
792
        for (ASIPithosObject *object in objects) {
793
            objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithPithos:pithos 
794
                                                                      containerName:containerName 
795
                                                                         objectName:object.name 
796
                                                                        contentType:nil 
797
                                                                    contentEncoding:nil 
798
                                                                 contentDisposition:nil 
799
                                                                           manifest:nil 
800
                                                                            sharing:nil 
801
                                                                           isPublic:ASIPithosObjectRequestPublicIgnore 
802
                                                                           metadata:nil 
803
                                                           destinationContainerName:destinationContainerName 
804
                                                              destinationObjectName:object.name 
805
                                                                 destinationAccount:nil];
806
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
807
                                      containerName, @"sourceContainerName", 
808
                                      object.name, @"sourceObjectName", 
809
                                      destinationContainerName, @"destinationContainerName", 
810
                                      object.name, @"destinationObjectName", 
811
                                      nil];
812
            [objectRequests addObject:objectRequest];
813
        }
814
    } else {
815
        if (![objectName hasSuffix:@"/"]) {
816
            objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithPithos:pithos 
817
                                                                      containerName:containerName 
818
                                                                         objectName:objectName 
819
                                                                        contentType:nil 
820
                                                                    contentEncoding:nil 
821
                                                                 contentDisposition:nil 
822
                                                                           manifest:nil 
823
                                                                            sharing:nil 
824
                                                                           isPublic:ASIPithosObjectRequestPublicIgnore 
825
                                                                           metadata:nil 
826
                                                           destinationContainerName:destinationContainerName 
827
                                                              destinationObjectName:destinationObjectName 
828
                                                                 destinationAccount:nil];
829
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
830
                                      containerName, @"sourceContainerName", 
831
                                      objectName, @"sourceObjectName", 
832
                                      destinationContainerName, @"destinationContainerName", 
833
                                      destinationObjectName, @"destinationObjectName", 
834
                                      nil];
835
            [objectRequests addObject:objectRequest];
836
        }
837
        NSRange prefixRange = NSMakeRange(0, [objectName length]);
838
        NSString *newObjectName;
839
        for (ASIPithosObject *object in objects) {
840
            newObjectName = [object.name stringByReplacingOccurrencesOfString:objectName
841
                                                                   withString:destinationObjectName
842
                                                                      options:NSAnchoredSearch
843
                                                                        range:prefixRange];
844
            objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithPithos:pithos 
845
                                                                      containerName:containerName 
846
                                                                         objectName:object.name
847
                                                                        contentType:nil 
848
                                                                    contentEncoding:nil 
849
                                                                 contentDisposition:nil 
850
                                                                           manifest:nil 
851
                                                                            sharing:nil 
852
                                                                           isPublic:ASIPithosObjectRequestPublicIgnore 
853
                                                                           metadata:nil 
854
                                                           destinationContainerName:destinationContainerName 
855
                                                              destinationObjectName:newObjectName 
856
                                                                 destinationAccount:nil];
857
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
858
                                      containerName, @"sourceContainerName", 
859
                                      object.name, @"sourceObjectName", 
860
                                      destinationContainerName, @"destinationContainerName", 
861
                                      newObjectName, @"destinationObjectName", 
862
                                      nil];
863
            [objectRequests addObject:objectRequest];
864
        }
865
    }
866
     
867
    if ([objectRequests count] == 0)
868
        return nil;
869
    return objectRequests;
870
}
871

    
872
#pragma mark -
873
#pragma mark Helper Methods
874

    
875
// Size of the file in bytes
876
+ (NSUInteger)bytesOfFile:(NSString *)filePath {
877
    NSFileManager *fileManager = [NSFileManager defaultManager];
878
    NSDictionary *attributes = [fileManager attributesOfItemAtPath:filePath error:nil];
879
    return [[attributes objectForKey:NSFileSize] intValue];
880
}
881

    
882
// Content type of the file or nil if it cannot be determined
883
+ (NSString *)contentTypeOfFile:(NSString *)filePath error:(NSError **)error {
884
    NSURLResponse *response = nil;
885
    [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath] 
886
                                                             cachePolicy:NSURLCacheStorageNotAllowed 
887
                                                         timeoutInterval:.1] 
888
                          returningResponse:&response 
889
                                      error:error];
890
    return [response MIMEType];
891
}
892

    
893
// Creates a directory if it doesn't exist and returns if successful
894
+ (BOOL)safeCreateDirectory:(NSString *)directoryPath error:(NSError **)error {
895
    NSFileManager *fileManager = [NSFileManager defaultManager];
896
    BOOL isDirectory;
897
    BOOL fileExists = [fileManager fileExistsAtPath:directoryPath isDirectory:&isDirectory];
898
    if (fileExists)
899
        return isDirectory;
900
    if (![fileManager createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:error] || *error)
901
        return NO;
902
    return YES;
903
}
904

    
905
// Returns if an object is a directory based on its content type
906
+ (BOOL)isContentTypeDirectory:(NSString *)contentType {
907
    return ([contentType isEqualToString:@"application/directory"] ||
908
            [contentType hasPrefix:@"application/directory;"] ||
909
            [contentType isEqualToString:@"application/folder"] ||
910
            [contentType hasPrefix:@"application/folder;"]);
911
}
912

    
913
// Returns if an object exists at the given container/object path and if this object is an application/directory
914
// If an error occured an alert is shown and it is returned so the caller won't proceed
915
+ (BOOL)objectExistsAtPithos:(ASIPithos *)pithos containerName:(NSString *)containerName objectName:(NSString *)objectName 
916
                       error:(NSError **)error isDirectory:(BOOL *)isDirectory sharingAccount:(NSString *)sharingAccount {
917
    ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectMetadataRequestWithPithos:pithos 
918
                                                                                      containerName:containerName 
919
                                                                                         objectName:objectName];
920
    if (sharingAccount)
921
        [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
922
    ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
923
    [networkQueue go];
924
    [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:objectRequest]] waitUntilFinished:YES];
925
    *error = [objectRequest error];
926
    if (*error) {
927
        [self httpRequestErrorAlertWithRequest:objectRequest];
928
        return NO;
929
    } else if (objectRequest.responseStatusCode == 200) {
930
        *isDirectory = [self isContentTypeDirectory:[objectRequest contentType]];
931
        return YES;
932
    }
933
    return NO;
934
}
935

    
936
// Returns if the caller should proceed, after an interactive check if an object exists 
937
// at the given container/object path is performed
938
+ (BOOL)proceedIfObjectExistsAtPithos:(ASIPithos *)pithos containerName:(NSString *)containerName objectName:(NSString *)objectName 
939
                       sharingAccount:(NSString *)sharingAccount {
940
    NSError *error = nil;
941
    BOOL isDirectory;
942
    BOOL objectExists = [self objectExistsAtPithos:pithos containerName:containerName objectName:objectName 
943
                                             error:&error isDirectory:&isDirectory sharingAccount:sharingAccount];
944
    if (error) {
945
        return NO;
946
    } else if (objectExists) {
947
        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
948
        if (isDirectory) {
949
            [alert setMessageText:@"Directory Exists"];
950
            if (sharingAccount)
951
                [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
952
            else
953
                [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
954
        } else {
955
            [alert setMessageText:@"Object Exists"];
956
            if (sharingAccount)
957
                [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
958
            else
959
                [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
960
        }
961
        [alert addButtonWithTitle:@"OK"];
962
        [alert addButtonWithTitle:@"Cancel"];
963
        NSInteger choice = [alert runModal];
964
        if (choice == NSAlertSecondButtonReturn)
965
            return NO;
966
    }
967
    return YES;
968
}
969

    
970
// List of objects at the given container/object path, with prefix and or delimiter
971
+ (NSArray *)objectsWithPithos:(ASIPithos *)pithos containerName:(NSString *)containerName objectNamePrefix:(NSString *)objectNamePrefix 
972
                     delimiter:(NSString *)delimiter sharingAccount:(NSString *)sharingAccount {
973
    NSMutableArray *objects = [NSMutableArray array];
974
    NSString *marker = nil;
975
    do {
976
        ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest listObjectsRequestWithPithos:pithos 
977
                                                                                                containerName:containerName 
978
                                                                                                        limit:0 
979
                                                                                                       marker:marker 
980
                                                                                                       prefix:objectNamePrefix 
981
                                                                                                    delimiter:delimiter 
982
                                                                                                         path:nil 
983
                                                                                                         meta:nil 
984
                                                                                                       shared:NO 
985
                                                                                                        until:nil];
986
        if (sharingAccount)
987
            [containerRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
988
        ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
989
        [networkQueue go];
990
        [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:containerRequest]] waitUntilFinished:YES];
991
        if ([containerRequest error]) {
992
            [self httpRequestErrorAlertWithRequest:containerRequest];
993
            return nil;
994
        }
995
        NSArray *someObjects = [containerRequest objects];
996
        [objects addObjectsFromArray:someObjects];
997
        if ([someObjects count] < 10000)
998
            marker = nil;
999
        else
1000
            marker = [[someObjects lastObject] name];
1001
    } while (marker);
1002
    return objects;
1003
}
1004

    
1005
// List of objects at the given container/object path, that may be a subdir or an application/directory, 
1006
// with prefix and or delimiter
1007
+ (NSArray *)objectsForSubdirWithPithos:(ASIPithos *)pithos containerName:(NSString *)containerName objectName:(NSString *)objectName 
1008
                              delimiter:(NSString *)delimiter sharingAccount:(NSString *)sharingAccount {
1009
    NSString *subdirNamePrefix = [NSString stringWithString:objectName];
1010
    if (![subdirNamePrefix hasSuffix:@"/"])
1011
        subdirNamePrefix = [subdirNamePrefix stringByAppendingString:@"/"];
1012
    return [self objectsWithPithos:pithos containerName:containerName objectNamePrefix:subdirNamePrefix 
1013
                         delimiter:delimiter sharingAccount:sharingAccount];
1014
}
1015

    
1016
// A safe object name at the given container/object path
1017
// The original name has " %d" appended to it before any ".*" suffix, for the first integer that produces a name that is free to use
1018
// If the original name hasn't got a "." but has a "/" suffix, then it is replaced with " %d/" instead
1019
// Subdirs are taken into consideration
1020
+ (NSString *)safeObjectNameForPithos:(ASIPithos *)pithos containerName:(NSString *)containerName objectName:(NSString *)objectName {
1021
    NSString *objectNamePrefix;
1022
    NSString *objectNameExtraSuffix;
1023
    NSRange lastDotRange = [objectName rangeOfString:@"." options:NSBackwardsSearch];
1024
    if (lastDotRange.length == 1) {
1025
        objectNamePrefix = [objectName substringToIndex:lastDotRange.location];
1026
        objectNameExtraSuffix = [objectName substringFromIndex:lastDotRange.location];
1027
    } else if ([objectName hasSuffix:@"/"]) {
1028
        objectNamePrefix = [objectName substringToIndex:([objectName length] - 1)];
1029
        objectNameExtraSuffix = [NSString stringWithString:@"/"];
1030
    } else {
1031
        objectNamePrefix = [NSString stringWithString:objectName];
1032
        objectNameExtraSuffix = [NSString string];
1033
    }
1034
    NSArray *objects = [self objectsWithPithos:pithos containerName:containerName 
1035
                              objectNamePrefix:[objectNamePrefix stringByDeletingLastPathComponent] 
1036
                                     delimiter:@"/" sharingAccount:nil];
1037
    if (objects == nil)
1038
        return nil;
1039
    if ([objects count] == 0)
1040
        return objectName;
1041
    NSMutableArray *objectNames = [NSMutableArray arrayWithArray:
1042
                                   [[objects objectsAtIndexes:
1043
                                     [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
1044
        if (pithosObject.subdir)
1045
            return NO;
1046
        return YES;
1047
    }]] valueForKey:@"name"]];
1048
    for (NSString *name in [[objects objectsAtIndexes:
1049
                             [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
1050
        if (pithosObject.subdir)
1051
            return YES;
1052
        return NO;
1053
    }]] valueForKey:@"name"]) {
1054
        [objectNames addObject:[name substringToIndex:([name length] - 1)]];
1055
    }
1056
    if (![objectNames containsObject:objectName])
1057
        return objectName;
1058
    NSUInteger objectNameSuffix = 2;
1059
    NSString *safeObjectName;
1060
    do {
1061
        safeObjectName = [objectNamePrefix stringByAppendingFormat:@" %lu%@", objectNameSuffix, objectNameExtraSuffix];
1062
        objectNameSuffix++;
1063
    } while ([objectNames containsObject:safeObjectName]);
1064
    return safeObjectName;    
1065
}
1066

    
1067
// A safe object name at the given container/object path that may be a subdir or application/directory
1068
// The original name has " %d" appended to it, for the first integer that produces a name that is free to use
1069
// If the original name has a "/" suffix, then it is replaced with " %d/" instead
1070
// Subdirs are taken into consideration
1071
+ (NSString *)safeSubdirNameForPithos:(ASIPithos *)pithos containerName:(NSString *)containerName subdirName:(NSString *)subdirName {
1072
    NSString *subdirNamePrefix;
1073
    NSString *subdirNameExtraSuffix;
1074
    if ([subdirName hasSuffix:@"/"]) {
1075
        subdirNamePrefix = [subdirName substringToIndex:([subdirName length] - 1)];
1076
        subdirNameExtraSuffix = [NSString stringWithString:@"/"];
1077
    } else {
1078
        subdirNamePrefix = [NSString stringWithString:subdirName];
1079
        subdirNameExtraSuffix = [NSString string];
1080
    }
1081
    NSArray *objects = [self objectsWithPithos:pithos containerName:containerName 
1082
                              objectNamePrefix:[subdirNamePrefix stringByDeletingLastPathComponent] 
1083
                                     delimiter:@"/" sharingAccount:nil];
1084
    if (objects == nil)
1085
        return nil;
1086
    if ([objects count] == 0)
1087
        return subdirName;
1088
    NSMutableArray *objectNames = [NSMutableArray arrayWithArray:
1089
                                   [[objects objectsAtIndexes:
1090
                                     [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
1091
        if (pithosObject.subdir)
1092
            return NO;
1093
        return YES;
1094
    }]] valueForKey:@"name"]];
1095
    for (NSString *name in [[objects objectsAtIndexes:
1096
                             [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
1097
        if (pithosObject.subdir)
1098
            return YES;
1099
        return NO;
1100
    }]] valueForKey:@"name"]) {
1101
        [objectNames addObject:[name substringToIndex:([name length] - 1)]];
1102
    }
1103
    if (![objectNames containsObject:subdirNamePrefix])
1104
        return subdirName;
1105
    NSUInteger subdirNameSuffix = 2;
1106
    NSString *safeSubdirName;
1107
    do {
1108
        safeSubdirName = [subdirNamePrefix stringByAppendingFormat:@" %lu", subdirNameSuffix];
1109
        subdirNameSuffix++;
1110
    } while ([objectNames containsObject:safeSubdirName]);
1111
    return [safeSubdirName stringByAppendingString:subdirNameExtraSuffix];
1112
}
1113

    
1114
#pragma mark -
1115
#pragma mark Alerts
1116

    
1117
+ (NSInteger)httpRequestErrorAlertWithRequest:(ASIPithosRequest *)request {
1118
    NSString *message = [NSString stringWithFormat:
1119
                         @"HTTP request error: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@", 
1120
                         [request error], 
1121
                         request.requestMethod, 
1122
                         request.url, 
1123
                         [request requestHeaders], 
1124
                         [request responseHeaders], 
1125
                         [request responseString]];
1126
    NSLog(@"%@", message);
1127
    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1128
    [alert setMessageText:@"HTTP Request Error"];
1129
    [alert setInformativeText:message];
1130
    [alert addButtonWithTitle:@"OK"];
1131
    return [alert runModal];
1132
}
1133

    
1134
+ (NSInteger)unexpectedResponseStatusAlertWithRequest:(ASIPithosRequest *)request {
1135
    NSString *message = [NSString stringWithFormat:
1136
                         @"Unexpected response status %d: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@", 
1137
                         request.responseStatusCode, 
1138
                         request.responseStatusMessage, 
1139
                         request.requestMethod, 
1140
                         request.url, 
1141
                         [request requestHeaders], 
1142
                         [request responseHeaders], 
1143
                         [request responseString]];
1144
    NSLog(@"%@", message);
1145
    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1146
    [alert setMessageText:@"Unexpected Response Status"];
1147
    [alert setInformativeText:message];
1148
    [alert addButtonWithTitle:@"OK"];
1149
    return [alert runModal];
1150
}
1151

    
1152
+ (NSInteger)fileActionFailedAlertWithTitle:(NSString *)title message:(NSString *)message error:(NSError *)error {
1153
    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1154
    [alert setMessageText:title];
1155
    if (error)
1156
        [alert setInformativeText:[NSString stringWithFormat:@"%@: %@", message, error]];
1157
    else
1158
        [alert setInformativeText:message];
1159
    [alert addButtonWithTitle:@"OK"];
1160
    return [alert runModal];
1161
}
1162

    
1163
#pragma mark -
1164
#pragma mark Request Helper Methods
1165

    
1166
+ (ASIPithosRequest *)prepareRequest:(ASIPithosRequest *)request priority:(NSOperationQueuePriority)priority {
1167
    [request setTimeOutSeconds:60];
1168
    request.numberOfTimesToRetryOnTimeout = 10;
1169
    [request setQueuePriority:priority];
1170
    return request;
1171
}
1172

    
1173
+ (ASIPithosRequest *)prepareRequest:(ASIPithosRequest *)request {
1174
    return [self prepareRequest:request priority:NSOperationQueuePriorityNormal];
1175
}
1176

    
1177
+ (ASIPithosRequest *)copyRequest:(ASIPithosRequest *)request {
1178
    NSMutableDictionary *userInfo = (NSMutableDictionary *)[[request.userInfo retain] autorelease];
1179
    request.userInfo = nil;
1180
    ASIPithosRequest *newRequest = [request copy];
1181
    newRequest.userInfo = userInfo;
1182
    return newRequest;
1183
}
1184

    
1185
@end