diff --git a/cli/update-mailbox-quotas.php b/cli/update-mailbox-quotas.php index 9364415..8f0b8b1 100755 --- a/cli/update-mailbox-quotas.php +++ b/cli/update-mailbox-quotas.php @@ -1,92 +1,100 @@ #!/usr/bin/php . require __DIR__ . '/../load.php'; /** * Script to update Mailbox quotas * + * See https://gitpull.it/T101 */ // get every active domain $domains = ( new DomainAPI() ) ->select( [ 'domain.domain_ID', 'domain_name', ] ) ->whereDomainIsActive() ->queryGenerator(); // for each active domain foreach( $domains as $domain ) { $domain_name = $domain->getDomainName(); // validate domain name if( strpos( $domain_name, __ ) !== false ) { error( "domain '$domain_name' is bad" ); continue; } // get every active mailbox of this domain $mailboxes = ( new MailboxAPI() ) ->select( [ 'mailbox_ID', 'mailbox_username', ] ) ->whereDomain( $domain ) ->whereMailboxIsActive() ->queryGenerator(); query( 'START TRANSACTION' ); // for each mailboxes foreach( $mailboxes as $mailbox ) { $mailbox_username = $mailbox->getMailboxUsername(); // validate mailbox name if( strpos( $mailbox_username, __ ) !== false ) { error( "$domain_name.$mailbox_username is bad" ); continue; } // calculate the quota size $bytes = 0; $expected_path = MAILBOX_BASE_PATH . __ . $domain_name . __ . $mailbox_username; if( file_exists( $expected_path ) ) { $bytes_raw = exec( sprintf( - "du --summarize -- %s | cut -f1", + 'du --summarize -- %s | cut -f1', escapeshellarg( $expected_path ) ) ); $bytes = (int) $bytes_raw; } - // store the value + // store the value in the history ( new MailboxSizeAPI() ) ->insertRow( [ 'mailbox_ID' => $mailbox->getMailboxID(), 'mailboxsize_bytes' => $bytes, new DBCol( 'mailboxsize_date', 'NOW()', '-' ), ] ); + + // update the denormalized latest Mailbox data + ( new MailboxAPI() ) + ->whereMailbox( $mailbox ) + ->update( [ + 'mailbox_lastsizebytes' => $bytes, + ] ); } query( 'COMMIT' ); } diff --git a/documentation/database/patches/patch-0002-rename-denormalized-mailbox-lastsizebytes.sql b/documentation/database/patches/patch-0002-rename-denormalized-mailbox-lastsizebytes.sql new file mode 100644 index 0000000..c4f2902 --- /dev/null +++ b/documentation/database/patches/patch-0002-rename-denormalized-mailbox-lastsizebytes.sql @@ -0,0 +1,2 @@ +ALTER TABLE `{$prefix}mailbox` CHANGE `mailbox_lastquotabytes` `mailbox_lastsizebytes` INT(10) UNSIGNED; + diff --git a/include/class-Mailbox.php b/include/class-Mailbox.php index ee40097..30984c2 100644 --- a/include/class-Mailbox.php +++ b/include/class-Mailbox.php @@ -1,191 +1,201 @@ . // load dependent traits class_exists( 'Domain' ); trait MailboxTrait { use DomainTrait; /** * Get the Mailbox ID * * @return int */ public function getMailboxID() { return $this->get( 'mailbox_ID' ); } /** * 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; } + /** + * Get the last size in bytes + * + * @return int + */ + public function getMailboxLastSizeBytes() { + return $this->get( 'mailbox_lastsizebytes' ); + } + /** * Normalize a Mailbox after being fetched from database */ protected function normalizeMailbox() { $this->normalizeDomain(); - $this->integers( 'mailbox_ID' ); + $this->integers( 'mailbox_ID', + 'mailbox_lastsizebytes' ); $this->booleans( 'mailbox_receive' ); } /** * 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() { // require a valid filename or throw $mailbox_user = $this->getMailboxUsername(); require_safe_dirname( $mailbox_user ); // mailboxes are stored under a $BASE/domain/username filesystem structure 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" ); } } diff --git a/load-post.php b/load-post.php index 21b67df..e6283f8 100644 --- a/load-post.php +++ b/load-post.php @@ -1,129 +1,129 @@ . /** * This is your versioned configuration file * * It does not contains secrets. * * This file is required after loading your * unversioned configuration file: * * load.php */ // database version // // you can increase your database version if you added some patches in: // documentation/database/patches -define( 'DATABASE_VERSION', 1 ); +define( 'DATABASE_VERSION', 2 ); // include path define_default( 'INCLUDE_PATH', ABSPATH . __ . 'include' ); // template path define_default( 'TEMPLATE_PATH', ABSPATH . __ . 'template' ); // autoload classes from the /include directory spl_autoload_register( function( $name ) { // TODO: autoload classes and create DomainTrait and use in Mailbox $path = INCLUDE_PATH . __ . "class-$name.php"; if( is_file( $path ) ) { require $path; } } ); // override default user class define( 'SESSIONUSER_CLASS', 'User' ); // load common functions require INCLUDE_PATH . __ . 'functions.php'; // jquery URL // provided by the libjs-jquery package as default define_default( 'JQUERY_URL', '/javascript/jquery/jquery.min.js' ); // Bootstrap CSS/JavaScript files without trailing slash // provided by the libjs-bootstrap package as default define_default( 'BOOTSTRAP_DIR_URL', '/javascript/bootstrap' ); // path to the Net SMTP class // provided by the php-net-smtp package as default define_default( 'NET_SMTP', '/usr/share/php/Net/SMTP.php' ); // base directory for your virtualhosts // e.g. you may have /var/www/example.com/index.html // do NOT end with a slash // TODO: support multiple hosts define_default( 'VIRTUALHOSTS_DIR', '/var/www' ); // default currency simbol define_default( 'DEFAULT_CURRENCY_SYMBOL', '€' ); // register JavaScript/CSS files register_js( 'jquery', JQUERY_URL ); register_js( 'bootstrap', BOOTSTRAP_DIR_URL . '/js/bootstrap.min.js' ); register_css( 'bootstrap', BOOTSTRAP_DIR_URL . '/css/bootstrap.min.css' ); register_css( 'custom-css', ROOT . '/content/style.css' ); // GNU Gettext i18n define( 'GETTEXT_DOMAIN', 'reyboz-hosting-panel' ); define( 'GETTEXT_DIRECTORY', 'l10n' ); define( 'GETTEXT_DEFAULT_ENCODE', CHARSET ); // UTF-8 // common strings define_default( 'SITE_NAME', "KISS Libre Hosting Panel" ); define_default( 'CONTACT_EMAIL', 'support@' . DOMAIN ); define_default( 'REPO_URL', 'https://gitpull.it/project/profile/15/' ); // limit session duration to 5 minutes (60s * 100m) define_default( 'SESSION_DURATION', 6000 ); /** * Mailbox base path * * Used by CLI scripts to calculate the current quotas. * * The mailboxes should have paths like: * MAILBOX_BASE_PATH/domain_name/user_name/ */ define_default( 'MAILBOX_BASE_PATH', '/home/vmail' ); // register web pages add_menu_entries( [ new MenuEntry( 'index', '/', __( "Dashboard" ), null, 'backend' ), new MenuEntry( 'login', 'login.php', __( "Login" ) ), new MenuEntry( 'profile', 'profile.php', __( "Profile" ) ), new MenuEntry( 'logout', 'logout.php', __( "Logout" ), null, 'read' ), new MenuEntry( 'user-list', 'user-list.php', __( "Users" ), null, 'edit-user-all' ), new MenuEntry( 'password-reset', 'password-reset.php', __( "Password reset" ) ), ] ); // permissions of a normal user register_permissions( 'user', [ 'read', 'backend', ] ); // permissions of an admin inherit_permissions( 'admin', 'user', [ 'edit-user-all', 'edit-email-all', 'edit-domain-all', 'edit-plan-all', 'edit-ftp-all', ] ); diff --git a/template/mailbox-stats.php b/template/mailbox-stats.php index d34c564..f44835d 100644 --- a/template/mailbox-stats.php +++ b/template/mailbox-stats.php @@ -1,57 +1,54 @@ . /* * This is the template for some mailbox stats * * Called from: * template/mailbox.php * * Available variables: * $mailbox object * $plan object */ // avoid to be load directly defined( 'BOZ_PHP' ) or die; -// calculate the quota -$quota = ( new MailboxSizeAPI() ) - ->select( 'mailboxsize_bytes' ) - ->whereMailbox( $mailbox ) - ->whereMailboxSizeIsLast() - ->queryRow(); ?>

- + getMailboxLastSizeBytes() !== null ): ?> - +
getMailboxSizeHumanSize() ?>getMailboxLastSizeBytes() ) ?>
- +