|
Server : LiteSpeed System : Linux terra.hostitbro.com 5.14.0-611.54.3.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Thu May 7 16:31:24 EDT 2026 x86_64 User : outerorb ( 1091) PHP Version : 8.1.34 Disable Function : mail Directory : /home2/outerorb/debtclearplans.co.uk/includes/ | |
|
Path: /home2/outerorb/debtclearplans.co.uk/includes/callback-form-handler.php
Size: 14.26 KB
Permissions: 0666
<?php
require_once __DIR__ . '/config.php';
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
function dcp_strlen($value)
{
if (function_exists('mb_strlen')) {
return mb_strlen($value);
}
return strlen($value);
}
function dcp_contains_newline($value)
{
return preg_match('/[\r\n]/', $value) === 1;
}
function dcp_readable_debt_range($value)
{
$map = array(
'under-5000' => 'Under GBP 5,000',
'5000-10000' => 'GBP 5,000 to GBP 10,000',
'10000-20000' => 'GBP 10,000 to GBP 20,000',
'20000-50000' => 'GBP 20,000 to GBP 50,000',
'50000-plus' => 'GBP 50,000+'
);
return isset($map[$value]) ? $map[$value] : $value;
}
function dcp_readable_best_time($value)
{
$map = array(
'morning' => 'Morning',
'afternoon' => 'Afternoon',
'evening' => 'Evening',
'anytime' => 'Anytime'
);
return isset($map[$value]) ? $map[$value] : $value;
}
function dcp_mail($to, $subject, $body, $headers)
{
if (!is_string($to) || !filter_var($to, FILTER_VALIDATE_EMAIL)) {
return false;
}
if (dcp_contains_newline($to) || dcp_contains_newline($subject)) {
return false;
}
try {
$transport = isset($GLOBALS['callback_mail_transport']) ? (string) $GLOBALS['callback_mail_transport'] : 'mail';
if ($transport === 'smtp') {
return dcp_send_via_smtp($to, (string) $subject, (string) $body, $headers);
}
if (!function_exists('mail')) {
return false;
}
return @mail($to, (string) $subject, (string) $body, implode("\r\n", $headers));
} catch (Throwable $exception) {
dcp_log_error('mail() failed: ' . $exception->getMessage());
return false;
}
}
function dcp_log_error($message)
{
$logDir = dirname(__DIR__) . '/storage';
$logFile = $logDir . '/callback-errors.log';
if (!is_dir($logDir)) {
@mkdir($logDir, 0755, true);
}
if (is_dir($logDir) && is_writable($logDir)) {
@file_put_contents(
$logFile,
'[' . gmdate('c') . '] ' . $message . PHP_EOL,
FILE_APPEND | LOCK_EX
);
}
}
function dcp_smtp_read_response($socket)
{
$response = '';
while (!feof($socket)) {
$line = fgets($socket, 515);
if ($line === false) {
break;
}
$response .= $line;
if (isset($line[3]) && $line[3] === ' ') {
break;
}
}
return $response;
}
function dcp_smtp_expect($socket, $allowedCodes, $context)
{
$response = dcp_smtp_read_response($socket);
$code = (int) substr($response, 0, 3);
if (!in_array($code, $allowedCodes, true)) {
dcp_log_error('SMTP ' . $context . ' failed: ' . trim($response));
return false;
}
return true;
}
function dcp_smtp_command($socket, $command, $allowedCodes, $context)
{
$written = fwrite($socket, $command . "\r\n");
if ($written === false) {
dcp_log_error('SMTP write failed during ' . $context . '.');
return false;
}
return dcp_smtp_expect($socket, $allowedCodes, $context);
}
function dcp_send_via_smtp($to, $subject, $body, $headers)
{
$host = isset($GLOBALS['callback_smtp_host']) ? trim((string) $GLOBALS['callback_smtp_host']) : '';
$port = isset($GLOBALS['callback_smtp_port']) ? (int) $GLOBALS['callback_smtp_port'] : 0;
$encryption = isset($GLOBALS['callback_smtp_encryption']) ? strtolower(trim((string) $GLOBALS['callback_smtp_encryption'])) : '';
$username = isset($GLOBALS['callback_smtp_username']) ? trim((string) $GLOBALS['callback_smtp_username']) : '';
$password = isset($GLOBALS['callback_smtp_password']) ? (string) $GLOBALS['callback_smtp_password'] : '';
$timeout = isset($GLOBALS['callback_smtp_timeout']) ? (int) $GLOBALS['callback_smtp_timeout'] : 15;
$fromEmail = isset($GLOBALS['callback_sender_email']) ? trim((string) $GLOBALS['callback_sender_email']) : $username;
if ($host === '' || $port <= 0 || $username === '' || $password === '' || $fromEmail === '') {
dcp_log_error('SMTP is selected but host, port, username, password, or sender email is missing.');
return false;
}
$transportHost = $host;
if ($encryption === 'ssl') {
$transportHost = 'ssl://' . $host;
}
$socket = @stream_socket_client(
$transportHost . ':' . $port,
$errorNumber,
$errorMessage,
$timeout,
STREAM_CLIENT_CONNECT
);
if (!is_resource($socket)) {
dcp_log_error('SMTP connection failed: ' . $errorMessage . ' (' . $errorNumber . ')');
return false;
}
stream_set_timeout($socket, $timeout);
if (!dcp_smtp_expect($socket, array(220), 'connect')) {
fclose($socket);
return false;
}
$serverName = isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST'] !== '' ? $_SERVER['HTTP_HOST'] : 'debtclearplans.co.uk';
if (!dcp_smtp_command($socket, 'EHLO ' . $serverName, array(250), 'EHLO')) {
fclose($socket);
return false;
}
if ($encryption === 'tls') {
if (!dcp_smtp_command($socket, 'STARTTLS', array(220), 'STARTTLS')) {
fclose($socket);
return false;
}
if (!@stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
dcp_log_error('SMTP STARTTLS negotiation failed.');
fclose($socket);
return false;
}
if (!dcp_smtp_command($socket, 'EHLO ' . $serverName, array(250), 'EHLO after STARTTLS')) {
fclose($socket);
return false;
}
}
if (!dcp_smtp_command($socket, 'AUTH LOGIN', array(334), 'AUTH LOGIN')) {
fclose($socket);
return false;
}
if (!dcp_smtp_command($socket, base64_encode($username), array(334), 'SMTP username')) {
fclose($socket);
return false;
}
if (!dcp_smtp_command($socket, base64_encode($password), array(235), 'SMTP password')) {
fclose($socket);
return false;
}
if (!dcp_smtp_command($socket, 'MAIL FROM:<' . $fromEmail . '>', array(250), 'MAIL FROM')) {
fclose($socket);
return false;
}
if (!dcp_smtp_command($socket, 'RCPT TO:<' . $to . '>', array(250, 251), 'RCPT TO')) {
fclose($socket);
return false;
}
if (!dcp_smtp_command($socket, 'DATA', array(354), 'DATA')) {
fclose($socket);
return false;
}
$messageHeaders = $headers;
$messageHeaders[] = 'To: ' . $to;
$messageHeaders[] = 'Subject: ' . $subject;
$normalizedBody = str_replace(array("\r\n", "\r"), "\n", $body);
$normalizedBody = str_replace("\n.", "\n..", $normalizedBody);
$message = implode("\r\n", $messageHeaders) . "\r\n\r\n" . str_replace("\n", "\r\n", $normalizedBody) . "\r\n.";
$written = fwrite($socket, $message . "\r\n");
if ($written === false) {
dcp_log_error('SMTP message body write failed.');
fclose($socket);
return false;
}
if (!dcp_smtp_expect($socket, array(250), 'message send')) {
fclose($socket);
return false;
}
dcp_smtp_command($socket, 'QUIT', array(221), 'QUIT');
fclose($socket);
return true;
}
function dcp_redirect_with_status($returnUrl, $status)
{
$path = 'index.php';
if (is_string($returnUrl) && $returnUrl !== '') {
$parts = parse_url($returnUrl);
if ($parts !== false && !isset($parts['scheme']) && !isset($parts['host'])) {
$sanitizedPath = isset($parts['path']) && $parts['path'] !== '' ? ltrim($parts['path'], '/') : 'index.php';
$sanitizedQuery = isset($parts['query']) ? $parts['query'] : '';
if (substr($sanitizedPath, -4) === '.php') {
$path = $sanitizedPath;
}
if ($sanitizedQuery !== '') {
parse_str($sanitizedQuery, $query);
} else {
$query = array();
}
unset($query['cb_status']);
$query['cb_status'] = $status;
$queryString = http_build_query($query);
$location = '/' . ltrim($path, '/') . ($queryString !== '' ? '?' . $queryString : '') . '#callback-form-top';
header('Location: ' . $location);
exit;
}
}
header('Location: /' . ltrim($path, '/') . '?cb_status=' . urlencode($status) . '#callback-form-top');
exit;
}
function dcp_is_submission_successful($email, $adminEmailSent, $visitorEmailSent)
{
if (!$adminEmailSent) {
return false;
}
if ($email !== '' && !$visitorEmailSent) {
return false;
}
return true;
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
dcp_redirect_with_status(isset($_POST['return_url']) ? $_POST['return_url'] : 'index.php', 'error');
}
$returnUrl = isset($_POST['return_url']) ? $_POST['return_url'] : 'index.php';
$token = isset($_POST['csrf_token']) ? (string) $_POST['csrf_token'] : '';
$sessionToken = isset($_SESSION['callback_csrf']) ? (string) $_SESSION['callback_csrf'] : '';
if ($token === '' || $sessionToken === '' || !hash_equals($sessionToken, $token)) {
dcp_redirect_with_status($returnUrl, 'error');
}
$honeypot = isset($_POST['honeypot']) ? trim((string) $_POST['honeypot']) : '';
if ($honeypot !== '') {
dcp_redirect_with_status($returnUrl, 'success');
}
$fullName = isset($_POST['full_name']) ? trim((string) $_POST['full_name']) : '';
$phone = isset($_POST['phone']) ? trim((string) $_POST['phone']) : '';
$email = isset($_POST['email']) ? trim((string) $_POST['email']) : '';
$debtRange = isset($_POST['debt_range']) ? trim((string) $_POST['debt_range']) : '';
$bestTime = isset($_POST['best_time']) ? trim((string) $_POST['best_time']) : '';
$consent = isset($_POST['consent']) ? (string) $_POST['consent'] : '';
$validDebtRanges = array('under-5000', '5000-10000', '10000-20000', '20000-50000', '50000-plus');
$validCallTimes = array('morning', 'afternoon', 'evening', 'anytime');
$hasValidName = $fullName !== '' && dcp_strlen($fullName) >= 2 && dcp_strlen($fullName) <= 120;
$hasValidPhone = $phone !== '' && preg_match('/^[0-9+()\-\s]{7,20}$/', $phone) === 1;
$hasValidEmail = $email === '' || filter_var($email, FILTER_VALIDATE_EMAIL);
$hasValidDebtRange = in_array($debtRange, $validDebtRanges, true);
$hasValidCallTime = in_array($bestTime, $validCallTimes, true);
$hasConsent = $consent === 'on' || $consent === '1' || $consent === 'yes';
if (!$hasValidName || !$hasValidPhone || !$hasValidEmail || !$hasValidDebtRange || !$hasValidCallTime || !$hasConsent) {
dcp_redirect_with_status($returnUrl, 'error');
}
if (dcp_contains_newline($fullName) || dcp_contains_newline($phone) || dcp_contains_newline($email)) {
dcp_redirect_with_status($returnUrl, 'error');
}
$debtRangeLabel = dcp_readable_debt_range($debtRange);
$bestTimeLabel = dcp_readable_best_time($bestTime);
$lead = array(
'created_at' => gmdate('c'),
'full_name' => $fullName,
'phone' => $phone,
'email' => $email,
'debt_range' => $debtRangeLabel,
'best_time' => $bestTimeLabel,
'source_page' => $returnUrl,
'ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '',
'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''
);
$storageDir = dirname(__DIR__) . '/storage';
$storageFile = $storageDir . '/callback-leads.log';
$stored = false;
if (!is_dir($storageDir)) {
@mkdir($storageDir, 0755, true);
}
$adminEmail = !empty($callback_recipient_email) && filter_var($callback_recipient_email, FILTER_VALIDATE_EMAIL)
? $callback_recipient_email
: 'hello@debtclearplans.co.uk';
$senderEmail = !empty($callback_sender_email) && filter_var($callback_sender_email, FILTER_VALIDATE_EMAIL)
? $callback_sender_email
: $adminEmail;
$clientDisplayEmail = $email !== '' ? $email : 'Not provided';
$sourcePath = parse_url($returnUrl, PHP_URL_PATH);
if (!is_string($sourcePath) || $sourcePath === '') {
$sourcePath = 'index.php';
}
$sourcePath = ltrim($sourcePath, '/');
$sourcePageUrl = 'https://debtclearplans.co.uk/' . $sourcePath;
$adminSubject = 'New callback request - ' . $site_name;
$adminBody = implode("\n", array(
'A new callback request was submitted on debtclearplans.co.uk.',
'',
'Name: ' . $fullName,
'Phone: ' . $phone,
'Email: ' . $clientDisplayEmail,
'Debt range: ' . $debtRangeLabel,
'Best time to call: ' . $bestTimeLabel,
'Consent provided: Yes',
'',
'Source URL: ' . $sourcePageUrl,
'Submitted at (UTC): ' . gmdate('Y-m-d H:i:s')
));
$adminHeaders = array(
'MIME-Version: 1.0',
'Content-Type: text/plain; charset=UTF-8',
'From: ' . $senderEmail
);
if ($email !== '') {
$adminHeaders[] = 'Reply-To: ' . $email;
}
$adminEmailSent = dcp_mail($adminEmail, $adminSubject, $adminBody, $adminHeaders);
$visitorEmailSent = false;
if ($email !== '') {
$visitorSubject = 'We received your callback request - ' . $site_name;
$visitorBody = implode("\n", array(
'Hello ' . $fullName . ',',
'',
'Thank you for requesting a callback from ' . $site_name . '.',
'A member of our team will contact you soon.',
'',
'Your submitted details:',
'Phone: ' . $phone,
'Email: ' . $email,
'Approximate debt: ' . $debtRangeLabel,
'Best time to call: ' . $bestTimeLabel,
'',
'If any detail is incorrect, please reply to this email.',
'',
'Regards,',
$site_name,
'Email: ' . $adminEmail,
'Website: https://debtclearplans.co.uk/'
));
$visitorHeaders = array(
'MIME-Version: 1.0',
'Content-Type: text/plain; charset=UTF-8',
'From: ' . $senderEmail,
'Reply-To: ' . $adminEmail
);
$visitorEmailSent = dcp_mail($email, $visitorSubject, $visitorBody, $visitorHeaders);
}
$lead['admin_email_sent'] = $adminEmailSent;
$lead['visitor_email_sent'] = $visitorEmailSent;
if (is_dir($storageDir) && is_writable($storageDir)) {
$written = @file_put_contents($storageFile, json_encode($lead, JSON_UNESCAPED_UNICODE) . PHP_EOL, FILE_APPEND | LOCK_EX);
$stored = $written !== false;
}
if (!dcp_is_submission_successful($email, $adminEmailSent, $visitorEmailSent)) {
if (!$stored) {
dcp_log_error('Callback submission failed before any durable storage or mail delivery.');
}
dcp_redirect_with_status($returnUrl, 'mail_error');
}
// Rotate CSRF token after successful processing to reduce replay risk.
try {
$_SESSION['callback_csrf'] = bin2hex(random_bytes(32));
} catch (Throwable $exception) {
dcp_log_error('CSRF token rotation failed: ' . $exception->getMessage());
$_SESSION['callback_csrf'] = sha1(uniqid((string) mt_rand(), true));
}
dcp_redirect_with_status($returnUrl, 'success');