created JSON API, TODO: refactor

This commit is contained in:
Synox 2018-03-06 22:21:43 +01:00
parent 33dedf0368
commit 75ed103d17
9 changed files with 198 additions and 54 deletions

View file

@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
- refactored code into multiple php files.
- Requires PHP version >=7.2
- make all addresses lowercase #30
- fixed error when downloading email
### Added
- better horizontal spacing for header (from @Spegeli) and style
@ -24,6 +25,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
- Blacklist some usernames, configurable #27
- copyToClipboard button #30
- mail counter in title
- rest api option
## [0.1.4] - 2017-04-15

View file

@ -1,8 +1,19 @@
<?php
require_once './imap_client.php';
require_once './view.php';
abstract class Page {
abstract class Controller {
/**
* @var ViewHandler
*/
protected $viewHandler;
public function setViewHandler(ViewHandler $outputHandler) {
$this->viewHandler = $outputHandler;
}
function invoke(ImapClient $imapClient) {
}
@ -23,22 +34,12 @@ abstract class Page {
$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\"}");
$this->viewHandler->newAddress("$name@$domain");
}
}
class RedirectToAddressPage extends Page {
class RedirectToAddressController extends Controller {
private $username;
private $domain;
private $config_blocked_usernames;
@ -51,11 +52,11 @@ class RedirectToAddressPage extends Page {
function invoke(ImapClient $imapClient) {
$user = User::parseUsernameAndDomain($this->username, $this->domain, $this->config_blocked_usernames);
header("location: ?" . $user->username . "@" . $user->domain);
$this->viewHandler->newAddress($user->username . "@" . $user->domain);
}
}
class DownloadEmailPage extends Page {
class DownloadEmailController extends Controller {
private $email_id;
private $address;
@ -75,21 +76,18 @@ class DownloadEmailPage extends Page {
$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;
$full_email = $imapClient->load_one_email_fully($download_email_id, $user);
if ($full_email !== null) {
$filename = $user->address . "-" . $download_email_id . ".eml";
$this->viewHandler->downloadEmailAsRfc822($full_email, $filename);
} else {
$this->error(404, 'download error: invalid username/mailid combination');
$this->viewHandler->error(404, 'download error: invalid username/mailid combination');
}
}
}
class DeleteEmailPage extends Page {
class DeleteEmailController extends Controller {
private $email_id;
private $address;
private $config_domains;
@ -108,14 +106,14 @@ class DeleteEmailPage extends Page {
$delete_email_id = filter_var($this->email_id, FILTER_SANITIZE_NUMBER_INT);
if ($imapClient->delete_email($delete_email_id, $user)) {
header("location: ?" . $user->address);
$this->viewHandler->done($this->address);
} else {
$this->error(404, 'delete error: invalid username/mailid combination');
$this->viewHandler->error(404, 'delete error: invalid username/mailid combination');
}
}
}
class RedirectToRandomAddressPage extends Page {
class RedirectToRandomAddressController extends Controller {
private $config_domains;
public function __construct($config_domains) {
@ -128,7 +126,7 @@ class RedirectToRandomAddressPage extends Page {
}
class DisplayEmailsPage extends Page {
class DisplayEmailsController extends Controller {
private $address;
private $config;
@ -142,16 +140,14 @@ class DisplayEmailsPage extends Page {
// 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";
$this->viewHandler->displayEmails($emails, $this->config, $user);
}
}
class InvalidRequestPage extends Page {
class InvalidRequestController extends Controller {
function invoke(ImapClient $imapClient) {
$this->error(400, "Bad Request");
$this->viewHandler->error(400, "Bad Request");
}
}

View file

@ -184,7 +184,7 @@ $purifier = new HTMLPurifier($purifier_config);
<a class="btn btn-sm btn-outline-danger"
role="button"
href="?action=delete_email&delete_email_id=<?php echo $safe_email_id; ?>&amp;address=<?php echo $user->address ?>">Delete
href="?action=delete_email&email_id=<?php echo $safe_email_id; ?>&amp;address=<?php echo $user->address ?>">Delete
</a>
</div>
</div>

View file

@ -54,13 +54,25 @@ class ImapClient {
}
public function load_one_email_fully($download_email_id, $user) {
if ($this->load_one_email($download_email_id, $user) !== null) {
$headers = imap_fetchheader($this->mailbox->getImapStream(), $download_email_id, FT_UID);
$body = imap_body($this->mailbox->getImapStream(), $download_email_id, FT_UID);
return $headers . "\n" . $body;
} else {
return 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) {
private
function _load_emails(array $mail_ids, User $user) {
$emails = array();
foreach ($mail_ids as $id) {
$mail = $this->mailbox->getMail($id);
@ -75,7 +87,8 @@ class ImapClient {
/**
* deletes messages older than X days.
*/
public function delete_old_messages(string $delete_messages_older_than) {
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);

View file

@ -7,13 +7,14 @@ require_once './backend-libs/autoload.php';
require_once './user.php';
require_once './imap_client.php';
require_once './pages.php';
require_once './controller.php';
require_once './router.php';
$imapClient = new ImapClient($config['imap']['url'], $config['imap']['username'], $config['imap']['password']);
$router = new Router($_SERVER['REQUEST_METHOD'], $_GET['action'] ?? NULL, $_GET, $_POST, $_SERVER['QUERY_STRING'], $config);
$page = $router->route();
$page->setViewHandler(new ServerRenderViewHandler());
$page->invoke($imapClient);
// delete after each request

23
src/rest.php Normal file
View file

@ -0,0 +1,23 @@
<?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';
require_once './user.php';
require_once './imap_client.php';
require_once './controller.php';
require_once './router.rest.php';
$imapClient = new ImapClient($config['imap']['url'], $config['imap']['username'], $config['imap']['password']);
$router = new RestRouter($_SERVER['REQUEST_METHOD'], $_GET['action'] ?? NULL, $_GET, $_POST, $_SERVER['QUERY_STRING'], $config);
$page = $router->route();
$page->setViewHandler(new JsonViewHandler());
$page->invoke($imapClient);
// delete after each request
$imapClient->delete_old_messages($config['delete_messages_older_than']);
?>

View file

@ -1,15 +1,15 @@
<?php
require_once './pages.php';
require_once './controller.php';
class Router {
private $method;
private $action;
private $get_vars;
private $post_vars;
private $query_string;
private $config;
protected $method;
protected $action;
protected $get_vars;
protected $post_vars;
protected $query_string;
protected $config;
public function __construct(string $method, string $action = NULL, array $get_vars, array $post_vars, string $query_string, array $config) {
$this->method = $method;
@ -21,30 +21,30 @@ class Router {
}
function route(): Page {
function route(): Controller {
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']);
return new RedirectToAddressController($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['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']);
return new DownloadEmailController($this->get_vars['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['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']);
return new DeleteEmailController($this->get_vars['email_id'], $this->get_vars['address'], $this->config['domains'], $this->config['blocked_usernames']);
} elseif ($this->action === 'random') {
return new RedirectToRandomAddressPage($this->config['domains']);
return new RedirectToRandomAddressController($this->config['domains']);
} elseif (!empty($this->query_string)) {
return new DisplayEmailsPage($this->query_string, $this->config);
return new DisplayEmailsController($this->query_string, $this->config);
} else {
return new RedirectToRandomAddressPage($this->config['domains']);
return new RedirectToRandomAddressController($this->config['domains']);
}
}
}

30
src/router.rest.php Normal file
View file

@ -0,0 +1,30 @@
<?php
require_once './controller.php';
require_once './router.php';
class RestRouter extends Router {
function route(): Controller {
if ($this->action === "download_email"
&& isset($this->get_vars['email_id'])
&& isset($this->get_vars['address'])) {
return new DownloadEmailController($this->get_vars['email_id'], $this->get_vars['address'], $this->config['domains'], $this->config['blocked_usernames']);
} elseif ($this->action === "delete_email"
&& isset($this->get_vars['email_id'])
&& isset($this->get_vars['address'])) {
return new DeleteEmailController($this->get_vars['email_id'], $this->get_vars['address'], $this->config['domains'], $this->config['blocked_usernames']);
} elseif ($this->action === 'get_random_username') {
return new RedirectToRandomAddressController($this->config['domains']);
} elseif ($this->action === 'get_emails' && isset($this->get_vars['address'])) {
return new DisplayEmailsController($this->get_vars['address'], $this->config);
} else {
return new InvalidRequestController();
}
}
}

79
src/view.php Normal file
View file

@ -0,0 +1,79 @@
<?php
interface ViewHandler {
function done($address);
/**
* print error and stop program.
* @param $status integer http status
* @param $text string error text
*/
function error($status, $text);
function displayEmails($emails, $config, $user);
function newAddress($string);
function downloadEmailAsRfc822($full_email, $filename);
}
class JsonViewHandler implements ViewHandler {
private function json($obj) {
header('Content-type: application/json');
// Never cache requests:
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
print json_encode($obj);
die();
}
function done($address) {
$this->json(array('status' => "success"));
}
function error($status, $msg) {
@http_response_code($status);
$this->json(array('status' => "failure", 'error' => $msg));
}
function displayEmails($emails, $config, $user) {
$this->json(array('status' => "success", 'emails' => $emails));
}
function newAddress($address) {
$this->json(array('status' => "failure", 'address' => $address));
}
function downloadEmailAsRfc822($full_email, $filename) {
$this->json(array('status' => "success", 'body' => $full_email));
}
}
class ServerRenderViewHandler implements ViewHandler {
function done($address) {
header("location: ?" . $address);
}
function error($status, $msg) {
@http_response_code($status);
die("{'result': 'error', 'error': '$msg'}");
}
function displayEmails($emails, $config, $user) {
// Set variables for frontend template: $emails, $config
require "frontend.template.php";
}
function newAddress($address) {
header("location: ?$address");
}
function downloadEmailAsRfc822($full_email, $filename) {
header("Content-Type: message/rfc822; charset=utf-8");
header("Content-Disposition: attachment; filename=\"$filename\"");
print $full_email;
}
}