Statistics
| Branch: | Tag: | Revision:

root / pithos-macos / PithosUtilities.m @ f059e0e6

History | View | Annotate | Download (69.5 kB)

1
//
2
//  PithosUtilities.m
3
//  pithos-macos
4
//
5
// Copyright 2011 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 "ASIPithosContainerRequest.h"
40
#import "ASIPithosObjectRequest.h"
41
#import "ASIPithosObject.h"
42
#import "HashMapHash.h"
43

    
44
@implementation PithosUtilities
45

    
46
#pragma mark -
47
#pragma mark Download
48

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

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

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

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

    
195
#pragma mark -
196
#pragma mark Download Block
197

    
198
+ (ASIPithosObjectRequest *)objectBlockDataRequestWithContainerName:(NSString *)containerName 
199
                                                             object:(ASIPithosObject *)object 
200
                                                         blockIndex:(NSUInteger)blockIndex 
201
                                                          blockSize:(NSUInteger)blockSize {
202
    NSUInteger rangeStart = blockIndex * blockSize;
203
    NSUInteger rangeEnd = (rangeStart + blockSize <= object.bytes) ? (rangeStart + blockSize - 1) : (object.bytes - 1);
204
    ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectDataRequestWithContainerName:containerName
205
                                                                                            objectName:object.name
206
                                                                                               version:nil
207
                                                                                                 range:[NSString stringWithFormat:@"bytes=%lu-%lu", rangeStart, rangeEnd]
208
                                                                                               ifMatch:object.hash];
209
    return objectRequest;
210
}
211

    
212
+ (NSIndexSet *)missingBlocksForFile:(NSString *)filePath
213
                           blockSize:(NSUInteger)blockSize 
214
                           blockHash:(NSString *)blockHash 
215
                          withHashes:(NSArray *)hashes {
216
    NSArray *fileHashes = [HashMapHash objectHashMapStrings:filePath withBlockHash:blockHash andBlockSize:blockSize];
217
    return [hashes indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
218
        if ((idx >= [fileHashes count]) || ![(NSString *)obj isEqualToString:[fileHashes objectAtIndex:idx]])
219
            return YES;
220
        return NO;
221
    }];
222
}
223

    
224
#pragma mark -
225
#pragma mark Upload
226

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

    
270
+ (NSIndexSet *)missingBlocksForHashes:(NSArray *)hashes withMissingHashesResponse:(NSString *)missingHashesResponse {
271
    NSArray *responseLines = [missingHashesResponse componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
272
    NSMutableIndexSet *missingBlocks = [NSMutableIndexSet indexSet];
273
    for (NSString *line in responseLines) {
274
        if (![line length])
275
            break;
276
        NSUInteger missingBlock = [hashes indexOfObject:line];
277
        if (missingBlock != NSNotFound)
278
            [missingBlocks addIndex:missingBlock];
279
    }
280
    return missingBlocks;
281
}
282

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

    
315
    [missingBlocks enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
316
        [fileHandle seekToFileOffset:(idx*blockSize)];
317
        [tempFileHandle writeData:[fileHandle readDataOfLength:blockSize]];
318
    }];
319
    [tempFileHandle closeFile];
320
    [fileHandle closeFile];
321

    
322
    ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest updateContainerDataRequestWithContainerName:containerName 
323
                                                                                                                  policy:nil 
324
                                                                                                                metadata:nil 
325
                                                                                                                  update:YES 
326
                                                                                                                    file:tempFilePath];
327
    if (sharingAccount)
328
        [containerRequest setRequestUserFromDefaultTo:sharingAccount];
329
    return containerRequest;
330
}
331

    
332
+ (ASIPithosContainerRequest *)updateContainerDataRequestWithContainerName:(NSString *)containerName 
333
                                                                 blockSize:(NSUInteger)blockSize 
334
                                                                   forFile:(NSString *)filePath 
335
                                                         missingBlockIndex:(NSUInteger)missingBlockIndex 
336
                                                            sharingAccount:(NSString *)sharingAccount {
337
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
338
    [fileHandle seekToFileOffset:(missingBlockIndex *blockSize)];
339
    NSData *blockData = [fileHandle readDataOfLength:blockSize];
340
    [fileHandle closeFile];
341
    ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest updateContainerDataRequestWithContainerName:containerName 
342
                                                                                                                  policy:nil 
343
                                                                                                                metadata:nil 
344
                                                                                                                  update:YES 
345
                                                                                                                    data:blockData];
