mirror of https://github.com/axmolengine/axmol.git
297 lines
13 KiB
Objective-C
Executable File
297 lines
13 KiB
Objective-C
Executable File
//
|
|
// OAMutableURLRequest.m
|
|
// OAuthConsumer
|
|
//
|
|
// Created by Jon Crosby on 10/19/07.
|
|
// Copyright 2007 Kaboomerang LLC. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
|
|
#import "OAMutableURLRequest.h"
|
|
#import "OARequestParameter.h"
|
|
#import "OAServiceTicket.h"
|
|
|
|
@interface OAMutableURLRequest ()
|
|
- (void)_generateTimestamp;
|
|
- (void)_generateNonce;
|
|
- (NSString *)_signatureBaseString;
|
|
|
|
@property (nonatomic, retain) OAConsumer *consumer;
|
|
@property (nonatomic, retain) OAToken *token;
|
|
@property (nonatomic, retain) NSString *realm;
|
|
@property (nonatomic, retain) id <OASignatureProviding> signatureProvider;
|
|
@property (nonatomic, retain) NSMutableDictionary *extraOAuthParameters;
|
|
|
|
@end
|
|
|
|
@interface NSURL (OABaseAdditions)
|
|
- (NSString *)URLStringWithoutQuery;
|
|
@end
|
|
|
|
@implementation NSURL (OABaseAdditions)
|
|
|
|
- (NSString *)URLStringWithoutQuery {
|
|
if (self.absoluteString.length == 0) {
|
|
return nil;
|
|
}
|
|
|
|
NSArray *parts = [self.absoluteString componentsSeparatedByString:@"?"];
|
|
return (parts.count == 0)?nil:[parts objectAtIndex:0];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation OAMutableURLRequest
|
|
|
|
+ (void)fetchDataForRequest:(OAMutableURLRequest *)request withCompletionHandler:(void(^)(OAServiceTicket *, NSData *, NSError *))block {
|
|
[request prepare];
|
|
|
|
[NSURLConnection sendAsynchronousRequest:request queue:[[[NSOperationQueue alloc]init]autorelease] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
|
|
OAServiceTicket *ticket = [[[OAServiceTicket alloc]initWithRequest:request response:response didSucceed:(error == nil)]autorelease];
|
|
block(ticket, data, error);
|
|
}];
|
|
}
|
|
|
|
+ (OAMutableURLRequest *)requestWithURL:(NSURL *)aUrl consumer:(OAConsumer *)aConsumer token:(OAToken *)aToken {
|
|
return [[[[self class]alloc]initWithURL:aUrl consumer:aConsumer token:aToken realm:nil signatureProvider:nil]autorelease];
|
|
}
|
|
|
|
+ (OAMutableURLRequest *)requestWithURL:(NSURL *)aUrl consumer:(OAConsumer *)aConsumer token:(OAToken *)aToken realm:(NSString *)aRealm signatureProvider:(id<OASignatureProviding, NSObject>)aProvider {
|
|
return [[[[self class]alloc]initWithURL:aUrl consumer:aConsumer token:aToken realm:aRealm signatureProvider:aProvider]autorelease];
|
|
}
|
|
|
|
- (id)initWithURL:(NSURL *)aUrl consumer:(OAConsumer *)aConsumer token:(OAToken *)aToken realm:(NSString *)aRealm signatureProvider:(id<OASignatureProviding, NSObject>)aProvider {
|
|
|
|
self = [super initWithURL:aUrl cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:25];
|
|
|
|
if (self) {
|
|
[self setConsumer:aConsumer];
|
|
|
|
// empty token for Unauthorized Request Token transaction
|
|
if (aToken == nil) {
|
|
[self setToken:[OAToken token]];
|
|
} else {
|
|
[self setToken:aToken];
|
|
}
|
|
|
|
if (aRealm == nil) {
|
|
[self setRealm:@""];
|
|
} else {
|
|
[self setRealm:aRealm];
|
|
}
|
|
|
|
// default to HMAC-SHA1
|
|
if (aProvider == nil) {
|
|
[self setSignatureProvider:[OAHMAC_SHA1SignatureProvider OAHMAC_SHA1SignatureProvider]];
|
|
} else {
|
|
[self setSignatureProvider:aProvider];
|
|
}
|
|
|
|
[self _generateTimestamp];
|
|
[self _generateNonce];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
// Setting a timestamp and nonce to known values. Can be helpful for testing
|
|
- (id)initWithURL:(NSURL *)aUrl consumer:(OAConsumer *)aConsumer token:(OAToken *)aToken realm:(NSString *)aRealm signatureProvider:(id<OASignatureProviding, NSObject>)aProvider nonce:(NSString *)aNonce timestamp:(NSString *)aTimestamp {
|
|
|
|
self = [super initWithURL:aUrl cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0];
|
|
|
|
if (self) {
|
|
[self setConsumer:aConsumer];
|
|
|
|
// empty token for Unauthorized Request Token transaction
|
|
if (aToken == nil) {
|
|
[self setToken:[OAToken token]];
|
|
} else {
|
|
[self setToken:aToken];
|
|
}
|
|
|
|
if (aRealm == nil) {
|
|
[self setRealm:@""];
|
|
} else {
|
|
[self setRealm:aRealm];
|
|
}
|
|
|
|
// default to HMAC-SHA1
|
|
if (aProvider == nil) {
|
|
[self setSignatureProvider:[OAHMAC_SHA1SignatureProvider OAHMAC_SHA1SignatureProvider]];
|
|
} else {
|
|
[self setSignatureProvider:aProvider];
|
|
}
|
|
|
|
[self setTimestamp:aTimestamp];
|
|
[self setNonce:aNonce];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)setOAuthParameterName:(NSString*)parameterName withValue:(NSString*)parameterValue {
|
|
|
|
if (!parameterName && !parameterValue) {
|
|
NSLog(@"%s There was not parameter name nor value specified.", __PRETTY_FUNCTION__);
|
|
return;
|
|
}
|
|
|
|
if (self.extraOAuthParameters == nil) {
|
|
[self setExtraOAuthParameters:[NSMutableDictionary dictionary]];
|
|
}
|
|
|
|
[self.extraOAuthParameters setObject:parameterValue forKey:parameterName];
|
|
}
|
|
|
|
- (void)prepare {
|
|
// sign
|
|
// Secrets must be urlencoded before concatenated with '&'
|
|
// TODO: if later RSA-SHA1 support is added then a little code redesign is needed
|
|
self.signature = [self.signatureProvider signClearText:[self _signatureBaseString] withSecret:[NSString stringWithFormat:@"%@&%@", [self.consumer.secret URLEncodedString], [self.token.secret URLEncodedString]]];
|
|
|
|
// set OAuth headers
|
|
NSString *oauthToken;
|
|
|
|
if ([self.token.key isEqualToString:@""]) {
|
|
oauthToken = @"oauth_callback=\"oob\", ";
|
|
} else if (self.token.verifier.length == 0) {
|
|
oauthToken = [NSString stringWithFormat:@"oauth_token=\"%@\", ", [self.token.key URLEncodedString]];
|
|
} else {
|
|
oauthToken = [NSString stringWithFormat:@"oauth_token=\"%@\", oauth_verifier=\"%@\", ", [self.token.key URLEncodedString], [self.token.verifier URLEncodedString]];
|
|
}
|
|
|
|
NSMutableString *extraParameters = [NSMutableString string];
|
|
|
|
// Adding the optional parameters in sorted order isn't required by the OAuth spec, but it makes it possible to hard-code expected values in the unit tests.
|
|
for (NSString *parameterName in [[self.extraOAuthParameters allKeys]sortedArrayUsingSelector:@selector(compare:)]) {
|
|
[extraParameters appendFormat:@", %@=\"%@\"",[parameterName URLEncodedString],[[self.extraOAuthParameters objectForKey:parameterName]URLEncodedString]];
|
|
}
|
|
|
|
NSString *oauthHeader = [NSString stringWithFormat:@"OAuth realm=\"%@\", oauth_consumer_key=\"%@\", %@oauth_signature_method=\"%@\", oauth_signature=\"%@\", oauth_timestamp=\"%@\", oauth_nonce=\"%@\", oauth_version=\"1.0\"%@", [self.realm URLEncodedString], [self.consumer.key URLEncodedString], oauthToken, [[self.signatureProvider name] URLEncodedString], [self.signature URLEncodedString], self.timestamp, self.nonce, extraParameters];
|
|
|
|
[self setValue:oauthHeader forHTTPHeaderField:@"Authorization"];
|
|
}
|
|
|
|
- (NSArray *)parameters {
|
|
|
|
NSString *encodedParameters = nil;
|
|
|
|
if ([self.HTTPMethod isEqualToString:@"GET"] || [self.HTTPMethod isEqualToString:@"DELETE"]) {
|
|
encodedParameters = self.URL.query;
|
|
} else if ([self.HTTPMethod isEqualToString:@"POST"] || [self.HTTPMethod isEqualToString:@"PUT"]) {
|
|
encodedParameters = [[[NSString alloc]initWithData:self.HTTPBody encoding:NSASCIIStringEncoding]autorelease];
|
|
}
|
|
|
|
if (encodedParameters.length == 0) {
|
|
return nil;
|
|
}
|
|
|
|
NSArray *encodedParameterPairs = [encodedParameters componentsSeparatedByString:@"&"];
|
|
NSMutableArray *requestParameters = [NSMutableArray arrayWithCapacity:16];
|
|
|
|
for (NSString *encodedPair in encodedParameterPairs) {
|
|
NSArray *encodedPairElements = [encodedPair componentsSeparatedByString:@"="];
|
|
OARequestParameter *parameter = [OARequestParameter requestParameterWithName:[[encodedPairElements objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] value:[[encodedPairElements objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
|
|
[requestParameters addObject:parameter];
|
|
}
|
|
|
|
return requestParameters;
|
|
}
|
|
|
|
- (void)setParameters:(NSArray *)parameters {
|
|
NSMutableString *encodedParameterPairs = [NSMutableString stringWithCapacity:256];
|
|
|
|
int position = 1;
|
|
for (OARequestParameter *requestParameter in parameters) {
|
|
[encodedParameterPairs appendString:[requestParameter URLEncodedNameValuePair]];
|
|
if (position < parameters.count) {
|
|
[encodedParameterPairs appendString:@"&"];
|
|
}
|
|
position++;
|
|
}
|
|
|
|
if ([self.HTTPMethod isEqualToString:@"GET"] || [self.HTTPMethod isEqualToString:@"DELETE"]) {
|
|
[self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@", [self.URL URLStringWithoutQuery], encodedParameterPairs]]];
|
|
} else if ([self.HTTPMethod isEqualToString:@"POST"] || [self.HTTPMethod isEqualToString:@"PUT"]) {
|
|
NSData *postData = [encodedParameterPairs dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
|
|
[self setHTTPBody:postData];
|
|
[self setValue:[NSString stringWithFormat:@"%d",postData.length] forHTTPHeaderField:@"Content-Length"];
|
|
[self setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
|
|
}
|
|
}
|
|
|
|
- (void)_generateTimestamp {
|
|
[self setTimestamp:[NSString stringWithFormat:@"%ld", time(nil)]];
|
|
}
|
|
|
|
- (void)_generateNonce {
|
|
CFUUIDRef theUUID = CFUUIDCreate(nil);
|
|
CFStringRef string = CFUUIDCreateString(nil, theUUID);
|
|
CFRelease(theUUID);
|
|
[self setNonce:[NSString stringWithString:(NSString *)string]];
|
|
CFRelease(string);
|
|
}
|
|
|
|
- (NSString *)_signatureBaseString {
|
|
// OAuth Spec, Section 9.1.1 "Normalize Request Parameters"
|
|
// build a sorted array of both request parameters and OAuth header parameters
|
|
NSArray *parameters = [self parameters];
|
|
|
|
NSMutableArray *parameterPairs = [NSMutableArray arrayWithCapacity:(6+parameters.count)]; // 6 being the number of OAuth params in the Signature Base String
|
|
|
|
[parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_consumer_key" value:self.consumer.key]URLEncodedNameValuePair]];
|
|
[parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_signature_method" value:[self.signatureProvider name]] URLEncodedNameValuePair]];
|
|
[parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_timestamp" value:self.timestamp]URLEncodedNameValuePair]];
|
|
[parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_nonce" value:self.nonce]URLEncodedNameValuePair]];
|
|
[parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_version" value:@"1.0"]URLEncodedNameValuePair]];
|
|
|
|
if (self.token.key.length > 0) {
|
|
[parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_token" value:self.token.key]URLEncodedNameValuePair]];
|
|
if (self.token.verifier.length > 0) {
|
|
[parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_verifier" value:self.token.verifier]URLEncodedNameValuePair]];
|
|
}
|
|
} else {
|
|
[parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_callback" value:@"oob"]URLEncodedNameValuePair]];
|
|
}
|
|
|
|
for (OARequestParameter *param in parameters) {
|
|
[parameterPairs addObject:[param URLEncodedNameValuePair]];
|
|
}
|
|
|
|
NSArray *sortedPairs = [parameterPairs sortedArrayUsingSelector:@selector(compare:)];
|
|
NSString *normalizedRequestParameters = [sortedPairs componentsJoinedByString:@"&"];
|
|
|
|
// OAuth Spec, Section 9.1.2 "Concatenate Request Elements"
|
|
NSString *ret = [NSString stringWithFormat:@"%@&%@&%@", self.HTTPMethod, [[self.URL URLStringWithoutQuery]URLEncodedString], [normalizedRequestParameters URLEncodedString]];
|
|
return ret;
|
|
}
|
|
|
|
- (void)dealloc {
|
|
[self setExtraOAuthParameters:nil];
|
|
[self setConsumer:nil];
|
|
[self setToken:nil];
|
|
[self setRealm:nil];
|
|
[self setSignatureProvider:nil];
|
|
[self setTimestamp:nil];
|
|
[self setNonce:nil];
|
|
[self setSignature:nil];
|
|
[super dealloc];
|
|
}
|
|
|
|
@end
|