без особой проверки этого кода, вот код, который я использую для входа в gmail, используя hhb_curl (и заметьте: изначально я притворялся iPhone, потому что было проще войти через страницу мобильного входачем страница входа на рабочий стол, но в итоге я перенес ее на версию для настольного компьютера, но в коде все еще есть остатки мобильной версии. Кроме того, иногда gmail произвольно запрашивает у вас подтверждение вашей электронной почты для восстановления при использовании этого метода, таким образом, $Переменная recoveryEmailChallengeAnswer. Это происходит не каждый раз, но Google замечает, что при входе в систему что-то «выключено», и, таким образом, запрашивает подтверждение восстановления электронной почты случайным образом. Мой код прозрачно обрабатывает эту ситуацию.):
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;
}