346
    if (sharingAccount)
347
        [containerRequest setRequestUserFromDefaultTo:sharingAccount];
348
    return containerRequest;
349
}
350

    
351
+ (NSArray *)writeObjectDataRequestsWithContainerName:(NSString *)containerName
352
                                           objectName:(NSString *)objectName
353
                                            blockSize:(NSUInteger)blockSize 
354
                                            blockHash:(NSString *)blockHash 
355
                                         forDirectory:(NSString *)directoryPath 
356
                                        checkIfExists:(BOOL)ifExists 
357
                                          objectNames:(NSMutableArray **)objectNames
358
                                         contentTypes:(NSMutableArray **)contentTypes
359
                                            filePaths:(NSMutableArray **)filePaths 
360
                                         hashesArrays:(NSMutableArray **)hashesArrays 
361
                              directoryObjectRequests:(NSMutableArray **) directoryObjectRequests 
362
                                       sharingAccount:(NSString *)sharingAccount {
363
    if (ifExists && ![self proceedIfObjectExistsAtContainerName:containerName objectName:objectName sharingAccount:sharingAccount])
364
        return nil;
365

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

    
480
#pragma mark -
481
#pragma mark Delete
482

    
483
+ (NSArray *)deleteObjectRequestsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName {
484
    NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil sharingAccount:nil];
485
    if (objects == nil)
486
        return nil;
487

    
488
    NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
489
    ASIPithosObjectRequest *objectRequest;
490
    if (![objectName hasSuffix:@"/"]) {
491
        objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithContainerName:containerName objectName:objectName];
492
        objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
493
                                  [objectName lastPathComponent], @"fileName", 
494
                                  nil];
495
        [objectRequests addObject:objectRequest];
496
    }
497
    NSString *fileName;
498
    for (ASIPithosObject *object in objects) {
499
        fileName = [object.name lastPathComponent];
500
        if ([object.name hasSuffix:@"/"])
501
            fileName = [fileName stringByAppendingString:@"/"];
502
        objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithContainerName:containerName objectName:object.name];
503
        objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
504
                                  fileName, @"fileName", 
505
                                  nil];
506
        [objectRequests addObject:objectRequest];
507
    }
508
    
509
    if ([objectRequests count] == 0)
510
        return nil;
511
    return objectRequests;
512
}
513

    
514
#pragma mark -
515
#pragma mark Copy
516

    
517
+ (ASIPithosObjectRequest *)copyObjectRequestWithContainerName:(NSString *)containerName 
518
                                                    objectName:(NSString *)objectName 
519
                                      destinationContainerName:(NSString *)destinationContainerName 
520
                                         destinationObjectName:(NSString *)destinationObjectName 
521
                                                 checkIfExists:(BOOL)ifExists 
522
                                                sharingAccount:(NSString *)sharingAccount {
523
    if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
524
        return nil;
525
    
526
    ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName 
527
                                                                                                objectName:objectName 
528
                                                                                               contentType:nil 
529
                                                                                           contentEncoding:nil 
530
                                                                                        contentDisposition:nil 
531
                                                                                                  manifest:nil 
532
                                                                                                   sharing:nil 
533
                                                                                                  isPublic:ASIPithosObjectRequestPublicIgnore 
534
                                                                                                  metadata:nil 
535
                                                                                  destinationContainerName:destinationContainerName 
536
                                                                                     destinationObjectName:destinationObjectName 
537
                                                                                        destinationAccount:nil
538
                                                                                             sourceVersion:nil];
539
    objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
540
                              containerName, @"sourceContainerName", 
541
                              objectName, @"sourceObjectName", 
542
                              destinationContainerName, @"destinationContainerName", 
543
                              destinationObjectName, @"destinationObjectName", 
544
                              nil];
545
    if (sharingAccount) 
546
        [objectRequest setRequestUserFromDefaultTo:sharingAccount];
547
    return objectRequest;
548
}
549

    
550
+ (NSArray *)copyObjectRequestsForSubdirWithContainerName:(NSString *)containerName 
551
                                               objectName:(NSString *)objectName 
552
                                 destinationContainerName:(NSString *)destinationContainerName 
