OAUTHnesia Client for Objective-C


Okay for the last few hours, I’ve been learning how to code in Objective-C and the first result is an OAUTH client for Urbanesia. I haven’t tested thoroughly though. The class basically wraps POST & GET requests to Urbanesia with OAUTH requirements.

Instantiating the class, you will have to provide Consumer Key & Secret obtained from Urbanesia. The current implementation requires User Key & Secret for every requests too. Future release of OAUTHnesia will include non Use Key/Secret methods and XAUTH.

You will have to do the HTTP connection yourselves with your own codes, OAUTHnesia only wraps the request with proper OAUTH requirements.

Base64Transcoder.h

/*
 *  Base64Transcoder.h
 *  Base64Test
 *
 *  Created by Jonathan Wight on Tue Mar 18 2003.
 *  Copyright (c) 2003 Toxic Software. 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.
 *
 */

#include 
#include 

extern size_t EstimateBas64EncodedDataSize(size_t inDataSize);
extern size_t EstimateBas64DecodedDataSize(size_t inDataSize);

extern bool Base64EncodeData(const void *inInputData, size_t inInputDataSize, char *outOutputData, size_t *ioOutputDataSize);
extern bool Base64DecodeData(const void *inInputData, size_t inInputDataSize, void *ioOutputData, size_t *ioOutputDataSize);

Base64Transcoder.c

/*
 *  Base64Transcoder.c
 *  Base64Test
 *
 *  Created by Jonathan Wight on Tue Mar 18 2003.
 *  Copyright (c) 2003 Toxic Software. 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.
 *
 */

#include "Base64Transcoder.h"

#include 
#include 

const u_int8_t kBase64EncodeTable[64] = {
	/*  0 */ 'A',	/*  1 */ 'B',	/*  2 */ 'C',	/*  3 */ 'D', 
	/*  4 */ 'E',	/*  5 */ 'F',	/*  6 */ 'G',	/*  7 */ 'H', 
	/*  8 */ 'I',	/*  9 */ 'J',	/* 10 */ 'K',	/* 11 */ 'L', 
	/* 12 */ 'M',	/* 13 */ 'N',	/* 14 */ 'O',	/* 15 */ 'P', 
	/* 16 */ 'Q',	/* 17 */ 'R',	/* 18 */ 'S',	/* 19 */ 'T', 
	/* 20 */ 'U',	/* 21 */ 'V',	/* 22 */ 'W',	/* 23 */ 'X', 
	/* 24 */ 'Y',	/* 25 */ 'Z',	/* 26 */ 'a',	/* 27 */ 'b', 
	/* 28 */ 'c',	/* 29 */ 'd',	/* 30 */ 'e',	/* 31 */ 'f', 
	/* 32 */ 'g',	/* 33 */ 'h',	/* 34 */ 'i',	/* 35 */ 'j', 
	/* 36 */ 'k',	/* 37 */ 'l',	/* 38 */ 'm',	/* 39 */ 'n', 
	/* 40 */ 'o',	/* 41 */ 'p',	/* 42 */ 'q',	/* 43 */ 'r', 
	/* 44 */ 's',	/* 45 */ 't',	/* 46 */ 'u',	/* 47 */ 'v', 
	/* 48 */ 'w',	/* 49 */ 'x',	/* 50 */ 'y',	/* 51 */ 'z', 
	/* 52 */ '0',	/* 53 */ '1',	/* 54 */ '2',	/* 55 */ '3', 
	/* 56 */ '4',	/* 57 */ '5',	/* 58 */ '6',	/* 59 */ '7', 
	/* 60 */ '8',	/* 61 */ '9',	/* 62 */ '+',	/* 63 */ '/'
};

/*
 -1 = Base64 end of data marker.
 -2 = White space (tabs, cr, lf, space)
 -3 = Noise (all non whitespace, non-base64 characters) 
 -4 = Dangerous noise
 -5 = Illegal noise (null byte)
 */

