Register your script implementation or send comments and feedback.
Historical contact form from the Script Archive - now preserved as a reference.
This page originally served two purposes: allowing users to register their implementations of Script Archive scripts (so they could be showcased) and providing a way to send feedback and comments. Users could also subscribe to the new-scripts mailing list for update notifications.
Showcase your implementation to the community.
Share feedback, bug reports, or suggestions.
Subscribe to updates for new script releases.
Below is a modern Bootstrap recreation of the original registration and comment form. This is a non-functional display showing the original form structure.
Here's how you would build a similar contact form today with proper validation and security:
<!-- Modern HTML5 Contact Form -->
<form id="contactForm" action="/api/contact" method="POST" novalidate>
<!-- CSRF Protection -->
<input type="hidden" name="_token" value="<?= csrf_token() ?>">
<!-- Honeypot (spam protection) -->
<div class="d-none" aria-hidden="true">
<input type="text" name="website" tabindex="-1" autocomplete="off">
</div>
<!-- Contact Type -->
<fieldset class="mb-3">
<legend class="h5">Contact Type</legend>
<div class="form-check">
<input class="form-check-input" type="radio" name="type"
id="typeGeneral" value="general" required checked>
<label class="form-check-label" for="typeGeneral">
General Inquiry
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="type"
id="typeBug" value="bug">
<label class="form-check-label" for="typeBug">
Bug Report
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="type"
id="typeFeature" value="feature">
<label class="form-check-label" for="typeFeature">
Feature Request
</label>
</div>
</fieldset>
<!-- Name -->
<div class="mb-3">
<label for="name" class="form-label">Name *</label>
<input type="text" class="form-control" id="name" name="name"
required minlength="2" maxlength="100"
pattern="[A-Za-z\s\-']+">
<div class="invalid-feedback">
Please enter a valid name (2-100 characters).
</div>
</div>
<!-- Email -->
<div class="mb-3">
<label for="email" class="form-label">Email *</label>
<input type="email" class="form-control" id="email" name="email"
required maxlength="254">
<div class="invalid-feedback">
Please enter a valid email address.
</div>
</div>
<!-- Message -->
<div class="mb-3">
<label for="message" class="form-label">Message *</label>
<textarea class="form-control" id="message" name="message"
rows="5" required minlength="10" maxlength="5000"></textarea>
<div class="form-text">
<span id="charCount">0</span> / 5000 characters
</div>
<div class="invalid-feedback">
Please enter a message (10-5000 characters).
</div>
</div>
<!-- reCAPTCHA -->
<div class="mb-3">
<div class="g-recaptcha" data-sitekey="your-site-key"></div>
</div>
<!-- Submit -->
<button type="submit" class="btn btn-primary" id="submitBtn">
<span class="spinner-border spinner-border-sm d-none" role="status"></span>
Send Message
</button>
</form>
<?php
// Modern PHP Contact Form Handler
// Requires: composer require phpmailer/phpmailer
namespace App\Controllers;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
class ContactController
{
private array $config;
public function __construct()
{
$this->config = [
'to_email' => '[email protected]',
'to_name' => 'Support Team',
'smtp_host' => 'smtp.example.com',
'smtp_user' => '[email protected]',
'smtp_pass' => getenv('SMTP_PASSWORD'),
'recaptcha_secret' => getenv('RECAPTCHA_SECRET')
];
}
public function handle(): array
{
try {
// Rate limiting
$this->checkRateLimit();
// CSRF validation
$this->validateCsrf();
// Honeypot check
if (!empty($_POST['website'])) {
throw new \Exception('Spam detected');
}
// reCAPTCHA verification
$this->verifyRecaptcha();
// Validate input
$data = $this->validateInput();
// Send email
$this->sendEmail($data);
// Log submission
$this->logSubmission($data);
return ['success' => true, 'message' => 'Message sent successfully!'];
} catch (\Exception $e) {
error_log('Contact form error: ' . $e->getMessage());
return ['success' => false, 'message' => $e->getMessage()];
}
}
private function validateInput(): array
{
$rules = [
'type' => ['required', 'in:general,bug,feature'],
'name' => ['required', 'min:2', 'max:100', 'regex:/^[A-Za-z\s\-\']+$/'],
'email' => ['required', 'email', 'max:254'],
'message' => ['required', 'min:10', 'max:5000']
];
$data = [];
$errors = [];
foreach ($rules as $field => $fieldRules) {
$value = trim($_POST[$field] ?? '');
foreach ($fieldRules as $rule) {
if ($rule === 'required' && empty($value)) {
$errors[$field] = ucfirst($field) . ' is required';
break;
}
if (str_starts_with($rule, 'min:')) {
$min = (int) substr($rule, 4);
if (strlen($value) < $min) {
$errors[$field] = ucfirst($field) . " must be at least $min characters";
break;
}
}
if (str_starts_with($rule, 'max:')) {
$max = (int) substr($rule, 4);
if (strlen($value) > $max) {
$errors[$field] = ucfirst($field) . " must be at most $max characters";
break;
}
}
if ($rule === 'email' && !filter_var($value, FILTER_VALIDATE_EMAIL)) {
$errors[$field] = 'Invalid email format';
break;
}
}
$data[$field] = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}
if (!empty($errors)) {
throw new \Exception('Validation failed: ' . implode(', ', $errors));
}
return $data;
}
private function verifyRecaptcha(): void
{
$recaptcha = $_POST['g-recaptcha-response'] ?? '';
$response = file_get_contents(
'https://www.google.com/recaptcha/api/siteverify?' . http_build_query([
'secret' => $this->config['recaptcha_secret'],
'response' => $recaptcha,
'remoteip' => $_SERVER['REMOTE_ADDR']
])
);
$result = json_decode($response, true);
if (!($result['success'] ?? false)) {
throw new \Exception('reCAPTCHA verification failed');
}
}
private function sendEmail(array $data): void
{
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = $this->config['smtp_host'];
$mail->SMTPAuth = true;
$mail->Username = $this->config['smtp_user'];
$mail->Password = $this->config['smtp_pass'];
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
$mail->setFrom($this->config['smtp_user'], 'Website Contact Form');
$mail->addAddress($this->config['to_email'], $this->config['to_name']);
$mail->addReplyTo($data['email'], $data['name']);
$mail->isHTML(true);
$mail->Subject = "[Contact] {$data['type']}: {$data['name']}";
$mail->Body = $this->formatEmailBody($data);
$mail->AltBody = strip_tags($this->formatEmailBody($data));
$mail->send();
}
private function formatEmailBody(array $data): string
{
return "<h2>New Contact Form Submission</h2>
<p><strong>Type:</strong> {$data['type']}</p>
<p><strong>Name:</strong> {$data['name']}</p>
<p><strong>Email:</strong> {$data['email']}</p>
<p><strong>Message:</strong></p>
<blockquote>{$data['message']}</blockquote>
<hr>
<p><small>Sent: " . date('Y-m-d H:i:s') . " from " .
$_SERVER['REMOTE_ADDR'] . "</small></p>";
}
private function checkRateLimit(): void
{
$ip = $_SERVER['REMOTE_ADDR'];
$key = "contact_rate_$ip";
$limit = 5; // 5 submissions
$window = 3600; // per hour
// Implementation depends on your cache system (Redis, file, etc.)
// This is a simplified example
$cacheFile = sys_get_temp_dir() . '/' . md5($key);
$count = (int) @file_get_contents($cacheFile);
if ($count >= $limit) {
throw new \Exception('Too many requests. Please try again later.');
}
file_put_contents($cacheFile, $count + 1);
}
private function validateCsrf(): void
{
// Implementation depends on your framework
// Laravel: csrf_field() automatically validates
// Custom: compare $_POST['_token'] with session token
}
private function logSubmission(array $data): void
{
$log = [
'timestamp' => date('Y-m-d H:i:s'),
'ip' => $_SERVER['REMOTE_ADDR'],
'type' => $data['type'],
'email' => $data['email']
];
file_put_contents(
'/var/log/contact_submissions.log',
json_encode($log) . "\n",
FILE_APPEND | LOCK_EX
);
}
}
?>
// Modern JavaScript Form Validation and Submission
class ContactForm {
constructor(formId) {
this.form = document.getElementById(formId);
this.submitBtn = this.form.querySelector('[type="submit"]');
this.spinner = this.submitBtn.querySelector('.spinner-border');
this.init();
}
init() {
// Real-time validation
this.form.querySelectorAll('input, textarea').forEach(field => {
field.addEventListener('blur', () => this.validateField(field));
field.addEventListener('input', () => this.clearError(field));
});
// Character counter
const message = this.form.querySelector('#message');
const counter = this.form.querySelector('#charCount');
message?.addEventListener('input', () => {
counter.textContent = message.value.length;
counter.classList.toggle('text-danger', message.value.length > 4500);
});
// Form submission
this.form.addEventListener('submit', (e) => this.handleSubmit(e));
}
validateField(field) {
const isValid = field.checkValidity();
field.classList.toggle('is-valid', isValid);
field.classList.toggle('is-invalid', !isValid);
return isValid;
}
clearError(field) {
field.classList.remove('is-invalid');
}
validateForm() {
let isValid = true;
this.form.querySelectorAll('[required]').forEach(field => {
if (!this.validateField(field)) {
isValid = false;
}
});
return isValid;
}
async handleSubmit(e) {
e.preventDefault();
// Client-side validation
if (!this.validateForm()) {
this.showAlert('Please fix the errors in the form.', 'danger');
return;
}
// Check reCAPTCHA
const recaptchaResponse = grecaptcha?.getResponse();
if (!recaptchaResponse) {
this.showAlert('Please complete the reCAPTCHA.', 'warning');
return;
}
// Show loading state
this.setLoading(true);
try {
const formData = new FormData(this.form);
formData.append('g-recaptcha-response', recaptchaResponse);
const response = await fetch(this.form.action, {
method: 'POST',
body: formData,
headers: {
'Accept': 'application/json'
}
});
const result = await response.json();
if (result.success) {
this.showAlert('Message sent successfully! We\'ll get back to you soon.', 'success');
this.form.reset();
grecaptcha?.reset();
this.form.querySelectorAll('.is-valid').forEach(f => f.classList.remove('is-valid'));
} else {
throw new Error(result.message || 'Failed to send message');
}
} catch (error) {
console.error('Form submission error:', error);
this.showAlert(error.message || 'An error occurred. Please try again.', 'danger');
} finally {
this.setLoading(false);
}
}
setLoading(loading) {
this.submitBtn.disabled = loading;
this.spinner.classList.toggle('d-none', !loading);
}
showAlert(message, type = 'info') {
// Remove existing alerts
this.form.querySelectorAll('.form-alert').forEach(a => a.remove());
const alert = document.createElement('div');
alert.className = `alert alert-${type} form-alert mt-3`;
alert.innerHTML = `
${message}
`;
this.form.insertBefore(alert, this.submitBtn.parentElement);
// Auto-dismiss success messages
if (type === 'success') {
setTimeout(() => alert.remove(), 5000);
}
}
}
// Initialize form
document.addEventListener('DOMContentLoaded', () => {
new ContactForm('contactForm');
});
// Email validation helper (RFC 5322 compliant)
function isValidEmail(email) {
const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return pattern.test(email) && email.length <= 254;
}
// Sanitize input (prevent XSS)
function sanitizeInput(input) {
const div = document.createElement('div');
div.textContent = input;
return div.innerHTML;
}
The original form-to-email CGI script.
Historical discussion lists and subscription info.
Another form-based interactive script.