root / Classes / OpenStackRequest.m @ ef74c42f
History | View | Annotate | Download (21.2 kB)
1 |
// |
---|---|
2 |
// OpenStackRequest.m |
3 |
// OpenStack |
4 |
// |
5 |
// Created by Mike Mayo on 10/8/10. |
6 |
// The OpenStack project is provided under the Apache 2.0 license. |
7 |
// |
8 |
|
9 |
#import "OpenStackRequest.h" |
10 |
#import "Provider.h" |
11 |
#import "OpenStackAccount.h" |
12 |
#import "JSON.h" |
13 |
#import "Container.h" |
14 |
#import "StorageObject.h" |
15 |
#import "Folder.h" |
16 |
#import "AccountManager.h" |
17 |
#import "APICallback.h" |
18 |
#import "APILogEntry.h" |
19 |
#import "NSString+Conveniences.h" |
20 |
|
21 |
@implementation OpenStackRequest |
22 |
|
23 |
@synthesize account, callback, errorAlerter, retryWithUpdatedURL, followUpSelectorString, notificationURL; |
24 |
|
25 |
#pragma mark - Constructors |
26 |
#pragma mark Generic |
27 |
|
28 |
+ (id)requestWithoutToken:(OpenStackAccount *)account method:(NSString *)method url:(NSURL *)url { |
29 |
OpenStackRequest *request = [[[self alloc] initWithURL:url] autorelease]; |
30 |
request.account = account; |
31 |
request.requestMethod = method; |
32 |
[request addRequestHeader:@"Content-Type" value:@"application/json"]; |
33 |
request.timeOutSeconds = 60; |
34 |
request.numberOfTimesToRetryOnTimeout = 5; |
35 |
request.validatesSecureCertificate = !account.ignoreSSLErrors; |
36 |
request.retryWithUpdatedURL = (account.provider.manual ? NO : YES); |
37 |
return request; |
38 |
} |
39 |
|
40 |
+ (id)request:(OpenStackAccount *)account method:(NSString *)method url:(NSURL *)url { |
41 |
OpenStackRequest *request = [self requestWithoutToken:account method:method url:url]; |
42 |
[request addRequestHeader:@"X-Auth-Token" value:account.authToken]; |
43 |
return request; |
44 |
} |
45 |
|
46 |
+ (id)filesRequest:(OpenStackAccount *)account method:(NSString *)method path:(NSString *)path { |
47 |
NSString *urlString = [account.filesURL description]; |
48 |
if (account.sharingAccount) { |
49 |
urlString = [NSString stringWithFormat:@"%@%@", |
50 |
[urlString substringToIndex:[urlString rangeOfString:account.username].location], |
51 |
account.sharingAccount]; |
52 |
} |
53 |
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@?format=json%@", |
54 |
urlString, |
55 |
path, |
56 |
(account.shared ? @"&shared=" : @"")]]; |
57 |
return [self request:account method:method url:url]; |
58 |
} |
59 |
|
60 |
#pragma mark NSCopying |
61 |
|
62 |
- (id)copyWithZone:(NSZone *)zone { |
63 |
OpenStackRequest *newRequest = [super copyWithZone:zone]; |
64 |
newRequest.account = self.account; |
65 |
newRequest.callback = self.callback; |
66 |
newRequest.errorAlerter = self.errorAlerter; |
67 |
newRequest.retryWithUpdatedURL = self.retryWithUpdatedURL; |
68 |
newRequest.followUpSelectorString = self.followUpSelectorString; |
69 |
newRequest.notificationURL = self.notificationURL; |
70 |
return newRequest; |
71 |
} |
72 |
|
73 |
#pragma mark Service Catalog |
74 |
+ (id)serviceCatalogRequest:(OpenStackAccount *)account { |
75 |
OpenStackRequest *request = [self requestWithoutToken:account method:@"POST" url:account.provider.tokensURL]; |
76 |
if (account.authToken.length) |
77 |
[request appendPostData:[[NSString stringWithFormat:@"{\"auth\":{\"token\":{\"id\":\"%@\"}}}", account.authToken] |
78 |
dataUsingEncoding:NSUTF8StringEncoding]]; |
79 |
return request; |
80 |
} |
81 |
|
82 |
- (NSDictionary *)access { |
83 |
SBJSON *parser = [[[SBJSON alloc] init] autorelease]; |
84 |
NSDictionary *access = [[parser objectWithString:[self responseString]] objectForKey:@"access"]; |
85 |
return access; |
86 |
} |
87 |
|
88 |
- (NSDictionary *)token { |
89 |
return [[self access] objectForKey:@"token"]; |
90 |
} |
91 |
|
92 |
- (NSArray *)serviceCatalog { |
93 |
return [[self access] objectForKey:@"serviceCatalog"]; |
94 |
} |
95 |
|
96 |
- (NSArray *)user { |
97 |
return [[self access] objectForKey:@"user"]; |
98 |
} |
99 |
|
100 |
#pragma mark User Catalog |
101 |
+ (id)userCatalogRequest:(OpenStackAccount *)account displaynames:(NSArray *)displaynames UUIDs:(NSArray *)UUIDs { |
102 |
OpenStackRequest *request = [self request:account method:@"POST" url:account.provider.userCatalogURL]; |
103 |
NSMutableString *dataString = [NSMutableString stringWithString:@"{\"displaynames\":["]; |
104 |
if (displaynames) { |
105 |
for (NSUInteger index = 0 ; index < displaynames.count ; index++) { |
106 |
[dataString appendFormat:@"\"%@\"%@", |
107 |
[[[displaynames objectAtIndex:index] stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"] |
108 |
stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""], |
109 |
((index == displaynames.count - 1) ? @"" : @",")]; |
110 |
} |
111 |
} |
112 |
[dataString appendFormat:@"],\"uuids\":["]; |
113 |
if (UUIDs) { |
114 |
for (NSUInteger index = 0 ; index < UUIDs.count ; index++) { |
115 |
[dataString appendFormat:@"\"%@\"%@", [UUIDs objectAtIndex:index], ((index == UUIDs.count - 1) ? @"" : @",")]; |
116 |
} |
117 |
} |
118 |
[dataString appendFormat:@"]}"]; |
119 |
[request appendPostData:[dataString dataUsingEncoding:NSUTF8StringEncoding]]; |
120 |
return request; |
121 |
} |
122 |
|
123 |
- (NSDictionary *)catalogs { |
124 |
SBJSON *parser = [[[SBJSON alloc] init] autorelease]; |
125 |
NSDictionary *catalogs = [parser objectWithString:[self responseString]]; |
126 |
return catalogs; |
127 |
} |
128 |
|
129 |
- (NSDictionary *)displaynameCatalog { |
130 |
return [[self catalogs] objectForKey:@"displayname_catalog"]; |
131 |
} |
132 |
|
133 |
- (NSDictionary *)UUIDCatalog { |
134 |
return [[self catalogs] objectForKey:@"uuid_catalog"]; |
135 |
} |
136 |
|
137 |
#pragma mark Top |
138 |
|
139 |
+ (id)authenticationRequest:(OpenStackAccount *)account { |
140 |
OpenStackRequest *request = [[[self alloc] initWithURL:account.provider.authEndpointURL] autorelease]; |
141 |
request.account = account; |
142 |
request.requestMethod = @"GET"; |
143 |
[request addRequestHeader:@"X-Auth-User" value:account.username]; |
144 |
[request addRequestHeader:@"X-Auth-Token" value:account.authToken]; |
145 |
request.timeOutSeconds = 60; |
146 |
request.numberOfTimesToRetryOnTimeout = 5; |
147 |
request.validatesSecureCertificate = !account.ignoreSSLErrors; |
148 |
request.retryWithUpdatedURL = (account.provider.manual ? NO : YES); |
149 |
return request; |
150 |
} |
151 |
|
152 |
+ (id)getSharingAccountsRequest:(OpenStackAccount *)account |
153 |
marker:(NSString *)marker sharingAccountsBuffer:(NSMutableDictionary *)sharingAccountsBuffer { |
154 |
OpenStackRequest *request = [self request:account method:@"GET" |
155 |
url:[NSURL URLWithString:[NSString stringWithFormat:@"%@?format=json", |
156 |
account.provider.authEndpointURL]]]; |
157 |
if (marker) |
158 |
request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&marker=%@", request.url.description, [NSString encodeToPercentEscape:marker]]]; |
159 |
if (!sharingAccountsBuffer) |
160 |
sharingAccountsBuffer = [NSMutableDictionary dictionary]; |
161 |
request.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: |
162 |
sharingAccountsBuffer, @"sharingAccountsBuffer", |
163 |
nil]; |
164 |
return request; |
165 |
} |
166 |
|
167 |
+ (id)getSharingAccountsRequest:(OpenStackAccount *)account { |
168 |
return [self getSharingAccountsRequest:account marker:nil sharingAccountsBuffer:nil]; |
169 |
} |
170 |
|
171 |
- (NSArray *)jsonSharingAccounts { |
172 |
return [[[[SBJSON alloc] init] autorelease] objectWithString:[self responseString]]; |
173 |
} |
174 |
|
175 |
#pragma mark Account |
176 |
|
177 |
+ (id)getStorageAccountInfoRequest:(OpenStackAccount *)account { |
178 |
return [self filesRequest:account method:@"HEAD" path:@""]; |
179 |
} |
180 |
|
181 |
+ (id)getContainersRequest:(OpenStackAccount *)account |
182 |
marker:(NSString *)marker containersBuffer:(NSMutableDictionary *)containersBuffer { |
183 |
OpenStackRequest *request = [self filesRequest:account method:@"GET" path:@""]; |
184 |
if (marker) |
185 |
request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&marker=%@", request.url.description, [NSString encodeToPercentEscape:marker]]]; |
186 |
if (!containersBuffer) |
187 |
containersBuffer = [NSMutableDictionary dictionary]; |
188 |
request.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: |
189 |
containersBuffer, @"containersBuffer", |
190 |
nil]; |
191 |
return request; |
192 |
} |
193 |
|
194 |
+ (id)getContainersRequest:(OpenStackAccount *)account { |
195 |
return [self getContainersRequest:account marker:nil containersBuffer:nil]; |
196 |
} |
197 |
|
198 |
- (NSArray *)jsonContainers { |
199 |
return [[[[SBJSON alloc] init] autorelease] objectWithString:[self responseString]]; |
200 |
} |
201 |
|
202 |
+ (id)writeAccountMetadataRequest:(OpenStackAccount *)account withAccountInfo:(NSDictionary *)accountInfo { |
203 |
OpenStackRequest *request = [self filesRequest:account method:@"POST" path:@""]; |
204 |
|
205 |
NSMutableDictionary *groups = [accountInfo objectForKey:@"groups"]; |
206 |
if (groups.count) { |
207 |
for (NSString *groupName in groups) { |
208 |
[request.requestHeaders setObject:[NSString encodeToPercentEscape:[groups objectForKey:groupName]] |
209 |
forKey:[NSString stringWithFormat:@"X-Account-Group-%@", [NSString encodeToPercentEscape:groupName]]]; |
210 |
} |
211 |
} else { |
212 |
[request.requestHeaders setObject:@"" forKey:@"X-Account-Group-group"]; |
213 |
} |
214 |
|
215 |
NSMutableDictionary *metadata = [accountInfo objectForKey:@"metadata"]; |
216 |
for (NSString *key in metadata) { |
217 |
[request.requestHeaders setObject:[NSString encodeToPercentEscape:[metadata objectForKey:key]] |
218 |
forKey:[NSString stringWithFormat:@"X-Account-Meta-%@", [NSString encodeToPercentEscape:key]]]; |
219 |
} |
220 |
|
221 |
return request; |
222 |
} |
223 |
|
224 |
#pragma mark Container |
225 |
|
226 |
+ (id)getContainerInfoRequest:(OpenStackAccount *)account container:(Container *)container { |
227 |
return [self filesRequest:account method:@"HEAD" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]]; |
228 |
} |
229 |
|
230 |
+ (id)createContainerRequest:(OpenStackAccount *)account container:(Container *)container { |
231 |
return [self filesRequest:account method:@"PUT" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]]; |
232 |
} |
233 |
|
234 |
+ (id)deleteContainerRequest:(OpenStackAccount *)account container:(Container *)container { |
235 |
return [self filesRequest:account method:@"DELETE" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]]; |
236 |
} |
237 |
|
238 |
+ (id)getObjectsRequest:(OpenStackAccount *)account container:(Container *)container |
239 |
marker:(NSString *)marker objectsBuffer:(NSMutableDictionary *)objectsBuffer { |
240 |
OpenStackRequest *request = [self filesRequest:account method:@"GET" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]]; |
241 |
if (marker) |
242 |
request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&marker=%@", request.url.description, [NSString encodeToPercentEscape:marker]]]; |
243 |
if (!objectsBuffer) |
244 |
objectsBuffer = [NSMutableDictionary dictionary]; |
245 |
request.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: |
246 |
container, @"container", |
247 |
objectsBuffer, @"objectsBuffer", |
248 |
nil]; |
249 |
return request; |
250 |
} |
251 |
|
252 |
+ (id)getObjectsRequest:(OpenStackAccount *)account container:(Container *)container { |
253 |
return [self getObjectsRequest:account container:container marker:nil objectsBuffer:nil]; |
254 |
} |
255 |
|
256 |
- (NSArray *)jsonObjects { |
257 |
return [[[[SBJSON alloc] init] autorelease] objectWithString:[self responseString]]; |
258 |
} |
259 |
|
260 |
+ (id)writeContainerPolicyRequest:(OpenStackAccount *)account container:(Container *)container { |
261 |
OpenStackRequest *request = [self filesRequest:account method:@"PUT" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]]; |
262 |
[request.requestHeaders setObject:container.versioning forKey:@"X-Container-Policy-Versioning"]; |
263 |
[request.requestHeaders setObject:[NSString stringWithFormat:@"%u", container.quota] forKey:@"X-Container-Policy-Quota"]; |
264 |
return request; |
265 |
} |
266 |
|
267 |
#pragma mark Storage Object |
268 |
|
269 |
+ (id)getObjectInfoRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object { |
270 |
NSString *objectFullPath = object.fullPath; |
271 |
if ([objectFullPath hasPrefix:@"/"]) |
272 |
objectFullPath = [objectFullPath substringFromIndex:1]; |
273 |
return [self filesRequest:account method:@"HEAD" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:objectFullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]]; |
274 |
} |
275 |
|
276 |
+ (id)getObjectInfoRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object version:(NSString *)version { |
277 |
OpenStackRequest *request = [self getObjectInfoRequest:account container:container object:object]; |
278 |
request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&version=%@", request.url.description, version]]; |
279 |
return request; |
280 |
} |
281 |
|
282 |
+ (id)getObjectVersionsRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object { |
283 |
OpenStackRequest *request = [self filesRequest:account method:@"GET" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:object.fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]]; |
284 |
request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&version=list", request.url.description]]; |
285 |
return request; |
286 |
} |
287 |
|
288 |
- (NSMutableArray *)versions { |
289 |
SBJSON *parser = [[[SBJSON alloc] init] autorelease]; |
290 |
NSArray *jsonVersions = [[parser objectWithString:[self responseString]] objectForKey:@"versions"]; |
291 |
NSMutableArray *versions = [NSMutableArray arrayWithCapacity:[jsonVersions count]]; |
292 |
for (NSArray *jsonVersion in jsonVersions) { |
293 |
[versions addObject:[NSDictionary dictionaryWithObjectsAndKeys: |
294 |
[jsonVersion objectAtIndex:0], @"versionID", |
295 |
[NSDate dateWithTimeIntervalSince1970:[[jsonVersion objectAtIndex:1] doubleValue]], @"versionDate", |
296 |
nil]]; |
297 |
} |
298 |
return versions; |
299 |
} |
300 |
|
301 |
|
302 |
+ (id)getObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object { |
303 |
return [self filesRequest:account method:@"GET" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:object.fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]]; |
304 |
} |
305 |
|
306 |
+ (id)getObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object version:(NSString *)version { |
307 |
OpenStackRequest *request = [self getObjectRequest:account container:container object:object]; |
308 |
if (version) |
309 |
request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&version=%@", request.url.description, version]]; |
310 |
return request; |
311 |
} |
312 |
|
313 |
+ (id)writeObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object { |
314 |
NSString *objectFullPath = object.fullPath; |
315 |
if ([objectFullPath hasPrefix:@"/"]) |
316 |
objectFullPath = [objectFullPath substringFromIndex:1]; |
317 |
OpenStackRequest *request = [self filesRequest:account method:@"PUT" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:objectFullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]]; |
318 |
|
319 |
if (object.sharing) |
320 |
[request.requestHeaders setObject:object.sharing forKey:@"X-Object-Sharing"]; |
321 |
|
322 |
NSString *metadataKeyHeaderPrefix; |
323 |
if ([objectFullPath length] == 0) |
324 |
metadataKeyHeaderPrefix = @"X-Container-Meta-"; |
325 |
else |
326 |
metadataKeyHeaderPrefix = @"X-Object-Meta-"; |
327 |
for (NSString *metadataKey in object.metadata) { |
328 |
NSString *metadataKeyHeader = [NSString stringWithFormat:@"%@%@", metadataKeyHeaderPrefix, metadataKey]; |
329 |
metadataKeyHeader = [NSString encodeToPercentEscape:metadataKeyHeader]; |
330 |
NSString *metadataValue = [NSString encodeToPercentEscape:[object.metadata objectForKey:metadataKey]]; |
331 |
[request.requestHeaders setObject:metadataValue forKey:metadataKeyHeader]; |
332 |
} |
333 |
|
334 |
[request setPostBody:[NSMutableData dataWithData:object.data]]; |
335 |
[request.requestHeaders setObject:object.contentType forKey:@"Content-Type"]; |
336 |
return request; |
337 |
} |
338 |
|
339 |
+ (id)writeObjectMetadataRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object { |
340 |
NSString *objectFullPath = object.fullPath; |
341 |
if ([objectFullPath hasPrefix:@"/"]) |
342 |
objectFullPath = [objectFullPath substringFromIndex:1]; |
343 |
OpenStackRequest *request = [self filesRequest:account method:@"POST" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:objectFullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]]; |
344 |
|
345 |
NSString *metadataKeyHeaderPrefix; |
346 |
if ([objectFullPath length] == 0) |
347 |
metadataKeyHeaderPrefix = @"X-Container-Meta-"; |
348 |
else |
349 |
metadataKeyHeaderPrefix = @"X-Object-Meta-"; |
350 |
for (NSString *metadataKey in object.metadata) { |
351 |
NSString *metadataKeyHeader = [NSString stringWithFormat:@"%@%@", metadataKeyHeaderPrefix, metadataKey]; |
352 |
metadataKeyHeader = [NSString encodeToPercentEscape:metadataKeyHeader]; |
353 |
NSString *metadataValue = [NSString encodeToPercentEscape:[object.metadata objectForKey:metadataKey]]; |
354 |
[request.requestHeaders setObject:metadataValue forKey:metadataKeyHeader]; |
355 |
} |
356 |
|
357 |
if (!account.sharingAccount) { |
358 |
[request.requestHeaders setObject:([object.publicURI length] ? @"true" : @"false") forKey:@"X-Object-Public"]; |
359 |
if (object.sharing) { |
360 |
[request.requestHeaders setObject:[NSString encodeToPercentEscape:object.sharing] forKey:@"X-Object-Sharing"]; |
361 |
} |
362 |
} |
363 |
return request; |
364 |
} |
365 |
|
366 |
+ (id)deleteObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object { |
367 |
NSString *objectFullPath = object.fullPath; |
368 |
if ([objectFullPath hasPrefix:@"/"]) |
369 |
objectFullPath = [objectFullPath substringFromIndex:1]; |
370 |
return [self filesRequest:account method:@"DELETE" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:objectFullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]]; |
371 |
} |
372 |
|
373 |
#pragma mark - ASIHTTPRequest Overrides |
374 |
|
375 |
- (void)setCompletionBlock:(ASIBasicBlock)aCompletionBlock { |
376 |
[super setCompletionBlock:aCompletionBlock]; |
377 |
[backupCompletionBlock release]; |
378 |
backupCompletionBlock = [aCompletionBlock copy]; |
379 |
} |
380 |
|
381 |
- (void)setFailedBlock:(ASIBasicBlock)aFailedBlock { |
382 |
[super setFailedBlock:aFailedBlock]; |
383 |
[backupFailureBlock release]; |
384 |
backupFailureBlock = [aFailedBlock copy]; |
385 |
} |
386 |
|
387 |
- (void)failWithError:(NSError *)theError { |
388 |
if (responseStatusCode == 503) { |
389 |
NSNotification *notification = [NSNotification notificationWithName:@"serviceUnavailable" object:nil userInfo:nil]; |
390 |
[[NSNotificationCenter defaultCenter] postNotification:notification]; |
391 |
// This crashes the app, check if it can be removed completely. |
392 |
// } else if (responseStatusCode == 0) { |
393 |
// NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; |
394 |
// if (![defaults boolForKey:@"already_failed_on_connection"]) { |
395 |
// UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Connection Error" message:@"Please check your connection or API URL and try again." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; |
396 |
// [alert show]; |
397 |
// [alert release]; |
398 |
// } |
399 |
// [defaults setBool:YES forKey:@"already_failed_on_connection"]; |
400 |
// [defaults synchronize]; |
401 |
} |
402 |
|
403 |
[super failWithError:theError]; |
404 |
} |
405 |
|
406 |
#pragma mark - Actions |
407 |
|
408 |
- (BOOL)isSuccess { |
409 |
return (200 <= [self responseStatusCode]) && ([self responseStatusCode] <= 299); |
410 |
} |
411 |
|
412 |
#pragma mark - Notifications |
413 |
|
414 |
- (void)notify:(NSString *)name { |
415 |
NSDictionary *callbackUserInfo = [NSDictionary dictionaryWithObject:self forKey:@"response"]; |
416 |
NSNotification *notification = [NSNotification notificationWithName:name object:nil userInfo:callbackUserInfo]; |
417 |
[[NSNotificationCenter defaultCenter] postNotification:notification]; |
418 |
} |
419 |
|
420 |
- (void)notify { |
421 |
NSString *observeName = [NSString stringWithFormat:@"%@ %@ %@", [self isSuccess] ? @"SUCCESS" : @"FAILURE", self.requestMethod, (self.notificationURL ? self.notificationURL.description : self.url.description)]; |
422 |
NSString *callbackName = [NSString stringWithFormat:@"%@ %@ %@ %@", [self isSuccess] ? @"SUCCESS" : @"FAILURE", self.requestMethod, (self.notificationURL ? self.notificationURL.description : self.url.description), self.callback.uuid]; |
423 |
|
424 |
NSDictionary *callbackUserInfo = [NSDictionary dictionaryWithObject:self forKey:@"response"]; |
425 |
|
426 |
NSNotification *observeNotification = [NSNotification notificationWithName:observeName object:nil userInfo:callbackUserInfo]; |
427 |
[[NSNotificationCenter defaultCenter] postNotification:observeNotification]; |
428 |
|
429 |
NSNotification *callbackNotification = [NSNotification notificationWithName:callbackName object:nil userInfo:callbackUserInfo]; |
430 |
[[NSNotificationCenter defaultCenter] postNotification:callbackNotification]; |
431 |
|
432 |
} |
433 |
|
434 |
#pragma mark - Memory Management |
435 |
|
436 |
- (void)releaseBackupBlocksOnMainThread { |
437 |
NSMutableArray *blocks = [NSMutableArray array]; |
438 |
if (backupCompletionBlock) { |
439 |
[blocks addObject:backupCompletionBlock]; |
440 |
[backupCompletionBlock release]; |
441 |
backupCompletionBlock = nil; |
442 |
} |
443 |
if (backupFailureBlock) { |
444 |
[blocks addObject:backupFailureBlock]; |
445 |
[backupFailureBlock release]; |
446 |
backupFailureBlock = nil; |
447 |
} |
448 |
[[self class] performSelectorOnMainThread:@selector(releaseBackupBlocks:) withObject:blocks waitUntilDone:[NSThread isMainThread]]; |
449 |
} |
450 |
|
451 |
// Always called on main thread |
452 |
+ (void)releaseBackupBlocks:(NSArray *)blocks { |
453 |
// Blocks will be released when this method exits |
454 |
} |
455 |
|
456 |
- (void)dealloc { |
457 |
[account release]; |
458 |
[errorAlerter release]; |
459 |
[followUpSelectorString release]; |
460 |
[notificationURL release]; |
461 |
[self releaseBackupBlocksOnMainThread]; |
462 |
[super dealloc]; |
463 |
} |
464 |
|
465 |
@end |