I've trying to be able to login in a website which has an html form with a captcha. The way I've been trying to do it is fetching the html of the login form, showing the captcha to a user, who will put it in a textfield and then I'm trying to submit the form.
The error I'm always getting is invalid key code, so I'm guessing the problem is the captcha I fetch in the first instance, isn't valid for the second one... Any ideas how I could do this?
The webpage is Fanfiction, and I'm doing this as a personal proyect and see if I'm capable of exporting my list of favorites and follows.
I do this in order to show the captcha to the user.
NSURL *url = [NSURL URLWithString:#"https://www.fanfiction.net"];
self.httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
[self.httpClient getPath:#"/login.php" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
TFHpple * doc = [[TFHpple alloc] initWithHTMLData:responseObject];
NSArray * elements = [doc searchWithXPathQuery:#"//img[#id='xcaptcha']"];
TFHppleElement * element = [elements objectAtIndex:0];
[self.captchaView setImageWithURL:[NSURL URLWithString:[element objectForKey:#"src"]]];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) { }];
And then, whenthe user has entered the captcha code in a textfield and pressed a UIButton, I do this
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
kFFNMail, #"email",
kFFNPass, #"password",
self.captchaField.text, #"captcha",
nil];
NSURLRequest *postRequest = [self.httpClient multipartFormRequestWithMethod:#"POST"
path:#"/login.php"
parameters:params
constructingBodyWithBlock:^(id<AFMultipartFormData> formData) { }];
/*
// I think this is the same as the one before in this case
NSMutableURLRequest *postRequest = [self.httpClient requestWithMethod:#"POST"
path:#"/login.php"
parameters:params2];
*/
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:postRequest];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
TFHpple * doc = [[TFHpple alloc] initWithHTMLData:responseObject];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) { }];
[operation start];
If is of any indication, I have this working in a ruby script like this
require 'rubygems'
require 'mechanize'
require "highline/import"
a = Mechanize.new
a.get('https://www.fanfiction.net/login.php') do |page|
images = page.search("#xcaptcha")
a.get(images.first.attributes["src"]).save "captcha.jpg"
# I read the saved image,and enter the captcha code
captcha = ask "Input captcha: "
# Submit the login form
my_page = page.form_with(:action => '/login.php') do |f|
f.email = my_mail
f.password = my_pass
f.captcha = captcha
end.click_button
# already logged!
a.get('https://www.fanfiction.net/alert/story.php') do |page|
page.links.each do |link|
text = link.text.strip
next unless text.length > 0
puts text
end
end
end
well, turns out I was doing everything ok, just that I had forgotten one extra parameter in the form, a hidden input which had the id of the captcha.
I just needed to capture the id at the same time that I captured the captcha image, and then send it in the form POST as an extra parameter.
Hope this helps anyone.
Related
I'm sharing the video on Facebook (Without the SLComposer) from my IOS App. it will send successfully but I want To add the HashTag Text With it. Im Trying it But It will not get add shared with the video (only video get shared ).
FBSDKShareVideo *ShareVideo = [FBSDKShareVideo videoWithVideoURL:appDelegateObj.finalVideoUrl];
ShareVideo.videoURL = appDelegateObj.finalVideoUrl;
FBSDKShareVideoContent *ShareContnt = [[FBSDKShareVideoContent alloc] init];
ShareContnt.video = ShareVideo;
ShareContnt.hashtag = [FBSDKHashtag hashtagWithString:[NSString stringWithFormat:#"%#",#"We are #sharing this #video for the #testing of #video and the #HashTag Text"]];
[FBSDKShareAPI shareWithContent:ShareContnt delegate:self];
Please Help me for this issues ?
100% Working
I got the ANS Of these...
//Using these code we only share can't send the text / Title or name of video...
-(void)facbookSharng
{
NSLog(#"Permission for sharing..%#",[FBSDKAccessToken currentAccessToken].permissions);
if ([[FBSDKAccessToken currentAccessToken] hasGranted:#"contact_email"])
{
FBSDKShareVideo *ShareVideo = [FBSDKShareVideo videoWithVideoURL:appDelegateObj.finalVideoUrl];
ShareVideo.videoURL = appDelegateObj.finalVideoUrl;
FBSDKShareVideoContent *ShareContnt = [[FBSDKShareVideoContent alloc] init];
ShareContnt.video = ShareVideo;
[FBSDKShareAPI shareWithContent:ShareContnt delegate:self]
// write the deleate methdo for post ID..
}
}
//But for these Facebook gives another way,
NSLog(#"Permission for sharing..%#",[FBSDKAccessToken currentAccessToken].permissions);
if ([[FBSDKAccessToken currentAccessToken] hasGranted:#"contact_email"])
{
NSData *videoData = [NSData dataWithContentsOfURL:appDelegateObj.finalVideoUrl];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:3L];
[params setObject:videoData forKey:#"video_filename.MOV"];
[params setObject:#"Title for this post." forKey:#"title"];
[params setObject:#"#Description for this post." forKey:#"description"];
[[[FBSDKGraphRequest alloc] initWithGraphPath:#"/me/videos" parameters:params HTTPMethod:#"POST"]
startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
if (!error) {
//video posted
NSLog(#"Facebook sharing completed %#:",result);
strFbSocialPostId = [result valueForKey:#"id"];//post ID
}
}];
}
First of all i am using parse.com to store information.
This code simply opens the Maps app every time this method is run and saves the users location in a server.
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
[request setSource:[MKMapItem mapItemForCurrentLocation]];
[request setDestination:endingItem];
[request setTransportType:MKDirectionsTransportTypeAutomobile];
[request setRequestsAlternateRoutes:YES];
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
if ( ! error && [response routes] > 0) {
MKRoute *route = [[response routes] objectAtIndex:0];
//route.distance = The distance
NSLog(#"total %f",route.expectedTravelTime );
int time = ceil(route.expectedTravelTime/60);
self.ETA = [#(time) stringValue];
NSLog(#"test %d",time);
NSLog(#"Total Distance (in Meters) :%0.1f",route.distance/1000);
self.distance = [#(route.distance*4899) stringValue];
// IF decline was pressed, need to fix if it's accepted
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[params setObject:self.distance forKey:#"dist"];
[PFCloud callFunctionInBackground:#"sendAccepted" withParameters:params block:^(id object, NSError *error) {
if (!error) {
NSLog(#"Success answer sent");
} else {
NSLog(#"Failed to push");
}
}];
}
}];
[endingItem openInMapsWithLaunchOptions:launchOptions];
}
What i noticed is that if Maps application is already open when this method is run then it does not save the users data until i return to the applikation. HOWEVER if i close the Maps application before this method is run the it is always sent to the server.
Now the problem i think is that it obviously takes more time for Maps app to open if it was not opened before hence giving my applikation more time to complete the update. How can i solve this so it will still update the location even if my applikation goes to the background?
I am in the process of switching over some of my code from AFNetworking 1.0 to 2.0.
Before when doing a POST, I was creating an AFHTTPClient, and an AFHTTPRequestOperation like so:
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:reqUrl];
[httpClient setParameterEncoding:AFJSONParameterEncoding];
httpClient.operationQueue.maxConcurrentOperationCount = 1;
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
req.viewName, #"viewName",
req.json, #"JSON",
req.dateAdded.description, #"dateTime",
req.latitude, #"latitude",
req.longitude, #"longitude",
req.heading, #"heading",
req.user, #"requestUser",
nil];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[op setCompletionBlockWithSuccess:
^(AFHTTPRequestOperation *operation,
id responseObject) {
.......convert responseObject (string) to NSDictionary.....
});
This worked fine, and my POSTs went through and I received a successful text response from the server. (which I then converted to a NSDictionary)
I now am using an AFHTTPSessionManager singleton, and calling the POST method from that. When initializing my AFHTTPSessionManager, I am doing the following:
AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer];
[self setResponseSerializer:responseSerializer];
self.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:#"text/html", nil];
Then in my other class, I am calling the POST like so:
NSDictionary *params = #{
#"viewName":req.viewName,
#"JSON":req.json,
#"dateTime":req.dateAdded.description,
#"latitude":req.latitude,
#"longitude":req.longitude,
#"heading":req.heading,
#"requestUser":req.user
};
[netManager POST:path parameters:params success:^(NSURLSessionDataTask *task, id responseObject) {
.....
} failure:^(NSURLSessionDataTask *task, NSError *error) {
//failing here
});
My data has not changed at all, but the POSTs always fail with the error:
Error Domain=AFNetworkingErrorDomain Code=-1011 "Request failed: bad request (400)" UserInfo=0x1704675c0 {AFNetworkingOperationFailingURLResponseErrorKey=<NSHTTPURLResponse: 0x178234660> { URL: ... } { status code: 400, headers {
"Content-Length" = 2738;
"Content-Type" = "text/html";
Date = "Thu, 15 May 2014 16:13:51 GMT";
Server = "Microsoft-IIS/7.0";
"X-Powered-By" = "ASP.NET";
Whats different that is causing the new AFNetworking 2.0 POST code to not work with this now? Is there anything I need to be setting? The URL and Parameters I am passing are the same as they were with the old way I was sending the POST.
Thanks
My solution ended up being a pretty simple one
In my AFHTTPSessionManager's init, I was not setting the RequestSerializer along with the ResponseSerializer.
After setting it correctly, my POSTs are going through fine again. Heres what I set:
[self setResponseSerializer:[AFJSONResponseSerializer serializer]];
self.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:#"application/json", nil];
[self setRequestSerializer:[AFJSONRequestSerializer serializer]];
EDIT
Aaron Brager stated that those first 2 lines are defaults and not needed. All I needed was to set the RequestSerializer. I tested and can verify this.
I m using the following twitter api to get the friends list:
https://api.twitter.com/1.1/friends/list.json?
And using [SLRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) to get the response data, and perform UI Changes and model data changes in performRequestWithHandler block.
But a single request at the maximum retrieves only 20 friends.(if you set the cursor parameter in api to -1).
I can use the cursor parameter of the api to send request to get the next 20 friends and so on until the cursor value is 0.
cursor parameter can be set to the 'next_cursor' parameter in the response data of the previous request.
But i m not aware of how to call another SLRequest with in the performRequestWithHandler of the previous request, until the 'next_cursor' value in response data of previous request is 0.
Can anybody tell me how to get all the friends using SLRequest or using any other way.
Any help is appreciated.
Thank you.
u can call the next request in the request handler just after you get the response of twitter friends.
Sorry for not elaborating. I thought you would understand.
Here is the code.
ACAccountStore *account = [[ACAccountStore alloc] init];
ACAccountType *accountType = [account accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
// Request access from the user to access their Twitter account
[account requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error)
{
// Did user allow us access?
if (granted == YES)
{
// Populate array with all available Twitter accounts
NSArray *arrayOfAccounts = [account accountsWithAccountType:accountType];
// Sanity check
if ([arrayOfAccounts count] > 0)
{
[self postRequest];
}
}
}];
- (void)PostRequest
{
// Keep it simple, use the first account available
ACAccount *acct = [arrayOfAccounts objectAtIndex:0];
NSMutableDictionary *tempDict = [[NSMutableDictionary alloc] init];
[tempDict setValue:#"Posting video" forKey:#"status"];
// Build a twitter request
SLRequest *postRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodPOST URL:[NSURL URLWithString:#"https://api.twitter.com/1.1/statuses/update.json"] parameters:tempDict];
[postRequest setAccount:acct];
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
NSLog(#"Twitter response, HTTP response: %i", [urlResponse statusCode]);
NSString *output = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(#"%#", output);
**// calling this again and again will help you with multiple post request.**
[self postRequest]
}];
}
Similar thing can be done for friend list too.
Hope I helped.
So I'm trying to use the built in Twitter API in iOS 5 to retrieve a list of all the followers for a given user. In all the example documentation I can find, requests are made to the API passing inline blocks to be executed when the request returns, which is fine for most of the simpler stuff, BUT when I'm trying to get ~1000 followers, and the request is returning them paged in sizes ~100, I'm stuck on how to recursively call the request again using the 'next paging address' returned and processed inside the completion block. Here is the code:
- (void)getTwitterFollowers {
// First, we need to obtain the account instance for the user's Twitter account
ACAccountStore *store = [[ACAccountStore alloc] init];
ACAccountType *twitterAccountType =
[store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
// Request access from the user for access to his Twitter accounts
[store requestAccessToAccountsWithType:twitterAccountType
withCompletionHandler:^(BOOL granted, NSError *error) {
if (!granted) {
// The user rejected your request
NSLog(#"User rejected access to his account.");
}
else {
// Grab the available accounts
NSArray *twitterAccounts =
[store accountsWithAccountType:twitterAccountType];
if ([twitterAccounts count] > 0) {
// Use the first account for simplicity
ACAccount *account = [twitterAccounts objectAtIndex:0];
// Now make an authenticated request to our endpoint
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[params setObject:#"1" forKey:#"include_entities"];
// The endpoint that we wish to call
NSURL *url = [NSURL URLWithString:#"http://api.twitter.com/1/followers.json"];
// Build the request with our parameter
request = [[TWRequest alloc] initWithURL:url
parameters:params
requestMethod:TWRequestMethodGET];
[params release];
// Attach the account object to this request
[request setAccount:account];
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if (!responseData) {
// inspect the contents of error
FullLog(#"%#", error);
}
else {
NSError *jsonError;
followers = [NSJSONSerialization JSONObjectWithData:responseData
options:NSJSONReadingMutableLeaves
error:&jsonError];
if (followers != nil) {
// THE DATA RETURNED HERE CONTAINS THE NEXT PAGE VALUE NEEDED TO REQUEST THE NEXT 100 FOLLOWERS,
//WHAT IS THE BEST WAY TO USE THIS??
FullLog(#"%#", followers);
}
else {
// inspect the contents of jsonError
FullLog(#"%#", jsonError);
}
}
}];
} // if ([twitterAccounts count] > 0)
} // if (granted)
}];
[store release];
}
Ideally I'd like some way to listen for this data being returned, check for a next page value and if it exists, reuse the code block and append the data returned. I', sure there must be a 'best-practice' way to achieve this, any help would be much appreciated!
To use any block recursively you have to declare it first and define it later. Try this:
__block void (^requestPageBlock)(NSInteger pageNumber) = NULL;
requestPageBlock = [^(NSInteger pageNumber) {
// do request with some calculations
if (nextPageExists) {
requestPageBlock(pageNumber + 1);
}
} copy];
// now call the block for the first page
requestPageBlock(0);
To expand on #Eimantas' answer, your request handler is expecting a specific block signature, so you need a different way to handle the page number.
-(void)getTwitterFollowers {
// set up request...
__block int page = 0;
__block void (^requestHandler)(NSData*, NSHTTPURLResponse*, NSError*) = null;
__block TWRequest* request = [[TWRequest alloc] initWithURL:url
parameters:params
requestMethod:TWRequestMethodGET];
requestHandler = [^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
followers = [NSJSONSerialization JSONObjectWithData:responseData
options:NSJSONReadingMutableLeaves
error:&jsonError];
if (followers != nil) {
// process followers
page++;
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:request.parameters];
// update params with page number
request = [[TWRequest alloc] initWithURL:url
parameters:params
requestMethod:TWRequestMethodGET];
[request performRequestWithHandler:requestHandler];
}
} copy];
// now call the block for the first page
[request performRequestWithHandler:requestHandler];
}