<commit_before><?php
namespace Platformsh\Cli\Command\Auth;

use CommerceGuys\Guzzle\Oauth2\AccessToken;
use GuzzleHttp\Client;
use Platformsh\Cli\Command\CommandBase;
use Platformsh\Cli\Service\Filesystem;
use Platformsh\Cli\Service\Url;
use Platformsh\Cli\Util\PortUtil;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;

class BrowserLoginCommand extends CommandBase
{
    protected function configure()
    {
        $service = $this->config()->get('service.name');
        $applicationName = $this->config()->get('application.name');
        $executable = $this->config()->get('application.executable');

        $this->setName('auth:browser-login');
        if ($this->config()->get('application.login_method') === 'browser') {
            $this->setAliases(['login']);
        }

        $this->setDescription('Log in to ' . $service . ' via a browser')
            ->addOption('force', 'f', InputOption::VALUE_NONE, 'Log in again, even if already logged in');
        Url::configureInput($this->getDefinition());

        $help = 'Use this command to log in to the ' . $applicationName . ' using a browser.'
            . "\n\nAlternatively, to log in with a username and password in the terminal, use:\n    <info>"
            . $executable . ' auth:password-login</info>';
        if ($aHelp = $this->getApiTokenHelp()) {
            $help .= "\n\n" . $aHelp;
        }
        $this->setHelp($help);
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        if ($this->api()->hasApiToken()) {
            $this->stdErr->writeln('Cannot log in: an API token is set');
            return 1;
        }
        if (!$input->isInteractive()) {
            $this->stdErr->writeln('Non-interactive login is not supported.');
            if ($aHelp = $this->getApiTokenHelp('comment')) {
                $this->stdErr->writeln("\n" . $aHelp);
            }
            return 1;
        }
        $connector = $this->api()->getClient(false)->getConnector();
        if (!$input->getOption('force') && $connector->isLoggedIn()) {
            $this->stdErr->writeln('You are already logged in.');
            return 0;
        }

        // Set up the local PHP web server, which will serve an OAuth2 redirect
        // and wait for the response.
        // Firstly, find an address. The port needs to be within a known range,
        // for validation by the remote server.
        $port = PortUtil::getPort(5000, null, 5005);
        $localAddress = '127.0.0.1:' . $port;
        $localUrl = 'http://' . $localAddress;

        // Then create the document root for the local server. This needs to be
        // outside the CLI itself (since the CLI may be run as a Phar).
        $listenerDir = $this->config()->getWritableUserDir() . '/oauth-listener';
        $this->createDocumentRoot($listenerDir);

        // Create the file where an authorization code will be saved (by the
        // local server script).
        $codeFile = $listenerDir . '/.code';
        if (file_put_contents($codeFile, '', LOCK_EX) === false) {
            throw new \RuntimeException('Failed to create temporary file: ' . $codeFile);
        }
        chmod($codeFile, 0600);

        // Find the authorization URL from the api.accounts_api_url.
        $apiUrl = $this->config()->get('api.accounts_api_url');
        $authHost = parse_url($apiUrl, PHP_URL_HOST);
        if (!$authHost) {
            throw new \RuntimeException('Failed to get API host.');
        }

        // Start the local server.
        $process = new Process([
            'php',
            '-dvariables_order=egps',
            '-S',
            $localAddress,
            '-t',
            $listenerDir
        ]);
        $process->setEnv([
            'CLI_OAUTH_APP_NAME' => $this->config()->get('application.name'),
            'CLI_OAUTH_STATE' => $this->getRandomState(),
            'CLI_OAUTH_AUTH_URL' => 'https://' . $authHost . '/oauth2/authorize',
            'CLI_OAUTH_CLIENT_ID' => $this->config()->get('api.oauth2_client_id'),
            'CLI_OAUTH_FILE' => $codeFile,
        ]);
        $process->setTimeout(null);
        $process->start();

        // Give the local server some time to start before checking its status
        // or opening the browser (0.5 seconds).
        usleep(500000);

        // Check the local server status.
        if (!$process->isRunning()) {
            $this->stdErr->writeln('Failed to start local web server.');
            $this->stdErr->writeln(trim($process->getErrorOutput()));

            return 1;
        }

        // Open the local server URL in a browser (or print the URL).
        /** @var \Platformsh\Cli\Service\Url $urlService */
        $urlService = $this->getService('url');
        if ($urlService->openUrl($localUrl, false)) {
            $this->stdErr->writeln('Please use the browser to log in.');
        } else {
            $this->stdErr->writeln('Open the following URL in a browser and log in:');
            $this->stdErr->writeln($localUrl);
            $this->stdErr->writeln('');
            $this->stdErr->writeln(sprintf(
                'For help, quit this process (e.g. with Ctrl+C), and run: <info>%s help login</info>',
                $this->config()->get('application.executable')
            ));
        }

        // Wait for the file to be filled with an OAuth2 authorization code.
        $code = null;
        while ($process->isRunning() && empty($code)) {
            usleep(300000);
            if (!file_exists($codeFile)) {
                $this->stdErr->writeln('File not found: <error>' . $codeFile . '</error>');
                break;
            }
            $code = file_get_contents($codeFile);
            if ($code === false) {
                $this->stdErr->writeln('Failed to read file: <error>' . $codeFile . '</error>');
                break;
            }
        }

        // Clean up.
        $process->stop();
        (new Filesystem())->remove([$listenerDir]);

        $this->stdErr->writeln('');

        if (empty($code)) {
            $this->stdErr->writeln('Failed to get an authorization code. Please try again.');<commit_msg>auth:browser-login: always display URL in case the default browser did not work<commit_after><?php
namespace Platformsh\Cli\Command\Auth;

use CommerceGuys\Guzzle\Oauth2\AccessToken;
use GuzzleHttp\Client;
use Platformsh\Cli\Command\CommandBase;
use Platformsh\Cli\Service\Filesystem;
use Platformsh\Cli\Service\Url;
use Platformsh\Cli\Util\PortUtil;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;

class BrowserLoginCommand extends CommandBase
{
    protected function configure()
    {
        $service = $this->config()->get('service.name');
        $applicationName = $this->config()->get('application.name');
        $executable = $this->config()->get('application.executable');

        $this->setName('auth:browser-login');
        if ($this->config()->get('application.login_method') === 'browser') {
            $this->setAliases(['login']);
        }

        $this->setDescription('Log in to ' . $service . ' via a browser')
            ->addOption('force', 'f', InputOption::VALUE_NONE, 'Log in again, even if already logged in');
        Url::configureInput($this->getDefinition());

        $help = 'Use this command to log in to the ' . $applicationName . ' using a browser.'
            . "\n\nAlternatively, to log in with a username and password in the terminal, use:\n    <info>"
            . $executable . ' auth:password-login</info>';
        if ($aHelp = $this->getApiTokenHelp()) {
            $help .= "\n\n" . $aHelp;
        }
        $this->setHelp($help);
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        if ($this->api()->hasApiToken()) {
            $this->stdErr->writeln('Cannot log in: an API token is set');
            return 1;
        }
        if (!$input->isInteractive()) {
            $this->stdErr->writeln('Non-interactive login is not supported.');
            if ($aHelp = $this->getApiTokenHelp('comment')) {
                $this->stdErr->writeln("\n" . $aHelp);
            }
            return 1;
        }
        $connector = $this->api()->getClient(false)->getConnector();
        if (!$input->getOption('force') && $connector->isLoggedIn()) {
            $this->stdErr->writeln('You are already logged in.');
            return 0;
        }

        // Set up the local PHP web server, which will serve an OAuth2 redirect
        // and wait for the response.
        // Firstly, find an address. The port needs to be within a known range,
        // for validation by the remote server.
        $port = PortUtil::getPort(5000, null, 5005);
        $localAddress = '127.0.0.1:' . $port;
        $localUrl = 'http://' . $localAddress;

        // Then create the document root for the local server. This needs to be
        // outside the CLI itself (since the CLI may be run as a Phar).
        $listenerDir = $this->config()->getWritableUserDir() . '/oauth-listener';
        $this->createDocumentRoot($listenerDir);

        // Create the file where an authorization code will be saved (by the
        // local server script).
        $codeFile = $listenerDir . '/.code';
        if (file_put_contents($codeFile, '', LOCK_EX) === false) {
            throw new \RuntimeException('Failed to create temporary file: ' . $codeFile);
        }
        chmod($codeFile, 0600);

        // Find the authorization URL from the api.accounts_api_url.
        $apiUrl = $this->config()->get('api.accounts_api_url');
        $authHost = parse_url($apiUrl, PHP_URL_HOST);
        if (!$authHost) {
            throw new \RuntimeException('Failed to get API host.');
        }

        // Start the local server.
        $process = new Process([
            'php',
            '-dvariables_order=egps',
            '-S',
            $localAddress,
            '-t',
            $listenerDir
        ]);
        $process->setEnv([
            'CLI_OAUTH_APP_NAME' => $this->config()->get('application.name'),
            'CLI_OAUTH_STATE' => $this->getRandomState(),
            'CLI_OAUTH_AUTH_URL' => 'https://' . $authHost . '/oauth2/authorize',
            'CLI_OAUTH_CLIENT_ID' => $this->config()->get('api.oauth2_client_id'),
            'CLI_OAUTH_FILE' => $codeFile,
        ]);
        $process->setTimeout(null);
        $process->start();

        // Give the local server some time to start before checking its status
        // or opening the browser (0.5 seconds).
        usleep(500000);

        // Check the local server status.
        if (!$process->isRunning()) {
            $this->stdErr->writeln('Failed to start local web server.');
            $this->stdErr->writeln(trim($process->getErrorOutput()));

            return 1;
        }

        // Open the local server URL in a browser (or print the URL).
        /** @var \Platformsh\Cli\Service\Url $urlService */
        $urlService = $this->getService('url');
        if ($urlService->openUrl($localUrl, false)) {
            $this->stdErr->writeln(sprintf('Opened URL: <info>%s</info>', $localUrl));
            $this->stdErr->writeln('Please use the browser to log in.');
        } else {
            $this->stdErr->writeln('Please open the following URL in a browser and log in:');
            $this->stdErr->writeln('<info>' . $localUrl . '</info>');
            $this->stdErr->writeln('');
            $this->stdErr->writeln(sprintf(
                'For help, quit this process (e.g. with Ctrl+C), and run: <info>%s help login</info>',
                $this->config()->get('application.executable')
            ));
        }

        // Wait for the file to be filled with an OAuth2 authorization code.
        $code = null;
        while ($process->isRunning() && empty($code)) {
            usleep(300000);
            if (!file_exists($codeFile)) {
                $this->stdErr->writeln('File not found: <error>' . $codeFile . '</error>');
                break;
            }
            $code = file_get_contents($codeFile);
            if ($code === false) {
                $this->stdErr->writeln('Failed to read file: <error>' . $codeFile . '</error>');
                break;
            }
        }

        // Clean up.
        $process->stop();
        (new Filesystem())->remove([$listenerDir]);

        $this->stdErr->writeln('');

        if (empty($code)) {
            $this->stdErr->writeln('Failed to get an authorization code. Please try again.');
{'fraction_non_alphanumeric': 0.1511128465443186, 'fraction_numerical': 0.006403748535728231, 'mean_word_length': 2.1745166088249874, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 187, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3003800', 'n_tokens_mistral': 3664, 'n_tokens_neox': 3564, 'n_words': 907}