Statistics
| Branch: | Revision:

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

History | View | Annotate | Download (13.3 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
    *error = [request error];
91
	if (!*error) {
92
		NSDictionary *responseHeaders = [request responseHeaders];
93
        pithos.authToken = [responseHeaders objectForKey:@"X-Auth-Token"];
94
        pithos.storageURL = [responseHeaders objectForKey:@"X-Storage-Url"];
95
        return pithos;
96
	}    
97
	return nil;
98
}
99

    
100
#pragma mark -
101
#pragma mark GET
102

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

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

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

    
133
#pragma mark -
134
#pragma mark NSXMLParserDelegate
135

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

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

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

    
168
#pragma mark -
169
#pragma mark Date Formatters
170

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

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

    
205
#pragma mark -
206
#pragma mark Extra Headers
207

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

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

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

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

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

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

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

    
249
#pragma mark -
250
#pragma mark Request User
251

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

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

    
270
#pragma mark -
271
#pragma mark Internal
272

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

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

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

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

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

    
311
@end