Skip to content

Commit

Permalink
Fix nasty broken links bug which depends on whether there's a / on th…
Browse files Browse the repository at this point in the history
…e end of the path defines
  • Loading branch information
ben-xo committed Oct 17, 2022
1 parent d9cbd52 commit f9693c3
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 64 deletions.
87 changes: 46 additions & 41 deletions dir2cast.php
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ protected function stripBasePath($filename)
{
if(strlen(RSS_File_Item::$FILES_DIR) && strpos($filename, RSS_File_Item::$FILES_DIR) === 0)
{
return ltrim(substr($filename, strlen(RSS_File_Item::$FILES_DIR)), '/');
$filename = ltrim(substr($filename, strlen(RSS_File_Item::$FILES_DIR)), '/');
}
return $filename;
}
Expand Down Expand Up @@ -1583,20 +1583,20 @@ public static function display($message, $errfile, $errline)
{
if(self::$errors)
{
if(!defined('CLI_ONLY') || !CLI_ONLY)
if(!defined('CLI_ONLY'))
{
if(!http_response_code())
{
http_response_code(500);
}
}

if((!defined('CLI_ONLY') || !CLI_ONLY) && !ini_get('html_errors'))
if((!defined('CLI_ONLY')) && !ini_get('html_errors'))
{
header("Content-type: text/plain"); // reset the content-type
}

if((!defined('CLI_ONLY') || !CLI_ONLY ) && ini_get('html_errors'))
if((!defined('CLI_ONLY')) && ini_get('html_errors'))
{
header("Content-type: text/html"); // reset the content-type

Expand Down Expand Up @@ -1650,7 +1650,7 @@ public static function display($message, $errfile, $errline)

public static function display404($message)
{
if(defined('CLI_ONLY') && CLI_ONLY)
if(defined('CLI_ONLY'))
{
http_response_code(404);
header("Content-type: text/plain");
Expand Down Expand Up @@ -1692,18 +1692,18 @@ public static function bootstrap(array $SERVER, array $GET, array $argv)
define('CLI_ONLY', true);
}

if(defined('CLI_ONLY') && CLI_ONLY) {
define('DIR2CAST_BASE', realpath(dirname($argv[0])));
if(defined('CLI_ONLY')) {
define('DIR2CAST_BASE', slashdir(realpath(dirname($argv[0]))));
} else {
define('DIR2CAST_BASE', dirname(__FILE__));
define('DIR2CAST_BASE', slashdir(dirname(__FILE__)));
}

// If an installation-wide config file exists, load it now.
// Installation-wide config can contain TMP_DIR, MP3_DIR and MIN_CACHE_TIME.
// Anything else it contains will be used as a fall-back if no dir-specific dir2cast.ini exists
if(file_exists( DIR2CAST_BASE . '/dir2cast.ini' ))
if(file_exists( DIR2CAST_BASE . 'dir2cast.ini' ))
{
$ini_file_name = DIR2CAST_BASE . '/dir2cast.ini';
$ini_file_name = DIR2CAST_BASE . 'dir2cast.ini';
self::load_from_ini( $ini_file_name );
self::finalize(array('TMP_DIR', 'MP3_BASE', 'MP3_DIR', 'MIN_CACHE_TIME', 'FORCE_PASSWORD'));
define('INI_FILE', $ini_file_name);
Expand All @@ -1725,11 +1725,12 @@ public static function bootstrap(array $SERVER, array $GET, array $argv)
}
if(!defined('MP3_DIR') && !empty($cli_options['media-dir']))
{
define('MP3_DIR', realpath($cli_options['media-dir']));
if(!is_dir(MP3_DIR) or !is_readable(MP3_DIR))

if(!is_dir($cli_options['media-dir']) or !is_readable($cli_options['media-dir']))
{
ErrorHandler::display404($cli_options['media-dir']);
}
define('MP3_DIR', slashdir(realpath($cli_options['media-dir'])));
}
if(!defined('MP3_URL') && !empty($cli_options['media-url']))
{
Expand Down Expand Up @@ -1768,13 +1769,13 @@ public static function bootstrap(array $SERVER, array $GET, array $argv)
define('FORCE_PASSWORD', '');

if(!defined('TMP_DIR')) {
define('TMP_DIR', DIR2CAST_BASE . '/temp');
define('TMP_DIR', DIR2CAST_BASE . 'temp');
}

if(!defined('MP3_BASE'))
{
if(!empty($SERVER['HTTP_HOST']))
define('MP3_BASE', dirname($SERVER['SCRIPT_FILENAME']));
define('MP3_BASE', slashdir(dirname($SERVER['SCRIPT_FILENAME'])));
else
define('MP3_BASE', DIR2CAST_BASE);
}
Expand All @@ -1783,14 +1784,14 @@ public static function bootstrap(array $SERVER, array $GET, array $argv)
{
if(!empty($GET['dir']))
{
define('MP3_DIR', MP3_BASE . '/' . safe_path(magic_stripslashes($GET['dir'])));
define('MP3_DIR', slashdir(slashdir(MP3_BASE) . safe_path(magic_stripslashes($GET['dir']))));
if(!is_dir(MP3_DIR) or !is_readable(MP3_DIR))
{
ErrorHandler::display404($GET['dir']);
}
}
else
define('MP3_DIR', MP3_BASE);
define('MP3_DIR', slashdir(MP3_BASE));
}
}

Expand All @@ -1801,15 +1802,14 @@ public static function defaults(array $SERVER)
{
// if an MP3_DIR specific config file exists, load it now, as long as it's not the same file as the global one!
if(
file_exists( MP3_DIR . '/dir2cast.ini' ) and
realpath(DIR2CAST_BASE . '/dir2cast.ini') != realpath( MP3_DIR . '/dir2cast.ini' )
file_exists( MP3_DIR . 'dir2cast.ini' ) and
realpath(DIR2CAST_BASE . 'dir2cast.ini') != realpath( MP3_DIR . 'dir2cast.ini' )
) {
self::load_from_ini( MP3_DIR . '/dir2cast.ini' );
self::load_from_ini( MP3_DIR . 'dir2cast.ini' );
}

self::finalize();


if(!defined('MP3_URL'))
{
# This works on the principle that MP3_DIR must be under DOCUMENT_ROOT (otherwise how will you serve the MP3s?)
Expand All @@ -1818,9 +1818,9 @@ public static function defaults(array $SERVER)

if(!empty($SERVER['HTTP_HOST']))
{
$path_part = substr(MP3_DIR, strlen($SERVER['DOCUMENT_ROOT']));
$path_part = substr(slashdir(MP3_DIR), strlen(slashdir($SERVER['DOCUMENT_ROOT'])));
define('MP3_URL',
'http' . (!empty($SERVER['HTTPS']) ? 's' : '') . '://' . $SERVER['HTTP_HOST'] . '/' . ltrim( rtrim( $path_part, '/' ) . '/', '/' ));
'http' . (!empty($SERVER['HTTPS']) ? 's' : '') . '://' . $SERVER['HTTP_HOST'] . '/' . ltrim( slashdir( $path_part ), '/' ));
}
else
define('MP3_URL', 'file://' . MP3_DIR );
Expand Down Expand Up @@ -1852,10 +1852,10 @@ public static function defaults(array $SERVER)

if(!defined('DESCRIPTION'))
{
if(file_exists(MP3_DIR . '/description.txt'))
define('DESCRIPTION', file_get_contents(MP3_DIR . '/description.txt'));
elseif(file_exists(DIR2CAST_BASE . '/description.txt'))
define('DESCRIPTION', file_get_contents(DIR2CAST_BASE . '/description.txt'));
if(file_exists(MP3_DIR . 'description.txt'))
define('DESCRIPTION', file_get_contents(MP3_DIR . 'description.txt'));
elseif(file_exists(DIR2CAST_BASE . 'description.txt'))
define('DESCRIPTION', file_get_contents(DIR2CAST_BASE . 'description.txt'));
else
define('DESCRIPTION', 'Podcast');
}
Expand All @@ -1877,20 +1877,20 @@ public static function defaults(array $SERVER)

if(!defined('ITUNES_SUBTITLE'))
{
if(file_exists(MP3_DIR . '/itunes_subtitle.txt'))
define('ITUNES_SUBTITLE', file_get_contents(MP3_DIR . '/itunes_subtitle.txt'));
elseif(file_exists(DIR2CAST_BASE . '/itunes_subtitle.txt'))
define('ITUNES_SUBTITLE', file_get_contents(DIR2CAST_BASE . '/itunes_subtitle.txt'));
if(file_exists(MP3_DIR . 'itunes_subtitle.txt'))
define('ITUNES_SUBTITLE', file_get_contents(MP3_DIR . 'itunes_subtitle.txt'));
elseif(file_exists(DIR2CAST_BASE . 'itunes_subtitle.txt'))
define('ITUNES_SUBTITLE', file_get_contents(DIR2CAST_BASE . 'itunes_subtitle.txt'));
else
define('ITUNES_SUBTITLE', DESCRIPTION);
}

if(!defined('ITUNES_SUMMARY'))
{
if(file_exists(MP3_DIR . '/itunes_summary.txt'))
define('ITUNES_SUMMARY', file_get_contents(MP3_DIR . '/itunes_summary.txt'));
elseif(file_exists(DIR2CAST_BASE . '/itunes_summary.txt'))
define('ITUNES_SUMMARY', file_get_contents(DIR2CAST_BASE . '/itunes_summary.txt'));
if(file_exists(MP3_DIR . 'itunes_summary.txt'))
define('ITUNES_SUMMARY', file_get_contents(MP3_DIR . 'itunes_summary.txt'));
elseif(file_exists(DIR2CAST_BASE . 'itunes_summary.txt'))
define('ITUNES_SUMMARY', file_get_contents(DIR2CAST_BASE . 'itunes_summary.txt'));
else
define('ITUNES_SUMMARY', DESCRIPTION);
}
Expand All @@ -1901,9 +1901,9 @@ public static function defaults(array $SERVER)
define('IMAGE', rtrim(MP3_URL, '/') . '/image.jpg');
elseif(file_exists(rtrim(MP3_DIR, '/') . '/image.png'))
define('IMAGE', rtrim(MP3_URL, '/') . '/image.png');
elseif(file_exists(DIR2CAST_BASE . '/image.jpg'))
elseif(file_exists(DIR2CAST_BASE . 'image.jpg'))
define('IMAGE', rtrim(MP3_URL, '/') . '/image.jpg');
elseif(file_exists(DIR2CAST_BASE . '/image.png'))
elseif(file_exists(DIR2CAST_BASE . 'image.png'))
define('IMAGE', rtrim(MP3_URL, '/') . '/image.png');
else
define('IMAGE', '');
Expand All @@ -1915,9 +1915,9 @@ public static function defaults(array $SERVER)
define('ITUNES_IMAGE', rtrim(MP3_URL, '/') . '/itunes_image.jpg');
elseif(file_exists(rtrim(MP3_DIR, '/') . '/itunes_image.png'))
define('ITUNES_IMAGE', rtrim(MP3_URL, '/') . '/itunes_image.png');
elseif(file_exists(DIR2CAST_BASE . '/itunes_image.jpg'))
elseif(file_exists(DIR2CAST_BASE . 'itunes_image.jpg'))
define('ITUNES_IMAGE', rtrim(MP3_URL, '/') . '/itunes_image.jpg');
elseif(file_exists(DIR2CAST_BASE . '/itunes_image.png'))
elseif(file_exists(DIR2CAST_BASE . 'itunes_image.png'))
define('ITUNES_IMAGE', rtrim(MP3_URL, '/') . '/itunes_image.png');
else
define('ITUNES_IMAGE', '');
Expand Down Expand Up @@ -1974,7 +1974,7 @@ public static function defaults(array $SERVER)
define('CLOCK_OFFSET', 0);

// Set up factory settings for Podcast subclasses
Dir_Podcast::$EMPTY_PODCAST_IS_ERROR = !defined('CLI_ONLY') || !CLI_ONLY;
Dir_Podcast::$EMPTY_PODCAST_IS_ERROR = !defined('CLI_ONLY');
Dir_Podcast::$RECURSIVE_DIRECTORY_ITERATOR = RECURSIVE_DIRECTORY_ITERATOR;
Dir_Podcast::$ITEM_COUNT = ITEM_COUNT;
Dir_Podcast::$MIN_FILE_AGE = MIN_FILE_AGE;
Expand Down Expand Up @@ -2070,7 +2070,7 @@ public function update_mtime_if_metadata_files_modified()
$filepath = rtrim(MP3_DIR, '/') . '/' . $file;
if(!file_exists($filepath))
{
$filepath = DIR2CAST_BASE . '/' . $file;
$filepath = DIR2CAST_BASE . $file;
}
if(!file_exists($filepath))
{
Expand Down Expand Up @@ -2125,7 +2125,7 @@ public function output()
if(!defined('OUTPUT_FILE'))
{
$output = $podcast->generate();
if(!defined('CLI_ONLY') || !CLI_ONLY)
if(!defined('CLI_ONLY'))
{
$podcast->http_headers(strlen($output));
}
Expand Down Expand Up @@ -2195,6 +2195,11 @@ function utf8_for_xml($s)
return preg_replace('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', '', $s);
}

function slashdir($dir)
{
return rtrim($dir, '/') . '/';
}

/* DISPATCH *********************************************/

function main($args)
Expand Down
2 changes: 1 addition & 1 deletion docker-compose/nginx/default.conf
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ server {
# Don't allow downloading of dir2cast.ini, as it may contain sensitive
# info such as the refresh password. Also, don't allow downloading of
# dir2cast.php, for security and privacy reasons.
location ~ /dir2cast\.(ini|php)$ {
location ~ \.(ini|php)$ {
return 404;
}

Expand Down
4 changes: 2 additions & 2 deletions test/FourOhFourTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ public static function setUpBeforeClass(): void

public function test_non_existent_dir_prints_bare_error_CLI_case(): void
{
exec('php dir2cast.php --output=out.xml --media-dir=imaginary-dir', $output, $returncode);
$this->assertEquals("Not Found: imaginary-dir", implode("\n", $output));
exec('php dir2cast.php --media-dir=dir2cast.ini', $output, $returncode);
$this->assertEquals("Not Found: dir2cast.ini", implode("\n", $output));
$this->assertEquals(254, $returncode); // 254 is -2
}

Expand Down
30 changes: 15 additions & 15 deletions test/SettingsHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public function test_default_defines_set()

// should not be defined as $argv was empty
$this->assertFalse(defined('CLI_ONLY'));
$this->assertEquals(DIR2CAST_BASE, realpath('..')); // from bootstrap.php
$this->assertEquals(DIR2CAST_BASE, slashdir(realpath('..'))); // from bootstrap.php
}

/**
Expand Down Expand Up @@ -172,7 +172,7 @@ public function test_defines_CLI_ONLY_if_argv0()
$this->assertFalse(defined('CLI_ONLY'));
SettingsHandler::bootstrap(array(), array(), array('dir2cast.php'));
$this->assertTrue(defined('CLI_ONLY'));
$this->assertEquals(DIR2CAST_BASE, getcwd()); // from fake $argv
$this->assertEquals(DIR2CAST_BASE, slashdir(getcwd())); // from fake $argv
}

/**
Expand All @@ -187,7 +187,7 @@ public function test_bootstrap_sets_sensible_global_defaults_for_entire_installa
SettingsHandler::bootstrap(array(), array(), array($argv0, $argv1));
$this->assertEquals(MIN_CACHE_TIME, 5);
$this->assertEquals(FORCE_PASSWORD, '');
$this->assertEquals(TMP_DIR, DIR2CAST_BASE . '/temp');
$this->assertEquals(TMP_DIR, DIR2CAST_BASE . 'temp');
$this->assertEquals(MP3_BASE, DIR2CAST_BASE);
$this->assertEquals(MP3_DIR, DIR2CAST_BASE);
}
Expand All @@ -207,8 +207,8 @@ public function test_when_SERVER_HTTP_HOST_then_MP3_BASE_defaults_to_same_dir()
/* $GET */ array(),
/* $argv */ array()
);
$this->assertEquals(MP3_BASE, '/var/www');
$this->assertEquals(MP3_DIR, '/var/www');
$this->assertEquals(MP3_BASE, '/var/www/');
$this->assertEquals(MP3_DIR, '/var/www/');
}

/**
Expand Down Expand Up @@ -322,8 +322,8 @@ public function test_cli_media_dir_a_ok()
mkdir($this->temp_file);

SettingsHandler::bootstrap(array(), array(), array("dir2cast.php", "--media-dir={$this->temp_file}"));
$this->assertEquals(MP3_BASE, realpath('.'));
$this->assertEquals(MP3_DIR, realpath($this->temp_file));
$this->assertEquals(MP3_BASE, slashdir(realpath('.')));
$this->assertEquals(MP3_DIR, slashdir(realpath($this->temp_file)));
$this->assertFalse(http_response_code());
}

Expand All @@ -338,8 +338,8 @@ public function test_GET_media_dir_a_ok()
mkdir('../' . $this->temp_file);

SettingsHandler::bootstrap(array(), array("dir" => $this->temp_file), array());
$this->assertEquals(MP3_BASE, realpath('..')); // due to bootstrap.php chdir
$this->assertEquals(MP3_DIR, realpath('../' . $this->temp_file));
$this->assertEquals(MP3_BASE, slashdir(realpath('..'))); // due to bootstrap.php chdir
$this->assertEquals(MP3_DIR, slashdir(realpath('../' . $this->temp_file)));
$this->assertFalse(http_response_code());
}

Expand All @@ -355,8 +355,8 @@ public function test_GET_media_dir_safe_dot_dot_1()
chdir('deep/root');
SettingsHandler::bootstrap(array(), array("dir" => ".."), array());

$this->assertEquals(MP3_BASE, realpath("{$this->starting_dir}/..")); // due to bootstrap.php chdir
$this->assertEquals(slashdir(MP3_DIR), slashdir(MP3_BASE));
$this->assertEquals(MP3_BASE, slashdir(realpath("{$this->starting_dir}/.."))); // due to bootstrap.php chdir
$this->assertEquals(MP3_DIR, slashdir(MP3_BASE));
$this->assertFalse(http_response_code());
}

Expand All @@ -372,8 +372,8 @@ public function test_GET_media_dir_safe_dot_dot_2()
chdir('deep/root');
SettingsHandler::bootstrap(array(), array("dir" => "../../.."), array());

$this->assertEquals(MP3_BASE, realpath("{$this->starting_dir}/..")); // due to bootstrap.php chdir
$this->assertEquals(slashdir(MP3_DIR), slashdir(MP3_BASE));
$this->assertEquals(MP3_BASE, slashdir(realpath("{$this->starting_dir}/.."))); // due to bootstrap.php chdir
$this->assertEquals(MP3_DIR, slashdir(MP3_BASE));
$this->assertFalse(http_response_code());
}

Expand Down Expand Up @@ -425,7 +425,7 @@ public function test_GET_media_dir_safe_dir_with_good_base()
SettingsHandler::bootstrap(array(), array("dir" => "root"), array());

$this->assertEquals(MP3_BASE, realpath(".."));
$this->assertEquals(MP3_DIR, realpath('.'));
$this->assertEquals(MP3_DIR, realpath('.') . '/');
$this->assertFalse(http_response_code());
}

Expand Down Expand Up @@ -528,7 +528,7 @@ public function test_CLI_ONLY_sensible_defaults()
SettingsHandler::bootstrap(array(), array(), array('dir2cast.php'));
SettingsHandler::defaults(array());

$this->assertEquals(MP3_URL, 'file://' . getcwd());
$this->assertEquals(MP3_URL, 'file://' . getcwd() . '/');
$this->assertEquals(LINK, 'http://www.example.com/');
$this->assertEquals(RSS_LINK, 'http://www.example.com/rss');
$this->assertEquals(TITLE, 'test'); // name of this folder
Expand Down
5 changes: 0 additions & 5 deletions test/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,6 @@ function fake_getopt($argv_in, $short_options, $long_options)
return array();
}

function slashdir($dir)
{
return rtrim($dir, '/') . '/';
}

define('NO_DISPATCHER', true);

require_once('../dir2cast.php');
Expand Down

0 comments on commit f9693c3

Please sign in to comment.