moved html to template file
This commit is contained in:
parent
f49c16d62e
commit
ea6b9b7039
235
src/backend.php
235
src/backend.php
|
@ -1,235 +0,0 @@
|
||||||
<?php
|
|
||||||
# set the new path of config.php (must be in a safe location outside the `public_html`)
|
|
||||||
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']);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 $address string email address
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
function get_emails($address) {
|
|
||||||
global $mailbox;
|
|
||||||
|
|
||||||
// Search for mails with the recipient $address in TO or CC.
|
|
||||||
$mailsIdsTo = imap_sort($mailbox->getImapStream(), SORTARRIVAL, true, SE_UID, 'TO "' . $address . '"');
|
|
||||||
$mailsIdsCc = imap_sort($mailbox->getImapStream(), SORTARRIVAL, true, SE_UID, 'CC "' . $address . '"');
|
|
||||||
$mail_ids = array_merge($mailsIdsTo, $mailsIdsCc);
|
|
||||||
|
|
||||||
$emails = _load_emails($mail_ids, $address);
|
|
||||||
return $emails;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* deletes emails by id and username. The $address must match the recipient in the email.
|
|
||||||
*
|
|
||||||
* @param $mailid integer imap email id
|
|
||||||
* @param $address string email address
|
|
||||||
* @internal param the $username matching username
|
|
||||||
*/
|
|
||||||
function delete_email($mailid, $address) {
|
|
||||||
global $mailbox;
|
|
||||||
|
|
||||||
if (_load_one_email($mailid, $address) !== 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 $address string email address
|
|
||||||
* @internal param the $username matching username
|
|
||||||
*/
|
|
||||||
|
|
||||||
function download_email($mailid, $address) {
|
|
||||||
global $mailbox;
|
|
||||||
|
|
||||||
if (_load_one_email($mailid, $address) !== null) {
|
|
||||||
header("Content-Type: message/rfc822; charset=utf-8");
|
|
||||||
header("Content-Disposition: attachment; filename=\"$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 $address String address
|
|
||||||
* @return email or null
|
|
||||||
*/
|
|
||||||
function _load_one_email($mailid, $address) {
|
|
||||||
// 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), $address);
|
|
||||||
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 $address String address
|
|
||||||
* @return array of emails
|
|
||||||
*/
|
|
||||||
function _load_emails($mail_ids, $address) {
|
|
||||||
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($address, $mail->to) || array_key_exists($address, $mail->cc)) {
|
|
||||||
$emails[] = $mail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $emails;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove illegal characters from username and remove everything after the @-sign. You may extend it if your server supports them.
|
|
||||||
* @param $username
|
|
||||||
* @return string clean username
|
|
||||||
*/
|
|
||||||
function _clean_username($username) {
|
|
||||||
$username = strtolower($username);
|
|
||||||
$username = preg_replace('/@.*$/', "", $username); // remove part after @
|
|
||||||
$username = preg_replace('/[^A-Za-z0-9_.+-]/', "", $username); // remove special characters
|
|
||||||
|
|
||||||
if (in_array($username, array('root', 'admin', 'administrator', 'hostmaster', 'postmaster', 'webmaster'))) {
|
|
||||||
// Forbidden name!
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $username;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _clean_domain($username) {
|
|
||||||
$username = strtolower($username);
|
|
||||||
$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);
|
|
||||||
|
|
||||||
|
|
||||||
$string = AutoLinkExtension::unescape($string);
|
|
||||||
|
|
||||||
return $string;
|
|
||||||
} # filter()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* unescape()
|
|
||||||
*
|
|
||||||
* @param string $text
|
|
||||||
* @return string $text
|
|
||||||
**/
|
|
||||||
static function unescape($text) {
|
|
||||||
global $escape_autolink_uri;
|
|
||||||
|
|
||||||
if (!$escape_autolink_uri)
|
|
||||||
return $text;
|
|
||||||
|
|
||||||
$unescape = array_reverse($escape_autolink_uri);
|
|
||||||
|
|
||||||
return str_replace(array_keys($unescape), array_values($unescape), $text);
|
|
||||||
} # unescape()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// run on every request
|
|
||||||
delete_old_messages();
|
|
271
src/frontend.template.php
Normal file
271
src/frontend.template.php
Normal file
|
@ -0,0 +1,271 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
input:
|
||||||
|
|
||||||
|
$address - username and domain
|
||||||
|
$username - username
|
||||||
|
$userDomain - domain
|
||||||
|
|
||||||
|
$config - config array
|
||||||
|
|
||||||
|
$emails - array of emails
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Load HTML Purifier
|
||||||
|
$purifier_config = HTMLPurifier_Config::createDefault();
|
||||||
|
$purifier_config->set('HTML.Nofollow', true);
|
||||||
|
$purifier_config->set('HTML.ForbiddenElements', array("img"));
|
||||||
|
$purifier = new HTMLPurifier($purifier_config);
|
||||||
|
|
||||||
|
?>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title><?php echo $address ?></title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<link rel="icon" type="image/x-icon" href="favicon.gif">
|
||||||
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/css/bootstrap.min.css"
|
||||||
|
integrity="sha384-AysaV+vQoT3kOAXZkl02PThvDr8HYKPZhNT5h/CXfBThSRXQ6jW5DO2ekP5ViFdi"
|
||||||
|
crossorigin="anonymous">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<link rel="stylesheet" href="spinner.css">
|
||||||
|
<script src="turbolinks.js"></script>
|
||||||
|
<meta name="turbolinks-cache-control" content="no-preview">
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// https://stackoverflow.com/a/44353026
|
||||||
|
var reloadWithTurbolinks = (function () {
|
||||||
|
var scrollPosition;
|
||||||
|
|
||||||
|
function reload() {
|
||||||
|
Turbolinks.visit(window.location.toString(), {action: 'replace'})
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('turbolinks:before-render', function () {
|
||||||
|
scrollPosition = [window.scrollX, window.scrollY];
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('turbolinks:load', function () {
|
||||||
|
if (scrollPosition) {
|
||||||
|
window.scrollTo.apply(window, scrollPosition);
|
||||||
|
scrollPosition = null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return reload;
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
setInterval(function () {
|
||||||
|
reloadWithTurbolinks();
|
||||||
|
}, 15000);
|
||||||
|
|
||||||
|
|
||||||
|
function showHtml(id) {
|
||||||
|
document.getElementById('email-' + id + '-html').style.display = 'block';
|
||||||
|
document.getElementById('email-' + id + '-plain').style.display = 'none';
|
||||||
|
document.getElementById('show-html-button-' + id).style.display = 'none';
|
||||||
|
document.getElementById('show-plain-button-' + id).style.display = 'inline-block';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showPlain(id) {
|
||||||
|
document.getElementById('email-' + id + '-html').style.display = 'none';
|
||||||
|
document.getElementById('email-' + id + '-plain').style.display = 'block';
|
||||||
|
document.getElementById('show-html-button-' + id).style.display = 'inline-block';
|
||||||
|
document.getElementById('show-plain-button-' + id).style.display = 'none';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
|
||||||
|
<body data-turbolinks="false">
|
||||||
|
|
||||||
|
<header data-turbolinks-permanent id="header">
|
||||||
|
<div class="container">
|
||||||
|
<small class="form-text text-muted">
|
||||||
|
change username:
|
||||||
|
</small>
|
||||||
|
|
||||||
|
<form action="?" method="get">
|
||||||
|
<div class="form-group row">
|
||||||
|
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input id="username" class="form-control form-control-lg" name="username" title="username"
|
||||||
|
value="<?php echo $username ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<?php
|
||||||
|
if (count($config['domains']) == 1) {
|
||||||
|
print "<h3>@" . $config['domains'][0] . "</h3>";
|
||||||
|
} else {
|
||||||
|
?>
|
||||||
|
<select id="domain" class="form-control form-control-lg" name="domain" title="domain"
|
||||||
|
onchange="this.form.submit()">
|
||||||
|
<?php
|
||||||
|
foreach ($config['domains'] as $domain) {
|
||||||
|
$selected = $domain === $userDomain ? ' selected ' : '';
|
||||||
|
print "<option value='$domain' $selected>@$domain</option>";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-3 random-column">
|
||||||
|
<span>or </span>
|
||||||
|
<a role="button" href="?random=true" class="btn btn-outline-primary">generate random
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="container min-height">
|
||||||
|
|
||||||
|
<?php
|
||||||
|
if (empty($emails)) {
|
||||||
|
?>
|
||||||
|
<div>
|
||||||
|
<div class="card waiting-screen">
|
||||||
|
<div class="card-block">
|
||||||
|
<p class="lead">Your mailbox <strong
|
||||||
|
><?php echo $address ?></strong> is ready. </p>
|
||||||
|
<p>Emails will appear here automatically. They will be deleted after 30 days.</p>
|
||||||
|
<div class="spinner">
|
||||||
|
<div class="rect1"></div>
|
||||||
|
<div class="rect2"></div>
|
||||||
|
<div class="rect3"></div>
|
||||||
|
<div class="rect4"></div>
|
||||||
|
<div class="rect5"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
} else {
|
||||||
|
|
||||||
|
foreach ($emails as $email) {
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="email-table">
|
||||||
|
|
||||||
|
<div class="card email">
|
||||||
|
<div class="card-block header-shadow sticky-header">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<h3 class="card-title">
|
||||||
|
<?php echo filter_var($email->subject, FILTER_SANITIZE_SPECIAL_CHARS); ?>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4 text-right">
|
||||||
|
<form class="form-inline float-xs-right">
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-outline-info btn-sm"
|
||||||
|
style="display: inline-block"
|
||||||
|
id="show-html-button-<?php echo filter_var($email->id, FILTER_VALIDATE_INT); ?>"
|
||||||
|
onclick="showHtml(<?php echo filter_var($email->id, FILTER_VALIDATE_INT); ?>)">
|
||||||
|
show html
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-outline-info btn-sm"
|
||||||
|
style="display: none"
|
||||||
|
id="show-plain-button-<?php echo filter_var($email->id, FILTER_VALIDATE_INT); ?>"
|
||||||
|
onclick="showPlain(<?php echo filter_var($email->id, FILTER_VALIDATE_INT); ?>)">
|
||||||
|
show text
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<a class="btn btn-sm btn-outline-primary " download="true"
|
||||||
|
role="button"
|
||||||
|
href="?download_email_id=<?php echo filter_var($email->id, FILTER_VALIDATE_INT); ?>&address=<?php echo $address ?>">Download
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="btn btn-sm btn-outline-danger"
|
||||||
|
role="button"
|
||||||
|
href="?delete_email_id=<?php echo filter_var($email->id, FILTER_VALIDATE_INT); ?>&address=<?php echo $address ?>">Delete
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<h6 class="card-subtitle mt-1 text-muted">
|
||||||
|
<?php
|
||||||
|
echo filter_var($email->fromName, FILTER_SANITIZE_SPECIAL_CHARS);
|
||||||
|
echo ' <';
|
||||||
|
echo filter_var($email->fromAddress, FILTER_SANITIZE_SPECIAL_CHARS);
|
||||||
|
echo '>';
|
||||||
|
?>
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<h6 class="card-subtitle mt-1 text-muted"
|
||||||
|
style="text-align: right">
|
||||||
|
<?php echo filter_var($email->date, FILTER_SANITIZE_SPECIAL_CHARS); ?>
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-block">
|
||||||
|
<h6 class="card-subtitle text-muted">
|
||||||
|
To: <?php echo filter_var($email->toString, FILTER_SANITIZE_SPECIAL_CHARS); ?></h6>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
foreach ($email->cc as $cc) {
|
||||||
|
print "<h6 class='card-subtitle text-muted'>CC: " . filter_var($cc, FILTER_SANITIZE_SPECIAL_CHARS) . "</h6>";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="mt-2 card-text">
|
||||||
|
<!-- show plaintext or html -->
|
||||||
|
<div id="email-<?php echo filter_var($email->id, FILTER_VALIDATE_INT); ?>-plain"
|
||||||
|
style="display: block;">
|
||||||
|
<?php $text = filter_var($email->textPlain, FILTER_SANITIZE_SPECIAL_CHARS);
|
||||||
|
// Keep newlines
|
||||||
|
$text = str_replace(' ', '<br />', $text);
|
||||||
|
echo \AutoLinkExtension::auto_link_text($text)
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<div id="email-<?php echo filter_var($email->id, FILTER_VALIDATE_INT); ?>-html"
|
||||||
|
style="display: none;">
|
||||||
|
<?php
|
||||||
|
$clean_html = $purifier->purify($email->textHtml);
|
||||||
|
echo $clean_html;
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</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>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
491
src/index.php
491
src/index.php
|
@ -1,10 +1,14 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('backend.php');
|
# set the new path of config.php (must be in a safe location outside the `public_html`)
|
||||||
|
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']);
|
||||||
|
|
||||||
$purifier_config = HTMLPurifier_Config::createDefault();
|
|
||||||
$purifier_config->set('HTML.Nofollow', true);
|
|
||||||
$purifier_config->set('HTML.ForbiddenElements', array("img"));
|
|
||||||
$purifier = new HTMLPurifier($purifier_config);
|
|
||||||
|
|
||||||
// simple router:
|
// simple router:
|
||||||
if (isset($_GET['username']) && isset($_GET['domain'])) {
|
if (isset($_GET['username']) && isset($_GET['domain'])) {
|
||||||
|
@ -35,257 +39,232 @@ if (isset($_GET['username']) && isset($_GET['domain'])) {
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
$emails = get_emails($address);
|
$emails = get_emails($address);
|
||||||
?>
|
require "frontend.template.php";
|
||||||
|
|
||||||
<html lang="en">
|
// run on every request
|
||||||
<head>
|
delete_old_messages();
|
||||||
<meta charset="utf-8">
|
|
||||||
<title><?php echo $address ?></title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.gif">
|
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/css/bootstrap.min.css"
|
|
||||||
integrity="sha384-AysaV+vQoT3kOAXZkl02PThvDr8HYKPZhNT5h/CXfBThSRXQ6jW5DO2ekP5ViFdi"
|
|
||||||
crossorigin="anonymous">
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<link rel="stylesheet" href="spinner.css">
|
|
||||||
<script src="turbolinks.js"></script>
|
|
||||||
<meta name="turbolinks-cache-control" content="no-preview">
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// https://stackoverflow.com/a/44353026
|
|
||||||
var reloadWithTurbolinks = (function () {
|
|
||||||
var scrollPosition;
|
|
||||||
|
|
||||||
function reload() {
|
|
||||||
Turbolinks.visit(window.location.toString(), {action: 'replace'})
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('turbolinks:before-render', function () {
|
|
||||||
scrollPosition = [window.scrollX, window.scrollY];
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('turbolinks:load', function () {
|
|
||||||
if (scrollPosition) {
|
|
||||||
window.scrollTo.apply(window, scrollPosition);
|
|
||||||
scrollPosition = null
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return reload
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
setInterval(function () {
|
|
||||||
reloadWithTurbolinks();
|
|
||||||
}, 15000);
|
|
||||||
|
|
||||||
|
|
||||||
function showHtml(id) {
|
|
||||||
document.getElementById('email-' + id + '-html').style.display = 'block';
|
|
||||||
document.getElementById('email-' + id + '-plain').style.display = 'none';
|
|
||||||
document.getElementById('show-html-button-' + id).style.display = 'none';
|
|
||||||
document.getElementById('show-plain-button-' + id).style.display = 'inline-block';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function showPlain(id) {
|
|
||||||
document.getElementById('email-' + id + '-html').style.display = 'none';
|
|
||||||
document.getElementById('email-' + id + '-plain').style.display = 'block';
|
|
||||||
document.getElementById('show-html-button-' + id).style.display = 'inline-block';
|
|
||||||
document.getElementById('show-plain-button-' + id).style.display = 'none';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
|
|
||||||
<body data-turbolinks="false">
|
|
||||||
|
|
||||||
<header data-turbolinks-permanent id="header">
|
|
||||||
<div class="container">
|
|
||||||
<small class="form-text text-muted">
|
|
||||||
change username:
|
|
||||||
</small>
|
|
||||||
|
|
||||||
<form action="?" method="get">
|
|
||||||
<div class="form-group row">
|
|
||||||
|
|
||||||
<div class="col-sm-4">
|
|
||||||
<input id="username" class="form-control form-control-lg" name="username" title="username"
|
|
||||||
value="<?php echo $username ?>">
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-3">
|
|
||||||
<?php
|
|
||||||
if (count($config['domains']) == 1) {
|
|
||||||
print "<h3>@" . $config['domains'][0] . "</h3>";
|
|
||||||
} else {
|
|
||||||
?>
|
|
||||||
<select id="domain" class="form-control form-control-lg" name="domain" title="domain">
|
|
||||||
<?php
|
|
||||||
foreach ($config['domains'] as $domain) {
|
|
||||||
$selected = $domain === $userDomain ? ' selected ' : '';
|
|
||||||
print "<option value='$domain' $selected>@$domain</option>";
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</select>
|
|
||||||
<?php
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-3 random-column">
|
|
||||||
<span>or </span>
|
|
||||||
<a role="button" href="?random=true" class="btn btn-outline-primary">generate random
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<div class="container min-height">
|
|
||||||
|
|
||||||
<?php
|
|
||||||
if (empty($emails)) {
|
|
||||||
?>
|
|
||||||
<div>
|
|
||||||
<div class="card waiting-screen">
|
|
||||||
<div class="card-block">
|
|
||||||
<p class="lead">Your mailbox <strong
|
|
||||||
><?php echo $address ?></strong> is ready. </p>
|
|
||||||
<p>Emails will appear here automatically. They will be deleted after 30 days.</p>
|
|
||||||
<div class="spinner">
|
|
||||||
<div class="rect1"></div>
|
|
||||||
<div class="rect2"></div>
|
|
||||||
<div class="rect3"></div>
|
|
||||||
<div class="rect4"></div>
|
|
||||||
<div class="rect5"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php
|
|
||||||
} else {
|
|
||||||
|
|
||||||
foreach ($emails as $email) {
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
||||||
<div class="email-table">
|
|
||||||
|
|
||||||
<div class="card email">
|
|
||||||
<div class="card-block header-shadow sticky-header">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-8">
|
|
||||||
<h3 class="card-title">
|
|
||||||
<?php echo filter_var($email->subject, FILTER_SANITIZE_SPECIAL_CHARS); ?>
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-4 text-right">
|
|
||||||
<form class="form-inline float-xs-right">
|
|
||||||
|
|
||||||
<button type="button" class="btn btn-outline-info btn-sm"
|
|
||||||
style="display: inline-block"
|
|
||||||
id="show-html-button-<?php echo filter_var($email->id, FILTER_VALIDATE_INT); ?>"
|
|
||||||
onclick="showHtml(<?php echo filter_var($email->id, FILTER_VALIDATE_INT); ?>)">
|
|
||||||
show html
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-outline-info btn-sm"
|
|
||||||
style="display: none"
|
|
||||||
id="show-plain-button-<?php echo filter_var($email->id, FILTER_VALIDATE_INT); ?>"
|
|
||||||
onclick="showPlain(<?php echo filter_var($email->id, FILTER_VALIDATE_INT); ?>)">
|
|
||||||
show text
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<a class="btn btn-sm btn-outline-primary " download="true"
|
|
||||||
role="button"
|
|
||||||
href="?download_email_id=<?php echo filter_var($email->id, FILTER_VALIDATE_INT); ?>&address=<?php echo $address ?>">Download
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="btn btn-sm btn-outline-danger"
|
|
||||||
role="button"
|
|
||||||
href="?delete_email_id=<?php echo filter_var($email->id, FILTER_VALIDATE_INT); ?>&address=<?php echo $address ?>">Delete
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-8">
|
|
||||||
<h6 class="card-subtitle mt-1 text-muted">
|
|
||||||
<?php
|
|
||||||
echo filter_var($email->fromName, FILTER_SANITIZE_SPECIAL_CHARS);
|
|
||||||
echo ' <';
|
|
||||||
echo filter_var($email->fromAddress, FILTER_SANITIZE_SPECIAL_CHARS);
|
|
||||||
echo '>';
|
|
||||||
?>
|
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-4">
|
|
||||||
<h6 class="card-subtitle mt-1 text-muted"
|
|
||||||
style="text-align: right">
|
|
||||||
<?php echo filter_var($email->date, FILTER_SANITIZE_SPECIAL_CHARS); ?>
|
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<h6 class="card-subtitle text-muted">
|
|
||||||
To: <?php echo filter_var($email->toString, FILTER_SANITIZE_SPECIAL_CHARS); ?></h6>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
foreach ($email->cc as $cc) {
|
|
||||||
print "<h6 class='card-subtitle text-muted'>CC: " . filter_var($cc, FILTER_SANITIZE_SPECIAL_CHARS) . "</h6>";
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="mt-2 card-text">
|
|
||||||
<!-- show plaintext or html -->
|
|
||||||
<div id="email-<?php echo filter_var($email->id, FILTER_VALIDATE_INT); ?>-plain"
|
|
||||||
style="display: block;">
|
|
||||||
<?php $text = filter_var($email->textPlain, FILTER_SANITIZE_SPECIAL_CHARS);
|
|
||||||
// Keep newlines
|
|
||||||
$text = str_replace(' ', '<br />', $text);
|
|
||||||
echo \AutoLinkExtension::auto_link_text($text)
|
|
||||||
?>
|
|
||||||
</div>
|
|
||||||
<div id="email-<?php echo filter_var($email->id, FILTER_VALIDATE_INT); ?>-html"
|
|
||||||
style="display: none;">
|
|
||||||
<?php
|
|
||||||
$clean_html = $purifier->purify($email->textHtml);
|
|
||||||
echo $clean_html;
|
|
||||||
?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</div>
|
|
||||||
</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>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
<?php
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 $address string email address
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function get_emails($address) {
|
||||||
|
global $mailbox;
|
||||||
|
|
||||||
|
// Search for mails with the recipient $address in TO or CC.
|
||||||
|
$mailsIdsTo = imap_sort($mailbox->getImapStream(), SORTARRIVAL, true, SE_UID, 'TO "' . $address . '"');
|
||||||
|
$mailsIdsCc = imap_sort($mailbox->getImapStream(), SORTARRIVAL, true, SE_UID, 'CC "' . $address . '"');
|
||||||
|
$mail_ids = array_merge($mailsIdsTo, $mailsIdsCc);
|
||||||
|
|
||||||
|
$emails = _load_emails($mail_ids, $address);
|
||||||
|
return $emails;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* deletes emails by id and username. The $address must match the recipient in the email.
|
||||||
|
*
|
||||||
|
* @param $mailid integer imap email id
|
||||||
|
* @param $address string email address
|
||||||
|
* @internal param the $username matching username
|
||||||
|
*/
|
||||||
|
function delete_email($mailid, $address) {
|
||||||
|
global $mailbox;
|
||||||
|
|
||||||
|
if (_load_one_email($mailid, $address) !== 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 $address string email address
|
||||||
|
* @internal param the $username matching username
|
||||||
|
*/
|
||||||
|
|
||||||
|
function download_email($mailid, $address) {
|
||||||
|
global $mailbox;
|
||||||
|
|
||||||
|
if (_load_one_email($mailid, $address) !== null) {
|
||||||
|
header("Content-Type: message/rfc822; charset=utf-8");
|
||||||
|
header("Content-Disposition: attachment; filename=\"$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 $address String address
|
||||||
|
* @return email or null
|
||||||
|
*/
|
||||||
|
function _load_one_email($mailid, $address) {
|
||||||
|
// 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), $address);
|
||||||
|
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 $address String address
|
||||||
|
* @return array of emails
|
||||||
|
*/
|
||||||
|
function _load_emails($mail_ids, $address) {
|
||||||
|
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($address, $mail->to) || array_key_exists($address, $mail->cc)) {
|
||||||
|
$emails[] = $mail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $emails;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove illegal characters from username and remove everything after the @-sign. You may extend it if your server supports them.
|
||||||
|
* @param $username
|
||||||
|
* @return string clean username
|
||||||
|
*/
|
||||||
|
function _clean_username($username) {
|
||||||
|
$username = strtolower($username);
|
||||||
|
$username = preg_replace('/@.*$/', "", $username); // remove part after @
|
||||||
|
$username = preg_replace('/[^A-Za-z0-9_.+-]/', "", $username); // remove special characters
|
||||||
|
|
||||||
|
if (in_array($username, array('root', 'admin', 'administrator', 'hostmaster', 'postmaster', 'webmaster'))) {
|
||||||
|
// Forbidden name!
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $username;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _clean_domain($username) {
|
||||||
|
$username = strtolower($username);
|
||||||
|
$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);
|
||||||
|
|
||||||
|
|
||||||
|
$string = AutoLinkExtension::unescape($string);
|
||||||
|
|
||||||
|
return $string;
|
||||||
|
} # filter()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unescape()
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return string $text
|
||||||
|
**/
|
||||||
|
static function unescape($text) {
|
||||||
|
global $escape_autolink_uri;
|
||||||
|
|
||||||
|
if (!$escape_autolink_uri)
|
||||||
|
return $text;
|
||||||
|
|
||||||
|
$unescape = array_reverse($escape_autolink_uri);
|
||||||
|
|
||||||
|
return str_replace(array_keys($unescape), array_values($unescape), $text);
|
||||||
|
} # unescape()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
?>
|
?>
|
Loading…
Reference in New Issue
Block a user