Merge pull request #56 from HaschekSolutions/htmx
Big overhaul of web and API. Breaking old API
This commit is contained in:
commit
492c64135e
34
.github/workflows/build-docker.yml
vendored
34
.github/workflows/build-docker.yml
vendored
|
@ -7,7 +7,6 @@ on:
|
|||
branches: [ master ]
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: $(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
jobs:
|
||||
|
@ -20,7 +19,8 @@ jobs:
|
|||
- name: Prepare
|
||||
id: prep
|
||||
run: |
|
||||
DOCKER_IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}"
|
||||
DOCKER_IMAGE="ghcr.io/${{ env.IMAGE_NAME }}"
|
||||
DOCKER_IMAGE_HUB="docker.io/${{ env.IMAGE_NAME }}"
|
||||
VERSION=latest
|
||||
SHORTREF=${GITHUB_SHA::8}
|
||||
|
||||
|
@ -29,18 +29,23 @@ jobs:
|
|||
VERSION=${GITHUB_REF#refs/tags/v}
|
||||
fi
|
||||
TAGS="${DOCKER_IMAGE}:${VERSION},${DOCKER_IMAGE}:${SHORTREF}"
|
||||
TAGS_HUB="${DOCKER_IMAGE_HUB}:${VERSION},${DOCKER_IMAGE_HUB}:${SHORTREF}"
|
||||
|
||||
# If the VERSION looks like a version number, assume that
|
||||
# this is the most recent version of the image and also
|
||||
# tag it 'latest'.
|
||||
if [[ $VERSION =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
||||
TAGS="$TAGS,${DOCKER_IMAGE}:latest"
|
||||
TAGS_HUB="$TAGS_HUB,${DOCKER_IMAGE_HUB}:latest"
|
||||
fi
|
||||
|
||||
# Set output parameters.
|
||||
echo ::set-output name=tags::${TAGS}
|
||||
echo ::set-output name=docker_image::${DOCKER_IMAGE}
|
||||
|
||||
echo ::set-output name=tags_hub::${TAGS_HUB}
|
||||
echo ::set-output name=docker_image_hub::${DOCKER_IMAGE_HUB}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@master
|
||||
with:
|
||||
|
@ -50,14 +55,21 @@ jobs:
|
|||
id: buildx
|
||||
uses: docker/setup-buildx-action@master
|
||||
|
||||
- name: Log in to the Container registry
|
||||
- name: Log in to Github Packages
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Build and push to GHCR
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
|
@ -65,4 +77,14 @@ jobs:
|
|||
file: docker/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.prep.outputs.tags }}
|
||||
tags: ${{ steps.prep.outputs.tags }}
|
||||
|
||||
- name: Build and push to Docker Hub
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.prep.outputs.tags_hub }}
|
21
README.md
21
README.md
|
@ -12,7 +12,7 @@
|
|||
![](https://img.shields.io/badge/php-7.1%2B-brightgreen.svg)
|
||||
![](https://img.shields.io/badge/python-2.7%2B-brightgreen.svg)
|
||||
[![](https://img.shields.io/docker/pulls/hascheksolutions/opentrashmail?color=brightgreen)](https://hub.docker.com/r/hascheksolutions/opentrashmail)
|
||||
[![](https://img.shields.io/docker/cloud/build/hascheksolutions/opentrashmail?color=brightgreen)](https://hub.docker.com/r/hascheksolutions/opentrashmail/builds)
|
||||
[![](https://github.com/hascheksolutions/opentrashmail/actions/workflows/build-docker.yml/badge.svg?color=brightgreen)](https://github.com/HaschekSolutions/opentrashmail/actions)
|
||||
[![Apache License](https://img.shields.io/badge/license-Apache-blue.svg?style=flat)](https://github.com/HaschekSolutions/opentrashmail/blob/master/LICENSE)
|
||||
[![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2FHaschekSolutions%2Fopentrashmail&count_bg=%2379C83D&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false)](https://hits.seeyoufarm.com)
|
||||
[![](https://img.shields.io/github/stars/HaschekSolutions/opentrashmail.svg?label=Stars&style=social)](https://github.com/HaschekSolutions/opentrashmail)
|
||||
|
@ -22,24 +22,31 @@
|
|||
</div>
|
||||
|
||||
|
||||
![Screenshot of Open Trashmail](https://pictshare.net/shz4tq.png)
|
||||
![Screenshot of Open Trashmail](https://pictshare.net/9tim7k.png)
|
||||
|
||||
# Features
|
||||
- Python-powered mail server that works out of the box for any domain you throw at it
|
||||
- API for integrating it in your own projects. Can be used to give users individual email addresses and read what they send to it
|
||||
- RSS feed for every email address
|
||||
- JSON API for integrating it in your own projects. Can be used to automate 2fa emails
|
||||
- Handles attachments
|
||||
- Web interface to manage emails
|
||||
- Generates random email addresses
|
||||
- 100% file based, no database needed
|
||||
- Can be used as Email Honeypot
|
||||
|
||||
# JSON API
|
||||
|
||||
| Endpoint | Explanation | Example output |
|
||||
|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------|
|
||||
| /json/`[email-address]` | Returns an array of received emails with links to the attachments and the parsed text based body of the email. If `ADMIN` email is entered, will return all emails of all accounts | [![](https://pictshare.net/100x100/sflw6t.png)](https://pictshare.net/sflw6t.png) |
|
||||
| /json/`[email-address]/[id]` | To see all the data of a received email, take the ID from the previous call and poll this to get the raw and HTML body of the email. Can be huge since the body can contain all attachments in base64 | [![](https://pictshare.net/100x100/eltku4.png)](https://pictshare.net/eltku4.png) |
|
||||
| /json/listaccounts | If `SHOW_ACCOUNT_LIST` is set to true in the config.ini, this endpoint will return an array of all email addresses which have received at least one email | [![](https://pictshare.net/100x100/u6agji.png)](https://pictshare.net/u6agji.png) |
|
||||
|
||||
## [INFO] March '23
|
||||
Since Docker Hub won't allow team Organizations anymore, we moved our images to GitHub Container Registry. So if you want to use the latest version, please use the new image ghcr.io/hascheksolutions/opentrashmail instead of hascheksolutions/opentrashmail
|
||||
|
||||
# Configuration
|
||||
Just edit the `config.ini` You can use the following settings
|
||||
|
||||
- `URL` -> The url under which the GUI will be hosted. No tailing slash! example: https://trashmail.mydomain.eu
|
||||
- `DOMAINS` -> Comma separated list of domains this mail server will be receiving emails on. It's just so the web interface can generate random addresses
|
||||
- `MAILPORT`-> The port the Python-powered SMTP server will listen on. `Default: 25`
|
||||
- `ADMIN` -> An email address (doesn't have to exist, just has to be valid) that will list all emails of all addresses the server has received. Kind of a catch-all
|
||||
|
@ -60,9 +67,9 @@ Just edit the `config.ini` You can use the following settings
|
|||
- [x] Admin overview for all available email addresses
|
||||
- [x] Option to show raw email
|
||||
- [x] Delete messages
|
||||
- [ ] Secure HTML, so no malicious things can be loaded
|
||||
- [x] Make better theme
|
||||
- [x] Secure HTML, so no malicious things can be loaded
|
||||
- [ ] Display embedded images inline using Content-ID
|
||||
- [ ] Make better theme
|
||||
- [ ] Configurable settings
|
||||
- [x] Choose domains for random generation
|
||||
- [x] Choose if out-of-scope emails are discarded
|
||||
|
|
|
@ -4,27 +4,14 @@ server {
|
|||
set $base /var/www/opentrashmail;
|
||||
root /var/www/opentrashmail/web/;
|
||||
|
||||
index index.html;
|
||||
index index.php;
|
||||
|
||||
client_max_body_size 10M;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
try_files $uri $uri/ /index.php;
|
||||
}
|
||||
|
||||
location /rss {
|
||||
index rss.php;
|
||||
if (!-e $request_filename){
|
||||
rewrite ^(.*)$ /rss.php?url=$1 last;
|
||||
}
|
||||
}
|
||||
|
||||
location /api {
|
||||
index api.php;
|
||||
if (!-e $request_filename){
|
||||
rewrite ^(.*)$ /api.php?url=$1 last;
|
||||
}
|
||||
}
|
||||
|
||||
# logging
|
||||
access_log /var/log/nginx/opentrashmail/web.access.log;
|
||||
|
|
|
@ -28,6 +28,13 @@ else
|
|||
echo "DOMAINS=localhost" >> /var/www/opentrashmail/config.ini
|
||||
fi
|
||||
|
||||
if [ "$URL" != "" ]; then
|
||||
echo "URL=$URL" >> /var/www/opentrashmail/config.ini
|
||||
echo " [i] URL of GUI is set to: $URL"
|
||||
else
|
||||
echo "URL=http://localhost:8080" >> /var/www/opentrashmail/config.ini
|
||||
fi
|
||||
|
||||
if [ "$SHOW_ACCOUNT_LIST" != "" ]; then
|
||||
echo "SHOW_ACCOUNT_LIST=$SHOW_ACCOUNT_LIST" >> /var/www/opentrashmail/config.ini
|
||||
echo " [i] Set show account list to: $SHOW_ACCOUNT_LIST"
|
||||
|
|
19
docs/Dev.md
Normal file
19
docs/Dev.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Quick testing
|
||||
|
||||
From the main directory run
|
||||
|
||||
```bash
|
||||
docker build -f docker/Dockerfile -t opentrashmail . && docker run --rm -it --name trashmail -p 3000:80 -p 2525:25 opentrashmail
|
||||
```
|
||||
|
||||
And check if it works on http://localhost:3000
|
||||
|
||||
## Sending debug emails from the command line
|
||||
|
||||
Using the text file `tools/testmail.txt` and the following line of bash you can send emails to your python mailserver and test if it's acceping emails like you want.
|
||||
|
||||
Note that if you change cour config.ini, the mail server needs to be restarted before it takes effect.
|
||||
|
||||
```bash
|
||||
cat "tools/testmail.txt" | while read L; do sleep "0.2"; echo "$L"; done | "nc" -C -v "localhost" "2525"
|
||||
```
|
|
@ -1,9 +0,0 @@
|
|||
# Quick testing
|
||||
|
||||
From the `docker` directory run
|
||||
|
||||
```bash
|
||||
docker build -t opentrashmail . && docker run --rm -it --name trashmail -p 3000:80 -p 2525:25 opentrashmail
|
||||
```
|
||||
|
||||
And check if it works on http://localhost:3000
|
|
@ -5,6 +5,10 @@
|
|||
; pro tip: Use a wildcard domain like *.yourdomain.com to auto-generate the subdomains (you'll need to add a wildcard dns record too)
|
||||
DOMAINS=yourdomain,sub.yourdomain,*.mydom.com
|
||||
|
||||
; This variable needs to be set in order for RSS to work
|
||||
; The URL of your webserver hosting the GUI. No trailing slash
|
||||
URL="http://localhost:8080"
|
||||
|
||||
; Enable to show a list of all existing accounts with mail
|
||||
;SHOW_ACCOUNT_LIST=true
|
||||
|
||||
|
|
2
python/mailserver.py
Normal file → Executable file
2
python/mailserver.py
Normal file → Executable file
|
@ -171,6 +171,8 @@ if __name__ == '__main__':
|
|||
DELETE_OLDER_THAN_DAYS = (Config.get("CLEANUP","DELETE_OLDER_THAN_DAYS").lower() == "true")
|
||||
|
||||
print "[i] Starting Mailserver on port",port
|
||||
print "[i] Discard unknown domains:",DISCARD_UNKNOWN
|
||||
print "[i] Listening for domains:",DOMAINS
|
||||
|
||||
server = CustomSMTPServer(('0.0.0.0', port), None) # use your public IP here
|
||||
print "[i] Ready to receive Emails"
|
||||
|
|
14
tools/testmail.txt
Normal file
14
tools/testmail.txt
Normal file
|
@ -0,0 +1,14 @@
|
|||
HELO localhost
|
||||
MAIL FROM:<system@example.com>
|
||||
RCPT TO:<random.user@rand.domain.tld>
|
||||
DATA
|
||||
Subject: Test Message
|
||||
|
||||
Hi there! This is supposed to be an email...
|
||||
|
||||
Have a good day!
|
||||
-- System
|
||||
|
||||
|
||||
.
|
||||
QUIT
|
157
web/api.php
157
web/api.php
|
@ -1,157 +0,0 @@
|
|||
<?php
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
define('ROOT', dirname(__FILE__));
|
||||
|
||||
error_reporting(E_ALL || ~E_NOTICE);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
include_once(ROOT.DS.'inc'.DS.'core.php');
|
||||
|
||||
if (PHP_SAPI === 'cli-server')
|
||||
$_SERVER['SCRIPT_NAME'] = pathinfo(__FILE__, PATHINFO_BASENAME);
|
||||
|
||||
if($_GET['url'])
|
||||
$url = explode('/',ltrim(parse_url($_GET['url'], PHP_URL_PATH),'/'));
|
||||
else $url = array_filter(explode('/',ltrim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH),'/')));
|
||||
|
||||
$action = strtolower($_REQUEST['a']);
|
||||
$email = strtolower($_REQUEST['email']);
|
||||
// quick hack to get admin email working.
|
||||
// by the time $email is checked its been over written.
|
||||
// store a copy in $admincheck to compare later.
|
||||
$admincheck = $email;
|
||||
|
||||
if(!empty($email)){
|
||||
if(!filter_var($email, FILTER_VALIDATE_EMAIL)){
|
||||
// email param provided, but invalid: skip action and show invalid email error
|
||||
$o = array('status'=>'err','reason'=>'Invalid Email address');
|
||||
unset($action);
|
||||
}
|
||||
$dir = getDirForEmail($email);
|
||||
$email = basename($dir);
|
||||
}
|
||||
|
||||
switch($action)
|
||||
{
|
||||
case 'del':
|
||||
$id = intval($_REQUEST['mid']);
|
||||
if(!is_dir($dir))
|
||||
$o = array('status'=>'err','reason'=>'No emails received on this address');
|
||||
else if(!is_numeric($id) || !emailIDExists($email,$id))
|
||||
$o = array('status'=>'err','reason'=>'Invalid Email ID');
|
||||
else
|
||||
{
|
||||
|
||||
if(deleteEmail($email,$id))
|
||||
$o = array('status'=>'ok');
|
||||
else
|
||||
$o = array('status'=>'err','reason'=>'Could not delete email. Permission problem?');
|
||||
}
|
||||
break;
|
||||
case 'getdoms':
|
||||
$settings = loadSettings();
|
||||
if($settings['DOMAINS'])
|
||||
$o = explode(',',$settings['DOMAINS']);
|
||||
else $o = [];
|
||||
break;
|
||||
case 'attachment':
|
||||
$id = intval($_REQUEST['id']);
|
||||
$filename = basename($_REQUEST['filename']);
|
||||
$filepath = $dir.DS.'attachments'.DS.$id.'-'.$filename;
|
||||
if(!is_dir($dir))
|
||||
$o = array('status'=>'err','reason'=>'No emails received on this address');
|
||||
else if(!is_numeric($id) || !emailIDExists($email,$id))
|
||||
$o = array('status'=>'err','reason'=>'Invalid Email ID');
|
||||
else if(!file_exists($filepath))
|
||||
$o = array('status'=>'err','reason'=>'File not found');
|
||||
else
|
||||
{
|
||||
header('Content-Type: '.mime_content_type($filepath));
|
||||
readfile($filepath);
|
||||
exit();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'load':
|
||||
$id = intval($_REQUEST['id']);
|
||||
if(empty($email))
|
||||
$o = array('status'=>'err','reason'=>'No email address provided');
|
||||
else if(!is_dir($dir))
|
||||
$o = array('status'=>'err','reason'=>'No emails received on this address');
|
||||
else if(!is_numeric($id) || !emailIDExists($email,$id))
|
||||
$o = array('status'=>'err','reason'=>'Invalid Email ID');
|
||||
else
|
||||
{
|
||||
$data = getEmail($email,$id);
|
||||
if($_REQUEST['raw']=='true')
|
||||
{
|
||||
header('Content-Type: text/plain');
|
||||
exit($data['raw']);
|
||||
}
|
||||
$o = array('status'=>'ok','data'=>$data);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'list':
|
||||
$settings = loadSettings();
|
||||
/*
|
||||
|
||||
if($settings['ADMIN'] && $settings['ADMIN']==$admincheck)
|
||||
{
|
||||
$o['status'] = 'ok';
|
||||
$o['type'] = 'admin';
|
||||
$o['dateformat'] = $settings['DATEFORMAT'];
|
||||
$emails = listEmailAdresses();
|
||||
$emaillist = array();
|
||||
|
||||
if(count($emails)>0)
|
||||
{
|
||||
foreach($emails as $email)
|
||||
{
|
||||
$emaildata = getEmailsOfEmail($email);
|
||||
foreach($emaildata as $time=>$d)
|
||||
$emaillist[$time.'-'.$email]=$d;
|
||||
}
|
||||
if(is_array($emaillist))
|
||||
krsort($emaillist);
|
||||
$data = (count($emaillist)?$emaillist:array());
|
||||
}
|
||||
|
||||
$o['emails']=$data;
|
||||
}
|
||||
else */
|
||||
|
||||
if(!is_dir($dir) && $settings['ADMIN']!=$admincheck)
|
||||
$o = array('status'=>'ok','emails'=>[]);
|
||||
else
|
||||
{
|
||||
if(!$email) $email = $admincheck;
|
||||
$data = getEmailsOfEmail($email);
|
||||
$lastid = $_REQUEST['lastid'];
|
||||
if($lastid && is_numeric($lastid))
|
||||
{
|
||||
foreach($data as $time=>$d)
|
||||
{
|
||||
if($time>$lastid)
|
||||
$emails[$time]=$d;
|
||||
}
|
||||
$data = (is_array($emails)?$emails:array());
|
||||
}
|
||||
|
||||
$o = array('status'=>'ok','emails'=>$data);
|
||||
}
|
||||
break;
|
||||
case 'show-list':
|
||||
$settings = loadSettings();
|
||||
$o = $settings['SHOW_ACCOUNT_LIST'];
|
||||
break;
|
||||
case 'list-addresses':
|
||||
$settings = loadSettings();
|
||||
$o = array('status'=>'ok','addresses'=>[]);
|
||||
|
||||
if ($settings['SHOW_ACCOUNT_LIST'])
|
||||
$o['addresses'] = listEmailAdresses();
|
||||
break;
|
||||
}
|
||||
|
||||
echo json_encode($o);
|
7
web/css/bootstrap.min.css
vendored
7
web/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
web/css/default.min.css
vendored
Normal file
2
web/css/default.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
3
web/css/default.min.css:Zone.Identifier
Normal file
3
web/css/default.min.css:Zone.Identifier
Normal file
|
@ -0,0 +1,3 @@
|
|||
[ZoneTransfer]
|
||||
ZoneId=3
|
||||
ReferrerUrl=C:\Users\chris\Downloads\pico-1.5.10.zip
|
|
@ -1,7 +1,35 @@
|
|||
body {
|
||||
padding-top: 5rem;
|
||||
}
|
||||
.starter-template {
|
||||
padding: 3rem 1.5rem;
|
||||
form {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
tr.htmx-swapping td {
|
||||
opacity: 0;
|
||||
transition: opacity 1s ease-out;
|
||||
}
|
||||
|
||||
.badge {
|
||||
background-color: #999;
|
||||
color: white;
|
||||
padding: 4px 8px;
|
||||
margin: 2px;
|
||||
text-align: center;
|
||||
}
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.htmx-indicator{
|
||||
display:none;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
/* pico css overrides */
|
||||
|
||||
:root {
|
||||
--form-element-spacing-vertical: 0.15rem;
|
||||
--form-element-spacing-horizontal: 1rem;
|
||||
}
|
5
web/css/pico.min.css
vendored
Normal file
5
web/css/pico.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
220
web/inc/OpenTrashmailBackend.class.php
Normal file
220
web/inc/OpenTrashmailBackend.class.php
Normal file
|
@ -0,0 +1,220 @@
|
|||
<?php
|
||||
|
||||
class OpenTrashmailBackend{
|
||||
private $url;
|
||||
private $settings;
|
||||
|
||||
public function __construct($url){
|
||||
$this->url = $url;
|
||||
$this->settings = loadSettings();
|
||||
}
|
||||
public function run(){
|
||||
|
||||
// api calls
|
||||
if($this->url[0]=='api')
|
||||
{
|
||||
switch($this->url[1]){
|
||||
case 'address':
|
||||
return $this->listAccount($_REQUEST['email']?:$this->url[2]);
|
||||
case 'read':
|
||||
return $this->readMail($_REQUEST['email']?:$this->url[2],$_REQUEST['id']?:$this->url[3]);
|
||||
case 'listaccounts':
|
||||
if($this->settings['SHOW_ACCOUNT_LIST'])
|
||||
return $this->listAccounts();
|
||||
else return '403 Forbidden';
|
||||
case 'raw-html':
|
||||
return $this->getRawMail($this->url[2],$this->url[3],true);
|
||||
case 'raw':
|
||||
return $this->getRawMail($this->url[2],$this->url[3]);
|
||||
case 'attachment':
|
||||
return $this->getAttachment($this->url[2],$this->url[3]);
|
||||
case 'delete':
|
||||
return $this->deleteMail($_REQUEST['email']?:$this->url[2],$_REQUEST['id']?:$this->url[3]);
|
||||
case 'random':
|
||||
$addr = generateRandomEmail();
|
||||
//add header HX-Redirect
|
||||
return $this->listAccount($addr);
|
||||
case 'deleteaccount':
|
||||
return $this->deleteAccount($_REQUEST['email']?:$this->url[2]);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// rss feed
|
||||
else if($this->url[0]=='rss')
|
||||
{
|
||||
header("Content-Type: application/rss+xml; charset=UTF8");
|
||||
$email = $this->url[1];
|
||||
if (!$email || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
http_response_code(404);
|
||||
exit('Error: Email not found');
|
||||
}
|
||||
return $this->renderTemplate('rss.xml',[
|
||||
'email'=>$email,
|
||||
'emaildata'=>getEmailsOfEmail($email),
|
||||
'url'=>$this->settings['URL'],
|
||||
]);
|
||||
}
|
||||
|
||||
//json api
|
||||
else if($this->url[0]=='json')
|
||||
{
|
||||
header("Content-Type: application/json; charset=UTF8");
|
||||
if($this->url[1]=='listaccounts')
|
||||
{
|
||||
if($this->settings['SHOW_ACCOUNT_LIST'])
|
||||
return json_encode(listEmailAdresses());
|
||||
else exit(json_encode(['error'=>'403 Forbidden']));
|
||||
}
|
||||
$email = $this->url[1];
|
||||
if (!$email || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
http_response_code(404);
|
||||
exit(json_encode(['error'=>'Email not found']));
|
||||
}
|
||||
$id = $this->url[2];
|
||||
if($id) //user wants a specific email ID
|
||||
{
|
||||
if(!emailIDExists($email,$id))
|
||||
{
|
||||
http_response_code(404);
|
||||
exit(json_encode(['error'=>'Email ID not found']));
|
||||
}
|
||||
else if(!ctype_digit($id))
|
||||
{
|
||||
http_response_code(400);
|
||||
exit(json_encode(['error'=>'Invalid ID']));
|
||||
}
|
||||
else
|
||||
return json_encode(getEmail($email,$id));
|
||||
}
|
||||
else
|
||||
return json_encode(getEmailsOfEmail($email,true,true));
|
||||
}
|
||||
|
||||
else return false;
|
||||
}
|
||||
|
||||
function deleteAccount($email)
|
||||
{
|
||||
if(!filter_var($email, FILTER_VALIDATE_EMAIL))
|
||||
return $this->error('Invalid email address');
|
||||
$path = getDirForEmail($email);
|
||||
if(is_dir($path))
|
||||
delTree($path);
|
||||
}
|
||||
|
||||
function listAccounts()
|
||||
{
|
||||
$accounts = listEmailAdresses();
|
||||
return $this->renderTemplate('account-list.html',[
|
||||
'emails'=>$accounts,
|
||||
'dateformat'=>$this->settings['DATEFORMAT']
|
||||
]);
|
||||
}
|
||||
|
||||
function deleteMail($email,$id)
|
||||
{
|
||||
if(!filter_var($email, FILTER_VALIDATE_EMAIL))
|
||||
return $this->error('Invalid email address');
|
||||
else if(!ctype_digit($id))
|
||||
return $this->error('Invalid id');
|
||||
else if(!emailIDExists($email,$id))
|
||||
return $this->error('Email not found');
|
||||
deleteEmail($email,$id);
|
||||
return '';
|
||||
}
|
||||
|
||||
function getRawMail($email,$id,$htmlbody=false)
|
||||
{
|
||||
if(!filter_var($email, FILTER_VALIDATE_EMAIL))
|
||||
return $this->error('Invalid email address');
|
||||
else if(!ctype_digit($id))
|
||||
return $this->error('Invalid id');
|
||||
else if(!emailIDExists($email,$id))
|
||||
return $this->error('Email not found');
|
||||
$emaildata = getEmail($email,$id);
|
||||
if($htmlbody)
|
||||
exit($emaildata['parsed']['htmlbody']);
|
||||
header('Content-Type: text/plain');
|
||||
echo $emaildata['raw'];
|
||||
exit;
|
||||
}
|
||||
|
||||
function getAttachment($email,$attachment)
|
||||
{
|
||||
$id = substr($attachment,0,13);
|
||||
$attachment = substr($attachment,14);
|
||||
if(!filter_var($email, FILTER_VALIDATE_EMAIL))
|
||||
return $this->error('Invalid email address');
|
||||
else if(!ctype_digit($id))
|
||||
return $this->error('Invalid id');
|
||||
else if(!emailIDExists($email,$id))
|
||||
return $this->error('Email not found');
|
||||
else if(!attachmentExists($email,$id,$attachment))
|
||||
return $this->error('Attachment not found');
|
||||
$dir = getDirForEmail($email);
|
||||
$file = $dir.DS.'attachments'.DS.$id.'-'.$attachment;
|
||||
$mime = mime_content_type($file);
|
||||
header('Content-Type: '.$mime);
|
||||
header('Content-Length: ' . filesize($file));
|
||||
readfile($file);
|
||||
exit;
|
||||
}
|
||||
|
||||
function readMail($email,$id)
|
||||
{
|
||||
if(!filter_var($email, FILTER_VALIDATE_EMAIL))
|
||||
return $this->error('Invalid email address');
|
||||
else if(!ctype_digit($id))
|
||||
return $this->error('Invalid id');
|
||||
else if(!emailIDExists($email,$id))
|
||||
return $this->error('Email not found');
|
||||
$emaildata = getEmail($email,$id);
|
||||
//$email['raw'] = file_get_contents(getDirForEmail($email['email']).DS.$email['id'].'.json');
|
||||
//$email['parsed'] = json_decode($email['raw'],true);
|
||||
|
||||
//var_dump($emaildata);
|
||||
return $this->renderTemplate('email.html',[
|
||||
'emaildata'=>$emaildata,
|
||||
'email'=>$email,
|
||||
'mailid'=>$id,
|
||||
'dateformat'=>$this->settings['DATEFORMAT']
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
public function listAccount($email)
|
||||
{
|
||||
if(!filter_var($email, FILTER_VALIDATE_EMAIL))
|
||||
return $this->error('Invalid email address');
|
||||
$emails = getEmailsOfEmail($email);
|
||||
//var_dump($emails);
|
||||
return $this->renderTemplate('email-table.html',[
|
||||
'email'=>$email,
|
||||
'emails'=>$emails,
|
||||
'dateformat'=>$this->settings['DATEFORMAT']
|
||||
]);
|
||||
}
|
||||
|
||||
public function error($text)
|
||||
{
|
||||
return '<h1>'.$text.'</h1>';
|
||||
}
|
||||
|
||||
public function renderTemplate($templatename,$variables=[])
|
||||
{
|
||||
ob_start();
|
||||
if(is_array($variables))
|
||||
extract($variables);
|
||||
if(file_exists(ROOT.DS.'templates'.DS.$templatename.'.php'))
|
||||
include(ROOT.DS.'templates'.DS.$templatename.'.php');
|
||||
else if(file_exists(ROOT.DS.'templates'.DS.$templatename))
|
||||
include(ROOT.DS.'templates'.DS.$templatename);
|
||||
$rendered = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
return $rendered;
|
||||
}
|
||||
|
||||
}
|
112
web/inc/core.php
112
web/inc/core.php
File diff suppressed because one or more lines are too long
|
@ -1,48 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="shortcut icon" href="favicon.ico">
|
||||
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="css/opentrashmail.css">
|
||||
<link rel="stylesheet" href="css/fontawesome.min.css">
|
||||
|
||||
<title>Open Trashmail</title>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
|
||||
<a class="navbar-brand" href="?"><img src="imgs/logo_300_light.png" width="50px" /> Open Trashmail </a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navi" aria-controls="navi" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navi">
|
||||
<form class="form-inline my-2 my-lg-0">
|
||||
<input class="form-control mr-sm-2" id="email" type="email" placeholder="email address" aria-label="email address">
|
||||
<button onClick="accessAccount()" class="btn btn-secondary my-2 my-sm-0"><i class="fas fa-arrow-left"></i><i class="fas fa-envelope"></i> Access account</button>
|
||||
<button onClick="generateAccount()" id="btn-gen-random" class="btn btn-secondary my-2 my-sm-0"><i class="fas fa-random"></i> Generate random</button>
|
||||
<button onClick="listAddresses(event)" id="btn-list-addresses" class="btn btn-secondary my-2 my-sm-0" style="display:none;"><i class="fas fa-list"></i> List accounts</button>
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main role="main" class="container" id="main">
|
||||
<div class="starter-template">
|
||||
<h1>Welcome to Open Trashmail</h1>
|
||||
<p class="lead">Access an email address or generate a new one.</p>
|
||||
</div>
|
||||
|
||||
</main><!-- /.container -->
|
||||
|
||||
<script src="js/jquery-3.4.1.min.js"></script>
|
||||
<script src="js/namegenerator.js"></script>
|
||||
<script src="js/opentrashmail.js"></script>
|
||||
<script src="js/bootstrap.bundle.min.js"></script>
|
||||
<script src="js/moment-with-locales.min.js"></script>
|
||||
</body>
|
||||
</html>
|
33
web/index.php
Normal file
33
web/index.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
define('ROOT', dirname(__FILE__));
|
||||
|
||||
include_once(ROOT.DS.'inc'.DS.'OpenTrashmailBackend.class.php');
|
||||
include_once(ROOT.DS.'inc'.DS.'core.php');
|
||||
|
||||
$url = array_filter(explode('/',ltrim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH),'/')));
|
||||
|
||||
$backend = new OpenTrashmailBackend($url);
|
||||
|
||||
if($_SERVER['HTTP_HX_REQUEST']!='true')
|
||||
{
|
||||
if(count($url)==0 || !file_exists(ROOT.DS.implode('/', $url)))
|
||||
if($url[0]!='api' && $url[0]!='rss' && $url[0]!='json')
|
||||
exit($backend->renderTemplate('index.html',[
|
||||
'url'=>implode('/', $url),
|
||||
'settings'=>loadSettings(),
|
||||
]));
|
||||
}
|
||||
else if(count($url)==1 && $url[0] == 'api') {
|
||||
exit($backend->renderTemplate('intro.html'));
|
||||
}
|
||||
|
||||
|
||||
$answer = $backend->run();
|
||||
|
||||
|
||||
if($answer === false)
|
||||
return false;
|
||||
else
|
||||
echo $answer;
|
||||
|
7
web/js/bootstrap.bundle.min.js
vendored
7
web/js/bootstrap.bundle.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
web/js/htmx.min.js
vendored
Normal file
1
web/js/htmx.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
web/js/jquery-3.4.1.min.js
vendored
2
web/js/jquery-3.4.1.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
101
web/rss.php
101
web/rss.php
|
@ -1,101 +0,0 @@
|
|||
<?php
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
define('ROOT', dirname(__FILE__));
|
||||
define('DOMAIN', $_SERVER['HTTP_HOST']);
|
||||
|
||||
error_reporting(E_ALL || ~E_NOTICE);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
include_once(ROOT . DS . 'inc' . DS . 'core.php');
|
||||
|
||||
header("Content-Type: application/rss+xml; charset=UTF8");
|
||||
|
||||
if (PHP_SAPI === 'cli-server')
|
||||
$_SERVER['SCRIPT_NAME'] = pathinfo(__FILE__, PATHINFO_BASENAME);
|
||||
|
||||
if ($_GET['url'])
|
||||
$url = explode('/', ltrim(parse_url($_GET['url'], PHP_URL_PATH), '/'));
|
||||
else $url = array_filter(explode('/', ltrim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/')));
|
||||
|
||||
array_shift($url);
|
||||
|
||||
$email = $url[0];
|
||||
if (!$email) {
|
||||
http_response_code(404);
|
||||
exit('Error: Email not found');
|
||||
}
|
||||
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) exit();
|
||||
|
||||
$rss = '<?xml version="1.0" ?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<atom:link href="https://' . DOMAIN . '/rss.php" rel="self" type="application/rss+xml" />
|
||||
<title>RSS for ' . $email . '</title>
|
||||
<link>https://' . DOMAIN . '/#' . $email . '</link>
|
||||
<description>RSS Feed for email address ' . $email . '</description>
|
||||
<lastBuildDate>' . date(DateTime::RFC2822, time()) . '</lastBuildDate>
|
||||
<image>
|
||||
<title>RSS for ' . $email . '</title>
|
||||
<url>https://raw.githubusercontent.com/HaschekSolutions/opentrashmail/master/web/imgs/logo_300.png</url>
|
||||
<link>https://github.com/HaschekSolutions/opentrashmail</link>
|
||||
</image>';
|
||||
|
||||
$emaildata = getEmailsOfEmail($email);
|
||||
foreach ($emaildata as $id => $d) {
|
||||
$data = getEmail($email, $id);
|
||||
//var_dump($data);
|
||||
$time = substr($id, 0, -3);
|
||||
$date = date("Y-m-d H:i", $time);
|
||||
$att_text = array();
|
||||
$encl = array();
|
||||
if (is_array($data['parsed']['attachments']))
|
||||
foreach ($data['parsed']['attachments'] as $filename) {
|
||||
$filepath = ROOT . DS . '..' . DS . 'data' . DS . $email . DS . 'attachments' . DS . $filename;
|
||||
$parts = explode('-', $filename);
|
||||
$fid = $parts[0];
|
||||
$fn = $parts[1];
|
||||
$url = 'https://' . DOMAIN . '/api.php?a=attachment&email=' . $email . '&id=' . $fid . '&filename=' . $fn;
|
||||
//$encl[] = '<enclosure url="'.rawurlencode($url).'" length="'.filesize($filepath).'" type="'.mime_content_type($filepath).'" />';
|
||||
$att_text[] = "<a href='$url' target='_blank'>$fn</a>";
|
||||
}
|
||||
$rss .= '
|
||||
<item>
|
||||
<title><![CDATA[' . $data['parsed']['subject'] . ']]></title>
|
||||
<pubDate>' . date(DateTime::RFC2822, $time) . '</pubDate>
|
||||
<link>https://' . DOMAIN . '/#' . $email . '</link>
|
||||
<guid>https://' . DOMAIN . '/api.php?a=load&email=' . $email . '&id=' . $id . '&raw=true</guid>
|
||||
<description>
|
||||
<![CDATA[
|
||||
Email from: ' . htmlentities($data['from']) . '<br/>
|
||||
Email to: ' . (is_array($data['rcpts']) ? htmlentities(implode(',', $data['rcpts'])) : htmlentities($email)) . '<br/>
|
||||
' . ((count($att_text) > 0) ? 'Attachments:<br/>' . array2ul($att_text) . '<br/>' : '') . '
|
||||
<a href="https://' . DOMAIN . '/api.php?a=load&email=' . $email . '&id=' . $id . '&raw=true">View raw email</a> <br/>
|
||||
<br/>---------<br/><br/>
|
||||
' . ($data['parsed']['htmlbody'] ? $data['parsed']['htmlbody'] : nl2br(htmlentities($data['parsed']['body']))) . '
|
||||
]]>
|
||||
</description>
|
||||
' ./*((count($encl)>0)?implode('<br/>',$encl):'').*/ '
|
||||
</item>';
|
||||
|
||||
//if (++$i > 5) break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
$rss .= '</channel>
|
||||
</rss> ';
|
||||
|
||||
echo $rss;
|
||||
|
||||
|
||||
function array2ul($array)
|
||||
{
|
||||
$out = "<ul>";
|
||||
foreach ($array as $key => $elem) {
|
||||
$out .= "<li>$elem</li>";
|
||||
}
|
||||
$out .= "</ul>";
|
||||
return $out;
|
||||
}
|
29
web/templates/account-list.html.php
Normal file
29
web/templates/account-list.html.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<div>
|
||||
<a role="button" class="outline" href="/json/listaccounts" target="_blank"><i class="fas fa-file-code"></i> JSON API</a>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Email Addess</th>
|
||||
<th>Emails in Inbox</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach($emails as $email): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/address/<?= $email; ?>" hx-get="/api/address/<?= $email; ?>" hx-push-url="/address/<?= $email; ?>" hx-target="#main">
|
||||
<?= escape($email) ?>
|
||||
</a>
|
||||
</td>
|
||||
<td><?= countEmailsOfAddress($email); ?></td>
|
||||
<td>
|
||||
<a href="/address/<?= $email; ?>" hx-get="/api/address/<?= $email; ?>" hx-push-url="/address/<?= $email; ?>" hx-target="#main" role="button" >Show</a>
|
||||
<a href="#" role="button" hx-get="/api/deleteaccount/<?= $email ?>" hx-confirm="Are you sure to delete this account and all its emails?" hx-target="closest tr" hx-swap="outerHTML swap:1s">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
53
web/templates/email-table.html.php
Normal file
53
web/templates/email-table.html.php
Normal file
|
@ -0,0 +1,53 @@
|
|||
<nav aria-label="breadcrumb">
|
||||
<ul>
|
||||
<li><?= escape($email) ?></li>
|
||||
<li></li>
|
||||
<li></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<div>
|
||||
<a role="button" class="outline" href="#" id="copyemailbtn" onclick="copyEmailToClipboard();return false;"><i class="far fa-clipboard"></i> Copy address to clipboard</a>
|
||||
<a role="button" class="outline" href="/rss/<?= $email ?>" target="_blank"><i class="fas fa-rss"></i> RSS Feed</a>
|
||||
<a role="button" class="outline" href="/json/<?= $email ?>" target="_blank"><i class="fas fa-file-code"></i> JSON API</a>
|
||||
</div>
|
||||
|
||||
<table role="grid">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col">From</th>
|
||||
<th scope="col">Subject</th>
|
||||
<th scope="col">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<?php if(count($emails)==0): ?>
|
||||
<tr>
|
||||
<td colspan="5"><center>No emails received on this address (yet..)</center></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php foreach($emails as $unixtime => $ed): ?>
|
||||
<tr>
|
||||
<th scope="row"><?= ++$i; ?></th>
|
||||
<td id="date-td-<?= $i ?>"><script>document.getElementById('date-td-<?= $i ?>').innerHTML = moment.unix(parseInt(<?=$unixtime?>/1000)).format('<?= $dateformat; ?>');</script></td>
|
||||
<td><?= escape($ed['from']) ?></td>
|
||||
<td><?= escape($ed['subject']) ?></td>
|
||||
<td>
|
||||
<a href="/read/<?= $email ?>/<?= $ed['id'] ?>" hx-get="/api/read/<?= $email ?>/<?= $ed['id'] ?>" hx-push-url="/read/<?= $email ?>/<?= $ed['id'] ?>" hx-target="#main" role="button">Open</a>
|
||||
<a href="#" hx-get="/api/delete/<?= $email ?>/<?= $ed['id'] ?>" hx-confirm="Are you sure?" hx-target="closest tr" hx-swap="outerHTML swap:1s" role="button">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
|
||||
<script>history.pushState({urlpath:"/address/<?= $email ?>"}, "", "/address/<?= $email ?>");</script>
|
||||
<script>
|
||||
function copyEmailToClipboard(){
|
||||
navigator.clipboard.writeText("<?= $email ?>");
|
||||
document.getElementById('copyemailbtn').innerHTML = '<i class="fas fa-check-circle" style="color: green;"></i> Copied!';
|
||||
}
|
||||
</script>
|
52
web/templates/email.html.php
Normal file
52
web/templates/email.html.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<nav aria-label="breadcrumb">
|
||||
<ul>
|
||||
<li><a href="/address/<?= $email ?>" hx-get="/api/address/<?= $email ?>" hx-target="#main"><?= escape($email) ?></a></li>
|
||||
<li><?= escape($emaildata['parsed']['subject']) ?></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<article>
|
||||
<header>
|
||||
<p>Subject: <?= escape($emaildata['parsed']['subject']) ?></p>
|
||||
<p>Received: <span id="date2-<?= $mailid ?>"><script>document.getElementById('date2-<?= $mailid ?>').innerHTML = moment.unix(parseInt(<?=$mailid?>/1000)).format('<?= $dateformat; ?>');</script></span></p>
|
||||
|
||||
<p>
|
||||
Reciepients:
|
||||
<?php foreach ($emaildata['rcpts'] as $to) : ?>
|
||||
<small class="badge"><?= escape($to) ?></small>
|
||||
<?php endforeach; ?>
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div id="emailbody">
|
||||
<?php if($emaildata['parsed']['htmlbody']): ?>
|
||||
<a href="#" hx-confirm="Warning: HTML may contain tracking functionality or scripts. Do you want to proceed?" hx-get="/api/raw-html/<?= $email ?>/<?= $mailid ?>" hx-target="#emailbody" role="button" class="secondary outline">Render email in HTML</a>
|
||||
<?php endif; ?>
|
||||
<hr>
|
||||
<pre><?= nl2br(escape($emaildata['parsed']['body'])) ?></pre>
|
||||
</div>
|
||||
<footer>
|
||||
Attachments
|
||||
<div>
|
||||
<?php if (count($emaildata['parsed']['attachments']) == 0) : ?>
|
||||
<small class="secondary">No attachments</small>
|
||||
<?php endif; ?>
|
||||
<ul>
|
||||
<?php foreach ($emaildata['parsed']['attachments'] as $attachment) : ?>
|
||||
<li>
|
||||
<a target="_blank" href="/api/attachment/<?= $email ?>/<?= $attachment ?>"><?= escape($attachment) ?></a>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
|
||||
<article>
|
||||
<header>Raw email</header>
|
||||
<a href="/api/raw/<?= $email ?>/<?= $mailid ?>" target="_blank">Open in new Window</a>
|
||||
<pre><button hx-get="/api/raw/<?= $email ?>/<?= $mailid ?>" hx-swap="outerHTML">Load Raw Email</button></pre>
|
||||
</article>
|
||||
|
||||
<!--
|
||||
<script>history.pushState({email:"<?= $email ?>",id:"<?= $mailid ?>"}, "", "/read/<?= $email ?>/<?= $mailid ?>");</script> -->
|
37
web/templates/index.html.php
Normal file
37
web/templates/index.html.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="/css/pico.min.css">
|
||||
<link rel="stylesheet" href="/css/fontawesome.min.css">
|
||||
<link rel="stylesheet" href="/css/opentrashmail.css">
|
||||
<title>Open Trashmail</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<nav>
|
||||
<ul>
|
||||
<li><img src="/imgs/logo_300_light.png" width="50px" /> Open Trashmail</li>
|
||||
<li><input id="email" hx-post="/api/address" hx-target="#main" name="email" type="email" hx-trigger="input changed delay:500ms" placeholder="email address" aria-label="email address"></li>
|
||||
<li><button hx-get="/api/random" hx-target="#main"><i class="fas fa-random"></i> Generate random</button></li>
|
||||
<?php if($settings['SHOW_ACCOUNT_LIST']): ?><li><button hx-get="/api/listaccounts" hx-target="#main" hx-push-url="/listaccounts"><i class="fas fa-list"></i> List accounts</button></li><?php endif; ?>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<button class="htmx-indicator" aria-busy="true">Loading…</button>
|
||||
|
||||
<main id="main" class="container" hx-get="/api/<?= $url ?>" hx-trigger="load">
|
||||
|
||||
</main>
|
||||
|
||||
<script src="/js/htmx.min.js"></script>
|
||||
<script src="/js/namegenerator.js"></script>
|
||||
<!-- <script src="/js/opentrashmail.js"></script> -->
|
||||
<script src="/js/moment-with-locales.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
6
web/templates/intro.html
Normal file
6
web/templates/intro.html
Normal file
|
@ -0,0 +1,6 @@
|
|||
<div class="text-center">
|
||||
<img src="/imgs/logo_300_roundbg.png" alt="OpenTrashmail Logo">
|
||||
<h1>Welcome to OpenTrashmail</h1>
|
||||
<p><a href="https://github.com/HaschekSolutions/opentrashmail" target="_blank" role="button" class="secondary"><i class="fab fa-github"></i> View on Github</a></p>
|
||||
<p>OpenTrashmail is an open source, selfhostable, disposable email service that helps you protect your privacy online. With OpenTrashmail, you can create your own trashmail addresses using your own domain, subdomain or wildcard domain</p>
|
||||
</div>
|
45
web/templates/rss.xml.php
Normal file
45
web/templates/rss.xml.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" ?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<atom:link href="<?= $url ?>/rss/<?= $email ?>" rel="self" type="application/rss+xml" />
|
||||
<title>RSS for <?= $email ?></title>
|
||||
<link><?= $url ?>/eml/<?= $email ?></link>
|
||||
<description>RSS Feed for email address <?= $email ?></description>
|
||||
<lastBuildDate><?= date(DateTime::RFC2822, time()) ?></lastBuildDate>
|
||||
<image>
|
||||
<title>RSS for <?= $email ?></title>
|
||||
<url>https://raw.githubusercontent.com/HaschekSolutions/opentrashmail/master/web/imgs/logo_300.png</url>
|
||||
<link>https://github.com/HaschekSolutions/opentrashmail</link>
|
||||
</image>
|
||||
<?php foreach ($emaildata as $id => $d):
|
||||
$data = getEmail($email, $id);
|
||||
$time = substr($id, 0, -3);
|
||||
$att_text = [];
|
||||
if (is_array($data['parsed']['attachments']))
|
||||
foreach ($data['parsed']['attachments'] as $filename) {
|
||||
$filepath = ROOT . DS . '..' . DS . 'data' . DS . $email . DS . 'attachments' . DS . $filename;
|
||||
$parts = explode('-', $filename);
|
||||
$fid = $parts[0];
|
||||
$fn = $parts[1];
|
||||
$att_url = $url . '/api/attachment/' . $email . '/' . $filename;
|
||||
$att_text[] = "<a href='$att_url' target='_blank'>$fn</a>";
|
||||
}
|
||||
?>
|
||||
<item>
|
||||
<title><![CDATA[<?= $data['parsed']['subject'] ?>]]></title>
|
||||
<pubDate><?= date(DateTime::RFC2822, $time) ?></pubDate>
|
||||
<link><?= $url ?>/eml/<?= $email ?>/<?= $id ?></link>
|
||||
<description>
|
||||
<![CDATA[
|
||||
Email from: <?= escape($d['from']) ?><br/>
|
||||
Email to: <?= escape(implode(';',$data['rcpts'])) ?><br/>
|
||||
<?= ((count($att_text) > 0) ? 'Attachments:<br/>' . array2ul($att_text) . '<br/>' : '') ?>
|
||||
<a href="<?= $url ?>api/raw/test@0xv.eu/1699459401553">View raw email</a> <br/>
|
||||
<br/>---------<br/><br/>
|
||||
<?= ($data['parsed']['htmlbody'] ? $data['parsed']['htmlbody'] : nl2br(htmlentities($data['parsed']['body']))) ?>
|
||||
]]>
|
||||
</description>
|
||||
</item>
|
||||
<?php endforeach; ?>
|
||||
</channel>
|
||||
</rss>
|
Loading…
Reference in a new issue