0

I wants to remote login on gmail account. I am trying below code but code not working, also code not showing me error. When i browse it on my server, its showing me "Login Failed" message, also this message already set on string. Please check this below code where is the problem.

<?php

$USERNAME = 'your_gmail_id_name@gmail.com';
$PASSWORD = 'your_gmail_password';
$COOKIEFILE = 'cookies.txt';

// initialize curl handle used for all requests
$ch = curl_init();

// set some options on the handle
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_COOKIEJAR, $COOKIEFILE);
curl_setopt($ch, CURLOPT_COOKIEFILE, $COOKIEFILE);
curl_setopt($ch, CURLOPT_HEADER, 0);  
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 120);
curl_setopt($ch, CURLOPT_TIMEOUT, 120);

// url of our first request fetches the account login page
curl_setopt($ch, CURLOPT_URL, 
  'https://accounts.google.com/ServiceLogin?hl=en&service=alerts&continue=http://www.google.com/alerts/manage');
$data = curl_exec($ch);

// extract form fields from account login page
$formFields = getFormFields($data);

// inject email and password into form
$formFields['Email']  = $USERNAME;
$formFields['Passwd'] = $PASSWORD;
unset($formFields['PersistentCookie']);

$post_string = http_build_query($formFields); // build urlencoded POST string for login

// set url to login page as a POST request
curl_setopt($ch, CURLOPT_URL, 'https://accounts.google.com/ServiceLoginAuth');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string);

// execute login request
$result = curl_exec($ch);

// check for "Redirecting" message in title to indicate success
// based on your language - you may need to change this to match some other string
if (strpos($result, '<title>Redirecting') === false) {
    die("Login failed");
    var_dump($result);
}

// login likely succeeded - request account page; unset POST so we do a regular GET
curl_setopt($ch, CURLOPT_URL, 'https://mail.google.com/mail/h/jeu23doknfnj/?zy=e&f=1');
curl_setopt($ch, CURLOPT_POST, 0);
curl_setopt($ch, CURLOPT_POSTFIELDS, null);

// execute request for login page using our cookies
$result = curl_exec($ch);

echo $result;


// helpef functions below

// find google "#gaia_loginform" for logging in
function getFormFields($data)
{
    if (preg_match('/(<form.*?class=.?RFjuSb.*?<\/form>)/is', $data, $matches)) {
        $inputs = getInputs($matches[1]);

        return $inputs;
    } else {
        die('didn\'t find login form');
    }
}

// extract all <input fields from a form
function getInputs($form)
{
    $inputs = array();

    $elements = preg_match_all('/(<input[^>]+>)/is', $form, $matches);

    if ($elements > 0) {
        for($i = 0; $i < $elements; $i++) {
            $el = preg_replace('/\s{2,}/', ' ', $matches[1][$i]);

            if (preg_match('/name=(?:["\'])?([^"\'\s]*)/i', $el, $name)) {
                $name  = $name[1];
                $value = '';

                if (preg_match('/value=(?:["\'])?([^"\'\s]*)/i', $el, $value)) {
                    $value = $value[1];
                }

                $inputs[$name] = $value;
            }
        }
    }

    return $inputs;
}
?>

I already searching solution on google and stackoverflow, but i am not getting solution and i am not getting alternative login codes or answers.

I think so, problem on fill up form and submit login button. I am not sure exactly where is the problem. stackoverflow has many genius coder, so i believe that, i will get solution from here.

This is login form fields:

$formFields['Email']  = $USERNAME;
$formFields['Passwd'] = $PASSWORD;
unset($formFields['PersistentCookie']);

Thanks

  • 4
    Don't do this. Use either the API, or IMAP instead if you want to retrieve emails programatically via code instead of via human interaction. The login page is designed for humans to use within a browser, not for other computer programs. See https://developers.google.com/gmail/api/ (web API) and https://davidwalsh.name/gmail-php-imap (IMAP example) – ADyson Feb 15 '18 at 10:53
  • 1
    This smells like an [XY Problem](http://xyproblem.info) where the real solution is [use the API instead](https://developers.google.com/gmail/api/quickstart/php) – Quentin Feb 15 '18 at 10:53
  • i wants to active on gmail always – Karen Miller Feb 15 '18 at 10:56

2 Answers2

0

I tried to do it with curl some years ago, doesn't work, try Gmail API Gmail API

Saad.elzwawy
  • 20,094
  • 1
  • 14
  • 16
0

you're doing several things wrong, for example, you're trying to parse HTML with regex, that's bound to fail. also, your regex-html-parser doesn't account for html encoding, so if there's any encoded characters in the input data, your code will send the wrong data. (for example, if 1 if the csrf tokens contains an &, it will be html encoded as &amp;, which you must then html decode back to an &, but your getInputs() function makes no attempt to detect/decode html encoded characters)

a little further down, you attempt to send both the username and password, in 1 go, to https://accounts.google.com/ServiceLoginAuth - that's not how it works, you must send them in 2 batches, first the username, then in a different request, the password, also the URLs are dynamic, it's different for each cookie session, but in your code, you have the urls hardcoded, stop doing that, instead make a request to https://gmail.com/ , it will http-location-redirect you a few times to a dynamic url, that url's html will contain a <form> with the id gaia_loginform, the form's "action" attribute will dictate where you send the username, it will also have a bunch of hidden <input> fields wich you need to parse out and add to the request. that request, if successful, will http-location-redirect you a few more times, to another dynamic url, which dictates where you're supposed to send the password, along with more hidden <input> fields..

if that request is successful, you've logged in. but use a proper DOM parser, like DOMDocument, don't use regex for parsing HTML.

luckily for you, i needed to login to gmail programmatically some time back too, here's how i did it, using hhb_curl -

EDIT: warning, when gmail detects that something is "weird" with your login, it will sometimes, apparently completely randomly, ask you to verify that it's really the account owner logging in. 1 of the ways to verify you, is to provide your recovery email (because only the account owner should have that information, right?), and the original code i posted here on SO will just crash when asked to verify identity. here's an updated code that takes a third parameter, the recovery email, and verifies identity automatically when asked to: https://gist.github.com/divinity76/544d7cadd3e88e057ea3504cb8b3bf7e

still, for historical reasons, and because i'm too lazy to keep the SO answer code updated, here's the original code i posted here:

<?php
declare(strict_types = 1);
// header ( "content-type: text/plain;charset=utf8" );
require_once ('hhb_.inc.php');
function loginGmail(string $username, string $password): \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;
        }
    }
    if (! empty ( $loginErrorText )) {
        throw new \RuntimeException ( 'errors loggin in: ' . $loginErrorText );
    } else {
        // logged in! :D
    }
    // 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" )) {
            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) {
        throw new \RuntimeException ( 'failed to find HTML version request form!' );
    }
    $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;
}
hanshenrik
  • 19,904
  • 4
  • 43
  • 89