first commit
This commit is contained in:
commit
aacf4b23ae
70
index.php
Normal file
70
index.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
define('EMAILS_BASE', __DIR__ . '/../data/emails');
|
||||||
|
|
||||||
|
require_once __DIR__."/lib.php";
|
||||||
|
|
||||||
|
$is_cli = (php_sapi_name() === 'cli');
|
||||||
|
$output_target = $is_cli ? '/dev/stdout' : null;
|
||||||
|
|
||||||
|
$input = get_input($is_cli);
|
||||||
|
|
||||||
|
|
||||||
|
$inputJSON = json_decode($input, true);
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
fail("Invalid JSON", $is_cli, $output_target);
|
||||||
|
}
|
||||||
|
|
||||||
|
$raw_msg = $inputJSON['raw'] ?? null;
|
||||||
|
$from = $inputJSON['from']['value'][0]['address'] ?? null;
|
||||||
|
|
||||||
|
$filename = uniqid('', true) . ".eml";
|
||||||
|
|
||||||
|
// --- Save to domain/sender folder ---
|
||||||
|
list($from_local,$from_domain) = explode("@",$from);
|
||||||
|
|
||||||
|
$sender_folder = EMAILS_BASE . "/$from_domain/$from_local";
|
||||||
|
if (!is_dir($sender_folder)) {
|
||||||
|
mkdir($sender_folder, 0775, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$target_path = "$sender_folder/$filename";
|
||||||
|
file_put_contents($target_path, $raw_msg);
|
||||||
|
|
||||||
|
// --- Run index.sh or index.php if exists ---
|
||||||
|
$index_sh = "$sender_folder/index.sh";
|
||||||
|
$index_php = "$sender_folder/index.php";
|
||||||
|
|
||||||
|
if (file_exists($index_sh)) {
|
||||||
|
$proc = proc_open("/bin/bash $index_sh", [
|
||||||
|
0 => ['pipe', 'r'],
|
||||||
|
1 => ['pipe', 'w'],
|
||||||
|
2 => ['pipe', 'w']
|
||||||
|
], $pipes);
|
||||||
|
if (is_resource($proc)) {
|
||||||
|
fwrite($pipes[0], $raw_msg);
|
||||||
|
fclose($pipes[0]);
|
||||||
|
fclose($pipes[1]);
|
||||||
|
fclose($pipes[2]);
|
||||||
|
proc_close($proc);
|
||||||
|
}
|
||||||
|
} elseif (file_exists($index_php)) {
|
||||||
|
$cmd = "php $index_php";
|
||||||
|
$proc = proc_open($cmd, [
|
||||||
|
0 => ['pipe', 'r'],
|
||||||
|
1 => ['pipe', 'w'],
|
||||||
|
2 => ['pipe', 'w']
|
||||||
|
], $pipes);
|
||||||
|
if (is_resource($proc)) {
|
||||||
|
fwrite($pipes[0], $raw_msg);
|
||||||
|
fclose($pipes[0]);
|
||||||
|
fclose($pipes[1]);
|
||||||
|
fclose($pipes[2]);
|
||||||
|
proc_close($proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$is_cli) {
|
||||||
|
http_response_code(200);
|
||||||
|
echo "Webhook processed successfully.";
|
||||||
|
}
|
||||||
150
lib.php
Normal file
150
lib.php
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a message depending on CLI or web context
|
||||||
|
*/
|
||||||
|
function log_message(string $message, bool $is_cli, ?string $target = null): void {
|
||||||
|
if ($is_cli) {
|
||||||
|
file_put_contents($target ?? '/dev/stdout', $message . PHP_EOL);
|
||||||
|
} else {
|
||||||
|
error_log($message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exit the script with an error message
|
||||||
|
*/
|
||||||
|
function fail(string $message, bool $is_cli, ?string $target = null, int $http_code = 400): void {
|
||||||
|
if ($is_cli) {
|
||||||
|
log_message($message, $is_cli, $target);
|
||||||
|
} else {
|
||||||
|
http_response_code($http_code);
|
||||||
|
echo $message;
|
||||||
|
}
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitize an email component to use in file paths
|
||||||
|
*/
|
||||||
|
function sanitize_path_part(string $part): string {
|
||||||
|
return preg_replace('/[^a-zA-Z0-9_.+-]/', '_', $part);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure Maildir structure exists for a given email
|
||||||
|
*/
|
||||||
|
function ensure_maildir(string $maildir_path, bool $is_cli, ?string $target): void {
|
||||||
|
if (is_dir($maildir_path)) return;
|
||||||
|
|
||||||
|
if (!mkdir($maildir_path, 0775, true)) {
|
||||||
|
log_message("Failed to create Maildir structure: $maildir_path", $is_cli, $target);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (['new', 'cur', 'tmp'] as $sub) {
|
||||||
|
mkdir("$maildir_path/$sub", 0775, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save an email message to a given Maildir
|
||||||
|
*/
|
||||||
|
function save_email(string $path, string $msg, bool $is_cli, ?string $target): void {
|
||||||
|
if (file_put_contents($path, $msg) === false) {
|
||||||
|
log_message("Failed to save email to $path", $is_cli, $target);
|
||||||
|
} else {
|
||||||
|
log_message("Email saved to $path", $is_cli, $target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get input payload depending on the environment
|
||||||
|
*/
|
||||||
|
function get_input(bool $is_cli): string {
|
||||||
|
if ($is_cli) {
|
||||||
|
$options = getopt('', ['cli']);
|
||||||
|
if (!isset($options['cli'])) {
|
||||||
|
fail("Missing --cli option for CLI execution.", true, '/dev/stderr');
|
||||||
|
}
|
||||||
|
return file_get_contents('php://stdin');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
fail("Method not allowed. Use POST.", false, null, 405);
|
||||||
|
}
|
||||||
|
|
||||||
|
return file_get_contents('php://input');
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Logs metadata about an incoming or outgoing email into a local SQLite3 database.
|
||||||
|
*
|
||||||
|
* This function ensures the database exists, creates the schema if it's the first run,
|
||||||
|
* extracts basic metadata (user, domain, subject, size), and inserts it into a table.
|
||||||
|
*
|
||||||
|
* @param string $email The recipient or sender email address.
|
||||||
|
* @param string $raw_msg The full raw email content.
|
||||||
|
* @param string $direction Direction of the email: 'incoming' or 'outgoing'. Default is 'incoming'.
|
||||||
|
*/
|
||||||
|
function log_email_to_sqlite(string $email, string $raw_msg, string $direction = 'incoming'): void {
|
||||||
|
$db_path = '/home/pi/emails.db';
|
||||||
|
$db_initialized = file_exists($db_path);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Open (or create) the SQLite3 database
|
||||||
|
$db = new SQLite3($db_path);
|
||||||
|
|
||||||
|
// Initialize the schema only if the database file did not previously exist
|
||||||
|
if (!$db_initialized) {
|
||||||
|
$db->exec("
|
||||||
|
CREATE TABLE emails (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
direction TEXT CHECK(direction IN ('incoming', 'outgoing')) NOT NULL,
|
||||||
|
user TEXT,
|
||||||
|
domain TEXT,
|
||||||
|
subject TEXT,
|
||||||
|
date TEXT,
|
||||||
|
attachments_no INTEGER,
|
||||||
|
sizeKb INTEGER
|
||||||
|
)
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract local part and domain from the email address
|
||||||
|
$local = preg_replace('/[^a-zA-Z0-9_.+-]/', '_', strstr($email, '@', true));
|
||||||
|
$domain = preg_replace('/[^a-zA-Z0-9_.+-]/', '_', substr(strstr($email, '@'), 1));
|
||||||
|
|
||||||
|
// Attempt to parse the Subject header from the raw message
|
||||||
|
$subject = '';
|
||||||
|
if (preg_match('/^Subject:\s*(.*)$/mi', $raw_msg, $matches)) {
|
||||||
|
$subject = trim($matches[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate message size in kilobytes
|
||||||
|
$size_kb = (int)(strlen($raw_msg) / 1024);
|
||||||
|
|
||||||
|
// Use the current timestamp as the 'date'
|
||||||
|
$timestamp = date('Y-m-d H:i:s');
|
||||||
|
|
||||||
|
// Prepare and execute the insert query
|
||||||
|
$stmt = $db->prepare("
|
||||||
|
INSERT INTO emails (direction, user, domain, subject, date, attachments_no, sizeKb)
|
||||||
|
VALUES (:direction, :user, :domain, :subject, :date, :attachments, :size)
|
||||||
|
");
|
||||||
|
$stmt->bindValue(':direction', $direction, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(':user', $local, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(':domain', $domain, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(':subject', $subject, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(':date', $timestamp, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(':attachments', 0, SQLITE3_INTEGER); // Can be extended later to count real attachments
|
||||||
|
$stmt->bindValue(':size', $size_kb, SQLITE3_INTEGER);
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
// Close the database connection
|
||||||
|
$db->close();
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// Log any unexpected SQLite errors (e.g., permission issues)
|
||||||
|
error_log("SQLite error: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user