From 1d6f24c4ed5ec511247e6412a88827d8d014d6dc Mon Sep 17 00:00:00 2001 From: yggverse Date: Tue, 25 Jun 2024 23:25:32 +0300 Subject: [PATCH] fix relative paths resolver, add comments --- src/Controller/Cli.php | 2 +- src/Model/Filesystem.php | 129 +++++++++++++++++++++++++-------------- 2 files changed, 85 insertions(+), 46 deletions(-) diff --git a/src/Controller/Cli.php b/src/Controller/Cli.php index c20f698..8b3dd44 100755 --- a/src/Controller/Cli.php +++ b/src/Controller/Cli.php @@ -243,7 +243,7 @@ class Cli // Absolute option skipped, make local path relative if (!$this->option->absolute) { - $local = Filesystem::getFilenameRelativeToDirname( + $local = $this->filesystem->getFilenameRelativeToDirname( $local, dirname( $filename diff --git a/src/Model/Filesystem.php b/src/Model/Filesystem.php index 9a784d0..e55155c 100755 --- a/src/Model/Filesystem.php +++ b/src/Model/Filesystem.php @@ -59,11 +59,22 @@ class Filesystem } } + /* + * Return --target realpath + * + * Currently not in use. + */ public function getFilepath(): string { return $this->_filepath; } + /* + * Build local filepath from \Yggverse\Net\Address + * + * Return absolute filename using --target defined + * Method doesn't check result location for exist. + */ public function getFilenameFromNetAddress( \Yggverse\Net\Address $address, ?string $index = null @@ -122,6 +133,15 @@ class Filesystem return $filename; } + /* + * Save data string to destination. + * + * This method also builds recursive directory path + * and overwrites existing files (data) on exist. + * + * $filename must start from --target defined + * $data is plain gemtext or binary (media) string + */ public function save( string $filename, string $data @@ -130,7 +150,11 @@ class Filesystem if (!str_starts_with($filename, $this->_filepath)) { throw new \Exception( - _('Target filename out of storage location') + sprintf( + _('Filename "%s" out of filesystem root "%s"'), + $filename, + $this->_filepath + ) ); } @@ -175,69 +199,84 @@ class Filesystem ); } - // Helpers - public static function getFilenameRelativeToDirname( + /* + * Build relative $filename path to given $dirname + * + * Method does not and must not check location for exist + * $filename and $dirname must contain --target defined. + * + * This implementation compatible with --external option + * resulting path get format: ../domain.com/path/to/file + */ + public function getFilenameRelativeToDirname( string $filename, string $dirname ): string { - // Validate paths - if (empty($filename)) + // Require absolute $filename + if (!str_starts_with($filename, DIRECTORY_SEPARATOR)) { throw new \Exception( - 'Filename is could not be empty' + 'Absolute filename required' ); } - if (empty($dirname)) + // Require absolute $dirname + if (!str_starts_with($dirname, DIRECTORY_SEPARATOR)) { throw new \Exception( - 'Dirname is could not be empty' + 'Absolute dirname required' ); } - if (str_starts_with($filename, $dirname)) + // Require valid $filename root location + if (!str_starts_with($filename, $this->_filepath)) { - return ltrim( - str_replace( + throw new \Exception( + sprintf( + _('Filename "%s" out of filesystem root "%s"'), + $filename, + $this->_filepath + ) + ); + } + + // Require valid $dirname root location + if (!str_starts_with($dirname, $this->_filepath)) + { + throw new \Exception( + sprintf( + _('Dirname "%s" out of filesystem root "%s"'), $dirname, - DIRECTORY_SEPARATOR, - $filename + $this->_filepath + ) + ); + } + + // Build path + return str_repeat( // iterate ../ up to the --target location + sprintf( + '..%s', + DIRECTORY_SEPARATOR + ), + substr_count( + ltrim( // strip leading slash from $dirname + str_replace( // strip --target prefix from $dirname + $this->_filepath, + DIRECTORY_SEPARATOR, + $dirname + ), + DIRECTORY_SEPARATOR ), DIRECTORY_SEPARATOR - ); - } - - $filepath = explode( - DIRECTORY_SEPARATOR, - dirname( - $filename - ) - ); - - $segments = []; - - foreach( - explode( + ) + 1 + ) . ltrim( // strip leading slash from $filename + str_replace( // strip --target prefix from $filename + $this->_filepath, DIRECTORY_SEPARATOR, - $dirname - ) as $level => $directory) - { - if (isset($filepath[$level]) && $filepath[$level] == $directory) - { - continue; - } - - $segments[] = '..'; - } - - $segments[] = basename( - $filename - ); - - return implode( - DIRECTORY_SEPARATOR, - $segments + $filename + ), + DIRECTORY_SEPARATOR ); } } \ No newline at end of file