From d82288b407fc4f14a746806d6e8f3d4a0ebf8dd1 Mon Sep 17 00:00:00 2001 From: Arik Devens Date: Wed, 20 Apr 2011 16:55:31 -0700 Subject: [PATCH] Adding Posterous support to ShareKit --- Classes/ShareKit/Core/SHKSharers.plist | 1 + Classes/ShareKit/SHKConfig.h | 3 +- .../ShareKit/Sharers/Services/SHKPosterous.h | 17 ++ .../ShareKit/Sharers/Services/SHKPosterous.m | 176 ++++++++++++++++++ ShareKit.xcodeproj/project.pbxproj | 14 ++ 5 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 Classes/ShareKit/Sharers/Services/SHKPosterous.h create mode 100644 Classes/ShareKit/Sharers/Services/SHKPosterous.m diff --git a/Classes/ShareKit/Core/SHKSharers.plist b/Classes/ShareKit/Core/SHKSharers.plist index db686302..d1673e08 100644 --- a/Classes/ShareKit/Core/SHKSharers.plist +++ b/Classes/ShareKit/Core/SHKSharers.plist @@ -21,6 +21,7 @@ SHKReadItLater SHKInstapaper SHKTumblr + SHKPosterous diff --git a/Classes/ShareKit/SHKConfig.h b/Classes/ShareKit/SHKConfig.h index c2aa3565..f8dd4a41 100644 --- a/Classes/ShareKit/SHKConfig.h +++ b/Classes/ShareKit/SHKConfig.h @@ -36,7 +36,8 @@ leaving that decision up to the user. */ - +// Posterous - http://apidocs.posterous.com/ +#define SHKPosterousAPIKey @"" // Delicious - https://developer.apps.yahoo.com/projects #define SHKDeliciousConsumerKey @"" diff --git a/Classes/ShareKit/Sharers/Services/SHKPosterous.h b/Classes/ShareKit/Sharers/Services/SHKPosterous.h new file mode 100644 index 00000000..9f4048a8 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/SHKPosterous.h @@ -0,0 +1,17 @@ +// +// SHKPinboard.h +// ShareKit +// +// Created by Arik Devens on 4/8/11. + +#import +#import "SHKSharer.h" + +@interface SHKPosterous : SHKSharer { + NSMutableData *photoData; + NSHTTPURLResponse *URLResponse; +} + +- (void)finish; + +@end diff --git a/Classes/ShareKit/Sharers/Services/SHKPosterous.m b/Classes/ShareKit/Sharers/Services/SHKPosterous.m new file mode 100644 index 00000000..b2961527 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/SHKPosterous.m @@ -0,0 +1,176 @@ +// +// SHKPinboard.m +// ShareKit +// +// Created by Arik Devens on 4/8/11. + +#import "SHKPosterous.h" +#include "Base64Transcoder.h" + +static NSString * const kPosterousPostURL = @"http://posterous.com/api/2/sites/primary/posts"; + +@implementation SHKPosterous + +#pragma mark - +#pragma mark Memory management +- (void)dealloc { + [photoData release]; + [URLResponse release]; + + [super dealloc]; +} + +#pragma mark - +#pragma mark Configuration : Service Definition + ++ (NSString *)sharerTitle { + return @"Posterous"; +} + ++ (BOOL)canShareText { + return YES; +} + ++ (BOOL)canShareImage { + return YES; +} + +#pragma mark - +#pragma mark Authorization + ++ (NSString *)authorizationFormCaption { + return SHKLocalizedString(@"Create an account at %@", @"http://posterous.com"); +} + +- (void)authorizationFormValidate:(SHKFormController *)form { + if (!quiet) { + [[SHKActivityIndicator currentIndicator] displayActivity:SHKLocalizedString(@"Logging In...")]; + } + + [form saveForm]; +} + +- (void)authFinished:(SHKRequest *)aRequest { + [[SHKActivityIndicator currentIndicator] hide]; +} + + +#pragma mark - +#pragma mark Share Form + +- (NSArray *)shareFormFieldsForType:(SHKShareType)type { + if (type == SHKShareTypeText) { + return [NSArray arrayWithObjects:[SHKFormFieldSettings label:SHKLocalizedString(@"Title") key:@"title" type:SHKFormFieldTypeText start:item.title], nil]; + } + + return nil; +} + +#pragma mark - +#pragma mark Share API Methods + +- (NSString *)httpAuthBasicHeaderWith:(NSString *)user andPass:(NSString *)pass { + NSData *data = [[NSString stringWithFormat:@"%@:%@", user, pass] dataUsingEncoding:NSUTF8StringEncoding]; + + size_t est_out_len = EstimateBas64DecodedDataSize([data length]); + size_t out_len = est_out_len + 512; // a safety margin of 512 is perhaps too big + char outdata[out_len]; + Base64EncodeData([data bytes], [data length], outdata, &out_len); + outdata[out_len] = '\0'; // make it a null terminated string + + NSString *value = [NSString stringWithFormat:@"Basic %@", [NSString stringWithCString:outdata encoding:NSUTF8StringEncoding]]; + + return value; +} + +- (BOOL)send { + if ([self validateItem]) { + NSMutableURLRequest *aRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:kPosterousPostURL] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:90]; + + NSString *username = [self getAuthValueForKey:@"username"]; + NSString *password = [self getAuthValueForKey:@"password"]; + NSString *boundary = @"0xKhTmLbOuNdArY"; + + NSMutableDictionary *headers = [NSMutableDictionary dictionaryWithDictionary: self.request.headerFields]; + [headers setObject:[self httpAuthBasicHeaderWith:username andPass:password] forKey:@"Authorization"]; + [headers setObject:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] forKey:@"Content-Type"]; + + [aRequest setAllHTTPHeaderFields:headers]; + [aRequest setHTTPMethod:@"POST"]; + + NSMutableData *body = [NSMutableData data]; + + [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"Content-Disposition: form-data; name=\"api_token\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[[NSString stringWithString:SHKPosterousAPIKey] dataUsingEncoding:NSUTF8StringEncoding]]; + + if ([item title]) { + [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"Content-Disposition: form-data; name=\"post[title]\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[item.title dataUsingEncoding:NSUTF8StringEncoding]]; + } + + if ([item text]) { + [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"Content-Disposition: form-data; name=\"post[body]\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[item.text dataUsingEncoding:NSUTF8StringEncoding]]; + } + + if ([item image]) { + [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"Content-Disposition: form-data; name=\"media[0]\"; filename=\"upload.jpg\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"Content-Transfer-Encoding: image/jpg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:UIImageJPEGRepresentation([item image], 0.9)]; + } + + [body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + + [aRequest setHTTPBody:body]; + + [NSURLConnection connectionWithRequest:aRequest delegate:self]; + [aRequest release]; + + [self sendDidStart]; + + return YES; + } + + return NO; +} + +#pragma mark - +#pragma mark NSURLConnection delegate methods +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)theResponse { + [URLResponse release]; + URLResponse = [theResponse retain]; + + [photoData setLength:0]; +} + +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)d { + [photoData appendData:d]; +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection { + [self finish]; +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { + [self finish]; +} + +- (void)finish { + if (URLResponse.statusCode == 200 || URLResponse.statusCode == 201) { + [self sendDidFinish]; + } else { + if (URLResponse.statusCode == 403) { + [self sendDidFailWithError:[SHK error:SHKLocalizedString(@"Invalid username or password.")] shouldRelogin:YES]; + } else if (URLResponse.statusCode == 500) { + [self sendDidFailWithError:[SHK error:SHKLocalizedString(@"The service encountered an error. Please try again later.")]]; + } else { + [self sendDidFailWithError:[SHK error:SHKLocalizedString(@"There was an error sending your post to Posterous.")]]; + } + } +} + +@end diff --git a/ShareKit.xcodeproj/project.pbxproj b/ShareKit.xcodeproj/project.pbxproj index adcbf90e..67d03bd3 100755 --- a/ShareKit.xcodeproj/project.pbxproj +++ b/ShareKit.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 07FE2747135FA99000E05A8E /* SHKPosterous.m in Sources */ = {isa = PBXBuildFile; fileRef = 07FE2746135FA99000E05A8E /* SHKPosterous.m */; }; 1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; }; 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; @@ -109,6 +110,8 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 07FE2745135FA99000E05A8E /* SHKPosterous.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKPosterous.h; sourceTree = ""; }; + 07FE2746135FA99000E05A8E /* SHKPosterous.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKPosterous.m; sourceTree = ""; }; 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 1D6058910D05DD3D006BFB54 /* ShareKit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ShareKit.app; sourceTree = BUILT_PRODUCTS_DIR; }; 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; @@ -330,6 +333,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 07FE2744135FA98000E05A8E /* Posterous */ = { + isa = PBXGroup; + children = ( + 07FE2745135FA99000E05A8E /* SHKPosterous.h */, + 07FE2746135FA99000E05A8E /* SHKPosterous.m */, + ); + name = Posterous; + sourceTree = ""; + }; 080E96DDFE201D6D7F000001 /* Classes */ = { isa = PBXGroup; children = ( @@ -726,6 +738,7 @@ 43A536DE11DBE3B9004A1712 /* Google Reader */, 43150A8A11E78697008C6B68 /* Instapaper */, 43A536E111DBE3B9004A1712 /* Pinboard */, + 07FE2744135FA98000E05A8E /* Posterous */, 43A536E411DBE3B9004A1712 /* Read It Later */, 43C91DF211EBAE4800F31FAE /* Tumblr */, 43A536E711DBE3B9004A1712 /* Twitter */, @@ -1052,6 +1065,7 @@ 4379F2C11291AC9700D2A41E /* SHKEvernote.m in Sources */, 4379F2EA1291AE5700D2A41E /* NSData+md5.m in Sources */, 4379F3B21291C45700D2A41E /* SHKTextMessage.m in Sources */, + 07FE2747135FA99000E05A8E /* SHKPosterous.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };