Files
ukaiautomation/quote-handler.php
Peter 623b29dea4 fix
2025-06-17 18:51:06 +01:00

410 lines
15 KiB
PHP

<?php
// Quote Form Handler with Enhanced Security
session_start();
// Security headers
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block');
header('Content-Type: application/json');
// 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);
}
// Rate limiting
function checkRateLimit() {
$ip = $_SERVER['REMOTE_ADDR'];
$key = 'quote_' . md5($ip);
if (!isset($_SESSION[$key])) {
$_SESSION[$key] = ['count' => 0, 'time' => time()];
}
$data = $_SESSION[$key];
if (time() - $data['time'] > 3600) {
$_SESSION[$key] = ['count' => 0, 'time' => time()];
$data = $_SESSION[$key];
}
if ($data['count'] >= 3) {
return false;
}
return true;
}
// Input validation
function validateInput($data, $type = 'text') {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
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;
default:
return $data;
}
}
// Response function
function sendResponse($success, $message, $data = null) {
$response = [
'success' => $success,
'message' => $message
];
if ($data !== null) {
$response['data'] = $data;
}
echo json_encode($response);
exit;
}
// Handle POST requests only
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
sendResponse(false, 'Invalid request method');
}
// Validate CSRF token (if present)
if (isset($_POST['csrf_token']) && !validateCSRFToken($_POST['csrf_token'])) {
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;
}
// Check for blocked IPs first
if (!checkBlockedIP()) {
sendResponse(false, 'Access temporarily restricted');
}
// Check rate limiting
if (!checkRateLimit()) {
sendResponse(false, 'Too many requests. Please try again later.');
}
// Spam protection - honeypot field
if (isset($_POST['website']) && !empty($_POST['website'])) {
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
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
$suspiciousAgents = ['curl', 'wget', 'python', 'bot', 'crawler', 'spider', 'scraper'];
foreach ($suspiciousAgents as $agent) {
if (stripos($userAgent, $agent) !== false) {
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');
}
}
// Sanitize services array
$services = array_map(function($service) {
return htmlspecialchars(trim($service), ENT_QUOTES, 'UTF-8');
}, $services);
// 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'
];
$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)'
];
$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'
];
$friendly_timeline = $timeline_names[$timeline] ?? $timeline;
// Prepare email content
$to = 'info@ukdataservices.co.uk';
$subject = 'New Quote Request - UK Data Services';
// 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, #667eea 0%, #764ba2 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 #667eea; }
.section-title { font-size: 18px; font-weight: bold; color: #667eea; 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>
<div class="section">
<div class="section-title">🎯 Services Required</div>
<ul class="services-list">';
foreach ($selected_services as $service) {
$emailHTML .= '<li>✓ ' . htmlspecialchars($service) . '</li>';
}
$emailHTML .= '</ul>
</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 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 .= "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) {
// 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');
}
?>