Statistics
| Branch: | Revision:

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