mirror of https://github.com/axmolengine/axmol.git
2243 lines
92 KiB
Objective-C
Executable File
2243 lines
92 KiB
Objective-C
Executable File
//
|
|
// FHSTwitterEngine.m
|
|
// FHSTwitterEngine
|
|
//
|
|
// Created by Nathaniel Symer on 8/22/12.
|
|
// Copyright (C) 2012 Nathaniel Symer.
|
|
//
|
|
// 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 "FHSTwitterEngine.h"
|
|
|
|
#import "OAuthConsumer.h"
|
|
#import <QuartzCore/QuartzCore.h>
|
|
#import <SystemConfiguration/SystemConfiguration.h>
|
|
#import <sys/socket.h>
|
|
#import <netinet/in.h>
|
|
#import <ifaddrs.h>
|
|
#import <objc/runtime.h>
|
|
|
|
NSString * const FHSProfileBackgroundColorKey = @"profile_background_color";
|
|
NSString * const FHSProfileLinkColorKey = @"profile_link_color";
|
|
NSString * const FHSProfileSidebarBorderColorKey = @"profile_sidebar_border_color";
|
|
NSString * const FHSProfileSidebarFillColorKey = @"profile_sidebar_fill_color";
|
|
NSString * const FHSProfileTextColorKey = @"profile_text_color";
|
|
|
|
NSString * const FHSProfileNameKey = @"name";
|
|
NSString * const FHSProfileURLKey = @"url";
|
|
NSString * const FHSProfileLocationKey = @"location";
|
|
NSString * const FHSProfileDescriptionKey = @"description";
|
|
|
|
|
|
static NSString * const errorFourhundred = @"Bad Request: The request you are trying to make has missing or bad parameters.";
|
|
|
|
static NSString * const authBlockKey = @"FHSTwitterEngineOAuthCompletion";
|
|
|
|
static FHSTwitterEngine *sharedInstance = nil;
|
|
|
|
id removeNull(id rootObject) {
|
|
if ([rootObject isKindOfClass:[NSDictionary class]]) {
|
|
NSMutableDictionary *sanitizedDictionary = [NSMutableDictionary dictionaryWithDictionary:rootObject];
|
|
[rootObject enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
|
id sanitized = removeNull(obj);
|
|
if (!sanitized) {
|
|
[sanitizedDictionary setObject:@"" forKey:key];
|
|
} else {
|
|
[sanitizedDictionary setObject:sanitized forKey:key];
|
|
}
|
|
}];
|
|
return [NSMutableDictionary dictionaryWithDictionary:sanitizedDictionary];
|
|
}
|
|
|
|
if ([rootObject isKindOfClass:[NSArray class]]) {
|
|
NSMutableArray *sanitizedArray = [NSMutableArray arrayWithArray:rootObject];
|
|
[rootObject enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
|
|
id sanitized = removeNull(obj);
|
|
if (!sanitized) {
|
|
[sanitizedArray replaceObjectAtIndex:[sanitizedArray indexOfObject:obj] withObject:@""];
|
|
} else {
|
|
[sanitizedArray replaceObjectAtIndex:[sanitizedArray indexOfObject:obj] withObject:sanitized];
|
|
}
|
|
}];
|
|
return [NSMutableArray arrayWithArray:sanitizedArray];
|
|
}
|
|
|
|
if ([rootObject isKindOfClass:[NSNull class]]) {
|
|
return (id)nil;
|
|
} else {
|
|
return rootObject;
|
|
}
|
|
}
|
|
|
|
NSError * getBadRequestError() {
|
|
return [NSError errorWithDomain:errorFourhundred code:400 userInfo:nil];
|
|
}
|
|
|
|
NSError * getNilReturnLengthError() {
|
|
return [NSError errorWithDomain:@"Twitter successfully processed the request, but did not return any content" code:204 userInfo:nil];
|
|
}
|
|
|
|
@interface FHSTwitterEngineController : UIViewController <UIWebViewDelegate>
|
|
|
|
@property (nonatomic, retain) UINavigationBar *navBar;
|
|
@property (nonatomic, retain) UIView *blockerView;
|
|
@property (nonatomic, retain) UIToolbar *pinCopyBar;
|
|
|
|
//@property (nonatomic, retain) FHSTwitterEngine *engine;
|
|
@property (nonatomic, retain) UIWebView *theWebView;
|
|
@property (nonatomic, retain) OAToken *requestToken;
|
|
|
|
//- (id)initWithEngine:(FHSTwitterEngine *)theEngine;
|
|
- (NSString *)locatePin;
|
|
- (void)showPinCopyPrompt;
|
|
|
|
@end
|
|
|
|
@interface FHSTwitterEngine()
|
|
|
|
// Login stuff
|
|
- (NSString *)getRequestTokenString;
|
|
|
|
// General Get request sender
|
|
- (id)sendRequest:(NSURLRequest *)request;
|
|
|
|
// These are here to obfuscate them from prying eyes
|
|
@property (retain, nonatomic) OAConsumer *consumer;
|
|
@property (assign, nonatomic) BOOL shouldClearConsumer;
|
|
@property (retain, nonatomic) NSDateFormatter *dateFormatter;
|
|
|
|
@end
|
|
|
|
@implementation NSString (FHSTwitterEngine)
|
|
|
|
- (NSString *)fhs_trimForTwitter {
|
|
NSString *string = [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
|
return (string.length > 140)?[string substringToIndex:140]:string;
|
|
}
|
|
|
|
- (NSString *)fhs_stringWithRange:(NSRange)range {
|
|
return [[self substringFromIndex:range.location]substringToIndex:range.length];
|
|
}
|
|
|
|
- (BOOL)fhs_isNumeric {
|
|
const char *raw = (const char *)[self UTF8String];
|
|
|
|
for (int i = 0; i < strlen(raw); i++) {
|
|
if (raw[i] < '0' || raw[i] > '9') {
|
|
return NO;
|
|
}
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation FHSTwitterEngine
|
|
|
|
static NSString * const url_search_tweets = @"https://api.twitter.com/1.1/search/tweets.json";
|
|
|
|
static NSString * const url_users_search = @"https://api.twitter.com/1.1/users/search.json";
|
|
static NSString * const url_users_show = @"https://api.twitter.com/1.1/users/show.json";
|
|
static NSString * const url_users_report_spam = @"https://api.twitter.com/1.1/users/report_spam.json";
|
|
static NSString * const url_users_lookup = @"https://api.twitter.com/1.1/users/lookup.json";
|
|
|
|
static NSString * const url_lists_create = @"https://api.twitter.com/1.1/lists/create.json";
|
|
static NSString * const url_lists_show = @"https://api.twitter.com/1.1/lists/show.json";
|
|
static NSString * const url_lists_update = @"https://api.twitter.com/1.1/lists/update.json";
|
|
static NSString * const url_lists_members = @"https://api.twitter.com/1.1/lists/members.json";
|
|
static NSString * const url_lists_members_destroy_all = @"https://api.twitter.com/1.1/lists/members/destroy_all.json";
|
|
static NSString * const url_lists_members_create_all = @"https://api.twitter.com/1.1/lists/members/create_all.json";
|
|
static NSString * const url_lists_statuses = @"https://api.twitter.com/1.1/lists/statuses.json";
|
|
static NSString * const url_lists_list = @"https://api.twitter.com/1.1/lists/list.json";
|
|
|
|
static NSString * const url_statuses_home_timeline = @"https://api.twitter.com/1.1/statuses/home_timeline.json";
|
|
static NSString * const url_statuses_update = @"https://api.twitter.com/1.1/statuses/update.json";
|
|
static NSString * const url_statuses_retweets_of_me = @"https://api.twitter.com/1.1/statuses/retweets_of_me.json";
|
|
static NSString * const url_statuses_user_timeline = @"https://api.twitter.com/1.1/statuses/user_timeline.json";
|
|
static NSString * const url_statuses_metions_timeline = @"https://api.twitter.com/1.1/statuses/mentions_timeline.json";
|
|
static NSString * const url_statuses_update_with_media = @"https://api.twitter.com/1.1/statuses/update_with_media.json";
|
|
static NSString * const url_statuses_destroy = @"https://api.twitter.com/1.1/statuses/destroy.json";
|
|
static NSString * const url_statuses_show = @"https://api.twitter.com/1.1/statuses/show.json";
|
|
static NSString * const url_statuses_oembed = @"https://api.twitter.com/1.1/statuses/oembed.json";
|
|
|
|
static NSString * const url_blocks_exists = @"https://api.twitter.com/1.1/blocks/exists.json";
|
|
static NSString * const url_blocks_blocking = @"https://api.twitter.com/1.1/blocks/blocking.json";
|
|
static NSString * const url_blocks_blocking_ids = @"https://api.twitter.com/1.1/blocks/blocking/ids.json";
|
|
static NSString * const url_blocks_destroy = @"https://api.twitter.com/1.1/blocks/destroy.json";
|
|
static NSString * const url_blocks_create = @"https://api.twitter.com/1.1/blocks/create.json";
|
|
|
|
static NSString * const url_help_languages = @"https://api.twitter.com/1.1/help/languages.json";
|
|
static NSString * const url_help_configuration = @"https://api.twitter.com/1.1/help/configuration.json";
|
|
static NSString * const url_help_privacy = @"https://api.twitter.com/1.1/help/privacy.json";
|
|
static NSString * const url_help_tos = @"https://api.twitter.com/1.1/help/tos.json";
|
|
static NSString * const url_help_test = @"https://api.twitter.com/1.1/help/test.json";
|
|
|
|
static NSString * const url_direct_messages_show = @"https://api.twitter.com/1.1/direct_messages/show.json";
|
|
static NSString * const url_direct_messages_new = @"https://api.twitter.com/1.1/direct_messages/new.json";
|
|
static NSString * const url_direct_messages_sent = @"https://api.twitter.com/1.1/direct_messages/sent.json";
|
|
static NSString * const url_direct_messages_destroy = @"https://api.twitter.com/1.1/direct_messages/destroy.json";
|
|
static NSString * const url_direct_messages = @"https://api.twitter.com/1.1/direct_messages.json";
|
|
|
|
static NSString * const url_friendships_no_retweets_ids = @"https://api.twitter.com/1.1/friendships/no_retweets/ids.json";
|
|
static NSString * const url_friendships_update = @"https://api.twitter.com/1.1/friendships/update.json";
|
|
static NSString * const url_friendships_outgoing = @"https://api.twitter.com/1.1/friendships/outgoing.json";
|
|
static NSString * const url_friendships_incoming = @"https://api.twitter.com/1.1/friendships/incoming.json";
|
|
static NSString * const url_friendships_lookup = @"https://api.twitter.com/1.1/friendships/lookup.json";
|
|
static NSString * const url_friendships_destroy = @"https://api.twitter.com/1.1/friendships/destroy.json";
|
|
static NSString * const url_friendships_create = @"https://api.twitter.com/1.1/friendships/create.json";
|
|
|
|
static NSString * const url_account_verify_credentials = @"https://api.twitter.com/1.1/account/verify_credentials.json";
|
|
static NSString * const url_account_update_profile_colors = @"https://api.twitter.com/1.1/account/update_profile_colors.json";
|
|
static NSString * const url_account_update_profile_background_image = @"https://api.twitter.com/1.1/account/update_profile_background_image.json";
|
|
static NSString * const url_account_update_profile_image = @"https://api.twitter.com/1.1/account/update_profile_image.json";
|
|
static NSString * const url_account_settings = @"https://api.twitter.com/1.1/account/settings.json";
|
|
static NSString * const url_account_update_profile = @"https://api.twitter.com/1.1/account/update_profile.json";
|
|
|
|
static NSString * const url_favorites_list = @"https://api.twitter.com/1.1/favorites/list.json";
|
|
static NSString * const url_favorites_create = @"https://api.twitter.com/1.1/favorites/create.json";
|
|
static NSString * const url_favorites_destroy = @"https://api.twitter.com/1.1/favorites/destroy.json";
|
|
|
|
static NSString * const url_application_rate_limit_status = @"https://api.twitter.com/1.1/application/rate_limit_status.json";
|
|
|
|
static NSString * const url_followers_ids = @"https://api.twitter.com/1.1/followers/ids.json";
|
|
static NSString * const url_followers_list = @"https://api.twitter.com/1.1/followers/list.json";
|
|
|
|
static NSString * const url_friends_ids = @"https://api.twitter.com/1.1/friends/ids.json";
|
|
static NSString * const url_friends_list = @"https://api.twitter.com/1.1/friends/list.json";
|
|
|
|
- (id)listFollowersForUser:(NSString *)user isID:(BOOL)isID withCursor:(NSString *)cursor {
|
|
|
|
if (user.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_friends_list];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *skipstatusP = [OARequestParameter requestParameterWithName:@"skip_status" value:@"true"];
|
|
OARequestParameter *include_entitiesP = [OARequestParameter requestParameterWithName:@"include_entities" value:self.includeEntities?@"true":@"false"];
|
|
OARequestParameter *screen_nameP = [OARequestParameter requestParameterWithName:isID?@"user_id":@"screen_name" value:user];
|
|
OARequestParameter *cursorP = [OARequestParameter requestParameterWithName:@"cursor" value:cursor];
|
|
return [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:include_entitiesP, skipstatusP, screen_nameP, cursorP, nil]];
|
|
}
|
|
|
|
- (id)listFriendsForUser:(NSString *)user isID:(BOOL)isID withCursor:(NSString *)cursor {
|
|
|
|
if (user.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_friends_list];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *skipstatusP = [OARequestParameter requestParameterWithName:@"skip_status" value:@"true"];
|
|
OARequestParameter *include_entitiesP = [OARequestParameter requestParameterWithName:@"include_entities" value:self.includeEntities?@"true":@"false"];
|
|
OARequestParameter *screen_nameP = [OARequestParameter requestParameterWithName:isID?@"user_id":@"screen_name" value:user];
|
|
OARequestParameter *cursorP = [OARequestParameter requestParameterWithName:@"cursor" value:cursor];
|
|
return [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:include_entitiesP, skipstatusP, screen_nameP, cursorP, nil]];
|
|
}
|
|
|
|
- (id)searchUsersWithQuery:(NSString *)q andCount:(int)count {
|
|
|
|
if (count == 0) {
|
|
return nil;
|
|
}
|
|
|
|
if (q.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
if (q.length > 1000) {
|
|
q = [q substringToIndex:1000];
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_users_search];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *include_entitiesP = [OARequestParameter requestParameterWithName:@"include_entities" value:self.includeEntities?@"true":@"false"];
|
|
OARequestParameter *countP = [OARequestParameter requestParameterWithName:@"count" value:[NSString stringWithFormat:@"%d",count]];
|
|
OARequestParameter *qP = [OARequestParameter requestParameterWithName:@"q" value:q];
|
|
return [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:include_entitiesP, countP, qP, nil]];
|
|
}
|
|
|
|
- (id)searchTweetsWithQuery:(NSString *)q count:(int)count resultType:(FHSTwitterEngineResultType)resultType unil:(NSDate *)untilDate sinceID:(NSString *)sinceID maxID:(NSString *)maxID {
|
|
|
|
if (count == 0) {
|
|
return nil;
|
|
}
|
|
|
|
if (q.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
if (q.length > 1000) {
|
|
q = [q substringToIndex:1000];
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_search_tweets];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *include_entitiesP = [OARequestParameter requestParameterWithName:@"include_entities" value:self.includeEntities?@"true":@"false"];
|
|
OARequestParameter *countP = [OARequestParameter requestParameterWithName:@"count" value:[NSString stringWithFormat:@"%d",count]];
|
|
OARequestParameter *untilP = [OARequestParameter requestParameterWithName:@"until" value:nil];
|
|
OARequestParameter *result_typeP = [OARequestParameter requestParameterWithName:@"result_type" value:nil];
|
|
OARequestParameter *qP = [OARequestParameter requestParameterWithName:@"q" value:q];
|
|
|
|
[self.dateFormatter setDateFormat:@"YYYY-MM-DD"];
|
|
NSString *untilString = [self.dateFormatter stringFromDate:untilDate];
|
|
[self.dateFormatter setDateFormat:@"EEE MMM dd HH:mm:ss ZZZZ yyyy"];
|
|
|
|
untilP.value = untilString;
|
|
|
|
if (resultType == FHSTwitterEngineResultTypeMixed) {
|
|
result_typeP.value = @"mixed";
|
|
} else if (resultType == FHSTwitterEngineResultTypeRecent) {
|
|
result_typeP.value = @"recent";
|
|
} else if (resultType == FHSTwitterEngineResultTypePopular) {
|
|
result_typeP.value = @"popular";
|
|
}
|
|
|
|
NSMutableArray *params = [NSMutableArray arrayWithObjects:countP, include_entitiesP, qP, nil];
|
|
|
|
if (maxID.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"max_id" value:maxID]];
|
|
}
|
|
|
|
if (sinceID.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"since_id" value:sinceID]];
|
|
}
|
|
|
|
return [self sendGETRequest:request withParameters:params];
|
|
}
|
|
|
|
- (NSError *)createListWithName:(NSString *)name isPrivate:(BOOL)isPrivate description:(NSString *)description {
|
|
|
|
if (name.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_lists_create];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *nameP = [OARequestParameter requestParameterWithName:@"name" value:name];
|
|
OARequestParameter *isPrivateP = [OARequestParameter requestParameterWithName:@"mode" value:isPrivate?@"private":@"public"];
|
|
|
|
NSMutableArray *params = [NSMutableArray arrayWithObjects:nameP, isPrivateP, nil];
|
|
|
|
if (description.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"description" value:description]];
|
|
}
|
|
|
|
return [self sendPOSTRequest:request withParameters:params];
|
|
}
|
|
|
|
- (id)getListWithID:(NSString *)listID {
|
|
|
|
if (listID.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_lists_show];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *listIDP = [OARequestParameter requestParameterWithName:@"list_id" value:listID];
|
|
return [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:listIDP, nil]];
|
|
}
|
|
|
|
- (NSError *)changeDescriptionOfListWithID:(NSString *)listID toDescription:(NSString *)newName {
|
|
|
|
if (listID.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
if (newName.length == 0 ) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_lists_update];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *listIDP = [OARequestParameter requestParameterWithName:@"list_id" value:listID];
|
|
OARequestParameter *nameP = [OARequestParameter requestParameterWithName:@"description" value:newName];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:listIDP, nameP, nil]];
|
|
}
|
|
|
|
- (NSError *)changeNameOfListWithID:(NSString *)listID toName:(NSString *)newName {
|
|
|
|
if (listID.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
if (newName.length == 0 ) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_lists_update];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *listIDP = [OARequestParameter requestParameterWithName:@"list_id" value:listID];
|
|
OARequestParameter *nameP = [OARequestParameter requestParameterWithName:@"name" value:newName];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:listIDP, nameP, nil]];
|
|
}
|
|
|
|
- (NSError *)setModeOfListWithID:(NSString *)listID toPrivate:(BOOL)isPrivate {
|
|
|
|
if (listID.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_lists_update];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *listIDP = [OARequestParameter requestParameterWithName:@"list_id" value:listID];
|
|
OARequestParameter *isPrivateP = [OARequestParameter requestParameterWithName:@"mode" value:isPrivate?@"private":@"public"];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:listIDP, isPrivateP, nil]];
|
|
}
|
|
|
|
- (id)listUsersInListWithID:(NSString *)listID {
|
|
|
|
if (listID.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_lists_members];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *listIDP = [OARequestParameter requestParameterWithName:@"list_id" value:listID];
|
|
return [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:listIDP, nil]];
|
|
}
|
|
|
|
- (NSError *)removeUsersFromListWithID:(NSString *)listID users:(NSArray *)users {
|
|
|
|
if (users.count == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
if (users.count > 100) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
if (listID.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_lists_members_destroy_all];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *screen_name = [OARequestParameter requestParameterWithName:@"screen_name" value:[users componentsJoinedByString:@","]];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:screen_name, nil]];
|
|
}
|
|
|
|
- (NSError *)addUsersToListWithID:(NSString *)listID users:(NSArray *)users {
|
|
|
|
if (users.count == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
if (users.count > 100) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
if (listID.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_lists_members_create_all];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *screen_name = [OARequestParameter requestParameterWithName:@"screen_name" value:[users componentsJoinedByString:@","]];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:screen_name, nil]];
|
|
}
|
|
|
|
- (id)getTimelineForListWithID:(NSString *)listID count:(int)count {
|
|
return [self getTimelineForListWithID:listID count:count sinceID:nil maxID:nil];
|
|
}
|
|
|
|
- (id)getTimelineForListWithID:(NSString *)listID count:(int)count sinceID:(NSString *)sinceID maxID:(NSString *)maxID {
|
|
return [self getTimelineForListWithID:listID count:count sinceID:sinceID maxID:maxID excludeRetweets:YES excludeReplies:YES];
|
|
}
|
|
|
|
- (id)getTimelineForListWithID:(NSString *)listID count:(int)count excludeRetweets:(BOOL)excludeRetweets excludeReplies:(BOOL)excludeReplies {
|
|
return [self getTimelineForListWithID:listID count:count sinceID:nil maxID:nil excludeRetweets:excludeRetweets excludeReplies:excludeReplies];
|
|
}
|
|
|
|
- (id)getTimelineForListWithID:(NSString *)listID count:(int)count sinceID:(NSString *)sinceID maxID:(NSString *)maxID excludeRetweets:(BOOL)excludeRetweets excludeReplies:(BOOL)excludeReplies {
|
|
|
|
if (count == 0) {
|
|
return nil;
|
|
}
|
|
|
|
if (listID.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_lists_statuses];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *countP = [OARequestParameter requestParameterWithName:@"count" value:[NSString stringWithFormat:@"%d",count]];
|
|
OARequestParameter *excludeRepliesP = [OARequestParameter requestParameterWithName:@"exclude_replies" value:excludeReplies?@"true":@"false"];
|
|
OARequestParameter *includeRTsP = [OARequestParameter requestParameterWithName:@"include_rts" value:excludeRetweets?@"false":@"true"];
|
|
OARequestParameter *listIDP = [OARequestParameter requestParameterWithName:@"list_id" value:listID];
|
|
|
|
NSMutableArray *params = [NSMutableArray arrayWithObjects:countP, excludeRepliesP, includeRTsP, listIDP, nil];
|
|
|
|
if (sinceID.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"since_id" value:sinceID]];
|
|
}
|
|
|
|
if (maxID.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"max_id" value:maxID]];
|
|
}
|
|
|
|
return [self sendGETRequest:request withParameters:params];
|
|
}
|
|
|
|
- (id)getListsForUser:(NSString *)user isID:(BOOL)isID {
|
|
|
|
if (user.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_lists_list];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *userP = [OARequestParameter requestParameterWithName:isID?@"user_id":@"screen_name" value:user];
|
|
return [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:userP, nil]];
|
|
}
|
|
|
|
- (id)getRetweetsForTweet:(NSString *)identifier count:(int)count {
|
|
|
|
if (count == 0) {
|
|
return nil;
|
|
}
|
|
|
|
if (identifier.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://api.twitter.com/1.1/statuses/retweets/%@.json",identifier]];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *identifierP = [OARequestParameter requestParameterWithName:@"count" value:[NSString stringWithFormat:@"%d",count]];
|
|
return [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:identifierP, nil]];
|
|
}
|
|
|
|
- (id)getRetweetedTimelineWithCount:(int)count {
|
|
return [self getRetweetedTimelineWithCount:count sinceID:nil maxID:nil];
|
|
}
|
|
|
|
- (id)getRetweetedTimelineWithCount:(int)count sinceID:(NSString *)sinceID maxID:(NSString *)maxID {
|
|
|
|
if (count == 0) {
|
|
return nil;
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_statuses_retweets_of_me];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *countP = [OARequestParameter requestParameterWithName:@"count" value:[NSString stringWithFormat:@"%d",count]];
|
|
OARequestParameter *excludeRepliesP = [OARequestParameter requestParameterWithName:@"exclude_replies" value:@"false"];
|
|
OARequestParameter *includeRTsP = [OARequestParameter requestParameterWithName:@"include_rts" value:@"true"];
|
|
|
|
NSMutableArray *params = [NSMutableArray array];
|
|
[params addObject:countP];
|
|
[params addObject:excludeRepliesP];
|
|
[params addObject:includeRTsP];
|
|
|
|
if (sinceID.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"since_id" value:sinceID]];
|
|
}
|
|
|
|
if (maxID.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"max_id" value:maxID]];
|
|
}
|
|
|
|
return [self sendGETRequest:request withParameters:params];
|
|
}
|
|
|
|
- (id)getMentionsTimelineWithCount:(int)count {
|
|
return [self getMentionsTimelineWithCount:count sinceID:nil maxID:nil];
|
|
}
|
|
|
|
- (id)getMentionsTimelineWithCount:(int)count sinceID:(NSString *)sinceID maxID:(NSString *)maxID {
|
|
|
|
if (count == 0) {
|
|
return nil;
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_statuses_metions_timeline];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *countP = [OARequestParameter requestParameterWithName:@"count" value:[NSString stringWithFormat:@"%d",count]];
|
|
OARequestParameter *excludeRepliesP = [OARequestParameter requestParameterWithName:@"exclude_replies" value:@"false"];
|
|
OARequestParameter *includeRTsP = [OARequestParameter requestParameterWithName:@"include_rts" value:@"true"];
|
|
|
|
NSMutableArray *params = [NSMutableArray array];
|
|
[params addObject:countP];
|
|
[params addObject:excludeRepliesP];
|
|
[params addObject:includeRTsP];
|
|
|
|
if (sinceID.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"since_id" value:sinceID]];
|
|
}
|
|
|
|
if (maxID.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"max_id" value:maxID]];
|
|
}
|
|
|
|
return [self sendGETRequest:request withParameters:params];
|
|
}
|
|
|
|
- (NSError *)postTweet:(NSString *)tweetString withImageData:(NSData *)theData {
|
|
return [self postTweet:tweetString withImageData:theData inReplyTo:nil];
|
|
}
|
|
|
|
- (NSError *)postTweet:(NSString *)tweetString withImageData:(NSData *)theData inReplyTo:(NSString *)irt {
|
|
|
|
if (tweetString.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
if (theData.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_statuses_update_with_media];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *statusP = [OARequestParameter requestParameterWithName:@"status" value:tweetString];
|
|
OARequestParameter *mediaP = [OARequestParameter requestParameterWithName:@"media_data[]" value:[theData base64EncodingWithLineLength:0]];
|
|
|
|
NSMutableArray *params = [NSMutableArray arrayWithObjects:statusP, mediaP, nil];
|
|
|
|
if (irt.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"in_reply_to_status_id" value:irt]];
|
|
}
|
|
|
|
return [self sendPOSTRequest:request withParameters:params];
|
|
}
|
|
|
|
- (NSError *)destroyTweet:(NSString *)identifier {
|
|
|
|
if (identifier.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_statuses_destroy];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *identifierP = [OARequestParameter requestParameterWithName:@"id" value:identifier];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:identifierP, nil]];
|
|
}
|
|
|
|
- (id)getDetailsForTweet:(NSString *)identifier {
|
|
|
|
if (identifier.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_statuses_show];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *identifierP = [OARequestParameter requestParameterWithName:@"id" value:identifier];
|
|
OARequestParameter *includeMyRetweet = [OARequestParameter requestParameterWithName:@"include_my_retweet" value:@"true"];
|
|
return [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:includeMyRetweet, identifierP, nil]];
|
|
}
|
|
|
|
- (id)oembedTweet:(NSString *)identifier maxWidth:(float)maxWidth alignmentMode:(FHSTwitterEngineAlignMode)alignmentMode {
|
|
|
|
if (identifier.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSString *language = [[NSLocale preferredLanguages]objectAtIndex:0];
|
|
NSString *alignment = [[NSArray arrayWithObjects:@"left", @"right", @"center", @"none", nil]objectAtIndex:alignmentMode];
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_statuses_oembed];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *identifierP = [OARequestParameter requestParameterWithName:@"id" value:identifier];
|
|
OARequestParameter *maxWidthP = [OARequestParameter requestParameterWithName:@"maxwidth" value:[NSString stringWithFormat:@"%f",maxWidth]];
|
|
OARequestParameter *languageP= [OARequestParameter requestParameterWithName:@"lang" value:language];
|
|
OARequestParameter *alignmentP = [OARequestParameter requestParameterWithName:@"align" value:alignment];
|
|
return [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:identifierP, maxWidthP, languageP,alignmentP, nil]];
|
|
}
|
|
|
|
- (NSError *)retweet:(NSString *)identifier {
|
|
|
|
if (identifier.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://api.twitter.com/1.1/statuses/retweet/%@.json",identifier]];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
return [self sendPOSTRequest:request withParameters:nil];
|
|
}
|
|
|
|
- (id)getTimelineForUser:(NSString *)user isID:(BOOL)isID count:(int)count {
|
|
return [self getTimelineForUser:user isID:isID count:count sinceID:nil maxID:nil];
|
|
}
|
|
|
|
- (id)getTimelineForUser:(NSString *)user isID:(BOOL)isID count:(int)count sinceID:(NSString *)sinceID maxID:(NSString *)maxID {
|
|
|
|
if (count == 0) {
|
|
return nil;
|
|
}
|
|
|
|
if (user.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_statuses_user_timeline];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *countP = [OARequestParameter requestParameterWithName:@"count" value:[NSString stringWithFormat:@"%d",count]];
|
|
OARequestParameter *userP = [OARequestParameter requestParameterWithName:isID?@"user_id":@"screen_name" value:user];
|
|
OARequestParameter *excludeRepliesP = [OARequestParameter requestParameterWithName:@"exclude_replies" value:@"false"];
|
|
OARequestParameter *includeRTsP = [OARequestParameter requestParameterWithName:@"include_rts" value:@"true"];
|
|
|
|
NSMutableArray *params = [NSMutableArray array];
|
|
[params addObject:countP];
|
|
[params addObject:userP];
|
|
[params addObject:excludeRepliesP];
|
|
[params addObject:includeRTsP];
|
|
|
|
if (sinceID.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"since_id" value:sinceID]];
|
|
}
|
|
|
|
if (maxID.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"max_id" value:maxID]];
|
|
}
|
|
|
|
return [self sendGETRequest:request withParameters:params];
|
|
}
|
|
|
|
- (id)getProfileImageForUsername:(NSString *)username andSize:(FHSTwitterEngineImageSize)size {
|
|
|
|
if (username.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_users_show];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *usernameP = [OARequestParameter requestParameterWithName:@"screen_name" value:username];
|
|
|
|
NSArray *params = [NSArray arrayWithObjects:usernameP, nil];
|
|
|
|
id userShowReturn = [self sendGETRequest:request withParameters:params];
|
|
|
|
if ([userShowReturn isKindOfClass:[NSError class]]) {
|
|
return [NSError errorWithDomain:[(NSError *)userShowReturn domain] code:[(NSError *)userShowReturn code] userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
|
|
} else if ([userShowReturn isKindOfClass:[NSDictionary class]]) {
|
|
NSString *url = [userShowReturn objectForKey:@"profile_image_url"]; // normal
|
|
|
|
if (size == 0) { // mini
|
|
url = [url stringByReplacingOccurrencesOfString:@"_normal" withString:@"_mini"];
|
|
} else if (size == 2) { // bigger
|
|
url = [url stringByReplacingOccurrencesOfString:@"_normal" withString:@"_bigger"];
|
|
} else if (size == 3) { // original
|
|
url = [url stringByReplacingOccurrencesOfString:@"_normal" withString:@""];
|
|
}
|
|
|
|
id ret = [self sendRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]]];
|
|
|
|
if ([ret isKindOfClass:[NSData class]]) {
|
|
return [UIImage imageWithData:(NSData *)ret];
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
return [NSError errorWithDomain:@"Bad Request: The request you attempted to make messed up royally." code:400 userInfo:nil];
|
|
}
|
|
|
|
- (id)authenticatedUserIsBlocking:(NSString *)user isID:(BOOL)isID {
|
|
|
|
if (user.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_blocks_exists];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *userP = [OARequestParameter requestParameterWithName:isID?@"user_id":@"screen_name" value:user];
|
|
OARequestParameter *skipstatusP = [OARequestParameter requestParameterWithName:@"skip_status" value:@"true"];
|
|
|
|
id obj = [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:skipstatusP, userP, nil]];
|
|
|
|
if ([obj isKindOfClass:[NSError class]]) {
|
|
return obj;
|
|
} else if ([obj isKindOfClass:[NSDictionary class]]) {
|
|
if ([[obj objectForKey:@"error"]isEqualToString:@"You are not blocking this user."]) {
|
|
return @"NO";
|
|
} else {
|
|
return @"YES";
|
|
}
|
|
}
|
|
|
|
return getBadRequestError();
|
|
}
|
|
|
|
- (id)listBlockedUsers {
|
|
NSURL *baseURL = [NSURL URLWithString:url_blocks_blocking];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *skipstatusP = [OARequestParameter requestParameterWithName:@"skip_status" value:@"true"];
|
|
return [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:skipstatusP, nil]];
|
|
}
|
|
|
|
- (id)listBlockedIDs {
|
|
NSURL *baseURL = [NSURL URLWithString:url_blocks_blocking_ids];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *stringifyIDsP = [OARequestParameter requestParameterWithName:@"stringify_ids" value:@"true"];
|
|
|
|
id object = [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:stringifyIDsP, nil]];
|
|
|
|
if ([object isKindOfClass:[NSDictionary class]]) {
|
|
return [(NSDictionary *)object objectForKey:@"ids"];
|
|
}
|
|
return object;
|
|
}
|
|
|
|
- (id)getLanguages {
|
|
NSURL *baseURL = [NSURL URLWithString:url_help_languages];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
return [self sendGETRequest:request withParameters:nil];
|
|
}
|
|
|
|
- (id)getConfiguration {
|
|
NSURL *baseURL = [NSURL URLWithString:url_help_configuration];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
return [self sendGETRequest:request withParameters:nil];
|
|
}
|
|
|
|
- (NSError *)reportUserAsSpam:(NSString *)user isID:(BOOL)isID {
|
|
|
|
if (user.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_users_report_spam];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *userP = [OARequestParameter requestParameterWithName:isID?@"user_id":@"screen_name" value:user];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:userP, nil]];
|
|
}
|
|
|
|
- (id)showDirectMessage:(NSString *)messageID {
|
|
|
|
if (messageID.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_direct_messages_show];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *id_ = [OARequestParameter requestParameterWithName:@"id" value:messageID];
|
|
return [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:id_, nil]];
|
|
}
|
|
|
|
- (NSError *)sendDirectMessage:(NSString *)body toUser:(NSString *)user isID:(BOOL)isID {
|
|
|
|
if (user.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
if (body.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_direct_messages_new];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *bodyP = [OARequestParameter requestParameterWithName:@"text" value:[body fhs_trimForTwitter]];
|
|
OARequestParameter *userP = [OARequestParameter requestParameterWithName:isID?@"user_id":@"screen_name" value:user];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:userP, bodyP, nil]];
|
|
}
|
|
|
|
- (id)getSentDirectMessages:(int)count {
|
|
|
|
if (count == 0) {
|
|
return nil;
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_direct_messages_sent];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *countP = [OARequestParameter requestParameterWithName:@"count" value:[NSString stringWithFormat:@"%d",count]];
|
|
return [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:countP, nil]];
|
|
}
|
|
|
|
- (NSError *)deleteDirectMessage:(NSString *)messageID {
|
|
|
|
if (messageID.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_direct_messages_destroy];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *identitifierP = [OARequestParameter requestParameterWithName:@"id" value:messageID];
|
|
OARequestParameter *includeEntitiesP = [OARequestParameter requestParameterWithName:@"include_entities" value:self.includeEntities?@"true":@"false"];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:identitifierP, includeEntitiesP, nil]];
|
|
}
|
|
|
|
- (id)getDirectMessages:(int)count {
|
|
|
|
if (count == 0) {
|
|
return nil;
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_direct_messages];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *countP = [OARequestParameter requestParameterWithName:@"count" value:[NSString stringWithFormat:@"%d",count]];
|
|
OARequestParameter *skipStatusP = [OARequestParameter requestParameterWithName:@"skip_status" value:@"true"];
|
|
return [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:countP, skipStatusP, nil]];
|
|
}
|
|
|
|
- (id)getPrivacyPolicy {
|
|
NSURL *baseURL = [NSURL URLWithString:url_help_privacy];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
|
|
id object = [self sendGETRequest:request withParameters:nil];
|
|
|
|
if ([object isKindOfClass:[NSDictionary class]]) {
|
|
NSDictionary *dict = (NSDictionary *)object;
|
|
if ([dict.allKeys containsObject:@"privacy"]) {
|
|
return [dict objectForKey:@"privacy"];
|
|
}
|
|
}
|
|
return object;
|
|
}
|
|
|
|
- (id)getTermsOfService {
|
|
NSURL *baseURL = [NSURL URLWithString:url_help_tos];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
|
|
id object = [self sendGETRequest:request withParameters:nil];
|
|
|
|
if ([object isKindOfClass:[NSDictionary class]]) {
|
|
NSDictionary *dict = (NSDictionary *)object;
|
|
if ([dict.allKeys containsObject:@"tos"]) {
|
|
return [dict objectForKey:@"tos"];
|
|
}
|
|
}
|
|
return object;
|
|
}
|
|
|
|
- (id)getNoRetweetIDs {
|
|
NSURL *baseURL = [NSURL URLWithString:url_friendships_no_retweets_ids];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *stringifyIDsP = [OARequestParameter requestParameterWithName:@"stringify_ids" value:@"true"];
|
|
return [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:stringifyIDsP, nil]];
|
|
}
|
|
|
|
- (NSError *)enableRetweets:(BOOL)enableRTs andDeviceNotifs:(BOOL)devNotifs forUser:(NSString *)user isID:(BOOL)isID {
|
|
|
|
if (user.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_friendships_update];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *userP = [OARequestParameter requestParameterWithName:isID?@"user_id":@"screen_name" value:user];
|
|
OARequestParameter *retweetsP = [OARequestParameter requestParameterWithName:@"retweets" value:enableRTs?@"true":@"false"];
|
|
OARequestParameter *deviceP = [OARequestParameter requestParameterWithName:@"device" value:devNotifs?@"true":@"false"];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:userP, retweetsP, deviceP, nil]];
|
|
}
|
|
|
|
- (id)getPendingOutgoingFollowers {
|
|
NSURL *baseURL = [NSURL URLWithString:url_friendships_outgoing];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *stringifyIDsP = [OARequestParameter requestParameterWithName:@"stringify_ids" value:@"true"];
|
|
|
|
id object = [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:stringifyIDsP, nil]];
|
|
|
|
if ([object isKindOfClass:[NSDictionary class]]) {
|
|
NSDictionary *dict = (NSDictionary *)object;
|
|
if ([dict.allKeys containsObject:@"ids"]) {
|
|
return [dict objectForKey:@"ids"];
|
|
}
|
|
}
|
|
return object;
|
|
}
|
|
|
|
- (id)getPendingIncomingFollowers {
|
|
NSURL *baseURL = [NSURL URLWithString:url_friendships_incoming];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *stringifyIDsP = [OARequestParameter requestParameterWithName:@"stringify_ids" value:@"true"];
|
|
|
|
id object = [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:stringifyIDsP, nil]];
|
|
|
|
if ([object isKindOfClass:[NSDictionary class]]) {
|
|
NSDictionary *dict = (NSDictionary *)object;
|
|
if ([dict.allKeys containsObject:@"ids"]) {
|
|
return [dict objectForKey:@"ids"];
|
|
}
|
|
}
|
|
return object;
|
|
}
|
|
|
|
- (id)lookupFriendshipStatusForUsers:(NSArray *)users areIDs:(BOOL)areIDs {
|
|
|
|
if (users.count == 0) {
|
|
return nil;
|
|
}
|
|
|
|
NSMutableArray *returnedDictionaries = [NSMutableArray array];
|
|
NSArray *reqStrings = [self generateRequestStringsFromArray:users];
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_friendships_lookup];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
|
|
for (NSString *reqString in reqStrings) {
|
|
OARequestParameter *userP = [OARequestParameter requestParameterWithName:areIDs?@"user_id":@"screen_name" value:reqString];
|
|
|
|
id retObj = [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:userP, nil]];
|
|
|
|
if ([retObj isKindOfClass:[NSArray class]]) {
|
|
[returnedDictionaries addObjectsFromArray:(NSArray *)retObj];
|
|
} else if ([retObj isKindOfClass:[NSError class]]) {
|
|
return retObj;
|
|
}
|
|
}
|
|
return returnedDictionaries;
|
|
}
|
|
|
|
- (NSError *)unfollowUser:(NSString *)user isID:(BOOL)isID {
|
|
|
|
if (user.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_friendships_destroy];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *userP = [OARequestParameter requestParameterWithName:isID?@"user_id":@"screen_name" value:user];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:userP, nil]];
|
|
}
|
|
|
|
- (NSError *)followUser:(NSString *)user isID:(BOOL)isID {
|
|
|
|
if (user.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_friendships_create];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *userP = [OARequestParameter requestParameterWithName:isID?@"user_id":@"screen_name" value:user];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:userP, nil]];
|
|
}
|
|
|
|
- (id)verifyCredentials {
|
|
NSURL *baseURL = [NSURL URLWithString:url_account_verify_credentials];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
return [self sendGETRequest:request withParameters:nil];
|
|
}
|
|
|
|
- (id)getFavoritesForUser:(NSString *)user isID:(BOOL)isID andCount:(int)count {
|
|
return [self getFavoritesForUser:user isID:isID andCount:count sinceID:nil maxID:nil];
|
|
}
|
|
|
|
- (id)getFavoritesForUser:(NSString *)user isID:(BOOL)isID andCount:(int)count sinceID:(NSString *)sinceID maxID:(NSString *)maxID {
|
|
if (count == 0) {
|
|
return nil;
|
|
}
|
|
|
|
if (user.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_favorites_list];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *countP = [OARequestParameter requestParameterWithName:@"count" value:[NSString stringWithFormat:@"%d",count]];
|
|
OARequestParameter *userP = [OARequestParameter requestParameterWithName:isID?@"user_id":@"screen_name" value:user];
|
|
OARequestParameter *includeEntitiesP = [OARequestParameter requestParameterWithName:@"include_entities" value:self.includeEntities?@"true":@"false"];
|
|
|
|
NSMutableArray *params = [NSMutableArray arrayWithObjects:countP, userP, includeEntitiesP, nil];
|
|
|
|
if (sinceID.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"since_id" value:sinceID]];
|
|
}
|
|
|
|
if (maxID.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"max_id" value:maxID]];
|
|
}
|
|
|
|
return [self sendGETRequest:request withParameters:params];
|
|
}
|
|
|
|
- (NSError *)markTweet:(NSString *)tweetID asFavorite:(BOOL)flag {
|
|
|
|
if (tweetID.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:flag?url_favorites_create:url_favorites_destroy];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *idP = [OARequestParameter requestParameterWithName:@"id" value:tweetID];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:idP, nil]];
|
|
}
|
|
|
|
- (id)getRateLimitStatus {
|
|
NSURL *baseURL = [NSURL URLWithString:url_application_rate_limit_status];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
return [self sendGETRequest:request withParameters:nil];
|
|
}
|
|
|
|
- (NSError *)updateProfileColorsWithDictionary:(NSDictionary *)dictionary {
|
|
|
|
// profile_background_color - hex color
|
|
// profile_link_color - hex color
|
|
// profile_sidebar_border_color - hex color
|
|
// profile_sidebar_fill_color - hex color
|
|
// profile_text_color - hex color
|
|
|
|
NSString *profile_background_color = nil;
|
|
NSString *profile_link_color = nil;
|
|
NSString *profile_sidebar_border_color = nil;
|
|
NSString *profile_sidebar_fill_color = nil;
|
|
NSString *profile_text_color = nil;
|
|
|
|
if (!dictionary) {
|
|
profile_background_color = @"C0DEED";
|
|
profile_link_color = @"0084B4";
|
|
profile_sidebar_border_color = @"C0DEED";
|
|
profile_sidebar_fill_color = @"DDEEF6";
|
|
profile_text_color = @"333333";
|
|
} else {
|
|
profile_background_color = [dictionary objectForKey:FHSProfileBackgroundColorKey];
|
|
profile_link_color = [dictionary objectForKey:FHSProfileLinkColorKey];
|
|
profile_sidebar_border_color = [dictionary objectForKey:FHSProfileSidebarBorderColorKey];
|
|
profile_sidebar_fill_color = [dictionary objectForKey:FHSProfileSidebarFillColorKey];
|
|
profile_text_color = [dictionary objectForKey:FHSProfileTextColorKey];
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_account_update_profile_colors];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *skipStatus = [OARequestParameter requestParameterWithName:@"skip_status" value:@"true"];
|
|
|
|
NSMutableArray *params = [NSMutableArray arrayWithObjects:skipStatus, nil];
|
|
|
|
if (profile_background_color.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"profile_background_color" value:profile_background_color]];
|
|
}
|
|
|
|
if (profile_link_color.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"profile_link_color" value:profile_link_color]];
|
|
}
|
|
|
|
if (profile_sidebar_border_color.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"profile_sidebar_border_color" value:profile_sidebar_border_color]];
|
|
}
|
|
|
|
if (profile_sidebar_fill_color.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"profile_sidebar_fill_color" value:profile_sidebar_fill_color]];
|
|
}
|
|
|
|
if (profile_text_color.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"profile_text_color" value:profile_text_color]];
|
|
}
|
|
|
|
return [self sendPOSTRequest:request withParameters:params];
|
|
}
|
|
|
|
- (NSError *)setUseProfileBackgroundImage:(BOOL)shouldUseBGImg {
|
|
NSURL *baseURL = [NSURL URLWithString:url_account_update_profile_background_image];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *skipStatus = [OARequestParameter requestParameterWithName:@"skip_status" value:@"true"];
|
|
OARequestParameter *useImage = [OARequestParameter requestParameterWithName:@"profile_use_background_image" value:shouldUseBGImg?@"true":@"false"];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:skipStatus, useImage, nil]];
|
|
}
|
|
|
|
- (NSError *)setProfileBackgroundImageWithImageData:(NSData *)data tiled:(BOOL)isTiled {
|
|
if (data.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
if (data.length >= 800000) {
|
|
return [NSError errorWithDomain:@"The image you are trying to upload is too large." code:422 userInfo:nil];
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_account_update_profile_background_image];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *tiled = [OARequestParameter requestParameterWithName:@"tiled" value:isTiled?@"true":@"false"];
|
|
OARequestParameter *skipStatus = [OARequestParameter requestParameterWithName:@"skip_status" value:@"true"];
|
|
OARequestParameter *useImage = [OARequestParameter requestParameterWithName:@"profile_use_background_image" value:@"true"];
|
|
OARequestParameter *image = [OARequestParameter requestParameterWithName:@"image" value:[data base64EncodingWithLineLength:0]];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:tiled, skipStatus, useImage, image, nil]];
|
|
}
|
|
|
|
- (NSError *)setProfileBackgroundImageWithImageAtPath:(NSString *)file tiled:(BOOL)isTiled {
|
|
return [self setProfileBackgroundImageWithImageData:[NSData dataWithContentsOfFile:file] tiled:isTiled];
|
|
}
|
|
|
|
- (NSError *)setProfileImageWithImageData:(NSData *)data {
|
|
if (data.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
if (data.length >= 700000) {
|
|
return [NSError errorWithDomain:@"The image you are trying to upload is too large." code:422 userInfo:nil];
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_account_update_profile_image];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *skipStatus = [OARequestParameter requestParameterWithName:@"skip_status" value:@"true"];
|
|
OARequestParameter *image = [OARequestParameter requestParameterWithName:@"image" value:[data base64EncodingWithLineLength:0]];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:image, skipStatus, nil]];
|
|
}
|
|
|
|
- (NSError *)setProfileImageWithImageAtPath:(NSString *)file {
|
|
return [self setProfileImageWithImageData:[NSData dataWithContentsOfFile:file]];
|
|
}
|
|
|
|
- (id)getUserSettings {
|
|
NSURL *baseURL = [NSURL URLWithString:url_account_settings];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
return [self sendGETRequest:request withParameters:nil];
|
|
}
|
|
|
|
- (NSError *)updateUserProfileWithDictionary:(NSDictionary *)settings {
|
|
|
|
if (!settings) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
// all of the values are just non-normalized strings. They appear:
|
|
|
|
// setting - length in characters
|
|
// name - 20
|
|
// url - 100
|
|
// location - 30
|
|
// description - 160
|
|
|
|
NSString *name = [settings objectForKey:FHSProfileNameKey];
|
|
NSString *url = [settings objectForKey:FHSProfileURLKey];
|
|
NSString *location = [settings objectForKey:FHSProfileLocationKey];
|
|
NSString *description = [settings objectForKey:FHSProfileDescriptionKey];
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_account_update_profile];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *skipStatus = [OARequestParameter requestParameterWithName:@"skip_status" value:@"true"];
|
|
|
|
NSMutableArray *params = [NSMutableArray arrayWithObjects:skipStatus, nil];
|
|
|
|
if (name.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"name" value:name]];
|
|
}
|
|
|
|
if (url.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"url" value:url]];
|
|
}
|
|
|
|
if (location.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"location" value:location]];
|
|
}
|
|
|
|
if (description.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"description" value:description]];
|
|
}
|
|
|
|
return [self sendPOSTRequest:request withParameters:params];
|
|
}
|
|
|
|
- (NSError *)updateSettingsWithDictionary:(NSDictionary *)settings {
|
|
|
|
if (!settings) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
// Dictionary with keys:
|
|
// All strings... You could have guessed that.
|
|
// sleep_time_enabled - true/false
|
|
// start_sleep_time - UTC time
|
|
// end_sleep_time - UTC time
|
|
// time_zone - Europe/Copenhagen, Pacific/Tongatapu
|
|
// lang - en, it, es
|
|
|
|
NSString *sleep_time_enabled = [settings objectForKey:@"sleep_time_enabled"];
|
|
NSString *start_sleep_time = [settings objectForKey:@"start_sleep_time"];
|
|
NSString *end_sleep_time = [settings objectForKey:@"end_sleep_time"];
|
|
NSString *time_zone = [settings objectForKey:@"time_zone"];
|
|
NSString *lang = [settings objectForKey:@"lang"];
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_account_settings];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
|
|
NSMutableArray *params = [NSMutableArray array];
|
|
|
|
if (sleep_time_enabled.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"sleep_time_enabled" value:sleep_time_enabled]];
|
|
}
|
|
|
|
if (start_sleep_time.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"start_sleep_time" value:start_sleep_time]];
|
|
}
|
|
|
|
if (end_sleep_time.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"end_sleep_time" value:end_sleep_time]];
|
|
}
|
|
|
|
if (time_zone.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"time_zone" value:time_zone]];
|
|
}
|
|
|
|
if (lang.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"lang" value:lang]];
|
|
}
|
|
|
|
return [self sendPOSTRequest:request withParameters:params];
|
|
}
|
|
|
|
- (id)lookupUsers:(NSArray *)users areIDs:(BOOL)areIDs {
|
|
|
|
if (users.count == 0) {
|
|
return nil;
|
|
}
|
|
|
|
if (users.count > 100) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_users_lookup];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *usernames = [OARequestParameter requestParameterWithName:areIDs?@"user_id":@"screen_name" value:[users componentsJoinedByString:@","]];
|
|
return [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:usernames, nil]];
|
|
}
|
|
|
|
- (NSError *)unblock:(NSString *)username {
|
|
|
|
if (username.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_blocks_destroy];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *usernameP = [OARequestParameter requestParameterWithName:@"screen_name" value:username];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:usernameP, nil]];
|
|
}
|
|
|
|
- (NSError *)block:(NSString *)username {
|
|
|
|
if (username.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_blocks_create];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *usernameP = [OARequestParameter requestParameterWithName:@"screen_name" value:username];
|
|
return [self sendPOSTRequest:request withParameters:[NSArray arrayWithObjects:usernameP, nil]];
|
|
}
|
|
|
|
- (id)testService {
|
|
NSURL *baseURL = [NSURL URLWithString:url_help_test];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
|
|
id retValue = [self sendGETRequest:request withParameters:nil];
|
|
|
|
if ([retValue isKindOfClass:[NSString class]]) {
|
|
if ([(NSString *)retValue isEqualToString:@"ok"]) {
|
|
return @"YES";
|
|
} else {
|
|
return @"NO";
|
|
}
|
|
} else if ([retValue isKindOfClass:[NSError class]]) {
|
|
return retValue;
|
|
}
|
|
|
|
return getBadRequestError();
|
|
}
|
|
|
|
- (id)getHomeTimelineSinceID:(NSString *)sinceID count:(int)count {
|
|
|
|
if (count == 0) {
|
|
return nil;
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_statuses_home_timeline];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *countParam = [OARequestParameter requestParameterWithName:@"count" value:[NSString stringWithFormat:@"%d", count]];
|
|
|
|
NSMutableArray *params = [NSMutableArray arrayWithObjects:countParam, nil];
|
|
|
|
if (sinceID.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"since_id" value:sinceID]];
|
|
}
|
|
|
|
return [self sendGETRequest:request withParameters:params];
|
|
}
|
|
|
|
- (NSError *)postTweet:(NSString *)tweetString inReplyTo:(NSString *)inReplyToString {
|
|
|
|
if (tweetString.length == 0) {
|
|
return getBadRequestError();
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:url_statuses_update];
|
|
OARequestParameter *status = [OARequestParameter requestParameterWithName:@"status" value:[tweetString fhs_trimForTwitter]];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
|
|
NSMutableArray *params = [NSMutableArray arrayWithObjects:status, nil];
|
|
|
|
if (inReplyToString.length > 0) {
|
|
[params addObject:[OARequestParameter requestParameterWithName:@"in_reply_to_status_id" value:inReplyToString]];
|
|
}
|
|
|
|
return [self sendPOSTRequest:request withParameters:params];
|
|
}
|
|
|
|
- (NSError *)postTweet:(NSString *)tweetString {
|
|
return [self postTweet:tweetString inReplyTo:nil];
|
|
}
|
|
|
|
- (id)getFollowersIDs {
|
|
NSURL *baseURL = [NSURL URLWithString:url_followers_ids];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *param = [OARequestParameter requestParameterWithName:@"screen_name" value:self.loggedInUsername];
|
|
OARequestParameter *stringify_ids = [OARequestParameter requestParameterWithName:@"stringify_ids" value:@"true"];
|
|
return [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:param, stringify_ids, nil]];
|
|
}
|
|
|
|
- (id)getFriendsIDs {
|
|
NSURL *baseURL = [NSURL URLWithString:url_friends_ids];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:self.accessToken];
|
|
OARequestParameter *param = [OARequestParameter requestParameterWithName:@"screen_name" value:self.loggedInUsername];
|
|
OARequestParameter *stringify_ids = [OARequestParameter requestParameterWithName:@"stringify_ids" value:@"true"];
|
|
return [self sendGETRequest:request withParameters:[NSArray arrayWithObjects:param, stringify_ids, nil]];
|
|
}
|
|
|
|
- (id)init {
|
|
self = [super init];
|
|
if (self) {
|
|
// Twitter API datestamps are UTC
|
|
// Don't question this code.
|
|
self.dateFormatter = [[[NSDateFormatter alloc]init]autorelease];
|
|
_dateFormatter.locale = [[[NSLocale alloc]initWithLocaleIdentifier:@"en_US"]autorelease];
|
|
_dateFormatter.dateStyle = NSDateFormatterLongStyle;
|
|
_dateFormatter.formatterBehavior = NSDateFormatterBehavior10_4;
|
|
_dateFormatter.dateFormat = @"EEE MMM dd HH:mm:ss ZZZZ yyyy";
|
|
}
|
|
return self;
|
|
}
|
|
|
|
// The shared* class method
|
|
+ (FHSTwitterEngine *)sharedEngine {
|
|
@synchronized (self) {
|
|
if (sharedInstance == nil) {
|
|
[[self alloc]init];
|
|
}
|
|
}
|
|
return sharedInstance;
|
|
}
|
|
|
|
// Override stuff to make sure that the singleton is never dealloc'd. Fun.
|
|
+ (id)allocWithZone:(NSZone *)zone {
|
|
@synchronized(self) {
|
|
if (sharedInstance == nil) {
|
|
sharedInstance = [super allocWithZone:zone];
|
|
return sharedInstance;
|
|
}
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
- (id)retain {
|
|
return self;
|
|
}
|
|
|
|
- (oneway void)release {
|
|
// Do nothing
|
|
}
|
|
|
|
- (id)autorelease {
|
|
return self;
|
|
}
|
|
|
|
- (NSUInteger)retainCount {
|
|
return NSUIntegerMax;
|
|
}
|
|
|
|
- (NSArray *)generateRequestStringsFromArray:(NSArray *)array {
|
|
|
|
NSString *initialString = [array componentsJoinedByString:@","];
|
|
|
|
if (array.count <= 100) {
|
|
return [NSArray arrayWithObjects:initialString, nil];
|
|
}
|
|
|
|
int offset = 0;
|
|
int remainder = fmod(array.count, 100);
|
|
int numberOfStrings = (array.count-remainder)/100;
|
|
|
|
NSMutableArray *reqStrs = [NSMutableArray array];
|
|
|
|
for (int i = 1; i <= numberOfStrings; ++i) {
|
|
NSString *ninetyNinththItem = (NSString *)[array objectAtIndex:(i*100)-1];
|
|
NSRange range = [initialString rangeOfString:ninetyNinththItem];
|
|
int endOffset = range.location+range.length;
|
|
NSRange rangeOfAString = NSMakeRange(offset, endOffset-offset);
|
|
offset = endOffset;
|
|
NSString *endResult = [initialString fhs_stringWithRange:rangeOfAString];
|
|
|
|
if ([[endResult substringToIndex:1]isEqualToString:@","]) {
|
|
endResult = [endResult substringFromIndex:1];
|
|
}
|
|
|
|
[reqStrs addObject:endResult];
|
|
}
|
|
|
|
NSString *remainderString = [initialString stringByReplacingOccurrencesOfString:[reqStrs componentsJoinedByString:@","] withString:@""];
|
|
|
|
if ([[remainderString substringToIndex:1]isEqualToString:@","]) {
|
|
remainderString = [remainderString substringFromIndex:1];
|
|
}
|
|
|
|
[reqStrs addObject:remainderString];
|
|
|
|
return reqStrs;
|
|
}
|
|
|
|
//
|
|
// XAuth
|
|
//
|
|
|
|
- (NSError *)getXAuthAccessTokenForUsername:(NSString *)username password:(NSString *)password {
|
|
|
|
if (password.length == 0) {
|
|
return [NSError errorWithDomain:@"Bad Request: The request you are trying to make is missing parameters." code:400 userInfo:nil];
|
|
}
|
|
|
|
if (username.length == 0) {
|
|
return [NSError errorWithDomain:@"Bad Request: The request you are trying to make is missing parameters." code:400 userInfo:nil];
|
|
}
|
|
|
|
NSURL *baseURL = [NSURL URLWithString:@"https://api.twitter.com/oauth/access_token"];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:baseURL consumer:self.consumer token:nil];
|
|
OARequestParameter *x_auth_mode = [OARequestParameter requestParameterWithName:@"x_auth_mode" value:@"client_auth"];
|
|
OARequestParameter *x_auth_username = [OARequestParameter requestParameterWithName:@"x_auth_username" value:username];
|
|
OARequestParameter *x_auth_password = [OARequestParameter requestParameterWithName:@"x_auth_password" value:password];
|
|
[request setHTTPMethod:@"POST"];
|
|
|
|
[request setParameters:[NSArray arrayWithObjects:x_auth_mode, x_auth_username, x_auth_password, nil]];
|
|
[request prepare];
|
|
|
|
if (self.shouldClearConsumer) {
|
|
self.shouldClearConsumer = NO;
|
|
self.consumer = nil;
|
|
}
|
|
|
|
id ret = [self sendRequest:request];
|
|
|
|
if ([ret isKindOfClass:[NSError class]]) {
|
|
return ret;
|
|
}
|
|
|
|
NSString *httpBody = [[[NSString alloc]initWithData:(NSData *)ret encoding:NSUTF8StringEncoding]autorelease];
|
|
|
|
if (httpBody.length > 0) {
|
|
[self storeAccessToken:httpBody];
|
|
} else {
|
|
[self storeAccessToken:nil];
|
|
return [NSError errorWithDomain:@"Twitter messed up and did not return anything for some reason. Please try again later." code:500 userInfo:nil];
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
//
|
|
// sendRequest:
|
|
//
|
|
|
|
- (id)sendRequest:(NSURLRequest *)request {
|
|
|
|
NSHTTPURLResponse *response = nil;
|
|
NSError *error = nil;
|
|
|
|
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
|
|
|
|
if (error) {
|
|
return error;
|
|
}
|
|
|
|
if (response == nil) {
|
|
return error;
|
|
}
|
|
|
|
if (response.statusCode >= 304) {
|
|
return error;
|
|
}
|
|
|
|
if (data.length == 0) {
|
|
return error;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
- (NSError *)sendPOSTRequest:(OAMutableURLRequest *)request withParameters:(NSArray *)params {
|
|
|
|
if (![self isAuthorized]) {
|
|
[self loadAccessToken];
|
|
if (![self isAuthorized]) {
|
|
return [NSError errorWithDomain:@"You are not authorized via OAuth" code:401 userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
|
|
}
|
|
}
|
|
|
|
[request setHTTPMethod:@"POST"];
|
|
[request setParameters:params];
|
|
[request prepare];
|
|
|
|
if (_shouldClearConsumer) {
|
|
self.shouldClearConsumer = NO;
|
|
self.consumer = nil;
|
|
}
|
|
|
|
id retobj = [self sendRequest:request];
|
|
|
|
if (retobj == nil) {
|
|
return getNilReturnLengthError();
|
|
}
|
|
|
|
if ([retobj isKindOfClass:[NSError class]]) {
|
|
return retobj;
|
|
}
|
|
|
|
id parsedJSONResponse = removeNull([NSJSONSerialization JSONObjectWithData:(NSData *)retobj options:NSJSONReadingMutableContainers error:nil]);
|
|
|
|
if ([parsedJSONResponse isKindOfClass:[NSDictionary class]]) {
|
|
NSString *errorMessage = [parsedJSONResponse objectForKey:@"error"];
|
|
NSArray *errorArray = [parsedJSONResponse objectForKey:@"errors"];
|
|
if (errorMessage.length > 0) {
|
|
return [NSError errorWithDomain:errorMessage code:[[parsedJSONResponse objectForKey:@"code"]intValue] userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
|
|
} else if (errorArray.count > 0) {
|
|
if (errorArray.count > 1) {
|
|
return [NSError errorWithDomain:@"Multiple Errors" code:1337 userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
|
|
} else {
|
|
NSDictionary *theError = [errorArray objectAtIndex:0];
|
|
return [NSError errorWithDomain:[theError objectForKey:@"message"] code:[[theError objectForKey:@"code"]integerValue] userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (id)sendGETRequest:(OAMutableURLRequest *)request withParameters:(NSArray *)params {
|
|
|
|
if (![self isAuthorized]) {
|
|
[self loadAccessToken];
|
|
if (![self isAuthorized]) {
|
|
return [NSError errorWithDomain:@"You are not authorized via OAuth" code:401 userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
|
|
}
|
|
}
|
|
|
|
[request setHTTPMethod:@"GET"];
|
|
[request setParameters:params];
|
|
[request prepare];
|
|
|
|
if (_shouldClearConsumer) {
|
|
self.shouldClearConsumer = NO;
|
|
self.consumer = nil;
|
|
}
|
|
|
|
id retobj = [self sendRequest:request];
|
|
|
|
if (retobj == nil) {
|
|
return getNilReturnLengthError();
|
|
}
|
|
|
|
if ([retobj isKindOfClass:[NSError class]]) {
|
|
return retobj;
|
|
}
|
|
|
|
id parsedJSONResponse = removeNull([NSJSONSerialization JSONObjectWithData:(NSData *)retobj options:NSJSONReadingMutableContainers error:nil]);
|
|
|
|
if ([parsedJSONResponse isKindOfClass:[NSDictionary class]]) {
|
|
NSString *errorMessage = [parsedJSONResponse objectForKey:@"error"];
|
|
NSArray *errorArray = [parsedJSONResponse objectForKey:@"errors"];
|
|
if (errorMessage.length > 0) {
|
|
return [NSError errorWithDomain:errorMessage code:[[parsedJSONResponse objectForKey:@"code"]intValue] userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
|
|
} else if (errorArray.count > 0) {
|
|
if (errorArray.count > 1) {
|
|
return [NSError errorWithDomain:@"Multiple Errors" code:1337 userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
|
|
} else {
|
|
NSDictionary *theError = [errorArray objectAtIndex:0];
|
|
return [NSError errorWithDomain:[theError objectForKey:@"message"] code:[[theError objectForKey:@"code"]integerValue] userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
|
|
}
|
|
}
|
|
}
|
|
|
|
return parsedJSONResponse;
|
|
}
|
|
|
|
|
|
//
|
|
// OAuth
|
|
//
|
|
|
|
- (NSString *)getRequestTokenString {
|
|
NSURL *url = [NSURL URLWithString:@"https://api.twitter.com/oauth/request_token"];
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:url consumer:self.consumer token:nil];
|
|
[request setHTTPMethod:@"POST"];
|
|
[request prepare];
|
|
|
|
id retobj = [self sendRequest:request];
|
|
|
|
if ([retobj isKindOfClass:[NSData class]]) {
|
|
return [[[NSString alloc]initWithData:(NSData *)retobj encoding:NSUTF8StringEncoding]autorelease];
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (BOOL)finishAuthWithRequestToken:(OAToken *)reqToken {
|
|
|
|
NSURL *url = [NSURL URLWithString:@"https://api.twitter.com/oauth/access_token"];
|
|
|
|
OAMutableURLRequest *request = [OAMutableURLRequest requestWithURL:url consumer:self.consumer token:reqToken];
|
|
[request setHTTPMethod:@"POST"];
|
|
[request prepare];
|
|
|
|
if (_shouldClearConsumer) {
|
|
self.shouldClearConsumer = NO;
|
|
self.consumer = nil;
|
|
}
|
|
|
|
id retobj = [self sendRequest:request];
|
|
|
|
if ([retobj isKindOfClass:[NSError class]]) {
|
|
return NO;
|
|
}
|
|
|
|
NSString *response = [[[NSString alloc]initWithData:(NSData *)retobj encoding:NSUTF8StringEncoding]autorelease];
|
|
|
|
if (response.length == 0) {
|
|
return NO;
|
|
}
|
|
|
|
[self storeAccessToken:response];
|
|
|
|
return YES;
|
|
}
|
|
|
|
//
|
|
// Access Token Management
|
|
//
|
|
|
|
- (void)loadAccessToken {
|
|
|
|
NSString *savedHttpBody = nil;
|
|
|
|
if (self.delegate && [self.delegate respondsToSelector:@selector(loadAccessToken)]) {
|
|
savedHttpBody = [self.delegate loadAccessToken];
|
|
} else {
|
|
savedHttpBody = [[NSUserDefaults standardUserDefaults]objectForKey:@"SavedAccessHTTPBody"];
|
|
}
|
|
|
|
self.accessToken = [OAToken tokenWithHTTPResponseBody:savedHttpBody];
|
|
self.loggedInUsername = [self extractValueForKey:@"screen_name" fromHTTPBody:savedHttpBody];
|
|
self.loggedInID = [self extractValueForKey:@"user_id" fromHTTPBody:savedHttpBody];
|
|
}
|
|
|
|
- (void)storeAccessToken:(NSString *)accessTokenZ {
|
|
self.accessToken = [OAToken tokenWithHTTPResponseBody:accessTokenZ];
|
|
self.loggedInUsername = [self extractValueForKey:@"screen_name" fromHTTPBody:accessTokenZ];
|
|
self.loggedInID = [self extractValueForKey:@"user_id" fromHTTPBody:accessTokenZ];
|
|
|
|
if ([self.delegate respondsToSelector:@selector(storeAccessToken:)]) {
|
|
[self.delegate storeAccessToken:accessTokenZ];
|
|
} else {
|
|
[[NSUserDefaults standardUserDefaults]setObject:accessTokenZ forKey:@"SavedAccessHTTPBody"];
|
|
}
|
|
}
|
|
|
|
- (NSString *)extractValueForKey:(NSString *)target fromHTTPBody:(NSString *)body {
|
|
if (body.length == 0) {
|
|
return nil;
|
|
}
|
|
|
|
if (target.length == 0) {
|
|
return nil;
|
|
}
|
|
|
|
NSArray *tuples = [body componentsSeparatedByString:@"&"];
|
|
if (tuples.count < 1) {
|
|
return nil;
|
|
}
|
|
|
|
for (NSString *tuple in tuples) {
|
|
NSArray *keyValueArray = [tuple componentsSeparatedByString:@"="];
|
|
|
|
if (keyValueArray.count >= 2) {
|
|
NSString *key = [keyValueArray objectAtIndex:0];
|
|
NSString *value = [keyValueArray objectAtIndex:1];
|
|
|
|
if ([key isEqualToString:target]) {
|
|
return value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (BOOL)isAuthorized {
|
|
if (!self.consumer) {
|
|
return NO;
|
|
}
|
|
|
|
if (self.accessToken.key && self.accessToken.secret) {
|
|
if (self.accessToken.key.length > 0 && self.accessToken.secret.length > 0) {
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (void)clearAccessToken {
|
|
[self storeAccessToken:@""];
|
|
self.accessToken = nil;
|
|
self.loggedInUsername = nil;
|
|
}
|
|
|
|
- (NSDate *)getDateFromTwitterCreatedAt:(NSString *)twitterDate {
|
|
return [self.dateFormatter dateFromString:twitterDate];
|
|
}
|
|
|
|
- (void)clearConsumer {
|
|
self.consumer = nil;
|
|
}
|
|
|
|
- (void)permanentlySetConsumerKey:(NSString *)consumerKey andSecret:(NSString *)consumerSecret {
|
|
self.shouldClearConsumer = NO;
|
|
self.consumer = [OAConsumer consumerWithKey:consumerKey secret:consumerSecret];
|
|
}
|
|
|
|
- (void)temporarilySetConsumerKey:(NSString *)consumerKey andSecret:(NSString *)consumerSecret {
|
|
self.shouldClearConsumer = YES;
|
|
self.consumer = [OAConsumer consumerWithKey:consumerKey secret:consumerSecret];
|
|
}
|
|
|
|
- (void)showOAuthLoginControllerFromViewController:(UIViewController *)sender {
|
|
[self showOAuthLoginControllerFromViewController:sender withCompletion:nil];
|
|
}
|
|
|
|
- (void)showOAuthLoginControllerFromViewController:(UIViewController *)sender withCompletion:(void(^)(BOOL success))block {
|
|
FHSTwitterEngineController *vc = [[[FHSTwitterEngineController alloc]init]autorelease];
|
|
vc.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
|
|
objc_setAssociatedObject(authBlockKey, "FHSTwitterEngineOAuthCompletion", block, OBJC_ASSOCIATION_COPY_NONATOMIC);
|
|
[sender presentModalViewController:vc animated:YES];
|
|
}
|
|
|
|
+ (BOOL)isConnectedToInternet {
|
|
struct sockaddr_in zeroAddress;
|
|
bzero(&zeroAddress, sizeof(zeroAddress));
|
|
zeroAddress.sin_len = sizeof(zeroAddress);
|
|
zeroAddress.sin_family = AF_INET;
|
|
|
|
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)&zeroAddress);
|
|
if (reachability) {
|
|
SCNetworkReachabilityFlags flags;
|
|
BOOL worked = SCNetworkReachabilityGetFlags(reachability, &flags);
|
|
CFRelease(reachability);
|
|
|
|
if (worked) {
|
|
|
|
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) {
|
|
return NO;
|
|
}
|
|
|
|
if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) {
|
|
return YES;
|
|
}
|
|
|
|
|
|
if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand) != 0) || (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) {
|
|
if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) {
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) {
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
- (void)dealloc {
|
|
[self setConsumer:nil];
|
|
[self setDateFormatter:nil];
|
|
[self setLoggedInUsername:nil];
|
|
[self setLoggedInID:nil];
|
|
[self setDelegate:nil];
|
|
[self setAccessToken:nil];
|
|
[super dealloc];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation FHSTwitterEngineController
|
|
|
|
static NSString * const newPinJS = @"var d = document.getElementById('oauth-pin'); if (d == null) d = document.getElementById('oauth_pin'); if (d) { var d2 = d.getElementsByTagName('code'); if (d2.length > 0) d2[0].innerHTML; }";
|
|
static NSString * const oldPinJS = @"var d = document.getElementById('oauth-pin'); if (d == null) d = document.getElementById('oauth_pin'); if (d) d = d.innerHTML; d;";
|
|
|
|
- (void)loadView {
|
|
[super loadView];
|
|
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(pasteboardChanged:) name:UIPasteboardChangedNotification object:nil];
|
|
|
|
self.view = [[[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 460)]autorelease];
|
|
self.view.backgroundColor = [UIColor grayColor];
|
|
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
|
|
self.theWebView = [[[UIWebView alloc]initWithFrame:CGRectMake(0, 44, 320, 416)]autorelease];
|
|
_theWebView.hidden = YES;
|
|
_theWebView.delegate = self;
|
|
_theWebView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
_theWebView.dataDetectorTypes = UIDataDetectorTypeNone;
|
|
_theWebView.backgroundColor = [UIColor darkGrayColor];
|
|
|
|
self.navBar = [[[UINavigationBar alloc]initWithFrame:CGRectMake(0, 0, 320, 44)]autorelease];
|
|
_navBar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;
|
|
|
|
[self.view addSubview:_theWebView];
|
|
[self.view addSubview:_navBar];
|
|
|
|
self.blockerView = [[[UIView alloc]initWithFrame:CGRectMake(0, 0, 200, 60)]autorelease];
|
|
_blockerView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.8];
|
|
_blockerView.center = CGPointMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2);
|
|
_blockerView.clipsToBounds = YES;
|
|
_blockerView.layer.cornerRadius = 10;
|
|
|
|
self.pinCopyBar = [[[UIToolbar alloc]initWithFrame:CGRectMake(0, 44, self.view.bounds.size.width, 44)]autorelease];
|
|
_pinCopyBar.barStyle = UIBarStyleBlackTranslucent;
|
|
_pinCopyBar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;
|
|
_pinCopyBar.items = [NSArray arrayWithObjects:[[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]autorelease], [[[UIBarButtonItem alloc]initWithTitle:@"Select and Copy the PIN" style: UIBarButtonItemStylePlain target:nil action: nil]autorelease], [[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]autorelease], nil];
|
|
|
|
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(0, 5, _blockerView.bounds.size.width, 15)];
|
|
label.text = @"Please Wait...";
|
|
label.backgroundColor = [UIColor clearColor];
|
|
label.textColor = [UIColor whiteColor];
|
|
label.textAlignment = UITextAlignmentCenter;
|
|
label.font = [UIFont boldSystemFontOfSize:15];
|
|
[_blockerView addSubview:label];
|
|
[label release];
|
|
|
|
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
|
|
spinner.center = CGPointMake(_blockerView.bounds.size.width/2, (_blockerView.bounds.size.height/2)+10);
|
|
[_blockerView addSubview:spinner];
|
|
[self.view addSubview:_blockerView];
|
|
[spinner startAnimating];
|
|
[spinner release];
|
|
|
|
UINavigationItem *navItem = [[[UINavigationItem alloc]initWithTitle:@"Twitter Login"]autorelease];
|
|
navItem.leftBarButtonItem = [[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(close)]autorelease];
|
|
[_navBar pushNavigationItem:navItem animated:NO];
|
|
|
|
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
|
|
|
|
dispatch_async(GCDBackgroundThread, ^{
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
|
|
|
|
NSString *reqString = [[FHSTwitterEngine sharedEngine]getRequestTokenString];
|
|
|
|
if (reqString.length == 0) {
|
|
[self dismissModalViewControllerAnimated:YES];
|
|
[pool release];
|
|
return;
|
|
}
|
|
|
|
self.requestToken = [OAToken tokenWithHTTPResponseBody:reqString];
|
|
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://api.twitter.com/oauth/authorize?oauth_token=%@",_requestToken.key]]];
|
|
|
|
dispatch_sync(GCDMainThread, ^{
|
|
NSAutoreleasePool *poolTwo = [[NSAutoreleasePool alloc]init];
|
|
[_theWebView loadRequest:request];
|
|
[poolTwo release];
|
|
});
|
|
|
|
[pool release];
|
|
});
|
|
}
|
|
|
|
- (void)gotPin:(NSString *)pin {
|
|
_requestToken.verifier = pin;
|
|
BOOL ret = [[FHSTwitterEngine sharedEngine]finishAuthWithRequestToken:_requestToken];
|
|
|
|
void(^block)(BOOL success) = objc_getAssociatedObject(authBlockKey, "FHSTwitterEngineOAuthCompletion");
|
|
objc_removeAssociatedObjects(authBlockKey);
|
|
|
|
if (block) {
|
|
block(ret);
|
|
}
|
|
|
|
[self dismissModalViewControllerAnimated:YES];
|
|
}
|
|
|
|
- (void)pasteboardChanged:(NSNotification *)note {
|
|
|
|
if (![note.userInfo objectForKey:UIPasteboardChangedTypesAddedKey]) {
|
|
return;
|
|
}
|
|
|
|
NSString *string = [[UIPasteboard generalPasteboard]string];
|
|
|
|
if (string.length != 7 || !string.fhs_isNumeric) {
|
|
return;
|
|
}
|
|
|
|
[self gotPin:string];
|
|
}
|
|
|
|
- (NSString *)locatePin {
|
|
NSString *pin = [[_theWebView stringByEvaluatingJavaScriptFromString:newPinJS]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
|
|
|
if (pin.length == 7) {
|
|
return pin;
|
|
} else {
|
|
pin = [[_theWebView stringByEvaluatingJavaScriptFromString:oldPinJS]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
|
|
|
if (pin.length == 7) {
|
|
return pin;
|
|
}
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (void)webViewDidFinishLoad:(UIWebView *)webView {
|
|
_theWebView.userInteractionEnabled = YES;
|
|
NSString *authPin = [self locatePin];
|
|
|
|
if (authPin.length > 0) {
|
|
[self gotPin:authPin];
|
|
return;
|
|
}
|
|
|
|
NSString *formCount = [webView stringByEvaluatingJavaScriptFromString:@"document.forms.length"];
|
|
|
|
if ([formCount isEqualToString:@"0"]) {
|
|
[self showPinCopyPrompt];
|
|
}
|
|
|
|
[UIView beginAnimations:nil context:nil];
|
|
_blockerView.hidden = YES;
|
|
[UIView commitAnimations];
|
|
|
|
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
|
|
|
|
_theWebView.hidden = NO;
|
|
}
|
|
|
|
- (void)showPinCopyPrompt {
|
|
if (_pinCopyBar.superview) {
|
|
return;
|
|
}
|
|
|
|
_pinCopyBar.center = CGPointMake(_pinCopyBar.bounds.size.width/2, _pinCopyBar.bounds.size.height/2);
|
|
[self.view insertSubview:_pinCopyBar belowSubview:_navBar];
|
|
|
|
[UIView beginAnimations:nil context:nil];
|
|
_pinCopyBar.center = CGPointMake(_pinCopyBar.bounds.size.width/2, _navBar.bounds.size.height+_pinCopyBar.bounds.size.height/2);
|
|
[UIView commitAnimations];
|
|
}
|
|
|
|
- (void)webViewDidStartLoad:(UIWebView *)webView {
|
|
_theWebView.userInteractionEnabled = NO;
|
|
[_theWebView setHidden:YES];
|
|
[_blockerView setHidden:NO];
|
|
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
|
|
}
|
|
|
|
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
|
|
|
|
if (strstr([request.URL.absoluteString UTF8String], "denied=")) {
|
|
[self dismissModalViewControllerAnimated:YES];
|
|
return NO;
|
|
}
|
|
|
|
NSData *data = request.HTTPBody;
|
|
char *raw = data?(char *)[data bytes]:"";
|
|
|
|
if (raw && (strstr(raw, "cancel=") || strstr(raw, "deny="))) {
|
|
[self dismissModalViewControllerAnimated:YES];
|
|
return NO;
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (void)close {
|
|
[self dismissModalViewControllerAnimated:YES];
|
|
}
|
|
|
|
- (void)dismissModalViewControllerAnimated:(BOOL)animated {
|
|
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
|
|
[[NSNotificationCenter defaultCenter]removeObserver:self];
|
|
[_theWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@""]]];
|
|
[super dismissModalViewControllerAnimated:animated];
|
|
}
|
|
|
|
- (void)dealloc {
|
|
[self setNavBar:nil];
|
|
[self setBlockerView:nil];
|
|
[self setPinCopyBar:nil];
|
|
[self setTheWebView:nil];
|
|
[self setRequestToken:nil];
|
|
[super dealloc];
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
static char const encodingTable[64] = {
|
|
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
|
|
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
|
|
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
|
|
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' };
|
|
|
|
@implementation NSData (Base64)
|
|
|
|
+ (NSData *)dataWithBase64EncodedString:(NSString *)string {
|
|
return [[[NSData alloc]initWithBase64EncodedString:string]autorelease];
|
|
}
|
|
|
|
- (id)initWithBase64EncodedString:(NSString *)string {
|
|
NSMutableData *mutableData = nil;
|
|
|
|
if (string) {
|
|
unsigned long ixtext = 0;
|
|
unsigned char ch = 0;
|
|
unsigned char inbuf[4] = {0,0,0,0};
|
|
unsigned char outbuf[3] = {0,0,0};
|
|
short ixinbuf = 0;
|
|
BOOL flignore = NO;
|
|
BOOL flendtext = NO;
|
|
|
|
NSData *base64Data = [string dataUsingEncoding:NSASCIIStringEncoding];
|
|
const unsigned char *base64Bytes = [base64Data bytes];
|
|
mutableData = [NSMutableData dataWithCapacity:base64Data.length];
|
|
unsigned long lentext = [base64Data length];
|
|
|
|
while (!(ixtext >= lentext)) {
|
|
|
|
ch = base64Bytes[ixtext++];
|
|
flignore = NO;
|
|
|
|
if ((ch >= 'A') && (ch <= 'Z')) {
|
|
ch = ch - 'A';
|
|
} else if ((ch >= 'a') && (ch <= 'z')) {
|
|
ch = ch - 'a' + 26;
|
|
} else if ((ch >= '0') && (ch <= '9')) {
|
|
ch = ch - '0' + 52;
|
|
} else if (ch == '+') {
|
|
ch = 62;
|
|
} else if (ch == '=') {
|
|
flendtext = YES;
|
|
} else if (ch == '/') {
|
|
ch = 63;
|
|
} else {
|
|
flignore = YES;
|
|
}
|
|
|
|
if (!flignore) {
|
|
short ctcharsinbuf = 3;
|
|
BOOL flbreak = NO;
|
|
|
|
if (flendtext) {
|
|
if (!ixinbuf) {
|
|
break;
|
|
}
|
|
|
|
if (ixinbuf == 1 || ixinbuf == 2) {
|
|
ctcharsinbuf = 1;
|
|
} else {
|
|
ctcharsinbuf = 2;
|
|
}
|
|
|
|
ixinbuf = 3;
|
|
flbreak = YES;
|
|
}
|
|
|
|
inbuf[ixinbuf++] = ch;
|
|
|
|
if (ixinbuf == 4) {
|
|
ixinbuf = 0;
|
|
outbuf[0] = (inbuf[0] << 2) | ((inbuf[1] & 0x30) >> 4);
|
|
outbuf[1] = ((inbuf[1] & 0x0F) << 4) | ((inbuf[2] & 0x3C) >> 2);
|
|
outbuf[2] = ((inbuf[2] & 0x03) << 6) | (inbuf[3] & 0x3F);
|
|
|
|
for (int i = 0; i < ctcharsinbuf; i++) {
|
|
[mutableData appendBytes:&outbuf[i] length:1];
|
|
}
|
|
}
|
|
|
|
if (flbreak) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
self = [self initWithData:mutableData];
|
|
return self;
|
|
}
|
|
|
|
- (NSString *)base64EncodingWithLineLength:(unsigned int)lineLength {
|
|
|
|
const unsigned char *bytes = [self bytes];
|
|
unsigned long ixtext = 0;
|
|
unsigned long lentext = [self length];
|
|
long ctremaining = 0;
|
|
unsigned char inbuf[3] = {0,0,0};
|
|
unsigned char outbuf[4] = {0,0,0,0};
|
|
|
|
short charsonline = 0;
|
|
short ctcopy = 0;
|
|
unsigned long ix = 0;
|
|
|
|
NSMutableString *result = [NSMutableString stringWithCapacity:lentext];
|
|
|
|
while (YES) {
|
|
ctremaining = lentext-ixtext;
|
|
|
|
if (ctremaining <= 0) {
|
|
break;
|
|
}
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
ix = ixtext + i;
|
|
inbuf[i] = (ix < lentext)?bytes[ix]:0;
|
|
}
|
|
|
|
outbuf[0] = (inbuf[0] & 0xFC) >> 2;
|
|
outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf[1] & 0xF0) >> 4);
|
|
outbuf[2] = ((inbuf[1] & 0x0F) << 2) | ((inbuf[2] & 0xC0) >> 6);
|
|
outbuf[3] = inbuf[2] & 0x3F;
|
|
|
|
switch (ctremaining) {
|
|
case 1:
|
|
ctcopy = 2;
|
|
break;
|
|
case 2:
|
|
ctcopy = 3;
|
|
break;
|
|
default:
|
|
ctcopy = 4;
|
|
break;
|
|
}
|
|
|
|
for (int i = 0; i < ctcopy; i++) {
|
|
[result appendFormat:@"%c",encodingTable[outbuf[i]]];
|
|
}
|
|
|
|
for (int i = ctcopy; i < 4; i++) {
|
|
[result appendString:@"="];
|
|
}
|
|
|
|
ixtext += 3;
|
|
charsonline += 4;
|
|
|
|
if (lineLength > 0) {
|
|
if (charsonline >= lineLength) {
|
|
charsonline = 0;
|
|
[result appendString:@"\n"];
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@end
|