View source
<?php
namespace Drupal\sms_user;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Utility\Token;
use Drupal\sms\Provider\SmsProviderInterface;
use Drupal\sms\Provider\PhoneNumberVerificationInterface;
use Drupal\sms\Message\SmsMessageInterface;
use Drupal\sms\Direction;
use Drupal\user\Entity\User;
use Drupal\Component\Utility\Random;
use Drupal\sms\Entity\SmsMessage;
use Drupal\Core\Entity\EntityConstraintViolationListInterface;
use Symfony\Component\Validator\ConstraintViolationListInterface;
use Drupal\user\UserInterface;
class AccountRegistration implements AccountRegistrationInterface {
protected $configFactory;
protected $token;
protected $smsProvider;
protected $phoneNumberVerificationProvider;
protected $userPhoneNumberSettings;
public function __construct(ConfigFactoryInterface $config_factory, Token $token, SmsProviderInterface $sms_provider, PhoneNumberVerificationInterface $phone_number_verification_provider) {
$this->configFactory = $config_factory;
$this->token = $token;
$this->smsProvider = $sms_provider;
$this->phoneNumberVerificationProvider = $phone_number_verification_provider;
}
public function createAccount(SmsMessageInterface $sms_message) {
$this->userPhoneNumberSettings = $this->phoneNumberVerificationProvider
->getPhoneNumberSettings('user', 'user');
if (!$this->userPhoneNumberSettings) {
return;
}
$sender_number = $sms_message
->getSenderNumber();
if (!empty($sender_number)) {
$entities = $this->phoneNumberVerificationProvider
->getPhoneVerificationByPhoneNumber($sender_number, NULL, 'user');
if (!count($entities)) {
if (!empty($this
->settings('unrecognized_sender.status'))) {
$this
->allUnknownNumbers($sms_message);
}
if (!empty($this
->settings('incoming_pattern.status'))) {
$this
->incomingPatternMessage($sms_message);
}
}
}
}
protected function allUnknownNumbers(SmsMessageInterface $sms_message) {
$user = User::create([
'name' => $this
->generateUniqueUsername(),
]);
$user
->activate();
$sender_number = $sms_message
->getSenderNumber();
$t_args['%sender_phone_number'] = $sender_number;
$phone_field_name = $this->userPhoneNumberSettings
->getFieldName('phone_number');
$user->{$phone_field_name}[] = $sender_number;
$password = user_password();
$user
->setPassword($password);
$validate = $this
->removeAcceptableViolations($user
->validate());
if ($validate
->count() == 0) {
$user
->save();
$t_args['%name'] = $user
->label();
$t_args['%uid'] = $user
->id();
\Drupal::logger('sms_user.account_registration.unrecognized_sender')
->info('Creating new account for %sender_phone_number. Username: %name. User ID: %uid', $t_args);
if (!empty($this
->settings('unrecognized_sender.reply.status'))) {
$message = $this
->settings('unrecognized_sender.reply.message');
$message = str_replace('[user:password]', $password, $message);
$this
->sendReply($sender_number, $user, $message);
}
}
else {
$t_args['@error'] = $this
->buildError($validate);
\Drupal::logger('sms_user.account_registration.unrecognized_sender')
->error('Could not create new account for %sender_phone_number because there was a problem with validation: @error', $t_args);
}
}
protected function incomingPatternMessage(SmsMessageInterface $sms_message) {
if (!empty($this
->settings('incoming_pattern.incoming_messages.0'))) {
$incoming_form = $this
->settings('incoming_pattern.incoming_messages.0');
$incoming_form = str_replace("\r\n", "\n", $incoming_form);
$compiled = $this
->compileFormRegex($incoming_form, '/');
$matches = [];
if (preg_match_all('/^' . $compiled . '$/', $sms_message
->getMessage(), $matches)) {
$contains_email = strpos($incoming_form, '[email]') !== FALSE;
$contains_username = strpos($incoming_form, '[username]') !== FALSE;
$contains_password = strpos($incoming_form, '[password]') !== FALSE;
$username = !empty($matches['username'][0]) && $contains_username ? $matches['username'][0] : $this
->generateUniqueUsername();
$user = User::create([
'name' => $username,
]);
$user
->activate();
$sender_number = $sms_message
->getSenderNumber();
$t_args['%sender_phone_number'] = $sender_number;
$phone_field_name = $this->userPhoneNumberSettings
->getFieldName('phone_number');
$user->{$phone_field_name}[] = $sender_number;
if (!empty($matches['email'][0]) && $contains_email) {
$user
->setEmail($matches['email'][0]);
}
$password = !empty($matches['password'][0]) && $contains_password ? $matches['password'][0] : user_password();
$user
->setPassword($password);
$validate = $this
->removeAcceptableViolations($user
->validate(), $incoming_form);
if ($validate
->count() == 0) {
$user
->save();
$message = $this
->settings('incoming_pattern.reply.message');
$message = str_replace('[user:password]', $password, $message);
\Drupal::logger('sms_user.account_registration.incoming_pattern')
->info('Creating new account for %sender_phone_number. Username: %name. User ID: %uid', $t_args + [
'%uid' => $user
->id(),
'%name' => $user
->label(),
]);
if (!$contains_password && !empty($this
->settings('incoming_pattern.send_activation_email'))) {
_user_mail_notify('register_no_approval_required', $user);
}
}
else {
$message = $this
->settings('incoming_pattern.reply.message_failure');
$error = $this
->buildError($validate);
$message = str_replace('[error]', $error, $message);
\Drupal::logger('sms_user.account_registration.incoming_pattern')
->warning('Could not create new account for %sender_phone_number because there was a problem with validation: @error', $t_args + [
'@error' => $error,
]);
}
if (!empty($this
->settings('incoming_pattern.reply.status'))) {
$this
->sendReply($sender_number, $user, $message);
}
}
}
}
protected function sendReply($sender_number, UserInterface $user, $message) {
$sms_message = SmsMessage::create();
$sms_message
->addRecipient($sender_number)
->setDirection(Direction::OUTGOING);
$data['sms-message'] = $sms_message;
$data['user'] = $user;
$sms_message
->setMessage($this->token
->replace($message, $data));
try {
$this->smsProvider
->queue($sms_message);
} catch (\Exception $e) {
$t_args['%recipient'] = $sender_number;
$t_args['%error'] = $e
->getMessage();
\Drupal::logger('sms_user.account_registration.incoming_pattern')
->warning('Reply message could not be sent to recipient %recipient: %error', $t_args);
}
}
protected function compileFormRegex($form_string, $delimiter) {
$placeholders = [
'username' => '.+',
'email' => '\\S+',
'password' => '.+',
];
$regex_placeholders = [];
foreach (array_keys($placeholders) as $d) {
$regex_placeholders[] = preg_quote('[' . $d . ']');
}
$regex = '/(' . implode('|', $regex_placeholders) . '+)/';
$words = preg_split($regex, $form_string, NULL, PREG_SPLIT_DELIM_CAPTURE);
$placeholder_usage = [];
$compiled = '';
foreach ($words as $word) {
$placeholder = mb_substr($word, 1, -1);
if (isset($placeholders[$placeholder])) {
$placeholder_regex = $placeholders[$placeholder];
if (!in_array($placeholder, $placeholder_usage)) {
$compiled .= '(?<' . $placeholder . '>' . $placeholder_regex . ')';
$placeholder_usage[] = $placeholder;
}
else {
$compiled .= '\\k{' . $placeholder . '}';
}
}
else {
$compiled .= preg_quote($word, $delimiter);
}
}
return $compiled;
}
protected function buildError(ConstraintViolationListInterface $violations) {
$error = '';
foreach ($violations as $violation) {
$error .= (string) $violation
->getMessage() . " ";
}
return strip_tags($error);
}
protected function generateUniqueUsername() {
$random = new Random();
do {
$username = $random
->name(8, TRUE);
} while (user_validate_name($username) || user_load_by_name($username));
return $username;
}
protected function removeAcceptableViolations(EntityConstraintViolationListInterface $violations, $incoming_form = NULL) {
$needs_email = isset($incoming_form) && strpos($incoming_form, '[email]') !== FALSE;
if (!$needs_email) {
foreach ($violations as $offset => $violation) {
if ($violation
->getPropertyPath() == 'mail') {
$violations
->remove($offset);
}
}
}
return $violations;
}
protected function settings($name) {
return $this->configFactory
->get('sms_user.settings')
->get('account_registration.' . $name);
}
}