const int8_t kBase64DecodeTable[128] = {
	/* 0x00 */ -5, 	/* 0x01 */ -3, 	/* 0x02 */ -3, 	/* 0x03 */ -3,
	/* 0x04 */ -3, 	/* 0x05 */ -3, 	/* 0x06 */ -3, 	/* 0x07 */ -3,
	/* 0x08 */ -3, 	/* 0x09 */ -2, 	/* 0x0a */ -2, 	/* 0x0b */ -2,
	/* 0x0c */ -2, 	/* 0x0d */ -2, 	/* 0x0e */ -3, 	/* 0x0f */ -3,
	/* 0x10 */ -3, 	/* 0x11 */ -3, 	/* 0x12 */ -3, 	/* 0x13 */ -3,
	/* 0x14 */ -3, 	/* 0x15 */ -3, 	/* 0x16 */ -3, 	/* 0x17 */ -3,
	/* 0x18 */ -3, 	/* 0x19 */ -3, 	/* 0x1a */ -3, 	/* 0x1b */ -3,
	/* 0x1c */ -3, 	/* 0x1d */ -3, 	/* 0x1e */ -3, 	/* 0x1f */ -3,
	/* ' ' */ -2,	/* '!' */ -3,	/* '"' */ -3,	/* '#' */ -3,
	/* '$' */ -3,	/* '%' */ -3,	/* '&' */ -3,	/* ''' */ -3,
	/* '(' */ -3,	/* ')' */ -3,	/* '*' */ -3,	/* '+' */ 62,
	/* ',' */ -3,	/* '-' */ -3,	/* '.' */ -3,	/* '/' */ 63,
	/* '0' */ 52,	/* '1' */ 53,	/* '2' */ 54,	/* '3' */ 55,
	/* '4' */ 56,	/* '5' */ 57,	/* '6' */ 58,	/* '7' */ 59,
	/* '8' */ 60,	/* '9' */ 61,	/* ':' */ -3,	/* ';' */ -3,
	/* '<' */ -3,	/* '=' */ -1,	/* '>' */ -3,	/* '?' */ -3,
	/* '@' */ -3,	/* 'A' */ 0,	/* 'B' */  1,	/* 'C' */  2,
	/* 'D' */  3,	/* 'E' */  4,	/* 'F' */  5,	/* 'G' */  6,
	/* 'H' */  7,	/* 'I' */  8,	/* 'J' */  9,	/* 'K' */ 10,
	/* 'L' */ 11,	/* 'M' */ 12,	/* 'N' */ 13,	/* 'O' */ 14,
	/* 'P' */ 15,	/* 'Q' */ 16,	/* 'R' */ 17,	/* 'S' */ 18,
	/* 'T' */ 19,	/* 'U' */ 20,	/* 'V' */ 21,	/* 'W' */ 22,
	/* 'X' */ 23,	/* 'Y' */ 24,	/* 'Z' */ 25,	/* '[' */ -3,
	/* '\' */ -3,	/* ']' */ -3,	/* '^' */ -3,	/* '_' */ -3,
	/* '`' */ -3,	/* 'a' */ 26,	/* 'b' */ 27,	/* 'c' */ 28,
	/* 'd' */ 29,	/* 'e' */ 30,	/* 'f' */ 31,	/* 'g' */ 32,
	/* 'h' */ 33,	/* 'i' */ 34,	/* 'j' */ 35,	/* 'k' */ 36,
	/* 'l' */ 37,	/* 'm' */ 38,	/* 'n' */ 39,	/* 'o' */ 40,
	/* 'p' */ 41,	/* 'q' */ 42,	/* 'r' */ 43,	/* 's' */ 44,
	/* 't' */ 45,	/* 'u' */ 46,	/* 'v' */ 47,	/* 'w' */ 48,
	/* 'x' */ 49,	/* 'y' */ 50,	/* 'z' */ 51,	/* '{' */ -3,
	/* '|' */ -3,	/* '}' */ -3,	/* '~' */ -3,	/* 0x7f */ -3
};

const u_int8_t kBits_00000011 = 0x03;
const u_int8_t kBits_00001111 = 0x0F;
const u_int8_t kBits_00110000 = 0x30;
const u_int8_t kBits_00111100 = 0x3C;
const u_int8_t kBits_00111111 = 0x3F;
const u_int8_t kBits_11000000 = 0xC0;
const u_int8_t kBits_11110000 = 0xF0;
const u_int8_t kBits_11111100 = 0xFC;

size_t EstimateBas64EncodedDataSize(size_t inDataSize)
{
    size_t theEncodedDataSize = (int)ceil(inDataSize / 3.0) * 4;
    theEncodedDataSize = theEncodedDataSize / 72 * 74 + theEncodedDataSize % 72;
    return(theEncodedDataSize);
}

size_t EstimateBas64DecodedDataSize(size_t inDataSize)
{
    size_t theDecodedDataSize = (int)ceil(inDataSize / 4.0) * 3;
    //theDecodedDataSize = theDecodedDataSize / 72 * 74 + theDecodedDataSize % 72;
    return(theDecodedDataSize);
}

