Changed:
- make blocked_usernames configurable - improved validation of user input - fixed problem where only one domain is defined
This commit is contained in:
parent
d4f3a28eed
commit
94317b12cc
|
@ -25,4 +25,9 @@ $config['imap']['password'] = "mypassword";
|
||||||
$config['domains'] = array('mydomain.com', 'example.com');
|
$config['domains'] = array('mydomain.com', 'example.com');
|
||||||
|
|
||||||
// When to delete old messages?
|
// When to delete old messages?
|
||||||
$config['delete_messages_older_than'] = '30 days ago';
|
$config['delete_messages_older_than'] = '30 days ago';
|
||||||
|
|
||||||
|
|
||||||
|
// Mails to those usernames can not be accessed:
|
||||||
|
$config['blocked_usernames'] = array('root', 'admin', 'administrator', 'hostmaster', 'postmaster', 'webmaster');
|
||||||
|
|
||||||
|
|
|
@ -2,14 +2,9 @@
|
||||||
/*
|
/*
|
||||||
input:
|
input:
|
||||||
|
|
||||||
$address - username and domain
|
$user - User object
|
||||||
$username - username
|
|
||||||
$domain - domain
|
|
||||||
|
|
||||||
$config - config array
|
$config - config array
|
||||||
|
|
||||||
$emails - array of emails
|
$emails - array of emails
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Load HTML Purifier
|
// Load HTML Purifier
|
||||||
|
@ -22,7 +17,7 @@ $purifier = new HTMLPurifier($purifier_config);
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title><?php echo $address ?></title>
|
<title><?php echo $user->address ?></title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.gif">
|
<link rel="icon" type="image/x-icon" href="favicon.gif">
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
@ -112,19 +107,21 @@ $purifier = new HTMLPurifier($purifier_config);
|
||||||
|
|
||||||
<div class="col-lg-5 col-md-4 col-sm-6 col-xs-12">
|
<div class="col-lg-5 col-md-4 col-sm-6 col-xs-12">
|
||||||
<input id="username" class="form-control form-control-lg" name="username" title="username"
|
<input id="username" class="form-control form-control-lg" name="username" title="username"
|
||||||
value="<?php echo $username ?>">
|
value="<?php echo $user->username ?>">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-4 col-md-4 col-sm-6 col-xs-12">
|
<div class="col-lg-4 col-md-4 col-sm-6 col-xs-12">
|
||||||
<?php
|
<?php
|
||||||
if (count($config['domains']) == 1) {
|
if (count($config['domains']) == 1) {
|
||||||
print "<h3>@" . $config['domains'][0] . "</h3>";
|
$domain = $config['domains'][0];
|
||||||
|
print "<h3>@$domain</h3>";
|
||||||
|
print "<input type='hidden' name='domain' value='$domain'/>";
|
||||||
} else {
|
} else {
|
||||||
?>
|
?>
|
||||||
<select id="domain" class="form-control form-control-lg" name="domain" title="domain"
|
<select id="domain" class="form-control form-control-lg" name="domain" title="domain"
|
||||||
onchange="this.form.submit()">
|
onchange="this.form.submit()">
|
||||||
<?php
|
<?php
|
||||||
foreach ($config['domains'] as $aDomain) {
|
foreach ($config['domains'] as $aDomain) {
|
||||||
$selected = $aDomain === $domain ? ' selected ' : '';
|
$selected = $aDomain === $user->domain ? ' selected ' : '';
|
||||||
print "<option value='$aDomain' $selected>@$aDomain</option>";
|
print "<option value='$aDomain' $selected>@$aDomain</option>";
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
@ -153,10 +150,10 @@ $purifier = new HTMLPurifier($purifier_config);
|
||||||
<div class="card waiting-screen">
|
<div class="card waiting-screen">
|
||||||
<div class="card-block">
|
<div class="card-block">
|
||||||
<p class="lead">Your mailbox <strong
|
<p class="lead">Your mailbox <strong
|
||||||
><?php echo $address ?></strong> is ready. </p>
|
><?php echo $user->address ?></strong> is ready. </p>
|
||||||
<p>
|
<p>
|
||||||
<button class="btn btn-outline-primary"
|
<button class="btn btn-outline-primary"
|
||||||
onClick="copyToClipboard('<?php echo $address ?>');">
|
onClick="copyToClipboard('<?php echo $user->address ?>');">
|
||||||
Copy email address
|
Copy email address
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
|
@ -206,12 +203,12 @@ $purifier = new HTMLPurifier($purifier_config);
|
||||||
|
|
||||||
<a class="btn btn-sm btn-outline-primary " download="true"
|
<a class="btn btn-sm btn-outline-primary " download="true"
|
||||||
role="button"
|
role="button"
|
||||||
href="?download_email_id=<?php echo $safe_email_id; ?>&address=<?php echo $address ?>">Download
|
href="?download_email_id=<?php echo $safe_email_id; ?>&address=<?php echo $user->address ?>">Download
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a class="btn btn-sm btn-outline-danger"
|
<a class="btn btn-sm btn-outline-danger"
|
||||||
role="button"
|
role="button"
|
||||||
href="?delete_email_id=<?php echo $safe_email_id; ?>&address=<?php echo $address ?>">Delete
|
href="?delete_email_id=<?php echo $safe_email_id; ?>&address=<?php echo $user->address ?>">Delete
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
123
src/index.php
123
src/index.php
|
@ -12,41 +12,42 @@ $mailbox = new PhpImap\Mailbox($config['imap']['url'],
|
||||||
|
|
||||||
// simple router:
|
// simple router:
|
||||||
if (isset($_POST['username']) && isset($_POST['domain'])) {
|
if (isset($_POST['username']) && isset($_POST['domain'])) {
|
||||||
$username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_EMAIL);
|
$user = User::parseUsernameAndDomain($_POST['username'], $_POST['domain']);
|
||||||
$domain = filter_input(INPUT_POST, 'domain', FILTER_SANITIZE_EMAIL);
|
header("location: ?" . $user->username . "@" . $user->domain);
|
||||||
header("location: ?$username@$domain");
|
|
||||||
exit();
|
exit();
|
||||||
} elseif (isset($_GET['download_email_id']) && isset($_GET['address'])) {
|
} elseif (isset($_GET['download_email_id']) && isset($_GET['address'])) {
|
||||||
$address = strtolower(filter_input(INPUT_GET, 'address', FILTER_SANITIZE_EMAIL));
|
$user = User::parseDomain($_GET['address']);
|
||||||
$download_email_id = filter_input(INPUT_GET, 'download_email_id', FILTER_SANITIZE_NUMBER_INT);
|
$download_email_id = filter_input(INPUT_GET, 'download_email_id', FILTER_SANITIZE_NUMBER_INT);
|
||||||
download_email($download_email_id, $address);
|
if ($user->isInvalid()) {
|
||||||
|
redirect_to_random($config['domains']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
download_email($download_email_id, $user);
|
||||||
exit();
|
exit();
|
||||||
} elseif (isset($_GET['delete_email_id']) && isset($_GET['address'])) {
|
} elseif (isset($_GET['delete_email_id']) && isset($_GET['address'])) {
|
||||||
$address = strtolower(filter_input(INPUT_GET, 'address', FILTER_SANITIZE_EMAIL));
|
$user = User::parseDomain($_GET['address']);
|
||||||
$delete_email_id = filter_input(INPUT_GET, 'delete_email_id', FILTER_SANITIZE_NUMBER_INT);
|
$delete_email_id = filter_input(INPUT_GET, 'delete_email_id', FILTER_SANITIZE_NUMBER_INT);
|
||||||
delete_email($delete_email_id, $address);
|
if ($user->isInvalid()) {
|
||||||
header("location: ?$address");
|
redirect_to_random($config['domains']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
delete_email($delete_email_id, $user);
|
||||||
|
header("location: ?" . $user->address);
|
||||||
exit();
|
exit();
|
||||||
} elseif (isset($_GET['random'])) {
|
} elseif (isset($_GET['random'])) {
|
||||||
redirect_to_random($config['domains']);
|
redirect_to_random($config['domains']);
|
||||||
exit();
|
exit();
|
||||||
} else {
|
} else {
|
||||||
// print emails with html template
|
// print emails with html template
|
||||||
$address = strtolower(filter_var($_SERVER['QUERY_STRING'], FILTER_SANITIZE_EMAIL));
|
$user = User::parseDomain($_SERVER['QUERY_STRING']);
|
||||||
$username = _clean_username($address);
|
if ($user->isInvalid()) {
|
||||||
$domain = _clean_domain($address);
|
|
||||||
if (empty($username) || empty($domain)) {
|
|
||||||
redirect_to_random($config['domains']);
|
redirect_to_random($config['domains']);
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
if (!in_array($domain, $config['domains'])) {
|
$emails = get_emails($user);
|
||||||
redirect_to_random($config['domains']);
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
$emails = get_emails($address);
|
|
||||||
require "frontend.template.php";
|
require "frontend.template.php";
|
||||||
|
|
||||||
// run on every request
|
// delete after each request
|
||||||
delete_old_messages();
|
delete_old_messages();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,33 +65,33 @@ function error($status, $text) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* print all mails for the given $user.
|
* print all mails for the given $user.
|
||||||
* @param $address string email address
|
* @param $user User
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
function get_emails($address) {
|
function get_emails($user) {
|
||||||
global $mailbox;
|
global $mailbox;
|
||||||
|
|
||||||
// Search for mails with the recipient $address in TO or CC.
|
// Search for mails with the recipient $address in TO or CC.
|
||||||
$mailsIdsTo = imap_sort($mailbox->getImapStream(), SORTARRIVAL, true, SE_UID, 'TO "' . $address . '"');
|
$mailsIdsTo = imap_sort($mailbox->getImapStream(), SORTARRIVAL, true, SE_UID, 'TO "' . $user->address . '"');
|
||||||
$mailsIdsCc = imap_sort($mailbox->getImapStream(), SORTARRIVAL, true, SE_UID, 'CC "' . $address . '"');
|
$mailsIdsCc = imap_sort($mailbox->getImapStream(), SORTARRIVAL, true, SE_UID, 'CC "' . $user->address . '"');
|
||||||
$mail_ids = array_merge($mailsIdsTo, $mailsIdsCc);
|
$mail_ids = array_merge($mailsIdsTo, $mailsIdsCc);
|
||||||
|
|
||||||
$emails = _load_emails($mail_ids, $address);
|
$emails = _load_emails($mail_ids, $user);
|
||||||
return $emails;
|
return $emails;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* deletes emails by id and username. The $address must match the recipient in the email.
|
* deletes emails by id and username. The address must match the recipient in the email.
|
||||||
*
|
*
|
||||||
* @param $mailid integer imap email id
|
* @param $mailid integer imap email id
|
||||||
* @param $address string email address
|
* @param $user User
|
||||||
* @internal param the $username matching username
|
* @internal param the $username matching username
|
||||||
*/
|
*/
|
||||||
function delete_email($mailid, $address) {
|
function delete_email($mailid, $user) {
|
||||||
global $mailbox;
|
global $mailbox;
|
||||||
|
|
||||||
if (_load_one_email($mailid, $address) !== null) {
|
if (_load_one_email($mailid, $user) !== null) {
|
||||||
$mailbox->deleteMail($mailid);
|
$mailbox->deleteMail($mailid);
|
||||||
$mailbox->expungeDeletedMails();
|
$mailbox->expungeDeletedMails();
|
||||||
} else {
|
} else {
|
||||||
|
@ -102,16 +103,16 @@ function delete_email($mailid, $address) {
|
||||||
* download email by id and username. The $address must match the recipient in the email.
|
* download email by id and username. The $address must match the recipient in the email.
|
||||||
*
|
*
|
||||||
* @param $mailid integer imap email id
|
* @param $mailid integer imap email id
|
||||||
* @param $address string email address
|
* @param $user User
|
||||||
* @internal param the $username matching username
|
* @internal param the $username matching username
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function download_email($mailid, $address) {
|
function download_email($mailid, $user) {
|
||||||
global $mailbox;
|
global $mailbox;
|
||||||
|
|
||||||
if (_load_one_email($mailid, $address) !== null) {
|
if (_load_one_email($mailid, $user) !== null) {
|
||||||
header("Content-Type: message/rfc822; charset=utf-8");
|
header("Content-Type: message/rfc822; charset=utf-8");
|
||||||
header("Content-Disposition: attachment; filename=\"$address-$mailid.eml\"");
|
header("Content-Disposition: attachment; filename=\"" . $user->address . "-" . $mailid . ".eml\"");
|
||||||
|
|
||||||
$headers = imap_fetchheader($mailbox->getImapStream(), $mailid, FT_UID);
|
$headers = imap_fetchheader($mailbox->getImapStream(), $mailid, FT_UID);
|
||||||
$body = imap_body($mailbox->getImapStream(), $mailid, FT_UID);
|
$body = imap_body($mailbox->getImapStream(), $mailid, FT_UID);
|
||||||
|
@ -124,47 +125,58 @@ function download_email($mailid, $address) {
|
||||||
/**
|
/**
|
||||||
* Load exactly one email, the $address in TO or CC has to match.
|
* Load exactly one email, the $address in TO or CC has to match.
|
||||||
* @param $mailid integer
|
* @param $mailid integer
|
||||||
* @param $address String address
|
* @param $user User
|
||||||
* @return email or null
|
* @return email or null
|
||||||
*/
|
*/
|
||||||
function _load_one_email($mailid, $address) {
|
function _load_one_email($mailid, $user) {
|
||||||
// in order to avoid https://www.owasp.org/index.php/Top_10_2013-A4-Insecure_Direct_Object_References
|
// 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.
|
// the recipient in the email has to match the $address.
|
||||||
$emails = _load_emails(array($mailid), $address);
|
$emails = _load_emails(array($mailid), $user);
|
||||||
return count($emails) === 1 ? $emails[0] : null;
|
return count($emails) === 1 ? $emails[0] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load emails using the $mail_ids, the mails have to match the $address in TO or CC.
|
* 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 $mail_ids array of integer ids
|
||||||
* @param $address String address
|
* @param $user User
|
||||||
* @return array of emails
|
* @return array of emails
|
||||||
*/
|
*/
|
||||||
function _load_emails($mail_ids, $address) {
|
function _load_emails($mail_ids, $user) {
|
||||||
global $mailbox;
|
global $mailbox;
|
||||||
|
|
||||||
$emails = array();
|
$emails = array();
|
||||||
foreach ($mail_ids as $id) {
|
foreach ($mail_ids as $id) {
|
||||||
$mail = $mailbox->getMail($id);
|
$mail = $mailbox->getMail($id);
|
||||||
// imap_search also returns partials matches. The mails have to be filtered again:
|
// imap_search also returns partials matches. The mails have to be filtered again:
|
||||||
if (array_key_exists($address, $mail->to) || array_key_exists($address, $mail->cc)) {
|
if (array_key_exists($user->address, $mail->to) || array_key_exists($user->address, $mail->cc)) {
|
||||||
$emails[] = $mail;
|
$emails[] = $mail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $emails;
|
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.
|
* Remove illegal characters from username and remove everything after the @-sign. You may extend it if your server supports them.
|
||||||
* @param $address
|
* @param $address
|
||||||
* @return string clean username
|
* @return string clean username
|
||||||
*/
|
*/
|
||||||
function _clean_username($address) {
|
function _clean_username($address) {
|
||||||
|
global $config;
|
||||||
$username = strtolower($address);
|
$username = strtolower($address);
|
||||||
$username = preg_replace('/@.*$/', "", $username); // remove part after @
|
$username = preg_replace('/@.*$/', "", $username); // remove part after @
|
||||||
$username = preg_replace('/[^A-Za-z0-9_.+-]/', "", $username); // remove special characters
|
$username = preg_replace('/[^A-Za-z0-9_.+-]/', "", $username); // remove special characters
|
||||||
|
|
||||||
if (in_array($username, array('root', 'admin', 'administrator', 'hostmaster', 'postmaster', 'webmaster'))) {
|
if (in_array($username, $config['blocked_usernames'])) {
|
||||||
// Forbidden name!
|
// Forbidden name!
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -172,6 +184,41 @@ function _clean_username($address) {
|
||||||
return $username;
|
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) {
|
function _clean_domain($address) {
|
||||||
$username = strtolower($address);
|
$username = strtolower($address);
|
||||||
$username = preg_replace('/^.*@/', "", $username); // remove part before @
|
$username = preg_replace('/^.*@/', "", $username); // remove part before @
|
||||||
|
|
Loading…
Reference in New Issue
Block a user