From e2dfefa119c9ab3558be852d6fea265be6342f63 Mon Sep 17 00:00:00 2001 From: Raruto Date: Thu, 26 Mar 2020 14:59:53 +0100 Subject: [PATCH] fix https://github.com/Raruto/tile-proxy-php/issues/1 --- .htaccess | 4 +- config.php | 7 ++ functions.php | 285 ++++++++++++++++++++++++++++++++++++++++++++++++++ index.php | 258 ++------------------------------------------- 4 files changed, 301 insertions(+), 253 deletions(-) create mode 100644 functions.php diff --git a/.htaccess b/.htaccess index 8d3a3f2..b15e90e 100644 --- a/.htaccess +++ b/.htaccess @@ -1,5 +1,5 @@ - - Order allow,deny + + Order deny,allow Deny from all diff --git a/config.php b/config.php index 85c49f5..79000a1 100644 --- a/config.php +++ b/config.php @@ -8,6 +8,13 @@ * @license https://www.gnu.org/licenses/gpl-3.0.txt GNU/GPLv3 */ +/** + * Public App name. + * + * @var string + */ +$config['user_agent'] = 'Tile-Proxy-PHP/0.1'; + /** * Whitelist of supported tile servers * diff --git a/functions.php b/functions.php new file mode 100644 index 0000000..de3de1c --- /dev/null +++ b/functions.php @@ -0,0 +1,285 @@ + + * @link https://github.com/Raruto/tile-proxy-php + * @license https://www.gnu.org/licenses/gpl-3.0.txt GNU/GPLv3 + */ + +/** + * Generate a real tile server url + * + * @param string $url eg: 'https://{switch:a,b,c}.tile.openstreetmap.org/{z}/{x}/{y}.png'. + * @return string eg: 'https://a.tile.openstreetmap.org/0/0/0.png'. + */ +function generate_tile_server_url( $url ) { + global $z, $x, $y; + $has_matches = preg_match( '/{switch:(.*?)}/', $url, $domains ); + if ( ! $has_matches ) { + $domains = [ '', '' ]; + } + $domains = explode( ',', $domains[1] ); + $url = preg_replace( '/{switch:(.*?)}/', '{s}', $url ); + $url = preg_replace( '/{s}/', $domains[ array_rand( $domains ) ], $url ); + $url = preg_replace( '/{z}/', $z, $url ); + $url = preg_replace( '/{x}/', $x, $url ); + $url = preg_replace( '/{y}/', $y, $url ); + return $url; +} + +/** + * Convert Map Degrees to Radiant + * + * @param float $deg map degrees. + * @return float + */ +function deg_to_rad( $deg ) { + return $deg * M_PI / 180; +} +/** + * Convert Map Longitude to Tile-X Coordinates + * + * @param float $lng map longitude. + * @param int $zoom map zoom level. + * @return int + */ +function lng_to_tile_x( $lng, $zoom ) { + return floor( ( ( $lng + 180 ) / 360 ) * pow( 2, $zoom ) ); +} + +/** + * Convert Map Latitude to Tile-Y Coordinates + * + * @param float $lat map latitude. + * @param int $zoom map zoom level. + * @return int + */ +function lat_to_tile_y( $lat, $zoom ) { + return floor( ( 1 - log( tan( deg_to_rad( $lat ) ) + 1 / cos( deg_to_rad( $lat ) ) ) / M_PI ) / 2 * pow( 2, $zoom ) ); +} +/** + * Finds if a number is within a given range + * + * @param float $number number to check. + * @param float $min left range number. + * @param float $max right range number. + * @return bool + */ +function in_range( $number, $min, $max ) { + return $number >= $min && $number <= $max; +} + +/** + * Check if cached file is expired + * + * @param string $file filename path. + * @return bool + */ +function is_file_expired( $file ) { + global $ttl; + return filemtime( $file ) < time() - ( $ttl * 30 ); +} + +/** + * Check if a remote resource exists + * + * @param string $url remote file url. + * @return bool + */ +function remote_file_exists( $url ) { + $ch = curl_init( $url ); + curl_setopt( $ch, CURLOPT_NOBODY, true ); + curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true ); + curl_exec( $ch ); + $http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE ); + curl_close( $ch ); + if ( $http_code == 200 ) { + return true; + } + return false; +} + +/** + * Download a remote resource + * + * @param string $url remote file url. + * @param string $folder folder where to download the remote resource. + * @param string $file file onto download the remote resource. + * @return void + */ +function download_remote_file( $url, $folder, $file ) { + global $user_agent; + + if ( ! is_dir( $folder ) ) { + mkdir( $folder, 0755, true ); + } + + $fp = fopen( $file, 'wb' ); + + $ch = curl_init( $url ); + curl_setopt( $ch, CURLOPT_FILE, $fp ); + curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true ); + curl_setopt( $ch, CURLOPT_HEADER, 0 ); + curl_setopt( $ch, CURLOPT_USERAGENT, $user_agent ); + + if ( is_localhost() ) { + curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, false ); + curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false ); + } + + curl_exec( $ch ); + curl_close( $ch ); + + fflush( $fp ); // need to insert this line for proper output when tile is first requested. + fclose( $fp ); +} + +/** + * Output a local file + * + * @param string $file file of the local resource. + * @return void + */ +function output_local_file( $file ) { + print_local_file_headers( $file ); + readfile( $file ); +} + +/** + * Proxy a remote resource + * + * @param string $url remote file url. + * @return void + */ +function proxy_remote_file( $url ) { + global $user_agent; + + $context = stream_context_create( + array( + 'http' => array( + 'method' => 'GET', + 'user_agent' => $user_agent, + ), + ) + ); + + $fp = @fopen( $url, 'rb', false, $context ); + if ( ! $fp ) { + print_404_page(); + return; + } + print_remote_file_headers( $url ); + fpassthru( $fp ); +} + +/** + * Get remote file headers, similar to: get_headers( $url ) + * + * @param string $url remote file url. + * @return array + * @link https://stackoverflow.com/a/41135574 + */ +function get_remote_file_headers( $url ) { + global $user_agent; + + $headers = []; + $ch = curl_init(); + + curl_setopt( $ch, CURLOPT_URL, $url ); + curl_setopt( $ch, CURLOPT_HEADER, true ); + curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true ); + curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); + curl_setopt( $ch, CURLOPT_USERAGENT, $user_agent ); + + if ( is_localhost() ) { + curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, false ); + curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false ); + } + + // this function is called by curl for each header received. + curl_setopt( + $ch, CURLOPT_HEADERFUNCTION, + function( $curl, $header ) use ( &$headers ) { + $headers[] = $header; + return strlen( $header ); + } + ); + curl_exec( $ch ); + curl_close( $ch ); + + return $headers; +} + +/** + * Check if this a local Server instance. + */ +function is_localhost() { + $server_addr = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : null; + return in_array( $server_addr, [ '127.0.0.1', '::1' ], true ); +} + +/** + * Print the default apache 404 page + * + * @return void + */ +function print_404_page() { + header( $_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found' ); + $request_uri = htmlspecialchars( $_SERVER['REQUEST_URI'] ); + echo ''; + echo "404 Not Found

