<?php
namespace XF\Proxy;
class Linker
{
protected $types = [];
protected $secret = '';
protected $pather;
// example: proxy.php?{type}={url}&hash={hash}
protected $linkFormat = '';
/**
* @var array[]
*/
protected $bypassDomains;
public function __construct($linkFormat, array $types, $secret, callable $pather)
{
$this->linkFormat = $linkFormat;
$this->types = $types;
$this->secret = $secret;
$this->pather = $pather;
}
/**
* @param string $type
* @param array $bypass List of domains to bypass, use * as a wildcard (a value of * will bypass everything)
*/
public function setBypassDomains(string $type, array $bypass)
{
$this->bypassDomains[$type] = array_filter($bypass, 'strlen');
}
public function generate($type, $url)
{
if (empty($this->types[$type]))
{
return null;
}
if ($this->isUrlBypassed($type, $url))
{
return null;
}
$link = strtr($this->linkFormat, [
'{type}' => urlencode($type),
'{url}' => urlencode($url),
'{hash}' => urlencode($this->hash($url))
]);
if ($this->pather)
{
$pather = $this->pather;
$link = $pather($link, 'base');
}
return $link;
}
public function generateExtended($type, $url, array $options = [])
{
$url = $this->generate($type, $url);
if ($url === null)
{
return null;
}
if ($options)
{
$queryString = http_build_query($options);
$url .= (strpos($url, '?') !== false ? '&' : '?') . $queryString;
}
return $url;
}
public function isTypeEnabled($type)
{
return !empty($this->types[$type]);
}
public function isUrlBypassed(string $type, string $url): bool
{
$bypassDomains = $this->bypassDomains[$type] ?? null;
if (!$bypassDomains)
{
return false;
}
$urlParsed = parse_url($url);
if (!$urlParsed)
{
return false;
}
if (!isset($urlParsed['host'], $urlParsed['scheme']))
{
return false;
}
if (strtolower($urlParsed['scheme']) !== 'https')
{
// always proxy non-https links
return false;
}
$host = strtolower($urlParsed['host']);
foreach ($bypassDomains AS $bypassDomain)
{
if ($bypassDomain == '*')
{
return true;
}
if ($bypassDomain[0] != '/')
{
$regex = '#(^|\.)' . preg_quote($bypassDomain, '#') . '$#i';
}
else
{
if (preg_match('/\W[\s\w]*e[\s\w]*$/', $bypassDomain))
{
// can't run a /e regex
continue;
}
$regex = $bypassDomain;
}
try
{
if (preg_match($regex, $host))
{
return true;
}
}
catch (\ErrorException $e) {}
}
return false;
}
public function verifyHash($url, $hash)
{
return $this->hash($url) === $hash;
}
public function hash($url)
{
return hash_hmac('md5', $url, $this->secret);
}
}