| Server IP : 103.243.232.44 / Your IP : 216.73.216.237 Web Server : LiteSpeed System : Linux server17213-10344.hostycare.online 5.14.0-687.38.1.el9_8.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Aug 12 17:19:12 EDT 2026 x86_64 User : iamakash ( 1400) PHP Version : 8.1.34 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /home/iamakash/kautuki.co.in/api/ |
Upload File : |
<?php
/**
* Kautuki — minimal authenticated SMTP sender. No dependencies.
*
* Sends one plain-text message through Google Workspace (smtp.gmail.com) so the
* lead notification is DKIM-signed by Google and covered by the domain's SPF,
* instead of leaving from the shared Hostycare IP via PHP mail().
*
* Port 465 = implicit TLS (default). Port 587 = STARTTLS (use if 465 is blocked).
* Returns true on success. On failure returns false and appends one line to
* leads/mail.log (git-ignored, web-blocked) — never the password.
*/
function kautuki_smtp_send(array $cfg, string $to, string $subject, string $body, string $replyTo = ''): bool
{
$host = (string)($cfg['host'] ?? 'smtp.gmail.com');
$port = (int) ($cfg['port'] ?? 465);
$user = (string)($cfg['user'] ?? '');
$pass = (string)($cfg['pass'] ?? '');
$from = (string)($cfg['from'] ?? $user);
$fromName = (string)($cfg['from_name'] ?? 'Kautuki');
$timeout = 15;
// Addresses come from validated input, but never let CR/LF into a header.
$clean = static fn(string $s): string => str_replace(["\r", "\n"], '', trim($s));
$to = $clean($to);
$from = $clean($from);
$replyTo = $clean($replyTo);
$sock = null;
try {
$implicitTls = ($port === 465);
$ctx = stream_context_create(['ssl' => ['verify_peer' => true, 'verify_peer_name' => true]]);
$sock = @stream_socket_client(
($implicitTls ? 'ssl://' : 'tcp://') . $host . ':' . $port,
$errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $ctx
);
if (!$sock) {
throw new RuntimeException("connect $host:$port failed: [$errno] $errstr");
}
stream_set_timeout($sock, $timeout);
kautuki_smtp_expect($sock, [220], 'greeting');
kautuki_smtp_cmd($sock, 'EHLO kautuki.co.in', [250], 'EHLO');
if (!$implicitTls) {
kautuki_smtp_cmd($sock, 'STARTTLS', [220], 'STARTTLS');
if (!stream_socket_enable_crypto($sock, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
throw new RuntimeException('STARTTLS handshake failed');
}
kautuki_smtp_cmd($sock, 'EHLO kautuki.co.in', [250], 'EHLO (tls)');
}
kautuki_smtp_cmd($sock, 'AUTH LOGIN', [334], 'AUTH');
kautuki_smtp_cmd($sock, base64_encode($user), [334], 'AUTH user');
kautuki_smtp_cmd($sock, base64_encode($pass), [235], 'AUTH pass');
kautuki_smtp_cmd($sock, "MAIL FROM:<$from>", [250], 'MAIL FROM');
kautuki_smtp_cmd($sock, "RCPT TO:<$to>", [250, 251], 'RCPT TO');
kautuki_smtp_cmd($sock, 'DATA', [354], 'DATA');
$headers = [
'Date: ' . date(DATE_RFC2822),
'From: ' . $fromName . ' <' . $from . '>',
'To: <' . $to . '>',
'Reply-To: <' . ($replyTo !== '' ? $replyTo : $from) . '>',
'Subject: ' . mb_encode_mimeheader($subject, 'UTF-8', 'B', "\r\n"),
'Message-ID: <' . bin2hex(random_bytes(8)) . '@kautuki.co.in>',
'MIME-Version: 1.0',
'Content-Type: text/plain; charset=utf-8',
'Content-Transfer-Encoding: 8bit',
'X-Mailer: Kautuki-Site',
];
// CRLF line endings and dot-stuffing, as SMTP requires.
$text = preg_replace("/\r\n|\r|\n/", "\r\n", $body);
$text = preg_replace('/^\./m', '..', $text);
fwrite($sock, implode("\r\n", $headers) . "\r\n\r\n" . $text . "\r\n");
kautuki_smtp_cmd($sock, '.', [250], 'end of DATA');
kautuki_smtp_cmd($sock, 'QUIT', [221], 'QUIT');
fclose($sock);
return true;
} catch (RuntimeException $e) {
if (is_resource($sock)) {
@fwrite($sock, "QUIT\r\n");
@fclose($sock);
}
@file_put_contents(
__DIR__ . '/../leads/mail.log',
date('c') . ' ' . $e->getMessage() . "\n",
FILE_APPEND
);
return false;
}
}
/** Send one command line and require one of the expected reply codes. */
function kautuki_smtp_cmd($sock, string $line, array $ok, string $label): string
{
fwrite($sock, $line . "\r\n");
return kautuki_smtp_expect($sock, $ok, $label);
}
/** Read one (possibly multi-line) SMTP reply; throw unless its code is expected. */
function kautuki_smtp_expect($sock, array $ok, string $label): string
{
$lines = [];
while (($line = fgets($sock, 1024)) !== false) {
$lines[] = rtrim($line, "\r\n");
if (strlen($line) < 4 || $line[3] !== '-') { break; } // "250-" continues, "250 " ends
}
if (!$lines) {
throw new RuntimeException("$label: no reply (timeout?)");
}
$code = (int)substr($lines[0], 0, 3);
if (!in_array($code, $ok, true)) {
throw new RuntimeException("$label: " . implode(' | ', $lines));
}
return $lines[0];
}