Skip to content

Instantly share code, notes, and snippets.

@HT154
Created August 14, 2012 05:21
Show Gist options
  • Save HT154/3346528 to your computer and use it in GitHub Desktop.
Save HT154/3346528 to your computer and use it in GitHub Desktop.
Tumblr Photo Post
#include <CommonCrypto/CommonHMAC.h>
#import <Foundation/Foundation.h>
#import "NSData+Base64.h" //This is pretty simple, just for the signature
@interface NSString (URLEncode)
- (NSString *) urlEncode;
@end
@implementation NSString (URLEncode)
-(NSString *)urlEncode{
NSString * encodedString = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)self, NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8);
return [encodedString autorelease];
}
@end
@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;
-(void)sendOAuthRequestWithURL:(NSString *)url method:(NSString *)httpMethod parameters:(NSDictionary *)arguments;
-(void)sendAPIKeyRequestWithURL:(NSString *)url paramaters:(NSDictionary *)arguments;
-(void)testPhotoUpload;
@end
@interface TumblrXAuth ()
- (void) resetNonce;
- (void) resetTimestamp;
- (NSString *) nonce;
- (NSString *) timestamp;
- (NSString *) baseString;
- (NSString *) signature;
- (NSString *) authorizationHeader;
@end
@implementation TumblrXAuth
@synthesize consumerKey, password, username, consumerSecret, token, tokenSecret;
@synthesize delegate;
-(id)init{
if ((self = [super init])) {
state = TumblrXAuthStateDefault;
data = [[NSMutableData alloc] init];
self.tokenSecret = @"";
method = @"GET";
}
return self;
}
-(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 urlEncode]],
[NSString stringWithFormat:@"%@=\"%@\"", @"oauth_signature_method", [[NSString stringWithString:@"HMAC-SHA1"] urlEncode]],
[NSString stringWithFormat:@"%@=\"%@\"", @"oauth_timestamp", [self.timestamp urlEncode]],
[NSString stringWithFormat:@"%@=\"%@\"", @"oauth_consumer_key", [self.consumerKey urlEncode]],
[NSString stringWithFormat:@"%@=\"%@\"", @"oauth_signature", [self.signature urlEncode]],
[NSString stringWithFormat:@"%@=\"%@\"", @"oauth_version", [[NSString stringWithString:@"1.0"] urlEncode]],
nil];
if (state == TumblrXAuthStateAPI && self.token && [self.token length] > 0)
keysAndValues = [keysAndValues arrayByAddingObject:[NSString stringWithFormat:@"%@=\"%@\"", @"oauth_token", [self.token urlEncode]]];
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 urlEncode]],
[NSString stringWithFormat:@"%@=%@", @"x_auth_username", [self.username urlEncode]],
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&parameters
NSString * url = nil;
url = [tumblrURL urlEncode];
NSString * parameters;
NSString * oauth_consumer_key = [self.consumerKey urlEncode];
NSString * oauth_nonce = [self.nonce urlEncode];
NSString * oauth_signature_method = [[NSString stringWithString:@"HMAC-SHA1"] urlEncode];
NSString * oauth_timestamp = [self.timestamp urlEncode];
NSString * oauth_version = [[NSString stringWithString:@"1.0"] urlEncode];
NSString * x_auth_mode = [[NSString stringWithString:@"client_auth"] urlEncode];
NSString * x_auth_password = [self.password urlEncode];
NSString * x_auth_username = [self.username urlEncode];
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 urlEncode]],
[NSString stringWithFormat:@"%@%%3D%@", @"x_auth_username", [x_auth_username urlEncode]],
nil]];
if (state == TumblrXAuthStateAPI){
params = [params arrayByAddingObject:[NSString stringWithFormat:@"%@%%3D%@", @"oauth_token", [self.token urlEncode]]];
if(currentParams){
for(id key in currentParams){
params = [params arrayByAddingObject:[NSString stringWithFormat:@"%@%%3D%@", key,[[[currentParams objectForKey:key] urlEncode] urlEncode]]];
}
}
}
//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 urlEncode],[[arguments objectForKey:key] urlEncode]]];}}
[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"];
[data setLength:0];
[NSURLConnection connectionWithRequest:postRequest delegate:self];
}
-(void)sendAPIKeyRequestWithURL:(NSString *)url paramaters:(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]];
}
}
-(void)testPhotoUpload{
NSMutableDictionary *params = [NSMutableDictionary dictionary];
[params setObject:@"photo" forKey:@"type"];
NSString *photoData = [[[NSFileHandle fileHandleForReadingAtPath:@"/Path/to/Photo.png"] readDataToEndOfFile] description];
[params setObject:photoData forKey:@"data[0]"];
[self sendOAuthRequestWithURL:[NSString stringWithFormat:@"http://api.tumblr.com/v2/blog/%@/post",@"someblog.tumblr.com"] method:@"POST" parameters:params];
}
@end
@HT154
Copy link
Author

HT154 commented Aug 14, 2012

Calling -testPhotoUpload with a valid blog hostname and photo path yields 401 errors. Any idea what's going on?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment