#!/usr/bin/php
<?php
/**
 * ```
 *                        _ _                    _ _ _           
 *  __ _ _  _ __ _ _ _ __| (_)__ _ _ _ ___ _ __ (_) | |_ ___ _ _ 
 * / _` | || / _` | '_/ _` | / _` | ' \___| '  \| | |  _/ -_) '_|
 * \__, |\_,_\__,_|_| \__,_|_\__,_|_||_|  |_|_|_|_|_|\__\___|_|  
 * |___/                                                         
 * ```
 *
 * Guardian-milter - Multi-purpose security milter
 *
 * @copyright Noumenia (C) 2019 - All rights reserved - Software Development - www.noumenia.gr
 * @license GNU GPL v3.0
 * @package guardian-milter
 * @subpackage cli
 */

// Cli execution only
if(php_sapi_name() !== "cli") {

	echo "[ERROR] This script can only be executed under a PHP shell cli.\n";
	exit(9);

}

// Parse command line parameters
$cmdParameters = getopt(

	// Short
	"Vhvu:g:p:l:c:i:",

	// Long
	array(
		"version",
		"help",
		"verbose",
		"user:",
		"group:",
		"pid:",
		"processlimit:",
		"connection:",
		"ini:",
		"addHeader",
		"reject",
		"removeName"
	)

);
if($cmdParameters === false)
	$cmdParameters = array();

// Handle invalid parameters
foreach($cmdParameters as &$value) {

	// Convert arrays of multiple parameters of the same name into a single string
	if(is_array($value))
		$value = array_pop($value);

	if(!is_string($value))
		$value = "";

}
/** @var array<string, string> $cmdParameters */

// Clean-up
unset($value);

// Composer or RPM packages
if(is_file(__DIR__ . "/vendor/autoload.php")) {

	require_once(__DIR__ . "/vendor/autoload.php");

} else {

	// Autoload libLoggerPHP
	require_once("/usr/share/php/libLoggerPHP/controller/common.inc.php");

	// Autoload libSDManagerPHP
	require_once("/usr/share/php/libSDManagerPHP/controller/common.inc.php");

	// Autoload libMilterPHP
	require_once("/usr/share/php/libMilterPHP/controller/common.inc.php");

}

// Load common library objects and default configuration parameters
if(is_file(__DIR__ . "/controller/common.inc.php")) {

	require_once(__DIR__ . "/controller/common.inc.php");

} elseif(is_file("/usr/share/php/guardian-milter/controller/common.inc.php")) {

	require_once("/usr/share/php/guardian-milter/controller/common.inc.php");

} else {

	$error = "[guardian-milter: ERROR - Failed to load the common autoloader: guardian-milter/controller/common.inc.php]";
	syslog(LOG_ERR, $error);
	echo $error . "\n";
	exit(9);

}

// Parse parameters
if(
	isset($cmdParameters['V']) ||
	isset($cmdParameters['version']) ||
	isset($cmdParameters['h']) ||
	isset($cmdParameters['help'])
) {

	if(is_file(__DIR__."/controller/help.inc.php")) {

		include __DIR__."/controller/help.inc.php";

	} elseif(is_file("/usr/share/php/guardian-milter/controller/help.inc.php")) {

		include "/usr/share/php/guardian-milter/controller/help.inc.php";

	} else {

		$error = "[guardian-milter: ERROR - Failed to load the help file: guardian-milter/controller/help.inc.php]";
		syslog(LOG_ERR, $error);
		echo $error . "\n";
		exit(9);

	}

}
// Daemon user
if(isset($cmdParameters['u']))
	Config::write("daemonUser", $cmdParameters['u']);
elseif(isset($cmdParameters['user']))
	Config::write("daemonUser", $cmdParameters['user']);

// Daemon group
if(isset($cmdParameters['g']))
	Config::write("daemonGroup", $cmdParameters['g']);
elseif(isset($cmdParameters['group']))
	Config::write("daemonGroup", $cmdParameters['group']);

