5 // Created by Mike Mayo on 10/21/10.
6 // The OpenStack project is provided under the Apache 2.0 license.
9 #import "AccountManager.h"
10 #import "OpenStackAccount.h"
11 #import "OpenStackRequest.h"
15 #import "StorageObject.h"
16 #import "GetObjectsRequest.h"
17 #import "ASINetworkQueue.h"
18 #import "APICallback.h"
20 #import "OpenStackAppDelegate.h"
21 #import "NSString+Conveniences.h"
23 @implementation AccountManager
25 @synthesize account, queue;
27 #pragma mark - Callbacks
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;
34 [request setCompletionBlock:^{
35 if ([request isSuccess]) {
43 [request setFailedBlock:^{
47 [request startAsynchronous];
51 - (APICallback *)callbackWithRequest:(id)request success:(APIResponseBlock)success {
52 return [self callbackWithRequest:request success:success failure:^(OpenStackRequest *request){}];
55 - (APICallback *)callbackWithRequest:(id)request {
56 return [self callbackWithRequest:request success:^(OpenStackRequest *request){} failure:^(OpenStackRequest *request){}];
59 #pragma mark - API Calls
60 #pragma mark Service Catalog
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"]
74 objectForKey:@"publicURL"]];
75 } else if (!astakosAccountURL && [serviceName isEqualToString:@"astakos_account"]) {
76 astakosAccountURL = [NSURL URLWithString:[[[service objectForKey:@"endpoints"]
78 objectForKey:@"publicURL"]];
79 } else if (!astakosWebloginURL && [serviceName isEqualToString:@"astakos_weblogin"]) {
80 astakosWebloginURL = [NSURL URLWithString:[[[service objectForKey:@"endpoints"]
82 objectForKey:@"SNF:webloginURL"]];
84 if (pithosObjectStoreURL && astakosAccountURL && astakosWebloginURL)
87 self.account.provider.pithosObjectStoreURL = pithosObjectStoreURL;
88 self.account.provider.astakosAccountURL = astakosAccountURL;
89 self.account.provider.astakosWebloginURL = astakosWebloginURL;
90 self.account.provider.manual = NO;
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"];
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;
107 #pragma mark User Catalog
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]];
118 NSDictionary *UUIDCatalog = [catalogs objectForKey:@"uuid_catalog"];
119 for (NSString *UUID in UUIDs) {
120 NSString *displayname = [UUIDCatalog objectForKey:UUID];
122 [self.account.userCatalog setObject:displayname forKey:UUID];
124 [self.account.userCatalog removeObjectForKey:UUID];
128 [self.account persist];
134 - (APICallback *)authenticate {
135 __block OpenStackRequest *request = [OpenStackRequest authenticationRequest:self.account];
136 return [self callbackWithRequest:request];
139 - (APICallback *)getSharingAccounts {
140 __block OpenStackRequest *request = [OpenStackRequest getSharingAccountsRequest:self.account];
141 return [self callbackWithRequest:request];
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];
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];
173 - (APICallback *)writeAccountMetadata:(NSDictionary *)accountInfo {
174 __block OpenStackRequest *request = [OpenStackRequest writeAccountMetadataRequest:self.account withAccountInfo:accountInfo];
175 return [self callbackWithRequest:request];
178 #pragma mark Container
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]]];
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];
201 - (APICallback *)deleteContainer:(Container *)container {
202 __block OpenStackRequest *request = [OpenStackRequest deleteContainerRequest:self.account container:container];
203 return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
207 - (void)getObjects:(Container *)container afterMarker:(NSString *)marker objectsBuffer:(NSMutableDictionary *)objectsBuffer {
209 self.queue = [ASINetworkQueue queue];
210 self.queue.shouldCancelAllRequestsOnFailure = NO;
213 GetObjectsRequest *request = [GetObjectsRequest request:self.account container:container marker:marker objectsBuffer:objectsBuffer];
214 [queue addOperation:request];
217 - (void)getObjects:(Container *)container {
218 [self getObjects:container afterMarker:nil objectsBuffer:nil];
221 - (APICallback *)writeContainerPolicy:(Container *)container {
222 __block OpenStackRequest *request = [OpenStackRequest writeContainerPolicyRequest:self.account container:container];
223 return [self callbackWithRequest:request];
226 #pragma mark Storage Object
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];
233 - (APICallback *)getObjectInfo:(Container *)container object:(StorageObject *)object version:(NSString *)version {
235 return [self getObjectInfo:container object:object];
238 __block OpenStackRequest *request = [OpenStackRequest getObjectInfoRequest:self.account container:container object:object version:version];
239 return [self callbackWithRequest:request];
242 - (APICallback *)getObjectVersionsList:(Container *)container object:(StorageObject *)object {
243 __block OpenStackRequest *request = [OpenStackRequest getObjectVersionsRequest:account container:container object:object];
244 return [self callbackWithRequest:request];
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;
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) {
262 object.hash = [request.responseHeaders objectForKey:@"X-Object-Hash"];
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];
270 failure:^(OpenStackRequest *request) {
271 OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate];
272 [app removeObjectDownloadRequestForAccount:account container:container object:object];
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];
281 - (APICallback *)getObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate {
282 return [self getObject:container object:object downloadProgressDelegate:downloadProgressDelegate requestUserInfo:nil version:nil];
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];
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];
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];
303 #pragma mark - Notifications
305 - (NSString *)notificationName:(NSString *)key identifier:(NSString *)identifier {
306 return [NSString stringWithFormat:@"%@-%@-%@", key, self.account.uuid, identifier];
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];
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];
319 #pragma mark - Request Delegates
321 - (void)requestFinished:(OpenStackRequest *)request {
322 NSString *notificationName = [request.userInfo objectForKey:@"notificationName"];
323 if (!notificationName)
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];
331 NSNotification *notification = [NSNotification notificationWithName:[NSString stringWithFormat:@"%@Failed", notificationName] object:notificationObject userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
332 [[NSNotificationCenter defaultCenter] postNotification:notification];
336 - (void)requestFailed:(OpenStackRequest *)request {
337 NSString *notificationName = [request.userInfo objectForKey:@"notificationName"];
338 if (!notificationName)
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];
346 #pragma mark - Observers
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];
356 NSNotification *notification = [NSNotification notificationWithName:@"getObjectsSucceeded" object:self.account userInfo:[NSDictionary dictionaryWithObject:container forKey:@"container"]];
357 [[NSNotificationCenter defaultCenter] postNotification:notification];
359 NSNotification *notification = [NSNotification notificationWithName:@"getObjectsFailed" object:self.account userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
360 [[NSNotificationCenter defaultCenter] postNotification:notification];
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];