%PDF- %PDF-
Direktori : /www/varak.net/dmarc.varak.net/classes/Domains/ |
Current File : /www/varak.net/dmarc.varak.net/classes/Domains/Domain.php |
<?php /** * dmarc-srg - A php parser, viewer and summary report generator for incoming DMARC reports. * Copyright (C) 2021-2025 Aleksey Andreev (liuch) * * Available at: * https://github.com/liuch/dmarc-srg * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, either version 3 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. * * ========================= * * This file contains the class Domain * * @category API * @package DmarcSrg * @author Aleksey Andreev (liuch) * @license https://www.gnu.org/licenses/gpl-3.0.html GNU/GPLv3 */ namespace Liuch\DmarcSrg\Domains; use Liuch\DmarcSrg\Core; use Liuch\DmarcSrg\Exception\SoftException; use Liuch\DmarcSrg\Exception\LogicException; use Liuch\DmarcSrg\Exception\DatabaseNotFoundException; /** * It's a class for accessing to stored domains data * * This class is designed for storing and manipulating domain data. * All queries to the database are made in lazy mode. */ class Domain { private $db = null; private $ex_f = null; private $data = [ 'id' => null, 'fqdn' => null, 'active' => null, 'description' => null, 'created_time' => null, 'updated_time' => null ]; /** * It's a constructor of the class * * Some examples of using: * (new Domain(1))->fqdn(); - will return the fully qualified domain name for the domain with id = 1 * (new Domain('example.com'))->description(); - will return the description for the domain example.com * (new Domain([ 'fqdn' => 'example.com', 'description' => 'an expample domain' ])->save(); - will add * this domain to the database if it does not exist in it. * * @param int|string|array $data Some domain data to identify it * int value is treated as domain id * string value is treated as a FQDN * array has these fields: `id`, `fqdn`, `active`, `description` * and usually uses for creating a new domain item. * Note: The values of the fields `created_time` and `updated_time` * will be ignored while saving to the database. * @param \Liuch\DmarcSrg\Database\DatabaseController $db The database controller * * @return void */ public function __construct($data, $db = null) { $this->db = $db ?? Core::instance()->database(); switch (gettype($data)) { case 'integer': $this->data['id'] = $data; return; case 'string': $this->data['fqdn'] = strtolower($data); $this->checkFqdn(); return; case 'array': if (isset($data['id'])) { if (gettype($data['id']) !== 'integer') { break; } $this->data['id'] = $data['id']; } if (isset($data['fqdn'])) { if (gettype($data['fqdn']) !== 'string') { break; } $this->data['fqdn'] = strtolower($data['fqdn']); $this->checkFqdn(); } if (isset($data['active'])) { if (gettype($data['active']) !== 'boolean') { break; } $this->data['active'] = $data['active']; } else { $this->data['active'] = false; } if (isset($data['description'])) { if (gettype($data['description']) !== 'string') { break; } $this->data['description'] = $data['description']; } if (isset($data['created_time'])) { if (gettype($data['created_time']) !== 'object') { break; } $this->data['created_time'] = $data['created_time']; } if (isset($data['updated_time'])) { if (gettype($data['updated_time']) !== 'object') { break; } $this->data['updated_time'] = $data['updated_time']; } if (!is_null($this->data['id']) || !is_null($this->data['fqdn'])) { return; } } throw new LogicException('Wrong domain data'); } /** * Returns true if the domain exists in the database or false otherwise * * @return bool Whether the domain exists */ public function exists(): bool { if (is_null($this->ex_f)) { $this->ex_f = $this->db->getMapper('domain')->exists($this->data); } return $this->ex_f; } /** * Ensures the domain is in the specified state and throws an exception if it is not. * * @param string $state Can be one of these values: 'exist', 'nonexist' * * @throws SoftException * * @return void */ public function ensure(string $state): void { switch ($state) { case 'exist': if (!$this->exists()) { $this->unknownDomain(); } break; case 'nonexist': if ($this->exists()) { throw new SoftException('The domain already exists'); } break; default: throw new LogicException('Unknown domain state'); } } /** * Returns the domain id * * @return int The domain id */ public function id(): int { if (is_null($this->data['id'])) { $this->fetchData(); } return $this->data['id']; } /** * Returns the domain's FQDN * * @return string FQDN for the domain */ public function fqdn(): string { if (is_null($this->data['fqdn'])) { $this->fetchData(); } return $this->data['fqdn']; } /** * Whether the domain is active or not * * When the domain is inactive, all incoming reports for it are ignored * but the domain will still be included in summary reports. * * @return bool */ public function active(): bool { if (is_null($this->data['active'])) { $this->fetchData(); } return $this->data['active']; } /** * Returns the domain's description * * @return string|null The description of the domain if it exists or null otherwise */ public function description() { if (is_null($this->data['id']) || is_null($this->data['fqdn'])) { $this->fetchData(); } return $this->data['description']; } /** * Returns an array with domain data * * @return array Domain data */ public function toArray(): array { foreach ([ 'id', 'fqdn', 'active' ] as $it) { if (is_null($this->data[$it])) { $this->fetchData(); break; } } $res = $this->data; unset($res['id']); return $res; } /** * Returns true if the domain exists in the database and is assigned to the user * * @param \Liuch\DmarcSrg\Users\User $user User to check * @param bool $exception Throw an exception instead of returning false * * @return bool */ public function isAssigned($user, bool $exception = false): bool { if ($this->ex_f !== false) { $user_id = $user->id(); if ($user_id === 0) { // It is the admin if ($this->exists()) { return true; } } elseif ($this->db->getMapper('domain')->isAssigned($this->data, $user_id)) { $this->ex_f = true; return true; } } if ($exception) { $this->unknownDomain(); } return false; } /** * Assigns the domain to a user * * @param \Liuch\DmarcSrg\Users\User $user The user to whom the domain should be assigned * * @return void */ public function assignUser($user): void { $user_id = $user->id(); if ($user_id !== 0) { $this->db->getMapper('domain')->assignUser($this->data, $user_id); } } /** * Unassigns the domain from a user * * @param \Liuch\DmarcSrg\Users\User $user The user from whom the domain should be unassigned * * @return void */ public function unassignUser($user): void { $user_id = $user->id(); if ($user_id !== 0) { $this->db->getMapper('domain')->unassignUser($this->data, $user_id); } } /** * Saves the domain to the database * * Updates the domain's description in the database if the domain exists or insert a new record otherwise. * The domain id is ignored in the insert mode. * * @return void */ public function save(): void { $this->db->getMapper('domain')->save($this->data); $this->ex_f = true; } /** * Deletes the domain from the database * * Deletes the domain if there are no reports for this domain in the database. * If you want to stop handling reports for this domain, just make it inactive. * * @param bool $force If there are incoming reports for this domain. * True: Remove the incoming report with the domain * False: Cancel domain deletion * * @return void */ public function delete(bool $force): void { if (is_null($this->data['id'])) { $this->fetchData(); } $this->db->getMapper('domain')->delete($this->data['id'], $force); $this->ex_f = false; } /** * Verifies domain ownership * * @throws SoftException * * @param string $key Verification key * @param string $method Verification method * * return void */ public function verifyOwnership(string $key, string $method): void { $passed = false; switch ($method) { case 'dns': $dr = dns_get_record($this->data['fqdn'], DNS_TXT); if ($dr) { $str = 'dmarcsrg-verification=' . $key; foreach ($dr as &$rec) { if (trim($rec['txt']) === $str) { $passed = true; break; } } unset($rec); } break; } if (!$passed) { throw new SoftException('Domain verification failed'); } } /** * Removes the trailing dot from the domain name and checks it for an empty value. * * @return void */ private function checkFqdn(): void { $fqdn = trim($this->data['fqdn']); if (substr($fqdn, -1) === '.') { $fqdn = trim(substr($fqdn, 0, -1)); } if ($fqdn === '') { throw new SoftException('The domain name must not be an empty string'); } $this->data['fqdn'] = $fqdn; } /** * Fetches the domain data from the database by its id or name * * @return void */ private function fetchData(): void { if ($this->ex_f === false) { return; } try { $this->db->getMapper('domain')->fetch($this->data); $this->ex_f = true; } catch (DatabaseNotFoundException $e) { $this->ex_f = false; $this->unknownDomain(); } } /** * Throws an exception that the domain doesn't exist * * @throws SoftException * * @return void */ private function unknownDomain(): void { $name = ''; if (!empty($this->data['fqdn'])) { $name = ': ' . $this->data['fqdn']; } throw new SoftException('Unknown domain' . $name); } }