Statistics
| Branch: | Tag: | Revision:

root / pithos-macos / PithosUtilities.m @ 919cb043

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 "ASIPithos.h"
40
#import "ASIPithosContainerRequest.h"
41
#import "ASIPithosObjectRequest.h"
42
#import "ASIPithosObject.h"
43
#import "HashMapHash.h"
44

    
45
@implementation PithosUtilities
46

    
47
#pragma mark -
48
#pragma mark Download
49

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

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

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

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

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

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

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

    
231
#pragma mark -
232
#pragma mark Upload
233

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

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

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

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

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

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

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

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

    
497
#pragma mark -
498
#pragma mark Delete
499

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

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

    
534
#pragma mark -
535
#pragma mark Copy
536

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

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

    
706
#pragma mark -
707
#pragma mark Move
708

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

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

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

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

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

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

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

    
902
// Returns if an object exists at the given container/object path and if this object is an application/directory
903
// If an error occured an alert is shown and it is returned so the caller won't proceed
904
+ (BOOL)objectExistsAtPithos:(ASIPithos *)pithos containerName:(NSString *)containerName objectName:(NSString *)objectName 
905
                       error:(NSError **)error isDirectory:(BOOL *)isDirectory sharingAccount:(NSString *)sharingAccount {
906
    ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectMetadataRequestWithPithos:pithos 
907
                                                                                      containerName:containerName 
908
                                                                                         objectName:objectName];
909
    if (sharingAccount)
910
        [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
911
    [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
912
    while (![objectRequest isFinished]) {
913
        usleep(1);
914
    }
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
        [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
979
        while (![containerRequest isFinished]) {
980
            usleep(1);
981
        }
982
        if ([containerRequest error]) {
983
            [self httpRequestErrorAlertWithRequest:containerRequest];
984
            return nil;
985
        }
986
        NSArray *someObjects = [containerRequest objects];
987
        [objects addObjectsFromArray:someObjects];
988
        if ([someObjects count] < 10000)
989
            marker = nil;
990
        else
991
            marker = [[someObjects lastObject] name];
992
    } while (marker);
993
    return objects;
994
}
995

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

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

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

    
1105
#pragma mark -
1106
#pragma mark Alerts
1107

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

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

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

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

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

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

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

    
1176
@end