Log In to Google Account through Curl - php

I am trying to get this script to work:
https://github.com/aaronpk/Google-Voice-PHP-API
Google's recently changed their way to log in to your account. What would be the steps to login through cURL to Google?
It will throw this now when trying to post to the form on this page (https://accounts.google.com/signin/challenge/sl/password):
Type: Exception
Message: Could not parse for GALX or gxf token. Inputs from page: array ()

without checking that code specifically, here's the code i use to login to gmail, using hhb_curl (and note: initially i pretended to be an iPhone, because it was easier to login via the mobile login page than the desktop login page, but eventually i ported it to the desktop version, but there's still some mobile-version-residue in the code. also, sometimes gmail will randomly ask you to verify your recovery email when using this method, thus the $recoveryEmailChallengeAnswer variable. it doesn't happen everytime, but google notice that something is "off" with the login, and thus asks for recovery email verification randomly. my code handles that situation transparently.):
function loginGmail(string $username, string $password, string $recoveryEmailChallengeAnswer, bool $requestHtmlVersion = true): \hhb_curl {
$hc = new hhb_curl ( '', true );
$hc->setopt_array ( array (
CURLOPT_TIMEOUT => 20, // i just have a shitty connection :(
CURLOPT_CONNECTTIMEOUT => 10
) );
if (0) {
$hc->setopt_array ( array (
CURLOPT_USERAGENT => 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1'
) );
}
$html = $hc->exec ( 'https://gmail.com' )->getStdOut ();
$domd = #DOMDocument::loadHTML ( $html );
$inputs = getDOMDocumentFormInputs ( $domd, true, false ) ['gaia_loginform'];
// hhb_var_dump ( $hc->getStdErr (), $hc->getStdOut (), $inputs ) & die();
$loginUrl = $domd->getElementById ( "gaia_loginform" )->getAttribute ( "action" );
$inputs ['Email'] = $username;
$html = $hc->setopt_array ( array (
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => http_build_query ( $inputs ),
CURLOPT_URL => $loginUrl
) )->exec ()->getStdOut ();
$domd = #DOMDocument::loadHTML ( $html );
$inputs = getDOMDocumentFormInputs ( $domd, true, false ) ['gaia_loginform'];
// hhb_var_dump ( $hc->getStdErr (), $hc->getStdOut (), $inputs );
$loginUrl = $domd->getElementById ( "gaia_loginform" )->getAttribute ( "action" );
$inputs ['Passwd'] = $password;
try {
$starttime = microtime ( true );
$html = $hc->setopt_array ( array (
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => http_build_query ( $inputs ),
CURLOPT_URL => $loginUrl
) )->exec ()->getStdOut ();
} finally{
// hhb_var_dump ( $hc->getStdErr (), $hc->getStdOut (), $inputs, (microtime ( true ) - $starttime) ) & die ();
}
$domd = #DOMDocument::loadHTML ( $html );
$xp = new DOMXPath ( $domd );
$loginErrors = $xp->query ( '//span[contains(#class,"error-msg")]' );
$loginErrorText = '';
foreach ( $loginErrors as $tmp ) {
$tmp = trim ( $tmp->textContent );
if (strlen ( $tmp )) {
$loginErrorText .= ' - ' . $tmp;
}
}
$authChallenge = $domd->getElementById ( "challengePickerList" );
if (NULL !== $authChallenge) {
// gotdammit... got an auth challenge page, asking you to choose a challenge
$loginErrorText .= ' - got an auth challenge page, asking you to choose a challenge: ' . trim ( $authChallenge->textContent );
// TODO: automatically select "provide recovery email" here instead of throwing a login error exception.
} else {
if (false !== stripos ( $html, 'Enter recovery email' ) && NULL !== ($authChallenge = $domd->getElementById ( "challenge" ))) {
// got an auth challenge for providing recovery email..
// luckily, we can automatically recover from this.
$inputs = getDOMDocumentFormInputs ( $domd, true, false ) ['challenge'];
assert ( array_key_exists ( 'email', $inputs ) );
$inputs ['email'] = $recoveryEmailChallengeAnswer;
$url = $authChallenge->getAttribute ( "action" );
if (! parse_url ( $url, PHP_URL_HOST )) {
$url = 'https://' . parse_url ( $hc->getinfo ( CURLINFO_EFFECTIVE_URL ), PHP_URL_HOST ) . $url;
}
$html = $hc->setopt_array ( array (
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => http_build_query ( $inputs ),
CURLOPT_URL => $url
) )->exec ()->getStdOut ();
$domd = #DOMDocument::loadHTML ( $html );
$xp = new DOMXPath ( $domd );
// TODO: detect incorrect recovery email supplied error here.
}
}
unset ( $authChallenge );
if (! empty ( $loginErrorText )) {
throw new \RuntimeException ( 'errors loggin in: ' . $loginErrorText );
} else {
// logged in! :D
}
if ($requestHtmlVersion) {
// now we need to enable HTML view, it's a <form> POST request, but we can't use getDOMDocumentFormInputs (bug?)
$found = false;
foreach ( $domd->getElementsByTagName ( "form" ) as $form ) {
if (false === stripos ( $form->textContent, "Gmail's basic HTML view, which doesn't require JavaScript" ) && $xp->query ( "./input[#value='Load basic HTML']", $form )->length === 0) {
continue;
}
$found = true;
$url = $form->getAttribute ( "action" );
if (! parse_url ( $url, PHP_URL_HOST )) {
$url = $hc->getinfo ( CURLINFO_EFFECTIVE_URL ) . $url;
}
// hhb_var_dump ( $url ) & die ();
$inputs = [ ];
foreach ( $form->getElementsByTagName ( "input" ) as $input ) {
$name = $input->getAttribute ( "name" );
if (empty ( $name )) {
continue;
}
$inputs [$name] = $input->getAttribute ( "value" );
}
// hhb_var_dump ( $inputs ) & die ();
break;
}
if (! $found) {
$str = 'failed to find HTML version request form!';
// hhb_var_dump ( $str, $hc->getStdErr (), $hc->getStdOut (), $inputs ); // & die ();
throw new \RuntimeException ( $str );
}
$html = $hc->setopt_array ( array (
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => http_build_query ( $inputs ),
CURLOPT_URL => $url
) )->exec ()->getStdOut ();
}
hhb_var_dump ( $hc->getStdErr (), $hc->getStdOut (), $inputs ); // & die ();
return $hc;
}
function rightTrim($str, $needle, $caseSensitive = true) {
$strPosFunction = $caseSensitive ? "strpos" : "stripos";
if ($strPosFunction ( $str, $needle, strlen ( $str ) - strlen ( $needle ) ) !== false) {
$str = substr ( $str, 0, - strlen ( $needle ) );
}
return $str;
}
function getDOMDocumentFormInputs(\DOMDocument $domd, bool $getOnlyFirstMatches = false, bool $getElements = true): array {
// :DOMNodeList?
if (! $getOnlyFirstMatches && ! $getElements) {
throw new \InvalidArgumentException ( '!$getElements is currently only implemented for $getOnlyFirstMatches (cus im lazy and nobody has written the code yet)' );
}
$forms = $domd->getElementsByTagName ( 'form' );
$parsedForms = array ();
$isDescendantOf = function (\DOMNode $decendant, \DOMNode $ele): bool {
$parent = $decendant;
while ( NULL !== ($parent = $parent->parentNode) ) {
if ($parent === $ele) {
return true;
}
}
return false;
};
// i can't use array_merge on DOMNodeLists :(
$merged = function () use (&$domd): array {
$ret = array ();
foreach ( $domd->getElementsByTagName ( "input" ) as $input ) {
$ret [] = $input;
}
foreach ( $domd->getElementsByTagName ( "textarea" ) as $textarea ) {
$ret [] = $textarea;
}
foreach ( $domd->getElementsByTagName ( "button" ) as $button ) {
$ret [] = $button;
}
return $ret;
};
$merged = $merged ();
foreach ( $forms as $form ) {
$inputs = function () use (&$domd, &$form, &$isDescendantOf, &$merged): array {
$ret = array ();
foreach ( $merged as $input ) {
// hhb_var_dump ( $input->getAttribute ( "name" ), $input->getAttribute ( "id" ) );
if ($input->hasAttribute ( "disabled" )) {
// ignore disabled elements?
continue;
}
$name = $input->getAttribute ( "name" );
if ($name === '') {
// echo "inputs with no name are ignored when submitted by mainstream browsers (presumably because of specs)... follow suite?", PHP_EOL;
continue;
}
if (! $isDescendantOf ( $input, $form ) && $form->getAttribute ( "id" ) !== '' && $input->getAttribute ( "form" ) !== $form->getAttribute ( "id" )) {
// echo "this input does not belong to this form.", PHP_EOL;
continue;
}
if (! array_key_exists ( $name, $ret )) {
$ret [$name] = array (
$input
);
} else {
$ret [$name] [] = $input;
}
}
return $ret;
};
$inputs = $inputs (); // sorry about that, Eclipse gets unstable on IIFE syntax.
$hasName = true;
$name = $form->getAttribute ( "id" );
if ($name === '') {
$name = $form->getAttribute ( "name" );
if ($name === '') {
$hasName = false;
}
}
if (! $hasName) {
$parsedForms [] = array (
$inputs
);
} else {
if (! array_key_exists ( $name, $parsedForms )) {
$parsedForms [$name] = array (
$inputs
);
} else {
$parsedForms [$name] [] = $tmp;
}
}
}
unset ( $form, $tmp, $hasName, $name, $i, $input );
if ($getOnlyFirstMatches) {
foreach ( $parsedForms as $key => $val ) {
$parsedForms [$key] = $val [0];
}
unset ( $key, $val );
foreach ( $parsedForms as $key1 => $val1 ) {
foreach ( $val1 as $key2 => $val2 ) {
$parsedForms [$key1] [$key2] = $val2 [0];
}
}
}
if ($getElements) {
return $parsedForms;
}
$ret = array ();
foreach ( $parsedForms as $formName => $arr ) {
$ret [$formName] = array ();
foreach ( $arr as $ele ) {
$ret [$formName] [$ele->getAttribute ( "name" )] = $ele->getAttribute ( "value" );
}
}
return $ret;
}

Related

PHP Login using CURL not working

I want to login website bukalapak.com, but I have problem I just got blank page after execute this php, here my code :
var_dump(login_bukalapak());
function login_bukalapak(){
$data_login = array(
'user_session[username]' => 'myusername',
'user_session[password]' => 'mypassword'
);
$url = "https://www.bukalapak.com/user_sessions";
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $url);
$cookie = 'cookies.txt';
$timeout = 30;
curl_setopt ($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout );
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie);
curl_setopt ($ch, CURLOPT_POST, 1);
curl_setopt ($ch, CURLOPT_POSTFIELDS,$data_login);
$result = curl_exec($ch);
/*$url = "https://www.bukalapak.com/products/new?from=dropdown";
curl_setopt ($ch, CURLOPT_POST, 0);
curl_setopt($ch, CURLOPT_URL, $url);
$result = curl_exec($ch);*/
curl_close($ch);
return $result;
}
I confuse I just have return blank, how the correct answer to make it run well and return successfully login ?
Thanks.
you're doing many things wrong here.
1: their login system use application/x-www-form-urlencoded encoding for the login POST request body, but your code try to login using multipart/form-data encoding.
2: their login system requires you to have a pre-existing cookie session before logging in, but your code does not obtain a cookie session prior to sending the login request.
3: they use a CSRF token scheme to protect their login, where you need to fetch the csrf token from the page, and add it to your login post fields before sending the login request, called authenticity_token (it's in their html as <input type="hidden" name="authenticity_token" value="<TOKEN>" />), your code makes no attempt to fetch and extract this token prior to logging in.
4: there is a bunch of login parameters you're missing, namely utf8, user_session[remember_me], comeback, button, and as i said previously, authenticity_token - not all of these parameters may be required, or perhaps all of them are, but at least some of them (authenticity_token) is 100% sure to be required, just add them all, unless you wanna waste time figuring out which parameters are required and which is not, it's probably not worth the effort.
5: your code lacks error checking, curl_setopt returns bool(false) if there was a problem setting your settings, and curl_exec returns bool(false) if there was a problem during the transfer
with that in mind, here is a sample implementation in hhb_curl that i think would work with a real username/password:
<?php
require_once ('hhb_.inc.php');
$hc = login_bukalapak ( "foo#bar.com", "password" );
var_dump ( $hc->exec ( 'https://www.bukalapak.com/' )->getStdOut () );
function login_bukalapak(string $username, string $password): \hhb_curl {
$hc = new hhb_curl ( '', true );
$html = $hc->exec ( 'https://www.bukalapak.com/' )->getStdOut ();
$domd = #DOMDocument::loadHTML ( $html );
$data_login = getDOMDocumentFormInputs ( $domd, true, false ) ['new_user_session'];
// var_dump ( $data_login ) & die ();
assert ( isset ( $data_login ['user_session[username]'], $data_login ['user_session[password]'] ), 'username/password field not found in login form!' );
$data_login ['user_session[username]'] = $username;
$data_login ['user_session[password]'] = $password;
$url = "https://www.bukalapak.com/user_sessions";
$html = $hc->setopt_array ( array (
CURLOPT_URL => 'https://www.bukalapak.com/user_sessions',
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => http_build_query ( $data_login )
) )->exec ()->getStdOut ();
//var_dump ( $html );
$domd = #DOMDocument::loadHTML ( $html );
$xp = new DOMXPath ( $domd );
$loginErrorEles = $xp->query ( '//*[contains(#class,"__error") and not(contains(#class,"hidden"))]' );
$loginErrors = '';
foreach ( $loginErrorEles as $loginError ) {
if (empty ( $loginError->textContent )) {
continue;
}
$loginErrors .= trim ( $loginError->textContent );
}
if (! empty ( $loginErrors )) {
throw new RuntimeException ( 'failed to log in: '.$loginErrors);
}
// assuming logged in successfully
// note that its highly unreliable, they sometimes say "wrong username/password", and someitmes not, seemingly randomly.
return $hc;
}
function getDOMDocumentFormInputs(\DOMDocument $domd, bool $getOnlyFirstMatches = false, bool $getElements = true): array {
// :DOMNodeList?
if (! $getOnlyFirstMatches && ! $getElements) {
throw new \InvalidArgumentException ( '!$getElements is currently only implemented for $getOnlyFirstMatches (cus im lazy and nobody has written the code yet)' );
}
$forms = $domd->getElementsByTagName ( 'form' );
$parsedForms = array ();
$isDescendantOf = function (\DOMNode $decendant, \DOMNode $ele): bool {
$parent = $decendant;
while ( NULL !== ($parent = $parent->parentNode) ) {
if ($parent === $ele) {
return true;
}
}
return false;
};
// i can't use array_merge on DOMNodeLists :(
$merged = function () use (&$domd): array {
$ret = array ();
foreach ( $domd->getElementsByTagName ( "input" ) as $input ) {
$ret [] = $input;
}
foreach ( $domd->getElementsByTagName ( "textarea" ) as $textarea ) {
$ret [] = $textarea;
}
foreach ( $domd->getElementsByTagName ( "button" ) as $button ) {
$ret [] = $button;
}
return $ret;
};
$merged = $merged ();
foreach ( $forms as $form ) {
$inputs = function () use (&$domd, &$form, &$isDescendantOf, &$merged): array {
$ret = array ();
foreach ( $merged as $input ) {
// hhb_var_dump ( $input->getAttribute ( "name" ), $input->getAttribute ( "id" ) );
if ($input->hasAttribute ( "disabled" )) {
// ignore disabled elements?
continue;
}
$name = $input->getAttribute ( "name" );
if ($name === '') {
// echo "inputs with no name are ignored when submitted by mainstream browsers (presumably because of specs)... follow suite?", PHP_EOL;
continue;
}
if (! $isDescendantOf ( $input, $form ) && $form->getAttribute ( "id" ) !== '' && $input->getAttribute ( "form" ) !== $form->getAttribute ( "id" )) {
// echo "this input does not belong to this form.", PHP_EOL;
continue;
}
if (! array_key_exists ( $name, $ret )) {
$ret [$name] = array (
$input
);
} else {
$ret [$name] [] = $input;
}
}
return $ret;
};
$inputs = $inputs (); // sorry about that, Eclipse gets unstable on IIFE syntax.
$hasName = true;
$name = $form->getAttribute ( "id" );
if ($name === '') {
$name = $form->getAttribute ( "name" );
if ($name === '') {
$hasName = false;
}
}
if (! $hasName) {
$parsedForms [] = array (
$inputs
);
} else {
if (! array_key_exists ( $name, $parsedForms )) {
$parsedForms [$name] = array (
$inputs
);
} else {
$parsedForms [$name] [] = $tmp;
}
}
}
unset ( $form, $tmp, $hasName, $name, $i, $input );
if ($getOnlyFirstMatches) {
foreach ( $parsedForms as $key => $val ) {
$parsedForms [$key] = $val [0];
}
unset ( $key, $val );
foreach ( $parsedForms as $key1 => $val1 ) {
foreach ( $val1 as $key2 => $val2 ) {
$parsedForms [$key1] [$key2] = $val2 [0];
}
}
}
if ($getElements) {
return $parsedForms;
}
$ret = array ();
foreach ( $parsedForms as $formName => $arr ) {
$ret [$formName] = array ();
foreach ( $arr as $ele ) {
$ret [$formName] [$ele->getAttribute ( "name" )] = $ele->getAttribute ( "value" );
}
}
return $ret;
}

Issue to connect to a third party website

I have this website [click to see]
[1]: http://www.forumjeep.ca/connect.php and as you can see, its only codes that we see. I'm trying to see the third party website but its not working. I'm able to login with the credentials, but im stuck from there. Here's the code i'm using thanks to Hanshenrik . Any help possible ?
<?php
declare(strict_types = 1);
const USERNAME = '???';
const PASSWORD = '???';
header ( "content-type: text/plain;charset=utf8" );
require_once ('hhb_.inc.php');
$hc = new hhb_curl ( '', true );
$html = $hc->exec ( 'https://app.cfe.gob.mx/Aplicaciones/CCFE/Recibos/Consulta/login.aspx' )->getStdOut ();
$domd = #DOMDocument::loadHTML ( $html );
$inputsRaw = getDOMDocumentFormInputs ( $domd, true ) ['aspnetForm'];
$inputs = array ();
foreach ( $inputsRaw as $tmp ) {
$inputs [$tmp->getAttribute ( "name" )] = $tmp->getAttribute ( "value" );
}
assert ( isset ( $inputs ['__VIEWSTATE'], $inputs ['__EVENTVALIDATION'] ) );
$inputs ['ctl00$PHContenidoPag$UCLogin2$LoginUsuario$UserName'] = USERNAME;
$inputs ['ctl00$PHContenidoPag$UCLogin2$LoginUsuario$Password'] = PASSWORD;
hhb_var_dump ( $inputs );
$html = $hc->setopt_array ( array (
CURLOPT_URL => 'https://app.cfe.gob.mx/Aplicaciones/CCFE/Recibos/Consulta/login.aspx',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query ( $inputs )
) )->exec ()->getStdOut ();
// hhb_var_dump($html) & die();
$domd = #DOMDocument::loadHTML ( $html );
$xp = new DOMXPath ( $domd );
$loginErrors = $xp->query ( '//*[(contains(#style,"color:Red") or contains(#color,"Red")) and not(contains(#style,"hidden"))]' );
foreach ( $loginErrors as $tmp ) {
echo "login error!! ";
var_dump ( $tmp->textContent );
}
if (0 === $loginErrors->length) {
echo "login success!";
}
function getDOMDocumentFormInputs(\DOMDocument $domd, bool $getOnlyFirstMatches = false): array {
// :DOMNodeList?
$forms = $domd->getElementsByTagName ( 'form' );
$parsedForms = array ();
$isDescendantOf = function (\DOMNode $decendant, \DOMNode $ele): bool {
$parent = $decendant;
while ( NULL !== ($parent = $parent->parentNode) ) {
if ($parent === $ele) {
return true;
}
}
return false;
};
// i can't use array_merge on DOMNodeLists :(
$merged = function () use (&$domd): array {
$ret = array ();
foreach ( $domd->getElementsByTagName ( "input" ) as $input ) {
$ret [] = $input;
}
foreach ( $domd->getElementsByTagName ( "textarea" ) as $textarea ) {
$ret [] = $textarea;
}
foreach ( $domd->getElementsByTagName ( "button" ) as $button ) {
$ret [] = $button;
}
return $ret;
};
$merged = $merged ();
foreach ( $forms as $form ) {
$inputs = function () use (&$domd, &$form, &$isDescendantOf, &$merged): array {
$ret = array ();
foreach ( $merged as $input ) {
// hhb_var_dump ( $input->getAttribute ( "name" ), $input->getAttribute ( "id" ) );
if ($input->hasAttribute ( "disabled" )) {
// ignore disabled elements?
continue;
}
$name = $input->getAttribute ( "name" );
if ($name === '') {
// echo "inputs with no name are ignored when submitted by mainstream browsers (presumably because of specs)... follow suite?", PHP_EOL;
continue;
}
if (! $isDescendantOf ( $input, $form ) && $form->getAttribute ( "id" ) !== '' && $input->getAttribute ( "form" ) !== $form->getAttribute ( "id" )) {
// echo "this input does not belong to this form.", PHP_EOL;
continue;
}
if (! array_key_exists ( $name, $ret )) {
$ret [$name] = array (
$input
);
} else {
$ret [$name] [] = $input;
}
}
return $ret;
};
$inputs = $inputs (); // sorry about that, Eclipse gets unstable on IIFE syntax.
$hasName = true;
$name = $form->getAttribute ( "id" );
if ($name === '') {
$name = $form->getAttribute ( "name" );
if ($name === '') {
$hasName = false;
}
}
if (! $hasName) {
$parsedForms [] = array (
$inputs
);
} else {
if (! array_key_exists ( $name, $parsedForms )) {
$parsedForms [$name] = array (
$inputs
);
} else {
$parsedForms [$name] [] = $tmp;
}
}
}
unset ( $form, $tmp, $hasName, $name, $i, $input );
if ($getOnlyFirstMatches) {
foreach ( $parsedForms as $key => $val ) {
$parsedForms [$key] = $val [0];
}
unset ( $key, $val );
foreach ( $parsedForms as $key1 => $val1 ) {
foreach ( $val1 as $key2 => $val2 ) {
$parsedForms [$key1] [$key2] = $val2 [0];
}
}
}
return $parsedForms;
}
The problem is that you are setting the content type header to text/plain
header ( "content-type: text/plain;charset=utf8" );
What you need to do is set it to text/html
header ( "content-type: text/html; charset=utf8" );
I would, however, suggest you re-evaluate how you notify the user of a successful login if you wish to dump HTML after, as this
login success!<!DOCTYPE html PUB...
is not good at all.
Try and keep everything you want to render inside the tags, and keep those tags inside your tags.
Good luck!

Logic using cURL, PHP and Twitter without using API

I have this code, which saves the cookies in a .txt file, and authenticates the user with Twitter
<?php
require_once 'class/Cookies.php';
$cookie = [];
$username = 'formUser';
$password = 'formPass';
$index_url = 'https://twitter.com';
$token = curl_init();
curl_setopt_array($token, [
CURLOPT_URL => $index_url,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'],
//CURLOPT_COOKIEFILE => __DIR__ . DIRECTORY_SEPARATOR . 'cookies' . DIRECTORY_SEPARATOR . $username . '.txt',
CURLOPT_COOKIEJAR => __DIR__ . DIRECTORY_SEPARATOR . 'cookies' . DIRECTORY_SEPARATOR . $username . '.txt',
CURLOPT_COOKIESESSION => true,
CURLOPT_REFERER => $index_url,
CURLOPT_HEADER => true,
CURLOPT_HTTPHEADER => ['Cookie:' . http_build_query($cookie, '', ';') . ';'],
CURLOPT_HEADERFUNCTION => function ($curl, $header) use (&$cookie) {
if (stripos($header, 'Set-Cookie:') === 0) {
if (preg_match('/Set-Cookie:\s?(.*?)=(.*?);/i', $header, $matches)) {
$cookie[$matches[1]] = urldecode($matches[2]);
}
}
return strlen($header);
}
]
);
$access = curl_exec($token);
preg_match('/value="(.*?)" name="authenticity_token"/', $access, $matches);
$authenticity_token = $matches[1];
//var_dump($authenticity_token);
$session_post = "session[username_or_email]=$username&session[password]=$password&return_to_ssl=true&scribe_log=&redirect_after_login=%2F&authenticity_token=$authenticity_token";
$session_url = 'https://twitter.com/sessions';
curl_setopt_array($token, [
CURLOPT_URL => $session_url,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $session_post,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Content-type: application/x-www-form-urlencoded",
'Cookie: '. http_build_query($cookie, '', ';').';'
],
CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'],
CURLOPT_HEADER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 2,
CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
CURLOPT_POSTREDIR => 2,
CURLOPT_AUTOREFERER => 1
]
);
$auth = curl_exec($token);
var_dump($cookie);
if (isset($cookie['auth_token']))
{
$twid = filter_var($cookie['twid'], FILTER_SANITIZE_NUMBER_INT);
Cookies::set('login_token', $cookie['ct0']);
Cookies::set('kdt', $cookie['kdt']);
Cookies::set('user_id', $twid);
Cookies::set('auth_token', $cookie['auth_token']);
Cookies::set('username', $username);
echo json_encode(array(
"status" => "success",
"message" => "Authentication successful, we are redirecting you.",
));
}
else
{
echo json_encode(
array(
"status" => "error",
'message'=> "Unable to authenticate with Twitter.",
));
}
And this code that captures the information of the logged in user:
<?php
$username = 'sessionUser';
$url = 'https://twitter.com/' . $username;
$user = curl_init();
curl_setopt_array($user, [
CURLOPT_URL => $url,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_CAINFO => 'cacert-2017-06-07.pem',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_HTTPHEADER => [
"Content-type:text/html;charset=utf-8",
],
CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'],
CURLOPT_HEADER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 2,
CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
CURLOPT_POSTREDIR => 2,
CURLOPT_AUTOREFERER => 1,
CURLOPT_ENCODING => "gzip"
]
);
$user_info = curl_exec($user);
$header_size = curl_getinfo($user, CURLINFO_HEADER_SIZE);
$header = substr($user_info, 0, $header_size);
$body = substr($user_info, $header_size);
$dom = new DOMDocument("5.0", "utf-8");
#$dom->loadHTML($body);
$data = json_decode($dom->getElementById("init-data")->getAttribute("value"));
echo "Nome: ", $data->profile_user->id, PHP_EOL;
echo "Nome: ", $data->profile_user->name, PHP_EOL;
echo "Usuário: ", $data->profile_user->screen_name, PHP_EOL;
echo "Foto de perfil: ", $data->profile_user->profile_image_url, PHP_EOL;
I need help so that the cookies of users saved in the .txt or in the database, make followers exchange.
How can I do this?
EDIT
Who downvote, leave comments.
EDIT 2
File follow.php
<?php
require_once '../modules/config.php';
require_once '../modules/class/Cookies.php';
$username = Cookies::get('username');
$friend_url = 'https://api.twitter.com/1.1/friendships/create.json';
$friend = curl_init();
curl_setopt_array($friend, [
CURLOPT_URL => $friend_url,
CURLOPT_SSL_VERIFYPEER => 1,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_CAINFO => ROOT . 'modules' . SEPARATOR . 'cacert' . SEPARATOR . 'cacert-2017-06-07.pem',
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => 'screen_name=' . $username,
CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Content-type: application/json; charset=utf-8",
],
CURLOPT_HEADER => true,
]
);
$response = curl_exec($friend);
var_dump($response);
Response:
C:\wamp64\www\brfollow\api\follow.php:32:string 'HTTP/1.1 400 Bad Request
content-length: 62
content-type: application/json; charset=utf-8
date: Fri, 07 Jul 2017 08:09:54 GMT
server: tsa_d
set-cookie: guest_id=v1%3A149941499419523606; Domain=.twitter.com; Path=/; Expires=Sun, 07-Jul-2019 08:09:54 UTC
strict-transport-security: max-age=631138519
x-connection-hash: 9e951d1215095efa246c5b852acd2e8a
x-response-time: 131
x-tsa-request-body-time: 0
{"errors":[{"code":215,"message":"Bad Authentication data."}]}' (length=472)
first some notes on your existing code:
don't use CURLOPT_CUSTOMREQUEST for GET and POST requests. for GET, use CURLOPT_HTTPGET=>true (also note that GET is the default request for libcurl), and for POST requests, use CURLOPT_POST=>true.
this line preg_match('/value="(.*?)" name="authenticity_token"/', $access, $matches);
will break if they put any additional properties between the value and the name,
and it will even break if they just move the name behind the value, and it will break
if they put a simmilar string in a comment (<!-- -->-style), and it will even break if they just put another space between value and name,
and parsing HTML with regex is generally a bad idea
a much more robust approach would be:
$authenticity_token=(new DOMXpath(#DOMDocument::loadHTML($access)))->query("//input[#name='authenticity_token']")->item(0)->getAttribute("value");
in this line, you do the same mistake 3 times:
$session_post = "session[username_or_email]=$username&session[password]=$password&return_to_ssl=true&scribe_log=&redirect_after_login=%2F&authenticity_token=$authenticity_token";
you don't urlencode $username , $password, and $authenticity_token . that means, if any of those 3 contains any characters with special meaning in application/x-www-urlencoded format, the server will get the wrong data (this includes spaces, &, =, [,Æ,Ø,Å, and a lot of other characters), the easy solution is to use urlencode() on them - and the pretty solution is to use http_build_query to make the string, like this:
$session_post = http_build_query ( array (
'session' => array (
'username_or_email' => $username,
'password' => $password
),
'return_to_sssl' => true,
'scribe_log' => '',
'redirect_after_login' => '/',
'authenticity_token' => $authenticity_token
) );
also you make the same mistake on this line:
CURLOPT_POSTFIELDS => 'screen_name=' . $username,
and this line must have been added by mistake:
CURLOPT_HTTPHEADER => [
"Content-type:text/html;charset=utf-8",
],
it is a GET request with no request body, thus there is no content-type, because there is no content, so there's no way that content-type header declaration is supposed to be there, get rid of it.
this line
CURLOPT_ENCODING => "gzip"
will break your code if curl was not compiled with gzip, and the server actually decide to use gzip (more specifically, you will get unintelligible binary data), and you provide no code to actually handle gzip binary data. a much more robust approach is to set it to emptystring "", then curl will provide all encodings that libcurl was compiled will, and will de-encode it for you on the fly (including gzip, if compiled in. its usually gzip and deflate, but its also future-proof because it will add any future encodings automatically)
this line
"Content-type: application/x-www-form-urlencoded",
don't add this header automatically. libcurl will automatically detect application/x-www-urlencoded and multipart/form-data encodings, and set the appropriate content-type headers automatically. and unlike you, libcurl won't make typos in doing so.
now, the next step is to get all your current followers, and send a follow request. you say you don't want to use the api, but THERE IS LITERALLY NO WAY to avoid it (short of hacking the twitter databases, ofc), even tiwtter's javascript's "follow" button uses the api. good news is, you can get away by using twitter's javascript's api token, and thus not having to use your own token. this may sound easy in theory, but its really not. never the less, here's an example implementation with hhb_curl (from https://github.com/divinity76/hhb_.inc.php/blob/master/hhb_.inc.php ), getting a list of your followers, and sending a follow request to each one, using twitters own api key (extracted automatically):
<?php
declare(strict_types = 1);
require_once ('hhb_.inc.php');
const USERNAME = '???';
const PASSWORD = '???';
$hc = new hhb_curl ( 'https://twitter.com/login', true );
$hc->exec ();
// get csrf token
$csrf_token = [ ];
preg_match ( '/\s+ct0\s*=\s*(.*?)\;/', implode ( "\n", $hc->getResponseHeaders () ), $csrf_token );
if (count ( $csrf_token ) !== 2) {
throw new Exception ( 'failed to extract the csrf token!' );
}
$csrf_token = $csrf_token [1];
// to log in...
$html = $hc->getStdOut ();
$domd = #DOMDocument::loadHTML ( $html );
$inputs = getDOMDocumentFormInputs ( $domd, true ) [0]; // << not sure why, but they have 6 seemingly duplicate login forms. the first 1 works fine.
$inputs = DOMInputsToArray ( $inputs );
$inputs ['session[username_or_email]'] = USERNAME;
$inputs ['session[password]'] = PASSWORD;
// hhb_var_dump ( $inputs ) & die ();
$html = $hc->setopt_array ( array (
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query ( $inputs ),
CURLOPT_URL => 'https://twitter.com/sessions'
) )->exec ()->getResponseBody ();
$domd = #DOMDocument::loadHTML ( $html );
$xpath = new DOMXPath ( $domd );
// hhb_var_dump ( $hc->getStdErr (), $hc->getStdOut () );
if (false !== stripos ( $hc->getinfo ( CURLINFO_EFFECTIVE_URL ), 'login/error' )) {
throw new Exception ( 'failed to login!' );
}
echo "logged in!", PHP_EOL;
// now to get the api key
$js = $hc->exec ( 'https://abs.twimg.com/k/en/init.en.c5a67fc1f42cedcdbbcd.js' )->getResponseBody ();
// hhb_var_dump ( $hc->getStdErr (), $hc->getStdOut () ) & die ();
// fragile regex: assumes that there's only 1x i="114 characters"; , and that the api key is exactly 114 characters.
preg_match ( '/i\s*\=\s*\"([^\"]{114})\"\s*\;/iu', $js, $matches );
// hhb_var_dump ( $matches ) & die ();
if (count ( $matches ) !== 2) {
throw new RuntimeException ( 'failed to extract the api auth key!' );
}
$api_auth_key = $matches [1];
$myurl = 'https://twitter.com/' . ltrim ( $xpath->query ( '//a[contains(#class,\'DashboardProfileCard\')]' )->item ( 0 )->getAttribute ( "href" ), '/' );
echo 'myurl: ' . $myurl . PHP_EOL;
// $myurl = 'https://twitter.com/scaleway';
$myurl .= '/followers';
$html = $hc->exec ( $myurl )->getResponseBody ();
// hhb_var_dump ( $hc->getStdErr (), $hc->getStdOut () ) & die ();
$toFollow = array ();
$domd = #DOMDocument::loadHTML ( $html );
$xpath = new DOMXPath ( $domd );
foreach ( $xpath->query ( '//div[contains(#class,\'ProfileCard-content\')]' ) as $followerDiv ) {
$name = $xpath->query ( './/*[#data-screen-name]', $followerDiv )->item ( 0 )->getAttribute ( "data-screen-name" );
$user_id = $xpath->query ( './/*[#data-user-id]', $followerDiv )->item ( 0 )->getAttribute ( "data-user-id" );
echo "following " . $name . ' (' . $user_id . ')' . PHP_EOL;
try {
$hc->setopt_array ( array (
CURLOPT_CUSTOMREQUEST => 'OPTIONS',
CURLOPT_URL => 'https://api.twitter.com/1.1/friendships/create.json',
CURLOPT_HTTPHEADER => array (
'Access-Control-Request-Method: POST',
'Access-Control-Request-Headers: authorization,x-csrf-token,x-twitter-active-user,x-twitter-auth-type',
'DNT: 1',
'Origin: https://twitter.com'
)
) )->exec ();
} catch ( Throwable $ex ) {
// there is a bug where it sometimes respond http 200 OK, but with 0 bytes content. hhb_curl doesn't like this, as 0-bytes-responses should actually be http 201.
// feel free to contact twitter with a bugreport.
}
// hhb_var_dump ( $hc->getStdErr () );
$hc->setopt ( CURLOPT_CUSTOMREQUEST, NULL );
$hc->setopt_array ( array (
CURLOPT_POST => true,
CURLOPT_URL => 'https://api.twitter.com/1.1/friendships/create.json',
CURLOPT_POSTFIELDS => http_build_query ( array (
'challenges_passed' => 'false',
'handles_challenges' => '1',
'impression_id' => '',
'include_blocked_by' => 'true',
'include_blocking' => 'true',
'include_can_dm' => 'true',
'include_followed_by' => 'true',
'include_mute_edge' => 'true',
'skip_status' => 'true',
'user_id' => $user_id
) ),
CURLOPT_HTTPHEADER => array (
'Accept: application/json, text/javascript, */*; q=0.01',
'Accept-Language: en-US,en;q=0.5',
'Authorization: Bearer ' . $api_auth_key,
'x-twitter-auth-type: OAuth2Session',
'x-csrf-token: ' . $csrf_token,
'X-Twitter-Active-User: yes',
'DNT: 1',
'Origin: https://twitter.com',
'Referer: ' . $myurl
)
) )->exec ();
// parse_str ( $hc->getopt ( CURLOPT_POSTFIELDS ), $fields );
// hhb_var_dump ( $fields, $hc->getStdErr (), $hc->getStdOut () ) & die ();
}
// hhb_var_dump ( $myurl );
function DOMInputsToArray($inputs): array {
$ret = [ ];
foreach ( $inputs as $in ) {
if ($in->hasAttribute ( "disabled" )) {
continue;
}
$name = $in->getAttribute ( "name" );
if (empty ( $name )) {
continue;
}
$ret [$name] = $in->getAttribute ( "value" );
}
return $ret;
}
function getDOMDocumentFormInputs(\DOMDocument $domd, bool $getOnlyFirstMatches = false): array {
// :DOMNodeList?
$forms = $domd->getElementsByTagName ( 'form' );
$parsedForms = array ();
$isDescendantOf = function (\DOMNode $decendant, \DOMNode $ele): bool {
$parent = $decendant;
while ( NULL !== ($parent = $parent->parentNode) ) {
if ($parent === $ele) {
return true;
}
}
return false;
};
// i can't use array_merge on DOMNodeLists :(
$merged = function () use (&$domd): array {
$ret = array ();
foreach ( $domd->getElementsByTagName ( "input" ) as $input ) {
$ret [] = $input;
}
foreach ( $domd->getElementsByTagName ( "textarea" ) as $textarea ) {
$ret [] = $textarea;
}
return $ret;
};
$merged = $merged ();
foreach ( $forms as $form ) {
$inputs = function () use (&$domd, &$form, &$isDescendantOf, &$merged): array {
$ret = array ();
foreach ( $merged as $input ) {
// hhb_var_dump ( $input->getAttribute ( "name" ), $input->getAttribute ( "id" ) );
if ($input->hasAttribute ( "disabled" )) {
// ignore disabled elements?
continue;
}
$name = $input->getAttribute ( "name" );
if ($name === '') {
// echo "inputs with no name are ignored when submitted by mainstream browsers (presumably because of specs)... follow suite?", PHP_EOL;
continue;
}
if (! $isDescendantOf ( $input, $form ) && $form->getAttribute ( "id" ) !== '' && $input->getAttribute ( "form" ) !== $form->getAttribute ( "id" )) {
// echo "this input does not belong to this form.", PHP_EOL;
continue;
}
if (! array_key_exists ( $name, $ret )) {
$ret [$name] = array (
$input
);
} else {
$ret [$name] [] = $input;
}
}
return $ret;
};
$inputs = $inputs (); // sorry about that, Eclipse gets unstable on IIFE syntax.
$hasName = true;
$name = $form->getAttribute ( "id" );
if ($name === '') {
$name = $form->getAttribute ( "name" );
if ($name === '') {
$hasName = false;
}
}
if (! $hasName) {
$parsedForms [] = array (
$inputs
);
} else {
if (! array_key_exists ( $name, $parsedForms )) {
$parsedForms [$name] = array (
$inputs
);
} else {
$parsedForms [$name] [] = $tmp;
}
}
}
unset ( $form, $tmp, $hasName, $name, $i, $input );
if ($getOnlyFirstMatches) {
foreach ( $parsedForms as $key => $val ) {
$parsedForms [$key] = $val [0];
}
unset ( $key, $val );
foreach ( $parsedForms as $key1 => $val1 ) {
foreach ( $val1 as $key2 => $val2 ) {
$parsedForms [$key1] [$key2] = $val2 [0];
}
}
}
return $parsedForms;
}
output:
logged in!
myurl: https://twitter.com/HansHenrik_
following tianwm (53056654)
following theburakcinar (2335591322)
following DnR_iData (260134525)
following 7wData (1713417312)
following deepudeepana (783199483404226560)
following remco_verhoef (201001391)
following PaulVlasin (1079477118)
^C
(i manually canceled it at ^C, as i dont want to follow everyone, but it was enough to prove that it worked) - and don't forget to replace username/password on line 4 & line 5 - and note that since i don't have any followers, i used https://twitter.com/scaleway/followers as a test page with lots of followers, you can see where i faked the url at the commented-out line 50. - also, it will probably just send a request to all the followers you can see on the followers page, which, if you have a lot of followers, is not the full list (this would crash your browser etc), so you'll have to find out how to get the full list of followers if you have a big amount of followers -
You have to use
CURLOPT_COOKIEJAR => __DIR__ . DIRECTORY_SEPARATOR . 'cookies' . DIRECTORY_SEPARATOR . $username . '.txt',
in your second request too.
This is against Twitter's developer policy and your application and IP risk being banned from the platform. Furthermore, Twitter's rules explicitly prohibit the kind of application you are building - see https://support.twitter.com/articles/20171936

Check the empty content in array

I have got an array that contains different kinds of element.
The array can have different dimension in different fields.
I want to write a function to check if there are some variables in the array are empty.
The sample array is like the following.
$array = new array(
'a'=>'A',
'b' => new array('B',''),
'c'=> ''
);
Here is a function that will go through all the arrays:
$isEmpty = checkArray( $arr );
if ( $isEmpty ) {
echo "there are empties!";
} else {
echo "no empties!";
}
function checkArray( $array ) {
foreach ( $array as $key => $value ) {
if ( is_array( $value ) ) {
if ( checkArray( $value ) ) return true;
} else {
if ( empty( $value ) ) return true;
}
}
return false;
}
How about this:
foreach ($array as $key => $value) {
if (empty($value)) {
do_stuff();
}
}

parsing random stdClass

I seem to be a little lost on what to so about this, im trying to parse out some info but the stdClass is going to always be changing, so im not too sure on what to do about it and could use come guidance.
//Query
$query = new EntityFieldQuery;
$result = $query
->entityCondition('entity_type', 'taxonomy_term')
->propertyCondition('name', 'GOOG')
->propertyCondition('vid', '3')
->execute();
//This is the output
Array
(
[taxonomy_term] => Array
(
[1868] => stdClass Object
(
[tid] => 1868
)
)
)
Now I can get to the tid by using
$result['taxonomy_term']['1868']->tid
but as mentioned before the the stdClass will be always changing.
You can use recurssive array search like this:
function array_searchRecursive( $needle, $haystack, $strict=false, $path=array() )
{
if( !is_array($haystack) ) {
return false;
}
foreach( $haystack as $key => $val ) {
if( is_array($val) && $subPath = array_searchRecursive($needle, $val, $strict, $path) ) {
$path = array_merge($path, array($key), $subPath);
return $path;
} elseif( (!$strict && $val == $needle) || ($strict && $val === $needle) ) {
$path[] = $key;
return $path;
}
}
return false;
}
Usage:
$arr = (array) $yourObject;
$keypath = array_searchRecursive('tid', $arr);
Example:
$class = new stdClass;
$class->foo = 'foo';
$class->bar = 'bar';
$arr = (array) $class;
$keypath = array_searchRecursive('foo', $arr);
print_r($keypath);
Results:
Array
(
[0] => foo
)
So now to get actual value:
echo $keypath[0]; // foo

Categories