openrat-cms

OpenRat Content Management System
git clone http://git.code.weiherhei.de/openrat-cms.git
Log | Files | Refs | README

commit c4b9fade581405ef5a933721c75ae2fede153675
parent e4e4fdee041f9219fafc54fe64b9d01cd18434c4
Author: Jan Dankert <develop@jandankert.de>
Date:   Thu, 19 Nov 2020 16:05:15 +0100

Refactoring: Better exception handling in class Mail; multiple CC and BCC receivers.

Diffstat:
Mmodules/cms/action/login/LoginPasswordAction.class.php | 32++++++++++++++++----------------
Mmodules/cms/action/login/LoginPasswordcodeAction.class.php | 9++++-----
Mmodules/cms/action/login/LoginRegisterAction.class.php | 20+++++++++++---------
Mmodules/cms/action/profile/ProfileMailAction.class.php | 21+++++++++++----------
Mmodules/util/Mail.class.php | 114+++++++++++++++++++++++++++++++++++--------------------------------------------
5 files changed, 93 insertions(+), 103 deletions(-)

diff --git a/modules/cms/action/login/LoginPasswordAction.class.php b/modules/cms/action/login/LoginPasswordAction.class.php @@ -6,6 +6,7 @@ use cms\base\Configuration; use cms\base\DB; use cms\model\User; use language\Messages; +use logger\Logger; use security\Password; use util\exception\ValidationException; use util\Mail; @@ -45,24 +46,23 @@ class LoginPasswordAction extends LoginAction implements Method { $code = rand(); $this->setSessionVar(Session::KEY_PASSWORD_COMMIT_CODE,$code); - $eMail = new Mail( $user->mail,'password_commit_code' ); + $eMail = new Mail($user->mail,Messages::MAIL_SUBJECT_PASSWORD_COMMIT_CODE,Messages::MAIL_TEXT_PASSWORD_COMMIT_CODE); $eMail->setVar('name',$user->getName()); $eMail->setVar('code',$code); - if ( $eMail->send() ) - $this->addNoticeFor( new User(), Messages::MAIL_SENT); - else - // Yes, the mail is not sent but we are faking a sent mail. - // so no one is able to check if the username exists (if the mail system is down) - $this->addNoticeFor( new User(), Messages::MAIL_SENT); - $this->setSessionVar(Session::KEY_PASSWORD_COMMIT_NAME,$user->name); + try { + $eMail->send(); + $this->setSessionVar(Session::KEY_PASSWORD_COMMIT_NAME,$user->name); + } + catch( \Exception $e ) { + Logger::warn( $e ); + } } - else - { - // There is no user with this name. - // We are faking a sending mail, so no one is able to check if this username exists. - sleep(1); - $this->addNoticeFor( new User(), Messages::MAIL_SENT); - } - } + + // For security reasons: + // Always display a message that a mail is sent. + // So no one is able to check if this username exists. + sleep(1); + $this->addNoticeFor( new User(), Messages::MAIL_SENT); + } } diff --git a/modules/cms/action/login/LoginPasswordcodeAction.class.php b/modules/cms/action/login/LoginPasswordcodeAction.class.php @@ -31,17 +31,16 @@ class LoginPasswordcodeAction extends LoginAction implements Method { { $newPw = $user->createPassword(); // Neues Kennwort erzeugen. - $eMail = new Mail( $user->mail,'password_new' ); + $eMail = new Mail($user->mail, Messages::MAIL_SUBJECT_PASSWORD_NEW,Messages::MAIL_TEXT_PASSWORD_NEW); $eMail->setVar('name' ,$user->getName()); $eMail->setVar('password',$newPw ); try { - if ( $eMail->send() ) - $user->setPassword( $newPw, false ); // Kennwort muss beim n?. Login ge?ndert werden. - else - Logger::warn('Mail could not be sent: '.$eMail->error); + $eMail->send(); + $user->setPassword( $newPw, false ); // Kennwort muss beim n?. Login ge?ndert werden. } catch( \Exception $e ) { Logger::warn( $e ); + Logger::warn('Mail could not be sent: '.$user->mail); } } diff --git a/modules/cms/action/login/LoginRegisterAction.class.php b/modules/cms/action/login/LoginRegisterAction.class.php @@ -2,10 +2,13 @@ namespace cms\action\login; use cms\action\LoginAction; use cms\action\Method; +use cms\action\RequestParams; use cms\model\User; use language\Messages; +use logger\Logger; use util\Mail; use util\Session; +use util\text\TextMessage; class LoginRegisterAction extends LoginAction implements Method { @@ -13,6 +16,7 @@ class LoginRegisterAction extends LoginAction implements Method { } public function post() { + $email_address = $this->getRequestVar('mail',RequestParams::FILTER_MAIL); if ( ! Mail::checkAddress($email_address) ) @@ -30,17 +34,15 @@ class LoginRegisterAction extends LoginAction implements Method { // E-Mail and die eingegebene Adresse verschicken - $mail = new Mail($email_address, - 'register_commit_code'); + $mail = new Mail($email_address, Messages::MAIL_SUBJECT_REGISTER_COMMIT_CODE,Messages::MAIL_TEXT_REGISTER_COMMIT_CODE); $mail->setVar('code',$registerCode); // Registrierungscode als Text-Variable - - if ( $mail->send() ) - { + + try { + $mail->send(); $this->addNoticeFor( new User(), Messages::MAIL_SENT); - } - else - { - $this->addErrorFor( new User(),Messages::MAIL_NOT_SENT, [], $mail->error); + } catch( \Exception $e ) { + Logger::warn( new \Exception(TextMessage::create('Mail could not be sent for unregistered user with adress ${0}', [$email_address]), $e) ); + $this->addErrorFor( new User(),Messages::MAIL_NOT_SENT); } } } diff --git a/modules/cms/action/profile/ProfileMailAction.class.php b/modules/cms/action/profile/ProfileMailAction.class.php @@ -7,16 +7,19 @@ use logger\Logger; use util\exception\ValidationException; use util\Mail; use util\Session; +use util\text\TextMessage; class ProfileMailAction extends ProfileAction implements Method { + public function view() { } + public function post() { srand ((double)microtime()*1000003); $code = rand(); // Zufalls-Freischaltcode erzeugen $newMail = $this->getRequestVar('mail'); - if ( empty($newMail) ) + if ( !$newMail ) { // Keine E-Mail-Adresse eingegeben. throw new ValidationException('mail'); @@ -28,18 +31,16 @@ class ProfileMailAction extends ProfileAction implements Method { Session::set( Session::KEY_MAIL_CHANGE_MAIL,$newMail); // E-Mail an die neue Adresse senden. - $mail = new Mail( $newMail,'mail_change_code' ); + $mail = new Mail($newMail, Messages::MAIL_SUBJECT_MAIL_CHANGE_CODE,Messages::MAIL_TEXT_MAIL_CHANGE_CODE); $mail->setVar('code',$code ); $mail->setVar('name',$this->user->getName()); - - if ( $mail->send() ) - { + + try { + $mail->send(); $this->addNoticeFor( $this->user, Messages::MAIL_SENT); - } - else - { - Logger::warn('Mail could not be sent: '.$mail->error); - $this->addNoticeFor($this->user, Messages::MAIL_NOT_SENT,[],$mail->error); // Meldung + } catch( \Exception $e ) { + Logger::warn( new \Exception( TextMessage::create('Mail could not be sent for user ${name} with the new email adress {mail} ',['name'=>$this->user->name,'mail'=>$newMail]),$e) ); + $this->addNoticeFor($this->user, Messages::MAIL_NOT_SENT ); } } } diff --git a/modules/util/Mail.class.php b/modules/util/Mail.class.php @@ -18,13 +18,11 @@ namespace util; -use Adresse; use cms\base\Configuration; +use cms\base\Language; use cms\base\Startup; -use cms\base\Version; use LogicException; -use util\Text; -use unknown_type; +use util\text\TextMessage; use util\text\variables\VariableResolver; /** @@ -63,7 +61,7 @@ class Mail * * @var array Fehler */ - var $error = array(); + public $debugLog = array(); /** * Set to true for debugging. @@ -71,17 +69,21 @@ class Mail * * @var bool */ - var $debug = true; + public $debug = true; + /** + * @var string|string + */ + private $mailKey; /** * Konstruktor. * Es werden folgende Parameter erwartet * @param String $to Empf�nger - * @param String der Textschl�ssel - * @return Mail + * @param string $subjectKey + * @param string $mailKey */ - function __construct($to, $text) + function __construct($to, $subjectKey, $mailKey) { $mailConfig = Configuration::subset('mail'); @@ -98,10 +100,10 @@ class Mail $this->header[] = 'X-Mailer: ' . $this->header_encode(Startup::TITLE . ' ' . Startup::VERSION); $this->header[] = 'Content-Type: text/plain; charset=UTF-8'; - $this->subject = $this->header_encode(\cms\base\Language::lang('mail_subject_' . $text)); + $this->subject = $this->header_encode(Language::lang($subjectKey)); $this->to = $this->header_encode($to); - $this->text = $this->nl . wordwrap(\cms\base\Language::lang('mail_text_' . $text), 70, $this->nl) . $this->nl; + $this->text = $this->nl . wordwrap(Language::lang($mailKey), 70, $this->nl) . $this->nl; // Signatur anhaengen (sofern konfiguriert) $signature = $mailConfig->get('signature',''); @@ -116,19 +118,21 @@ class Mail // Kopie-Empf�nger if ( $mailConfig->has('cc')) - $this->cc = $this->header_encode($mailConfig->get('cc')); + $this->cc = $this->header_encode( implode(', ',$mailConfig->get('cc',[]))); // Blindkopie-Empf�nger if ( $mailConfig->has('bcc')) - $this->bcc = $this->header_encode($mailConfig->get('bcc')); + $this->bcc = $this->header_encode( implode(', ',$mailConfig->get('bcc',[]))); + + $this->mailKey = $mailKey; } /** * Kodiert einen Text in das Format "Quoted-printable".<br> * See RFC 2045. - * @param String $text Eingabe - * @return Text im quoted-printable-Format + * @param string $text Eingabe + * @return string Text im quoted-printable-Format */ private function quoted_printable_encode($text) { @@ -154,8 +158,6 @@ class Mail /** * Mail absenden. * Die E-Mail wird versendet. - * - * @return boolean Erfolg */ public function send() { @@ -172,8 +174,7 @@ class Mail if ($white) { if (!$this->containsDomain($to_domain, $white)) { // Wenn Domain nicht in Whitelist gefunden, dann Mail nicht verschicken. - $this->error[] = 'Mail-Domain is not whitelisted'; - return false; + throw new LogicException( TextMessage::create('Mail-Domain ${0} is not whitelisted',[$to_domain])); } } @@ -183,8 +184,7 @@ class Mail if ($black) { if ($this->containsDomain($to_domain, $black)) { // Wenn Domain in Blacklist gefunden, dann Mail nicht verschicken. - $this->error[] = 'Mail-Domain is blacklisted'; - return false; + throw new LogicException( TextMessage::create('Mail-Domain ${0} is blacklisted',[$to_domain])); } } @@ -218,9 +218,8 @@ class Mail // Die E-Mail wurde nicht akzeptiert. // Genauer geht es leider nicht, da mail() nur einen boolean-Wert // zur�ck liefert. - $this->error[] = 'Mail was NOT accepted by mail()'; + throw new LogicException('Mail was NOT accepted by mail(), no further information available. Please look into your system logs.'); - return $result; } else { // eigenen SMTP-Dialog verwenden. $smtpConf = $mailConfig->subset('smtp'); @@ -233,10 +232,8 @@ class Mail // Mail direkt zustellen. $mxHost = $this->getMxHost($this->to); - if (empty($mxHost)) { - $this->error[] = "No MX-Entry found. Mail could not be sent."; - return false; - } + if (empty($mxHost)) + throw new LogicException( TextMessage::create('No MX-Entry found for ${0}',[$this->to])); if ($smtpConf->is('ssl',false)) $mxPort = 465; @@ -260,31 +257,27 @@ class Mail $smtpSocket = fsockopen($proto . '://' . $mxHost, $mxPort, $errno, $errstr, intval($smtpConf['timeout'])); if (!is_resource($smtpSocket)) { - $this->error[] = 'Connection failed to: ' . $proto . '://' . $mxHost . ':' . $mxPort . ' (' . $errstr . '/' . $errno . ')'; - return false; + throw new LogicException('Connection failed to: ' . $proto . '://' . $mxHost . ':' . $mxPort . ' (' . $errstr . '/' . $errno . ')'); } $smtpResponse = fgets($smtpSocket, 4096); if ($this->debug) - $this->error[] = trim($smtpResponse); + $this->debugLog[] = trim($smtpResponse); if (substr($smtpResponse, 0, 3) != '220') { - $this->error[] = 'No 220: ' . trim($smtpResponse); - return false; + throw new LogicException('No 220: ' . trim($smtpResponse) ); } if (!is_resource($smtpSocket)) { - $this->error[] = 'Connection failed to: ' . $smtpConf['host'] . ':' . $smtpConf['port'] . ' (' . $smtpResponse . ')'; - return false; + throw new LogicException('Connection failed to: ' . $smtpConf['host'] . ':' . $smtpConf['port'] . ' (' . $smtpResponse . ')' ); } //you have to say HELO again after TLS is started $smtpResponse = $this->sendSmtpCommand($smtpSocket, 'HELO ' . $myHost); if (substr($smtpResponse, 0, 3) != '250') { - $this->error[] = "No 2xx after HELO, server says: " . $smtpResponse; $this->sendSmtpQuit($smtpSocket); - return false; + throw new LogicException("No 2xx after HELO, server says: " . $smtpResponse); } if ($smtpConf['tls']) { @@ -295,9 +288,8 @@ class Mail $smtpResponse = $this->sendSmtpCommand($smtpSocket, 'HELO ' . $myHost); if (substr($smtpResponse, 0, 3) != '250') { - $this->error[] = "No 2xx after HELO, server says: " . $smtpResponse; $this->sendSmtpQuit($smtpSocket); - return false; + throw new LogicException("No 2xx after HELO, server says: " . $smtpResponse ); } } else { // STARTTLS ging in die Hose. Einfach weitermachen. @@ -308,53 +300,47 @@ class Mail if ( $smtpConf->has('auth_username') && $smtpConf->has('host') ) { $smtpResponse = $this->sendSmtpCommand($smtpSocket, "AUTH LOGIN"); if (substr($smtpResponse, 0, 3) != '334') { - $this->error[] = "No 334 after AUTH_LOGIN, server says: " . $smtpResponse; $this->sendSmtpQuit($smtpSocket); - return false; + throw new LogicException("No 334 after AUTH_LOGIN, server says: " . $smtpResponse); } if ($this->debug) - $this->error[] = 'Login for ' . $smtpConf->get('auth_username'); + $this->debugLog[] = 'Login for ' . $smtpConf->get('auth_username'); //send the username $smtpResponse = $this->sendSmtpCommand($smtpSocket, base64_encode($smtpConf->get('auth_username'))); if (substr($smtpResponse, 0, 3) != '334') { - $this->error[] = "No 3xx after setting username, server says: " . $smtpResponse; $this->sendSmtpQuit($smtpSocket); - return false; + throw new LogicException("No 3xx after setting username, server says: " . $smtpResponse); } //send the password $smtpResponse = $this->sendSmtpCommand($smtpSocket, base64_encode($smtpConf->get('auth_password'))); if (substr($smtpResponse, 0, 3) != '235') { - $this->error[] = "No 235 after sending password, server says: " . $smtpResponse; $this->sendSmtpQuit($smtpSocket); - return false; + throw new LogicException("No 235 after sending password, server says: " . $smtpResponse ); } } //email from $smtpResponse = $this->sendSmtpCommand($smtpSocket, 'MAIL FROM: <' . $mailConfig->get('from') . '>'); if (substr($smtpResponse, 0, 3) != '250') { - $this->error[] = "No 2xx after MAIL_FROM, server says: " . $smtpResponse; $this->sendSmtpQuit($smtpSocket); - return false; + throw new LogicException("No 2xx after MAIL_FROM, server says: " . $smtpResponse); } //email to $smtpResponse = $this->sendSmtpCommand($smtpSocket, 'RCPT TO: <' . $this->to . '>'); if (substr($smtpResponse, 0, 3) != '250') { - $this->error[] = "No 2xx after RCPT_TO, server says: " . $smtpResponse; $this->sendSmtpQuit($smtpSocket); - return false; + throw new LogicException("No 2xx after RCPT_TO, server says: " . $smtpResponse); } //the email $smtpResponse = $this->sendSmtpCommand($smtpSocket, "DATA"); if (substr($smtpResponse, 0, 3) != '354') { - $this->error[] = "No 354 after DATA, server says: " . $smtpResponse; $this->sendSmtpQuit($smtpSocket); - return false; + throw new LogicException("No 354 after DATA, server says: " . $smtpResponse); } $this->header[] = 'To: ' . $this->to; @@ -365,14 +351,12 @@ class Mail //observe the . after the newline, it signals the end of message $smtpResponse = $this->sendSmtpCommand($smtpSocket, implode($this->nl, $this->header) . $this->nl . $this->nl . $text . $this->nl . '.'); if (substr($smtpResponse, 0, 3) != '250') { - $this->error[] = "No 2xx after putting DATA, server says: " . $smtpResponse; $this->sendSmtpQuit($smtpSocket); - return false; + throw new LogicException("No 2xx after putting DATA, server says: " . $smtpResponse); } // say goodbye $this->sendSmtpQuit($smtpSocket); - return true; } } @@ -388,20 +372,20 @@ class Mail private function sendSmtpCommand($socket, $cmd) { if ($this->debug) - $this->error[] = 'CLIENT: >>> ' . trim($cmd); + $this->debugLog[] = 'CLIENT: >>> ' . trim($cmd); if (!is_resource($socket)) { // Die Verbindung ist geschlossen. Dies kann bei dieser // Implementierung eigentlich nur dann passieren, wenn // der Server die Verbindung schlie�t. // Dieser Client trennt die Verbindung nur nach einem "QUIT". - $this->error[] = "Connection lost"; + $this->debugLog[] = "Connection lost"; return; } fputs($socket, $cmd . $this->nl); $response = trim(fgets($socket, 4096)); if ($this->debug) - $this->error[] = 'SERVER: <<< ' . $response; + $this->debugLog[] = 'SERVER: <<< ' . $response; return $response; } @@ -416,7 +400,7 @@ class Mail { if ($this->debug) - $this->error[] = "CLIENT: >>> QUIT"; + $this->debugLog[] = "CLIENT: >>> QUIT"; if (!is_resource($socket)) return; // Wenn die Verbindung nicht mehr da ist, brauchen wir @@ -426,10 +410,10 @@ class Mail fputs($socket, 'QUIT' . $this->nl); $response = trim(fgets($socket, 4096)); if ($this->debug) - $this->error[] = 'SERVER: <<< ' . $response; + $this->debugLog[] = 'SERVER: <<< ' . $response; if (substr($response, 0, 3) != '221') - $this->error[] = 'QUIT FAILED: ' . $response; + $this->debugLog[] = 'QUIT FAILED: ' . $response; fclose($socket); } @@ -486,8 +470,7 @@ class Mail list($user, $host) = explode('@', $to . '@'); if (empty($host)) { - $this->error[] = 'Illegal mail address - No hostname found.'; - return ""; + throw new LogicException( TextMessage::create('Illegal mail address ${0}: No hostname found',[$to]) ); } list($host) = explode('>', $host); @@ -511,7 +494,7 @@ class Mail * Es wird nur die Syntax geprüft. Ob die Adresse wirklich existiert, steht dadurch noch lange * nicht fest. Dazu müsste man die MX-Records auflösen und einen Zustellversuch unternehmen. * - * @param $email_address Adresse + * @param $email_address string Adresse * @return true, falls Adresse OK, sonst false */ public static function checkAddress($email_address) @@ -542,4 +525,9 @@ class Mail } return false; } + + public function __toString() + { + return TextMessage::create('Mail to ${0} with subject key ${1}',[$this->to,$this->mailKey]); + } } \ No newline at end of file