Statistics
| Branch: | Tag: | Revision:

root / pithos-macos / PithosUtilities.m @ d8426ffb

History | View | Annotate | Download (71.8 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
                                            toDirectory:(NSString *)directoryPath 
55
                                          checkIfExists:(BOOL)ifExists 
56
                                         sharingAccount:(NSString *)sharingAccount {
57
    NSString *fileName = [objectName lastPathComponent];
58
    if([objectName hasSuffix:@"/"])
59
        fileName = [fileName stringByAppendingString:@"/"];    
60
    fileName = [fileName stringByReplacingOccurrencesOfString:@"/" withString:@":"];
61
    
62
    NSFileManager *fileManager = [NSFileManager defaultManager];
63
    
64
    NSString *destinationPath = [directoryPath stringByAppendingPathComponent:fileName];
65
    if (ifExists && [fileManager fileExistsAtPath:destinationPath]) {
66
        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
67
        [alert setMessageText:@"File Exists"];
68
        [alert setInformativeText:[NSString stringWithFormat:@"A file or directory named '%@' already exists, do you want to replace it?", fileName]];
69
        [alert addButtonWithTitle:@"OK"];
70
        [alert addButtonWithTitle:@"Cancel"];
71
        NSInteger choice = [alert runModal];
72
        if (choice == NSAlertSecondButtonReturn)
73
            return nil;
74
    }
75
    
76
    BOOL directoryIsDirectory;
77
    BOOL directoryExists = [fileManager fileExistsAtPath:directoryPath isDirectory:&directoryIsDirectory];
78
    NSError *error = nil;
79
    if (!directoryExists) {
80
        [fileManager createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error];
81
    } else if (!directoryIsDirectory) {
82
        [fileManager removeItemAtPath:directoryPath error:&error];
83
    }
84
    if (error) {
85
        NSLog(@"Cannot remove existing file '%@': %@", fileName, error);
86
        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
87
        [alert setMessageText:@"Removal Error"];
88
        [alert setInformativeText:[NSString stringWithFormat:@"Cannot remove existing file '%@': %@", fileName, error]];
89
        [alert addButtonWithTitle:@"OK"];
90
        [alert runModal];
91
        return nil;
92
    }
93

    
94
    ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectDataRequestWithPithos:pithos 
95
                                                                                  containerName:containerName 
96
                                                                                     objectName:objectName];
97
    if (sharingAccount)
98
        [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
99
    objectRequest.downloadDestinationPath = destinationPath;
100
    objectRequest.allowResumeForFileDownloads = YES;
101
    objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
102
                              fileName, @"fileName", 
103
                              destinationPath, @"filePath", 
104
                              nil];
105
    return objectRequest;
106
}
107

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

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

    
201
#pragma mark -
202
#pragma mark Download Block
203

    
204
+ (ASIPithosObjectRequest *)objectBlockDataRequestWithPithos:(ASIPithos *)pithos 
205
                                               containerName:(NSString *)containerName 
206
                                                      object:(ASIPithosObject *)object 
207
                                                  blockIndex:(NSUInteger)blockIndex 
208
                                                   blockSize:(NSUInteger)blockSize {
209
    NSUInteger rangeStart = blockIndex * blockSize;
210
    NSUInteger rangeEnd = (rangeStart + blockSize <= object.bytes) ? (rangeStart + blockSize - 1) : (object.bytes - 1);
211
    ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectDataRequestWithPithos:pithos 
212
                                                                                  containerName:containerName
213
                                                                                     objectName:object.name
214
                                                                                        version:nil
215
                                                                                          range:[NSString stringWithFormat:@"bytes=%lu-%lu", rangeStart, rangeEnd]
216
                                                                                        ifMatch:object.hash];
217
    return objectRequest;
218
}
219

    
220
+ (NSIndexSet *)missingBlocksForFile:(NSString *)filePath
221
                           blockSize:(NSUInteger)blockSize 
222
                           blockHash:(NSString *)blockHash 
223
                          withHashes:(NSArray *)hashes {
224
    NSArray *fileHashes = [HashMapHash objectHashMapStrings:filePath withBlockHash:blockHash andBlockSize:blockSize];
225
    return [hashes indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
226
        if ((idx >= [fileHashes count]) || ![(NSString *)obj isEqualToString:[fileHashes objectAtIndex:idx]])
227
            return YES;
228
        return NO;
229
    }];
