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