diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..896fff6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.DS_Store + +project.xcworkspace +xcuserdata diff --git a/TwitterKit.xcodeproj/project.pbxproj b/TwitterKit.xcodeproj/project.pbxproj new file mode 100644 index 0000000..1d3cc1b --- /dev/null +++ b/TwitterKit.xcodeproj/project.pbxproj @@ -0,0 +1,318 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 054089C81423EA800096B897 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 054089C71423EA800096B897 /* Foundation.framework */; }; + 054089DE1423EAB20096B897 /* TKRequestMethod.h in Headers */ = {isa = PBXBuildFile; fileRef = 054089D81423EAB20096B897 /* TKRequestMethod.h */; }; + 054089DF1423EAB20096B897 /* TKRequestMethod.m in Sources */ = {isa = PBXBuildFile; fileRef = 054089D91423EAB20096B897 /* TKRequestMethod.m */; }; + 054089E01423EAB20096B897 /* TKTwitterRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 054089DA1423EAB20096B897 /* TKTwitterRequest.h */; }; + 054089E11423EAB20096B897 /* TKTwitterRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 054089DB1423EAB20096B897 /* TKTwitterRequest.m */; }; + 054089FB1423EB0C0096B897 /* TKBase64Transcoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 054089F41423EB0C0096B897 /* TKBase64Transcoder.c */; }; + 054089FC1423EB0C0096B897 /* TKBase64Transcoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 054089F51423EB0C0096B897 /* TKBase64Transcoder.h */; }; + 054089FD1423EB0C0096B897 /* TKOAHMACSHA1SignatureProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 054089F61423EB0C0096B897 /* TKOAHMACSHA1SignatureProvider.h */; }; + 054089FE1423EB0C0096B897 /* TKOAHMACSHA1SignatureProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 054089F71423EB0C0096B897 /* TKOAHMACSHA1SignatureProvider.m */; }; + 054089FF1423EB0C0096B897 /* TKOASignatureProviding.h in Headers */ = {isa = PBXBuildFile; fileRef = 054089F81423EB0C0096B897 /* TKOASignatureProviding.h */; }; + 05408A001423EB0C0096B897 /* TKTwitterOAuthSignature.h in Headers */ = {isa = PBXBuildFile; fileRef = 054089F91423EB0C0096B897 /* TKTwitterOAuthSignature.h */; }; + 05408A011423EB0C0096B897 /* TKTwitterOAuthSignature.m in Sources */ = {isa = PBXBuildFile; fileRef = 054089FA1423EB0C0096B897 /* TKTwitterOAuthSignature.m */; }; + 05408A141423EB280096B897 /* NSDictionary+TKTwitterRequestHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 05408A0E1423EB280096B897 /* NSDictionary+TKTwitterRequestHelpers.h */; }; + 05408A151423EB280096B897 /* NSDictionary+TKTwitterRequestHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 05408A0F1423EB280096B897 /* NSDictionary+TKTwitterRequestHelpers.m */; }; + 05408A161423EB280096B897 /* NSError+TKTwitterRequestHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 05408A101423EB280096B897 /* NSError+TKTwitterRequestHelpers.h */; }; + 05408A171423EB280096B897 /* NSError+TKTwitterRequestHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 05408A111423EB280096B897 /* NSError+TKTwitterRequestHelpers.m */; }; + 05408A181423EB280096B897 /* NSString+TKOAURLEncodingAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 05408A121423EB280096B897 /* NSString+TKOAURLEncodingAdditions.h */; }; + 05408A191423EB280096B897 /* NSString+TKOAURLEncodingAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 05408A131423EB280096B897 /* NSString+TKOAURLEncodingAdditions.m */; }; + 0562A85D1425AE0900A6A9F2 /* TKTwitterOAuthAuthenticator.h in Headers */ = {isa = PBXBuildFile; fileRef = 0562A85B1425AE0900A6A9F2 /* TKTwitterOAuthAuthenticator.h */; }; + 0562A85E1425AE0900A6A9F2 /* TKTwitterOAuthAuthenticator.m in Sources */ = {isa = PBXBuildFile; fileRef = 0562A85C1425AE0900A6A9F2 /* TKTwitterOAuthAuthenticator.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 054089C41423EA800096B897 /* libTwitterKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTwitterKit.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 054089C71423EA800096B897 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 054089CB1423EA800096B897 /* TwitterKit-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TwitterKit-Prefix.pch"; sourceTree = ""; }; + 054089D81423EAB20096B897 /* TKRequestMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TKRequestMethod.h; sourceTree = ""; }; + 054089D91423EAB20096B897 /* TKRequestMethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TKRequestMethod.m; sourceTree = ""; }; + 054089DA1423EAB20096B897 /* TKTwitterRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TKTwitterRequest.h; sourceTree = ""; }; + 054089DB1423EAB20096B897 /* TKTwitterRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TKTwitterRequest.m; sourceTree = ""; }; + 054089F41423EB0C0096B897 /* TKBase64Transcoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = TKBase64Transcoder.c; path = oauth/TKBase64Transcoder.c; sourceTree = ""; }; + 054089F51423EB0C0096B897 /* TKBase64Transcoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TKBase64Transcoder.h; path = oauth/TKBase64Transcoder.h; sourceTree = ""; }; + 054089F61423EB0C0096B897 /* TKOAHMACSHA1SignatureProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TKOAHMACSHA1SignatureProvider.h; path = oauth/TKOAHMACSHA1SignatureProvider.h; sourceTree = ""; }; + 054089F71423EB0C0096B897 /* TKOAHMACSHA1SignatureProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TKOAHMACSHA1SignatureProvider.m; path = oauth/TKOAHMACSHA1SignatureProvider.m; sourceTree = ""; }; + 054089F81423EB0C0096B897 /* TKOASignatureProviding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TKOASignatureProviding.h; path = oauth/TKOASignatureProviding.h; sourceTree = ""; }; + 054089F91423EB0C0096B897 /* TKTwitterOAuthSignature.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TKTwitterOAuthSignature.h; path = oauth/TKTwitterOAuthSignature.h; sourceTree = ""; }; + 054089FA1423EB0C0096B897 /* TKTwitterOAuthSignature.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TKTwitterOAuthSignature.m; path = oauth/TKTwitterOAuthSignature.m; sourceTree = ""; }; + 05408A0E1423EB280096B897 /* NSDictionary+TKTwitterRequestHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+TKTwitterRequestHelpers.h"; path = "shared/NSDictionary+TKTwitterRequestHelpers.h"; sourceTree = ""; }; + 05408A0F1423EB280096B897 /* NSDictionary+TKTwitterRequestHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+TKTwitterRequestHelpers.m"; path = "shared/NSDictionary+TKTwitterRequestHelpers.m"; sourceTree = ""; }; + 05408A101423EB280096B897 /* NSError+TKTwitterRequestHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSError+TKTwitterRequestHelpers.h"; path = "shared/NSError+TKTwitterRequestHelpers.h"; sourceTree = ""; }; + 05408A111423EB280096B897 /* NSError+TKTwitterRequestHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSError+TKTwitterRequestHelpers.m"; path = "shared/NSError+TKTwitterRequestHelpers.m"; sourceTree = ""; }; + 05408A121423EB280096B897 /* NSString+TKOAURLEncodingAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+TKOAURLEncodingAdditions.h"; path = "shared/NSString+TKOAURLEncodingAdditions.h"; sourceTree = ""; }; + 05408A131423EB280096B897 /* NSString+TKOAURLEncodingAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+TKOAURLEncodingAdditions.m"; path = "shared/NSString+TKOAURLEncodingAdditions.m"; sourceTree = ""; }; + 0562A85B1425AE0900A6A9F2 /* TKTwitterOAuthAuthenticator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TKTwitterOAuthAuthenticator.h; sourceTree = ""; }; + 0562A85C1425AE0900A6A9F2 /* TKTwitterOAuthAuthenticator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TKTwitterOAuthAuthenticator.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 054089C11423EA800096B897 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 054089C81423EA800096B897 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 054089B91423EA800096B897 = { + isa = PBXGroup; + children = ( + 054089C91423EA800096B897 /* TwitterKit */, + 054089C61423EA800096B897 /* Frameworks */, + 054089C51423EA800096B897 /* Products */, + ); + sourceTree = ""; + }; + 054089C51423EA800096B897 /* Products */ = { + isa = PBXGroup; + children = ( + 054089C41423EA800096B897 /* libTwitterKit.a */, + ); + name = Products; + sourceTree = ""; + }; + 054089C61423EA800096B897 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 054089C71423EA800096B897 /* Foundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 054089C91423EA800096B897 /* TwitterKit */ = { + isa = PBXGroup; + children = ( + 054089D81423EAB20096B897 /* TKRequestMethod.h */, + 054089D91423EAB20096B897 /* TKRequestMethod.m */, + 054089DA1423EAB20096B897 /* TKTwitterRequest.h */, + 054089DB1423EAB20096B897 /* TKTwitterRequest.m */, + 0562A85B1425AE0900A6A9F2 /* TKTwitterOAuthAuthenticator.h */, + 0562A85C1425AE0900A6A9F2 /* TKTwitterOAuthAuthenticator.m */, + 054089E41423EAB70096B897 /* oauth */, + 054089E51423EABE0096B897 /* shared */, + 054089CA1423EA800096B897 /* Supporting Files */, + ); + path = TwitterKit; + sourceTree = ""; + }; + 054089CA1423EA800096B897 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 054089CB1423EA800096B897 /* TwitterKit-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 054089E41423EAB70096B897 /* oauth */ = { + isa = PBXGroup; + children = ( + 054089F61423EB0C0096B897 /* TKOAHMACSHA1SignatureProvider.h */, + 054089F71423EB0C0096B897 /* TKOAHMACSHA1SignatureProvider.m */, + 054089F81423EB0C0096B897 /* TKOASignatureProviding.h */, + 054089F91423EB0C0096B897 /* TKTwitterOAuthSignature.h */, + 054089FA1423EB0C0096B897 /* TKTwitterOAuthSignature.m */, + 054089F41423EB0C0096B897 /* TKBase64Transcoder.c */, + 054089F51423EB0C0096B897 /* TKBase64Transcoder.h */, + ); + name = oauth; + sourceTree = ""; + }; + 054089E51423EABE0096B897 /* shared */ = { + isa = PBXGroup; + children = ( + 05408A0E1423EB280096B897 /* NSDictionary+TKTwitterRequestHelpers.h */, + 05408A0F1423EB280096B897 /* NSDictionary+TKTwitterRequestHelpers.m */, + 05408A101423EB280096B897 /* NSError+TKTwitterRequestHelpers.h */, + 05408A111423EB280096B897 /* NSError+TKTwitterRequestHelpers.m */, + 05408A121423EB280096B897 /* NSString+TKOAURLEncodingAdditions.h */, + 05408A131423EB280096B897 /* NSString+TKOAURLEncodingAdditions.m */, + ); + name = shared; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 054089C21423EA800096B897 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 054089DE1423EAB20096B897 /* TKRequestMethod.h in Headers */, + 054089E01423EAB20096B897 /* TKTwitterRequest.h in Headers */, + 054089FC1423EB0C0096B897 /* TKBase64Transcoder.h in Headers */, + 054089FD1423EB0C0096B897 /* TKOAHMACSHA1SignatureProvider.h in Headers */, + 054089FF1423EB0C0096B897 /* TKOASignatureProviding.h in Headers */, + 05408A001423EB0C0096B897 /* TKTwitterOAuthSignature.h in Headers */, + 05408A141423EB280096B897 /* NSDictionary+TKTwitterRequestHelpers.h in Headers */, + 05408A161423EB280096B897 /* NSError+TKTwitterRequestHelpers.h in Headers */, + 05408A181423EB280096B897 /* NSString+TKOAURLEncodingAdditions.h in Headers */, + 0562A85D1425AE0900A6A9F2 /* TKTwitterOAuthAuthenticator.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 054089C31423EA800096B897 /* TwitterKit */ = { + isa = PBXNativeTarget; + buildConfigurationList = 054089D11423EA800096B897 /* Build configuration list for PBXNativeTarget "TwitterKit" */; + buildPhases = ( + 054089C01423EA800096B897 /* Sources */, + 054089C11423EA800096B897 /* Frameworks */, + 054089C21423EA800096B897 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TwitterKit; + productName = TwitterKit; + productReference = 054089C41423EA800096B897 /* libTwitterKit.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 054089BB1423EA800096B897 /* Project object */ = { + isa = PBXProject; + attributes = { + ORGANIZATIONNAME = "High Order Bit"; + }; + buildConfigurationList = 054089BE1423EA800096B897 /* Build configuration list for PBXProject "TwitterKit" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 054089B91423EA800096B897; + productRefGroup = 054089C51423EA800096B897 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 054089C31423EA800096B897 /* TwitterKit */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 054089C01423EA800096B897 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 054089DF1423EAB20096B897 /* TKRequestMethod.m in Sources */, + 054089E11423EAB20096B897 /* TKTwitterRequest.m in Sources */, + 054089FB1423EB0C0096B897 /* TKBase64Transcoder.c in Sources */, + 054089FE1423EB0C0096B897 /* TKOAHMACSHA1SignatureProvider.m in Sources */, + 05408A011423EB0C0096B897 /* TKTwitterOAuthSignature.m in Sources */, + 05408A151423EB280096B897 /* NSDictionary+TKTwitterRequestHelpers.m in Sources */, + 05408A171423EB280096B897 /* NSError+TKTwitterRequestHelpers.m in Sources */, + 05408A191423EB280096B897 /* NSString+TKOAURLEncodingAdditions.m in Sources */, + 0562A85E1425AE0900A6A9F2 /* TKTwitterOAuthAuthenticator.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 054089CF1423EA800096B897 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = com.apple.compilers.llvmgcc42; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 4.3; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 054089D01423EA800096B897 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + COPY_PHASE_STRIP = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_VERSION = com.apple.compilers.llvmgcc42; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 4.3; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 054089D21423EA800096B897 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DSTROOT = /tmp/TwitterKit.dst; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "TwitterKit/TwitterKit-Prefix.pch"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 054089D31423EA800096B897 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DSTROOT = /tmp/TwitterKit.dst; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "TwitterKit/TwitterKit-Prefix.pch"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 054089BE1423EA800096B897 /* Build configuration list for PBXProject "TwitterKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 054089CF1423EA800096B897 /* Debug */, + 054089D01423EA800096B897 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 054089D11423EA800096B897 /* Build configuration list for PBXNativeTarget "TwitterKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 054089D21423EA800096B897 /* Debug */, + 054089D31423EA800096B897 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 054089BB1423EA800096B897 /* Project object */; +} diff --git a/TwitterKit/TKRequestMethod.h b/TwitterKit/TKRequestMethod.h new file mode 100644 index 0000000..6503744 --- /dev/null +++ b/TwitterKit/TKRequestMethod.h @@ -0,0 +1,22 @@ +// +// TRRequestMethod.h +// Twitbit +// +// Created by John Debay on 7/18/11. +// Copyright 2011 High Order Bit. All rights reserved. +// + +#import + +typedef enum { + TKRequestMethodGET, + TKRequestMethodPOST, + TKRequestMethodDELETE +} TKRequestMethod; + + +@interface NSString (TRRequestMethodHelpers) + ++ (id)stringForRequestMethod:(TKRequestMethod)requestMethod; + +@end \ No newline at end of file diff --git a/TwitterKit/TKRequestMethod.m b/TwitterKit/TKRequestMethod.m new file mode 100644 index 0000000..f49bb91 --- /dev/null +++ b/TwitterKit/TKRequestMethod.m @@ -0,0 +1,32 @@ +// +// TRRequestMethod.m +// Twitbit +// +// Created by John Debay on 7/18/11. +// Copyright 2011 High Order Bit. All rights reserved. +// + +#import "TKRequestMethod.h" + +@implementation NSString (TKRequestMethodHelpers) + ++ (id)stringForRequestMethod:(TKRequestMethod)requestMethod +{ + NSString *s = nil; + + switch (requestMethod) { + case TKRequestMethodGET: + s = @"GET"; + break; + case TKRequestMethodPOST: + s = @"POST"; + break; + case TKRequestMethodDELETE: + s = @"DELETE"; + break; + } + + return s; +} + +@end diff --git a/TwitterKit/TKTwitterOAuthAuthenticator.h b/TwitterKit/TKTwitterOAuthAuthenticator.h new file mode 100644 index 0000000..46ff77c --- /dev/null +++ b/TwitterKit/TKTwitterOAuthAuthenticator.h @@ -0,0 +1,41 @@ +// +// TwitterOAuthService.h +// Twitbit +// +// Created by John Debay on 7/16/11. +// Copyright 2011 High Order Bit. All rights reserved. +// + +#import + +typedef void(^TKTokenCompletion)(NSString *urlQueryString, NSError *error); +typedef void(^TKCredentialsCompletion)(NSDictionary *creds, NSError *error); + +@interface TKTwitterOAuthAuthenticator : NSObject + +@property (nonatomic, copy, readonly) NSString *consumerKey; +@property (nonatomic, copy, readonly) NSString *consumerSecret; + +#pragma mark - Initialization + +- (id)initWithConsumerKey:(NSString *)key consumerSecret:(NSString *)secret; + +#pragma mark - Fetching the token + +- (void)fetchTwitterTokenWithCallbackURL:(NSURL *)callbackURL + completion:(TKTokenCompletion)completion; + +#pragma mark - Authorizing the token + +- (void)authenticateTwitterToken:(NSString *)token + withVerifier:(NSString *)verifier + completion:(TKCredentialsCompletion)completion; + +#pragma mark - URLs + ++ (NSURL *)twitterTokenURL; ++ (NSURL *)authenticationURLForOAuthToken:(NSString *)token; + ++ (NSURL *)twitterAuthorizationURL; + +@end diff --git a/TwitterKit/TKTwitterOAuthAuthenticator.m b/TwitterKit/TKTwitterOAuthAuthenticator.m new file mode 100644 index 0000000..c803c95 --- /dev/null +++ b/TwitterKit/TKTwitterOAuthAuthenticator.m @@ -0,0 +1,334 @@ +// +// TwitterOAuthService.m +// Twitbit +// +// Created by John Debay on 7/16/11. +// Copyright 2011 High Order Bit. All rights reserved. +// + +#import "TKTwitterOAuthAuthenticator.h" + +#import "TKOAHMACSHA1SignatureProvider.h" +#import "NSString+TKOAURLEncodingAdditions.h" +#import "NSError+TKTwitterRequestHelpers.h" + +typedef void(^TKConnectionCompletion)(NSURLResponse * response, + NSData *data, + NSError *error); + +static NSString *OAUTH_SIGNATURE_METHOD = @"HMAC-SHA1"; +static NSString *OAUTH_VERSION = @"1.0"; + + +@interface TKTwitterOAuthAuthenticator () + +- (NSDictionary *)oauthComponentsForParams:(NSDictionary *)params; + +- (NSString *)oauthSignatureBase:(NSString *)httpMethod + withUrl:(NSString *)url + andOauthComponents:(NSDictionary *)parts; + +- (NSString *)oauthAuthorizationHeader:(NSString *)oauthSignature + withOauthComponents:(NSDictionary *)components; + +- (NSString *)oauthHeaderForMethod:(NSString *)method + andUrl:(NSString *)url + andParams:(NSDictionary *)params + andTokenSecret:(NSString *)tokenSecret; + +- (void)performRequest:(NSURLRequest *)request + completion:(TKConnectionCompletion)completion; + +@end + + +@implementation TKTwitterOAuthAuthenticator + +@synthesize consumerKey = consumerKey_; +@synthesize consumerSecret = consumerSecret_; + +#pragma mark - Memory management + +- (void)dealloc +{ + [consumerKey_ release]; + [consumerSecret_ release]; + + [super dealloc]; +} + +#pragma mark - Initialization + +- (id)initWithConsumerKey:(NSString *)key consumerSecret:(NSString *)secret +{ + self = [super init]; + if (self) { + consumerKey_ = [key copy]; + consumerSecret_ = [secret copy]; + } + + return self; +} + +#pragma mark - Fetching the token + +- (void)fetchTwitterTokenWithCallbackURL:(NSURL *)callbackURL + completion:(TKTokenCompletion)tokenCompletion +{ + NSURL *url = [[self class] twitterTokenURL]; + NSDictionary *params = + [NSDictionary dictionaryWithObject:[callbackURL absoluteString] + forKey:@"oauth_callback"]; + NSString *oauthHeader = + [self oauthHeaderForMethod:@"POST" + andUrl:[url absoluteString] + andParams:params + andTokenSecret:@""]; + + NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url]; + [req setHTTPMethod:@"POST"]; + [req addValue:oauthHeader forHTTPHeaderField:@"Authorization"]; + + [self performRequest:req + completion: + ^(NSURLResponse *response, NSData *data, NSError *error) { + NSString *token = nil; + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response; + if ([httpResponse statusCode] == 200 && data) + token = [[[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding] + autorelease]; + else + // jad: Parameters weren't in the response, so just throw + // up a Twitter error. I'm fixing this bug because I first + // saw it when getting over capacity messages, so that's + // the message I'm putting here, whether or not it's actually true. + error = [NSError tk_twitterOvercapacityError]; + + tokenCompletion(token, error); + }]; +} + +#pragma mark - Authorizing the token + +- (void)authenticateTwitterToken:(NSString *)token + withVerifier:(NSString *)verifier + completion:(TKCredentialsCompletion)credentialsCompletion +{ + NSURL *url = [[self class] twitterAuthorizationURL]; + + // We manually specify the token as a param, because it has not yet been + // authorized and the automatic state checking wouldn't include it in + // signature construction or header, since oauthTokenAuthorized is still NO + // by this point. + NSDictionary *params = + [NSDictionary dictionaryWithObjectsAndKeys: + token, @"oauth_token", verifier, @"oauth_verifier", nil]; + + NSString *header = [self oauthHeaderForMethod:@"POST" + andUrl:[url absoluteString] + andParams:params + andTokenSecret:[self consumerSecret]]; + + NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url]; + [req setHTTPMethod:@"POST"]; + [req addValue:header forHTTPHeaderField:@"Authorization"]; + + [self performRequest:req completion: + ^(NSURLResponse *response, NSData *data, NSError *error) { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response; + NSMutableDictionary *values = [NSMutableDictionary dictionary]; + + if ([httpResponse statusCode] == 200 && data) { + NSString *s = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + NSArray *comps = [s componentsSeparatedByString:@"&"]; + [s release], s = nil; + + for (NSString *comp in comps) { + NSArray *vals = [comp componentsSeparatedByString:@"="]; + if ([vals count] == 2) + [values setObject:[vals objectAtIndex:1] + forKey:[vals objectAtIndex:0]]; + else { + // jad: Parameters weren't in the response, so just throw + // up a Twitter error. I'm fixing this bug because I first + // saw it when getting over capacity messages, so that's + // the message I'm putting here, whether or not it's + // actually true. + error = [NSError tk_twitterOvercapacityError]; + break; + } + } + } + + credentialsCompletion(values, error); + }]; +} + +#pragma mark - URLs + ++ (NSURL *)twitterTokenURL +{ + return [NSURL URLWithString:@"https://api.twitter.com/oauth/request_token"]; +} + ++ (NSURL *)authenticationURLForOAuthToken:(NSString *)token +{ + // Add the force_login parameter per recommendation by Twitter here: + // + // http://dev.twitter.com/pages/application-permission-model-faq + // + NSString *urlString = + [NSString stringWithFormat: + @"https://api.twitter.com/oauth/authorize?%@&force_login=true", token]; + return [NSURL URLWithString:urlString]; +} + ++ (NSURL *)twitterAuthorizationURL +{ + return [NSURL URLWithString:@"https://api.twitter.com/oauth/access_token"]; +} + +#pragma mark - Helper methods + +- (NSDictionary *)oauthComponentsForParams:(NSDictionary *)params +{ + // Freshen the context. Get a fresh timestamp and calculate a nonce. + // Nonce algorithm is sha1(timestamp || random), i.e + // we concatenate timestamp with a random string, and then sha1 it. + int timestamp = time(NULL); + NSString *oauthTimestamp = [NSString stringWithFormat:@"%d", timestamp]; + int myRandom = random(); + NSString *oauthNonce = + [[NSString stringWithFormat:@"%d%d", timestamp, myRandom] tk_sha1]; + + NSMutableDictionary *parts = [NSMutableDictionary dictionary]; + [parts setObject:oauthTimestamp forKey:@"oauth_timestamp"]; + [parts setObject:oauthNonce forKey:@"oauth_nonce"]; + [parts setObject:OAUTH_SIGNATURE_METHOD forKey:@"oauth_signature_method"]; + [parts setObject:OAUTH_VERSION forKey:@"oauth_version"]; + [parts setObject:[self consumerKey] forKey:@"oauth_consumer_key"]; + + if (params) + [parts addEntriesFromDictionary:params]; + + return parts; +} + +- (NSString *)oauthSignatureBase:(NSString *)httpMethod + withUrl:(NSString *)url + andOauthComponents:(NSDictionary *)parts +{ + // Sort the base string components and make them into string key=value pairs + NSMutableArray *normalizedBase = + [NSMutableArray arrayWithCapacity:[parts count]]; + NSArray *sortedKeys = + [[parts allKeys] sortedArrayUsingSelector:@selector(compare:)]; + for (NSString *key in sortedKeys) { + NSString *s = + [NSString stringWithFormat:@"%@=%@", key, [parts objectForKey:key]]; + [normalizedBase addObject:s]; + } + + NSString *normalizedRequestParameters = + [normalizedBase componentsJoinedByString:@"&"]; + + // Return the signature base string. Note that the individual parameters + // must have previously already URL-encoded and here we are encoding them + // again; thus you will see some double URL-encoding for params. This is + // normal. + return [NSString stringWithFormat:@"%@&%@&%@", + httpMethod, + [url tk_encodedURLParameterString], + [normalizedRequestParameters tk_encodedURLParameterString]]; +} + +/** + * Given a calculated signature (by this point it is Base64-encoded string) and + * a set of parameters, return the header value that you will stick in the + * "Authorization" header. + */ +- (NSString *)oauthAuthorizationHeader:(NSString *)oauthSignature + withOauthComponents:(NSDictionary *)components +{ + NSMutableArray *chunks = [NSMutableArray array]; + + // First add all the base components. + [chunks addObject:[NSString stringWithString:@"OAuth realm=\"\""]]; + + for (NSString *part in components) { + NSString *value = [components valueForKey:part]; + NSString *s = + [NSString stringWithFormat:@"%@=\"%@\"", part, + /*[*/value /*encodedURLParameterString]*/]; + [chunks addObject:s]; + } + + // Signature will be the last component of our header. + NSString *signature = + [NSString stringWithFormat:@"%@=\"%@\"", @"oauth_signature", + [oauthSignature tk_encodedURLParameterString]]; + [chunks addObject:signature]; + + return [NSString stringWithFormat:@"%@", + [chunks componentsJoinedByString:@", "]]; +} + + + +- (NSString *)oauthHeaderForMethod:(NSString *)method + andUrl:(NSString *)url + andParams:(NSDictionary *)params + andTokenSecret:(NSString *)tokenSecret +{ + TKOAHMACSHA1SignatureProvider *sigProvider = + [[[TKOAHMACSHA1SignatureProvider alloc] init] autorelease]; + + // If there were any params, URLencode them. + NSMutableDictionary *encodedParams = + [NSMutableDictionary dictionaryWithCapacity:[params count]]; + if (params) + for (NSString *key in [params allKeys]) { + NSString *s = + [[params objectForKey:key] tk_encodedURLParameterString]; + [encodedParams setObject:s forKey:key]; + } + + NSString *consumerSecret = [self consumerSecret]; + NSString *secret = + [NSString stringWithFormat:@"%@&%@", consumerSecret, tokenSecret]; + + // Given a signature base and secret key, calculate the signature. + NSDictionary *components = [self oauthComponentsForParams:encodedParams]; + NSString *clearText = [self oauthSignatureBase:method + withUrl:url + andOauthComponents:components]; + NSString *signature = [sigProvider signClearText:clearText + withSecret:secret]; + + // Return the authorization header using the signature and parameters + // (if any). + return [self oauthAuthorizationHeader:signature + withOauthComponents:components]; +} + +- (void)performRequest:(NSURLRequest *)request + completion:(TKConnectionCompletion)completion +{ + dispatch_queue_t async_queue = + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_queue_t current_queue = dispatch_get_current_queue(); + + dispatch_async(async_queue, ^{ + NSURLResponse *response = nil; NSError *error = nil; + NSData *data = [NSURLConnection sendSynchronousRequest:request + returningResponse:&response + error:&error]; + dispatch_async(current_queue, ^{ + completion(response, data, error); + }); + }); +} + +@end diff --git a/TwitterKit/TKTwitterRequest.h b/TwitterKit/TKTwitterRequest.h new file mode 100644 index 0000000..456abdc --- /dev/null +++ b/TwitterKit/TKTwitterRequest.h @@ -0,0 +1,56 @@ +// +// TwitterRequest.h +// Twitbit +// +// Created by John Debay on 7/18/11. +// Copyright 2011 High Order Bit. All rights reserved. +// + +#import +#import "TKRequestMethod.h" + + +typedef void(^TKRequestHandler)(NSHTTPURLResponse *response, + NSData *responseData, + NSError *error); + + +@interface TKTwitterRequest : NSObject + +@property (nonatomic, copy, readonly) NSURL *url; +@property (nonatomic, copy, readonly) NSDictionary *parameters; +@property (nonatomic, assign, readonly) TKRequestMethod requestMethod; + +@property (nonatomic, copy, readonly) NSString *consumerKey; +@property (nonatomic, copy, readonly) NSString *consumerSecret; + +#pragma mark - Configuration + ++ (void)setDefaultConsumerKey:(NSString *)key consumerSecret:(NSString *)secret; + +#pragma mark - Initialization + +- (id)initWithURL:(NSURL *)url + parameters:(NSDictionary *)parameters + requestMethod:(TKRequestMethod)requestMethod; + +#pragma mark - OAuth + +// +// The request will use the default credentials unless specified here +// +- (void)setConsumerKey:(NSString *)key consumerSecret:(NSString *)secret; + +#pragma mark - Getting a signed request + +- (NSURLRequest *)signedRequestWithOAuthToken:(NSString *)token + tokenSecret:(NSString *)tokenSecret; + +#pragma mark - Sending the request + +- (void)performRequestWithHandler:(TKRequestHandler)handler; +- (void)performSignedRequestWithOAuthToken:(NSString *)token + tokenSecret:(NSString *)tokenSecret + handler:(TKRequestHandler)handler; + +@end diff --git a/TwitterKit/TKTwitterRequest.m b/TwitterKit/TKTwitterRequest.m new file mode 100644 index 0000000..e3e562d --- /dev/null +++ b/TwitterKit/TKTwitterRequest.m @@ -0,0 +1,268 @@ +// +// TwitterRequest.m +// Twitbit +// +// Created by John Debay on 7/18/11. +// Copyright 2011 High Order Bit. All rights reserved. +// + +#import "TKTwitterRequest.h" +#import "TKTwitterOAuthSignature.h" +#import "NSDictionary+TKTwitterRequestHelpers.h" + +#pragma mark - NSURL private category + +@interface NSURL (TKTwitterRequestPrivateHelpers) +- (NSURL *)URLByAppendingGetParameters:(NSDictionary *)params; +@end + +@implementation NSURL (TKTwitterRequestPrivateHelpers) +- (NSURL *)URLByAppendingGetParameters:(NSDictionary *)params +{ + NSMutableString *s = + [NSMutableString stringWithString:[self absoluteString]]; + + if ([params count]) { + [s appendString:@"?"]; + [s appendString:[params tk_URLParameterString]]; + } + + return [NSURL URLWithString:s]; +} +@end + + + +#pragma mark - TKTwitterRequest implementation + +static NSMutableDictionary *classOAuthCredentials_ = nil; + +#pragma mark - Private interface + +@interface TKTwitterRequest () +@property (nonatomic, copy) NSString *consumerKey; +@property (nonatomic, copy) NSString *consumerSecret; + +@property (nonatomic, copy, readonly) NSURL *fullUrl; + +@property (nonatomic, retain) NSURLConnection *connection; +@property (nonatomic, retain) NSHTTPURLResponse *connectionResponse; +@property (nonatomic, retain) NSMutableData *connectionData; +@property (nonatomic, retain) NSError *connectionError; +@property (nonatomic, copy) TKRequestHandler requestHandler; +@end + +@implementation TKTwitterRequest + +@synthesize url = url_; +@synthesize parameters = parameters_; +@synthesize requestMethod = requestMethod_; + +@synthesize consumerKey = consumerKey_; +@synthesize consumerSecret = consumerSecret_; + +@synthesize fullUrl = fullUrl_; + +@synthesize connection = connection_; +@synthesize connectionResponse = connectionResponse_; +@synthesize connectionData = connectionData_; +@synthesize connectionError = connectionError_; +@synthesize requestHandler = requestHandler_; + +#pragma mark - Configuration + ++ (void)setDefaultConsumerKey:(NSString *)key consumerSecret:(NSString *)secret +{ + if (!classOAuthCredentials_) + classOAuthCredentials_ = [[NSMutableDictionary alloc] init]; + + [classOAuthCredentials_ setValue:key forKey:@"key"]; + [classOAuthCredentials_ setValue:secret forKey:@"secret"]; +} + ++ (NSString *)defaultConsumerKey +{ + return [classOAuthCredentials_ valueForKey:@"key"]; +} + ++ (NSString *)defaultConsumerSecret +{ + return [classOAuthCredentials_ valueForKey:@"secret"]; +} + +#pragma mark - Memory management + +- (void)dealloc +{ + [url_ release]; + [parameters_ release]; + + [consumerKey_ release]; + [consumerSecret_ release]; + + [fullUrl_ release]; + + [connection_ cancel]; + [connection_ release]; + [connectionResponse_ release]; + [connectionData_ release]; + [connectionError_ release]; + [requestHandler_ release]; + + [super dealloc]; +} + +#pragma mark - Initialization + +- (id)initWithURL:(NSURL *)url + parameters:(NSDictionary *)parameters + requestMethod:(TKRequestMethod)requestMethod +{ + self = [super init]; + if (self) { + url_ = [url copy]; + parameters_ = [parameters copy]; + requestMethod_ = requestMethod; + } + + return self; +} + +#pragma mark - OAuth + +- (void)setConsumerKey:(NSString *)key consumerSecret:(NSString *)secret +{ + [self setConsumerKey:key]; + [self setConsumerSecret:secret]; +} + +#pragma mark - Getting a signed request + +- (NSURLRequest *)signedRequestWithOAuthToken:(NSString *)token + tokenSecret:(NSString *)tokenSecret +{ + NSString *consumerKey = + [self consumerKey] ? + [self consumerKey] : [[self class] defaultConsumerKey]; + NSString *consumerSecret = + [self consumerSecret] ? + [self consumerSecret] : [[self class] defaultConsumerSecret]; + + TKTwitterOAuthSignature *sig = + [[TKTwitterOAuthSignature alloc] initWithConsumerKey:consumerKey + consumerSecret:consumerSecret + token:token + tokenSecret:tokenSecret]; + NSURLRequest *req = [sig signedRequestForURL:[self fullUrl] + parameters:[self parameters] + requestMethod:[self requestMethod]]; + [sig release], sig = nil; + + return req; +} + +#pragma mark - Sending the request + +- (void)performRequestWithHandler:(TKRequestHandler)handler +{ + NSAssert2(0, @"%@: %@ - Not implemented.", + NSStringFromClass([self class]), NSStringFromSelector(_cmd)); +} + +- (void)performSignedRequestWithOAuthToken:(NSString *)token + tokenSecret:(NSString *)tokenSecret + handler:(TKRequestHandler)handler +{ + [self setRequestHandler:handler]; + + NSURLRequest *request = [self signedRequestWithOAuthToken:token + tokenSecret:tokenSecret]; + + dispatch_queue_t async_queue = + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_queue_t current_queue = dispatch_get_current_queue(); + dispatch_async(async_queue, ^{ + NSURLResponse *response = nil; NSError *error = nil; + NSData *data =[NSURLConnection sendSynchronousRequest:request + returningResponse:&response + error:&error]; + dispatch_async(current_queue, ^{ + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response; + handler(httpResponse, data, error); + }); + }); + + + /* + NSURLConnection *connection = [NSURLConnection connectionWithRequest:request + delegate:self]; + [self setConnection:connection]; + */ +} + +#pragma mark - Connection events + +- (void)processConnectionCompleted +{ + NSHTTPURLResponse *response = [self connectionResponse]; + NSData *data = [self connectionData]; + NSError *error = [self connectionError]; + + [self requestHandler](response, data, error); + + [self setConnectionResponse:nil]; + [self setConnectionData:nil]; + [self setConnectionError:nil]; + [self setConnection:nil]; +} + +#pragma mark - NSURLConnectionDelegate implementation + +- (void)connection:(NSURLConnection *)connection +didReceiveResponse:(NSURLResponse *)response +{ + [self setConnectionResponse:(NSHTTPURLResponse *) response]; + [self setConnectionData:[NSMutableData data]]; +} + +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + [[self connectionData] appendData:data]; +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + [self processConnectionCompleted]; +} + +- (void)connection:(NSURLConnection *)connection + didFailWithError:(NSError *)error +{ + [self setConnectionError:error]; + [self processConnectionCompleted]; +} + +#pragma mark - Accessors + +- (NSURL *)fullUrl +{ + if (!fullUrl_) { + switch ([self requestMethod]) { + case TKRequestMethodGET: + fullUrl_ = + [[self url] URLByAppendingGetParameters:[self parameters]]; + break; + case TKRequestMethodPOST: + fullUrl_ = [self url]; + break; + default: + NSAssert1(NO, @"Unknown request method: %d", + [self requestMethod]); + break; + } + } + + return fullUrl_; +} + +@end diff --git a/TwitterKit/TwitterKit-Prefix.pch b/TwitterKit/TwitterKit-Prefix.pch new file mode 100644 index 0000000..9347c4c --- /dev/null +++ b/TwitterKit/TwitterKit-Prefix.pch @@ -0,0 +1,7 @@ +// +// Prefix header for all source files of the 'TwitterKit' target in the 'TwitterKit' project +// + +#ifdef __OBJC__ + #import +#endif diff --git a/TwitterKit/oauth/TKBase64Transcoder.c b/TwitterKit/oauth/TKBase64Transcoder.c new file mode 100644 index 0000000..61c2b3e --- /dev/null +++ b/TwitterKit/oauth/TKBase64Transcoder.c @@ -0,0 +1,230 @@ +/* + * TKBase64Transcoder.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 "TKBase64Transcoder.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 tk_EstimateBas64EncodedDataSize(size_t inDataSize) +{ +size_t theEncodedDataSize = (int)ceil(inDataSize / 3.0) * 4; +theEncodedDataSize = theEncodedDataSize / 72 * 74 + theEncodedDataSize % 72; +return(theEncodedDataSize); +} + +size_t tk_EstimateBas64DecodedDataSize(size_t inDataSize) +{ +size_t theDecodedDataSize = (int)ceil(inDataSize / 4.0) * 3; +//theDecodedDataSize = theDecodedDataSize / 72 * 74 + theDecodedDataSize % 72; +return(theDecodedDataSize); +} + +bool tk_Base64EncodeData(const void *inInputData, size_t inInputDataSize, char *outOutputData, size_t *ioOutputDataSize) +{ +size_t theEncodedDataSize = tk_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 tk_Base64DecodeData(const void *inInputData, size_t inInputDataSize, void *ioOutputData, size_t *ioOutputDataSize) +{ +memset(ioOutputData, '.', *ioOutputDataSize); + +size_t theDecodedDataSize = tk_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); +} diff --git a/TwitterKit/oauth/TKBase64Transcoder.h b/TwitterKit/oauth/TKBase64Transcoder.h new file mode 100644 index 0000000..0b9c697 --- /dev/null +++ b/TwitterKit/oauth/TKBase64Transcoder.h @@ -0,0 +1,36 @@ +/* + * TKBase64Transcoder.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 tk_EstimateBas64EncodedDataSize(size_t inDataSize); +extern size_t tk_EstimateBas64DecodedDataSize(size_t inDataSize); + +extern bool tk_Base64EncodeData(const void *inInputData, size_t inInputDataSize, char *outOutputData, size_t *ioOutputDataSize); +extern bool tk_Base64DecodeData(const void *inInputData, size_t inInputDataSize, void *ioOutputData, size_t *ioOutputDataSize); + diff --git a/TwitterKit/oauth/TKOAHMACSHA1SignatureProvider.h b/TwitterKit/oauth/TKOAHMACSHA1SignatureProvider.h new file mode 100644 index 0000000..41aa672 --- /dev/null +++ b/TwitterKit/oauth/TKOAHMACSHA1SignatureProvider.h @@ -0,0 +1,32 @@ +// +// TKOAHMACSHA1SignatureProvider.h +// OAuthConsumer +// +// Created by Jon Crosby on 10/19/07. +// Copyright 2007 Kaboomerang LLC. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +#import +#import "TKOASignatureProviding.h" + + +@interface TKOAHMACSHA1SignatureProvider : NSObject +@end diff --git a/TwitterKit/oauth/TKOAHMACSHA1SignatureProvider.m b/TwitterKit/oauth/TKOAHMACSHA1SignatureProvider.m new file mode 100644 index 0000000..d74e46a --- /dev/null +++ b/TwitterKit/oauth/TKOAHMACSHA1SignatureProvider.m @@ -0,0 +1,59 @@ +// +// TKOAHMACSHA1SignatureProvider.m +// OAuthConsumer +// +// Created by Jon Crosby on 10/19/07. +// Copyright 2007 Kaboomerang LLC. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +#import "TKOAHMACSHA1SignatureProvider.h" +#import + +#include "TKBase64Transcoder.h" + +@implementation TKOAHMACSHA1SignatureProvider + +- (NSString *)name +{ + return @"HMAC-SHA1"; +} + +- (NSString *)signClearText:(NSString *)text withSecret:(NSString *)secret +{ + NSData *secretData = [secret dataUsingEncoding:NSUTF8StringEncoding]; + NSData *clearTextData = [text dataUsingEncoding:NSUTF8StringEncoding]; + unsigned char result[20]; + CCHmac(kCCHmacAlgSHA1, [secretData bytes], [secretData length], [clearTextData bytes], [clearTextData length], result); + + //Base64 Encoding + + char base64Result[32]; + size_t theResultLength = 32; + tk_Base64EncodeData(result, 20, base64Result, &theResultLength); + NSData *theData = [NSData dataWithBytes:base64Result length:theResultLength]; + + NSString *base64EncodedResult = + [[NSString alloc] initWithData:theData encoding:NSUTF8StringEncoding]; + + return [base64EncodedResult autorelease]; +} + +@end diff --git a/TwitterKit/oauth/TKOASignatureProviding.h b/TwitterKit/oauth/TKOASignatureProviding.h new file mode 100644 index 0000000..cc1f920 --- /dev/null +++ b/TwitterKit/oauth/TKOASignatureProviding.h @@ -0,0 +1,34 @@ +// +// TKOASignatureProviding.h +// +// Created by Jon Crosby on 10/19/07. +// Copyright 2007 Kaboomerang LLC. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +#import + + +@protocol TKOASignatureProviding + +- (NSString *)name; +- (NSString *)signClearText:(NSString *)text withSecret:(NSString *)secret; + +@end diff --git a/TwitterKit/oauth/TKTwitterOAuthSignature.h b/TwitterKit/oauth/TKTwitterOAuthSignature.h new file mode 100644 index 0000000..151a626 --- /dev/null +++ b/TwitterKit/oauth/TKTwitterOAuthSignature.h @@ -0,0 +1,33 @@ +// +// TwitterOAuthSignature.h +// Twitbit +// +// Created by John Debay on 7/18/11. +// Copyright 2011 High Order Bit. All rights reserved. +// + +#import +#import "TKRequestMethod.h" + +@interface TKTwitterOAuthSignature : NSObject + +@property (nonatomic, copy, readonly) NSString *consumerKey; +@property (nonatomic, copy, readonly) NSString *consumerSecret; + +@property (nonatomic, copy, readonly) NSString *token; +@property (nonatomic, copy, readonly) NSString *tokenSecret; + +#pragma mark - Initialization + +- (id)initWithConsumerKey:(NSString *)consumerKey + consumerSecret:(NSString *)consumerSecret + token:(NSString *)token + tokenSecret:(NSString *)tokenSecret; + +#pragma mark - Getting a signed request + +- (NSURLRequest *)signedRequestForURL:(NSURL *)url + parameters:(NSDictionary *)parameters + requestMethod:(TKRequestMethod)requestMethod; + +@end diff --git a/TwitterKit/oauth/TKTwitterOAuthSignature.m b/TwitterKit/oauth/TKTwitterOAuthSignature.m new file mode 100644 index 0000000..a913a65 --- /dev/null +++ b/TwitterKit/oauth/TKTwitterOAuthSignature.m @@ -0,0 +1,221 @@ +// +// TwitterOAuthSignature.m +// Twitbit +// +// Created by John Debay on 7/18/11. +// Copyright 2011 High Order Bit. All rights reserved. +// + +#import "TKTwitterOAuthSignature.h" + +#import "TKOAHMACSHA1SignatureProvider.h" +#import "NSString+TKOAURLEncodingAdditions.h" +#import "NSDictionary+TKTwitterRequestHelpers.h" + +static NSString *OAUTH_SIGNATURE_METHOD = @"HMAC-SHA1"; +static NSString *OAUTH_VERSION = @"1.0"; + +@interface TKTwitterOAuthSignature () + +- (NSString *)oauthHeaderForMethod:(NSString *)method + andUrl:(NSURL *)url + andParams:(NSDictionary *)params; + +- (NSString *)oauthSignatureBase:(NSString *)httpMethod + withUrl:(NSURL *)url + andOauthComponents:(NSDictionary *)parts; +- (NSString *)oauthAuthorizationHeader:(NSString *)oauthSignature + withOauthComponents:(NSDictionary *)components; +- (NSDictionary *)oauthComponentsForParams:(NSDictionary *)params; + +@end + +@implementation TKTwitterOAuthSignature + +@synthesize consumerKey = consumerKey_, consumerSecret = consumerSecret_; +@synthesize token = token_, tokenSecret = tokenSecret_; + +#pragma mark - Memory management + +- (void)dealloc +{ + [consumerKey_ release]; + [consumerSecret_ release]; + + [token_ release]; + [tokenSecret_ release]; + + [super dealloc]; +} + +#pragma mark - Initialization + +- (id)initWithConsumerKey:(NSString *)consumerKey + consumerSecret:(NSString *)consumerSecret + token:(NSString *)token + tokenSecret:(NSString *)tokenSecret +{ + self = [super init]; + if (self) { + consumerKey_ = [consumerKey copy]; + consumerSecret_ = [consumerSecret copy]; + token_ = [token copy]; + tokenSecret_ = [tokenSecret copy]; + } + + return self; +} + +#pragma mark - Getting a signed request + +- (NSURLRequest *)signedRequestForURL:(NSURL *)url + parameters:(NSDictionary *)parameters + requestMethod:(TKRequestMethod)requestMethod +{ + NSString *requestMethodString = + [NSString stringForRequestMethod:requestMethod]; + + NSMutableDictionary *params = + [NSMutableDictionary dictionaryWithDictionary:parameters]; + [params setObject:[self token] forKey:@"oauth_token"]; + + NSString *header = [self oauthHeaderForMethod:requestMethodString + andUrl:url + andParams:params]; + + NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url]; + [req setHTTPMethod:requestMethodString]; + if (requestMethod == TKRequestMethodPOST) { + NSString *bodyString = [parameters tk_URLParameterString]; + [req setHTTPBody:[bodyString dataUsingEncoding:NSUTF8StringEncoding]]; + } + [req addValue:header forHTTPHeaderField:@"Authorization"]; + + return req; +} + +#pragma mark - Helper methods + +- (NSString *)oauthHeaderForMethod:(NSString *)method + andUrl:(NSURL *)url + andParams:(NSDictionary *)params +{ + TKOAHMACSHA1SignatureProvider *sigProvider = + [[[TKOAHMACSHA1SignatureProvider alloc] init] autorelease]; + + // If there were any params, URLencode them. + NSMutableDictionary *encodedParams = + [NSMutableDictionary dictionaryWithCapacity:[params count]]; + if (params) + for (NSString *key in [params allKeys]) { + NSString *val = [params objectForKey:key]; + [encodedParams setObject:[val tk_encodedURLParameterString] + forKey:key]; + } + + NSString *secret = + [NSString stringWithFormat:@"%@&%@", + [self consumerSecret], [self tokenSecret]]; + + // Given a signature base and secret key, calculate the signature. + NSDictionary *components = [self oauthComponentsForParams:encodedParams]; + NSString *clearText = [self oauthSignatureBase:method + withUrl:url + andOauthComponents:components]; + NSString *signature = [sigProvider signClearText:clearText + withSecret:secret]; + + // Return the authorization header using the signature and parameters + // (if any). + return [self oauthAuthorizationHeader:signature + withOauthComponents:components]; +} + +- (NSString *)oauthSignatureBase:(NSString *)httpMethod + withUrl:(NSURL *)url + andOauthComponents:(NSDictionary *)parts +{ + // Sort the base string components and make them into string key=value pairs + NSMutableArray *normalizedBase = + [NSMutableArray arrayWithCapacity:[parts count]]; + NSArray *sortedKeys = + [[parts allKeys] sortedArrayUsingSelector:@selector(compare:)]; + for (NSString *key in sortedKeys) { + NSString *s = + [NSString stringWithFormat:@"%@=%@", key, [parts objectForKey:key]]; + [normalizedBase addObject:s]; + } + + NSString *normalizedRequestParameters = + [normalizedBase componentsJoinedByString:@"&"]; + + // Return the signature base string. Note that the individual parameters + // must have previously already URL-encoded and here we are encoding them + // again; thus you will see some double URL-encoding for params. This is + // normal. + + // need to strip the query parameters from the URL, if any + NSString *baseUrl = [url absoluteString]; + NSRange where = [baseUrl rangeOfString:[url path]]; + if (!NSEqualRanges(NSMakeRange(NSNotFound, 0), where)) + baseUrl = [baseUrl substringToIndex:where.location + where.length]; + + return + [NSString stringWithFormat:@"%@&%@&%@", + httpMethod, + [baseUrl tk_encodedURLParameterString], + [normalizedRequestParameters tk_encodedURLParameterString]]; +} + +/** + * Given a calculated signature (by this point it is Base64-encoded string) and + * a set of parameters, return the header value that you will stick in the + * "Authorization" header. + */ +- (NSString *)oauthAuthorizationHeader:(NSString *)oauthSignature + withOauthComponents:(NSDictionary *)components +{ + NSMutableArray *chunks = [NSMutableArray array]; + + // First add all the base components. + [chunks addObject:[NSString stringWithString:@"OAuth realm=\"\""]]; + + for (NSString *key in components) { + NSString *value = [components valueForKey:key]; + [chunks addObject:[NSString stringWithFormat:@"%@=\"%@\"", key, value]]; + } + + // Signature will be the last component of our header. + NSString *signature = + [NSString stringWithFormat:@"%@=\"%@\"", @"oauth_signature", + [oauthSignature tk_encodedURLParameterString]]; + [chunks addObject:signature]; + + return [chunks componentsJoinedByString:@", "]; +} + +- (NSDictionary *)oauthComponentsForParams:(NSDictionary *)params +{ + // Freshen the context. Get a fresh timestamp and calculate a nonce. + // Nonce algorithm is sha1(timestamp || random), i.e + // we concatenate timestamp with a random string, and then sha1 it. + int timestamp = time(NULL); + NSString *oauthTimestamp = [NSString stringWithFormat:@"%d", timestamp]; + int myRandom = random(); + NSString *oauthNonce = + [[NSString stringWithFormat:@"%d%d", timestamp, myRandom] tk_sha1]; + + NSMutableDictionary *parts = [NSMutableDictionary dictionary]; + [parts setObject:oauthTimestamp forKey:@"oauth_timestamp"]; + [parts setObject:oauthNonce forKey:@"oauth_nonce"]; + [parts setObject:OAUTH_SIGNATURE_METHOD forKey:@"oauth_signature_method"]; + [parts setObject:OAUTH_VERSION forKey:@"oauth_version"]; + [parts setObject:[self consumerKey] forKey:@"oauth_consumer_key"]; + + if (params) + [parts addEntriesFromDictionary:params]; + + return parts; +} + +@end diff --git a/TwitterKit/shared/NSDictionary+TKTwitterRequestHelpers.h b/TwitterKit/shared/NSDictionary+TKTwitterRequestHelpers.h new file mode 100644 index 0000000..f810282 --- /dev/null +++ b/TwitterKit/shared/NSDictionary+TKTwitterRequestHelpers.h @@ -0,0 +1,15 @@ +// +// NSDictionary+TKTwitterRequestHelpers.h +// TwitterKit +// +// Created by John Debay on 9/16/11. +// Copyright 2011 High Order Bit. All rights reserved. +// + +#import + +@interface NSDictionary (TKTwitterRequestHelpers) + +- (NSString *)tk_URLParameterString; + +@end diff --git a/TwitterKit/shared/NSDictionary+TKTwitterRequestHelpers.m b/TwitterKit/shared/NSDictionary+TKTwitterRequestHelpers.m new file mode 100644 index 0000000..63ae6b1 --- /dev/null +++ b/TwitterKit/shared/NSDictionary+TKTwitterRequestHelpers.m @@ -0,0 +1,35 @@ +// +// NSDictionary+TKTwitterRequestHelpers.m +// TwitterKit +// +// Created by John Debay on 9/16/11. +// Copyright 2011 High Order Bit. All rights reserved. +// + +#import "NSDictionary+TKTwitterRequestHelpers.h" + +@implementation NSDictionary (TKTwitterRequestHelpers) + +- (NSString *)tk_URLParameterString +{ + NSMutableString *s = [NSMutableString string]; + [self enumerateKeysAndObjectsUsingBlock: + ^(id keyObject, id valueObject, BOOL *stop) { + NSString *key = (NSString *) keyObject; + NSString *value = (NSString *) valueObject; + + NSStringEncoding encoding = NSUTF8StringEncoding; + NSString *encodedKey = + [key stringByAddingPercentEscapesUsingEncoding:encoding]; + NSString *encodedValue = + [value stringByAddingPercentEscapesUsingEncoding:encoding]; + [s appendFormat:@"%@=%@&", encodedKey, encodedValue]; + }]; + + if ([s length] && [s characterAtIndex:[s length] - 1] == '&') + [s deleteCharactersInRange:NSMakeRange([s length] - 1, 1)]; + + return s; +} + +@end diff --git a/TwitterKit/shared/NSError+TKTwitterRequestHelpers.h b/TwitterKit/shared/NSError+TKTwitterRequestHelpers.h new file mode 100644 index 0000000..bc5fa9b --- /dev/null +++ b/TwitterKit/shared/NSError+TKTwitterRequestHelpers.h @@ -0,0 +1,15 @@ +// +// NSError+TKTwitterRequestHelpers.h +// TwitterKit +// +// Created by John Debay on 9/16/11. +// Copyright 2011 High Order Bit. All rights reserved. +// + +#import + +@interface NSError (TKTwitterRequestHelpers) + ++ (id)tk_twitterOvercapacityError; + +@end diff --git a/TwitterKit/shared/NSError+TKTwitterRequestHelpers.m b/TwitterKit/shared/NSError+TKTwitterRequestHelpers.m new file mode 100644 index 0000000..8aabb94 --- /dev/null +++ b/TwitterKit/shared/NSError+TKTwitterRequestHelpers.m @@ -0,0 +1,29 @@ +// +// NSError+TKTwitterRequestHelpers.m +// TwitterKit +// +// Created by John Debay on 9/16/11. +// Copyright 2011 High Order Bit. All rights reserved. +// + +#import "NSError+TKTwitterRequestHelpers.h" + +@implementation NSError (TKTwitterRequestHelpers) + ++ (id)tk_twitterOvercapacityError +{ + // jad: Parameters weren't in the response, so just throw + // up a Twitter error. I'm fixing this bug because I first + // saw it when getting over capacity messages, so that's + // the message I'm putting here, whether or not it's + // actually true. + NSString *key = NSLocalizedDescriptionKey; + NSString *msg = NSLocalizedString(@"Twitter is over capacity.", nil); + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:msg forKey:key]; + + return [NSError errorWithDomain:@"Twitter" + code:500 + userInfo:userInfo]; +} + +@end diff --git a/TwitterKit/shared/NSString+TKOAURLEncodingAdditions.h b/TwitterKit/shared/NSString+TKOAURLEncodingAdditions.h new file mode 100644 index 0000000..3defa39 --- /dev/null +++ b/TwitterKit/shared/NSString+TKOAURLEncodingAdditions.h @@ -0,0 +1,38 @@ +// +// NSString+URLEncoding.h +// +// Created by Jon Crosby on 10/19/07. +// Copyright 2007 Kaboomerang LLC. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +#import + + +@interface NSString (TKOAURLEncodingAdditions) + +- (NSString *)tk_encodedURLString; +- (NSString *)tk_encodedURLParameterString; +- (NSString *)tk_decodedURLString; +- (NSString *)tk_removeQuotes; + +- (NSString *)tk_sha1; + +@end diff --git a/TwitterKit/shared/NSString+TKOAURLEncodingAdditions.m b/TwitterKit/shared/NSString+TKOAURLEncodingAdditions.m new file mode 100644 index 0000000..1d34924 --- /dev/null +++ b/TwitterKit/shared/NSString+TKOAURLEncodingAdditions.m @@ -0,0 +1,89 @@ +// +// NSString+URLEncoding.m +// +// Created by Jon Crosby on 10/19/07. +// Copyright 2007 Kaboomerang LLC. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +#import "NSString+TKOAURLEncodingAdditions.h" + +#include + +@implementation NSString (TKOAURLEncodingAdditions) + +- (NSString *)tk_encodedURLString +{ + NSString *result = (NSString *) CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, + (CFStringRef) self, + 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; +} + +- (NSString *)tk_encodedURLParameterString +{ + NSString *result = (NSString *) CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, + (CFStringRef) self, + NULL, + CFSTR(":/=,!$&'()*+;[]@#?"), + kCFStringEncodingUTF8); + return result; +} + +- (NSString *)tk_decodedURLString +{ + NSString *result = (NSString *) CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, + (CFStringRef) self, + CFSTR(""), + kCFStringEncodingUTF8); + return result; + +} + +- (NSString *)tk_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; +} + +- (NSString *)tk_sha1 +{ + const char *cStr = [self UTF8String]; + unsigned char result[CC_SHA1_DIGEST_LENGTH]; + CC_SHA1(cStr, strlen(cStr), result); + NSMutableString *s = [NSMutableString stringWithCapacity:20]; + for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) + [s appendFormat:@"%02X", result[i]]; + + return [s lowercaseString]; +} + + +@end +