553
                                    destinationObjectName:(NSString *)destinationObjectName 
554
                                            checkIfExists:(BOOL)ifExists 
555
                                           sharingAccount:(NSString *)sharingAccount {
556
    if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
557
        return nil;
558
    
559
    NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName 
560
                                                     delimiter:nil sharingAccount:sharingAccount];
561
    if (objects == nil)
562
        return nil;
563
    
564
    NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
565
    ASIPithosObjectRequest *objectRequest;
566
    if ([objectName isEqualToString:destinationObjectName]) {
567
        if (![objectName hasSuffix:@"/"]) {
568
            objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName 
569
                                                                                objectName:objectName 
570
                                                                               contentType:nil 
571
                                                                           contentEncoding:nil 
572
                                                                        contentDisposition:nil 
573
                                                                                  manifest:nil 
574
                                                                                   sharing:nil 
575
                                                                                  isPublic:ASIPithosObjectRequestPublicIgnore 
576
                                                                                  metadata:nil 
577
                                                                  destinationContainerName:destinationContainerName 
578
                                                                     destinationObjectName:objectName 
579
                                                                        destinationAccount:nil 
580
                                                                             sourceVersion:nil];
581
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
582
                                      containerName, @"sourceContainerName", 
583
                                      objectName, @"sourceObjectName", 
584
                                      destinationContainerName, @"destinationContainerName", 
585
                                      objectName, @"destinationObjectName", 
586
                                      nil];
587
            if (sharingAccount)
588
                [objectRequest setRequestUserFromDefaultTo:sharingAccount];
589
            [objectRequests addObject:objectRequest];
590
        }
591
        for (ASIPithosObject *object in objects) {
592
            objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName 
593
                                                                                objectName:object.name 
594
                                                                               contentType:nil 
595
                                                                           contentEncoding:nil 
596
                                                                        contentDisposition:nil 
597
                                                                                  manifest:nil 
598
                                                                                   sharing:nil 
599
                                                                                  isPublic:ASIPithosObjectRequestPublicIgnore 
600
                                                                                  metadata:nil 
601
                                                                  destinationContainerName:destinationContainerName 
602
                                                                     destinationObjectName:object.name 
603
                                                                        destinationAccount:nil 
604
                                                                             sourceVersion:nil];
605
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
606
                                      containerName, @"sourceContainerName", 
607
                                      object.name, @"sourceObjectName", 
608
                                      destinationContainerName, @"destinationContainerName", 
609
                                      object.name, @"destinationObjectName", 
610
                                      nil];
611
            if (sharingAccount)
612
                [objectRequest setRequestUserFromDefaultTo:sharingAccount];
613
            [objectRequests addObject:objectRequest];
614
        }
615
    } else {
616
        if (![objectName hasSuffix:@"/"]) {
617
            objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName 
618
                                                                                objectName:objectName 
619
                                                                               contentType:nil 
620
                                                                           contentEncoding:nil 
621
                                                                        contentDisposition:nil 
622
                                                                                  manifest:nil 
623
                                                                                   sharing:nil 
624
                                                                                  isPublic:ASIPithosObjectRequestPublicIgnore 
625
                                                                                  metadata:nil 
626
                                                                  destinationContainerName:destinationContainerName 
627
                                                                     destinationObjectName:destinationObjectName 
628
                                                                        destinationAccount:nil 
629
                                                                             sourceVersion:nil];
630
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
631
                                      containerName, @"sourceContainerName", 
632
                                      objectName, @"sourceObjectName", 
633
                                      destinationContainerName, @"destinationContainerName", 
634
                                      destinationObjectName, @"destinationObjectName", 
635
                                      nil];
636
            if (sharingAccount)
637
                [objectRequest setRequestUserFromDefaultTo:sharingAccount];
638
            [objectRequests addObject:objectRequest];
639
        }
640
        NSRange prefixRange = NSMakeRange(0, [objectName length]);
641
        NSString *newObjectName;
