*/ private array $values; /** * @param array $values */ private function __construct(array $values) { $this->values = $values; $this->validate(); } public static function load(string $path): self { $values = self::defaults(); if (is_file($path)) { $lines = file($path, FILE_IGNORE_NEW_LINES); if ($lines !== false) { foreach ($lines as $line) { $line = trim($line); if ($line === '' || str_starts_with($line, '#') || !str_contains($line, '=')) { continue; } [$key, $value] = explode('=', $line, 2); $values[trim($key)] = trim($value); } } } return new self($values); } /** * @return array */ public static function defaults(): array { return [ 'DEFAULT_PLUGIN_LANGUAGE' => 'pl', 'DEFAULT_ENABLE' => 'true', 'GLOBAL_AUTORESPONDER_TIMEZONE' => 'Europe/Warsaw', 'GLOBAL_AUTORESPONDER_BACKEND' => 'da', 'GLOBAL_AUTORESPONDER_DYNAMIC_MAILBOX_SCOPE' => 'true', 'GLOBAL_AUTORESPONDER_MAX_SUBJECT_BYTES' => '255', 'GLOBAL_AUTORESPONDER_MAX_BODY_BYTES' => '20000', ]; } public function getString(string $key, string $default = ''): string { return $this->values[$key] ?? $default; } public function getBool(string $key, bool $default = false): bool { $raw = strtolower($this->getString($key, $default ? 'true' : 'false')); return in_array($raw, ['1', 'true', 'yes', 'on'], true); } public function getInt(string $key, int $default): int { $raw = $this->getString($key, (string)$default); return preg_match('/^-?[0-9]+$/', $raw) ? (int)$raw : $default; } public function defaultLanguage(): string { $lang = strtolower($this->getString('DEFAULT_PLUGIN_LANGUAGE', 'pl')); return preg_match('/^[a-z]{2}$/', $lang) ? $lang : 'pl'; } public function defaultEnable(): bool { return $this->getBool('DEFAULT_ENABLE', true); } public function timezone(): string { return $this->getString('GLOBAL_AUTORESPONDER_TIMEZONE', 'Europe/Warsaw'); } public function backendMode(): string { return $this->getString('GLOBAL_AUTORESPONDER_BACKEND', 'da'); } public function scheduleMode(): string { return $this->backendMode() === 'da' ? 'directadmin_period' : 'exact_hour'; } public function dynamicMailboxScope(): bool { return $this->getBool('GLOBAL_AUTORESPONDER_DYNAMIC_MAILBOX_SCOPE', true); } public function maxSubjectBytes(): int { return $this->getInt('GLOBAL_AUTORESPONDER_MAX_SUBJECT_BYTES', 255); } public function maxBodyBytes(): int { return $this->getInt('GLOBAL_AUTORESPONDER_MAX_BODY_BYTES', 20000); } public function loadOrCreateSecret(string $path = self::CONFIG_DIR . '/secret.key'): string { if (is_file($path)) { $secret = trim((string)file_get_contents($path)); if ($secret !== '') { return $secret; } } $dir = dirname($path); if (!is_dir($dir)) { mkdir($dir, 0700, true); } $secret = bin2hex(random_bytes(32)); file_put_contents($path, $secret . "\n", LOCK_EX); @chmod($path, 0600); return $secret; } private function validate(): void { try { new DateTimeZone($this->timezone()); } catch (Throwable) { throw new InvalidArgumentException('Invalid timezone: ' . $this->timezone()); } if (!in_array($this->backendMode(), ['da', 'exim'], true)) { throw new InvalidArgumentException('Invalid backend mode: ' . $this->backendMode()); } if ($this->maxSubjectBytes() < 1 || $this->maxBodyBytes() < 1) { throw new InvalidArgumentException('Invalid size limits'); } } }