summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGab <24553253+gabrix73@users.noreply.github.com>2025-11-16 14:34:37 +0100
committerGitHub <noreply@github.com>2025-11-16 14:34:37 +0100
commit5df20b67288ac60b2213d67400407626a765a4b5 (patch)
tree5cad42a545829db921fd19b59886432d84796f08
parentb43534c33cec426e6e5dbea6c901f94c6f31b254 (diff)
downloadyamnweb-5df20b67288ac60b2213d67400407626a765a4b5.tar.gz
yamnweb-5df20b67288ac60b2213d67400407626a765a4b5.tar.xz
yamnweb-5df20b67288ac60b2213d67400407626a765a4b5.zip
Refactor Tor integration and improve email handling
Refactor Tor integration for YAMN, including new functions for logging, checking Tor availability, and sending emails through Tor. Updated configuration file paths and added SMTP relay handling.
-rw-r--r--tor_extension.php669
1 files changed, 283 insertions, 386 deletions
diff --git a/tor_extension.php b/tor_extension.php
index 9611cad..4c18f9f 100644
--- a/tor_extension.php
+++ b/tor_extension.php
@@ -1,445 +1,342 @@
<?php
/**
- * Secure Tor Extension for YAMN Web Interface
- *
- * Security Features:
- * - No metadata retention (no detailed logging)
- * - Randomized timing delays
- * - Secure command execution
- * - Tor enforcement with verification
- * - Protection against timing attacks
- *
- * This implementation follows privacy-first principles
+ * Tor Extension for YAMN Web Interface
+ * Handles Tor integration for anonymous remailer network
*/
// Constants
define('YAMN_PATH', '/opt/yamn-master/yamn');
-define('YAMN_CONFIG', '/opt/yamn-master/yamn.yml');
+define('YAMN_CONFIG', '/opt/yamn-master/yamn.cfg');
define('TOR_PROXY', '127.0.0.1:9050');
+define('TOR_CONFIG_FILE', '/var/www/yamnweb/tor_config.ini');
define('TORSOCKS_PATH', 'torsocks');
-define('SECURE_POOL_DIR', '/opt/yamn-data/pool');
+
+// SMTP Onion Relays - Primary and Fallback
+define('SMTP_RELAYS', [
+ [
+ 'host' => '4uwpi53u524xdphjw2dv5kywsxmyjxtk4facb76jgl3sc3nda3sz4fqd.onion',
+ 'port' => 25,
+ 'name' => 'Primary Onion SMTP'
+ ],
+ [
+ 'host' => 'xilb7y4kj6u6qfo45o3yk2kilfv54ffukzei3puonuqlncy7cn2afwyd.onion',
+ 'port' => 25,
+ 'name' => 'Fallback Onion SMTP'
+ ]
+]);
+
+/**
+ * Debug logger function
+ *
+ * @param string $message Message to log
+ * @param bool $include_timestamp Whether to include timestamp
+ * @return void
+ */
+function logDebug($message, $include_timestamp = true) {
+ $log_file = '/var/www/yamnweb/debug.log';
+ $log_entry = ($include_timestamp ? date('Y-m-d H:i:s') . " - " : "") . $message . "\n";
+ file_put_contents($log_file, $log_entry, FILE_APPEND);
+}
/**
- * Check if Tor is available and working
- *
- * @return bool True if Tor is available
+ * Check if Tor is available on the system
+ *
+ * @return bool True if Tor service is available
*/
function isTorAvailable() {
+ logDebug("Checking if Tor is available...");
+
// Check if torsocks is installed
- $torsocksPath = shell_exec('which torsocks 2>/dev/null');
- if (empty($torsocksPath)) {
- return false;
+ $torsocks_check = shell_exec('which torsocks 2>&1');
+ if (empty($torsocks_check) || strpos($torsocks_check, 'no torsocks') !== false) {
+ logDebug("WARNING: torsocks not found in PATH. Tor routing may not work correctly.");
+ } else {
+ logDebug("torsocks found: " . trim($torsocks_check));
}
-
+
// Check if Tor SOCKS proxy is listening
- $connection = @fsockopen('127.0.0.1', 9050, $errno, $errstr, 2);
- if (!is_resource($connection)) {
- return false;
+ $connection = @fsockopen('127.0.0.1', 9050, $errno, $errstr, 1);
+ if (is_resource($connection)) {
+ fclose($connection);
+ logDebug("Tor is available: SOCKS proxy connection successful");
+ return true;
}
-
- fclose($connection);
-
- // Verify Tor by checking external IP through Tor
- // This ensures Tor is actually working, not just listening
- $ch = curl_init();
- curl_setopt_array($ch, [
- CURLOPT_URL => 'https://check.torproject.org/api/ip',
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_PROXY => '127.0.0.1:9050',
- CURLOPT_PROXYTYPE => CURLPROXY_SOCKS5_HOSTNAME,
- CURLOPT_TIMEOUT => 30,
- CURLOPT_SSL_VERIFYPEER => true
- ]);
-
- $result = curl_exec($ch);
- $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
- curl_close($ch);
-
- if ($httpCode === 200 && $result) {
- $data = json_decode($result, true);
- return isset($data['IsTor']) && $data['IsTor'] === true;
+
+ logDebug("SOCKS proxy connection failed: $errno - $errstr");
+
+ // Alternative: Check if Tor service is running
+ $tor_running = shell_exec('pgrep -f "tor" | wc -l');
+ if (intval($tor_running) > 0) {
+ logDebug("Tor is available: Tor process is running");
+ return true;
}
-
- return false;
-}
-/**
- * Secure random delay to prevent timing correlation
- *
- * @param int $minSeconds Minimum delay in seconds
- * @param int $maxSeconds Maximum delay in seconds
- */
-function secureRandomDelay($minSeconds = 10, $maxSeconds = 120) {
- $delay = random_int($minSeconds, $maxSeconds);
- sleep($delay);
+ logDebug("Tor is NOT available: No Tor process found");
+ return false;
}
/**
- * Validate remailer chain to prevent injection
- *
- * @param string $chain The remailer chain
- * @return bool True if valid
+ * Check if an email address belongs to an .onion domain
+ *
+ * @param string $email The email address to check
+ * @return bool True if the email is for an .onion domain
*/
-function isValidChain($chain) {
- // Chain should only contain alphanumeric, comma, asterisk, and hyphen
- return preg_match('/^[a-zA-Z0-9,\*\-]+$/', $chain) === 1;
+function isOnionEmail($email) {
+ // Extract domain from email address
+ if (preg_match('/@([^>]+)/', $email, $matches)) {
+ $domain = $matches[1];
+ $is_onion = (strpos($domain, '.onion') !== false);
+ logDebug("Email domain check: $domain - " . ($is_onion ? "is .onion" : "is NOT .onion"));
+ return $is_onion;
+ }
+ logDebug("No domain found in email: $email");
+ return false;
}
/**
- * Secure command execution with proper escaping
- *
- * @param string $command Command to execute
- * @param array &$output Output array
- * @param int &$returnVar Return code
+ * Determine if Tor should be used for the current email
+ *
+ * @param string $to The recipient email address
+ * @return bool True if Tor should be used
*/
-function secureExec($command, &$output, &$returnVar) {
- // Execute with output buffering
- exec($command, $output, $returnVar);
-}
+function shouldUseTor($to) {
+ logDebug("Checking if Tor should be used for recipient: $to");
-/**
- * Send email through YAMN with Tor (always enforced)
- *
- * @param string $chain Remailer chain
- * @param int $copies Number of copies
- * @param string $messageFile Path to message file
- * @param bool $useTor Ignored, Tor is always used
- * @return array Result status
- */
-function sendYamnEmailSecure($chain, $copies, $messageFile, $useTor = true) {
- // Validate inputs
- if (!isValidChain($chain)) {
- return [
- 'success' => false,
- 'errors' => ['Invalid remailer chain format']
- ];
- }
-
- if ($copies < 1 || $copies > 3) {
- return [
- 'success' => false,
- 'errors' => ['Invalid number of copies']
- ];
- }
-
- // Verify prerequisites
- if (!file_exists($messageFile)) {
- return [
- 'success' => false,
- 'errors' => ['Message file not found']
- ];
+ // Use Tor if the destination is an .onion address
+ if (isOnionEmail($to)) {
+ logDebug("Tor will be used: .onion address detected");
+ return true;
}
-
- if (!file_exists(YAMN_PATH) || !is_executable(YAMN_PATH)) {
- return [
- 'success' => false,
- 'errors' => ['YAMN executable not available']
- ];
- }
-
- if (!file_exists(YAMN_CONFIG)) {
- return [
- 'success' => false,
- 'errors' => ['YAMN configuration not found']
- ];
- }
-
- // Verify torsocks is available
- $torsocksCheck = shell_exec('which torsocks 2>/dev/null');
- if (empty($torsocksCheck)) {
- return [
- 'success' => false,
- 'errors' => ['torsocks not installed']
- ];
- }
-
- // Random delay BEFORE adding to pool (prevent timing correlation)
- secureRandomDelay(5, 30);
-
- // Escape arguments properly
- $escapedChain = escapeshellarg($chain);
- $escapedCopies = (int)$copies;
- $escapedMessageFile = escapeshellarg($messageFile);
- $escapedYamnPath = escapeshellarg(YAMN_PATH);
- $escapedConfig = escapeshellarg(YAMN_CONFIG);
-
- // Build command with proper escaping
- // Use process substitution to avoid leaving message file readable
- $commandAddToPool = sprintf(
- '%s %s --config=%s --mail --chain=%s --copies=%d 2>&1 < %s',
- TORSOCKS_PATH,
- $escapedYamnPath,
- $escapedConfig,
- $escapedChain,
- $escapedCopies,
- $escapedMessageFile
- );
-
- // Execute add to pool
- $outputAddToPool = [];
- $returnVarAddToPool = 0;
- secureExec($commandAddToPool, $outputAddToPool, $returnVarAddToPool);
-
- // Check if add to pool succeeded
- if ($returnVarAddToPool !== 0) {
- return [
- 'success' => false,
- 'errors' => ['Failed to add message to pool']
- ];
+
+ // Use Tor if forced by configuration file
+ if (file_exists(TOR_CONFIG_FILE)) {
+ logDebug("Config file exists: " . TOR_CONFIG_FILE);
+ $config = parse_ini_file(TOR_CONFIG_FILE, true);
+ if (isset($config['tor']) && isset($config['tor']['force_tor'])) {
+ $force_tor = ($config['tor']['force_tor'] === 'true' || $config['tor']['force_tor'] === '1');
+ logDebug("Config file force_tor setting: " . ($force_tor ? "true" : "false"));
+ return $force_tor;
+ }
}
-
- // Random delay BETWEEN operations
- secureRandomDelay(3, 15);
-
- // Build send command
- $commandSend = sprintf(
- '%s %s --config=%s -S 2>&1',
- TORSOCKS_PATH,
- $escapedYamnPath,
- $escapedConfig
- );
-
- // Execute send
- $outputSend = [];
- $returnVarSend = 0;
- secureExec($commandSend, $outputSend, $returnVarSend);
-
- // Determine success
- $success = ($returnVarSend === 0 && $returnVarAddToPool === 0);
-
- // Random delay AFTER sending (prevent timing correlation)
- secureRandomDelay(2, 10);
-
- // Return minimal information (no detailed logs)
- if ($success) {
- return [
- 'success' => true
- ];
- } else {
- return [
- 'success' => false,
- 'errors' => ['Failed to send message']
- ];
+
+ // Check environment variables
+ $force_tor_env = getenv('YAMN_TOR_FORCE');
+ if ($force_tor_env) {
+ $result = ($force_tor_env === 'true' || $force_tor_env === '1');
+ logDebug("Environment variable YAMN_TOR_FORCE: " . ($result ? "true" : "false"));
+ return $result;
}
-}
-/**
- * Legacy wrapper for backward compatibility
- * Always uses Tor regardless of $useTor parameter
- */
-function sendYamnEmail($chain, $copies, $messageFile, $useTor = true) {
- return sendYamnEmailSecure($chain, $copies, $messageFile, true);
+ logDebug("Tor will NOT be used: No .onion address or force settings");
+ return false;
}
/**
- * Verify Tor circuit and get new identity
- * Useful before sending to ensure fresh circuit
- *
- * @return bool True if successful
+ * Configure Tor environment settings for YAMN
+ *
+ * @param bool $useTor Whether to force Tor usage
+ * @return void
*/
-function getTorNewIdentity() {
- // Connect to Tor control port
- $fp = @fsockopen('127.0.0.1', 9051, $errno, $errstr, 5);
- if (!$fp) {
- return false;
- }
-
- // Authenticate (assuming no password or cookie auth)
- // In production, use proper authentication
- fwrite($fp, "AUTHENTICATE\r\n");
- $response = fgets($fp);
-
- if (strpos($response, '250') === false) {
- fclose($fp);
- return false;
+function configureTorForYamn($useTor = false) {
+ if ($useTor) {
+ logDebug("Configuring Tor environment variables");
+
+ // Set SOCKS proxy environment variables
+ putenv("SOCKS_PROXY=" . TOR_PROXY);
+ putenv("ALL_PROXY=socks5://" . TOR_PROXY);
+ putenv("socks_proxy=" . TOR_PROXY);
+ putenv("all_proxy=socks5://" . TOR_PROXY);
+ putenv("YAMN_USE_TOR=1");
+ putenv("YAMN_TOR_ENABLED=true");
+ putenv("YAMN_TOR_FORCE=true");
+
+ logDebug("Tor environment configured with proxy: " . TOR_PROXY);
+ } else {
+ logDebug("Clearing Tor environment variables");
+
+ // Clear environment variables when not using Tor
+ putenv("SOCKS_PROXY=");
+ putenv("ALL_PROXY=");
+ putenv("socks_proxy=");
+ putenv("all_proxy=");
+ putenv("YAMN_USE_TOR=0");
+ putenv("YAMN_TOR_ENABLED=false");
+ putenv("YAMN_TOR_FORCE=false");
}
-
- // Request new identity
- fwrite($fp, "SIGNAL NEWNYM\r\n");
- $response = fgets($fp);
-
- fclose($fp);
-
- return strpos($response, '250') !== false;
}
/**
- * Enhanced Tor check with circuit verification
- *
- * @return bool True if Tor is working properly
+ * Test SMTP relay connectivity through Tor
+ *
+ * @param string $host SMTP relay hostname
+ * @param int $port SMTP port
+ * @return bool True if relay is reachable
*/
-function verifyTorCircuit() {
- // Check basic Tor availability
- if (!isTorAvailable()) {
- return false;
- }
-
- // Try to get a new circuit
- // This ensures Tor is not just running but actually functional
- if (!getTorNewIdentity()) {
- // If we can't get new identity, Tor might still work
- // Don't fail, just continue
- }
-
- // Wait a moment for circuit to build
- sleep(2);
-
- return true;
+function testSmtpRelay($host, $port) {
+ logDebug("Testing SMTP relay: $host:$port");
+
+ // Use torsocks to test connection through Tor
+ $command = "timeout 10 torsocks nc -zv $host $port 2>&1";
+ exec($command, $output, $return_var);
+
+ $success = ($return_var == 0);
+ logDebug("SMTP relay test result: " . ($success ? "SUCCESS" : "FAILED"));
+ logDebug("Test output: " . implode(" ", $output));
+
+ return $success;
}
/**
- * Initialize secure environment for YAMN operations
- *
- * @return bool True if successful
+ * Update YAMN config with specific SMTP relay
+ * VERSIONE DISABILITATA - Non modifica il file, usa configurazione statica
+ *
+ * @param string $host SMTP relay hostname
+ * @param int $port SMTP port
+ * @return bool True if update successful
*/
-function initSecureEnvironment() {
- // Ensure secure directories exist
- if (!file_exists(SECURE_POOL_DIR)) {
- mkdir(SECURE_POOL_DIR, 0700, true);
- }
-
- // Set restrictive umask
- umask(0077);
-
- // Verify Tor is available
- if (!isTorAvailable()) {
+function updateYamnConfig($host, $port) {
+ logDebug("Config update DISABLED - using static configuration");
+ logDebug("Requested relay: $host:$port (will be ignored, using config file as-is)");
+
+ // Verifica solo che il file esista
+ if (!file_exists(YAMN_CONFIG)) {
+ logDebug("ERROR: Config file not found: " . YAMN_CONFIG);
return false;
}
-
+
+ logDebug("Config file exists, skipping dynamic update");
return true;
}
/**
- * Clean up function to be called after operations
- * Removes temporary files securely
- *
- * @param string $filepath File to securely delete
+ * Send an email through YAMN with optional Tor routing and automatic fallback
+ *
+ * @param string $chain The remailer chain string (e.g., "entry,middle,exit")
+ * @param int $copies Number of copies to send (1-3)
+ * @param string $messageFile Path to the message file
+ * @param bool $useTor Whether to force Tor usage
+ * @return array Array containing success status, error message, and output
*/
-function secureCleanup($filepath) {
- if (!file_exists($filepath)) {
- return;
- }
-
- $filesize = filesize($filepath);
-
- // DoD 5220.22-M standard: 3-pass overwrite
- for ($i = 0; $i < 3; $i++) {
- $fp = fopen($filepath, 'w');
- if ($fp) {
- fwrite($fp, random_bytes($filesize));
- fclose($fp);
- }
- }
-
- // Final overwrite with zeros
- $fp = fopen($filepath, 'w');
- if ($fp) {
- fwrite($fp, str_repeat("\0", $filesize));
- fclose($fp);
- }
-
- // Delete file
- unlink($filepath);
-}
+function sendYamnEmail($chain, $copies, $messageFile, $useTor = false) {
+ logDebug("=== Starting sendYamnEmail (Static Config Mode) ===");
+ logDebug("Chain: $chain");
+ logDebug("Copies: $copies");
+ logDebug("Message file: $messageFile");
+ logDebug("Use Tor: " . ($useTor ? "YES" : "NO"));
-/**
- * Get Tor circuit information (for debugging only)
- * DO NOT use in production as it may leak information
- *
- * @return array|false Circuit info or false
- */
-function getTorCircuitInfo() {
- // Only enable if explicitly requested via environment variable
- if (getenv('YAMN_DEBUG_TOR') !== 'true') {
- return false;
- }
-
- $fp = @fsockopen('127.0.0.1', 9051, $errno, $errstr, 5);
- if (!$fp) {
- return false;
- }
-
- fwrite($fp, "AUTHENTICATE\r\n");
- $response = fgets($fp);
-
- if (strpos($response, '250') === false) {
- fclose($fp);
- return false;
+ // Configure environment for YAMN to use Tor if needed
+ configureTorForYamn($useTor);
+
+ // Check if message file exists
+ if (!file_exists($messageFile)) {
+ $error = "Message file does not exist: $messageFile";
+ logDebug("ERROR: $error");
+ return [
+ 'success' => false,
+ 'error' => $error,
+ 'relay_used' => null,
+ 'output' => []
+ ];
}
-
- fwrite($fp, "GETINFO circuit-status\r\n");
- $response = '';
- while (!feof($fp)) {
- $line = fgets($fp);
- $response .= $line;
- if (strpos($line, '250 OK') !== false) {
- break;
- }
+
+ // Verifica che il config file esista
+ if (!file_exists(YAMN_CONFIG)) {
+ $error = "Config file does not exist: " . YAMN_CONFIG;
+ logDebug("ERROR: $error");
+ return [
+ 'success' => false,
+ 'error' => $error,
+ 'relay_used' => null,
+ 'output' => []
+ ];
}
-
- fclose($fp);
-
- return ['circuit_status' => $response];
-}
-/**
- * Test Tor connectivity without leaving traces
- *
- * @return array Test results
- */
-function testTorConnectivity() {
- $results = [
- 'tor_available' => false,
- 'socks_proxy' => false,
- 'circuit_working' => false,
- 'torsocks_installed' => false
- ];
-
- // Check SOCKS proxy
- $connection = @fsockopen('127.0.0.1', 9050, $errno, $errstr, 2);
- if (is_resource($connection)) {
- $results['socks_proxy'] = true;
- fclose($connection);
+ // Build YAMN command to add message to pool
+ $command_add_to_pool = YAMN_PATH . " " .
+ "--config=" . YAMN_CONFIG . " " .
+ "--mail " .
+ "--chain=\"$chain\" " .
+ "--copies=$copies < " . escapeshellarg($messageFile) . " 2>&1";
+
+ // Command to send emails in pool
+ $command_send = YAMN_PATH . " " .
+ "--config=" . YAMN_CONFIG . " -S 2>&1";
+
+ // Log the commands
+ logDebug("Add to pool command: $command_add_to_pool");
+
+ // Execute add to pool command
+ logDebug("Executing add to pool command...");
+ exec($command_add_to_pool, $output_add_to_pool, $return_var_add_to_pool);
+
+ // Log the output and return code
+ logDebug("Add to pool output: " . implode("\n", $output_add_to_pool));
+ logDebug("Add to pool return code: $return_var_add_to_pool");
+
+ // Initialize send variables
+ $output_send = [];
+ $return_var_send = -1;
+
+ // Only execute send command if add to pool was successful
+ if ($return_var_add_to_pool == 0) {
+ logDebug("Send command: $command_send");
+ logDebug("Executing send command...");
+ exec($command_send, $output_send, $return_var_send);
+ logDebug("Send output: " . implode("\n", $output_send));
+ logDebug("Send return code: $return_var_send");
+ } else {
+ logDebug("Skipping send command due to add to pool failure");
+ $output_send = ["Skipped - add to pool command failed"];
+ $return_var_send = -1;
}
-
- // Check torsocks
- $torsocksPath = shell_exec('which torsocks 2>/dev/null');
- $results['torsocks_installed'] = !empty($torsocksPath);
-
- // Check if Tor is actually working
- $ch = curl_init();
- curl_setopt_array($ch, [
- CURLOPT_URL => 'https://check.torproject.org/api/ip',
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_PROXY => '127.0.0.1:9050',
- CURLOPT_PROXYTYPE => CURLPROXY_SOCKS5_HOSTNAME,
- CURLOPT_TIMEOUT => 15,
- CURLOPT_SSL_VERIFYPEER => true
- ]);
-
- $result = curl_exec($ch);
- $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
- curl_close($ch);
-
- if ($httpCode === 200 && $result) {
- $data = json_decode($result, true);
- if (isset($data['IsTor']) && $data['IsTor'] === true) {
- $results['circuit_working'] = true;
+
+ // Check if succeeded
+ $success = ($return_var_send == 0 && $return_var_add_to_pool == 0);
+
+ if ($success) {
+ // SUCCESS! Log and return
+ $log_entry = date('Y-m-d H:i:s') . " | SUCCESS | Chain: $chain | Copies: $copies | Tor: " .
+ ($useTor ? "Yes" : "No") . "\n";
+ file_put_contents('/var/www/yamnweb/yamn_send.log', $log_entry, FILE_APPEND);
+
+ logDebug("✓ SUCCESS");
+ logDebug("=== End sendYamnEmail ===");
+
+ return [
+ 'success' => true,
+ 'error' => '',
+ 'relay_used' => 'Static Config',
+ 'tor_used' => $useTor,
+ 'outputs' => [
+ 'add_to_pool' => $output_add_to_pool,
+ 'send' => $output_send
+ ]
+ ];
+ } else {
+ // Failed
+ $lastError = "Failed to send message";
+ if ($return_var_add_to_pool != 0) {
+ $lastError = "Failed to add message to pool: " . implode(" ", $output_add_to_pool);
+ } elseif ($return_var_send != 0) {
+ $lastError = "Failed to send from pool: " . implode(" ", $output_send);
}
- }
-
- $results['tor_available'] = (
- $results['socks_proxy'] &&
- $results['torsocks_installed'] &&
- $results['circuit_working']
- );
-
- return $results;
-}
-// Initialize on load
-if (!initSecureEnvironment()) {
- // Silent fail - don't leak information
- // In production, handle this appropriately
+ $log_entry = date('Y-m-d H:i:s') . " | FAILED | Chain: $chain | Copies: $copies | Tor: " .
+ ($useTor ? "Yes" : "No") . " | Error: $lastError\n";
+ file_put_contents('/var/www/yamnweb/yamn_send.log', $log_entry, FILE_APPEND);
+
+ logDebug("✗ FAILED: $lastError");
+ logDebug("=== End sendYamnEmail ===");
+
+ return [
+ 'success' => false,
+ 'error' => $lastError,
+ 'relay_used' => null,
+ 'tor_used' => $useTor,
+ 'outputs' => [
+ 'add_to_pool' => $output_add_to_pool,
+ 'send' => $output_send
+ ]
+ ];
+ }
}
-?>