Fix quote page gap: wrap noscript fallback, reduce hero/form padding

This commit is contained in:
root
2026-02-04 03:17:55 +00:00
parent b0234123a3
commit 3a0d8034c7
5 changed files with 521 additions and 762 deletions

View File

@@ -1,26 +1,38 @@
<?php
// Quote Form Handler with Enhanced Security
// Ensure session cookie is available for AJAX requests
ini_set('session.cookie_samesite', 'Lax');
ini_set('session.cookie_httponly', '1');
ini_set('session.cookie_secure', '1'); // Set to '1' if using HTTPS
ini_set('session.cookie_secure', '1');
session_start();
// Logging
$logDir = __DIR__ . '/logs';
if (!is_dir($logDir)) {
mkdir($logDir, 0755, true);
}
function logDebug($message) {
global $logDir;
$entry = date('Y-m-d H:i:s') . " - " . $message . "\n";
file_put_contents($logDir . '/quote-debug.log', $entry, FILE_APPEND | LOCK_EX);
}
// Security headers
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block');
// CSRF Protection
function generateCSRFToken() {
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
function validateCSRFToken($token) {
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
if (!isset($_SESSION['csrf_token'])) {
logDebug("CSRF: No session token exists");
return false;
}
$match = hash_equals($_SESSION['csrf_token'], $token);
if (!$match) {
logDebug("CSRF: Token mismatch - Session: " . substr($_SESSION['csrf_token'], 0, 16) . "... POST: " . substr($token, 0, 16) . "...");
}
return $match;
}
// Rate limiting
@@ -39,11 +51,7 @@ function checkRateLimit() {
$data = $_SESSION[$key];
}
if ($data['count'] >= 3) {
return false;
}
return true;
return $data['count'] < 5; // Allow 5 submissions per hour
}
// Input validation
@@ -55,12 +63,8 @@ function validateInput($data, $type = 'text') {
switch ($type) {
case 'email':
return filter_var($data, FILTER_VALIDATE_EMAIL) ? $data : false;
case 'phone':
return preg_match('/^[\+]?[0-9\s\-\(\)]+$/', $data) ? $data : false;
case 'text':
return strlen($data) > 0 ? $data : false;
case 'long_text':
return strlen($data) >= 20 ? $data : false;
return strlen($data) > 0 ? $data : '';
default:
return $data;
}
@@ -73,260 +77,24 @@ function isAjaxRequest() {
}
// Response function
function sendResponse($success, $message, $data = null) {
// If AJAX request, send JSON
function sendResponse($success, $message) {
if (isAjaxRequest()) {
$response = [
'success' => $success,
'message' => $message
];
if ($data !== null) {
$response['data'] = $data;
}
header('Content-Type: application/json');
echo json_encode($response);
echo json_encode(['success' => $success, 'message' => $message]);
exit;
}
// Otherwise, display HTML response page
displayHTMLResponse($success, $message);
}
// Display HTML response page
function displayHTMLResponse($success, $message) {
$pageTitle = $success ? 'Quote Request Sent Successfully' : 'Quote Submission Error';
$messageClass = $success ? 'success' : 'error';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($pageTitle); ?> - UK Data Services</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
background: linear-gradient(135deg, #144784 0%, #179e83 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.response-container {
background: white;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
max-width: 600px;
width: 100%;
padding: 40px;
text-align: center;
animation: slideUp 0.4s ease;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.logo {
margin-bottom: 30px;
}
.logo img {
height: 50px;
width: auto;
}
.icon {
width: 80px;
height: 80px;
margin: 0 auto 20px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 40px;
}
.icon.success {
background: rgba(23, 158, 131, 0.1);
color: #179e83;
}
.icon.error {
background: rgba(231, 76, 60, 0.1);
color: #e74c3c;
}
h1 {
font-size: 28px;
margin-bottom: 15px;
color: #1a1a1a;
font-weight: 700;
}
.message {
font-size: 16px;
color: #666;
margin-bottom: 30px;
line-height: 1.6;
}
.message.error {
color: #721c24;
background: #fee;
padding: 15px;
border-radius: 8px;
border: 1px solid #fcc;
}
.message.success {
color: #155724;
background: #efe;
padding: 15px;
border-radius: 8px;
border: 1px solid #cfc;
}
.buttons {
display: flex;
gap: 15px;
justify-content: center;
flex-wrap: wrap;
}
.btn {
display: inline-block;
padding: 14px 32px;
border-radius: 50px;
text-decoration: none;
font-weight: 600;
transition: all 0.3s ease;
cursor: pointer;
border: none;
font-size: 16px;
}
.btn-primary {
background: #179e83;
color: white;
box-shadow: 0 4px 14px rgba(23, 158, 131, 0.25);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(23, 158, 131, 0.35);
background: #15896f;
}
.btn-secondary {
background: transparent;
color: #144784;
border: 2px solid #144784;
}
.btn-secondary:hover {
background: #144784;
color: white;
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(20, 71, 132, 0.25);
}
.contact-info {
margin-top: 30px;
padding-top: 30px;
border-top: 1px solid #e1e5e9;
font-size: 14px;
color: #666;
}
.contact-info a {
color: #179e83;
text-decoration: none;
font-weight: 600;
}
.contact-info a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="response-container">
<div class="logo">
<img src="/assets/images/ukds-main-logo.webp" alt="UK Data Services" onerror="this.onerror=null; this.src='/assets/images/ukds-main-logo.png';">
</div>
<div class="icon <?php echo $messageClass; ?>">
<?php echo $success ? '✓' : '✕'; ?>
</div>
<h1><?php echo $success ? 'Thank You!' : 'Oops! Something Went Wrong'; ?></h1>
<div class="message <?php echo $messageClass; ?>">
<?php
if ($success) {
echo htmlspecialchars($message);
} else {
// Parse multiple error messages if present
if (strpos($message, '. ') !== false) {
$errors = explode('. ', $message);
echo '<ul style="text-align: left; padding-left: 20px; list-style: disc;">';
foreach ($errors as $error) {
if (trim($error)) {
echo '<li>' . htmlspecialchars(trim($error)) . '</li>';
}
}
echo '</ul>';
} else {
echo htmlspecialchars($message);
}
}
?>
</div>
<div class="buttons">
<?php if ($success): ?>
<a href="/" class="btn btn-primary">Return to Home</a>
<a href="/blog/" class="btn btn-secondary">Read Our Blog</a>
<?php else: ?>
<button onclick="history.back()" class="btn btn-primary">Go Back & Try Again</button>
<a href="/" class="btn btn-secondary">Return to Home</a>
<?php endif; ?>
</div>
<div class="contact-info">
<?php if ($success): ?>
<p>We'll be in touch within 24 hours with a detailed proposal.</p>
<p>Have questions? Email us at <a href="mailto:info@ukdataservices.co.uk">info@ukdataservices.co.uk</a></p>
<?php else: ?>
<p>If you continue to experience issues, please contact us directly:</p>
<p>Email: <a href="mailto:info@ukdataservices.co.uk">info@ukdataservices.co.uk</a></p>
<p>Phone: +44 1692 689150</p>
<?php endif; ?>
</div>
</div>
</body>
</html>
<?php
// HTML fallback
$title = $success ? 'Quote Request Sent' : 'Error';
$class = $success ? 'success' : 'error';
echo "<!DOCTYPE html><html><head><title>$title</title>
<style>body{font-family:sans-serif;padding:40px;text-align:center;}
.msg{padding:20px;border-radius:8px;max-width:500px;margin:0 auto;}
.success{background:#d4edda;color:#155724;border:1px solid #c3e6cb;}
.error{background:#f8d7da;color:#721c24;border:1px solid #f5c6cb;}
a{color:#007bff;}</style></head>
<body><div class='msg $class'><h2>$title</h2><p>" . htmlspecialchars($message) . "</p>
<p><a href='/quote'>Back to form</a> | <a href='/'>Home</a></p></div></body></html>";
exit;
}
@@ -335,386 +103,197 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
sendResponse(false, 'Invalid request method');
}
// Validate CSRF token
if (!isset($_POST['csrf_token'])) {
logDebug("Form submission from " . $_SERVER['REMOTE_ADDR'] . " - Session ID: " . session_id());
// CSRF Validation
$csrfToken = $_POST['csrf_token'] ?? '';
if (empty($csrfToken)) {
logDebug("CSRF: No token in POST data");
sendResponse(false, 'Security validation failed. Please refresh the page and try again.');
}
if (!validateCSRFToken($_POST['csrf_token'])) {
if (!validateCSRFToken($csrfToken)) {
sendResponse(false, 'Security validation failed. Please refresh the page and try again.');
}
// Check for blocked IPs
function checkBlockedIP() {
$ip = $_SERVER['REMOTE_ADDR'];
$blockFile = 'logs/blocked-ips.txt';
if (file_exists($blockFile)) {
$blockedIPs = file($blockFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($blockedIPs as $blockedEntry) {
$parts = explode('|', $blockedEntry);
if (isset($parts[0]) && $parts[0] === $ip) {
$blockTime = isset($parts[1]) ? (int)$parts[1] : 0;
// Block for 24 hours
if (time() - $blockTime < 86400) {
return false;
}
}
}
}
return true;
}
logDebug("CSRF validation passed");
// Check for blocked IPs first
if (!checkBlockedIP()) {
sendResponse(false, 'Access temporarily restricted');
}
// Check rate limiting
// Rate limiting
if (!checkRateLimit()) {
logDebug("Rate limit exceeded for " . $_SERVER['REMOTE_ADDR']);
sendResponse(false, 'Too many requests. Please try again later.');
}
// reCAPTCHA Verification
require_once '.recaptcha-config.php';
function validateRecaptcha($token) {
if (!RECAPTCHA_ENABLED) {
// Skip validation if reCAPTCHA is disabled (test keys)
error_log("reCAPTCHA validation skipped - test keys in use");
return true;
// reCAPTCHA (if enabled)
require_once __DIR__ . '/.recaptcha-config.php';
if (RECAPTCHA_ENABLED) {
$recaptchaResponse = $_POST['recaptcha_response'] ?? '';
if (empty($recaptchaResponse)) {
sendResponse(false, 'Security verification failed. Please try again.');
}
if (empty($token)) {
return false;
}
$secretKey = RECAPTCHA_SECRET_KEY;
$verifyURL = 'https://www.google.com/recaptcha/api/siteverify';
$data = [
'secret' => $secretKey,
'response' => $token,
$verifyData = [
'secret' => RECAPTCHA_SECRET_KEY,
'response' => $recaptchaResponse,
'remoteip' => $_SERVER['REMOTE_ADDR']
];
$options = [
'http' => [
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
$result = @file_get_contents('https://www.google.com/recaptcha/api/siteverify', false,
stream_context_create(['http' => [
'method' => 'POST',
'content' => http_build_query($data)
]
];
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => http_build_query($verifyData)
]]));
$context = stream_context_create($options);
$result = file_get_contents($verifyURL, false, $context);
if ($result === false) {
error_log('reCAPTCHA verification request failed');
return false;
if ($result) {
$resultJson = json_decode($result, true);
if (!$resultJson['success'] || ($resultJson['score'] ?? 1) < RECAPTCHA_THRESHOLD) {
logDebug("reCAPTCHA failed: " . print_r($resultJson, true));
sendResponse(false, 'Security verification failed. Please try again.');
}
}
$resultJson = json_decode($result, true);
if ($resultJson['success'] && isset($resultJson['score'])) {
return $resultJson['score'] >= RECAPTCHA_THRESHOLD;
}
return false;
}
// Verify reCAPTCHA
$recaptchaResponse = $_POST['recaptcha_response'] ?? '';
if (!validateRecaptcha($recaptchaResponse)) {
sendResponse(false, 'Security verification failed. Please try again.');
}
// Spam protection - honeypot field
if (isset($_POST['website']) && !empty($_POST['website'])) {
// Honeypot check
if (!empty($_POST['website'])) {
logDebug("Honeypot triggered");
sendResponse(false, 'Spam detected');
}
// Validate and sanitize inputs
$services = $_POST['services'] ?? [];
$project_scale = validateInput($_POST['project_scale'] ?? '', 'text');
$timeline = validateInput($_POST['timeline'] ?? '', 'text');
$name = validateInput($_POST['name'] ?? '', 'text');
$email = validateInput($_POST['email'] ?? '', 'email');
$company = validateInput($_POST['company'] ?? '', 'text');
$phone = validateInput($_POST['phone'] ?? '', 'phone');
$data_sources = validateInput($_POST['data_sources'] ?? '', 'text');
$requirements = validateInput($_POST['requirements'] ?? '', 'long_text');
$budget = validateInput($_POST['budget'] ?? '', 'text');
// Validation
$errors = [];
if (empty($services) || !is_array($services)) {
$errors[] = 'Please select at least one service';
}
if (!$project_scale) {
$errors[] = 'Please select a project scale';
}
if (!$timeline) {
$errors[] = 'Please select a timeline';
}
if (!$name || strlen($name) < 2) {
$errors[] = 'Please enter a valid name';
}
if (!$email) {
$errors[] = 'Please enter a valid email address';
}
if (!$requirements) {
$errors[] = 'Please provide detailed project requirements';
}
if (!empty($errors)) {
sendResponse(false, implode('. ', $errors));
}
// Enhanced spam protection - content filtering
$spamKeywords = [
'viagra', 'cialis', 'casino', 'lottery', 'bitcoin', 'forex', 'loan', 'debt',
'pharmacy', 'click here', 'act now', 'limited time', 'risk free', 'guarantee',
'no obligation', 'free money', 'make money fast', 'work from home', 'get rich',
'investment opportunity', 'credit repair', 'refinance', 'consolidate debt',
'weight loss', 'miracle cure', 'lose weight', 'adult content', 'porn',
'sex', 'dating', 'singles', 'webcam', 'escort', 'massage'
];
$contentToCheck = strtolower($requirements . ' ' . $name . ' ' . $company . ' ' . $data_sources);
foreach ($spamKeywords as $keyword) {
if (strpos($contentToCheck, $keyword) !== false) {
sendResponse(false, 'Invalid content detected');
}
}
// Bot detection - check for suspicious patterns
// Bot detection - user agent check
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
$suspiciousAgents = ['curl', 'wget', 'python', 'bot', 'crawler', 'spider', 'scraper'];
$suspiciousAgents = ['curl', 'wget', 'python-requests', 'scrapy'];
foreach ($suspiciousAgents as $agent) {
if (stripos($userAgent, $agent) !== false) {
logDebug("Suspicious user agent: $userAgent");
sendResponse(false, 'Automated submissions not allowed');
}
}
// Check submission speed (too fast = likely bot) - More lenient timing
if (isset($_SESSION['form_start_time'])) {
$submissionTime = time() - $_SESSION['form_start_time'];
if ($submissionTime < 3) { // Only block if under 3 seconds (very aggressive bots)
sendResponse(false, 'Form submitted too quickly');
}
// Extract and validate form data
$service_type = validateInput($_POST['service_type'] ?? '');
$scale = validateInput($_POST['scale'] ?? '');
$name = validateInput($_POST['name'] ?? '');
$email = validateInput($_POST['email'] ?? '', 'email');
$company = validateInput($_POST['company'] ?? '');
$timeline = validateInput($_POST['timeline'] ?? '');
$data_sources = validateInput($_POST['data_sources'] ?? '');
$requirements = validateInput($_POST['requirements'] ?? '');
// Validation
$errors = [];
if (empty($service_type)) $errors[] = 'Please select a service';
if (empty($scale)) $errors[] = 'Please select a scale';
if (empty($name) || strlen($name) < 2) $errors[] = 'Please enter a valid name';
if (!$email) $errors[] = 'Please enter a valid email address';
if (empty($timeline)) $errors[] = 'Please select a timeline';
if (!empty($errors)) {
logDebug("Validation errors: " . implode(', ', $errors));
sendResponse(false, implode('. ', $errors));
}
// Sanitize services array
$services = array_map(function($service) {
return htmlspecialchars(trim($service), ENT_QUOTES, 'UTF-8');
}, $services);
// Spam content check
$spamKeywords = ['viagra', 'cialis', 'casino', 'lottery', 'bitcoin', 'forex', 'pharmacy', 'click here', 'act now'];
$contentToCheck = strtolower($requirements . ' ' . $name . ' ' . $company . ' ' . $data_sources);
foreach ($spamKeywords as $keyword) {
if (strpos($contentToCheck, $keyword) !== false) {
logDebug("Spam keyword detected: $keyword");
sendResponse(false, 'Invalid content detected');
}
}
// Update rate limit counter
$ip = $_SERVER['REMOTE_ADDR'];
$key = 'quote_' . md5($ip);
$_SESSION[$key]['count']++;
// Create friendly service names
$service_names = [
'web-scraping' => 'Web Scraping & Data Extraction',
'business-intelligence' => 'Business Intelligence & Analytics',
'data-processing' => 'Data Processing & Cleaning',
'automation' => 'Automation & APIs',
'consulting' => 'Custom Development',
'other' => 'Other Services'
// Prepare friendly labels
$serviceLabels = [
'web-scraping' => 'Web Scraping',
'data-cleaning' => 'Data Cleaning',
'api-development' => 'API Development',
'automation' => 'Automation',
'custom' => 'Custom Solution',
'other' => 'Other'
];
$selected_services = array_map(function($service) use ($service_names) {
return $service_names[$service] ?? $service;
}, $services);
// Create friendly scale names
$scale_names = [
'small' => 'Small Project (One-time extraction, < 10k records)',
'medium' => 'Medium Project (Regular updates, 10k-100k records)',
'large' => 'Large Project (Ongoing service, 100k+ records)',
'enterprise' => 'Enterprise (Complex multi-source solution)'
$scaleLabels = [
'small' => 'Small (under 1,000 records)',
'medium' => 'Medium (1,000 - 50,000 records)',
'large' => 'Large (50,000 - 500,000 records)',
'enterprise' => 'Enterprise (500,000+ records)',
'unsure' => 'Not sure yet'
];
$friendly_scale = $scale_names[$project_scale] ?? $project_scale;
// Create friendly timeline names
$timeline_names = [
'asap' => 'ASAP (Rush job)',
'1-week' => 'Within 1 week',
'2-4-weeks' => '2-4 weeks',
'flexible' => 'Flexible timeline'
$timelineLabels = [
'asap' => 'ASAP',
'2weeks' => 'Within 2 weeks',
'1month' => 'Within a month',
'flexible' => 'Flexible / No rush'
];
$friendly_timeline = $timeline_names[$timeline] ?? $timeline;
// Prepare email content
// Build email
$to = 'info@ukdataservices.co.uk';
$subject = 'New Quote Request - UK Data Services';
$subject = 'New Quote Request - ' . ($serviceLabels[$service_type] ?? $service_type);
// Create detailed HTML email
$emailHTML = '
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>New Quote Request</title>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 700px; margin: 0 auto; padding: 20px; }
.header { background: linear-gradient(135deg, #144784 0%, #179e83 100%); color: white; padding: 30px; text-align: center; border-radius: 8px 8px 0 0; }
.content { background: #f9f9f9; padding: 30px; border-radius: 0 0 8px 8px; }
.section { margin-bottom: 30px; padding: 20px; background: white; border-radius: 8px; border-left: 4px solid #179e83; }
.section-title { font-size: 18px; font-weight: bold; color: #144784; margin-bottom: 15px; }
.field { margin-bottom: 12px; }
.field-label { font-weight: bold; color: #555; }
.field-value { margin-top: 5px; padding: 8px; background: #f8f9fa; border-radius: 4px; }
.services-list { list-style: none; padding: 0; }
.services-list li { padding: 8px; background: #e3f2fd; margin: 5px 0; border-radius: 4px; }
.priority { background: #fff3cd; border-left-color: #ffc107; }
.contact-info { background: #d4edda; border-left-color: #28a745; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🚀 New Quote Request</h1>
<p>UK Data Services</p>
<p style="font-size: 14px; opacity: 0.9;">Received: ' . date('Y-m-d H:i:s') . ' UTC</p>
</div>
<div class="content">
<div class="section contact-info">
<div class="section-title">👤 Contact Information</div>
<div class="field">
<div class="field-label">Name:</div>
<div class="field-value">' . htmlspecialchars($name) . '</div>
</div>
<div class="field">
<div class="field-label">Email:</div>
<div class="field-value">' . htmlspecialchars($email) . '</div>
</div>
<div class="field">
<div class="field-label">Company:</div>
<div class="field-value">' . htmlspecialchars($company ?: 'Not provided') . '</div>
</div>
<div class="field">
<div class="field-label">Phone:</div>
<div class="field-value">' . htmlspecialchars($phone ?: 'Not provided') . '</div>
</div>
</div>
$emailHTML = '<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body style="font-family:Arial,sans-serif;line-height:1.6;">
<div style="max-width:600px;margin:0 auto;padding:20px;">
<div style="background:linear-gradient(135deg,#144784,#179e83);color:white;padding:20px;border-radius:8px 8px 0 0;text-align:center;">
<h1 style="margin:0;">New Quote Request</h1>
<p style="margin:5px 0 0;">UK Data Services</p>
</div>
<div style="background:#f9f9f9;padding:20px;border-radius:0 0 8px 8px;">
<div class="section">
<div class="section-title">🎯 Services Required</div>
<ul class="services-list">';
<div style="background:white;padding:15px;border-radius:8px;margin-bottom:15px;border-left:4px solid #28a745;">
<h3 style="margin:0 0 10px;color:#144784;">Contact Details</h3>
<p><strong>Name:</strong> ' . $name . '</p>
<p><strong>Email:</strong> ' . $email . '</p>
<p><strong>Company:</strong> ' . ($company ?: 'Not provided') . '</p>
</div>
foreach ($selected_services as $service) {
$emailHTML .= '<li>✓ ' . htmlspecialchars($service) . '</li>';
<div style="background:white;padding:15px;border-radius:8px;margin-bottom:15px;border-left:4px solid #179e83;">
<h3 style="margin:0 0 10px;color:#144784;">Project Details</h3>
<p><strong>Service:</strong> ' . ($serviceLabels[$service_type] ?? $service_type) . '</p>
<p><strong>Scale:</strong> ' . ($scaleLabels[$scale] ?? $scale) . '</p>
<p><strong>Timeline:</strong> ' . ($timelineLabels[$timeline] ?? $timeline) . '</p>
<p><strong>Target Sources:</strong> ' . ($data_sources ?: 'Not specified') . '</p>
</div>';
if (!empty($requirements)) {
$emailHTML .= '<div style="background:white;padding:15px;border-radius:8px;margin-bottom:15px;border-left:4px solid #ffc107;">
<h3 style="margin:0 0 10px;color:#144784;">Requirements</h3>
<p>' . nl2br($requirements) . '</p>
</div>';
}
$emailHTML .= '</ul>
</div>
$emailHTML .= '<div style="background:#e9ecef;padding:10px;border-radius:8px;font-size:12px;color:#666;">
<p><strong>Submitted:</strong> ' . date('Y-m-d H:i:s') . ' UTC</p>
<p><strong>IP:</strong> ' . $_SERVER['REMOTE_ADDR'] . '</p>
</div>
<div class="section ' . ($timeline === 'asap' ? 'priority' : '') . '">
<div class="section-title">📊 Project Details</div>
<div class="field">
<div class="field-label">Project Scale:</div>
<div class="field-value">' . htmlspecialchars($friendly_scale) . '</div>
</div>
<div class="field">
<div class="field-label">Timeline:</div>
<div class="field-value">' . htmlspecialchars($friendly_timeline) . '</div>
</div>
<div class="field">
<div class="field-label">Budget Range:</div>
<div class="field-value">' . htmlspecialchars($budget ?: 'Not specified') . '</div>
</div>
</div>
</div></div></body></html>';
<div class="section">
<div class="section-title">🌐 Data Sources</div>
<div class="field-value">' . nl2br(htmlspecialchars($data_sources ?: 'Not specified')) . '</div>
</div>
<div class="section">
<div class="section-title">📝 Detailed Requirements</div>
<div class="field-value">' . nl2br(htmlspecialchars($requirements)) . '</div>
</div>
<div class="section">
<div class="section-title">🔍 Submission Details</div>
<div class="field">
<div class="field-label">IP Address:</div>
<div class="field-value">' . htmlspecialchars($_SERVER['REMOTE_ADDR']) . '</div>
</div>
<div class="field">
<div class="field-label">User Agent:</div>
<div class="field-value">' . htmlspecialchars($_SERVER['HTTP_USER_AGENT']) . '</div>
</div>
<div class="field">
<div class="field-label">Referrer:</div>
<div class="field-value">' . htmlspecialchars($_SERVER['HTTP_REFERER'] ?? 'Direct') . '</div>
</div>
</div>
</div>
</div>
</body>
</html>';
// Email headers
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: text/html; charset=UTF-8\r\n";
$headers .= "From: \"UK Data Services Quote System\" <noreply@ukdataservices.co.uk>\r\n";
$headers .= "From: \"UK Data Services\" <noreply@ukdataservices.co.uk>\r\n";
$headers .= "Reply-To: " . $email . "\r\n";
$headers .= "X-Mailer: PHP/" . phpversion() . "\r\n";
$headers .= "X-Priority: " . ($timeline === 'asap' ? '1' : '3') . "\r\n";
// Create logs directory if it doesn't exist
if (!file_exists('logs')) {
mkdir('logs', 0755, true);
}
// Send email
try {
// Clear any previous errors
error_clear_last();
$emailSent = @mail($to, $subject, $emailHTML, $headers);
if ($emailSent) {
logDebug("SUCCESS: Quote from $email ($name) - Service: $service_type");
$emailSent = mail($to, $subject, $emailHTML, $headers);
// Log to file
$logEntry = date('Y-m-d H:i:s') . " | $name | $email | $service_type | $scale | $timeline\n";
file_put_contents($logDir . '/quote-requests.log', $logEntry, FILE_APPEND | LOCK_EX);
if ($emailSent) {
// Log successful submission
$logEntry = date('Y-m-d H:i:s') . " - Quote request from " . $email . " (" . $_SERVER['REMOTE_ADDR'] . ") - Services: " . implode(', ', $services) . "\n";
file_put_contents('logs/quote-requests.log', $logEntry, FILE_APPEND | LOCK_EX);
sendResponse(true, 'Thank you for your quote request! We will send you a detailed proposal within 24 hours.');
} else {
// Get detailed error information
$lastError = error_get_last();
$errorMsg = $lastError ? $lastError['message'] : 'Unknown mail error';
// Log failed email with detailed error
$logEntry = date('Y-m-d H:i:s') . " - FAILED quote request from " . $email . " (" . $_SERVER['REMOTE_ADDR'] . ") - Error: " . $errorMsg . "\n";
file_put_contents('logs/quote-errors.log', $logEntry, FILE_APPEND | LOCK_EX);
// Check common issues
if (strpos($errorMsg, 'sendmail') !== false) {
error_log("Mail server configuration issue: " . $errorMsg);
}
sendResponse(false, 'There was an error sending your quote request. Please try again or contact us directly at info@ukdataservices.co.uk');
}
} catch (Exception $e) {
// Log exception with full details
$logEntry = date('Y-m-d H:i:s') . " - EXCEPTION: " . $e->getMessage() . " from " . $email . " (" . $_SERVER['REMOTE_ADDR'] . ") - File: " . $e->getFile() . " Line: " . $e->getLine() . "\n";
file_put_contents('logs/quote-errors.log', $logEntry, FILE_APPEND | LOCK_EX);
sendResponse(false, 'There was an error processing your quote request. Please contact us directly at info@ukdataservices.co.uk');
sendResponse(true, 'Thank you for your quote request! We will send you a detailed proposal within 24 hours.');
} else {
$error = error_get_last();
logDebug("FAILED: Email send failed - " . ($error['message'] ?? 'Unknown error'));
sendResponse(false, 'There was an error sending your request. Please try again or contact us at info@ukdataservices.co.uk');
}
?>
?>