Not Found

The requested URL {$request_uri} was not found on this server.

"; +} + +/** + * Send to browser remote file headers + * + * @param string $url remote file url. + * @return void + */ +function print_remote_file_headers( $url ) { + $headers = get_remote_file_headers( $url ); + print_file_headers( $headers ); +} + +/** + * Send to browser local file headers + * + * @param string $file local file. + * @return void + */ +function print_local_file_headers( $file ) { + global $ttl; + $headers = array( + 'Expires:' => gmdate( 'D, d M Y H:i:s', time() + $ttl * 60 ) . ' GMT', + 'Last-Modified:' => gmdate( 'D, d M Y H:i:s', filemtime( $file ) ) . ' GMT', + 'Cache-Control:' => 'public, max-age=' . ( $ttl * 60 ), // for MSIE 5. + 'Content-Type:' => 'image/png', + ); + print_file_headers( $headers ); +} + +/** + * Send to browser file headers filtering it with the previously array in config file + * + * @param array $file_headers eg: array( 'Content-Type:' => 'image/png' ) or array( 'Access-Control-Allow-Origin: *' ). + * + * @return void + */ +function print_file_headers( &$file_headers ) { + global $headers; + // send headers only if not previously defined in config file. + foreach ( $file_headers as $header => $value ) { + if ( is_string( $header ) ) { + header( $header . ' ' . $value ); + } else { + header( $value ); + } + } + // send all remaing default headers defined in config file. + foreach ( $headers as $header => $value ) { + header_remove( rtrim( $header, ':' ) ); + header( $header . ' ' . $value ); + } +} diff --git a/index.php b/index.php index 02eb2a7..13cb25f 100644 --- a/index.php +++ b/index.php @@ -15,19 +15,20 @@ * @license https://www.gnu.org/licenses/gpl-3.0.txt GNU/GPLv3 */ -// TODO: code refactoring ( wrap it within a dedicated class.. ) - // User configs. require_once 'config.php'; +// Tile functions. +require_once 'functions.php'; // Default configs. -$servers = @$config['servers'] ?: array( +$user_agent = @$config['user_agent'] ?: ''; +$servers = @$config['servers'] ?: array( 'osm' => 'https://{switch:a,b,c}.tile.openstreetmap.org/{z}/{x}/{y}.png', 'otm' => 'https://{switch:a,b,c}.tile.opentopomap.org/{z}/{x}/{y}.png', ); -$bbox = @$config['bbox'] ?: '-180,-90,180,90'; -$ttl = @$config['ttl'] ?: 86400; -$headers = array_change_key_case( +$bbox = @$config['bbox'] ?: '-180,-90,180,90'; +$ttl = @$config['ttl'] ?: 86400; +$headers = array_change_key_case( @$config['headers'] ?: array( 'Access-Control-Allow-Origin:' => '*', ), CASE_LOWER @@ -81,248 +82,3 @@ if ( ! is_file( $file ) || ( is_file_expired( $file ) && remote_file_exists( $ur // Send to browser any previously cached tile. output_local_file( $file ); exit; - -/* // functions ///////////////////////////////////////////////////////////// */ - -/** - * Generate a real tile server url - * - * @param string $url eg: 'https://{switch:a,b,c}.tile.openstreetmap.org/{z}/{x}/{y}.png'. - * @return string eg: 'https://a.tile.openstreetmap.org/0/0/0.png'. - */ -function generate_tile_server_url( $url ) { - global $z, $x, $y; - $has_matches = preg_match( '/{switch:(.*?)}/', $url, $domains ); - if ( ! $has_matches ) { - $domains = [ '', '' ]; - } - $domains = explode( ',', $domains[1] ); - $url = preg_replace( '/{switch:(.*?)}/', '{s}', $url ); - $url = preg_replace( '/{s}/', $domains[ array_rand( $domains ) ], $url ); - $url = preg_replace( '/{z}/', $z, $url ); - $url = preg_replace( '/{x}/', $x, $url ); - $url = preg_replace( '/{y}/', $y, $url ); - return $url; -} - -/** - * Convert Map Degrees to Radiant - * - * @param float $deg map degrees. - * @return float - */ -function deg_to_rad( $deg ) { - return $deg * M_PI / 180; -} -/** - * Convert Map Longitude to Tile-X Coordinates - * - * @param float $lng map longitude. - * @param int $zoom map zoom level. - * @return int - */ -function lng_to_tile_x( $lng, $zoom ) { - return floor( ( ( $lng + 180 ) / 360 ) * pow( 2, $zoom ) ); -} - -/** - * Convert Map Latitude to Tile-Y Coordinates - * - * @param float $lat map latitude. - * @param int $zoom map zoom level. - * @return int - */ -function lat_to_tile_y( $lat, $zoom ) { - return floor( ( 1 - log( tan( deg_to_rad( $lat ) ) + 1 / cos( deg_to_rad( $lat ) ) ) / M_PI ) / 2 * pow( 2, $zoom ) ); -} -/** - * Finds if a number is within a given range - * - * @param float $number number to check. - * @param float $min left range number. - * @param float $max right range number. - * @return bool - */ -function in_range( $number, $min, $max ) { - return $number >= $min && $number <= $max; -} - -/** - * Check if cached file is expired - * - * @param string $file filename path. - * @return bool - */ -function is_file_expired( $file ) { - global $ttl; - return filemtime( $file ) < time() - ( $ttl * 30 ); -} - -/** - * Check if a remote resource exists - * - * @param string $url remote file url. - * @return bool - */ -function remote_file_exists( $url ) { - $ch = curl_init( $url ); - curl_setopt( $ch, CURLOPT_NOBODY, true ); - curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true ); - curl_exec( $ch ); - $http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE ); - curl_close( $ch ); - if ( $http_code == 200 ) { - return true; - } - return false; -} - -/** - * Download a remote resource - * - * @param string $url remote file url. - * @param string $folder folder where to download the remote resource. - * @param string $file file onto download the remote resource. - * @return void - */ -function download_remote_file( $url, $folder, $file ) { - if ( ! is_dir( $folder ) ) { - mkdir( $folder, 0755, true ); - } - - $fp = fopen( $file, 'wb' ); - - $ch = curl_init( $url ); - curl_setopt( $ch, CURLOPT_FILE, $fp ); - curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true ); - curl_setopt( $ch, CURLOPT_HEADER, 0 ); - curl_exec( $ch ); - curl_close( $ch ); - - fflush( $fp ); // need to insert this line for proper output when tile is first requested. - fclose( $fp ); -} - -/** - * Output a local file - * - * @param string $file file of the local resource. - * @return void - */ -function output_local_file( $file ) { - print_local_file_headers( $file ); - readfile( $file ); -} - -/** - * Proxy a remote resource - * - * @param string $url remote file url. - * @return void - */ -function proxy_remote_file( $url ) { - $fp = @fopen( $url, 'rb' ); - if ( ! $fp ) { - print_404_page(); - return; - } - print_remote_file_headers( $url ); - fpassthru( $fp ); -} - -/** - * Get remote file headers, similar to: get_headers( $url ) - * - * @param string $url remote file url. - * @return array - * @link https://stackoverflow.com/a/41135574 - */ -function get_remote_file_headers( $url ) { - $headers = []; - $ch = curl_init(); - - curl_setopt( $ch, CURLOPT_URL, $url ); - curl_setopt( $ch, CURLOPT_HEADER, true ); - curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true ); - curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); - - // this function is called by curl for each header received. - curl_setopt( - $ch, CURLOPT_HEADERFUNCTION, - function( $curl, $header ) use ( &$headers ) { - $headers[] = $header; - return strlen( $header ); - } - ); - curl_exec( $ch ); - curl_close( $ch ); - - return $headers; -} - -/** - * Print the default apache 404 page - * - * @return void - */ -function print_404_page() { - header( $_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found' ); - $request_uri = htmlspecialchars( $_SERVER['REQUEST_URI'] ); - echo ''; - echo "404 Not Found