230
}
231

    
232
#pragma mark -
233
#pragma mark Upload
234

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

    
280
+ (NSIndexSet *)missingBlocksForHashes:(NSArray *)hashes withMissingHashes:(NSArray *)missingHashes {
281
    NSMutableIndexSet *missingBlocks = [NSMutableIndexSet indexSet];
282
    for (NSString *missingHash in missingHashes) {
283
        if (![missingHash length])
284
            break;
285
        NSUInteger missingBlock = [hashes indexOfObject:missingHash];
286
        if (missingBlock != NSNotFound)
287
            [missingBlocks addIndex:missingBlock];
288
    }
289
    return missingBlocks;
290
}
291

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

    
325
    [missingBlocks enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
326
        [fileHandle seekToFileOffset:(idx*blockSize)];
327
        [tempFileHandle writeData:[fileHandle readDataOfLength:blockSize]];
328
    }];
329
    [tempFileHandle closeFile];
330
    [fileHandle closeFile];
331

    
332
    ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest updateContainerDataRequestWithPithos:pithos 
333
                                                                                                    containerName:containerName 
334
                                                                                                           policy:nil 
335
                                                                                                         metadata:nil 
336
                                                                                                           update:YES 
337
                                                                                                             file:tempFilePath];
338
    if (sharingAccount)
339
        [containerRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
340
    return containerRequest;
341
}
342

    
343
+ (ASIPithosContainerRequest *)updateContainerDataRequestWithPithos:(ASIPithos *)pithos 
344
                                                      containerName:(NSString *)containerName 
345
                                                          blockSize:(NSUInteger)blockSize 
346
                                                            forFile:(NSString *)filePath 
347
                                                  missingBlockIndex:(NSUInteger)missingBlockIndex 
348
                                                     sharingAccount:(NSString *)sharingAccount {
349
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
350
    [fileHandle seekToFileOffset:(missingBlockIndex *blockSize)];
351
    NSData *blockData = [fileHandle readDataOfLength:blockSize];
352
    [fileHandle closeFile];
353
    ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest updateContainerDataRequestWithPithos:pithos 
354
                                                                                                    containerName:containerName 
355
                                                                                                           policy:nil 
356
                                                                                                         metadata:nil 
357
                                                                                                           update:YES 
358
                                                                                                             data:blockData];
359
    if (sharingAccount)
360
        [containerRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
361
    return containerRequest;
362
}
363

    
364
+ (NSArray *)writeObjectDataRequestsWithPithos:(ASIPithos *)pithos 
365
                                 containerName:(NSString *)containerName
366
                                    objectName:(NSString *)objectName
367
                                     blockSize:(NSUInteger)blockSize 
368
                                     blockHash:(NSString *)blockHash 
369
                                  forDirectory:(NSString *)directoryPath 
370
                                 checkIfExists:(BOOL)ifExists 
371
                                   objectNames:(NSMutableArray **)objectNames
372
                                  contentTypes:(NSMutableArray **)contentTypes
373
                                     filePaths:(NSMutableArray **)filePaths 
374
                                  hashesArrays:(NSMutableArray **)hashesArrays 
375
                       directoryObjectRequests:(NSMutableArray **) directoryObjectRequests 
376
                                sharingAccount:(NSString *)sharingAccount {
377
    if (ifExists && ![self proceedIfObjectExistsAtPithos:pithos containerName:containerName objectName:objectName 
378
                                          sharingAccount:sharingAccount])
379
        return nil;
380

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

    
498
#pragma mark -
499
#pragma mark Delete
500

    
501
+ (NSArray *)deleteObjectRequestsForSubdirWithPithos:(ASIPithos *)pithos 
502
                                       containerName:(NSString *)containerName 
503
                                          objectName:(NSString *)objectName {
504
    NSArray *objects = [self objectsForSubdirWithPithos:pithos containerName:containerName objectName:objectName delimiter:nil 
505
                                         sharingAccount:nil];
506
    if (objects == nil)
507
        return nil;
508

    
509
    NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
510
    ASIPithosObjectRequest *objectRequest;
511
    if (![objectName hasSuffix:@"/"]) {
512
        objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos containerName:containerName objectName:objectName];
513
        objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
514
                                  [objectName lastPathComponent], @"fileName", 
515
                                  nil];
516
        [objectRequests addObject:objectRequest];
517
    }
