Created
November 2, 2012 19:19
-
-
Save HT154/4003721 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// TumblrXAuth | |
// | |
// Created by Eric Johnson on 08/27/2010. | |
// Copyright 2010 Eric Johnson. All rights reserved. | |
// | |
// Heavily modified by HT154 on 06/15/2012. | |
// | |
// Permission is given to use this source code file, free of charge, in any | |
// project, commercial or otherwise, entirely at your risk, with the condition | |
// that any redistribution (in part or whole) of source code must retain | |
// this copyright and permission notice. Attribution in compiled projects is | |
// appreciated but not required. | |
#import <Foundation/Foundation.h> | |
#import "JSONKit.h" | |
#include <CommonCrypto/CommonHMAC.h> | |
#import "NSData+Base64.h" | |
@interface NSString (URLEncode) | |
- (NSString *)encodeForURL; | |
- (NSString *)encodeForURLReplacingSpacesWithPlus; | |
- (NSString *)decodeFromURL; | |
@end | |
@interface NSData (URLEncode) | |
- (NSString *)encodeForURL; | |
- (NSString *)stringWithoutURLEncoding; | |
- (NSString *)encodeForOauthBaseString; | |
@end | |
@class TumblrXAuth; | |
@protocol TumblrXAuthDelegate <NSObject> | |
@optional | |
-(void)tumblrXAuthAuthorizationDidFail:(TumblrXAuth *)tumblrXAuth error:(NSError *)error; | |
-(void)tumblrXAuthDidAuthorize:(TumblrXAuth *)tumblrXAuth; | |
-(void)tumblrXAuthDidFail:(TumblrXAuth *)tumblrXAuth error:(NSError *)error; | |
-(void)tumblrXAuthDidRespond:(TumblrXAuth *)tumblrXAuth response:(NSString *)response; | |
@end | |
typedef enum { | |
TumblrXAuthStateDefault, | |
TumblrXAuthStateAuthorize, | |
TumblrXAuthStateAPI, | |
} TumblrXAuthState; | |
@interface TumblrXAuth : NSObject { | |
NSString *nonce; | |
NSString *timestamp; | |
NSString *consumerKey; | |
NSString *password; | |
NSString *username; | |
NSString *consumerSecret; | |
NSString *token; | |
NSString *tokenSecret; | |
NSMutableData * data; | |
TumblrXAuthState state; | |
id<TumblrXAuthDelegate> delegate; | |
NSDictionary *currentParams; | |
NSString *tumblrURL; | |
NSString *method; | |
} | |
@property (nonatomic,copy) NSString * consumerKey; | |
@property (nonatomic,copy) NSString * password; | |
@property (nonatomic,copy) NSString * username; | |
@property (nonatomic,copy) NSString * consumerSecret; | |
@property (nonatomic,copy) NSString * token; //oauth_token | |
@property (nonatomic,copy) NSString * tokenSecret; //oauth_token_secret | |
@property (nonatomic,assign) id<TumblrXAuthDelegate> delegate; | |
-(void)authorize; | |
-(id)initWithDelegate:(id<TumblrXAuthDelegate>)aDelegate consumerKey:(NSString *)ck consumerSecret:(NSString *)cs tokenKey:(NSString *)tk tokenSecret:(NSString *)ts; | |
-(void)sendOAuthRequestWithURL:(NSString *)url method:(NSString *)httpMethod parameters:(NSDictionary *)arguments; | |
-(void)sendAPIKeyRequestWithURL:(NSString *)url parameters:(NSDictionary *)arguments; | |
- (NSString *) nonce; | |
@end | |
@implementation NSString (URLEncode) | |
- (NSString *)encodeForURL{ | |
const CFStringRef legalURLCharactersToBeEscaped = CFSTR("!*'();:@&=+$,/?#[]<>\"{}|\\`^% "); | |
return [NSMakeCollectable(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, NULL, legalURLCharactersToBeEscaped, kCFStringEncodingUTF8)) autorelease]; | |
} | |
- (NSString *)encodeForURLReplacingSpacesWithPlus;{ | |
const CFStringRef legalURLCharactersToBeEscaped = CFSTR("!*'();:@&=$,/?#[]<>\"{}|\\`^% "); | |
NSString *replaced = [self stringByReplacingOccurrencesOfString:@" " withString:@"+"]; | |
return [NSMakeCollectable(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)replaced, NULL, legalURLCharactersToBeEscaped, kCFStringEncodingUTF8)) autorelease]; | |
} | |
- (NSString *)decodeFromURL{ | |
NSString *decoded = [NSMakeCollectable(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef)self, CFSTR(""), kCFStringEncodingUTF8)) autorelease]; | |
return [decoded stringByReplacingOccurrencesOfString:@"+" withString:@" "]; | |
} | |
@end | |
@implementation NSData (URLEncode) | |
- (NSString *) stringWithoutURLEncoding { | |
NSString *hexDataDesc = [self description]; | |
hexDataDesc = [[hexDataDesc stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]] stringByReplacingOccurrencesOfString:@" " withString:@""]; | |
NSMutableString * newString = [NSMutableString string]; | |
for (int x=0; x<[hexDataDesc length]; x+=2) { | |
NSString *component = [hexDataDesc substringWithRange:NSMakeRange(x, 2)]; | |
int value = 0; | |
sscanf([component cStringUsingEncoding:NSASCIIStringEncoding], "%x", &value); | |
if((value <=46 && value >= 45) || (value <=57 && value >= 48) || (value <=90 && value >= 65) || (value == 95) || (value <=122 && value >= 97)){ //48-57, 65-90, 97-122 | |
[newString appendFormat:@"%c", (char)value]; | |
}else{ | |
[newString appendFormat:@"%%%@", [component uppercaseString]]; | |
} | |
} | |
NSString *aNewString = [newString stringByReplacingOccurrencesOfString:@"%20" withString:@"+"]; | |
return aNewString; | |
} | |
- (NSString *) encodeForURL { | |
NSString *newString = [self stringWithoutURLEncoding]; | |
newString = [newString stringByReplacingOccurrencesOfString:@"+" withString:@"%20"]; | |
const CFStringRef legalURLCharactersToBeEscaped = CFSTR("!*'();:@&=+$,/?#[]<>\"{}|\\`^% "); | |
NSString *encodeForURLdString = [NSMakeCollectable(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)newString, NULL, legalURLCharactersToBeEscaped, kCFStringEncodingUTF8)) autorelease]; | |
return encodeForURLdString; | |
} | |
- (NSString *) encodeForOauthBaseString { | |
NSString *newString = [self encodeForURL]; | |
newString =[newString stringByReplacingOccurrencesOfString:@"%257E" withString:@"~"]; | |
return newString; | |
} | |
@end | |
@interface TumblrXAuth () | |
- (void) resetNonce; | |
- (void) resetTimestamp; | |
- (NSString *) timestamp; | |
- (NSString *) baseString; | |
- (NSString *) signature; | |
- (NSString *) authorizationHeader; | |
@end | |
@implementation TumblrXAuth | |
@synthesize consumerKey, password, username, consumerSecret, token, tokenSecret; | |
@synthesize delegate; | |
-(id)initWithDelegate:(id<TumblrXAuthDelegate>)aDelegate consumerKey:(NSString *)ck consumerSecret:(NSString *)cs tokenKey:(NSString *)tk tokenSecret:(NSString *)ts{ | |
if ((self = [super init])) { | |
state = TumblrXAuthStateDefault; | |
data = [[NSMutableData alloc] init]; | |
if(!ts){self.tokenSecret = @""; | |
}else{self.tokenSecret = ts;} | |
if(tk){self.token = tk;} | |
if(ck){self.consumerKey = ck;} | |
if(cs){self.consumerSecret = cs;} | |
if(aDelegate){self.delegate = aDelegate;} | |
method = @"GET"; | |
} | |
return self; | |
} | |
-(id)init{ | |
return [self initWithDelegate:nil consumerKey:nil consumerSecret:nil tokenKey:nil tokenSecret:nil]; | |
} | |
-(void)dealloc{ | |
[data release]; | |
[currentParams release]; | |
[super dealloc]; | |
} | |
#pragma mark Authentication Methods | |
-(void)resetNonce{[nonce release]; nonce = nil;} | |
-(void)resetTimestamp{[timestamp release]; timestamp = nil;} | |
-(NSString *)nonce{if (nonce == nil){nonce = [[NSString stringWithFormat:@"%d", arc4random()] retain];} return nonce;} | |
-(NSString *)timestamp{if (timestamp == nil){timestamp = [[NSString stringWithFormat:@"%d", (int)(((float)([[NSDate date] timeIntervalSince1970])) + 0.5)] retain];} return timestamp;} | |
-(NSString *)signature{ | |
NSString * secret = [NSString stringWithFormat:@"%@&%@", self.consumerSecret, self.tokenSecret]; | |
NSData * secretData = [secret dataUsingEncoding:NSUTF8StringEncoding]; | |
NSData * baseData = [self.baseString dataUsingEncoding:NSUTF8StringEncoding]; | |
//uint8_t digest[CC_SHA1_DIGEST_LENGTH] = {0}; | |
uint8_t digest[20] = {0}; | |
CCHmac(kCCHmacAlgSHA1, secretData.bytes, secretData.length, baseData.bytes, baseData.length, digest); | |
//NSData * signatureData = [NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH]; | |
NSData * signatureData = [NSData dataWithBytes:digest length:20]; | |
return [signatureData base64EncodedString]; | |
} | |
-(NSString *)authorizationHeader{ | |
NSArray * keysAndValues = [NSArray arrayWithObjects: | |
[NSString stringWithFormat:@"%@=\"%@\"", @"oauth_nonce", [self.nonce encodeForURL]], | |
[NSString stringWithFormat:@"%@=\"%@\"", @"oauth_signature_method", [[NSString stringWithString:@"HMAC-SHA1"] encodeForURL]], | |
[NSString stringWithFormat:@"%@=\"%@\"", @"oauth_timestamp", [self.timestamp encodeForURL]], | |
[NSString stringWithFormat:@"%@=\"%@\"", @"oauth_consumer_key", [self.consumerKey encodeForURL]], | |
[NSString stringWithFormat:@"%@=\"%@\"", @"oauth_signature", [self.signature encodeForURL]], | |
[NSString stringWithFormat:@"%@=\"%@\"", @"oauth_version", [[NSString stringWithString:@"1.0"] encodeForURL]], | |
nil]; | |
if (state == TumblrXAuthStateAPI && self.token && [self.token length] > 0) | |
keysAndValues = [keysAndValues arrayByAddingObject:[NSString stringWithFormat:@"%@=\"%@\"", @"oauth_token", [self.token encodeForURL]]]; | |
return [NSString stringWithFormat:@"OAuth %@", [keysAndValues componentsJoinedByString:@", "]]; | |
} | |
-(void)authorize{ | |
//send POST to https://www.tumblr.com/oauth/access_token with parameters: x_auth_username, x_auth_password, x_auth_mode | |
[self resetTimestamp]; | |
[self resetNonce]; | |
state = TumblrXAuthStateAuthorize; | |
[tumblrURL release]; | |
tumblrURL = [[NSString stringWithString:@"https://www.tumblr.com/oauth/access_token"] retain]; | |
NSMutableURLRequest* postRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:tumblrURL]]; | |
[postRequest setHTTPMethod: @"POST"]; | |
method = @"POST"; | |
NSArray * parameterArray = [NSArray arrayWithObjects: | |
[NSString stringWithFormat:@"%@=%@", @"x_auth_mode", @"client_auth"], | |
[NSString stringWithFormat:@"%@=%@", @"x_auth_password", [self.password encodeForURL]], | |
[NSString stringWithFormat:@"%@=%@", @"x_auth_username", [self.username encodeForURL]], | |
nil]; | |
[postRequest setHTTPBody:[[parameterArray componentsJoinedByString:@"&"] dataUsingEncoding:NSUTF8StringEncoding]]; | |
[postRequest addValue:self.authorizationHeader forHTTPHeaderField:@"Authorization"]; | |
[data setLength:0]; | |
[NSURLConnection connectionWithRequest:postRequest delegate:self]; | |
} | |
-(NSString *)baseString{ | |
//method&url¶meters | |
NSString * url = nil; | |
url = [tumblrURL encodeForURL]; | |
NSString * parameters; | |
NSString * oauth_consumer_key = [self.consumerKey encodeForURL]; | |
NSString * oauth_nonce = [self.nonce encodeForURL]; | |
NSString * oauth_signature_method = [[NSString stringWithString:@"HMAC-SHA1"] encodeForURL]; | |
NSString * oauth_timestamp = [self.timestamp encodeForURL]; | |
NSString * oauth_version = [[NSString stringWithString:@"1.0"] encodeForURL]; | |
NSString * x_auth_mode = [[NSString stringWithString:@"client_auth"] encodeForURL]; | |
NSString * x_auth_password = [self.password encodeForURL]; | |
NSString * x_auth_username = [self.username encodeForURL]; | |
NSArray * params = [NSArray arrayWithObjects: | |
[NSString stringWithFormat:@"%@%%3D%@", @"oauth_consumer_key", oauth_consumer_key], | |
[NSString stringWithFormat:@"%@%%3D%@", @"oauth_nonce", oauth_nonce], | |
[NSString stringWithFormat:@"%@%%3D%@", @"oauth_signature_method", oauth_signature_method], | |
[NSString stringWithFormat:@"%@%%3D%@", @"oauth_timestamp", oauth_timestamp], | |
[NSString stringWithFormat:@"%@%%3D%@", @"oauth_version", oauth_version], | |
nil]; | |
if (state == TumblrXAuthStateAuthorize) | |
params = [params arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:[NSString stringWithFormat:@"%@%%3D%@", @"x_auth_mode", x_auth_mode], | |
[NSString stringWithFormat:@"%@%%3D%@", @"x_auth_password", [x_auth_password encodeForURL]], | |
[NSString stringWithFormat:@"%@%%3D%@", @"x_auth_username", [x_auth_username encodeForURL]], | |
nil]]; | |
if (state == TumblrXAuthStateAPI){ | |
params = [params arrayByAddingObject:[NSString stringWithFormat:@"%@%%3D%@", @"oauth_token", [self.token encodeForURL]]]; | |
if(currentParams){ | |
for(id key in currentParams){ | |
params = [params arrayByAddingObject:[NSString stringWithFormat:@"%@%%3D%@", key,[[[currentParams objectForKey:key] encodeForURL] encodeForURL]]]; | |
} | |
} | |
} | |
//sort paramaters alphabetically | |
params = [[params sortedArrayUsingSelector:@selector(compare:)] retain]; | |
parameters = [params componentsJoinedByString:@"%26"]; | |
NSArray *baseComponents = [NSArray arrayWithObjects:method, url, parameters, nil]; | |
NSString *baseString = [baseComponents componentsJoinedByString:@"&"]; | |
return baseString; | |
} | |
#pragma mark API Assists | |
-(void)sendOAuthRequestWithURL:(NSString *)url method:(NSString *)httpMethod parameters:(NSDictionary *)arguments{ | |
[self resetTimestamp]; | |
[self resetNonce]; | |
state = TumblrXAuthStateAPI; | |
currentParams = arguments; | |
NSMutableArray * parameterArray = [NSMutableArray array]; | |
if(arguments){for(id key in arguments){[parameterArray addObject:[NSString stringWithFormat:@"%@=%@", [key encodeForURL],[[arguments objectForKey:key] encodeForURL]]];}} | |
[tumblrURL release]; | |
tumblrURL = [[NSString stringWithString:url] retain]; | |
if([httpMethod isEqual:@"GET"]){ | |
url = [url stringByAppendingFormat:@"?%@",[parameterArray componentsJoinedByString:@"&"]]; | |
} | |
NSMutableURLRequest* postRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]]; | |
[postRequest setHTTPMethod:httpMethod]; | |
method = httpMethod; | |
if([httpMethod isEqual:@"POST"]){ | |
[postRequest setHTTPBody:[[parameterArray componentsJoinedByString:@"&"] dataUsingEncoding:NSUTF8StringEncoding]]; | |
} | |
[postRequest addValue:self.authorizationHeader forHTTPHeaderField:@"Authorization"]; | |
[postRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-type"]; | |
[data setLength:0]; | |
[NSURLConnection connectionWithRequest:postRequest delegate:self]; | |
} | |
-(void)sendAPIKeyRequestWithURL:(NSString *)url parameters:(NSDictionary *)arguments{ | |
state = TumblrXAuthStateAPI; | |
NSMutableString *tURL = [NSMutableString stringWithCapacity:30]; | |
[tURL appendString:[NSString stringWithFormat:@"%@?api_key=%@",url,self.consumerKey]]; | |
if(arguments){ | |
for (id key in arguments){ | |
[tURL appendString:[NSString stringWithFormat:@"&%@=%@",key,[arguments objectForKey:key]]]; | |
} | |
} | |
NSMutableURLRequest *postRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:tURL]]; | |
[postRequest setHTTPMethod: @"GET"]; | |
[data setLength:0]; | |
[NSURLConnection connectionWithRequest:postRequest delegate:self]; | |
} | |
#pragma mark NSURLConnection Delegate Methods | |
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{ | |
if (state == TumblrXAuthStateAuthorize && delegate && [delegate respondsToSelector:@selector(tumblrXAuthAuthorizationDidFail:error:)]) | |
[delegate tumblrXAuthAuthorizationDidFail:self error:error]; | |
if (state == TumblrXAuthStateAPI && delegate && [delegate respondsToSelector:@selector(tumblrXAuthDidFail:error:)]) | |
[delegate tumblrXAuthDidFail:self error:error]; | |
} | |
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)newData{ | |
[data appendData:newData]; | |
} | |
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ | |
if ([response respondsToSelector:@selector(statusCode)]) { | |
NSInteger statusCode = [((NSHTTPURLResponse *)response) statusCode]; | |
if (statusCode >= 400) { | |
[connection cancel]; | |
NSDictionary * errorInfo = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:NSLocalizedString(@"Server returned status code %d",@""), statusCode] forKey:NSLocalizedDescriptionKey]; | |
NSError * statusError = [NSError errorWithDomain:@"HTTP Property Status Code" code:statusCode userInfo:errorInfo]; | |
[self connection:connection didFailWithError:statusError]; | |
} | |
} | |
} | |
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{ | |
if (state == TumblrXAuthStateAuthorize) { | |
NSString * response = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; | |
NSArray * parameters = [response componentsSeparatedByString:@"&"]; | |
NSMutableDictionary * dictionary = [NSMutableDictionary dictionary]; | |
for (NSString * parameter in parameters) { | |
NSArray * keyAndValue = [parameter componentsSeparatedByString:@"="]; | |
if (keyAndValue == nil || [keyAndValue count] != 2) | |
continue; | |
NSString * key = [keyAndValue objectAtIndex:0]; | |
NSString * value = [keyAndValue lastObject]; | |
[dictionary setObject:value forKey:key]; | |
} | |
if ([dictionary objectForKey:@"oauth_token_secret"]) | |
self.tokenSecret = [dictionary objectForKey:@"oauth_token_secret"]; | |
if ([dictionary objectForKey:@"oauth_token"]) | |
self.token = [dictionary objectForKey:@"oauth_token"]; | |
if (delegate && [delegate respondsToSelector:@selector(tumblrXAuthDidAuthorize:)]) | |
[delegate tumblrXAuthDidAuthorize:self]; | |
}else if (state == TumblrXAuthStateAPI) { | |
if (delegate && [delegate respondsToSelector:@selector(tumblrXAuthDidRespond:response:)]) | |
[delegate tumblrXAuthDidRespond:self response:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]]; | |
} | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment