diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..b68a679e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.pbxuser +*.perspectivev3 +*.mode1v3 +build diff --git a/Classes/Example/ExampleShareFile.h b/Classes/Example/ExampleShareFile.h new file mode 100644 index 00000000..43ce4379 --- /dev/null +++ b/Classes/Example/ExampleShareFile.h @@ -0,0 +1,38 @@ +// +// ExampleShareFile.h +// ShareKit +// +// Created by Nathan Weiner on 6/29/10. + +// +// 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 ExampleShareFile : UIViewController +{ + UIWebView *webView; +} + +@property (nonatomic, retain) UIWebView *webView; + +@end diff --git a/Classes/Example/ExampleShareFile.m b/Classes/Example/ExampleShareFile.m new file mode 100644 index 00000000..142d00cb --- /dev/null +++ b/Classes/Example/ExampleShareFile.m @@ -0,0 +1,83 @@ +// +// ExampleShareFile.m +// ShareKit +// +// Created by Nathan Weiner on 6/29/10. + +// +// 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 "ExampleShareFile.h" +#import "SHK.h" +#import "SHKActionSheet.h" + +@implementation ExampleShareFile + +@synthesize webView; + +- (void)dealloc +{ + [webView release]; + [super dealloc]; +} + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) + { + self.toolbarItems = [NSArray arrayWithObjects: + [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] autorelease], + [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(share)] autorelease], + [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] autorelease], + nil + ]; + } + + return self; +} + +- (void)loadView +{ + self.webView = [[UIWebView alloc] initWithFrame:CGRectZero]; + webView.delegate = self; + webView.scalesPageToFit = YES; + [webView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"example.pdf"]]]]; + + self.view = webView; + [webView release]; +} + +- (void)share +{ + NSString *filePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"example.pdf"]; + NSData *file = [NSData dataWithContentsOfFile:filePath]; + + SHKItem *item = [SHKItem file:file filename:@"Awesome.pdf" mimeType:@"application/pdf" title:@"My Awesome PDF"]; + SHKActionSheet *actionSheet = [SHKActionSheet actionSheetForItem:item]; + [actionSheet showFromToolbar:self.navigationController.toolbar]; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + return YES; +} + +@end diff --git a/Classes/Example/ExampleShareImage.h b/Classes/Example/ExampleShareImage.h new file mode 100644 index 00000000..10526ad9 --- /dev/null +++ b/Classes/Example/ExampleShareImage.h @@ -0,0 +1,38 @@ +// +// ExampleShareImage.h +// ShareKit +// +// Created by Nathan Weiner on 6/18/10. + +// +// 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 ExampleShareImage : UIViewController +{ + UIImageView *imageView; +} + +@property (nonatomic, retain) UIImageView *imageView; + +@end diff --git a/Classes/Example/ExampleShareImage.m b/Classes/Example/ExampleShareImage.m new file mode 100644 index 00000000..e7031f0f --- /dev/null +++ b/Classes/Example/ExampleShareImage.m @@ -0,0 +1,83 @@ + // +// ExampleShareImage.m +// ShareKit +// +// Created by Nathan Weiner on 6/18/10. + +// +// 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 "ExampleShareImage.h" +#import "SHKItem.h" +#import "SHKActionSheet.h" + +@implementation ExampleShareImage + +@synthesize imageView; + +- (void)dealloc +{ + [imageView release]; + [super dealloc]; +} + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) + { + self.toolbarItems = [NSArray arrayWithObjects: + [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] autorelease], + [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(share)] autorelease], + [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] autorelease], + nil + ]; + } + + return self; +} + +- (void)loadView +{ + [super loadView]; + + self.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"sanFran.jpg"]]; + + imageView.frame = CGRectMake(0,0,self.view.bounds.size.width,self.view.bounds.size.height); + + [self.view addSubview:imageView]; + + [imageView release]; +} + +- (void)share +{ + SHKItem *item = [SHKItem image:imageView.image title:@"San Francisco"]; + SHKActionSheet *actionSheet = [SHKActionSheet actionSheetForItem:item]; + + [actionSheet showFromToolbar:self.navigationController.toolbar]; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + return YES; +} + +@end diff --git a/Classes/Example/ExampleShareLink.h b/Classes/Example/ExampleShareLink.h new file mode 100644 index 00000000..5fffd2a1 --- /dev/null +++ b/Classes/Example/ExampleShareLink.h @@ -0,0 +1,38 @@ +// +// ExampleShareLink.h +// ShareKit +// +// Created by Nathan Weiner on 6/17/10. + +// +// 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 ExampleShareLink : UIViewController +{ + UIWebView *webView; +} + +@property (nonatomic, retain) UIWebView *webView; + +@end diff --git a/Classes/Example/ExampleShareLink.m b/Classes/Example/ExampleShareLink.m new file mode 100644 index 00000000..308de462 --- /dev/null +++ b/Classes/Example/ExampleShareLink.m @@ -0,0 +1,79 @@ + // +// ExampleShareLink.m +// ShareKit +// +// Created by Nathan Weiner on 6/17/10. + +// +// 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 "ExampleShareLink.h" +#import "SHK.h" + +@implementation ExampleShareLink + +@synthesize webView; + +- (void)dealloc +{ + [webView release]; + [super dealloc]; +} + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) + { + self.toolbarItems = [NSArray arrayWithObjects: + [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] autorelease], + [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(share)] autorelease], + [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] autorelease], + nil + ]; + } + + return self; +} + +- (void)share +{ + SHKItem *item = [SHKItem URL:webView.request.URL title:[webView pageTitle]]; + SHKActionSheet *actionSheet = [SHKActionSheet actionSheetForItem:item]; + [actionSheet showFromToolbar:self.navigationController.toolbar]; +} + +- (void)loadView +{ + self.webView = [[UIWebView alloc] initWithFrame:CGRectZero]; + webView.delegate = self; + webView.scalesPageToFit = YES; + [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://apple.com"]]]; + + self.view = webView; + [webView release]; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + return YES; +} + +@end diff --git a/Classes/Example/ExampleShareText.h b/Classes/Example/ExampleShareText.h new file mode 100644 index 00000000..1625839b --- /dev/null +++ b/Classes/Example/ExampleShareText.h @@ -0,0 +1,38 @@ +// +// ExampleShareText.h +// ShareKit +// +// Created by Nathan Weiner on 6/18/10. + +// +// 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 ExampleShareText : UIViewController +{ + UITextView *textView; +} + +@property (nonatomic, retain) UITextView *textView; + +@end diff --git a/Classes/Example/ExampleShareText.m b/Classes/Example/ExampleShareText.m new file mode 100644 index 00000000..cc66d29f --- /dev/null +++ b/Classes/Example/ExampleShareText.m @@ -0,0 +1,91 @@ + // +// ExampleShareText.m +// ShareKit +// +// Created by Nathan Weiner on 6/18/10. +// +// 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 "ExampleShareText.h" +#import "SHKItem.h" +#import "SHKActionSheet.h" + +@implementation ExampleShareText + +@synthesize textView; + +- (void)dealloc +{ + [textView release]; + [super dealloc]; +} + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) + { + self.toolbarItems = [NSArray arrayWithObjects: + [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] autorelease], + [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(share)] autorelease], + [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] autorelease], + nil + ]; + } + + return self; +} + +- (void)loadView +{ + [super loadView]; + + self.textView = [[UITextView alloc] initWithFrame:CGRectMake(0,0,self.view.bounds.size.width,self.view.bounds.size.height)]; + [self.view addSubview:textView]; + + textView.text = @"This is a chunk of text. If you highlight it, you'll be able to share the selection. If you tap the share button below, it will share all of it."; + textView.editable = NO; + + [textView release]; +} + +- (void)share +{ + NSString *text; + + if (textView.selectedRange.length > 0) + text = [textView.text substringWithRange:textView.selectedRange]; + + else + text = textView.text; + + + SHKItem *item = [SHKItem text:text]; + SHKActionSheet *actionSheet = [SHKActionSheet actionSheetForItem:item]; + + [actionSheet showFromToolbar:self.navigationController.toolbar]; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + return YES; +} + + +@end diff --git a/Classes/Example/RootViewController.h b/Classes/Example/RootViewController.h new file mode 100644 index 00000000..289d499a --- /dev/null +++ b/Classes/Example/RootViewController.h @@ -0,0 +1,14 @@ +// +// RootViewController.h +// ShareKit +// +// Created by Nathan Weiner on 6/4/10. +// Copyright Idea Shower, LLC 2010. All rights reserved. +// + +#import + +@interface RootViewController : UITableViewController { +} + +@end diff --git a/Classes/Example/RootViewController.m b/Classes/Example/RootViewController.m new file mode 100644 index 00000000..0e873e2e --- /dev/null +++ b/Classes/Example/RootViewController.m @@ -0,0 +1,140 @@ +// +// RootViewController.m +// ShareKit +// +// Created by Nathan Weiner on 6/4/10. +// Copyright Idea Shower, LLC 2010. All rights reserved. +// + +#import "RootViewController.h" +#import "ExampleShareLink.h" +#import "ExampleShareImage.h" +#import "ExampleShareText.h" +#import "ExampleShareFile.h" +#import "SHK.h" + +@implementation RootViewController + +- (void)loadView +{ + [super loadView]; + + self.toolbarItems = [NSArray arrayWithObjects: + [[[UIBarButtonItem alloc] initWithTitle:SHKLocalizedString(@"Logout") style:UIBarButtonItemStyleBordered target:self action:@selector(logout)] autorelease], + nil + ]; +} + +#pragma mark - +#pragma mark Table view data source + + +// Customize the number of sections in the table view. +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return 1; +} + + +// Customize the number of rows in the table view. +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return 4;//5; +} + + +// Customize the appearance of table view cells. +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + static NSString *CellIdentifier = @"Cell"; + + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; + if (cell == nil) { + cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; + } + + switch (indexPath.row) + { + case 0: + cell.textLabel.text = SHKLocalizedString(@"Sharing a Link"); + break; + + case 1: + cell.textLabel.text = SHKLocalizedString(@"Sharing an Image"); + break; + + case 2: + cell.textLabel.text = SHKLocalizedString(@"Sharing Text"); + break; + + case 3: + cell.textLabel.text = SHKLocalizedString(@"Sharing a File"); + break; + + //case 4: + // cell.textLabel.text = @"Logout of All Services"; + // break; + } + + return cell; +} + + +#pragma mark - +#pragma mark Table view delegate + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + switch (indexPath.row) + { + case 0: + [self.navigationController pushViewController:[[[ExampleShareLink alloc] initWithNibName:nil bundle:nil] autorelease] animated:YES]; + break; + + case 1: + + [self.navigationController pushViewController:[[[ExampleShareImage alloc] initWithNibName:nil bundle:nil] autorelease] animated:YES]; + break; + + case 2: + [self.navigationController pushViewController:[[[ExampleShareText alloc] initWithNibName:nil bundle:nil] autorelease] animated:YES]; + break; + + case 3: + [self.navigationController pushViewController:[[[ExampleShareFile alloc] initWithNibName:nil bundle:nil] autorelease] animated:YES]; + break; + + //case 4: + // [SHK logoutOfAll]; + // break; + + } +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + return YES; +} + + +#pragma mark - + +- (void)logout +{ + [[[[UIAlertView alloc] initWithTitle:SHKLocalizedString(@"Logout") + message:SHKLocalizedString(@"Are you sure you want to logout of all share services?") + delegate:self + cancelButtonTitle:SHKLocalizedString(@"Cancel") + otherButtonTitles:@"Logout",nil] autorelease] show]; + + [SHK logoutOfAll]; +} + +- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex +{ + if (buttonIndex == 0) + [SHK logoutOfAll]; +} + + +@end + diff --git a/Classes/Example/ShareKitAppDelegate.h b/Classes/Example/ShareKitAppDelegate.h new file mode 100644 index 00000000..0824f973 --- /dev/null +++ b/Classes/Example/ShareKitAppDelegate.h @@ -0,0 +1,21 @@ +// +// ShareKitAppDelegate.h +// ShareKit +// +// Created by Nathan Weiner on 6/4/10. +// Copyright Idea Shower, LLC 2010. All rights reserved. +// + +#import + +@interface ShareKitAppDelegate : NSObject { + + UIWindow *window; + UINavigationController *navigationController; +} + +@property (nonatomic, retain) IBOutlet UIWindow *window; +@property (nonatomic, retain) IBOutlet UINavigationController *navigationController; + +@end + diff --git a/Classes/Example/ShareKitAppDelegate.m b/Classes/Example/ShareKitAppDelegate.m new file mode 100644 index 00000000..e905ec41 --- /dev/null +++ b/Classes/Example/ShareKitAppDelegate.m @@ -0,0 +1,60 @@ +// +// ShareKitAppDelegate.m +// ShareKit +// +// Created by Nathan Weiner on 6/4/10. +// Copyright Idea Shower, LLC 2010. All rights reserved. +// + +#import "ShareKitAppDelegate.h" +#import "RootViewController.h" + +#import "SHKReadItLater.h" +#import "SHKFacebook.h" + +@implementation ShareKitAppDelegate + +@synthesize window; +@synthesize navigationController; + + +#pragma mark - +#pragma mark Application lifecycle + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after app launch + + [window addSubview:[navigationController view]]; + [window makeKeyAndVisible]; + + navigationController.topViewController.title = SHKLocalizedString(@"Examples"); + [navigationController setToolbarHidden:NO]; + + [self performSelector:@selector(testOffline) withObject:nil afterDelay:0.5]; + + return YES; +} + +- (void)testOffline +{ + [SHK flushOfflineQueue]; +} + +- (void)applicationWillTerminate:(UIApplication *)application +{ + // Save data if appropriate +} + + +#pragma mark - +#pragma mark Memory management + +- (void)dealloc { + [navigationController release]; + [window release]; + [super dealloc]; +} + + +@end + diff --git a/Classes/Example/example.pdf b/Classes/Example/example.pdf new file mode 100644 index 00000000..4da5f931 Binary files /dev/null and b/Classes/Example/example.pdf differ diff --git a/Classes/Example/sanFran.jpg b/Classes/Example/sanFran.jpg new file mode 100644 index 00000000..6ad777ca Binary files /dev/null and b/Classes/Example/sanFran.jpg differ diff --git a/Classes/ShareKit/Core/Base Sharer Classes/SHKOAuthSharer.h b/Classes/ShareKit/Core/Base Sharer Classes/SHKOAuthSharer.h new file mode 100644 index 00000000..9bcfec55 --- /dev/null +++ b/Classes/ShareKit/Core/Base Sharer Classes/SHKOAuthSharer.h @@ -0,0 +1,91 @@ +// +// SHKOAuthSharer.h +// ShareKit +// +// Created by Nathan Weiner on 6/21/10. + +// +// 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 "SHKSharer.h" +#import "SHKOAuthView.h" +#import "OAuthConsumer.h" + +@interface SHKOAuthSharer : SHKSharer +{ + NSString *consumerKey; + NSString *secretKey; + NSURL *authorizeCallbackURL; + + NSURL *authorizeURL; + NSURL *accessURL; + NSURL *requestURL; + + OAConsumer *consumer; + OAToken *requestToken; + OAToken *accessToken; + + id signatureProvider; + + NSDictionary *authorizeResponseQueryVars; +} + +@property (nonatomic, retain) NSString *consumerKey; +@property (nonatomic, retain) NSString *secretKey; +@property (nonatomic, retain) NSURL *authorizeCallbackURL; + +@property (nonatomic, retain) NSURL *authorizeURL; +@property (nonatomic, retain) NSURL *accessURL; +@property (nonatomic, retain) NSURL *requestURL; + +@property (retain) OAConsumer *consumer; +@property (retain) OAToken *requestToken; +@property (retain) OAToken *accessToken; + +@property (retain) id signatureProvider; + +@property (nonatomic, retain) NSDictionary *authorizeResponseQueryVars; + + + +#pragma mark - +#pragma mark OAuth Authorization + +- (void)tokenRequest; +- (void)tokenRequestModifyRequest:(OAMutableURLRequest *)oRequest; +- (void)tokenRequestTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data; +- (void)tokenRequestTicket:(OAServiceTicket *)ticket didFailWithError:(NSError*)error; + +- (void)tokenAuthorize; + +- (void)tokenAccess; +- (void)tokenAccess:(BOOL)refresh; +- (void)tokenAccessModifyRequest:(OAMutableURLRequest *)oRequest; +- (void)tokenAccessTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data; +- (void)tokenAccessTicket:(OAServiceTicket *)ticket didFailWithError:(NSError*)error; + +- (void)storeAccessToken; +- (BOOL)restoreAccessToken; +- (void)refreshToken; + + +@end diff --git a/Classes/ShareKit/Core/Base Sharer Classes/SHKOAuthSharer.m b/Classes/ShareKit/Core/Base Sharer Classes/SHKOAuthSharer.m new file mode 100644 index 00000000..e2edf057 --- /dev/null +++ b/Classes/ShareKit/Core/Base Sharer Classes/SHKOAuthSharer.m @@ -0,0 +1,348 @@ +// +// SHKOAuthSharer.m +// ShareKit +// +// Created by Nathan Weiner on 6/21/10. + +// +// 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 "SHKOAuthSharer.h" +#import "SHKOAuthView.h" +#import "OAuthConsumer.h" + + +@implementation SHKOAuthSharer + +@synthesize consumerKey, secretKey, authorizeCallbackURL; +@synthesize authorizeURL, requestURL, accessURL; +@synthesize consumer, requestToken, accessToken; +@synthesize signatureProvider; +@synthesize authorizeResponseQueryVars; + + +- (void)dealloc +{ + [consumerKey release]; + [secretKey release]; + [authorizeCallbackURL release]; + [authorizeURL release]; + [requestURL release]; + [accessURL release]; + [consumer release]; + [requestToken release]; + [accessToken release]; + [signatureProvider release]; + [authorizeResponseQueryVars release]; + + [super dealloc]; +} + + + +#pragma mark - +#pragma mark Authorization + +- (BOOL)isAuthorized +{ + return [self restoreAccessToken]; +} + +- (void)promptAuthorization +{ + [self tokenRequest]; +} + + +#pragma mark Request + +- (void)tokenRequest +{ + [[SHKActivityIndicator currentIndicator] displayActivity:SHKLocalizedString(@"Connecting...")]; + + OAMutableURLRequest *oRequest = [[OAMutableURLRequest alloc] initWithURL:requestURL + consumer:consumer + token:nil // we don't have a Token yet + realm:nil // our service provider doesn't specify a realm + signatureProvider:signatureProvider]; + + + [oRequest setHTTPMethod:@"POST"]; + + [self tokenRequestModifyRequest:oRequest]; + + OAAsynchronousDataFetcher *fetcher = [OAAsynchronousDataFetcher asynchronousFetcherWithRequest:oRequest + delegate:self + didFinishSelector:@selector(tokenRequestTicket:didFinishWithData:) + didFailSelector:@selector(tokenRequestTicket:didFailWithError:)]; + [fetcher start]; + [oRequest release]; +} + +- (void)tokenRequestModifyRequest:(OAMutableURLRequest *)oRequest +{ + // Subclass to add custom paramaters and headers +} + +- (void)tokenRequestTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data +{ + if (SHKDebugShowLogs) // check so we don't have to alloc the string with the data if we aren't logging + SHKLog(@"tokenRequestTicket Response Body: %@", [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]); + + [[SHKActivityIndicator currentIndicator] hide]; + + if (ticket.didSucceed) + { + NSString *responseBody = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + self.requestToken = [[OAToken alloc] initWithHTTPResponseBody:responseBody]; + [responseBody release]; + + [self tokenAuthorize]; + } + + else + // TODO - better error handling here + [self tokenRequestTicket:ticket didFailWithError:[SHK error:SHKLocalizedString(@"There was a problem requesting authorization from %@", [self sharerTitle])]]; +} + +- (void)tokenRequestTicket:(OAServiceTicket *)ticket didFailWithError:(NSError*)error +{ + [[SHKActivityIndicator currentIndicator] hide]; + + [[[[UIAlertView alloc] initWithTitle:SHKLocalizedString(@"Request Error") + message:error!=nil?[error localizedDescription]:SHKLocalizedString(@"There was an error while sharing") + delegate:nil + cancelButtonTitle:SHKLocalizedString(@"Close") + otherButtonTitles:nil] autorelease] show]; +} + + +#pragma mark Authorize + +- (void)tokenAuthorize +{ + NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@?oauth_token=%@", authorizeURL.absoluteString, requestToken.key]]; + + SHKOAuthView *auth = [[SHKOAuthView alloc] initWithURL:url delegate:self]; + [[SHK currentHelper] showViewController:auth]; + [auth release]; +} + +- (void)tokenAuthorizeView:(SHKOAuthView *)authView didFinishWithSuccess:(BOOL)success queryParams:(NSMutableDictionary *)queryParams error:(NSError *)error; +{ + [[SHK currentHelper] hideCurrentViewControllerAnimated:YES]; + + if (!success) + { + [[[[UIAlertView alloc] initWithTitle:SHKLocalizedString(@"Authorize Error") + message:error!=nil?[error localizedDescription]:SHKLocalizedString(@"There was an error while authorizing") + delegate:nil + cancelButtonTitle:SHKLocalizedString(@"Close") + otherButtonTitles:nil] autorelease] show]; + } + + else + { + self.authorizeResponseQueryVars = queryParams; + + [self tokenAccess]; + } +} + +- (void)tokenAuthorizeCancelledView:(SHKOAuthView *)authView +{ + [[SHK currentHelper] hideCurrentViewControllerAnimated:YES]; +} + + +#pragma mark Access + +- (void)tokenAccess +{ + [self tokenAccess:NO]; +} + +- (void)tokenAccess:(BOOL)refresh +{ + if (!refresh) + [[SHKActivityIndicator currentIndicator] displayActivity:SHKLocalizedString(@"Authenticating...")]; + + OAMutableURLRequest *oRequest = [[OAMutableURLRequest alloc] initWithURL:accessURL + consumer:consumer + token:(refresh ? accessToken : requestToken) + realm:nil // our service provider doesn't specify a realm + signatureProvider:signatureProvider]; // use the default method, HMAC-SHA1 + + [oRequest setHTTPMethod:@"POST"]; + + [self tokenAccessModifyRequest:oRequest]; + + OAAsynchronousDataFetcher *fetcher = [OAAsynchronousDataFetcher asynchronousFetcherWithRequest:oRequest + delegate:self + didFinishSelector:@selector(tokenAccessTicket:didFinishWithData:) + didFailSelector:@selector(tokenAccessTicket:didFailWithError:)]; + [fetcher start]; + [oRequest release]; +} + +- (void)tokenAccessModifyRequest:(OAMutableURLRequest *)oRequest +{ + // Subclass to add custom paramaters or headers +} + +- (void)tokenAccessTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data +{ + if (SHKDebugShowLogs) // check so we don't have to alloc the string with the data if we aren't logging + SHKLog(@"tokenAccessTicket Response Body: %@", [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]); + + [[SHKActivityIndicator currentIndicator] hide]; + + if (ticket.didSucceed) + { + NSString *responseBody = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + self.accessToken = [[OAToken alloc] initWithHTTPResponseBody:responseBody]; + [responseBody release]; + + [self storeAccessToken]; + + [self tryPendingAction]; + } + + + else + // TODO - better error handling here + [self tokenAccessTicket:ticket didFailWithError:[SHK error:SHKLocalizedString(@"There was a problem requesting access from %@", [self sharerTitle])]]; +} + +- (void)tokenAccessTicket:(OAServiceTicket *)ticket didFailWithError:(NSError*)error +{ + [[SHKActivityIndicator currentIndicator] hide]; + + [[[[UIAlertView alloc] initWithTitle:SHKLocalizedString(@"Access Error") + message:error!=nil?[error localizedDescription]:SHKLocalizedString(@"There was an error while sharing") + delegate:nil + cancelButtonTitle:SHKLocalizedString(@"Close") + otherButtonTitles:nil] autorelease] show]; +} + +- (void)storeAccessToken +{ + [SHK setAuthValue:accessToken.key + forKey:@"accessKey" + forSharer:[self sharerId]]; + + [SHK setAuthValue:accessToken.secret + forKey:@"accessSecret" + forSharer:[self sharerId]]; + + [SHK setAuthValue:accessToken.sessionHandle + forKey:@"sessionHandle" + forSharer:[self sharerId]]; +} + ++ (void)deleteStoredAccessToken +{ + NSString *sharerId = [self sharerId]; + + [SHK removeAuthValueForKey:@"accessKey" forSharer:sharerId]; + [SHK removeAuthValueForKey:@"accessSecret" forSharer:sharerId]; + [SHK removeAuthValueForKey:@"sessionHandle" forSharer:sharerId]; +} + ++ (void)logout +{ + [self deleteStoredAccessToken]; + + // Clear cookies (for OAuth, doesn't affect XAuth) + // TODO - move the authorizeURL out of the init call (into a define) so we don't have to create an object just to get it + SHKOAuthSharer *sharer = [[self alloc] init]; + if (sharer.authorizeURL) + { + NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + NSArray *cookies = [storage cookiesForURL:sharer.authorizeURL]; + for (NSHTTPCookie *each in cookies) + { + [storage deleteCookie:each]; + } + } + [sharer release]; +} + +- (BOOL)restoreAccessToken +{ + self.consumer = [[[OAConsumer alloc] initWithKey:consumerKey secret:secretKey] autorelease]; + + if (accessToken != nil) + return YES; + + NSString *key = [SHK getAuthValueForKey:@"accessKey" + forSharer:[self sharerId]]; + + NSString *secret = [SHK getAuthValueForKey:@"accessSecret" + forSharer:[self sharerId]]; + + NSString *sessionHandle = [SHK getAuthValueForKey:@"sessionHandle" + forSharer:[self sharerId]]; + + if (key != nil && secret != nil) + { + self.accessToken = [[[OAToken alloc] initWithKey:key secret:secret] autorelease]; + + if (sessionHandle != nil) + accessToken.sessionHandle = sessionHandle; + + return accessToken != nil; + } + + return NO; +} + + +#pragma mark Expired + +- (void)refreshToken +{ + self.pendingAction = SHKPendingRefreshToken; + [self tokenAccess:YES]; +} + +#pragma mark - +#pragma mark Pending Actions +#pragma mark - +#pragma mark Pending Actions + +- (void)tryPendingAction +{ + switch (pendingAction) + { + case SHKPendingRefreshToken: + [self tryToSend]; // try to resend + break; + + default: + [super tryPendingAction]; + } +} + + +@end diff --git a/Classes/ShareKit/Core/Base Sharer Classes/SHKSharer.h b/Classes/ShareKit/Core/Base Sharer Classes/SHKSharer.h new file mode 100644 index 00000000..e608d072 --- /dev/null +++ b/Classes/ShareKit/Core/Base Sharer Classes/SHKSharer.h @@ -0,0 +1,187 @@ +// +// SHKSharer.h +// ShareKit +// +// Created by Nathan Weiner on 6/8/10. + +// +// 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 "SHK.h" +#import "SHKCustomFormController.h" + + +@class SHKSharer; + +@protocol SHKSharerDelegate + +- (void)sharerStartedSending:(SHKSharer *)sharer; +- (void)sharerFinishedSending:(SHKSharer *)sharer; +- (void)sharer:(SHKSharer *)sharer failedWithError:(NSError *)error shouldRelogin:(BOOL)shouldRelogin; +- (void)sharerCancelledSending:(SHKSharer *)sharer; + +@end + + +typedef enum +{ + SHKPendingNone, + SHKPendingShare, + SHKPendingRefreshToken +} SHKSharerPendingAction; + + +@interface SHKSharer : UINavigationController +{ + id shareDelegate; + + SHKItem *item; + SHKFormController *pendingForm; + SHKRequest *request; + + NSError *lastError; + + BOOL quiet; + SHKSharerPendingAction pendingAction; +} + +@property (nonatomic, retain) id shareDelegate; + +@property (retain) SHKItem *item; +@property (retain) SHKFormController *pendingForm; +@property (retain) SHKRequest *request; + +@property (nonatomic, retain) NSError *lastError; + +@property BOOL quiet; +@property SHKSharerPendingAction pendingAction; + + + +#pragma mark - +#pragma mark Configuration : Service Defination + ++ (NSString *)sharerTitle; +- (NSString *)sharerTitle; ++ (NSString *)sharerId; +- (NSString *)sharerId; ++ (BOOL)canShareText; ++ (BOOL)canShareURL; ++ (BOOL)canShareImage; ++ (BOOL)canShareFile; ++ (BOOL)shareRequiresInternetConnection; ++ (BOOL)canShareOffline; ++ (BOOL)requiresAuthentication; ++ (BOOL)canShareType:(SHKShareType)type; ++ (BOOL)canAutoShare; + + +#pragma mark - +#pragma mark Configuration : Dynamic Enable + ++ (BOOL)canShare; +- (BOOL)shouldAutoShare; + +#pragma mark - +#pragma mark Initialization + +- (id)init; + + +#pragma mark - +#pragma mark Share Item Loading Convenience Methods + ++ (id)shareItem:(SHKItem *)i; + ++ (id)shareURL:(NSURL *)url; ++ (id)shareURL:(NSURL *)url title:(NSString *)title; + ++ (id)shareImage:(UIImage *)image title:(NSString *)title; + ++ (id)shareText:(NSString *)text; + ++ (id)shareFile:(NSData *)file filename:(NSString *)filename mimeType:(NSString *)mimeType title:(NSString *)title; + + +#pragma mark - +#pragma mark Commit Share + +- (void)share; + +#pragma mark - +#pragma mark Authentication + +- (BOOL)isAuthorized; +- (BOOL)authorize; +- (void)promptAuthorization; +- (NSString *)getAuthValueForKey:(NSString *)key; + +#pragma mark Authorization Form + +- (void)authorizationFormShow; +- (void)authorizationFormValidate:(SHKFormController *)form; +- (void)authorizationFormSave:(SHKFormController *)form; +- (NSArray *)authorizationFormFields; +- (NSString *)authorizationFormCaption; ++ (NSArray *)authorizationFormFields; ++ (NSString *)authorizationFormCaption; ++ (void)logout; ++ (BOOL)isServiceAuthorized; + +#pragma mark - +#pragma mark API Implementation + +- (BOOL)validateItem; +- (BOOL)tryToSend; +- (BOOL)send; + +#pragma mark - +#pragma mark UI Implementation + +- (void)show; + +#pragma mark - +#pragma mark Share Form + +- (NSArray *)shareFormFieldsForType:(SHKShareType)type; +- (void)shareFormValidate:(SHKFormController *)form; +- (void)shareFormSave:(SHKFormController *)form; + +#pragma mark - +#pragma mark Pending Actions + +- (void)tryPendingAction; + +#pragma mark - +#pragma mark Delegate Notifications + +- (void)sendDidStart; +- (void)sendDidFinish; +- (void)sendDidFailShouldRelogin; +- (void)sendDidFailWithError:(NSError *)error; +- (void)sendDidFailWithError:(NSError *)error shouldRelogin:(BOOL)shouldRelogin; +- (void)sendDidCancel; + +@end + + + diff --git a/Classes/ShareKit/Core/Base Sharer Classes/SHKSharer.m b/Classes/ShareKit/Core/Base Sharer Classes/SHKSharer.m new file mode 100644 index 00000000..f7c331fa --- /dev/null +++ b/Classes/ShareKit/Core/Base Sharer Classes/SHKSharer.m @@ -0,0 +1,727 @@ + // +// SHKSharer.m +// ShareKit +// +// Created by Nathan Weiner on 6/8/10. + +// +// 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 "SHKSharer.h" +#import "SHKActivityIndicator.h" + +@implementation SHKSharer + +@synthesize shareDelegate; +@synthesize item, pendingForm, request; +@synthesize lastError; +@synthesize quiet, pendingAction; + +- (void)dealloc +{ + [item release]; + [shareDelegate release]; + [pendingForm release]; + [request release]; + [lastError release]; + + [super dealloc]; +} + + +#pragma mark - +#pragma mark Configuration : Service Defination + +// Each service should subclass these and return YES/NO to indicate what type of sharing they support. +// Superclass defaults to NO so that subclasses only need to add methods for types they support + ++ (NSString *)sharerTitle +{ + return @""; +} + +- (NSString *)sharerTitle +{ + return [[self class] sharerTitle]; +} + ++ (NSString *)sharerId +{ + return NSStringFromClass([self class]); +} + +- (NSString *)sharerId +{ + return [[self class] sharerId]; +} + ++ (BOOL)canShareText +{ + return NO; +} + ++ (BOOL)canShareURL +{ + return NO; +} + ++ (BOOL)canShareImage +{ + return NO; +} + ++ (BOOL)canShareFile +{ + return NO; +} + ++ (BOOL)shareRequiresInternetConnection +{ + return YES; +} + ++ (BOOL)canShareOffline +{ + return YES; +} + ++ (BOOL)requiresAuthentication +{ + return YES; +} + ++ (BOOL)canShareType:(SHKShareType)type +{ + switch (type) + { + case SHKShareTypeURL: + return [self canShareURL]; + break; + + case SHKShareTypeImage: + return [self canShareImage]; + break; + + case SHKShareTypeText: + return [self canShareText]; + break; + + case SHKShareTypeFile: + return [self canShareFile]; + break; + } + return NO; +} + ++ (BOOL)canAutoShare +{ + return YES; +} + + + +#pragma mark - +#pragma mark Configuration : Dynamic Enable + +// Allows a subclass to programically disable/enable services depending on the current environment + ++ (BOOL)canShare +{ + return YES; +} + +- (BOOL)shouldAutoShare +{ + return [[NSUserDefaults standardUserDefaults] boolForKey:[NSString stringWithFormat:@"%@_shouldAutoShare", [self sharerId]]]; +} + + +#pragma mark - +#pragma mark Initialization + +- (id)init +{ + if (self = [super initWithNibName:nil bundle:nil]) + { + self.shareDelegate = self; + self.item = [[[SHKItem alloc] init] autorelease]; + + if ([self respondsToSelector:@selector(modalPresentationStyle)]) + self.modalPresentationStyle = [SHK modalPresentationStyle]; + + if ([self respondsToSelector:@selector(modalTransitionStyle)]) + self.modalTransitionStyle = [SHK modalTransitionStyle]; + } + return self; +} + + +#pragma mark - +#pragma mark Share Item Loading Convenience Methods + ++ (id)shareItem:(SHKItem *)i +{ + [SHK pushOnFavorites:[self sharerId] forType:i.shareType]; + + // Create controller and set share options + SHKSharer *controller = [[self alloc] init]; + controller.item = i; + + // share and/or show UI + [controller share]; + + return [controller autorelease]; +} + ++ (id)shareURL:(NSURL *)url +{ + return [self shareURL:url title:nil]; +} + ++ (id)shareURL:(NSURL *)url title:(NSString *)title +{ + // Create controller and set share options + SHKSharer *controller = [[self alloc] init]; + controller.item.shareType = SHKShareTypeURL; + controller.item.URL = url; + controller.item.title = title; + + // share and/or show UI + [controller share]; + + return [controller autorelease]; +} + ++ (id)shareImage:(UIImage *)image title:(NSString *)title +{ + // Create controller and set share options + SHKSharer *controller = [[self alloc] init]; + controller.item.shareType = SHKShareTypeImage; + controller.item.image = image; + controller.item.title = title; + + // share and/or show UI + [controller share]; + + return [controller autorelease]; +} + ++ (id)shareText:(NSString *)text +{ + // Create controller and set share options + SHKSharer *controller = [[self alloc] init]; + controller.item.shareType = SHKShareTypeText; + controller.item.text = text; + + // share and/or show UI + [controller share]; + + return [controller autorelease]; +} + ++ (id)shareFile:(NSData *)file filename:(NSString *)filename mimeType:(NSString *)mimeType title:(NSString *)title +{ + // Create controller and set share options + SHKSharer *controller = [[self alloc] init]; + controller.item.shareType = SHKShareTypeFile; + controller.item.data = file; + controller.item.filename = filename; + controller.item.mimeType = mimeType; + controller.item.title = title; + + // share and/or show UI + [controller share]; + + return [controller autorelease]; +} + + +#pragma mark - +#pragma mark Commit Share + +- (void)share +{ + // isAuthorized - If service requires login and details have not been saved, present login dialog + if (![self authorize]) + self.pendingAction = SHKPendingShare; + + // A. First check if auto share is set + // B. If it is, try to send + // If either A or B fail, display the UI + else if (![self shouldAutoShare] || ![self tryToSend]) + [self show]; +} + + +#pragma mark - +#pragma mark Authentication + +- (BOOL)isAuthorized +{ + if (![[self class] requiresAuthentication]) + return YES; + + // Checks to make sure we just have at least one variable from the authorization form + // If the send request fails it'll reprompt the user for their new login anyway + + NSString *sharerId = [self sharerId]; + NSArray *fields = [self authorizationFormFields]; + for (SHKFormFieldSettings *field in fields) + { + if ([SHK getAuthValueForKey:field.key forSharer:sharerId] != nil) + return YES; + } + + return NO; +} + +- (BOOL)authorize +{ + if ([self isAuthorized]) + return YES; + + else + [self promptAuthorization]; + + return NO; +} + +- (void)promptAuthorization +{ + if ([[self class] shareRequiresInternetConnection] && ![SHK connected]) + { + if (!quiet) + { + [[[[UIAlertView alloc] initWithTitle:SHKLocalizedString(@"Offline") + message:SHKLocalizedString(@"You must be online to login to %@", [self sharerTitle]) + delegate:nil + cancelButtonTitle:SHKLocalizedString(@"Close") + otherButtonTitles:nil] autorelease] show]; + } + return; + } + + [self authorizationFormShow]; +} + +- (NSString *)getAuthValueForKey:(NSString *)key +{ + return [SHK getAuthValueForKey:key forSharer:[self sharerId]]; +} + +- (void)setShouldAutoShare:(BOOL)b +{ + return [[NSUserDefaults standardUserDefaults] setBool:b forKey:[NSString stringWithFormat:@"%@_shouldAutoShare", [self sharerId]]]; +} + +#pragma mark Authorization Form + +- (void)authorizationFormShow +{ + // Create the form + SHKCustomFormController *form = [[SHKCustomFormController alloc] initWithStyle:UITableViewStyleGrouped title:SHKLocalizedString(@"Login") rightButtonTitle:SHKLocalizedString(@"Login")]; + [form addSection:[self authorizationFormFields] header:nil footer:[self authorizationFormCaption]]; + form.delegate = self; + form.validateSelector = @selector(authorizationFormValidate:); + form.saveSelector = @selector(authorizationFormSave:); + form.autoSelect = YES; + + [[SHK currentHelper] showViewController:form]; + [form release]; +} + +- (void)authorizationFormValidate:(SHKFormController *)form +{ + /* + + Services should subclass this. + You can get a dictionary of the field values from [form formValues] + + -- + + You should perform one of the following actions: + + 1. Display an error - If the user input was incorrect, display an error to the user and tell them what to do to fix it + + 2. Save the form - If everything is correct call [form saveForm] + + 3. Display a pending indicator - If you need to authorize the details on the server, display an activity indicator with [form displayActivity:@"DESCRIPTION OF WHAT YOU ARE DOING"] + After your process completes be sure to perform either 1 or 2 above. + + */ +} + +- (void)authorizationFormSave:(SHKFormController *)form +{ + // -- Save values + NSDictionary *formValues = [form formValues]; + + NSString *value; + NSString *sharerId = [self sharerId]; + NSArray *fields = [[[form sections] objectAtIndex:0] objectForKey:@"rows"]; + for(SHKFormFieldSettings *field in fields) + { + value = [formValues objectForKey:field.key]; + [SHK setAuthValue:value forKey:field.key forSharer:sharerId]; + } + + // -- Try to share again + [self share]; +} + +- (NSArray *)authorizationFormFields +{ + return [[self class] authorizationFormFields]; +} + ++ (NSArray *)authorizationFormFields +{ + return [NSArray arrayWithObjects: + [SHKFormFieldSettings label:SHKLocalizedString(@"Username") key:@"username" type:SHKFormFieldTypeText start:nil], + [SHKFormFieldSettings label:SHKLocalizedString(@"Password") key:@"password" type:SHKFormFieldTypePassword start:nil], + nil]; +} + +- (NSString *)authorizationFormCaption +{ + return [[self class] authorizationFormCaption]; +} + ++ (NSString *)authorizationFormCaption +{ + return nil; +} + ++ (void)logout +{ + NSString *sharerId = [self sharerId]; + NSArray *authFields = [self authorizationFormFields]; + if (authFields != nil) + { + for(SHKFormFieldSettings *field in authFields) + [SHK removeAuthValueForKey:field.key forSharer:sharerId]; + } +} + +// Credit: GreatWiz ++ (BOOL)isServiceAuthorized +{ + SHKSharer *controller = [[self alloc] init]; + BOOL isAuthorized = [controller isAuthorized]; + [controller release]; + + return isAuthorized; +} + + + + +#pragma mark - +#pragma mark UI Implementation + +- (void)show +{ + NSArray *shareFormFields = [self shareFormFieldsForType:item.shareType]; + + if (shareFormFields == nil) + [self tryToSend]; + + else + { + SHKCustomFormController *rootView = [[SHKCustomFormController alloc] initWithStyle:UITableViewStyleGrouped + title:nil + rightButtonTitle:SHKLocalizedString(@"Send to %@", [[self class] sharerTitle]) + ]; + [rootView addSection:[self shareFormFieldsForType:item.shareType] header:nil footer:item.URL!=nil?item.URL.absoluteString:nil]; + + if ([[self class] canAutoShare]) + { + [rootView addSection: + [NSArray arrayWithObject: + [SHKFormFieldSettings label:SHKLocalizedString(@"Auto Share") key:@"autoShare" type:SHKFormFieldTypeSwitch start:([self shouldAutoShare]?SHKFormFieldSwitchOn:SHKFormFieldSwitchOff)] + ] + header:nil + footer:SHKLocalizedString(@"Enable auto share to skip this step in the future.")]; + } + + rootView.delegate = self; + rootView.validateSelector = @selector(shareFormValidate:); + rootView.saveSelector = @selector(shareFormSave:); + + [self pushViewController:rootView animated:NO]; + + [[SHK currentHelper] showViewController:self]; + } +} + + + + +#pragma mark - +#pragma mark Share Form + +- (NSArray *)shareFormFieldsForType:(SHKShareType)type +{ + if (type == SHKShareTypeURL) + return [NSArray arrayWithObjects: + [SHKFormFieldSettings label:SHKLocalizedString(@"Title") key:@"title" type:SHKFormFieldTypeText start:item.title], + nil]; + + return nil; +} + +- (void)shareFormValidate:(SHKCustomFormController *)form +{ + /* + + Services should subclass this if they need to validate any data before sending. + You can get a dictionary of the field values from [form formValues] + + -- + + You should perform one of the following actions: + + 1. Display an error - If the user input was incorrect, display an error to the user and tell them what to do to fix it + + 2. Save the form - If everything is correct call [form save] + + 3. Display a pending indicator - If you need to authorize the details on the server, display an activity indicator with [form displayActivity:@"DESCRIPTION OF WHAT YOU ARE DOING"] + After your process completes be sure to perform either 1 or 2 above. + + */ + + + // default does no checking and proceeds to share + [form saveForm]; +} + +- (void)shareFormSave:(SHKFormController *)form +{ + // Update item with new values from form + NSDictionary *formValues = [form formValues]; + for(NSString *key in formValues) + { + if ([key isEqualToString:@"title"]) + item.title = [formValues objectForKey:key]; + + else if ([key isEqualToString:@"text"]) + item.text = [formValues objectForKey:key]; + + else if ([key isEqualToString:@"tags"]) + item.tags = [formValues objectForKey:key]; + + else + [item setCustomValue:[formValues objectForKey:key] forKey:key]; + } + + // Update shouldAutoShare + if ([[self class] canAutoShare]) + { + NSDictionary *advancedOptions = [form formValuesForSection:1]; + if ([advancedOptions objectForKey:@"autoShare"] != nil) + [self setShouldAutoShare:[[advancedOptions objectForKey:@"autoShare"] isEqualToString:SHKFormFieldSwitchOn]]; + } + + // Send the share + [self tryToSend]; +} + + +#pragma mark - +#pragma mark API Implementation + +- (BOOL)validateItem +{ + switch (item.shareType) + { + case SHKShareTypeURL: + return (item.URL != nil); + break; + + case SHKShareTypeImage: + return (item.image != nil); + break; + + case SHKShareTypeText: + return (item.text != nil); + break; + + case SHKShareTypeFile: + return (item.data != nil); + break; + } + + return NO; +} + +- (BOOL)tryToSend +{ + if (![[self class] shareRequiresInternetConnection] || [SHK connected]) + return [self send]; + + else if ([[self class] canShareOffline]) + return [SHK addToOfflineQueue:item forSharer:[self sharerId]]; + + else if (!quiet) + { + [[[[UIAlertView alloc] initWithTitle:SHKLocalizedString(@"Offline") + message:SHKLocalizedString(@"You must be online in order to share with %@", [self sharerTitle]) + delegate:nil + cancelButtonTitle:SHKLocalizedString(@"Close") + otherButtonTitles:nil] autorelease] show]; + + return YES; + } + + + return NO; +} + +- (BOOL)send +{ + // Does not actually send anything. + // Your subclass should implement the sending logic. + // There is no reason to call [super send] in your subclass + + // You should never call [XXX send] directly, you should use [XXX tryToSend]. TryToSend will perform an online check before trying to send. + return NO; +} + +#pragma mark - +#pragma mark Default UI Updating + +// These are used if you do not provide your own custom UI and delegate + + +- (void)sharerStartedSending:(SHKSharer *)sharer +{ + if (!quiet) + [[SHKActivityIndicator currentIndicator] displayActivity:SHKLocalizedString(@"Saving to %@", [[self class] sharerTitle])]; +} + +- (void)sharerFinishedSending:(SHKSharer *)sharer +{ + if (!quiet) + [[SHKActivityIndicator currentIndicator] displayCompleted:SHKLocalizedString(@"Saved!")]; +} + +- (void)sharer:(SHKSharer *)sharer failedWithError:(NSError *)error shouldRelogin:(BOOL)shouldRelogin +{ + if (!quiet) + { + [[SHKActivityIndicator currentIndicator] hide]; + + [[[[UIAlertView alloc] initWithTitle:SHKLocalizedString(@"Error") + message:sharer.lastError!=nil?[sharer.lastError localizedDescription]:SHKLocalizedString(@"There was an error while sharing") + delegate:nil + cancelButtonTitle:SHKLocalizedString(@"Close") + otherButtonTitles:nil] autorelease] show]; + + if (shouldRelogin) + [self promptAuthorization]; + } +} + +- (void)sharerCancelledSending:(SHKSharer *)sharer +{ + +} + +#pragma mark - +#pragma mark Pending Actions + +- (void)tryPendingAction +{ + switch (pendingAction) + { + case SHKPendingShare: + [self share]; + break; + } +} + + + +#pragma mark - + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + return YES; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; + + // Remove the SHK view wrapper from the window + [[SHK currentHelper] viewWasDismissed]; +} + + +#pragma mark - +#pragma mark Delegate Notifications + +- (void)sendDidStart +{ + if ([shareDelegate respondsToSelector:@selector(sharerStartedSending:)]) + [shareDelegate performSelector:@selector(sharerStartedSending:) withObject:self]; + + [[NSNotificationCenter defaultCenter] postNotificationName:@"SHKSendDidStartNotification" object:self]; +} + +- (void)sendDidFinish +{ + if ([shareDelegate respondsToSelector:@selector(sharerFinishedSending:)]) + [shareDelegate performSelector:@selector(sharerFinishedSending:) withObject:self]; + + [[NSNotificationCenter defaultCenter] postNotificationName:@"SHKSendDidFinish" object:self]; +} + +- (void)sendDidFailShouldRelogin +{ + [self sendDidFailWithError:[SHK error:SHKLocalizedString(@"Could not authenticate you. Please relogin.")] shouldRelogin:YES]; +} + +- (void)sendDidFailWithError:(NSError *)error +{ + [self sendDidFailWithError:error shouldRelogin:NO]; +} + +- (void)sendDidFailWithError:(NSError *)error shouldRelogin:(BOOL)shouldRelogin +{ + self.lastError = error; + + if ([shareDelegate respondsToSelector:@selector(sharer:failedWithError:shouldRelogin:)]) + [(SHKSharer *)shareDelegate sharer:self failedWithError:error shouldRelogin:shouldRelogin]; + + [[NSNotificationCenter defaultCenter] postNotificationName:@"SHKSendDidFailWithError" object:self]; +} + +- (void)sendDidCancel +{ + if ([shareDelegate respondsToSelector:@selector(sharerCancelledSending:)]) + [shareDelegate performSelector:@selector(sharerCancelledSending:) withObject:self]; + + [[NSNotificationCenter defaultCenter] postNotificationName:@"SHKSendDidCancel" object:self]; +} + + +@end diff --git a/Classes/ShareKit/Core/Categories/UIWebView+SHK.h b/Classes/ShareKit/Core/Categories/UIWebView+SHK.h new file mode 100644 index 00000000..94d420a7 --- /dev/null +++ b/Classes/ShareKit/Core/Categories/UIWebView+SHK.h @@ -0,0 +1,38 @@ +// +// UIWebView+SHK.h +// ShareKit +// +// Created by Nathan Weiner on 6/16/10. + +// +// 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 UIWebView (SHK) + +// Retrieves the title from the page loaded in a UIWebView +// Useful for providing a title when sharing URLs + +- (NSString *)pageTitle; + +@end diff --git a/Classes/ShareKit/Core/Categories/UIWebView+SHK.m b/Classes/ShareKit/Core/Categories/UIWebView+SHK.m new file mode 100644 index 00000000..3570617f --- /dev/null +++ b/Classes/ShareKit/Core/Categories/UIWebView+SHK.m @@ -0,0 +1,37 @@ +// +// UIWebView+SHK.m +// ShareKit +// +// Created by Nathan Weiner on 6/16/10. + +// +// 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 "UIWebView+SHK.h" + +@implementation UIWebView (SHK) + +- (NSString *)pageTitle +{ + return [self stringByEvaluatingJavaScriptFromString:@"document.title"]; +} + +@end diff --git a/Classes/ShareKit/Core/Helpers/Keychain/SFHFKeychainUtils.h b/Classes/ShareKit/Core/Helpers/Keychain/SFHFKeychainUtils.h new file mode 100644 index 00000000..70b83aca --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/Keychain/SFHFKeychainUtils.h @@ -0,0 +1,41 @@ +// +// SFHFKeychainUtils.h +// +// Created by Buzz Andersen on 10/20/08. +// Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone. +// Copyright 2008 Sci-Fi Hi-Fi. 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 SFHFKeychainUtils : NSObject { + +} + ++ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error; ++ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error; ++ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error; + +@end \ No newline at end of file diff --git a/Classes/ShareKit/Core/Helpers/Keychain/SFHFKeychainUtils.m b/Classes/ShareKit/Core/Helpers/Keychain/SFHFKeychainUtils.m new file mode 100644 index 00000000..b05ed5f8 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/Keychain/SFHFKeychainUtils.m @@ -0,0 +1,434 @@ +// +// SFHFKeychainUtils.m +// +// Created by Buzz Andersen on 10/20/08. +// Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone. +// Copyright 2008 Sci-Fi Hi-Fi. 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 "SFHFKeychainUtils.h" +#import + +static NSString *SFHFKeychainUtilsErrorDomain = @"SFHFKeychainUtilsErrorDomain"; + +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR +@interface SFHFKeychainUtils (PrivateMethods) ++ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error; +@end +#endif + +@implementation SFHFKeychainUtils + +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR + ++ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error { + if (!username || !serviceName) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil]; + return nil; + } + + SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error]; + + if (*error || !item) { + return nil; + } + + // from Advanced Mac OS X Programming, ch. 16 + UInt32 length; + char *password; + SecKeychainAttribute attributes[8]; + SecKeychainAttributeList list; + + attributes[0].tag = kSecAccountItemAttr; + attributes[1].tag = kSecDescriptionItemAttr; + attributes[2].tag = kSecLabelItemAttr; + attributes[3].tag = kSecModDateItemAttr; + + list.count = 4; + list.attr = attributes; + + OSStatus status = SecKeychainItemCopyContent(item, NULL, &list, &length, (void **)&password); + + if (status != noErr) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil]; + return nil; + } + + NSString *passwordString = nil; + + if (password != NULL) { + char passwordBuffer[1024]; + + if (length > 1023) { + length = 1023; + } + strncpy(passwordBuffer, password, length); + + passwordBuffer[length] = '\0'; + passwordString = [NSString stringWithCString:passwordBuffer]; + } + + SecKeychainItemFreeContent(&list, password); + + CFRelease(item); + + return passwordString; +} + ++ (void) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error { + if (!username || !password || !serviceName) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil]; + return; + } + + OSStatus status = noErr; + + SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error]; + + if (*error && [*error code] != noErr) { + return; + } + + *error = nil; + + if (item) { + status = SecKeychainItemModifyAttributesAndData(item, + NULL, + strlen([password UTF8String]), + [password UTF8String]); + + CFRelease(item); + } + else { + status = SecKeychainAddGenericPassword(NULL, + strlen([serviceName UTF8String]), + [serviceName UTF8String], + strlen([username UTF8String]), + [username UTF8String], + strlen([password UTF8String]), + [password UTF8String], + NULL); + } + + if (status != noErr) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil]; + } +} + ++ (void) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error { + if (!username || !serviceName) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: 2000 userInfo: nil]; + return; + } + + *error = nil; + + SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error]; + + if (*error && [*error code] != noErr) { + return; + } + + OSStatus status; + + if (item) { + status = SecKeychainItemDelete(item); + + CFRelease(item); + } + + if (status != noErr) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil]; + } +} + ++ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error { + if (!username || !serviceName) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil]; + return nil; + } + + *error = nil; + + SecKeychainItemRef item; + + OSStatus status = SecKeychainFindGenericPassword(NULL, + strlen([serviceName UTF8String]), + [serviceName UTF8String], + strlen([username UTF8String]), + [username UTF8String], + NULL, + NULL, + &item); + + if (status != noErr) { + if (status != errSecItemNotFound) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil]; + } + + return nil; + } + + return item; +} + +#else + ++ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error { + if (!username || !serviceName) { + if (error != nil) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil]; + } + return nil; + } + + if (error != nil) { + *error = nil; + } + + // Set up a query dictionary with the base query attributes: item type (generic), username, and service + + NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, kSecAttrAccount, kSecAttrService, nil] autorelease]; + NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, username, serviceName, nil] autorelease]; + + NSMutableDictionary *query = [[[NSMutableDictionary alloc] initWithObjects: objects forKeys: keys] autorelease]; + + // First do a query for attributes, in case we already have a Keychain item with no password data set. + // One likely way such an incorrect item could have come about is due to the previous (incorrect) + // version of this code (which set the password as a generic attribute instead of password data). + + NSDictionary *attributeResult = NULL; + NSMutableDictionary *attributeQuery = [query mutableCopy]; + [attributeQuery setObject: (id) kCFBooleanTrue forKey:(id) kSecReturnAttributes]; + OSStatus status = SecItemCopyMatching((CFDictionaryRef) attributeQuery, (CFTypeRef *) &attributeResult); + + [attributeResult release]; + [attributeQuery release]; + + if (status != noErr) { + // No existing item found--simply return nil for the password + if (error != nil && status != errSecItemNotFound) { + //Only return an error if a real exception happened--not simply for "not found." + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil]; + } + + return nil; + } + + // We have an existing item, now query for the password data associated with it. + + NSData *resultData = nil; + NSMutableDictionary *passwordQuery = [query mutableCopy]; + [passwordQuery setObject: (id) kCFBooleanTrue forKey: (id) kSecReturnData]; + + status = SecItemCopyMatching((CFDictionaryRef) passwordQuery, (CFTypeRef *) &resultData); + + [resultData autorelease]; + [passwordQuery release]; + + if (status != noErr) { + if (status == errSecItemNotFound) { + // We found attributes for the item previously, but no password now, so return a special error. + // Users of this API will probably want to detect this error and prompt the user to + // re-enter their credentials. When you attempt to store the re-entered credentials + // using storeUsername:andPassword:forServiceName:updateExisting:error + // the old, incorrect entry will be deleted and a new one with a properly encrypted + // password will be added. + if (error != nil) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo: nil]; + } + } + else { + // Something else went wrong. Simply return the normal Keychain API error code. + if (error != nil) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil]; + } + } + + return nil; + } + + NSString *password = nil; + + if (resultData) { + password = [[NSString alloc] initWithData: resultData encoding: NSUTF8StringEncoding]; + } + else { + // There is an existing item, but we weren't able to get password data for it for some reason, + // Possibly as a result of an item being incorrectly entered by the previous code. + // Set the -1999 error so the code above us can prompt the user again. + if (error != nil) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo: nil]; + } + } + + return [password autorelease]; +} + ++ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error +{ + if (!username || !password || !serviceName) + { + if (error != nil) + { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil]; + } + return NO; + } + + // See if we already have a password entered for these credentials. + NSError *getError = nil; + NSString *existingPassword = [SFHFKeychainUtils getPasswordForUsername: username andServiceName: serviceName error:&getError]; + + if ([getError code] == -1999) + { + // There is an existing entry without a password properly stored (possibly as a result of the previous incorrect version of this code. + // Delete the existing item before moving on entering a correct one. + + getError = nil; + + [self deleteItemForUsername: username andServiceName: serviceName error: &getError]; + + if ([getError code] != noErr) + { + if (error != nil) + { + *error = getError; + } + return NO; + } + } + else if ([getError code] != noErr) + { + if (error != nil) + { + *error = getError; + } + return NO; + } + + if (error != nil) + { + *error = nil; + } + + OSStatus status = noErr; + + if (existingPassword) + { + // We have an existing, properly entered item with a password. + // Update the existing item. + + if (![existingPassword isEqualToString:password] && updateExisting) + { + //Only update if we're allowed to update existing. If not, simply do nothing. + + NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, + kSecAttrService, + kSecAttrLabel, + kSecAttrAccount, + nil] autorelease]; + + NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, + serviceName, + serviceName, + username, + nil] autorelease]; + + NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease]; + + status = SecItemUpdate((CFDictionaryRef) query, (CFDictionaryRef) [NSDictionary dictionaryWithObject: [password dataUsingEncoding: NSUTF8StringEncoding] forKey: (NSString *) kSecValueData]); + } + } + else + { + // No existing entry (or an existing, improperly entered, and therefore now + // deleted, entry). Create a new entry. + + NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, + kSecAttrService, + kSecAttrLabel, + kSecAttrAccount, + kSecValueData, + nil] autorelease]; + + NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, + serviceName, + serviceName, + username, + [password dataUsingEncoding: NSUTF8StringEncoding], + nil] autorelease]; + + NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease]; + + status = SecItemAdd((CFDictionaryRef) query, NULL); + } + + if (error != nil && status != noErr) + { + // Something went wrong with adding the new item. Return the Keychain error code. + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil]; + + return NO; + } + + return YES; +} + ++ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error +{ + if (!username || !serviceName) + { + if (error != nil) + { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil]; + } + return NO; + } + + if (error != nil) + { + *error = nil; + } + + NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, kSecAttrAccount, kSecAttrService, kSecReturnAttributes, nil] autorelease]; + NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, username, serviceName, kCFBooleanTrue, nil] autorelease]; + + NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease]; + + OSStatus status = SecItemDelete((CFDictionaryRef) query); + + if (error != nil && status != noErr) + { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil]; + + return NO; + } + + return YES; +} + +#endif + +@end \ No newline at end of file diff --git a/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSMutableURLRequest+Parameters.h b/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSMutableURLRequest+Parameters.h new file mode 100755 index 00000000..13bf9afa --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSMutableURLRequest+Parameters.h @@ -0,0 +1,35 @@ +// +// NSMutableURLRequest+Parameters.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 +#import "OARequestParameter.h" +#import "NSURL+Base.h" + + +@interface NSMutableURLRequest (OAParameterAdditions) + +- (NSArray *)parameters; +- (void)setParameters:(NSArray *)parameters; + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSMutableURLRequest+Parameters.m b/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSMutableURLRequest+Parameters.m new file mode 100755 index 00000000..24035ed8 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSMutableURLRequest+Parameters.m @@ -0,0 +1,97 @@ +// +// NSMutableURLRequest+Parameters.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 "NSMutableURLRequest+Parameters.h" + + +@implementation NSMutableURLRequest (OAParameterAdditions) + +- (NSArray *)parameters +{ + NSString *encodedParameters; + BOOL shouldfree = NO; + + if ([[self HTTPMethod] isEqualToString:@"GET"] || [[self HTTPMethod] isEqualToString:@"DELETE"]) + encodedParameters = [[self URL] query]; + else + { + // POST, PUT + shouldfree = YES; + encodedParameters = [[NSString alloc] initWithData:[self HTTPBody] encoding:NSASCIIStringEncoding]; + } + + if ((encodedParameters == nil) || ([encodedParameters isEqualToString:@""])) + { + if (shouldfree) + [encodedParameters release]; + + return nil; + } + + NSArray *encodedParameterPairs = [encodedParameters componentsSeparatedByString:@"&"]; + NSMutableArray *requestParameters = [[[NSMutableArray alloc] initWithCapacity:16] autorelease]; + + for (NSString *encodedPair in encodedParameterPairs) + { + NSArray *encodedPairElements = [encodedPair componentsSeparatedByString:@"="]; + OARequestParameter *parameter = [OARequestParameter requestParameterWithName:[[encodedPairElements objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] + value:[[encodedPairElements objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + [requestParameters addObject:parameter]; + } + + // Cleanup + if (shouldfree) + [encodedParameters release]; + + return requestParameters; +} + +- (void)setParameters:(NSArray *)parameters +{ + NSMutableString *encodedParameterPairs = [NSMutableString stringWithCapacity:256]; + + int position = 1; + for (OARequestParameter *requestParameter in parameters) + { + [encodedParameterPairs appendString:[requestParameter URLEncodedNameValuePair]]; + if (position < [parameters count]) + [encodedParameterPairs appendString:@"&"]; + + position++; + } + + if ([[self HTTPMethod] isEqualToString:@"GET"] || [[self HTTPMethod] isEqualToString:@"DELETE"]) + [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@", [[self URL] URLStringWithoutQuery], encodedParameterPairs]]]; + else + { + // POST, PUT + NSData *postData = [encodedParameterPairs dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; + [self setHTTPBody:postData]; + [self setValue:[NSString stringWithFormat:@"%d", [postData length]] forHTTPHeaderField:@"Content-Length"]; + [self setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; + } +} + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSString+URLEncoding.h b/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSString+URLEncoding.h new file mode 100755 index 00000000..de06fe77 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSString+URLEncoding.h @@ -0,0 +1,34 @@ +// +// 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 (OAURLEncodingAdditions) + +- (NSString *)URLEncodedString; +- (NSString *)URLDecodedString; + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSString+URLEncoding.m b/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSString+URLEncoding.m new file mode 100755 index 00000000..540492de --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSString+URLEncoding.m @@ -0,0 +1,52 @@ +// +// 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+URLEncoding.h" + + +@implementation NSString (OAURLEncodingAdditions) + +- (NSString *)URLEncodedString +{ + NSString *result = (NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, + (CFStringRef)self, + NULL, + CFSTR("!*'();:@&=+$,/?%#[]"), + kCFStringEncodingUTF8); + [result autorelease]; + return result; +} + +- (NSString*)URLDecodedString +{ + NSString *result = (NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, + (CFStringRef)self, + CFSTR(""), + kCFStringEncodingUTF8); + [result autorelease]; + return result; +} + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSURL+Base.h b/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSURL+Base.h new file mode 100755 index 00000000..5d12b696 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSURL+Base.h @@ -0,0 +1,34 @@ +// +// NSURL+Base.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 + + +@interface NSURL (OABaseAdditions) + +- (NSString *)URLStringWithoutQuery; + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSURL+Base.m b/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSURL+Base.m new file mode 100755 index 00000000..8d4d4209 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/Categories/NSURL+Base.m @@ -0,0 +1,38 @@ +// +// NSURL+Base.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 "NSURL+Base.h" + + +@implementation NSURL (OABaseAdditions) + +- (NSString *)URLStringWithoutQuery +{ + NSArray *parts = [[self absoluteString] componentsSeparatedByString:@"?"]; + return [parts objectAtIndex:0]; +} + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/Base64Transcoder.c b/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/Base64Transcoder.c new file mode 100755 index 00000000..a655581e --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/Base64Transcoder.c @@ -0,0 +1,230 @@ +/* + * 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); +} diff --git a/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/Base64Transcoder.h b/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/Base64Transcoder.h new file mode 100755 index 00000000..87520989 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/Base64Transcoder.h @@ -0,0 +1,36 @@ +/* + * 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); + diff --git a/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/hmac.c b/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/hmac.c new file mode 100755 index 00000000..eea9a707 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/hmac.c @@ -0,0 +1,86 @@ +// +// hmac.c +// OAuthConsumer +// +// Created by Jonathan Wight on 4/8/8. +// Copyright 2008 Jonathan Wight. 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. + +/* + * Implementation of HMAC-SHA1. Adapted from example at http://tools.ietf.org/html/rfc2104 + + */ + +#include "sha1.h" + +#include +#include + +void hmac_sha1(const unsigned char *inText, size_t inTextLength, unsigned char* inKey, size_t inKeyLength, unsigned char *outDigest) +{ +const size_t B = 64; +const size_t L = 20; + +SHA1_CTX theSHA1Context; +unsigned char k_ipad[B + 1]; /* inner padding - key XORd with ipad */ +unsigned char k_opad[B + 1]; /* outer padding - key XORd with opad */ + +/* if key is longer than 64 bytes reset it to key=SHA1 (key) */ +if (inKeyLength > B) + { + SHA1Init(&theSHA1Context); + SHA1Update(&theSHA1Context, inKey, inKeyLength); + SHA1Final(inKey, &theSHA1Context); + inKeyLength = L; + } + +/* start out by storing key in pads */ +memset(k_ipad, 0, sizeof k_ipad); +memset(k_opad, 0, sizeof k_opad); +memcpy(k_ipad, inKey, inKeyLength); +memcpy(k_opad, inKey, inKeyLength); + +/* XOR key with ipad and opad values */ +int i; +for (i = 0; i < B; i++) + { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + +/* +* perform inner SHA1 +*/ +SHA1Init(&theSHA1Context); /* init context for 1st pass */ +SHA1Update(&theSHA1Context, k_ipad, B); /* start with inner pad */ +SHA1Update(&theSHA1Context, (unsigned char *)inText, inTextLength); /* then text of datagram */ +SHA1Final((unsigned char *)outDigest, &theSHA1Context); /* finish up 1st pass */ + +/* +* perform outer SHA1 +*/ +SHA1Init(&theSHA1Context); /* init context for 2nd +* pass */ +SHA1Update(&theSHA1Context, k_opad, B); /* start with outer pad */ +SHA1Update(&theSHA1Context, outDigest, L); /* then results of 1st +* hash */ +SHA1Final(outDigest, &theSHA1Context); /* finish up 2nd pass */ + +} \ No newline at end of file diff --git a/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/hmac.h b/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/hmac.h new file mode 100755 index 00000000..51eca9f6 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/hmac.h @@ -0,0 +1,31 @@ +// +// hmac.h +// OAuthConsumer +// +// Created by Jonathan Wight on 4/8/8. +// Copyright 2008 Jonathan Wight. 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. + +#ifndef HMAC_H +#define HMAC_H 1 + +extern void hmac_sha1(const unsigned char *inText, int inTextLength, unsigned char* inKey, const unsigned int inKeyLength, unsigned char *outDigest); + +#endif /* HMAC_H */ \ No newline at end of file diff --git a/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/sha1.c b/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/sha1.c new file mode 100755 index 00000000..16f5dbcf --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/sha1.c @@ -0,0 +1,169 @@ +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define LITTLE_ENDIAN * This should be #define'd if true. */ +#if __LITTLE_ENDIAN__ +#define LITTLE_ENDIAN +#endif +/* #define SHA1HANDSOFF * Copies data before messing with it. */ + +#include +#include + +#include "sha1.h" + +void SHA1Transform(unsigned long state[5], unsigned char buffer[64]); + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#ifdef LITTLE_ENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#else +#define blk0(i) block->l[i] +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void SHA1Transform(unsigned long state[5], unsigned char buffer[64]) +{ +unsigned long a, b, c, d, e; +typedef union { + unsigned char c[64]; + unsigned long l[16]; +} CHAR64LONG16; +CHAR64LONG16* block; +#ifdef SHA1HANDSOFF +static unsigned char workspace[64]; + block = (CHAR64LONG16*)workspace; + memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16*)buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* SHA1Init - Initialize new context */ + +void SHA1Init(SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len) +{ +unsigned int i, j; + + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + + +/* Add padding and return the message digest. */ + +void SHA1Final(unsigned char digest[20], SHA1_CTX* context) +{ +unsigned long i, j; +unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + SHA1Update(context, (unsigned char *)"\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1Update(context, (unsigned char *)"\0", 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + /* Wipe variables */ + i = j = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(&finalcount, 0, 8); +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */ + SHA1Transform(context->state, context->buffer); +#endif +} diff --git a/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/sha1.h b/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/sha1.h new file mode 100755 index 00000000..f3218ee5 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/Crytpo/sha1.h @@ -0,0 +1,12 @@ + +// From http://www.mirrors.wiretapped.net/security/cryptography/hashes/sha1/sha1.c + +typedef struct { + unsigned long state[5]; + unsigned long count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +extern void SHA1Init(SHA1_CTX* context); +extern void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len); +extern void SHA1Final(unsigned char digest[20], SHA1_CTX* context); diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OAAsynchronousDataFetcher.h b/Classes/ShareKit/Core/Helpers/OAuth/OAAsynchronousDataFetcher.h new file mode 100644 index 00000000..dd6c0498 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OAAsynchronousDataFetcher.h @@ -0,0 +1,45 @@ +// +// OAAsynchronousDataFetcher.h +// OAuthConsumer +// +// Created by Zsombor Szabó on 12/3/08. +// +// 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 "OAMutableURLRequest.h" + +@interface OAAsynchronousDataFetcher : NSObject { + OAMutableURLRequest *request; + NSHTTPURLResponse *response; + NSURLConnection *connection; + NSMutableData *responseData; + id delegate; + SEL didFinishSelector; + SEL didFailSelector; +} + ++ (id)asynchronousFetcherWithRequest:(OAMutableURLRequest *)aRequest delegate:(id)aDelegate didFinishSelector:(SEL)finishSelector didFailSelector:(SEL)failSelector; +- (id)initWithRequest:(OAMutableURLRequest *)aRequest delegate:(id)aDelegate didFinishSelector:(SEL)finishSelector didFailSelector:(SEL)failSelector; + +- (void)start; +- (void)cancel; + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OAAsynchronousDataFetcher.m b/Classes/ShareKit/Core/Helpers/OAuth/OAAsynchronousDataFetcher.m new file mode 100644 index 00000000..58472bc8 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OAAsynchronousDataFetcher.m @@ -0,0 +1,134 @@ +// +// OAAsynchronousDataFetcher.m +// OAuthConsumer +// +// Created by Zsombor Szabó on 12/3/08. +// +// 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 "OAAsynchronousDataFetcher.h" + +#import "OAServiceTicket.h" + +@implementation OAAsynchronousDataFetcher + ++ (id)asynchronousFetcherWithRequest:(OAMutableURLRequest *)aRequest delegate:(id)aDelegate didFinishSelector:(SEL)finishSelector didFailSelector:(SEL)failSelector +{ + return [[[OAAsynchronousDataFetcher alloc] initWithRequest:aRequest delegate:aDelegate didFinishSelector:finishSelector didFailSelector:failSelector] autorelease]; +} + +- (id)initWithRequest:(OAMutableURLRequest *)aRequest delegate:(id)aDelegate didFinishSelector:(SEL)finishSelector didFailSelector:(SEL)failSelector +{ + if (self = [super init]) + { + request = [aRequest retain]; + delegate = aDelegate; + didFinishSelector = finishSelector; + didFailSelector = failSelector; + } + return self; +} + +- (void)start +{ + [request prepare]; + + if (connection) + [connection release]; + + connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; + + if (connection) + { + if (responseData) + [responseData release]; + responseData = [[NSMutableData data] retain]; + } + else + { + OAServiceTicket *ticket= [[OAServiceTicket alloc] initWithRequest:request + response:nil + didSucceed:NO]; + [delegate performSelector:didFailSelector + withObject:ticket + withObject:nil]; + [ticket release]; + } +} + +- (void)cancel +{ + if (connection) + { + [connection cancel]; + [connection release]; + connection = nil; + } +} + +- (void)dealloc +{ + if (request) [request release]; + if (connection) [connection release]; + if (response) [response release]; + if (responseData) [responseData release]; + [super dealloc]; +} + +#pragma mark - +#pragma mark NSURLConnection methods + +- (void)connection:(NSURLConnection *)aConnection didReceiveResponse:(NSURLResponse *)aResponse +{ + if (response) + [response release]; + response = [aResponse retain]; + [responseData setLength:0]; +} + +- (void)connection:(NSURLConnection *)aConnection didReceiveData:(NSData *)data +{ + [responseData appendData:data]; +} + +- (void)connection:(NSURLConnection *)aConnection didFailWithError:(NSError *)error +{ + OAServiceTicket *ticket= [[OAServiceTicket alloc] initWithRequest:request + response:response + didSucceed:NO]; + [delegate performSelector:didFailSelector + withObject:ticket + withObject:error]; + + [ticket release]; +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection +{ + OAServiceTicket *ticket = [[OAServiceTicket alloc] initWithRequest:request + response:response + didSucceed:[(NSHTTPURLResponse *)response statusCode] < 400]; + [delegate performSelector:didFinishSelector + withObject:ticket + withObject:responseData]; + + [ticket release]; +} + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OAConsumer.h b/Classes/ShareKit/Core/Helpers/OAuth/OAConsumer.h new file mode 100755 index 00000000..73bdab65 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OAConsumer.h @@ -0,0 +1,40 @@ +// +// OAConsumer.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 + + +@interface OAConsumer : NSObject { +@protected + NSString *key; + NSString *secret; +} +@property(retain) NSString *key; +@property(retain) NSString *secret; + +- (id)initWithKey:(NSString *)aKey secret:(NSString *)aSecret; + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OAConsumer.m b/Classes/ShareKit/Core/Helpers/OAuth/OAConsumer.m new file mode 100755 index 00000000..0582ad9d --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OAConsumer.m @@ -0,0 +1,51 @@ +// +// OAConsumer.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 "OAConsumer.h" + + +@implementation OAConsumer +@synthesize key, secret; + +#pragma mark init + +- (id)initWithKey:(NSString *)aKey secret:(NSString *)aSecret +{ + if (self = [super init]) + { + self.key = aKey; + self.secret = aSecret; + } + return self; +} + +- (void)dealloc +{ + [key release]; + [secret release]; + [super dealloc]; +} + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OADataFetcher.h b/Classes/ShareKit/Core/Helpers/OAuth/OADataFetcher.h new file mode 100755 index 00000000..74273e37 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OADataFetcher.h @@ -0,0 +1,45 @@ +// +// OADataFetcher.h +// OAuthConsumer +// +// Created by Jon Crosby on 11/5/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 "OAMutableURLRequest.h" +#import "OAServiceTicket.h" + + +@interface OADataFetcher : NSObject { +@private + OAMutableURLRequest *request; + NSHTTPURLResponse *response; + NSURLConnection *connection; + NSError *error; + NSData *responseData; + id delegate; + SEL didFinishSelector; + SEL didFailSelector; +} + +- (void)fetchDataWithRequest:(OAMutableURLRequest *)aRequest delegate:(id)aDelegate didFinishSelector:(SEL)finishSelector didFailSelector:(SEL)failSelector; + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OADataFetcher.m b/Classes/ShareKit/Core/Helpers/OAuth/OADataFetcher.m new file mode 100755 index 00000000..b9c6d67c --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OADataFetcher.m @@ -0,0 +1,65 @@ +// +// OADataFetcher.m +// OAuthConsumer +// +// Created by Jon Crosby on 11/5/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 "OADataFetcher.h" + + +@implementation OADataFetcher + +- (void)fetchDataWithRequest:(OAMutableURLRequest *)aRequest + delegate:(id)aDelegate + didFinishSelector:(SEL)finishSelector + didFailSelector:(SEL)failSelector +{ + request = aRequest; + delegate = aDelegate; + didFinishSelector = finishSelector; + didFailSelector = failSelector; + + [request prepare]; + + responseData = [NSURLConnection sendSynchronousRequest:request + returningResponse:&response + error:&error]; + + if (response == nil || responseData == nil || error != nil) { + OAServiceTicket *ticket= [[OAServiceTicket alloc] initWithRequest:request + response:response + didSucceed:NO]; + [delegate performSelector:didFailSelector + withObject:ticket + withObject:error]; + } else { + OAServiceTicket *ticket = [[OAServiceTicket alloc] initWithRequest:request + response:response + didSucceed:[(NSHTTPURLResponse *)response statusCode] < 400]; + [delegate performSelector:didFinishSelector + withObject:ticket + withObject:responseData]; + } +} + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OAHMAC_SHA1SignatureProvider.h b/Classes/ShareKit/Core/Helpers/OAuth/OAHMAC_SHA1SignatureProvider.h new file mode 100755 index 00000000..d259c4ea --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OAHMAC_SHA1SignatureProvider.h @@ -0,0 +1,32 @@ +// +// OAHMAC_SHA1SignatureProvider.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 "OASignatureProviding.h" + + +@interface OAHMAC_SHA1SignatureProvider : NSObject +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OAHMAC_SHA1SignatureProvider.m b/Classes/ShareKit/Core/Helpers/OAuth/OAHMAC_SHA1SignatureProvider.m new file mode 100755 index 00000000..432baf7a --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OAHMAC_SHA1SignatureProvider.m @@ -0,0 +1,58 @@ +// +// OAHMAC_SHA1SignatureProvider.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 "OAHMAC_SHA1SignatureProvider.h" +#import + +#include "Base64Transcoder.h" + +@implementation OAHMAC_SHA1SignatureProvider + +- (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; + 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/Classes/ShareKit/Core/Helpers/OAuth/OAMutableURLRequest.h b/Classes/ShareKit/Core/Helpers/OAuth/OAMutableURLRequest.h new file mode 100755 index 00000000..03c6cfac --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OAMutableURLRequest.h @@ -0,0 +1,69 @@ +// +// OAMutableURLRequest.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 "OAConsumer.h" +#import "OAToken.h" +#import "OAHMAC_SHA1SignatureProvider.h" +#import "OASignatureProviding.h" +#import "NSMutableURLRequest+Parameters.h" +#import "NSURL+Base.h" + + +@interface OAMutableURLRequest : NSMutableURLRequest { +@protected + OAConsumer *consumer; + OAToken *token; + NSString *realm; + NSString *signature; + id signatureProvider; + NSString *nonce; + NSString *timestamp; + NSMutableDictionary *extraOAuthParameters; + BOOL didPrepare; +} +@property(readonly) NSString *signature; +@property(readonly) NSString *nonce; + +- (id)initWithURL:(NSURL *)aUrl + consumer:(OAConsumer *)aConsumer + token:(OAToken *)aToken + realm:(NSString *)aRealm +signatureProvider:(id)aProvider; + +- (id)initWithURL:(NSURL *)aUrl + consumer:(OAConsumer *)aConsumer + token:(OAToken *)aToken + realm:(NSString *)aRealm +signatureProvider:(id)aProvider + nonce:(NSString *)aNonce + timestamp:(NSString *)aTimestamp; + +- (void)prepare; + +- (void)setOAuthParameterName:(NSString*)parameterName withValue:(NSString*)parameterValue; + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OAMutableURLRequest.m b/Classes/ShareKit/Core/Helpers/OAuth/OAMutableURLRequest.m new file mode 100755 index 00000000..a91a01e7 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OAMutableURLRequest.m @@ -0,0 +1,247 @@ +// +// OAMutableURLRequest.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 "OAMutableURLRequest.h" +#import "SHKConfig.h" + + +@interface OAMutableURLRequest (Private) +- (void)_generateTimestamp; +- (void)_generateNonce; +- (NSString *)_signatureBaseString; +@end + +@implementation OAMutableURLRequest +@synthesize signature, nonce; + +#pragma mark init + +- (id)initWithURL:(NSURL *)aUrl + consumer:(OAConsumer *)aConsumer + token:(OAToken *)aToken + realm:(NSString *)aRealm +signatureProvider:(id)aProvider +{ + if (self = [super initWithURL:aUrl + cachePolicy:NSURLRequestReloadIgnoringCacheData + timeoutInterval:10.0]) + { + consumer = [aConsumer retain]; + + // empty token for Unauthorized Request Token transaction + if (aToken == nil) + token = [[OAToken alloc] init]; + else + token = [aToken retain]; + + if (aRealm == nil) + realm = [[NSString alloc] initWithString:@""]; + else + realm = [aRealm retain]; + + // default to HMAC-SHA1 + if (aProvider == nil) + signatureProvider = [[OAHMAC_SHA1SignatureProvider alloc] init]; + else + signatureProvider = [aProvider retain]; + + [self _generateTimestamp]; + [self _generateNonce]; + + didPrepare = NO; + } + return self; +} + +// Setting a timestamp and nonce to known +// values can be helpful for testing +- (id)initWithURL:(NSURL *)aUrl + consumer:(OAConsumer *)aConsumer + token:(OAToken *)aToken + realm:(NSString *)aRealm +signatureProvider:(id)aProvider + nonce:(NSString *)aNonce + timestamp:(NSString *)aTimestamp +{ + if (self = [super initWithURL:aUrl + cachePolicy:NSURLRequestReloadIgnoringCacheData + timeoutInterval:10.0]) + { + consumer = [aConsumer retain]; + + // empty token for Unauthorized Request Token transaction + if (aToken == nil) + token = [[OAToken alloc] init]; + else + token = [aToken retain]; + + if (aRealm == nil) + realm = [[NSString alloc] initWithString:@""]; + else + realm = [aRealm retain]; + + // default to HMAC-SHA1 + if (aProvider == nil) + signatureProvider = [[OAHMAC_SHA1SignatureProvider alloc] init]; + else + signatureProvider = [aProvider retain]; + + timestamp = [aTimestamp retain]; + nonce = [aNonce retain]; + + didPrepare = NO; + } + return self; +} + +- (void)dealloc +{ + [consumer release]; + [token release]; + [realm release]; + [signatureProvider release]; + [timestamp release]; + [nonce release]; + [extraOAuthParameters release]; + [super dealloc]; +} + +#pragma mark - +#pragma mark Public + +- (void)setOAuthParameterName:(NSString*)parameterName withValue:(NSString*)parameterValue +{ + assert(parameterName && parameterValue); + + if (extraOAuthParameters == nil) { + extraOAuthParameters = [NSMutableDictionary new]; + } + + [extraOAuthParameters setObject:parameterValue forKey:parameterName]; +} + +- (void)prepare +{ + if (didPrepare) { + return; + } + didPrepare = YES; + // sign + // Secrets must be urlencoded before concatenated with '&' + // TODO: if later RSA-SHA1 support is added then a little code redesign is needed + signature = [signatureProvider signClearText:[self _signatureBaseString] + withSecret:[NSString stringWithFormat:@"%@&%@", + [consumer.secret URLEncodedString], + [token.secret URLEncodedString]]]; + + // set OAuth headers + NSString *oauthToken; + if ([token.key isEqualToString:@""]) + oauthToken = @""; // not used on Request Token transactions + else + oauthToken = [NSString stringWithFormat:@"oauth_token=\"%@\", ", [token.key URLEncodedString]]; + + NSMutableString *extraParameters = [NSMutableString string]; + + // Adding the optional parameters in sorted order isn't required by the OAuth spec, but it makes it possible to hard-code expected values in the unit tests. + for(NSString *parameterName in [[extraOAuthParameters allKeys] sortedArrayUsingSelector:@selector(compare:)]) + { + [extraParameters appendFormat:@", %@=\"%@\"", + [parameterName URLEncodedString], + [[extraOAuthParameters objectForKey:parameterName] URLEncodedString]]; + } + + NSString *oauthHeader = [NSString stringWithFormat:@"OAuth realm=\"%@\", oauth_consumer_key=\"%@\", %@oauth_signature_method=\"%@\", oauth_signature=\"%@\", oauth_timestamp=\"%@\", oauth_nonce=\"%@\", oauth_version=\"1.0\"%@", + [realm URLEncodedString], + [consumer.key URLEncodedString], + oauthToken, + [[signatureProvider name] URLEncodedString], + [signature URLEncodedString], + timestamp, + nonce, + extraParameters]; + + [self setValue:oauthHeader forHTTPHeaderField:@"Authorization"]; +} + +#pragma mark - +#pragma mark Private + +- (void)_generateTimestamp +{ + timestamp = [[NSString stringWithFormat:@"%d", time(NULL)] retain]; +} + +- (void)_generateNonce +{ + CFUUIDRef theUUID = CFUUIDCreate(NULL); + CFStringRef string = CFUUIDCreateString(NULL, theUUID); + NSMakeCollectable(theUUID); + nonce = (NSString *)string; +} + +- (NSString *)_signatureBaseString +{ + // OAuth Spec, Section 9.1.1 "Normalize Request Parameters" + // build a sorted array of both request parameters and OAuth header parameters + NSMutableArray *parameterPairs = [NSMutableArray arrayWithCapacity:(6)]; // 6 being the number of OAuth params in the Signature Base String + + [parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_consumer_key" value:consumer.key] URLEncodedNameValuePair]]; + [parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_signature_method" value:[signatureProvider name]] URLEncodedNameValuePair]]; + [parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_timestamp" value:timestamp] URLEncodedNameValuePair]]; + [parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_nonce" value:nonce] URLEncodedNameValuePair]]; + [parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_version" value:@"1.0"] URLEncodedNameValuePair]]; + + if (![token.key isEqualToString:@""]) { + [parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_token" value:token.key] URLEncodedNameValuePair]]; + } + + + for(NSString *parameterName in [[extraOAuthParameters allKeys] sortedArrayUsingSelector:@selector(compare:)]) { + [parameterPairs addObject:[[OARequestParameter requestParameterWithName:[parameterName URLEncodedString] value: [[extraOAuthParameters objectForKey:parameterName] URLEncodedString]] URLEncodedNameValuePair]]; + } + + if (![[self valueForHTTPHeaderField:@"Content-Type"] hasPrefix:@"multipart/form-data"]) { + for (OARequestParameter *param in [self parameters]) { + [parameterPairs addObject:[param URLEncodedNameValuePair]]; + } + } + + NSArray *sortedPairs = [parameterPairs sortedArrayUsingSelector:@selector(compare:)]; + NSString *normalizedRequestParameters = [sortedPairs componentsJoinedByString:@"&"]; + + // OAuth Spec, Section 9.1.2 "Concatenate Request Elements" + NSString *ret = [NSString stringWithFormat:@"%@&%@&%@", + [self HTTPMethod], + [[[self URL] URLStringWithoutQuery] URLEncodedString], + [normalizedRequestParameters URLEncodedString]]; + + SHKLog(@"OAMutableURLRequest parameters %@", normalizedRequestParameters); + + return ret; +} + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OAPlaintextSignatureProvider.h b/Classes/ShareKit/Core/Helpers/OAuth/OAPlaintextSignatureProvider.h new file mode 100755 index 00000000..96bb2f28 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OAPlaintextSignatureProvider.h @@ -0,0 +1,31 @@ +// +// OAPlaintextSignatureProvider.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 "OASignatureProviding.h" + +@interface OAPlaintextSignatureProvider : NSObject +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OAPlaintextSignatureProvider.m b/Classes/ShareKit/Core/Helpers/OAuth/OAPlaintextSignatureProvider.m new file mode 100755 index 00000000..6f0c1c3a --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OAPlaintextSignatureProvider.m @@ -0,0 +1,43 @@ +// +// OAPlaintextSignatureProvider.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 "OAPlaintextSignatureProvider.h" +#import "NSString+URLEncoding.h" + + +@implementation OAPlaintextSignatureProvider + +- (NSString *)name +{ + return @"PLAINTEXT"; +} + +- (NSString *)signClearText:(NSString *)text withSecret:(NSString *)secret +{ + return secret; +} + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OARequestParameter.h b/Classes/ShareKit/Core/Helpers/OAuth/OARequestParameter.h new file mode 100755 index 00000000..03eee3e0 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OARequestParameter.h @@ -0,0 +1,45 @@ +// +// OARequestParameter.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 "NSString+URLEncoding.h" + + +@interface OARequestParameter : NSObject { +@protected + NSString *name; + NSString *value; +} +@property(retain) NSString *name; +@property(retain) NSString *value; + ++ (id)requestParameterWithName:(NSString *)aName value:(NSString *)aValue; +- (id)initWithName:(NSString *)aName value:(NSString *)aValue; +- (NSString *)URLEncodedName; +- (NSString *)URLEncodedValue; +- (NSString *)URLEncodedNameValuePair; + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OARequestParameter.m b/Classes/ShareKit/Core/Helpers/OAuth/OARequestParameter.m new file mode 100755 index 00000000..59b6a8a6 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OARequestParameter.m @@ -0,0 +1,70 @@ +// +// OARequestParameter.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 "OARequestParameter.h" + + +@implementation OARequestParameter +@synthesize name, value; + ++ (id)requestParameterWithName:(NSString *)aName value:(NSString *)aValue +{ + return [[[OARequestParameter alloc] initWithName:aName value:aValue] autorelease]; +} + +- (id)initWithName:(NSString *)aName value:(NSString *)aValue +{ + if (self = [super init]) + { + self.name = aName; + self.value = aValue; + } + return self; +} + +- (void)dealloc +{ + [name release]; + [value release]; + [super dealloc]; +} + +- (NSString *)URLEncodedName +{ + return [self.name URLEncodedString]; +} + +- (NSString *)URLEncodedValue +{ + return [self.value URLEncodedString]; +} + +- (NSString *)URLEncodedNameValuePair +{ + return [NSString stringWithFormat:@"%@=%@", [self URLEncodedName], [self URLEncodedValue]]; +} + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OAServiceTicket.h b/Classes/ShareKit/Core/Helpers/OAuth/OAServiceTicket.h new file mode 100755 index 00000000..9a8eb348 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OAServiceTicket.h @@ -0,0 +1,47 @@ +// +// OAServiceTicket.h +// OAuthConsumer +// +// Created by Jon Crosby on 11/5/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 "OAMutableURLRequest.h" + + +@interface OAServiceTicket : NSObject { +@private + OAMutableURLRequest *request; + NSHTTPURLResponse *response; + NSData *data; + BOOL didSucceed; +} +@property(readonly) OAMutableURLRequest *request; +@property(readonly) NSHTTPURLResponse *response; +@property(readonly) NSData *data; +@property(readonly) BOOL didSucceed; +@property(readonly) NSString *body; + +- (id)initWithRequest:(OAMutableURLRequest *)aRequest response:(NSHTTPURLResponse *)aResponse didSucceed:(BOOL)success; +- (id)initWithRequest:(OAMutableURLRequest *)aRequest response:(NSHTTPURLResponse *)aResponse data:(NSData *)aData didSucceed:(BOOL)success; + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OAServiceTicket.m b/Classes/ShareKit/Core/Helpers/OAuth/OAServiceTicket.m new file mode 100755 index 00000000..fb6e3b7c --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OAServiceTicket.m @@ -0,0 +1,56 @@ +// +// OAServiceTicket.m +// OAuthConsumer +// +// Created by Jon Crosby on 11/5/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 "OAServiceTicket.h" + + +@implementation OAServiceTicket +@synthesize request, response, data, didSucceed; + +- (id)initWithRequest:(OAMutableURLRequest *)aRequest response:(NSHTTPURLResponse *)aResponse didSucceed:(BOOL)success +{ + return [self initWithRequest:aRequest response:aResponse data:nil didSucceed:success]; +} + +- (id)initWithRequest:(OAMutableURLRequest *)aRequest response:(NSHTTPURLResponse *)aResponse data:(NSData *)aData didSucceed:(BOOL)success { + [super init]; + request = aRequest; + response = aResponse; + data = aData; + didSucceed = success; + return self; +} + +- (NSString *)body +{ + if (!data) { + return nil; + } + + return [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; +} + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OASignatureProviding.h b/Classes/ShareKit/Core/Helpers/OAuth/OASignatureProviding.h new file mode 100755 index 00000000..0c7e4f8c --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OASignatureProviding.h @@ -0,0 +1,34 @@ +// +// OASignatureProviding.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 OASignatureProviding + +- (NSString *)name; +- (NSString *)signClearText:(NSString *)text withSecret:(NSString *)secret; + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OAToken.h b/Classes/ShareKit/Core/Helpers/OAuth/OAToken.h new file mode 100755 index 00000000..e9904d7e --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OAToken.h @@ -0,0 +1,43 @@ +// +// OAToken.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 + +@interface OAToken : NSObject { +@protected + NSString *key; + NSString *secret; + NSString *sessionHandle; +} +@property(retain) NSString *key; +@property(retain) NSString *secret; +@property(retain) NSString *sessionHandle; + +- (id)initWithKey:(NSString *)aKey secret:(NSString *)aSecret; +- (id)initWithUserDefaultsUsingServiceProviderName:(NSString *)provider prefix:(NSString *)prefix; +- (id)initWithHTTPResponseBody:(NSString *)body; +- (int)storeInUserDefaultsWithServiceProviderName:(NSString *)provider prefix:(NSString *)prefix; + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OAToken.m b/Classes/ShareKit/Core/Helpers/OAuth/OAToken.m new file mode 100755 index 00000000..22c4b9d1 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OAToken.m @@ -0,0 +1,109 @@ +// +// OAToken.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 "OAToken.h" + + +@implementation OAToken + +@synthesize key, secret, sessionHandle; + +#pragma mark init + +- (id)init +{ + if (self = [super init]) + { + self.key = @""; + self.secret = @""; + self.sessionHandle = @""; + } + return self; +} + +- (id)initWithKey:(NSString *)aKey secret:(NSString *)aSecret +{ + if (self = [super init]) + { + self.key = aKey; + self.secret = aSecret; + } + return self; +} + +- (id)initWithHTTPResponseBody:(NSString *)body +{ + if (self = [super init]) + { + NSArray *pairs = [body componentsSeparatedByString:@"&"]; + + for (NSString *pair in pairs) { + NSArray *elements = [pair componentsSeparatedByString:@"="]; + if ([[elements objectAtIndex:0] isEqualToString:@"oauth_token"]) { + self.key = [[elements objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + } else if ([[elements objectAtIndex:0] isEqualToString:@"oauth_token_secret"]) { + self.secret = [[elements objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + } else if ([[elements objectAtIndex:0] isEqualToString:@"oauth_session_handle"]) { + self.sessionHandle = [[elements objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + } + } + } + return self; +} + +- (id)initWithUserDefaultsUsingServiceProviderName:(NSString *)provider prefix:(NSString *)prefix +{ + if (self = [super init]) + { + NSString *theKey = [[NSUserDefaults standardUserDefaults] stringForKey:[NSString stringWithFormat:@"OAUTH_%@_%@_KEY", prefix, provider]]; + NSString *theSecret = [[NSUserDefaults standardUserDefaults] stringForKey:[NSString stringWithFormat:@"OAUTH_%@_%@_SECRET", prefix, provider]]; + if (theKey == NULL || theSecret == NULL) + return(nil); + self.key = theKey; + self.secret = theSecret; + } + return self; +} + +- (void)dealloc +{ + [key release]; + [secret release]; + [sessionHandle release]; + [super dealloc]; +} + +#pragma mark - + +- (int)storeInUserDefaultsWithServiceProviderName:(NSString *)provider prefix:(NSString *)prefix +{ + [[NSUserDefaults standardUserDefaults] setObject:self.key forKey:[NSString stringWithFormat:@"OAUTH_%@_%@_KEY", prefix, provider]]; + [[NSUserDefaults standardUserDefaults] setObject:self.secret forKey:[NSString stringWithFormat:@"OAUTH_%@_%@_SECRET", prefix, provider]]; + [[NSUserDefaults standardUserDefaults] synchronize]; + return(0); +} + +@end diff --git a/Classes/ShareKit/Core/Helpers/OAuth/OAuthConsumer.h b/Classes/ShareKit/Core/Helpers/OAuth/OAuthConsumer.h new file mode 100755 index 00000000..1ac26b20 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/OAuth/OAuthConsumer.h @@ -0,0 +1,39 @@ +// +// OAuthConsumer.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 "OAToken.h" +#import "OAConsumer.h" +#import "OAMutableURLRequest.h" +#import "NSString+URLEncoding.h" +#import "NSMutableURLRequest+Parameters.h" +#import "NSURL+Base.h" +#import "OASignatureProviding.h" +#import "OAHMAC_SHA1SignatureProvider.h" +#import "OAPlaintextSignatureProvider.h" +#import "OARequestParameter.h" +#import "OAServiceTicket.h" +#import "OADataFetcher.h" +#import "OAAsynchronousDataFetcher.h" \ No newline at end of file diff --git a/Classes/ShareKit/Core/Helpers/SHKRequest.h b/Classes/ShareKit/Core/Helpers/SHKRequest.h new file mode 100644 index 00000000..9e66adf5 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/SHKRequest.h @@ -0,0 +1,74 @@ +// +// SHKRequest.h +// ShareKit +// +// Created by Nathan Weiner on 6/9/10. + +// +// 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 SHKRequest : NSObject +{ + NSURL *url; + NSString *params; + NSString *method; + NSDictionary *headerFields; + + id delegate; + SEL isFinishedSelector; + + NSURLConnection *connection; + + NSHTTPURLResponse *response; + NSDictionary *headers; + + NSMutableData *data; + NSString *result; + BOOL success; +} + +@property (retain) NSURL *url; +@property (retain) NSString *params; +@property (retain) NSString *method; +@property (retain) NSDictionary *headerFields; + +@property (assign) id delegate; +@property (assign) SEL isFinishedSelector; + +@property (retain) NSURLConnection *connection; + +@property (retain) NSHTTPURLResponse *response; +@property (retain) NSDictionary *headers; + +@property (retain) NSMutableData *data; +@property (retain, getter=getResult) NSString *result; +@property (nonatomic) BOOL success; + +- (id)initWithURL:(NSURL *)u params:(NSString *)p delegate:(id)d isFinishedSelector:(SEL)s method:(NSString *)m autostart:(BOOL)autostart; + +- (void)start; +- (void)finish; + + +@end diff --git a/Classes/ShareKit/Core/Helpers/SHKRequest.m b/Classes/ShareKit/Core/Helpers/SHKRequest.m new file mode 100644 index 00000000..4603d2e5 --- /dev/null +++ b/Classes/ShareKit/Core/Helpers/SHKRequest.m @@ -0,0 +1,149 @@ +// +// SHKRequest.m +// ShareKit +// +// Created by Nathan Weiner on 6/9/10. + +// +// 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 "SHKRequest.h" +#import "SHKConfig.h" + +#define SHK_TIMEOUT 90 + +@implementation SHKRequest + +@synthesize url, params, method, headerFields; +@synthesize delegate, isFinishedSelector; +@synthesize data, result, headers, response, connection; +@synthesize success; + +- (void)dealloc +{ + [url release]; + [params release]; + [method release]; + [headerFields release]; + [connection release]; + [data release]; + [result release]; + [response release]; + [super dealloc]; +} + +- (id)initWithURL:(NSURL *)u params:(NSString *)p delegate:(id)d isFinishedSelector:(SEL)s method:(NSString *)m autostart:(BOOL)autostart +{ + if (self = [super init]) + { + self.url = u; + self.params = p; + self.method = m; + + self.delegate = d; + self.isFinishedSelector = s; + + if (autostart) + [self start]; + } + + return self; +} + + +#pragma mark - + +- (void)start +{ + self.data = [[NSMutableData alloc] initWithLength:0]; + [data release]; + + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url + cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData + timeoutInterval:SHK_TIMEOUT]; + + // overwrite header fields (generally for cookies) + if (headerFields != nil) + [request setAllHTTPHeaderFields:headerFields]; + + // Setup Request Data/Params + if (params != nil) + { + NSData *paramsData = [ NSData dataWithBytes:[params UTF8String] length:[params length] ]; + + // Fill Request + [request setHTTPMethod:method]; + [request setHTTPBody:paramsData]; + } + + // Start Connection + SHKLog(@"Start SHKRequest:\nURL: %@\nparams: %@", url, params); + self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]; + [request release]; + [connection release]; +} + + +#pragma mark - + +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)theResponse +{ + self.response = theResponse; + self.headers = [[response allHeaderFields] mutableCopy]; + [headers release]; + + [data setLength:0]; +} + +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)d +{ + [data appendData:d]; +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + [self finish]; +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + [self finish]; +} + +#pragma mark - + +- (void)finish +{ + self.success = (response.statusCode == 200 || response.statusCode == 201); + + if ([delegate respondsToSelector:isFinishedSelector]) + [delegate performSelector:isFinishedSelector withObject:self]; +} + +- (NSString *)getResult +{ + if (result == nil) + self.result = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; + return result; +} + + +@end diff --git a/Classes/ShareKit/Core/SHK.h b/Classes/ShareKit/Core/SHK.h new file mode 100644 index 00000000..73a9aba1 --- /dev/null +++ b/Classes/ShareKit/Core/SHK.h @@ -0,0 +1,130 @@ +// +// SHK.h +// ShareKit +// +// Created by Nathan Weiner on 6/10/10. + +// +// 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. +// +// + +#define SHK_VERSION @"0.2.1" + +#import +#import "SHKConfig.h" +#import "SHKItem.h" +#import "SHKActionSheet.h" +#import "SHKRequest.h" +#import "SHKActivityIndicator.h" +#import "SHKFormFieldSettings.h" +#import "UIWebView+SHK.h" + + +@class SHKActionSheet; +@class SHKViewControllerWrapper; + + +@interface SHK : NSObject +{ + UIViewController *rootViewController; + UIViewController *currentView; + UIViewController *pendingView; + BOOL isDismissingView; + + NSOperationQueue *offlineQueue; +} + +@property (nonatomic, assign) UIViewController *rootViewController; +@property (nonatomic, retain) UIViewController *currentView; +@property (nonatomic, retain) UIViewController *pendingView; +@property BOOL isDismissingView; + +@property (nonatomic, retain) NSOperationQueue *offlineQueue; + + + +#pragma mark - + ++ (SHK *)currentHelper; + ++ (NSDictionary *)sharersDictionary; + + +#pragma mark - +#pragma mark View Management + ++ (void)setRootViewController:(UIViewController *)vc; +- (void)showViewController:(UIViewController *)vc; +- (void)hideCurrentViewControllerAnimated:(BOOL)animated; +- (void)viewWasDismissed; +- (UIViewController *)getTopViewController; + ++ (UIBarStyle)barStyle; ++ (UIModalPresentationStyle)modalPresentationStyle; ++ (UIModalTransitionStyle)modalTransitionStyle; + +#pragma mark - +#pragma mark Favorites + ++ (NSArray *)favoriteSharersForType:(SHKShareType)type; ++ (void)pushOnFavorites:(NSString *)className forType:(SHKShareType)type; ++ (void)setFavorites:(NSArray *)favs forType:(SHKShareType)type; + ++ (NSDictionary *)getUserExclusions; ++ (void)setUserExclusions:(NSDictionary *)exclusions; + + +#pragma mark - +#pragma mark Credentials + ++ (NSString *)getAuthValueForKey:(NSString *)key forSharer:(NSString *)sharerId; ++ (void)setAuthValue:(NSString *)value forKey:(NSString *)key forSharer:(NSString *)sharerId; ++ (void)removeAuthValueForKey:(NSString *)key forSharer:(NSString *)sharerId; + ++ (void)logoutOfAll; ++ (void)logoutOfService:(NSString *)sharerId; + +#pragma mark - +#pragma mark Offline Support + ++ (NSString *)offlineQueuePath; ++ (NSString *)offlineQueueListPath; ++ (NSMutableArray *)getOfflineQueueList; ++ (void)saveOfflineQueueList:(NSMutableArray *)queueList; ++ (BOOL)addToOfflineQueue:(SHKItem *)item forSharer:(NSString *)sharerId; ++ (void)flushOfflineQueue; + +#pragma mark - + ++ (NSError *)error:(NSString *)description, ...; + +#pragma mark - +#pragma mark Network + ++ (BOOL)connected; + +@end + + +NSString * SHKStringOrBlank(NSString * value); +NSString * SHKEncode(NSString * value); +NSString * SHKEncodeURL(NSURL * value); +NSString* SHKLocalizedString(NSString* key, ...); +void SHKSwizzle(Class c, SEL orig, SEL newClassName); diff --git a/Classes/ShareKit/Core/SHK.m b/Classes/ShareKit/Core/SHK.m new file mode 100644 index 00000000..057de631 --- /dev/null +++ b/Classes/ShareKit/Core/SHK.m @@ -0,0 +1,601 @@ +// +// SHK.m +// ShareKit +// +// Created by Nathan Weiner on 6/10/10. + +// +// 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 "SHK.h" +#import "SHKActivityIndicator.h" +#import "SHKViewControllerWrapper.h" +#import "SHKActionSheet.h" +#import "SHKOfflineSharer.h" +#import "SFHFKeychainUtils.h" +#import "Reachability.h" +#import +#import + + +@implementation SHK + +@synthesize currentView, pendingView, isDismissingView; +@synthesize rootViewController; +@synthesize offlineQueue; + +static SHK *currentHelper = nil; +BOOL SHKinit; + + ++ (SHK *)currentHelper +{ + if (currentHelper == nil) + currentHelper = [[SHK alloc] init]; + + return currentHelper; +} + ++ (void)initialize +{ + [super initialize]; + + if (!SHKinit) + { + SHKSwizzle([MFMailComposeViewController class], @selector(viewDidDisappear:), @selector(SHKviewDidDisappear:)); + SHKinit = YES; + } +} + +- (void)dealloc +{ + [currentView release]; + [pendingView release]; + [offlineQueue release]; + [super dealloc]; +} + + + +#pragma mark - +#pragma mark View Management + ++ (void)setRootViewController:(UIViewController *)vc +{ + SHK *helper = [self currentHelper]; + [helper setRootViewController:vc]; +} + +- (void)showViewController:(UIViewController *)vc +{ + if (rootViewController == nil) + { + // Try to find the root view controller programmically + + // Find the top window (that is not an alert view or other window) + UIWindow *topWindow = [[UIApplication sharedApplication] keyWindow]; + if (topWindow.windowLevel != UIWindowLevelNormal) + { + NSArray *windows = [[UIApplication sharedApplication] windows]; + for(topWindow in windows) + { + if (topWindow.windowLevel == UIWindowLevelNormal) + break; + } + } + + UIView *rootView = [[topWindow subviews] objectAtIndex:0]; + id nextResponder = [rootView nextResponder]; + + if ([nextResponder isKindOfClass:[UIViewController class]]) + self.rootViewController = nextResponder; + + else + NSAssert(NO, @"ShareKit: Could not find a root view controller. You can assign one manually by calling [[SHK currentHelper] setRootViewController:YOURROOTVIEWCONTROLLER]."); + } + + // Find the top most view controller being displayed (so we can add the modal view to it and not one that is hidden) + UIViewController *topViewController = [self getTopViewController]; + if (topViewController == nil) + NSAssert(NO, @"ShareKit: There is no view controller to display from"); + + + // If a view is already being shown, hide it, and then try again + if (currentView != nil) + { + self.pendingView = vc; + [[currentView parentViewController] dismissModalViewControllerAnimated:YES]; + return; + } + + // Wrap the view in a nav controller if not already + if (![vc respondsToSelector:@selector(pushViewController:animated:)]) + { + UINavigationController *nav = [[[UINavigationController alloc] initWithRootViewController:vc] autorelease]; + + if ([nav respondsToSelector:@selector(modalPresentationStyle)]) + nav.modalPresentationStyle = [SHK modalPresentationStyle]; + + if ([nav respondsToSelector:@selector(modalTransitionStyle)]) + nav.modalTransitionStyle = [SHK modalTransitionStyle]; + + nav.navigationBar.barStyle = nav.toolbar.barStyle = [SHK barStyle]; + + [topViewController presentModalViewController:nav animated:YES]; + self.currentView = nav; + } + + // Show the nav controller + else + { + if ([vc respondsToSelector:@selector(modalPresentationStyle)]) + vc.modalPresentationStyle = [SHK modalPresentationStyle]; + + if ([vc respondsToSelector:@selector(modalTransitionStyle)]) + vc.modalTransitionStyle = [SHK modalTransitionStyle]; + + [topViewController presentModalViewController:vc animated:YES]; + [(UINavigationController *)vc navigationBar].barStyle = + [(UINavigationController *)vc toolbar].barStyle = [SHK barStyle]; + self.currentView = vc; + } + + self.pendingView = nil; +} + +- (void)hideCurrentViewController +{ + [self hideCurrentViewControllerAnimated:YES]; +} + +- (void)hideCurrentViewControllerAnimated:(BOOL)animated +{ + if (isDismissingView) + return; + + if (currentView != nil) + { + // Dismiss the modal view + if ([currentView parentViewController] != nil) + { + self.isDismissingView = YES; + [[currentView parentViewController] dismissModalViewControllerAnimated:animated]; + } + + else + self.currentView = nil; + } +} + +- (void)showPendingView +{ + if (pendingView) + [self showViewController:pendingView]; +} + + +- (void)viewWasDismissed +{ + self.isDismissingView = NO; + + if (currentView != nil) + currentView = nil; + + if (pendingView) + { + // This is an ugly way to do it, but it works. + // There seems to be an issue chaining modal views otherwise + // See: http://github.com/ideashower/ShareKit/issues#issue/24 + [self performSelector:@selector(showPendingView) withObject:nil afterDelay:0.02]; + return; + } +} + +- (UIViewController *)getTopViewController +{ + UIViewController *topViewController = rootViewController; + while (topViewController.modalViewController != nil) + topViewController = topViewController.modalViewController; + return topViewController; +} + ++ (UIBarStyle)barStyle +{ + if ([SHKBarStyle isEqualToString:@"UIBarStyleBlack"]) + return UIBarStyleBlack; + + else if ([SHKBarStyle isEqualToString:@"UIBarStyleBlackOpaque"]) + return UIBarStyleBlackOpaque; + + else if ([SHKBarStyle isEqualToString:@"UIBarStyleBlackTranslucent"]) + return UIBarStyleBlackTranslucent; + + return UIBarStyleDefault; +} + ++ (UIModalPresentationStyle)modalPresentationStyle +{ + if ([SHKModalPresentationStyle isEqualToString:@"UIModalPresentationFullScreen"]) + return UIModalPresentationFullScreen; + + else if ([SHKModalPresentationStyle isEqualToString:@"UIModalPresentationPageSheet"]) + return UIModalPresentationPageSheet; + + else if ([SHKModalPresentationStyle isEqualToString:@"UIModalPresentationFormSheet"]) + return UIModalPresentationFormSheet; + + return UIModalPresentationCurrentContext; +} + ++ (UIModalTransitionStyle)modalTransitionStyle +{ + if ([SHKModalTransitionStyle isEqualToString:@"UIModalTransitionStyleFlipHorizontal"]) + return UIModalTransitionStyleFlipHorizontal; + + else if ([SHKModalTransitionStyle isEqualToString:@"UIModalTransitionStyleCrossDissolve"]) + return UIModalTransitionStyleCrossDissolve; + + else if ([SHKModalTransitionStyle isEqualToString:@"UIModalTransitionStylePartialCurl"]) + return UIModalTransitionStylePartialCurl; + + return UIModalTransitionStyleCoverVertical; +} + + +#pragma mark - +#pragma mark Favorites + + ++ (NSArray *)favoriteSharersForType:(SHKShareType)type +{ + NSArray *favoriteSharers = [[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithFormat:@"%@%i", SHK_FAVS_PREFIX_KEY, type]]; + + // set defaults + if (favoriteSharers == nil) + { + switch (type) + { + case SHKShareTypeURL: + favoriteSharers = [NSArray arrayWithObjects:@"SHKTwitter",@"SHKFacebook",@"SHKReadItLater",nil]; + break; + + case SHKShareTypeImage: + favoriteSharers = [NSArray arrayWithObjects:@"SHKMail",@"SHKFacebook",@"SHKCopy",nil]; + break; + + case SHKShareTypeText: + favoriteSharers = [NSArray arrayWithObjects:@"SHKMail",@"SHKTwitter",@"SHKFacebook", nil]; + break; + + case SHKShareTypeFile: + favoriteSharers = [NSArray arrayWithObjects:@"SHKMail", nil]; + break; + } + + // Save defaults to prefs + [self setFavorites:favoriteSharers forType:type]; + } + + // Make sure the favorites are not using any exclusions, remove them if they are. + NSArray *exclusions = [[NSUserDefaults standardUserDefaults] objectForKey:@"SHKExcluded"]; + if (exclusions != nil) + { + NSMutableArray *newFavs = [favoriteSharers mutableCopy]; + for(NSString *sharerId in exclusions) + { + [newFavs removeObject:sharerId]; + } + + // Update + favoriteSharers = [NSArray arrayWithArray:newFavs]; + [self setFavorites:favoriteSharers forType:type]; + + [newFavs release]; + } + + return favoriteSharers; +} + ++ (void)pushOnFavorites:(NSString *)className forType:(SHKShareType)type +{ + NSMutableArray *favs = [[self favoriteSharersForType:type] mutableCopy]; + + [favs removeObject:className]; + [favs insertObject:className atIndex:0]; + + while (favs.count > SHK_MAX_FAV_COUNT) + [favs removeLastObject]; + + [self setFavorites:favs forType:type]; + + [favs release]; +} + ++ (void)setFavorites:(NSArray *)favs forType:(SHKShareType)type +{ + [[NSUserDefaults standardUserDefaults] setObject:favs forKey:[NSString stringWithFormat:@"%@%i", SHK_FAVS_PREFIX_KEY, type]]; +} + +#pragma mark - + ++ (NSDictionary *)getUserExclusions +{ + return [[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithFormat:@"%@Exclusions", SHK_FAVS_PREFIX_KEY]]; +} + ++ (void)setUserExclusions:(NSDictionary *)exclusions +{ + return [[NSUserDefaults standardUserDefaults] setObject:exclusions forKey:[NSString stringWithFormat:@"%@Exclusions", SHK_FAVS_PREFIX_KEY]]; +} + + + +#pragma mark - +#pragma mark Credentials + +// TODO someone with more keychain experience may want to clean this up. The use of SFHFKeychainUtils may be unnecessary? + ++ (NSString *)getAuthValueForKey:(NSString *)key forSharer:(NSString *)sharerId +{ +#if TARGET_IPHONE_SIMULATOR + // Using NSUserDefaults for storage is very insecure, but because Keychain only exists on a device + // we use NSUserDefaults when running on the simulator to store objects. This allows you to still test + // in the simulator. You should NOT modify in a way that does not use keychain when actually deployed to a device. + return [[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithFormat:@"%@%@%@",SHK_AUTH_PREFIX,sharerId,key]]; +#else + return [SFHFKeychainUtils getPasswordForUsername:key andServiceName:[NSString stringWithFormat:@"%@%@",SHK_AUTH_PREFIX,sharerId] error:nil]; +#endif +} + ++ (void)setAuthValue:(NSString *)value forKey:(NSString *)key forSharer:(NSString *)sharerId +{ +#if TARGET_IPHONE_SIMULATOR + // Using NSUserDefaults for storage is very insecure, but because Keychain only exists on a device + // we use NSUserDefaults when running on the simulator to store objects. This allows you to still test + // in the simulator. You should NOT modify in a way that does not use keychain when actually deployed to a device. + [[NSUserDefaults standardUserDefaults] setObject:value forKey:[NSString stringWithFormat:@"%@%@%@",SHK_AUTH_PREFIX,sharerId,key]]; +#else + [SFHFKeychainUtils storeUsername:key andPassword:value forServiceName:[NSString stringWithFormat:@"%@%@",SHK_AUTH_PREFIX,sharerId] updateExisting:YES error:nil]; +#endif +} + ++ (void)removeAuthValueForKey:(NSString *)key forSharer:(NSString *)sharerId +{ +#if TARGET_IPHONE_SIMULATOR + // Using NSUserDefaults for storage is very insecure, but because Keychain only exists on a device + // we use NSUserDefaults when running on the simulator to store objects. This allows you to still test + // in the simulator. You should NOT modify in a way that does not use keychain when actually deployed to a device. + [[NSUserDefaults standardUserDefaults] removeObjectForKey:[NSString stringWithFormat:@"%@%@%@",SHK_AUTH_PREFIX,sharerId,key]]; +#else + [SFHFKeychainUtils deleteItemForUsername:key andServiceName:[NSString stringWithFormat:@"%@%@",SHK_AUTH_PREFIX,sharerId] error:nil]; +#endif +} + ++ (void)logoutOfAll +{ + NSArray *sharers = [[SHK sharersDictionary] objectForKey:@"services"]; + for (NSString *sharerId in sharers) + [self logoutOfService:sharerId]; +} + ++ (void)logoutOfService:(NSString *)sharerId +{ + [NSClassFromString(sharerId) logout]; +} + + +#pragma mark - + +static NSDictionary *sharersDictionary = nil; + ++ (NSDictionary *)sharersDictionary +{ + if (sharersDictionary == nil) + sharersDictionary = [[NSDictionary dictionaryWithContentsOfFile:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"SHKSharers.plist"]] retain]; + + return sharersDictionary; +} + + +#pragma mark - +#pragma mark Offline Support + ++ (NSString *)offlineQueuePath +{ + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSArray *paths = NSSearchPathForDirectoriesInDomains( NSCachesDirectory, NSUserDomainMask, YES); + NSString *cache = [paths objectAtIndex:0]; + NSString *SHKPath = [cache stringByAppendingPathComponent:@"SHK"]; + + // Check if the path exists, otherwise create it + if (![fileManager fileExistsAtPath:SHKPath]) + [fileManager createDirectoryAtPath:SHKPath withIntermediateDirectories:YES attributes:nil error:nil]; + + return SHKPath; +} + ++ (NSString *)offlineQueueListPath +{ + return [[self offlineQueuePath] stringByAppendingPathComponent:@"SHKOfflineQueue.plist"]; +} + ++ (NSMutableArray *)getOfflineQueueList +{ + return [[[NSArray arrayWithContentsOfFile:[self offlineQueueListPath]] mutableCopy] autorelease]; +} + ++ (void)saveOfflineQueueList:(NSMutableArray *)queueList +{ + [queueList writeToFile:[self offlineQueueListPath] atomically:YES]; // TODO - should do this off of the main thread +} + ++ (BOOL)addToOfflineQueue:(SHKItem *)item forSharer:(NSString *)sharerId +{ + // Generate a unique id for the share to use when saving associated files + NSString *uid = [NSString stringWithFormat:@"%@-%i-%i-%i", sharerId, item.shareType, [[NSDate date] timeIntervalSince1970], arc4random()]; + + + // store image in cache + if (item.shareType == SHKShareTypeImage && item.image) + [UIImageJPEGRepresentation(item.image, 1) writeToFile:[[self offlineQueuePath] stringByAppendingPathComponent:uid] atomically:YES]; + + // store file in cache + else if (item.shareType == SHKShareTypeFile) + [item.data writeToFile:[[self offlineQueuePath] stringByAppendingPathComponent:uid] atomically:YES]; + + // Open queue list + NSMutableArray *queueList = [self getOfflineQueueList]; + if (queueList == nil) + queueList = [NSMutableArray arrayWithCapacity:0]; + + // Add to queue list + [queueList addObject:[NSDictionary dictionaryWithObjectsAndKeys: + [item dictionaryRepresentation],@"item", + sharerId,@"sharer", + uid,@"uid", + nil]]; + + [self saveOfflineQueueList:queueList]; + + return YES; +} + ++ (void)flushOfflineQueue +{ + // TODO - if an item fails, after all items are shared, it should present a summary view and allow them to see which items failed/succeeded + + // Check for a connection + if (![self connected]) + return; + + // Open list + NSMutableArray *queueList = [self getOfflineQueueList]; + + // Run through each item in the quietly in the background + // TODO - Is this the best behavior? Instead, should the user confirm sending these again? Maybe only if it has been X days since they were saved? + // - want to avoid a user being suprised by a post to Twitter if that happens long after they forgot they even shared it. + if (queueList != nil) + { + SHK *helper = [self currentHelper]; + + if (helper.offlineQueue == nil) + helper.offlineQueue = [[NSOperationQueue alloc] init]; + + SHKItem *item; + NSString *sharerId, *uid; + + for (NSDictionary *entry in queueList) + { + item = [SHKItem itemFromDictionary:[entry objectForKey:@"item"]]; + sharerId = [entry objectForKey:@"sharer"]; + uid = [entry objectForKey:@"uid"]; + + if (item != nil && sharerId != nil) + [helper.offlineQueue addOperation:[[[SHKOfflineSharer alloc] initWithItem:item forSharer:sharerId uid:uid] autorelease]]; + } + + // Remove offline queue - TODO: only do this if everything was successful? + [[NSFileManager defaultManager] removeItemAtPath:[self offlineQueueListPath] error:nil]; + + } +} + +#pragma mark - + ++ (NSError *)error:(NSString *)description, ... +{ + va_list args; + va_start(args, description); + NSString *string = [[[NSString alloc] initWithFormat:description arguments:args] autorelease]; + va_end(args); + + return [NSError errorWithDomain:@"sharekit" code:1 userInfo:[NSDictionary dictionaryWithObject:string forKey:NSLocalizedDescriptionKey]]; +} + +#pragma mark - +#pragma mark Network + ++ (BOOL)connected +{ + //return NO; // force for offline testing + Reachability *hostReach = [Reachability reachabilityForInternetConnection]; + NetworkStatus netStatus = [hostReach currentReachabilityStatus]; + return !(netStatus == NotReachable); +} + +@end + + +NSString * SHKStringOrBlank(NSString * value) +{ + return value == nil ? @"" : value; +} + +NSString * SHKEncode(NSString * value) +{ + if (value == nil) + return @""; + + NSString *string = value; + + string = [string stringByReplacingOccurrencesOfString:@"&" withString:@"&"]; + string = [string stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + string = [string stringByReplacingOccurrencesOfString:@"&" withString:@"%26"]; + + return string; +} + +NSString * SHKEncodeURL(NSURL * value) +{ + if (value == nil) + return @""; + + NSString *result = (NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, + (CFStringRef)value.absoluteString, + NULL, + CFSTR("!*'();:@&=+$,/?%#[]"), + kCFStringEncodingUTF8); + [result autorelease]; + return result; +} + +void SHKSwizzle(Class c, SEL orig, SEL newClassName) +{ + Method origMethod = class_getInstanceMethod(c, orig); + Method newMethod = class_getInstanceMethod(c, newClassName); + if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) + class_replaceMethod(c, newClassName, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); + else + method_exchangeImplementations(origMethod, newMethod); +} + +NSString* SHKLocalizedString(NSString* key, ...) +{ + // Localize the format + NSString *localizedStringFormat = NSLocalizedString(key, key); + + va_list args; + va_start(args, key); + NSString *string = [[[NSString alloc] initWithFormat:localizedStringFormat arguments:args] autorelease]; + va_end(args); + + return string; +} diff --git a/Classes/ShareKit/Core/SHKItem.h b/Classes/ShareKit/Core/SHKItem.h new file mode 100644 index 00000000..19064a41 --- /dev/null +++ b/Classes/ShareKit/Core/SHKItem.h @@ -0,0 +1,86 @@ +// +// SHKItem.h +// ShareKit +// +// Created by Nathan Weiner on 6/18/10. + +// +// 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 + +typedef enum +{ + SHKShareTypeUndefined, + SHKShareTypeURL, + SHKShareTypeText, + SHKShareTypeImage, + SHKShareTypeFile +} SHKShareType; + + +@interface SHKItem : NSObject +{ + SHKShareType shareType; + + NSURL *URL; + + UIImage *image; + + NSString *title; + NSString *text; + NSString *tags; + + NSData *data; + NSString *mimeType; + NSString *filename; + + @private + NSMutableDictionary *custom; +} + +@property (nonatomic) SHKShareType shareType; + +@property (nonatomic, retain) NSURL *URL; + +@property (nonatomic, retain) UIImage *image; + +@property (nonatomic, retain) NSString *title; +@property (nonatomic, retain) NSString *text; +@property (nonatomic, retain) NSString *tags; + +@property (nonatomic, retain) NSData *data; +@property (nonatomic, retain) NSString *mimeType; +@property (nonatomic, retain) NSString *filename; + ++ (SHKItem *)URL:(NSURL *)url title:(NSString *)title; ++ (SHKItem *)image:(UIImage *)image title:(NSString *)title; ++ (SHKItem *)text:(NSString *)text; ++ (SHKItem *)file:(NSData *)data filename:(NSString *)filename mimeType:(NSString *)mimeType title:(NSString *)title; + +- (void)setCustomValue:(NSString *)value forKey:(NSString *)key; +- (NSString *)customValueForKey:(NSString *)key; +- (BOOL)customBoolForSwitchKey:(NSString *)key; + +- (NSDictionary *)dictionaryRepresentation; ++ (SHKItem *)itemFromDictionary:(NSDictionary *)dictionary; + +@end diff --git a/Classes/ShareKit/Core/SHKItem.m b/Classes/ShareKit/Core/SHKItem.m new file mode 100644 index 00000000..76a44613 --- /dev/null +++ b/Classes/ShareKit/Core/SHKItem.m @@ -0,0 +1,185 @@ +// +// SHKItem.m +// ShareKit +// +// Created by Nathan Weiner on 6/18/10. + +// +// 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 "SHKItem.h" +#import "SHK.h" + + +@interface SHKItem() +@property (nonatomic, retain) NSMutableDictionary *custom; +@end + + +@implementation SHKItem + +@synthesize shareType; +@synthesize URL, image, title, text, tags, data, mimeType, filename; +@synthesize custom; + +- (void)dealloc +{ + [URL release]; + + [image release]; + + [title release]; + [text release]; + [tags release]; + + [data release]; + [mimeType release]; + [filename release]; + + [custom release]; + + [super dealloc]; +} + + ++ (SHKItem *)URL:(NSURL *)url +{ + return [self URL:url title:nil]; +} + ++ (SHKItem *)URL:(NSURL *)url title:(NSString *)title +{ + SHKItem *item = [[SHKItem alloc] init]; + item.shareType = SHKShareTypeURL; + item.URL = url; + item.title = title; + + return [item autorelease]; +} + ++ (SHKItem *)image:(UIImage *)image +{ + return [SHKItem image:image title:nil]; +} + ++ (SHKItem *)image:(UIImage *)image title:(NSString *)title +{ + SHKItem *item = [[SHKItem alloc] init]; + item.shareType = SHKShareTypeImage; + item.image = image; + item.title = title; + + return [item autorelease]; +} + ++ (SHKItem *)text:(NSString *)text +{ + SHKItem *item = [[SHKItem alloc] init]; + item.shareType = SHKShareTypeText; + item.text = text; + + return [item autorelease]; +} + ++ (SHKItem *)file:(NSData *)data filename:(NSString *)filename mimeType:(NSString *)mimeType title:(NSString *)title +{ + SHKItem *item = [[SHKItem alloc] init]; + item.shareType = SHKShareTypeFile; + item.data = data; + item.filename = filename; + item.mimeType = mimeType; + item.title = title; + + return [item autorelease]; +} + +#pragma mark - + +- (void)setCustomValue:(NSString *)value forKey:(NSString *)key +{ + if (custom == nil) + self.custom = [NSMutableDictionary dictionaryWithCapacity:0]; + + if (value == nil) + [custom removeObjectForKey:key]; + + else + [custom setObject:value forKey:key]; +} + +- (NSString *)customValueForKey:(NSString *)key +{ + return [custom objectForKey:key]; +} + +- (BOOL)customBoolForSwitchKey:(NSString *)key +{ + return [[custom objectForKey:key] isEqualToString:SHKFormFieldSwitchOn]; +} + + +#pragma mark - + ++ (SHKItem *)itemFromDictionary:(NSDictionary *)dictionary +{ + SHKItem *item = [[SHKItem alloc] init]; + item.shareType = [[dictionary objectForKey:@"shareType"] intValue]; + + if ([dictionary objectForKey:@"URL"] != nil) + item.URL = [NSURL URLWithString:[dictionary objectForKey:@"URL"]]; + + item.title = [dictionary objectForKey:@"title"]; + item.text = [dictionary objectForKey:@"text"]; + item.tags = [dictionary objectForKey:@"tags"]; + + if ([dictionary objectForKey:@"custom"] != nil) + item.custom = [[[dictionary objectForKey:@"custom"] mutableCopy] autorelease]; + + return [item autorelease]; +} + +- (NSDictionary *)dictionaryRepresentation +{ + NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:0]; + + [dictionary setObject:[NSNumber numberWithInt:shareType] forKey:@"shareType"]; + + if (custom != nil) + [dictionary setObject:custom forKey:@"custom"]; + + if (URL != nil) + [dictionary setObject:[URL.absoluteString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKey:@"URL"]; + + if (title != nil) + [dictionary setObject:title forKey:@"title"]; + + if (text != nil) + [dictionary setObject:text forKey:@"text"]; + + if (tags != nil) + [dictionary setObject:tags forKey:@"tags"]; + + // If you add anymore, make sure to add a method for retrieving them to the itemWithDictionary function too + + return dictionary; +} + +@end diff --git a/Classes/ShareKit/Core/SHKOfflineSharer.h b/Classes/ShareKit/Core/SHKOfflineSharer.h new file mode 100644 index 00000000..c2b74a14 --- /dev/null +++ b/Classes/ShareKit/Core/SHKOfflineSharer.h @@ -0,0 +1,56 @@ +// +// SHKOfflineSharer.h +// ShareKit +// +// Created by Nathan Weiner on 6/22/10. + +// +// 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 "SHK.h" +#import "SHKSharer.h" + +@interface SHKOfflineSharer : NSOperation +{ + SHKItem *item; + NSString *sharerId; + NSString *uid; + BOOL readyToFinish; + NSThread *runLoopThread; + SHKSharer *sharer; +} + +@property (nonatomic, retain) SHKItem *item; +@property (nonatomic, retain) NSString *sharerId; +@property (nonatomic, retain) NSString *uid; +@property BOOL readyToFinish; +@property (nonatomic, retain) NSThread *runLoopThread; +@property (nonatomic, retain) SHKSharer *sharer; + +- (id)initWithItem:(SHKItem *)i forSharer:(NSString *)s uid:(NSString *)u; + +- (void)share; +- (BOOL)shouldRun; +- (void)finish; +- (void)lastSpin; + +@end diff --git a/Classes/ShareKit/Core/SHKOfflineSharer.m b/Classes/ShareKit/Core/SHKOfflineSharer.m new file mode 100644 index 00000000..0c9ce9d8 --- /dev/null +++ b/Classes/ShareKit/Core/SHKOfflineSharer.m @@ -0,0 +1,156 @@ +// +// SHKOfflineSharer.m +// ShareKit +// +// Created by Nathan Weiner on 6/22/10. + +// +// 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 "SHKOfflineSharer.h" +#import "SHKSharer.h" + +@implementation SHKOfflineSharer + +@synthesize item, sharerId, uid; +@synthesize readyToFinish; +@synthesize runLoopThread; +@synthesize sharer; + +- (void)dealloc +{ + [item release]; + [sharerId release]; + [uid release]; + [runLoopThread release]; + [sharer release]; + [super dealloc]; +} + +- (id)initWithItem:(SHKItem *)i forSharer:(NSString *)s uid:(NSString *)u +{ + if (self = [super init]) + { + self.item = i; + self.sharerId = s; + self.uid = u; + } + return self; +} + +- (void)main +{ + // Make sure it hasn't been cancelled + if (![self shouldRun]) + return; + + // Save the thread so we can spin up the run loop later + self.runLoopThread = [NSThread currentThread]; + + // Run actual sharing on the main thread to avoid thread issues + [self performSelectorOnMainThread:@selector(share) withObject:nil waitUntilDone:YES]; + + // Keep the operation alive while we perform the send async + // This way only one will run at a time + while([self shouldRun]) + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; +} + +- (void)share +{ + // create sharer + self.sharer = [[NSClassFromString(sharerId) alloc] init]; + sharer.item = item; + sharer.quiet = YES; + sharer.shareDelegate = self; + + if (![sharer isAuthorized]) + { + [self finish]; + return; + } + + // reload image from disk and remove the file + NSString *path; + if (item.shareType == SHKShareTypeImage) + { + path = [[SHK offlineQueuePath] stringByAppendingPathComponent:uid]; + sharer.item.image = [UIImage imageWithContentsOfFile:path]; + [[NSFileManager defaultManager] removeItemAtPath:path error:nil]; + + } + + // reload file from disk and remove the file + else if (item.shareType == SHKShareTypeFile) + { + path = [[SHK offlineQueueListPath] stringByAppendingPathComponent:uid]; + sharer.item.data = [NSData dataWithContentsOfFile:[[SHK offlineQueuePath] stringByAppendingPathComponent:uid]]; + [[NSFileManager defaultManager] removeItemAtPath:path error:nil]; + + } + + [sharer tryToSend]; + [sharer release]; +} + +- (BOOL)shouldRun +{ + return ![self isCancelled] && ![self isFinished] && !readyToFinish; +} + +- (void)finish +{ + self.readyToFinish = YES; + [self performSelector:@selector(lastSpin) onThread:runLoopThread withObject:nil waitUntilDone:NO]; +} + +- (void)lastSpin +{ + // Just used to make the run loop spin +} + +#pragma mark - +#pragma mark SHKSharerDelegate + +- (void)sharerStartedSending:(SHKSharer *)aSharer +{ + +} + +- (void)sharerFinishedSending:(SHKSharer *)aSharer +{ + sharer.shareDelegate = nil; + [self finish]; +} + +- (void)sharer:(SHKSharer *)aSharer failedWithError:(NSError *)error shouldRelogin:(BOOL)shouldRelogin +{ + sharer.shareDelegate = nil; + [self finish]; +} + +- (void)sharerCancelledSending:(SHKSharer *)aSharer +{ + sharer.shareDelegate = nil; + [self finish]; +} + +@end diff --git a/Classes/ShareKit/Core/SHKSharers.plist b/Classes/ShareKit/Core/SHKSharers.plist new file mode 100644 index 00000000..8e2b86ff --- /dev/null +++ b/Classes/ShareKit/Core/SHKSharers.plist @@ -0,0 +1,25 @@ + + + + + actions + + SHKPhotoAlbum + SHKCopy + SHKMail + SHKSafari + + services + + SHKTwitter + SHKPinboard + SHKDelicious + SHKGoogleReader + SHKFacebook + SHKReadItLater + SHKInstapaper + SHKTumblr + SHKLicorize + + + diff --git a/Classes/ShareKit/Customize UI/SHKCustomFormController.h b/Classes/ShareKit/Customize UI/SHKCustomFormController.h new file mode 100644 index 00000000..f93e0280 --- /dev/null +++ b/Classes/ShareKit/Customize UI/SHKCustomFormController.h @@ -0,0 +1,35 @@ +// +// SHKCustomFormController.h +// ShareKit +// +// Created by Nathan Weiner on 6/28/10. + +// +// 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 "SHKFormController.h" + +@interface SHKCustomFormController : SHKFormController { + +} + +@end diff --git a/Classes/ShareKit/Customize UI/SHKCustomFormController.m b/Classes/ShareKit/Customize UI/SHKCustomFormController.m new file mode 100644 index 00000000..a83f0597 --- /dev/null +++ b/Classes/ShareKit/Customize UI/SHKCustomFormController.m @@ -0,0 +1,35 @@ +// +// SHKCustomFormController.m +// ShareKit +// +// Created by Nathan Weiner on 6/28/10. + +// +// 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 "SHKCustomFormController.h" + + +@implementation SHKCustomFormController + +// See http://getsharekit.com/customize/ for additional information on customizing + +@end diff --git a/Classes/ShareKit/Customize UI/SHKCustomFormFieldCell.h b/Classes/ShareKit/Customize UI/SHKCustomFormFieldCell.h new file mode 100644 index 00000000..37c34a97 --- /dev/null +++ b/Classes/ShareKit/Customize UI/SHKCustomFormFieldCell.h @@ -0,0 +1,36 @@ +// +// SHKCustomFormFieldCell.h +// ShareKit +// +// Created by Nathan Weiner on 6/28/10. + +// +// 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 "SHKFormFieldCell.h" + +@interface SHKCustomFormFieldCell : SHKFormFieldCell +{ + +} + +@end diff --git a/Classes/ShareKit/Customize UI/SHKCustomFormFieldCell.m b/Classes/ShareKit/Customize UI/SHKCustomFormFieldCell.m new file mode 100644 index 00000000..a72d76e3 --- /dev/null +++ b/Classes/ShareKit/Customize UI/SHKCustomFormFieldCell.m @@ -0,0 +1,35 @@ +// +// SHKCustomFormFieldCell.m +// ShareKit +// +// Created by Nathan Weiner on 6/28/10. + +// +// 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 "SHKCustomFormFieldCell.h" + + +@implementation SHKCustomFormFieldCell + +// See http://getsharekit.com/customize/ for additional information on customizing + +@end diff --git a/Classes/ShareKit/Customize UI/SHKCustomShareMenu.h b/Classes/ShareKit/Customize UI/SHKCustomShareMenu.h new file mode 100644 index 00000000..6328077f --- /dev/null +++ b/Classes/ShareKit/Customize UI/SHKCustomShareMenu.h @@ -0,0 +1,40 @@ +// +// SHKCustomShareMenu.h +// RIL +// +// Created by Nathan Weiner on 6/30/10. + +// +// 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 "SHKShareMenu.h" + + + +@interface SHKCustomShareMenu : SHKShareMenu +{ + // See http://getsharekit.com/customize/ for additional information on customizing +} + + + +@end diff --git a/Classes/ShareKit/Customize UI/SHKCustomShareMenu.m b/Classes/ShareKit/Customize UI/SHKCustomShareMenu.m new file mode 100644 index 00000000..aa6edec0 --- /dev/null +++ b/Classes/ShareKit/Customize UI/SHKCustomShareMenu.m @@ -0,0 +1,36 @@ +// +// SHKCustomShareMenu.m +// RIL +// +// Created by Nathan Weiner on 6/30/10. + +// +// 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 "SHKCustomShareMenu.h" + +@implementation SHKCustomShareMenu + + +// See http://getsharekit.com/customize/ for additional information on customizing + + +@end diff --git a/Classes/ShareKit/Customize UI/SHKCustomShareMenuCell.h b/Classes/ShareKit/Customize UI/SHKCustomShareMenuCell.h new file mode 100644 index 00000000..5b226131 --- /dev/null +++ b/Classes/ShareKit/Customize UI/SHKCustomShareMenuCell.h @@ -0,0 +1,45 @@ +// +// SHKTableViewCell.h +// RIL +// +// Created by Nathan Weiner on 6/30/10. + +// +// 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 + + +/* + Two ways to customize: + + 1. If you already have a custom UITableViewCell subclass you are using in your app, + simply replace UITableViewCell with your own class. + + 2. Add any customizations by subclassing this (SHKTableViewCell) +*/ + +@interface SHKCustomShareMenuCell : UITableViewCell +{ + // See http://getsharekit.com/customize/ for additional information on customizing +} + +@end diff --git a/Classes/ShareKit/Customize UI/SHKCustomShareMenuCell.m b/Classes/ShareKit/Customize UI/SHKCustomShareMenuCell.m new file mode 100644 index 00000000..cd31fba3 --- /dev/null +++ b/Classes/ShareKit/Customize UI/SHKCustomShareMenuCell.m @@ -0,0 +1,35 @@ +// +// SHKTableViewCell.m +// RIL +// +// Created by Nathan Weiner on 6/30/10. + +// +// 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 "SHKCustomShareMenuCell.h" + + +@implementation SHKCustomShareMenuCell + +// See http://getsharekit.com/customize/ for additional information on customizing + +@end diff --git a/Classes/ShareKit/Localization/de.lproj/Localizable.strings b/Classes/ShareKit/Localization/de.lproj/Localizable.strings new file mode 100644 index 00000000..bb36dab7 --- /dev/null +++ b/Classes/ShareKit/Localization/de.lproj/Localizable.strings @@ -0,0 +1,96 @@ +"Email" = "Email"; +"Username" = "Username"; +"Password" = "Passwort"; +"Follow %@" = "Folge %@"; +"Continue" = "Weiter"; +"Share" = "Teile"; +"More..." = "Mehr..."; +"Cancel" = "Abbrechen"; +"Slug" = "Slug"; +"Private" = "Privat"; +"Public" = "Öffentlich"; +"Caption" = "Kommentar"; +"Title" = "Titel"; +"Tags" = "Tags"; +"Close" = "Schließen"; +"Notes" = "Notizen"; +"Note" = "Notiz"; +"Share" = "Teilen"; +"Shared" = "Geteilt"; +"Edit" = "Editieren"; +"Actions" = "Aktionen"; +"Services" = "Dienste"; +"Send to %@" = "Zu %@ senden"; +"Copy" = "Kopieren"; +"Copied!" = "Kopiert!"; +"Saving to %@" = "Nach %@ speichern"; +"Saved!" = "Gespeichert!"; +"Offline" = "Offline"; +"Error" = "Fehler"; +"Login" = "Login"; +"Logging In..." = "Einloggen..."; +"Login Error" = "Fehler beim Login"; +"Connecting..." = "Verbinde..."; + +"Shortening URL..." = "Verkleinere URL..."; +"Shorten URL Error" = "Fehler beim Verkleinern der URL"; +"We could not shorten the URL." = "URL konnte nicht verkleinert werden."; + +"Create a free account at %@" = "Registrieren Sie einen kostenlosen Account bei %@"; +"Create an account at %@" = "Erstelle einen Account auf %@"; + +"Send to Twitter" = "An Twitter senden"; + +"Message is too long" = "Nachricht zu lang"; +"Twitter posts can only be 140 characters in length." = "Twitter Nachrichten können maximal 140 Zeichen lang sein."; +"Message is empty" = "Nachricht ist leer"; +"You must enter a message in order to post." = "Sie müssen eine Nachricht eingeben um diese absenden zu können"; + +"Enter your message:" = "Nachricht eingeben:"; + +"Invalid email or password." = "Falsche Email oder Passwort."; +"The service encountered an error. Please try again later." = "Der Dienst konnte nicht abgeschlossen werden. Bitte versuchen Sie es später erneut."; +"There was a sending your post to Tumblr." = "Beim Posten auf Tumblr trat ein Fehler auf."; + +"There was an error saving to Pinboard" = "Beim Speichern zu Pinboard trat ein Fehler auf."; + +"Sorry, Instapaper did not accept your credentials. Please try again." = "Instapaper akzeptierte Ihren Login nicht. Bitte versuchen Sie es erneut."; +"Sorry, Instapaper encountered an error. Please try again." = "Instapaper schlug fehl. Bitte versuchen Sie es erneut."; +"There was a problem saving to Instapaper." = "Beim Speichern auf Instapaper trat ein Problem auf."; + +"Incorrect username and password" = "Falscher Username und Passwort"; +"There was an error logging into Google Reader" = "Beim Einloggen zu Google Reader trat ein Fehler auf"; +"There was a problem authenticating your account." = "Beim Authentifizieren Ihres Accounts trat ein Fehler auf."; +"There was a problem saving your note." = "Beim Speichern Ihrer Notiz trat ein Problem auf."; + +"There was a problem saving to Delicious." = "Beim Speichern auf Delicious trat ein Problem auf."; + +"Open in Safari" = "In Safari öffnen"; +"Attached: %@" = "Angehängt: %@"; + +"You must be online to login to %@" = "Sie müssen online sein um sich bei %@ einzuloggen."; + +"Auto Share" = "Auto Share"; +"Enable auto share to skip this step in the future." = "Aktivieren Sie Auto Share um diesen Schritt zu überspringen."; + +"You must be online in order to share with %@" = "Um mit %@ zu teilen müssen Sie online sein."; +"There was an error while sharing" = "Beim Teilen trat ein Fehler auf."; + +"Could not authenticate you. Please relogin." = "Konnte Sie nicht authentifizieren. Bitte neu einloggen."; +"There was a problem requesting authorization from %@" = "Beim Abfragen der Authorisation für %@ trat ein Fehler auf."; +"Request Error" = "Anfragefehler"; +"There was an error while sharing" = "Beim Teilen trat ein Fehler auf."; + +"Authorize Error" = "Authorisierungsfehler"; +"There was an error while authorizing" = "Beim Authorisieren trat ein Fehler auf."; + +"Authenticating..." = "Authentifizieren..."; +"There was a problem requesting access from %@" = "Beim Nachfragen zum Zugang zu %@ trat ein Fehler auf."; + +"Access Error" = "Zugriffsfehler"; + +"Sharing a Link" = "Teile Link"; +"Sharing an Image" = "Teile Bild"; +"Sharing Text" = "Teile Text"; +"Sharing a File" = "Teile Datei"; +"Logout of All Services" = "Aus allen Services ausloggen"; \ No newline at end of file diff --git a/Classes/ShareKit/Localization/en.lproj/Localizable.strings b/Classes/ShareKit/Localization/en.lproj/Localizable.strings new file mode 100644 index 00000000..cf945769 --- /dev/null +++ b/Classes/ShareKit/Localization/en.lproj/Localizable.strings @@ -0,0 +1,91 @@ +"Email" = "Email"; +"Username" = "Username"; +"Password" = "Password"; +"Follow %@" = "Follow %@"; +"Continue" = "Continue"; +"Share" = "Share"; +"More..." = "More..."; +"Cancel" = "Cancel"; +"Slug" = "Slug"; +"Private" = "Private"; +"Public" = "Public"; +"Caption" = "Caption"; +"Title" = "Title"; +"Tags" = "Tags"; +"Close" = "Close"; +"Notes" = "Notes"; +"Note" = "Note"; +"Share" = "Share"; +"Shared" = "Shared"; +"Edit" = "Edit"; +"Actions" = "Actions"; +"Services" = "Services"; +"Send to %@" = "Send to %@"; +"Copy" = "Copy"; +"Copied!" = "Copied!"; +"Saving to %@" = "Saving to %@"; +"Saved!" = "Saved!"; +"Offline" = "Offline"; +"Error" = "Error"; +"Login" = "Login"; +"Logging In..." = "Logging In..."; +"Login Error" = "Login Error"; +"Connecting..." = "Connecting..."; + +"Shortening URL..." = "Shortening URL..."; +"Shorten URL Error" = "Shorten URL Error"; +"We could not shorten the URL." = "We could not shorten the URL."; + +"Create a free account at %@" = "Create a free account at %@"; +"Create an account at %@" = "Create an account at %@"; + +"Send to Twitter" = "Send to Twitter"; + +"Message is too long" = "Message is too long"; +"Twitter posts can only be 140 characters in length." = "Twitter posts can only be 140 characters in length."; +"Message is empty" = "Message is empty"; +"You must enter a message in order to post." = "You must enter a message in order to post."; + +"Enter your message:" = "Enter your message:"; + +"Invalid email or password." = "Invalid email or password."; +"The service encountered an error. Please try again later." = "The service encountered an error. Please try again later."; +"There was a sending your post to Tumblr." = "There was a sending your post to Tumblr."; + +"There was an error saving to Pinboard" = "There was an error saving to Pinboard"; + +"Sorry, Instapaper did not accept your credentials. Please try again." = "Sorry, Instapaper did not accept your credentials. Please try again."; +"Sorry, Instapaper encountered an error. Please try again." = "Sorry, Instapaper encountered an error. Please try again."; +"There was a problem saving to Instapaper." = "There was a problem saving to Instapaper."; + +"Incorrect username and password" = "Incorrect username and password"; +"There was an error logging into Google Reader" = "There was an error logging into Google Reader"; +"There was a problem authenticating your account." = "There was a problem authenticating your account."; +"There was a problem saving your note." = "There was a problem saving your note."; + +"There was a problem saving to Delicious." = "There was a problem saving to Delicious."; + +"Open in Safari" = "Open in Safari"; +"Attached: %@" = "Attached: %@"; + +"You must be online to login to %@" = "You must be online to login to %@"; + +"Auto Share" = "Auto Share"; +"Enable auto share to skip this step in the future." = "Enable auto share to skip this step in the future."; + +"You must be online in order to share with %@" = "You must be online in order to share with %@"; +"There was an error while sharing" = "There was an error while sharing"; + +"Could not authenticate you. Please relogin." = "Could not authenticate you. Please relogin."; +"There was a problem requesting authorization from %@" = "There was a problem requesting authorization from %@"; +"Request Error" = "Request Error"; +"There was an error while sharing" = "There was an error while sharing"; + +"Authorize Error" = "Authorize Error"; +"There was an error while authorizing" = "There was an error while authorizing"; + +"Authenticating..." = "Authenticating..."; +"There was a problem requesting access from %@" = "There was a problem requesting access from %@"; + +"Access Error" = "Access Error"; +"There was an error while sharing" = "There was an error while sharing"; \ No newline at end of file diff --git a/Classes/ShareKit/Reachability/Reachability.h b/Classes/ShareKit/Reachability/Reachability.h new file mode 100644 index 00000000..1f72d45a --- /dev/null +++ b/Classes/ShareKit/Reachability/Reachability.h @@ -0,0 +1,88 @@ +/* + + File: Reachability.h + Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs. + + Version: 2.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under + Apple's copyrights in this original Apple software (the "Apple Software"), to + use, reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions + of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may be used + to endorse or promote products derived from the Apple Software without specific + prior written permission from Apple. Except as expressly stated in this notice, + no other rights or licenses, express or implied, are granted by Apple herein, + including but not limited to any patent rights that may be infringed by your + derivative works or by other works in which the Apple Software may be + incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2010 Apple Inc. All Rights Reserved. + +*/ + + +#import +#import + +typedef enum { + NotReachable = 0, + ReachableViaWiFi, + ReachableViaWWAN +} NetworkStatus; +#define kReachabilityChangedNotification @"kNetworkReachabilityChangedNotification" + +@interface Reachability: NSObject +{ + BOOL localWiFiRef; + SCNetworkReachabilityRef reachabilityRef; +} + +//reachabilityWithHostName- Use to check the reachability of a particular host name. ++ (Reachability*) reachabilityWithHostName: (NSString*) hostName; + +//reachabilityWithAddress- Use to check the reachability of a particular IP address. ++ (Reachability*) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress; + +//reachabilityForInternetConnection- checks whether the default route is available. +// Should be used by applications that do not connect to a particular host ++ (Reachability*) reachabilityForInternetConnection; + +//reachabilityForLocalWiFi- checks whether a local wifi connection is available. ++ (Reachability*) reachabilityForLocalWiFi; + +//Start listening for reachability notifications on the current run loop +- (BOOL) startNotifier; +- (void) stopNotifier; + +- (NetworkStatus) currentReachabilityStatus; +//WWAN may be available, but not active until a connection has been established. +//WiFi may require a connection for VPN on Demand. +- (BOOL) connectionRequired; +@end + + diff --git a/Classes/ShareKit/Reachability/Reachability.m b/Classes/ShareKit/Reachability/Reachability.m new file mode 100644 index 00000000..a7f69abd --- /dev/null +++ b/Classes/ShareKit/Reachability/Reachability.m @@ -0,0 +1,273 @@ +/* + + File: Reachability.m + Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs. + + Version: 2.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under + Apple's copyrights in this original Apple software (the "Apple Software"), to + use, reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions + of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may be used + to endorse or promote products derived from the Apple Software without specific + prior written permission from Apple. Except as expressly stated in this notice, + no other rights or licenses, express or implied, are granted by Apple herein, + including but not limited to any patent rights that may be infringed by your + derivative works or by other works in which the Apple Software may be + incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2010 Apple Inc. All Rights Reserved. + +*/ + +#import +#import +#import +#import +#import +#import + +#import + +#import "Reachability.h" + +#define kShouldPrintReachabilityFlags 0 + +static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment) +{ +#if kShouldPrintReachabilityFlags + + NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n", + (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-', + (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-', + + (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-', + (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-', + (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-', + (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-', + comment + ); +#endif +} + + +@implementation Reachability +static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) +{ + #pragma unused (target, flags) + NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback"); + NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback"); + + //We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively + // in case someon uses the Reachablity object in a different thread. + NSAutoreleasePool* myPool = [[NSAutoreleasePool alloc] init]; + + Reachability* noteObject = (Reachability*) info; + // Post a notification to notify the client that the network reachability changed. + [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject]; + + [myPool release]; +} + +- (BOOL) startNotifier +{ + BOOL retVal = NO; + SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL}; + if(SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context)) + { + if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) + { + retVal = YES; + } + } + return retVal; +} + +- (void) stopNotifier +{ + if(reachabilityRef!= NULL) + { + SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + } +} + +- (void) dealloc +{ + [self stopNotifier]; + if(reachabilityRef!= NULL) + { + CFRelease(reachabilityRef); + } + [super dealloc]; +} + ++ (Reachability*) reachabilityWithHostName: (NSString*) hostName; +{ + Reachability* retVal = NULL; + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]); + if(reachability!= NULL) + { + retVal= [[[self alloc] init] autorelease]; + if(retVal!= NULL) + { + retVal->reachabilityRef = reachability; + retVal->localWiFiRef = NO; + } + } + return retVal; +} + ++ (Reachability*) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress; +{ + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress); + Reachability* retVal = NULL; + if(reachability!= NULL) + { + retVal= [[[self alloc] init] autorelease]; + if(retVal!= NULL) + { + retVal->reachabilityRef = reachability; + retVal->localWiFiRef = NO; + } + } + return retVal; +} + ++ (Reachability*) reachabilityForInternetConnection; +{ + struct sockaddr_in zeroAddress; + bzero(&zeroAddress, sizeof(zeroAddress)); + zeroAddress.sin_len = sizeof(zeroAddress); + zeroAddress.sin_family = AF_INET; + return [self reachabilityWithAddress: &zeroAddress]; +} + ++ (Reachability*) reachabilityForLocalWiFi; +{ + struct sockaddr_in localWifiAddress; + bzero(&localWifiAddress, sizeof(localWifiAddress)); + localWifiAddress.sin_len = sizeof(localWifiAddress); + localWifiAddress.sin_family = AF_INET; + // IN_LINKLOCALNETNUM is defined in as 169.254.0.0 + localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); + Reachability* retVal = [self reachabilityWithAddress: &localWifiAddress]; + if(retVal!= NULL) + { + retVal->localWiFiRef = YES; + } + return retVal; +} + +#pragma mark Network Flag Handling + +- (NetworkStatus) localWiFiStatusForFlags: (SCNetworkReachabilityFlags) flags +{ + PrintReachabilityFlags(flags, "localWiFiStatusForFlags"); + + BOOL retVal = NotReachable; + if((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect)) + { + retVal = ReachableViaWiFi; + } + return retVal; +} + +- (NetworkStatus) networkStatusForFlags: (SCNetworkReachabilityFlags) flags +{ + PrintReachabilityFlags(flags, "networkStatusForFlags"); + if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) + { + // if target host is not reachable + return NotReachable; + } + + BOOL retVal = NotReachable; + + if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) + { + // if target host is reachable and no connection is required + // then we'll assume (for now) that your on Wi-Fi + retVal = ReachableViaWiFi; + } + + + if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || + (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) + { + // ... and the connection is on-demand (or on-traffic) if the + // calling application is using the CFSocketStream or higher APIs + + if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) + { + // ... and no [user] intervention is needed + retVal = ReachableViaWiFi; + } + } + + if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) + { + // ... but WWAN connections are OK if the calling application + // is using the CFNetwork (CFSocketStream?) APIs. + retVal = ReachableViaWWAN; + } + return retVal; +} + +- (BOOL) connectionRequired; +{ + NSAssert(reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef"); + SCNetworkReachabilityFlags flags; + if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) + { + return (flags & kSCNetworkReachabilityFlagsConnectionRequired); + } + return NO; +} + +- (NetworkStatus) currentReachabilityStatus +{ + NSAssert(reachabilityRef != NULL, @"currentNetworkStatus called with NULL reachabilityRef"); + NetworkStatus retVal = NotReachable; + SCNetworkReachabilityFlags flags; + if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) + { + if(localWiFiRef) + { + retVal = [self localWiFiStatusForFlags: flags]; + } + else + { + retVal = [self networkStatusForFlags: flags]; + } + } + return retVal; +} +@end diff --git a/Classes/ShareKit/SHKConfig.h b/Classes/ShareKit/SHKConfig.h new file mode 100644 index 00000000..c373684f --- /dev/null +++ b/Classes/ShareKit/SHKConfig.h @@ -0,0 +1,174 @@ + + + + + +// PLEASE SEE INSTALL/CONFIG INSTRUCTIONS: +// http://getsharekit.com/install + + + + + + +// App Description +// These values are used by any service that shows 'shared from XYZ' + +#define SHKMyAppName @"My App Name" +#define SHKMyAppURL @"http://example.com" + + + +/* + API Keys + -------- + This is the longest step to getting set up, it involves filling in API keys for the supported services. + It should be pretty painless though and should hopefully take no more than a few minutes. + + Each key below as a link to a page where you can generate an api key. Fill in the key for each service below. + + A note on services you don't need: + If, for example, your app only shares URLs then you probably won't need image services like Flickr. + In these cases it is safe to leave an API key blank. + + However, it is STRONGLY recommended that you do your best to support all services for the types of sharing you support. + The core principle behind ShareKit is to leave the service choices up to the user. Thus, you should not remove any services, + leaving that decision up to the user. + */ + + + +// Delicious - https://developer.apps.yahoo.com/projects +#define SHKDeliciousConsumerKey @"" +#define SHKDeliciousSecretKey @"" + +// Facebook - http://www.facebook.com/developers +// If SHKFacebookUseSessionProxy is enabled then SHKFacebookSecret is ignored and should be left blank + +#define SHKFacebookUseSessionProxy NO +#define SHKFacebookKey @"" +#define SHKFacebookSecret @"" +#define SHKFacebookSessionProxyURL @"" + +// Read It Later - http://readitlaterlist.com/api/?shk +#define SHKReadItLaterKey @"" + +// Twitter - http://dev.twitter.com/apps/new +/* + Important Twitter settings to get right: + + Differences between OAuth and xAuth + -- + There are two types of authentication provided for Twitter, OAuth and xAuth. OAuth is the default and will + present a web view to log the user in. xAuth presents a native entry form but requires Twitter to add xAuth to your app (you have to request it from them). + If your app has been approved for xAuth, set SHKTwitterUseXAuth to 1. + + Callback URL (important to get right for OAuth users) + -- + 1. Open your application settings at http://dev.twitter.com/apps/ + 2. 'Application Type' should be set to BROWSER (not client) + 3. 'Callback URL' should match whatever you enter in SHKTwitterCallbackUrl. The callback url doesn't have to be an actual existing url. The user will never get to it because ShareKit intercepts it before the user is redirected. It just needs to match. + */ +#define SHKTwitterConsumerKey @"" +#define SHKTwitterSecret @"" +#define SHKTwitterCallbackUrl @"" // You need to set this if using OAuth, see note above (xAuth users can skip it) +#define SHKTwitterUseXAuth 0 // To use xAuth, set to 1 +#define SHKTwitterUsername @"" // Enter your app's twitter account if you'd like to ask the user to follow it when logging in. (Only for xAuth) + +// Bit.ly (for shortening URLs on Twitter) - http://bit.ly/account/register - after signup: http://bit.ly/a/your_api_key +#define SHKBitLyLogin @"" +#define SHKBitLyKey @"" + +// ShareMenu Ordering +#define SHKShareMenuAlphabeticalOrder 1 // Setting this to 1 will show list in Alphabetical Order, setting to 0 will follow the order in SHKShares.plist + +// Append 'Shared With 'Signature to Email (and related forms) +#define SHKSharedWithSignature 0 + +// Licorize - http://licorize.com +/* + Important Licorize settings to get right: + + Differences between OAuth and xAuth + -- + There are two types of authentication provided for Licorize clients, OAuth and xAuth, based on open standards. + OAuth is the default and will present a web page to log the user in. + xAuth is a simplified version of OAuth: xAuth presents a native entry form but you have to email the Licorize API team to enable xAuth for your app. + If your app has been approved for xAuth, set SHKLicorizeUseXAuth to 1. + + Callback URL (it's important to get it right for OAuth users) + -- + 1. Fill the form to get your application keys at http://licorize.com/api/ + 2. 'Application Type' should be set to BROWSER (not client) + 3. 'Callback URL' should match whatever you enter in SHKLicorizeCallbackUrl. The callback url doesn't have to be an actual existing url. The user will never get to it because ShareKit intercepts it before the user is redirected. It just needs to match. + */ +#define SHKLicorizeConsumerKey @"" +#define SHKLicorizeSecret @"" +#define SHKLicorizeCallbackUrl @"" // You need to set this if using OAuth, see note above (xAuth users can skip it) +#define SHKLicorizeUseXAuth 0 // To use xAuth, set to 1 + +/* + UI Configuration : Basic + ------ + These provide controls for basic UI settings. For more advanced configuration see below. + */ + +// Toolbars +#define SHKBarStyle @"UIBarStyleDefault" // See: http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIKitDataTypesReference/Reference/reference.html#//apple_ref/c/econst/UIBarStyleDefault +#define SHKBarTintColorRed -1 // Value between 0-255, set all to -1 for default +#define SHKBarTintColorGreen -1 // Value between 0-255, set all to -1 for default +#define SHKBarTintColorBlue -1 // Value between 0-255, set all to -1 for default + +// Forms +#define SHKFormFontColorRed -1 // Value between 0-255, set all to -1 for default +#define SHKFormFontColorGreen -1 // Value between 0-255, set all to -1 for default +#define SHKFormFontColorBlue -1 // Value between 0-255, set all to -1 for default + +#define SHKFormBgColorRed -1 // Value between 0-255, set all to -1 for default +#define SHKFormBgColorGreen -1 // Value between 0-255, set all to -1 for default +#define SHKFormBgColorBlue -1 // Value between 0-255, set all to -1 for default + +// iPad views +#define SHKModalPresentationStyle @"UIModalPresentationFormSheet" // See: http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instp/UIViewController/modalPresentationStyle +#define SHKModalTransitionStyle @"UIModalTransitionStyleCoverVertical" // See: http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instp/UIViewController/modalTransitionStyle + +/* + UI Configuration : Advanced + ------ + If you'd like to do more advanced customization of the ShareKit UI, like background images and more, + check out http://getsharekit.com/customize + */ + + + +/* + Debugging + ------ + To show debug output in the console: + 1. uncomment section A below + 2. comment out section B below + + To hide debug output in the console: + 1. uncomment section B below + 2. comment out section A below + */ + +// A : show debug output +//#define SHKDebugShowLogs 1 +//#define SHKLog( s, ... ) NSLog( @"<%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] ) + +// B : hide debug output +#define SHKDebugShowLogs 0 +#define SHKLog( s, ... ) + + + +/* + Advanced Configuration + ------ + These settings can be left as is. This only need to be changed for uber custom installs. + */ + +#define SHK_MAX_FAV_COUNT 3 +#define SHK_FAVS_PREFIX_KEY @"SHK_FAVS_" +#define SHK_AUTH_PREFIX @"SHK_AUTH_" \ No newline at end of file diff --git a/Classes/ShareKit/Sharers/Actions/Copy/SHKCopy.h b/Classes/ShareKit/Sharers/Actions/Copy/SHKCopy.h new file mode 100644 index 00000000..857f5810 --- /dev/null +++ b/Classes/ShareKit/Sharers/Actions/Copy/SHKCopy.h @@ -0,0 +1,36 @@ +// +// SHKCopy.h +// ShareKit +// +// Created by Nathan Weiner on 6/20/10. + +// +// 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 "SHKSharer.h" + +@interface SHKCopy : SHKSharer +{ + +} + +@end diff --git a/Classes/ShareKit/Sharers/Actions/Copy/SHKCopy.m b/Classes/ShareKit/Sharers/Actions/Copy/SHKCopy.m new file mode 100644 index 00000000..910dc04c --- /dev/null +++ b/Classes/ShareKit/Sharers/Actions/Copy/SHKCopy.m @@ -0,0 +1,92 @@ +// +// SHKCopy.m +// ShareKit +// +// Created by Nathan Weiner on 6/20/10. + +// +// 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 "SHKCopy.h" + + +@implementation SHKCopy + +#pragma mark - +#pragma mark Configuration : Service Defination + ++ (NSString *)sharerTitle +{ + return SHKLocalizedString(@"Copy"); +} + ++ (BOOL)canShareURL +{ + return YES; +} + ++ (BOOL)canShareImage +{ + return YES; +} + ++ (BOOL)shareRequiresInternetConnection +{ + return NO; +} + ++ (BOOL)requiresAuthentication +{ + return NO; +} + + +#pragma mark - +#pragma mark Configuration : Dynamic Enable + +- (BOOL)shouldAutoShare +{ + return YES; +} + + +#pragma mark - +#pragma mark Share API Methods + +- (BOOL)send +{ + if (item.shareType == SHKShareTypeURL) + [[UIPasteboard generalPasteboard] setString:item.URL.absoluteString]; + + else + [[UIPasteboard generalPasteboard] setImage:item.image]; + + // Notify user + [[SHKActivityIndicator currentIndicator] displayCompleted:SHKLocalizedString(@"Copied!")]; + + // Notify delegate, but quietly + self.quiet = YES; + [self sendDidFinish]; + + return YES; +} + +@end diff --git a/Classes/ShareKit/Sharers/Actions/Email/SHKMail.h b/Classes/ShareKit/Sharers/Actions/Email/SHKMail.h new file mode 100644 index 00000000..510e158e --- /dev/null +++ b/Classes/ShareKit/Sharers/Actions/Email/SHKMail.h @@ -0,0 +1,39 @@ +// +// SHKMail.h +// ShareKit +// +// Created by Nathan Weiner on 6/17/10. + +// +// 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 "SHKSharer.h" +#import + +@interface SHKMail : SHKSharer +{ + +} + +- (BOOL)sendMail; + +@end diff --git a/Classes/ShareKit/Sharers/Actions/Email/SHKMail.m b/Classes/ShareKit/Sharers/Actions/Email/SHKMail.m new file mode 100644 index 00000000..c94511c6 --- /dev/null +++ b/Classes/ShareKit/Sharers/Actions/Email/SHKMail.m @@ -0,0 +1,198 @@ +// +// SHKMail.m +// ShareKit +// +// Created by Nathan Weiner on 6/17/10. + +// +// 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 "SHKMail.h" + + +@implementation MFMailComposeViewController (SHK) + +- (void)SHKviewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; + + // Remove the SHK view wrapper from the window + [[SHK currentHelper] viewWasDismissed]; +} + +@end + + + +@implementation SHKMail + +#pragma mark - +#pragma mark Configuration : Service Defination + ++ (NSString *)sharerTitle +{ + return @"Email"; +} + ++ (BOOL)canShareText +{ + return YES; +} + ++ (BOOL)canShareURL +{ + return YES; +} + ++ (BOOL)canShareImage +{ + return YES; +} + ++ (BOOL)canShareFile +{ + return YES; +} + ++ (BOOL)shareRequiresInternetConnection +{ + return NO; +} + ++ (BOOL)requiresAuthentication +{ + return NO; +} + + +#pragma mark - +#pragma mark Configuration : Dynamic Enable + ++ (BOOL)canShare +{ + return [MFMailComposeViewController canSendMail]; +} + +- (BOOL)shouldAutoShare +{ + return YES; +} + + + +#pragma mark - +#pragma mark Share API Methods + +- (BOOL)send +{ + self.quiet = YES; + + if (![self validateItem]) + return NO; + + return [self sendMail]; // Put the actual sending action in another method to make subclassing SHKMail easier +} + +- (BOOL)sendMail +{ + MFMailComposeViewController *mailController = [[[MFMailComposeViewController alloc] init] autorelease]; + mailController.mailComposeDelegate = self; + + NSString *body = [item customValueForKey:@"body"]; + + if (body == nil) + { + if (item.text != nil) + body = item.text; + + if (item.URL != nil) + { + NSString *urlStr = [item.URL.absoluteString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + + if (body != nil) + body = [body stringByAppendingFormat:@"

%@", urlStr]; + + else + body = urlStr; + } + + if (item.data) + { + NSString *attachedStr = SHKLocalizedString(@"Attached: %@", item.title ? item.title : item.filename); + + if (body != nil) + body = [body stringByAppendingFormat:@"

%@", attachedStr]; + + else + body = attachedStr; + + [mailController addAttachmentData:item.data mimeType:item.mimeType fileName:item.filename]; + } + + if (item.image) + [mailController addAttachmentData:UIImageJPEGRepresentation(item.image, 1) mimeType:@"image/jpeg" fileName:@"Image.jpg"]; + + // fallback + if (body == nil) + body = @""; + + // sig + if (SHKSharedWithSignature) + { + body = [body stringByAppendingString:@"

"]; + body = [body stringByAppendingString:SHKLocalizedString(@"Sent from %@", SHKMyAppName)]; + } + + // save changes to body + [item setCustomValue:body forKey:@"body"]; + } + + [mailController setSubject:item.title]; + [mailController setMessageBody:body isHTML:YES]; + + [[SHK currentHelper] showViewController:mailController]; + + return YES; +} + +- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error +{ + [[SHK currentHelper] hideCurrentViewControllerAnimated:YES]; + + switch (result) + { + case MFMailComposeResultSent: + [self sendDidFinish]; + break; + case MFMailComposeResultSaved: + [self sendDidFinish]; + break; + case MFMailComposeResultCancelled: + [self sendDidCancel]; + break; + case MFMailComposeResultFailed: + [self sendDidFailWithError:nil]; + break; + } +} + + +@end diff --git a/Classes/ShareKit/Sharers/Actions/Open in Safari/SHKSafari.h b/Classes/ShareKit/Sharers/Actions/Open in Safari/SHKSafari.h new file mode 100644 index 00000000..96cd16fd --- /dev/null +++ b/Classes/ShareKit/Sharers/Actions/Open in Safari/SHKSafari.h @@ -0,0 +1,36 @@ +// +// SHKSafari.h +// ShareKit +// +// Created by Nathan Weiner on 6/20/10. + +// +// 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 "SHKSharer.h" + +@interface SHKSafari : SHKSharer +{ + +} + +@end diff --git a/Classes/ShareKit/Sharers/Actions/Open in Safari/SHKSafari.m b/Classes/ShareKit/Sharers/Actions/Open in Safari/SHKSafari.m new file mode 100644 index 00000000..489520f6 --- /dev/null +++ b/Classes/ShareKit/Sharers/Actions/Open in Safari/SHKSafari.m @@ -0,0 +1,83 @@ +// +// SHKSafari.m +// ShareKit +// +// Created by Nathan Weiner on 6/20/10. + +// +// 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 "SHKSafari.h" + + +@implementation SHKSafari + + + +#pragma mark - +#pragma mark Configuration : Service Defination + ++ (NSString *)sharerTitle +{ + return SHKLocalizedString(@"Open in Safari"); +} + ++ (BOOL)canShareURL +{ + return YES; +} + ++ (BOOL)shareRequiresInternetConnection +{ + return NO; +} + ++ (BOOL)requiresAuthentication +{ + return NO; +} + + + +#pragma mark - +#pragma mark Configuration : Dynamic Enable + +- (BOOL)shouldAutoShare +{ + return YES; +} + + +#pragma mark - +#pragma mark Share API Methods + +- (BOOL)send +{ + self.quiet = YES; + + [[UIApplication sharedApplication] openURL:item.URL]; + + [self sendDidFinish]; + + return YES; +} + +@end diff --git a/Classes/ShareKit/Sharers/Actions/Save to Album/SHKPhotoAlbum.h b/Classes/ShareKit/Sharers/Actions/Save to Album/SHKPhotoAlbum.h new file mode 100644 index 00000000..caf00757 --- /dev/null +++ b/Classes/ShareKit/Sharers/Actions/Save to Album/SHKPhotoAlbum.h @@ -0,0 +1,36 @@ +// +// SHKPhotoAlbum.h +// ShareKit +// +// Created by Richard Johnson on 7/22/10. + +// +// 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 "SHKSharer.h" + +@interface SHKPhotoAlbum : SHKSharer +{ + +} + +@end diff --git a/Classes/ShareKit/Sharers/Actions/Save to Album/SHKPhotoAlbum.m b/Classes/ShareKit/Sharers/Actions/Save to Album/SHKPhotoAlbum.m new file mode 100644 index 00000000..447ec94f --- /dev/null +++ b/Classes/ShareKit/Sharers/Actions/Save to Album/SHKPhotoAlbum.m @@ -0,0 +1,80 @@ +// +// SHKPhotoAlbum.m +// ShareKit +// +// Created by Richard Johnson on 7/22/10. + +// +// 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 "SHKPhotoAlbum.h" + + +@implementation SHKPhotoAlbum + +#pragma mark - +#pragma mark Configuration : Service Definition + ++ (NSString *)sharerTitle +{ + return @"Save to Photo Album"; +} + ++ (BOOL)canShareImage +{ + return YES; +} + ++ (BOOL)shareRequiresInternetConnection +{ + return NO; +} + ++ (BOOL)requiresAuthentication +{ + return NO; +} + + +#pragma mark - +#pragma mark Configuration : Dynamic Enable + +- (BOOL)shouldAutoShare +{ + return YES; +} + + +#pragma mark - +#pragma mark Share API Methods + +- (BOOL)send +{ + if (item.shareType == SHKShareTypeImage) + UIImageWriteToSavedPhotosAlbum(item.image, nil, nil, nil); + + // Notify user + [[SHKActivityIndicator currentIndicator] displayCompleted:@"Saved!"]; + + return YES; +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Delicious/SHKDelicious.h b/Classes/ShareKit/Sharers/Services/Delicious/SHKDelicious.h new file mode 100644 index 00000000..9d71d47c --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Delicious/SHKDelicious.h @@ -0,0 +1,41 @@ +// +// SHKDelicious.h +// ShareKit +// +// Created by Nathan Weiner on 6/21/10. + +// +// 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 "SHKOAuthSharer.h" + +@interface SHKDelicious : SHKOAuthSharer +{ + +} + +- (BOOL)handleResponse:(SHKRequest *)aRequest; + +- (void)sendTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data; +- (void)sendTicket:(OAServiceTicket *)ticket didFailWithError:(NSError*)error; + +@end diff --git a/Classes/ShareKit/Sharers/Services/Delicious/SHKDelicious.m b/Classes/ShareKit/Sharers/Services/Delicious/SHKDelicious.m new file mode 100644 index 00000000..59e8b10d --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Delicious/SHKDelicious.m @@ -0,0 +1,224 @@ +// +// SHKDelicious.m +// ShareKit +// +// Created by Nathan Weiner on 6/21/10. + +// +// 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 "SHKDelicious.h" +#import "OAuthConsumer.h" + + +// You can leave this be. The user will actually never see this url. ShareKit just looks for +// when delicious redirects to this url and intercepts it. It can be any url. +#define SHKDeliciousCallbackUrl @"http://getsharekit.com/oauthcallback" + + +// http://github.com/jdg/oauthconsumer/blob/master/OATokenManager.m + +@implementation SHKDelicious + + +- (id)init +{ + if (self = [super init]) + { + self.consumerKey = SHKDeliciousConsumerKey; + self.secretKey = SHKDeliciousSecretKey; + self.authorizeCallbackURL = [NSURL URLWithString:SHKDeliciousCallbackUrl];// HOW-TO: In your Twitter application settings, use the "Callback URL" field. If you do not have this field in the settings, set your application type to 'Browser'. + + + // -- // + + + // You do not need to edit these, they are the same for everyone + self.authorizeURL = [NSURL URLWithString:@"https://api.login.yahoo.com/oauth/v2/request_auth"]; + self.requestURL = [NSURL URLWithString:@"https://api.login.yahoo.com/oauth/v2/get_request_token"]; + self.accessURL = [NSURL URLWithString:@"https://api.login.yahoo.com/oauth/v2/get_token"]; + + self.signatureProvider = [[[OAPlaintextSignatureProvider alloc] init] autorelease]; + } + return self; +} + + +#pragma mark - +#pragma mark Configuration : Service Defination + ++ (NSString *)sharerTitle +{ + return @"Delicious"; +} + ++ (BOOL)canShareURL +{ + return YES; +} + + +#pragma mark - +#pragma mark Authentication + +- (void)tokenRequestModifyRequest:(OAMutableURLRequest *)oRequest +{ + [oRequest setOAuthParameterName:@"oauth_callback" withValue:authorizeCallbackURL.absoluteString]; +} + +- (void)tokenAccessModifyRequest:(OAMutableURLRequest *)oRequest +{ + if (pendingAction == SHKPendingRefreshToken) + { + if (accessToken.sessionHandle != nil) + [oRequest setOAuthParameterName:@"oauth_session_handle" withValue:accessToken.sessionHandle]; + } + + else + [oRequest setOAuthParameterName:@"oauth_verifier" withValue:[authorizeResponseQueryVars objectForKey:@"oauth_verifier"]]; +} + +- (BOOL)handleResponse:(SHKRequest *)aRequest +{ + NSString *response = [aRequest getResult]; + + if ([response isEqualToString:@"401 Forbidden"]) + { + [self sendDidFailShouldRelogin]; + return NO; + } + + return YES; +} + + +#pragma mark - +#pragma mark Share Form + +- (NSArray *)shareFormFieldsForType:(SHKShareType)type +{ + if (type == SHKShareTypeURL) + return [NSArray arrayWithObjects: + [SHKFormFieldSettings label:SHKLocalizedString(@"Title") key:@"title" type:SHKFormFieldTypeText start:item.title], + [SHKFormFieldSettings label:SHKLocalizedString(@"Tags") key:@"tags" type:SHKFormFieldTypeText start:item.tags], + [SHKFormFieldSettings label:SHKLocalizedString(@"Notes") key:@"text" type:SHKFormFieldTypeText start:item.text], + [SHKFormFieldSettings label:SHKLocalizedString(@"Shared") key:@"shared" type:SHKFormFieldTypeSwitch start:SHKFormFieldSwitchOff], + nil]; + + return nil; +} + + + +#pragma mark - +#pragma mark Share API Methods + +- (BOOL)send +{ + if ([self validateItem]) + { + OAMutableURLRequest *oRequest = [[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://api.del.icio.us/v2/posts/add"] + consumer:consumer + token:accessToken + realm:nil + signatureProvider:nil]; + + [oRequest setHTTPMethod:@"GET"]; + + + OARequestParameter *urlParam = [OARequestParameter requestParameterWithName:@"url" + value:[item.URL.absoluteString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + + OARequestParameter *descParam = [OARequestParameter requestParameterWithName:@"description" + value:SHKStringOrBlank(item.title)]; + + OARequestParameter *tagsParam = [OARequestParameter requestParameterWithName:@"tags" + value:SHKStringOrBlank(item.tags)]; + + OARequestParameter *extendedParam = [OARequestParameter requestParameterWithName:@"extended" + value:SHKStringOrBlank(item.text)]; + + OARequestParameter *sharedParam = [OARequestParameter requestParameterWithName:@"shared" + value:[item customBoolForSwitchKey:@"shared"]?@"yes":@"no"]; + + + [oRequest setParameters:[NSArray arrayWithObjects:descParam, extendedParam, sharedParam, tagsParam, urlParam, nil]]; + + OAAsynchronousDataFetcher *fetcher = [OAAsynchronousDataFetcher asynchronousFetcherWithRequest:oRequest + delegate:self + didFinishSelector:@selector(sendTicket:didFinishWithData:) + didFailSelector:@selector(sendTicket:didFailWithError:)]; + + [fetcher start]; + [oRequest release]; + + // Notify delegate + [self sendDidStart]; + + return YES; + } + + return NO; +} + + +- (void)sendTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data +{ + if (ticket.didSucceed && [ticket.body rangeOfString:@"\"done\""].location != NSNotFound) + { + // Do anything? + } + + else + { + if (SHKDebugShowLogs) // check so we don't have to alloc the string with the data if we aren't logging + SHKLog(@"SHKDelicious sendTicket Response Body: %@", [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]); + + // Look for oauth problems + // TODO - I'd prefer to use regex for this but that would require OS4 or adding a regex library + NSError *error; + NSString *body = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; + + // Expired token + if ([body rangeOfString:@"token_expired"].location != NSNotFound) + { + [self refreshToken]; + return; + } + + else + error = [SHK error:SHKLocalizedString(@"There was a problem saving to Delicious.")]; + + [self sendTicket:ticket didFailWithError:error]; + } + + // Notify delegate + [self sendDidFinish]; +} + +- (void)sendTicket:(OAServiceTicket *)ticket didFailWithError:(NSError*)error +{ + [self sendDidFailWithError:error]; +} + + + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/close.png b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/close.png new file mode 100644 index 00000000..a89b82fa Binary files /dev/null and b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/close.png differ diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/fbicon.png b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/fbicon.png new file mode 100644 index 00000000..413396be Binary files /dev/null and b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/fbicon.png differ diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/login.png b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/login.png new file mode 100644 index 00000000..77bc30c2 Binary files /dev/null and b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/login.png differ diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/login2.png b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/login2.png new file mode 100644 index 00000000..03e8eba9 Binary files /dev/null and b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/login2.png differ diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/login2_down.png b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/login2_down.png new file mode 100644 index 00000000..6c4cb4ef Binary files /dev/null and b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/login2_down.png differ diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/login_down.png b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/login_down.png new file mode 100644 index 00000000..b847a0a1 Binary files /dev/null and b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/login_down.png differ diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/logout.png b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/logout.png new file mode 100644 index 00000000..290272aa Binary files /dev/null and b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/logout.png differ diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/logout_down.png b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/logout_down.png new file mode 100644 index 00000000..1ab14b9d Binary files /dev/null and b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.bundle/images/logout_down.png differ diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.h b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.h new file mode 100644 index 00000000..80f5f0ff --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnect.h @@ -0,0 +1,22 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBSession.h" +#import "FBRequest.h" +#import "FBLoginButton.h" +#import "FBLoginDialog.h" +#import "FBPermissionDialog.h" +#import "FBStreamDialog.h" diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnectGlobal.h b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnectGlobal.h new file mode 100644 index 00000000..99d51eee --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnectGlobal.h @@ -0,0 +1,218 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import +#import + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +extern const NSString* kFB_SDK_VersionNumber; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef DEBUG +#define FBLOG +#define FBLOG2 +#else +#define FBLOG +#define FBLOG2 +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef unsigned long long FBUID; +typedef unsigned long long FBID; + +#define FBAPI_ERROR_DOMAIN @"api.facebook.com" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Error codes + +#define FBAPI_EC_SUCCESS 0 +#define FBAPI_EC_UNKNOWN 1 +#define FBAPI_EC_SERVICE 2 +#define FBAPI_EC_METHOD 3 +#define FBAPI_EC_TOO_MANY_CALLS 4 +#define FBAPI_EC_BAD_IP 5 +#define FBAPI_EC_HOST_API 6 +#define FBAPI_EC_HOST_UP 7 +#define FBAPI_EC_SECURE 8 +#define FBAPI_EC_RATE 9 +#define FBAPI_EC_PERMISSION_DENIED 10 +#define FBAPI_EC_DEPRECATED 11 +#define FBAPI_EC_VERSION 12 + +#define FBAPI_EC_PARAM 100 +#define FBAPI_EC_PARAM_FBAPI_KEY 101 +#define FBAPI_EC_PARAM_SESSION_KEY 102 +#define FBAPI_EC_PARAM_CALL_ID 103 +#define FBAPI_EC_PARAM_SIGNATURE 104 +#define FBAPI_EC_PARAM_TOO_MANY 105 +#define FBAPI_EC_PARAM_USER_ID 110 +#define FBAPI_EC_PARAM_USER_FIELD 111 +#define FBAPI_EC_PARAM_SOCIAL_FIELD 112 +#define FBAPI_EC_PARAM_EMAIL 113 +#define FBAPI_EC_PARAM_ALBUM_ID 120 +#define FBAPI_EC_PARAM_PHOTO_ID 121 +#define FBAPI_EC_PARAM_FEED_PRIORITY 130 +#define FBAPI_EC_PARAM_CATEGORY 140 +#define FBAPI_EC_PARAM_SUBCATEGORY 141 +#define FBAPI_EC_PARAM_TITLE 142 +#define FBAPI_EC_PARAM_DESCRIPTION 143 +#define FBAPI_EC_PARAM_BAD_JSON 144 +#define FBAPI_EC_PARAM_BAD_EID 150 +#define FBAPI_EC_PARAM_UNKNOWN_CITY 151 +#define FBAPI_EC_PARAM_BAD_PAGE_TYPE 152 + +#define FBAPI_EC_PERMISSION 200 +#define FBAPI_EC_PERMISSION_USER 210 +#define FBAPI_EC_PERMISSION_ALBUM 220 +#define FBAPI_EC_PERMISSION_PHOTO 221 +#define FBAPI_EC_PERMISSION_MESSAGE 230 +#define FBAPI_EC_PERMISSION_MARKUP_OTHER_USER 240 +#define FBAPI_EC_PERMISSION_STATUS_UPDATE 250 +#define FBAPI_EC_PERMISSION_PHOTO_UPLOAD 260 +#define FBAPI_EC_PERMISSION_SMS 270 +#define FBAPI_EC_PERMISSION_CREATE_LISTING 280 +#define FBAPI_EC_PERMISSION_EVENT 290 +#define FBAPI_EC_PERMISSION_LARGE_FBML_TEMPLATE 291 +#define FBAPI_EC_PERMISSION_LIVEMESSAGE 292 +#define FBAPI_EC_PERMISSION_RSVP_EVENT 299 + +#define FBAPI_EC_EDIT 300 +#define FBAPI_EC_EDIT_USER_DATA 310 +#define FBAPI_EC_EDIT_PHOTO 320 +#define FBAPI_EC_EDIT_ALBUM_SIZE 321 +#define FBAPI_EC_EDIT_PHOTO_TAG_SUBJECT 322 +#define FBAPI_EC_EDIT_PHOTO_TAG_PHOTO 323 +#define FBAPI_EC_EDIT_PHOTO_FILE 324 +#define FBAPI_EC_EDIT_PHOTO_PENDING_LIMIT 325 +#define FBAPI_EC_EDIT_PHOTO_TAG_LIMIT 326 +#define FBAPI_EC_EDIT_ALBUM_REORDER_PHOTO_NOT_IN_ALBUM 327 +#define FBAPI_EC_EDIT_ALBUM_REORDER_TOO_FEW_PHOTOS 328 +#define FBAPI_EC_MALFORMED_MARKUP 329 +#define FBAPI_EC_EDIT_MARKUP 330 +#define FBAPI_EC_EDIT_FEED_TOO_MANY_USER_CALLS 340 +#define FBAPI_EC_EDIT_FEED_TOO_MANY_USER_ACTION_CALLS 341 +#define FBAPI_EC_EDIT_FEED_TITLE_LINK 342 +#define FBAPI_EC_EDIT_FEED_TITLE_LENGTH 343 +#define FBAPI_EC_EDIT_FEED_TITLE_NAME 344 +#define FBAPI_EC_EDIT_FEED_TITLE_BLANK 345 +#define FBAPI_EC_EDIT_FEED_BODY_LENGTH 346 +#define FBAPI_EC_EDIT_FEED_PHOTO_SRC 347 +#define FBAPI_EC_EDIT_FEED_PHOTO_LINK 348 +#define FBAPI_EC_EDIT_VIDEO_SIZE 350 +#define FBAPI_EC_EDIT_VIDEO_INVALID_FILE 351 +#define FBAPI_EC_EDIT_VIDEO_INVALID_TYPE 352 +#define FBAPI_EC_EDIT_FEED_TITLE_ARRAY 360 +#define FBAPI_EC_EDIT_FEED_TITLE_PARAMS 361 +#define FBAPI_EC_EDIT_FEED_BODY_ARRAY 362 +#define FBAPI_EC_EDIT_FEED_BODY_PARAMS 363 +#define FBAPI_EC_EDIT_FEED_PHOTO 364 +#define FBAPI_EC_EDIT_FEED_TEMPLATE 365 +#define FBAPI_EC_EDIT_FEED_TARGET 366 +#define FBAPI_EC_USERS_CREATE_INVALID_EMAIL 370 +#define FBAPI_EC_USERS_CREATE_EXISTING_EMAIL 371 +#define FBAPI_EC_USERS_CREATE_BIRTHDAY 372 +#define FBAPI_EC_USERS_CREATE_PASSWORD 373 +#define FBAPI_EC_USERS_REGISTER_INVALID_CREDENTIAL 374 +#define FBAPI_EC_USERS_REGISTER_CONF_FAILURE 375 +#define FBAPI_EC_USERS_REGISTER_EXISTING 376 +#define FBAPI_EC_USERS_REGISTER_DEFAULT_ERROR 377 +#define FBAPI_EC_USERS_REGISTER_PASSWORD_BLANK 378 +#define FBAPI_EC_USERS_REGISTER_PASSWORD_INVALID_CHARS 379 +#define FBAPI_EC_USERS_REGISTER_PASSWORD_SHORT 380 +#define FBAPI_EC_USERS_REGISTER_PASSWORD_WEAK 381 +#define FBAPI_EC_USERS_REGISTER_USERNAME_ERROR 382 +#define FBAPI_EC_USERS_REGISTER_MISSING_INPUT 383 +#define FBAPI_EC_USERS_REGISTER_INCOMPLETE_BDAY 384 +#define FBAPI_EC_USERS_REGISTER_INVALID_EMAIL 385 +#define FBAPI_EC_USERS_REGISTER_EMAIL_DISABLED 386 +#define FBAPI_EC_USERS_REGISTER_ADD_USER_FAILED 387 +#define FBAPI_EC_USERS_REGISTER_NO_GENDER 388 + +#define FBAPI_EC_AUTH_EMAIL 400 +#define FBAPI_EC_AUTH_LOGIN 401 +#define FBAPI_EC_AUTH_SIG 402 +#define FBAPI_EC_AUTH_TIME 403 + +#define FBAPI_EC_SESSION_METHOD 451 +#define FBAPI_EC_SESSION_REQUIRED 453 +#define FBAPI_EC_SESSION_REQUIRED_FOR_SECRET 454 +#define FBAPI_EC_SESSION_CANNOT_USE_SESSION_SECRET 455 + +#define FBAPI_EC_MESG_BANNED 500 +#define FBAPI_EC_MESG_NO_BODY 501 +#define FBAPI_EC_MESG_TOO_LONG 502 +#define FBAPI_EC_MESG_RATE 503 +#define FBAPI_EC_MESG_INVALID_THREAD 504 +#define FBAPI_EC_MESG_INVALID_RECIP 505 +#define FBAPI_EC_POKE_INVALID_RECIP 510 +#define FBAPI_EC_POKE_OUTSTANDING 511 +#define FBAPI_EC_POKE_RATE 512 + +#define FQL_EC_UNKNOWN_ERROR 600 +#define FQL_EC_PARSER_ERROR 601 +#define FQL_EC_UNKNOWN_FIELD 602 +#define FQL_EC_UNKNOWN_TABLE 603 +#define FQL_EC_NO_INDEX 604 +#define FQL_EC_UNKNOWN_FUNCTION 605 +#define FQL_EC_INVALID_PARAM 606 +#define FQL_EC_INVALID_FIELD 607 +#define FQL_EC_INVALID_SESSION 608 + +#define FBAPI_EC_REF_SET_FAILED 700 +#define FBAPI_EC_FB_APP_UNKNOWN_ERROR 750 +#define FBAPI_EC_FB_APP_FETCH_FAILED 751 +#define FBAPI_EC_FB_APP_NO_DATA 752 +#define FBAPI_EC_FB_APP_NO_PERMISSIONS 753 +#define FBAPI_EC_FB_APP_TAG_MISSING 754 + +#define FBAPI_EC_DATA_UNKNOWN_ERROR 800 +#define FBAPI_EC_DATA_INVALID_OPERATION 801 +#define FBAPI_EC_DATA_QUOTA_EXCEEDED 802 +#define FBAPI_EC_DATA_OBJECT_NOT_FOUND 803 +#define FBAPI_EC_DATA_OBJECT_ALREADY_EXISTS 804 +#define FBAPI_EC_DATA_DATABASE_ERROR 805 +#define FBAPI_EC_DATA_CREATE_TEMPLATE_ERROR 806 +#define FBAPI_EC_DATA_TEMPLATE_EXISTS_ERROR 807 +#define FBAPI_EC_DATA_TEMPLATE_HANDLE_TOO_LONG 808 +#define FBAPI_EC_DATA_TEMPLATE_HANDLE_ALREADY_IN_USE 809 +#define FBAPI_EC_DATA_TOO_MANY_TEMPLATE_BUNDLES 810 +#define FBAPI_EC_DATA_MALFORMED_ACTION_LINK 811 +#define FBAPI_EC_DATA_TEMPLATE_USES_RESERVED_TOKEN 812 + +#define FBAPI_EC_NO_SUCH_APP 900 +#define FBAPI_BATCH_TOO_MANY_ITEMS 950 +#define FBAPI_EC_BATCH_ALREADY_STARTED 951 +#define FBAPI_EC_BATCH_NOT_STARTED 952 +#define FBAPI_EC_BATCH_METHOD_NOT_ALLOWED_IN_BATCH_MODE 953 + +#define FBAPI_EC_EVENT_INVALID_TIME 1000 +#define FBAPI_EC_INFO_NO_INFORMATION 1050 +#define FBAPI_EC_INFO_SET_FAILED 1051 + +#define FBAPI_EC_LIVEMESSAGE_SEND_FAILED 1100 +#define FBAPI_EC_LIVEMESSAGE_EVENT_NAME_TOO_LONG 1101 +#define FBAPI_EC_LIVEMESSAGE_MESSAGE_TOO_LONG 1102 + +#define FBAPI_EC_PAGES_CREATE 1201 + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +NSMutableArray* FBCreateNonRetainingArray(); + +BOOL FBIsDeviceIPad(); diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnectGlobal.m b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnectGlobal.m new file mode 100644 index 00000000..77ec5ca4 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBConnectGlobal.m @@ -0,0 +1,45 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBConnectGlobal.h" + +const NSString* kFB_SDK_VersionNumber = @"iphone/1.3.0"; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// private + +const void* RetainNoOp(CFAllocatorRef allocator, const void *value) { return value; } +void ReleaseNoOp(CFAllocatorRef allocator, const void *value) { } + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// public + +NSMutableArray* FBCreateNonRetainingArray() { + CFArrayCallBacks callbacks = kCFTypeArrayCallBacks; + callbacks.retain = RetainNoOp; + callbacks.release = ReleaseNoOp; + return (NSMutableArray*)CFArrayCreateMutable(nil, 0, &callbacks); +} + + +BOOL FBIsDeviceIPad() { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 30200 + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { + return YES; + } +#endif + return NO; +} diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBDialog.h b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBDialog.h new file mode 100644 index 00000000..98205489 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBDialog.h @@ -0,0 +1,137 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBConnectGlobal.h" + +@protocol FBDialogDelegate; +@class FBSession; + +@interface FBDialog : UIView { + id _delegate; + FBSession* _session; + NSURL* _loadingURL; + UIWebView* _webView; + UIActivityIndicatorView* _spinner; + UIImageView* _iconView; + UILabel* _titleLabel; + UIButton* _closeButton; + UIDeviceOrientation _orientation; + BOOL _showingKeyboard; +} + +/** + * The delegate. + */ +@property(nonatomic,assign) id delegate; + +/** + * The session for which the login is taking place. + */ +@property(nonatomic,assign) FBSession* session; + +/** + * The title that is shown in the header atop the view; + */ +@property(nonatomic,copy) NSString* title; + +/** + * Creates the view but does not display it. + */ +- (id)initWithSession:(FBSession*)session; + +/** + * Displays the view with an animation. + * + * The view will be added to the top of the current key window. + */ +- (void)show; + +/** + * Displays the first page of the dialog. + * + * Do not ever call this directly. It is intended to be overriden by subclasses. + */ +- (void)load; + +/** + * Displays a URL in the dialog. + */ +- (void)loadURL:(NSString*)url method:(NSString*)method get:(NSDictionary*)getParams + post:(NSDictionary*)postParams; + +/** + * Hides the view and notifies delegates of success or cancellation. + */ +- (void)dismissWithSuccess:(BOOL)success animated:(BOOL)animated; + +/** + * Hides the view and notifies delegates of an error. + */ +- (void)dismissWithError:(NSError*)error animated:(BOOL)animated; + +/** + * Subclasses may override to perform actions just prior to showing the dialog. + */ +- (void)dialogWillAppear; + +/** + * Subclasses may override to perform actions just after the dialog is hidden. + */ +- (void)dialogWillDisappear; + +/** + * Subclasses should override to process data returned from the server in a 'fbconnect' url. + * + * Implementations must call dismissWithSuccess:YES at some point to hide the dialog. + */ +- (void)dialogDidSucceed:(NSURL*)url; + +@end + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +@protocol FBDialogDelegate + +@optional + +/** + * Called when the dialog succeeds and is about to be dismissed. + */ +- (void)dialogDidSucceed:(FBDialog*)dialog; + +/** + * Called when the dialog is cancelled and is about to be dismissed. + */ +- (void)dialogDidCancel:(FBDialog*)dialog; + +/** + * Called when dialog failed to load due to an error. + */ +- (void)dialog:(FBDialog*)dialog didFailWithError:(NSError*)error; + +/** + * Asks if a link touched by a user should be opened in an external browser. + * + * If a user touches a link, the default behavior is to open the link in the Safari browser, + * which will cause your app to quit. You may want to prevent this from happening, open the link + * in your own internal browser, or perhaps warn the user that they are about to leave your app. + * If so, implement this method on your delegate and return NO. If you warn the user, you + * should hold onto the URL and once you have received their acknowledgement open the URL yourself + * using [[UIApplication sharedApplication] openURL:]. + */ +- (BOOL)dialog:(FBDialog*)dialog shouldOpenURLInExternalBrowser:(NSURL*)url; + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBDialog.m b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBDialog.m new file mode 100644 index 00000000..fd119f25 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBDialog.m @@ -0,0 +1,626 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBDialog.h" +#import "FBSession.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// global + +static NSString* kDefaultTitle = @"Connect to Facebook"; +static NSString* kStringBoundary = @"3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f"; + +static CGFloat kFacebookBlue[4] = {0.42578125, 0.515625, 0.703125, 1.0}; +static CGFloat kBorderGray[4] = {0.3, 0.3, 0.3, 0.8}; +static CGFloat kBorderBlack[4] = {0.3, 0.3, 0.3, 1}; +static CGFloat kBorderBlue[4] = {0.23, 0.35, 0.6, 1.0}; + +static CGFloat kTransitionDuration = 0.3; + +static CGFloat kTitleMarginX = 8; +static CGFloat kTitleMarginY = 4; +static CGFloat kPadding = 10; +static CGFloat kBorderWidth = 10; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation FBDialog + +@synthesize session = _session, delegate = _delegate; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// private + +- (void)addRoundedRectToPath:(CGContextRef)context rect:(CGRect)rect radius:(float)radius { + CGContextBeginPath(context); + CGContextSaveGState(context); + + if (radius == 0) { + CGContextTranslateCTM(context, CGRectGetMinX(rect), CGRectGetMinY(rect)); + CGContextAddRect(context, rect); + } else { + rect = CGRectOffset(CGRectInset(rect, 0.5, 0.5), 0.5, 0.5); + CGContextTranslateCTM(context, CGRectGetMinX(rect)-0.5, CGRectGetMinY(rect)-0.5); + CGContextScaleCTM(context, radius, radius); + float fw = CGRectGetWidth(rect) / radius; + float fh = CGRectGetHeight(rect) / radius; + + CGContextMoveToPoint(context, fw, fh/2); + CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1); + CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1); + CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1); + CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1); + } + + CGContextClosePath(context); + CGContextRestoreGState(context); +} + +- (void)drawRect:(CGRect)rect fill:(const CGFloat*)fillColors radius:(CGFloat)radius { + CGContextRef context = UIGraphicsGetCurrentContext(); + CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); + + if (fillColors) { + CGContextSaveGState(context); + CGContextSetFillColor(context, fillColors); + if (radius) { + [self addRoundedRectToPath:context rect:rect radius:radius]; + CGContextFillPath(context); + } else { + CGContextFillRect(context, rect); + } + CGContextRestoreGState(context); + } + + CGColorSpaceRelease(space); +} + +- (void)strokeLines:(CGRect)rect stroke:(const CGFloat*)strokeColor { + CGContextRef context = UIGraphicsGetCurrentContext(); + CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); + + CGContextSaveGState(context); + CGContextSetStrokeColorSpace(context, space); + CGContextSetStrokeColor(context, strokeColor); + CGContextSetLineWidth(context, 1.0); + + { + CGPoint points[] = {rect.origin.x+0.5, rect.origin.y-0.5, + rect.origin.x+rect.size.width, rect.origin.y-0.5}; + CGContextStrokeLineSegments(context, points, 2); + } + { + CGPoint points[] = {rect.origin.x+0.5, rect.origin.y+rect.size.height-0.5, + rect.origin.x+rect.size.width-0.5, rect.origin.y+rect.size.height-0.5}; + CGContextStrokeLineSegments(context, points, 2); + } + { + CGPoint points[] = {rect.origin.x+rect.size.width-0.5, rect.origin.y, + rect.origin.x+rect.size.width-0.5, rect.origin.y+rect.size.height}; + CGContextStrokeLineSegments(context, points, 2); + } + { + CGPoint points[] = {rect.origin.x+0.5, rect.origin.y, + rect.origin.x+0.5, rect.origin.y+rect.size.height}; + CGContextStrokeLineSegments(context, points, 2); + } + + CGContextRestoreGState(context); + + CGColorSpaceRelease(space); +} + +- (BOOL)shouldRotateToOrientation:(UIDeviceOrientation)orientation { + if (orientation == _orientation) { + return NO; + } else { + return orientation == UIDeviceOrientationLandscapeLeft + || orientation == UIDeviceOrientationLandscapeRight + || orientation == UIDeviceOrientationPortrait + || orientation == UIDeviceOrientationPortraitUpsideDown; + } +} + +- (CGAffineTransform)transformForOrientation { + UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; + if (orientation == UIInterfaceOrientationLandscapeLeft) { + return CGAffineTransformMakeRotation(M_PI*1.5); + } else if (orientation == UIInterfaceOrientationLandscapeRight) { + return CGAffineTransformMakeRotation(M_PI/2); + } else if (orientation == UIInterfaceOrientationPortraitUpsideDown) { + return CGAffineTransformMakeRotation(-M_PI); + } else { + return CGAffineTransformIdentity; + } +} + +- (void)sizeToFitOrientation:(BOOL)transform { + if (transform) { + self.transform = CGAffineTransformIdentity; + } + + CGRect frame = [UIScreen mainScreen].applicationFrame; + CGPoint center = CGPointMake( + frame.origin.x + ceil(frame.size.width/2), + frame.origin.y + ceil(frame.size.height/2)); + + CGFloat scale_factor = 1.0f; + if (FBIsDeviceIPad()) { + // On the iPad the dialog's dimensions should only be 60% of the screen's + scale_factor = 0.6f; + } + + CGFloat width = floor(scale_factor * frame.size.width) - kPadding * 2; + CGFloat height = floor(scale_factor * frame.size.height) - kPadding * 2; + + _orientation = [UIApplication sharedApplication].statusBarOrientation; + if (UIInterfaceOrientationIsLandscape(_orientation)) { + self.frame = CGRectMake(kPadding, kPadding, height, width); + } else { + self.frame = CGRectMake(kPadding, kPadding, width, height); + } + self.center = center; + + if (transform) { + self.transform = [self transformForOrientation]; + } +} + +- (void)updateWebOrientation { + UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; + if (UIInterfaceOrientationIsLandscape(orientation)) { + [_webView stringByEvaluatingJavaScriptFromString: + @"document.body.setAttribute('orientation', 90);"]; + } else { + [_webView stringByEvaluatingJavaScriptFromString: + @"document.body.removeAttribute('orientation');"]; + } +} + +- (void)bounce1AnimationStopped { + [UIView beginAnimations:nil context:nil]; + [UIView setAnimationDuration:kTransitionDuration/2]; + [UIView setAnimationDelegate:self]; + [UIView setAnimationDidStopSelector:@selector(bounce2AnimationStopped)]; + self.transform = CGAffineTransformScale([self transformForOrientation], 0.9, 0.9); + [UIView commitAnimations]; +} + +- (void)bounce2AnimationStopped { + [UIView beginAnimations:nil context:nil]; + [UIView setAnimationDuration:kTransitionDuration/2]; + self.transform = [self transformForOrientation]; + [UIView commitAnimations]; +} + +- (NSURL*)generateURL:(NSString*)baseURL params:(NSDictionary*)params { + if (params) { + NSMutableArray* pairs = [NSMutableArray array]; + for (NSString* key in params.keyEnumerator) { + NSString* value = [params objectForKey:key]; + NSString* val = [value stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + NSString* pair = [NSString stringWithFormat:@"%@=%@", key, val]; + [pairs addObject:pair]; + } + + NSString* query = [pairs componentsJoinedByString:@"&"]; + NSString* url = [NSString stringWithFormat:@"%@?%@", baseURL, query]; + return [NSURL URLWithString:url]; + } else { + return [NSURL URLWithString:baseURL]; + } +} + +- (NSMutableData*)generatePostBody:(NSDictionary*)params { + if (!params) { + return nil; + } + + NSMutableData* body = [NSMutableData data]; + NSString* endLine = [NSString stringWithFormat:@"\r\n--%@\r\n", kStringBoundary]; + + [body appendData:[[NSString stringWithFormat:@"--%@\r\n", kStringBoundary] + dataUsingEncoding:NSUTF8StringEncoding]]; + + for (id key in [params keyEnumerator]) { + [body appendData:[[NSString + stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] + dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[[params valueForKey:key] dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[endLine dataUsingEncoding:NSUTF8StringEncoding]]; + } + + return body; +} + +- (void)addObservers { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(deviceOrientationDidChange:) + name:@"UIDeviceOrientationDidChangeNotification" object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(keyboardWillShow:) name:@"UIKeyboardWillShowNotification" object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(keyboardWillHide:) name:@"UIKeyboardWillHideNotification" object:nil]; +} + +- (void)removeObservers { + [[NSNotificationCenter defaultCenter] removeObserver:self + name:@"UIDeviceOrientationDidChangeNotification" object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:@"UIKeyboardWillShowNotification" object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:@"UIKeyboardWillHideNotification" object:nil]; +} + +- (void)postDismissCleanup { + [self removeObservers]; + [self removeFromSuperview]; +} + +- (void)dismiss:(BOOL)animated { + [self dialogWillDisappear]; + + [_loadingURL release]; + _loadingURL = nil; + + if (animated) { + [UIView beginAnimations:nil context:nil]; + [UIView setAnimationDuration:kTransitionDuration]; + [UIView setAnimationDelegate:self]; + [UIView setAnimationDidStopSelector:@selector(postDismissCleanup)]; + self.alpha = 0; + [UIView commitAnimations]; + } else { + [self postDismissCleanup]; + } +} + +- (void)cancel { + [self dismissWithSuccess:NO animated:YES]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// NSObject + +- (id)init { + return [self initWithSession:[FBSession session]]; +} + +- (id)initWithSession:(FBSession*)session { + if (self = [super initWithFrame:CGRectZero]) { + _delegate = nil; + _session = [session retain]; + _loadingURL = nil; + _orientation = UIDeviceOrientationUnknown; + _showingKeyboard = NO; + + self.backgroundColor = [UIColor clearColor]; + self.autoresizesSubviews = YES; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + self.contentMode = UIViewContentModeRedraw; + + UIImage* iconImage = [UIImage imageNamed:@"FBConnect.bundle/images/fbicon.png"]; + UIImage* closeImage = [UIImage imageNamed:@"FBConnect.bundle/images/close.png"]; + + _iconView = [[UIImageView alloc] initWithImage:iconImage]; + [self addSubview:_iconView]; + + UIColor* color = [UIColor colorWithRed:167.0/255 green:184.0/255 blue:216.0/255 alpha:1]; + _closeButton = [[UIButton buttonWithType:UIButtonTypeCustom] retain]; + [_closeButton setImage:closeImage forState:UIControlStateNormal]; + [_closeButton setTitleColor:color forState:UIControlStateNormal]; + [_closeButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted]; + [_closeButton addTarget:self action:@selector(cancel) + forControlEvents:UIControlEventTouchUpInside]; + if ([_closeButton respondsToSelector:@selector(titleLabel)]) { + _closeButton.titleLabel.font = [UIFont boldSystemFontOfSize:12]; + } else { // This triggers a deprecation warning but at least it will work on OS 2.x + _closeButton.font = [UIFont boldSystemFontOfSize:12]; + } + _closeButton.showsTouchWhenHighlighted = YES; + _closeButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin + | UIViewAutoresizingFlexibleBottomMargin; + [self addSubview:_closeButton]; + + CGFloat titleLabelFontSize = (FBIsDeviceIPad() ? 18 : 14); + _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + _titleLabel.text = kDefaultTitle; + _titleLabel.backgroundColor = [UIColor clearColor]; + _titleLabel.textColor = [UIColor whiteColor]; + _titleLabel.font = [UIFont boldSystemFontOfSize:titleLabelFontSize]; + _titleLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin + | UIViewAutoresizingFlexibleBottomMargin; + [self addSubview:_titleLabel]; + + _webView = [[UIWebView alloc] initWithFrame:CGRectZero]; + _webView.delegate = self; + _webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self addSubview:_webView]; + + _spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: + UIActivityIndicatorViewStyleWhiteLarge]; + _spinner.autoresizingMask = + UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin + | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; + [self addSubview:_spinner]; + } + return self; +} + +- (void)dealloc { + _webView.delegate = nil; + [_webView release]; + [_spinner release]; + [_titleLabel release]; + [_iconView release]; + [_closeButton release]; + [_loadingURL release]; + [_session release]; + [super dealloc]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// UIView + +- (void)drawRect:(CGRect)rect { + CGRect grayRect = CGRectOffset(rect, -0.5, -0.5); + [self drawRect:grayRect fill:kBorderGray radius:10]; + + CGRect headerRect = CGRectMake( + ceil(rect.origin.x + kBorderWidth), ceil(rect.origin.y + kBorderWidth), + rect.size.width - kBorderWidth*2, _titleLabel.frame.size.height); + [self drawRect:headerRect fill:kFacebookBlue radius:0]; + [self strokeLines:headerRect stroke:kBorderBlue]; + + CGRect webRect = CGRectMake( + ceil(rect.origin.x + kBorderWidth), headerRect.origin.y + headerRect.size.height, + rect.size.width - kBorderWidth*2, _webView.frame.size.height+1); + [self strokeLines:webRect stroke:kBorderBlack]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// UIWebViewDelegate + +- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request + navigationType:(UIWebViewNavigationType)navigationType { + NSURL* url = request.URL; + if ([url.scheme isEqualToString:@"fbconnect"]) { + if ([url.resourceSpecifier isEqualToString:@"cancel"]) { + [self dismissWithSuccess:NO animated:YES]; + } else { + [self dialogDidSucceed:url]; + } + return NO; + } else if ([_loadingURL isEqual:url]) { + return YES; + } else if (navigationType == UIWebViewNavigationTypeLinkClicked) { + if ([_delegate respondsToSelector:@selector(dialog:shouldOpenURLInExternalBrowser:)]) { + if (![_delegate dialog:self shouldOpenURLInExternalBrowser:url]) { + return NO; + } + } + + [[UIApplication sharedApplication] openURL:request.URL]; + return NO; + } else { + return YES; + } +} + +- (void)webViewDidFinishLoad:(UIWebView *)webView { + [_spinner stopAnimating]; + _spinner.hidden = YES; + + self.title = [_webView stringByEvaluatingJavaScriptFromString:@"document.title"]; + [self updateWebOrientation]; +} + +- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { + // 102 == WebKitErrorFrameLoadInterruptedByPolicyChange + if (!([error.domain isEqualToString:@"WebKitErrorDomain"] && error.code == 102)) { + [self dismissWithError:error animated:YES]; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// UIDeviceOrientationDidChangeNotification + +- (void)deviceOrientationDidChange:(void*)object { + UIDeviceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; + if (!_showingKeyboard && [self shouldRotateToOrientation:orientation]) { + [self updateWebOrientation]; + + CGFloat duration = [UIApplication sharedApplication].statusBarOrientationAnimationDuration; + [UIView beginAnimations:nil context:nil]; + [UIView setAnimationDuration:duration]; + [self sizeToFitOrientation:YES]; + [UIView commitAnimations]; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// UIKeyboardNotifications + +- (void)keyboardWillShow:(NSNotification*)notification { + if (FBIsDeviceIPad()) { + // On the iPad the screen is large enough that we don't need to + // resize the dialog to accomodate the keyboard popping up + return; + } + + UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; + if (UIInterfaceOrientationIsLandscape(orientation)) { + _webView.frame = CGRectInset(_webView.frame, + -(kPadding + kBorderWidth), + -(kPadding + kBorderWidth) - _titleLabel.frame.size.height); + } + + _showingKeyboard = YES; +} + +- (void)keyboardWillHide:(NSNotification*)notification { + if (FBIsDeviceIPad()) { + return; + } + UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; + if (UIInterfaceOrientationIsLandscape(orientation)) { + _webView.frame = CGRectInset(_webView.frame, + kPadding + kBorderWidth, + kPadding + kBorderWidth + _titleLabel.frame.size.height); + } + + _showingKeyboard = NO; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// public + +- (NSString*)title { + return _titleLabel.text; +} + +- (void)setTitle:(NSString*)title { + _titleLabel.text = title; +} + +- (void)show { + [self load]; + [self sizeToFitOrientation:NO]; + + CGFloat innerWidth = self.frame.size.width - (kBorderWidth+1)*2; + [_iconView sizeToFit]; + [_titleLabel sizeToFit]; + [_closeButton sizeToFit]; + + _titleLabel.frame = CGRectMake( + kBorderWidth + kTitleMarginX + _iconView.frame.size.width + kTitleMarginX, + kBorderWidth, + innerWidth - (_titleLabel.frame.size.height + _iconView.frame.size.width + kTitleMarginX*2), + _titleLabel.frame.size.height + kTitleMarginY*2); + + _iconView.frame = CGRectMake( + kBorderWidth + kTitleMarginX, + kBorderWidth + floor(_titleLabel.frame.size.height/2 - _iconView.frame.size.height/2), + _iconView.frame.size.width, + _iconView.frame.size.height); + + _closeButton.frame = CGRectMake( + self.frame.size.width - (_titleLabel.frame.size.height + kBorderWidth), + kBorderWidth, + _titleLabel.frame.size.height, + _titleLabel.frame.size.height); + + _webView.frame = CGRectMake( + kBorderWidth+1, + kBorderWidth + _titleLabel.frame.size.height, + innerWidth, + self.frame.size.height - (_titleLabel.frame.size.height + 1 + kBorderWidth*2)); + + [_spinner sizeToFit]; + [_spinner startAnimating]; + _spinner.center = _webView.center; + + UIWindow* window = [UIApplication sharedApplication].keyWindow; + if (!window) { + window = [[UIApplication sharedApplication].windows objectAtIndex:0]; + } + [window addSubview:self]; + + [self dialogWillAppear]; + + self.transform = CGAffineTransformScale([self transformForOrientation], 0.001, 0.001); + [UIView beginAnimations:nil context:nil]; + [UIView setAnimationDuration:kTransitionDuration/1.5]; + [UIView setAnimationDelegate:self]; + [UIView setAnimationDidStopSelector:@selector(bounce1AnimationStopped)]; + self.transform = CGAffineTransformScale([self transformForOrientation], 1.1, 1.1); + [UIView commitAnimations]; + + [self addObservers]; +} + +- (void)dismissWithSuccess:(BOOL)success animated:(BOOL)animated { + if (success) { + if ([_delegate respondsToSelector:@selector(dialogDidSucceed:)]) { + [_delegate dialogDidSucceed:self]; + } + } else { + if ([_delegate respondsToSelector:@selector(dialogDidCancel:)]) { + [_delegate dialogDidCancel:self]; + } + } + + [self dismiss:animated]; +} + +- (void)dismissWithError:(NSError*)error animated:(BOOL)animated { + if ([_delegate respondsToSelector:@selector(dialog:didFailWithError:)]) { + [_delegate dialog:self didFailWithError:error]; + } + + [self dismiss:animated]; +} + +- (void)load { + // Intended for subclasses to override +} + +- (void)loadURL:(NSString*)url method:(NSString*)method get:(NSDictionary*)getParams + post:(NSDictionary*)postParams { + // This "test cookie" is required by login.php, or it complains that you need to enable JavaScript + NSHTTPCookieStorage* cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + NSHTTPCookie* testCookie = [NSHTTPCookie cookieWithProperties: + [NSDictionary dictionaryWithObjectsAndKeys: + @"1", NSHTTPCookieValue, + @"test_cookie", NSHTTPCookieName, + @".facebook.com", NSHTTPCookieDomain, + @"/", NSHTTPCookiePath, + nil]]; + [cookies setCookie:testCookie]; + + [_loadingURL release]; + _loadingURL = [[self generateURL:url params:getParams] retain]; + NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:_loadingURL]; + + if (method) { + [request setHTTPMethod:method]; + + if ([[method uppercaseString] isEqualToString:@"POST"]) { + NSString* contentType = [NSString + stringWithFormat:@"multipart/form-data; boundary=%@", kStringBoundary]; + [request setValue:contentType forHTTPHeaderField:@"Content-Type"]; + + NSData* body = [self generatePostBody:postParams]; + if (body) { + [request setHTTPBody:body]; + } + } + } + + [_webView loadRequest:request]; +} + +- (void)dialogWillAppear { +} + +- (void)dialogWillDisappear { +} + +- (void)dialogDidSucceed:(NSURL*)url { + [self dismissWithSuccess:YES animated:YES]; +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBFeedDialog.h b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBFeedDialog.h new file mode 100644 index 00000000..50ba0f61 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBFeedDialog.h @@ -0,0 +1,42 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBDialog.h" + +@interface FBFeedDialog : FBDialog { + long long _templateBundleId; + NSString* _templateData; + NSString* _bodyGeneral; +} + +/** + * The id for a previously registered template bundle. + */ +@property(nonatomic) long long templateBundleId; + +/** + * A JSON string containing template data. + * + * See http://wiki.developers.facebook.com/index.php/Template_Data + */ +@property(nonatomic,copy) NSString* templateData; + +/** + * Additional markup for a short story. + */ +@property(nonatomic,copy) NSString* bodyGeneral; + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBFeedDialog.m b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBFeedDialog.m new file mode 100644 index 00000000..cc310fef --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBFeedDialog.m @@ -0,0 +1,85 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBFeedDialog.h" +#import "FBSession.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// global + +static NSString* kFeedURL = @"http://www.facebook.com/connect/prompt_feed.php"; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation FBFeedDialog + +@synthesize templateBundleId = _templateBundleId, templateData = _templateData, + bodyGeneral = _bodyGeneral; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// private + +- (NSString*)generateFeedInfo { + NSMutableArray* pairs = [NSMutableArray array]; + + if (_templateBundleId) { + [pairs addObject:[NSString stringWithFormat:@"\"template_id\": %lld", _templateBundleId]]; + } + if (_templateData) { + [pairs addObject:[NSString stringWithFormat:@"\"template_data\": %@", _templateData]]; + } + if (_bodyGeneral) { + [pairs addObject:[NSString stringWithFormat:@"\"body_general\": \"%@\"", _bodyGeneral]]; + } + + return [NSString stringWithFormat:@"{%@}", [pairs componentsJoinedByString:@","]]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// NSObject + +- (id)initWithSession:(FBSession*)session { + if (self = [super initWithSession:session]) { + _templateBundleId = 0; + _templateData = nil; + _bodyGeneral = nil; + } + return self; +} + +- (void)dealloc { + [_templateData release]; + [_bodyGeneral release]; + [super dealloc]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// FBDialog + +- (void)load { + NSDictionary* getParams = [NSDictionary dictionaryWithObjectsAndKeys: + @"touch", @"display", nil]; + + NSString* feedInfo = [self generateFeedInfo]; + NSDictionary* postParams = [NSDictionary dictionaryWithObjectsAndKeys: + _session.apiKey, @"api_key", _session.sessionKey, @"session_key", + @"1", @"preview", @"fbconnect:success", @"callback", @"fbconnect:cancel", @"cancel", + feedInfo, @"feed_info", @"self_feed", @"feed_target_type", nil]; + + [self loadURL:kFeedURL method:@"POST" get:getParams post:postParams]; +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBLoginButton.h b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBLoginButton.h new file mode 100644 index 00000000..4f98f4f5 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBLoginButton.h @@ -0,0 +1,49 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBSession.h" + +typedef enum { + FBLoginButtonStyleNormal, + FBLoginButtonStyleWide, +} FBLoginButtonStyle; + +/** + * Standard button which lets the user log in or out of the session. + * + * The button will automatically change to reflect the state of the session, showing + * "login" if the session is not connected, and "logout" if the session is connected. + */ +@interface FBLoginButton : UIControl { + FBLoginButtonStyle _style; + FBSession* _session; + UIImageView* _imageView; +} + +/** + * The visual style of the button. + */ +@property(nonatomic) FBLoginButtonStyle style; + +/** + * The session object that the button will log in and out of. + * + * The default value is the global session singleton, so there is usually no need to + * set this property yourself. + */ +@property(nonatomic,retain) FBSession* session; + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBLoginButton.m b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBLoginButton.m new file mode 100644 index 00000000..06b5818b --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBLoginButton.m @@ -0,0 +1,196 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBLoginButton.h" +#import "FBLoginDialog.h" + +#import + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static UIAccessibilityTraits *traitImage = nil, *traitButton = nil; + +@implementation FBLoginButton + +@synthesize session = _session, style = _style; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// private + ++ (void)initialize { + if (self == [FBLoginButton class]) { + // Try to load the accessibility trait values on OS 3.0 + traitImage = dlsym(RTLD_SELF, "UIAccessibilityTraitImage"); + traitButton = dlsym(RTLD_SELF, "UIAccessibilityTraitButton"); + } +} + +- (UIImage*)buttonImage { + if (_session.isConnected) { + return [UIImage imageNamed:@"FBConnect.bundle/images/logout.png"]; + } else { + if (_style == FBLoginButtonStyleNormal) { + return [UIImage imageNamed:@"FBConnect.bundle/images/login.png"]; + } else if (_style == FBLoginButtonStyleWide) { + return [UIImage imageNamed:@"FBConnect.bundle/images/login2.png"]; + } else { + return nil; + } + } +} + +- (UIImage*)buttonHighlightedImage { + if (_session.isConnected) { + return [UIImage imageNamed:@"FBConnect.bundle/images/logout_down.png"]; + } else { + if (_style == FBLoginButtonStyleNormal) { + return [UIImage imageNamed:@"FBConnect.bundle/images/login_down.png"]; + } else if (_style == FBLoginButtonStyleWide) { + return [UIImage imageNamed:@"FBConnect.bundle/images/login2_down.png"]; + } else { + return nil; + } + } +} + +- (void)updateImage { + if (self.highlighted) { + _imageView.image = [self buttonHighlightedImage]; + } else { + _imageView.image = [self buttonImage]; + } +} + +- (void)touchUpInside { + if (_session.isConnected) { + [_session logout]; + } else { + FBLoginDialog* dialog = [[[FBLoginDialog alloc] initWithSession:_session] autorelease]; + [dialog show]; + } +} + +- (void)initButton { + _style = FBLoginButtonStyleNormal; + + _imageView = [[UIImageView alloc] initWithFrame:CGRectZero]; + _imageView.contentMode = UIViewContentModeCenter; + [self addSubview:_imageView]; + + self.backgroundColor = [UIColor clearColor]; + [self addTarget:self action:@selector(touchUpInside) + forControlEvents:UIControlEventTouchUpInside]; + + self.session = [FBSession session]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// NSObject + +- (id)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + [self initButton]; + if (CGRectIsEmpty(frame)) { + [self sizeToFit]; + } + } + return self; +} + +- (void)awakeFromNib { + [self initButton]; +} + +- (void)dealloc { + [_session.delegates removeObject:self]; + [_session release]; + [_imageView release]; + [super dealloc]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// UIView + +- (CGSize)sizeThatFits:(CGSize)size { + return _imageView.image.size; +} + +- (void)layoutSubviews { + _imageView.frame = self.bounds; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// UIControl + +- (void)setHighlighted:(BOOL)highlighted { + [super setHighlighted:highlighted]; + [self updateImage]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// FBSessionDelegate + +- (void)session:(FBSession*)session didLogin:(FBUID)uid { + [self updateImage]; +} + +- (void)sessionDidLogout:(FBSession*)session { + [self updateImage]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// UIAccessibility informal protocol (on 3.0 only) + +- (BOOL)isAccessibilityElement { + return YES; +} + +- (UIAccessibilityTraits)accessibilityTraits { + if (traitImage && traitButton) + return [super accessibilityTraits]|*traitImage|*traitButton; + else + return [super accessibilityTraits]; +} + +- (NSString *)accessibilityLabel { + if (_session.isConnected) { + return NSLocalizedString(@"Disconnect from Facebook", @"Accessibility label"); + } else { + return NSLocalizedString(@"Connect to Facebook", @"Accessibility label"); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// public + +- (void)setSession:(FBSession*)session { + if (session != _session) { + [_session.delegates removeObject:self]; + [_session release]; + _session = [session retain]; + [_session.delegates addObject:self]; + + [self updateImage]; + } +} + +- (void)setStyle:(FBLoginButtonStyle)style { + _style = style; + + [self updateImage]; +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBLoginDialog.h b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBLoginDialog.h new file mode 100644 index 00000000..8eb0b908 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBLoginDialog.h @@ -0,0 +1,24 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBDialog.h" +#import "FBRequest.h" + +@interface FBLoginDialog : FBDialog { + FBRequest* _getSessionRequest; +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBLoginDialog.m b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBLoginDialog.m new file mode 100644 index 00000000..9d49f603 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBLoginDialog.m @@ -0,0 +1,131 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBLoginDialog.h" +#import "FBSession.h" +#import "FBRequest.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// global + +static NSString* kLoginURL = @"http://www.facebook.com/login.php"; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation FBLoginDialog + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// private + +- (void)connectToGetSession:(NSString*)token { + _getSessionRequest = [[FBRequest requestWithSession:_session delegate:self] retain]; + NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObject:token forKey:@"auth_token"]; + if (!_session.apiSecret) { + [params setObject:@"1" forKey:@"generate_session_secret"]; + } + + if (_session.getSessionProxy) { + [_getSessionRequest post:_session.getSessionProxy params:params]; + } else { + [_getSessionRequest call:@"facebook.auth.getSession" params:params]; + } +} + +- (void)loadLoginPage { + NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys: + @"1", @"fbconnect", @"touch", @"connect_display", _session.apiKey, @"api_key", + @"fbconnect://success", @"next", nil]; + + [self loadURL:kLoginURL method:@"GET" get:params post:nil]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// NSObject + +- (id)initWithSession:(FBSession*)session { + if (self = [super initWithSession:session]) { + _getSessionRequest = nil; + } + return self; +} + +- (void)dealloc { + _getSessionRequest.delegate = nil; + [_getSessionRequest release]; + [super dealloc]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// FBDialog + +- (void)load { + [self loadLoginPage]; +} + +- (void)dialogWillDisappear { + [_webView stringByEvaluatingJavaScriptFromString:@"email.blur();"]; + + [_getSessionRequest cancel]; + + if (![_session isConnected]) { + [_session cancelLogin]; + } +} + +- (void)dialogDidSucceed:(NSURL*)url { + NSString* q = url.query; + NSRange start = [q rangeOfString:@"auth_token="]; + if (start.location != NSNotFound) { + NSRange end = [q rangeOfString:@"&"]; + NSUInteger offset = start.location+start.length; + NSString* token = end.location == NSNotFound + ? [q substringFromIndex:offset] + : [q substringWithRange:NSMakeRange(offset, end.location-offset)]; + + if (token) { + [self connectToGetSession:token]; + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// FBRequestDelegate + +- (void)request:(FBRequest*)request didLoad:(id)result { + NSDictionary* object = result; + FBUID uid = [[object objectForKey:@"uid"] longLongValue]; + NSString* sessionKey = [object objectForKey:@"session_key"]; + NSString* sessionSecret = [object objectForKey:@"secret"]; + NSTimeInterval expires = [[object objectForKey:@"expires"] floatValue]; + NSDate* expiration = expires ? [NSDate dateWithTimeIntervalSince1970:expires] : nil; + + [_getSessionRequest release]; + _getSessionRequest = nil; + + [_session begin:uid sessionKey:sessionKey sessionSecret:sessionSecret expires:expiration]; + [_session resume]; + + [self dismissWithSuccess:YES animated:YES]; +} + +- (void)request:(FBRequest*)request didFailWithError:(NSError*)error { + [_getSessionRequest release]; + _getSessionRequest = nil; + + [self dismissWithError:error animated:YES]; +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBPermissionDialog.h b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBPermissionDialog.h new file mode 100644 index 00000000..d203ddbe --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBPermissionDialog.h @@ -0,0 +1,31 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBLoginDialog.h" + +@interface FBPermissionDialog : FBLoginDialog { + NSString* _permission; + NSTimer* _redirectTimer; +} + +/** + * The extended permission to request. + * + * See http://wiki.developers.facebook.com/index.php/Extended_permissions + */ +@property(nonatomic,copy) NSString* permission; + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBPermissionDialog.m b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBPermissionDialog.m new file mode 100644 index 00000000..794964a2 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBPermissionDialog.m @@ -0,0 +1,100 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBPermissionDialog.h" +#import "FBSession.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// global + +static NSString* kPermissionURL = @"http://www.facebook.com/connect/prompt_permission.php"; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation FBPermissionDialog + +@synthesize permission = _permission; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// private + +- (void)redirectToLogin { + _redirectTimer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self + selector:@selector(redirectToLoginDelayed) userInfo:nil repeats:NO]; +} + +- (void)redirectToLoginDelayed { + _redirectTimer = nil; + + // This loads the login page, which will just redirect back to the callback url + // since the login cookies are set + [super load]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// NSObject + +- (id)initWithSession:(FBSession*)session { + if (self = [super initWithSession:session]) { + _permission = nil; + _redirectTimer = nil; + } + return self; +} + +- (void)dealloc { + [_redirectTimer invalidate]; + [_permission release]; + [super dealloc]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// FBDialog + +- (void)load { + NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys: + @"touch", @"display", _session.apiKey, @"api_key", _session.sessionKey, @"session_key", + _permission, @"ext_perm", @"fbconnect:success", @"next", @"fbconnect:cancel", @"cancel", nil]; + + [self loadURL:kPermissionURL method:@"GET" get:params post:nil]; +} + +- (void)dialogDidSucceed:(NSURL*)url { + if ([_permission isEqualToString:@"offline_access"]) { + [super dialogDidSucceed:url]; + } else { + [self dismissWithSuccess:YES animated:YES]; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// UIWebViewDelegate + +- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request + navigationType:(UIWebViewNavigationType)navigationType { + if ([_permission isEqualToString:@"offline_access"]) { + NSURL* url = request.URL; + if ([url.scheme isEqualToString:@"fbconnect"]) { + if ([url.resourceSpecifier isEqualToString:@"success"]) { + [self redirectToLogin]; + return NO; + } + } + } + return [super webView:webView shouldStartLoadWithRequest:request navigationType:navigationType]; +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBRequest.h b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBRequest.h new file mode 100644 index 00000000..1c5852b3 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBRequest.h @@ -0,0 +1,165 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBConnectGlobal.h" + +@protocol FBRequestDelegate; +@class FBSession; + +@interface FBRequest : NSObject { + FBSession* _session; + id _delegate; + NSString* _url; + NSString* _method; + id _userInfo; + NSMutableDictionary* _params; + NSObject* _dataParam; + NSDate* _timestamp; + NSURLConnection* _connection; + NSMutableData* _responseText; +} + +/** + * Creates a new API request for the global session. + */ ++ (FBRequest*)request; + +/** + * Creates a new API request for the global session with a delegate. + */ ++ (FBRequest*)requestWithDelegate:(id)delegate; + +/** + * Creates a new API request for a particular session. + */ ++ (FBRequest*)requestWithSession:(FBSession*)session; + +/** + * Creates a new API request for the global session with a delegate. + */ ++ (FBRequest*)requestWithSession:(FBSession*)session delegate:(id)delegate; + +@property(nonatomic,assign) id delegate; + +/** + * The URL which will be contacted to execute the request. + */ +@property(nonatomic,readonly) NSString* url; + +/** + * The API method which will be called. + */ +@property(nonatomic,readonly) NSString* method; + +/** + * An object used by the user of the request to help identify the meaning of the request. + */ +@property(nonatomic,retain) id userInfo; + +/** + * The dictionary of parameters to pass to the method. + * + * These values in the dictionary will be converted to strings using the + * standard Objective-C object-to-string conversion facilities. + */ +@property(nonatomic,readonly) NSDictionary* params; + +/** + * A data parameter. + * + * Used for methods such as photos.upload, video.upload, events.create, and + * events.edit. + */ +@property(nonatomic,readonly) NSObject* dataParam; + +/** + * The timestamp of when the request was sent to the server. + */ +@property(nonatomic,readonly) NSDate* timestamp; + +/** + * Indicates if the request has been sent and is awaiting a response. + */ +@property(nonatomic,readonly) BOOL loading; + +/** + * Creates a new request paired to a session. + */ +- (id)initWithSession:(FBSession*)session; + +/** + * Calls a method on the server asynchronously. + * + * The delegate will be called for each stage of the loading process. + */ +- (void)call:(NSString*)method params:(NSDictionary*)params; + +/** + * Calls a method on the server asynchronously, with a file upload component. + * + * The delegate will be called for each stage of the loading process. + */ +- (void)call:(NSString*)method params:(NSDictionary*)params dataParam:(NSData*)dataParam; + +/** + * Calls a URL on the server asynchronously. + * + * The delegate will be called for each stage of the loading process. + */ +- (void)post:(NSString*)url params:(NSDictionary*)params; + +/** + * Stops an active request before the response has returned. + */ +- (void)cancel; + +@end + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +@protocol FBRequestDelegate + +@optional + +/** + * Called just before the request is sent to the server. + */ +- (void)requestLoading:(FBRequest*)request; + +/** + * Called when the server responds and begins to send back data. + */ +- (void)request:(FBRequest*)request didReceiveResponse:(NSURLResponse*)response; + +/** + * Called when an error prevents the request from completing successfully. + */ +- (void)request:(FBRequest*)request didFailWithError:(NSError*)error; + +/** + * Called when a request returns and its response has been parsed into an object. + * + * The resulting object may be a dictionary, an array, a string, or a number, depending + * on thee format of the API response. + */ +- (void)request:(FBRequest*)request didLoad:(id)result; + +/** + * Called when the request was cancelled. + */ +- (void)requestWasCancelled:(FBRequest*)request; + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBRequest.m b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBRequest.m new file mode 100644 index 00000000..50dd54e5 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBRequest.m @@ -0,0 +1,378 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBRequest.h" +#import "FBSession.h" +#import "FBXMLHandler.h" +#import + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// global + +static NSString* kAPIVersion = @"1.0"; +static NSString* kAPIFormat = @"XML"; +static NSString* kUserAgent = @"FacebookConnect"; +static NSString* kStringBoundary = @"3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f"; + +static const NSTimeInterval kTimeoutInterval = 180.0; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation FBRequest + +@synthesize delegate = _delegate, + url = _url, + method = _method, + params = _params, + dataParam = _dataParam, + userInfo = _userInfo, + timestamp = _timestamp; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// class public + ++ (FBRequest*)request { + return [self requestWithSession:[FBSession session]]; +} + ++ (FBRequest*)requestWithDelegate:(id)delegate { + return [self requestWithSession:[FBSession session] delegate:delegate]; +} + ++ (FBRequest*)requestWithSession:(FBSession*)session { + return [[[FBRequest alloc] initWithSession:session] autorelease]; +} + ++ (FBRequest*)requestWithSession:(FBSession*)session delegate:(id)delegate { + FBRequest* request = [[[FBRequest alloc] initWithSession:session] autorelease]; + request.delegate = delegate; + return request; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// private + +- (NSString*)md5HexDigest:(NSString*)input { + const char* str = [input UTF8String]; + unsigned char result[CC_MD5_DIGEST_LENGTH]; + CC_MD5(str, strlen(str), result); + + NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2]; + for(int i = 0; i", _method ? _method : _url]; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +// NSURLConnectionDelegate + +- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response { + _responseText = [[NSMutableData alloc] init]; + + NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; + if ([_delegate respondsToSelector:@selector(request:didReceiveResponse:)]) { + [_delegate request:self didReceiveResponse:httpResponse]; + } +} + +-(void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data { + [_responseText appendData:data]; +} + +- (NSCachedURLResponse*)connection:(NSURLConnection*)connection + willCacheResponse:(NSCachedURLResponse*)cachedResponse { + return nil; +} + +-(void)connectionDidFinishLoading:(NSURLConnection*)connection { + [self handleResponseData:_responseText]; + + [_responseText release]; + _responseText = nil; + [_connection release]; + _connection = nil; +} + +- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error { + [self failWithError:error]; + + [_responseText release]; + _responseText = nil; + [_connection release]; + _connection = nil; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +// public + +- (BOOL)loading { + return !!_connection; +} + +- (void)call:(NSString*)method params:(NSDictionary*)params { + [self call:method params:params dataParam:nil]; +} + +- (void)call:(NSString*)method params:(NSDictionary*)params dataParam:(NSData*)dataParam { + _url = [[self urlForMethod:method] retain]; + _method = [method copy]; + _params = params + ? [[NSMutableDictionary alloc] initWithDictionary:params] + : [[NSMutableDictionary alloc] init]; + _dataParam = dataParam; + + [_params setObject:_method forKey:@"method"]; + [_params setObject:_session.apiKey forKey:@"api_key"]; + [_params setObject:kAPIVersion forKey:@"v"]; + [_params setObject:kAPIFormat forKey:@"format"]; + + if (![self isSpecialMethod]) { + [_params setObject:_session.sessionKey forKey:@"session_key"]; + [_params setObject:[self generateCallId] forKey:@"call_id"]; + + if (_session.sessionSecret) { + [_params setObject:@"1" forKey:@"ss"]; + } + } + + [_params setObject:[self generateSig] forKey:@"sig"]; + + [_session send:self]; +} + +- (void)post:(NSString*)url params:(NSDictionary*)params { + _url = [url retain]; + _params = params + ? [[NSMutableDictionary alloc] initWithDictionary:params] + : [[NSMutableDictionary alloc] init]; + + [_session send:self]; +} + +- (void)cancel { + if (_connection) { + [_connection cancel]; + [_connection release]; + _connection = nil; + + if ([_delegate respondsToSelector:@selector(requestWasCancelled:)]) { + [_delegate requestWasCancelled:self]; + } + } +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBSession.h b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBSession.h new file mode 100644 index 00000000..19b63fe3 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBSession.h @@ -0,0 +1,203 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBConnectGlobal.h" + +@protocol FBSessionDelegate; +@class FBRequest; + +/** + * An FBSession represents a single user's authenticated session for a Facebook application. + * + * To create a session, you must use the session key of your application (which can + * be found on the Facebook developer website). You may then use the login dialog to ask + * the user to enter their email address and password. If successful, you will get back a + * session key which can be used to make requests to the Facebook API. + * + * Session keys are cached and stored on the disk of the device so that you do not need to ask + * the user to login every time they launch the app. To restore the last active session, call the + * resume method after instantiating your session. + */ +@interface FBSession : NSObject { + NSMutableArray* _delegates; + NSString* _apiKey; + NSString* _apiSecret; + NSString* _getSessionProxy; + FBUID _uid; + NSString* _sessionKey; + NSString* _sessionSecret; + NSDate* _expirationDate; + NSMutableArray* _requestQueue; + NSDate* _lastRequestTime; + int _requestBurstCount; + NSTimer* _requestTimer; +} + +/** + * Delegates which implement FBSessionDelegate. + */ +@property(nonatomic,readonly) NSMutableArray* delegates; + +/** + * The URL used for API HTTP requests. + */ +@property(nonatomic,readonly) NSString* apiURL; + +/** + * The URL used for secure API HTTP requests. + */ +@property(nonatomic,readonly) NSString* apiSecureURL; + +/** + * Your application's API key, as passed to the constructor. + */ +@property(nonatomic,readonly) NSString* apiKey; + +/** + * Your application's API secret, as passed to the constructor. + */ +@property(nonatomic,readonly) NSString* apiSecret; + +/** + * The URL to call to create a session key after login. + * + * This is an alternative to calling auth.getSession directly using the secret key. + */ +@property(nonatomic,readonly) NSString* getSessionProxy; + +/** + * The current user's Facebook id. + */ +@property(nonatomic,readonly) FBUID uid; + +/** + * The current user's session key. + */ +@property(nonatomic,readonly) NSString* sessionKey; + +/** + * The current user's session secret. + */ +@property(nonatomic,readonly) NSString* sessionSecret; + +/** + * The expiration date of the session key. + */ +@property(nonatomic,readonly) NSDate* expirationDate; + +/** + * Determines if the session is active and connected to a user. + */ +@property(nonatomic,readonly) BOOL isConnected; + +/** + * The globally shared session instance. + */ ++ (FBSession*)session; + +/** + * Sets the globally shared session instance. + * + * This session is not retained, so you are still responsible for retaining it yourself. The + * first session that is created is automatically stored here. + */ ++ (void)setSession:(FBSession*)session; + +/** + * Constructs a session and stores it as the globally shared session instance. + * + * @param secret the application secret (optional) + */ ++ (FBSession*)sessionForApplication:(NSString*)key secret:(NSString*)secret + delegate:(id)delegate; + +/** + * Constructs a session and stores it as the global singleton. + * + * @param getSessionProxy a url to that proxies auth.getSession (optional) + */ ++ (FBSession*)sessionForApplication:(NSString*)key getSessionProxy:(NSString*)getSessionProxy + delegate:(id)delegate; + +/** + * Constructs a session for an application. + * + * @param secret the application secret (optional) + * @param getSessionProxy a url to that proxies auth.getSession (optional) + */ +- (FBSession*)initWithKey:(NSString*)key secret:(NSString*)secret + getSessionProxy:(NSString*)getSessionProxy; + +/** + * Begins a session for a user with a given key and secret. + */ +- (void)begin:(FBUID)uid sessionKey:(NSString*)sessionKey sessionSecret:(NSString*)sessionSecret + expires:(NSDate*)expires; + +/** + * Resumes a previous session whose uid, session key, and secret are cached on disk. + */ +- (BOOL)resume; + +/** + * Cancels a login (no-op if the login is already complete). + */ +- (void)cancelLogin; + +/** + * Ends the current session and deletes the uid, session key, and secret from disk. + */ +- (void)logout; + +/** + * Sends a fully configured request to the server for execution. + */ +- (void)send:(FBRequest*)request; + +/** + * Deletes all cookies belonging to facebook + */ +- (void)deleteFacebookCookies; + +@end + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +@protocol FBSessionDelegate + +/** + * Called when a user has successfully logged in and begun a session. + */ +- (void)session:(FBSession*)session didLogin:(FBUID)uid; + +@optional + +/** + * Called when a user closes the login dialog without logging in. + */ +- (void)sessionDidNotLogin:(FBSession*)session; + +/** + * Called when a session is about to log out. + */ +- (void)session:(FBSession*)session willLogout:(FBUID)uid; + +/** + * Called when a session has logged out. + */ +- (void)sessionDidLogout:(FBSession*)session; + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBSession.m b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBSession.m new file mode 100644 index 00000000..c21f11bb --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBSession.m @@ -0,0 +1,301 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBSession.h" +#import "FBRequest.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// global + +static NSString* kAPIRestURL = @"http://api.facebook.com/restserver.php"; +static NSString* kAPIRestSecureURL = @"https://api.facebook.com/restserver.php"; + +static const int kMaxBurstRequests = 3; +static const NSTimeInterval kBurstDuration = 2; + +static FBSession* sharedSession = nil; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation FBSession + +@synthesize delegates = _delegates, apiKey = _apiKey, apiSecret = _apiSecret, + getSessionProxy = _getSessionProxy, uid = _uid, sessionKey = _sessionKey, + sessionSecret = _sessionSecret, expirationDate = _expirationDate; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// class public + ++ (FBSession*)session { + return sharedSession; +} + ++ (void)setSession:(FBSession*)session { + sharedSession = session; +} + ++ (FBSession*)sessionForApplication:(NSString*)key secret:(NSString*)secret + delegate:(id)delegate { + FBSession* session = [[[FBSession alloc] initWithKey:key secret:secret + getSessionProxy:nil] autorelease]; + [session.delegates addObject:delegate]; + return session; +} + ++ (FBSession*)sessionForApplication:(NSString*)key getSessionProxy:(NSString*)getSessionProxy + delegate:(id)delegate { + FBSession* session = [[[FBSession alloc] initWithKey:key secret:nil + getSessionProxy:getSessionProxy] autorelease]; + [session.delegates addObject:delegate]; + return session; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// private + +- (void)save { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + if (_uid) { + [defaults setObject:[NSNumber numberWithLongLong:_uid] forKey:@"FBUserId"]; + } else { + [defaults removeObjectForKey:@"FBUserId"]; + } + + if (_sessionKey) { + [defaults setObject:_sessionKey forKey:@"FBSessionKey"]; + } else { + [defaults removeObjectForKey:@"FBSessionKey"]; + } + + if (_sessionSecret) { + [defaults setObject:_sessionSecret forKey:@"FBSessionSecret"]; + } else { + [defaults removeObjectForKey:@"FBSessionSecret"]; + } + + if (_expirationDate) { + [defaults setObject:_expirationDate forKey:@"FBSessionExpires"]; + } else { + [defaults removeObjectForKey:@"FBSessionExpires"]; + } + + [defaults synchronize]; +} + +- (void)unsave { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + [defaults removeObjectForKey:@"FBUserId"]; + [defaults removeObjectForKey:@"FBSessionKey"]; + [defaults removeObjectForKey:@"FBSessionSecret"]; + [defaults removeObjectForKey:@"FBSessionExpires"]; + [defaults synchronize]; +} + +- (void)startFlushTimer { + if (!_requestTimer) { + NSTimeInterval t = kBurstDuration + [_lastRequestTime timeIntervalSinceNow]; + _requestTimer = [NSTimer scheduledTimerWithTimeInterval:t target:self + selector:@selector(requestTimerReady) userInfo:nil repeats:NO]; + } +} + +- (void)enqueueRequest:(FBRequest*)request { + [_requestQueue addObject:request]; + [self startFlushTimer]; +} + +- (BOOL)performRequest:(FBRequest*)request enqueue:(BOOL)enqueue { + // Stagger requests that happen in short bursts to prevent the server from rejecting + // them for making too many requests in a short time + NSTimeInterval t = [_lastRequestTime timeIntervalSinceNow]; + BOOL burst = t && t > -kBurstDuration; + if (burst && ++_requestBurstCount > kMaxBurstRequests) { + if (enqueue) { + [self enqueueRequest:request]; + } + return NO; + } else { + [request performSelector:@selector(connect)]; + + if (!burst) { + _requestBurstCount = 1; + [_lastRequestTime release]; + _lastRequestTime = [[request timestamp] retain]; + } + } + return YES; +} + +- (void)flushRequestQueue { + while (_requestQueue.count) { + FBRequest* request = [_requestQueue objectAtIndex:0]; + if ([self performRequest:request enqueue:NO]) { + [_requestQueue removeObjectAtIndex:0]; + } else { + [self startFlushTimer]; + break; + } + } +} + +- (void)requestTimerReady { + _requestTimer = nil; + [self flushRequestQueue]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// NSObject + +- (FBSession*)initWithKey:(NSString*)key secret:(NSString*)secret + getSessionProxy:(NSString*)getSessionProxy { + if (self = [super init]) { + if (!sharedSession) { + sharedSession = self; + } + + _delegates = FBCreateNonRetainingArray(); + _apiKey = [key copy]; + _apiSecret = [secret copy]; + _getSessionProxy = [getSessionProxy copy]; + _uid = 0; + _sessionKey = nil; + _sessionSecret = nil; + _expirationDate = nil; + _requestQueue = [[NSMutableArray alloc] init]; + _lastRequestTime = nil; + _requestBurstCount = 0; + _requestTimer = nil; + } + return self; +} + +- (void)dealloc { + if (sharedSession == self) { + sharedSession = nil; + } + + [_delegates release]; + [_requestQueue release]; + [_apiKey release]; + [_apiSecret release]; + [_getSessionProxy release]; + [_sessionKey release]; + [_sessionSecret release]; + [_expirationDate release]; + [_lastRequestTime release]; + [super dealloc]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// public + +- (NSString*)apiURL { + return kAPIRestURL; +} + +- (NSString*)apiSecureURL { + return kAPIRestSecureURL; +} + +- (BOOL)isConnected { + return !!_sessionKey; +} + +- (void)begin:(FBUID)uid sessionKey:(NSString*)sessionKey + sessionSecret:(NSString*)sessionSecret expires:(NSDate*)expires { + _uid = uid; + _sessionKey = [sessionKey copy]; + _sessionSecret = [sessionSecret copy]; + _expirationDate = [expires retain]; + + [self save]; +} + +- (BOOL)resume { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + FBUID uid = [[defaults objectForKey:@"FBUserId"] longLongValue]; + if (uid) { + NSDate* expirationDate = [defaults objectForKey:@"FBSessionExpires"]; + if (!expirationDate || [expirationDate timeIntervalSinceNow] > 0) { + _uid = uid; + _sessionKey = [[defaults stringForKey:@"FBSessionKey"] copy]; + _sessionSecret = [[defaults stringForKey:@"FBSessionSecret"] copy]; + _expirationDate = [expirationDate retain]; + + for (id delegate in _delegates) { + [delegate session:self didLogin:_uid]; + } + return YES; + } + } + return NO; +} + +- (void)cancelLogin { + if (![self isConnected]) { + for (id delegate in _delegates) { + if ([delegate respondsToSelector:@selector(sessionDidNotLogin:)]) { + [delegate sessionDidNotLogin:self]; + } + } + } +} + +- (void)logout { + if (_sessionKey) { + for (id delegate in _delegates) { + if ([delegate respondsToSelector:@selector(session:willLogout:)]) { + [delegate session:self willLogout:_uid]; + } + } + + [self deleteFacebookCookies]; + + + _uid = 0; + [_sessionKey release]; + _sessionKey = nil; + [_sessionSecret release]; + _sessionSecret = nil; + [_expirationDate release]; + _expirationDate = nil; + [self unsave]; + + for (id delegate in _delegates) { + if ([delegate respondsToSelector:@selector(sessionDidLogout:)]) { + [delegate sessionDidLogout:self]; + } + } + } else { + [self deleteFacebookCookies]; + [self unsave]; + } +} + +- (void)send:(FBRequest*)request { + [self performRequest:request enqueue:YES]; +} + +- (void)deleteFacebookCookies { + NSHTTPCookieStorage* cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + NSArray* facebookCookies = [cookies cookiesForURL: + [NSURL URLWithString:@"http://login.facebook.com"]]; + for (NSHTTPCookie* cookie in facebookCookies) { + [cookies deleteCookie:cookie]; + } +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBStreamDialog.h b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBStreamDialog.h new file mode 100755 index 00000000..b926a01e --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBStreamDialog.h @@ -0,0 +1,62 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FBDialog.h" + +@interface FBStreamDialog : FBDialog { + NSString* _attachment; + NSString* _actionLinks; + NSString* _targetId; + NSString* _userMessagePrompt; +} + +/** + * A JSON-encoded object containing the text of the post, relevant links, a + * media type (image, video, mp3, flash), as well as any other key/value pairs + * you may want to add. + * + * Note: If you want to use this call to update a user's status, don't pass an + * attachment; the content of the userMessage parameter will become the user's + * new status and will appear at the top of the user's profile. + * + * For more info, see http://wiki.developers.facebook.com/index.php/Attachment_(Streams) + */ +@property(nonatomic,copy) NSString* attachment; + +/** + * A JSON-encoded array of action link objects, containing the link text and a + * hyperlink. + */ +@property(nonatomic,copy) NSString* actionLinks; + +/** + * The ID of the user or the Page where you are publishing the content. If this + * is specified, the post appears on the Wall of the target user, not on the + * Wall of the user who published the post. This mimics the action of posting + * on a friend's Wall on Facebook itself. + * + * Note: To post on the user's own wall, leave this blank. + */ +@property(nonatomic,copy) NSString* targetId; + +/** + * Text you provide the user as a prompt to specify a userMessage. This appears + * above the box where the user enters a custom message. + * For example, "What's on your mind?" + */ +@property(nonatomic,copy) NSString* userMessagePrompt; + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBStreamDialog.m b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBStreamDialog.m new file mode 100755 index 00000000..99fdf142 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBStreamDialog.m @@ -0,0 +1,77 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FBStreamDialog.h" +#import "FBSession.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// global + +static NSString* kStreamURL = @"http://www.facebook.com/connect/prompt_feed.php"; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation FBStreamDialog + +@synthesize attachment = _attachment, + actionLinks = _actionLinks, + targetId = _targetId, + userMessagePrompt = _userMessagePrompt; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// NSObject + +- (id)initWithSession:(FBSession*)session { + if (self = [super initWithSession:session]) { + _attachment = @""; + _actionLinks = @""; + _targetId = @""; + _userMessagePrompt = @""; + } + return self; +} + +- (void)dealloc { + [_attachment release]; + [_actionLinks release]; + [_targetId release]; + [_userMessagePrompt release]; + [super dealloc]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// FBDialog + +- (void)load { + NSDictionary* getParams = [NSDictionary dictionaryWithObjectsAndKeys: + @"touch", @"display", nil]; + + NSDictionary* postParams = [NSDictionary dictionaryWithObjectsAndKeys: + _session.apiKey, @"api_key", + _session.sessionKey, @"session_key", + @"1", @"preview", + @"fbconnect:success", @"callback", + @"fbconnect:cancel", @"cancel", + _attachment, @"attachment", + _actionLinks, @"action_links", + _targetId, @"target_id", + _userMessagePrompt, @"user_message_prompt", + nil]; + + [self loadURL:kStreamURL method:@"POST" get:getParams post:postParams]; +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBXMLHandler.h b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBXMLHandler.h new file mode 100644 index 00000000..27b1607a --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBXMLHandler.h @@ -0,0 +1,32 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBConnectGlobal.h" + +@interface FBXMLHandler : NSObject { + NSMutableArray* _stack; + NSMutableArray* _nameStack; + id _rootObject; + NSString* _rootName; + NSMutableString* _chars; + NSError* _parseError; +} + +@property(nonatomic,readonly) id rootObject; +@property(nonatomic,readonly) NSString* rootName; +@property(nonatomic,readonly) NSError* parseError; + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBXMLHandler.m b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBXMLHandler.m new file mode 100644 index 00000000..54de261f --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/FBConnect/FBXMLHandler.m @@ -0,0 +1,152 @@ +/* + * Copyright 2009 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#import "FBXMLHandler.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation FBXMLHandler + +@synthesize rootObject = _rootObject, rootName = _rootName, parseError = _parseError; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// private + +- (NSString*)topName { + return [_nameStack lastObject]; +} + +- (id)topObject:(BOOL)create { + id object = [_stack objectAtIndex:_stack.count-1]; + if (object == [NSNull null] && create) { + object = [NSMutableDictionary dictionary]; + [_stack replaceObjectAtIndex:_stack.count-1 withObject:object]; + } + return object; +} + +- (id)topContainer { + if (_stack.count < 2) { + return nil; + } else { + id object = [_stack objectAtIndex:_stack.count-2]; + if (object == [NSNull null]) { + object = [NSMutableDictionary dictionary]; + [_stack replaceObjectAtIndex:_stack.count-2 withObject:object]; + } + return object; + } +} + +- (void)flushCharacters { + NSCharacterSet* whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet]; + for (NSInteger i = 0; i < _chars.length; ++i) { + unichar c = [_chars characterAtIndex:i]; + if (![whitespace characterIsMember:c]) { + id topContainer = self.topContainer; + if ([topContainer isKindOfClass:[NSMutableArray class]]) { + id object = [NSDictionary dictionaryWithObject:_chars forKey:self.topName]; + [_stack replaceObjectAtIndex:_stack.count-1 withObject:object]; + } else { + [_stack replaceObjectAtIndex:_stack.count-1 withObject:_chars]; + } + break; + } + } + + [_chars release]; + _chars = nil; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// NSObject + +- (id)init { + if (self = [super init]) { + _stack = [[NSMutableArray alloc] init]; + _nameStack = [[NSMutableArray alloc] init]; + _rootObject = nil; + _rootName = nil; + _chars = nil; + _parseError = nil; + } + return self; +} + +- (void)dealloc { + [_stack release]; + [_nameStack release]; + [_rootObject release]; + [_rootName release]; + [_chars release]; + [_parseError release]; + [super dealloc]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// NSXMLParserDelegate + +- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName + namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName + attributes:(NSDictionary *)attributeDict { + [self flushCharacters]; + + id object = nil; + if ([[attributeDict objectForKey:@"list"] isEqualToString:@"true"]) { + object = [NSMutableArray array]; + } else { + object = [NSNull null]; + } + + [_stack addObject:object]; + [_nameStack addObject:elementName]; +} + +- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { + if (!_chars) { + _chars = [string mutableCopy]; + } else { + [_chars appendString:string]; + } +} + +- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName + namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { + [self flushCharacters]; + + id object = [[[self topObject:NO] retain] autorelease]; + NSString* name = [[self.topName retain] autorelease]; + [_stack removeLastObject]; + [_nameStack removeLastObject]; + + if (!_stack.count) { + _rootObject = [object retain]; + _rootName = [name retain]; + } else { + id topObject = [self topObject:YES]; + if ([topObject isKindOfClass:[NSMutableArray class]]) { + [topObject addObject:object]; + } else if ([topObject isKindOfClass:[NSMutableDictionary class]]) { + [topObject setObject:object forKey:name]; + } + } +} + +- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)error { + _parseError = [error retain]; +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/SHKFBStreamDialog.h b/Classes/ShareKit/Sharers/Services/Facebook/SHKFBStreamDialog.h new file mode 100644 index 00000000..a5190a03 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/SHKFBStreamDialog.h @@ -0,0 +1,19 @@ +// +// SHKFBStreamDialog.h +// Adds a little bit extra control to FBStreamDialog +// +// Created by Nathan Weiner on 7/26/10. +// Copyright 2010 Idea Shower, LLC. All rights reserved. +// + +#import +#import "FBStreamDialog.h" + +@interface SHKFBStreamDialog : FBStreamDialog +{ + NSString *defaultStatus; +} + +@property (nonatomic, retain) NSString *defaultStatus; + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/SHKFBStreamDialog.m b/Classes/ShareKit/Sharers/Services/Facebook/SHKFBStreamDialog.m new file mode 100644 index 00000000..53ec2152 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/SHKFBStreamDialog.m @@ -0,0 +1,40 @@ +// +// SHKFBStreamDialog.m +// RIL +// +// Created by Nathan Weiner on 7/26/10. +// Copyright 2010 Idea Shower, LLC. All rights reserved. +// + +#import "SHKFBStreamDialog.h" +#import "SHK.h" + +@implementation SHKFBStreamDialog + +@synthesize defaultStatus; + +- (void)dealloc +{ + [defaultStatus release]; + [super dealloc]; +} + +- (void)webViewDidFinishLoad:(UIWebView *)webView +{ + [super webViewDidFinishLoad:webView]; + + if (defaultStatus) + { + // Set the pre-filled status message + [_webView stringByEvaluatingJavaScriptFromString: + [NSString stringWithFormat:@"document.getElementsByName('feedform_user_message')[0].value = decodeURIComponent('%@')", + [SHKEncode(defaultStatus) stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"] + ] + ]; + + // Make the text field bigger + [_webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByName('feedform_user_message')[0].style.height='100px'"]; + } +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/SHKFacebook.h b/Classes/ShareKit/Sharers/Services/Facebook/SHKFacebook.h new file mode 100644 index 00000000..ab0ee137 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/SHKFacebook.h @@ -0,0 +1,52 @@ +// +// SHKFacebook.h +// ShareKit +// +// Created by Nathan Weiner on 6/18/10. + +// +// 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 "SHKSharer.h" +#import "FBConnect.h" + +typedef enum +{ + SHKFacebookPendingNone, + SHKFacebookPendingLogin, + SHKFacebookPendingStatus, + SHKFacebookPendingImage +} SHKFacebookPendingAction; + + +@interface SHKFacebook : SHKSharer +{ + FBSession *session; + SHKFacebookPendingAction pendingFacebookAction; + FBLoginDialog *login; +} + +@property (retain) FBSession *session; +@property SHKFacebookPendingAction pendingFacebookAction; +@property (retain) FBLoginDialog *login; + +@end diff --git a/Classes/ShareKit/Sharers/Services/Facebook/SHKFacebook.m b/Classes/ShareKit/Sharers/Services/Facebook/SHKFacebook.m new file mode 100644 index 00000000..44f0b85a --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Facebook/SHKFacebook.m @@ -0,0 +1,262 @@ +// +// SHKFacebook.m +// ShareKit +// +// Created by Nathan Weiner on 6/18/10. + +// +// 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 "SHKFacebook.h" +#import "SHKFBStreamDialog.h" + +@implementation SHKFacebook + +@synthesize session; +@synthesize pendingFacebookAction; +@synthesize login; + +- (void)dealloc +{ + [session.delegates removeObject:self]; + [session release]; + [login release]; + [super dealloc]; +} + + +#pragma mark - +#pragma mark Configuration : Service Defination + ++ (NSString *)sharerTitle +{ + return @"Facebook"; +} + ++ (BOOL)canShareURL +{ + return YES; +} + ++ (BOOL)canShareText +{ + return YES; +} + ++ (BOOL)canShareImage +{ + return YES; +} + ++ (BOOL)canShareOffline +{ + return NO; // TODO - would love to make this work +} + +#pragma mark - +#pragma mark Configuration : Dynamic Enable + +- (BOOL)shouldAutoShare +{ + return YES; // FBConnect presents its own dialog +} + +#pragma mark - +#pragma mark Authentication + +- (BOOL)isAuthorized +{ + if (session == nil) + { + + if(!SHKFacebookUseSessionProxy){ + self.session = [FBSession sessionForApplication:SHKFacebookKey + secret:SHKFacebookSecret + delegate:self]; + + }else { + self.session = [FBSession sessionForApplication:SHKFacebookKey + getSessionProxy:SHKFacebookSessionProxyURL + delegate:self]; + } + + + return [session resume]; + } + + return [session isConnected]; +} + +- (void)promptAuthorization +{ + self.pendingFacebookAction = SHKFacebookPendingLogin; + self.login = [[[FBLoginDialog alloc] initWithSession:[self session]] autorelease]; + [login show]; +} + +- (void)authFinished:(SHKRequest *)request +{ + +} + ++ (void)logout +{ + FBSession *fbSession; + + if(!SHKFacebookUseSessionProxy){ + fbSession = [FBSession sessionForApplication:SHKFacebookKey + secret:SHKFacebookSecret + delegate:self]; + + }else { + fbSession = [FBSession sessionForApplication:SHKFacebookKey + getSessionProxy:SHKFacebookSessionProxyURL + delegate:self]; + } + + [fbSession logout]; +} + +#pragma mark - +#pragma mark Share API Methods + +- (BOOL)send +{ + if (item.shareType == SHKShareTypeURL) + { + self.pendingFacebookAction = SHKFacebookPendingStatus; + + SHKFBStreamDialog* dialog = [[[SHKFBStreamDialog alloc] init] autorelease]; + dialog.delegate = self; + dialog.userMessagePrompt = SHKLocalizedString(@"Enter your message:"); + dialog.attachment = [NSString stringWithFormat: + @"{\ + \"name\":\"%@\",\ + \"href\":\"%@\"\ + }", + item.title == nil ? SHKEncodeURL(item.URL) : SHKEncode(item.title), + SHKEncodeURL(item.URL) + ]; + dialog.defaultStatus = item.text; + dialog.actionLinks = [NSString stringWithFormat:@"[{\"text\":\"Get %@\",\"href\":\"%@\"}]", + SHKEncode(SHKMyAppName), + SHKEncode(SHKMyAppURL)]; + [dialog show]; + + } + + else if (item.shareType == SHKShareTypeText) + { + self.pendingFacebookAction = SHKFacebookPendingStatus; + + SHKFBStreamDialog* dialog = [[[SHKFBStreamDialog alloc] init] autorelease]; + dialog.delegate = self; + dialog.userMessagePrompt = @"Enter your message:"; + dialog.defaultStatus = item.text; + dialog.actionLinks = [NSString stringWithFormat:@"[{\"text\":\"Get %@\",\"href\":\"%@\"}]", + SHKEncode(SHKMyAppName), + SHKEncode(SHKMyAppURL)]; + [dialog show]; + + } + + else if (item.shareType == SHKShareTypeImage) + { + self.pendingFacebookAction = SHKFacebookPendingImage; + + FBPermissionDialog* dialog = [[[FBPermissionDialog alloc] init] autorelease]; + dialog.delegate = self; + dialog.permission = @"photo_upload"; + [dialog show]; + } + + return YES; +} + +- (void)sendImage +{ + [self sendDidStart]; + + [[FBRequest requestWithDelegate:self] call:@"facebook.photos.upload" + params:[NSDictionary dictionaryWithObjectsAndKeys:item.title, @"caption", nil] + dataParam:UIImageJPEGRepresentation(item.image,1.0)]; +} + +- (void)dialogDidSucceed:(FBDialog*)dialog +{ + if (pendingFacebookAction == SHKFacebookPendingImage) + [self sendImage]; + + // TODO - the dialog has a SKIP button. Skipping still calls this even though it doesn't appear to post. + // - need to intercept the skip and handle it as a cancel? + else if (pendingFacebookAction == SHKFacebookPendingStatus) + [self sendDidFinish]; +} + +- (void)dialogDidCancel:(FBDialog*)dialog +{ + if (pendingFacebookAction == SHKFacebookPendingStatus) + [self sendDidCancel]; +} + +- (BOOL)dialog:(FBDialog*)dialog shouldOpenURLInExternalBrowser:(NSURL*)url +{ + return YES; +} + + +#pragma mark FBSessionDelegate methods + +- (void)session:(FBSession*)session didLogin:(FBUID)uid +{ + // Try to share again + if (pendingFacebookAction == SHKFacebookPendingLogin) + { + self.pendingFacebookAction = SHKFacebookPendingNone; + [self share]; + } +} + +- (void)session:(FBSession*)session willLogout:(FBUID)uid +{ + // Not handling this +} + + +#pragma mark FBRequestDelegate methods + +- (void)request:(FBRequest*)aRequest didLoad:(id)result +{ + if ([aRequest.method isEqualToString:@"facebook.photos.upload"]) + { + // PID is in [result objectForKey:@"pid"]; + [self sendDidFinish]; + } +} + +- (void)request:(FBRequest*)aRequest didFailWithError:(NSError*)error +{ + [self sendDidFailWithError:error]; +} + + + +@end diff --git a/Classes/ShareKit/Sharers/Services/Google Reader/SHKGoogleReader.h b/Classes/ShareKit/Sharers/Services/Google Reader/SHKGoogleReader.h new file mode 100644 index 00000000..9ac34068 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Google Reader/SHKGoogleReader.h @@ -0,0 +1,44 @@ +// +// SHKGoogleReader.h +// ShareKit +// +// Created by Nathan Weiner on 6/20/10. + +// +// 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 "SHKSharer.h" + +@interface SHKGoogleReader : SHKSharer +{ + NSMutableDictionary *session; + BOOL sendAfterLogin; +} + +@property (nonatomic, retain) NSMutableDictionary *session; +@property (nonatomic) BOOL sendAfterLogin; + +- (void)sendWithToken:(NSString *)token; +- (void)getSession:(NSString *)email password:(NSString *)password; +- (void)signRequest:(SHKRequest *)aRequest; + +@end diff --git a/Classes/ShareKit/Sharers/Services/Google Reader/SHKGoogleReader.m b/Classes/ShareKit/Sharers/Services/Google Reader/SHKGoogleReader.m new file mode 100644 index 00000000..d2aa6b6b --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Google Reader/SHKGoogleReader.m @@ -0,0 +1,301 @@ +// +// SHKGoogleReader.m +// ShareKit +// +// Created by Nathan Weiner on 6/20/10. + +// +// 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. +// +// + + +/* + +Google Reader API is unoffical, this was hobbled together from: + http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI + http://stackoverflow.com/questions/1041389/adding-notes-using-google-readers-api + http://www.google.com/support/reader/bin/answer.py?hl=en&answer=147149 +*/ + + +#import "SHKGoogleReader.h" + + +@implementation SHKGoogleReader + +@synthesize session; +@synthesize sendAfterLogin; + + +- (void)dealloc +{ + [session release]; + [super dealloc]; +} + + + +#pragma mark - +#pragma mark Configuration : Service Defination + ++ (NSString *)sharerTitle +{ + return @"Google Reader"; +} + ++ (BOOL)canShareURL +{ + return YES; +} + + + +#pragma mark - +#pragma mark Authorization + ++ (NSString *)authorizationFormCaption +{ + return SHKLocalizedString(@"Create a free account at %@", @"Google.com/reader"); +} + ++ (NSArray *)authorizationFormFields +{ + return [NSArray arrayWithObjects: + [SHKFormFieldSettings label:SHKLocalizedString(@"Email") key:@"email" type:SHKFormFieldTypeText start:nil], + [SHKFormFieldSettings label:SHKLocalizedString(@"Password") key:@"password" type:SHKFormFieldTypePassword start:nil], + nil]; +} + +- (void)authorizationFormValidate:(SHKFormController *)form +{ + // Display an activity indicator + if (!quiet) + [[SHKActivityIndicator currentIndicator] displayActivity:SHKLocalizedString(@"Logging In...")]; + + + // Authorize the user through the server + NSDictionary *formValues = [form formValues]; + + [self getSession:[formValues objectForKey:@"email"] + password:[formValues objectForKey:@"password"]]; + + self.pendingForm = form; +} + +- (void)getSession:(NSString *)email password:(NSString *)password +{ + NSString *params = [NSMutableString stringWithFormat:@"service=reader&source=%@&Email=%@&Passwd=%@&accountType=GOOGLE", + [NSString stringWithFormat:@"ShareKit-%@-%@", SHKEncode(SHKMyAppName), SHK_VERSION], + SHKEncode(email), + SHKEncode(password) + ]; + + self.request = [[[SHKRequest alloc] initWithURL:[NSURL URLWithString:@"https://www.google.com/accounts/ClientLogin"] + params:params + delegate:self + isFinishedSelector:@selector(authFinished:) + method:@"POST" + autostart:YES] autorelease]; +} + +- (void)authFinished:(SHKRequest *)aRequest +{ + // TODO - better error handling - use error codes from ( http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html ) + // TODO - capatcha support + + // Hide the activity indicator + if (!sendAfterLogin) + [[SHKActivityIndicator currentIndicator] hide]; + + // Parse Result + self.session = [NSMutableDictionary dictionaryWithCapacity:0]; + NSString *result = [request getResult]; + NSArray *parts; + + if (result != nil) + { + NSArray *lines = [result componentsSeparatedByString:@"\n"]; + for( NSString *line in lines) + { + parts = [line componentsSeparatedByString:@"="]; + if (parts.count == 2) + [session setObject:[parts objectAtIndex:1] forKey:[parts objectAtIndex:0]]; + } + } + + if (session != nil && [session objectForKey:@"Auth"]) + { + if (sendAfterLogin) + [self tryToSend]; + + else + [pendingForm saveForm]; + } + + else + { + NSString *error = [session objectForKey:@"Error"]; + NSString *message = nil; + + if (error != nil) + message = [error isEqualToString:@"BadAuthentication"] ? SHKLocalizedString(@"Incorrect username and password") : error; + + if (message == nil) // TODO - Could use some clearer message here. + message = SHKLocalizedString(@"There was an error logging into Google Reader"); + + [[[[UIAlertView alloc] initWithTitle:SHKLocalizedString(@"Login Error") + message:message + delegate:nil + cancelButtonTitle:SHKLocalizedString(@"Close") + otherButtonTitles:nil] autorelease] show]; + } +} + + +#pragma mark - +#pragma mark Share Form + +- (NSArray *)shareFormFieldsForType:(SHKShareType)type +{ + if (type == SHKShareTypeURL) + return [NSArray arrayWithObjects: + [SHKFormFieldSettings label:SHKLocalizedString(@"Note") key:@"text" type:SHKFormFieldTypeText start:item.text], + [SHKFormFieldSettings label:SHKLocalizedString(@"Public") key:@"share" type:SHKFormFieldTypeSwitch start:SHKFormFieldSwitchOff], + nil]; + + return nil; +} + + +#pragma mark - +#pragma mark Share API Methods + +- (void)signRequest:(SHKRequest *)aRequest +{ + // Add session cookie + NSDictionary *cookieDictionary; + NSHTTPCookie *cookie; + NSMutableArray *cookies = [NSMutableArray arrayWithCapacity:0]; + for (NSString *cookieName in session) + { + + cookieDictionary = [NSDictionary dictionaryWithObjectsAndKeys: + cookieName, NSHTTPCookieName, + [session objectForKey:cookieName], NSHTTPCookieValue, + @".google.com", NSHTTPCookieDomain, + @"/", NSHTTPCookiePath, + [NSDate dateWithTimeIntervalSinceNow:1600000000], NSHTTPCookieExpires, + nil]; + cookie = [NSHTTPCookie cookieWithProperties:cookieDictionary]; + [cookies addObject:cookie]; + } + NSMutableDictionary *headers = [[[NSHTTPCookie requestHeaderFieldsWithCookies:cookies] mutableCopy] autorelease]; + + [headers setObject:[NSString stringWithFormat:@"GoogleLogin auth=%@",[session objectForKey:@"Auth"]] forKey:@"Authorization"]; + + [aRequest setHeaderFields:headers]; +} + +- (BOOL)send +{ + if ([self validateItem]) + { + BOOL sentAfterLogin = sendAfterLogin; + + if (session == nil) + { + // Login first + self.sendAfterLogin = YES; + [self getSession:[self getAuthValueForKey:@"email"] + password:[self getAuthValueForKey:@"password"]]; + } + + else + { + + self.sendAfterLogin = NO; + + self.request = [[[SHKRequest alloc] initWithURL:[NSURL URLWithString: + [NSString stringWithFormat: + @"http://www.google.com/reader/api/0/token?ck=%i", + [[NSDate date] timeIntervalSince1970] + ]] + params:nil + delegate:self + isFinishedSelector:@selector(tokenFinished:) + method:@"GET" + autostart:NO] autorelease]; + [self signRequest:request]; + [request start]; + } + + // Notify delegate + if (!sentAfterLogin) + [self sendDidStart]; + + return YES; + } + + return NO; +} + +- (void)tokenFinished:(SHKRequest *)aRequest +{ + if (aRequest.success) + [self sendWithToken:[request getResult]]; + + else + [self sendDidFailWithError:[SHK error:SHKLocalizedString(@"There was a problem authenticating your account.")]]; // TODO better error handling/message +} + +- (void)sendWithToken:(NSString *)token +{ + NSString *params = [NSMutableString stringWithFormat:@"T=%@&linkify=false&snippet=%@&srcTitle=%@&srcUrl=%@&title=%@&url=%@&share=%@", + token, + SHKEncode(item.text), + SHKEncode(SHKMyAppName), + SHKEncode(SHKMyAppURL), + SHKEncode(item.title), + SHKEncodeURL(item.URL), + [item customBoolForSwitchKey:@"share"]?@"true":@"" + ]; + + self.request = [[[SHKRequest alloc] initWithURL:[NSURL URLWithString:@"https://www.google.com/reader/api/0/item/edit"] + params:params + delegate:self + isFinishedSelector:@selector(sendFinished:) + method:@"POST" + autostart:NO] autorelease]; + + [self signRequest:request]; + [request start]; +} + +- (void)sendFinished:(SHKRequest *)aRequest +{ + if (aRequest.success) + [self sendDidFinish]; + + else + [self sendDidFailWithError:[SHK error:SHKLocalizedString(@"There was a problem saving your note.")]]; // TODO better error handling/message +} + + +@end diff --git a/Classes/ShareKit/Sharers/Services/Instapaper/SHKInstapaper.h b/Classes/ShareKit/Sharers/Services/Instapaper/SHKInstapaper.h new file mode 100755 index 00000000..5c769162 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Instapaper/SHKInstapaper.h @@ -0,0 +1,34 @@ +// +// SHKInstapaper.h +// ShareKit +// +// Created by Sean Murphy on 7/8/10. +// +// 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 "SHKSharer.h" + +@interface SHKInstapaper : SHKSharer { + +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Instapaper/SHKInstapaper.m b/Classes/ShareKit/Sharers/Services/Instapaper/SHKInstapaper.m new file mode 100755 index 00000000..437c94bc --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Instapaper/SHKInstapaper.m @@ -0,0 +1,166 @@ +// +// SHKInstapaper.m +// ShareKit +// +// Created by Sean Murphy on 7/8/10. +// +// 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 "SHKInstapaper.h" + +static NSString * const kInstapaperAuthenticationURL = @"https://www.instapaper.com/api/authenticate"; +static NSString * const kInstapaperSharingURL = @"https://www.instapaper.com/api/add"; + +@implementation SHKInstapaper + +#pragma mark - +#pragma mark Configuration : Service Defination + ++ (NSString *)sharerTitle +{ + return @"Instapaper"; +} + ++ (BOOL)canShareURL +{ + return YES; +} + +#pragma mark - +#pragma mark Configuration : Dynamic Enable + ++ (BOOL)canShare +{ + return YES; +} + +#pragma mark - +#pragma mark Authorization + ++ (NSString *)authorizationFormCaption +{ + return SHKLocalizedString(@"Create a free account at %@", @"Instapaper.com"); +} + +- (void)authorizationFormValidate:(SHKFormController *)form +{ + // Display an activity indicator + if (!quiet) + [[SHKActivityIndicator currentIndicator] displayActivity:SHKLocalizedString(@"Logging In...")]; + + + // Authorize the user through the server + NSDictionary *formValues = [form formValues]; + + NSString *params = [NSMutableString stringWithFormat:@"username=%@&password=%@", + SHKEncode([formValues objectForKey:@"username"]), + SHKEncode([formValues objectForKey:@"password"]) + ]; + + self.request = [[[SHKRequest alloc] initWithURL:[NSURL URLWithString:kInstapaperAuthenticationURL] + params:params + delegate:self + isFinishedSelector:@selector(authFinished:) + method:@"POST" + autostart:YES] autorelease]; + + self.pendingForm = form; +} + +- (void)authFinished:(SHKRequest *)aRequest +{ + [[SHKActivityIndicator currentIndicator] hide]; + + if (aRequest.success) + [pendingForm saveForm]; + + else { + NSString *errorMessage = nil; + if (aRequest.response.statusCode == 403) + errorMessage = SHKLocalizedString(@"Sorry, Instapaper did not accept your credentials. Please try again."); + else + errorMessage = SHKLocalizedString(@"Sorry, Instapaper encountered an error. Please try again."); + + [[[[UIAlertView alloc] initWithTitle:SHKLocalizedString(@"Login Error") + message:errorMessage + delegate:nil + cancelButtonTitle:SHKLocalizedString(@"Close") + otherButtonTitles:nil] autorelease] show]; + } +} + +#pragma mark - +#pragma mark Share Form + +- (NSArray *)shareFormFieldsForType:(SHKShareType)type +{ + // Instapaper will automatically obtain a title for the URL, so we do not need + // any other information. + return nil; +} + +#pragma mark - +#pragma mark Share API Methods + +- (BOOL)send +{ + if ([self validateItem]) { + NSString *params = [NSMutableString stringWithFormat:@"url=%@&username=%@&password=%@", + SHKEncodeURL(self.item.URL), + SHKEncode([self getAuthValueForKey:@"username"]), + SHKEncode([self getAuthValueForKey:@"password"])]; + + self.request = [[[SHKRequest alloc] initWithURL:[NSURL URLWithString:kInstapaperSharingURL] + params:params + delegate:self + isFinishedSelector:@selector(sendFinished:) + method:@"POST" + autostart:YES] autorelease]; + + // Notify delegate + [self sendDidStart]; + + return YES; + } + + return NO; +} + +- (void)sendFinished:(SHKRequest *)aRequest +{ + if (!aRequest.success) { + if (aRequest.response.statusCode == 403) { + [self sendDidFailWithError:[SHK error:SHKLocalizedString(@"Sorry, Instapaper did not accept your credentials. Please try again.")] shouldRelogin:YES]; + return; + } + else if (aRequest.response.statusCode == 500) { + [self sendDidFailWithError:[SHK error:SHKLocalizedString(@"The service encountered an error. Please try again later.")]]; + return; + } + + [self sendDidFailWithError:[SHK error:SHKLocalizedString(@"There was a problem saving to Instapaper.")]]; + return; + } + + [self sendDidFinish]; +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Licorize/SHKLicorize.h b/Classes/ShareKit/Sharers/Services/Licorize/SHKLicorize.h new file mode 100644 index 00000000..eabb0e65 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Licorize/SHKLicorize.h @@ -0,0 +1,25 @@ +// +// SHKLicorize.h +// ShareKit +// +// Created by Federico Soldani on 11/13/10. + +#import +#import "SHKOAuthSharer.h" + +@interface SHKLicorize : SHKOAuthSharer { + BOOL xAuth; +} + +@property BOOL xAuth; + +#pragma mark - +#pragma mark Share API Methods + +- (void)remindMeLater; +- (void)saveStrip; + +- (void)sendTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data; +- (void)sendTicket:(OAServiceTicket *)ticket didFailWithError:(NSError *)error; + +@end diff --git a/Classes/ShareKit/Sharers/Services/Licorize/SHKLicorize.m b/Classes/ShareKit/Sharers/Services/Licorize/SHKLicorize.m new file mode 100644 index 00000000..4f8b682d --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Licorize/SHKLicorize.m @@ -0,0 +1,297 @@ +// +// SHKLicorize.h +// ShareKit +// +// Created by Federico Soldani on 11/13/10. + +#import "SHKLicorize.h" + +@interface SHKLicorize () +NSString * StringOrDefault(NSString * value, NSString * defaultValue); +NSString * UrlOrBlank(NSURL * value); +BOOL SendDidSuccess(NSData * data); +@end + +@implementation SHKLicorize + +@synthesize xAuth; + +#pragma mark - +#pragma mark Configuration : Service Defination + +// Enter the name of the service ++ (NSString *)sharerTitle { + return @"Licorize"; +} + ++ (BOOL)canShareURL { + return YES; +} + ++ (BOOL)canShareText { + return YES; +} + +#pragma mark - +#pragma mark Configuration : Dynamic Enable + +// Subclass if you need to dynamically enable/disable the service. (For example if it only works with specific hardware) ++ (BOOL)canShare { + return YES; +} + +#pragma mark - +#pragma mark Authorization + +- (BOOL)isAuthorized { + return [self restoreAccessToken]; +} + +- (void)promptAuthorization { + if (xAuth) { + [super authorizationFormShow]; // xAuth process + } else { + [super promptAuthorization]; // OAuth process + } +} + + +#pragma mark xAuth + ++ (NSString *)authorizationFormCaption { + return SHKLocalizedString(@"For a free account sign up at %@", @"Licorize.com"); +} + +- (void)authorizationFormValidate:(SHKFormController *)form { + self.pendingForm = form; + [self tokenAccess]; +} + +#pragma mark - +#pragma mark Authentication + +- (id)init { + if (self = [super init]) { + // OAUTH + self.consumerKey = SHKLicorizeConsumerKey; + self.secretKey = SHKLicorizeSecret; + self.authorizeCallbackURL = [NSURL URLWithString:SHKLicorizeCallbackUrl]; + + // XAUTH + self.xAuth = SHKLicorizeUseXAuth ? YES : NO; + + + // -- // + + + // You do not need to edit these, they are the same for everyone + self.requestURL = [NSURL URLWithString:@"http://api.licorize.com/oauth/request_token"]; + self.authorizeURL = [NSURL URLWithString:@"http://api.licorize.com/oauth/authorize"]; + self.accessURL = [NSURL URLWithString:@"http://api.licorize.com/oauth/access_token"]; + + self.signatureProvider = [[[OAHMAC_SHA1SignatureProvider alloc] init] autorelease]; + } + return self; +} + +- (void)tokenAccessModifyRequest:(OAMutableURLRequest *)oRequest { + if (xAuth) { + NSDictionary *formValues = [pendingForm formValues]; + + OARequestParameter *username = [[[OARequestParameter alloc] initWithName:@"x_auth_username" + value:[formValues objectForKey:@"username"]] autorelease]; + + OARequestParameter *password = [[[OARequestParameter alloc] initWithName:@"x_auth_password" + value:[formValues objectForKey:@"password"]] autorelease]; + + OARequestParameter *mode = [[[OARequestParameter alloc] initWithName:@"x_auth_mode" + value:@"client_auth"] autorelease]; + + [oRequest setParameters:[NSArray arrayWithObjects:username, password, mode, nil]]; + } +} + +// Validate the user input on the share form +- (void)shareFormValidate:(SHKCustomFormController *)form { + /* + + Services should subclass this if they need to validate any data before sending. + You can get a dictionary of the field values from [form formValues] + + -- + + You should perform one of the following actions: + + 1. Save the form - If everything is correct call [form saveForm] + + 2. Display an error - If the user input was incorrect, display an error to the user and tell them what to do to fix it + + + */ + + // default does no checking and proceeds to share + [form saveForm]; +} + +#pragma mark - +#pragma mark Implementation + +// Send the share item to the server +- (BOOL)send { + if (![self validateItem]) + return NO; + + if ( (item.shareType == SHKShareTypeURL) || (item.shareType == SHKShareTypeText)) { + if ( item.shareType == SHKShareTypeURL ) { + [self remindMeLater]; + } else { + [self saveStrip]; + } + + // Notify delegate + [self sendDidStart]; + + return YES; + } else { + return NO; + } +} + +- (void)remindMeLater { + OAMutableURLRequest *oRequest = [[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://api.licorize.com/1/strips/remindMeLater.json"] + consumer:consumer + token:accessToken + realm:nil + signatureProvider:nil]; + + [oRequest setHTTPMethod:@"POST"]; + + OARequestParameter *urlParam = [[OARequestParameter alloc] initWithName:@"url" + value:UrlOrBlank(item.URL)]; + OARequestParameter *titleParam = [[OARequestParameter alloc] initWithName:@"title" + value:SHKStringOrBlank(item.title)]; + + NSArray *params = [NSArray arrayWithObjects:urlParam, titleParam, nil]; + [oRequest setParameters:params]; + [urlParam release]; + [titleParam release]; + + OAAsynchronousDataFetcher *fetcher = [OAAsynchronousDataFetcher asynchronousFetcherWithRequest:oRequest + delegate:self + didFinishSelector:@selector(sendTicket:didFinishWithData:) + didFailSelector:@selector(sendTicket:didFailWithError:)]; + + [fetcher start]; + [oRequest release]; +} + +- (void)saveStrip { + OAMutableURLRequest *oRequest = [[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://api.licorize.com/1/strips/update.json"] + consumer:consumer + token:accessToken + realm:nil + signatureProvider:nil]; + + [oRequest setHTTPMethod:@"POST"]; + + OARequestParameter *urlParam = [[OARequestParameter alloc] initWithName:@"url" + value:UrlOrBlank(item.URL)]; + OARequestParameter *titleParam = [[OARequestParameter alloc] initWithName:@"title" + value:SHKStringOrBlank(item.title)]; + OARequestParameter *textParam = [[OARequestParameter alloc] initWithName:@"notes" + value:SHKStringOrBlank(item.text)]; + OARequestParameter *typeParam = [[OARequestParameter alloc] initWithName:@"type" + value:StringOrDefault([item customValueForKey:@"type"], @"NOTE")]; + OARequestParameter *tagsParam = [[OARequestParameter alloc] initWithName:@"tags" + value:SHKStringOrBlank(item.tags)]; + + NSArray *params = [NSArray arrayWithObjects:urlParam, titleParam, textParam, typeParam, tagsParam, nil]; + [oRequest setParameters:params]; + [urlParam release]; + [titleParam release]; + [textParam release]; + [typeParam release]; + [tagsParam release]; + + OAAsynchronousDataFetcher *fetcher = [OAAsynchronousDataFetcher asynchronousFetcherWithRequest:oRequest + delegate:self + didFinishSelector:@selector(sendTicket:didFinishWithData:) + didFailSelector:@selector(sendTicket:didFailWithError:)]; + + [fetcher start]; + [oRequest release]; +} + +- (void)sendTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data { + if (ticket.didSucceed && SendDidSuccess(data)) { + [self sendDidFinish]; + } else { + NSString *string = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; + + if (SHKDebugShowLogs) + SHKLog(@"Licorize RemindMeLater Error: %@", string); + + // in case our makeshift parsing does not yield an error message + NSString *errorMessage = @"Unknown Error"; + + NSScanner *scanner = [NSScanner scannerWithString:string]; + + // skip until error message + [scanner scanUpToString:@"\"message\":\"" intoString:nil]; + + + if ([scanner scanString:@"\"message\":\"" intoString:nil]) { + // get the message until the closing double quotes + [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\""] intoString:&errorMessage]; + } + + + // this is the error message for revoked access + if ([errorMessage isEqualToString:@"Invalid / used nonce"]) { + [self sendDidFailShouldRelogin]; + } else { + NSError *error = [NSError errorWithDomain:@"Licorize" code:2 userInfo:[NSDictionary dictionaryWithObject:errorMessage forKey:NSLocalizedDescriptionKey]]; + [self sendDidFailWithError:error]; + } + } +} + +- (void)sendTicket:(OAServiceTicket *)ticket didFailWithError:(NSError *)error { + [self sendDidFailWithError:error]; +} + +#pragma mark - +#pragma mark Utilities + +NSString * StringOrDefault(NSString * value, NSString * defaultValue) { + return value == nil ? defaultValue : value; +} + +NSString * UrlOrBlank(NSURL * value) { + return value == nil ? @"" : [value absoluteString]; +} + +BOOL SendDidSuccess(NSData * data) { + NSString *string = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; + + NSScanner *scanner = [NSScanner scannerWithString:string]; + + NSString *success = @"true"; + + // skip until success message + [scanner scanUpToString:@"\"ok\":" intoString:nil]; + + if ([scanner scanString:@"\"ok\":" intoString:nil]) { + // get the message until the closing double quotes + [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@","] intoString:&success]; + } + + if([success isEqualToString:@"true"]) { + return YES; + } else { + return NO; + } + +} + +@end \ No newline at end of file diff --git a/Classes/ShareKit/Sharers/Services/Pinboard/SHKPinboard.h b/Classes/ShareKit/Sharers/Services/Pinboard/SHKPinboard.h new file mode 100644 index 00000000..6995bb28 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Pinboard/SHKPinboard.h @@ -0,0 +1,36 @@ +// +// SHKPinboard.h +// ShareKit +// +// Created by Nathan Weiner on 6/21/10. + +// +// 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 "SHKSharer.h" + +@interface SHKPinboard : SHKSharer +{ + +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Pinboard/SHKPinboard.m b/Classes/ShareKit/Sharers/Services/Pinboard/SHKPinboard.m new file mode 100644 index 00000000..b0d4911a --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Pinboard/SHKPinboard.m @@ -0,0 +1,173 @@ +// +// SHKPinboard.m +// ShareKit +// +// Created by Nathan Weiner on 6/21/10. + +// +// 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 "SHKPinboard.h" + + +@implementation SHKPinboard + + + +#pragma mark - +#pragma mark Configuration : Service Defination + ++ (NSString *)sharerTitle +{ + return @"Pinboard"; +} + ++ (BOOL)canShareURL +{ + return YES; +} + + +#pragma mark - +#pragma mark Authorization + ++ (NSString *)authorizationFormCaption +{ + return SHKLocalizedString(@"Create an account at %@", @"http://pinboard.in"); +} + +- (void)authorizationFormValidate:(SHKFormController *)form +{ + // Display an activity indicator + if (!quiet) + [[SHKActivityIndicator currentIndicator] displayActivity:SHKLocalizedString(@"Logging In...")]; + + + // Authorize the user through the server + NSDictionary *formValues = [form formValues]; + + self.request = [[[SHKRequest alloc] initWithURL:[NSURL URLWithString: + [NSString stringWithFormat:@"https://%@:%@@api.pinboard.in/v1/posts/get", + SHKEncode([formValues objectForKey:@"username"]), + SHKEncode([formValues objectForKey:@"password"]) + ]] + params:nil + delegate:self + isFinishedSelector:@selector(authFinished:) + method:@"POST" + autostart:YES] autorelease]; + + self.pendingForm = form; +} + +- (BOOL)handleResponse:(SHKRequest *)aRequest +{ + NSString *response = [aRequest getResult]; + + if ([response isEqualToString:SHKLocalizedString(@"401 Forbidden")]) + { + [self sendDidFailShouldRelogin]; + return NO; + } + + return YES; +} + +- (void)authFinished:(SHKRequest *)aRequest +{ + // Hide the activity indicator + [[SHKActivityIndicator currentIndicator] hide]; + + if ([self handleResponse:aRequest]) + { + [pendingForm saveForm]; + } +} + + +#pragma mark - +#pragma mark Share Form + +- (NSArray *)shareFormFieldsForType:(SHKShareType)type +{ + if (type == SHKShareTypeURL) + return [NSArray arrayWithObjects: + [SHKFormFieldSettings label:SHKLocalizedString(@"Title") key:@"title" type:SHKFormFieldTypeText start:item.title], + [SHKFormFieldSettings label:SHKLocalizedString(@"Tags") key:@"tags" type:SHKFormFieldTypeText start:item.tags], + [SHKFormFieldSettings label:SHKLocalizedString(@"Notes") key:@"text" type:SHKFormFieldTypeText start:item.text], + [SHKFormFieldSettings label:SHKLocalizedString(@"Shared") key:@"shared" type:SHKFormFieldTypeSwitch start:SHKFormFieldSwitchOff], + nil]; + + return nil; +} + + + +#pragma mark - +#pragma mark Share API Methods + +- (BOOL)send +{ + if ([self validateItem]) + { + self.request = [[[SHKRequest alloc] initWithURL:[NSURL URLWithString: + [NSString stringWithFormat:@"https://%@:%@@api.pinboard.in/v1/posts/add?url=%@&description=%@&tags=%@&extended=%@&shared=%@", + SHKEncode([self getAuthValueForKey:@"username"]), + SHKEncode([self getAuthValueForKey:@"password"]), + SHKEncodeURL(item.URL), + SHKEncode(item.title), + SHKEncode(item.tags), + SHKEncode(item.text), + [item customBoolForSwitchKey:@"shared"]?@"yes":@"no" + ]] + params:nil + delegate:self + isFinishedSelector:@selector(sendFinished:) + method:@"GET" + autostart:YES] autorelease]; + + + // Notify delegate + [self sendDidStart]; + + return YES; + } + + return NO; +} + +- (void)sendFinished:(SHKRequest *)aRequest +{ + if ([self handleResponse:aRequest]) + { + // TODO parse +#import "SHKSharer.h" + +@interface SHKReadItLater : SHKSharer +{ +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Read It Later/SHKReadItLater.m b/Classes/ShareKit/Sharers/Services/Read It Later/SHKReadItLater.m new file mode 100644 index 00000000..c9e42584 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Read It Later/SHKReadItLater.m @@ -0,0 +1,188 @@ + // +// SHKReadItLater.m +// ShareKit +// +// Created by Nathan Weiner on 6/8/10. + +// +// 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. +// +// + +// SHOULD FUNCS - can these be implemented like dataSource and delegate on tableview? + +#import "SHKReadItLater.h" + + +@implementation SHKReadItLater + + +#pragma mark - +#pragma mark Configuration : Service Defination + ++ (NSString *)sharerTitle +{ + return @"Read It Later"; +} + ++ (BOOL)canShareURL +{ + return YES; +} + + +#pragma mark - +#pragma mark Configuration : Dynamic Enable + +// Though manual sharing is supported (by changing removing this subclass), one tap to save is the ideal 'read later' behavior +- (BOOL)shouldAutoShare +{ + return YES; +} + + + +#pragma mark - +#pragma mark Authorization + ++ (NSString *)authorizationFormCaption +{ + return SHKLocalizedString(@"Create a free account at %@", @"Readitlaterlist.com"); +} + +- (void)authorizationFormValidate:(SHKFormController *)form +{ + // Display an activity indicator + if (!quiet) + [[SHKActivityIndicator currentIndicator] displayActivity:SHKLocalizedString(@"Logging In...")]; + + + // Authorize the user through the server + NSDictionary *formValues = [form formValues]; + + NSString *params = [NSMutableString stringWithFormat:@"apikey=%@&username=%@&password=%@", + SHKReadItLaterKey, + SHKEncode([formValues objectForKey:@"username"]), + SHKEncode([formValues objectForKey:@"password"]) + ]; + + self.request = [[[SHKRequest alloc] initWithURL:[NSURL URLWithString:@"http://readitlaterlist.com/v2/auth"] + params:params + delegate:self + isFinishedSelector:@selector(authFinished:) + method:@"POST" + autostart:YES] autorelease]; + + self.pendingForm = form; +} + +- (void)authFinished:(SHKRequest *)aRequest +{ + // Hide the activity indicator + [[SHKActivityIndicator currentIndicator] hide]; + + if (aRequest.success) + [pendingForm saveForm]; + + else + { + [[[[UIAlertView alloc] initWithTitle:SHKLocalizedString(@"Login Error") + message:[request.headers objectForKey:@"X-Error"] + delegate:nil + cancelButtonTitle:SHKLocalizedString(@"Close") + otherButtonTitles:nil] autorelease] show]; + } +} + + + +#pragma mark - +#pragma mark Share Form + +- (NSArray *)shareFormFieldsForType:(SHKShareType)type +{ + if (type == SHKShareTypeURL) + return [NSArray arrayWithObjects: + [SHKFormFieldSettings label:SHKLocalizedString(@"Title") key:@"title" type:SHKFormFieldTypeText start:item.title], + [SHKFormFieldSettings label:SHKLocalizedString(@"Tags") key:@"tags" type:SHKFormFieldTypeText start:item.tags], + nil]; + + return nil; +} + + +#pragma mark - +#pragma mark Share API Methods + +- (BOOL)send +{ + if ([self validateItem]) + { + NSString *new = [NSString stringWithFormat:@"&new={\"0\":{\"url\":\"%@\",\"title\":\"%@\"}}", + SHKEncodeURL(item.URL), + SHKEncode(item.title)]; + + NSString *tags = item.tags == nil || !item.tags.length ? @"" : + [NSString stringWithFormat:@"&update_tags={\"0\":{\"url\":\"%@\",\"tags\":\"%@\"}}", + SHKEncodeURL(item.URL), SHKEncode(item.tags)]; + + NSString *params = [NSMutableString stringWithFormat:@"apikey=%@&username=%@&password=%@%@%@", + SHKReadItLaterKey, + SHKEncode([self getAuthValueForKey:@"username"]), + SHKEncode([self getAuthValueForKey:@"password"]), + new, + tags]; + + self.request = [[[SHKRequest alloc] initWithURL:[NSURL URLWithString:@"http://readitlaterlist.com/v2/send"] + params:params + delegate:self + isFinishedSelector:@selector(sendFinished:) + method:@"POST" + autostart:YES] autorelease]; + + + // Notify delegate + [self sendDidStart]; + + return YES; + } + + return NO; +} + +- (void)sendFinished:(SHKRequest *)aRequest +{ + if (!aRequest.success) + { + if (aRequest.response.statusCode == 401) + { + [self sendDidFailShouldRelogin]; + return; + } + + [self sendDidFailWithError:[SHK error:[request.headers objectForKey:@"X-Error"]]]; + return; + } + + [self sendDidFinish]; +} + + + +@end diff --git a/Classes/ShareKit/Sharers/Services/Tumblr/SHKTumblr.h b/Classes/ShareKit/Sharers/Services/Tumblr/SHKTumblr.h new file mode 100644 index 00000000..3ba93c4b --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Tumblr/SHKTumblr.h @@ -0,0 +1,18 @@ +// +// SHKTumblr.h +// ShareKit +// +// Created by Jamie Pinkham on 7/10/10. +// Copyright 2010 Mobelux. All rights reserved. +// + +#import +#import "SHKSharer.h" + +@interface SHKTumblr : SHKSharer { + //for photo posts + NSMutableData *data; + NSHTTPURLResponse *response; +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Tumblr/SHKTumblr.m b/Classes/ShareKit/Sharers/Services/Tumblr/SHKTumblr.m new file mode 100644 index 00000000..aa751e9d --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Tumblr/SHKTumblr.m @@ -0,0 +1,367 @@ +// +// SHKTumblr.m +// ShareKit +// +// Created by Jamie Pinkham on 7/10/10. +// Copyright 2010 Mobelux. All rights reserved. +// + +#import "SHKTumblr.h" + +static NSString * const kTumblrAuthenticationURL = @"https://www.tumblr.com/api/authenticate"; +static NSString * const kTumblrWriteURL = @"https://www.tumblr.com/api/write"; + +@interface SHKTumblr() +- (void)finish; +@end + +@implementation SHKTumblr + +#pragma mark - +#pragma mark Memory management +- (void)dealloc{ + [data release]; + [response release]; + [super dealloc]; +} + +#pragma mark - +#pragma mark Configuration : Service Defination + ++ (NSString *)sharerTitle{ + return @"Tumblr"; +} + ++ (BOOL)canShareURL{ + return YES; +} + ++ (BOOL)canShareText{ + return YES; +} + ++ (BOOL)canShareImage{ + return YES; +} + +#pragma mark - +#pragma mark Configuration : Dynamic Enable + ++ (BOOL)canShare{ + return YES; +} + +- (BOOL)shouldAutoShare{ + return NO; +} + +#pragma mark - +#pragma mark Authorization + +- (NSString *)authorizationFormCaption{ + return SHKLocalizedString(@"Set up a free account at %@", @"Tumblr.com"); +} + +- (void)authorizationFormValidate:(SHKFormController *)form{ + // Display an activity indicator + if (!quiet) + [[SHKActivityIndicator currentIndicator] displayActivity:SHKLocalizedString(@"Logging In...")]; + + + // Authorize the user through the server + NSDictionary *formValues = [form formValues]; + + NSString *params = [NSMutableString stringWithFormat:@"email=%@&password=%@", + SHKEncode([formValues objectForKey:@"email"]), + SHKEncode([formValues objectForKey:@"password"]) + ]; + + self.request = [[[SHKRequest alloc] initWithURL:[NSURL URLWithString:kTumblrAuthenticationURL] + params:params + delegate:self + isFinishedSelector:@selector(authFinished:) + method:@"POST" + autostart:YES] autorelease]; + + self.pendingForm = form; +} + +- (void)authFinished:(SHKRequest *)aRequest{ + [[SHKActivityIndicator currentIndicator] hide]; + if (aRequest.success) + [pendingForm saveForm]; + + else { + NSString *errorMessage = nil; + if (aRequest.response.statusCode == 403) + errorMessage = SHKLocalizedString(@"Invalid email or password."); + else + errorMessage = SHKLocalizedString(@"The service encountered an error. Please try again later."); + + [[[[UIAlertView alloc] initWithTitle:SHKLocalizedString(@"Login Error") + message:errorMessage + delegate:nil + cancelButtonTitle:SHKLocalizedString(@"Close") + otherButtonTitles:nil] autorelease] show]; + } +} + +#pragma mark - +#pragma mark Authorize form +- (NSArray *)authorizationFormFields{ + return [NSArray arrayWithObjects: + [SHKFormFieldSettings label:SHKLocalizedString(@"Email") + key:@"email" + type:SHKFormFieldTypeText + start:nil], + [SHKFormFieldSettings label:SHKLocalizedString(@"Password") + key:@"password" + type:SHKFormFieldTypePassword + start:nil], + nil]; +} + +#pragma mark - +#pragma mark Share Form + +- (NSArray *)shareFormFieldsForType:(SHKShareType)type{ + NSMutableArray *baseArray = [NSMutableArray arrayWithObjects: + [SHKFormFieldSettings label:SHKLocalizedString(@"Tags") + key:@"tags" + type:SHKFormFieldTypeText + start:item.tags], + [SHKFormFieldSettings label:SHKLocalizedString(@"Slug") + key:@"slug" + type:SHKFormFieldTypeText + start:nil], + [SHKFormFieldSettings label:SHKLocalizedString(@"Private") + key:@"private" + type:SHKFormFieldTypeSwitch + start:SHKFormFieldSwitchOff], + [SHKFormFieldSettings label:SHKLocalizedString(@"Send to Twitter") + key:@"twitter" + type:SHKFormFieldTypeSwitch + start:SHKFormFieldSwitchOff], + nil + ]; + if([item shareType] == SHKShareTypeImage){ + [baseArray insertObject:[SHKFormFieldSettings label:SHKLocalizedString(@"Caption") + key:@"caption" + type:SHKFormFieldTypeText + start:nil] + atIndex:0]; + }else{ + [baseArray insertObject:[SHKFormFieldSettings label:SHKLocalizedString(@"Title") + key:@"title" + type:SHKFormFieldTypeText + start:item.title] + atIndex:0]; + } + return baseArray; +} + +#pragma mark - +#pragma mark Share API Methods + +- (BOOL)send{ + if ([self validateItem]) { + if([item shareType] == SHKShareTypeText || [item shareType] == SHKShareTypeURL){ + NSMutableString *params = [NSMutableString stringWithFormat:@"email=%@&password=%@", + SHKEncode([self getAuthValueForKey:@"email"]), + SHKEncode([self getAuthValueForKey:@"password"])]; + + //set send to twitter param + if([item customBoolForSwitchKey:@"twitter"]){ + [params appendFormat:@"&send-to-twitter=auto"]; + }else{ + [params appendFormat:@"&send-to-twitter=no"]; + } + + //set tags param + NSString *tags = [item tags]; + if(tags){ + [params appendFormat:@"&tags=%@",[item tags]]; + } + + //set slug param + NSString *slug = [item customValueForKey:@"slug"]; + if(slug){ + [params appendFormat:@"&slug=%@", slug]; + } + + //set private param + if([item customBoolForSwitchKey:@"private"]){ + [params appendFormat:@"&private=1"]; + }else{ + [params appendFormat:@"&private=0"]; + } + + //set type param + if ([item shareType] == SHKShareTypeURL){ + [params appendString:@"&type=link"]; + [params appendFormat:@"&url=%@",SHKEncodeURL([item URL])]; + if([item title]){ + [params appendFormat:@"&name=%@", SHKEncode([item title])]; + } + }else{ + [params appendString:@"&type=regular"]; + if([item title]){ + [params appendFormat:@"&title=%@", SHKEncode([item title])]; + } + [params appendFormat:@"&body=%@", SHKEncode([item text])]; + } + self.request = [[[SHKRequest alloc] initWithURL:[NSURL URLWithString:kTumblrWriteURL] + params:params + delegate:self + isFinishedSelector:@selector(sendFinished:) + method:@"POST" + autostart:YES] autorelease]; + } + else if([item shareType] == SHKShareTypeImage){ + + NSData *imageData = UIImageJPEGRepresentation([item image], 0.9); + NSMutableURLRequest *aRequest = [[[NSMutableURLRequest alloc] init] autorelease]; + [aRequest setURL:[NSURL URLWithString:kTumblrWriteURL]]; + [aRequest setHTTPMethod:@"POST"]; + NSString *boundary = @"0xKhTmLbOuNdArY"; + //NSString *boundary = [NSString stringWithString:@"---------------------------14737809831466499882746641449"]; + NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary]; + [aRequest addValue:contentType forHTTPHeaderField: @"Content-Type"]; + + /* + now lets create the body of the post + */ + NSMutableData *body = [NSMutableData data]; + [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] + dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"Content-Disposition: form-data; name=\"email\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[[self getAuthValueForKey:@"email"] dataUsingEncoding:NSUTF8StringEncoding]]; + + [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] + dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"Content-Disposition: form-data; name=\"password\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[[self getAuthValueForKey:@"password"] dataUsingEncoding:NSUTF8StringEncoding]]; + + [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] + dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"Content-Disposition: form-data; name=\"type\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"photo" dataUsingEncoding:NSUTF8StringEncoding]]; + + if([item tags]){ + [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] + dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"Content-Disposition: form-data; name=\"tags\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[[item tags] dataUsingEncoding:NSUTF8StringEncoding]]; + } + if([item customValueForKey:@"caption"]){ + [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] + dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"Content-Disposition: form-data; name=\"caption\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[[item customValueForKey:@"caption"] dataUsingEncoding:NSUTF8StringEncoding]]; + + } + if([item customValueForKey:@"slug"]){ + [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] + dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"Content-Disposition: form-data; name=\"slug\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[[item customValueForKey:@"slug"] dataUsingEncoding:NSUTF8StringEncoding]]; + } + [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] + dataUsingEncoding:NSUTF8StringEncoding]]; + if([item customBoolForSwitchKey:@"private"]){ + [body appendData:[@"Content-Disposition: form-data; name=\"private\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"1" dataUsingEncoding:NSUTF8StringEncoding]]; + }else{ + [body appendData:[@"Content-Disposition: form-data; name=\"private\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"0" dataUsingEncoding:NSUTF8StringEncoding]]; + } + + [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] + dataUsingEncoding:NSUTF8StringEncoding]]; + if([item customBoolForSwitchKey:@"twitter"]){ + [body appendData:[@"Content-Disposition: form-data; name=\"twitter\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"auto" dataUsingEncoding:NSUTF8StringEncoding]]; + }else{ + [body appendData:[@"Content-Disposition: form-data; name=\"twitter\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"no" dataUsingEncoding:NSUTF8StringEncoding]]; + } + + [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"Content-Disposition: form-data; name=\"data\"; filename=\"upload.jpg\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"Content-Transfer-Encoding: image/jpg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:imageData]; + [body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + + // setting the body of the post to the reqeust + [aRequest setHTTPBody:body]; + [NSURLConnection connectionWithRequest:aRequest delegate:self]; + } + + + // Notify delegate + [self sendDidStart]; + + return YES; + } + + return NO; +} + +- (void)sendFinished:(SHKRequest *)aRequest{ + if (!aRequest.success) { + if (aRequest.response.statusCode == 403) { + [self sendDidFailWithError:[SHK error:SHKLocalizedString(@"Invalid email or password.")] shouldRelogin:YES]; + return; + } + else if (aRequest.response.statusCode == 500) { + [self sendDidFailWithError:[SHK error:SHKLocalizedString(@"The service encountered an error. Please try again later.")]]; + return; + } + + [self sendDidFailWithError:[SHK error:SHKLocalizedString(@"There was a sending your post to Tumblr.")]]; + return; + } + + [self sendDidFinish]; +} + +#pragma mark - +#pragma mark NSURLConnection delegate methods for image posts +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)theResponse { + [response release]; + response = [theResponse retain]; + + [data setLength:0]; +} + +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)d { + [data appendData:d]; +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection { + [self finish]; +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { + + [self finish]; +} + +- (void)finish{ + if(response.statusCode == 200 || response.statusCode == 201){ + [self sendDidFinish]; + }else{ + if(response.statusCode == 403) { + [self sendDidFailWithError:[SHK error:SHKLocalizedString(@"Invalid email or password.")] shouldRelogin:YES]; + return; + } + else if (response.statusCode == 500) { + [self sendDidFailWithError:[SHK error:SHKLocalizedString(@"The service encountered an error. Please try again later.")]]; + return; + } + [self sendDidFailWithError:[SHK error:SHKLocalizedString(@"There was a sending your post to Tumblr.")]]; + } + +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Twitter/SHKTwitter.h b/Classes/ShareKit/Sharers/Services/Twitter/SHKTwitter.h new file mode 100644 index 00000000..cbb02783 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Twitter/SHKTwitter.h @@ -0,0 +1,62 @@ +// +// SHKTwitter.h +// ShareKit +// +// Created by Nathan Weiner on 6/21/10. + +// +// 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 "SHKOAuthSharer.h" +#import "SHKTwitterForm.h" + +@interface SHKTwitter : SHKOAuthSharer +{ + BOOL xAuth; +} + +@property BOOL xAuth; + + +#pragma mark - +#pragma mark UI Implementation + +- (void)showTwitterForm; + +#pragma mark - +#pragma mark Share API Methods + +- (void)shortenURL; +- (void)shortenURLFinished:(SHKRequest *)aRequest; + +- (void)sendForm:(SHKTwitterForm *)form; + +- (void)sendStatus; +- (void)sendStatusTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data; +- (void)sendStatusTicket:(OAServiceTicket *)ticket didFailWithError:(NSError*)error; +- (void)sendImage; +- (void)sendImageTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data; +- (void)sendImageTicket:(OAServiceTicket *)ticket didFailWithError:(NSError*)error; + +- (void)followMe; + +@end diff --git a/Classes/ShareKit/Sharers/Services/Twitter/SHKTwitter.m b/Classes/ShareKit/Sharers/Services/Twitter/SHKTwitter.m new file mode 100644 index 00000000..c49b4a83 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Twitter/SHKTwitter.m @@ -0,0 +1,550 @@ +// +// SHKTwitter.m +// ShareKit +// +// Created by Nathan Weiner on 6/21/10. + +// +// 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. +// +// + +// TODO - SHKTwitter supports offline sharing, however the url cannot be shortened without an internet connection. Need a graceful workaround for this. + + +#import "SHKTwitter.h" + +@implementation SHKTwitter + +@synthesize xAuth; + +- (id)init +{ + if (self = [super init]) + { + // OAUTH + self.consumerKey = SHKTwitterConsumerKey; + self.secretKey = SHKTwitterSecret; + self.authorizeCallbackURL = [NSURL URLWithString:SHKTwitterCallbackUrl];// HOW-TO: In your Twitter application settings, use the "Callback URL" field. If you do not have this field in the settings, set your application type to 'Browser'. + + // XAUTH + self.xAuth = SHKTwitterUseXAuth?YES:NO; + + + // -- // + + + // You do not need to edit these, they are the same for everyone + self.authorizeURL = [NSURL URLWithString:@"https://twitter.com/oauth/authorize"]; + self.requestURL = [NSURL URLWithString:@"https://twitter.com/oauth/request_token"]; + self.accessURL = [NSURL URLWithString:@"https://twitter.com/oauth/access_token"]; + } + return self; +} + + +#pragma mark - +#pragma mark Configuration : Service Defination + ++ (NSString *)sharerTitle +{ + return @"Twitter"; +} + ++ (BOOL)canShareURL +{ + return YES; +} + ++ (BOOL)canShareText +{ + return YES; +} + +// TODO use img.ly to support this ++ (BOOL)canShareImage +{ + return YES; +} + + +#pragma mark - +#pragma mark Configuration : Dynamic Enable + +- (BOOL)shouldAutoShare +{ + return NO; +} + + +#pragma mark - +#pragma mark Authorization + +- (BOOL)isAuthorized +{ + return [self restoreAccessToken]; +} + +- (void)promptAuthorization +{ + if (xAuth) + [super authorizationFormShow]; // xAuth process + + else + [super promptAuthorization]; // OAuth process +} + + +#pragma mark xAuth + ++ (NSString *)authorizationFormCaption +{ + return SHKLocalizedString(@"Create a free account at %@", @"Twitter.com"); +} + ++ (NSArray *)authorizationFormFields +{ + if ([SHKTwitterUsername isEqualToString:@""]) + return [super authorizationFormFields]; + + return [NSArray arrayWithObjects: + [SHKFormFieldSettings label:SHKLocalizedString(@"Username") key:@"username" type:SHKFormFieldTypeText start:nil], + [SHKFormFieldSettings label:SHKLocalizedString(@"Password") key:@"password" type:SHKFormFieldTypePassword start:nil], + [SHKFormFieldSettings label:SHKLocalizedString(@"Follow %@", SHKTwitterUsername) key:@"followMe" type:SHKFormFieldTypeSwitch start:SHKFormFieldSwitchOn], + nil]; +} + +- (void)authorizationFormValidate:(SHKFormController *)form +{ + self.pendingForm = form; + [self tokenAccess]; +} + +- (void)tokenAccessModifyRequest:(OAMutableURLRequest *)oRequest +{ + if (xAuth) + { + NSDictionary *formValues = [pendingForm formValues]; + + OARequestParameter *username = [[[OARequestParameter alloc] initWithName:@"x_auth_username" + value:[formValues objectForKey:@"username"]] autorelease]; + + OARequestParameter *password = [[[OARequestParameter alloc] initWithName:@"x_auth_password" + value:[formValues objectForKey:@"password"]] autorelease]; + + OARequestParameter *mode = [[[OARequestParameter alloc] initWithName:@"x_auth_mode" + value:@"client_auth"] autorelease]; + + [oRequest setParameters:[NSArray arrayWithObjects:username, password, mode, nil]]; + } +} + +- (void)tokenAccessTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data +{ + if (xAuth) + { + if (ticket.didSucceed) + { + [item setCustomValue:[[pendingForm formValues] objectForKey:@"followMe"] forKey:@"followMe"]; + [pendingForm close]; + } + + else + { + NSString *response = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; + + SHKLog(@"tokenAccessTicket Response Body: %@", response); + + [self tokenAccessTicket:ticket didFailWithError:[SHK error:response]]; + return; + } + } + + [super tokenAccessTicket:ticket didFinishWithData:data]; +} + + +#pragma mark - +#pragma mark UI Implementation + +- (void)show +{ + if (item.shareType == SHKShareTypeURL) + { + [self shortenURL]; + } + + else if (item.shareType == SHKShareTypeImage) + { + [item setCustomValue:item.title forKey:@"status"]; + [self showTwitterForm]; + } + + else if (item.shareType == SHKShareTypeText) + { + [item setCustomValue:item.text forKey:@"status"]; + [self showTwitterForm]; + } +} + +- (void)showTwitterForm +{ + SHKTwitterForm *rootView = [[SHKTwitterForm alloc] initWithNibName:nil bundle:nil]; + rootView.delegate = self; + + // force view to load so we can set textView text + [rootView view]; + + rootView.textView.text = [item customValueForKey:@"status"]; + rootView.hasAttachment = item.image != nil; + + [self pushViewController:rootView animated:NO]; + + [[SHK currentHelper] showViewController:self]; +} + +- (void)sendForm:(SHKTwitterForm *)form +{ + [item setCustomValue:form.textView.text forKey:@"status"]; + [self tryToSend]; +} + + +#pragma mark - + +- (void)shortenURL +{ + if (![SHK connected]) + { + [item setCustomValue:[NSString stringWithFormat:@"%@ %@", item.title, [item.URL.absoluteString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] forKey:@"status"]; + [self showTwitterForm]; + return; + } + + if (!quiet) + [[SHKActivityIndicator currentIndicator] displayActivity:SHKLocalizedString(@"Shortening URL...")]; + + self.request = [[[SHKRequest alloc] initWithURL:[NSURL URLWithString:[NSMutableString stringWithFormat:@"http://api.bit.ly/v3/shorten?login=%@&apikey=%@&longUrl=%@&format=txt", + SHKBitLyLogin, + SHKBitLyKey, + SHKEncodeURL(item.URL) + ]] + params:nil + delegate:self + isFinishedSelector:@selector(shortenURLFinished:) + method:@"GET" + autostart:YES] autorelease]; +} + +- (void)shortenURLFinished:(SHKRequest *)aRequest +{ + [[SHKActivityIndicator currentIndicator] hide]; + + NSString *result = [[aRequest getResult] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; + + if (result == nil || [NSURL URLWithString:result] == nil) + { + // TODO - better error message + [[[[UIAlertView alloc] initWithTitle:SHKLocalizedString(@"Shorten URL Error") + message:SHKLocalizedString(@"We could not shorten the URL.") + delegate:nil + cancelButtonTitle:SHKLocalizedString(@"Continue") + otherButtonTitles:nil] autorelease] show]; + + [item setCustomValue:[NSString stringWithFormat:@"%@ %@", item.text ? item.text : item.title, [item.URL.absoluteString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] forKey:@"status"]; + } + + else + { + ///if already a bitly login, use url instead + if ([result isEqualToString:@"ALREADY_A_BITLY_LINK"]) + result = [item.URL.absoluteString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + + [item setCustomValue:[NSString stringWithFormat:@"%@ %@", item.text ? item.text : item.title, result] forKey:@"status"]; + } + + [self showTwitterForm]; +} + + +#pragma mark - +#pragma mark Share API Methods + +- (BOOL)validate +{ + NSString *status = [item customValueForKey:@"status"]; + return status != nil && status.length >= 0 && status.length <= 140; +} + +- (BOOL)send +{ + // Check if we should send follow request too + if (xAuth && [item customBoolForSwitchKey:@"followMe"]) + [self followMe]; + + if (![self validate]) + [self show]; + + else + { + if (item.shareType == SHKShareTypeImage) { + [self sendImage]; + } else { + [self sendStatus]; + } + + // Notify delegate + [self sendDidStart]; + + return YES; + } + + return NO; +} + +- (void)sendStatus +{ + OAMutableURLRequest *oRequest = [[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://api.twitter.com/1/statuses/update.json"] + consumer:consumer + token:accessToken + realm:nil + signatureProvider:nil]; + + [oRequest setHTTPMethod:@"POST"]; + + OARequestParameter *statusParam = [[OARequestParameter alloc] initWithName:@"status" + value:[item customValueForKey:@"status"]]; + NSArray *params = [NSArray arrayWithObjects:statusParam, nil]; + [oRequest setParameters:params]; + [statusParam release]; + + OAAsynchronousDataFetcher *fetcher = [OAAsynchronousDataFetcher asynchronousFetcherWithRequest:oRequest + delegate:self + didFinishSelector:@selector(sendStatusTicket:didFinishWithData:) + didFailSelector:@selector(sendStatusTicket:didFailWithError:)]; + + [fetcher start]; + [oRequest release]; +} + +- (void)sendStatusTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data +{ + // TODO better error handling here + + if (ticket.didSucceed) + [self sendDidFinish]; + + else + { + if (SHKDebugShowLogs) + SHKLog(@"Twitter Send Status Error: %@", [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]); + + // CREDIT: Oliver Drobnik + + NSString *string = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; + + // in case our makeshift parsing does not yield an error message + NSString *errorMessage = @"Unknown Error"; + + NSScanner *scanner = [NSScanner scannerWithString:string]; + + // skip until error message + [scanner scanUpToString:@"\"error\":\"" intoString:nil]; + + + if ([scanner scanString:@"\"error\":\"" intoString:nil]) + { + // get the message until the closing double quotes + [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\""] intoString:&errorMessage]; + } + + + // this is the error message for revoked access + if ([errorMessage isEqualToString:@"Invalid / used nonce"]) + { + [self sendDidFailShouldRelogin]; + } + else + { + NSError *error = [NSError errorWithDomain:@"Twitter" code:2 userInfo:[NSDictionary dictionaryWithObject:errorMessage forKey:NSLocalizedDescriptionKey]]; + [self sendDidFailWithError:error]; + } + } +} + +- (void)sendStatusTicket:(OAServiceTicket *)ticket didFailWithError:(NSError*)error +{ + [self sendDidFailWithError:error]; +} + +- (void)sendImage { + + NSURL *serviceURL = nil; + if([item customValueForKey:@"profile_update"]){ + serviceURL = [NSURL URLWithString:@"http://api.twitter.com/1/account/update_profile_image.json"]; + } else { + serviceURL = [NSURL URLWithString:@"https://api.twitter.com/1/account/verify_credentials.json"]; + } + + OAMutableURLRequest *oRequest = [[OAMutableURLRequest alloc] initWithURL:serviceURL + consumer:consumer + token:accessToken + realm:@"http://api.twitter.com/" + signatureProvider:signatureProvider]; + [oRequest setHTTPMethod:@"GET"]; + + if([item customValueForKey:@"profile_update"]){ + [oRequest prepare]; + } else { + [oRequest prepare]; + + NSDictionary * headerDict = [oRequest allHTTPHeaderFields]; + NSString * oauthHeader = [NSString stringWithString:[headerDict valueForKey:@"Authorization"]]; + + [oRequest release]; + oRequest = nil; + + serviceURL = [NSURL URLWithString:@"http://img.ly/api/2/upload.xml"]; + oRequest = [[OAMutableURLRequest alloc] initWithURL:serviceURL + consumer:consumer + token:accessToken + realm:@"http://api.twitter.com/" + signatureProvider:signatureProvider]; + [oRequest setHTTPMethod:@"POST"]; + [oRequest setValue:@"https://api.twitter.com/1/account/verify_credentials.json" forHTTPHeaderField:@"X-Auth-Service-Provider"]; + [oRequest setValue:oauthHeader forHTTPHeaderField:@"X-Verify-Credentials-Authorization"]; + } + + CGFloat compression = 0.9f; + NSData *imageData = UIImageJPEGRepresentation([item image], compression); + + // TODO + // Note from Nate to creator of sendImage method - This seems like it could be a source of sluggishness. + // For example, if the image is large (say 3000px x 3000px for example), it would be better to resize the image + // to an appropriate size (max of img.ly) and then start trying to compress. + + while ([imageData length] > 700000 && compression > 0.1) { + // NSLog(@"Image size too big, compression more: current data size: %d bytes",[imageData length]); + compression -= 0.1; + imageData = UIImageJPEGRepresentation([item image], compression); + + } + + NSString *boundary = @"0xKhTmLbOuNdArY"; + NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary]; + [oRequest setValue:contentType forHTTPHeaderField:@"Content-Type"]; + + NSMutableData *body = [NSMutableData data]; + NSString *dispKey = @""; + if([item customValueForKey:@"profile_update"]){ + dispKey = @"Content-Disposition: form-data; name=\"image\"; filename=\"upload.jpg\"\r\n"; + } else { + dispKey = @"Content-Disposition: form-data; name=\"media\"; filename=\"upload.jpg\"\r\n"; + } + + + [body appendData:[[NSString stringWithFormat:@"--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[dispKey dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"Content-Type: image/jpg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:imageData]; + [body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + + if([item customValueForKey:@"profile_update"]){ + // no ops + } else { + [body appendData:[[NSString stringWithFormat:@"--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[[NSString stringWithString:@"Content-Disposition: form-data; name=\"message\"\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[[item customValueForKey:@"status"] dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + } + + [body appendData:[[NSString stringWithFormat:@"--%@--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + + // setting the body of the post to the reqeust + [oRequest setHTTPBody:body]; + + // Notify delegate + [self sendDidStart]; + + // Start the request + OAAsynchronousDataFetcher *fetcher = [OAAsynchronousDataFetcher asynchronousFetcherWithRequest:oRequest + delegate:self + didFinishSelector:@selector(sendImageTicket:didFinishWithData:) + didFailSelector:@selector(sendImageTicket:didFailWithError:)]; + + [fetcher start]; + + + [oRequest release]; +} + +- (void)sendImageTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data { + // TODO better error handling here + // NSLog([[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]); + + if (ticket.didSucceed) { + [self sendDidFinish]; + // Finished uploading Image, now need to posh the message and url in twitter + NSString *dataString = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; + NSRange startingRange = [dataString rangeOfString:@"" options:NSCaseInsensitiveSearch]; + //NSLog(@"found start string at %d, len %d",startingRange.location,startingRange.length); + NSRange endingRange = [dataString rangeOfString:@"" options:NSCaseInsensitiveSearch]; + //NSLog(@"found end string at %d, len %d",endingRange.location,endingRange.length); + + if (startingRange.location != NSNotFound && endingRange.location != NSNotFound) { + NSString *urlString = [dataString substringWithRange:NSMakeRange(startingRange.location + startingRange.length, endingRange.location - (startingRange.location + startingRange.length))]; + //NSLog(@"extracted string: %@",urlString); + [item setCustomValue:[NSString stringWithFormat:@"%@ %@",[item customValueForKey:@"status"],urlString] forKey:@"status"]; + [self sendStatus]; + } + + + } else { + [self sendDidFailWithError:nil]; + } +} + +- (void)sendImageTicket:(OAServiceTicket *)ticket didFailWithError:(NSError*)error { + [self sendDidFailWithError:error]; +} + + +- (void)followMe +{ + // remove it so in case of other failures this doesn't get hit again + [item setCustomValue:nil forKey:@"followMe"]; + + OAMutableURLRequest *oRequest = [[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://api.twitter.com/1/friendships/create/%@.json", SHKTwitterUsername]] + consumer:consumer + token:accessToken + realm:nil + signatureProvider:nil]; + + [oRequest setHTTPMethod:@"POST"]; + + OAAsynchronousDataFetcher *fetcher = [OAAsynchronousDataFetcher asynchronousFetcherWithRequest:oRequest + delegate:nil // Currently not doing any error handling here. If it fails, it's probably best not to bug the user to follow you again. + didFinishSelector:nil + didFailSelector:nil]; + + [fetcher start]; + [oRequest release]; +} + +@end diff --git a/Classes/ShareKit/Sharers/Services/Twitter/SHKTwitterForm.h b/Classes/ShareKit/Sharers/Services/Twitter/SHKTwitterForm.h new file mode 100644 index 00000000..99cb3776 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Twitter/SHKTwitterForm.h @@ -0,0 +1,46 @@ +// +// SHKTwitterForm.h +// ShareKit +// +// Created by Nathan Weiner on 6/22/10. + +// +// 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 SHKTwitterForm : UIViewController +{ + id delegate; + UITextView *textView; + UILabel *counter; + BOOL hasAttachment; +} + +@property (nonatomic, retain) id delegate; +@property (nonatomic, retain) UITextView *textView; +@property (nonatomic, retain) UILabel *counter; +@property BOOL hasAttachment; + +- (void)layoutCounter; + +@end diff --git a/Classes/ShareKit/Sharers/Services/Twitter/SHKTwitterForm.m b/Classes/ShareKit/Sharers/Services/Twitter/SHKTwitterForm.m new file mode 100644 index 00000000..565aaa3c --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Twitter/SHKTwitterForm.m @@ -0,0 +1,233 @@ +// +// SHKTwitterForm.m +// ShareKit +// +// Created by Nathan Weiner on 6/22/10. + +// +// 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 "SHKTwitterForm.h" +#import "SHK.h" +#import "SHKTwitter.h" + + +@implementation SHKTwitterForm + +@synthesize delegate; +@synthesize textView; +@synthesize counter; +@synthesize hasAttachment; + +- (void)dealloc +{ + [delegate release]; + [textView release]; + [counter release]; + [super dealloc]; +} + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) + { + self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel + target:self + action:@selector(cancel)]; + + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Send to Twitter" + style:UIBarButtonItemStyleDone + target:self + action:@selector(save)]; + } + return self; +} + + + +- (void)loadView +{ + [super loadView]; + + self.view.backgroundColor = [UIColor whiteColor]; + + self.textView = [[UITextView alloc] initWithFrame:self.view.bounds]; + textView.delegate = self; + textView.font = [UIFont systemFontOfSize:15]; + textView.contentInset = UIEdgeInsetsMake(5,5,0,0); + textView.backgroundColor = [UIColor whiteColor]; + textView.autoresizesSubviews = YES; + textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + + [self.view addSubview:textView]; +} + +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear:animated]; + + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(keyboardWillShow:) name: UIKeyboardWillShowNotification object:nil]; + + [self.textView becomeFirstResponder]; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; + + // Remove observers + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc removeObserver:self name: UIKeyboardWillShowNotification object:nil]; + + // Remove the SHK view wrapper from the window + [[SHK currentHelper] viewWasDismissed]; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + return YES; +} + +- (void)keyboardWillShow:(NSNotification *)notification +{ + CGRect keyboardFrame; + CGFloat keyboardHeight; + + // 3.2 and above + /*if (UIKeyboardFrameEndUserInfoKey) + { + [[notification.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrame]; + if ([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortrait || [[UIDevice currentDevice] orientation] == UIDeviceOrientationPortraitUpsideDown) + keyboardHeight = keyboardFrame.size.height; + else + keyboardHeight = keyboardFrame.size.width; + } + + // < 3.2 + else + {*/ + [[notification.userInfo valueForKey:UIKeyboardBoundsUserInfoKey] getValue:&keyboardFrame]; + keyboardHeight = keyboardFrame.size.height; + //} + + // Find the bottom of the screen (accounting for keyboard overlay) + // This is pretty much only for pagesheet's on the iPad + UIInterfaceOrientation orient = [[UIApplication sharedApplication] statusBarOrientation]; + BOOL inLandscape = orient == UIInterfaceOrientationLandscapeLeft || orient == UIInterfaceOrientationLandscapeRight; + BOOL upsideDown = orient == UIInterfaceOrientationPortraitUpsideDown || orient == UIInterfaceOrientationLandscapeRight; + + CGPoint topOfViewPoint = [self.view convertPoint:CGPointZero toView:nil]; + CGFloat topOfView = inLandscape ? topOfViewPoint.x : topOfViewPoint.y; + + CGFloat screenHeight = inLandscape ? [[UIScreen mainScreen] applicationFrame].size.width : [[UIScreen mainScreen] applicationFrame].size.height; + + CGFloat distFromBottom = screenHeight - ((upsideDown ? screenHeight - topOfView : topOfView ) + self.view.bounds.size.height) + ([UIApplication sharedApplication].statusBarHidden || upsideDown ? 0 : 20); + CGFloat maxViewHeight = self.view.bounds.size.height - keyboardHeight + distFromBottom; + + textView.frame = CGRectMake(0,0,self.view.bounds.size.width,maxViewHeight); + [self layoutCounter]; +} + +#pragma mark - + +- (void)updateCounter +{ + if (counter == nil) + { + self.counter = [[UILabel alloc] initWithFrame:CGRectZero]; + counter.backgroundColor = [UIColor clearColor]; + counter.opaque = NO; + counter.font = [UIFont boldSystemFontOfSize:14]; + counter.textAlignment = UITextAlignmentRight; + + counter.autoresizesSubviews = YES; + counter.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin; + + [self.view addSubview:counter]; + [self layoutCounter]; + + [counter release]; + } + + int count = (hasAttachment?115:140) - textView.text.length; + counter.text = [NSString stringWithFormat:@"%@%i", hasAttachment ? @"Image + ":@"" , count]; + counter.textColor = count >= 0 ? [UIColor blackColor] : [UIColor redColor]; +} + +- (void)layoutCounter +{ + counter.frame = CGRectMake(textView.bounds.size.width-150-15, + textView.bounds.size.height-15-9, + 150, + 15); +} + +- (void)textViewDidBeginEditing:(UITextView *)textView +{ + [self updateCounter]; +} + +- (void)textViewDidChange:(UITextView *)textView +{ + [self updateCounter]; +} + +- (void)textViewDidEndEditing:(UITextView *)textView +{ + [self updateCounter]; +} + +#pragma mark - + +- (void)cancel +{ + [[SHK currentHelper] hideCurrentViewControllerAnimated:YES]; +} + +- (void)save +{ + if (textView.text.length > (hasAttachment?115:140)) + { + [[[[UIAlertView alloc] initWithTitle:SHKLocalizedString(@"Message is too long") + message:SHKLocalizedString(@"Twitter posts can only be 140 characters in length.") + delegate:nil + cancelButtonTitle:SHKLocalizedString(@"Close") + otherButtonTitles:nil] autorelease] show]; + return; + } + + else if (textView.text.length == 0) + { + [[[[UIAlertView alloc] initWithTitle:SHKLocalizedString(@"Message is empty") + message:SHKLocalizedString(@"You must enter a message in order to post.") + delegate:nil + cancelButtonTitle:SHKLocalizedString(@"Close") + otherButtonTitles:nil] autorelease] show]; + return; + } + + [(SHKTwitter *)delegate sendForm:self]; + + [[SHK currentHelper] hideCurrentViewControllerAnimated:YES]; +} + +@end diff --git a/Classes/ShareKit/UI/SHKActionSheet.h b/Classes/ShareKit/UI/SHKActionSheet.h new file mode 100644 index 00000000..0d2d76b3 --- /dev/null +++ b/Classes/ShareKit/UI/SHKActionSheet.h @@ -0,0 +1,45 @@ +// +// SHKActionSheet.h +// ShareKit +// +// Created by Nathan Weiner on 6/10/10. + +// +// 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 "SHK.h" + +@interface SHKActionSheet : UIActionSheet +{ + NSMutableArray *sharers; + + SHKItem *item; +} + +@property (retain) NSMutableArray *sharers; + +@property (retain) SHKItem *item; + ++ (SHKActionSheet *)actionSheetForType:(SHKShareType)type; ++ (SHKActionSheet *)actionSheetForItem:(SHKItem *)i; + +@end \ No newline at end of file diff --git a/Classes/ShareKit/UI/SHKActionSheet.m b/Classes/ShareKit/UI/SHKActionSheet.m new file mode 100644 index 00000000..8921a7a8 --- /dev/null +++ b/Classes/ShareKit/UI/SHKActionSheet.m @@ -0,0 +1,107 @@ +// +// SHKActionSheet.m +// ShareKit +// +// Created by Nathan Weiner on 6/10/10. + +// +// 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 "SHKActionSheet.h" +#import "SHK.h" +#import "SHKSharer.h" +#import "SHKCustomShareMenu.h" +#import + +@implementation SHKActionSheet + +@synthesize item, sharers; + +- (void)dealloc +{ + [item release]; + [sharers release]; + [super dealloc]; +} + ++ (SHKActionSheet *)actionSheetForType:(SHKShareType)type +{ + SHKActionSheet *as = [[SHKActionSheet alloc] initWithTitle:SHKLocalizedString(@"Share") + delegate:self + cancelButtonTitle:nil + destructiveButtonTitle:nil + otherButtonTitles:nil]; + as.item = [[[SHKItem alloc] init] autorelease]; + as.item.shareType = type; + + as.sharers = [NSMutableArray arrayWithCapacity:0]; + NSArray *favoriteSharers = [SHK favoriteSharersForType:type]; + + // Add buttons for each favorite sharer + id class; + for(NSString *sharerId in favoriteSharers) + { + class = NSClassFromString(sharerId); + if ([class canShare]) + { + [as addButtonWithTitle: [class sharerTitle] ]; + [as.sharers addObject:sharerId]; + } + } + + // Add More button + [as addButtonWithTitle:SHKLocalizedString(@"More...")]; + + // Add Cancel button + [as addButtonWithTitle:SHKLocalizedString(@"Cancel")]; + as.cancelButtonIndex = as.numberOfButtons -1; + + return [as autorelease]; +} + ++ (SHKActionSheet *)actionSheetForItem:(SHKItem *)i +{ + SHKActionSheet *as = [self actionSheetForType:i.shareType]; + as.item = i; + return as; +} + +- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated +{ + // Sharers + if (buttonIndex >= 0 && buttonIndex < sharers.count) + { + [NSClassFromString([sharers objectAtIndex:buttonIndex]) performSelector:@selector(shareItem:) withObject:item]; + } + + // More + else if (buttonIndex == sharers.count) + { + SHKShareMenu *shareMenu = [[SHKCustomShareMenu alloc] initWithStyle:UITableViewStyleGrouped]; + shareMenu.item = item; + [[SHK currentHelper] showViewController:shareMenu]; + [shareMenu release]; + } + + [super dismissWithClickedButtonIndex:buttonIndex animated:animated]; +} + +@end \ No newline at end of file diff --git a/Classes/ShareKit/UI/SHKActivityIndicator.h b/Classes/ShareKit/UI/SHKActivityIndicator.h new file mode 100644 index 00000000..122bd911 --- /dev/null +++ b/Classes/ShareKit/UI/SHKActivityIndicator.h @@ -0,0 +1,59 @@ +// +// SHKActivityIndicator.h +// ShareKit +// +// Created by Nathan Weiner on 6/16/10. + +// +// 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 SHKActivityIndicator : UIView +{ + UILabel *centerMessageLabel; + UILabel *subMessageLabel; + + UIActivityIndicatorView *spinner; +} + +@property (nonatomic, retain) UILabel *centerMessageLabel; +@property (nonatomic, retain) UILabel *subMessageLabel; + +@property (nonatomic, retain) UIActivityIndicatorView *spinner; + + ++ (SHKActivityIndicator *)currentIndicator; + +- (void)show; +- (void)hideAfterDelay; +- (void)hide; +- (void)hidden; +- (void)displayActivity:(NSString *)m; +- (void)displayCompleted:(NSString *)m; +- (void)setCenterMessage:(NSString *)message; +- (void)setSubMessage:(NSString *)message; +- (void)showSpinner; +- (void)setProperRotation; +- (void)setProperRotation:(BOOL)animated; + +@end diff --git a/Classes/ShareKit/UI/SHKActivityIndicator.m b/Classes/ShareKit/UI/SHKActivityIndicator.m new file mode 100644 index 00000000..baf3ee04 --- /dev/null +++ b/Classes/ShareKit/UI/SHKActivityIndicator.m @@ -0,0 +1,277 @@ +// +// SHKActivityIndicator.m +// ShareKit +// +// Created by Nathan Weiner on 6/16/10. + +// +// 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 "SHKActivityIndicator.h" +#import + +#define SHKdegreesToRadians(x) (M_PI * x / 180.0) + +@implementation SHKActivityIndicator + +@synthesize centerMessageLabel, subMessageLabel; +@synthesize spinner; + +static SHKActivityIndicator *currentIndicator = nil; + + ++ (SHKActivityIndicator *)currentIndicator +{ + if (currentIndicator == nil) + { + UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; + + CGFloat width = 160; + CGFloat height = 160; + CGRect centeredFrame = CGRectMake(round(keyWindow.bounds.size.width/2 - width/2), + round(keyWindow.bounds.size.height/2 - height/2), + width, + height); + + currentIndicator = [[SHKActivityIndicator alloc] initWithFrame:centeredFrame]; + + currentIndicator.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5]; + currentIndicator.opaque = NO; + currentIndicator.alpha = 0; + + currentIndicator.layer.cornerRadius = 10; + + currentIndicator.userInteractionEnabled = NO; + currentIndicator.autoresizesSubviews = YES; + currentIndicator.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; + + [currentIndicator setProperRotation:NO]; + + [[NSNotificationCenter defaultCenter] addObserver:currentIndicator + selector:@selector(setProperRotation) + name:UIDeviceOrientationDidChangeNotification + object:nil]; + } + + return currentIndicator; +} + +#pragma mark - + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; + + [centerMessageLabel release]; + [subMessageLabel release]; + [spinner release]; + + [super dealloc]; +} + +#pragma mark Creating Message + +- (void)show +{ + if ([self superview] != [[UIApplication sharedApplication] keyWindow]) + [[[UIApplication sharedApplication] keyWindow] addSubview:self]; + + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hide) object:nil]; + + [UIView beginAnimations:nil context:NULL]; + [UIView setAnimationDuration:0.3]; + + self.alpha = 1; + + [UIView commitAnimations]; +} + +- (void)hideAfterDelay +{ + [self performSelector:@selector(hide) withObject:nil afterDelay:0.6]; +} + +- (void)hide +{ + [UIView beginAnimations:nil context:NULL]; + [UIView setAnimationDuration:0.4]; + [UIView setAnimationDelegate:self]; + [UIView setAnimationDidStopSelector:@selector(hidden)]; + + self.alpha = 0; + + [UIView commitAnimations]; +} + +- (void)persist +{ + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hide) object:nil]; + + [UIView beginAnimations:nil context:NULL]; + [UIView setAnimationBeginsFromCurrentState:YES]; + [UIView setAnimationDuration:0.1]; + + self.alpha = 1; + + [UIView commitAnimations]; +} + +- (void)hidden +{ + if (currentIndicator.alpha > 0) + return; + + [currentIndicator removeFromSuperview]; + currentIndicator = nil; +} + +- (void)displayActivity:(NSString *)m +{ + [self setSubMessage:m]; + [self showSpinner]; + + [centerMessageLabel removeFromSuperview]; + centerMessageLabel = nil; + + if ([self superview] == nil) + [self show]; + else + [self persist]; +} + +- (void)displayCompleted:(NSString *)m +{ + [self setCenterMessage:@"✓"]; + [self setSubMessage:m]; + + [spinner removeFromSuperview]; + spinner = nil; + + if ([self superview] == nil) + [self show]; + else + [self persist]; + + [self hideAfterDelay]; +} + +- (void)setCenterMessage:(NSString *)message +{ + if (message == nil && centerMessageLabel != nil) + self.centerMessageLabel = nil; + + else if (message != nil) + { + if (centerMessageLabel == nil) + { + self.centerMessageLabel = [[[UILabel alloc] initWithFrame:CGRectMake(12,round(self.bounds.size.height/2-50/2),self.bounds.size.width-24,50)] autorelease]; + centerMessageLabel.backgroundColor = [UIColor clearColor]; + centerMessageLabel.opaque = NO; + centerMessageLabel.textColor = [UIColor whiteColor]; + centerMessageLabel.font = [UIFont boldSystemFontOfSize:40]; + centerMessageLabel.textAlignment = UITextAlignmentCenter; + centerMessageLabel.shadowColor = [UIColor darkGrayColor]; + centerMessageLabel.shadowOffset = CGSizeMake(1,1); + centerMessageLabel.adjustsFontSizeToFitWidth = YES; + + [self addSubview:centerMessageLabel]; + } + + centerMessageLabel.text = message; + } +} + +- (void)setSubMessage:(NSString *)message +{ + if (message == nil && subMessageLabel != nil) + self.subMessageLabel = nil; + + else if (message != nil) + { + if (subMessageLabel == nil) + { + self.subMessageLabel = [[[UILabel alloc] initWithFrame:CGRectMake(12,self.bounds.size.height-45,self.bounds.size.width-24,30)] autorelease]; + subMessageLabel.backgroundColor = [UIColor clearColor]; + subMessageLabel.opaque = NO; + subMessageLabel.textColor = [UIColor whiteColor]; + subMessageLabel.font = [UIFont boldSystemFontOfSize:17]; + subMessageLabel.textAlignment = UITextAlignmentCenter; + subMessageLabel.shadowColor = [UIColor darkGrayColor]; + subMessageLabel.shadowOffset = CGSizeMake(1,1); + subMessageLabel.adjustsFontSizeToFitWidth = YES; + + [self addSubview:subMessageLabel]; + } + + subMessageLabel.text = message; + } +} + +- (void)showSpinner +{ + if (spinner == nil) + { + self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; + + spinner.frame = CGRectMake(round(self.bounds.size.width/2 - spinner.frame.size.width/2), + round(self.bounds.size.height/2 - spinner.frame.size.height/2), + spinner.frame.size.width, + spinner.frame.size.height); + [spinner release]; + } + + [self addSubview:spinner]; + [spinner startAnimating]; +} + +#pragma mark - +#pragma mark Rotation + +- (void)setProperRotation +{ + [self setProperRotation:YES]; +} + +- (void)setProperRotation:(BOOL)animated +{ + UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; + + if (animated) + { + [UIView beginAnimations:nil context:NULL]; + [UIView setAnimationDuration:0.3]; + } + + if (orientation == UIDeviceOrientationPortraitUpsideDown) + self.transform = CGAffineTransformRotate(CGAffineTransformIdentity, SHKdegreesToRadians(180)); + + else if (orientation == UIDeviceOrientationLandscapeLeft) + self.transform = CGAffineTransformRotate(CGAffineTransformIdentity, SHKdegreesToRadians(90)); + + else if (orientation == UIDeviceOrientationLandscapeRight) + self.transform = CGAffineTransformRotate(CGAffineTransformIdentity, SHKdegreesToRadians(-90)); + + if (animated) + [UIView commitAnimations]; +} + + +@end diff --git a/Classes/ShareKit/UI/SHKFormController.h b/Classes/ShareKit/UI/SHKFormController.h new file mode 100644 index 00000000..2d7fa35b --- /dev/null +++ b/Classes/ShareKit/UI/SHKFormController.h @@ -0,0 +1,82 @@ +// +// SHKFormController.h +// ShareKit +// +// Created by Nathan Weiner on 6/17/10. + +// +// 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 "SHKFormFieldSettings.h" +#import "SHKCustomFormFieldCell.h" + +@interface SHKFormController : UITableViewController +{ + id delegate; + SEL validateSelector; + SEL saveSelector; + + NSMutableArray *sections; + NSMutableDictionary *values; + + CGFloat labelWidth; + + UITextField *activeField; + + BOOL autoSelect; +} + +@property (retain) id delegate; +@property SEL validateSelector; +@property SEL saveSelector; + +@property (retain) NSMutableArray *sections; +@property (retain) NSMutableDictionary *values; + +@property CGFloat labelWidth; + +@property (nonatomic, retain) UITextField *activeField; + +@property BOOL autoSelect; + + +- (id)initWithStyle:(UITableViewStyle)style title:(NSString *)barTitle rightButtonTitle:(NSString *)rightButtonTitle; +- (void)addSection:(NSArray *)fields header:(NSString *)header footer:(NSString *)footer; + +#pragma mark - + +- (SHKFormFieldSettings *)rowSettingsForIndexPath:(NSIndexPath *)indexPath; + +#pragma mark - +#pragma mark Completion + +- (void)close; +- (void)cancel; +- (void)validateForm; +- (void)saveForm; + +#pragma mark - + +- (NSMutableDictionary *)formValues; +- (NSMutableDictionary *)formValuesForSection:(int)section; + +@end diff --git a/Classes/ShareKit/UI/SHKFormController.m b/Classes/ShareKit/UI/SHKFormController.m new file mode 100644 index 00000000..6d687432 --- /dev/null +++ b/Classes/ShareKit/UI/SHKFormController.m @@ -0,0 +1,288 @@ +// +// SHKFormController.m +// ShareKit +// +// Created by Nathan Weiner on 6/17/10. + +// +// 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 "SHK.h" +#import "SHKCustomFormController.h" +#import "SHKCustomFormFieldCell.h" + + +@implementation SHKFormController + +@synthesize delegate, validateSelector, saveSelector; +@synthesize sections, values; +@synthesize labelWidth; +@synthesize activeField; +@synthesize autoSelect; + + +- (void)dealloc +{ + [delegate release]; + [sections release]; + [values release]; + [activeField release]; + + [super dealloc]; +} + + +#pragma mark - +#pragma mark Initialization + +- (id)initWithStyle:(UITableViewStyle)style title:(NSString *)barTitle rightButtonTitle:(NSString *)rightButtonTitle +{ + if (self = [super initWithStyle:style]) + { + self.title = barTitle; + + self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel + target:self + action:@selector(cancel)]; + + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:rightButtonTitle + style:UIBarButtonItemStyleDone + target:self + action:@selector(validateForm)]; + + self.values = [NSMutableDictionary dictionaryWithCapacity:0]; + } + return self; +} + +- (void)addSection:(NSArray *)fields header:(NSString *)header footer:(NSString *)footer +{ + if (sections == nil) + self.sections = [NSMutableArray arrayWithCapacity:0]; + + NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:0]; + [dict setObject:fields forKey:@"rows"]; + + if (header) + [dict setObject:header forKey:@"header"]; + + if (footer) + [dict setObject:footer forKey:@"footer"]; + + + [sections addObject:dict]; + + // Find the max length of the labels so we can use this value to align the left side of all form fields + // TODO - should probably save this per section for flexibility + if (sections.count == 1) + { + CGFloat newWidth = 0; + CGSize size; + + for (SHKFormFieldSettings *field in fields) + { + // only use text field rows + if (field.type != SHKFormFieldTypeText && field.type != SHKFormFieldTypePassword) + continue; + + size = [field.label sizeWithFont:[UIFont boldSystemFontOfSize:17]]; + if (size.width > newWidth) + newWidth = size.width; + } + + self.labelWidth = newWidth; + } +} + +#pragma mark - + +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear:animated]; + + if (autoSelect) + [self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] animated:NO scrollPosition:UITableViewScrollPositionNone]; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; + + // Remove the SHK view wrapper from the window + [[SHK currentHelper] viewWasDismissed]; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + if (SHKFormBgColorRed != -1) + self.tableView.backgroundColor = [UIColor colorWithRed:SHKFormBgColorRed/255 green:SHKFormBgColorGreen/255 blue:SHKFormBgColorBlue/255 alpha:1]; +} + + +#pragma mark - +#pragma mark Table view data source + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView +{ + return sections.count; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return [[[sections objectAtIndex:section] objectForKey:@"rows"] count]; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + static NSString *CellIdentifier = @"Cell"; + + SHKCustomFormFieldCell *cell = (SHKCustomFormFieldCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; + if (cell == nil) + { + cell = [[[SHKCustomFormFieldCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; + cell.form = self; + + if (SHKFormFontColorRed != -1) + cell.textLabel.textColor = [UIColor colorWithRed:SHKFormFontColorRed/255 green:SHKFormFontColorGreen/255 blue:SHKFormFontColorBlue/255 alpha:1]; + } + + // Since we are reusing table cells, make sure to save any existing values before overwriting + if (cell.settings.key != nil && [cell getValue]) + [values setObject:[cell getValue] forKey:cell.settings.key]; + + cell.settings = [self rowSettingsForIndexPath:indexPath]; + cell.labelWidth = labelWidth; + cell.textLabel.text = cell.settings.label; + + NSString *value = [values objectForKey:cell.settings.key]; + if (value == nil && cell.settings.start != nil) + value = cell.settings.start; + + [cell setValue:value]; + + return cell; +} + +- (SHKFormFieldSettings *)rowSettingsForIndexPath:(NSIndexPath *)indexPath +{ + return [[[sections objectAtIndex:indexPath.section] objectForKey:@"rows"] objectAtIndex:indexPath.row]; +} + +- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section +{ + return [[sections objectAtIndex:section] objectForKey:@"header"]; +} + +- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section +{ + return [[sections objectAtIndex:section] objectForKey:@"footer"]; +} + + +#pragma mark - + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + return YES; +} + + +#pragma mark - +#pragma mark UITextFieldDelegate + +- (BOOL)textFieldShouldReturn:(UITextField *)textField +{ + [self validateForm]; + return YES; +} + +- (void)textFieldDidBeginEditing:(UITextField *)textField +{ + self.activeField = textField; +} + + +#pragma mark - +#pragma mark Completion + +- (void)close +{ + [[SHK currentHelper] hideCurrentViewControllerAnimated:YES]; +} + +- (void)cancel +{ + [self close]; +} + +- (void)validateForm +{ + [activeField resignFirstResponder]; + [delegate performSelector:validateSelector withObject:self]; +} + +- (void)saveForm +{ + [delegate performSelector:saveSelector withObject:self]; + [self close]; +} + +#pragma mark - + +- (NSMutableDictionary *)formValues +{ + return [self formValuesForSection:0]; +} + +- (NSMutableDictionary *)formValuesForSection:(int)section +{ + // go through all form fields and get values + NSMutableDictionary *formValues = [NSMutableDictionary dictionaryWithCapacity:0]; + + SHKCustomFormFieldCell *cell; + int row = 0; + NSArray *fields = [[sections objectAtIndex:section] objectForKey:@"rows"]; + + for(SHKFormFieldSettings *field in fields) + { + cell = (SHKCustomFormFieldCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:section]]; + + // Use text field if visible first + if ([cell.settings.key isEqualToString:field.key] && [cell getValue] != nil) + [formValues setObject:[cell getValue] forKey:field.key]; + + // If field is not visible, use cached value + else if ([values objectForKey:field.key] != nil) + [formValues setObject:[values objectForKey:field.key] forKey:field.key]; + + row++; + } + + return formValues; +} + + + + +@end + diff --git a/Classes/ShareKit/UI/SHKFormFieldCell.h b/Classes/ShareKit/UI/SHKFormFieldCell.h new file mode 100644 index 00000000..803026cf --- /dev/null +++ b/Classes/ShareKit/UI/SHKFormFieldCell.h @@ -0,0 +1,61 @@ +// +// SHKFormFieldCell.h +// ShareKit +// +// Created by Nathan Weiner on 6/17/10. + +// +// 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 "SHK.h" + +@class SHKFormController; + +@interface SHKFormFieldCell : UITableViewCell +{ + SHKFormFieldSettings *settings; + + CGFloat labelWidth; + + UITextField *textField; + UISwitch *toggle; + + NSString *tmpValue; + + SHKFormController *form; +} + +@property (retain) SHKFormFieldSettings *settings; + +@property (nonatomic) CGFloat labelWidth; + +@property (nonatomic, retain, getter=getTextField) UITextField *textField; +@property (nonatomic, retain) UISwitch *toggle; + +@property (nonatomic, retain) NSString *tmpValue; + +@property (nonatomic, assign) SHKFormController *form; + +- (void)setValue:(NSString *)value; +- (NSString *)getValue; + +@end diff --git a/Classes/ShareKit/UI/SHKFormFieldCell.m b/Classes/ShareKit/UI/SHKFormFieldCell.m new file mode 100644 index 00000000..8a97abb2 --- /dev/null +++ b/Classes/ShareKit/UI/SHKFormFieldCell.m @@ -0,0 +1,161 @@ +// +// SHKFormFieldCell.m +// ShareKit +// +// Created by Nathan Weiner on 6/17/10. + +// +// 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 "SHKFormFieldCell.h" +#import "SHKCustomFormController.h" + +@implementation SHKFormFieldCell + +@synthesize settings; +@synthesize labelWidth; +@synthesize textField, toggle, tmpValue; +@synthesize form; + +#define SHK_FORM_CELL_PAD_LEFT 24 +#define SHK_FORM_CELL_PAD_RIGHT 10 + + +- (void)dealloc +{ + [settings release]; + [textField release]; + [toggle release]; + [tmpValue release]; + [super dealloc]; +} + +- (UITextField *)getTextField +{ + if (textField == nil) + { + self.textField = [[UITextField alloc] initWithFrame:CGRectMake(0,0,0,25)]; + textField.clearsOnBeginEditing = NO; + textField.returnKeyType = UIReturnKeyDone; + textField.font = [UIFont systemFontOfSize:17]; + textField.textColor = [UIColor darkGrayColor]; + textField.autocapitalizationType = UITextAutocapitalizationTypeNone; + textField.autocorrectionType = UITextAutocorrectionTypeNo; + textField.delegate = form; + [self.contentView addSubview:textField]; + [textField release]; + + [self setValue:tmpValue]; + } + return textField; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + if (settings.type == SHKFormFieldTypeText || settings.type == SHKFormFieldTypePassword) + { + self.textField.secureTextEntry = settings.type == SHKFormFieldTypePassword; + + textField.frame = CGRectMake(labelWidth + SHK_FORM_CELL_PAD_LEFT, + 2 + round(self.contentView.bounds.size.height/2 - textField.bounds.size.height/2), + self.contentView.bounds.size.width - SHK_FORM_CELL_PAD_RIGHT - SHK_FORM_CELL_PAD_LEFT - labelWidth, + textField.bounds.size.height); + + if (toggle != nil) + [toggle removeFromSuperview]; + } + + else if (settings.type == SHKFormFieldTypeSwitch) + { + if (toggle == nil) + { + self.toggle = [[UISwitch alloc] initWithFrame:CGRectZero]; + [self.contentView addSubview:toggle]; + [self setValue:tmpValue]; + [toggle release]; + } + + toggle.frame = CGRectMake(self.contentView.bounds.size.width-toggle.bounds.size.width-SHK_FORM_CELL_PAD_RIGHT, + round(self.contentView.bounds.size.height/2-toggle.bounds.size.height/2), + toggle.bounds.size.width, + toggle.bounds.size.height); + + if (textField != nil) + [textField removeFromSuperview]; + } + + [self.contentView bringSubviewToFront:textField]; +} + +- (void)setSelected:(BOOL)selected animated:(BOOL)animated +{ + // don't actually select the row + //[super setSelected:selected animated:animated]; + + if (selected) + [textField becomeFirstResponder]; + + else + [textField resignFirstResponder]; +} + + +#pragma mark - + +- (void)setSettings:(SHKFormFieldSettings *)s +{ + [settings release]; + settings = [s retain]; + [self setNeedsLayout]; +} + +- (void)setValue:(NSString *)value +{ + self.tmpValue = value; // used to hold onto the value in case the form field element is created after this is set + + switch (settings.type) + { + case SHKFormFieldTypeSwitch: + [toggle setOn:[value isEqualToString:SHKFormFieldSwitchOn] animated:NO]; + break; + + case SHKFormFieldTypeText: + case SHKFormFieldTypePassword: + textField.text = value; + break; + } +} + +- (NSString *)getValue +{ + switch (settings.type) + { + case SHKFormFieldTypeSwitch: + return toggle.on ? SHKFormFieldSwitchOn : SHKFormFieldSwitchOff; + break; + } + + return textField.text; +} + +@end diff --git a/Classes/ShareKit/UI/SHKFormFieldSettings.h b/Classes/ShareKit/UI/SHKFormFieldSettings.h new file mode 100644 index 00000000..4db9aba8 --- /dev/null +++ b/Classes/ShareKit/UI/SHKFormFieldSettings.h @@ -0,0 +1,55 @@ +// +// SHKFormFieldSettings.h +// ShareKit +// +// Created by Nathan Weiner on 6/22/10. + +// +// 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 + +typedef enum +{ + SHKFormFieldTypeText, + SHKFormFieldTypePassword, + SHKFormFieldTypeSwitch +} SHKFormFieldType; + +#define SHKFormFieldSwitchOff @"0" +#define SHKFormFieldSwitchOn @"1" + +@interface SHKFormFieldSettings : NSObject +{ + NSString *label; + NSString *key; + SHKFormFieldType type; + NSString *start; +} + +@property (nonatomic, retain) NSString *label; +@property (nonatomic, retain) NSString *key; +@property SHKFormFieldType type; +@property (nonatomic, retain) NSString *start; + ++ (id)label:(NSString *)l key:(NSString *)k type:(SHKFormFieldType)t start:(NSString *)s; + +@end diff --git a/Classes/ShareKit/UI/SHKFormFieldSettings.m b/Classes/ShareKit/UI/SHKFormFieldSettings.m new file mode 100644 index 00000000..c58826fb --- /dev/null +++ b/Classes/ShareKit/UI/SHKFormFieldSettings.m @@ -0,0 +1,54 @@ +// +// SHKFormFieldSettings.m +// ShareKit +// +// Created by Nathan Weiner on 6/22/10. + +// +// 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 "SHKFormFieldSettings.h" + + +@implementation SHKFormFieldSettings + +@synthesize label, key, type, start; + +- (void)dealloc +{ + [label release]; + [key release]; + [start release]; + + [super dealloc]; +} + ++ (id)label:(NSString *)l key:(NSString *)k type:(SHKFormFieldType)t start:(NSString *)s +{ + SHKFormFieldSettings *settings = [[SHKFormFieldSettings alloc] init]; + settings.label = l; + settings.key = k; + settings.type = t; + settings.start = s; + return [settings autorelease]; +} + +@end diff --git a/Classes/ShareKit/UI/SHKOAuthView.h b/Classes/ShareKit/UI/SHKOAuthView.h new file mode 100644 index 00000000..7604ee51 --- /dev/null +++ b/Classes/ShareKit/UI/SHKOAuthView.h @@ -0,0 +1,58 @@ +// +// SHKTwitterAuthView.h +// ShareKit +// +// Created by Nathan Weiner on 6/21/10. + +// +// 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 + +@class SHKOAuthView; +@class SHKOAuthSharer; + +@protocol SHKOAuthViewDelegate + +- (void)tokenAuthorizeView:(SHKOAuthView *)authView didFinishWithSuccess:(BOOL)success queryParams:(NSMutableDictionary *)queryParams error:(NSError *)error; +- (void)tokenAuthorizeCancelledView:(SHKOAuthView *)authView; +- (NSURL *)authorizeCallbackURL; + +@end + + +@interface SHKOAuthView : UIViewController +{ + UIWebView *webView; + id delegate; + UIActivityIndicatorView *spinner; +} + +@property (nonatomic, retain) UIWebView *webView; +@property (retain) id delegate; +@property (nonatomic, retain) UIActivityIndicatorView *spinner; + +- (id)initWithURL:(NSURL *)authorizeURL delegate:(id)d; + +- (void)startSpinner; +- (void)stopSpinner; + +@end diff --git a/Classes/ShareKit/UI/SHKOAuthView.m b/Classes/ShareKit/UI/SHKOAuthView.m new file mode 100644 index 00000000..7b735ca0 --- /dev/null +++ b/Classes/ShareKit/UI/SHKOAuthView.m @@ -0,0 +1,163 @@ + // +// SHKTwitterAuthView.m +// ShareKit +// +// Created by Nathan Weiner on 6/21/10. + +// +// 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 "SHKOAuthView.h" +#import "SHK.h" +#import "SHKOAuthSharer.h" + +@implementation SHKOAuthView + +@synthesize webView, delegate, spinner; + +- (void)dealloc +{ + [webView release]; + [delegate release]; + [spinner release]; + [super dealloc]; +} + +- (id)initWithURL:(NSURL *)authorizeURL delegate:(id)d +{ + if ((self = [super initWithNibName:nil bundle:nil])) + { + [self.navigationItem setLeftBarButtonItem:[[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel + target:self + action:@selector(cancel)] autorelease] animated:NO]; + + self.delegate = d; + + self.webView = [[UIWebView alloc] initWithFrame:CGRectZero]; + webView.delegate = self; + webView.scalesPageToFit = YES; + webView.dataDetectorTypes = UIDataDetectorTypeNone; + [webView release]; + + [webView loadRequest:[NSURLRequest requestWithURL:authorizeURL]]; + + } + return self; +} + +- (void)loadView +{ + self.view = webView; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; + + // Remove the SHK view wrapper from the window + [[SHK currentHelper] viewWasDismissed]; +} + + +- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType +{ + if ([request.URL.absoluteString rangeOfString:[delegate authorizeCallbackURL].absoluteString].location != NSNotFound) + { + // Get query + NSMutableDictionary *queryParams = nil; + if (request.URL.query != nil) + { + queryParams = [NSMutableDictionary dictionaryWithCapacity:0]; + NSArray *vars = [request.URL.query componentsSeparatedByString:@"&"]; + NSArray *parts; + for(NSString *var in vars) + { + parts = [var componentsSeparatedByString:@"="]; + if (parts.count == 2) + [queryParams setObject:[parts objectAtIndex:1] forKey:[parts objectAtIndex:0]]; + } + } + + [delegate tokenAuthorizeView:self didFinishWithSuccess:YES queryParams:queryParams error:nil]; + self.delegate = nil; + + return NO; + } + + return YES; +} + +- (void)webViewDidStartLoad:(UIWebView *)webView +{ + [self startSpinner]; +} + +- (void)webViewDidFinishLoad:(UIWebView *)aWebView +{ + [self stopSpinner]; + + // Extra sanity check for Twitter OAuth users to make sure they are using BROWSER with a callback instead of pin based auth + if ([webView.request.URL.host isEqualToString:@"twitter.com"] && [webView stringByEvaluatingJavaScriptFromString:@"document.getElementById('oauth_pin').innerHTML"].length) + [delegate tokenAuthorizeView:self didFinishWithSuccess:NO queryParams:nil error:[SHK error:@"Your SHKTwitter config is incorrect. You must set your application type to Browser and define a callback url. See SHKConfig.h for more details"]]; +} + +- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error +{ + if ([error code] != NSURLErrorCancelled && [error code] != 102 && [error code] != NSURLErrorFileDoesNotExist) + { + [self stopSpinner]; + [delegate tokenAuthorizeView:self didFinishWithSuccess:NO queryParams:nil error:error]; + } +} + +- (void)startSpinner +{ + if (spinner == nil) + { + self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; + [self.navigationItem setRightBarButtonItem:[[[UIBarButtonItem alloc] initWithCustomView:spinner] autorelease] animated:NO]; + spinner.hidesWhenStopped = YES; + [spinner release]; + } + + [spinner startAnimating]; +} + +- (void)stopSpinner +{ + [spinner stopAnimating]; +} + + +#pragma mark - + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + return YES; +} + +- (void)cancel +{ + [delegate tokenAuthorizeCancelledView:self]; + [[SHK currentHelper] hideCurrentViewControllerAnimated:YES]; +} + +@end diff --git a/Classes/ShareKit/UI/SHKShareMenu.h b/Classes/ShareKit/UI/SHKShareMenu.h new file mode 100644 index 00000000..36193be7 --- /dev/null +++ b/Classes/ShareKit/UI/SHKShareMenu.h @@ -0,0 +1,56 @@ +// +// SHKShareMenu.h +// ShareKit +// +// Created by Nathan Weiner on 6/18/10. + +// +// 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 "SHK.h" + +@interface SHKShareMenu : UITableViewController +{ + SHKItem *item; + NSMutableArray *tableData; + NSMutableDictionary *exclusions; +} + +@property (retain) SHKItem *item; +@property (retain) NSMutableArray *tableData; +@property (retain) NSMutableDictionary *exclusions; + + +- (void)rebuildTableDataAnimated:(BOOL)animated; +- (NSMutableArray *)section:(NSString *)section; +- (NSDictionary *)rowDataAtIndexPath:(NSIndexPath *)indexPath; + +#pragma mark - +#pragma mark Toolbar Buttons + +- (void)cancel; +- (void)edit; +- (void)save; + + + +@end diff --git a/Classes/ShareKit/UI/SHKShareMenu.m b/Classes/ShareKit/UI/SHKShareMenu.m new file mode 100644 index 00000000..a496a112 --- /dev/null +++ b/Classes/ShareKit/UI/SHKShareMenu.m @@ -0,0 +1,325 @@ +// +// SHKShareMenu.m +// ShareKit +// +// Created by Nathan Weiner on 6/18/10. + +// +// 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 "SHKShareMenu.h" +#import "SHK.h" +#import "SHKSharer.h" +#import "SHKCustomShareMenuCell.h" + +@implementation SHKShareMenu + +@synthesize item; +@synthesize tableData; +@synthesize exclusions; + +#pragma mark - +#pragma mark Initialization + +- (void)dealloc +{ + [item release]; + [tableData release]; + [exclusions release]; + [super dealloc]; +} + + +- (id)initWithStyle:(UITableViewStyle)style +{ + if (self = [super initWithStyle:style]) + { + self.title = SHKLocalizedString(@"Share"); + + self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel + target:self + action:@selector(cancel)]; + + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:SHKLocalizedString(@"Edit") + style:UIBarButtonItemStyleBordered + target:self + action:@selector(edit)]; + + } + return self; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; + + // Remove the SHK view wrapper from the window + [[SHK currentHelper] viewWasDismissed]; +} + + +- (void)setItem:(SHKItem *)i +{ + [item release]; + item = [i retain]; + + [self rebuildTableDataAnimated:NO]; +} + +- (void)rebuildTableDataAnimated:(BOOL)animated +{ + self.tableView.allowsSelectionDuringEditing = YES; + self.tableData = [NSMutableArray arrayWithCapacity:0]; + [tableData addObject:[self section:@"actions"]]; + [tableData addObject:[self section:@"services"]]; + + // Handling Excluded items + // If in editing mode, show them + // If not editing, hide them + self.exclusions = [[[[NSUserDefaults standardUserDefaults] objectForKey:@"SHKExcluded"] mutableCopy] autorelease]; + + if (exclusions == nil) + self.exclusions = [NSMutableDictionary dictionaryWithCapacity:0]; + + NSMutableArray *excluded = [NSMutableArray arrayWithCapacity:0]; + + if (!self.tableView.editing || animated) + { + int s = 0; + int r = 0; + + // Use temp objects so we can mutate as we are enumerating + NSMutableArray *sectionCopy; + NSMutableDictionary *tableDataCopy = [[tableData mutableCopy] autorelease]; + NSMutableIndexSet *indexes = [[NSMutableIndexSet alloc] init]; + + for(NSMutableArray *section in tableDataCopy) + { + r = 0; + [indexes removeAllIndexes]; + + sectionCopy = [[section mutableCopy] autorelease]; + + for (NSMutableDictionary *row in section) + { + if ([exclusions objectForKey:[row objectForKey:@"className"]]) + { + [excluded addObject:[NSIndexPath indexPathForRow:r inSection:s]]; + + if (!self.tableView.editing) + [indexes addIndex:r]; + } + + r++; + } + + if (!self.tableView.editing) + { + [sectionCopy removeObjectsAtIndexes:indexes]; + [tableData replaceObjectAtIndex:s withObject:sectionCopy]; + } + + s++; + } + + [indexes release]; + + if (animated) + { + [self.tableView beginUpdates]; + + if (!self.tableView.editing) + [self.tableView deleteRowsAtIndexPaths:excluded withRowAnimation:UITableViewRowAnimationFade]; + else + [self.tableView insertRowsAtIndexPaths:excluded withRowAnimation:UITableViewRowAnimationFade]; + + [self.tableView endUpdates]; + } + } + +} + +- (NSMutableArray *)section:(NSString *)section +{ + id class; + NSMutableArray *sectionData = [NSMutableArray arrayWithCapacity:0]; + NSArray *source = [[SHK sharersDictionary] objectForKey:section]; + + for( NSString *sharerClassName in source) + { + class = NSClassFromString(sharerClassName); + if ( [class canShare] && [class canShareType:item.shareType] ) + [sectionData addObject:[NSDictionary dictionaryWithObjectsAndKeys:sharerClassName,@"className",[class sharerTitle],@"name",nil]]; + } + + if (sectionData.count && SHKShareMenuAlphabeticalOrder) + [sectionData sortUsingDescriptors:[NSArray arrayWithObject:[[[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)] autorelease]]]; + + return sectionData; +} + +#pragma mark - +#pragma mark View lifecycle + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + return YES; +} + + +#pragma mark - +#pragma mark Table view data source + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView +{ + return tableData.count; +} + + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return [[tableData objectAtIndex:section] count]; +} + + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + static NSString *CellIdentifier = @"Cell"; + + SHKCustomShareMenuCell *cell = (SHKCustomShareMenuCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; + if (cell == nil) + { + cell = [[[SHKCustomShareMenuCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + } + + NSDictionary *rowData = [self rowDataAtIndexPath:indexPath]; + cell.textLabel.text = [rowData objectForKey:@"name"]; + + if (cell.editingAccessoryView == nil) + { + UISwitch *toggle = [[UISwitch alloc] initWithFrame:CGRectZero]; + toggle.userInteractionEnabled = NO; + cell.editingAccessoryView = toggle; + [toggle release]; + } + + [(UISwitch *)cell.editingAccessoryView setOn:[exclusions objectForKey:[rowData objectForKey:@"className"]] == nil]; + + return cell; +} + +- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath +{ + return UITableViewCellEditingStyleNone; +} + +- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath +{ + return NO; +} + + +#pragma mark - +#pragma mark Table view delegate + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + NSDictionary *rowData = [self rowDataAtIndexPath:indexPath]; + + if (tableView.editing) + { + UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; + + UISwitch *toggle = (UISwitch *)[cell editingAccessoryView]; + [toggle setOn:!toggle.on animated:YES]; + + if (toggle.on) + [exclusions removeObjectForKey:[rowData objectForKey:@"className"]]; + + else + [exclusions setObject:@"1" forKey:[rowData objectForKey:@"className"]]; + + [self.tableView deselectRowAtIndexPath:indexPath animated:NO]; + } + + else + { + [NSClassFromString([rowData objectForKey:@"className"]) shareItem:item]; + + [[SHK currentHelper] hideCurrentViewControllerAnimated:YES]; + } +} + +- (NSDictionary *)rowDataAtIndexPath:(NSIndexPath *)indexPath +{ + return [[tableData objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]; +} + +- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section +{ + if ([[tableData objectAtIndex:section] count]) + { + if (section == 0) + return SHKLocalizedString(@"Actions"); + + else if (section == 1) + return SHKLocalizedString(@"Services"); + } + + return nil; +} + + +#pragma mark - +#pragma mark Toolbar Buttons + +- (void)cancel +{ + [[SHK currentHelper] hideCurrentViewControllerAnimated:YES]; +} + +- (void)edit +{ + [self.tableView setEditing:YES animated:YES]; + [self rebuildTableDataAnimated:YES]; + + [self.navigationItem setRightBarButtonItem:[[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone + target:self + action:@selector(save)] autorelease] animated:YES]; +} + +- (void)save +{ + [[NSUserDefaults standardUserDefaults] setObject:exclusions forKey:@"SHKExcluded"]; + + [self.tableView setEditing:NO animated:YES]; + [self rebuildTableDataAnimated:YES]; + + [self.navigationItem setRightBarButtonItem:[[[UIBarButtonItem alloc] initWithTitle:SHKLocalizedString(@"Edit") + style:UIBarButtonItemStyleBordered + target:self + action:@selector(edit)] autorelease] animated:YES]; + +} + +@end + diff --git a/Classes/ShareKit/UI/SHKViewControllerWrapper.h b/Classes/ShareKit/UI/SHKViewControllerWrapper.h new file mode 100644 index 00000000..c672216a --- /dev/null +++ b/Classes/ShareKit/UI/SHKViewControllerWrapper.h @@ -0,0 +1,35 @@ +// +// SHKViewControllerWrapper.h +// ShareKit +// +// Created by Nathan Weiner on 6/28/10. + +// +// 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 SHKViewControllerWrapper : UIViewController { + +} + +@end diff --git a/Classes/ShareKit/UI/SHKViewControllerWrapper.m b/Classes/ShareKit/UI/SHKViewControllerWrapper.m new file mode 100644 index 00000000..5da50bce --- /dev/null +++ b/Classes/ShareKit/UI/SHKViewControllerWrapper.m @@ -0,0 +1,38 @@ + // +// SHKViewControllerWrapper.m +// ShareKit +// +// Created by Nathan Weiner on 6/28/10. + +// +// 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 "SHKViewControllerWrapper.h" + + +@implementation SHKViewControllerWrapper + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + return YES; +} + +@end diff --git a/MainWindow.xib b/MainWindow.xib new file mode 100644 index 00000000..0cac026c --- /dev/null +++ b/MainWindow.xib @@ -0,0 +1,279 @@ + + + + 800 + 10D541 + 760 + 1038.29 + 460.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 81 + + + YES + + + + YES + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + YES + + YES + + + YES + + + + YES + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + IBCocoaTouchFramework + + + + 1316 + + {320, 480} + + 1 + MSAxIDEAA + + NO + NO + + IBCocoaTouchFramework + YES + + + + IBCocoaTouchFramework + + + 256 + {0, 0} + NO + YES + YES + IBCocoaTouchFramework + + + YES + + + + IBCocoaTouchFramework + + + RootViewController + + IBCocoaTouchFramework + + + + + + + YES + + + delegate + + + + 4 + + + + window + + + + 5 + + + + navigationController + + + + 15 + + + + + YES + + 0 + + + + + + 2 + + + YES + + + + + -1 + + + File's Owner + + + 3 + + + + + -2 + + + + + 9 + + + YES + + + + + + + 11 + + + + + 13 + + + YES + + + + + + 14 + + + + + + + YES + + YES + -1.CustomClassName + -2.CustomClassName + 11.IBPluginDependency + 13.CustomClassName + 13.IBPluginDependency + 2.IBAttributePlaceholdersKey + 2.IBEditorWindowLastContentRect + 2.IBPluginDependency + 3.CustomClassName + 3.IBPluginDependency + 9.IBEditorWindowLastContentRect + 9.IBPluginDependency + + + YES + UIApplication + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + RootViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + YES + + + YES + + + {{673, 376}, {320, 480}} + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + ShareKitAppDelegate + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + {{186, 376}, {320, 480}} + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + YES + + + YES + + + + + YES + + + YES + + + + 15 + + + + YES + + RootViewController + UITableViewController + + IBProjectSource + Classes/RootViewController.h + + + + ShareKitAppDelegate + NSObject + + YES + + YES + navigationController + window + + + YES + UINavigationController + UIWindow + + + + IBProjectSource + Classes/ShareKitAppDelegate.h + + + + + 0 + IBCocoaTouchFramework + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + + + YES + ShareKit.xcodeproj + 3 + 81 + + diff --git a/README b/README index b938907e..87f0bac7 100644 --- a/README +++ b/README @@ -1,9 +1,22 @@ -The project hosted on github is for contributions and ongoing development. It should not be used in existing projects. +The code hosted here on github is for ongoing development and contributions and may contain untested code. Please use a stable release from http://getsharekit.com for use in your own app. -For stable releases, please visit: -http://getsharekit.com +*** + +To download a stable release visit: +http://getsharekit.com/install -For documentation and install instructions, please visit: +Installation instructions: http://getsharekit.com/install +How to add new services: +http://getsharekit.com/add + +How to customize the look of ShareKit: +http://getsharekit.com/customize + +Full documentation: +http://getsharekit.com/docs + *** + +Follow @IdeaShower or http://getsharekit.com/blog for updates diff --git a/Resources-iPad/MainWindow-iPad.xib b/Resources-iPad/MainWindow-iPad.xib new file mode 100644 index 00000000..7497640a --- /dev/null +++ b/Resources-iPad/MainWindow-iPad.xib @@ -0,0 +1,304 @@ + + + + 1024 + 10D573 + 788 + 1038.29 + 460.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 117 + + + YES + + + YES + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + YES + + YES + + + YES + + + + YES + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + IBCocoaTouchFramework + + + + 1316 + + {320, 480} + + 1 + MSAxIDEAA + + NO + NO + + IBCocoaTouchFramework + YES + + + + + 1 + + IBCocoaTouchFramework + NO + + + 256 + {0, 0} + NO + YES + YES + IBCocoaTouchFramework + + + YES + + + IBCocoaTouchFramework + + + RootViewController + + + 1 + + IBCocoaTouchFramework + NO + + + + + + + YES + + + delegate + + + + 4 + + + + window + + + + 5 + + + + navigationController + + + + 15 + + + + + YES + + 0 + + + + + + 2 + + + YES + + + + + -1 + + + File's Owner + + + 3 + + + + + -2 + + + + + 9 + + + YES + + + + + + + 11 + + + + + 13 + + + YES + + + + + + 14 + + + + + + + YES + + YES + -1.CustomClassName + -2.CustomClassName + 11.IBPluginDependency + 13.CustomClassName + 13.IBPluginDependency + 2.IBAttributePlaceholdersKey + 2.IBEditorWindowLastContentRect + 2.IBPluginDependency + 3.CustomClassName + 3.IBPluginDependency + 9.IBEditorWindowLastContentRect + 9.IBPluginDependency + + + YES + UIApplication + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + RootViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + YES + + + YES + + + {{673, 376}, {320, 480}} + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + ShareKitAppDelegate + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + {{186, 376}, {320, 480}} + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + YES + + + YES + + + + + YES + + + YES + + + + 15 + + + + YES + + RootViewController + UITableViewController + + IBProjectSource + Classes/RootViewController.h + + + + ShareKitAppDelegate + NSObject + + YES + + YES + navigationController + window + + + YES + UINavigationController + UIWindow + + + + YES + + YES + navigationController + window + + + YES + + navigationController + UINavigationController + + + window + UIWindow + + + + + IBProjectSource + Classes/ShareKitAppDelegate.h + + + + + 0 + IBCocoaTouchFramework + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + + + YES + ShareKit.xcodeproj + 3 + 117 + + diff --git a/RootViewController.xib b/RootViewController.xib new file mode 100644 index 00000000..de9ed051 --- /dev/null +++ b/RootViewController.xib @@ -0,0 +1,384 @@ + + + + 784 + 10D541 + 760 + 1038.29 + 460.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 81 + + + YES + + + + YES + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + YES + + YES + + + YES + + + + YES + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 274 + {320, 247} + + + 3 + MQA + + NO + YES + NO + IBCocoaTouchFramework + NO + 1 + 0 + YES + 44 + 22 + 22 + + + + + YES + + + view + + + + 3 + + + + dataSource + + + + 4 + + + + delegate + + + + 5 + + + + + YES + + 0 + + + + + + -1 + + + File's Owner + + + -2 + + + + + 2 + + + + + + + YES + + YES + -1.CustomClassName + -2.CustomClassName + 2.IBEditorWindowLastContentRect + 2.IBPluginDependency + + + YES + RootViewController + UIResponder + {{144, 609}, {320, 247}} + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + YES + + + YES + + + + + YES + + + YES + + + + 5 + + + + YES + + RootViewController + UITableViewController + + IBProjectSource + Classes/RootViewController.h + + + + + YES + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSError.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSFileManager.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueCoding.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueObserving.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyedArchiver.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSNetServices.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSObject.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSPort.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSRunLoop.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSStream.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSThread.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURL.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURLConnection.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSXMLParser.h + + + + NSObject + + IBFrameworkSource + UIKit.framework/Headers/UIAccessibility.h + + + + NSObject + + IBFrameworkSource + UIKit.framework/Headers/UINibLoading.h + + + + NSObject + + IBFrameworkSource + UIKit.framework/Headers/UIResponder.h + + + + UIResponder + NSObject + + + + UIScrollView + UIView + + IBFrameworkSource + UIKit.framework/Headers/UIScrollView.h + + + + UISearchBar + UIView + + IBFrameworkSource + UIKit.framework/Headers/UISearchBar.h + + + + UISearchDisplayController + NSObject + + IBFrameworkSource + UIKit.framework/Headers/UISearchDisplayController.h + + + + UITableView + UIScrollView + + IBFrameworkSource + UIKit.framework/Headers/UITableView.h + + + + UITableViewController + UIViewController + + IBFrameworkSource + UIKit.framework/Headers/UITableViewController.h + + + + UIView + + IBFrameworkSource + UIKit.framework/Headers/UITextField.h + + + + UIView + UIResponder + + IBFrameworkSource + UIKit.framework/Headers/UIView.h + + + + UIViewController + + IBFrameworkSource + UIKit.framework/Headers/UINavigationController.h + + + + UIViewController + + IBFrameworkSource + UIKit.framework/Headers/UITabBarController.h + + + + UIViewController + UIResponder + + IBFrameworkSource + UIKit.framework/Headers/UIViewController.h + + + + + 0 + IBCocoaTouchFramework + + com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + + + YES + ShareKit.xcodeproj + 3 + 81 + + diff --git a/ShareKit-Info.plist b/ShareKit-Info.plist new file mode 100644 index 00000000..d5dc4bd3 --- /dev/null +++ b/ShareKit-Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.yourcompany.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + NSMainNibFile + MainWindow + NSMainNibFile~ipad + MainWindow-iPad + + diff --git a/ShareKit.xcodeproj/project.pbxproj b/ShareKit.xcodeproj/project.pbxproj new file mode 100755 index 00000000..8afa3bd1 --- /dev/null +++ b/ShareKit.xcodeproj/project.pbxproj @@ -0,0 +1,991 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 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 */; }; + 2892E4100DC94CBA00A64D0F /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2892E40F0DC94CBA00A64D0F /* CoreGraphics.framework */; }; + 28AD73600D9D9599002E5188 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28AD735F0D9D9599002E5188 /* MainWindow.xib */; }; + 28F335F11007B36200424DE2 /* RootViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28F335F01007B36200424DE2 /* RootViewController.xib */; }; + 4312CF7C11CB33E200E61D7A /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4312CF7B11CB33E200E61D7A /* MessageUI.framework */; }; + 43150A8D11E78697008C6B68 /* SHKInstapaper.m in Sources */ = {isa = PBXBuildFile; fileRef = 43150A8C11E78697008C6B68 /* SHKInstapaper.m */; }; + 432B147C11FF4B0700291B37 /* SHKPhotoAlbum.m in Sources */ = {isa = PBXBuildFile; fileRef = 432B147B11FF4B0700291B37 /* SHKPhotoAlbum.m */; }; + 43A5370011DBE3B9004A1712 /* SHKOAuthSharer.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5367511DBE3B9004A1712 /* SHKOAuthSharer.m */; }; + 43A5370111DBE3B9004A1712 /* SHKSharer.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5367711DBE3B9004A1712 /* SHKSharer.m */; }; + 43A5370211DBE3B9004A1712 /* UIWebView+SHK.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5367A11DBE3B9004A1712 /* UIWebView+SHK.m */; }; + 43A5370311DBE3B9004A1712 /* SFHFKeychainUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5367E11DBE3B9004A1712 /* SFHFKeychainUtils.m */; }; + 43A5370411DBE3B9004A1712 /* NSMutableURLRequest+Parameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5368211DBE3B9004A1712 /* NSMutableURLRequest+Parameters.m */; }; + 43A5370511DBE3B9004A1712 /* NSString+URLEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5368411DBE3B9004A1712 /* NSString+URLEncoding.m */; }; + 43A5370611DBE3B9004A1712 /* NSURL+Base.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5368611DBE3B9004A1712 /* NSURL+Base.m */; }; + 43A5370711DBE3B9004A1712 /* Base64Transcoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 43A5368811DBE3B9004A1712 /* Base64Transcoder.c */; }; + 43A5370811DBE3B9004A1712 /* hmac.c in Sources */ = {isa = PBXBuildFile; fileRef = 43A5368A11DBE3B9004A1712 /* hmac.c */; }; + 43A5370911DBE3B9004A1712 /* sha1.c in Sources */ = {isa = PBXBuildFile; fileRef = 43A5368C11DBE3B9004A1712 /* sha1.c */; }; + 43A5370A11DBE3B9004A1712 /* OAAsynchronousDataFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5368F11DBE3B9004A1712 /* OAAsynchronousDataFetcher.m */; }; + 43A5370B11DBE3B9004A1712 /* OAConsumer.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5369111DBE3B9004A1712 /* OAConsumer.m */; }; + 43A5370C11DBE3B9004A1712 /* OADataFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5369311DBE3B9004A1712 /* OADataFetcher.m */; }; + 43A5370D11DBE3B9004A1712 /* OAHMAC_SHA1SignatureProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5369511DBE3B9004A1712 /* OAHMAC_SHA1SignatureProvider.m */; }; + 43A5370E11DBE3B9004A1712 /* OAMutableURLRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5369711DBE3B9004A1712 /* OAMutableURLRequest.m */; }; + 43A5370F11DBE3B9004A1712 /* OAPlaintextSignatureProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5369911DBE3B9004A1712 /* OAPlaintextSignatureProvider.m */; }; + 43A5371111DBE3B9004A1712 /* OARequestParameter.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5369D11DBE3B9004A1712 /* OARequestParameter.m */; }; + 43A5371211DBE3B9004A1712 /* OAServiceTicket.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5369F11DBE3B9004A1712 /* OAServiceTicket.m */; }; + 43A5371311DBE3B9004A1712 /* OAToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536A211DBE3B9004A1712 /* OAToken.m */; }; + 43A5371411DBE3B9004A1712 /* SHKRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536A511DBE3B9004A1712 /* SHKRequest.m */; }; + 43A5371511DBE3B9004A1712 /* SHK.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536A711DBE3B9004A1712 /* SHK.m */; }; + 43A5371611DBE3B9004A1712 /* SHKItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536A911DBE3B9004A1712 /* SHKItem.m */; }; + 43A5371711DBE3B9004A1712 /* SHKOfflineSharer.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536AB11DBE3B9004A1712 /* SHKOfflineSharer.m */; }; + 43A5371811DBE3B9004A1712 /* SHKSharers.plist in Resources */ = {isa = PBXBuildFile; fileRef = 43A536AC11DBE3B9004A1712 /* SHKSharers.plist */; }; + 43A5371911DBE3B9004A1712 /* SHKCustomFormController.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536AF11DBE3B9004A1712 /* SHKCustomFormController.m */; }; + 43A5371A11DBE3B9004A1712 /* SHKCustomFormFieldCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536B111DBE3B9004A1712 /* SHKCustomFormFieldCell.m */; }; + 43A5371B11DBE3B9004A1712 /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536B411DBE3B9004A1712 /* Reachability.m */; }; + 43A5371C11DBE3B9004A1712 /* SHKCopy.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536B911DBE3B9004A1712 /* SHKCopy.m */; }; + 43A5371D11DBE3B9004A1712 /* SHKMail.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536BC11DBE3B9004A1712 /* SHKMail.m */; }; + 43A5371E11DBE3B9004A1712 /* SHKSafari.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536BF11DBE3B9004A1712 /* SHKSafari.m */; }; + 43A5371F11DBE3B9004A1712 /* SHKDelicious.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536C311DBE3B9004A1712 /* SHKDelicious.m */; }; + 43A5372011DBE3B9004A1712 /* FBConnect.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 43A536C611DBE3B9004A1712 /* FBConnect.bundle */; }; + 43A5372111DBE3B9004A1712 /* FBConnectGlobal.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536C911DBE3B9004A1712 /* FBConnectGlobal.m */; }; + 43A5372211DBE3B9004A1712 /* FBDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536CB11DBE3B9004A1712 /* FBDialog.m */; }; + 43A5372311DBE3B9004A1712 /* FBFeedDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536CD11DBE3B9004A1712 /* FBFeedDialog.m */; }; + 43A5372411DBE3B9004A1712 /* FBLoginButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536CF11DBE3B9004A1712 /* FBLoginButton.m */; }; + 43A5372511DBE3B9004A1712 /* FBLoginDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536D111DBE3B9004A1712 /* FBLoginDialog.m */; }; + 43A5372611DBE3B9004A1712 /* FBPermissionDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536D311DBE3B9004A1712 /* FBPermissionDialog.m */; }; + 43A5372711DBE3B9004A1712 /* FBRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536D511DBE3B9004A1712 /* FBRequest.m */; }; + 43A5372811DBE3B9004A1712 /* FBSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536D711DBE3B9004A1712 /* FBSession.m */; }; + 43A5372911DBE3B9004A1712 /* FBStreamDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536D911DBE3B9004A1712 /* FBStreamDialog.m */; }; + 43A5372A11DBE3B9004A1712 /* FBXMLHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536DB11DBE3B9004A1712 /* FBXMLHandler.m */; }; + 43A5372B11DBE3B9004A1712 /* SHKFacebook.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536DD11DBE3B9004A1712 /* SHKFacebook.m */; }; + 43A5372C11DBE3B9004A1712 /* SHKGoogleReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536E011DBE3B9004A1712 /* SHKGoogleReader.m */; }; + 43A5372D11DBE3B9004A1712 /* SHKPinboard.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536E311DBE3B9004A1712 /* SHKPinboard.m */; }; + 43A5372E11DBE3B9004A1712 /* SHKReadItLater.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536E611DBE3B9004A1712 /* SHKReadItLater.m */; }; + 43A5372F11DBE3B9004A1712 /* SHKTwitter.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536E911DBE3B9004A1712 /* SHKTwitter.m */; }; + 43A5373011DBE3B9004A1712 /* SHKTwitterForm.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536EB11DBE3B9004A1712 /* SHKTwitterForm.m */; }; + 43A5373111DBE3B9004A1712 /* SHKActionSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536EF11DBE3B9004A1712 /* SHKActionSheet.m */; }; + 43A5373211DBE3B9004A1712 /* SHKActivityIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536F111DBE3B9004A1712 /* SHKActivityIndicator.m */; }; + 43A5373311DBE3B9004A1712 /* SHKFormController.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536F311DBE3B9004A1712 /* SHKFormController.m */; }; + 43A5373411DBE3B9004A1712 /* SHKFormFieldCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536F511DBE3B9004A1712 /* SHKFormFieldCell.m */; }; + 43A5373511DBE3B9004A1712 /* SHKFormFieldSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536F711DBE3B9004A1712 /* SHKFormFieldSettings.m */; }; + 43A5373711DBE3B9004A1712 /* SHKOAuthView.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536FB11DBE3B9004A1712 /* SHKOAuthView.m */; }; + 43A5373811DBE3B9004A1712 /* SHKShareMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536FD11DBE3B9004A1712 /* SHKShareMenu.m */; }; + 43A5373911DBE3B9004A1712 /* SHKViewControllerWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A536FF11DBE3B9004A1712 /* SHKViewControllerWrapper.m */; }; + 43A5382811DBE480004A1712 /* example.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 43A5382611DBE480004A1712 /* example.pdf */; }; + 43A5382911DBE480004A1712 /* sanFran.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 43A5382711DBE480004A1712 /* sanFran.jpg */; }; + 43A5383611DBE493004A1712 /* ExampleShareFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5382B11DBE493004A1712 /* ExampleShareFile.m */; }; + 43A5383711DBE493004A1712 /* ExampleShareImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5382D11DBE493004A1712 /* ExampleShareImage.m */; }; + 43A5383811DBE493004A1712 /* ExampleShareLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5382F11DBE493004A1712 /* ExampleShareLink.m */; }; + 43A5383911DBE493004A1712 /* ExampleShareText.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5383111DBE493004A1712 /* ExampleShareText.m */; }; + 43A5383A11DBE493004A1712 /* RootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5383311DBE493004A1712 /* RootViewController.m */; }; + 43A5383B11DBE493004A1712 /* ShareKitAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5383511DBE493004A1712 /* ShareKitAppDelegate.m */; }; + 43A53C0911DC07A9004A1712 /* SHKCustomShareMenuCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A53C0811DC07A9004A1712 /* SHKCustomShareMenuCell.m */; }; + 43A53C1211DC08B1004A1712 /* SHKCustomShareMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A53C1111DC08B1004A1712 /* SHKCustomShareMenu.m */; }; + 43B934B511FE682600C9D3F3 /* SHKFBStreamDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = 43B934B411FE682600C9D3F3 /* SHKFBStreamDialog.m */; }; + 43C91D1D11EB963600F31FAE /* MainWindow-iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 43C91D1C11EB963600F31FAE /* MainWindow-iPad.xib */; }; + 43C91DF511EBAE4800F31FAE /* SHKTumblr.m in Sources */ = {isa = PBXBuildFile; fileRef = 43C91DF411EBAE4800F31FAE /* SHKTumblr.m */; }; + 43D1DEF011D5CDD200550D75 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43D1DEEF11D5CDD200550D75 /* SystemConfiguration.framework */; }; + 43EF406E11D3FFF800B1F700 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43EF406D11D3FFF800B1F700 /* Security.framework */; }; + 43FF9C7412270E9F00ADE53C /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43FF9C7212270E9F00ADE53C /* Localizable.strings */; }; + 7A6206941290046B00C638F3 /* SHKLicorize.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A6206931290046B00C638F3 /* SHKLicorize.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 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; }; + 2892E40F0DC94CBA00A64D0F /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 28A0AAE50D9B0CCF005BE974 /* ShareKit_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShareKit_Prefix.pch; sourceTree = ""; }; + 28AD735F0D9D9599002E5188 /* MainWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow.xib; sourceTree = ""; }; + 28F335F01007B36200424DE2 /* RootViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RootViewController.xib; sourceTree = ""; }; + 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 4312CF7B11CB33E200E61D7A /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; }; + 43150A8B11E78697008C6B68 /* SHKInstapaper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKInstapaper.h; sourceTree = ""; }; + 43150A8C11E78697008C6B68 /* SHKInstapaper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKInstapaper.m; sourceTree = ""; }; + 432B147A11FF4B0700291B37 /* SHKPhotoAlbum.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKPhotoAlbum.h; sourceTree = ""; }; + 432B147B11FF4B0700291B37 /* SHKPhotoAlbum.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKPhotoAlbum.m; sourceTree = ""; }; + 43A5367411DBE3B9004A1712 /* SHKOAuthSharer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKOAuthSharer.h; sourceTree = ""; }; + 43A5367511DBE3B9004A1712 /* SHKOAuthSharer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKOAuthSharer.m; sourceTree = ""; }; + 43A5367611DBE3B9004A1712 /* SHKSharer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKSharer.h; sourceTree = ""; }; + 43A5367711DBE3B9004A1712 /* SHKSharer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKSharer.m; sourceTree = ""; }; + 43A5367911DBE3B9004A1712 /* UIWebView+SHK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIWebView+SHK.h"; sourceTree = ""; }; + 43A5367A11DBE3B9004A1712 /* UIWebView+SHK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIWebView+SHK.m"; sourceTree = ""; }; + 43A5367D11DBE3B9004A1712 /* SFHFKeychainUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SFHFKeychainUtils.h; sourceTree = ""; }; + 43A5367E11DBE3B9004A1712 /* SFHFKeychainUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SFHFKeychainUtils.m; sourceTree = ""; }; + 43A5368111DBE3B9004A1712 /* NSMutableURLRequest+Parameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableURLRequest+Parameters.h"; sourceTree = ""; }; + 43A5368211DBE3B9004A1712 /* NSMutableURLRequest+Parameters.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableURLRequest+Parameters.m"; sourceTree = ""; }; + 43A5368311DBE3B9004A1712 /* NSString+URLEncoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+URLEncoding.h"; sourceTree = ""; }; + 43A5368411DBE3B9004A1712 /* NSString+URLEncoding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+URLEncoding.m"; sourceTree = ""; }; + 43A5368511DBE3B9004A1712 /* NSURL+Base.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURL+Base.h"; sourceTree = ""; }; + 43A5368611DBE3B9004A1712 /* NSURL+Base.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURL+Base.m"; sourceTree = ""; }; + 43A5368811DBE3B9004A1712 /* Base64Transcoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Base64Transcoder.c; sourceTree = ""; }; + 43A5368911DBE3B9004A1712 /* Base64Transcoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Base64Transcoder.h; sourceTree = ""; }; + 43A5368A11DBE3B9004A1712 /* hmac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hmac.c; sourceTree = ""; }; + 43A5368B11DBE3B9004A1712 /* hmac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hmac.h; sourceTree = ""; }; + 43A5368C11DBE3B9004A1712 /* sha1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sha1.c; sourceTree = ""; }; + 43A5368D11DBE3B9004A1712 /* sha1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sha1.h; sourceTree = ""; }; + 43A5368E11DBE3B9004A1712 /* OAAsynchronousDataFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OAAsynchronousDataFetcher.h; sourceTree = ""; }; + 43A5368F11DBE3B9004A1712 /* OAAsynchronousDataFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OAAsynchronousDataFetcher.m; sourceTree = ""; }; + 43A5369011DBE3B9004A1712 /* OAConsumer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OAConsumer.h; sourceTree = ""; }; + 43A5369111DBE3B9004A1712 /* OAConsumer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OAConsumer.m; sourceTree = ""; }; + 43A5369211DBE3B9004A1712 /* OADataFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OADataFetcher.h; sourceTree = ""; }; + 43A5369311DBE3B9004A1712 /* OADataFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OADataFetcher.m; sourceTree = ""; }; + 43A5369411DBE3B9004A1712 /* OAHMAC_SHA1SignatureProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OAHMAC_SHA1SignatureProvider.h; sourceTree = ""; }; + 43A5369511DBE3B9004A1712 /* OAHMAC_SHA1SignatureProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OAHMAC_SHA1SignatureProvider.m; sourceTree = ""; }; + 43A5369611DBE3B9004A1712 /* OAMutableURLRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OAMutableURLRequest.h; sourceTree = ""; }; + 43A5369711DBE3B9004A1712 /* OAMutableURLRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OAMutableURLRequest.m; sourceTree = ""; }; + 43A5369811DBE3B9004A1712 /* OAPlaintextSignatureProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OAPlaintextSignatureProvider.h; sourceTree = ""; }; + 43A5369911DBE3B9004A1712 /* OAPlaintextSignatureProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OAPlaintextSignatureProvider.m; sourceTree = ""; }; + 43A5369C11DBE3B9004A1712 /* OARequestParameter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OARequestParameter.h; sourceTree = ""; }; + 43A5369D11DBE3B9004A1712 /* OARequestParameter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OARequestParameter.m; sourceTree = ""; }; + 43A5369E11DBE3B9004A1712 /* OAServiceTicket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OAServiceTicket.h; sourceTree = ""; }; + 43A5369F11DBE3B9004A1712 /* OAServiceTicket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OAServiceTicket.m; sourceTree = ""; }; + 43A536A011DBE3B9004A1712 /* OASignatureProviding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OASignatureProviding.h; sourceTree = ""; }; + 43A536A111DBE3B9004A1712 /* OAToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OAToken.h; sourceTree = ""; }; + 43A536A211DBE3B9004A1712 /* OAToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OAToken.m; sourceTree = ""; }; + 43A536A311DBE3B9004A1712 /* OAuthConsumer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OAuthConsumer.h; sourceTree = ""; }; + 43A536A411DBE3B9004A1712 /* SHKRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKRequest.h; sourceTree = ""; }; + 43A536A511DBE3B9004A1712 /* SHKRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKRequest.m; sourceTree = ""; }; + 43A536A611DBE3B9004A1712 /* SHK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHK.h; sourceTree = ""; }; + 43A536A711DBE3B9004A1712 /* SHK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHK.m; sourceTree = ""; }; + 43A536A811DBE3B9004A1712 /* SHKItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKItem.h; sourceTree = ""; }; + 43A536A911DBE3B9004A1712 /* SHKItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKItem.m; sourceTree = ""; }; + 43A536AA11DBE3B9004A1712 /* SHKOfflineSharer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKOfflineSharer.h; sourceTree = ""; }; + 43A536AB11DBE3B9004A1712 /* SHKOfflineSharer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKOfflineSharer.m; sourceTree = ""; }; + 43A536AC11DBE3B9004A1712 /* SHKSharers.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = SHKSharers.plist; sourceTree = ""; }; + 43A536AE11DBE3B9004A1712 /* SHKCustomFormController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKCustomFormController.h; sourceTree = ""; }; + 43A536AF11DBE3B9004A1712 /* SHKCustomFormController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKCustomFormController.m; sourceTree = ""; }; + 43A536B011DBE3B9004A1712 /* SHKCustomFormFieldCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKCustomFormFieldCell.h; sourceTree = ""; }; + 43A536B111DBE3B9004A1712 /* SHKCustomFormFieldCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKCustomFormFieldCell.m; sourceTree = ""; }; + 43A536B311DBE3B9004A1712 /* Reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = ""; }; + 43A536B411DBE3B9004A1712 /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = ""; }; + 43A536B811DBE3B9004A1712 /* SHKCopy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKCopy.h; sourceTree = ""; }; + 43A536B911DBE3B9004A1712 /* SHKCopy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKCopy.m; sourceTree = ""; }; + 43A536BB11DBE3B9004A1712 /* SHKMail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKMail.h; sourceTree = ""; }; + 43A536BC11DBE3B9004A1712 /* SHKMail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKMail.m; sourceTree = ""; }; + 43A536BE11DBE3B9004A1712 /* SHKSafari.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKSafari.h; sourceTree = ""; }; + 43A536BF11DBE3B9004A1712 /* SHKSafari.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKSafari.m; sourceTree = ""; }; + 43A536C211DBE3B9004A1712 /* SHKDelicious.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKDelicious.h; sourceTree = ""; }; + 43A536C311DBE3B9004A1712 /* SHKDelicious.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKDelicious.m; sourceTree = ""; }; + 43A536C611DBE3B9004A1712 /* FBConnect.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = FBConnect.bundle; sourceTree = ""; }; + 43A536C711DBE3B9004A1712 /* FBConnect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBConnect.h; sourceTree = ""; }; + 43A536C811DBE3B9004A1712 /* FBConnectGlobal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBConnectGlobal.h; sourceTree = ""; }; + 43A536C911DBE3B9004A1712 /* FBConnectGlobal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBConnectGlobal.m; sourceTree = ""; }; + 43A536CA11DBE3B9004A1712 /* FBDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBDialog.h; sourceTree = ""; }; + 43A536CB11DBE3B9004A1712 /* FBDialog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBDialog.m; sourceTree = ""; }; + 43A536CC11DBE3B9004A1712 /* FBFeedDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBFeedDialog.h; sourceTree = ""; }; + 43A536CD11DBE3B9004A1712 /* FBFeedDialog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBFeedDialog.m; sourceTree = ""; }; + 43A536CE11DBE3B9004A1712 /* FBLoginButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBLoginButton.h; sourceTree = ""; }; + 43A536CF11DBE3B9004A1712 /* FBLoginButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBLoginButton.m; sourceTree = ""; }; + 43A536D011DBE3B9004A1712 /* FBLoginDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBLoginDialog.h; sourceTree = ""; }; + 43A536D111DBE3B9004A1712 /* FBLoginDialog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBLoginDialog.m; sourceTree = ""; }; + 43A536D211DBE3B9004A1712 /* FBPermissionDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBPermissionDialog.h; sourceTree = ""; }; + 43A536D311DBE3B9004A1712 /* FBPermissionDialog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBPermissionDialog.m; sourceTree = ""; }; + 43A536D411DBE3B9004A1712 /* FBRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBRequest.h; sourceTree = ""; }; + 43A536D511DBE3B9004A1712 /* FBRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBRequest.m; sourceTree = ""; }; + 43A536D611DBE3B9004A1712 /* FBSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSession.h; sourceTree = ""; }; + 43A536D711DBE3B9004A1712 /* FBSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSession.m; sourceTree = ""; }; + 43A536D811DBE3B9004A1712 /* FBStreamDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBStreamDialog.h; sourceTree = ""; }; + 43A536D911DBE3B9004A1712 /* FBStreamDialog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBStreamDialog.m; sourceTree = ""; }; + 43A536DA11DBE3B9004A1712 /* FBXMLHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBXMLHandler.h; sourceTree = ""; }; + 43A536DB11DBE3B9004A1712 /* FBXMLHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBXMLHandler.m; sourceTree = ""; }; + 43A536DC11DBE3B9004A1712 /* SHKFacebook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKFacebook.h; sourceTree = ""; }; + 43A536DD11DBE3B9004A1712 /* SHKFacebook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKFacebook.m; sourceTree = ""; }; + 43A536DF11DBE3B9004A1712 /* SHKGoogleReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKGoogleReader.h; sourceTree = ""; }; + 43A536E011DBE3B9004A1712 /* SHKGoogleReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKGoogleReader.m; sourceTree = ""; }; + 43A536E211DBE3B9004A1712 /* SHKPinboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKPinboard.h; sourceTree = ""; }; + 43A536E311DBE3B9004A1712 /* SHKPinboard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKPinboard.m; sourceTree = ""; }; + 43A536E511DBE3B9004A1712 /* SHKReadItLater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKReadItLater.h; sourceTree = ""; }; + 43A536E611DBE3B9004A1712 /* SHKReadItLater.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKReadItLater.m; sourceTree = ""; }; + 43A536E811DBE3B9004A1712 /* SHKTwitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKTwitter.h; sourceTree = ""; }; + 43A536E911DBE3B9004A1712 /* SHKTwitter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKTwitter.m; sourceTree = ""; }; + 43A536EA11DBE3B9004A1712 /* SHKTwitterForm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKTwitterForm.h; sourceTree = ""; }; + 43A536EB11DBE3B9004A1712 /* SHKTwitterForm.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKTwitterForm.m; sourceTree = ""; }; + 43A536EC11DBE3B9004A1712 /* SHKConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKConfig.h; sourceTree = ""; }; + 43A536EE11DBE3B9004A1712 /* SHKActionSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKActionSheet.h; sourceTree = ""; }; + 43A536EF11DBE3B9004A1712 /* SHKActionSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKActionSheet.m; sourceTree = ""; }; + 43A536F011DBE3B9004A1712 /* SHKActivityIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKActivityIndicator.h; sourceTree = ""; }; + 43A536F111DBE3B9004A1712 /* SHKActivityIndicator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKActivityIndicator.m; sourceTree = ""; }; + 43A536F211DBE3B9004A1712 /* SHKFormController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKFormController.h; sourceTree = ""; }; + 43A536F311DBE3B9004A1712 /* SHKFormController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKFormController.m; sourceTree = ""; }; + 43A536F411DBE3B9004A1712 /* SHKFormFieldCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKFormFieldCell.h; sourceTree = ""; }; + 43A536F511DBE3B9004A1712 /* SHKFormFieldCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKFormFieldCell.m; sourceTree = ""; }; + 43A536F611DBE3B9004A1712 /* SHKFormFieldSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKFormFieldSettings.h; sourceTree = ""; }; + 43A536F711DBE3B9004A1712 /* SHKFormFieldSettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKFormFieldSettings.m; sourceTree = ""; }; + 43A536FA11DBE3B9004A1712 /* SHKOAuthView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKOAuthView.h; sourceTree = ""; }; + 43A536FB11DBE3B9004A1712 /* SHKOAuthView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKOAuthView.m; sourceTree = ""; }; + 43A536FC11DBE3B9004A1712 /* SHKShareMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKShareMenu.h; sourceTree = ""; }; + 43A536FD11DBE3B9004A1712 /* SHKShareMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKShareMenu.m; sourceTree = ""; }; + 43A536FE11DBE3B9004A1712 /* SHKViewControllerWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKViewControllerWrapper.h; sourceTree = ""; }; + 43A536FF11DBE3B9004A1712 /* SHKViewControllerWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKViewControllerWrapper.m; sourceTree = ""; }; + 43A5382611DBE480004A1712 /* example.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = example.pdf; path = Classes/Example/example.pdf; sourceTree = ""; }; + 43A5382711DBE480004A1712 /* sanFran.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; name = sanFran.jpg; path = Classes/Example/sanFran.jpg; sourceTree = ""; }; + 43A5382A11DBE493004A1712 /* ExampleShareFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ExampleShareFile.h; path = Example/ExampleShareFile.h; sourceTree = ""; }; + 43A5382B11DBE493004A1712 /* ExampleShareFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ExampleShareFile.m; path = Example/ExampleShareFile.m; sourceTree = ""; }; + 43A5382C11DBE493004A1712 /* ExampleShareImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ExampleShareImage.h; path = Example/ExampleShareImage.h; sourceTree = ""; }; + 43A5382D11DBE493004A1712 /* ExampleShareImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ExampleShareImage.m; path = Example/ExampleShareImage.m; sourceTree = ""; }; + 43A5382E11DBE493004A1712 /* ExampleShareLink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ExampleShareLink.h; path = Example/ExampleShareLink.h; sourceTree = ""; }; + 43A5382F11DBE493004A1712 /* ExampleShareLink.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ExampleShareLink.m; path = Example/ExampleShareLink.m; sourceTree = ""; }; + 43A5383011DBE493004A1712 /* ExampleShareText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ExampleShareText.h; path = Example/ExampleShareText.h; sourceTree = ""; }; + 43A5383111DBE493004A1712 /* ExampleShareText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ExampleShareText.m; path = Example/ExampleShareText.m; sourceTree = ""; }; + 43A5383211DBE493004A1712 /* RootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RootViewController.h; path = Example/RootViewController.h; sourceTree = ""; }; + 43A5383311DBE493004A1712 /* RootViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RootViewController.m; path = Example/RootViewController.m; sourceTree = ""; }; + 43A5383411DBE493004A1712 /* ShareKitAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ShareKitAppDelegate.h; path = Example/ShareKitAppDelegate.h; sourceTree = ""; }; + 43A5383511DBE493004A1712 /* ShareKitAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ShareKitAppDelegate.m; path = Example/ShareKitAppDelegate.m; sourceTree = ""; }; + 43A53C0711DC07A9004A1712 /* SHKCustomShareMenuCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKCustomShareMenuCell.h; sourceTree = ""; }; + 43A53C0811DC07A9004A1712 /* SHKCustomShareMenuCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKCustomShareMenuCell.m; sourceTree = ""; }; + 43A53C1011DC08B1004A1712 /* SHKCustomShareMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKCustomShareMenu.h; sourceTree = ""; }; + 43A53C1111DC08B1004A1712 /* SHKCustomShareMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKCustomShareMenu.m; sourceTree = ""; }; + 43B934B311FE682600C9D3F3 /* SHKFBStreamDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKFBStreamDialog.h; sourceTree = ""; }; + 43B934B411FE682600C9D3F3 /* SHKFBStreamDialog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKFBStreamDialog.m; sourceTree = ""; }; + 43C91D1C11EB963600F31FAE /* MainWindow-iPad.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "MainWindow-iPad.xib"; path = "Resources-iPad/MainWindow-iPad.xib"; sourceTree = ""; }; + 43C91DF311EBAE4800F31FAE /* SHKTumblr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKTumblr.h; sourceTree = ""; }; + 43C91DF411EBAE4800F31FAE /* SHKTumblr.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKTumblr.m; sourceTree = ""; }; + 43D1DEEF11D5CDD200550D75 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; + 43EF406D11D3FFF800B1F700 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + 43FF9C7312270E9F00ADE53C /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = Classes/ShareKit/Localization/en.lproj/Localizable.strings; sourceTree = SOURCE_ROOT; }; + 43FF9C7512270EB900ADE53C /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = Classes/ShareKit/Localization/de.lproj/Localizable.strings; sourceTree = SOURCE_ROOT; }; + 7A6206921290046B00C638F3 /* SHKLicorize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKLicorize.h; sourceTree = ""; }; + 7A6206931290046B00C638F3 /* SHKLicorize.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKLicorize.m; sourceTree = ""; }; + 8D1107310486CEB800E47090 /* ShareKit-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "ShareKit-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, + 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, + 2892E4100DC94CBA00A64D0F /* CoreGraphics.framework in Frameworks */, + 4312CF7C11CB33E200E61D7A /* MessageUI.framework in Frameworks */, + 43EF406E11D3FFF800B1F700 /* Security.framework in Frameworks */, + 43D1DEF011D5CDD200550D75 /* SystemConfiguration.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 080E96DDFE201D6D7F000001 /* Classes */ = { + isa = PBXGroup; + children = ( + 43A5367111DBE3B9004A1712 /* ShareKit */, + 4362EB3311B9937300E3DB3A /* Example Project */, + ); + path = Classes; + sourceTree = ""; + }; + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 1D6058910D05DD3D006BFB54 /* ShareKit.app */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + isa = PBXGroup; + children = ( + 080E96DDFE201D6D7F000001 /* Classes */, + 29B97315FDCFA39411CA2CEA /* Other Sources */, + 29B97317FDCFA39411CA2CEA /* Resources */, + 43C91D1B11EB963600F31FAE /* Resources-iPad */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = CustomTemplate; + sourceTree = ""; + }; + 29B97315FDCFA39411CA2CEA /* Other Sources */ = { + isa = PBXGroup; + children = ( + 28A0AAE50D9B0CCF005BE974 /* ShareKit_Prefix.pch */, + 29B97316FDCFA39411CA2CEA /* main.m */, + ); + name = "Other Sources"; + sourceTree = ""; + }; + 29B97317FDCFA39411CA2CEA /* Resources */ = { + isa = PBXGroup; + children = ( + 43A5382611DBE480004A1712 /* example.pdf */, + 43A5382711DBE480004A1712 /* sanFran.jpg */, + 28F335F01007B36200424DE2 /* RootViewController.xib */, + 28AD735F0D9D9599002E5188 /* MainWindow.xib */, + 8D1107310486CEB800E47090 /* ShareKit-Info.plist */, + ); + name = Resources; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, + 1D30AB110D05D00D00671497 /* Foundation.framework */, + 2892E40F0DC94CBA00A64D0F /* CoreGraphics.framework */, + 4312CF7B11CB33E200E61D7A /* MessageUI.framework */, + 43EF406D11D3FFF800B1F700 /* Security.framework */, + 43D1DEEF11D5CDD200550D75 /* SystemConfiguration.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 43150A8A11E78697008C6B68 /* Instapaper */ = { + isa = PBXGroup; + children = ( + 43150A8B11E78697008C6B68 /* SHKInstapaper.h */, + 43150A8C11E78697008C6B68 /* SHKInstapaper.m */, + ); + path = Instapaper; + sourceTree = ""; + }; + 432B147911FF4B0700291B37 /* Save to Album */ = { + isa = PBXGroup; + children = ( + 432B147A11FF4B0700291B37 /* SHKPhotoAlbum.h */, + 432B147B11FF4B0700291B37 /* SHKPhotoAlbum.m */, + ); + path = "Save to Album"; + sourceTree = ""; + }; + 4362EB3311B9937300E3DB3A /* Example Project */ = { + isa = PBXGroup; + children = ( + 43A5382A11DBE493004A1712 /* ExampleShareFile.h */, + 43A5382B11DBE493004A1712 /* ExampleShareFile.m */, + 43A5382C11DBE493004A1712 /* ExampleShareImage.h */, + 43A5382D11DBE493004A1712 /* ExampleShareImage.m */, + 43A5382E11DBE493004A1712 /* ExampleShareLink.h */, + 43A5382F11DBE493004A1712 /* ExampleShareLink.m */, + 43A5383011DBE493004A1712 /* ExampleShareText.h */, + 43A5383111DBE493004A1712 /* ExampleShareText.m */, + 43A5383211DBE493004A1712 /* RootViewController.h */, + 43A5383311DBE493004A1712 /* RootViewController.m */, + 43A5383411DBE493004A1712 /* ShareKitAppDelegate.h */, + 43A5383511DBE493004A1712 /* ShareKitAppDelegate.m */, + ); + name = "Example Project"; + sourceTree = ""; + }; + 43A5367111DBE3B9004A1712 /* ShareKit */ = { + isa = PBXGroup; + children = ( + 43A536EC11DBE3B9004A1712 /* SHKConfig.h */, + 43A536AD11DBE3B9004A1712 /* Customize UI */, + 43A5367211DBE3B9004A1712 /* Core */, + 43A536B511DBE3B9004A1712 /* Sharers */, + 43A536ED11DBE3B9004A1712 /* UI */, + 43A536B211DBE3B9004A1712 /* Reachability */, + ); + path = ShareKit; + sourceTree = ""; + }; + 43A5367211DBE3B9004A1712 /* Core */ = { + isa = PBXGroup; + children = ( + 43A536AC11DBE3B9004A1712 /* SHKSharers.plist */, + 43A536A611DBE3B9004A1712 /* SHK.h */, + 43A536A711DBE3B9004A1712 /* SHK.m */, + 43A536A811DBE3B9004A1712 /* SHKItem.h */, + 43A536A911DBE3B9004A1712 /* SHKItem.m */, + 43A536AA11DBE3B9004A1712 /* SHKOfflineSharer.h */, + 43A536AB11DBE3B9004A1712 /* SHKOfflineSharer.m */, + 43A5367311DBE3B9004A1712 /* Base Sharer Classes */, + 43A5367811DBE3B9004A1712 /* Categories */, + 43A5367B11DBE3B9004A1712 /* Helpers */, + 43FF9C7112270E7F00ADE53C /* Localization */, + ); + path = Core; + sourceTree = ""; + }; + 43A5367311DBE3B9004A1712 /* Base Sharer Classes */ = { + isa = PBXGroup; + children = ( + 43A5367411DBE3B9004A1712 /* SHKOAuthSharer.h */, + 43A5367511DBE3B9004A1712 /* SHKOAuthSharer.m */, + 43A5367611DBE3B9004A1712 /* SHKSharer.h */, + 43A5367711DBE3B9004A1712 /* SHKSharer.m */, + ); + path = "Base Sharer Classes"; + sourceTree = ""; + }; + 43A5367811DBE3B9004A1712 /* Categories */ = { + isa = PBXGroup; + children = ( + 43A5367911DBE3B9004A1712 /* UIWebView+SHK.h */, + 43A5367A11DBE3B9004A1712 /* UIWebView+SHK.m */, + ); + path = Categories; + sourceTree = ""; + }; + 43A5367B11DBE3B9004A1712 /* Helpers */ = { + isa = PBXGroup; + children = ( + 43A5367C11DBE3B9004A1712 /* Keychain */, + 43A5367F11DBE3B9004A1712 /* OAuth */, + 43A536A411DBE3B9004A1712 /* SHKRequest.h */, + 43A536A511DBE3B9004A1712 /* SHKRequest.m */, + ); + path = Helpers; + sourceTree = ""; + }; + 43A5367C11DBE3B9004A1712 /* Keychain */ = { + isa = PBXGroup; + children = ( + 43A5367D11DBE3B9004A1712 /* SFHFKeychainUtils.h */, + 43A5367E11DBE3B9004A1712 /* SFHFKeychainUtils.m */, + ); + path = Keychain; + sourceTree = ""; + }; + 43A5367F11DBE3B9004A1712 /* OAuth */ = { + isa = PBXGroup; + children = ( + 43A5368011DBE3B9004A1712 /* Categories */, + 43A5368711DBE3B9004A1712 /* Crytpo */, + 43A5368E11DBE3B9004A1712 /* OAAsynchronousDataFetcher.h */, + 43A5368F11DBE3B9004A1712 /* OAAsynchronousDataFetcher.m */, + 43A5369011DBE3B9004A1712 /* OAConsumer.h */, + 43A5369111DBE3B9004A1712 /* OAConsumer.m */, + 43A5369211DBE3B9004A1712 /* OADataFetcher.h */, + 43A5369311DBE3B9004A1712 /* OADataFetcher.m */, + 43A5369411DBE3B9004A1712 /* OAHMAC_SHA1SignatureProvider.h */, + 43A5369511DBE3B9004A1712 /* OAHMAC_SHA1SignatureProvider.m */, + 43A5369611DBE3B9004A1712 /* OAMutableURLRequest.h */, + 43A5369711DBE3B9004A1712 /* OAMutableURLRequest.m */, + 43A5369811DBE3B9004A1712 /* OAPlaintextSignatureProvider.h */, + 43A5369911DBE3B9004A1712 /* OAPlaintextSignatureProvider.m */, + 43A5369C11DBE3B9004A1712 /* OARequestParameter.h */, + 43A5369D11DBE3B9004A1712 /* OARequestParameter.m */, + 43A5369E11DBE3B9004A1712 /* OAServiceTicket.h */, + 43A5369F11DBE3B9004A1712 /* OAServiceTicket.m */, + 43A536A011DBE3B9004A1712 /* OASignatureProviding.h */, + 43A536A111DBE3B9004A1712 /* OAToken.h */, + 43A536A211DBE3B9004A1712 /* OAToken.m */, + 43A536A311DBE3B9004A1712 /* OAuthConsumer.h */, + ); + path = OAuth; + sourceTree = ""; + }; + 43A5368011DBE3B9004A1712 /* Categories */ = { + isa = PBXGroup; + children = ( + 43A5368111DBE3B9004A1712 /* NSMutableURLRequest+Parameters.h */, + 43A5368211DBE3B9004A1712 /* NSMutableURLRequest+Parameters.m */, + 43A5368311DBE3B9004A1712 /* NSString+URLEncoding.h */, + 43A5368411DBE3B9004A1712 /* NSString+URLEncoding.m */, + 43A5368511DBE3B9004A1712 /* NSURL+Base.h */, + 43A5368611DBE3B9004A1712 /* NSURL+Base.m */, + ); + path = Categories; + sourceTree = ""; + }; + 43A5368711DBE3B9004A1712 /* Crytpo */ = { + isa = PBXGroup; + children = ( + 43A5368811DBE3B9004A1712 /* Base64Transcoder.c */, + 43A5368911DBE3B9004A1712 /* Base64Transcoder.h */, + 43A5368A11DBE3B9004A1712 /* hmac.c */, + 43A5368B11DBE3B9004A1712 /* hmac.h */, + 43A5368C11DBE3B9004A1712 /* sha1.c */, + 43A5368D11DBE3B9004A1712 /* sha1.h */, + ); + path = Crytpo; + sourceTree = ""; + }; + 43A536AD11DBE3B9004A1712 /* Customize UI */ = { + isa = PBXGroup; + children = ( + 43A53C0B11DC0835004A1712 /* Form */, + 43A53C0A11DC080C004A1712 /* Share Menu */, + ); + path = "Customize UI"; + sourceTree = ""; + }; + 43A536B211DBE3B9004A1712 /* Reachability */ = { + isa = PBXGroup; + children = ( + 43A536B311DBE3B9004A1712 /* Reachability.h */, + 43A536B411DBE3B9004A1712 /* Reachability.m */, + ); + path = Reachability; + sourceTree = ""; + }; + 43A536B511DBE3B9004A1712 /* Sharers */ = { + isa = PBXGroup; + children = ( + 43A536B611DBE3B9004A1712 /* Actions */, + 43A536C011DBE3B9004A1712 /* Services */, + ); + path = Sharers; + sourceTree = ""; + }; + 43A536B611DBE3B9004A1712 /* Actions */ = { + isa = PBXGroup; + children = ( + 43A536B711DBE3B9004A1712 /* Copy */, + 43A536BA11DBE3B9004A1712 /* Email */, + 43A536BD11DBE3B9004A1712 /* Open in Safari */, + 432B147911FF4B0700291B37 /* Save to Album */, + ); + path = Actions; + sourceTree = ""; + }; + 43A536B711DBE3B9004A1712 /* Copy */ = { + isa = PBXGroup; + children = ( + 43A536B811DBE3B9004A1712 /* SHKCopy.h */, + 43A536B911DBE3B9004A1712 /* SHKCopy.m */, + ); + path = Copy; + sourceTree = ""; + }; + 43A536BA11DBE3B9004A1712 /* Email */ = { + isa = PBXGroup; + children = ( + 43A536BB11DBE3B9004A1712 /* SHKMail.h */, + 43A536BC11DBE3B9004A1712 /* SHKMail.m */, + ); + path = Email; + sourceTree = ""; + }; + 43A536BD11DBE3B9004A1712 /* Open in Safari */ = { + isa = PBXGroup; + children = ( + 43A536BE11DBE3B9004A1712 /* SHKSafari.h */, + 43A536BF11DBE3B9004A1712 /* SHKSafari.m */, + ); + path = "Open in Safari"; + sourceTree = ""; + }; + 43A536C011DBE3B9004A1712 /* Services */ = { + isa = PBXGroup; + children = ( + 7A6206911290046B00C638F3 /* Licorize */, + 43A536C111DBE3B9004A1712 /* Delicious */, + 43A536C411DBE3B9004A1712 /* Facebook */, + 43A536DE11DBE3B9004A1712 /* Google Reader */, + 43150A8A11E78697008C6B68 /* Instapaper */, + 43A536E111DBE3B9004A1712 /* Pinboard */, + 43A536E411DBE3B9004A1712 /* Read It Later */, + 43C91DF211EBAE4800F31FAE /* Tumblr */, + 43A536E711DBE3B9004A1712 /* Twitter */, + ); + path = Services; + sourceTree = ""; + }; + 43A536C111DBE3B9004A1712 /* Delicious */ = { + isa = PBXGroup; + children = ( + 43A536C211DBE3B9004A1712 /* SHKDelicious.h */, + 43A536C311DBE3B9004A1712 /* SHKDelicious.m */, + ); + path = Delicious; + sourceTree = ""; + }; + 43A536C411DBE3B9004A1712 /* Facebook */ = { + isa = PBXGroup; + children = ( + 43A536C511DBE3B9004A1712 /* FBConnect */, + 43A536DC11DBE3B9004A1712 /* SHKFacebook.h */, + 43A536DD11DBE3B9004A1712 /* SHKFacebook.m */, + 43B934B311FE682600C9D3F3 /* SHKFBStreamDialog.h */, + 43B934B411FE682600C9D3F3 /* SHKFBStreamDialog.m */, + ); + path = Facebook; + sourceTree = ""; + }; + 43A536C511DBE3B9004A1712 /* FBConnect */ = { + isa = PBXGroup; + children = ( + 43A536C611DBE3B9004A1712 /* FBConnect.bundle */, + 43A536C711DBE3B9004A1712 /* FBConnect.h */, + 43A536C811DBE3B9004A1712 /* FBConnectGlobal.h */, + 43A536C911DBE3B9004A1712 /* FBConnectGlobal.m */, + 43A536CA11DBE3B9004A1712 /* FBDialog.h */, + 43A536CB11DBE3B9004A1712 /* FBDialog.m */, + 43A536CC11DBE3B9004A1712 /* FBFeedDialog.h */, + 43A536CD11DBE3B9004A1712 /* FBFeedDialog.m */, + 43A536CE11DBE3B9004A1712 /* FBLoginButton.h */, + 43A536CF11DBE3B9004A1712 /* FBLoginButton.m */, + 43A536D011DBE3B9004A1712 /* FBLoginDialog.h */, + 43A536D111DBE3B9004A1712 /* FBLoginDialog.m */, + 43A536D211DBE3B9004A1712 /* FBPermissionDialog.h */, + 43A536D311DBE3B9004A1712 /* FBPermissionDialog.m */, + 43A536D411DBE3B9004A1712 /* FBRequest.h */, + 43A536D511DBE3B9004A1712 /* FBRequest.m */, + 43A536D611DBE3B9004A1712 /* FBSession.h */, + 43A536D711DBE3B9004A1712 /* FBSession.m */, + 43A536D811DBE3B9004A1712 /* FBStreamDialog.h */, + 43A536D911DBE3B9004A1712 /* FBStreamDialog.m */, + 43A536DA11DBE3B9004A1712 /* FBXMLHandler.h */, + 43A536DB11DBE3B9004A1712 /* FBXMLHandler.m */, + ); + path = FBConnect; + sourceTree = ""; + }; + 43A536DE11DBE3B9004A1712 /* Google Reader */ = { + isa = PBXGroup; + children = ( + 43A536DF11DBE3B9004A1712 /* SHKGoogleReader.h */, + 43A536E011DBE3B9004A1712 /* SHKGoogleReader.m */, + ); + path = "Google Reader"; + sourceTree = ""; + }; + 43A536E111DBE3B9004A1712 /* Pinboard */ = { + isa = PBXGroup; + children = ( + 43A536E211DBE3B9004A1712 /* SHKPinboard.h */, + 43A536E311DBE3B9004A1712 /* SHKPinboard.m */, + ); + path = Pinboard; + sourceTree = ""; + }; + 43A536E411DBE3B9004A1712 /* Read It Later */ = { + isa = PBXGroup; + children = ( + 43A536E511DBE3B9004A1712 /* SHKReadItLater.h */, + 43A536E611DBE3B9004A1712 /* SHKReadItLater.m */, + ); + path = "Read It Later"; + sourceTree = ""; + }; + 43A536E711DBE3B9004A1712 /* Twitter */ = { + isa = PBXGroup; + children = ( + 43A536E811DBE3B9004A1712 /* SHKTwitter.h */, + 43A536E911DBE3B9004A1712 /* SHKTwitter.m */, + 43A536EA11DBE3B9004A1712 /* SHKTwitterForm.h */, + 43A536EB11DBE3B9004A1712 /* SHKTwitterForm.m */, + ); + path = Twitter; + sourceTree = ""; + }; + 43A536ED11DBE3B9004A1712 /* UI */ = { + isa = PBXGroup; + children = ( + 43A536EE11DBE3B9004A1712 /* SHKActionSheet.h */, + 43A536EF11DBE3B9004A1712 /* SHKActionSheet.m */, + 43A536F011DBE3B9004A1712 /* SHKActivityIndicator.h */, + 43A536F111DBE3B9004A1712 /* SHKActivityIndicator.m */, + 43A536F211DBE3B9004A1712 /* SHKFormController.h */, + 43A536F311DBE3B9004A1712 /* SHKFormController.m */, + 43A536F411DBE3B9004A1712 /* SHKFormFieldCell.h */, + 43A536F511DBE3B9004A1712 /* SHKFormFieldCell.m */, + 43A536F611DBE3B9004A1712 /* SHKFormFieldSettings.h */, + 43A536F711DBE3B9004A1712 /* SHKFormFieldSettings.m */, + 43A536FC11DBE3B9004A1712 /* SHKShareMenu.h */, + 43A536FD11DBE3B9004A1712 /* SHKShareMenu.m */, + 43A536FA11DBE3B9004A1712 /* SHKOAuthView.h */, + 43A536FB11DBE3B9004A1712 /* SHKOAuthView.m */, + 43A536FE11DBE3B9004A1712 /* SHKViewControllerWrapper.h */, + 43A536FF11DBE3B9004A1712 /* SHKViewControllerWrapper.m */, + ); + path = UI; + sourceTree = ""; + }; + 43A53C0A11DC080C004A1712 /* Share Menu */ = { + isa = PBXGroup; + children = ( + 43A53C1011DC08B1004A1712 /* SHKCustomShareMenu.h */, + 43A53C1111DC08B1004A1712 /* SHKCustomShareMenu.m */, + 43A53C0711DC07A9004A1712 /* SHKCustomShareMenuCell.h */, + 43A53C0811DC07A9004A1712 /* SHKCustomShareMenuCell.m */, + ); + name = "Share Menu"; + sourceTree = ""; + }; + 43A53C0B11DC0835004A1712 /* Form */ = { + isa = PBXGroup; + children = ( + 43A536AE11DBE3B9004A1712 /* SHKCustomFormController.h */, + 43A536AF11DBE3B9004A1712 /* SHKCustomFormController.m */, + 43A536B011DBE3B9004A1712 /* SHKCustomFormFieldCell.h */, + 43A536B111DBE3B9004A1712 /* SHKCustomFormFieldCell.m */, + ); + name = Form; + sourceTree = ""; + }; + 43C91D1B11EB963600F31FAE /* Resources-iPad */ = { + isa = PBXGroup; + children = ( + 43C91D1C11EB963600F31FAE /* MainWindow-iPad.xib */, + ); + name = "Resources-iPad"; + sourceTree = ""; + }; + 43C91DF211EBAE4800F31FAE /* Tumblr */ = { + isa = PBXGroup; + children = ( + 43C91DF311EBAE4800F31FAE /* SHKTumblr.h */, + 43C91DF411EBAE4800F31FAE /* SHKTumblr.m */, + ); + path = Tumblr; + sourceTree = ""; + }; + 43FF9C7112270E7F00ADE53C /* Localization */ = { + isa = PBXGroup; + children = ( + 43FF9C7212270E9F00ADE53C /* Localizable.strings */, + ); + name = Localization; + sourceTree = ""; + }; + 7A6206911290046B00C638F3 /* Licorize */ = { + isa = PBXGroup; + children = ( + 7A6206921290046B00C638F3 /* SHKLicorize.h */, + 7A6206931290046B00C638F3 /* SHKLicorize.m */, + ); + path = Licorize; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D6058900D05DD3D006BFB54 /* ShareKit */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "ShareKit" */; + buildPhases = ( + 1D60588D0D05DD3D006BFB54 /* Resources */, + 1D60588E0D05DD3D006BFB54 /* Sources */, + 1D60588F0D05DD3D006BFB54 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ShareKit; + productName = ShareKit; + productReference = 1D6058910D05DD3D006BFB54 /* ShareKit.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "ShareKit" */; + compatibilityVersion = "Xcode 3.1"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + en, + de, + ); + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D6058900D05DD3D006BFB54 /* ShareKit */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 1D60588D0D05DD3D006BFB54 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 28AD73600D9D9599002E5188 /* MainWindow.xib in Resources */, + 28F335F11007B36200424DE2 /* RootViewController.xib in Resources */, + 43A5371811DBE3B9004A1712 /* SHKSharers.plist in Resources */, + 43A5372011DBE3B9004A1712 /* FBConnect.bundle in Resources */, + 43A5382811DBE480004A1712 /* example.pdf in Resources */, + 43A5382911DBE480004A1712 /* sanFran.jpg in Resources */, + 43C91D1D11EB963600F31FAE /* MainWindow-iPad.xib in Resources */, + 43FF9C7412270E9F00ADE53C /* Localizable.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D60588E0D05DD3D006BFB54 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D60589B0D05DD56006BFB54 /* main.m in Sources */, + 43A5370011DBE3B9004A1712 /* SHKOAuthSharer.m in Sources */, + 43A5370111DBE3B9004A1712 /* SHKSharer.m in Sources */, + 43A5370211DBE3B9004A1712 /* UIWebView+SHK.m in Sources */, + 43A5370311DBE3B9004A1712 /* SFHFKeychainUtils.m in Sources */, + 43A5370411DBE3B9004A1712 /* NSMutableURLRequest+Parameters.m in Sources */, + 43A5370511DBE3B9004A1712 /* NSString+URLEncoding.m in Sources */, + 43A5370611DBE3B9004A1712 /* NSURL+Base.m in Sources */, + 43A5370711DBE3B9004A1712 /* Base64Transcoder.c in Sources */, + 43A5370811DBE3B9004A1712 /* hmac.c in Sources */, + 43A5370911DBE3B9004A1712 /* sha1.c in Sources */, + 43A5370A11DBE3B9004A1712 /* OAAsynchronousDataFetcher.m in Sources */, + 43A5370B11DBE3B9004A1712 /* OAConsumer.m in Sources */, + 43A5370C11DBE3B9004A1712 /* OADataFetcher.m in Sources */, + 43A5370D11DBE3B9004A1712 /* OAHMAC_SHA1SignatureProvider.m in Sources */, + 43A5370E11DBE3B9004A1712 /* OAMutableURLRequest.m in Sources */, + 43A5370F11DBE3B9004A1712 /* OAPlaintextSignatureProvider.m in Sources */, + 43A5371111DBE3B9004A1712 /* OARequestParameter.m in Sources */, + 43A5371211DBE3B9004A1712 /* OAServiceTicket.m in Sources */, + 43A5371311DBE3B9004A1712 /* OAToken.m in Sources */, + 43A5371411DBE3B9004A1712 /* SHKRequest.m in Sources */, + 43A5371511DBE3B9004A1712 /* SHK.m in Sources */, + 43A5371611DBE3B9004A1712 /* SHKItem.m in Sources */, + 43A5371711DBE3B9004A1712 /* SHKOfflineSharer.m in Sources */, + 43A5371911DBE3B9004A1712 /* SHKCustomFormController.m in Sources */, + 43A5371A11DBE3B9004A1712 /* SHKCustomFormFieldCell.m in Sources */, + 43A5371B11DBE3B9004A1712 /* Reachability.m in Sources */, + 43A5371C11DBE3B9004A1712 /* SHKCopy.m in Sources */, + 43A5371D11DBE3B9004A1712 /* SHKMail.m in Sources */, + 43A5371E11DBE3B9004A1712 /* SHKSafari.m in Sources */, + 43A5371F11DBE3B9004A1712 /* SHKDelicious.m in Sources */, + 43A5372111DBE3B9004A1712 /* FBConnectGlobal.m in Sources */, + 43A5372211DBE3B9004A1712 /* FBDialog.m in Sources */, + 43A5372311DBE3B9004A1712 /* FBFeedDialog.m in Sources */, + 43A5372411DBE3B9004A1712 /* FBLoginButton.m in Sources */, + 43A5372511DBE3B9004A1712 /* FBLoginDialog.m in Sources */, + 43A5372611DBE3B9004A1712 /* FBPermissionDialog.m in Sources */, + 43A5372711DBE3B9004A1712 /* FBRequest.m in Sources */, + 43A5372811DBE3B9004A1712 /* FBSession.m in Sources */, + 43A5372911DBE3B9004A1712 /* FBStreamDialog.m in Sources */, + 43A5372A11DBE3B9004A1712 /* FBXMLHandler.m in Sources */, + 43A5372B11DBE3B9004A1712 /* SHKFacebook.m in Sources */, + 43A5372C11DBE3B9004A1712 /* SHKGoogleReader.m in Sources */, + 43A5372D11DBE3B9004A1712 /* SHKPinboard.m in Sources */, + 43A5372E11DBE3B9004A1712 /* SHKReadItLater.m in Sources */, + 43A5372F11DBE3B9004A1712 /* SHKTwitter.m in Sources */, + 43A5373011DBE3B9004A1712 /* SHKTwitterForm.m in Sources */, + 43A5373111DBE3B9004A1712 /* SHKActionSheet.m in Sources */, + 43A5373211DBE3B9004A1712 /* SHKActivityIndicator.m in Sources */, + 43A5373311DBE3B9004A1712 /* SHKFormController.m in Sources */, + 43A5373411DBE3B9004A1712 /* SHKFormFieldCell.m in Sources */, + 43A5373511DBE3B9004A1712 /* SHKFormFieldSettings.m in Sources */, + 43A5373711DBE3B9004A1712 /* SHKOAuthView.m in Sources */, + 43A5373811DBE3B9004A1712 /* SHKShareMenu.m in Sources */, + 43A5373911DBE3B9004A1712 /* SHKViewControllerWrapper.m in Sources */, + 43A5383611DBE493004A1712 /* ExampleShareFile.m in Sources */, + 43A5383711DBE493004A1712 /* ExampleShareImage.m in Sources */, + 43A5383811DBE493004A1712 /* ExampleShareLink.m in Sources */, + 43A5383911DBE493004A1712 /* ExampleShareText.m in Sources */, + 43A5383A11DBE493004A1712 /* RootViewController.m in Sources */, + 43A5383B11DBE493004A1712 /* ShareKitAppDelegate.m in Sources */, + 43A53C0911DC07A9004A1712 /* SHKCustomShareMenuCell.m in Sources */, + 43A53C1211DC08B1004A1712 /* SHKCustomShareMenu.m in Sources */, + 43150A8D11E78697008C6B68 /* SHKInstapaper.m in Sources */, + 43C91DF511EBAE4800F31FAE /* SHKTumblr.m in Sources */, + 43B934B511FE682600C9D3F3 /* SHKFBStreamDialog.m in Sources */, + 432B147C11FF4B0700291B37 /* SHKPhotoAlbum.m in Sources */, + 7A6206941290046B00C638F3 /* SHKLicorize.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 43FF9C7212270E9F00ADE53C /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 43FF9C7312270E9F00ADE53C /* en */, + 43FF9C7512270EB900ADE53C /* de */, + ); + name = Localizable.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 1D6058940D05DD3E006BFB54 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = ShareKit_Prefix.pch; + INFOPLIST_FILE = "ShareKit-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 3.2; + PRODUCT_NAME = ShareKit; + SDKROOT = iphoneos4.1; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 1D6058950D05DD3E006BFB54 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + COPY_PHASE_STRIP = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = ShareKit_Prefix.pch; + INFOPLIST_FILE = "ShareKit-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 3.2; + PRODUCT_NAME = ShareKit; + SDKROOT = iphoneos4.1; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 3.1; + PREBINDING = NO; + SDKROOT = iphoneos4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 3.1; + OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; + PREBINDING = NO; + SDKROOT = iphoneos4.1; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "ShareKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D6058940D05DD3E006BFB54 /* Debug */, + 1D6058950D05DD3E006BFB54 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "ShareKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/ShareKit_Prefix.pch b/ShareKit_Prefix.pch new file mode 100644 index 00000000..b3c02d5f --- /dev/null +++ b/ShareKit_Prefix.pch @@ -0,0 +1,14 @@ +// +// Prefix header for all source files of the 'ShareKit' target in the 'ShareKit' project +// +#import + +#ifndef __IPHONE_3_0 +#warning "This project uses features only available in iPhone SDK 3.0 and later." +#endif + + +#ifdef __OBJC__ + #import + #import +#endif diff --git a/main.m b/main.m new file mode 100644 index 00000000..4018ee81 --- /dev/null +++ b/main.m @@ -0,0 +1,17 @@ +// +// main.m +// ShareKit +// +// Created by Nathan Weiner on 6/4/10. +// Copyright Idea Shower, LLC 2010. All rights reserved. +// + +#import + +int main(int argc, char *argv[]) { + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + int retVal = UIApplicationMain(argc, argv, nil, nil); + [pool release]; + return retVal; +}