<commit_before><?php

/*
 * This file is part of the Acme PHP project.
 *
 * (c) <NAME> <<EMAIL>>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace AcmePhp\Cli\Command;

use AcmePhp\Cli\Configuration\DomainConfiguration;
use AcmePhp\Core\Challenge\ConfigurableServiceInterface;
use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface;
use AcmePhp\Core\Challenge\SolverInterface;
use AcmePhp\Core\Challenge\ValidatorInterface;
use AcmePhp\Core\Exception\Protocol\ChallengeNotSupportedException;
use AcmePhp\Core\Exception\Server\MalformedServerException;
use AcmePhp\Core\Protocol\AuthorizationChallenge;
use AcmePhp\Core\Protocol\CertificateOrder;
use AcmePhp\Ssl\CertificateRequest;
use AcmePhp\Ssl\CertificateResponse;
use AcmePhp\Ssl\DistinguishedName;
use AcmePhp\Ssl\KeyPair;
use AcmePhp\Ssl\ParsedCertificate;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Yaml\Yaml;
use Webmozart\PathUtil\Path;

/**
 * @author <NAME> <<EMAIL>>
 */
class RunCommand extends AbstractCommand
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this->setName('run')
            ->setDefinition(
                [
                    new InputArgument('config', InputArgument::REQUIRED, 'path to the config file'),
                    new InputOption(
                        'delay',
                        'd',
                        InputOption::VALUE_REQUIRED,
                        'Time to live of certificate (in days) before forcing the renewal',
                        30
                    ),
                ]
            )
            ->setDescription('Automaticaly chalenge domain and request certificates configured in the given file')
            ->setHelp(
                <<<'EOF'
                The <info>%command.name%</info> challenge the domains, request the certificates and install them following a given configuration.
EOF
            );
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $config = $this->getConfig(Path::makeAbsolute($input->getArgument('config'), getcwd()));

        $this->register($config['contact_email']);
        foreach ($config['certificates'] as $domainConfig) {
            $order = $this->challengeDomains($domainConfig);
            $response = $this->requestCertificate($order, $domainConfig, (int) $input->getOption('delay'));
            $this->installCertificate($response, $domainConfig['install']);
        }
    }

    private function register($email)
    {
        $this->output->writeln(
            sprintf(
                '<comment>Registering contact %s...</comment>',
                $email
            )
        );

        $repository = $this->getRepository();
        if (!$repository->hasAccountKeyPair()) {
            $this->output->writeln('<info>No account key pair was found, generating one...</info>');

            /** @var KeyPair $accountKeyPair */
            $accountKeyPair = $this->getContainer()->get('ssl.key_pair_generator')->generateKeyPair();

            $repository->storeAccountKeyPair($accountKeyPair);
        }

        $client = $this->getClient();
        $this->output->writeln('<info>Registering on the ACME server...</info>');

        try {
            $client->registerAccount(null, $email);
            $this->output->writeln('<info>Account registered successfully!</info>');
        } catch (MalformedServerException $e) {
            $this->output->writeln('<info>Account already registered!</info>');
        }
    }

    private function installCertificate(CertificateResponse $response, array $actions)
    {
        $this->output->writeln(
            sprintf(
                '<comment>Installing certificate for domain %s...</comment>',
                $response->getCertificateRequest()->getDistinguishedName()->getCommonName()
            )
        );

        foreach ($actions as $actionConfig) {
            $handler = $this->getContainer()->get('acmephp.action.locator')->get($actionConfig['action']);
            $handler->handle($actionConfig, $response);

            $this->output->writeln(
                sprintf(
                    '<info>Certificate installed with the action %s.</info>',
                    $actionConfig['action']
                )
            );
        }
    }

    private function isUpToDate($domain, $domainConfig, $delay)
    {
        $repository = $this->getRepository();

        if (!$repository->hasDomainDistinguishedName($domain)) {
            return false;
        }

        $distinguishedName = $repository->loadDomainDistinguishedName($domain);
        $wantedCertificates = array_values(
            array_unique(array_merge([$domain], $domainConfig['subject_alternative_names']))
        );
        $requestedCertificates = array_values(
            array_unique(
                array_merge([$distinguishedName->getCommonName()], $distinguishedName->getSubjectAlternativeNames())
            )
        );
        if ($wantedCertificates !== $requestedCertificates) {
            return false;
        }

        if (!$repository->hasDomainCertificate($domain)) {
            return false;
        }

        $certificate = $repository->loadDomainCertificate($domain);
        /** @var ParsedCertificate $parsedCertificate */
        $parsedCertificate = $this->getContainer()->get('ssl.certificate_parser')->parse($certificate);
        if ($parsedCertificate->getValidTo() < new \DateTime(sprintf('%d days', $delay))) {
            return false;
        }

        return true;
    }

    private function requestCertificate(CertificateOrder $order, $domainConfig, $delay)
    {
        $domain = $domainConfig['domain'];
        $this->output->writeln(sprintf('<comment>Requesting certificate for domain %s...</comment>', $domain));

        $repository = $this->getRepository();
        if ($this->isUpToDate($domain, $domainConfig, $delay)) {
            $certificate = $repository->loadDomainCertificate($domain);
            /** @var ParsedCertificate $parsedCertificate */
            $parsedCertificate = $this->getContainer()->get('ssl.certificate_parser')->parse($certificate);

            $this->output->writeln(sprintf('<info>Current certificate is valid until %s, renewal is not necessary. Change --delay parameter to force renewal.</info>', $parsedCertificate->getValidTo()->format(\DateTime::RSS)));

            return new CertificateResponse(
                new CertificateRequest(
                    $repository->loadDomainDistinguishedName($domain),
                    $repository->loadDomainKeyPair($domain)
                ),
                $certificate
            );
        }
<commit_msg>Skip challenge when no renewal<commit_after><?php

/*
 * This file is part of the Acme PHP project.
 *
 * (c) <NAME> <<EMAIL>>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace AcmePhp\Cli\Command;

use AcmePhp\Cli\Configuration\DomainConfiguration;
use AcmePhp\Core\Challenge\ConfigurableServiceInterface;
use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface;
use AcmePhp\Core\Challenge\SolverInterface;
use AcmePhp\Core\Challenge\ValidatorInterface;
use AcmePhp\Core\Exception\Protocol\ChallengeNotSupportedException;
use AcmePhp\Core\Exception\Server\MalformedServerException;
use AcmePhp\Core\Protocol\AuthorizationChallenge;
use AcmePhp\Core\Protocol\CertificateOrder;
use AcmePhp\Ssl\CertificateRequest;
use AcmePhp\Ssl\CertificateResponse;
use AcmePhp\Ssl\DistinguishedName;
use AcmePhp\Ssl\KeyPair;
use AcmePhp\Ssl\ParsedCertificate;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Yaml\Yaml;
use Webmozart\PathUtil\Path;

/**
 * @author <NAME> <<EMAIL>>
 */
class RunCommand extends AbstractCommand
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this->setName('run')
            ->setDefinition(
                [
                    new InputArgument('config', InputArgument::REQUIRED, 'path to the config file'),
                    new InputOption(
                        'delay',
                        'd',
                        InputOption::VALUE_REQUIRED,
                        'Time to live of certificate (in days) before forcing the renewal',
                        30
                    ),
                ]
            )
            ->setDescription('Automaticaly chalenge domain and request certificates configured in the given file')
            ->setHelp(
                <<<'EOF'
                The <info>%command.name%</info> challenge the domains, request the certificates and install them following a given configuration.
EOF
            );
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $config = $this->getConfig(Path::makeAbsolute($input->getArgument('config'), getcwd()));

        $this->register($config['contact_email']);
        foreach ($config['certificates'] as $domainConfig) {
            $domain = $domainConfig['domain'];

            if ($this->isUpToDate($domain, $domainConfig, (int) $input->getOption('delay'))) {
                $repository = $this->getRepository();
                $certificate = $this->getRepository()->loadDomainCertificate($domain);
                /** @var ParsedCertificate $parsedCertificate */
                $parsedCertificate = $this->getContainer()->get('ssl.certificate_parser')->parse($certificate);
                $this->output->writeln(sprintf('<info>Current certificate is valid until %s, renewal is not necessary. Change --delay parameter to force renewal.</info>', $parsedCertificate->getValidTo()->format(\DateTime::RSS)));

                $response = new CertificateResponse(
                    new CertificateRequest(
                        $repository->loadDomainDistinguishedName($domain),
                        $repository->loadDomainKeyPair($domain)
                    ),
                    $certificate
                );
            } else {
                $order = $this->challengeDomains($domainConfig);
                $response = $this->requestCertificate($order, $domainConfig, (int) $input->getOption('delay'));
            }

            $this->installCertificate($response, $domainConfig['install']);
        }
    }

    private function register($email)
    {
        $this->output->writeln(
            sprintf(
                '<comment>Registering contact %s...</comment>',
                $email
            )
        );

        $repository = $this->getRepository();
        if (!$repository->hasAccountKeyPair()) {
            $this->output->writeln('<info>No account key pair was found, generating one...</info>');

            /** @var KeyPair $accountKeyPair */
            $accountKeyPair = $this->getContainer()->get('ssl.key_pair_generator')->generateKeyPair();

            $repository->storeAccountKeyPair($accountKeyPair);
        }

        $client = $this->getClient();
        $this->output->writeln('<info>Registering on the ACME server...</info>');

        try {
            $client->registerAccount(null, $email);
            $this->output->writeln('<info>Account registered successfully!</info>');
        } catch (MalformedServerException $e) {
            $this->output->writeln('<info>Account already registered!</info>');
        }
    }

    private function installCertificate(CertificateResponse $response, array $actions)
    {
        $this->output->writeln(
            sprintf(
                '<comment>Installing certificate for domain %s...</comment>',
                $response->getCertificateRequest()->getDistinguishedName()->getCommonName()
            )
        );

        foreach ($actions as $actionConfig) {
            $handler = $this->getContainer()->get('acmephp.action.locator')->get($actionConfig['action']);
            $handler->handle($actionConfig, $response);

            $this->output->writeln(
                sprintf(
                    '<info>Certificate installed with the action %s.</info>',
                    $actionConfig['action']
                )
            );
        }
    }

    private function isUpToDate($domain, $domainConfig, $delay)
    {
        $repository = $this->getRepository();

        if (!$repository->hasDomainDistinguishedName($domain)) {
            return false;
        }

        $distinguishedName = $repository->loadDomainDistinguishedName($domain);
        $wantedCertificates = array_values(
            array_unique(array_merge([$domain], $domainConfig['subject_alternative_names']))
        );
        $requestedCertificates = array_values(
            array_unique(
                array_merge([$distinguishedName->getCommonName()], $distinguishedName->getSubjectAlternativeNames())
            )
        );
        if ($wantedCertificates !== $requestedCertificates) {
            return false;
        }

        if (!$repository->hasDomainCertificate($domain)) {
            return false;
        }

        $certificate = $repository->loadDomainCertificate($domain);
        /** @var ParsedCertificate $parsedCertificate */
        $parsedCertificate = $this->getContainer()->get('ssl.certificate_parser')->parse($certificate);
        if ($parsedCertificate->getValidTo() < new \DateTime(sprintf('%d days', $delay))) {
            return false;
        }

        return true;
    }

    private function requestCertificate(CertificateOrder $order, $domainConfig, $delay)
    {
        $domain = $domainConfig['domain'];
        $this->output->writeln(sprintf('<comment>Requesting certificate for domain %s...</comment>', $domain));

        $repository = $this->getRepository();
{'fraction_non_alphanumeric': 0.12757401372807323, 'fraction_numerical': 0.00027733481245233307, 'mean_word_length': 2.4882708585247886, 'pattern_counts': {'":': 0, '<': 65, '<?xml version=': 0, '>': 188, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, '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': '2465532', 'n_tokens_mistral': 3722, 'n_tokens_neox': 3624, 'n_words': 702}