Statistics
| Branch: | Revision:

root / asi-http-request-with-pithos / Classes / Pithos / ASIPithosRequest.m @ 7c60da08

History | View | Annotate | Download (13.4 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

    
42
@implementation ASIPithosRequest
43
@synthesize currentElement, currentContent, currentAccount;
44

    
45
#pragma mark -
46
#pragma mark Constructors
47

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

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

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

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

    
75
#pragma mark -
76
#pragma mark Authentication
77

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

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

    
102
#pragma mark -
103
#pragma mark GET
104

    
105
// GET authURL
106
+ (id)listSharingAccountsRequestWithPithos:(ASIPithos *)pithos {
107
    return [self authRequestWithMethod:@"GET" pithos:pithos queryString:@"?format=xml" useAuthToken:YES];
108
}
109

    
110
// GET authURL?[limit=limit]&[marker=marker]
111
+ (id)listSharingAccountsRequestWithPithos:(ASIPithos *)pithos limit:(NSUInteger)limit marker:(NSString *)marker {
112
    NSString *queryString = @"?format=xml";
113
	if (limit && (limit > 0))
114
		queryString = [queryString stringByAppendingString:[NSString stringWithFormat:@"&limit=%lu", limit]];
115
	if (marker)
116
		queryString = [queryString stringByAppendingString:[NSString stringWithFormat:@"&marker=%@", [self encodeToPercentEscape:marker]]];
117
    
118
    return [self authRequestWithMethod:@"GET" pithos:pithos queryString:queryString useAuthToken:YES];
119
}
120

    
121
- (NSArray *)sharingAccounts {
122
    if (sharingAccounts == nil) {
123
        sharingAccounts = [[NSMutableArray alloc] init];
124
        
125
        NSXMLParser *parser = [[[NSXMLParser alloc] initWithData:[self responseData]] autorelease];
126
        [parser setDelegate:self];
127
        [parser setShouldProcessNamespaces:NO];
128
        [parser setShouldReportNamespacePrefixes:NO];
129
        [parser setShouldResolveExternalEntities:NO];
130
        [parser parse];
131
    }    
132
	return sharingAccounts;
133
}
134

    
135
#pragma mark -
136
#pragma mark NSXMLParserDelegate
137

    
138
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
139
	self.currentElement = elementName;
140
	if ([elementName isEqualToString:@"account"]) {
141
		self.currentAccount = [ASIPithosAccount account];
142
	}
143
    
144
	self.currentContent = @"";
145
}
146

    
147
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
148
	if ([elementName isEqualToString:@"name"]) {
149
        currentAccount.name = currentContent;
150
	} else if ([elementName isEqualToString:@"count"]) {
151
        currentAccount.containerCount = [currentContent integerValue];
152
    } else if ([elementName isEqualToString:@"last_modified"]) {
153
        currentAccount.lastModified = [[self dateFormatterWithFormatId:0] dateFromString:
154
                                       [currentContent stringByReplacingOccurrencesOfString:@":" 
155
                                                                                 withString:@"" 
156
                                                                                    options:NSBackwardsSearch 
157
                                                                                      range:NSMakeRange(([currentContent length] - 3), 1)]];
158
	} else if ([elementName isEqualToString:@"bytes"]) {
159
        currentAccount.bytesUsed = [currentContent integerValue];
160
	} else if ([elementName isEqualToString:@"account"]) {
161
		[sharingAccounts addObject:currentAccount];
162
        self.currentAccount = nil;
163
	}
164
}
165

    
166
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
167
    self.currentContent = [currentContent stringByAppendingString:string];
168
}
169

    
170
#pragma mark -
171
#pragma mark Date Formatters
172

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

    
177
-(NSDateFormatter *)dateFormatterWithFormatId:(NSUInteger)formatId {
178
    NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary];
179
    NSString *dateFormatterKey = [NSString stringWithFormat:@"ASIPithosDateFormatter%lu", formatId];
180
    NSDateFormatter *dateFormatter = [threadDict objectForKey:dateFormatterKey];
