Add 'index.php'

This commit is contained in:
2025-09-30 21:26:16 -04:00
parent 9f9b71d9e6
commit 36487f9c99

248
index.php Normal file
View File

@ -0,0 +1,248 @@
<?php
// Simple URL shortener in a single PHP file
// by bagaag.com
// Configuration
$last_file = 'last.txt'; // file to store the last incremented key
$urls_file = 'urls.txt'; // file to store the URLs with their keys
$charset = 'acr8mqbs7di4tuh6e9fjv2gwx0nlk5poy1z3'; // unique set of characters for generating keys
$max_url_length = 100; // urls longer than this are considered nefarious
$allowed_domains = []; // list of allowed domains for URL shortening
$password = 'change me!'; // password to shorten a URL, leave empty to disable password protection
$enable_test = false; // enable the test function to output a series of incremented keys
// Returns the contents of $last_file, or empty string if the file does not exist
function get_last() {
global $last_file;
if (file_exists($last_file)) {
return trim(file_get_contents($last_file));
}
return '';
}
// Writes to $last_file
function set_last($s) {
global $last_file;
file_put_contents($last_file, $s);
}
// Provides the next key by incrementing the previous key.
// Start with empty string and then feed the return value
// back into this function to get the next key, and repeat.
function increment_string($s) {
global $charset;
// convert the charset to an array of characters
$chars = str_to_chars($charset);
// if the string is empty, return the first character
if ($s === '') {
return $chars[0];
}
// convert the input string to an array of characters
$string = str_to_chars($s);
// iterate over the input string characters from end to beginning
for ($index = count($string) - 1; $index >= 0; $index--) {
// find and validate the position of the character in the charset
$char = $string[$index];
$pos = array_search($char, $chars);
if ($pos === false) {
return "Character not in set: $char";
}
// if the character is not the last in the charset, increment it and break
if ($pos < count($chars) - 1) {
$string[$index] = $chars[$pos + 1];
break;
}
// character is the last in the charset; wrap around to the first character
$string[$index] = $chars[0];
// if we are at the beginning of the string, prepend the first character and break
if ($index === 0) {
array_unshift($string, $chars[0]);
break;
}
}
return join('', $string);
}
// Splits a string into an array of characters
function str_to_chars($str, $l = 0) {
if ($l > 0) {
$ret = array();
$len = mb_strlen($str, "UTF-8");
for ($i = 0; $i < $len; $i += $l) {
$ret[] = mb_substr($str, $i, $l, "UTF-8");
}
return $ret;
}
return preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY);
}
// Reads the last key, increments it, saves it, and returns it
function next_key() {
$last = get_last();
$next = increment_string($last);
set_last($next);
return $next;
}
// Adds a URL to urls.txt and returns the generated key
function add_url($url) {
global $urls_file;
$key = next_key();
// append the key and url to urls.txt
file_put_contents($urls_file, $key . ' ' . $url . "\n", FILE_APPEND);
return $key;
}
// Looks up a key in urls.txt and returns the corresponding URL, or null if not found
function get_url($key) {
global $urls_file;
$lines = file($urls_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
list($k, $url) = explode(' ', $line, 2);
if ($k === $key) {
return $url;
}
}
return null;
}
// Test function to print a series of incremented keys
// Usage: output_keys('', 100);
function output_keys($key, $count) {
header('Content-Type: text/plain');
for ($i = 0; $i < $count; $i++) {
$key = increment_string($key);
// set content type to plain text
print($key . "\n");
}
}
// Get input either from a GET or POST request
function get_input($name) {
if (isset($_POST[$name])) {
return $_POST[$name];
} elseif (isset($_GET[$name])) {
return $_GET[$name];
}
return null;
}
// Test if a host is in the allowed domains list
function test_host($host) {
global $allowed_domains;
if (empty($allowed_domains)) {
return true; // if the list is empty, allow all domains
}
return in_array($host, $allowed_domains);
}
/* HTTP Request Handling */
// read input parameters
$url = get_input('url');
$input_password = get_input('password');
$key = get_input('key');
$test_start = get_input('test_start');
$test_count = get_input('test_count');
// If a URL is provided, attempt to shorten it
if ($url) {
// if a password is set, check it
if ($password !== '' && $input_password !== $password) {
http_response_code(401); // Unauthorized
echo "Unauthorized: Incorrect password.\n";
exit;
}
// return 422 if the URL is longer than max_url_length
if (strlen($url) > $max_url_length) {
http_response_code(422); // Unprocessable Entity
echo "Invalid URL.\n";
exit;
}
// return 403 if the URL's domain is not in the allowed list
$parsed_url = parse_url($url);
if ($parsed_url === false || !isset($parsed_url['host']) || !test_host($parsed_url['host'])) {
http_response_code(403);
echo "Forbidden: This URL is not within the list of allowed domains.\n";
exit;
}
// validate the URL and shorten it
if (filter_var($url, FILTER_VALIDATE_URL)) {
$key = add_url($url);
$shortened_url = "https://$_SERVER[HTTP_HOST]/$key";
echo "$shortened_url\n";
} else {
http_response_code(422); // Unprocessable Entity
echo "Invalid URL.\n";
}
exit;
}
// If test parameters are provided, output a series of incremented keys
if ($enabed_test && $test_start !== null && $test_count !== null) {
// validate password
if ($password !== '' && $input_password !== $password) {
http_response_code(401); // Unauthorized
echo "Unauthorized: Incorrect password.\n";
exit;
}
output_keys($test_start, (int)$test_count);
exit;
}
// Otherwise, assume the request URI is a key to look up
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$key = trim($path, '/');
// If the key is valid, look up the URL and redirect
if ($key !== '') {
$url = get_url($key);
if ($url !== null) {
header("Location: $url", true, 301);
exit;
} else {
http_response_code(404);
echo "Not Found.\n";
exit;
}
}
// If no key or url is provided, show a simple HTML form to submit a URL
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bagaag.com Micro URL Shortener</title>
</head>
<body>
<h1>Bagaag.com Micro URL Shortener</h1>
<form method="post" action="">
<label for="url">Enter URL to shorten:</label><br>
<input type="text" id="url" name="url" size="50" required><br><br>
<?php if ($password !== ''): ?>
<label for="password">Enter the password:</label><br>
<input type="password" id="password" name="password" size="20" required><br><br>
<?php endif; ?>
<input type="submit" value="Shorten">
</form>
<?php if ($enable_test): ?>
<h2>Test Key Generation</h2>
<form method="post" action="">
<label for="test_start">Generate keys from:</label><br>
<input type="text" id="test_start" name="test_start" size="50" placeholder="Leave blank to start from beginning"><br><br>
<label for="test_count">How many keys to generate:</label><br>
<input type="text" id="test_count" name="test_count" size="10" required><br><br>
<?php if ($password !== ''): ?>
<label for="password">Enter the password:</label><br>
<input type="password" id="password" name="password" size="20" required><br><br>
<?php endif; ?>
<input type="submit" value="Generate">
</form>
<?php endif; ?>
</body>
</html>