2025-06-07 10:53:32 +01:00
|
|
|
<?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');
|
|
|
|
|
|
2025-06-17 18:43:06 +01:00
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-07 10:53:32 +01:00
|
|
|
// 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');
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-17 18:51:06 +01:00
|
|
|
// Validate CSRF token (if present)
|
|
|
|
|
if (isset($_POST['csrf_token']) && !validateCSRFToken($_POST['csrf_token'])) {
|
2025-06-17 18:43:06 +01:00
|
|
|
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');
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-07 10:53:32 +01:00
|
|
|
// Check rate limiting
|
|
|
|
|
if (!checkRateLimit()) {
|
|
|
|
|
sendResponse(false, 'Too many requests. Please try again later.');
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-17 18:43:06 +01:00
|
|
|
// Spam protection - honeypot field
|
|
|
|
|
if (isset($_POST['website']) && !empty($_POST['website'])) {
|
|
|
|
|
sendResponse(false, 'Spam detected');
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-07 10:53:32 +01:00
|
|
|
// 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));
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-17 18:43:06 +01:00
|
|
|
// 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');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-17 18:51:06 +01:00
|
|
|
// 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');
|
|
|
|
|
}
|
2025-06-17 18:43:06 +01:00
|
|
|
}
|
|
|
|
|
|
2025-06-07 10:53:32 +01:00
|
|
|
// 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 {
|
2025-06-17 18:51:06 +01:00
|
|
|
// Clear any previous errors
|
|
|
|
|
error_clear_last();
|
|
|
|
|
|
2025-06-07 10:53:32 +01:00
|
|
|
$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 {
|
2025-06-17 18:51:06 +01:00
|
|
|
// 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";
|
2025-06-07 10:53:32 +01:00
|
|
|
file_put_contents('logs/quote-errors.log', $logEntry, FILE_APPEND | LOCK_EX);
|
|
|
|
|
|
2025-06-17 18:51:06 +01:00
|
|
|
// 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');
|
2025-06-07 10:53:32 +01:00
|
|
|
}
|
|
|
|
|
} catch (Exception $e) {
|
2025-06-17 18:51:06 +01:00
|
|
|
// 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";
|
2025-06-07 10:53:32 +01:00
|
|
|
file_put_contents('logs/quote-errors.log', $logEntry, FILE_APPEND | LOCK_EX);
|
|
|
|
|
|
2025-06-17 18:51:06 +01:00
|
|
|
sendResponse(false, 'There was an error processing your quote request. Please contact us directly at info@ukdataservices.co.uk');
|
2025-06-07 10:53:32 +01:00
|
|
|
}
|
|
|
|
|
?>
|