<?php
declare( strict_types=1 );
require_once 'ArithCoder.php';
require_once 'PageGenerator.php';
const MAX_URL_LENGTH = 2048;
error_reporting( 0 );
ini_set( 'display_errors', '0' );
ini_set( 'display_startup_errors', '0' );
ini_set( 'log_errors', '0' );
function redirect_home(): never {
if( !headers_sent() ) {
header( 'Location: /', true, 302 );
}
exit;
}
set_error_handler( function(): bool {
redirect_home();
return true;
});
set_exception_handler( function(): void {
redirect_home();
});
register_shutdown_function( function(): void {
$e = error_get_last();
if( $e !== null && in_array( $e['type'],
[E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR], true ) ) {
redirect_home();
}
});
function base64url_encode( string $data ): string {
return rtrim( strtr( base64_encode( $data ), '+/', '-_' ), '=' );
}
function base64url_decode( string $data ): string|false {
return base64_decode( strtr( $data, '-_', '+/' ), true );
}
function encode_url( string $url ): string {
return base64url_encode( ArithCoder::encode( $url ) );
}
function decode_url( string $encoded ): string|false {
$raw = base64url_decode( $encoded );
return ($raw === false || $raw === '') ? false : ArithCoder::decode( $raw );
}
function is_safe_url( string $url ): bool {
return strlen( $url ) <= MAX_URL_LENGTH
&& !preg_match( '/[\x00-\x1F\x7F]/', $url )
&& filter_var( $url, FILTER_VALIDATE_URL ) !== false
&& in_array( strtolower( parse_url( $url, PHP_URL_SCHEME ) ?? '' ),
['http', 'https'], true );
}
function e( string $s ): string {
return htmlspecialchars( $s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8' );
}
function is_facebook_ua(): bool {
$ua = strtolower( $_SERVER['HTTP_USER_AGENT'] ?? '' );
return str_starts_with( $ua, 'face' ) || str_starts_with( $ua, 'meta-external' );
}
function render_preview_page(): never {
$generator = new PageGenerator();
echo $generator->generate();
exit;
}
$scheme = empty( $_SERVER['HTTPS'] ) || $_SERVER['HTTPS'] === 'off'
? 'http' : 'https';
$base_url = $scheme . '://' . $_SERVER['HTTP_HOST']
. strtok( $_SERVER['REQUEST_URI'], '?' );
if( isset( $_GET['r'] ) && $_GET['r'] !== '' ) {
$raw = $_GET['r'];
if( !preg_match( '#^[A-Za-z0-9\-_]+=*$#', $raw ) ) {
redirect_home();
} else {
$url = decode_url( $raw );
if( $url !== false && is_safe_url( $url ) ) {
if( is_facebook_ua() ) {
render_preview_page();
} else {
header( 'Location: ' . $url, true, 302 );
exit;
}
} else {
redirect_home();
}
}
} else {
session_start();
if( empty( $_SESSION['csrf'] ) ) {
$_SESSION['csrf'] = bin2hex( random_bytes( 32 ) );
}
$csrf = $_SESSION['csrf'];
$result = '';
$error = '';
$input = '';
if( $_SERVER['REQUEST_METHOD'] === 'POST' ) {
if( !isset( $_POST['csrf'] ) || !hash_equals( $csrf, $_POST['csrf'] ) ) {
redirect_home();
} else {
$_SESSION['csrf'] = bin2hex( random_bytes( 32 ) );
$csrf = $_SESSION['csrf'];
$input = trim( $_POST['url'] ?? '' );
if( is_safe_url( $input ) ) {
$result = $base_url . '?r=' . encode_url( $input );
} else {
$error = 'Enter a valid http:// or https:// URL.';
}
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Compressor</title>
<style>
body { font-family: sans-serif; max-width: 600px; margin: 2rem auto; padding: 0 1rem; }
input[type="url"], input[type="text"] { width: 100%; padding: .5rem; font-size: 1rem; margin: .5rem 0; box-sizing: border-box; }
button { padding: .5rem 1rem; font-size: 1rem; cursor: pointer; }
.result { margin-top: 1rem; }
.row { display: flex; gap: .5rem; align-items: center; }
.row input { flex: 1; }
.error { color: red; }
.social-share { display: flex; gap: 1rem; margin-top: 1.5rem; justify-content: center; }
.social-share a, .social-share button { display: block; width: 32px; height: 32px; background: none; border: none; padding: 0; cursor: pointer; fill:
.social-share a:hover, .social-share button:hover { fill:
.social-share svg { width: 100%; height: 100%; display: block; }
</style>
</head>
<body>
<h2>Link</h2>
<form method="post">
<input type="hidden" name="csrf" value="<?= e( $csrf ) ?>">
<input type="url" name="url" placeholder="https://example.com/path" value="<?= e( $input ) ?>" maxlength="<?= MAX_URL_LENGTH ?>" required autofocus>
<button type="submit">Submit</button>
</form>
<?php if( $error ) { ?>
<p class="error"><?= e( $error ) ?></p>
<?php } ?>
<?php if( $result ) { ?>
<div class="result">
<h2>Share</h2>
<div class="row">
<input type="text" id="result" value="<?= e( $result ) ?>" readonly>
<button onclick="
navigator.clipboard.writeText(document.getElementById('result').value)
.then(() => {
this.textContent = 'Copied!';
setTimeout(() => this.textContent = 'Copy', 1500);
})
">Copy</button>
</div>
<div class="social-share">
<a href="https://www.facebook.com/sharer/sharer.php?u=<?=
urlencode( $result ) ?>"e=<?= urlencode( $result ) ?>"
target="_blank" rel="noopener" title="Share to Facebook">
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M9.101 23.691v-7.98H6.627v-3.667h2.474v-1.58c0-4.085 1.848-5.978 5.858-5.978.401 0 .955.042 1.468.103a8.68 8.68 0 0 1 1.141.195v3.325a8.623 8.623 0 0 0-.653-.036 26.805 26.805 0 0 0-.733-.009c-.707 0-1.259.096-1.675.309a1.686 1.686 0 0 0-.679.622c-.258.42-.374.995-.374 1.752v1.297h3.919l-.386 2.103-.287 1.564h-3.246v8.245C19.396 23.238 24 18.179 24 12.044c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.628 3.874 10.35 9.101 11.647Z"/>
</svg>
</a>
<a href="https://x.com/intent/tweet?url=<?= urlencode( $result ) ?>" target="_blank" rel="noopener" title="Share to X">
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>X</title><path d="M14.234 10.162 22.977 0h-2.072l-7.591 8.824L7.251 0H.258l9.168 13.343L.258 24H2.33l8.016-9.318L16.749 24h6.993zm-2.837 3.299-.929-1.329L3.076 1.56h3.182l5.965 8.532.929 1.329 7.754 11.09h-3.182z"/></svg>
</a>
<a href="https://bsky.app/intent/compose?text=<?= urlencode( $result ) ?>" target="_blank" rel="noopener" title="Share to Bluesky">
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Bluesky</title><path d="M5.202 2.857C7.954 4.922 10.913 9.11 12 11.358c1.087-2.247 4.046-6.436 6.798-8.501C20.783 1.366 24 .213 24 3.883c0 .732-.42 6.156-.667 7.037-.856 3.061-3.978 3.842-6.755 3.37 4.854.826 6.089 3.562 3.422 6.299-5.065 5.196-7.28-1.304-7.847-2.97-.104-.305-.152-.448-.153-.327 0-.121-.05.022-.153.327-.568 1.666-2.782 8.166-7.847 2.97-2.667-2.737-1.432-5.473 3.422-6.3-2.777.473-5.899-.308-6.755-3.369C.42 10.04 0 4.615 0 3.883c0-3.67 3.217-2.517 5.202-1.026"/></svg>
</a>
<button type="button" onclick="shareOrCopy('<?= e( $result ) ?>')" title="Share to Instagram">
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Instagram</title><path d="M7.0301.084c-1.2768.0602-2.1487.264-2.911.5634-.7888.3075-1.4575.72-2.1228 1.3877-.6652.6677-1.075 1.3368-1.3802 2.127-.2954.7638-.4956 1.6365-.552 2.914-.0564 1.2775-.0689 1.6882-.0626 4.947.0062 3.2586.0206 3.6671.0825 4.9473.061 1.2765.264 2.1482.5635 2.9107.308.7889.72 1.4573 1.388 2.1228.6679.6655 1.3365 1.0743 2.1285 1.38.7632.295 1.6361.4961 2.9134.552 1.2773.056 1.6884.069 4.9462.0627 3.2578-.0062 3.668-.0207 4.9478-.0814 1.28-.0607 2.147-.2652 2.9098-.5633.7889-.3086 1.4578-.72 2.1228-1.3881.665-.6682 1.0745-1.3378 1.3795-2.1284.2957-.7632.4966-1.636.552-2.9124.056-1.2809.0692-1.6898.063-4.948-.0063-3.2583-.021-3.6668-.0817-4.9465-.0607-1.2797-.264-2.1487-.5633-2.9117-.3084-.7889-.72-1.4568-1.3876-2.1228C21.2982 1.33 20.628.9208 19.8378.6165 19.074.321 18.2017.1197 16.9244.0645 15.6471.0093 15.236-.005 11.977.0014 8.718.0076 8.31.0215 7.0301.0839m.1402 21.6932c-1.17-.0509-1.8053-.2453-2.2287-.408-.5606-.216-.96-.4771-1.3819-.895-.422-.4178-.6811-.8186-.9-1.378-.1644-.4234-.3624-1.058-.4171-2.228-.0595-1.2645-.072-1.6442-.079-4.848-.007-3.2037.0053-3.583.0607-4.848.05-1.169.2456-1.805.408-2.2282.216-.5613.4762-.96.895-1.3816.4188-.4217.8184-.6814 1.3783-.9003.423-.1651 1.0575-.3614 2.227-.4171 1.2655-.06 1.6447-.072 4.848-.079 3.2033-.007 3.5835.005 4.8495.0608 1.169.0508 1.8053.2445 2.228.408.5608.216.96.4754 1.3816.895.4217.4194.6816.8176.9005 1.3787.1653.4217.3617 1.056.4169 2.2263.0602 1.2655.0739 1.645.0796 4.848.0058 3.203-.0055 3.5834-.061 4.848-.051 1.17-.245 1.8055-.408 2.2294-.216.5604-.4763.96-.8954 1.3814-.419.4215-.8181.6811-1.3783.9-.4224.1649-1.0577.3617-2.2262.4174-1.2656.0595-1.6448.072-4.8493.079-3.2045.007-3.5825-.006-4.848-.0608M16.953 5.5864A1.44 1.44 0 1 0 18.39 4.144a1.44 1.44 0 0 0-1.437 1.4424M5.8385 12.012c.0067 3.4032 2.7706 6.1557 6.173 6.1493 3.4026-.0065 6.157-2.7701 6.1506-6.1733-.0065-3.4032-2.771-6.1565-6.174-6.1498-3.403.0067-6.156 2.771-6.1496 6.1738M8 12.0077a4 4 0 1 1 4.008 3.9921A3.9996 3.9996 0 0 1 8 12.0077"/></svg>
</button>
<button type="button" onclick="shareOrCopy('<?= e( $result ) ?>')" title="Share to TikTok">
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>TikTok</title><path d="M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z"/></svg>
</button>
</div>
</div>
<script>
function shareOrCopy(url) {
if( navigator.share ) {
navigator.share({ url: url })
.catch(() => navigator.clipboard.writeText(url).then(() => alert('Link copied!')));
} else {
navigator.clipboard.writeText(url).then(() => alert('Link copied!'));
}
}
</script>
<?php } ?>
<div style="text-align:center; margin-top: 3rem;">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="10" viewBox="0 0 9600 4800">
<path fill="#f00" d="m0 0h2400l99 99h4602l99-99h2400v4800h-2400l-99-99h-4602l-99 99H0z"/>
<path fill="#fff" d="m2400 0h4800v4800h-4800zm2490 4430-45-863a95 95 0 0 1 111-98l859 151-116-320a65 65 0 0 1 20-73l941-762-212-99a65 65 0 0 1-34-79l186-572-542 115a65 65 0 0 1-73-38l-105-247-423 454a65 65 0 0 1-111-57l204-1052-327 189a65 65 0 0 1-91-27l-332-652-332 652a65 65 0 0 1-91 27l-327-189 204 1052a65 65 0 0 1-111 57l-423-454-105 247a65 65 0 0 1-73 38l-542-115 186 572a65 65 0 0 1-34 79l-212 99 941 762a65 65 0 0 1 20 73l-116 320 859-151a95 95 0 0 1 111 98l-45 863z"/>
</svg>
</div>
</body>
</html>