642
        for (ASIPithosObject *object in objects) {
643
            newObjectName = [object.name stringByReplacingOccurrencesOfString:objectName
644
                                                                   withString:destinationObjectName
645
                                                                      options:NSAnchoredSearch
646
                                                                        range:prefixRange];
647
            objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithContainerName:containerName 
648
                                                                                objectName:object.name 
649
                                                                               contentType:nil 
650
                                                                           contentEncoding:nil 
651
                                                                        contentDisposition:nil 
652
                                                                                  manifest:nil 
653
                                                                                   sharing:nil 
654
                                                                                  isPublic:ASIPithosObjectRequestPublicIgnore 
655
                                                                                  metadata:nil 
656
                                                                  destinationContainerName:destinationContainerName 
657
                                                                     destinationObjectName:newObjectName 
658
                                                                        destinationAccount:nil 
659
                                                                             sourceVersion:nil];
660
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
661
                                      containerName, @"sourceContainerName", 
662
                                      object.name, @"sourceObjectName", 
663
                                      destinationContainerName, @"destinationContainerName", 
664
                                      newObjectName, @"destinationObjectName", 
665
                                      nil];
666
            if (sharingAccount)
667
                [objectRequest setRequestUserFromDefaultTo:sharingAccount];
668
            [objectRequests addObject:objectRequest];
669
        }
670
    }
671
    
672
    if ([objectRequests count] == 0)
673
        return nil;
674
    return objectRequests;
675
}
676

    
677
#pragma mark -
678
#pragma mark Move
679

    
680
+ (ASIPithosObjectRequest *)moveObjectRequestWithContainerName:(NSString *)containerName 
681
                                     objectName:(NSString *)objectName 
682
                       destinationContainerName:(NSString *)destinationContainerName 
683
                          destinationObjectName:(NSString *)destinationObjectName 
684
                                  checkIfExists:(BOOL)ifExists {
685
    if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
686
        return nil;
687
    
688
    ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName 
689
                                                                                                objectName:objectName 
690
                                                                                               contentType:nil 
691
                                                                                           contentEncoding:nil 
692
                                                                                        contentDisposition:nil 
693
                                                                                                  manifest:nil 
694
                                                                                                   sharing:nil 
695
                                                                                                  isPublic:ASIPithosObjectRequestPublicIgnore 
696
                                                                                                  metadata:nil 
697
                                                                                  destinationContainerName:destinationContainerName 
698
                                                                                     destinationObjectName:destinationObjectName 
699
                                                                                        destinationAccount:nil];
700
    objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
701
                              containerName, @"sourceContainerName", 
702
                              objectName, @"sourceObjectName", 
703
                              destinationContainerName, @"destinationContainerName", 
704
                              destinationObjectName, @"destinationObjectName", 
705
                              nil];
706
    return objectRequest;
707
}
708

    
709
+ (NSArray *)moveObjectRequestsForSubdirWithContainerName:(NSString *)containerName 
710
                                               objectName:(NSString *)objectName 
711
                                 destinationContainerName:(NSString *)destinationContainerName 
712
                                    destinationObjectName:(NSString *)destinationObjectName 
713
                                            checkIfExists:(BOOL)ifExists {
714
    if (ifExists && ![self proceedIfObjectExistsAtContainerName:destinationContainerName objectName:destinationObjectName sharingAccount:nil])
715
        return nil;
716
    
717
    NSArray *objects = [self objectsForSubdirWithContainerName:containerName objectName:objectName delimiter:nil sharingAccount:nil];
718
    if (objects == nil)
719
        return nil;
720
    
721
    ASIPithosObjectRequest *objectRequest;
722
    NSMutableArray *objectRequests = [NSMutableArray arrayWithCapacity:([objects count] + 1)];
723
    if ([objectName isEqualToString:destinationObjectName]) {
724
        if (![objectName hasSuffix:@"/"]) {
725
            objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName 
726
                                                                                objectName:objectName 
727
                                                                               contentType:nil 
728
                                                                           contentEncoding:nil 
729
                                                                        contentDisposition:nil 
730
                                                                                  manifest:nil 
731
                                                                                   sharing:nil 
732
                                                                                  isPublic:ASIPithosObjectRequestPublicIgnore 
733
                                                                                  metadata:nil 
734
                                                                  destinationContainerName:destinationContainerName 
735
                                                                     destinationObjectName:objectName 
736
                                                                        destinationAccount:nil];
737
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
738
                                      containerName, @"sourceContainerName", 
739
                                      objectName, @"sourceObjectName", 
740
                                      destinationContainerName, @"destinationContainerName", 
741
                                      objectName, @"destinationObjectName", 
742
                                      nil];
743
            [objectRequests addObject:objectRequest];
744
        }
