Statistics
| Branch: | Revision:

root / asi-http-request-with-pithos / Classes / Pithos / ASIPithosRequest.m @ 0be02d23

History | View | Annotate | Download (15.2 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 Constructors
48

    
49
+ (id)authRequestWithMethod:(NSString *)method pithos:(ASIPithos *)pithos queryString:(NSString *)queryString useAuthToken:(BOOL)useAuthToken {
50
    NSString *urlString = [NSString stringWithString:pithos.authURL];
51
    if (queryString)
52
        urlString = [urlString stringByAppendingString:queryString];
53
    
54
    ASIPithosRequest *request = [self requestWithURL:[NSURL URLWithString:urlString]];
55
	[request setRequestMethod:method];
56
    if (useAuthToken)
57
        [request addRequestHeader:@"X-Auth-Token" value:pithos.authToken];
58
	return request;
59
}
60

    
61
+ (id)authRequestWithMethod:(NSString *)method pithos:(ASIPithos *)pithos useAuthToken:(BOOL)useAuthToken {
62
	return [self authRequestWithMethod:method pithos:pithos queryString:nil useAuthToken:useAuthToken];
63
}
64

    
65
#pragma mark -
66
#pragma mark Memory Management
67

    
68
- (void)dealloc {
69
	[currentAccount release];
70
    [currentContent release];
71
    [currentElement release];
72
    [sharingAccounts release];
73
	[super dealloc];
74
}
75

    
76
#pragma mark -
77
#pragma mark Authentication
78

    
79
+ (id)authenticationRequestWithPithos:(ASIPithos *)pithos {
80
    ASIPithosRequest *request = [self authRequestWithMethod:@"GET" pithos:pithos useAuthToken:NO];
81
	[request addRequestHeader:@"X-Auth-User" value:pithos.authUser];
82
	[request addRequestHeader:@"X-Auth-Key" value:pithos.authKey];
83
	return request;
84
}
85

    
86
+ (ASIPithos *)authenticateWithPithos:(ASIPithos *)pithos error:(NSError **)error {
87
	ASIPithosRequest *request = [self authenticationRequestWithPithos:pithos];
88
	[request startSynchronous];
89
	// XXX start asynchronous with polling loop?
90
    
91
    if (error != NULL) {
92
        *error = [request error];
93
        if (!*error) {
94
            NSDictionary *responseHeaders = [request responseHeaders];
95
            pithos.authToken = [responseHeaders objectForKey:@"X-Auth-Token"];
96
            pithos.storageURL = [responseHeaders objectForKey:@"X-Storage-Url"];
97
            return pithos;
98
        }
99
    }
100
	return nil;
101
}
102

    
103
#pragma mark -
104
#pragma mark User Catalog
105

    
106
// POST userCatalogURL
107
+ (id)userCatalogRequestWithPithos:(ASIPithos *)pithos displaynames:(NSArray *)displaynames UUIDs:(NSArray *)UUIDs {
108
    NSString *urlString = [NSString stringWithString:pithos.userCatalogURL];
109
    
110
    ASIPithosRequest *request = [self requestWithURL:[NSURL URLWithString:urlString]];
111
	[request setRequestMethod:@"POST"];
112
    [request addRequestHeader:@"X-Auth-Token" value:pithos.authToken];
113
    [request addRequestHeader:@"Content-Type" value:@"application/json"];
114
    
115
    NSMutableString *dataString = [NSMutableString stringWithString:@"{\"displaynames\":["];
116
    if (displaynames) {
117
        for (NSUInteger index = 0 ; index < displaynames.count ; index++) {
118
            [dataString appendFormat:@"\"%@\"%@",
119
             [[[displaynames objectAtIndex:index] stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"]
120
              stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""],
121
             ((index == displaynames.count - 1) ? @"" : @",")];
122
        }
123
    }
124
    [dataString appendFormat:@"],\"uuids\":["];
125
    if (UUIDs) {
126
        for (NSUInteger index = 0 ; index < UUIDs.count ; index++) {
127
            [dataString appendFormat:@"\"%@\"%@", [UUIDs objectAtIndex:index], ((index == UUIDs.count - 1) ? @"" : @",")];
128
        }
129
    }
130
    [dataString appendFormat:@"]}"];
131
    [request appendPostData:[dataString dataUsingEncoding:NSUTF8StringEncoding]];
132
    
133
	return request;
134
}
135

    
136
- (NSDictionary *)catalogs {
137
    SBJsonParser *parser = [[SBJsonParser alloc] init];
138
    NSDictionary *catalogs = [parser objectWithString:[self responseString]];
139
    return catalogs;
140
}
141

    
142
- (NSDictionary *)displaynameCatalog {
143
    return [[self catalogs] objectForKey:@"displayname_catalog"];
144
}
145

    
146
- (NSDictionary *)UUIDCatalog {
147
    return [[self catalogs] objectForKey:@"uuid_catalog"];
148
}
149

    
150
#pragma mark -
151
#pragma mark GET
152

    
153
// GET authURL
154
+ (id)listSharingAccountsRequestWithPithos:(ASIPithos *)pithos {
155
    return [self authRequestWithMethod:@"GET" pithos:pithos queryString:@"?format=xml" useAuthToken:YES];
156
}
157

    
158
// GET authURL?[limit=limit]&[marker=marker]
159
+ (id)listSharingAccountsRequestWithPithos:(ASIPithos *)pithos limit:(NSUInteger)limit marker:(NSString *)marker {
160
    NSString *queryString = @"?format=xml";
161
	if (limit && (limit > 0))
162
		queryString = [queryString stringByAppendingString:[NSString stringWithFormat:@"&limit=%lu", limit]];
163
	if (marker)
164
		queryString = [queryString stringByAppendingString:[NSString stringWithFormat:@"&marker=%@", [self encodeToPercentEscape:marker]]];
165
    
166
    return [self authRequestWithMethod:@"GET" pithos:pithos queryString:queryString useAuthToken:YES];
167
}
168

    
169
- (NSArray *)sharingAccounts {
170
    if (sharingAccounts == nil) {
171
        sharingAccounts = [[NSMutableArray alloc] init];
172
        
173
        NSXMLParser *parser = [[[NSXMLParser alloc] initWithData:[self responseData]] autorelease];
174
        [parser setDelegate:self];
175
        [parser setShouldProcessNamespaces:NO];
176
        [parser setShouldReportNamespacePrefixes:NO];
177
        [parser setShouldResolveExternalEntities:NO];
178
        [parser parse];
179
    }    
180
	return sharingAccounts;
181
}
182

    
183
#pragma mark -
184
#pragma mark NSXMLParserDelegate
185

    
186
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
187
	self.currentElement = elementName;
188
	if ([elementName isEqualToString:@"account"]) {
189
		self.currentAccount = [ASIPithosAccount account];
190
	}
191
    
192
	self.currentContent = @"";
193
}
194

    
195
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
196
	if ([elementName isEqualToString:@"name"]) {
197
        currentAccount.name = currentContent;
198
	} else if ([elementName isEqualToString:@"count"]) {
199
        currentAccount.containerCount = [currentContent integerValue];
200
    } else if ([elementName isEqualToString:@"last_modified"]) {
201
        currentAccount.lastModified = [[self dateFormatterWithFormatId:0] dateFromString:
202
                                       [currentContent stringByReplacingOccurrencesOfString:@":" 
203
                                                                                 withString:@"" 
204
                                                                                    options:NSBackwardsSearch 
205
                                                                                      range:NSMakeRange(([currentContent length] - 3), 1)]];
206
	} else if ([elementName isEqualToString:@"bytes"]) {
207
        currentAccount.bytesUsed = [currentContent integerValue];
208
	} else if ([elementName isEqualToString:@"account"]) {
209
		[sharingAccounts addObject:currentAccount];
210
        self.currentAccount = nil;
211
	}
212
}
213

    
214
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
215
    self.currentContent = [currentContent stringByAppendingString:string];
