Create application/directory for subdir, if metadata or permissions are applied.
[pithos-ios] / Classes / OpenStackRequest.m
index dd04b44..945f651 100755 (executable)
@@ -23,6 +23,7 @@
 #import "GetFlavorsRequest.h"
 #import "APICallback.h"
 #import "APILogEntry.h"
+#import "NSString+Conveniences.h"
 
 
 static NSRecursiveLock *accessDetailsLock = nil;
@@ -44,7 +45,7 @@ static NSRecursiveLock *accessDetailsLock = nil;
 - (void)notify {
     NSString *observeName = [NSString stringWithFormat:@"%@ %@ %@", [self isSuccess] ? @"SUCCESS" : @"FAILURE", self.requestMethod, [self.url description]];
     NSString *callbackName = [NSString stringWithFormat:@"%@ %@ %@ %@", [self isSuccess] ? @"SUCCESS" : @"FAILURE", self.requestMethod, [self.url description], self.callback.uuid];
-    
+
     NSDictionary *callbackUserInfo = [NSDictionary dictionaryWithObject:self forKey:@"response"];
 
     NSNotification *observeNotification = [NSNotification notificationWithName:observeName object:nil userInfo:callbackUserInfo];
@@ -149,13 +150,13 @@ static NSRecursiveLock *accessDetailsLock = nil;
        [request addRequestHeader:@"X-Auth-Token" value:[account authToken]];
     [request addRequestHeader:@"Content-Type" value:@"application/json"];
     [request setTimeOutSeconds:60];
+    [request setNumberOfTimesToRetryOnTimeout:5];
     request.retriedCount = 0;
        return request;
 }
 
 + (id)getSharingAccountsRequest:(OpenStackAccount *)account {
-    NSString *topLevelUrlString = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"GRNetStorageUrlPrefix"];
-    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@?format=json",topLevelUrlString]];
+    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@?format=json",account.provider.authEndpointURL]];
     return [OpenStackRequest request:account method:@"GET" url:url];
 }
 
@@ -175,15 +176,15 @@ static NSRecursiveLock *accessDetailsLock = nil;
         filesUrl = [NSString stringWithFormat:@"%@%@",
                     [urlString substringToIndex:authUserRange.location],
                     account.sharingAccount];
-    } else {
+    } else
         filesUrl = urlString;
-       }
     
        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@?format=json%@&now=%@",
                                        filesUrl,
                                        path,
                                        account.shared ? @"&shared=" : @"",
-                                       now]];   
+                                       now]];  
+    
     return [OpenStackRequest request:account method:method url:url];
 }
 
@@ -193,16 +194,15 @@ static NSRecursiveLock *accessDetailsLock = nil;
     return [OpenStackRequest request:account method:method url:url];
 }
 
+
 #pragma mark -
 #pragma mark Auth Retry
 
 - (void)authRetrySucceded:(OpenStackRequest *)retryRequest {
-    
     if ([self isKindOfClass:[GetFlavorsRequest class]]) {
         NSLog(@"flavor request");
     }
-    
-    self.account.authToken = [[retryRequest responseHeaders] objectForKey:@"X-Auth-Token"];    
+    self.account.authToken = [[retryRequest responseHeaders] objectForKey:@"X-Auth-Token"]; 
     [self.account persist];
     
     // TODO: make this work for GetServersRequest, etc
@@ -210,6 +210,7 @@ static NSRecursiveLock *accessDetailsLock = nil;
     // try the original request again!
     retried = YES;
     retriedRequest = [self copy];
+
        [retriedRequest addRequestHeader:@"X-Auth-Token" value:self.account.authToken];    
     
     if (backupCompletionBlock) {
@@ -222,12 +223,12 @@ static NSRecursiveLock *accessDetailsLock = nil;
             backupFailureBlock();
         }];
     }
+
     [retriedRequest startSynchronous];     
 }
 
 - (void)authRetryFailed:(OpenStackRequest *)retryRequest {
     // if it fails due to bad connection, try again?
-    NSLog(@"auth retry failed with status %i", retryRequest.responseStatusCode);
     NSNotification *notification = [NSNotification notificationWithName:[self.account.manager notificationName:@"authRetryFailed" identifier:0] object:nil userInfo:[NSDictionary dictionaryWithObject:retryRequest forKey:@"request"]];
     [[NSNotificationCenter defaultCenter] postNotification:notification];
 }
