Use service catalog and update version
[pithos-ios] / Classes / AccountManager.m
1 //
2 //  AccountManager.m
3 //  OpenStack
4 //
5 //  Created by Mike Mayo on 10/21/10.
6 //  The OpenStack project is provided under the Apache 2.0 license.
7 //
8
9 #import "AccountManager.h"
10 #import "OpenStackAccount.h"
11 #import "OpenStackRequest.h"
12 #import "Provider.h"
13 #import "Container.h"
14 #import "Folder.h"
15 #import "StorageObject.h"
16 #import "GetObjectsRequest.h"
17 #import "ASINetworkQueue.h"
18 #import "APICallback.h"
19 #import "JSON.h"
20 #import "OpenStackAppDelegate.h"
21 #import "NSString+Conveniences.h"
22
23 @implementation AccountManager
24
25 @synthesize account, queue;
26
27 #pragma mark - Callbacks
28
29 - (APICallback *)callbackWithRequest:(id)request success:(APIResponseBlock)success failure:(APIResponseBlock)failure {
30     APICallback *callback = [[[APICallback alloc] initWithAccount:self.account request:request] autorelease];
31     ((OpenStackRequest *)request).delegate = self;
32     ((OpenStackRequest *)request).callback = callback;
33     
34     [request setCompletionBlock:^{
35         if ([request isSuccess]) {
36             success(request);
37             [request notify];
38         } else {
39             failure(request);
40             [request notify];
41         }
42     }];
43     [request setFailedBlock:^{
44         failure(request);
45         [request notify];
46     }];
47     [request startAsynchronous];
48     return callback;
49 }
50
51 - (APICallback *)callbackWithRequest:(id)request success:(APIResponseBlock)success {
52     return [self callbackWithRequest:request success:success failure:^(OpenStackRequest *request){}];
53 }
54
55 - (APICallback *)callbackWithRequest:(id)request {
56     return [self callbackWithRequest:request success:^(OpenStackRequest *request){} failure:^(OpenStackRequest *request){}];
57 }
58
59 #pragma mark - API Calls
60 #pragma mark Service Catalog
61
62 - (APICallback *)serviceCatalog {
63     __block OpenStackRequest *request = [OpenStackRequest serviceCatalogRequest:self.account];
64     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
65         NSURL *pithosObjectStoreURL = nil;
66         NSURL *astakosAccountURL = nil;
67         NSURL *astakosWebloginURL = nil;
68         NSArray *serviceCatalog = [request serviceCatalog];
69         for (NSDictionary *service in serviceCatalog) {
70             NSString *serviceName = [service objectForKey:@"name"];
71             if (!pithosObjectStoreURL && [serviceName isEqualToString:@"pithos_object-store"]) {
72                 pithosObjectStoreURL = [NSURL URLWithString:[[[service objectForKey:@"endpoints"]
73                                                               objectAtIndex:0]
74                                                              objectForKey:@"publicURL"]];
75             } else if (!astakosAccountURL && [serviceName isEqualToString:@"astakos_account"]) {
76                 astakosAccountURL = [NSURL URLWithString:[[[service objectForKey:@"endpoints"]
77                                                             objectAtIndex:0]
78                                                            objectForKey:@"publicURL"]];
79             } else if (!astakosWebloginURL && [serviceName isEqualToString:@"astakos_weblogin"]) {
80                 astakosWebloginURL = [NSURL URLWithString:[[[service objectForKey:@"endpoints"]
81                                                             objectAtIndex:0]
82                                                            objectForKey:@"SNF:webloginURL"]];
83             }
84             if (pithosObjectStoreURL && astakosAccountURL && astakosWebloginURL)
85                 break;
86         }
87         self.account.provider.pithosObjectStoreURL = pithosObjectStoreURL;
88         self.account.provider.astakosAccountURL = astakosAccountURL;
89         self.account.provider.astakosWebloginURL = astakosWebloginURL;
90         self.account.provider.manual = NO;
91         
92         if (self.account.authToken.length) {
93             NSDictionary *token = [request token];
94             self.account.authToken = [token objectForKey:@"id"];
95             self.account.username = [[token objectForKey:@"tenant"] objectForKey:@"id"];
96         }
97     } failure:^(OpenStackRequest *request) {
98         if (request.responseStatusCode == 404) {
99             self.account.provider.pithosObjectStoreURL = [self.account.provider.authURL URLByAppendingPathComponent:@"v1"];
100             self.account.provider.astakosAccountURL = [[self.account.provider.authURL copy] autorelease];
101             self.account.provider.astakosWebloginURL = [[self.account.provider.authURL copy] autorelease];
102             self.account.provider.manual = YES;
103         }
104     }];
105 }
106
107 #pragma mark User Catalog
108
109 - (APICallback *)userCatalogForDisplaynames:(NSArray *)displaynames UUIDs:(NSArray *)UUIDs {
110     __block OpenStackRequest *request = [OpenStackRequest userCatalogRequest:self.account displaynames:displaynames UUIDs:UUIDs];
111     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
112         NSDictionary *catalogs = [request catalogs];
113         NSDictionary *displaynameCatalog = [catalogs objectForKey:@"displayname_catalog"];
114         for (NSString *displayname in displaynameCatalog) {
115             [self.account.userCatalog setObject:displayname forKey:[displaynameCatalog objectForKey:displayname]];
116         }
117         if (UUIDs) {
118             NSDictionary *UUIDCatalog = [catalogs objectForKey:@"uuid_catalog"];
119             for (NSString *UUID in UUIDs) {
120                 NSString *displayname = [UUIDCatalog objectForKey:UUID];
121                 if (displayname) {
122                     [self.account.userCatalog setObject:displayname forKey:UUID];
123                 } else {
124                     [self.account.userCatalog removeObjectForKey:UUID];
125                 }
126             }
127         }
128         [self.account persist];
129     }];
130 }
131
132 #pragma mark Top
133
134 - (APICallback *)authenticate {
135     __block OpenStackRequest *request = [OpenStackRequest authenticationRequest:self.account];
136     return [self callbackWithRequest:request];
137 }
138
139 - (APICallback *)getSharingAccounts {
140     __block OpenStackRequest *request = [OpenStackRequest getSharingAccountsRequest:self.account];
141     return [self callbackWithRequest:request];
142 }
143
144 #pragma mark Account
145
146 - (APICallback *)getStorageAccountInfo {
147     __block OpenStackRequest *request = [OpenStackRequest getStorageAccountInfoRequest:self.account];
148     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
149         NSString *bytesUsedString = [request.responseHeaders objectForKey:@"X-Account-Bytes-Used"];
150         self.account.bytesUsed = (bytesUsedString ?
151                                   [NSNumber numberWithUnsignedLongLong:strtoull([bytesUsedString UTF8String], NULL, 0)] : nil);
152         NSString *policyQuotaString = [request.responseHeaders objectForKey:@"X-Account-Policy-Quota"];
153         self.account.policyQuota = (policyQuotaString ?
154                                     [NSNumber numberWithUnsignedLongLong:strtoull([policyQuotaString UTF8String], NULL, 0)] : nil);
155         [self.account persist];
156     }];
157 }
158
159 - (APICallback *)getContainers {
160     __block OpenStackRequest *request = [OpenStackRequest getContainersRequest:self.account];
161     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
162         self.account.containers = [request containers];
163         NSString *bytesUsedString = [request.responseHeaders objectForKey:@"X-Account-Bytes-Used"];
164         self.account.bytesUsed = (bytesUsedString ?
165                                   [NSNumber numberWithUnsignedLongLong:strtoull([bytesUsedString UTF8String], NULL, 0)] : nil);
166         NSString *policyQuotaString = [request.responseHeaders objectForKey:@"X-Account-Policy-Quota"];
167         self.account.policyQuota = (policyQuotaString ?
168                                     [NSNumber numberWithUnsignedLongLong:strtoull([policyQuotaString UTF8String], NULL, 0)] : nil);
169         [self.account persist];
170     }];
171 }
172
173 - (APICallback *)writeAccountMetadata:(NSDictionary *)accountInfo {
174     __block OpenStackRequest *request = [OpenStackRequest writeAccountMetadataRequest:self.account withAccountInfo:accountInfo];
175     return [self callbackWithRequest:request];
176 }
177
178 #pragma mark Container
179
180 - (APICallback *)getContainerInfo:(Container *)container {
181     __block OpenStackRequest *request = [OpenStackRequest getContainerInfoRequest:self.account container:container];
182     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
183         container.metadata = [NSMutableDictionary dictionary];
184         for (NSString *headerName in request.responseHeaders) {
185             if ([headerName hasPrefix:@"X-Container-Meta-"]) {
186                 [container.metadata setObject:[NSString decodeFromPercentEscape:[request.responseHeaders objectForKey:headerName]]
187                                                                          forKey:[NSString decodeFromPercentEscape:[headerName substringFromIndex:17]]];
188             }
189         }
190     }];
191 }
192
193 - (APICallback *)createContainer:(Container *)container {
194     __block OpenStackRequest *request = [OpenStackRequest createContainerRequest:self.account container:container];
195     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
196         [self.account.containers setObject:container forKey:container.name];
197         [self.account persist];
198     }];
199 }
200
201 - (APICallback *)deleteContainer:(Container *)container {
202     __block OpenStackRequest *request = [OpenStackRequest deleteContainerRequest:self.account container:container];
203     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
204     }];
205 }
206
207 - (void)getObjects:(Container *)container afterMarker:(NSString *)marker objectsBuffer:(NSMutableDictionary *)objectsBuffer {
208     if (!self.queue) {
209         self.queue = [ASINetworkQueue queue];
210         self.queue.shouldCancelAllRequestsOnFailure = NO;
211         [self.queue go];
212     }
213     GetObjectsRequest *request = [GetObjectsRequest request:self.account container:container marker:marker objectsBuffer:objectsBuffer];
214     [queue addOperation:request];
215 }
216
217 - (void)getObjects:(Container *)container {
218     [self getObjects:container afterMarker:nil objectsBuffer:nil];
219 }
220
221 - (APICallback *)writeContainerPolicy:(Container *)container {
222     __block OpenStackRequest *request = [OpenStackRequest writeContainerPolicyRequest:self.account container:container];
223     return [self callbackWithRequest:request];
224 }
225
226 #pragma mark Storage Object
227
228 - (APICallback *)getObjectInfo:(Container *)container object:(StorageObject *)object {
229     __block OpenStackRequest *request = [OpenStackRequest getObjectInfoRequest:self.account container:container object:object];
230     return [self callbackWithRequest:request];
231 }
232
233 - (APICallback *)getObjectInfo:(Container *)container object:(StorageObject *)object version:(NSString *)version {
234     if (!version) {
235         return [self getObjectInfo:container object:object];
236     }
237     
238     __block OpenStackRequest *request = [OpenStackRequest getObjectInfoRequest:self.account container:container object:object version:version];
239     return [self callbackWithRequest:request];
240 }
241
242 - (APICallback *)getObjectVersionsList:(Container *)container object:(StorageObject *)object {
243     __block OpenStackRequest *request = [OpenStackRequest getObjectVersionsRequest:account container:container object:object];
244     return [self callbackWithRequest:request];
245 }
246
247 - (APICallback *)getObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate
248            requestUserInfo:(NSDictionary *)requestUserInfo version:(NSString *)version {
249     __block OpenStackRequest *request = [OpenStackRequest getObjectRequest:self.account container:container object:object version:version];
250     request.delegate = self;
251     request.downloadProgressDelegate = downloadProgressDelegate;
252     request.showAccurateProgress = YES;
253     if (requestUserInfo) {
254         request.userInfo = requestUserInfo;
255     }
256     
257     OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
258     [app setObjectDownloadRequest:request forAccount:account container:container object:object];
259     return [self callbackWithRequest:request
260                              success:^(OpenStackRequest *request) {
261                                  if (!object.hash) {
262                                      object.hash = [request.responseHeaders objectForKey:@"X-Object-Hash"];
263                                  }
264                                  OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
265                                  NSString *filePath = [app.cacheDirectoryPath stringByAppendingFormat:@"/%@.%@", object.hash, object.name.pathExtension];
266                                  [[request responseData] writeToFile:filePath atomically:YES];
267                                  [app setCacheFilePath:filePath forHash:object.hash];
268                                  [app removeObjectDownloadRequestForAccount:account container:container object:object];
269                              }
270                              failure:^(OpenStackRequest *request) {
271                                  OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
272                                  [app removeObjectDownloadRequestForAccount:account container:container object:object];
273                              }];
274 }
275
276 - (APICallback *)getObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate
277            requestUserInfo:(NSDictionary *)requestUserInfo {
278     return [self getObject:container object:object downloadProgressDelegate:downloadProgressDelegate requestUserInfo:requestUserInfo version:nil];
279 }
280
281 - (APICallback *)getObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate {
282     return [self getObject:container object:object downloadProgressDelegate:downloadProgressDelegate requestUserInfo:nil version:nil];
283 }
284
285 - (APICallback *)writeObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate {
286     __block OpenStackRequest *request = [OpenStackRequest writeObjectRequest:self.account container:container object:object];
287     request.delegate = self;
288     request.uploadProgressDelegate = downloadProgressDelegate;
289     request.showAccurateProgress = YES;
290     return [self callbackWithRequest:request];
291 }
292
293 - (APICallback *)writeObjectMetadata:(Container *)container object:(StorageObject *)object {
294     __block OpenStackRequest *request = [OpenStackRequest writeObjectMetadataRequest:self.account container:container object:object];
295     return [self callbackWithRequest:request];
296 }
297
298 - (APICallback *)deleteObject:(Container *)container object:(StorageObject *)object {
299     __block OpenStackRequest *request = [OpenStackRequest deleteObjectRequest:self.account container:container object:object];
300     return [self callbackWithRequest:request];
301 }
302
303 #pragma mark - Notifications
304
305 - (NSString *)notificationName:(NSString *)key identifier:(NSString *)identifier {
306     return [NSString stringWithFormat:@"%@-%@-%@", key, self.account.uuid, identifier];
307 }
308
309 - (void)notify:(NSString *)name request:(OpenStackRequest *)request {
310     NSNotification *notification = [NSNotification notificationWithName:name object:nil userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
311     [[NSNotificationCenter defaultCenter] postNotification:notification];
312 }
313
314 - (void)notify:(NSString *)name request:(OpenStackRequest *)request object:(id)object {
315     NSNotification *notification = [NSNotification notificationWithName:name object:object userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
316     [[NSNotificationCenter defaultCenter] postNotification:notification];
317 }
318
319 #pragma mark - Request Delegates
320
321 - (void)requestFinished:(OpenStackRequest *)request {
322     NSString *notificationName = [request.userInfo objectForKey:@"notificationName"];
323     if (!notificationName)
324         return;
325     
326     id notificationObject = [request.userInfo objectForKey:@"notificationObject"];
327     if ([request isSuccess]) {
328         NSNotification *notification = [NSNotification notificationWithName:[NSString stringWithFormat:@"%@Succeeded", notificationName] object:notificationObject];
329         [[NSNotificationCenter defaultCenter] postNotification:notification];
330     } else {
331         NSNotification *notification = [NSNotification notificationWithName:[NSString stringWithFormat:@"%@Failed", notificationName] object:notificationObject userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
332         [[NSNotificationCenter defaultCenter] postNotification:notification];
333     }
334 }
335
336 - (void)requestFailed:(OpenStackRequest *)request {
337     NSString *notificationName = [request.userInfo objectForKey:@"notificationName"];
338     if (!notificationName)
339         return;
340     
341     id notificationObject = [request.userInfo objectForKey:@"notificationObject"];
342     NSNotification *notification = [NSNotification notificationWithName:[NSString stringWithFormat:@"%@Failed", notificationName] object:notificationObject userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
343     [[NSNotificationCenter defaultCenter] postNotification:notification];
344 }
345
346 #pragma mark - Observers
347
348 - (void)getObjectsSucceeded:(OpenStackRequest *)request {
349     if ([request isSuccess]) {
350         Container *container = [request.userInfo objectForKey:@"container"];
351         NSMutableDictionary *objects = [request objects];
352         container.rootFolder = [Folder folder];
353         container.rootFolder.objects = objects;
354         [self.account persist];
355         
356         NSNotification *notification = [NSNotification notificationWithName:@"getObjectsSucceeded" object:self.account userInfo:[NSDictionary dictionaryWithObject:container forKey:@"container"]];
357         [[NSNotificationCenter defaultCenter] postNotification:notification];
358     } else {
359         NSNotification *notification = [NSNotification notificationWithName:@"getObjectsFailed" object:self.account userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
360         [[NSNotificationCenter defaultCenter] postNotification:notification];
361     }
362 }
363
364 - (void)getObjectsFailed:(OpenStackRequest *)request {
365     NSNotification *notification = [NSNotification notificationWithName:@"getObjectsFailed" object:self.account userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
366     [[NSNotificationCenter defaultCenter] postNotification:notification];
367 }
368
369 @end