// PID file
if(isset($cmdParameters['p']))
	Config::write("filePid", $cmdParameters['p']);
elseif(isset($cmdParameters['pid']))
	Config::write("filePid", $cmdParameters['pid']);

// Process limit
if(isset($cmdParameters['l']))
	Config::write("processLimit", intval($cmdParameters['l']));
elseif(isset($cmdParameters['processlimit']))
	Config::write("processLimit", intval($cmdParameters['processlimit']));

// Connection
if(isset($cmdParameters['c']))
	Config::write("connection", $cmdParameters['c']);
elseif(isset($cmdParameters['connection']))
	Config::write("connection", $cmdParameters['connection']);

// Add Authentication-Results header
if(isset($cmdParameters['addHeader']))
	Config::write("addHeader", true);

// Reject failed emails
if(isset($cmdParameters['reject']))
	Config::write("reject", true);

// Remove From: header name part
if(isset($cmdParameters['removeName']))
	Config::write("removeName", true);

// Supported actions
/** @var array<int> $supportedActions */
$supportedActions = array(
	SMFIF_ADDHDRS,	// Add headers
	SMFIF_ADDRCPT,	// Add recipients
	SMFIF_CHGHDRS,	// Change headers
	SMFIF_CHGBODY	// Change body
);

// Ignore content that is not being used by the milter
/** @var array<int> $ignoredContent */
$ignoredContent = array(
	SMFIP_NOEOH	// Ignore the end of headers action
);
if(LIBMILTERPHP_PROTOCOL >= 4)
	$ignoredContent[] = SMFIP_NODATA; // Ignore DATA action

// Type checks
$daemonUser = Config::readStr("daemonUser");
$daemonGroup = Config::readStr("daemonGroup");
$filePid = Config::readStr("filePid");
$processLimit = Config::read("processLimit");
if(!is_int($processLimit))
	$processLimit = 1024;
$connection = Config::readStr("connection");
$timeout = intval(Config::read("timeout"));

// Transform horrible winmail.dat (TNEF) into MIME attachments
if(Config::read("winmailDatToMime") === true) {

	if(is_file("/usr/bin/tnef"))
		Config::write("winmailDatCommand", "/usr/bin/tnef");
	elseif(is_file("/usr/bin/ytnef"))
		Config::write("winmailDatCommand", "/usr/bin/ytnef");
	else {

		Config::write("winmailDatToMime", false);
		\libLoggerPHP\Log::warning("[guardian-milter: WARNING - tnef or ytnef not found, winmailDatToMime has been disabled]");

	}

}

// Change current directory to the same location as the file PID
if(!empty($filePid)) {

	// Extract the directory only
	$dirPid = dirname($filePid);

	// Validate that it exists
	if(!is_dir($dirPid)) {

		// Create PID directory
		$rc = @mkdir($dirPid, 0755);
		if($rc === false) {

			\libLoggerPHP\Log::error("[guardian-milter: ERROR - PID directory error, could not create the PID directory]");
			exit(9);

		}

		$rc = chown($dirPid, $daemonUser);
		if($rc === false) {

			\libLoggerPHP\Log::error("[guardian-milter: ERROR - PID directory error, could not set the user owner]");
			exit(9);

		}

		$rc = chgrp($dirPid, $daemonGroup);
		if($rc === false) {

			\libLoggerPHP\Log::error("[guardian-milter: ERROR - PID directory error, could not set the group owner]");
			exit(9);

		}

	}

	// Change to the PID directory
	$rc = chdir($dirPid);
	if($rc === false) {

		\libLoggerPHP\Log::error("[guardian-milter: ERROR - Failed to change to the PID directory: " . $dirPid . "]");
		exit(9);

	}

	// Clean-up
	unset($dirPid);

}

// Create daemon object
$daemon = new Daemon(
	$daemonUser,
	$daemonGroup,
	$filePid,
	$processLimit,
	$connection,
	$timeout,
	$supportedActions,
	$ignoredContent
);

// Accept connections
$daemon->acceptConnections();

