Exceptions, Mail, Auth, Register, UserMapper, Validator

This commit is contained in:
Visman 2017-03-04 19:01:10 +07:00
parent a1f5f61a50
commit 35c9d5583c
12 changed files with 159 additions and 99 deletions

View file

@ -0,0 +1,9 @@
<?php
namespace ForkBB\Core\Exceptions;
use Exception;
class ForkException extends Exception
{
}

View file

@ -0,0 +1,7 @@
<?php
namespace ForkBB\Core\Exceptions;
class MailException extends ForkException
{
}

View file

@ -0,0 +1,7 @@
<?php
namespace ForkBB\Core\Exceptions;
class StmpException extends MailException
{
}

View file

@ -2,7 +2,8 @@
namespace ForkBB\Core;
use RuntimeException;
use ForkBB\Core\Exceptions\MailException;
use ForkBB\Core\Exceptions\StmpException;
class Mail
{
@ -81,14 +82,33 @@ class Mail
/**
* Валидация email
* @param mixed $email
* @return bool
* @param bool $strict
* @param bool $idna
* @return false|string
*/
public function valid($email)
public function valid($email, $strict = false, $idna = false)
{
return is_string($email)
&& strlen($email) <= 80
&& trim($email) === $email
&& preg_match('%^.+@.+$%D', $email);
if (! is_string($email)
|| mb_strlen($email, 'UTF-8') > 80
|| ! preg_match('%^([\w!#$\%&\'*+-/=?^`{|}~]+(?:\.[\w!#$\%&\'*+-/=?^`{|}~]+)*)@([^\x00-\x20]+)$%D', $email, $matches)
) {
return false;
}
$local = $matches[1];
$domain = mb_strtolower($matches[2], 'UTF-8');
if (! preg_match('%^(?:[\p{L}\p{N}]+(?:\-[\p{L}\p{N}]+)*\.)*\p{L}+$%u', $domain)) {
return false;
}
$domainASCII = idn_to_ascii($domain);
if ($strict) {
$mx = dns_get_record($domainASCII, DNS_MX);
if (empty($mx)) {
return false;
}
}
return $local . '@' . ($idna ? $domainASCII : $domain);
}
/**
@ -127,8 +147,8 @@ class Mail
$email = preg_split('%[,\n\r]%', (string) $email, -1, PREG_SPLIT_NO_EMPTY);
}
foreach($email as $cur) {
$cur = trim((string) $cur);
if ($this->valid($cur)) {
$cur = $this->valid(trim((string) $cur), false, true);
if (false !== $cur) {
$this->to[] = $this->formatAddress($cur, $name);
}
}
@ -155,7 +175,8 @@ class Mail
*/
public function setFrom($email, $name = null)
{
if ($this->valid($email)) {
$email = $this->valid($email, false, true);
if (false !== $email) {
$this->headers['From'] = $this->formatAddress($email, $name);
}
return $this;
@ -169,7 +190,8 @@ class Mail
*/
public function setReplyTo($email, $name = null)
{
if ($this->valid($email)) {
$email = $this->valid($email, false, true);
if (false !== $email) {
$this->headers['Reply-To'] = $this->formatAddress($email, $name);
}
return $this;
@ -183,8 +205,7 @@ class Mail
*/
protected function formatAddress($email, $name = null)
{
$email = $this->filterEmail($email);
if (null === $name || ! is_string($name) || strlen(trim($name)) == 0) {
if (! is_string($name) || strlen(trim($name)) == 0) {
return $email;
} else {
$name = $this->encodeText($this->filterName($name));
@ -206,16 +227,6 @@ class Mail
}
}
/**
* Фильтрация email
* @param string $email
* @return string
*/
protected function filterEmail($email)
{
return preg_replace('%[\x00-\x1F",<>]%', '', $email);
}
/**
* Фильтрация имени
* @param string $name
@ -223,14 +234,7 @@ class Mail
*/
protected function filterName($name)
{
return strtr(trim($name), [
"\r" => '',
"\n" => '',
"\t" => '',
'"' => '\'',
'<' => '[',
'>' => ']',
]);
return addcslashes(preg_replace('%[\x00-\x1F]%', '', trim($name)), '\\"');
}
/**
@ -259,14 +263,14 @@ class Mail
* Задает сообщение по шаблону
* @param string $tpl
* @param array $data
* @throws \RuntimeException
* @throws MailException
* @return Mail
*/
public function setTpl($tpl, array $data)
{
$file = rtrim($this->folder, '\\/') . '/' . $this->language . '/mail/' . $tpl;
if (! file_exists($file)) {
throw new RuntimeException('Tpl not found');
throw new MailException('The template isn\'t found (' . $file . ').');
}
$tpl = trim(file_get_contents($file));
foreach ($data as $key => $val) {
@ -274,7 +278,7 @@ class Mail
}
list($subject, $tpl) = explode("\n", $tpl, 2);
if (! isset($tpl)) {
throw new RuntimeException('Tpl empty');
throw new MailException('The template is empty (' . $file . ').');
}
$this->setSubject(substr($subject, 8));
return $this->setMessage($tpl);
@ -283,7 +287,6 @@ class Mail
/**
* Задает сообщение
* @param string $message
* @throws \RuntimeException
* @return Mail
*/
public function setMessage($message)
@ -297,21 +300,22 @@ class Mail
/**
* Отправляет письмо
* @throws MailException
* @return bool
*/
public function send()
{
if (empty($this->to)) {
throw new RuntimeException('No recipient(s)');
throw new MailException('No recipient for the email.');
}
if (empty($this->headers['From'])) {
throw new RuntimeException('No sender');
throw new MailException('No sender for the email.');
}
if (! isset($this->headers['Subject'])) {
throw new RuntimeException('Subject empty');
throw new MailException('The subject of the email is empty.');
}
if (trim($this->message) == '') {
throw new RuntimeException('Message empty');
throw new MailException('The body of the email is empty.');
}
$this->headers = array_replace($this->headers, [
@ -359,7 +363,7 @@ class Mail
/**
* Отправка письма через smtp
* @throws \RuntimeException
* @throws SmtpException
* @return bool
*/
protected function smtp()
@ -367,7 +371,7 @@ class Mail
// подлючение
if (! is_resource($this->connect)) {
if (($connect = @fsockopen($this->smtp['host'], $this->smtp['port'], $errno, $errstr, 5)) === false) {
throw new RuntimeException('Could not connect to smtp host "' . $this->smtp['host'] . '" (' . $errno . ') (' . $errstr . ')');
throw new SmtpException('Couldn\'t connect to smtp host "' . $this->smtp['host'] . ':' . $this->smtp['port'] . '" (' . $errno . ') (' . $errstr . ').');
}
stream_set_timeout($connect, 5);
$this->connect = $connect;
@ -389,15 +393,6 @@ class Mail
return true;
}
public function __destruct()
{
// завершение сеанса smtp
if (is_resource($this->connect)) {
$this->smtpData('QUIT', null);
@fclose($this->connect);
}
}
/**
* Hello SMTP server
*/
@ -425,23 +420,25 @@ class Mail
}
/**
* Отправляет данные на сервер
* Проверяет ответ
* Возвращает код ответа
* @param string $data
* @param mixed $code
* @throws \RuntimeException
* @throws SmtpException
* @return string
*/
protected function smtpData($data, $code)
{
//var_dump($data);
if (is_string($data)) {
@fwrite($this->connect, $data . $this->EOL);
if (is_resource($this->connect) && is_string($data)) {
if (@fwrite($this->connect, $data . $this->EOL) === false) {
throw new SmtpException('Couldn\'t send data to mail server.');
}
}
$response = '';
// while (! isset($get{3}) || $get{3} !== ' ') {
while (is_resource($this->connect) && !feof($this->connect)) {
while (is_resource($this->connect) && ! feof($this->connect)) {
if (($get = @fgets($this->connect, 512)) === false) {
throw new RuntimeException('Couldn\'t get mail server response codes');
throw new SmtpException('Couldn\'t get mail server response codes.');
}
$response .= $get;
if (isset($get{3}) && $get{3} === ' ') {
@ -449,9 +446,8 @@ class Mail
break;
}
}
//var_dump($response);
if ($code !== null && ! in_array($return, (array) $code)) {
throw new RuntimeException('Unable to send email. Response of the SMTP server: "'.$get.'"');
throw new SmtpException('Unable to send email. Response of mail server: "' . $get . '"');
}
return $return;
}
@ -481,4 +477,16 @@ class Mail
? (isset($_SERVER['SERVER_ADDR']) ? '[' . $_SERVER['SERVER_ADDR'] . ']' : '[127.0.0.1]')
: $_SERVER['SERVER_NAME'];
}
/**
* Деструктор
*/
public function __destruct()
{
// завершение сеанса smtp
if (is_resource($this->connect)) {
$this->smtpData('QUIT', null);
@fclose($this->connect);
}
}
}

View file

@ -2,8 +2,9 @@
namespace ForkBB\Models\Pages;
use ForkBB\Models\Validator;
use ForkBB\Core\Exceptions\MailException;
use ForkBB\Models\User;
use ForkBB\Models\Validator;
class Auth extends Page
{
@ -226,14 +227,21 @@ class Auth extends Page
'username' => $this->tmpUser->username,
'link' => $link,
];
$mail = $this->c->Mail->reset()
->setFolder($this->c->DIR_LANG)
->setLanguage($this->tmpUser->language)
->setTo($v->email, $this->tmpUser->username)
->setFrom($this->config['o_webmaster_email'], __('Mailer', $this->config['o_board_title']))
->setTpl('password_reset.tpl', $tplData);
if ($mail->send()) {
try {
$isSent = $this->c->Mail
->reset()
->setFolder($this->c->DIR_LANG)
->setLanguage($this->tmpUser->language)
->setTo($v->email, $this->tmpUser->username)
->setFrom($this->config['o_webmaster_email'], __('Mailer', $this->config['o_board_title']))
->setTpl('password_reset.tpl', $tplData)
->send();
} catch (MailException $e) {
$isSent = false;
}
if ($isSent) {
$this->c->UserMapper->updateUser($this->tmpUser->id, ['activate_string' => $key, 'last_email_sent' => time()]);
return $this->c->Message->message(__('Forget mail', $this->config['o_admin_email']), false, 200);
} else {
@ -283,7 +291,6 @@ class Auth extends Page
} else {
// что-то пошло не так
if (! hash_equals($args['hash'], $this->c->Secury->hash($args['email'] . $args['key']))
|| ! $this->c->Mail->valid($args['email'])
|| ! ($user = $this->c->UserMapper->getUser($args['email'], 'email')) instanceof User
|| empty($user->activateString)
|| $user->activateString{0} !== 'p'
@ -295,6 +302,12 @@ class Auth extends Page
$this->c->Lang->load('auth');
if ($user->isUnverified) {
$this->c->UserMapper->updateUser($user->id, ['group_id' => $this->config['o_default_user_group'], 'email_confirmed' => 1]);
$this->c->{'users_info update'};
$this->iswev['i'][] = __('Account activated');
}
$this->titles = [
__('Change pass'),
];
@ -315,7 +328,6 @@ class Auth extends Page
{
// что-то пошло не так
if (! hash_equals($args['hash'], $this->c->Secury->hash($args['email'] . $args['key']))
|| ! $this->c->Mail->valid($args['email'])
|| ! ($user = $this->c->UserMapper->getUser($args['email'], 'email')) instanceof User
|| empty($user->activateString)
|| $user->activateString{0} !== 'p'

View file

@ -2,8 +2,9 @@
namespace ForkBB\Models\Pages;
use ForkBB\Models\Validator;
use ForkBB\Core\Exceptions\MailException;
use ForkBB\Models\User;
use ForkBB\Models\Validator;
class Register extends Page
{
@ -134,11 +135,9 @@ class Register extends Page
if ($this->config['o_regs_verify'] == '1') {
$groupId = PUN_UNVERIFIED;
$key = 'w' . $this->c->Secury->randomPass(79);
$visit = 0;
} else {
$groupId = $this->config['o_default_user_group'];
$key = null;
$visit = time(); //????
}
$newUserId = $this->c->UserMapper->newUser(new User([
@ -147,8 +146,8 @@ class Register extends Page
'password' => password_hash($v->password, PASSWORD_DEFAULT),
'email' => $v->email,
'email_confirmed' => 0,
'last_visit' => $visit,
'activate_string' => $key,
'u_mark_all_read' => time(),
], $this->c));
// обновление статистики по пользователям
@ -157,7 +156,7 @@ class Register extends Page
}
// уведомление о регистрации
if ($this->config['o_mailing_list'] != '' && $this->config['o_regs_report'] == '1') {
if ($this->config['o_regs_report'] == '1' && $this->config['o_mailing_list'] != '') {
$tplData = [
'fTitle' => $this->config['o_board_title'],
'fRootLink' => $this->c->Router->link('Index'),
@ -165,13 +164,19 @@ class Register extends Page
'username' => $v->username,
'userLink' => $this->c->Router->link('User', ['id' => $newUserId, 'name' => $v->username]),
];
$mail = $this->c->Mail->reset()
->setFolder($this->c->DIR_LANG)
->setLanguage($this->config['o_default_lang'])
->setTo($this->config['o_mailing_list'])
->setFrom($this->config['o_webmaster_email'], __('Mailer', $this->config['o_board_title']))
->setTpl('new_user.tpl', $tplData)
->send();
try {
$this->c->Mail
->reset()
->setFolder($this->c->DIR_LANG)
->setLanguage($this->config['o_default_lang'])
->setTo($this->config['o_mailing_list'])
->setFrom($this->config['o_webmaster_email'], __('Mailer', $this->config['o_board_title']))
->setTpl('new_user.tpl', $tplData)
->send();
} catch (MailException $e) {
//????
}
}
$this->c->Lang->load('register');
@ -187,15 +192,22 @@ class Register extends Page
'username' => $v->username,
'link' => $link,
];
$mail = $this->c->Mail->reset()
->setFolder($this->c->DIR_LANG)
->setLanguage($this->c->user->language)
->setTo($v->email)
->setFrom($this->config['o_webmaster_email'], __('Mailer', $this->config['o_board_title']))
->setTpl('welcome.tpl', $tplData);
try {
$isSent = $this->c->Mail
->reset()
->setFolder($this->c->DIR_LANG)
->setLanguage($this->c->user->language)
->setTo($v->email)
->setFrom($this->config['o_webmaster_email'], __('Mailer', $this->config['o_board_title']))
->setTpl('welcome.tpl', $tplData)
->send();
} catch (MailException $e) {
$isSent = false;
}
// письмо активации аккаунта отправлено
if ($mail->send()) {
if ($isSent) {
return $this->c->Message->message(__('Reg email', $this->config['o_admin_email']), false, 200);
// форма сброса пароля
} else {

View file

@ -2,8 +2,8 @@
namespace ForkBB\Models;
use ForkBB\Models\User;
use ForkBB\Core\Container;
use ForkBB\Models\User;
use RuntimeException;
use InvalidArgumentException;
@ -172,7 +172,7 @@ class UserMapper
*/
public function newUser(User $user)
{
$this->db->query('INSERT INTO '.$this->db->prefix.'users (username, group_id, password, email, email_confirmed, email_setting, timezone, dst, language, style, registered, registration_ip, last_visit, activate_string) VALUES(\''.$this->db->escape($user->username).'\', '.$user->groupId.', \''.$this->db->escape($user->password).'\', \''.$this->db->escape($user->email).'\', '.$user->emailConfirmed.', '.$this->config['o_default_email_setting'].', '.$this->config['o_default_timezone'].' , '.$this->config['o_default_dst'].', \''.$this->db->escape($user->language).'\', \''.$user->style.'\', '.time().', \''.$this->db->escape($this->getIpAddress()).'\', '.$user->lastVisit.', \''.$this->db->escape($user->activateString).'\')') or error('Unable to create user', __FILE__, __LINE__, $this->db->error());
$this->db->query('INSERT INTO '.$this->db->prefix.'users (username, group_id, password, email, email_confirmed, email_setting, timezone, dst, language, style, registered, registration_ip, activate_string, u_mark_all_read) VALUES(\''.$this->db->escape($user->username).'\', '.$user->groupId.', \''.$this->db->escape($user->password).'\', \''.$this->db->escape($user->email).'\', '.$user->emailConfirmed.', '.$this->config['o_default_email_setting'].', '.$this->config['o_default_timezone'].' , '.$this->config['o_default_dst'].', \''.$this->db->escape($user->language).'\', \''.$user->style.'\', '.time().', \''.$this->db->escape($this->getIpAddress()).'\', \''.$this->db->escape($user->activateString).'\', '.$user->uMarkAllRead.')') or error('Unable to create user', __FILE__, __LINE__, $this->db->error());
$new_uid = $this->db->insert_id(); //????
return $new_uid;
}

View file

@ -491,13 +491,12 @@ class Validator
{
if (null === $value) {
return [$value, $type, false];
} elseif ($this->c->Mail->valid($value)) {
return [$value, $type, false];
}
$email = $this->c->Mail->valid($value, true);
if (false === $email) {
return [(string) $value, $type, 'The :alias is not valid email'];
} else {
if (! is_string($value)) {
$value = (string) $value;
}
return [$value, $type, 'The :alias is not valid email'];
return [$email, $type, false];
}
}

View file

@ -74,3 +74,6 @@ msgstr "Passwords must be at least 8 characters long. Passwords are case sensiti
msgid "Pass updated"
msgstr "Your password has been updated. You can now login with your new password."
msgid "Account activated"
msgstr "Account activated."

View file

@ -67,4 +67,4 @@ msgid "Login format"
msgstr "The username must begin with a letter. May contain letters, numbers, spaces, dots, dashes and underscores."
msgid "Error welcom mail"
msgstr "When sending email there was an error. Please use the password reset form or contact the forum administrator at <a href=\"mailto:%1$s\">%1$s</a>."
msgstr "When sending email there was an error. Please use the password reset form for activate your account or contact the forum administrator at <a href=\"mailto:%1$s\">%1$s</a>."

View file

@ -74,3 +74,6 @@ msgstr "Пароль должен состоять минимум из 8 сим
msgid "Pass updated"
msgstr "Ваш пароль изменён. Вы можете войти на форум с новым паролем."
msgid "Account activated"
msgstr "Аккаунт активирован."

View file

@ -67,4 +67,4 @@ msgid "Login format"
msgstr "Имя пользователя должно начинаться с буквы. Может содержать буквы, цифры, пробел, точку, дефис и знак подчеркивания."
msgid "Error welcom mail"
msgstr "При отправке письма возникла ошибка. Пожалуйста, воспользуйтесь формой восстановления пароля или свяжитесь с администратором форума по адресу <a href=\"mailto:%1$s\">%1$s</a>."
msgstr "При отправке письма возникла ошибка. Пожалуйста, воспользуйтесь формой восстановления пароля для активации аккаунта или свяжитесь с администратором форума по адресу <a href=\"mailto:%1$s\">%1$s</a>."