root / asi-http-request-with-pithos / Classes / Pithos / ASIPithosRequest.m @ 8fb25a1b
History | View | Annotate | Download (14.8 kB)
1 |
// ASIPithosRequest.m |
---|---|
2 |
// Based on ASICloudFilesRequest.m |
3 |
// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest |
4 |
// |
5 |
// Copyright 2011-2012 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 "ASIPithosRequest.h" |
39 |
#import "ASIPithos.h" |
40 |
#import "ASIPithosAccount.h" |
41 |
#import "SBJson.h" |
42 |
|
43 |
@implementation ASIPithosRequest |
44 |
@synthesize currentElement, currentContent, currentAccount; |
45 |
|
46 |
#pragma mark - |
47 |
#pragma mark Memory Management |
48 |
|
49 |
- (void)dealloc { |
50 |
[currentAccount release]; |
51 |
[currentContent release]; |
52 |
[currentElement release]; |
53 |
[sharingAccounts release]; |
54 |
[super dealloc]; |
55 |
} |
56 |
|
57 |
#pragma mark - |
58 |
#pragma mark Service Catalog |
59 |
|
60 |
// POST tokensURL |
61 |
+ (id)serviceCatalogRequestWithPithos:(ASIPithos *)pithos { |
62 |
ASIPithosRequest *request = [self requestWithURL:[NSURL URLWithString:pithos.tokensURL]]; |
63 |
[request setRequestMethod:@"POST"]; |
64 |
[request addRequestHeader:@"Content-Type" value:@"application/json"]; |
65 |
if (pithos.authToken.length) |
66 |
[request appendPostData:[[NSString stringWithFormat:@"{\"auth\":{\"token\":{\"id\":\"%@\"}}}", pithos.authToken] |
67 |
dataUsingEncoding:NSUTF8StringEncoding]]; |
68 |
request.validatesSecureCertificate = !pithos.ignoreSSLErrors; |
69 |
return request; |
70 |
} |
71 |
|
72 |
- (NSDictionary *)access { |
73 |
SBJsonParser *parser = [[[SBJsonParser alloc] init] autorelease]; |
74 |
NSDictionary *access = [[parser objectWithString:[self responseString]] objectForKey:@"access"]; |
75 |
return access; |
76 |
} |
77 |
|
78 |
- (NSDictionary *)token { |
79 |
return [[self access] objectForKey:@"token"]; |
80 |
} |
81 |
|
82 |
- (NSArray *)serviceCatalog { |
83 |
return [[self access] objectForKey:@"serviceCatalog"]; |
84 |
} |
85 |
|
86 |
- (NSArray *)user { |
87 |
return [[self access] objectForKey:@"user"]; |
88 |
} |
89 |
|
90 |
#pragma mark - |
91 |
#pragma mark User Catalog |
92 |
|
93 |
// POST userCatalogURL |
94 |
+ (id)userCatalogRequestWithPithos:(ASIPithos *)pithos displaynames:(NSArray *)displaynames UUIDs:(NSArray *)UUIDs { |
95 |
ASIPithosRequest *request = [self requestWithURL:[NSURL URLWithString:pithos.userCatalogURL]]; |
96 |
[request setRequestMethod:@"POST"]; |
97 |
[request addRequestHeader:@"X-Auth-Token" value:pithos.authToken]; |
98 |
[request addRequestHeader:@"Content-Type" value:@"application/json"]; |
99 |
|
100 |
NSMutableString *dataString = [NSMutableString stringWithString:@"{\"displaynames\":["]; |
101 |
if (displaynames) { |
102 |
for (NSUInteger index = 0 ; index < displaynames.count ; index++) { |
103 |
[dataString appendFormat:@"\"%@\"%@", |
104 |
[[[displaynames objectAtIndex:index] stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"] |
105 |
stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""], |
106 |
((index == displaynames.count - 1) ? @"" : @",")]; |
107 |
} |
108 |
} |
109 |
[dataString appendFormat:@"],\"uuids\":["]; |
110 |
if (UUIDs) { |
111 |
for (NSUInteger index = 0 ; index < UUIDs.count ; index++) { |
112 |
[dataString appendFormat:@"\"%@\"%@", [UUIDs objectAtIndex:index], ((index == UUIDs.count - 1) ? @"" : @",")]; |
113 |
} |
114 |
} |
115 |
[dataString appendFormat:@"]}"]; |
116 |
[request appendPostData:[dataString dataUsingEncoding:NSUTF8StringEncoding]]; |
117 |
|
118 |
request.validatesSecureCertificate = !pithos.ignoreSSLErrors; |
119 |
return request; |
120 |
} |
121 |
|
122 |
- (NSDictionary *)catalogs { |
123 |
SBJsonParser *parser = [[[SBJsonParser alloc] init] autorelease]; |
124 |
NSDictionary *catalogs = [parser objectWithString:[self responseString]]; |
125 |
return catalogs; |
126 |
} |
127 |
|
128 |
- (NSDictionary *)displaynameCatalog { |
129 |
return [[self catalogs] objectForKey:@"displayname_catalog"]; |
130 |
} |
131 |
|
132 |
- (NSDictionary *)UUIDCatalog { |
133 |
return [[self catalogs] objectForKey:@"uuid_catalog"]; |
134 |
} |
135 |
|
136 |
#pragma mark - |
137 |
#pragma mark GET |
138 |
|
139 |
// GET storageURLPrefix |
140 |
+ (id)listSharingAccountsRequestWithPithos:(ASIPithos *)pithos { |
141 |
return [self listSharingAccountsRequestWithPithos:pithos limit:0 marker:nil]; |
142 |
} |
143 |
|
144 |
// GET storageURLPrefix?[limit=limit]&[marker=marker] |
145 |
+ (id)listSharingAccountsRequestWithPithos:(ASIPithos *)pithos limit:(NSUInteger)limit marker:(NSString *)marker { |
146 |
NSString *urlString = [NSString stringWithFormat:@"%@/?format=xml", pithos.storageURLPrefix]; |
147 |
if (limit && (limit > 0)) |
148 |
urlString = [urlString stringByAppendingString:[NSString stringWithFormat:@"&limit=%lu", limit]]; |
149 |
if (marker) |
150 |
urlString = [urlString stringByAppendingString:[NSString stringWithFormat:@"&marker=%@", [self encodeToPercentEscape:marker]]]; |
151 |
ASIPithosRequest *request = [self requestWithURL:[NSURL URLWithString:urlString]]; |
152 |
[request setRequestMethod:@"GET"]; |
153 |
[request addRequestHeader:@"X-Auth-Token" value:pithos.authToken]; |
154 |
request.validatesSecureCertificate = !pithos.ignoreSSLErrors; |
155 |
return request; |
156 |
} |
157 |
|
158 |
- (NSArray *)sharingAccounts { |
159 |
if (sharingAccounts == nil) { |
160 |
sharingAccounts = [[NSMutableArray alloc] init]; |
161 |
|
162 |
NSXMLParser *parser = [[[NSXMLParser alloc] initWithData:[self responseData]] autorelease]; |
163 |
[parser setDelegate:self]; |
164 |
[parser setShouldProcessNamespaces:NO]; |
165 |
[parser setShouldReportNamespacePrefixes:NO]; |
166 |
[parser setShouldResolveExternalEntities:NO]; |
167 |
[parser parse]; |
168 |
} |
169 |
return sharingAccounts; |
170 |
} |
171 |
|
172 |
#pragma mark - |
173 |
#pragma mark NSXMLParserDelegate |
174 |
|
175 |
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { |
176 |
self.currentElement = elementName; |
177 |
if ([elementName isEqualToString:@"account"]) { |
178 |
self.currentAccount = [ASIPithosAccount account]; |
179 |
} |
180 |
|
181 |
self.currentContent = @""; |
182 |
} |
183 |
|
184 |
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { |
185 |
if ([elementName isEqualToString:@"name"]) { |
186 |
currentAccount.name = currentContent; |
187 |
} else if ([elementName isEqualToString:@"count"]) { |
188 |
currentAccount.containerCount = [currentContent integerValue]; |
189 |
} else if ([elementName isEqualToString:@"last_modified"]) { |
190 |
currentAccount.lastModified = [[self dateFormatterWithFormatId:0] dateFromString: |
191 |
[currentContent stringByReplacingOccurrencesOfString:@":" |
192 |
withString:@"" |
193 |
options:NSBackwardsSearch |
194 |
range:NSMakeRange(([currentContent length] - 3), 1)]]; |
195 |
} else if ([elementName isEqualToString:@"bytes"]) { |
196 |
currentAccount.bytesUsed = [currentContent integerValue]; |
197 |
} else if ([elementName isEqualToString:@"account"]) { |
198 |
[sharingAccounts addObject:currentAccount]; |
199 |
self.currentAccount = nil; |
200 |
} |
201 |
} |
202 |
|
203 |
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { |
204 |
self.currentContent = [currentContent stringByAppendingString:string]; |
205 |
} |
206 |
|
207 |
#pragma mark - |
208 |
#pragma mark Date Formatters |
209 |
|
210 |
// We store our date formatters in the calling thread's dictionary |
211 |
// NSDateFormatter is not thread-safe, this approach ensures each formatter is only used on a single thread |
212 |
// Each formatter can be reused many times in parsing a single response, so it would be expensive to keep creating new date formatters |
213 |
|
214 |
-(NSDateFormatter *)dateFormatterWithFormatId:(NSUInteger)formatId { |
215 |
NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary]; |
216 |
NSString *dateFormatterKey = [NSString stringWithFormat:@"ASIPithosDateFormatter%lu", formatId]; |
217 |
NSDateFormatter *dateFormatter = [threadDict objectForKey:dateFormatterKey]; |
218 |
if (dateFormatter == nil) { |
219 |
switch (formatId) { |
220 |
case 0: |
221 |
// date format: 2009-11-04T19:46:20.123456+0000 |
222 |
// Needed for last_modified |
223 |
dateFormatter = [[[NSDateFormatter alloc] init] autorelease]; |
224 |
[dateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"] autorelease]]; |
225 |
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ"]; |
226 |
[threadDict setObject:dateFormatter forKey:dateFormatterKey]; |
227 |
break; |
228 |
case 1: |
229 |
// date to format (RFC 1123): Sun, 06 Nov 1994 08:49:37 GMT |
230 |
// Needed for reading X-Account-Until-Timestamp, Last-Modified |
231 |
// and writing If-Modified-Since, If-Unmodified-Since in the request headers |
232 |
dateFormatter = [[[NSDateFormatter alloc] init] autorelease]; |
233 |
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]]; |
234 |
[dateFormatter setDateFormat:@"eee, dd MMM yyyy HH:mm:ss 'GMT'"]; |
235 |
[threadDict setObject:dateFormatter forKey:dateFormatterKey]; |
236 |
break; |
237 |
default: |
238 |
break; |
239 |
} |
240 |
} |
241 |
return dateFormatter; |
242 |
} |
243 |
|
244 |
#pragma mark - |
245 |
#pragma mark Extra Headers |
246 |
|
247 |
- (void)addRequestIfModifiedSinceHeader:(NSDate *)sinceTimestamp { |
248 |
if (sinceTimestamp) |
249 |
[self addRequestHeader:@"If-Modified-Since" value:[[self dateFormatterWithFormatId:1] stringFromDate:sinceTimestamp]]; |
250 |
} |
251 |
|
252 |
- (void)addRequestIfUnmodifiedSinceHeader:(NSDate *)sinceTimestamp { |
253 |
if (sinceTimestamp) |
254 |
[self addRequestHeader:@"If-Unmodified-Since" value:[[self dateFormatterWithFormatId:1] stringFromDate:sinceTimestamp]]; |
255 |
} |
256 |
|
257 |
- (void)addRequestIfMatchHeader:(NSString *)matchETag { |
258 |
if (matchETag) |
259 |
[self addRequestHeader:@"If-Match" value:matchETag]; |
260 |
} |
261 |
|
262 |
- (void)addRequestIfNoneMatchHeader:(NSString *)matchETag { |
263 |
if (matchETag) |
264 |
[self addRequestHeader:@"If-None-Match" value:matchETag]; |
265 |
} |
266 |
|
267 |
- (void)addRequestRangeHeader:(NSString *)rangeString { |
268 |
if (rangeString) |
269 |
[self addRequestHeader:@"Range" value:rangeString]; |
270 |
} |
271 |
|
272 |
- (void)addRequestRangeHeader:(NSString *)rangeString ifRangeETag:(NSString *)rangeETag { |
273 |
if (rangeString) { |
274 |
[self addRequestHeader:@"Range" value:rangeString]; |
275 |
if (rangeETag) |
276 |
[self addRequestHeader:@"If-Range" value:rangeETag]; |
277 |
} |
278 |
} |
279 |
|
280 |
- (void)addRequestRangeHeader:(NSString *)rangeString ifRangeTimestamp:(NSDate *)rangeTimestamp { |
281 |
if (rangeString) { |
282 |
[self addRequestHeader:@"Range" value:rangeString]; |
283 |
if (rangeTimestamp) |
284 |
[self addRequestHeader:@"If-Range" value:[[self dateFormatterWithFormatId:1] stringFromDate:rangeTimestamp]]; |
285 |
} |
286 |
} |
287 |
|
288 |
#pragma mark - |
289 |
#pragma mark Request User |
290 |
|
291 |
- (void)setRequestUserFromDefaultTo:(NSString *)newAuthUser withPithos:(ASIPithos *)pithos { |
292 |
NSString *urlString = [self.url description]; |
293 |
NSRange storageURLRange = [urlString rangeOfString:[pithos.storageURL description]]; |
294 |
if (storageURLRange.length && (storageURLRange.location == 0)) |
295 |
self.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", |
296 |
[pithos storageURLWithAuthUser:newAuthUser], |
297 |
[urlString substringFromIndex:storageURLRange.length]]]; |
298 |
} |
299 |
|
300 |
- (void)changeRequestUserFrom:(NSString *)oldAuthUser To:(NSString *)newAuthUser withPithos:(ASIPithos *)pithos { |
301 |
NSString *urlString = [self.url description]; |
302 |
NSRange storageURLRange = [urlString rangeOfString:[[pithos storageURLWithAuthUser:oldAuthUser] description]]; |
303 |
if (storageURLRange.length && (storageURLRange.location == 0)) |
304 |
self.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", |
305 |
[pithos storageURLWithAuthUser:newAuthUser], |
306 |
[urlString substringFromIndex:storageURLRange.length]]]; |
307 |
} |
308 |
|
309 |
#pragma mark - |
310 |
#pragma mark Internal |
311 |
|
312 |
- (NSMutableDictionary *)getHeadersDictionaryForPrefix:(NSString *)prefix { |
313 |
NSMutableDictionary *headersDictionary = [NSMutableDictionary dictionary]; |
314 |
for (NSString *key in [self responseHeaders]) { |
315 |
NSRange prefixRange = [key rangeOfString:prefix]; |
316 |
if (prefixRange.location == 0) { |
317 |
[headersDictionary setObject:[self decodeFromPercentEscape:[[self responseHeaders] objectForKey:key]] |
318 |
forKey:[[[self decodeFromPercentEscape:[key substringFromIndex:prefix.length]] |
319 |
stringByReplacingOccurrencesOfString:@"-" withString:@"_"] |
320 |
lowercaseString]]; |
321 |
} |
322 |
} |
323 |
return headersDictionary; |
324 |
} |
325 |
|
326 |
// http://cybersam.com/programming/proper-url-percent-encoding-in-ios |
327 |
+ (NSString *)encodeToPercentEscape:(NSString *)string { |
328 |
return [((NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, |
329 |
(CFStringRef)string, |
330 |
NULL, |
331 |
(CFStringRef)@"!*'();:@&=+$,/?%#[]", |
332 |
kCFStringEncodingUTF8)) autorelease]; |
333 |
} |
334 |
|
335 |
+ (NSString *)decodeFromPercentEscape:(NSString *)string { |
336 |
return [((NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL, |
337 |
(CFStringRef)string, |
338 |
(CFStringRef)@"", |
339 |
kCFStringEncodingUTF8)) autorelease]; |
340 |
} |
341 |
|
342 |
- (NSString *)encodeToPercentEscape:(NSString *)string { |
343 |
return [ASIPithosRequest encodeToPercentEscape:string]; |
344 |
} |
345 |
|
346 |
- (NSString *)decodeFromPercentEscape:(NSString *)string { |
347 |
return [ASIPithosRequest decodeFromPercentEscape:string]; |
348 |
} |
349 |
|
350 |
@end |