diff --git a/include/class-Log.php b/include/class-Log.php index 256ccee..2bbc586 100644 --- a/include/class-Log.php +++ b/include/class-Log.php @@ -1,216 +1,216 @@ . // make sure that this class is loaded at startup class_exists( User::class ); class_exists( Domain::class ); class_exists( Mailbox::class ); trait LogTrait { /** * Get the log actor name * * @return string */ public function getLogActorFirm() { return User::firm( $this->get( 'actor_uid' ) ); } /** * Get the action family * * @return string */ public function getLogFamily() { return $this->get( 'log_family' ); } /** * Get the action */ public function getLogAction() { return $this->get( 'log_action' ); } /** * Get the action */ public function getLogDate() { return $this->get( 'log_timestamp' ); } /** * Get the log message * * @param array $args Arguments * @return self */ public function getLogMessage( $args ) { $family = $this->getLogFamily(); $action = $this->getLogAction(); // trigger the right message family switch( $family ) { case 'domain': return self::domainMessage( $action, $this, $args ); case 'mailbox': return self::mailboxMessage( $action, $this, $args ); } return self::unknownAction( $family, $action ); } /** * Get the log message alongside the date and the actor name * * @param array $args Arguments * @return self */ public function getLogMessageWithDateAndUser( $args ) { $actor = $args['actor'] ?? $this; // create the Actor firm from the passed User object or from the Log $actor_firm = $actor instanceof User ? $actor->getUserFirm() : $actor->getLogActorFirm(); return sprintf( "%s - %s %s", $this->getLogDate()->format( __( "Y-m-d H:i" ) ), $actor_firm, $this->getLogMessage( $args ) ); } protected function normalizeLog() { $this->datetimes( 'log_timestamp' ); } } /** * A generic log of an action * * Something happened. Dunno what. */ class Log extends Queried { use LogTrait; use UserTrait; use DomainTrait; use MailboxTrait; public function __construct() { $this->normalizeLog(); } /** * Database table name */ const T = 'log'; /** * Generate a Domain-related message * * @param string $action The related action name * @param object $log * @param array $args Arguments * @return string Message */ public static function domainMessage( $action, $log, $args ) { /** * You can pass some objects to build the message: * * A complete 'actor' User object * A complete 'domain' Domain object */ $domain = $args['domain'] ?? $log; $plan = $args['plan'] ?? $log; switch( $action ) { // an administrator has changed the Plan for a Domain case 'plan.change': return sprintf( __( "changed the Plan for %s to %s" ), $domain->getDomainFirm(), esc_html( $plan->getPlanName() ) ); } // default dummy message return self::unknownAction( 'domain', $action ); } /** * Generate a Mailbox-related message * * @param string $action The related action name * @param object $log * @param array $args Arguments * @return string Message */ public static function mailboxMessage( $action, $log, $args ) { /** * You can pass some objects to build the message: * * A complete 'actor' User object * A complete 'domain' Domain object * A complete 'mailbox' Mailbox object */ $actor = $args['actor'] ?? $log; $domain = $args['domain'] ?? $log; $mailbox = $args['mailbox'] ?? $log; $mailbox_firm = Mailbox::firm( - $mailbox->getMailboxUsername(), - $domain->getDomainName() + $domain->getDomainName(), + $mailbox->getMailboxUsername() ); // trigger the right action message switch( $action ) { // the mailbox was created case 'create': return sprintf( __( "created the mailbox %s" ), $mailbox_firm ); case 'description.change': return sprintf( __( "edited description of %s" ), $mailbox_firm ); } // default dummy message return self::unknownAction( 'mailbox', $action ); } private static function unknownAction( $family, $action ) { return esc_html( sprintf( __( "misterious action about %s (%s)" ), $family, $action ) ); } } diff --git a/include/class-Mailbox.php b/include/class-Mailbox.php index b748f41..9a83690 100644 --- a/include/class-Mailbox.php +++ b/include/class-Mailbox.php @@ -1,268 +1,268 @@ . // 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 Mailbox::address( - $this->get( 'mailbox_username' ), - $this->get( 'domain_name' ) + $this->get( 'domain_name' ), + $this->get( 'mailbox_username' ) ); } /** * Get the mailbox description (if any) * * @return string */ public function getMailboxDescription() { return $this->get( 'mailbox_description' ); } /** * Get the mailbox permalink * * @return string */ public function getMailboxPermalink( $absolute = false ) { return Mailbox::permalink( $this->get( 'domain_name' ), $this->get( 'mailbox_username' ) ); } /** * Get a printable Mailbox firm * * @return string */ public function getMailboxFirm() { return Mailbox::firm( $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 ); query( 'START TRANSACTION' ); // update ( new MailboxAPI() ) ->whereMailbox( $this ) ->update( [ new DBCol( 'mailbox_password', $enc_password, 's' ), ] ); // register this event in the registry APILog::insert( [ 'family' => 'mailbox', 'action' => 'newpassword', 'domain' => $this, 'mailbox' => $this, ] ); query( 'COMMIT' ); 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', '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" ); } /** * Force to get a Mailbox ID, whatever is passed * * @param mixed $mailbox Mailbox object or Mailbox ID * @return int */ public static function getID( $mailbox ) { return is_object( $mailbox ) ? $mailbox->getMailboxID() : (int)$mailbox; } /** * Get the Mailbox address * * @param string $domain_name Domain name * @param string $mailbox_username Mailbox username without domain name * @return string */ public static function address( $domain_name, $mailbox_username ) { - return $domain_name . '@' . $mailbox_username; + return $mailbox_username . '@' . $domain_name; } /** * Get a printable Mailbox firm * * @param string $domain_name Domain name * @param string $mailbox_username Mailbox username without domain name * @return string */ public static function firm( $domain_name, $mailbox_username ) { return HTML::a( self::permalink( $domain_name, $mailbox_username ), esc_html( self::address( $domain_name, $mailbox_username ) ) ); } } diff --git a/include/class-Mailforwardfrom.php b/include/class-Mailforwardfrom.php index 9aa8393..f4339dd 100644 --- a/include/class-Mailforwardfrom.php +++ b/include/class-Mailforwardfrom.php @@ -1,127 +1,127 @@ . /** * Methods for a Mailforwardfrom class */ trait MailforwardfromTrait { /** * Get the mailforward from ID * * @return int */ public function getMailforwardfromID() { return $this->get( 'mailforwardfrom_ID' ); } /** * Get the mailforward from address * * @return string E-mail */ public function getMailforwardfromAddress() { return Mailbox::address( - $this->getMailforwardfromUsername(), - $this->getDomainName() + $this->getDomainName(), + $this->getMailforwardfromUsername() ); } /** * Get the mailforward from username * * @return string */ public function getMailforwardfromUsername() { return $this->get( 'mailforwardfrom_username' ); } /** * Get the mailforward permalink * * @param $absolute boolean * @return string */ public function getMailforwardfromPermalink( $absolute = false ) { return Mailforwardfrom::permalink( $this->getDomainName(), $this->getMailforwardfromUsername(), $absolute ); } /** * Normalize a Mailforwardfrom object after being retrieved from database */ protected function normalizeMailforwardfrom() { $this->integers( 'mailforwardfrom_ID' ); } } /** * An e-mail forwarding */ class Mailforwardfrom extends Queried { use MailforwardfromTrait; use DomainTrait; /** * Constructor */ public function __constructor() { $this->normalizeMailforwardfrom(); $this->normalizeDomain(); } /** * Database table name */ const T = 'mailforwardfrom'; /** * Update the related database row */ public function update( $columns ) { query_update( self::T, $columns, sprintf( "mailforwardfrom_ID = %d", $this->getMailforwardfromID() ) ); } /** * Get the mailforward permalink * * @return string */ public static function permalink( $domain, $mailforward = false, $absolute = false ) { $part = site_page( 'mailforward.php', $absolute ) . _ . $domain; if( $mailforward ) { $part .= _ . $mailforward; } return $part; } /** * Force to get a Mailforwardfrom ID, whatever is passed * * @param mixed $mailforward Mailforwardfrom object or its ID * @return int */ public static function getID( $mailforward ) { return is_object( $mailforward ) ? $mailforward->getMailforwardfromID() : (int)$mailforward; } }