From tj, 4 Months ago, written in Plain Text.
Embed
  1. <?php
  2.  
  3. /**
  4.  * This is a lightweight proxy for serving images, generally meant to be used alongside SSL
  5.  *
  6.  * Simple Machines Forum (SMF)
  7.  *
  8.  * @package SMF
  9.  * @author Simple Machines http://www.simplemachines.org
  10.  * @copyright 2016 Simple Machines and individual contributors
  11.  * @license http://www.simplemachines.org/about/smf/license.php BSD
  12.  *
  13.  * @version 2.0.14
  14.  */
  15.  
  16. define('SMF', 'proxy');
  17.  
  18. /**
  19.  * Class ProxyServer
  20.  */
  21. class ProxyServer
  22. {
  23.         /** @var bool $enabled Whether or not this is enabled */
  24.         protected $enabled;
  25.  
  26.         /** @var int $maxSize The maximum size for files to cache */
  27.         protected $maxSize;
  28.  
  29.         /** @var string $secret A secret code used for hashing */
  30.         protected $secret;
  31.  
  32.         /** @var string The cache directory */
  33.         protected $cache;
  34.  
  35.  
  36.         /**
  37.          * Constructor, loads up the Settings for the proxy
  38.          *
  39.          * @access public
  40.          */
  41.         public function __construct()
  42.         {
  43.                 global $image_proxy_enabled, $image_proxy_maxsize, $image_proxy_secret, $cachedir, $sourcedir;
  44.  
  45.                 require_once(dirname(__FILE__) . '/Settings.php');
  46.                 require_once($sourcedir . '/Class-CurlFetchWeb.php');
  47.  
  48.                 // Turn off all error reporting; any extra junk makes for an invalid image.
  49.                 error_reporting(0);
  50.  
  51.                 $this->enabled = (bool) $image_proxy_enabled;
  52.                 $this->maxSize = (int) $image_proxy_maxsize;
  53.                 $this->secret = (string) $image_proxy_secret;
  54.                 $this->cache = $cachedir . '/images';
  55.         }
  56.  
  57.         /**
  58.          * Checks whether the request is valid or not
  59.          *
  60.          * @access public
  61.          * @return bool Whether the request is valid
  62.          */
  63.         public function checkRequest()
  64.         {
  65.                 if (!$this->enabled)
  66.                         return false;
  67.  
  68.                 // Try to create the image cache directory if it doesn't exist
  69.                 if (!file_exists($this->cache))
  70.                         if (!mkdir($this->cache) || !copy(dirname($this->cache) . '/index.php', $this->cache . '/index.php'))
  71.                                 return false;
  72.  
  73.                 if (empty($_GET['hash']) || empty($_GET['request']))
  74.                         return false;
  75.  
  76.                 $hash = $_GET['hash'];
  77.                 $request = $_GET['request'];
  78.  
  79.                 if (md5($request . $this->secret) != $hash)
  80.                         return false;
  81.  
  82.                 // Attempt to cache the request if it doesn't exist
  83.                 if (!$this->isCached($request)) {
  84.                         return $this->cacheImage($request);
  85.                 }
  86.  
  87.                 return true;
  88.         }
  89.        
  90.  
  91.         /**
  92.          * Serves the request
  93.          *
  94.          * @access public
  95.          * @return void
  96.          */
  97.         public function serve()
  98.         {
  99.                 $request = $_GET['request'];
  100.                 $cached_file = $this->getCachedPath($request);
  101.                 $cached = json_decode(file_get_contents($cached_file), true);
  102.  
  103.                 // Did we get an error when trying to fetch the image
  104.                 $response = $this->checkRequest();
  105.                 if (!$response) {
  106.                         // Throw a 404
  107.                         header('HTTP/1.0 404 Not Found');
  108.                         exit;
  109.                 }
  110.                 // Right, image not cached? Simply redirect, then.
  111.                 if (!$response) {
  112.                         header('Location: ' . $request, false, 301);
  113.                 }
  114.  
  115.                 // Is the cache expired?
  116.                 if (!$cached || time() - $cached['time'] > (5 * 86400))
  117.                 {
  118.                         @unlink($cached_file);
  119.                         if ($this->checkRequest())
  120.                                 $this->serve();
  121.                         exit;
  122.                 }
  123.  
  124.                 // Make sure we're serving an image
  125.                 $contentParts = explode('/', !empty($cached['content_type']) ? $cached['content_type'] : '');
  126.                 if ($contentParts[0] != 'image')
  127.                         exit;
  128.  
  129.                 header('Content-type: ' . $cached['content_type']);
  130.                 header('Content-length: ' . $cached['size']);
  131.                 echo base64_decode($cached['body']);
  132.         }
  133.  
  134.         /**
  135.          * Returns the request's hashed filepath
  136.          *
  137.          * @access public
  138.          * @param string $request The request to get the path for
  139.          * @return string The hashed filepath for the specified request
  140.          */
  141.         protected function getCachedPath($request)
  142.         {
  143.                 return $this->cache . '/' . sha1($request . $this->secret);
  144.         }
  145.  
  146.         /**
  147.          * Check whether the image exists in local cache or not
  148.          *
  149.          * @access protected
  150.          * @param string $request The image to check for in the cache
  151.          * @return bool Whether or not the requested image is cached
  152.          */
  153.         protected function isCached($request)
  154.         {
  155.                 return file_exists($this->getCachedPath($request));
  156.         }
  157.  
  158.         /**
  159.          * Attempts to cache the image while validating it
  160.          *
  161.          * @access protected
  162.          * @param string $request The image to cache/validate
  163.          * @return bool|int Whether the specified image was cached or error code when accessing
  164.          */
  165.         protected function cacheImage($request)
  166.         {
  167.                 $dest = $this->getCachedPath($request);
  168.                 $curl = new curl_fetch_web_data(array(CURLOPT_BINARYTRANSFER => 1));
  169.                 $request = $curl->get_url_data($request);
  170.                 $responseCode = $request->result('code');
  171.                 $response = $request->result();
  172.                
  173.                 if (empty($response)) {
  174.                         return false;
  175.                 }
  176.                
  177.                 if ($responseCode != 200) {
  178.                         return false;
  179.                 }
  180.  
  181.                 $headers = $response['headers'];
  182.  
  183.                 // Make sure the url is returning an image
  184.                 $contentParts = explode('/', !empty($headers['content-type']) ? $headers['content-type'] : '');
  185.                 if ($contentParts[0] != 'image')
  186.                         return false;
  187.  
  188.                 // Validate the filesize
  189.                 if ($response['size'] > ($this->maxSize * 1024))
  190.                         return false;
  191.  
  192.                 return (file_put_contents($dest, json_encode(array(
  193.                         'content_type' => $headers['content-type'],
  194.                         'size' => $response['size'],
  195.                         'time' => time(),
  196.                         'body' => base64_encode($response['body']),
  197.                 )))) === false ? 1 : null;
  198.         }
  199. }
  200.  
  201. $proxy = new ProxyServer();
  202. $proxy->serve();
  203.  

captcha