array ( 'dir' => '0775', 'file' => '0664', 'user' => '', 'group' => '', ), 'permissionMap' => array( /** array('0664', '0775') */ 'writable' => array( 'data', 'custom', ), /** array('0644', '0755') */ 'readable' => array( 'api', 'application', 'client', 'vendor', 'index.php', 'cron.php', 'rebuild.php', 'main.html', 'reset.html', ), ), ); protected $permissionRules = array( 'writable' => array('0664', '0775'), 'readable' => array('0644', '0755'), ); public function __construct(Manager $fileManager, array $params = null) { $this->fileManager = $fileManager; if (isset($params)) { $this->params = $params; } } protected function getFileManager() { return $this->fileManager; } protected function getParams() { return $this->params; } /** * Get default settings * * @return object */ public function getDefaultPermissions() { $params = $this->getParams(); return $params['defaultPermissions']; } public function getPermissionRules() { return $this->permissionRules; } /** * Set default permission * * @param string $path * @param bool $recurse * * @return bool */ public function setDefaultPermissions($path, $recurse = false) { if (!file_exists($path)) { return false; } $permission = $this->getDefaultPermissions(); $result = $this->chmod($path, array($permission['file'], $permission['dir']), $recurse); if (!empty($permission['user'])) { $result &= $this->chown($path, $permission['user'], $recurse); } if (!empty($permission['group'])) { $result &= $this->chgrp($path, $permission['group'], $recurse); } return $result; } /** * Get current permissions * * @param string $filename * @return string | bool */ public function getCurrentPermission($filePath) { if (!file_exists($filePath)) { return false; } $fileInfo= stat($filePath); return substr(base_convert($fileInfo['mode'],10,8), -4); } /** * Change permissions * * @param string $filename * @param int | array $octal - ex. 0755, array(0644, 0755), array('file'=>0644, 'dir'=>0755) * @param bool $recurse * * @return bool */ public function chmod($path, $octal, $recurse = false) { if (!file_exists($path)) { return false; } //check the input format $permission= array(); if (is_array($octal)) { $count= 0; $rule= array('file', 'dir'); foreach ($octal as $key => $val) { $pKey= strval($key); if (!in_array($pKey, $rule)) { $pKey= $rule[$count]; } if (!empty($pKey)) { $permission[$pKey]= $val; } $count++; } } elseif (is_int((int)$octal)) { $permission= array( 'file' => $octal, 'dir' => $octal, ); } else { return false; } //conver to octal value foreach($permission as $key => $val) { if (is_string($val)) { $permission[$key]= base_convert($val,8,10); } } //Set permission for non-recursive request if (!$recurse) { if (is_dir($path)) { return $this->chmodReal($path, $permission['dir']); } return $this->chmodReal($path, $permission['file']); } //Recursive permission return $this->chmodRecurse($path, $permission['file'], $permission['dir']); } /** * Change permissions recursive * * @param string $filename * @param int $fileOctal - ex. 0644 * @param int $dirOctal - ex. 0755 * * @return bool */ protected function chmodRecurse($path, $fileOctal = 0644, $dirOctal = 0755) { if (!file_exists($path)) { return false; } if (!is_dir($path)) { return $this->chmodReal($path, $fileOctal); } $result = $this->chmodReal($path, $dirOctal); $allFiles = $this->getFileManager()->getFileList($path); foreach ($allFiles as $item) { $result &= $this->chmodRecurse($path . Utils\Util::getSeparator() . $item, $fileOctal, $dirOctal); } return (bool) $result; } /** * Change owner permission * * @param string $path * @param int | string $user * @param bool $recurse * * @return bool */ public function chown($path, $user = '', $recurse = false) { if (!file_exists($path)) { return false; } if (empty($user)) { $user = $this->getDefaultOwner(); } //Set chown for non-recursive request if (!$recurse) { return $this->chownReal($path, $user); } //Recursive chown return $this->chownRecurse($path, $user); } /** * Change owner permission recursive * * @param string $path * @param string $user * * @return bool */ protected function chownRecurse($path, $user) { if (!file_exists($path)) { return false; } if (!is_dir($path)) { return $this->chownReal($path, $user); } $result = $this->chownReal($path, $user); $allFiles = $this->getFileManager()->getFileList($path); foreach ($allFiles as $item) { $result &= $this->chownRecurse($path . Utils\Util::getSeparator() . $item, $user); } return (bool) $result; } /** * Change group permission * * @param string $path * @param int | string $group * @param bool $recurse * * @return bool */ public function chgrp($path, $group = null, $recurse = false) { if (!file_exists($path)) { return false; } if (!isset($group)) { $group = $this->getDefaultGroup(); } //Set chgrp for non-recursive request if (!$recurse) { return $this->chgrpReal($path, $group); } //Recursive chown return $this->chgrpRecurse($path, $group); } /** * Change group permission recursive * * @param string $filename * @param int $fileOctal - ex. 0644 * @param int $dirOctal - ex. 0755 * * @return bool */ protected function chgrpRecurse($path, $group) { if (!file_exists($path)) { return false; } if (!is_dir($path)) { return $this->chgrpReal($path, $group); } $result = $this->chgrpReal($path, $group); $allFiles = $this->getFileManager()->getFileList($path); foreach ($allFiles as $item) { $result &= $this->chgrpRecurse($path . Utils\Util::getSeparator() . $item, $group); } return (bool) $result; } /** * Change permissions recursive * * @param string $filename * @param int $mode - ex. 0644 * * @return bool */ protected function chmodReal($filename, $mode) { try { $result = @chmod($filename, $mode); } catch (\Exception $e) { $result = false; } if (!$result) { $this->chown($filename, $this->getDefaultOwner(true)); $this->chgrp($filename, $this->getDefaultGroup(true)); try { $result = @chmod($filename, $mode); } catch (\Exception $e) { throw new Error($e->getMessage()); } } return $result; } protected function chownReal($path, $user) { try { $result = @chown($path, $user); } catch (\Exception $e) { throw new Error($e->getMessage()); } return $result; } protected function chgrpReal($path, $group) { try { $result = @chgrp($path, $group); } catch (\Exception $e) { throw new Error($e->getMessage()); } return $result; } /** * Get default owner user * * @return int - owner id */ public function getDefaultOwner($usePosix = false) { $defaultPermissions = $this->getDefaultPermissions(); $owner = $defaultPermissions['user']; if (empty($owner) && $usePosix) { $owner = function_exists('posix_getuid') ? posix_getuid() : null; } if (empty($owner)) { return false; } return $owner; } /** * Get default group user * * @return int - group id */ public function getDefaultGroup($usePosix = false) { $defaultPermissions = $this->getDefaultPermissions(); $group = $defaultPermissions['group']; if (empty($group) && $usePosix) { $group = function_exists('posix_getegid') ? posix_getegid() : null; } if (empty($group)) { return false; } return $group; } /** * Set permission regarding defined in permissionMap * * @return bool */ public function setMapPermission($mode = null) { $this->permissionError = array(); $this->permissionErrorRules = array(); $params = $this->getParams(); $permissionRules = $this->permissionRules; if (isset($mode)) { foreach ($permissionRules as &$value) { $value = $mode; } } $result = true; foreach ($params['permissionMap'] as $type => $items) { $permission = $permissionRules[$type]; foreach ($items as $item) { if (file_exists($item)) { try { $this->chmod($item, $permission, true); } catch (\Exception $e) { } $res = is_readable($item); /** check is wtitable */ if ($type == 'writable') { $res &= is_writable($item); if (is_dir($item)) { $name = uniqid(); try { $res &= $this->getFileManager()->putContents(array($item, $name), 'test'); $res &= $this->getFileManager()->removeFile($name, $item); } catch (\Exception $e) { $res = false; } } } if (!$res) { $result = false; $this->permissionError[] = $item; $this->permissionErrorRules[$item] = $permission; } } } } return $result; } /** * Get last permission error * * @return array | string */ public function getLastError() { return $this->permissionError; } /** * Get last permission error rules * * @return array | string */ public function getLastErrorRules() { return $this->permissionErrorRules; } /** * Arrange permission file list * e.g. array('application/Espo/Controllers/Email.php', 'application/Espo/Controllers/Import.php'), result is array('application/Espo/Controllers') * * @param array $fileList * @return array */ public function arrangePermissionList($fileList) { $betterList = array(); foreach ($fileList as $fileName) { $pathInfo = pathinfo($fileName); $dirname = $pathInfo['dirname']; $currentPath = $fileName; if ($this->getSearchCount($dirname, $fileList) > 1) { $currentPath = $dirname; } if (!$this->isItemIncludes($currentPath, $betterList)) { $betterList[] = $currentPath; } } return $betterList; } /** * Get count of a search string in a array * * @param string $search * @param array $array * @return bool */ protected function getSearchCount($search, array $array) { $search = $this->getPregQuote($search); $number = 0; foreach ($array as $value) { if (preg_match('/^'.$search.'/', $value)) { $number++; } } return $number; } protected function isItemIncludes($item, $array) { foreach ($array as $value) { $value = $this->getPregQuote($value); if (preg_match('/^'.$value.'/', $item)) { return true; } } return false; } protected function getPregQuote($string) { return preg_quote($string, '/-+=.'); } }