216
}
217

    
218
#pragma mark -
219
#pragma mark Date Formatters
220

    
221
// We store our date formatters in the calling thread's dictionary
222
// NSDateFormatter is not thread-safe, this approach ensures each formatter is only used on a single thread
223
// Each formatter can be reused many times in parsing a single response, so it would be expensive to keep creating new date formatters
224

    
225
-(NSDateFormatter *)dateFormatterWithFormatId:(NSUInteger)formatId {
226
    NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary];
227
    NSString *dateFormatterKey = [NSString stringWithFormat:@"ASIPithosDateFormatter%lu", formatId];
228
    NSDateFormatter *dateFormatter = [threadDict objectForKey:dateFormatterKey];
229
	if (dateFormatter == nil) {
230
        switch (formatId) {
231
            case 0:
232
                // date format: 2009-11-04T19:46:20.123456+0000
233
                // Needed for last_modified
234
                dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
235
                [dateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"] autorelease]];
236
                [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ"];
237
                [threadDict setObject:dateFormatter forKey:dateFormatterKey];
238
                break;
239
            case 1:
240
                // date to format (RFC 1123): Sun, 06 Nov 1994 08:49:37 GMT
241
                // Needed for reading X-Account-Until-Timestamp, Last-Modified
242
                // and writing If-Modified-Since, If-Unmodified-Since in the request headers
243
                dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
244
                [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]];
245
                [dateFormatter setDateFormat:@"eee, dd MMM yyyy HH:mm:ss 'GMT'"];
246
                [threadDict setObject:dateFormatter forKey:dateFormatterKey];
247
                break;
248
            default:
249
                break;
250
        }
251
    }
252
    return dateFormatter;
253
}
254

    
255
#pragma mark -
256
#pragma mark Extra Headers
257

    
258
- (void)addRequestIfModifiedSinceHeader:(NSDate *)sinceTimestamp {
259
    if (sinceTimestamp)
260
        [self addRequestHeader:@"If-Modified-Since" value:[[self dateFormatterWithFormatId:1] stringFromDate:sinceTimestamp]];        
261
}
262

    
263
- (void)addRequestIfUnmodifiedSinceHeader:(NSDate *)sinceTimestamp {
264
    if (sinceTimestamp)
265
        [self addRequestHeader:@"If-Unmodified-Since" value:[[self dateFormatterWithFormatId:1] stringFromDate:sinceTimestamp]];
266
}
267

    
268
- (void)addRequestIfMatchHeader:(NSString *)matchETag {
269
    if (matchETag)
270
        [self addRequestHeader:@"If-Match" value:matchETag];
271
}
272

    
273
- (void)addRequestIfNoneMatchHeader:(NSString *)matchETag {
274
    if (matchETag)
275
        [self addRequestHeader:@"If-None-Match" value:matchETag];
276
}
277

    
278
- (void)addRequestRangeHeader:(NSString *)rangeString {
279
    if (rangeString)
280
        [self addRequestHeader:@"Range" value:rangeString];    
281
}
282

    
283
- (void)addRequestRangeHeader:(NSString *)rangeString ifRangeETag:(NSString *)rangeETag {
284
    if (rangeString) {
285
        [self addRequestHeader:@"Range" value:rangeString];
286
        if (rangeETag)
287
            [self addRequestHeader:@"If-Range" value:rangeETag];
288
    }
289
}
290

    
291
- (void)addRequestRangeHeader:(NSString *)rangeString ifRangeTimestamp:(NSDate *)rangeTimestamp {
292
    if (rangeString) {
293
        [self addRequestHeader:@"Range" value:rangeString];
294
        if (rangeTimestamp)
295
            [self addRequestHeader:@"If-Range" value:[[self dateFormatterWithFormatId:1] stringFromDate:rangeTimestamp]];
296
    }
297
}
298

    
299
#pragma mark -
300
#pragma mark Request User
301

    
302
- (void)setRequestUserFromDefaultTo:(NSString *)newAuthUser withPithos:(ASIPithos *)pithos {
303
    NSString *urlString = [self.url description];
304
    NSRange storageURLRange = [urlString rangeOfString:pithos.storageURL];
305
    if (storageURLRange.length && (storageURLRange.location == 0))
306
        self.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", 
307
                                         [pithos storageURLWithAuthUser:newAuthUser], 
308
                                         [urlString substringFromIndex:storageURLRange.length]]];
