Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add in extensions by Alberto Hierro (http://fi.am/)

  • Loading branch information...
commit 01ce025e01c89a84364d21b0b32de6907c80d666 1 parent 1274318
@jdg jdg authored
View
6 Categories/NSMutableURLRequest+Parameters.h
@@ -29,7 +29,9 @@
@interface NSMutableURLRequest (OAParameterAdditions)
-- (NSArray *)parameters;
-- (void)setParameters:(NSArray *)parameters;
+@property(nonatomic, retain) NSArray *parameters;
+
+- (void)setHTTPBodyWithString:(NSString *)body;
+- (void)attachFileWithName:(NSString *)name filename:(NSString*)filename contentType:(NSString *)contentType data:(NSData*)data;
@end
View
91 Categories/NSMutableURLRequest+Parameters.m
@@ -25,25 +25,31 @@
#import "NSMutableURLRequest+Parameters.h"
+static NSString *Boundary = @"-----------------------------------0xCoCoaouTHeBouNDaRy";
@implementation NSMutableURLRequest (OAParameterAdditions)
+- (BOOL)isMultipart {
+ return [[self valueForHTTPHeaderField:@"Content-Type"] hasPrefix:@"multipart/form-data"];
+}
+
- (NSArray *)parameters {
- NSString *encodedParameters;
+ NSString *encodedParameters = nil;
- if ([[self HTTPMethod] isEqualToString:@"GET"] || [[self HTTPMethod] isEqualToString:@"DELETE"]) {
- encodedParameters = [[self URL] query];
- } else {
- // POST, PUT
- encodedParameters = [[NSString alloc] initWithData:[self HTTPBody] encoding:NSASCIIStringEncoding];
- }
+ if (![self isMultipart]) {
+ if ([[self HTTPMethod] isEqualToString:@"GET"] || [[self HTTPMethod] isEqualToString:@"DELETE"]) {
+ encodedParameters = [[self URL] query];
+ } else {
+ encodedParameters = [[[NSString alloc] initWithData:[self HTTPBody] encoding:NSASCIIStringEncoding] autorelease];
+ }
+ }
- if ((encodedParameters == nil) || ([encodedParameters isEqualToString:@""])) {
+ if (encodedParameters == nil || [encodedParameters isEqualToString:@""]) {
return nil;
}
-
+ NSLog(@"raw parameters %@", encodedParameters);
NSArray *encodedParameterPairs = [encodedParameters componentsSeparatedByString:@"&"];
- NSMutableArray *requestParameters = [[NSMutableArray alloc] initWithCapacity:16];
+ NSMutableArray *requestParameters = [NSMutableArray arrayWithCapacity:[encodedParameterPairs count]];
for (NSString *encodedPair in encodedParameterPairs) {
NSArray *encodedPairElements = [encodedPair componentsSeparatedByString:@"="];
@@ -55,27 +61,52 @@ - (NSArray *)parameters {
return requestParameters;
}
-- (void)setParameters:(NSArray *)parameters {
- NSMutableString *encodedParameterPairs = [[NSMutableString alloc] initWithCapacity:256];
+- (void)setParameters:(NSArray *)parameters
+{
+ NSMutableArray *pairs = [[[NSMutableArray alloc] initWithCapacity:[parameters count]] autorelease];
+ for (OARequestParameter *requestParameter in parameters) {
+ [pairs addObject:[requestParameter URLEncodedNameValuePair]];
+ }
+
+ NSString *encodedParameterPairs = [pairs componentsJoinedByString:@"&"];
- 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 {
- // POST, 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"];
- }
+ if ([[self HTTPMethod] isEqualToString:@"GET"] || [[self HTTPMethod] isEqualToString:@"DELETE"]) {
+ [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@", [[self URL] URLStringWithoutQuery], encodedParameterPairs]]];
+ } else {
+ // POST, PUT
+ [self setHTTPBodyWithString:encodedParameterPairs];
+ [self setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
+ }
+}
+
+- (void)setHTTPBodyWithString:(NSString *)body {
+ NSData *bodyData = [body dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
+ [self setValue:[NSString stringWithFormat:@"%d", [bodyData length]] forHTTPHeaderField:@"Content-Length"];
+ [self setHTTPBody:bodyData];
+}
+
+- (void)attachFileWithName:(NSString *)name filename:(NSString*)filename contentType:(NSString *)contentType data:(NSData*)data {
+
+ NSArray *parameters = [self parameters];
+ [self setValue:[@"multipart/form-data; boundary=" stringByAppendingString:Boundary] forHTTPHeaderField:@"Content-type"];
+
+ NSMutableData *bodyData = [NSMutableData new];
+ for (OARequestParameter *parameter in parameters) {
+ NSString *param = [NSString stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n",
+ Boundary, [parameter URLEncodedName], [parameter value]];
+
+ [bodyData appendData:[param dataUsingEncoding:NSUTF8StringEncoding]];
+ }
+
+ NSString *filePrefix = [NSString stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\nContent-Type: %@\r\n\r\n",
+ Boundary, name, filename, contentType];
+ [bodyData appendData:[filePrefix dataUsingEncoding:NSUTF8StringEncoding]];
+ [bodyData appendData:data];
+
+ [bodyData appendData:[[[@"--" stringByAppendingString:Boundary] stringByAppendingString:@"--"] dataUsingEncoding:NSUTF8StringEncoding]];
+ [self setValue:[NSString stringWithFormat:@"%d", [bodyData length]] forHTTPHeaderField:@"Content-Length"];
+ [self setHTTPBody:bodyData];
+ [bodyData release];
}
@end
View
3  Categories/NSString+URLEncoding.h
@@ -30,5 +30,6 @@
- (NSString *)encodedURLString;
- (NSString *)encodedURLParameterString;
-
+- (NSString *)decodedURLString;
+- (NSString *)removeQuotes;
@end
View
28 Categories/NSString+URLEncoding.m
@@ -34,7 +34,7 @@ - (NSString *)encodedURLString {
NULL, // characters to leave unescaped (NULL = all escaped sequences are replaced)
CFSTR("?=&+"), // legal URL characters to be escaped (NULL = all legal characters are replaced)
kCFStringEncodingUTF8); // encoding
- return result;
+ return [result autorelease];
}
- (NSString *)encodedURLParameterString {
@@ -43,7 +43,31 @@ - (NSString *)encodedURLParameterString {
NULL,
CFSTR(":/=,!$&'()*+;[]@#?"),
kCFStringEncodingUTF8);
- return result;
+ return [result autorelease];
+}
+
+- (NSString *)decodedURLString {
+ NSString *result = (NSString*)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault,
+ (CFStringRef)self,
+ CFSTR(""),
+ kCFStringEncodingUTF8);
+
+ return [result autorelease];
+
+}
+
+-(NSString *)removeQuotes
+{
+ NSUInteger length = [self length];
+ NSString *ret = self;
+ if ([self characterAtIndex:0] == '"') {
+ ret = [ret substringFromIndex:1];
+ }
+ if ([self characterAtIndex:length - 1] == '"') {
+ ret = [ret substringToIndex:length - 2];
+ }
+
+ return ret;
}
@end
View
63 OACall.h
@@ -0,0 +1,63 @@
+//
+// OACall.h
+// OAuthConsumer
+//
+// Created by Alberto García Hierro on 04/09/08.
+// Copyright 2008 Alberto García Hierro. All rights reserved.
+// bynotes.com
+
+#import <Foundation/Foundation.h>
+
+@class OAProblem;
+@class OACall;
+
+@protocol OACallDelegate
+
+- (void)call:(OACall *)call failedWithError:(NSError *)error;
+- (void)call:(OACall *)call failedWithProblem:(OAProblem *)problem;
+
+@end
+
+@class OAConsumer;
+@class OAToken;
+@class OADataFetcher;
+@class OAMutableURLRequest;
+@class OAServiceTicket;
+
+@interface OACall : NSObject {
+ NSURL *url;
+ NSString *method;
+ NSArray *parameters;
+ NSDictionary *files;
+ NSObject <OACallDelegate> *delegate;
+ SEL finishedSelector;
+ OADataFetcher *fetcher;
+ OAMutableURLRequest *request;
+ OAServiceTicket *ticket;
+}
+
+@property(readonly) NSURL *url;
+@property(readonly) NSString *method;
+@property(readonly) NSArray *parameters;
+@property(readonly) NSDictionary *files;
+@property(nonatomic, retain) OAServiceTicket *ticket;
+
+- (id)init;
+- (id)initWithURL:(NSURL *)aURL;
+- (id)initWithURL:(NSURL *)aURL method:(NSString *)aMethod;
+- (id)initWithURL:(NSURL *)aURL parameters:(NSArray *)theParameters;
+- (id)initWithURL:(NSURL *)aURL method:(NSString *)aMethod parameters:(NSArray *)theParameters;
+- (id)initWithURL:(NSURL *)aURL parameters:(NSArray *)theParameters files:(NSDictionary*)theFiles;
+
+- (id)initWithURL:(NSURL *)aURL
+ method:(NSString *)aMethod
+ parameters:(NSArray *)theParameters
+ files:(NSDictionary*)theFiles;
+
+- (void)perform:(OAConsumer *)consumer
+ token:(OAToken *)token
+ realm:(NSString *)realm
+ delegate:(NSObject <OACallDelegate> *)aDelegate
+ didFinish:(SEL)finished;
+
+@end
View
168 OACall.m
@@ -0,0 +1,168 @@
+//
+// OACall.m
+// OAuthConsumer
+//
+// Created by Alberto García Hierro on 04/09/08.
+// Copyright 2008 Alberto García Hierro. All rights reserved.
+// bynotes.com
+
+#import "OAConsumer.h"
+#import "OAToken.h"
+#import "OAProblem.h"
+#import "OADataFetcher.h"
+#import "OAServiceTicket.h"
+#import "OAMutableURLRequest.h"
+#import "OACall.h"
+
+@interface OACall (Private)
+
+- (void)callFinished:(OAServiceTicket *)ticket withData:(NSData *)data;
+- (void)callFailed:(OAServiceTicket *)ticket withError:(NSError *)error;
+
+@end
+
+@implementation OACall
+
+@synthesize url, method, parameters, files, ticket;
+
+- (id)init {
+ return [self initWithURL:nil
+ method:nil
+ parameters:nil
+ files:nil];
+}
+
+- (id)initWithURL:(NSURL *)aURL {
+ return [self initWithURL:aURL
+ method:nil
+ parameters:nil
+ files:nil];
+}
+
+- (id)initWithURL:(NSURL *)aURL method:(NSString *)aMethod {
+ return [self initWithURL:aURL
+ method:aMethod
+ parameters:nil
+ files:nil];
+}
+
+- (id)initWithURL:(NSURL *)aURL parameters:(NSArray *)theParameters {
+ return [self initWithURL:aURL
+ method:nil
+ parameters:theParameters];
+}
+
+- (id)initWithURL:(NSURL *)aURL method:(NSString *)aMethod parameters:(NSArray *)theParameters {
+ return [self initWithURL:aURL
+ method:aMethod
+ parameters:theParameters
+ files:nil];
+}
+
+- (id)initWithURL:(NSURL *)aURL parameters:(NSArray *)theParameters files:(NSDictionary*)theFiles {
+ return [self initWithURL:aURL
+ method:@"POST"
+ parameters:theParameters
+ files:theFiles];
+}
+
+- (id)initWithURL:(NSURL *)aURL
+ method:(NSString *)aMethod
+ parameters:(NSArray *)theParameters
+ files:(NSDictionary*)theFiles {
+ url = [aURL retain];
+ method = [aMethod retain];
+ parameters = [theParameters retain];
+ files = [theFiles retain];
+ fetcher = nil;
+ request = nil;
+
+ return self;
+}
+
+- (void)dealloc {
+ [url release];
+ [method release];
+ [parameters release];
+ [files release];
+ [fetcher release];
+ [request release];
+ [ticket release];
+ [super dealloc];
+}
+
+- (void)callFailed:(OAServiceTicket *)aTicket withError:(NSError *)error {
+ NSLog(@"error body: %@", aTicket.body);
+ self.ticket = aTicket;
+ [aTicket release];
+ OAProblem *problem = [OAProblem problemWithResponseBody:ticket.body];
+ if (problem) {
+ [delegate call:self failedWithProblem:problem];
+ } else {
+ [delegate call:self failedWithError:error];
+ }
+}
+
+- (void)callFinished:(OAServiceTicket *)aTicket withData:(NSData *)data {
+ self.ticket = aTicket;
+ [aTicket release];
+ if (ticket.didSucceed) {
+// NSLog(@"Call body: %@", ticket.body);
+ [delegate performSelector:finishedSelector withObject:self withObject:ticket.body];
+ } else {
+// NSLog(@"Failed call body: %@", ticket.body);
+ [self callFailed:[ticket retain] withError:nil];
+ }
+}
+
+- (void)perform:(OAConsumer *)consumer
+ token:(OAToken *)token
+ realm:(NSString *)realm
+ delegate:(NSObject <OACallDelegate> *)aDelegate
+ didFinish:(SEL)finished
+
+{
+ delegate = aDelegate;
+ finishedSelector = finished;
+
+ request = [[OAMutableURLRequest alloc] initWithURL:url
+ consumer:consumer
+ token:token
+ realm:realm
+ signatureProvider:nil];
+ if(method) {
+ [request setHTTPMethod:method];
+ }
+
+ if (self.parameters) {
+ [request setParameters:self.parameters];
+ }
+ if (self.files) {
+ for (NSString *key in self.files) {
+ [request attachFileWithName:@"file" filename:NSLocalizedString(@"Photo.jpg", @"") data:[self.files objectForKey:key]];
+ }
+ }
+ fetcher = [[OADataFetcher alloc] init];
+ [fetcher fetchDataWithRequest:request
+ delegate:self
+ didFinishSelector:@selector(callFinished:withData:)
+ didFailSelector:@selector(callFailed:withError:)];
+}
+
+/*- (BOOL)isEqual:(id)object {
+ if ([object isKindOfClass:[self class]]) {
+ return [self isEqualToCall:(OACall *)object];
+ }
+ return NO;
+}
+
+- (BOOL)isEqualToCall:(OACall *)aCall {
+ return (delegate == aCall->delegate
+ && finishedSelector == aCall->finishedSelector
+ && [url isEqualTo:aCall.url]
+ && [method isEqualToString:aCall.method]
+ && [parameters isEqualToArray:aCall.parameters]
+ && [files isEqualToDictionary:aCall.files]);
+}*/
+
+@end
View
4 OAConsumer.h
@@ -35,6 +35,8 @@
@property(copy, readwrite) NSString *key;
@property(copy, readwrite) NSString *secret;
-- (id)initWithKey:(NSString *)aKey secret:(NSString *)aSecret;
+- (id)initWithKey:(const NSString *)aKey secret:(const NSString *)aSecret;
+
+- (BOOL)isEqualToConsumer:(OAConsumer *)aConsumer;
@end
View
18 OAConsumer.m
@@ -31,11 +31,23 @@ @implementation OAConsumer
#pragma mark init
-- (id)initWithKey:(NSString *)aKey secret:(NSString *)aSecret {
+- (id)initWithKey:(const NSString *)aKey secret:(const NSString *)aSecret {
[super init];
- self.key = aKey;
- self.secret = aSecret;
+ self.key = [aKey retain];
+ self.secret = [aSecret retain];
return self;
}
+- (BOOL)isEqual:(id)object {
+ if ([object isKindOfClass:[self class]]) {
+ return [self isEqualToConsumer:(OAConsumer*)object];
+ }
+ return NO;
+}
+
+- (BOOL)isEqualToConsumer:(OAConsumer *)aConsumer {
+ return ([self.key isEqualToString:aConsumer.key] &&
+ [self.secret isEqualToString:aConsumer.secret]);
+}
+
@end
View
3  OADataFetcher.h
@@ -33,8 +33,7 @@
OAMutableURLRequest *request;
NSURLResponse *response;
NSURLConnection *connection;
- NSError *error;
- NSData *responseData;
+ NSMutableData *responseData;
id delegate;
SEL didFinishSelector;
SEL didFailSelector;
View
67 OADataFetcher.m
@@ -29,33 +29,58 @@
@implementation OADataFetcher
+- (id)init {
+ [super init];
+ responseData = [[NSMutableData alloc] init];
+ return self;
+}
+
+- (void)dealloc {
+ [connection release];
+ [response release];
+ [responseData release];
+ [request release];
+ [super dealloc];
+}
+
+/* Protocol for async URL loading */
+- (void)connection:(NSURLConnection *)aConnection didReceiveResponse:(NSURLResponse *)aResponse {
+ [response release];
+ response = [aResponse retain];
+ [responseData setLength:0];
+}
+
+- (void)connection:(NSURLConnection *)aConnection didFailWithError:(NSError *)error {
+ OAServiceTicket *ticket = [[OAServiceTicket alloc] initWithRequest:request
+ response:response
+ data:responseData
+ didSucceed:NO];
+
+ [delegate performSelector:didFailSelector withObject:ticket withObject:error];
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
+ [responseData appendData:data];
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
+ OAServiceTicket *ticket = [[OAServiceTicket alloc] initWithRequest:request
+ response:response
+ data:responseData
+ didSucceed:[(NSHTTPURLResponse *)response statusCode] < 400];
+
+ [delegate performSelector:didFinishSelector withObject:ticket withObject:responseData];
+}
+
- (void)fetchDataWithRequest:(OAMutableURLRequest *)aRequest delegate:(id)aDelegate didFinishSelector:(SEL)finishSelector didFailSelector:(SEL)failSelector {
- request = aRequest;
+ request = [aRequest retain];
delegate = aDelegate;
didFinishSelector = finishSelector;
didFailSelector = failSelector;
[request prepare];
-
- responseData = [NSURLConnection sendSynchronousRequest:request
- returningResponse:&response
- error:&error];
-
- if (response == nil || responseData == nil || error != nil) {
- OAServiceTicket *ticket= [[OAServiceTicket alloc] initWithRequest:request
- response:response
- didSucceed:NO];
- [delegate performSelector:didFailSelector
- withObject:ticket
- withObject:error];
- } else {
- OAServiceTicket *ticket = [[OAServiceTicket alloc] initWithRequest:request
- response:response
- didSucceed:[(NSHTTPURLResponse *)response statusCode] < 400];
- [delegate performSelector:didFinishSelector
- withObject:ticket
- withObject:responseData];
- }
+
+ connection = [[NSURLConnection alloc] initWithRequest:aRequest delegate:self];
}
@end
View
60 OAMutableURLRequest.m
@@ -98,28 +98,30 @@ - (id)initWithURL:(NSURL *)aUrl
- (void)prepare {
// sign
- signature = [signatureProvider signClearText:[self _signatureBaseString]
+ NSLog(@"Base string is: %@", [self _signatureBaseString]);
+ signature = [signatureProvider signClearText:[self _signatureBaseString]
withSecret:[NSString stringWithFormat:@"%@&%@",
consumer.secret,
- token.secret]];
+ token.secret ? token.secret : @""]];
// set OAuth headers
-
- NSString *oauthToken;
- if ([token.key isEqualToString:@""]) {
- oauthToken = @""; // not used on Request Token transactions
- } else {
- oauthToken = [NSString stringWithFormat:@"oauth_token=\"%@\", ", [token.key encodedURLParameterString]];
- }
-
- NSString *oauthHeader = [NSString stringWithFormat:@"OAuth realm=\"%@\", oauth_consumer_key=\"%@\", %@oauth_signature_method=\"%@\", oauth_signature=\"%@\", oauth_timestamp=\"%@\", oauth_nonce=\"%@\", oauth_version=\"1.0\"",
- [realm encodedURLParameterString],
- [consumer.key encodedURLParameterString],
- oauthToken,
- [[signatureProvider name] encodedURLParameterString],
- [signature encodedURLParameterString],
- timestamp,
- nonce];
+ NSMutableArray *chunks = [[NSMutableArray alloc] init];
+ [chunks addObject:[NSString stringWithFormat:@"realm=\"%@\"", [realm encodedURLParameterString]]];
+ [chunks addObject:[NSString stringWithFormat:@"oauth_consumer_key=\"%@\"", [consumer.key encodedURLParameterString]]];
+
+ NSDictionary *tokenParameters = [token parameters];
+ for (NSString *k in tokenParameters) {
+ [chunks addObject:[NSString stringWithFormat:@"%@=\"%@\"", k, [[tokenParameters objectForKey:k] encodedURLParameterString]]];
+ }
+
+ [chunks addObject:[NSString stringWithFormat:@"oauth_signature_method=\"%@\"", [[signatureProvider name] encodedURLParameterString]]];
+ [chunks addObject:[NSString stringWithFormat:@"oauth_signature=\"%@\"", [signature encodedURLParameterString]]];
+ [chunks addObject:[NSString stringWithFormat:@"oauth_timestamp=\"%@\"", timestamp]];
+ [chunks addObject:[NSString stringWithFormat:@"oauth_nonce=\"%@\"", nonce]];
+ [chunks addObject:@"oauth_version=\"1.0\""];
+
+ NSString *oauthHeader = [NSString stringWithFormat:@"OAuth %@", [chunks componentsJoinedByString:@", "]];
+ [chunks release];
[self setValue:oauthHeader forHTTPHeaderField:@"Authorization"];
}
@@ -138,25 +140,31 @@ - (void)_generateNonce {
- (NSString *)_signatureBaseString {
// OAuth Spec, Section 9.1.1 "Normalize Request Parameters"
// build a sorted array of both request parameters and OAuth header parameters
- NSMutableArray *parameterPairs = [[NSMutableArray alloc] initWithCapacity:(6 + [[self parameters] count])]; // 6 being the number of OAuth params in the Signature Base String
+ NSDictionary *tokenParameters = [token parameters];
+ // 6 being the number of OAuth params in the Signature Base String
+ NSMutableArray *parameterPairs = [[NSMutableArray alloc] initWithCapacity:(5 + [[self parameters] count] + [tokenParameters count])];
[parameterPairs addObject:[[[OARequestParameter alloc] initWithName:@"oauth_consumer_key" value:consumer.key] URLEncodedNameValuePair]];
[parameterPairs addObject:[[[OARequestParameter alloc] initWithName:@"oauth_signature_method" value:[signatureProvider name]] URLEncodedNameValuePair]];
[parameterPairs addObject:[[[OARequestParameter alloc] initWithName:@"oauth_timestamp" value:timestamp] URLEncodedNameValuePair]];
[parameterPairs addObject:[[[OARequestParameter alloc] initWithName:@"oauth_nonce" value:nonce] URLEncodedNameValuePair]];
[parameterPairs addObject:[[[OARequestParameter alloc] initWithName:@"oauth_version" value:@"1.0"] URLEncodedNameValuePair]];
+
+
+ for(NSString *k in tokenParameters) {
+ [parameterPairs addObject:[[OARequestParameter requestParameter:k value:[tokenParameters objectForKey:k]] URLEncodedNameValuePair]];
+ }
- if (![token.key isEqualToString:@""]) {
- [parameterPairs addObject:[[[OARequestParameter alloc] initWithName:@"oauth_token" value:token.key] URLEncodedNameValuePair]];
- }
-
- for (OARequestParameter *param in [self parameters]) {
- [parameterPairs addObject:[param URLEncodedNameValuePair]];
- }
+ if (![[self valueForHTTPHeaderField:@"Content-Type"] hasPrefix:@"multipart/form-data"]) {
+ for (OARequestParameter *param in [self parameters]) {
+ [parameterPairs addObject:[param URLEncodedNameValuePair]];
+ }
+ }
NSArray *sortedPairs = [parameterPairs sortedArrayUsingSelector:@selector(compare:)];
NSString *normalizedRequestParameters = [sortedPairs componentsJoinedByString:@"&"];
+ NSLog(@"Normalized: %@", normalizedRequestParameters);
// OAuth Spec, Section 9.1.2 "Concatenate Request Elements"
return [NSString stringWithFormat:@"%@&%@&%@",
[self HTTPMethod],
View
53 OAProblem.h
@@ -0,0 +1,53 @@
+//
+// OAProblem.h
+// OAuthConsumer
+//
+// Created by Alberto García Hierro on 03/09/08.
+// Copyright 2008 Alberto García Hierro. All rights reserved.
+// bynotes.com
+
+#import <Foundation/Foundation.h>
+
+enum {
+ kOAProblemSignatureMethodRejected = 0,
+ kOAProblemParameterAbsent,
+ kOAProblemVersionRejected,
+ kOAProblemConsumerKeyUnknown,
+ kOAProblemTokenRejected,
+ kOAProblemSignatureInvalid,
+ kOAProblemNonceUsed,
+ kOAProblemTimestampRefused,
+ kOAProblemTokenExpired,
+ kOAProblemTokenNotRenewable
+};
+
+@interface OAProblem : NSObject {
+ const NSString *problem;
+}
+
+@property (readonly) const NSString *problem;
+
+- (id)initWithProblem:(const NSString *)aProblem;
+- (id)initWithResponseBody:(const NSString *)response;
+
+- (BOOL)isEqualToProblem:(OAProblem *)aProblem;
+- (BOOL)isEqualToString:(const NSString *)aProblem;
+- (BOOL)isEqualTo:(id)aProblem;
+- (int)code;
+
++ (OAProblem *)problemWithResponseBody:(const NSString *)response;
+
++ (const NSArray *)validProblems;
+
++ (OAProblem *)SignatureMethodRejected;
++ (OAProblem *)ParameterAbsent;
++ (OAProblem *)VersionRejected;
++ (OAProblem *)ConsumerKeyUnknown;
++ (OAProblem *)TokenRejected;
++ (OAProblem *)SignatureInvalid;
++ (OAProblem *)NonceUsed;
++ (OAProblem *)TimestampRefused;
++ (OAProblem *)TokenExpired;
++ (OAProblem *)TokenNotRenewable;
+
+@end
View
165 OAProblem.m
@@ -0,0 +1,165 @@
+//
+// OAProblem.m
+// OAuthConsumer
+//
+// Created by Alberto García Hierro on 03/09/08.
+// Copyright 2008 Alberto García Hierro. All rights reserved.
+// bynotes.com
+
+#import "OAProblem.h"
+
+const NSString *signature_method_rejected = @"signature_method_rejected";
+const NSString *parameter_absent = @"parameter_absent";
+const NSString *version_rejected = @"version_rejected";
+const NSString *consumer_key_unknown = @"consumer_key_unknown";
+const NSString *token_rejected = @"token_rejected";
+const NSString *signature_invalid = @"signature_invalid";
+const NSString *nonce_used = @"nonce_used";
+const NSString *timestamp_refused = @"timestamp_refused";
+const NSString *token_expired = @"token_expired";
+const NSString *token_not_renewable = @"token_not_renewable";
+
+@implementation OAProblem
+
+@synthesize problem;
+
+- (id)initWithPointer:(const NSString *) aPointer
+{
+ [super init];
+ problem = aPointer;
+ return self;
+}
+
+- (id)initWithProblem:(const NSString *) aProblem
+{
+ NSUInteger idx = [[OAProblem validProblems] indexOfObject:aProblem];
+ if (idx == NSNotFound) {
+ return nil;
+ }
+
+ return [self initWithPointer: [[OAProblem validProblems] objectAtIndex:idx]];
+}
+
+- (id)initWithResponseBody:(const NSString *) response
+{
+ NSArray *fields = [response componentsSeparatedByString:@"&"];
+ for (NSString *field in fields) {
+ if ([field hasPrefix:@"oauth_problem="]) {
+ NSString *value = [[field componentsSeparatedByString:@"="] objectAtIndex:1];
+ return [self initWithProblem:value];
+ }
+ }
+
+ return nil;
+}
+
++ (OAProblem *)problemWithResponseBody:(const NSString *) response
+{
+ return [[[OAProblem alloc] initWithResponseBody:response] autorelease];
+}
+
++ (const NSArray *)validProblems
+{
+ static NSArray *array;
+ if (!array) {
+ array = [[NSArray alloc] initWithObjects:signature_method_rejected,
+ parameter_absent,
+ version_rejected,
+ consumer_key_unknown,
+ token_rejected,
+ signature_invalid,
+ nonce_used,
+ timestamp_refused,
+ token_expired,
+ token_not_renewable,
+ nil];
+ }
+
+ return array;
+}
+
+- (BOOL)isEqualToProblem:(OAProblem *) aProblem
+{
+ return [problem isEqualToString:(NSString *)aProblem->problem];
+}
+
+- (BOOL)isEqualToString:(const NSString *) aProblem
+{
+ return [problem isEqualToString:(NSString *)aProblem];
+}
+
+- (BOOL)isEqualTo:(id) aProblem
+{
+ if ([aProblem isKindOfClass:[NSString class]]) {
+ return [self isEqualToString:aProblem];
+ }
+
+ if ([aProblem isKindOfClass:[OAProblem class]]) {
+ return [self isEqualToProblem:aProblem];
+ }
+
+ return NO;
+}
+
+- (int)code {
+ return [[[self class] validProblems] indexOfObject:problem];
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"OAuth Problem: %@", (NSString *)problem];
+}
+
+#pragma mark class_methods
+
++ (OAProblem *)SignatureMethodRejected
+{
+ return [[[OAProblem alloc] initWithPointer:signature_method_rejected] autorelease];
+}
+
++ (OAProblem *)ParameterAbsent
+{
+ return [[[OAProblem alloc] initWithPointer:parameter_absent] autorelease];
+}
+
++ (OAProblem *)VersionRejected
+{
+ return [[[OAProblem alloc] initWithPointer:version_rejected] autorelease];
+}
+
++ (OAProblem *)ConsumerKeyUnknown
+{
+ return [[[OAProblem alloc] initWithPointer:consumer_key_unknown] autorelease];
+}
+
++ (OAProblem *)TokenRejected
+{
+ return [[[OAProblem alloc] initWithPointer:token_rejected] autorelease];
+}
+
++ (OAProblem *)SignatureInvalid
+{
+ return [[[OAProblem alloc] initWithPointer:signature_invalid] autorelease];
+}
+
++ (OAProblem *)NonceUsed
+{
+ return [[[OAProblem alloc] initWithPointer:nonce_used] autorelease];
+}
+
++ (OAProblem *)TimestampRefused
+{
+ return [[[OAProblem alloc] initWithPointer:timestamp_refused] autorelease];
+}
+
++ (OAProblem *)TokenExpired
+{
+ return [[[OAProblem alloc] initWithPointer:token_expired] autorelease];
+}
+
++ (OAProblem *)TokenNotRenewable
+{
+ return [[[OAProblem alloc] initWithPointer:token_not_renewable] autorelease];
+}
+
+@end
View
4 OARequestParameter.h
@@ -41,4 +41,8 @@
- (NSString *)URLEncodedValue;
- (NSString *)URLEncodedNameValuePair;
+- (BOOL)isEqualToRequestParameter:(OARequestParameter *)parameter;
+
++ (id)requestParameter:(NSString *)aName value:(NSString *)aValue;
+
@end
View
22 OARequestParameter.m
@@ -38,7 +38,8 @@ - (id)initWithName:(NSString *)aName value:(NSString *)aValue {
}
- (NSString *)URLEncodedName {
- return [self.name encodedURLParameterString];
+ return self.name;
+// return [self.name encodedURLParameterString];
}
- (NSString *)URLEncodedValue {
@@ -49,4 +50,23 @@ - (NSString *)URLEncodedNameValuePair {
return [NSString stringWithFormat:@"%@=%@", [self URLEncodedName], [self URLEncodedValue]];
}
+- (BOOL)isEqual:(id)object {
+ if ([object isKindOfClass:[self class]]) {
+ return [self isEqualToRequestParameter:(OARequestParameter *)object];
+ }
+
+ return NO;
+}
+
+- (BOOL)isEqualToRequestParameter:(OARequestParameter *)parameter {
+ return ([self.name isEqualToString:parameter.name] &&
+ [self.value isEqualToString:parameter.value]);
+}
+
+
++ (id)requestParameter:(NSString *)aName value:(NSString *)aValue
+{
+ return [[[self alloc] initWithName:aName value:aValue] autorelease];
+}
+
@end
View
5 OAServiceTicket.h
@@ -32,12 +32,15 @@
@private
OAMutableURLRequest *request;
NSURLResponse *response;
+ NSData *data;
BOOL didSucceed;
}
@property(readonly) OAMutableURLRequest *request;
@property(readonly) NSURLResponse *response;
+@property(readonly) NSData *data;
@property(readonly) BOOL didSucceed;
+@property(readonly) NSString *body;
-- (id)initWithRequest:(OAMutableURLRequest *)aRequest response:(NSURLResponse *)aResponse didSucceed:(BOOL)success;
+- (id)initWithRequest:(OAMutableURLRequest *)aRequest response:(NSURLResponse *)aResponse data:(NSData *)aData didSucceed:(BOOL)success;
@end
View
14 OAServiceTicket.m
@@ -28,14 +28,24 @@
@implementation OAServiceTicket
-@synthesize request, response, didSucceed;
+@synthesize request, response, data, didSucceed;
-- (id)initWithRequest:(OAMutableURLRequest *)aRequest response:(NSURLResponse *)aResponse didSucceed:(BOOL)success {
+- (id)initWithRequest:(OAMutableURLRequest *)aRequest response:(NSURLResponse *)aResponse data:(NSData *)aData didSucceed:(BOOL)success {
[super init];
request = aRequest;
response = aResponse;
+ data = aData;
didSucceed = success;
return self;
}
+- (NSString *)body
+{
+ if (!data) {
+ return nil;
+ }
+
+ return [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
+}
+
@end
View
33 OAToken.h
@@ -29,13 +29,44 @@
@protected
NSString *key;
NSString *secret;
+ NSString *session;
+ NSNumber *duration;
+ NSMutableDictionary *attributes;
+ NSDate *created;
+ BOOL renewable;
+ BOOL forRenewal;
}
@property(copy, readwrite) NSString *key;
@property(copy, readwrite) NSString *secret;
+@property(copy, readwrite) NSString *session;
+@property(retain, readwrite) NSNumber *duration;
+@property(retain, readwrite) NSDictionary *attributes;
+@property(readwrite, getter=isForRenewal) BOOL forRenewal;
- (id)initWithKey:(NSString *)aKey secret:(NSString *)aSecret;
-- (id)initWithUserDefaultsUsingServiceProviderName:(NSString *)provider prefix:(NSString *)prefix;
+- (id)initWithKey:(NSString *)aKey secret:(NSString *)aSecret session:(NSString *)aSession
+ duration:(NSNumber *)aDuration attributes:(NSDictionary *)theAttributes created:(NSDate *)creation
+ renewable:(BOOL)renew;
- (id)initWithHTTPResponseBody:(NSString *)body;
+
+- (id)initWithUserDefaultsUsingServiceProviderName:(NSString *)provider prefix:(NSString *)prefix;
- (int)storeInUserDefaultsWithServiceProviderName:(NSString *)provider prefix:(NSString *)prefix;
+- (BOOL)isValid;
+
+- (void)setAttribute:(NSString *)aKey value:(NSString *)aValue;
+- (NSString *)attribute:(NSString *)aKey;
+- (void)setAttributesWithString:(NSString *)aAttributes;
+- (NSString *)attributeString;
+
+- (BOOL)hasExpired;
+- (BOOL)isRenewable;
+- (void)setDurationWithString:(NSString *)aDuration;
+- (BOOL)hasAttributes;
+- (NSDictionary *)parameters;
+
+- (BOOL)isEqualToToken:(OAToken *)aToken;
+
++ (void)removeFromUserDefaultsWithServiceProviderName:(const NSString *)provider prefix:(const NSString *)prefix;
+
@end
View
292 OAToken.m
@@ -24,65 +24,301 @@
// THE SOFTWARE.
+#import "NSString+URLEncoding.h"
#import "OAToken.h"
+@interface OAToken (Private)
+
++ (NSString *)settingsKey:(const NSString *)name provider:(const NSString *)provider prefix:(const NSString *)prefix;
++ (id)loadSetting:(const NSString *)name provider:(const NSString *)provider prefix:(const NSString *)prefix;
++ (void)saveSetting:(NSString *)name object:(id)object provider:(const NSString *)provider prefix:(const NSString *)prefix;
++ (NSNumber *)durationWithString:(NSString *)aDuration;
++ (NSDictionary *)attributesWithString:(NSString *)theAttributes;
+
+@end
@implementation OAToken
-@synthesize key, secret;
+@synthesize key, secret, session, duration, attributes, forRenewal;
#pragma mark init
- (id)init {
- [super init];
- self.key = @"";
- self.secret = @"";
- return self;
+ return [self initWithKey:nil secret:nil];
}
- (id)initWithKey:(NSString *)aKey secret:(NSString *)aSecret {
+ return [self initWithKey:aKey secret:aSecret session:nil duration:nil
+ attributes:nil created:nil renewable:NO];
+}
+
+- (id)initWithKey:(NSString *)aKey secret:(NSString *)aSecret session:(NSString *)aSession
+ duration:(NSNumber *)aDuration attributes:(NSDictionary *)theAttributes created:(NSDate *)creation
+ renewable:(BOOL)renew {
[super init];
self.key = aKey;
self.secret = aSecret;
+ self.session = aSession;
+ self.duration = aDuration;
+ self.attributes = theAttributes;
+ created = [creation retain];
+ renewable = renew;
+ forRenewal = NO;
+
return self;
}
-- (id)initWithHTTPResponseBody:(NSString *)body {
- [super init];
- NSArray *pairs = [body componentsSeparatedByString:@"&"];
-
- for (NSString *pair in pairs) {
+- (id)initWithHTTPResponseBody:(const NSString *)body {
+ NSString *aKey = nil;
+ NSString *aSecret = nil;
+ NSString *aSession = nil;
+ NSNumber *aDuration = nil;
+ NSDate *creationDate = nil;
+ NSDictionary *attrs = nil;
+ BOOL renew = NO;
+ NSArray *pairs = [body componentsSeparatedByString:@"&"];
+
+ for (NSString *pair in pairs) {
NSArray *elements = [pair componentsSeparatedByString:@"="];
if ([[elements objectAtIndex:0] isEqualToString:@"oauth_token"]) {
- self.key = [elements objectAtIndex:1];
+ aKey = [elements objectAtIndex:1];
} else if ([[elements objectAtIndex:0] isEqualToString:@"oauth_token_secret"]) {
- self.secret = [elements objectAtIndex:1];
- }
+ aSecret = [elements objectAtIndex:1];
+ } else if ([[elements objectAtIndex:0] isEqualToString:@"oauth_session_handle"]) {
+ aSession = [elements objectAtIndex:1];
+ } else if ([[elements objectAtIndex:0] isEqualToString:@"oauth_token_duration"]) {
+ aDuration = [[self class] durationWithString:[elements objectAtIndex:1]];
+ creationDate = [NSDate date];
+ } else if ([[elements objectAtIndex:0] isEqualToString:@"oauth_token_attributes"]) {
+ attrs = [[self class] attributesWithString:[[elements objectAtIndex:1] decodedURLString]];
+ } else if ([[elements objectAtIndex:0] isEqualToString:@"oauth_token_renewable"]) {
+ NSString *lowerCase = [[elements objectAtIndex:1] lowercaseString];
+ if ([lowerCase isEqualToString:@"true"] || [lowerCase isEqualToString:@"t"]) {
+ renew = YES;
+ }
+ }
}
- return self;
+ return [self initWithKey:aKey secret:aSecret session:aSession duration:aDuration
+ attributes:attrs created:creationDate renewable:renew];
+}
+
+- (id)initWithUserDefaultsUsingServiceProviderName:(const NSString *)provider prefix:(const NSString *)prefix {
+ [super init];
+ key = [OAToken loadSetting:@"key" provider:provider prefix:prefix];
+ secret = [OAToken loadSetting:@"secret" provider:provider prefix:prefix];
+ session = [OAToken loadSetting:@"session" provider:provider prefix:prefix];
+ duration = [OAToken loadSetting:@"duration" provider:provider prefix:prefix];
+ attributes = [OAToken loadSetting:@"attributes" provider:provider prefix:prefix];
+ created = [OAToken loadSetting:@"created" provider:provider prefix:prefix];
+ renewable = [[OAToken loadSetting:@"renewable" provider:provider prefix:prefix] boolValue];
+
+ if (![self isValid]) {
+ [self autorelease];
+ return nil;
+ }
+
+ return self;
+}
+
+#pragma mark dealloc
+
+- (void)dealloc {
+ [key release];
+ [secret release];
+ [duration release];
+ [attributes release];
+ [super dealloc];
+}
+
+#pragma mark settings
+
+- (BOOL)isValid {
+ return (key != nil && ![key isEqualToString:@""] && secret != nil && ![secret isEqualToString:@""]);
+}
+
+- (int)storeInUserDefaultsWithServiceProviderName:(const NSString *)provider prefix:(const NSString *)prefix {
+ [OAToken saveSetting:@"key" object:key provider:provider prefix:prefix];
+ [OAToken saveSetting:@"secret" object:secret provider:provider prefix:prefix];
+ [OAToken saveSetting:@"created" object:created provider:provider prefix:prefix];
+ [OAToken saveSetting:@"duration" object:duration provider:provider prefix:prefix];
+ [OAToken saveSetting:@"session" object:session provider:provider prefix:prefix];
+ [OAToken saveSetting:@"attributes" object:attributes provider:provider prefix:prefix];
+ [OAToken saveSetting:@"renewable" object:renewable ? @"t" : @"f" provider:provider prefix:prefix];
+
+ [[NSUserDefaults standardUserDefaults] synchronize];
+ return(0);
+}
+
+#pragma mark duration
+
+- (void)setDurationWithString:(NSString *)aDuration {
+ self.duration = [[self class] durationWithString:aDuration];
+}
+
+- (BOOL)hasExpired
+{
+ return created && [created timeIntervalSinceNow] > [duration intValue];
+}
+
+- (BOOL)isRenewable
+{
+ return session && renewable && created && [created timeIntervalSinceNow] < (2 * [duration intValue]);
+}
+
+
+#pragma mark attributes
+
+- (void)setAttribute:(const NSString *)aKey value:(const NSString *)aAttribute {
+ if (!attributes) {
+ attributes = [[NSMutableDictionary alloc] init];
+ }
+ [attributes setObject: aAttribute forKey: aKey];
+}
+
+- (void)setAttributes:(NSDictionary *)theAttributes {
+ [attributes release];
+ attributes = [[NSMutableDictionary alloc] initWithDictionary:theAttributes];
+
}
+- (BOOL)hasAttributes {
+ return (attributes && [attributes count] > 0);
+}
+
+- (NSString *)attributeString {
+ if (![self hasAttributes]) {
+ return @"";
+ }
+
+ NSMutableArray *chunks = [[NSMutableArray alloc] init];
+ for(NSString *aKey in self->attributes) {
+ [chunks addObject:[NSString stringWithFormat:@"%@:%@", aKey, [attributes objectForKey:aKey]]];
+ }
+ NSString *attrs = [chunks componentsJoinedByString:@";"];
+ [chunks release];
+ return attrs;
+}
-- (id)initWithUserDefaultsUsingServiceProviderName:(NSString *)provider prefix:(NSString *)prefix
+- (NSString *)attribute:(NSString *)aKey
{
-[super init];
-NSString *theKey = [[NSUserDefaults standardUserDefaults] stringForKey:[NSString stringWithFormat:@"OAUTH_%@_%@_KEY", prefix, provider]];
-NSString *theSecret = [[NSUserDefaults standardUserDefaults] stringForKey:[NSString stringWithFormat:@"OAUTH_%@_%@_SECRET", prefix, provider]];
-if (theKey == NULL || theSecret == NULL)
- return(nil);
-self.key = theKey;
-self.secret = theSecret;
-return(self);
+ return [attributes objectForKey:aKey];
}
+- (void)setAttributesWithString:(NSString *)theAttributes
+{
+ self.attributes = [[self class] attributesWithString:theAttributes];
+}
-- (int)storeInUserDefaultsWithServiceProviderName:(NSString *)provider prefix:(NSString *)prefix
+- (NSDictionary *)parameters
{
-[[NSUserDefaults standardUserDefaults] setObject:self.key forKey:[NSString stringWithFormat:@"OAUTH_%@_%@_KEY", prefix, provider]];
-[[NSUserDefaults standardUserDefaults] setObject:self.secret forKey:[NSString stringWithFormat:@"OAUTH_%@_%@_SECRET", prefix, provider]];
-[[NSUserDefaults standardUserDefaults] synchronize];
-return(0);
+ NSMutableDictionary *params = [[[NSMutableDictionary alloc] init] autorelease];
+
+ if (key) {
+ [params setObject:key forKey:@"oauth_token"];
+ if ([self isForRenewal]) {
+ [params setObject:session forKey:@"oauth_session_handle"];
+ }
+ } else {
+ if (duration) {
+ [params setObject:[duration stringValue] forKey: @"oauth_token_duration"];
+ }
+ if ([attributes count]) {
+ [params setObject:[self attributeString] forKey:@"oauth_token_attributes"];
+ }
+ }
+ return params;
+}
+
+#pragma mark comparisions
+
+- (BOOL)isEqual:(id)object {
+ if([object isKindOfClass:[self class]]) {
+ return [self isEqualToToken:(OAToken *)object];
+ }
+ return NO;
+}
+
+- (BOOL)isEqualToToken:(OAToken *)aToken {
+ /* Since ScalableOAuth determines that the token may be
+ renewed using the same key and secret, we must also
+ check the creation date */
+ if ([self.key isEqualToString:aToken.key] &&
+ [self.secret isEqualToString:aToken.secret]) {
+ /* May be nil */
+ if (created == aToken->created || [created isEqualToDate:aToken->created]) {
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+#pragma mark class_functions
+
++ (NSString *)settingsKey:(NSString *)name provider:(NSString *)provider prefix:(NSString *)prefix {
+ return [NSString stringWithFormat:@"OAUTH_%@_%@_%@", provider, prefix, [name uppercaseString]];
+}
+
++ (id)loadSetting:(NSString *)name provider:(NSString *)provider prefix:(NSString *)prefix {
+ return [[NSUserDefaults standardUserDefaults] objectForKey:[self settingsKey:name
+ provider:provider
+ prefix:prefix]];
+}
+
++ (void)saveSetting:(NSString *)name object:(id)object provider:(NSString *)provider prefix:(NSString *)prefix {
+ [[NSUserDefaults standardUserDefaults] setObject:object forKey:[self settingsKey:name
+ provider:provider
+ prefix:prefix]];
+}
+
++ (void)removeFromUserDefaultsWithServiceProviderName:(NSString *)provider prefix:(NSString *)prefix {
+ NSArray *keys = [NSArray arrayWithObjects:@"key", @"secret", @"created", @"duration", @"session", @"attributes", @"renewable", nil];
+ for(NSString *name in keys) {
+ [[NSUserDefaults standardUserDefaults] removeObjectForKey:[OAToken settingsKey:name provider:provider prefix:prefix]];
+ }
+}
+
++ (NSNumber *)durationWithString:(NSString *)aDuration {
+ NSUInteger length = [aDuration length];
+ unichar c = toupper([aDuration characterAtIndex:length - 1]);
+ int mult;
+ if (c >= '0' && c <= '9') {
+ return [NSNumber numberWithInt:[aDuration intValue]];
+ }
+ if (c == 'S') {
+ mult = 1;
+ } else if (c == 'H') {
+ mult = 60 * 60;
+ } else if (c == 'D') {
+ mult = 60 * 60 * 24;
+ } else if (c == 'W') {
+ mult = 60 * 60 * 24 * 7;
+ } else if (c == 'M') {
+ mult = 60 * 60 * 24 * 30;
+ } else if (c == 'Y') {
+ mult = 60 * 60 * 365;
+ } else {
+ mult = 1;
+ }
+
+ return [NSNumber numberWithInt: mult * [[aDuration substringToIndex:length - 1] intValue]];
+}
+
++ (NSDictionary *)attributesWithString:(NSString *)theAttributes {
+ NSArray *attrs = [theAttributes componentsSeparatedByString:@";"];
+ NSMutableDictionary *dct = [[NSMutableDictionary alloc] init];
+ for (NSString *pair in attrs) {
+ NSArray *elements = [pair componentsSeparatedByString:@":"];
+ [dct setObject:[elements objectAtIndex:1] forKey:[elements objectAtIndex:0]];
+ }
+ return [dct autorelease];
+}
+
+#pragma mark description
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"Key \"%@\" Secret:\"%@\"", key, secret];
}
@end
View
68 OATokenManager.h
@@ -0,0 +1,68 @@
+//
+// OATokenManager.h
+// OAuthConsumer
+//
+// Created by Alberto García Hierro on 01/09/08.
+// Copyright 2008 Alberto García Hierro. All rights reserved.
+// bynotes.com
+
+#import <Foundation/Foundation.h>
+
+#import "OACall.h"
+
+@class OATokenManager;
+
+@protocol OATokenManagerDelegate
+
+- (BOOL)tokenManager:(OATokenManager *)manager failedCall:(OACall *)call withError:(NSError *)error;
+- (BOOL)tokenManager:(OATokenManager *)manager failedCall:(OACall *)call withProblem:(OAProblem *)problem;
+
+@optional
+
+- (BOOL)tokenManagerNeedsToken:(OATokenManager *)manager;
+
+@end
+
+@class OAConsumer;
+@class OAToken;
+
+@interface OATokenManager : NSObject<OACallDelegate> {
+ OAConsumer *consumer;
+ OAToken *acToken;
+ OAToken *reqToken;
+ OAToken *initialToken;
+ NSString *authorizedTokenKey;
+ NSString *oauthBase;
+ NSString *realm;
+ NSString *callback;
+ NSObject <OATokenManagerDelegate> *delegate;
+ NSMutableArray *calls;
+ NSMutableArray *selectors;
+ NSMutableDictionary *delegates;
+ BOOL isDispatching;
+}
+
+
+- (id)init;
+
+- (id)initWithConsumer:(OAConsumer *)aConsumer token:(OAToken *)aToken oauthBase:(const NSString *)base
+ realm:(const NSString *)aRealm callback:(const NSString *)aCallback
+ delegate:(NSObject <OATokenManagerDelegate> *)aDelegate;
+
+- (void)authorizedToken:(const NSString *)key;
+
+- (void)fetchData:(NSString *)aURL finished:(SEL)didFinish;
+
+- (void)fetchData:(NSString *)aURL method:(NSString *)aMethod parameters:(NSArray *)theParameters
+ finished:(SEL)didFinish;
+
+- (void)fetchData:(NSString *)aURL method:(NSString *)aMethod parameters:(NSArray *)theParameters
+ files:(NSDictionary *)theFiles finished:(SEL)didFinish;
+
+- (void)fetchData:(NSString *)aURL method:(NSString *)aMethod parameters:(NSArray *)theParameters
+ files:(NSDictionary *)theFiles finished:(SEL)didFinish delegate:(NSObject*)aDelegate;
+
+- (void)call:(OACall *)call failedWithError:(NSError *)error;
+- (void)call:(OACall *)call failedWithProblem:(OAProblem *)problem;
+
+@end
View
400 OATokenManager.m
@@ -0,0 +1,400 @@
+//
+// OATokenManager.m
+// OAuthConsumer
+//
+// Created by Alberto García Hierro on 01/09/08.
+// Copyright 2008 Alberto García Hierro. All rights reserved.
+// bynotes.com
+
+#import "OAConsumer.h"
+#import "OAToken.h"
+#import "OAProblem.h"
+#import "OACall.h"
+#import "OATokenManager.h"
+
+@interface OATokenManager (Private)
+
+- (void)callProblem:(OACall *)call problem:(OAProblem *)problem;
+- (void)callError:(OACall *)call error:(NSError *)error;
+- (void)callFinished:(OACall *)call body:(NSString *)body;
+
+- (void)dispatch;
+- (void)performCall:(OACall *)aCall;
+
+- (void)requestToken;
+- (void)requestTokenReceived;
+- (void)exchangeToken;
+- (void)renewToken;
+- (void)accessTokenReceived;
+- (void)setAccessToken:(OAToken *)token;
+- (void)deleteSavedRequestToken;
+
+- (OACall *)queue;
+- (void)enqueue:(OACall *)call selector:(SEL)selector;
+- (void)dequeue:(OACall *)call;
+- (SEL)getSelector:(OACall *)call;
+
+@end
+
+@implementation OATokenManager
+
+- (id)init {
+ return [self initWithConsumer:nil
+ token:nil
+ oauthBase:nil
+ realm:nil
+ callback:nil
+ delegate:nil];
+}
+
+- (id)initWithConsumer:(OAConsumer *)aConsumer token:(OAToken *)aToken oauthBase:(const NSString *)base
+ realm:(const NSString *)aRealm callback:(const NSString *)aCallback
+ delegate:(NSObject <OATokenManagerDelegate> *)aDelegate {
+
+ [super init];
+ consumer = [aConsumer retain];
+ acToken = nil;
+ reqToken = nil;
+ initialToken = [aToken retain];
+ authorizedTokenKey = nil;
+ oauthBase = [base copy];
+ realm = [aRealm copy];
+ callback = [aCallback copy];
+ delegate = aDelegate;
+ calls = [[NSMutableArray alloc] init];
+ selectors = [[NSMutableArray alloc] init];
+ delegates = [[NSMutableDictionary alloc] init];
+ isDispatching = NO;
+
+ return self;
+}
+
+- (void)dealloc {
+ [consumer release];
+ [acToken release];
+ [reqToken release];
+ [initialToken release];
+ [authorizedTokenKey release];
+ [oauthBase release];
+ [realm release];
+ [callback release];
+ [calls release];
+ [selectors release];
+ [delegates release];
+ [super dealloc];
+}
+
+// The application got a new authorized
+// request token and is notifying us
+- (void)authorizedToken:(const NSString *)aKey
+{
+ if (reqToken && [aKey isEqualToString:reqToken.key]) {
+ [self exchangeToken];
+ } else {
+ [authorizedTokenKey release];
+ authorizedTokenKey = [aKey retain];
+ }
+}
+
+
+// Private functions
+
+// Deal with problems and errors in calls
+
+- (void)call:(OACall *)call failedWithProblem:(OAProblem *)problem
+{
+ /* Always clear the saved request token, just in case */
+ [self deleteSavedRequestToken];
+
+ if ([problem isEqualToProblem:[OAProblem TokenExpired]]) {
+ /* renewToken checks if it's renewable */
+ [self renewToken];
+ } else if ([problem isEqualToProblem:[OAProblem TokenNotRenewable]] ||
+ [problem isEqualToProblem:[OAProblem TokenRejected]]) {
+ /* This token may have been revoked by the user, get a new one
+ after removing the stored requestToken, since the problem may be in
+ it */
+ [self setAccessToken:nil];
+ [self requestToken];
+ } else if ([problem isEqualToProblem:[OAProblem NonceUsed]]) {
+ /* Just repeat this request */
+ [self performCall:call];
+ } else {
+ /* Non-recoverable error, tell the delegate and dequeue the call
+ if appropiate */
+ if([delegate tokenManager:self failedCall:call withProblem:problem]) {
+ [self dequeue:call];
+ }
+ @synchronized(self) {
+ isDispatching = NO;
+ }
+ }
+}
+
+- (void)call:(OACall *)call failedWithError:(NSError *)error
+{
+ if([delegate tokenManager:self failedCall:call withError:error]) {
+ [self dequeue:call];
+ }
+ @synchronized(self) {
+ isDispatching = NO;
+ }
+}
+
+// When a call finish, notify the delegate
+- (void)callFinished:(OACall *)call body:(NSString *)body
+{
+ SEL selector = [self getSelector:call];
+ id deleg = [delegates objectForKey:[NSString stringWithFormat:@"%p", call]];
+ if (deleg) {
+ [deleg performSelector:selector withObject:body];
+ [delegates removeObjectForKey:call];
+ } else {
+ [delegate performSelector:selector withObject:body];
+ }
+ @synchronized(self) {
+ isDispatching = NO;
+ }
+ [self dequeue:call];
+ [self dispatch];
+}
+
+- (OACall *)queue {
+ id obj = nil;
+ @synchronized(calls) {
+ if ([calls count]) {
+ obj = [calls objectAtIndex:0];
+ }
+ }
+ return obj;
+}
+
+- (void)enqueue:(OACall *)call selector:(SEL)selector {
+ NSUInteger idx = [calls indexOfObject:call];
+ if (idx == NSNotFound) {
+ @synchronized(calls) {
+ [calls addObject:call];
+ [call release];
+ [selectors addObject:NSStringFromSelector(selector)];
+ }
+ }
+}
+
+- (void)dequeue:(OACall *)call {
+ NSUInteger idx = [calls indexOfObject:call];
+ if (idx != NSNotFound) {
+ @synchronized(calls) {
+ [calls removeObjectAtIndex:idx];
+ [selectors removeObjectAtIndex:idx];
+ }
+ }
+}
+
+- (SEL)getSelector:(OACall *)call
+{
+ NSUInteger idx = [calls indexOfObject:call];
+ if (idx != NSNotFound) {
+ return NSSelectorFromString([selectors objectAtIndex:idx]);
+ }
+ return 0;
+}
+
+// Token management functions
+
+// Requesting a new token
+
+// Gets a new token and opens the default
+// browser for authorizing it. The application
+// is expected to call authorizedToken when it
+// gets the authorized token back
+
+- (void)requestToken
+{
+ /* Try to load an access token from settings */
+ OAToken *atoken = [[[OAToken alloc] initWithUserDefaultsUsingServiceProviderName:oauthBase prefix:[@"access:" stringByAppendingString:realm]] autorelease];
+ if (atoken && [atoken isValid]) {
+ [self setAccessToken:atoken];
+ return;
+ }
+ /* Try to load a stored requestToken from
+ settings (useful for iPhone) */
+ OAToken *token = [[[OAToken alloc] initWithUserDefaultsUsingServiceProviderName:oauthBase prefix:[@"request:" stringByAppendingString:realm]] autorelease];
+ /* iPhone specific, the manager must have got the authorized token before reaching this point */
+ NSLog(@"request token in settings %@", token);
+ if (token && token.key && [authorizedTokenKey isEqualToString:token.key]) {
+ reqToken = [token retain];
+ [self exchangeToken];
+ return;
+ }
+ if ([delegate respondsToSelector:@selector(tokenManagerNeedsToken:)]) {
+ if (![delegate tokenManagerNeedsToken:self]) {
+ return;
+ }
+ }
+ OACall *call = [[OACall alloc] initWithURL:[NSURL URLWithString:[oauthBase stringByAppendingString:@"request_token"]] method:@"POST"];
+ [call perform:consumer
+ token:initialToken
+ realm:realm
+ delegate:self
+ didFinish:@selector(requestTokenReceived:body:)];
+
+}
+
+- (void)requestTokenReceived:(OACall *)call body:(NSString *)body
+{
+ /* XXX: Check if token != nil */
+ NSLog(@"Received request token %@", body);
+ OAToken *token = [[[OAToken alloc] initWithHTTPResponseBody:body] autorelease];
+ if (token) {
+ [reqToken release];
+ reqToken = [token retain];
+
+ [reqToken storeInUserDefaultsWithServiceProviderName:oauthBase prefix:[@"request:" stringByAppendingString:realm]];
+ /* Save the token in case we exit and start again
+ before the token is authorized (useful for iPhone) */
+ NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@authorize?oauth_token=%@&oauth_callback=%@",
+ oauthBase, token.key, callback]];
+ [[UIApplication sharedApplication] openURL:url];
+
+ }
+ [call release];
+}
+
+// Exchaing a request token for an access token
+
+// Exchanges the current authorized
+// request token for an access token
+- (void)exchangeToken
+{
+ if (!reqToken) {
+ [self requestToken];
+ return;
+ }
+ NSURL *url = [NSURL URLWithString:[oauthBase stringByAppendingString:@"access_token"]];
+ OACall *call = [[OACall alloc] initWithURL:url method:@"POST"];
+ [call perform:consumer
+ token:reqToken
+ realm:realm
+ delegate:self
+ didFinish:@selector(accessTokenReceived:body:)];
+}
+
+- (void)accessTokenReceived:(OACall *)call body:(NSString *)body
+{
+ OAToken *token = [[OAToken alloc] initWithHTTPResponseBody:body];
+ [self setAccessToken:token];
+}
+
+- (void)renewToken {
+ NSLog(@"Renewing token");
+ if (!acToken || ![acToken isRenewable]) {
+ [self requestToken];
+ return;
+ }
+ acToken.forRenewal = YES;
+ NSURL *url = [NSURL URLWithString:[oauthBase stringByAppendingString:@"access_token"]];
+ OACall *call = [[OACall alloc] initWithURL:url method:@"POST"];
+ [call perform:consumer
+ token:acToken
+ realm:realm
+ delegate:self
+ didFinish:@selector(accessTokenReceived:body:)];
+}
+
+- (void)setAccessToken:(OAToken *)token {
+ /* Remove the stored requestToken which generated
+ this access token */
+ [self deleteSavedRequestToken];
+ if (token) {
+ [acToken release];
+ acToken = [token retain];
+ [acToken storeInUserDefaultsWithServiceProviderName:oauthBase prefix:[@"access:" stringByAppendingString:realm]];
+ @synchronized(self) {
+ isDispatching = NO;
+ }
+ [self dispatch];
+ } else {
+ /* Clear the in-memory and saved access tokens */
+ [acToken release];
+ acToken = nil;
+ [OAToken removeFromUserDefaultsWithServiceProviderName:oauthBase prefix:[@"access:" stringByAppendingString:realm]];
+ }
+}
+
+- (void)deleteSavedRequestToken {
+ [OAToken removeFromUserDefaultsWithServiceProviderName:oauthBase prefix:[@"request:" stringByAppendingString:realm]];
+ [reqToken release];
+ reqToken = nil;
+}
+
+- (void)performCall:(OACall *)aCall {
+ NSLog(@"Performing call");
+ [aCall perform:consumer
+ token:acToken
+ realm:realm
+ delegate:self
+ didFinish:@selector(callFinished:body:)];
+}
+
+- (void)dispatch {
+ OACall *call = [self queue];
+ if (!call) {
+ return;
+ }
+ @synchronized(self) {
+ if (isDispatching) {
+ return;
+ }
+ isDispatching = YES;
+ }
+ NSLog(@"Started dispatching");
+ if(acToken) {
+ [self performCall:call];
+ } else if(reqToken) {
+ [self exchangeToken];
+ } else {
+ [self requestToken];
+ }
+}
+
+- (void)fetchData:(NSString *)aURL method:(NSString *)aMethod parameters:(NSArray *)theParameters
+ files:(NSDictionary *)theFiles finished:(SEL)didFinish delegate:(NSObject*)aDelegate {
+
+ OACall *call = [[OACall alloc] initWithURL:[NSURL URLWithString:aURL]
+ method:aMethod
+ parameters:theParameters
+ files:theFiles];
+ NSLog(@"Received request for: %@", aURL);
+ [self enqueue:call selector:didFinish];
+ if (aDelegate) {
+ [delegates setObject:aDelegate forKey:[NSString stringWithFormat:@"%p", call]];
+ }
+ [self dispatch];
+}
+
+- (void)fetchData:(NSString *)aURL method:(NSString *)aMethod parameters:(NSArray *)theParameters
+ files:(NSDictionary *)theFiles finished:(SEL)didFinish {
+
+ [self fetchData:aURL method:aMethod parameters:theParameters files:theFiles
+ finished:didFinish delegate:nil];
+}
+
+
+- (void)fetchData:(NSString *)aURL method:(NSString *)aMethod parameters:(NSArray *)theParameters
+ finished:(SEL)didFinish {
+
+ [self fetchData:aURL method:aMethod parameters:theParameters files:nil finished:didFinish];
+}
+
+- (void)fetchData:(NSString *)aURL parameters:(NSArray *)theParameters files:(NSDictionary *)theFiles
+ finished:(SEL)didFinish {
+
+ [self fetchData:aURL method:@"POST" parameters:theParameters files:theFiles finished:didFinish];
+}
+
+- (void)fetchData:(NSString *)aURL finished:(SEL)didFinish {
+ [self fetchData:aURL method:nil parameters:nil files:nil finished:didFinish];
+}
+
+
+@end
View
4 OAuthConsumer.h
@@ -24,6 +24,7 @@
// THE SOFTWARE.
#import <Foundation/Foundation.h>
+#import "OAProblem.h"
#import "OAuthConsumer/OAToken.h"
#import "OAuthConsumer/OAConsumer.h"
#import "OAuthConsumer/OAMutableURLRequest.h"
@@ -35,4 +36,5 @@
#import "OAuthConsumer/OAPlaintextSignatureProvider.h"
#import "OAuthConsumer/OARequestParameter.h"
#import "OAuthConsumer/OAServiceTicket.h"
-#import "OAuthConsumer/OADataFetcher.h"
+#import "OAuthConsumer/OADataFetcher.h"
+#import "OATokenManager.h"
Please sign in to comment.
Something went wrong with that request. Please try again.