File: //home/ezoshosting.com/public_html/api/lead.php
<?php
/**
* EZOS.Hosting lead intake handler.
* Accepts POST from /contact-us/#intake-form, mails sales@ and logs a copy.
* Anti-spam: honeypot field, JS time-trap (enforced only when present),
* per-IP rate limit. Redirects: success -> /contact-us/thanks/,
* validation problem -> /contact-us/?lead=error#intake-form.
*/
declare(strict_types=1);
const LEAD_TO = 'sales@ezoshosting.com';
const LEAD_FROM = 'noreply@ezoshosting.com';
const LEAD_LOG = '/tmp/ezos-leads.log';
const RATE_DIR = '/tmp';
const RATE_MAX = 5; // submissions ...
const RATE_WINDOW = 3600; // ... per hour per IP
const MIN_SECONDS = 3; // JS time-trap threshold
$topics = array(
'managed-local-ai' => 'Managed Local AI',
'open-source-hosting' => 'Open Source Hosting',
'domains' => 'Domains',
'migration' => 'Migration',
'other' => 'Other',
);
function redirect_to(string $path): void
{
header('Location: ' . $path, true, 303);
exit;
}
/** Strip header-injection vectors and control chars from single-line values. */
function clean_line(?string $value, int $max): string
{
$value = trim((string) $value);
$value = str_replace(array("\r", "\n", "\0", "%0a", "%0d"), ' ', $value);
$value = preg_replace('/[\x00-\x1F\x7F]/', ' ', $value);
return mb_substr($value, 0, $max);
}
function clean_block(?string $value, int $max): string
{
$value = trim((string) $value);
$value = str_replace(array("\r\n", "\r"), "\n", $value);
$value = preg_replace('/[^\P{C}\n]/u', ' ', $value);
return mb_substr($value, 0, $max);
}
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
redirect_to('/contact-us/#intake-form');
}
$ip = clean_line($_SERVER['REMOTE_ADDR'] ?? 'unknown', 64);
// Honeypot filled or time-trap tripped: act successful, send nothing.
$honeypot = trim((string) ($_POST['website'] ?? ''));
$ts = (int) ($_POST['ts'] ?? 0);
$tooFast = $ts > 0 && (time() - $ts) < MIN_SECONDS;
$tooOld = $ts > 0 && (time() - $ts) > 86400;
if ($honeypot !== '' || $tooFast || $tooOld) {
redirect_to('/contact-us/thanks/');
}
// Per-IP rate limit (best effort; failure to persist never blocks a lead).
$rateFile = RATE_DIR . '/ezos-lead-rl-' . md5($ip);
$now = time();
$stamps = array();
$raw = @file_get_contents($rateFile);
if ($raw !== false) {
foreach (explode("\n", trim($raw)) as $line) {
$t = (int) $line;
if ($t > 0 && ($now - $t) < RATE_WINDOW) {
$stamps[] = $t;
}
}
}
if (count($stamps) >= RATE_MAX) {
redirect_to('/contact-us/thanks/');
}
$stamps[] = $now;
@file_put_contents($rateFile, implode("\n", $stamps) . "\n", LOCK_EX);
// Validate input.
$name = clean_line($_POST['name'] ?? '', 100);
$email = clean_line($_POST['email'] ?? '', 200);
$company = clean_line($_POST['company'] ?? '', 120);
$topic = clean_line($_POST['topic'] ?? '', 40);
$source = clean_line($_POST['source'] ?? 'contact-us', 60);
$message = clean_block($_POST['message'] ?? '', 5000);
$errors = array();
if (mb_strlen($name) < 2) {
$errors[] = 'name';
}
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
$errors[] = 'email';
}
if (!isset($topics[$topic])) {
$errors[] = 'topic';
}
if (mb_strlen($message) < 10) {
$errors[] = 'message';
}
if ($errors) {
redirect_to('/contact-us/?lead=error#intake-form');
}
$topicLabel = $topics[$topic];
$userAgent = clean_line($_SERVER['HTTP_USER_AGENT'] ?? '', 250);
$body = "New lead via www.ezoshosting.com\n"
. "--------------------------------\n"
. 'Name: ' . $name . "\n"
. 'Email: ' . $email . "\n"
. 'Company: ' . ($company !== '' ? $company : '-') . "\n"
. 'Topic: ' . $topicLabel . "\n"
. 'Source: ' . $source . "\n"
. 'IP: ' . $ip . "\n"
. 'Agent: ' . $userAgent . "\n"
. 'Time: ' . gmdate('Y-m-d H:i:s') . " UTC\n"
. "--------------------------------\n\n"
. $message . "\n";
$headers = 'From: EZOS.Hosting <' . LEAD_FROM . ">\r\n"
. 'Reply-To: ' . $name . ' <' . $email . ">\r\n"
. "MIME-Version: 1.0\r\n"
. "Content-Type: text/plain; charset=UTF-8\r\n"
. 'X-EZOS-Lead-Source: ' . $source;
$subject = '[EZOS Lead] ' . $topicLabel . ' - ' . $name;
$sent = @mail(LEAD_TO, $subject, $body, $headers, '-f ' . LEAD_FROM);
// Always keep a local copy so no lead is lost if the MTA hiccups.
$logLine = json_encode(array(
'time' => gmdate('c'),
'sent' => (bool) $sent,
'name' => $name,
'email' => $email,
'company' => $company,
'topic' => $topic,
'source' => $source,
'ip' => $ip,
'message' => $message,
), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
@file_put_contents(LEAD_LOG, $logLine . "\n", FILE_APPEND | LOCK_EX);
redirect_to('/contact-us/thanks/');