181
	if (dateFormatter == nil) {
182
        switch (formatId) {
183
            case 0:
184
                // date format: 2009-11-04T19:46:20.123456+0000
185
                // Needed for last_modified
186
                dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
187
                [dateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"] autorelease]];
188
                [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ"];
189
                [threadDict setObject:dateFormatter forKey:dateFormatterKey];
190
                break;
191
            case 1:
192
                // date to format (RFC 1123): Sun, 06 Nov 1994 08:49:37 GMT
193
                // Needed for reading X-Account-Until-Timestamp, Last-Modified
194
                // and writing If-Modified-Since, If-Unmodified-Since in the request headers
195
                dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
196
                [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]];
197
                [dateFormatter setDateFormat:@"eee, dd MMM yyyy HH:mm:ss 'GMT'"];
198
                [threadDict setObject:dateFormatter forKey:dateFormatterKey];
199
                break;
200
            default:
201
                break;
202
        }
203
    }
204
    return dateFormatter;
205
}
206

    
207
#pragma mark -
208
#pragma mark Extra Headers
209

    
210
- (void)addRequestIfModifiedSinceHeader:(NSDate *)sinceTimestamp {
211
    if (sinceTimestamp)
212
        [self addRequestHeader:@"If-Modified-Since" value:[[self dateFormatterWithFormatId:1] stringFromDate:sinceTimestamp]];        
213
}
214

    
215
- (void)addRequestIfUnmodifiedSinceHeader:(NSDate *)sinceTimestamp {
216
    if (sinceTimestamp)
217
        [self addRequestHeader:@"If-Unmodified-Since" value:[[self dateFormatterWithFormatId:1] stringFromDate:sinceTimestamp]];
218
}
219

    
220
- (void)addRequestIfMatchHeader:(NSString *)matchETag {
221
    if (matchETag)
222
        [self addRequestHeader:@"If-Match" value:matchETag];
223
}
224

    
225
- (void)addRequestIfNoneMatchHeader:(NSString *)matchETag {
226
    if (matchETag)
227
        [self addRequestHeader:@"If-None-Match" value:matchETag];
228
}
229

    
230
- (void)addRequestRangeHeader:(NSString *)rangeString {
231
    if (rangeString)
232
        [self addRequestHeader:@"Range" value:rangeString];    
233
}
234

    
235
- (void)addRequestRangeHeader:(NSString *)rangeString ifRangeETag:(NSString *)rangeETag {
236
    if (rangeString) {
237
        [self addRequestHeader:@"Range" value:rangeString];
238
        if (rangeETag)
239
            [self addRequestHeader:@"If-Range" value:rangeETag];
240
    }
241
}
242

    
243
- (void)addRequestRangeHeader:(NSString *)rangeString ifRangeTimestamp:(NSDate *)rangeTimestamp {
244
    if (rangeString) {
245
        [self addRequestHeader:@"Range" value:rangeString];
246
        if (rangeTimestamp)
247
            [self addRequestHeader:@"If-Range" value:[[self dateFormatterWithFormatId:1] stringFromDate:rangeTimestamp]];
248
    }
249
}
250

    
251
#pragma mark -
252
#pragma mark Request User
253

    
254
- (void)setRequestUserFromDefaultTo:(NSString *)newAuthUser withPithos:(ASIPithos *)pithos {
255
    NSString *urlString = [self.url description];
256
    NSRange storageURLRange = [urlString rangeOfString:pithos.storageURL];
257
    if (storageURLRange.length && (storageURLRange.location == 0))
258
        self.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", 
259
                                         [pithos storageURLWithAuthUser:newAuthUser], 
260
                                         [urlString substringFromIndex:storageURLRange.length]]];
261
}
262

    
263
- (void)changeRequestUserFrom:(NSString *)oldAuthUser To:(NSString *)newAuthUser withPithos:(ASIPithos *)pithos {
264
    NSString *urlString = [self.url description];
265
    NSRange storageURLRange = [urlString rangeOfString:[pithos storageURLWithAuthUser:oldAuthUser]];
266
    if (storageURLRange.length && (storageURLRange.location == 0))
267
        self.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", 
268
                                         [pithos storageURLWithAuthUser:newAuthUser], 
269
                                         [urlString substringFromIndex:storageURLRange.length]]];
270
}
271

    
272
#pragma mark -
273
#pragma mark Internal
274

    
275
- (NSMutableDictionary *)getHeadersDictionaryForPrefix:(NSString *)prefix {
276
    NSMutableDictionary *headersDictionary = [NSMutableDictionary dictionary];
277
    for (NSString *key in [[self responseHeaders] keyEnumerator]) {
278
        NSRange prefixRange = [key rangeOfString:prefix];
279
        if (prefixRange.location == 0) {
280
            [headersDictionary setObject:[self decodeFromPercentEscape:[[self responseHeaders] objectForKey:key]] 
281
                                  forKey:[[[self decodeFromPercentEscape:[key substringFromIndex:prefix.length]] 
282
                                           stringByReplacingOccurrencesOfString:@"-" withString:@"_"] 
283
                                          lowercaseString]];
284
        }
285
    }
286
    return headersDictionary;
287
}
288

    
289
// http://cybersam.com/programming/proper-url-percent-encoding-in-ios
290
+ (NSString *)encodeToPercentEscape:(NSString *)string {
291
    return [((NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, 
292
                                                                 (CFStringRef)string, 
293
                                                                 NULL, 
294
                                                                 (CFStringRef)@"!*'();:@&=+$,/?%#[]", 
295
                                                                 kCFStringEncodingUTF8)) autorelease];
296
}
297

    
298
+ (NSString *)decodeFromPercentEscape:(NSString *)string {
299
    return [((NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL, 
300
                                                                                 (CFStringRef)string,
301
                                                                                 (CFStringRef)@"", 
302
                                                                                 kCFStringEncodingUTF8)) autorelease];
303
}
304

    
305
- (NSString *)encodeToPercentEscape:(NSString *)string {
306
    return [ASIPithosRequest encodeToPercentEscape:string];
307
}
308

    
309
- (NSString *)decodeFromPercentEscape:(NSString *)string {
310
    return [ASIPithosRequest decodeFromPercentEscape:string];
311
}
312

    
313
@end