309
}
310

    
311
- (void)changeRequestUserFrom:(NSString *)oldAuthUser To:(NSString *)newAuthUser withPithos:(ASIPithos *)pithos {
312
    NSString *urlString = [self.url description];
313
    NSRange storageURLRange = [urlString rangeOfString:[pithos storageURLWithAuthUser:oldAuthUser]];
314
    if (storageURLRange.length && (storageURLRange.location == 0))
315
        self.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", 
316
                                         [pithos storageURLWithAuthUser:newAuthUser], 
317
                                         [urlString substringFromIndex:storageURLRange.length]]];
318
}
319

    
320
#pragma mark -
321
#pragma mark Internal
322

    
323
- (NSMutableDictionary *)getHeadersDictionaryForPrefix:(NSString *)prefix {
324
    NSMutableDictionary *headersDictionary = [NSMutableDictionary dictionary];
325
    for (NSString *key in [self responseHeaders]) {
326
        NSRange prefixRange = [key rangeOfString:prefix];
327
        if (prefixRange.location == 0) {
328
            [headersDictionary setObject:[self decodeFromPercentEscape:[[self responseHeaders] objectForKey:key]] 
329
                                  forKey:[[[self decodeFromPercentEscape:[key substringFromIndex:prefix.length]] 
330
                                           stringByReplacingOccurrencesOfString:@"-" withString:@"_"] 
331
                                          lowercaseString]];
332
        }
333
    }
334
    return headersDictionary;
335
}
336

    
337
// http://cybersam.com/programming/proper-url-percent-encoding-in-ios
338
+ (NSString *)encodeToPercentEscape:(NSString *)string {
339
    return [((NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, 
340
                                                                 (CFStringRef)string, 
341
                                                                 NULL, 
342
                                                                 (CFStringRef)@"!*'();:@&=+$,/?%#[]", 
343
                                                                 kCFStringEncodingUTF8)) autorelease];
344
}
345

    
346
+ (NSString *)decodeFromPercentEscape:(NSString *)string {
347
    return [((NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL, 
348
                                                                                 (CFStringRef)string,
349
                                                                                 (CFStringRef)@"", 
350
                                                                                 kCFStringEncodingUTF8)) autorelease];
351
}
352

    
353
- (NSString *)encodeToPercentEscape:(NSString *)string {
354
    return [ASIPithosRequest encodeToPercentEscape:string];
355
}
356

    
357
- (NSString *)decodeFromPercentEscape:(NSString *)string {
358
    return [ASIPithosRequest decodeFromPercentEscape:string];
359
}
360

    
361
@end