Not Found

The requested URL {$request_uri} was not found on this server.

"; -} - -/** - * Send to browser remote file headers - * - * @param string $url remote file url. - * @return void - */ -function print_remote_file_headers( $url ) { - $headers = get_remote_file_headers( $url ); - print_file_headers( $headers ); -} - -/** - * Send to browser local file headers - * - * @param string $file local file. - * @return void - */ -function print_local_file_headers( $file ) { - global $ttl; - $exp_gmt = gmdate( 'D, d M Y H:i:s', time() + $ttl * 60 ) . ' GMT'; - $mod_gmt = gmdate( 'D, d M Y H:i:s', filemtime( $file ) ) . ' GMT'; - $max_age = $ttl * 60; - $headers = array( - 'Expires:' => $exp_gmt, - 'Last-Modified:' => $mod_gmt, - 'Cache-Control:' => 'public, max-age=' . $max_age, // for MSIE 5. - 'Content-Type:' => 'image/png', - ); - print_file_headers( $headers ); -} - -/** - * Send to browser file headers filtering it with the previously array in config file - * - * @param array $file_headers eg: array( 'Content-Type:' => 'image/png' ) or array( 'Access-Control-Allow-Origin: *' ). - * - * @return void - */ -function print_file_headers( &$file_headers ) { - global $headers; - // send headers only if not previously defined in config file. - foreach ( $file_headers as $header => $value ) { - if ( is_string( $header ) ) { - header( $header . ' ' . $value ); - } else { - header( $value ); - } - } - // send all remaing default headers defined in config file. - foreach ( $headers as $header => $value ) { - header_remove( rtrim( $header, ':' ) ); - header( $header . ' ' . $value ); - } -}