Initial version

This commit is contained in:
Kumi 2020-09-03 08:31:10 +02:00
commit d55282ec38
7 changed files with 767 additions and 0 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "vendor/PHPMailer"]
path = vendor/PHPMailer
url = https://github.com/PHPMailer/PHPMailer

15
config.php Normal file
View file

@ -0,0 +1,15 @@
<?php
$MAIL_HOST = "localhost";
$MAIL_USER = ""; // Leave blank to not use authentication
$MAIL_PASS = "";
$MAIL_STARTTLS = false;
$MAIL_SMTPS = false;
$MAIL_PORT = null; // Default: 587 (no encryption/STARTTLS), 465 (SMTPS)
$MAIL_FROM_MAIL = "noreply@expmail.example"; // Leave blank to use $MAIL_USER
$MAIL_FROM_NAME = "EXPMail";
$API_KEYS = [
"verysecretstring"
]

544
doc/index.html Normal file

File diff suppressed because one or more lines are too long

113
doc/swagger.json Normal file
View file

@ -0,0 +1,113 @@
swagger: "2.0"
info:
description: "A simple endpoint to send email messages"
version: "0.1"
title: "EXPMail"
contact:
email: "support@kumi.systems"
host: "expmail.kumi.live"
tags:
- name: "sending"
description: "Sending out an email"
schemes:
- "https"
paths:
/sender.php:
post:
tags:
- "sending"
summary: "Send out an email"
operationId: "sendMail"
consumes:
- "application/json"
produces:
- "application/json"
parameters:
- in: "body"
name: "data"
description: "JSON defining the email message to be sent"
required: true
schema:
$ref: "#/definitions/Mail"
responses:
200:
description: "The request was received and processed."
schema:
type: "object"
properties:
status:
type: "string"
description: "\"Success\" if message was successfully sent, else \"error\""
required: true
error:
type: "string"
description: "Error message, only included if an error has occurred"
required: false
definitions:
Attachment:
type: "object"
properties:
url:
type: "string"
format: "url"
required: true
description: "URL from where to fetch the file to attach"
filename:
type: "string"
required: false
description: "File name to use for email attachment. If not set, defaults to name from Content-Disposition header of URL if it exists, else the base name of the URL."
Recipient:
type: "object"
properties:
email:
type: "string"
format: "email"
required: true
description: "Email address of the recipient"
name:
type: "string"
required: false
description: "Name of the recipient"
Mail:
type: "object"
properties:
subject:
type: "string"
description: "Subject of the email"
html:
type: "string"
description: "String containing the HTML content of the email. Takes precedence over `htmlurl` if provided. If both `html` and `text` or `texturl` are provided, will create a multi-part MIME message."
htmlurl:
type: "string"
description: "String containing the URL to a file containing the HTML content of the email. Ignored (but still validated) if `html` if provided. If both `htmlurl` and `text` or `texturl` are provided, will create a multi-part MIME message."
text:
type: "string"
description: "String containing the plain text content of the email. Takes precedence over `texturl` if provided. If both `text` and `html` or `htmlurl` are provided, will create a multi-part MIME message."
texturl:
type: "string"
description: "String containing the URL to a file containing the plain text content of the email. Ignored (but still validated) if `text` is provided. If both `texturl` and `html` or `htmlurl` are provided, will create a multi-part MIME message."
recipients:
required: true
type: "array"
description: "Array of `Recipient` objects to be used as \"To:\" addresses for the email"
items:
$ref: "#/definitions/Recipient"
ccs:
type: "array"
description: "Array of `Recipient` objects to be used as \"CC:\" addresses for the email"
items:
$ref: "#/definitions/Recipient"
bccs:
type: "array"
description: "Array of `Recipient` objects to be used as \"BCC:\" addresses for the email"
items:
$ref: "#/definitions/Recipient"
attachments:
type: "array"
description: "Array of `Attachment` objects to be attached to the email"
items:
$ref: "#/definitions/Attachment"
key:
type: "string"
description: "API key to authenticate request with"

