A collection of community-contributed scripts, add-ons, and extensions for Script Archive programs.
Contributions from users who extended and enhanced the original scripts.
Over the years, the Script Archive community created numerous add-ons, modifications, and utility scripts that extended the functionality of the core CGI programs. These contributions ranged from simple enhancements to complete rewrites with new features.
Auto-reply emails, database logging, fax integration, and file sending capabilities.
Multiple boards, search, preview, subscriptions, digests, and access control.
Database converters, duplicate removers, bookmark importers, and more.
| Add-on | Author | Description | Features |
|---|---|---|---|
| BFormMail | Brian Sietz | Enhanced FormMail v1.6 with multiple additions | Auto-reply Database Fax Support |
| MailFile Script | Mike Wheeler | Send multiple files by email from web requests | File Attachment Multi-file |
| SendCGI | SMH Software | Email text files to users from web forms | Text Email Simple Form |
<?php
// Modern email with auto-reply using PHPMailer
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
require 'vendor/autoload.php';
function processFormWithAutoReply($formData) {
// Send main notification
$mail = new PHPMailer(true);
try {
$mail->isSMTP();
$mail->Host = 'smtp.example.com';
$mail->SMTPAuth = true;
$mail->Username = '[email protected]';
$mail->Password = 'password';
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
$mail->setFrom('[email protected]', 'Website');
$mail->addAddress('[email protected]');
$mail->Subject = 'New Form Submission';
$mail->Body = formatFormData($formData);
$mail->send();
// Send auto-reply to visitor
$autoReply = new PHPMailer(true);
$autoReply->isSMTP();
// ... same SMTP config
$autoReply->setFrom('[email protected]', 'Website');
$autoReply->addAddress($formData['email']);
$autoReply->Subject = 'Thank you for your message';
$autoReply->Body = "Dear {$formData['name']},\n\nThank you for contacting us...";
$autoReply->send();
// Log to database
logToDatabase($formData);
return true;
} catch (Exception $e) {
error_log("Mail Error: " . $mail->ErrorInfo);
return false;
}
}
function logToDatabase($data) {
$pdo = new PDO('mysql:host=localhost;dbname=forms', 'user', 'pass');
$stmt = $pdo->prepare("INSERT INTO submissions (name, email, message, created_at) VALUES (?, ?, ?, NOW())");
$stmt->execute([$data['name'], $data['email'], $data['message']]);
}
?>
Craig D. Horton of DBASICS created an extensive collection of WWWBoard modifications that added enterprise-level features to the basic discussion board.
Run multiple separate discussion boards using a single wwwboard.cgi script installation.
Add search functionality to find messages across all WWWBoard posts.
Allow users to preview their message before final submission.
Restrict posting to authorized users or block troublesome posters.
Email notifications when new messages are posted.
Send daily digest emails instead of individual notifications.
<?php
// Modern forum subscription system
class ForumSubscription {
private $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function subscribe($userId, $threadId, $type = 'instant') {
$stmt = $this->pdo->prepare("
INSERT INTO subscriptions (user_id, thread_id, notification_type, created_at)
VALUES (?, ?, ?, NOW())
ON DUPLICATE KEY UPDATE notification_type = ?
");
return $stmt->execute([$userId, $threadId, $type, $type]);
}
public function unsubscribe($userId, $threadId) {
$stmt = $this->pdo->prepare("
DELETE FROM subscriptions WHERE user_id = ? AND thread_id = ?
");
return $stmt->execute([$userId, $threadId]);
}
public function notifySubscribers($threadId, $newPost) {
// Get instant notification subscribers
$stmt = $this->pdo->prepare("
SELECT u.email, u.name, s.notification_type
FROM subscriptions s
JOIN users u ON s.user_id = u.id
WHERE s.thread_id = ? AND s.notification_type = 'instant'
");
$stmt->execute([$threadId]);
foreach ($stmt->fetchAll() as $subscriber) {
$this->sendNotification($subscriber, $newPost);
}
}
public function sendDailyDigest() {
// Get digest subscribers and their threads
$stmt = $this->pdo->prepare("
SELECT u.id, u.email, u.name
FROM users u
JOIN subscriptions s ON u.id = s.user_id
WHERE s.notification_type = 'digest'
GROUP BY u.id
");
$stmt->execute();
foreach ($stmt->fetchAll() as $user) {
$posts = $this->getTodaysPosts($user['id']);
if (!empty($posts)) {
$this->sendDigestEmail($user, $posts);
}
}
}
}
?>
// Real-time forum notifications with WebSockets
class ForumNotifications {
constructor(userId) {
this.userId = userId;
this.socket = null;
this.subscriptions = new Set();
}
connect() {
this.socket = new WebSocket('wss://example.com/ws/forum');
this.socket.onopen = () => {
this.socket.send(JSON.stringify({
type: 'auth',
userId: this.userId
}));
};
this.socket.onmessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case 'new_post':
this.handleNewPost(data);
break;
case 'subscription_confirmed':
this.subscriptions.add(data.threadId);
break;
}
};
}
subscribe(threadId) {
this.socket.send(JSON.stringify({
type: 'subscribe',
threadId: threadId
}));
}
unsubscribe(threadId) {
this.socket.send(JSON.stringify({
type: 'unsubscribe',
threadId: threadId
}));
this.subscriptions.delete(threadId);
}
handleNewPost(data) {
// Show browser notification
if (Notification.permission === 'granted') {
new Notification('New Reply', {
body: `${data.author} replied to "${data.threadTitle}"`,
icon: '/images/forum-icon.png'
});
}
// Update UI
this.updateThreadBadge(data.threadId);
this.showToast(`New reply in ${data.threadTitle}`);
}
showToast(message) {
const toast = document.createElement('div');
toast.className = 'toast show position-fixed bottom-0 end-0 m-3';
toast.innerHTML = `
${message}
`;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 5000);
}
}
// Initialize
const notifications = new ForumNotifications(currentUserId);
notifications.connect();
Author: Jude Lynell Hollins
A freeware Perl script for interactive sorting of bibliographic citations on the web. Based on the Free For All Link concept, it allows users to submit and organize citations by category.
Version: 1.0 (December 15, 1995)
Converts a Free For All link page into a database format suitable for the Random Link Generator.
#!/usr/bin/perl
# make_list.pl - Convert FFA links to Random Link database
# Usage: perl make_list.pl links.html > urls.txt
use strict;
use warnings;
my $input_file = shift || 'links.html';
open(my $fh, '<', $input_file) or die "Cannot open $input_file: $!";
while (my $line = <$fh>) {
# Extract URLs from anchor tags
while ($line =~ /href\s*=\s*["']?([^"'\s>]+)/gi) {
my $url = $1;
# Skip internal links
next if $url =~ /^#/;
next if $url =~ /^mailto:/i;
print "$url\n";
}
}
close($fh);
Author: Usama Wazeer
Language: C
Automatically removes duplicate links from your links.html file. Reports all duplicate URLs and creates a clean output file.
<?php
// Remove duplicate links from HTML file
function removeDuplicateLinks($inputFile, $outputFile) {
$content = file_get_contents($inputFile);
$urls = [];
$duplicates = [];
$lineNum = 0;
$lines = explode("\n", $content);
$cleanLines = [];
foreach ($lines as $line) {
$lineNum++;
// Check for URLs in this line
if (preg_match_all('/href\s*=\s*["\']?([^"\'>\s]+)/i', $line, $matches)) {
foreach ($matches[1] as $url) {
$normalizedUrl = strtolower(trim($url));
if (isset($urls[$normalizedUrl])) {
$duplicates[] = [
'url' => $url,
'first_line' => $urls[$normalizedUrl],
'duplicate_line' => $lineNum
];
continue 2; // Skip this entire line
}
$urls[$normalizedUrl] = $lineNum;
}
}
$cleanLines[] = $line;
}
// Write clean file
file_put_contents($outputFile, implode("\n", $cleanLines));
// Report duplicates
if (!empty($duplicates)) {
echo "Found " . count($duplicates) . " duplicate URLs:\n";
foreach ($duplicates as $dup) {
echo " {$dup['url']} - First: line {$dup['first_line']}, Duplicate: line {$dup['duplicate_line']}\n";
}
}
return count($duplicates);
}
// Usage
removeDuplicateLinks('links.html', 'links_clean.html');
?>
Author: Jon Wichmann Hansen
Converts Netscape Navigator 1.2 - 2.0 beta bookmark files into a format readable by the Random Link Generator.
// Convert browser bookmarks (HTML) to various formats
class BookmarkConverter {
constructor(htmlContent) {
this.parser = new DOMParser();
this.doc = this.parser.parseFromString(htmlContent, 'text/html');
this.bookmarks = [];
this.parseBookmarks(this.doc.body);
}
parseBookmarks(element, folder = 'root') {
const links = element.querySelectorAll('a');
links.forEach(link => {
if (link.href && !link.href.startsWith('javascript:')) {
this.bookmarks.push({
title: link.textContent.trim(),
url: link.href,
folder: folder,
addDate: link.getAttribute('add_date'),
icon: link.getAttribute('icon')
});
}
});
// Parse folders
const folders = element.querySelectorAll('h3');
folders.forEach(h3 => {
const folderName = h3.textContent.trim();
const dl = h3.nextElementSibling;
if (dl && dl.tagName === 'DL') {
this.parseBookmarks(dl, folderName);
}
});
}
toRandomLinkFormat() {
// Format: URL|Title (pipe-delimited)
return this.bookmarks
.map(b => `${b.url}|${b.title}`)
.join('\n');
}
toJSON() {
return JSON.stringify(this.bookmarks, null, 2);
}
toCSV() {
const header = 'Title,URL,Folder\n';
const rows = this.bookmarks
.map(b => `"${b.title.replace(/"/g, '""')}","${b.url}","${b.folder}"`)
.join('\n');
return header + rows;
}
}
// Usage with file input
document.getElementById('bookmarkFile').addEventListener('change', async (e) => {
const file = e.target.files[0];
const content = await file.text();
const converter = new BookmarkConverter(content);
console.log('Random Link Format:');
console.log(converter.toRandomLinkFormat());
console.log('\nJSON Format:');
console.log(converter.toJSON());
});
These add-ons were made possible by generous contributions from the Script Archive community:
The original form-to-email script that many add-ons extended.
The discussion board that inspired numerous community enhancements.
The script that utility tools like bookmark converters targeted.