bool Base64EncodeData(const void *inInputData, size_t inInputDataSize, char *outOutputData, size_t *ioOutputDataSize)
{
    size_t theEncodedDataSize = EstimateBas64EncodedDataSize(inInputDataSize);
    if (*ioOutputDataSize < theEncodedDataSize)
        return(false);
    *ioOutputDataSize = theEncodedDataSize;
    const u_int8_t *theInPtr = (const u_int8_t *)inInputData;
    u_int32_t theInIndex = 0, theOutIndex = 0;
    for (; theInIndex < (inInputDataSize / 3) * 3; theInIndex += 3)
	{
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_11111100) >> 2];
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_00000011) << 4 | (theInPtr[theInIndex + 1] & kBits_11110000) >> 4];
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex + 1] & kBits_00001111) << 2 | (theInPtr[theInIndex + 2] & kBits_11000000) >> 6];
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex + 2] & kBits_00111111) >> 0];
        if (theOutIndex % 74 == 72)
		{
            outOutputData[theOutIndex++] = '\r';
            outOutputData[theOutIndex++] = '\n';
		}
	}
    const size_t theRemainingBytes = inInputDataSize - theInIndex;
    if (theRemainingBytes == 1)
	{
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_11111100) >> 2];
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_00000011) << 4 | (0 & kBits_11110000) >> 4];
        outOutputData[theOutIndex++] = '=';
        outOutputData[theOutIndex++] = '=';
        if (theOutIndex % 74 == 72)
		{
            outOutputData[theOutIndex++] = '\r';
            outOutputData[theOutIndex++] = '\n';
		}
	}
    else if (theRemainingBytes == 2)
	{
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_11111100) >> 2];
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_00000011) << 4 | (theInPtr[theInIndex + 1] & kBits_11110000) >> 4];
        outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex + 1] & kBits_00001111) << 2 | (0 & kBits_11000000) >> 6];
        outOutputData[theOutIndex++] = '=';
        if (theOutIndex % 74 == 72)
		{
            outOutputData[theOutIndex++] = '\r';
            outOutputData[theOutIndex++] = '\n';
		}
	}
    return(true);
}

bool Base64DecodeData(const void *inInputData, size_t inInputDataSize, void *ioOutputData, size_t *ioOutputDataSize)
{
    memset(ioOutputData, '.', *ioOutputDataSize);
    
    size_t theDecodedDataSize = EstimateBas64DecodedDataSize(inInputDataSize);
    if (*ioOutputDataSize < theDecodedDataSize)
        return(false);
    *ioOutputDataSize = 0;
    const u_int8_t *theInPtr = (const u_int8_t *)inInputData;
    u_int8_t *theOutPtr = (u_int8_t *)ioOutputData;
    size_t theInIndex = 0, theOutIndex = 0;
    u_int8_t theOutputOctet;
    size_t theSequence = 0;
    for (; theInIndex < inInputDataSize; )
	{
        int8_t theSextet = 0;
        
        int8_t theCurrentInputOctet = theInPtr[theInIndex];
        theSextet = kBase64DecodeTable[theCurrentInputOctet];
        if (theSextet == -1)
            break;
        while (theSextet == -2)
		{
            theCurrentInputOctet = theInPtr[++theInIndex];
            theSextet = kBase64DecodeTable[theCurrentInputOctet];
		}
        while (theSextet == -3)
		{
            theCurrentInputOctet = theInPtr[++theInIndex];
            theSextet = kBase64DecodeTable[theCurrentInputOctet];
		}
        if (theSequence == 0)
		{
            theOutputOctet = (theSextet >= 0 ? theSextet : 0) << 2 & kBits_11111100;
		}
        else if (theSequence == 1)
		{
            theOutputOctet |= (theSextet >- 0 ? theSextet : 0) >> 4 & kBits_00000011;
            theOutPtr[theOutIndex++] = theOutputOctet;
		}
        else if (theSequence == 2)
		{
            theOutputOctet = (theSextet >= 0 ? theSextet : 0) << 4 & kBits_11110000;
		}
        else if (theSequence == 3)
		{
            theOutputOctet |= (theSextet >= 0 ? theSextet : 0) >> 2 & kBits_00001111;
            theOutPtr[theOutIndex++] = theOutputOctet;
		}
        else if (theSequence == 4)
		{
            theOutputOctet = (theSextet >= 0 ? theSextet : 0) << 6 & kBits_11000000;
		}
        else if (theSequence == 5)
		{
            theOutputOctet |= (theSextet >= 0 ? theSextet : 0) >> 0 & kBits_00111111;
            theOutPtr[theOutIndex++] = theOutputOctet;
		}
        theSequence = (theSequence + 1) % 6;
        if (theSequence != 2 && theSequence != 4)
            theInIndex++;
	}
    *ioOutputDataSize = theOutIndex;
    return(true);
}

OAUTHnesia.h

#import 

@interface OAUTHnesia : NSObject {
@protected
    NSString *CONSUMER_KEY;
    NSString *CONSUMER_SECRET;
    NSString *API_BASE_URL;
}

@property(nonatomic, retain) NSString *CONSUMER_KEY;
@property(nonatomic, retain) NSString *CONSUMER_SECRET;
@property(nonatomic, retain) NSString *USER_KEY;
@property(nonatomic, retain) NSString *USER_SECRET;
@property(nonatomic, retain) NSString *API_BASE_URL;
@property(nonatomic, retain) NSString *POST_URL;
@property(nonatomic, retain) NSString *POST_DATA;

