Feature/refactor imap client (#48)

Complete refactrtoring, goal: more OO, no if-else. Split functions into multiple files.
This commit is contained in:
Aravindo Wingeier 2018-03-03 21:33:29 +01:00 committed by GitHub
parent 8d2f592e44
commit 33dedf0368
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 454 additions and 317 deletions

View file

@ -7,20 +7,22 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
### Changed
- Rewrote to use mostly pure php. Uses Javascript only where its necessary.
- make blocked_usernames configurable
- improved validation of user input
- fixed problem where only one domain is defined
- horizontal spacing for header (from @Spegeli) and style
- fix: restore focus on reload
- Added $config['prefer_plaintext'] = true; Prefer HTML or Text and removed toggle buttons.
- #33 improve button style
- fixed bug where html in plaintext emails are interpreted as html.
- changed footer style
- refactored code into multiple php files.
- Requires PHP version >=7.2
- make all addresses lowercase #30
### Added
- Added multiple domain support (https://github.com/synox/disposable-mailbox/issues/21)
- Blacklist some usernames (https://github.com/synox/disposable-mailbox/issues/27)
- copyToClipboard button (https://github.com/synox/disposable-mailbox/issues/30)
- make all addresses lowercase (https://github.com/synox/disposable-mailbox/issues/30)
- better horizontal spacing for header (from @Spegeli) and style
- improved validation of user input
- Added $config['prefer_plaintext'] = true; Prefer HTML or Text and removed toggle buttons.
- Added multiple domain support #21
- Blacklist some usernames, configurable #27
- copyToClipboard button #30
- mail counter in title
## [0.1.4] - 2017-04-15

View file

@ -14,13 +14,13 @@ A **self-hosted** disposable mailbox service (aka trash mail) :cloud: :envelop
* Automatic refresh. Download and delete your emails.
* Display emails as text or html with sanitization filter.
* Display emails based on one [catch-all imap mailbox](https://www.google.ch/search?q=how+to+setup+catch-all+imap+mailbox).
* Only requires PHP >=5.3.0 and [imap extension](http://php.net/manual/book.imap.php)
* Only requires PHP >=7.2 and [imap extension](http://php.net/manual/book.imap.php)
## Usage
### Requirements
* webserver with php >=5.3.0
* webserver with php >=7.2
* php [imap extension](http://php.net/manual/book.imap.php)
* IMAP account and a domain with [catch-all configuration](https://www.google.ch/search?q=how+to+setup+catch-all+imap+mailbox). (all emails go to one mailbox).

49
src/autolink.php Normal file
View file

@ -0,0 +1,49 @@
<?php
class AutoLinkExtension {
static public function auto_link_text(string $string) {
$string = preg_replace_callback("/
((?<![\"']) # don't look inside quotes
(\b
( # protocol or www.
[a-z]{3,}:\/\/
|
www\.
)
(?: # domain
[a-zA-Z0-9_\-]+
(?:\.[a-zA-Z0-9_\-]+)*
|
localhost
)
(?: # port
\:[0-9]+
)?
(?: # path
\/[a-z0-9:%_|~.-]*
(?:\/[a-z0-9:%_|~.-]*)*
)?
(?: # attributes
\?[a-z0-9:%_|~.=&#;-]*
)?
(?: # anchor
\#[a-z0-9:%_|~.=&#;-]*
)?
)
(?![\"']))
/ix",
function ($match) {
$url = $match[0];
$href = $url;
if (false === strpos($href, 'http')) {
$href = 'http://' . $href;
}
return '<a href="' . $href . '" rel="noreferrer">' . $url . '</a>';
}, $string);
return $string;
}
}

View file

@ -2,11 +2,13 @@
/*
input:
$user - User object
$config - config array
$emails - array of emails
User $user - User object
array $config - config array
array $emails - array of emails
*/
require_once './autolink.php';
// Load HTML Purifier
$purifier_config = HTMLPurifier_Config::createDefault();
$purifier_config->set('HTML.Nofollow', true);
@ -88,7 +90,7 @@ $purifier = new HTMLPurifier($purifier_config);
mailbox:
</small>
<form id="header-form" data-turbolinks-permanent action="?" method="post">
<form id="header-form" data-turbolinks-permanent action="?action=redirect" method="post">
<div class="form-group row">
<div class="col-lg-5 col-md-4 col-sm-6 col-xs-12">
@ -117,7 +119,7 @@ $purifier = new HTMLPurifier($purifier_config);
?>
</div>
<div class="col-lg-3 col-md-4 col-sm-12 col-xs-12 random-column">
<a role="button" href="?random=true"
<a role="button" href="?action=random"
class="btn btn-outline-primary col-sm-12 col-xs-12 random-button">Generate
Random</a>
</div>
@ -177,12 +179,12 @@ $purifier = new HTMLPurifier($purifier_config);
<div class="col-sm-4 text-right">
<a class="btn btn-sm btn-outline-primary " download="true"
role="button"
href="?download_email_id=<?php echo $safe_email_id; ?>&amp;address=<?php echo $user->address ?>">Download
href="?action=download_email&download_email_id=<?php echo $safe_email_id; ?>&amp;address=<?php echo $user->address ?>">Download
</a>
<a class="btn btn-sm btn-outline-danger"
role="button"
href="?delete_email_id=<?php echo $safe_email_id; ?>&amp;address=<?php echo $user->address ?>">Delete
href="?action=delete_email&delete_email_id=<?php echo $safe_email_id; ?>&amp;address=<?php echo $user->address ?>">Delete
</a>
</div>
</div>
@ -256,8 +258,7 @@ $purifier = new HTMLPurifier($purifier_config);
</main>
<footer>
<p>Powered by <a href="https://github.com/synox/disposable-mailbox"><strong>synox/disposable-mailbox</strong></a>
| <a href="https://github.com/synox/disposable-mailbox"><span class="octicon octicon-mark-github"></span>
Fork me on github</a></p>
| <a href="https://github.com/synox/disposable-mailbox">Contribute to the development on Github.</a></p>
</footer>
</body>
</html>

85
src/imap_client.php Normal file
View file

@ -0,0 +1,85 @@
<?php
class ImapClient {
/*PhpImap\Mailbox */
private $mailbox;
public function __construct($imapPath, $login, $password) {
$this->mailbox = new PhpImap\Mailbox($imapPath, $login, $password);
}
/**
* returns all mails for the given $user.
* @param $user User
* @return array
*/
public function get_emails(User $user): array {
// Search for mails with the recipient $address in TO or CC.
$mailsIdsTo = imap_sort($this->mailbox->getImapStream(), SORTARRIVAL, true, SE_UID, 'TO "' . $user->address . '"');
$mailsIdsCc = imap_sort($this->mailbox->getImapStream(), SORTARRIVAL, true, SE_UID, 'CC "' . $user->address . '"');
$mail_ids = array_merge($mailsIdsTo, $mailsIdsCc);
$emails = $this->_load_emails($mail_ids, $user);
return $emails;
}
/**
* deletes emails by id and username. The address must match the recipient in the email.
*
* @param $mailid integer imap email id
* @param $user User
* @internal param the $username matching username
* @return true if success
*/
public function delete_email(string $mailid, User $user): bool {
if ($this->load_one_email($mailid, $user) !== null) {
$this->mailbox->deleteMail($mailid);
$this->mailbox->expungeDeletedMails();
return true;
} else {
return false;
}
}
/**
* Load exactly one email, the $address in TO or CC has to match.
*/
public function load_one_email(int $mailid, User $user): \PhpImap\IncomingMail {
// in order to avoid https://www.owasp.org/index.php/Top_10_2013-A4-Insecure_Direct_Object_References
// the recipient in the email has to match the $address.
$emails = $this->_load_emails(array($mailid), $user);
return count($emails) === 1 ? $emails[0] : null;
}
/**
* Load emails using the $mail_ids, the mails have to match the $address in TO or CC.
* @param $mail_ids array of integer ids
* @param $user User
* @return array of emails
*/
private function _load_emails(array $mail_ids, User $user) {
$emails = array();
foreach ($mail_ids as $id) {
$mail = $this->mailbox->getMail($id);
// imap_search also returns partials matches. The mails have to be filtered again:
if (array_key_exists($user->address, $mail->to) || array_key_exists($user->address, $mail->cc)) {
$emails[] = $mail;
}
}
return $emails;
}
/**
* deletes messages older than X days.
*/
public function delete_old_messages(string $delete_messages_older_than) {
$ids = $this->mailbox->searchMailbox('BEFORE ' . date('d-M-Y', strtotime($delete_messages_older_than)));
foreach ($ids as $id) {
$this->mailbox->deleteMail($id);
}
$this->mailbox->expungeDeletedMails();
}
}

View file

@ -5,298 +5,18 @@ require_once '../../config.php';
# load php dependencies:
require_once './backend-libs/autoload.php';
$mailbox = new PhpImap\Mailbox($config['imap']['url'],
$config['imap']['username'],
$config['imap']['password']);
require_once './user.php';
require_once './imap_client.php';
require_once './pages.php';
require_once './router.php';
$imapClient = new ImapClient($config['imap']['url'], $config['imap']['username'], $config['imap']['password']);
// simple router:
if (isset($_POST['username']) && isset($_POST['domain'])) {
$user = User::parseUsernameAndDomain($_POST['username'], $_POST['domain']);
header("location: ?" . $user->username . "@" . $user->domain);
exit();
} elseif (isset($_GET['download_email_id']) && isset($_GET['address'])) {
$user = User::parseDomain($_GET['address']);
$download_email_id = filter_input(INPUT_GET, 'download_email_id', FILTER_SANITIZE_NUMBER_INT);
if ($user->isInvalid()) {
redirect_to_random($config['domains']);
exit();
}
download_email($download_email_id, $user);
exit();
} elseif (isset($_GET['delete_email_id']) && isset($_GET['address'])) {
$user = User::parseDomain($_GET['address']);
$delete_email_id = filter_input(INPUT_GET, 'delete_email_id', FILTER_SANITIZE_NUMBER_INT);
if ($user->isInvalid()) {
redirect_to_random($config['domains']);
exit();
}
delete_email($delete_email_id, $user);
header("location: ?" . $user->address);
exit();
} elseif (isset($_GET['random'])) {
redirect_to_random($config['domains']);
exit();
} else {
// print emails with html template
$user = User::parseDomain($_SERVER['QUERY_STRING']);
if ($user->isInvalid()) {
redirect_to_random($config['domains']);
exit();
}
$emails = get_emails($user);
require "frontend.template.php";
$router = new Router($_SERVER['REQUEST_METHOD'], $_GET['action'] ?? NULL, $_GET, $_POST, $_SERVER['QUERY_STRING'], $config);
$page = $router->route();
$page->invoke($imapClient);
// delete after each request
delete_old_messages();
}
/**
* print error and stop program.
* @param $status integer http status
* @param $text string error text
*/
function error($status, $text) {
@http_response_code($status);
@print("{\"error\": \"$text\"}");
die();
}
/**
* print all mails for the given $user.
* @param $user User
* @return array
*/
function get_emails($user) {
global $mailbox;
// Search for mails with the recipient $address in TO or CC.
$mailsIdsTo = imap_sort($mailbox->getImapStream(), SORTARRIVAL, true, SE_UID, 'TO "' . $user->address . '"');
$mailsIdsCc = imap_sort($mailbox->getImapStream(), SORTARRIVAL, true, SE_UID, 'CC "' . $user->address . '"');
$mail_ids = array_merge($mailsIdsTo, $mailsIdsCc);
$emails = _load_emails($mail_ids, $user);
return $emails;
}
/**
* deletes emails by id and username. The address must match the recipient in the email.
*
* @param $mailid integer imap email id
* @param $user User
* @internal param the $username matching username
*/
function delete_email($mailid, $user) {
global $mailbox;
if (_load_one_email($mailid, $user) !== null) {
$mailbox->deleteMail($mailid);
$mailbox->expungeDeletedMails();
} else {
error(404, 'delete error: invalid username/mailid combination');
}
}
/**
* download email by id and username. The $address must match the recipient in the email.
*
* @param $mailid integer imap email id
* @param $user User
* @internal param the $username matching username
*/
function download_email($mailid, $user) {
global $mailbox;
if (_load_one_email($mailid, $user) !== null) {
header("Content-Type: message/rfc822; charset=utf-8");
header("Content-Disposition: attachment; filename=\"" . $user->address . "-" . $mailid . ".eml\"");
$headers = imap_fetchheader($mailbox->getImapStream(), $mailid, FT_UID);
$body = imap_body($mailbox->getImapStream(), $mailid, FT_UID);
print $headers . "\n" . $body;
} else {
error(404, 'download error: invalid username/mailid combination');
}
}
/**
* Load exactly one email, the $address in TO or CC has to match.
* @param $mailid integer
* @param $user User
* @return email or null
*/
function _load_one_email($mailid, $user) {
// in order to avoid https://www.owasp.org/index.php/Top_10_2013-A4-Insecure_Direct_Object_References
// the recipient in the email has to match the $address.
$emails = _load_emails(array($mailid), $user);
return count($emails) === 1 ? $emails[0] : null;
}
/**
* Load emails using the $mail_ids, the mails have to match the $address in TO or CC.
* @param $mail_ids array of integer ids
* @param $user User
* @return array of emails
*/
function _load_emails($mail_ids, $user) {
global $mailbox;
$emails = array();
foreach ($mail_ids as $id) {
$mail = $mailbox->getMail($id);
// imap_search also returns partials matches. The mails have to be filtered again:
if (array_key_exists($user->address, $mail->to) || array_key_exists($user->address, $mail->cc)) {
$emails[] = $mail;
}
}
return $emails;
}
/**
* Remove illegal characters from address.
* @param $address
* @return string clean address
*/
function _clean_address($address) {
return strtolower(filter_var($address, FILTER_SANITIZE_EMAIL));
}
/**
* Remove illegal characters from username and remove everything after the @-sign. You may extend it if your server supports them.
* @param $address
* @return string clean username
*/
function _clean_username($address) {
global $config;
$username = strtolower($address);
$username = preg_replace('/@.*$/', "", $username); // remove part after @
$username = preg_replace('/[^A-Za-z0-9_.+-]/', "", $username); // remove special characters
if (in_array($username, $config['blocked_usernames'])) {
// Forbidden name!
return '';
}
return $username;
}
class User {
public $address;
public $username;
public $domain;
public function isInvalid() {
global $config;
if (empty($this->username) || empty($this->domain)) {
return true;
} else if (!in_array($this->domain, $config['domains'])) {
return true;
} else {
return false;
}
}
public static function parseDomain($address) {
$clean_address = _clean_address($address);
$user = new User();
$user->username = _clean_username($clean_address);
$user->domain = _clean_domain($clean_address);
$user->address = $user->username . '@' . $user->domain;
return $user;
}
public static function parseUsernameAndDomain($username, $domain) {
$user = new User();
$user->username = _clean_username($username);
$user->domain = _clean_domain($domain);
$user->address = $user->username . '@' . $user->domain;
return $user;
}
}
function _clean_domain($address) {
$username = strtolower($address);
$username = preg_replace('/^.*@/', "", $username); // remove part before @
return preg_replace('/[^A-Za-z0-9_.+-]/', "", $username); // remove special characters
}
function redirect_to_random($domains) {
$wordLength = rand(3, 8);
$container = new PronounceableWord_DependencyInjectionContainer();
$generator = $container->getGenerator();
$word = $generator->generateWordOfGivenLength($wordLength);
$nr = rand(51, 91);
$name = $word . $nr;
$domain = $domains[array_rand($domains)];
header("location: ?$name@$domain");
}
/**
* deletes messages older than X days.
*/
function delete_old_messages() {
global $mailbox, $config;
$ids = $mailbox->searchMailbox('BEFORE ' . date('d-M-Y', strtotime($config['delete_messages_older_than'])));
foreach ($ids as $id) {
$mailbox->deleteMail($id);
}
$mailbox->expungeDeletedMails();
}
class AutoLinkExtension {
static public function auto_link_text($string) {
$string = preg_replace_callback("/
((?<![\"']) # don't look inside quotes
(\b
( # protocol or www.
[a-z]{3,}:\/\/
|
www\.
)
(?: # domain
[a-zA-Z0-9_\-]+
(?:\.[a-zA-Z0-9_\-]+)*
|
localhost
)
(?: # port
\:[0-9]+
)?
(?: # path
\/[a-z0-9:%_|~.-]*
(?:\/[a-z0-9:%_|~.-]*)*
)?
(?: # attributes
\?[a-z0-9:%_|~.=&#;-]*
)?
(?: # anchor
\#[a-z0-9:%_|~.=&#;-]*
)?
)
(?![\"']))
/ix",
function ($match) {
$url = $match[0];
$href = $url;
if (false === strpos($href, 'http')) {
$href = 'http://' . $href;
}
return '<a href="' . $href . '" rel="noreferrer">' . $url . '</a>';
}, $string);
return $string;
}
}
$imapClient->delete_old_messages($config['delete_messages_older_than']);
?>

157
src/pages.php Normal file
View file

@ -0,0 +1,157 @@
<?php
require_once './imap_client.php';
abstract class Page {
function invoke(ImapClient $imapClient) {
}
function if_invalid_redirect_to_random(User $user, array $config_domains) {
if ($user->isInvalid($config_domains)) {
$this->redirect_to_random($config_domains);
exit();
}
}
function redirect_to_random(array $domains) {
$wordLength = rand(3, 8);
$container = new PronounceableWord_DependencyInjectionContainer();
$generator = $container->getGenerator();
$word = $generator->generateWordOfGivenLength($wordLength);
$nr = rand(51, 91);
$name = $word . $nr;
$domain = $domains[array_rand($domains)];
header("location: ?$name@$domain");
}
/**
* print error and stop program.
* @param $status integer http status
* @param $text string error text
*/
function error($status, $text) {
@http_response_code($status);
die("{\"error\": \"$text\"}");
}
}
class RedirectToAddressPage extends Page {
private $username;
private $domain;
private $config_blocked_usernames;
public function __construct(string $username, string $domain, array $config_blocked_usernames) {
$this->username = $username;
$this->domain = $domain;
$this->config_blocked_usernames = $config_blocked_usernames;
}
function invoke(ImapClient $imapClient) {
$user = User::parseUsernameAndDomain($this->username, $this->domain, $this->config_blocked_usernames);
header("location: ?" . $user->username . "@" . $user->domain);
}
}
class DownloadEmailPage extends Page {
private $email_id;
private $address;
private $config_domains;
private $config_blocked_usernames;
public function __construct(string $email_id, string $address, array $config_domains, array $config_blocked_usernames) {
$this->email_id = $email_id;
$this->address = $address;
$this->config_domains = $config_domains;
$this->config_blocked_usernames = $config_blocked_usernames;
}
function invoke(ImapClient $imapClient) {
$user = User::parseDomain($this->address, $this->config_blocked_usernames);
$this->if_invalid_redirect_to_random($user, $this->config_domains);
$download_email_id = filter_var($this->email_id, FILTER_SANITIZE_NUMBER_INT);
if ($imapClient->load_one_email($download_email_id, $user) !== null) {
header("Content-Type: message/rfc822; charset=utf-8");
header("Content-Disposition: attachment; filename=\"" . $user->address . "-" . $download_email_id . ".eml\"");
$headers = imap_fetchheader($this->mailbox->getImapStream(), $download_email_id, FT_UID);
$body = imap_body($this->mailbox->getImapStream(), $download_email_id, FT_UID);
print $headers . "\n" . $body;
} else {
$this->error(404, 'download error: invalid username/mailid combination');
}
}
}
class DeleteEmailPage extends Page {
private $email_id;
private $address;
private $config_domains;
private $config_blocked_usernames;
public function __construct($email_id, $address, $config_domains, array $config_blocked_usernames) {
$this->email_id = $email_id;
$this->address = $address;
$this->config_domains = $config_domains;
$this->config_blocked_usernames = $config_blocked_usernames;
}
function invoke(ImapClient $imapClient) {
$user = User::parseDomain($this->address, $this->config_blocked_usernames);
$this->if_invalid_redirect_to_random($user, $this->config_domains);
$delete_email_id = filter_var($this->email_id, FILTER_SANITIZE_NUMBER_INT);
if ($imapClient->delete_email($delete_email_id, $user)) {
header("location: ?" . $user->address);
} else {
$this->error(404, 'delete error: invalid username/mailid combination');
}
}
}
class RedirectToRandomAddressPage extends Page {
private $config_domains;
public function __construct($config_domains) {
$this->config_domains = $config_domains;
}
function invoke(ImapClient $imapClient) {
$this->redirect_to_random($this->config_domains);
}
}
class DisplayEmailsPage extends Page {
private $address;
private $config;
public function __construct($address, $config) {
$this->address = $address;
$this->config = $config;
}
function invoke(ImapClient $imapClient) {
// print emails with html template
$user = User::parseDomain($this->address, $this->config['blocked_usernames']);
$this->if_invalid_redirect_to_random($user, $this->config['domains']);
// Set variables for frontend template:
$emails = $imapClient->get_emails($user);
$config = $this->config;
require "frontend.template.php";
}
}
class InvalidRequestPage extends Page {
function invoke(ImapClient $imapClient) {
$this->error(400, "Bad Request");
}
}

50
src/router.php Normal file
View file

@ -0,0 +1,50 @@
<?php
require_once './pages.php';
class Router {
private $method;
private $action;
private $get_vars;
private $post_vars;
private $query_string;
private $config;
public function __construct(string $method, string $action = NULL, array $get_vars, array $post_vars, string $query_string, array $config) {
$this->method = $method;
$this->action = $action;
$this->get_vars = $get_vars;
$this->post_vars = $post_vars;
$this->query_string = $query_string;
$this->config = $config;
}
function route(): Page {
if ($this->action === "redirect"
&& isset($this->post_vars['username'])
&& isset($this->post_vars['domain'])) {
return new RedirectToAddressPage($this->post_vars['username'], $this->post_vars['domain'], $this->config['blocked_usernames']);
} elseif ($this->action === "download_email"
&& isset($this->get_vars['download_email_id'])
&& isset($this->get_vars['address'])) {
return new DownloadEmailPage($this->get_vars['download_email_id'], $this->get_vars['address'], $this->config['domains'], $this->config['blocked_usernames']);
} elseif ($this->action === "delete_email"
&& isset($this->get_vars['delete_email_id'])
&& isset($this->get_vars['address'])) {
return new DeleteEmailPage($this->get_vars['delete_email_id'], $this->get_vars['address'], $this->config['domains'], $this->config['blocked_usernames']);
} elseif ($this->action === 'random') {
return new RedirectToRandomAddressPage($this->config['domains']);
} elseif (!empty($this->query_string)) {
return new DisplayEmailsPage($this->query_string, $this->config);
} else {
return new RedirectToRandomAddressPage($this->config['domains']);
}
}
}

View file

@ -1,12 +1,21 @@
html > body {
/* override bootstrap background */
background: #f9f9f9;
display: flex;
min-height: 100vh;
flex-direction: column;
}
footer p {
margin-top: 50px;
main {
flex: 1;
padding-bottom: 30px;
}
footer {
font-size: 12px;
text-align: center;
padding-top: 1em;
}
.card-block {
@ -28,11 +37,6 @@ header {
margin-bottom: 15px;
}
#openRandomButton {
margin-top: 6px;
}
.email-table > .email {
border-top: 5px solid #7C96AB;

69
src/user.php Normal file
View file

@ -0,0 +1,69 @@
<?php
class User {
public $address;
public $username;
public $domain;
public function isInvalid(array $config_domains): bool {
if (empty($this->username) || empty($this->domain)) {
return true;
} else if (!in_array($this->domain, $config_domains)) {
return true;
} else {
return false;
}
}
public static function parseDomain(string $address, array $config_blocked_usernames): User {
$clean_address = User::_clean_address($address);
$user = new User();
$user->username = User::_clean_username($clean_address, $config_blocked_usernames);
$user->domain = User::_clean_domain($clean_address);
$user->address = $user->username . '@' . $user->domain;
return $user;
}
public static function parseUsernameAndDomain(string $username, string $domain, $config_blocked_usernames): User {
$user = new User();
$user->username = User::_clean_username($username, $config_blocked_usernames);
$user->domain = User::_clean_domain($domain);
$user->address = $user->username . '@' . $user->domain;
return $user;
}
/**
* Remove illegal characters from address.
* @return string clean address
*/
private static function _clean_address(string $address) {
return strtolower(filter_var($address, FILTER_SANITIZE_EMAIL));
}
/**
* Remove illegal characters from username and remove everything after the @-sign. You may extend it if your server supports them.
*
* @return string clean username
*/
private static function _clean_username(string $address, array $config_blocked_usernames) {
$username = strtolower($address);
$username = preg_replace('/@.*$/', "", $username); // remove part after @
$username = preg_replace('/[^A-Za-z0-9_.+-]/', "", $username); // remove special characters
if (in_array($username, $config_blocked_usernames)) {
// Forbidden name!
return '';
}
return $username;
}
private static function _clean_domain(string $address) {
$username = strtolower($address);
$username = preg_replace('/^.*@/', "", $username); // remove part before @
return preg_replace('/[^A-Za-z0-9_.+-]/', "", $username); // remove special characters
}
}