@@ -248,10 +249,9 @@ static NSRecursiveLock *accessDetailsLock = nil;
 }
 
 - (void)failWithError:(NSError *)theError {
-
     if (responseStatusCode == 401 && ![url isEqual:account.provider.authEndpointURL]) {
         // auth is expired, so get a fresh token
-        if (account) {
+        if (account && ![account.provider isGRNet]) {
             OpenStackRequest *retryRequest = [OpenStackRequest authenticationRequest:account];
             retryRequest.delegate = self;
             retryRequest.didFinishSelector = @selector(authRetrySucceded:);
@@ -320,6 +320,8 @@ static NSRecursiveLock *accessDetailsLock = nil;
     }
 }
 
+
+
 + (OpenStackRequest *)authenticationRequest:(OpenStackAccount *)account {
 
        OpenStackRequest *request = [[[OpenStackRequest alloc] initWithURL:account.provider.authEndpointURL] autorelease];
@@ -628,20 +630,21 @@ static NSRecursiveLock *accessDetailsLock = nil;
 }
 
 + (OpenStackRequest *)createContainerRequest:(OpenStackAccount *)account container:(Container *)container {    
-    return [OpenStackRequest filesRequest:account method:@"PUT" path:[[NSString stringWithFormat:@"/%@", container.name] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+    return [OpenStackRequest filesRequest:account method:@"PUT" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]];
 }
 
 + (OpenStackRequest *)deleteContainerRequest:(OpenStackAccount *)account container:(Container *)container {
-    return [OpenStackRequest filesRequest:account method:@"DELETE" path:[[NSString stringWithFormat:@"/%@", container.name] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+    return [OpenStackRequest filesRequest:account method:@"DELETE" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]];
 }
 
 + (OpenStackRequest *)getObjectsRequest:(OpenStackAccount *)account container:(Container *)container {
-    return [OpenStackRequest filesRequest:account method:@"GET" path:[[NSString stringWithFormat:@"/%@", container.name] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];    
+    return [OpenStackRequest filesRequest:account method:@"GET" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]];    
 }
 
 - (NSMutableDictionary *)objects {
     SBJSON *parser = [[SBJSON alloc] init];
     NSArray *jsonObjects = [parser objectWithString:[self responseString]];
+
     NSMutableDictionary *objects = [[[NSMutableDictionary alloc] initWithCapacity:[jsonObjects count]] autorelease];
     
     for (int i = 0; i < [jsonObjects count]; i++) {
@@ -655,11 +658,11 @@ static NSRecursiveLock *accessDetailsLock = nil;
 }
 
 + (OpenStackRequest *)getObjectInfoRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
-    return [OpenStackRequest filesRequest:account method:@"HEAD" path:[[NSString stringWithFormat:@"/%@/%@", container.name, object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];    
+    return [OpenStackRequest filesRequest:account method:@"HEAD" path:[NSString stringWithFormat:@"/%@/%@",[NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:object.fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];    
 }
 
 + (OpenStackRequest *)getObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
-    return [OpenStackRequest filesRequest:account method:@"GET" path:[[NSString stringWithFormat:@"/%@/%@", container.name, object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];    
+    return [OpenStackRequest filesRequest:account method:@"GET" path:[NSString stringWithFormat:@"/%@/%@",[NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:object.fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];    
 }
 
 + (OpenStackRequest *)writeObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
@@ -667,55 +670,77 @@ static NSRecursiveLock *accessDetailsLock = nil;
     if ([fullPath characterAtIndex:0] == '/') {
         fullPath = [fullPath substringFromIndex:1];
     }
+        
+    OpenStackRequest *request = [OpenStackRequest filesRequest:account method:@"PUT" path:[NSString stringWithFormat:@"/%@/%@",[NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];    
     
-    OpenStackRequest *request = [OpenStackRequest filesRequest:account method:@"PUT" path:[[NSString stringWithFormat:@"/%@/%@", container.name, fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];    
+    if (object.sharing)
+        [request.requestHeaders setObject:object.sharing forKey:@"X-Object-Sharing"];
+    
+    NSString *metadataKeyHeaderPrefix;
+    if ([fullPath length] == 0)
+        metadataKeyHeaderPrefix = @"X-Container-Meta-";
+    else
+        metadataKeyHeaderPrefix = @"X-Object-Meta-";
+    
+    for (NSString *metadataKey in object.metadata) {
+        NSString *metadataKeyHeader = [NSString stringWithFormat:@"%@%@", metadataKeyHeaderPrefix, metadataKey]; 
+        metadataKeyHeader = [NSString encodeToPercentEscape:metadataKeyHeader];
+        NSString *metadataValue = [NSString encodeToPercentEscape:[object.metadata objectForKey:metadataKey]];
+        [request.requestHeaders setObject:metadataValue forKey:metadataKeyHeader];
+    }
+
        [request setPostBody:[NSMutableData dataWithData:object.data]];
-    [request.requestHeaders setObject:object.contentType forKey:@"Content-Type"];
+    [request.requestHeaders setObject:object.contentType forKey:@"Content-Type"];    
        return request;
 }
 
 + (OpenStackRequest *)writeObjectMetadataRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
-    NSString *fullPath = [NSString stringWithString:object.fullPath];
-    if ([fullPath length] && ([fullPath characterAtIndex:0] == '/'))
+    NSString *fullPath = object.fullPath;
+    if ([fullPath length] != 0 && [fullPath characterAtIndex:0] == '/') {
         fullPath = [fullPath substringFromIndex:1];
+    }
 
     NSString *metadataKeyHeaderPrefix;
-    if (![fullPath length])
+    if ([fullPath length] == 0)
         metadataKeyHeaderPrefix = @"X-Container-Meta-";
     else
         metadataKeyHeaderPrefix = @"X-Object-Meta-";
         
-    OpenStackRequest *request = [OpenStackRequest filesRequest:account method:@"POST" path:[[NSString stringWithFormat:@"/%@/%@", container.name, fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; 
-    
+    OpenStackRequest *request = [OpenStackRequest filesRequest:account method:@"POST" path:[NSString stringWithFormat:@"/%@/%@",[NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]]; 
+
     for (NSString *metadataKey in object.metadata) {
         NSString *metadataKeyHeader = [NSString stringWithFormat:@"%@%@", metadataKeyHeaderPrefix, metadataKey]; 
-        [request.requestHeaders setObject:[object.metadata objectForKey:metadataKey] forKey:metadataKeyHeader];
+        metadataKeyHeader = [NSString encodeToPercentEscape:metadataKeyHeader];
+        NSString *metadataValue = [NSString encodeToPercentEscape:[object.metadata objectForKey:metadataKey]];
+        [request.requestHeaders setObject:metadataValue forKey:metadataKeyHeader];
     }
     if (!account.sharingAccount) {
-        NSString *objectIsPublic = ([object.publicURI length]) ? @"true" : @"false";
+        NSString *objectIsPublic = ([object.publicURI length] > 0) ? @"true" : @"false";
         [request.requestHeaders setObject:objectIsPublic forKey:@"X-Object-Public"];
     
-        if (object.sharing)
-            [request.requestHeaders setObject:object.sharing forKey:@"X-Object-Sharing"];
+        if (object.sharing) {
+            NSString *urlEncodedSharingString = [NSString encodeToPercentEscape:object.sharing];
+            [request.requestHeaders setObject:urlEncodedSharingString forKey:@"X-Object-Sharing"];
+        }
     }
-
     return request;
 }
 
+
 + (OpenStackRequest *)deleteObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
     if ([object.fullPath characterAtIndex:0] == '/') {
-        return [OpenStackRequest filesRequest:account method:@"DELETE" path:[[NSString stringWithFormat:@"/%@%@", container.name, object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+        return [OpenStackRequest filesRequest:account method:@"DELETE" path:[NSString stringWithFormat:@"/%@%@",[NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:object.fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
     } else {
-        return [OpenStackRequest filesRequest:account method:@"DELETE" path:[[NSString stringWithFormat:@"/%@/%@", container.name, object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+        return [OpenStackRequest filesRequest:account method:@"DELETE" path:[NSString stringWithFormat:@"/%@/%@",[NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:object.fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
     }
 }
 
 + (OpenStackRequest *)writeContainerPolicyRequest:(OpenStackAccount *)account container:(Container *)container
 {
-    OpenStackRequest *request = [OpenStackRequest filesRequest:account method:@"PUT" path:[[NSString stringWithFormat:@"/%@", container.name] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; 
+    OpenStackRequest *request = [OpenStackRequest filesRequest:account method:@"PUT" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]]; 
     
     [request.requestHeaders setObject:container.versioning forKey:@"X-Container-Policy-Versioning"];
-    [request.requestHeaders setObject:[NSString stringWithFormat:@"%lu", container.quota] forKey:@"X-Container-Policy-Quota"];
+    [request.requestHeaders setObject:[NSString stringWithFormat:@"%u", container.quota] forKey:@"X-Container-Policy-Quota"];
     
     return request;
 }
@@ -726,19 +751,24 @@ static NSRecursiveLock *accessDetailsLock = nil;
     
     NSMutableDictionary *groups = [accountInfo objectForKey:@"groups"];
     for (NSString *groupName in groups) {
-        [request.requestHeaders setObject:[groups objectForKey:groupName] forKey:[NSString stringWithFormat:@"X-Account-Group-%@", groupName]];
+        NSString *group = [NSString encodeToPercentEscape:[groups objectForKey:groupName]];
+        groupName = [NSString encodeToPercentEscape:groupName];
+        [request.requestHeaders setObject:group forKey:[NSString stringWithFormat:@"X-Account-Group-%@", groupName]];
     }
     if ([groups count] == 0)
         [request.requestHeaders setObject:@"" forKey:@"X-Account-Group-group"];
     
     NSMutableDictionary *accountMetadata = [accountInfo objectForKey:@"metadata"];
     for (NSString *metadataKey in accountMetadata) {
-        [request.requestHeaders setObject:[accountMetadata objectForKey:metadataKey] forKey:[NSString stringWithFormat:@"X-Account-Meta-%@",metadataKey]];
+        NSString *metadataValue = [NSString encodeToPercentEscape:[accountMetadata objectForKey:metadataKey]];
+        metadataKey = [NSString encodeToPercentEscape:[accountMetadata objectForKey:metadataKey]];
+        [request.requestHeaders setObject:metadataValue forKey:[NSString stringWithFormat:@"X-Account-Meta-%@",metadataKey]];
     }
     
     return request;
 }
 
+
 #pragma mark -
 #pragma mark Memory Management