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" );
 	}
 
 }