diff --git a/src/helper/class-ee-site.php b/src/helper/class-ee-site.php index 08ad268e..a64c1d94 100644 --- a/src/helper/class-ee-site.php +++ b/src/helper/class-ee-site.php @@ -1540,7 +1540,10 @@ protected function init_le( $site_url, $site_fs_path, $wildcard = false, $www_or $this->site_data['site_fs_path'] = $site_fs_path; $this->site_data['site_ssl_wildcard'] = $wildcard; $client = new Site_Letsencrypt(); - $this->le_mail = \EE::get_runner()->config['le-mail'] ?? \EE::input( 'Enter your mail id: ' ); + if ( ! isset( $this->le_mail ) ) { + // Throws instead of exiting so site create/update can roll back. + $this->le_mail = $this->get_validated_le_mail( \EE::get_runner()->config['le-mail'] ?? null ); + } \EE::get_runner()->ensure_present_in_config( 'le-mail', $this->le_mail ); if ( ! $client->register( $this->le_mail ) ) { $this->site_data['site_ssl'] = null; @@ -1577,6 +1580,31 @@ protected function init_le( $site_url, $site_fs_path, $wildcard = false, $www_or } } + /** + * Resolves and validates the Let's Encrypt account email. + * + * `??` only guards null/unset, so an empty/garbage le-mail (e.g. an empty STDIN + * read during non-interactive cron runs) used to slip through to ACME and fail + * with an opaque error. Validate here so callers always get a real email. + * + * @param string|null $config_mail Email resolved from config by the caller, if any. + * + * @return string A non-empty, valid email address. + * @throws \Exception If no valid email can be resolved. + */ + private function get_validated_le_mail( $config_mail = null ) { + $mail = $config_mail; + if ( empty( $mail ) || ! filter_var( $mail, FILTER_VALIDATE_EMAIL ) ) { + // One re-prompt for interactive users; on non-interactive runs STDIN is empty and this stays empty. + $mail = \EE::input( 'Enter your mail id: ' ); + } + if ( empty( $mail ) || ! filter_var( $mail, FILTER_VALIDATE_EMAIL ) ) { + throw new \Exception( 'A valid Let\'s Encrypt email is required. Set it with `ee config set le-mail `.' ); + } + + return $mail; + } + /** * Returns all domains required by cert * @@ -1679,7 +1707,11 @@ public function ssl_verify( $args = [], $assoc_args = [], $www_or_non_www = fals } if ( ! isset( $this->le_mail ) ) { - $this->le_mail = \EE::get_config( 'le-mail' ) ?? \EE::input( 'Enter your mail id: ' ); + try { + $this->le_mail = $this->get_validated_le_mail( \EE::get_config( 'le-mail' ) ); + } catch ( \Exception $e ) { + EE::error( $e->getMessage() ); + } } $force = \EE\Utils\get_flag_value( $assoc_args, 'force' ); @@ -1944,10 +1976,6 @@ public function ssl_renew( $args, $assoc_args ) { EE::log( 'Starting SSL cert renewal' ); - if ( ! isset( $this->le_mail ) ) { - $this->le_mail = EE::get_config( 'le-mail' ) ?? EE::input( 'Enter your mail id: ' ); - } - $force = get_flag_value( $assoc_args, 'force', false ); $all = get_flag_value( $assoc_args, 'all', false ); @@ -1999,6 +2027,15 @@ private function renew_ssl_cert( $args, $force ) { EE::error( 'Only Letsencrypt certificate renewal is supported.' ); } + // Resolve le-mail only after confirming the site is LE, so non-LE sites hit the site-type error first. + if ( ! isset( $this->le_mail ) ) { + try { + $this->le_mail = $this->get_validated_le_mail( EE::get_config( 'le-mail' ) ); + } catch ( \Exception $e ) { + EE::error( $e->getMessage() ); + } + } + $client = new Site_Letsencrypt(); $preferred_challenge = get_preferred_ssl_challenge( get_domains_of_site( $this->site_data['site_url'] ) );