102 lines
3.5 KiB
PHP
102 lines
3.5 KiB
PHP
<?php
|
|
namespace App\Core;
|
|
|
|
use Db\Database;
|
|
|
|
class Mailer {
|
|
|
|
public static function send(string $to, string $subject, string $htmlBody): bool {
|
|
$cfg = self::getConfig();
|
|
if (empty($cfg['smtp_host']) || empty($cfg['smtp_user'])) {
|
|
// fallback to mail()
|
|
$headers = "MIME-Version: 1.0\r\nContent-type:text/html;charset=UTF-8\r\nFrom: {$cfg['smtp_from']}\r\n";
|
|
return @mail($to, $subject, $htmlBody, $headers);
|
|
}
|
|
|
|
$host = $cfg['smtp_host'];
|
|
$port = (int)($cfg['smtp_port'] ?: 465);
|
|
$user = $cfg['smtp_user'];
|
|
$pass = $cfg['smtp_pass'];
|
|
$from = $cfg['smtp_from'] ?: $user;
|
|
$fromName = $cfg['smtp_from_name'] ?: 'System';
|
|
$encryption = $cfg['smtp_encryption'] ?: 'ssl';
|
|
|
|
try {
|
|
$target = ($encryption === 'ssl') ? "ssl://{$host}" : $host;
|
|
$sock = @fsockopen($target, $port, $errno, $errstr, 10);
|
|
if (!$sock) throw new \RuntimeException("Connect failed: {$errstr}");
|
|
|
|
self::readLine($sock);
|
|
self::cmd($sock, "EHLO localhost");
|
|
|
|
// STARTTLS for tls mode
|
|
if ($encryption === 'tls') {
|
|
self::cmd($sock, "STARTTLS");
|
|
stream_socket_enable_crypto($sock, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
|
|
self::cmd($sock, "EHLO localhost");
|
|
}
|
|
|
|
// AUTH LOGIN
|
|
self::cmd($sock, "AUTH LOGIN");
|
|
self::cmd($sock, base64_encode($user));
|
|
self::cmd($sock, base64_encode($pass));
|
|
|
|
self::cmd($sock, "MAIL FROM:<{$from}>");
|
|
self::cmd($sock, "RCPT TO:<{$to}>");
|
|
self::cmd($sock, "DATA");
|
|
|
|
$msg = "From: {$fromName} <{$from}>\r\n"
|
|
. "To: {$to}\r\n"
|
|
. "Subject: {$subject}\r\n"
|
|
. "MIME-Version: 1.0\r\n"
|
|
. "Content-Type: text/html; charset=UTF-8\r\n"
|
|
. "\r\n"
|
|
. $htmlBody . "\r\n.\r\n";
|
|
fwrite($sock, $msg);
|
|
self::readLine($sock);
|
|
|
|
self::cmd($sock, "QUIT");
|
|
fclose($sock);
|
|
return true;
|
|
} catch (\Throwable $e) {
|
|
error_log("Mailer error: " . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static function cmd($sock, string $cmd): string {
|
|
fwrite($sock, $cmd . "\r\n");
|
|
return self::readLine($sock);
|
|
}
|
|
|
|
private static function readLine($sock): string {
|
|
$resp = '';
|
|
while ($line = fgets($sock, 512)) {
|
|
$resp .= $line;
|
|
if (isset($line[3]) && $line[3] === ' ') break;
|
|
}
|
|
return $resp;
|
|
}
|
|
|
|
public static function getConfig(): array {
|
|
$defaults = [
|
|
'smtp_host' => '', 'smtp_port' => '465', 'smtp_user' => '',
|
|
'smtp_pass' => '', 'smtp_from' => '', 'smtp_from_name' => 'PK10',
|
|
'smtp_encryption' => 'ssl',
|
|
];
|
|
try {
|
|
$db = new Database();
|
|
$rows = $db->select('system_settings', ['setting_key', 'setting_value'], [
|
|
'setting_key[~]' => 'smtp_%'
|
|
]);
|
|
foreach ($rows as $r) $defaults[$r['setting_key']] = $r['setting_value'];
|
|
} catch (\Throwable $e) {}
|
|
return $defaults;
|
|
}
|
|
|
|
public static function test(string $to): array {
|
|
$ok = self::send($to, 'SMTP Test', '<h2>SMTP configuration is working!</h2><p>Time: ' . date('Y-m-d H:i:s') . '</p>');
|
|
return ['success' => $ok, 'message' => $ok ? 'Test email sent' : 'Failed to send, check SMTP settings'];
|
|
}
|
|
}
|