diff --git a/include/class-Domain.php b/include/class-Domain.php index 4a3f3e7..d4ba3fd 100644 --- a/include/class-Domain.php +++ b/include/class-Domain.php @@ -1,235 +1,253 @@ <?php # Copyright (C) 2018, 2019, 2020 Valerio Bozzolan # Boz Libre Hosting Panel # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # 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 Affero 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/>. // load Plan trait class_exists( 'Plan' ); /** * Methods for a Domain class */ trait DomainTrait { use PlanTrait; /** * Count of the Domain's Mailboxes * * This is a kind of cache * * @var int */ private $domainMailboxCount = null; /** * Count of the Domain's FTP accounts * * This is a kind of cache * * @var int */ private $domainFTPAccountCount = null; /** * Get domain ID * * @return int */ public function getDomainID() { return $this->get( 'domain_ID' ); } /* * Get domain name * * @return string */ public function getDomainName() { return $this->get( 'domain_name' ); } /** * Get the domain edit URl * * @param boolean $absolute True for an absolute URL * @return string */ public function getDomainPermalink( $absolute = false ) { return Domain::permalink( $this->get( 'domain_name' ), $absolute ); } /** * Check if you can create a new Mailbox for this Domain * * The Domain must have Plan informations. * * @return boolean */ public function canCreateMailboxInDomain() { return $this->getPlanMailboxes() > $this->getDomainMailboxCount() || has_permission( 'edit-email-all' ); } /** * Check if you can create a new FTP account for this Domain * * The Domain must have Plan informations. * * The Domain must have Plan informations. */ public function canCreateFTPAccountForDomain() { return $this->getPlanFTPUsers() > $this->getDomainFTPAccountCount() || has_permission( 'edit-ftp-all' ); } /** * Factory mailbox from this domain * * @return MailboxFullAPI */ public function factoryMailbox() { return ( new MailboxFullAPI() )->whereDomain( $this ); } /** * Factory e-mail forward from this domain * * @return MailforwardFullAPI */ public function factoryMailforwardfrom() { return ( new MailforwardfromQuery() )->whereDomain( $this ); } /** * Set a count of Domain's Mailboxes * * This method should not be used directly. * * @param $count int * @return self */ public function setDomainMailboxCount( $count ) { $this->domainMailboxCount = $count; } /** * Get the number of Mailboxes of this Domain * * This method has a layer of cache. * * @return int */ public function getDomainMailboxCount() { // check if we already know the count if( !isset( $this->domainMailboxCount ) ) { // count the number of mailboxes associated to this Domain $count = $this->factoryMailbox() ->select( 'COUNT(*) count' ) ->queryValue( 'count' ); // save in cache $this->domainMailboxCount = (int) $count; } return $this->domainMailboxCount; } /** * Get the number of FTP accounts of this Domain * * This method has a layer of cache. * * @return int */ public function getDomainFTPAccountCount() { // check if we already know the count if( !isset( $this->domainFTPAccountCount ) ) { // count the number of mailboxes associated to this Domain $count = $this->factoryFTP() ->select( 'COUNT(*) count' ) ->queryValue( 'count' ); // save in cache $this->domainFTPAccountCount = (int) $count; } return $this->domainFTPAccountCount; } + /** + * Get the MTA directory containing Domain's mailboxes + * + * TODO: actually all the mailbox are on the same host. + * Then, we should support multiple hosts. + * + * @return string + */ + public function getDomainMailboxesPath() { + + // require a valid filename or throw + $domain_name = $this->getDomainName(); + require_safe_filename( $domain_name ); + + // mailboxes are stored under a $BASE/domain/username filesystem structure + return MAILBOX_BASE_PATH . __ . $domain_name; + } + /** * Factory FTP users from this domain * * @return FTPAPI */ public function factoryFTP() { return ( new FTPAPI() )->whereDomain( $this ); } /** * Normalize a Domain object after being retrieved from database */ protected function normalizeDomain() { $this->integers( 'domain_ID' ); $this->booleans( 'domain_active' ); $this->dates( 'domain_born', 'domain_expiration' ); $this->normalizePlan(); } } /** * Describe the 'domain' table */ class Domain extends Queried { use DomainTrait; /** * Table name */ const T = 'domain'; const UID = 'domain_name'; /** * Constructor */ public function __construct() { $this->normalizeDomain(); } /** * Get the domain permalink * * @param string $domain_name Domain name * @param boolean $absolute True for an absolute URL */ public static function permalink( $domain_name = null, $absolute = false ) { $url = 'domain.php'; if( $domain_name ) { $url .= _ . $domain_name; } return site_page( $url, $absolute ); } } diff --git a/include/class-Mailbox.php b/include/class-Mailbox.php index a2be1c6..ece7e06 100644 --- a/include/class-Mailbox.php +++ b/include/class-Mailbox.php @@ -1,184 +1,181 @@ <?php -# Copyright (C) 2018, 2019 Valerio Bozzolan +# Copyright (C) 2018, 2019, 2020 Valerio Bozzolan # Boz Libre Hosting Panel # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # 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 Affero 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/>. // load dependent traits class_exists( 'Domain' ); trait MailboxTrait { use DomainTrait; /** * Get the mailbox username * * @return string */ public function getMailboxUsername() { return $this->get( 'mailbox_username' ); } /** * Get the mailbox address * * @return string E-mail */ public function getMailboxAddress() { return sprintf( "%s@%s", $this->get( 'mailbox_username' ), $this->get( 'domain_name' ) ); } /** * Get the mailbox permalink * * @return string */ public function getMailboxPermalink( $absolute = false ) { return Mailbox::permalink( $this->get( 'domain_name' ), $this->get( 'mailbox_username' ) ); } /** * Update this mailbox password * * @param string $password * @return string */ public function updateMailboxPassword( $password = null ) { if( ! $password ) { $password = generate_password(); } $enc_password = Mailbox::encryptPassword( $password ); // update ( new MailboxAPI() ) ->whereMailbox( $this ) ->update( [ new DBCol( 'mailbox_password', $enc_password, 's' ), ] ); return $password; } /** * Normalize a Mailbox after being fetched from database */ protected function normalizeMailbox() { $this->normalizeDomain(); $this->booleans( 'mailbox_receive' ); } /** - * Get the mailbox filesystem pathname + * Get the mailbox filesystem pathname in the MTA host * * TODO: actually all the mailbox are on the same host. * Then, we should support multiple hosts. * * @return string */ public function getMailboxPath() { - $domain_name = $this->getDomainName(); - $mailbox_user = $this->getMailboxUsername(); - // require a valid filename or throw - require_safe_filename( $domain_name ); + $mailbox_user = $this->getMailboxUsername(); require_safe_filename( $mailbox_user ); // mailboxes are stored under a $BASE/domain/username filesystem structure - return MAILBOX_BASE_PATH . __ . $domain_name . __ . $mailbox_user; + return $this->getDomainMailboxesPath() . __ . $mailbox_user; } } /** * A mailbox */ class Mailbox extends Queried { use MailboxTrait; /** * Database table */ const T = 'mailbox'; /** * Constructor */ public function __construct() { $this->normalizeMailbox(); } /** * Get the mailbox permalink * * @param $domain string * @param $mailbox string * @param $absolute boolean * @return string */ public static function permalink( $domain, $mailbox = null, $absolute = false ) { $part = site_page( 'mailbox.php', $absolute ) . _ . $domain; if( $mailbox ) { $part .= _ . $mailbox; } return $part; } /** * Encrypt a password * * @param string $password Clear text password * @return string One-way encrypted password */ public static function encryptPassword( $password ) { global $HOSTING_CONFIG; // the Mailbox password encryption mechanism can be customized if( isset( $HOSTING_CONFIG->MAILBOX_ENCRYPT_PWD ) ) { return call_user_func( $HOSTING_CONFIG->MAILBOX_ENCRYPT_PWD, $password ); } // or then just a default behaviour /** * The default behaviour is to adopt the crypt() encryption mechanism * with SHA512 and some random salt. It's strong enough nowadays. * * Read your MTA/MDA documentation, whatever you are using. * We don't know how your infrastructure works, so we don't know * how you want your password encrypted in the database and what kind * of password encryption mechanisms your MTA/MDA supports. * * In short if you are using Postfix this default configuration may work * because you may have Postfix configured as follow: * * Anyway you can use whatever MTA/MDA that talks with a MySQL database * and so you should adopt the most stronger encryption mechanism available. * * https://doc.dovecot.org/configuration_manual/authentication/password_schemes/ */ $salt = bin2hex( openssl_random_pseudo_bytes( 3 ) ); return '{SHA512-CRYPT}' . crypt( $password, "$6$$salt" ); } }