- (id)initWithKey:(NSString *)cons_key secret:(NSString *)cons_secret;
- (void) setUserKey: (NSString *)user_key;
- (void) setUserSecret: (NSString *)user_secret;
- (NSString *) encodeForOauth: (NSString *) postget;
- (NSString *) getNonce;
- (NSString *) getTime;
- (NSString *) generateBaseSignature: (NSString *) base_sig uri:(NSString *) uri;
- (NSString *) hmacsha1: (NSString *)text key:(NSString *)secret;
- (NSString *) md5: (NSString *)text;
- (NSString *) urlencode: (NSString *)text;

- (void) oAuth:(NSString *)oUri oPost:(NSString *)oPost oGet:(NSString *)oGet;

@end

OAUTHnesia.m

#import "OAUTHnesia.h"
//
//  OAUTHnesia.m
//  Jajan
//
//  Created by Batista Harahap on 8/14/11.
//  Copyright 2011 Urbanesia.com. All rights reserved.
//

#import "OAUTHnesia.h"
#include "Base64Transcoder.h"
#import 
#import 

@implementation OAUTHnesia
@synthesize CONSUMER_KEY, CONSUMER_SECRET, USER_KEY, USER_SECRET, API_BASE_URL, POST_URL, POST_DATA;

- (id)initWithKey:(NSString *)cons_key secret:(NSString *)cons_secret {
    if (self = [super init]) {
        self.CONSUMER_KEY = cons_key;
        self.CONSUMER_SECRET = cons_secret;
        self.API_BASE_URL = @"http://api1.urbanesia.com/";
        self.POST_URL = self.POST_DATA = @"";
    }
    
    return self;
}

- (void)oAuth:(NSString *)oUri oPost:(NSString *)oPost oGet:(NSString *)oGet {
    // Post yang pasti harus ada!
    NSString *postIncludes = [NSString stringWithFormat:
        @"oauth_consumer_key=%@&oauth_nonce=%@&oauth_signature_method=HMAC-SHA1&oauth_timestamp=%@&oauth_token=%@&oauth_version=1.0",
        self.CONSUMER_KEY, self.getNonce, self.getTime, self.USER_KEY];
    
    // Cek ada POST ga di request?
    if([oPost isEqual:@""]) {
        oPost = postIncludes;
    } else {
        oPost = [NSString stringWithFormat:@"%@&%@", oPost, postIncludes];
    }
    
    // Cek ada GET ga di request?
    if(![oGet isEqual:@""]) {
        oGet = [NSString stringWithFormat:@"&%@", oGet];
    }
    
    // Encode for OAUTH & Generate Base Signature
    NSString *base_sig = [self generateBaseSignature:[self encodeForOauth:[NSString stringWithFormat:@"%@%@", oPost, oGet]] uri:oUri];
    
    // OAUTH Signature
    NSString *signature = [self hmacsha1:[NSString stringWithFormat:@"%@&%@", self.CONSUMER_SECRET, self.USER_SECRET] key:base_sig];
    
    // POST URL
    self.POST_URL = [NSString stringWithFormat:@"%@%@?oauth_signature=%@%@", self.API_BASE_URL, oUri, signature, oGet];
    
    // POST DATA
    self.POST_DATA = oPost;
}

- (void)setUserKey:(NSString *)user_key {
    self.USER_KEY = user_key;
}

- (void)setUserSecret:(NSString *)user_secret {
    self.USER_SECRET = user_secret;
}

- (NSString *)getNonce {
    NSTimeInterval timePassed_ms = [[NSDate date] timeIntervalSinceNow] * -1000.0;
    return [self md5:[NSString stringWithFormat:@"%@", timePassed_ms]];
}

- (NSString *)getTime {
    NSTimeInterval timePassed_ms = [[NSDate date] timeIntervalSinceNow] * -1000.0;
    return [NSString stringWithFormat:@"%@", timePassed_ms];
}

- (NSString *)generateBaseSignature:(NSString *)base_sig uri:(NSString *)uri {
    NSString *dest = [NSString stringWithFormat:@"%@%@", self.API_BASE_URL, uri];
    dest = [self urlencode:dest];
    base_sig = [self urlencode:base_sig];
    return [NSString stringWithFormat:@"POST&%@&%@", dest, base_sig];
}

- (NSString *)encodeForOauth: (NSString *)postget {
    // Explode to array
    NSArray *par = [postget componentsSeparatedByString:@"&"];
    
    // Sort by array keys
    par = [par sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
    
    NSString *ret = @"";
    
    for(int i=0, j=0, max=par.count; i