518
    NSString *fileName;
519
    for (ASIPithosObject *object in objects) {
520
        fileName = [object.name lastPathComponent];
521
        if ([object.name hasSuffix:@"/"])
522
            fileName = [fileName stringByAppendingString:@"/"];
523
        objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos containerName:containerName objectName:object.name];
524
        objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
525
                                  fileName, @"fileName", 
526
                                  nil];
527
        [objectRequests addObject:objectRequest];
528
    }
529
    
530
    if ([objectRequests count] == 0)
531
        return nil;
532
    return objectRequests;
533
}
534

    
535
#pragma mark -
536
#pragma mark Copy
537

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

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

    
707
#pragma mark -
708
#pragma mark Move
709

    
710
+ (ASIPithosObjectRequest *)moveObjectRequestWithPithos:(ASIPithos *)pithos 
711
                                          containerName:(NSString *)containerName 
712
                                             objectName:(NSString *)objectName 
713
                               destinationContainerName:(NSString *)destinationContainerName 
714
                                  destinationObjectName:(NSString *)destinationObjectName 
715
                                          checkIfExists:(BOOL)ifExists {
716
    if (ifExists && ![self proceedIfObjectExistsAtPithos:pithos containerName:destinationContainerName objectName:destinationObjectName 
717
                                          sharingAccount:nil])
718
        return nil;
719
    
720
    ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithPithos:pithos 
721
                                                                                      containerName:containerName 
722
                                                                                         objectName:objectName 
723
                                                                                        contentType:nil 
724
                                                                                    contentEncoding:nil 
725
                                                                                 contentDisposition:nil 
726
                                                                                           manifest:nil 
727
                                                                                            sharing:nil 
728
                                                                                           isPublic:ASIPithosObjectRequestPublicIgnore 
729
                                                                                           metadata:nil 
730
                                                                           destinationContainerName:destinationContainerName 
731
                                                                              destinationObjectName:destinationObjectName 
732
                                                                                 destinationAccount:nil];
733
    objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
734
                              containerName, @"sourceContainerName", 
735
                              objectName, @"sourceObjectName", 
736
                              destinationContainerName, @"destinationContainerName", 
737
                              destinationObjectName, @"destinationObjectName", 
738
                              nil];
739
    return objectRequest;
740
}
741

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

    
862
#pragma mark -
863
#pragma mark Helper Methods
864

    
865
// Size of the file in bytes
866
+ (NSUInteger)bytesOfFile:(NSString *)filePath {
867
    NSFileManager *fileManager = [NSFileManager defaultManager];
868
    NSDictionary *attributes = [fileManager attributesOfItemAtPath:filePath error:nil];
869
    return [[attributes objectForKey:NSFileSize] intValue];
870
}
871

    
872
// Content type of the file or nil if it cannot be determined
873
+ (NSString *)contentTypeOfFile:(NSString *)filePath error:(NSError **)error {
874
    NSURLResponse *response = nil;
875
    [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath] 
876
                                                             cachePolicy:NSURLCacheStorageNotAllowed 
877
                                                         timeoutInterval:.1] 
878
                          returningResponse:&response 
879
                                      error:error];
880
    return [response MIMEType];
881
}
882

    
883
// Creates a directory if it doesn't exist and returns if successful
884
+ (BOOL)safeCreateDirectory:(NSString *)directoryPath error:(NSError **)error {
885
    NSFileManager *fileManager = [NSFileManager defaultManager];
886
    BOOL isDirectory;
887
    BOOL fileExists = [fileManager fileExistsAtPath:directoryPath isDirectory:&isDirectory];
888
    if (fileExists)
889
        return isDirectory;
890
    if (![fileManager createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:error] || *error)
891
        return NO;
892
    return YES;
893
}
894

    
895
// Returns if an object is a directory based on its content type
896
+ (BOOL)isContentTypeDirectory:(NSString *)contentType {
897
    return ([contentType isEqualToString:@"application/directory"] ||
898
            [contentType hasPrefix:@"application/directory;"] ||
899
            [contentType isEqualToString:@"application/folder"] ||
900
            [contentType hasPrefix:@"application/folder;"]);
901
}
902

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

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

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

    
995
// List of objects at the given container/object path, that may be a subdir or an application/directory, 
996
// with prefix and or delimiter
997
+ (NSArray *)objectsForSubdirWithPithos:(ASIPithos *)pithos containerName:(NSString *)containerName objectName:(NSString *)objectName 
998
                              delimiter:(NSString *)delimiter sharingAccount:(NSString *)sharingAccount {
999
    NSString *subdirNamePrefix = [NSString stringWithString:objectName];
1000
    if (![subdirNamePrefix hasSuffix:@"/"])
1001
        subdirNamePrefix = [subdirNamePrefix stringByAppendingString:@"/"];
1002
    return [self objectsWithPithos:pithos containerName:containerName objectNamePrefix:subdirNamePrefix 
1003
                         delimiter:delimiter sharingAccount:sharingAccount];
1004
}
1005

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

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

    
1104
#pragma mark -
1105
#pragma mark Alerts
1106

    
1107
+ (NSInteger)httpRequestErrorAlertWithRequest:(ASIPithosRequest *)request {
1108
    NSString *message = [NSString stringWithFormat:
1109
                         @"HTTP request error: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@", 
1110
                         [request error], 
1111
                         request.requestMethod, 
1112
                         request.url, 
1113
                         [request requestHeaders], 
1114
                         [request responseHeaders], 
1115
                         [request responseString]];
1116
    NSLog(@"%@", message);
1117
    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1118
    [alert setMessageText:@"HTTP Request Error"];
1119
    [alert setInformativeText:message];
1120
    [alert addButtonWithTitle:@"OK"];
1121
    return [alert runModal];
1122
}
1123

    
1124
+ (NSInteger)unexpectedResponseStatusAlertWithRequest:(ASIPithosRequest *)request {
1125
    NSString *message = [NSString stringWithFormat:
1126
                         @"Unexpected response status %d: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@", 
1127
                         request.responseStatusCode, 
1128
                         request.responseStatusMessage, 
1129
                         request.requestMethod, 
1130
                         request.url, 
1131
                         [request requestHeaders], 
1132
                         [request responseHeaders], 
1133
                         [request responseString]];
1134
    NSLog(@"%@", message);
1135
    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1136
    [alert setMessageText:@"Unexpected Response Status"];
1137
    [alert setInformativeText:message];
1138
    [alert addButtonWithTitle:@"OK"];
1139
    return [alert runModal];
1140
}
1141

    
1142
+ (NSInteger)fileActionFailedAlertWithTitle:(NSString *)title message:(NSString *)message error:(NSError *)error {
1143
    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1144
    [alert setMessageText:title];
1145
    if (error)
1146
        [alert setInformativeText:[NSString stringWithFormat:@"%@: %@", message, error]];
1147
    else
1148
        [alert setInformativeText:message];
1149
    [alert addButtonWithTitle:@"OK"];
1150
    return [alert runModal];
1151
}
1152

    
1153
#pragma mark -
1154
#pragma mark Request Helper Methods
1155

    
1156
+ (ASIPithosRequest *)prepareRequest:(ASIPithosRequest *)request priority:(NSOperationQueuePriority)priority {
1157
    [request setTimeOutSeconds:60];
1158
    request.numberOfTimesToRetryOnTimeout = 10;
1159
    [request setQueuePriority:priority];
1160
    return request;
1161
}
1162

    
1163
+ (ASIPithosRequest *)prepareRequest:(ASIPithosRequest *)request {
1164
    return [self prepareRequest:request priority:NSOperationQueuePriorityNormal];
1165
}
1166

    
1167
+ (ASIPithosRequest *)copyRequest:(ASIPithosRequest *)request {
1168
    NSMutableDictionary *userInfo = (NSMutableDictionary *)[[request.userInfo retain] autorelease];
1169
    request.userInfo = nil;
1170
    ASIPithosRequest *newRequest = [request copy];
1171
    newRequest.userInfo = userInfo;
1172
    return newRequest;
1173
}
1174

    
1175
@end