22
helpers.php Normal file
View file

@ -0,0 +1,22 @@
<?php
function getFilename($url){
$content = get_headers($url,1);
$content = array_change_key_case($content, CASE_LOWER);
if ($content['content-disposition']) {
$tmp_name = explode('=', $content['content-disposition']);
if ($tmp_name[1]) $realfilename = trim($tmp_name[1],'";\'');
} else {
$stripped_url = preg_replace('/\\?.*/', '', $url);
$realfilename = basename($stripped_url);
}
}
function checkURL($url) {
$url = filter_var($url, FILTER_SANITIZE_URL);
if (filter_var($url, FILTER_VALIDATE_URL) === FALSE || !in_array(strtolower(parse_url($url, PHP_URL_SCHEME)), ['http','https'], true)) return false;
$file_headers = @get_headers($url);
if ($httpStatus<400) return true;
return false;
}

66
sender.php Normal file
View file

@ -0,0 +1,66 @@
<?php
require_once 'vendor/autoload.php';
require_once 'config.php';
require_once 'helpers.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
$mailer = new PHPMailer(true);
try {
if (!$_POST || !$data=$_POST["data"]) throw new Exception("There is nothing here.");
$json = json_decode($data, true);
if (!$json["key"]) throw new Exception("What's the code word?");
if (!in_array($json["key"], $API_KEYS)) throw new Exception("Who are you?");
if ($_POST["htmlurl"] && !checkURL($_POST["htmlurl"])) throw new Exception("Your HTML URL isn't working.");
if ($_POST["texturl"] && !checkURL($_POST["texturl"])) throw new Exception("Your text URL isn't working.");
$html = ($_POST["html"] ? $_POST["html"] : ($_POST["htmlurl"] ? file_get_contents($_POST["htmlurl"]) : null));
$text = ($_POST["text"] ? $_POST["text"] : ($_POST["texturl"] ? file_get_contents($_POST["texturl"]) : null));
$mailer->isSMTP();
$mailer->Host = $MAIL_HOST;
$mailer->SMTPAuth = (bool) $MAIL_USER;
$mailer->Username = $MAIL_USER;
$mailer->Password = $MAIL_PASS;
$mailer->SMTPSecure = ($MAIL_STARTTLS ? PHPMailer::ENCRYPTION_STARTTLS : ($MAIL_SMTPS ? PHPMailer::ENCRYPTION_SMTPS : false));
$mailer->Port = ($MAIL_PORT ? $MAIL_PORT : ($MAIL_SMTPS ? 465 : 587));
$mailer->setFrom(($MAIL_FROM_MAIL ? $MAIL_FROM_MAIL : $MAIL_USER), $MAIL_FROM_NAME);
foreach ($json["recipients"] as $recipient) $mailer->addAddress($recipient["email"], $recipient["name"]);
foreach ($json["ccs"] as $cc) $mailer->addCC($cc["email"], $cc["name"]);
foreach ($json["bccs"] as $bcc) $mailer->addBCC($bcc["email"], $bcc["name"]);
$mailer->isHTML((bool) $html);
$mailer->Subject = $json["subject"];
$mailer->Body = ($html ? $html : $text);
$mailer->AltBody = ($html ? $text : null);
foreach ($attachments as $attachment) {
$tempfile = tempnam(sys_get_temp_dir(), "EXPMAIL_");
file_put_contents($tempfile, file_get_contents($attachment["url"]));
$filename = ($attachment["filename"] ? $attachment["filename"] : getFilename($attachment["url"]));
$mailer->addAttachment($tempfile, $filename);
}
$mailer->send();
$response = array(
"status" => "success"
);
} catch (Exception $e) {
$response = array(
"status" => "error",
"error" => "{$e->getMessage()}"
);
};
echo json_encode($response);

4
vendor/autoload.php vendored Normal file
View file

@ -0,0 +1,4 @@
<?php
require_once 'vendor/PHPMailer/src/Exception.php';
require_once 'vendor/PHPMailer/src/PHPMailer.php';