745
        for (ASIPithosObject *object in objects) {
746
            objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName 
747
                                                                                objectName:object.name 
748
                                                                               contentType:nil 
749
                                                                           contentEncoding:nil 
750
                                                                        contentDisposition:nil 
751
                                                                                  manifest:nil 
752
                                                                                   sharing:nil 
753
                                                                                  isPublic:ASIPithosObjectRequestPublicIgnore 
754
                                                                                  metadata:nil 
755
                                                                  destinationContainerName:destinationContainerName 
756
                                                                     destinationObjectName:object.name 
757
                                                                        destinationAccount:nil];
758
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
759
                                      containerName, @"sourceContainerName", 
760
                                      object.name, @"sourceObjectName", 
761
                                      destinationContainerName, @"destinationContainerName", 
762
                                      object.name, @"destinationObjectName", 
763
                                      nil];
764
            [objectRequests addObject:objectRequest];
765
        }
766
    } else {
767
        if (![objectName hasSuffix:@"/"]) {
768
            objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName 
769
                                                                                objectName:objectName 
770
                                                                               contentType:nil 
771
                                                                           contentEncoding:nil 
772
                                                                        contentDisposition:nil 
773
                                                                                  manifest:nil 
774
                                                                                   sharing:nil 
775
                                                                                  isPublic:ASIPithosObjectRequestPublicIgnore 
776
                                                                                  metadata:nil 
777
                                                                  destinationContainerName:destinationContainerName 
778
                                                                     destinationObjectName:destinationObjectName 
779
                                                                        destinationAccount:nil];
780
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
781
                                      containerName, @"sourceContainerName", 
782
                                      objectName, @"sourceObjectName", 
783
                                      destinationContainerName, @"destinationContainerName", 
784
                                      destinationObjectName, @"destinationObjectName", 
785
                                      nil];
786
            [objectRequests addObject:objectRequest];
787
        }
788
        NSRange prefixRange = NSMakeRange(0, [objectName length]);
789
        NSString *newObjectName;
790
        for (ASIPithosObject *object in objects) {
791
            newObjectName = [object.name stringByReplacingOccurrencesOfString:objectName
792
                                                                   withString:destinationObjectName
793
                                                                      options:NSAnchoredSearch
794
                                                                        range:prefixRange];
795
            objectRequest = [ASIPithosObjectRequest moveObjectDataRequestWithContainerName:containerName 
796
                                                                                objectName:object.name
797
                                                                               contentType:nil 
798
                                                                           contentEncoding:nil 
799
                                                                        contentDisposition:nil 
800
                                                                                  manifest:nil 
801
                                                                                   sharing:nil 
802
                                                                                  isPublic:ASIPithosObjectRequestPublicIgnore 
803
                                                                                  metadata:nil 
804
                                                                  destinationContainerName:destinationContainerName 
805
                                                                     destinationObjectName:newObjectName 
806
                                                                        destinationAccount:nil];
807
            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
808
                                      containerName, @"sourceContainerName", 
809
                                      object.name, @"sourceObjectName", 
810
                                      destinationContainerName, @"destinationContainerName", 
811
                                      newObjectName, @"destinationObjectName", 
812
                                      nil];
813
            [objectRequests addObject:objectRequest];
814
        }
815
    }
816
     
817
    if ([objectRequests count] == 0)
818
        return nil;
819
    return objectRequests;
820
}
821

    
822
#pragma mark -
823
#pragma mark Helper Methods
824

    
825
// Size of the file in bytes
826
+ (NSUInteger)bytesOfFile:(NSString *)filePath {
827
    NSFileManager *fileManager = [NSFileManager defaultManager];
828
    NSDictionary *attributes = [fileManager attributesOfItemAtPath:filePath error:nil];
829
    return [[attributes objectForKey:NSFileSize] intValue];
830
}
831

    
832
// Content type of the file or nil if it cannot be determined
833
+ (NSString *)contentTypeOfFile:(NSString *)filePath error:(NSError **)error {
834
    NSURLResponse *response = nil;
835
    [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath] 
836
                                                             cachePolicy:NSURLCacheStorageNotAllowed 
837
                                                         timeoutInterval:.1] 
838
                          returningResponse:&response 
