root / asi-http-request-with-pithos / Classes / Pithos / ASIPithosRequest.m @ 78355da3
History | View | Annotate | Download (14.7 kB)
1 |
// ASIPithosRequest.m |
---|---|
2 |
// Based on ASICloudFilesRequest.m |
3 |
// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest |
4 |
// |
5 |
// Copyright 2011 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 "ASIPithosAccount.h" |
40 |
|
41 |
static NSString *authURL = nil; |
42 |
static NSString *authUser = nil; |
43 |
static NSString *authKey = nil; |
44 |
|
45 |
static NSString *storageURLPrefix = nil; |
46 |
static NSString *storageURL = nil; |
47 |
static NSString *authToken = nil; |
48 |
|
49 |
static NSString *publicURLPrefix = nil; |
50 |
|
51 |
static NSRecursiveLock *accessDetailsLock = nil; |
52 |
|
53 |
@implementation ASIPithosRequest |
54 |
@synthesize currentElement, currentContent, currentAccount; |
55 |
|
56 |
+ (void)initialize { |
57 |
if (self == [ASIPithosRequest class]) { |
58 |
accessDetailsLock = [[NSRecursiveLock alloc] init]; |
59 |
} |
60 |
} |
61 |
|
62 |
#pragma mark - |
63 |
#pragma mark Constructors |
64 |
|
65 |
+ (id)authRequestWithMethod:(NSString *)method queryString:(NSString *)queryString useAuthToken:(BOOL)useAuthToken { |
66 |
NSString *urlString = [NSString stringWithString:authURL]; |
67 |
if (queryString) |
68 |
urlString = [urlString stringByAppendingString:queryString]; |
69 |
|
70 |
ASIPithosRequest *request = [self requestWithURL:[NSURL URLWithString:urlString]]; |
71 |
[request setRequestMethod:method]; |
72 |
if (useAuthToken) |
73 |
[request addRequestHeader:@"X-Auth-Token" value:authToken]; |
74 |
return request; |
75 |
} |
76 |
|
77 |
+ (id)authRequestWithMethod:(NSString *)method useAuthToken:(BOOL)useAuthToken { |
78 |
return [self authRequestWithMethod:method queryString:nil useAuthToken:useAuthToken]; |
79 |
} |
80 |
|
81 |
#pragma mark - |
82 |
#pragma mark Memory Management |
83 |
|
84 |
- (void)dealloc { |
85 |
[currentAccount release]; |
86 |
[currentContent release]; |
87 |
[currentElement release]; |
88 |
[sharingAccounts release]; |
89 |
[super dealloc]; |
90 |
} |
91 |
|
92 |
#pragma mark - |
93 |
#pragma mark Properties |
94 |
|
95 |
+ (NSString *)authURL { |
96 |
return authURL; |
97 |
} |
98 |
|
99 |
+ (void)setAuthURL:(NSString *)newAuthURL { |
100 |
[accessDetailsLock lock]; |
101 |
[authURL release]; |
102 |
authURL = [newAuthURL retain]; |
103 |
[accessDetailsLock unlock]; |
104 |
} |
105 |
|
106 |
+ (NSString *)authUser { |
107 |
return authUser; |
108 |
} |
109 |
|
110 |
+ (void)setAuthUser:(NSString *)newAuthUser { |
111 |
[accessDetailsLock lock]; |
112 |
[authUser release]; |
113 |
authUser = [newAuthUser retain]; |
114 |
[accessDetailsLock unlock]; |
115 |
} |
116 |
|
117 |
+ (NSString *)authKey { |
118 |
return authKey; |
119 |
} |
120 |
|
121 |
+ (void)setAuthKey:(NSString *)newAuthKey { |
122 |
[accessDetailsLock lock]; |
123 |
[authKey release]; |
124 |
authKey = [newAuthKey retain]; |
125 |
[accessDetailsLock unlock]; |
126 |
} |
127 |
|
128 |
+ (NSString *)storageURLPrefix { |
129 |
return storageURLPrefix; |
130 |
} |
131 |
|
132 |
+ (void)setStorageURLPrefix:(NSString *)newStorageURLPrefix { |
133 |
[accessDetailsLock lock]; |
134 |
[storageURLPrefix release]; |
135 |
storageURLPrefix = [newStorageURLPrefix retain]; |
136 |
[accessDetailsLock unlock]; |
137 |
} |
138 |
|
139 |
+ (NSString *)storageURL { |
140 |
if (storageURL) |
141 |
return storageURL; |
142 |
else if (storageURLPrefix && authUser) |
143 |
return [NSString stringWithFormat:@"%@/%@", storageURLPrefix, [authUser stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; |
144 |
return nil; |
145 |
} |
146 |
|
147 |
+ (void)setStorageURL:(NSString *)newStorageURL { |
148 |
[accessDetailsLock lock]; |
149 |
[storageURL release]; |
150 |
storageURL = [newStorageURL retain]; |
151 |
[accessDetailsLock unlock]; |
152 |
} |
153 |
|
154 |
+ (NSString *)authToken { |
155 |
return authToken; |
156 |
} |
157 |
|
158 |
+ (void)setAuthToken:(NSString *)newAuthToken { |
159 |
[accessDetailsLock lock]; |
160 |
[authToken release]; |
161 |
authToken = [newAuthToken retain]; |
162 |
[accessDetailsLock unlock]; |
163 |
} |
164 |
|
165 |
+ (NSString *)storageURLWithAuthUser:(NSString *)anAuthUser { |
166 |
if (storageURLPrefix) |
167 |
return [NSString stringWithFormat:@"%@/%@", storageURLPrefix, [anAuthUser stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; |
168 |
return nil; |
169 |
} |
170 |
|
171 |
+ (NSString *)publicURLPrefix { |
172 |
return publicURLPrefix; |
173 |
} |
174 |
|
175 |
+ (void)setPublicURLPrefix:(NSString *)newPublicURLPrefix { |
176 |
[accessDetailsLock lock]; |
177 |
[publicURLPrefix release]; |
178 |
publicURLPrefix = [newPublicURLPrefix retain]; |
179 |
[accessDetailsLock unlock]; |
180 |
} |
181 |
|
182 |
#pragma mark - |
183 |
#pragma mark Authentication |
184 |
|
185 |
+ (id)authenticationRequest { |
186 |
[accessDetailsLock lock]; |
187 |
ASIPithosRequest *request = [self authRequestWithMethod:@"GET" useAuthToken:NO]; |
188 |
[request addRequestHeader:@"X-Auth-User" value:authUser]; |
189 |
[request addRequestHeader:@"X-Auth-Key" value:authKey]; |
190 |
[accessDetailsLock unlock]; |
191 |
return request; |
192 |
} |
193 |
|
194 |
+ (NSError *)authenticate { |
195 |
[accessDetailsLock lock]; |
196 |
ASIPithosRequest *request = [self authenticationRequest]; |
197 |
[request startSynchronous]; |
198 |
|
199 |
if (![request error]) { |
200 |
NSDictionary *responseHeaders = [request responseHeaders]; |
201 |
[ASIPithosRequest setAuthToken:[responseHeaders objectForKey:@"X-Auth-Token"]]; |
202 |
[ASIPithosRequest setStorageURL:[responseHeaders objectForKey:@"X-Storage-Url"]]; |
203 |
} |
204 |
[accessDetailsLock unlock]; |
205 |
return [request error]; |
206 |
} |
207 |
|
208 |
#pragma mark - |
209 |
#pragma mark GET |
210 |
|
211 |
// GET authURL |
212 |
+ (id)listSharingAccountsRequest { |
213 |
return [self authRequestWithMethod:@"GET" queryString:@"?format=xml" useAuthToken:YES]; |
214 |
} |
215 |
|
216 |
// GET authURL?[limit=limit]&[marker=marker] |
217 |
+ (id)listSharingAccountsRequestWithLimit:(NSUInteger)limit marker:(NSString *)marker { |
218 |
NSString *queryString = @"?format=xml"; |
219 |
if (limit && (limit > 0)) |
220 |
queryString = [queryString stringByAppendingString:[NSString stringWithFormat:@"&limit=%lu", limit]]; |
221 |
if (marker) |
222 |
queryString = [queryString stringByAppendingString:[NSString stringWithFormat:@"&marker=%@", [marker stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]; |
223 |
|
224 |
return [self authRequestWithMethod:@"GET" queryString:queryString useAuthToken:YES]; |
225 |
} |
226 |
|
227 |
- (NSArray *)sharingAccounts { |
228 |
if (sharingAccounts == nil) { |
229 |
sharingAccounts = [[NSMutableArray alloc] init]; |
230 |
|
231 |
NSXMLParser *parser = [[[NSXMLParser alloc] initWithData:[self responseData]] autorelease]; |
232 |
[parser setDelegate:self]; |
233 |
[parser setShouldProcessNamespaces:NO]; |
234 |
[parser setShouldReportNamespacePrefixes:NO]; |
235 |
[parser setShouldResolveExternalEntities:NO]; |
236 |
[parser parse]; |
237 |
} |
238 |
return sharingAccounts; |
239 |
} |
240 |
|
241 |
#pragma mark - |
242 |
#pragma mark NSXMLParserDelegate |
243 |
|
244 |
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { |
245 |
self.currentElement = elementName; |
246 |
if ([elementName isEqualToString:@"account"]) { |
247 |
self.currentAccount = [ASIPithosAccount account]; |
248 |
} |
249 |
|
250 |
self.currentContent = @""; |
251 |
} |
252 |
|
253 |
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { |
254 |
if ([elementName isEqualToString:@"name"]) { |
255 |
currentAccount.name = currentContent; |
256 |
} else if ([elementName isEqualToString:@"count"]) { |
257 |
currentAccount.containerCount = [currentContent intValue]; |
258 |
} else if ([elementName isEqualToString:@"last_modified"]) { |
259 |
currentAccount.lastModified = [[self dateFormatterWithFormatId:0] dateFromString: |
260 |
[currentContent stringByReplacingOccurrencesOfString:@":" |
261 |
withString:@"" |
262 |
options:NSBackwardsSearch |
263 |
range:NSMakeRange(([currentContent length] - 3), 1)]]; |
264 |
} else if ([elementName isEqualToString:@"bytes"]) { |
265 |
currentAccount.bytesUsed = [currentContent intValue]; |
266 |
} else if ([elementName isEqualToString:@"account"]) { |
267 |
[sharingAccounts addObject:currentAccount]; |
268 |
self.currentAccount = nil; |
269 |
} |
270 |
} |
271 |
|
272 |
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { |
273 |
self.currentContent = [currentContent stringByAppendingString:string]; |
274 |
} |
275 |
|
276 |
#pragma mark - |
277 |
#pragma mark Date Formatters |
278 |
|
279 |
// We store our date formatters in the calling thread's dictionary |
280 |
// NSDateFormatter is not thread-safe, this approach ensures each formatter is only used on a single thread |
281 |
// Each formatter can be reused many times in parsing a single response, so it would be expensive to keep creating new date formatters |
282 |
|
283 |
-(NSDateFormatter *)dateFormatterWithFormatId:(NSUInteger)formatId { |
284 |
NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary]; |
285 |
NSString *dateFormatterKey = [NSString stringWithFormat:@"ASIPithosDateFormatter%lu", formatId]; |
286 |
NSDateFormatter *dateFormatter = [threadDict objectForKey:dateFormatterKey]; |
287 |
if (dateFormatter == nil) { |
288 |
switch (formatId) { |
289 |
case 0: |
290 |
// date format: 2009-11-04T19:46:20.123456+0000 |
291 |
// Needed for last_modified |
292 |
dateFormatter = [[[NSDateFormatter alloc] init] autorelease]; |
293 |
[dateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"] autorelease]]; |
294 |
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ"]; |
295 |
[threadDict setObject:dateFormatter forKey:dateFormatterKey]; |
296 |
break; |
297 |
case 1: |
298 |
// date format: Wed, 20 Jul 2011 13:39:19 GMT |
299 |
// Needed for reading X-Account-Until-Timestamp, Last-Modified |
300 |
dateFormatter = [[[NSDateFormatter alloc] init] autorelease]; |
301 |
[dateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"] autorelease]]; |
302 |
[dateFormatter setDateFormat:@"eee, dd MMM yyyy HH:mm:ss zzz"]; |
303 |
[threadDict setObject:dateFormatter forKey:dateFormatterKey]; |
304 |
break; |
305 |
case 2: |
306 |
// date to format: Thu Jul 21 10:54:45 2010 |
307 |
// Can be used to write If-Modified-Since, If-Unmodified-Since in the request headers |
308 |
dateFormatter = [[[NSDateFormatter alloc] init] autorelease]; |
309 |
[dateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"] autorelease]]; |
310 |
[dateFormatter setDateFormat:@"eee MMM dd HH:mm:ss yyyy"]; |
311 |
[threadDict setObject:dateFormatter forKey:dateFormatterKey]; |
312 |
break; |
313 |
default: |
314 |
break; |
315 |
} |
316 |
} |
317 |
return dateFormatter; |
318 |
} |
319 |
|
320 |
#pragma mark - |
321 |
#pragma mark Extra Headers |
322 |
|
323 |
- (void)addRequestIfModifiedSinceHeader:(NSDate *)sinceTimestamp { |
324 |
if (sinceTimestamp) |
325 |
[self addRequestHeader:@"If-Modified-Since" value:[[self dateFormatterWithFormatId:2] stringFromDate:sinceTimestamp]]; |
326 |
} |
327 |
|
328 |
- (void)addRequestIfUnmodifiedSinceHeader:(NSDate *)sinceTimestamp { |
329 |
if (sinceTimestamp) |
330 |
[self addRequestHeader:@"If-Unmodified-Since" value:[[self dateFormatterWithFormatId:2] stringFromDate:sinceTimestamp]]; |
331 |
} |
332 |
|
333 |
- (void)addRequestIfMatchHeader:(NSString *)matchETag { |
334 |
if (matchETag) |
335 |
[self addRequestHeader:@"If-Match" value:matchETag]; |
336 |
} |
337 |
|
338 |
- (void)addRequestIfNoneMatchHeader:(NSString *)matchETag { |
339 |
if (matchETag) |
340 |
[self addRequestHeader:@"If-None-Match" value:matchETag]; |
341 |
} |
342 |
|
343 |
- (void)addRequestRangeHeader:(NSString *)rangeString { |
344 |
if (rangeString) |
345 |
[self addRequestHeader:@"Range" value:rangeString]; |
346 |
} |
347 |
|
348 |
- (void)addRequestRangeHeader:(NSString *)rangeString ifRangeETag:(NSString *)rangeETag { |
349 |
if (rangeString) { |
350 |
[self addRequestHeader:@"Range" value:rangeString]; |
351 |
if (rangeETag) |
352 |
[self addRequestHeader:@"If-Range" value:rangeETag]; |
353 |
} |
354 |
} |
355 |
|
356 |
- (void)addRequestRangeHeader:(NSString *)rangeString ifRangeTimestamp:(NSDate *)rangeTimestamp { |
357 |
if (rangeString) { |
358 |
[self addRequestHeader:@"Range" value:rangeString]; |
359 |
if (rangeTimestamp) |
360 |
[self addRequestHeader:@"If-Range" value:[[self dateFormatterWithFormatId:2] stringFromDate:rangeTimestamp]]; |
361 |
} |
362 |
} |
363 |
|
364 |
#pragma mark - |
365 |
#pragma mark Request User |
366 |
|
367 |
- (void)setRequestUserFromDefaultTo:(NSString *)newAuthUser { |
368 |
NSString *urlString = [self.url description]; |
369 |
NSRange storageURLRange = [urlString rangeOfString:[ASIPithosRequest storageURL]]; |
370 |
if (storageURLRange.length && (storageURLRange.location == 0)) |
371 |
self.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", |
372 |
[ASIPithosRequest storageURLWithAuthUser:newAuthUser], |
373 |
[urlString substringFromIndex:storageURLRange.length]]]; |
374 |
} |
375 |
|
376 |
- (void)changeRequestUserFrom:(NSString *)oldAuthUser To:(NSString *)newAuthUser { |
377 |
NSString *urlString = [self.url description]; |
378 |
NSRange storageURLRange = [urlString rangeOfString:[ASIPithosRequest storageURLWithAuthUser:oldAuthUser]]; |
379 |
if (storageURLRange.length && (storageURLRange.location == 0)) |
380 |
self.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", |
381 |
[ASIPithosRequest storageURLWithAuthUser:newAuthUser], |
382 |
[urlString substringFromIndex:storageURLRange.length]]]; |
383 |
} |
384 |
|
385 |
#pragma mark - |
386 |
#pragma mark Internal |
387 |
|
388 |
- (NSMutableDictionary *)getHeadersDictionaryForPrefix:(NSString *)prefix { |
389 |
NSMutableDictionary *headersDictionary = [NSMutableDictionary dictionary]; |
390 |
for (NSString *key in [[self responseHeaders] keyEnumerator]) { |
391 |
NSRange prefixRange = [key rangeOfString:prefix]; |
392 |
if (prefixRange.location == 0) { |
393 |
[headersDictionary setObject:[[self responseHeaders] objectForKey:key] |
394 |
forKey:[[[key substringFromIndex:prefix.length] |
395 |
stringByReplacingOccurrencesOfString:@"-" withString:@"_"] |
396 |
lowercaseString]]; |
397 |
} |
398 |
} |
399 |
return headersDictionary; |
400 |
} |
401 |
|
402 |
@end |