1089 lines
38 KiB
PHP
1089 lines
38 KiB
PHP
<?php
|
|
|
|
/** DomFramework
|
|
* @package domframework
|
|
* @author Dominique Fournier <dominique@fournier38.fr>
|
|
* @license BSD
|
|
*/
|
|
|
|
namespace Domframework;
|
|
|
|
/** The file method allow to manage files like PHP with a working chroot on all
|
|
* plateforms, and a right management compatible with database
|
|
* Don't follow links !
|
|
*
|
|
* To allow an external authorization test in plus of the filesystem check, you
|
|
* must extends the class and overload checkExternalPathRO and
|
|
* checkExternalPathRW.
|
|
*/
|
|
class File
|
|
{
|
|
/** The virtual current working directory */
|
|
private $cwd = ".";
|
|
/** The real directory used as root in virtual chroot */
|
|
private $baseDir = "/";
|
|
|
|
/** The lock stack */
|
|
private $locks = array();
|
|
|
|
/** Activate the debug and define the minimum priority to save */
|
|
public $debug = 0;
|
|
|
|
/** Change the current working directory
|
|
* @param string $directory Go in the provided directory
|
|
* @return bool true if the directory is changed
|
|
* @throws If directory not exists, or the directory is not executable
|
|
*/
|
|
public function chdir($directory)
|
|
{
|
|
$this->debug(2, "chdir ($directory)");
|
|
$tmpdirectory = $this->realpath($directory);
|
|
$this->checkPathRO($tmpdirectory);
|
|
if ($this->baseDir === "/") {
|
|
$this->cwd = $tmpdirectory;
|
|
} else {
|
|
$this->cwd = substr($tmpdirectory, strlen($this->baseDir));
|
|
}
|
|
$this->debug(1, "chdir $directory -> $this->cwd");
|
|
return true;
|
|
}
|
|
|
|
/** Change the group for a file/dir...
|
|
* @param string $filename The file/directory to change
|
|
* @param mixed $group The group name or group GID
|
|
* @throws If filename not exists, or the directory is not RW
|
|
*/
|
|
public function chgrp($filename, $group)
|
|
{
|
|
$this->debug(2, "chgrp ($filename, $group)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRW(dirname($filename));
|
|
if (! file_exists($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' to chmod doesn't exists"
|
|
),
|
|
$filename
|
|
), 404);
|
|
}
|
|
if (! is_writeable($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' to chmod is not writeable"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
$rc = chgrp($filename, $group);
|
|
$this->debug(1, "chgrp ($filename, $group) => $rc");
|
|
return $rc;
|
|
}
|
|
|
|
/** Change the rights mode for a file/dir...
|
|
* @param string $filename The file/directory to change
|
|
* @param integer $mode The mode to use for the filename
|
|
* @throws If filename not exists, or the directory is not RW
|
|
*/
|
|
public function chmod($filename, $mode)
|
|
{
|
|
$this->debug(2, "chmod ($filename, $mode)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRW(dirname($filename));
|
|
if (! file_exists($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' to chmod doesn't exists"
|
|
),
|
|
$filename
|
|
), 404);
|
|
}
|
|
if (! is_writeable($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' to chmod is not writeable"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
$rc = chmod($filename, $mode);
|
|
$this->debug(1, "chmod ($filename, $mode) => $rc");
|
|
return $rc;
|
|
}
|
|
|
|
/** Change the owner for a file/dir...
|
|
* @param string $filename The file/directory to change
|
|
* @param mixed $user The user name or user UID
|
|
* @throws If filename not exists, or the directory is not RW
|
|
*/
|
|
public function chown($filename, $user)
|
|
{
|
|
$this->debug(2, "chown ($filename, $user)");
|
|
if (
|
|
posix_getuid() !== 0 &&
|
|
posix_getuid() !== $user &&
|
|
posix_getpwuid(posix_getuid()) !== $user
|
|
) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"Only root user can change the file owner for '%s'"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRW(dirname($filename));
|
|
if (! file_exists($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' to chmod doesn't exists"
|
|
),
|
|
$filename
|
|
), 404);
|
|
}
|
|
if (! is_writeable($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' to chmod is not writeable"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
$rc = chown($filename, $user);
|
|
$this->debug(1, "chown ($filename, $user) => $rc");
|
|
return $rc;
|
|
}
|
|
|
|
/** Chroot in the provided directory
|
|
* @param string $directory The directory to chroot
|
|
* @return boolean true if the chroot is done, false if there is a failure
|
|
* @throws If directory not exists, or the directory is not executable
|
|
*/
|
|
public function chroot($directory)
|
|
{
|
|
// Use the checkPathRO (using the $this->baseDir) to not allow to go away of
|
|
// the chroot.
|
|
$this->debug(2, "chroot ($directory)");
|
|
$directory = $this->realpath($directory);
|
|
$this->checkPathRO($directory);
|
|
$this->baseDir = preg_replace("#//+#", "/", $directory);
|
|
$this->cwd = "/";
|
|
$this->debug(1, "chroot $directory -> $this->baseDir");
|
|
return true;
|
|
}
|
|
|
|
/** Get the file contents in an array (like 'file' function, but can not
|
|
* have the same name as the class...)
|
|
* @param string $filename Name of the file to read
|
|
* @return string Content of the file
|
|
* @throws If parent directory not exists, is not readable, the file is not
|
|
* exists or is not readable
|
|
*/
|
|
public function fileArray($filename)
|
|
{
|
|
$this->debug(2, "file ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRO(dirname($filename));
|
|
if (! is_file($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' is not a file"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
if (! is_readable($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' is not readable"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
$contents = file($filename);
|
|
$this->debug(1, "file ($filename) => " . count($contents) . " rows");
|
|
return $contents;
|
|
}
|
|
|
|
/** Checks whether a file or directory exists
|
|
* @param string $filename The file or directory to verify
|
|
* @return bool true if the file exists, false otherwise
|
|
* @throws If parent directory not exists, or is not executable
|
|
*/
|
|
public function file_exists($filename)
|
|
{
|
|
$this->debug(2, "file_exists ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
try {
|
|
$this->checkPathRO(dirname($filename));
|
|
} catch (\Exception $e) {
|
|
if ($e->getCode() !== 404) {
|
|
throw new \Exception($e->getMessage(), $e->getCode());
|
|
}
|
|
}
|
|
if (file_exists($filename) && ! is_link($filename)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** Get the file contents
|
|
* @param string $filename Name of the file to read
|
|
* @return string Content of the file
|
|
* @throws If parent directory not exists, is not readable, the file is not
|
|
* exists or is not readable
|
|
*/
|
|
public function file_get_contents($filename)
|
|
{
|
|
$this->debug(2, "file_get_contents ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRO(dirname($filename));
|
|
if (! is_file($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' is not a file"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
if (! is_readable($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' is not readable"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
$contents = file_get_contents($filename);
|
|
$this->debug(1, "file_get_contents ($filename) => " . strlen($contents) .
|
|
" bytes");
|
|
return $contents;
|
|
}
|
|
|
|
/** Write a string to a file
|
|
* @param string $filename Path to the file where to write the data
|
|
* @param string|integer $data The data to write
|
|
* @param integer|null $flags The optional flags
|
|
* @return integer the length of the data stored
|
|
* @throws If parent directory not exists, is not writeable, or the file
|
|
* exists and is not writeable
|
|
*/
|
|
public function file_put_contents($filename, $data, $flags = 0)
|
|
{
|
|
$this->debug(2, "file_put_contents ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRW(dirname($filename));
|
|
if (file_exists($filename) && ! is_writeable($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' is not writeable"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
if (file_exists($filename) && ! is_file($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' is not a file"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
$contents = file_put_contents($filename, $data, $flags);
|
|
$this->debug(1, "file_put_contents ($filename, \$data) => " .
|
|
"$contents bytes");
|
|
return $contents;
|
|
}
|
|
|
|
/** Get the file modification time of a file
|
|
* @param string $filename Path to the file
|
|
* @return integer|boolean the time the file was last modified, or FALSE
|
|
* on failure. The time is returned as a Unix timestamp, which is suitable
|
|
* for the date() function.
|
|
* @throws If parent directory not exists, is not writeable
|
|
*/
|
|
public function filemtime($filename)
|
|
{
|
|
$this->debug(2, "filemtime ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRO(dirname($filename));
|
|
return filemtime($filename);
|
|
}
|
|
|
|
/** Get the file size
|
|
* @param string $filename Path to the file
|
|
* @return integer|boolean the size of the file or FALSE on failure.
|
|
* @throws If parent directory not exists, is not writeable
|
|
*/
|
|
public function filesize($filename)
|
|
{
|
|
$this->debug(2, "filesize ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRO(dirname($filename));
|
|
return filesize($filename);
|
|
}
|
|
|
|
/** Get the file info of the provided filename
|
|
* @param string $filename Path to the file
|
|
* @return string the mimetype of the file
|
|
* @throws If parent directory not exists, is not writeable
|
|
*/
|
|
public function fileinfoMimeType($filename)
|
|
{
|
|
$this->debug(2, "filesize ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRO(dirname($filename));
|
|
$finfo = new \finfo(FILEINFO_MIME_TYPE);
|
|
return $finfo->file($filename);
|
|
}
|
|
|
|
/** Return the current working directory
|
|
* @return string the current working directory */
|
|
public function getcwd()
|
|
{
|
|
$this->debug(1, "getcwd $this->cwd");
|
|
return $this->cwd;
|
|
}
|
|
|
|
/** Find pathnames matching a pattern
|
|
* If there is some unreadable files, skip them quietly
|
|
* @param string $pattern The pattern to found
|
|
* @param integer|null $flags The additional flags
|
|
* @return array Return an array if there is an error
|
|
* @throws If parent directory not exists, or is not executable
|
|
* or if there is one file unreadable
|
|
*/
|
|
public function glob($pattern, $flags = 0)
|
|
{
|
|
$this->debug(2, "glob ($pattern, $flags)");
|
|
$this->checkPathRO($this->baseDir);
|
|
if (substr($pattern, 0, 1) === "/") {
|
|
$relative = 0;
|
|
} else {
|
|
$relative = 1;
|
|
}
|
|
$pattern = $this->realpath($pattern);
|
|
$files = glob($pattern, $flags);
|
|
if ($files === false) {
|
|
// FIXME : In the exception : how found the file which is not readable ?
|
|
throw new \Exception("Glob : can't read some files", 500);
|
|
}
|
|
foreach ($files as &$file) {
|
|
if (strlen($this->baseDir) > 1) {
|
|
if ($relative == 1) {
|
|
$file = substr($file, strlen($this->baseDir) + strlen($this->cwd) + 1);
|
|
} else {
|
|
$file = substr($file, strlen($this->baseDir));
|
|
}
|
|
} else {
|
|
if ($relative == 1) {
|
|
$file = substr($file, strlen($this->cwd) + 1);
|
|
}
|
|
}
|
|
}
|
|
return $files;
|
|
}
|
|
|
|
/** Tells whether the given filename is a directory
|
|
* @param string $filename The filename to test
|
|
* @return bool true if the $filename is a directory and exists, false
|
|
* otherwise
|
|
* @throws If parent directory not exists, or is not executable
|
|
*/
|
|
public function is_dir($filename)
|
|
{
|
|
$this->debug(2, "is_dir ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRO(dirname($filename));
|
|
if (file_exists($filename) && is_dir($filename)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** Tells whether the given filename is a valid file
|
|
* @param string $filename The filename to test
|
|
* @return bool true if the $filename is a file and exists, false otherwise
|
|
* @throws If parent directory not exists, or is not executable
|
|
*/
|
|
public function is_file($filename)
|
|
{
|
|
$this->debug(2, "is_file ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRO(dirname($filename));
|
|
if (file_exists($filename) && is_file($filename)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** Tells whether a file exists and is executable
|
|
* @param string $filename The filename to test
|
|
* @return bool true if the $filename is a file exists and is writeable
|
|
* @throws If parent directory not exists, or is not executable
|
|
*/
|
|
public function is_executable($filename)
|
|
{
|
|
$this->debug(2, "is_executable ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRO(dirname($filename));
|
|
if (file_exists($filename) && is_executable($filename)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** Tells whether a file exists and is readable
|
|
* @param string $filename The filename to test
|
|
* @return bool true if the $filename is a file exists and is readable
|
|
* @throws If parent directory not exists, or is not executable
|
|
*/
|
|
public function is_readable($filename)
|
|
{
|
|
$this->debug(2, "is_readable ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRO(dirname($filename));
|
|
if (file_exists($filename) && is_readable($filename)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** Tells whether a file exists and is writeable
|
|
* @param string $filename The filename to test
|
|
* @return bool true if the $filename is a file exists and is writeable
|
|
* @throws If parent directory not exists, or is not executable
|
|
*/
|
|
public function is_writeable($filename)
|
|
{
|
|
$this->debug(2, "is_writeable ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRO(dirname($filename));
|
|
if (file_exists($filename) && is_writeable($filename)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** Lock a file exclusively
|
|
* @param string $filename The file to lock
|
|
* @return bool true if the lock is acquired, false otherwise
|
|
* @throws If parent directory not exists, or is not writeable
|
|
*/
|
|
public function lockEX($filename)
|
|
{
|
|
$this->debug(2, "lockEX ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRW(dirname($filename));
|
|
if (! file_exists($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' doesn't exists : could not be locked"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
if (! is_readable($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' is not readable : could not be locked"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
$this->locks[$filename] = fopen($filename, "rt");
|
|
return flock($this->locks[$filename], LOCK_EX);
|
|
}
|
|
|
|
/** Lock a file shared (allow multiple read)
|
|
* @param string $filename The file to lock
|
|
* @return bool true if the lock is acquired, false otherwise
|
|
* @throws If parent directory not exists, or is not writeable
|
|
*/
|
|
public function lockSH($filename)
|
|
{
|
|
$this->debug(2, "lockSH ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRW(dirname($filename));
|
|
if (! file_exists($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' doesn't exists : could not be locked"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
if (! is_readable($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' is not readable : could not be locked"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
$this->locks[$filename] = fopen($filename, "rt");
|
|
return flock($this->locks[$filename], LOCK_SH);
|
|
}
|
|
|
|
/** Unlock a file previously locked
|
|
* @param string $filename The file to lock
|
|
* @return bool true if the lock is acquired, false otherwise
|
|
* @throws If parent directory not exists, or is not writeable
|
|
*/
|
|
public function lockUN($filename)
|
|
{
|
|
$this->debug(2, "lockUN ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRW(dirname($filename));
|
|
$res = true;
|
|
if (isset($this->locks[$filename])) {
|
|
$res = flock($this->locks[$filename], LOCK_UN);
|
|
fclose($this->locks[$filename]);
|
|
unset($this->locks[$filename]);
|
|
}
|
|
return $res;
|
|
}
|
|
|
|
/** Calculate the md5 sum of a file
|
|
* @param string $filename The file to hash
|
|
* @return string the calulated hash
|
|
* @throws If the file doesn't exists
|
|
*/
|
|
public function md5_file($filename)
|
|
{
|
|
$this->debug(2, "md5_file ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRO(dirname($filename));
|
|
if (! is_file($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' is not a file"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
if (! is_readable($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' is not readable"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
return md5_file($filename);
|
|
}
|
|
|
|
/** Create a new directory
|
|
* @param string $pathname The directory to create
|
|
* @param integer $mode The mode to create (0777 by default)
|
|
* @param boolean $recursive (false by default)
|
|
* @return bool true if the directory is correctely created, false if the
|
|
* directory already exists
|
|
* @throws If parent directory not exists, is not writeable
|
|
*/
|
|
public function mkdir($pathname, $mode = 0777, $recursive = false)
|
|
{
|
|
$this->debug(2, "mkdir ($pathname, $mode, $recursive)");
|
|
$pathname = $this->realpath($pathname);
|
|
if ($recursive) {
|
|
$parents = explode("/", $pathname);
|
|
array_pop($parents);
|
|
$parent = "";
|
|
foreach ($parents as $p) {
|
|
$parent = $parent . $p . "/";
|
|
if (! file_exists($parent)) {
|
|
if (is_writeable(dirname($parent))) {
|
|
break;
|
|
}
|
|
throw new \Exception(sprintf(
|
|
"Last Directory '%s' is readonly",
|
|
dirname($parent)
|
|
), 500);
|
|
}
|
|
}
|
|
if ($parent === dirname($pathname) && ! is_writeable(dirname($parent))) {
|
|
throw new \Exception(sprintf(
|
|
"Parent directory '%s' is readonly",
|
|
dirname($parent)
|
|
), 500);
|
|
}
|
|
} else {
|
|
$this->checkPathRW(dirname($pathname));
|
|
}
|
|
if (file_exists($pathname)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"Directory '%s' already exists"
|
|
),
|
|
$pathname
|
|
), 500);
|
|
}
|
|
$rc = mkdir($pathname, $mode, $recursive);
|
|
$this->debug(1, "mkdir ($pathname, $mode, $recursive) => $rc");
|
|
return $rc;
|
|
}
|
|
|
|
/** Copy a file or a directory
|
|
* @param string $oldname The file to copy
|
|
* @param string $newname The new name of the file. It will be
|
|
* overwrited if it already exists
|
|
* @return bool
|
|
*/
|
|
public function copy($oldname, $newname)
|
|
{
|
|
$this->debug(2, "copy ($oldname, $newname)");
|
|
$oldname = $this->realpath($oldname);
|
|
$newname = $this->realpath($newname);
|
|
$this->checkPathRO(dirname($oldname));
|
|
$this->checkPathRW(dirname($newname));
|
|
if (is_dir($oldname)) {
|
|
// Copy directory structure
|
|
if (! $this->file_exists($newname)) {
|
|
$this->mkdir($newname);
|
|
}
|
|
$files = $this->scandirNotSorted($oldname);
|
|
foreach ($files as $file) {
|
|
$this->copy("$oldname/$file", "$newname/$file");
|
|
}
|
|
} else {
|
|
$rc = copy($oldname, $newname);
|
|
}
|
|
$this->debug(1, "copy ($oldname, $newname) => $rc");
|
|
return $rc;
|
|
}
|
|
|
|
/** Renames a file or directory
|
|
* @param string $oldname The file or directory to rename
|
|
* @param string $newname The new name of the file or directory. It will be
|
|
* overwrited if it already exists
|
|
* @return bool
|
|
*/
|
|
public function rename($oldname, $newname)
|
|
{
|
|
$this->debug(2, "rename ($oldname, $newname)");
|
|
$oldname = $this->realpath($oldname);
|
|
$newname = $this->realpath($newname);
|
|
$this->checkPathRO(dirname($oldname));
|
|
$this->checkPathRW(dirname($newname));
|
|
$rc = rename($oldname, $newname);
|
|
$this->debug(1, "rename ($oldname, $newname) => $rc");
|
|
return $rc;
|
|
}
|
|
|
|
/** Return a ini file converted to an array
|
|
* @param string $filename The filename of the ini file being parsed.
|
|
* @param boolean $process_sections Process the sections
|
|
* @return array
|
|
*/
|
|
public function parse_ini_file($filename, $process_sections = false)
|
|
{
|
|
$this->debug(2, "parse_ini_file ($filename, $process_sections)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRO(dirname($filename));
|
|
if (! is_file($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' is not a file"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
if (! is_readable($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' is not readable"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
return parse_ini_file($filename, $process_sections);
|
|
}
|
|
|
|
/** Return the canonical absolute path. Do not check if the directory exists,
|
|
* if there is links. Just calculate the realpath based on the chroot value
|
|
* @param string $path the path to analyze
|
|
* @return string the canonical absolute path
|
|
*/
|
|
public function realpath($path)
|
|
{
|
|
$oriPath = $path;
|
|
$this->debug(2, "realpath ($oriPath)");
|
|
$path = preg_replace("#//+#", "/", $path);
|
|
if (substr($path, -1) === "/") {
|
|
$path = substr($path, 0, -1);
|
|
}
|
|
$parts = explode("/", $path);
|
|
$current = $this->cwd;
|
|
$tmp = explode("/", $current);
|
|
foreach ($parts as $part) {
|
|
if ($part === "") {
|
|
$tmp = array();
|
|
} elseif ($part === ".") {
|
|
continue;
|
|
} elseif ($part === "..") {
|
|
array_pop($tmp);
|
|
continue;
|
|
} else {
|
|
array_push($tmp, $part);
|
|
}
|
|
}
|
|
if (reset($tmp) === ".") {
|
|
array_shift($tmp);
|
|
$path = $current . "/" . implode("/", $tmp);
|
|
} else {
|
|
$path = "/" . implode("/", $tmp);
|
|
}
|
|
if ($this->baseDir !== "/") {
|
|
$path = $this->baseDir . $path;
|
|
}
|
|
$path = preg_replace("#//+#", "/", $path);
|
|
if ($path !== "/" && substr($path, -1) === "/") {
|
|
$path = substr($path, 0, -1);
|
|
}
|
|
$this->debug(1, "realpath ($oriPath) => $path");
|
|
return $path;
|
|
}
|
|
|
|
/** Remove the provided directory
|
|
* If the recurse flag is true, remove the content too (files and
|
|
* directories)
|
|
* @param string $dirname The directory to remove
|
|
* @param boolean $recursive Remove recursively
|
|
* @return bool true if all is removed, false otherwise
|
|
* @throws If parent directory not exists, is not writeable or the current
|
|
* dir is not writeable
|
|
*/
|
|
public function rmdir($dirname, $recursive = false)
|
|
{
|
|
$this->debug(2, "rmdir ($dirname, $recursive)");
|
|
$tmpdirname = $this->realpath($dirname);
|
|
$this->checkPathRW(dirname($tmpdirname));
|
|
$this->checkPathRW($tmpdirname);
|
|
if ($recursive === false) {
|
|
return @rmdir($tmpdirname);
|
|
}
|
|
$files = array_diff(scandir($tmpdirname), array(".", ".."));
|
|
foreach ($files as $file) {
|
|
if (is_dir("$tmpdirname/$file")) {
|
|
$this->rmdir("$dirname/$file", $recursive);
|
|
} else {
|
|
unlink("$tmpdirname/$file");
|
|
}
|
|
}
|
|
return rmdir($tmpdirname);
|
|
}
|
|
|
|
/** Return the list of files and directories in the directory.
|
|
* Do not return the . and .. virtual dirs.
|
|
* The result is sorted
|
|
* @param string $directory The directory to read
|
|
* @return array the list of files and dirs
|
|
* @throws If directory not exists, or is not executable
|
|
*/
|
|
public function scandir($directory)
|
|
{
|
|
$this->debug(2, "scandir ($directory)");
|
|
$directory = $this->realpath($directory);
|
|
$this->checkPathRO($directory);
|
|
$res = array_values(array_diff(scandir($directory), array('..', '.')));
|
|
natsort($res);
|
|
return $res;
|
|
}
|
|
|
|
/** Return the list of files and directories in the directory.
|
|
* Do not return the . and .. virtual dirs.
|
|
* The result is NOT sorted
|
|
* @param string $directory The directory to read
|
|
* @return array the list of files and dirs
|
|
* @throws If directory not exists, or is not executable
|
|
*/
|
|
public function scandirNotSorted($directory)
|
|
{
|
|
$this->debug(2, "scandirNotSorted ($directory)");
|
|
$directory = $this->realpath($directory);
|
|
$this->checkPathRO($directory);
|
|
$res = array_values(array_diff(
|
|
scandir($directory, SCANDIR_SORT_NONE),
|
|
array('..', '.')
|
|
));
|
|
return $res;
|
|
}
|
|
|
|
/** Calculate the sha1 sum of a file
|
|
* @param string $filename The file to hash
|
|
* @return string the calulated hash
|
|
* @throws If the file doesn't exists
|
|
*/
|
|
public function sha1_file($filename)
|
|
{
|
|
$this->debug(2, "sha1_file ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRO(dirname($filename));
|
|
if (! is_file($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' is not a file"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
if (! is_readable($filename)) {
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"File '%s' is not readable"
|
|
),
|
|
$filename
|
|
), 500);
|
|
}
|
|
return sha1_file($filename);
|
|
}
|
|
|
|
/** Create a new file or update the timestamp if the file exists
|
|
* @param string $filename the filename
|
|
* @param integer|null $time the timestamp to use (actual timestamp if not
|
|
* defined)
|
|
* @param integer|null $atime the access timestamp to use (actual timestamp
|
|
* if not defined)
|
|
* @return bool true or false on failure
|
|
* @throws If parent directory not exists, is not writeable
|
|
*/
|
|
public function touch($filename, $time = null, $atime = null)
|
|
{
|
|
$this->debug(2, "touch ($filename, $time, $atime)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRW(dirname($filename));
|
|
if ($time === null) {
|
|
$time = time();
|
|
}
|
|
if ($atime === null) {
|
|
$atime = time();
|
|
}
|
|
$rc = touch($filename, $time, $atime);
|
|
$this->debug(1, "touch ($filename, $time, $atime) => $rc");
|
|
return $rc;
|
|
}
|
|
|
|
/** Delete an existing file.
|
|
* @param string $filename The filename to remove
|
|
* @return bool true if the file si removed, false otherwise
|
|
* @throws If parent directory not exists, or is not executable
|
|
*/
|
|
public function unlink($filename)
|
|
{
|
|
$this->debug(2, "unlink ($filename)");
|
|
$filename = $this->realpath($filename);
|
|
$this->checkPathRW(dirname($filename));
|
|
if (! file_exists($filename) || ! is_writeable($filename)) {
|
|
return false;
|
|
}
|
|
return unlink($filename);
|
|
}
|
|
|
|
/** Check all the parents of the $directory if they are available, and
|
|
* executable. The path must exists.
|
|
* Must use the filesystem path (complete) and not the version in chroot.
|
|
* The last directoy must be executable and readable (no test for writeable)
|
|
* @param string $path The directory path to check
|
|
* @return boolean true if the path is executable for all the parents and
|
|
* for the last directory
|
|
* @throws if there is a missing part, or a parent is not executable
|
|
*/
|
|
private function checkPathRO($path)
|
|
{
|
|
$this->debug(2, "checkPathRO ($path)");
|
|
$path = preg_replace("#//+#", "/", $path);
|
|
$parents = explode("/", $path);
|
|
array_pop($parents);
|
|
$parent = "";
|
|
foreach ($parents as $p) {
|
|
$parent = $parent . $p . "/";
|
|
if (! file_exists($parent)) {
|
|
$this->debug(
|
|
1,
|
|
"checkPathRO ($path) => Parent Path '$parent' not found"
|
|
);
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"Parent Path '%s' not found"
|
|
),
|
|
$parent
|
|
), 404);
|
|
}
|
|
if (! is_executable($parent)) {
|
|
$this->debug(1, "checkPathRO ($path) => " .
|
|
"Parent Directory '$parent' not executable");
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"Parent Directory '%s' not executable"
|
|
),
|
|
$parent
|
|
), 500);
|
|
}
|
|
if ($this->checkExternalPathRO($parent) !== true) {
|
|
$this->debug(1, "checkPathRO ($path) => " .
|
|
"Parent Directory '$parent' not accessible by " .
|
|
"external check read-only");
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"Parent Directory '%s' not accessible " .
|
|
"by external check read-only"
|
|
),
|
|
$parent
|
|
), 500);
|
|
}
|
|
}
|
|
if (! file_exists($path)) {
|
|
$this->debug(1, "checkPathRO ($path) => Path '$path' not found");
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"Path '%s' not found"
|
|
),
|
|
$path
|
|
), 404);
|
|
}
|
|
if (! is_dir($path)) {
|
|
$this->debug(1, "checkPathRO ($path) => " .
|
|
"Path '$path' is not a directory");
|
|
throw new \Exception(
|
|
sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"Path '%s' is not a directory"
|
|
),
|
|
$path
|
|
),
|
|
500
|
|
);
|
|
}
|
|
if (! is_executable($path)) {
|
|
$this->debug(1, "checkPathRO ($path) => " .
|
|
"Directory '$path' is not executable");
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"Directory '%s' is not executable"
|
|
),
|
|
$path
|
|
), 500);
|
|
}
|
|
if (! is_readable($path)) {
|
|
$this->debug(1, "checkPathRO ($path) => " .
|
|
"Directory '$path' is not readable");
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"Directory '%s' is not readable"
|
|
),
|
|
$path
|
|
), 500);
|
|
}
|
|
return $this->checkExternalPathRO($path);
|
|
}
|
|
|
|
/** Check all the parents of the $directory if they are available, and
|
|
* executable. The path must exists.
|
|
* Must use the filesystem path (complete) and not the version in chroot.
|
|
* The last directoy must be executable and readable and writeable
|
|
* @param string $path The directory path to check
|
|
* @return true if the path is executable for all the parents and for the
|
|
* last directory
|
|
* @throws if there is a missing part, or a parent is not executable
|
|
*/
|
|
private function checkPathRW($path)
|
|
{
|
|
$this->debug(2, "checkPathRW ($path)");
|
|
$this->checkPathRO($path);
|
|
if (! is_writeable($path)) {
|
|
$this->debug(1, "checkPathRW ($path) => " .
|
|
"Directory '$path' is not writeable");
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"Directory '%s' is not writeable"
|
|
),
|
|
$path
|
|
), 500);
|
|
}
|
|
if ($this->checkExternalPathRW($path) !== true) {
|
|
$this->debug(1, "checkPathRW ($path) => " .
|
|
"Directory '$path' not accessible by " .
|
|
"external check read-write");
|
|
throw new \Exception(sprintf(
|
|
dgettext(
|
|
"domframework",
|
|
"Directory '%s' not accessible " .
|
|
"by external check read-write"
|
|
),
|
|
$path
|
|
), 500);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** Save a debug log
|
|
* @param integer $prio The message priority. Should be higher than
|
|
* $this->debug to save the message
|
|
* @param string $message The message to save
|
|
* @return null
|
|
*/
|
|
private function debug($prio, $message)
|
|
{
|
|
if ($this->debug === false || $this->debug === 0) {
|
|
return;
|
|
}
|
|
if ($prio <= $this->debug) {
|
|
echo "[$prio] $message\n";
|
|
//file_put_contents ("/tmp/domframework.file.debug",
|
|
// date ("Y:m:d H:i:s")." [$prio] $message\n",
|
|
// FILE_APPEND);
|
|
}
|
|
}
|
|
|
|
/** External function allowed to be overloaded to test the RO access to a
|
|
* resource
|
|
* @param string $path The path to test in the filesystem
|
|
* @return boolean true if RO access, false if not
|
|
*/
|
|
public function checkExternalPathRO($path)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/** External function allowed to be overloaded to test the RW access to a
|
|
* resource
|
|
* @param string $path The path to test in the filesystem
|
|
* @return boolean true if RW access, false if not
|
|
*/
|
|
public function checkExternalPathRW($path)
|
|
{
|
|
return true;
|
|
}
|
|
}
|