839
                                      error:error];
840
    return [response MIMEType];
841
}
842

    
843
// Returns if an object is a directory based on its content type
844
+ (BOOL)isContentTypeDirectory:(NSString *)contentType {
845
    return ([contentType isEqualToString:@"application/directory"] ||
846
            [contentType hasPrefix:@"application/directory;"] ||
847
            [contentType isEqualToString:@"application/folder"] ||
848
            [contentType hasPrefix:@"application/folder;"]);
849
}
850

    
851
// Returns if an object exists at the given container/object path and if this object is an application/directory
852
// If an error occured an alert is shown and it is returned so the caller won't proceed
853
+ (BOOL)objectExistsAtContainerName:(NSString *)containerName objectName:(NSString *)objectName 
854
                              error:(NSError **)error isDirectory:(BOOL *)isDirectory 
855
                     sharingAccount:(NSString *)sharingAccount {
856
    ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectMetadataRequestWithContainerName:containerName 
857
                                                                                                objectName:objectName];
858
    if (sharingAccount)
859
        [objectRequest setRequestUserFromDefaultTo:sharingAccount];
860
    [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
861
    while (![objectRequest isFinished]) {
862
        usleep(1);
863
    }
864
    *error = [objectRequest error];
865
    if (*error) {
866
        [self httpRequestErrorAlertWithRequest:objectRequest];
867
        return NO;
868
    } else if (objectRequest.responseStatusCode == 200) {
869
        *isDirectory = [self isContentTypeDirectory:[objectRequest contentType]];
870
        return YES;
871
    }
872
    return NO;
873
}
874

    
875
// Returns if the called should proceed, after an interactive check if an object exists 
876
// at the given container/object path is performed
877
+ (BOOL)proceedIfObjectExistsAtContainerName:(NSString *)containerName objectName:(NSString *)objectName 
878
                              sharingAccount:(NSString *)sharingAccount {
879
    NSError *error = nil;
880
    BOOL isDirectory;
881
    BOOL objectExists = [self objectExistsAtContainerName:containerName 
882
                                               objectName:objectName
883
                                                    error:&error 
884
                                              isDirectory:&isDirectory 
885
                                           sharingAccount:sharingAccount];
886
    if (error) {
887
        return NO;
888
    } else if (objectExists) {
889
        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
890
        if (isDirectory) {
891
            [alert setMessageText:@"Directory Exists"];
892
            if (sharingAccount)
893
                [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
894
            else
895
                [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
896
        } else {
897
            [alert setMessageText:@"Object Exists"];
898
            if (sharingAccount)
899
                [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
900
            else
901
                [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
902
        }
903
        [alert addButtonWithTitle:@"OK"];
904
        [alert addButtonWithTitle:@"Cancel"];
905
        NSInteger choice = [alert runModal];
906
        if (choice == NSAlertSecondButtonReturn)
907
            return NO;
908
    }
909
    return YES;
910
}
911

    
912
// List of objects at the given container/object path, with prefix and or delimiter
913
+ (NSArray *)objectsWithContainerName:(NSString *)containerName objectNamePrefix:(NSString *)objectNamePrefix 
914
                            delimiter:(NSString *)delimiter sharingAccount:(NSString *)sharingAccount {
915
    NSMutableArray *objects = [NSMutableArray array];
916
    NSString *marker = nil;
917
    do {
918
        ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest listObjectsRequestWithContainerName:containerName 
919
                                                                                                               limit:0 
920
                                                                                                              marker:marker 
921
                                                                                                              prefix:objectNamePrefix 
922
                                                                                                           delimiter:delimiter 
923
                                                                                                                path:nil 
924
                                                                                                                meta:nil 
925
                                                                                                              shared:NO 
926
                                                                                                               until:nil];
927
        if (sharingAccount)
928
            [containerRequest setRequestUserFromDefaultTo:sharingAccount];
929
        [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
930
        while (![containerRequest isFinished]) {
931
            usleep(1);
932
        }
933
        if ([containerRequest error]) {
934
            [self httpRequestErrorAlertWithRequest:containerRequest];
935
            return nil;
936
        }
937
        NSArray *someObjects = [containerRequest objects];
938
        [objects addObjectsFromArray:someObjects];
939
        if ([someObjects count] < 10000)
940
            marker = nil;
941
        else
942
            marker = [[someObjects lastObject] name];
943
    } while (marker);
944
    return objects;
945
}
946

    
947
// List of objects at the given container/object path, that may be a subdir or an application/directory, 
948
// with prefix and or delimiter
949
+ (NSArray *)objectsForSubdirWithContainerName:(NSString *)containerName objectName:(NSString *)objectName 
950
                                     delimiter:(NSString *)delimiter sharingAccount:(NSString *)sharingAccount {
951
    NSString *subdirNamePrefix = [NSString stringWithString:objectName];
952
    if (![subdirNamePrefix hasSuffix:@"/"])
953
        subdirNamePrefix = [subdirNamePrefix stringByAppendingString:@"/"];
954
    return [self objectsWithContainerName:containerName objectNamePrefix:subdirNamePrefix 
955
                                delimiter:delimiter sharingAccount:sharingAccount];
956
}
957

    
958
// A safe object name at the given container/object path
959
// The original name has " %d" appended to it before any ".*" suffix, for the first integer that produces a name that is free to use
960
// If the original name hasn't got a "." but has a "/" suffix, then it is replaced with " %d/" instead
961
// Subdirs are taken into consideration
962
+ (NSString *)safeObjectNameForContainerName:(NSString *)containerName objectName:(NSString *)objectName {
963
    NSString *objectNamePrefix;
964
    NSString *objectNameExtraSuffix;
965
    NSRange lastDotRange = [objectName rangeOfString:@"." options:NSBackwardsSearch];
966
    if (lastDotRange.length == 1) {
967
        objectNamePrefix = [objectName substringToIndex:lastDotRange.location];
968
        objectNameExtraSuffix = [objectName substringFromIndex:lastDotRange.location];
969
    } else if ([objectName hasSuffix:@"/"]) {
970
        objectNamePrefix = [objectName substringToIndex:([objectName length] - 1)];
971
        objectNameExtraSuffix = [NSString stringWithString:@"/"];
972
    } else {
973
        objectNamePrefix = [NSString stringWithString:objectName];
974
        objectNameExtraSuffix = [NSString string];
975
    }
976
    NSArray *objects = [self objectsWithContainerName:containerName 
977
                                     objectNamePrefix:[objectNamePrefix stringByDeletingLastPathComponent] 
978
                                            delimiter:@"/" 
979
                                       sharingAccount:nil];
980
    if (objects == nil)
981
        return nil;
982
    if ([objects count] == 0)
983
        return objectName;
984
    NSMutableArray *objectNames = [NSMutableArray arrayWithArray:
985
                                   [[objects objectsAtIndexes:
986
                                     [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
987
        if (pithosObject.subdir)
988
            return NO;
989
        return YES;
990
    }]] valueForKey:@"name"]];
991
    for (NSString *name in [[objects objectsAtIndexes:
992
                             [objects indexesOfObjectsPassingTest:^(ASIPithosObject *pithosObject, NSUInteger idx, BOOL *stop){
993
        if (pithosObject.subdir)
994
            return YES;
995
        return NO;
996
    }]] valueForKey:@"name"]) {
997
        [objectNames addObject:[name substringToIndex:([name length] - 1)]];
998
    }
999
    if (![objectNames containsObject:objectName])
1000
        return objectName;
1001
    NSUInteger objectNameSuffix = 2;
1002
    NSString *safeObjectName;
1003
    do {
1004
        safeObjectName = [objectNamePrefix stringByAppendingFormat:@" %lu%@", objectNameSuffix, objectNameExtraSuffix];
1005
        objectNameSuffix++;
1006
    } while ([objectNames containsObject:safeObjectName]);
1007
    return safeObjectName;    
1008
}
1009

    
1010
// A safe object name at the given container/object path that may be a subdir or application/directory
1011
// The original name has " %d" appended to it, for the first integer that produces a name that is free to use
1012
// If the original name has a "/" suffix, then it is replaced with " %d/" instead
1013
// Subdirs are taken into consideration
1014
+ (NSString *)safeSubdirNameForContainerName:(NSString *)containerName subdirName:(NSString *)subdirName {
1015
    NSString *subdirNamePrefix;
1016
    NSString *subdirNameExtraSuffix;
1017
    if ([subdirName hasSuffix:@"/"]) {
1018
        subdirNamePrefix = [subdirName substringToIndex:([subdirName length] - 1)];
1019
        subdirNameExtraSuffix = [NSString stringWithString:@"/"];
1020
    } else {
1021
        subdirNamePrefix = [NSString stringWithString:subdirName];
1022
        subdirNameExtraSuffix = [NSString string];
1023
    }
1024
    NSArray *objects = [self objectsWithContainerName:containerName 
1025
                                     objectNamePrefix:[subdirNamePrefix stringByDeletingLastPathComponent] 
1026
                                            delimiter:@"/" 
1027
                                       sharingAccount:nil];
1028
    if (objects == nil)
1029
        return nil;
1030
    if ([objects count] == 0)
1031
        return subdirName;
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:subdirNamePrefix])
1048
        return subdirName;
1049
    NSUInteger subdirNameSuffix = 2;
1050
    NSString *safeSubdirName;
1051
    do {
1052
        safeSubdirName = [subdirNamePrefix stringByAppendingFormat:@" %lu", subdirNameSuffix];
1053
        subdirNameSuffix++;
1054
    } while ([objectNames containsObject:safeSubdirName]);
1055
    return [safeSubdirName stringByAppendingString:subdirNameExtraSuffix];
1056
}
1057

    
1058
#pragma mark -
1059
#pragma mark Alerts
1060

    
1061
+ (NSInteger)httpRequestErrorAlertWithRequest:(ASIPithosRequest *)request {
1062
    NSString *message = [NSString stringWithFormat:
1063
                         @"HTTP request error: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@", 
1064
                         [request error], 
1065
                         request.requestMethod, 
1066
                         request.url, 
1067
                         [request requestHeaders], 
1068
                         [request responseHeaders], 
1069
                         [request responseString]];
1070
    NSLog(@"%@", message);
1071
    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1072
    [alert setMessageText:@"HTTP Request Error"];
1073
    [alert setInformativeText:message];
1074
    [alert addButtonWithTitle:@"OK"];
1075
    return [alert runModal];
1076
}
1077

    
1078
+ (NSInteger)unexpectedResponseStatusAlertWithRequest:(ASIPithosRequest *)request {
1079
    NSString *message = [NSString stringWithFormat:
1080
                         @"Unexpected response status %d: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@", 
1081
                         request.responseStatusCode, 
1082
                         request.responseStatusMessage, 
1083
                         request.requestMethod, 
1084
                         request.url, 
1085
                         [request requestHeaders], 
1086
                         [request responseHeaders], 
1087
                         [request responseString]];
1088
    NSLog(@"%@", message);
1089
    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1090
    [alert setMessageText:@"Unexpected Response Status"];
1091
    [alert setInformativeText:message];
1092
    [alert addButtonWithTitle:@"OK"];
1093
    return [alert runModal];
1094
}
1095

    
1096
+ (NSInteger)fileActionFailedAlertWithTitle:(NSString *)title message:(NSString *)message error:(NSError *)error {
1097
    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1098
    [alert setMessageText:title];
1099
    if (error)
1100
        [alert setInformativeText:[NSString stringWithFormat:@"%@: %@", message, error]];
1101
    else
1102
        [alert setInformativeText:message];
1103
    [alert addButtonWithTitle:@"OK"];
1104
    return [alert runModal];
1105
}
1106

    
1107
#pragma mark -
1108
#pragma mark Request Helper Methods
1109

    
1110
+ (ASIPithosRequest *)prepareRequest:(ASIPithosRequest *)request priority:(NSOperationQueuePriority)priority {
1111
    [request setTimeOutSeconds:60];
1112
    request.numberOfTimesToRetryOnTimeout = 10;
1113
    [request setQueuePriority:priority];
1114
    return request;
1115
}
1116

    
1117
+ (ASIPithosRequest *)prepareRequest:(ASIPithosRequest *)request {
1118
    return [self prepareRequest:request priority:NSOperationQueuePriorityNormal];
1119
}
1120

    
1121
+ (ASIPithosRequest *)copyRequest:(ASIPithosRequest *)request {
1122
    NSMutableDictionary *userInfo = [[request.userInfo retain] autorelease];
1123
    request.userInfo = nil;
1124
    ASIPithosRequest *newRequest = [request copy];
1125
    newRequest.userInfo = userInfo;
1126
    return newRequest;
1127
}
1128

    
1129
@end