summaryrefslogblamecommitdiffstats
path: root/modules-available/webinterface/inc/acme.inc.php
blob: c23578cc4244fe41f19bc8543633da1eb303c9f6 (plain) (tree)



































































































































































                                                                                                                              
<?php

class Acme
{

	const PROP_ERROR = 'acme.error-string';
	const PROP_PROVIDER = 'acme.provider';
	const PROP_KEY_ID = 'acme.key-id';
	const PROP_HMAC_KEY = 'acme.hmac-key';
	const PROP_DOMAINS = 'acme.domains';
	const PROP_MAIL = 'acme.mail';
	const VALID_PROVIDERS = [
		'letsencrypt' => "Let's Encrypt",
		'zerossl' => 'ZeroSSL.com',
		'buypass' => 'BuyPass.com',
		'geant/sectigo' => 'GEANT via Sectigo',
	];

	const PROVIDER_ALIASES = [
		'geant/sectigo' => 'https://acme.sectigo.com/v2/GEANTOV',
	];

	public static function getLastError(): ?string
	{
		return Property::get(self::PROP_ERROR, null);
	}

	public static function getProvider(): ?string
	{
		return Property::get(self::PROP_PROVIDER, null);
	}

	public static function getKeyId(): ?string
	{
		return Property::get(self::PROP_KEY_ID, null);
	}

	public static function getHmacKey(): ?string
	{
		return Property::get(self::PROP_HMAC_KEY, null);
	}

	/**
	 * @return string[] list of [id] => friendly name
	 */
	public static function getProviders(): array
	{
		return self::VALID_PROVIDERS;
	}

	public static function getMail(): ?string
	{
		return Property::get(self::PROP_MAIL, null);
	}

	public static function getDomains(): array
	{
		return explode(' ', Property::get(self::PROP_DOMAINS));
	}

	public static function setConfig(string $provider, string $mail, ?string $keyId = null, ?string $hmacKey = null): bool
	{
		if (!isset(self::VALID_PROVIDERS[$provider])) {
			Message::addError('webinterface.acme-invalid-provider', $provider);
			return false;
		}
		Property::set(self::PROP_PROVIDER, $provider);
		Property::set(self::PROP_MAIL, $mail);
		Property::set(self::PROP_KEY_ID, $keyId);
		Property::set(self::PROP_HMAC_KEY, $hmacKey);
		return true;
	}

	public static function setDomains(array $list): void
	{
		Property::set(self::PROP_DOMAINS, implode(' ', $list));
	}

	private static function handleErrorAsync($task): void
	{
		if (!is_array($task) || !Taskmanager::isTask($task))
			return;
		$task = Taskmanager::waitComplete($task, 250);
		if (Taskmanager::isFinished($task)) {
			self::callbackErrorCheck($task);
		} else {
			Property::set(self::PROP_ERROR, false);
			TaskmanagerCallback::addCallback($task, 'acmeErrors');
		}
	}

	public static function callbackErrorCheck(array $task): void
	{
		if (!Taskmanager::isFinished($task))
			return;
		if (Taskmanager::isFailed($task)) {
			Property::set(self::PROP_ERROR, $task['data']['error'] ?? 'Unknown error');
		} else {
			Property::set(self::PROP_ERROR, false);
		}
	}

	public static function issueNewCertificate(bool $wipeAll = false): ?string
	{
		$provider = self::getProvider();
		if ($provider === null) {
			Message::addError('webinterface.acme-no-provider');
			return null;
		}
		$mail = self::getMail();
		if (empty($mail)) {
			Message::addError('webinterface.acme-no-mail');
			return null;
		}
		$domains = self::getDomains();
		if (empty($domains)) {
			Message::addError('webinterface.acme-no-domains');
			return null;
		}
		$redirect = Property::get(WebInterface::PROP_REDIRECT);
		$task = Taskmanager::submit('LighttpdHttps', [
			'redirect' => $redirect,
			'acmeMode' => 'issue',
			'acmeMail' => $mail,
			'acmeDomains' => $domains,
			'acmeProvider' => self::PROVIDER_ALIASES[$provider] ?? $provider,
			'acmeKeyId' => self::getKeyId(),
			'acmeHmacKey' => self::getHmacKey(),
			'acmeWipeAll' => $wipeAll,
		]);
		self::handleErrorAsync($task);
		return $task['id'] ?? null;
	}

	public static function renew(): ?string
	{
		error_log("Renew called");
		$domains = self::getDomains();
		if (empty($domains)) {
			Message::addError('webinterface.acme-no-domains');
			return null;
		}
		$redirect = Property::get(WebInterface::PROP_REDIRECT);
		$task = Taskmanager::submit('LighttpdHttps', [
			'redirect' => $redirect,
			'acmeMode' => 'renew',
			'acmeDomains' => $domains,
		]);
		self::handleErrorAsync($task);
		return $task['id'] ?? null;
	}

	public static function tryEnable(): bool
	{
		$redirect = Property::get(WebInterface::PROP_REDIRECT);
		$task = Taskmanager::submit('LighttpdHttps', [
			'redirect' => $redirect,
			'acmeMode' => 'try-enable',
		]);
		$task = Taskmanager::waitComplete($task, 10000);
		return Taskmanager::isFinished($task) && !Taskmanager::isFailed($task);
	}

}