<?php
/**
* TightURL :: A blind redirection service
*
* Copyright (c) 2004-2008, Ron Guerin <hide@address.com>
* portions Copyright (c) 2002,2003 Free Software Foundation
*
* This file implements a blind redirection service named TightURL.
* TightURL is Free Software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* TightURL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* If you are not able to view the LICENSE, which should
* always be possible within a valid and working TightURL release,
* please write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* to get a copy of the GNU General Public License or to report a
* possible license violation.
*
* @package TightURL
* @author Ron Guerin <hide@address.com>
* @license http://www.fsf.org/licenses/gpl.html GNU Public License
* @copyright Copyright © 2004 Ron Guerin
* @filesource
* @link http://tighturl.com TightURL
* @version 0.1
*
*/
define("VERSION", "0.1.3.3");
define("REQUIRED_PHP_VERSION", "4.3.0");
// System defaults, DO NOT EDIT THIS FILE
// Edit tighturl.config.inc.php instead!
global $copyright, $conn, $db, $os, $svcname;
$dbhost = "localhost";
$dbuser = "dbuser";
$dbpass = "dbpass";
$dbname = "tighturl";
$dbtable = "urls";
$FOFMethod=FALSE; //0=Full URL path or mod_rewrite, 1=404-Method compressed URLs
$os="";
// URIBL variables
$uribl = array("multi.surbl.org", "black.uribl.com");
$uriblurl = array("www.surbl.org", "www.uribl.com");
// Bad Behavior variables
$BB2 = true;
$BBstats = true;
$BBstrict = false;
$BBverbose = true;
$BBLogging = true;
$bb2_settings_defaults = "";
// Require submitted URLs to exist?
$mustexist = true;
// Text strings and style variables
$svcname = "URLSquisher";
$verbtext = "Squish";
$pasttext = "Squished";
$tagline = "Squish long URLs to make short ones";
$headcolor = "#006600";
$tablecolor = "#00CC99";
$copystart = date("Y");
$copyrightholder = "SquishURL Enterprises";
// Reserved URLs
$ReservedURL = array("x", "rest", "xmlrpc", "soap", "xml", "atom", "rss", "blog",
"faq", "help", "about", "api", "code", "source", "docs",
"cvs", "arch", "url", "admin", "setup", "svn", "project", "abuse", "cgi-sys", "exploited");
// You REALLY don't want to edit below here unless you know what you're doing.
// *************************************************************************
if (version_compare(phpversion(), REQUIRED_PHP_VERSION)<0) {
die_HTML($svcname, "Error: TightURL ".VERSION." needs PHP >= ".REQUIRED_PHP_VERSION." (you are using ".phpversion().")");
}
if (file_exists("tighturl-install.php")) die_HTML($svcname, "Error: You must remove tighturl-install.php before using $svcname.");
$os=strpos(strtolower(PHP_OS), "win")===false?"nix":"win";
$validurlpattern = "\:\/\/([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&%\$\-]+)*@)"
. "*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])"
. "\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)"
. "\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)"
. "\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])"
. "|((([0-9A-F]{1,4}(((:[0-9A-F]{1,4}){5}::[0-9A-F]{1,4})|((:[0-9A-F]{1,4}){4}"
. "::[0-9A-F]{1,4}(:[0-9A-F]{1,4}){0,1})|((:[0-9A-F]{1,4}){3}::[0-9A-F]{1,4}"
. "(:[0-9A-F]{1,4}){0,2})|((:[0-9A-F]{1,4}){2}::[0-9A-F]{1,4}(:[0-9A-F]{1,4})"
. "{0,3})|(:[0-9A-F]{1,4}::[0-9A-F]{1,4}(:[0-9A-F]{1,4}){0,4})|(::[0-9A-F]{1,4}"
. "(:[0-9A-F]{1,4}){0,5})|(:[0-9A-F]{1,4}){7}))|(::[0-9A-F]{1,4}(:[0-9A-F]{1,4}"
. "){0,6}))|::)|((([0-9A-F]{1,4}(((:[0-9A-F]{1,4}){3}::([0-9A-F]{1,4}){1})"
. "|((:[0-9A-F]{1,4}){2}::[0-9A-F]{1,4}(:[0-9A-F]{1,4}){0,1})|((:[0-9A-F]{1,4})"
. "{1}::[0-9A-F]{1,4}(:[0-9A-F]{1,4}){0,2})|(::[0-9A-F]{1,4}(:[0-9A-F]{1,4}"
. "){0,3})|((:[0-9A-F]{1,4}){0,5})))|([:]{2}[0-9A-F]{1,4}(:[0-9A-F]{1,4}){0,4}))"
. ":|::)((25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{0,2})\.){3}(25[0-5]|2[0-4][0-9]|"
. "[0-1]?[0-9]{0,2})"
. "|localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|int|mil|net|org"
. "|mobi|biz|arpa|info|name|pro|aero|coop|museum"
. "|[a-zA-Z]{2}))(\:[0-9]+)*(\/.($|[a-zA-Z0-9\.\:\,\?\'\(\)\\\*\+&%\$;|#\=~_\-\s@]*))*\/*";
$validipv4pattern = ":\/\/(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\."
. "(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])"
. "\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\/*";
$forbid = "\.(cmd|bat|exe|scr|pif|vbs|js|pif|msi|cdr)";
// ****** !All overridable configuration variables must go above this line! ******
if (! isset($antiabuse)) $antiabuse = true;
if (! isset($netchecks)) $netchecks = true;
if (! isset($mustexist)) $mustexist = true;
// Status: 0=Ok, 1=Warn, 2=Black, 3=Policy, 4=Complaints
if (file_exists("tighturl.urlpattern.inc.php")) include("tighturl.urlpattern.inc.php");
if (file_exists("tighturl.tltpattern.inc.php")) include("tighturl.tltpattern.inc.php");
if (file_exists("tighturl.redirpattern.inc.php")) include("tighturl.redirpattern.inc.php");
if (file_exists("tighturl.ptcpattern.inc.php")) include("tighturl.ptcpattern.inc.php");
if (file_exists("tighturl.blpattern.inc.php")) include("tighturl.blpattern.inc.php");
if (file_exists("tighturl.config.inc.php")) include("tighturl.config.inc.php");
// Figure out our copyright string
$thisyear = date("Y");
$copyright = $copystart;
if ($copystart != $thisyear) $copyright .= "-" . $thisyear;
$uribls = "";
for ($i=0; $i<count($uribl); $i++) {
if ($i > 0) $uribls .= ", ";
$uribls .= "<a href='http://" . $uriblurl[$i] . "'>" . $uribl[$i] . "</a>";
}
if (! $FOFMethod) $parm = "?i="; // We need the parameter tag
// Figure out correct self
if (strncmp($_SERVER['PHP_SELF'], $_SERVER['REQUEST_URI'], strlen($_SERVER['PHP_SELF'])) != 0) {
if (preg_match("|(.*)/.*$|",$_SERVER['PHP_SELF'],$matches)) $self = $matches[1];
if (! preg_match("|.*/$|", $self)) $self .= "/";
}
else {
$self = $_SERVER['PHP_SELF']; // We need the script name
if (! preg_match("|.*/$|", $self)) $self .= "/";
}
// Connect to MySQL, open database.
$conn = @mysql_connect($dbhost, $dbuser, $dbpass) or die_HTML($svcname, "Error: Cannot connect to database.");
$db = mysql_select_db($dbname, $conn) or die_HTML($svcname, "Error: Cannot select database. ". mysql_error());
// When in doubt, turn Bad Behavior on, set it to FALSE in the config to turn it off.
if (! isset($BB2)) $BB2 = true;
// If user has not turned off Bad Behavior in the config, use BB2 (highly recommended)
if ($BB2 && file_exists("bad-behavior/bad-behavior-tighturl.php")) require_once("bad-behavior/bad-behavior-tighturl.php");
// Figure out what kind of request this is and service it.
// This is klugey. Clean up later.
// also I think data should be sanitized immediately
if ((isset($_REQUEST['save']) && $_REQUEST['save'] == 'y')
&& (isset($_REQUEST['url']) && ! empty($_REQUEST['url']) && trim($_REQUEST['url']) != ""
&& (preg_match("/^.*url=(.*)$/", $_SERVER['QUERY_STRING'], $matches) != 0))) {
$url = trim($matches[1]);
if (preg_match("/^(.*)&tighturlaction.*$/", $matches[1], $matches)) $url = $matches[1];
save_URL(urldecode($url));
}
elseif (isset($_REQUEST['i']) && !empty($_REQUEST['i'])) {
lookup_ID($_REQUEST['i']);
}
elseif ($FOFMethod && preg_match("/^\/+([a-zA-Z0-9]+)\/*(.*)\/*$/", $_SERVER['REQUEST_URI'], $matches)) {
lookup_ID($matches[1]);
}
elseif ($FOFMethod && $_SERVER['REQUEST_URI'] != "/") {
display_HTML("", "", "Error: Couldn't find a valid " . $svcname . " URI.");
}
else {
display_HTML("", "main");
}
exit;
// *************************************************************************
/**
* sanitize a string for SQL input
*/
function sanitize_sql_string($string) {
return(mysql_real_escape_string($string));
}
/**
* Counts the number of times a substring is contained in a given string.
*/
function countSubstrs($haystack, $needle) {
return (($p = strpos($haystack, $needle)) === false) ? 0 : (1 + countSubstrs(substr($haystack, $p+1), $needle));
}
/**
* Checks to see if a given URI is on a URI blacklist.
* Currently this means SURBL (http://www.surbl.org) and URIBL (http://www.uribl.com)
*
* Returns TRUE if the domain is listed on any configured URIBLs, returns FALSE if
* anything goes wrong or the anti-abuse system is turned off.
*
* A companion URI extractor must be written for the below issues
* Must be changed to do full resolution of redirections on URI, simulating a browser
* Must be changed to do IPv6 lookups
* Must be changed to check multiple URIs (maybe a wrapper instead)
* Must be changed to optionally check HTML entity encoded versions of URIs
* Must be changed to handle URIBL's inclusion of some third-level domains.
*
*/
function URI_on_URIBL($uri) {
// This code does not yet properly implement a correct and efficient querying
// of URI BL data.
global $uribl, $uribluri, $validschemes, $validurlpattern, $validipv4pattern,
$antiabuse, $netchecks, $tltlds;
// Everything gets a pass if antiabuse or network tests are off.
if ((! $antiabuse) || (! $netchecks)) return(false);
$uribls = "";
if ($uri) {
// Test for IPv4 address, reverse the quads if found
if (preg_match("/^".$validschemes.$validipv4pattern."/", $uri, $matches)) {
$domain=$matches[5] . "." . $matches[4] . "." . $matches[3] . "." . $matches[2];
}
else {
// strip out second-level domain name, *unless* on exception list,
// in which case, strip out third level also and test that instead.
// FIX: when testing uribl.com lists, also test additional level. First hit wins.
preg_match("/^".$validschemes.$validurlpattern."$/", $uri, $matches);
$domain = $matches[4];
if (preg_match("/".$tltlds."$/", $domain, $matches)) {$levels = 2;} else {$levels = 1;}
// klugey stripping routine to reduce domain to base domain name
// expect regex wojuld be better
$ss = countSubstrs($domain, ".");
while ($ss > $levels) {
$chop = strpos($domain, ".");
$domain = substr($domain, $chop + 1);
$ss = countSubstrs($domain, ".");
}
}
// Query URI blacklists to see if domain/IP appears as target in known spam
// or something involved in a malware/phishing attack.
for ($i=0; $i<count($uribl); $i++) {
$fqdn = $domain . "." . $uribl[$i];
$recexists = gethostbyname($fqdn); // ghbn weirdly returns the name on failure
if (($recexists != $fqdn) && preg_match("/^127\.", $recexists)) {
if ($i > 0) $uribls .= ", ";
$uribls .= $uribl[$i];
}
}
return ($uribls); // change to return an array of indexes into the URIBL array
}
}
/**
* Checks to see if a given URL is a Reserved URL.
*
* Returns TRUE if the ID is listed as a Reserved URL.
*/
function on_Reserve($decimal) {
global $ReservedURL;
$res=FALSE;
if ($decimal) {
$sexatrigesimal = base_convert($decimal, 10, 36);
for ($i=0; $i<count($ReservedURL); $i++) {
if ($sexatrigesimal == strtolower($ReservedURL[$i])) return TRUE;
}
return FALSE;
}
}
/**
* Save the given URL in the database if unique and return the ID or return an existing ID for given URL.
* The ID returned will be a sexatrigesimal (Base-36) number.
*
* Saves the URL in the database, converts the decimal ID value returned by the database to
* a sexatrigesimal value, and displays the generated TightURL.
*/
function save_URL($url) {
global $dbtable, $svcname, $FOFMethod, $validschemes, $validurlpattern, $pasttext, $self, $redir, $bl,
$antiabuse, $netchecks, $ptc;
$selfref = "|^(http://" . $_SERVER['HTTP_HOST'] . $self . ")|i";
$aliasref = "|^(http://www." . $_SERVER['HTTP_HOST'] . $self . ")|i";
$remote = $_SERVER['REMOTE_ADDR'];
if (preg_match($selfref, $url) || preg_match($aliasref, $url)) {
display_HTML("", "", "Error: A " . $svcname . " URL cannot point to another URL within " . $_SERVER['HTTP_HOST'] . $self . " .", $url, "", $url);
}
elseif (! preg_match("/^".$validschemes.$validurlpattern."$/", $url)) {
display_HTML("", "", "Error: That URL (".htmlspecialchars(strip_tags($url)).") is not valid.", $url, "", $url);
}
elseif (($forbid != "") && (preg_match("!.*".$forbid."$!i", $url))) {
display_HTML("", "", "Error: Executable URIs are not accepted here due to phishing/malware abuse.", $url, "", $url);
}
// This code was never meant to be in a release. oops
// elseif (preg_match("!.*\.gif$!", $url)) { // replace this with image comparison
// display_HTML("", "", "Error: URL rejected for service abuse.", $url, "", $url);
// }
elseif (isset($mustexist) && $mustexist && isset($netchecks) && $netchecks) {
if (! Resolve_URL($decodedurl)) display_HTML("", "", "Error: Submitted URL does not exist on the public Internet.", $url, "", $url);
}
elseif (isset($bl) && preg_match("/.*".$bl.".*/i", $url)) { // delete this crap
// echo "url: $url bl: $bl\n";
display_HTML("", "", "Error: URL rejected for violating our terms of use.", $url, "", $url);
}
elseif (preg_match("/.*".$redir.".*/i", $url)) {
display_HTML("", "", "Error: ".$svcname." is not a URL obfuscation service, and does not accept redirection links.", $url, "", $url);
}
elseif (isset($ptc) && preg_match("/.*".$ptc.".*/i", $url)) {
display_HTML("", "", "Error: ".$svcname." does not accept PTC (Pay To Click) links due to spamming abuse.", $url, "", $url);
}
else {
$safeurl = sanitize_sql_string($url);
$result = mysql_query("SELECT MAX(id) FROM $dbtable") or display_HTML("", "", "Error: $svcname system error.", $url, "", $url);
$lastid = mysql_result($result, 0) + 1;
$guesssexatrigesimal = base_convert($lastid, 10, 36);
$guessurl = "http://" . $_SERVER['HTTP_HOST'] . $self;
if (! $FOFMethod) $guessurl .= "?i="; // We need the parameter tag
$guessurl .= $guesssexatrigesimal; // Append the Base-36 ID to the URL
if ( strlen($guessurl) >= strlen($url) ) {
display_HTML("", "", "Fail: That URL cannot be shortened by $svcname. Sorry!", $url, "", $url);
}
else {
if ($antiabuse && $netchecks) {$lists = URI_on_URIBL($url);} else {$lists = false;}
if (! $lists) {
$rows=0; $srows=0; $testurl=$safeurl;
if (preg_match("/\/$/", $testurl)) $testurl = rtrim($testurl,"/");
$req = "SELECT * FROM $dbtable WHERE url = '$testurl/';";
$res = mysql_query($req);
$srows = @mysql_num_rows($res) or $srows = 0;
if ($srows == 0) {
$req = "SELECT * FROM $dbtable WHERE url = '$testurl';";
$res = mysql_query($req);
$rows = @mysql_num_rows($res) or $rows = 0;
}
if ($rows == 0 && $srows == 0) {
do {
$req ="INSERT INTO $dbtable (id, url, adddate, addip) ";
$req .= "VALUES ('', '$safeurl', NOW(), '$remote');";
if (mysql_query($req)) {
$decimal = mysql_insert_id();
}
else {
die_HTML($svcname, "Error: Database failure.");
}
$reserved_id = on_Reserve($decimal);
if ($reserved_id) {
// Delete this record so it doesn't override the reserved ID. (?)
$req = "DELETE FROM $dbtable WHERE id = '$decimal';";
$res = mysql_query($req) or die_HTML($svcname, "Error: Database failure.");
}
} while ($reserved_id);
}
else {
// Return existing ID for this duplicate request
$decimal = mysql_result($res, 0, "id");
}
$sexatrigesimal = base_convert($decimal, 10, 36);
$address = "http://" . $_SERVER['HTTP_HOST'] . $self;
if (! $FOFMethod) $address .= "?i="; // We need the parameter tag
$address .= $sexatrigesimal; // Append the Base-36 ID to the URL
display_HTML("", "save", "", $url, $address);
}
else {
display_HTML("HTTP/1.0 403 Forbidden", "", "Error: Submitted URL (" . $url . ") is listed in " . $lists . ". You may not create a " . $svcname . " link for it.");
}
}
}
}
/**
* Looks up given ID in the database and redirects, displays template, or
* displays error page. Expects the ID to be a sexatrigesimal (Base-36) number,
* which is the format used by TightURLs.
*
* We convert the ID to decimal before looking it up in the database, as the
* ID field is a MySQL autoincrement decimal value.
*/
function lookup_ID($sexatrigesimal) {
global $dbtable, $svcname;
// First, convert unsafe user input sexatrigesimal to decimal, which will be safe.
$decimal = base_convert ($sexatrigesimal, 36, 10);
$req = "SELECT * FROM $dbtable WHERE id = '$decimal';";
$res = mysql_query($req) or die_HTML($svcname, "Error: Query failed");
$rows = mysql_num_rows($res);
if (($rows != 0) && (mysql_result($res, 0, "url") != "")) {
// Change this logic to display a templated page instead?
switch (mysql_result($res, 0, "status")) {
case 5:
display_HTML("HTTP/1.0 403 Forbidden", "complaints");
return;
break;
case 4:
display_HTML("HTTP/1.0 403 Forbidden", "policy");
return;
break;
case 3:
display_HTML("HTTP/1.0 403 Forbidden", "blacklist");
return;
break;
default:
$url = stripslashes(mysql_result($res, 0, "url"));
break;
}
$req ="update $dbtable set lasthit=NOW(), hits=hits+1 where id='$decimal';";
$res = mysql_query($req);
header("HTTP/1.0 301 Moved Permanently");
header("Location: $url");
}
elseif (! on_Reserve($decimal)) { // Not found, Not on reserve
display_HTML("HTTP/1.0 404 Not Found", "", "Error: That " . $svcname . " ID is not in our database.");
}
else { // It's a(n implied) Reserved URL
// Is this a template or an API?
$sexatrigesimal = strtolower($sexatrigesimal);
switch ($sexatrigesimal) {
case "rest":
api_REST();
break;
case "xmlrpc":
api_XMLRPC();
break;
case "soap":
api_SOAP();
break;
default:
display_HTML("", $sexatrigesimal);
}
}
}
function api_REST() {
die_HTML($svcname, "Error: REST API not implemented yet.", "HTTP/1.0 501 Not Implemented");
}
function api_XMLRPC() {
die_HTML($svcname, "Error: XML-RPC API not implemented yet.", "HTTP/1.0 501 Not Implemented");
}
function api_SOAP() {
die_HTML($svcname, "Error: SOAP API not implemented yet.", "HTTP/1.0 501 Not Implemented");
}
/**
* Display HTML page using template and template variables.
*
* Reads in the main system template file (tighturl.tmpl) into $html .
*
* $code
* HTTP 1.0 status code and message.
*
* $template
* Checks for the existence of a subtemplate named tighturl.$template.tmpl
* and replaces template variable $HTML in the main template tighturl.tmpl
* with the contents of tighturl.$template.tmpl if any.
*
* Then any remaining $HTML from the only or inner template is replaced by $content,
* along with $url, $tighturl, and $input. A variety of other replacements are
* made using various global variables.
*
* $content
* HTML content to be replace template variable $HTML
*
* $url
* URL submitted to TightURL
*
* $tighturl
* TightURL generated for $url
*
* $input
* When submitted URL does not validate it is passed back as $input
*
* Template variables are words in all capital letters that start with a
* $ symbol, such as $TEMPLATEVARIABLE. TightURL now supports at least
* 20 template variables. At runtime, these template variables are replaced
* by program variables.
* - $HTML : HTML passed into the function as $input by the program or an inner template
* - $PARM : Parameter tag when not using 404-Method
* - $URL : URL submitted to TightURL
* - $URLLEN : Length of the submitted URL
* - $TIGHTURL : TightURL generated for the submitted URL
* - $TIGHTURLLEN : Length of generated TightURL
* - $DIFF : Difference in length between submitted and TightURLs
* - $INPUT : Bad input URL being passed back to output form
* - $SVCNAME : Name of the TightURL service
* - $HEADCOLOR : Color of the H1 Header tag
* - $TABLECOLOR : Color of the table containing URL input field
* - $TAGLINE : Tagline of the TightURL service
* - $CPASTTEXT : Capitalized past-tense word for tightening URLs
* - $PASTTEXT : Non-Capitalized past-tense word for tightening URLs
* - $CVERBTEXT : Capitalized action word for tightening URLs
* - $VERBTEXT : Non-Capitalized action word for tightening URLs
* - $COPYRIGHT : Copyright duration string generated from $copystart global variable,
* will be current 4-digit year if $copystart not defined.
* - $COPYRIGHTHOLDER : Name of copyright holder
* - $URLBLS : HTML string of URIBLs TightURL is checking
* - $HOST : Hostname TightURL is running on
* - $SELF : Name TightURL is invoked as
*/
function display_HTML ($code, $template, $content="", $url="", $tighturl="", $input="") {
global $svcname, $verbtext, $pasttext, $tagline, $uribls, $parm,
$headcolor, $tablecolor, $copyright, $copyrightholder, $self, $BB2;
// $url = htmlspecialchars($url);
if ($code="") $code = "HTTP/1.0 200 OK";
if (preg_match("/\/$/", $template)) $template = rtrim($template,"/");
if (file_exists("tighturl.tmpl")) {
$html = file_get_contents("tighturl.tmpl");
if (($template != "") && file_exists("tighturl." . $template . ".tmpl")) {
$template = file_get_contents("tighturl." . $template . ".tmpl");
$html = preg_replace("/\\\$HTML/", $template, $html);
}
elseif ($template != "") {
die_HTML($svcname, "Error: Template file tighturl." . $template . ".tmpl cannot be found.");
}
if (substr($content, 0, 6) == "Error:") {
$content = preg_replace("/Error:/", "<big><font color='red'>Error:", $content)."</font></big>";
}
if ($content) $content .= "<br />\n";
// Always replace longer similar tokens before shorter ones. Things won't work the
// way you expect if you replace $URL first, and then replace $URLMORELETTERS.
$html = preg_replace("/\\\$HTML/", $content, $html);
$html = preg_replace("/\\\$PARM/", $parm, $html);
$html = preg_replace("/\\\$URLLEN/", strlen($url), $html);
$html = preg_replace("/\\\$URL/", htmlspecialchars(strip_tags($url), ENT_QUOTES), $html);
$html = preg_replace("/\\\$INPUT/", $input, $html);
$html = preg_replace("/\\\$TIGHTURLVER/", VERSION, $html);
$html = preg_replace("/\\\$TIGHTURLLEN/", strlen($tighturl), $html);
$html = preg_replace("/\\\$TIGHTURL/", $tighturl, $html);
$html = preg_replace("/\\\$DIFF/", strlen($url)-strlen($tighturl), $html);
$html = preg_replace("/\\\$SVCNAME/", $svcname, $html);
$html = preg_replace("/\\\$HEADCOLOR/", $headcolor, $html);
$html = preg_replace("/\\\$TABLECOLOR/", $tablecolor, $html);
$html = preg_replace("/\\\$TAGLINE/", $tagline, $html);
$html = preg_replace("/\\\$CPASTTEXT/", $pasttext, $html);
$html = preg_replace("/\\\$PASTTEXT/", strtolower($pasttext), $html);
$html = preg_replace("/\\\$VERBTEXT/", strtolower($verbtext), $html);
$html = preg_replace("/\\\$CVERBTEXT/", $verbtext, $html);
$html = preg_replace("/\\\$COPYRIGHTHOLDER/", $copyrightholder, $html);
$html = preg_replace("/\\\$COPYRIGHT/", $copyright, $html);
$html = preg_replace("/\\\$URIBLS/", $uribls, $html);
$html = preg_replace("/\\\$HOST/", $_SERVER['HTTP_HOST'], $html);
$html = preg_replace("/\\\$SELF/", $self, $html);
$html = preg_replace("/\\\$__/", "$", $html); // Template Variables shown as text instead of substituted
if (preg_match("|<title>(.*)</title>|is", $html, $matches))
$html = preg_replace("|<title>(.*)</title>|is", "<title>" . strip_tags($matches[1]) . "</title>", $html);
if ($BB2) {
$html = preg_replace("/\\\$BBSTATS/", bb2_insert_stats(), $html);
$bb2code = bb2_insert_head();
if (preg_match("|<head>(.*)</head>|is", $html, $matches))
$html = preg_replace("|<head>(.*)</head>|is", "<head>\n" . $bb2code . $matches[1] . "</head>", $html);
}
else {
$html = preg_replace("/\\\$BBSTATS/", "", $html);
}
header($code);
echo $html;
}
else {
die_HTML($svcname, "Error: <big><font color='red'>Error: TightURL Redirection service (" . $svcname . ") site template not found.</font></big>");
}
}
/**
* Die in an HTML-friendly way, without the benefit of a template.
* Use display_HTML to "die" using the TightURL site template.
*/
function die_HTML($svcname, $errmsg, $code="HTTP/1.0 500 Internal Server Error") {
header($code);
echo "<html>\n <head>\n <title>" . $svcname . "</title>\n </head>\n <body>\n";
echo " " . $errmsg . "<br />\n";
echo " </body>\n</html>";
die;
}
/**
* Verifies the existence and accessibility of a resource in a given URL.
*
* Returns FALSE if the resource does not exist or cannot be accessed using
* supplied authentication information, else returns the resolved and verified
* URL. Given URL is returned as resolved to itself if $netchecks are off.
*
* Will recurse through redirection chains up to 12 times by default. This
* value is preferably selectable by the user in a configuration screen
* somewhere, but probably should not be lower than 12, as attempts are made
* to detect HTML and JavaScript redirects in addition to HTTP redirects, and
* a dozen redirects to find the end is quite possible.
*
* Unfortunately Google's GFE server erroneously returns 404 errors when
* they should be returning something like a 405, making it impossible to
* use HTTP HEAD to verify the existence of resources front-ended by GFE.
* Additionally Amazon throws a 405 attempting to HEAD some of their resources
* so this function does not attempt to use HEAD at all.
*
* BUG: Presently only does HTTP
*
*/
//function Resolve_URL ($url, $chain=12, &$resolvedchain=array("")) {
function Resolve_URL ($url, $chain=12, &$resolvedchain) {
// $resolvedchain = array($url);
if (! isset($resolvedchain)) $resolvedchain=array("");
global $netchecks;
if (! $netchecks) {
// If network checks off, accept submitted URL as resolved.
return($url);
}
$parsed = parse_url($url);
$pre = isset($parsed['scheme']) ? $parsed['scheme'].':'.((strtolower($parsed['scheme']) == 'mailto') ? '' : '//') : '';
$pre .= isset($parsed['user']) ? $parsed['user'].(isset($parsed['pass']) ? ':'.$parsed['pass'] : '').'@' : '';
$pre .= isset($parsed['host']) ? $parsed['host'] : '';
$pre .= isset($parsed['port']) ? ':'.$parsed['port'] : '';
if(isset($parsed['path']))
$post = (substr($parsed['path'], 0, 1) == '/') ? $parsed['path'] : ('/'.$parsed['path']);
$post .= isset($parsed['query']) ? '?'.$parsed['query'] : '';
$post .= isset($parsed['fragment']) ? '#'.$parsed['fragment'] : '';
$resolved = false;
// Change this to support all protocols TightURL supports, not just HTTP
if (! isset($parsed['port']) || $parsed['port'] == 0) $parsed['port'] = 80;;
if($connection = @fsockopen ($parsed['host'], $parsed['port'], $errno, $errstr, 5)) {
stream_set_timeout($connection, 5);
// HTTP send Connection: Close so we don't have to wait
// Google's GFE handling of HEAD is broken, and Amazon returns 405 on HEAD so had to use GET
fwrite($connection, "GET ".$post." HTTP/1.0\r\nHost: ".$parsed['host']."\r\nConnection: Close\r\n\r\n");
while (!feof($connection)) {
$line_read=fgets($connection);
if ($line_read == "") break; //blank line is header delimiter, if you see it you're done here
//Fix: change this and start parsing the body for HTML-based redirections.
if (preg_match("/HTTP\/\S* +(\S*) /", $line_read, $matches)) { // Look for certain HTTP status codes
switch ($matches[1]) {
case 200: // Ok, we have a final destination (as far as HTTP is concerned)
case 201: // Created, we have a final destination
case 202: // Accepted, we have a final destination
case 203: // Non-authoritative reply, we have a final destination
case 204: // No content, we have a final destination
case 205: // Reset content, we have a final destination
case 206: // Partial content, we have a final destination
case 207: // Multi-status, we have a final destination
case 304: // Not Modified (this is ok)
case 401: // Authorization required (this is ok)
case 402: // Payment required (this is ok)
case 403: // Forbidden (but also ok)
case 405: // Method not allowed (but also ok)
case 406: // Not acceptable (acceptable here unless someone tells us otherwise)
case 409: // Conflict (acceptable unless someone tells us otherwise)
case 421: // Too many connections (fail ok)
case 426: // Use TLS (fail ok)
case 500: // Internal server error (fail ok)
case 502: // Bad gateway (fail ok)
case 503: // Service unavailable (fail ok)
case 504: // Gateway timeout (fail ok)
case 505: // HTTP version not supported (fail ok)
case 509: // Bandwidth exceeded pseudo code (fail ok)
$resolved = $url;
$resolvedchain[] = $url;
break 2;
case 300:
case 301:
case 302:
case 307:
break;
case 404: // Not found
case 408: // Request timeout (this URL will never work again)
case 410: // Gone (and not coming back)
break 2;
default:
$resolved = $url;
$resolvedchain[] = $url;
break 2;
}
}
// If this is a redirect (300, 301, 302, 307), follow it if the chain isn't too long
if (preg_match("/Location: (.*)\r\n/", $line_read, $matches)) {
fclose($connection);
$connection = false;
$resolvedchain[] = $url;
if ($chain > 0 ) $resolved = Resolve_URL($matches[1], $chain - 1, $resolvedchain);
break;
}
}
// Parse body here?
if ($connection) fclose($connection);
}
return($resolved);
}
?>