<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OC\Security;
use OCP\ICertificate;
class Certificate implements ICertificate {
protected string $name;
protected ?string $commonName;
protected ?string $organization;
protected \DateTime $issueDate;
protected \DateTime $expireDate;
protected ?string $issuerName;
protected ?string $issuerOrganization;
/**
* @param string $data base64 encoded certificate
* @throws \Exception If the certificate could not get parsed
*/
public function __construct(string $data, string $name) {
$this->name = $name;
$gmt = new \DateTimeZone('GMT');
// If string starts with "file://" ignore the certificate
$query = 'file://';
if (strtolower(substr($data, 0, strlen($query))) === $query) {
throw new \Exception('Certificate could not get parsed.');
}
$info = openssl_x509_parse($data);
if (!is_array($info)) {
// There is a non-standardized certificate format only used by OpenSSL. Replace all
// separators and try again.
$data = str_replace(
['-----BEGIN TRUSTED CERTIFICATE-----', '-----END TRUSTED CERTIFICATE-----'],
['-----BEGIN CERTIFICATE-----', '-----END CERTIFICATE-----'],
$data,
);
$info = openssl_x509_parse($data);
}
if (!is_array($info)) {
throw new \Exception('Certificate could not get parsed.');
}
$this->commonName = $info['subject']['CN'] ?? null;
$this->organization = $info['subject']['O'] ?? null;
$this->issueDate = new \DateTime('@' . $info['validFrom_time_t'], $gmt);
$this->expireDate = new \DateTime('@' . $info['validTo_time_t'], $gmt);
$this->issuerName = $info['issuer']['CN'] ?? null;
$this->issuerOrganization = $info['issuer']['O'] ?? null;
}
public function getName(): string {
return $this->name;
}
public function getCommonName(): ?string {
return $this->commonName;
}
public function getOrganization(): ?string {
return $this->organization;
}
public function getIssueDate(): \DateTime {
return $this->issueDate;
}
public function getExpireDate(): \DateTime {
return $this->expireDate;
}
public function isExpired(): bool {
$now = new \DateTime();
return $this->issueDate > $now or $now > $this->expireDatepre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */<?php
/**
* ownCloud
*
* @author Frank Karlitschek
* @copyright 2012 Frank Karlitschek frank@owncloud.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* Class for abstraction of filesystem functions
* This class won't call any filesystem functions for itself but but will pass them to the correct OC_Filestorage object
* this class should also handle all the file permission related stuff
*
* Hooks provided:
* read(path)
* write(path, &run)
* post_write(path)
* create(path, &run) (when a file is created, both create and write will be emited in that order)
* post_create(path)
* delete(path, &run)
* post_delete(path)
* rename(oldpath,newpath, &run)
* post_rename(oldpath,newpath)
* copy(oldpath,newpath, &run) (if the newpath doesn't exists yes, copy, create and write will be emited in that order)
* post_rename(oldpath,newpath)
*
* the &run parameter can be set to false to prevent the operation from occuring
*/
class OC_Filesystem{
static private $storages=array();
static private $mounts=array();
public static $loaded=false;
/**
* @var OC_Filestorage $defaultInstance
*/
static private $defaultInstance;
/**
* classname which used for hooks handling
* used as signalclass in OC_Hooks::emit()
*/
const CLASSNAME = 'OC_Filesystem';
/**
* signalname emited before file renaming
* @param oldpath
* @param newpath
*/
const signal_rename = 'rename';
/**
* signal emited after file renaming
* @param oldpath
* @param newpath
*/
const signal_post_rename = 'post_rename';
/**
* signal emited before file/dir creation
* @param path
* @param run changing this flag to false in hook handler will cancel event
*/
const signal_create = 'create';
/**
* signal emited after file/dir creation
* @param path
* @param run changing this flag to false in hook handler will cancel event
*/
const signal_post_create = 'post_create';
/**
* signal emits before file/dir copy
* @param oldpath
* @param newpath
* @param run changing this flag to false in hook handler will cancel event
*/
const signal_copy = 'copy';
/**
* signal emits after file/dir copy
* @param oldpath
* @param newpath
*/
const signal_post_copy = 'post_copy';
/**
* signal emits before file/dir save
* @param path
* @param run changing this flag to false in hook handler will cancel event
*/
const signal_write = 'write';
/**
* signal emits after file/dir save
* @param path
*/
const signal_post_write = 'post_write';
/**
* signal emits when reading file/dir
* @param path
*/
const signal_read = 'read';
/**
* signal emits when removing file/dir
* @param path
*/
const signal_delete = 'delete';
/**
* parameters definitions for signals
*/
const signal_param_path = 'path';
const signal_param_oldpath = 'oldpath';
const signal_param_newpath = 'newpath';
/**
* run - changing this flag to false in hook handler will cancel event
*/
const signal_param_run = 'run';
/**
* get the mountpoint of the storage object for a path
( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account
*
* @param string path
* @return string
*/
static public function getMountPoint($path){
OC_Hook::emit(self::CLASSNAME,'get_mountpoint',array('path'=>$path));
if(!$path){
$path='/';
}
if($path[0]!=='/'){
$path='/'.$path;
}
$path=str_replace('//', '/',$path);
$foundMountPoint='';
$mountPoints=array_keys(OC_Filesystem::$mounts);
foreach($mountPoints as $mountpoint){
if($mountpoint==$path){
return $mountpoint;
}
if(strpos($path,$mountpoint)===0 and strlen($mountpoint)>strlen($foundMountPoint)){
$foundMountPoint=$mountpoint;
}
}
return $foundMountPoint;
}
/**
* get the part of the path relative to the mountpoint of the storage it's stored in
* @param string path
* @return bool
*/
static public function getInternalPath($path){
$mountPoint=self::getMountPoint($path);
$internalPath=substr($path,strlen($mountPoint));
return $internalPath;
}
/**
* get the storage object for a path
* @param string path
* @return OC_Filestorage
*/
static public function getStorage($path){
$mountpoint=self::getMountPoint($path);
if($mountpoint){
if(!isset(OC_Filesystem::$storages[$mountpoint])){
$mount=OC_Filesystem::$mounts[$mountpoint];
OC_Filesystem::$storages[$mountpoint]=OC_Filesystem::createStorage($mount['class'],$mount['arguments']);
}
return OC_Filesystem::$storages[$mountpoint];
}
}
static public function init($root){
if(self::$defaultInstance){
return false;
}
self::$defaultInstance=new OC_FilesystemView($root);
//load custom mount config
if(is_file(OC::$SERVERROOT.'/config/mount.php')){
$mountConfig=include(OC::$SERVERROOT.'/config/mount.php');
if(isset($mountConfig['global'])){
foreach($mountConfig['global'] as $mountPoint=>$options){
self::mount($options['class'],$options['options'],$mountPoint);
}
}
if(isset($mountConfig['group'])){
foreach($mountConfig['group'] as $group=>$mounts){
if(OC_Group::inGroup(OC_User::getUser(),$group)){
foreach($mounts as $mountPoint=>$options){
$mountPoint=self::setUserVars($mountPoint);
foreach($options as &$option){
$option=self::setUserVars($option);
}
self::mount($options['class'],$options['options'],$mountPoint);
}
}
}
}
if(isset($mountConfig['user'])){
foreach($mountConfig['user'] as $user=>$mounts){
if($user==='all' or strtolower($user)===strtolower(OC_User::getUser())){
foreach($mounts as $mountPoint=>$options){
$mountPoint=self::setUserVars($mountPoint);
foreach($options as &$option){
$option=self::setUserVars($option);
}
self::mount($options['class'],$options['options'],$mountPoint);
}
}
}
}
}
self::$loaded=true;
}
/**
* fill in the correct values for $user, and $password placeholders
* @param string intput
* @return string
*/
private static function setUserVars($input){
return str_replace('$user',OC_User::getUser(),$input);
}
/**
* get the default filesystem view
* @return OC_FilesystemView
*/
static public function getView(){
return self::$defaultInstance;
}
/**
* tear down the filesystem, removing all storage providers
*/
static public function tearDown(){
self::$storages=array();
}
/**
* create a new storage of a specific type
* @param string type
* @param array arguments
* @return OC_Filestorage
*/
static private function createStorage($class,$arguments){
if(class_exists($class)){
try {
return new $class($arguments);
} catch (Exception $exception) {
OC_Log::write('core', $exception->getMessage(), OC_Log::ERROR);
return false;
}
}else{
OC_Log::write('core','storage backend '.$class.' not found',OC_Log::ERROR);
return false;
}
}
/**
* change the root to a fake root
* @param string fakeRoot
* @return bool
*/
static public function chroot($fakeRoot){
return self::$defaultInstance->chroot($fakeRoot);
}
/**
* @brief get the relative path of the root data directory for the current user
* @return string
*
* Returns path like /admin/files
*/
static public function getRoot(){
return self::$defaultInstance->getRoot();
}
/**
* clear all mounts and storage backends
*/
public static function clearMounts(){
self::$mounts=array();
self::$storages=array();
}
/**
* mount an OC_Filestorage in our virtual filesystem
* @param OC_Filestorage storage
* @param string mountpoint
*/
static public function mount($class,$arguments,$mountpoint){
if($mountpoint[0]!='/'){
$mountpoint='/'.$mountpoint;
}
if(substr($mountpoint,-1)!=='/'){
$mountpoint=$mountpoint.'/';
}
self::$mounts[$mountpoint]=array('class'=>$class,'arguments'=>$arguments);
}
/**
* return the path to a local version of the file
* we need this because we can't know if a file is stored local or not from outside the filestorage and for some purposes a local file is needed
* @param string path
* @return string
*/
static public function getLocalFile($path){
return self::$defaultInstance->getLocalFile($path);
}
/**
* @param string path
* @return string
*/
static public function getLocalFolder($path){
return self::$defaultInstance->getLocalFolder($path);
}
/**
* return path to file which reflects one visible in browser
* @param string path
* @return string
*/
static public function getLocalPath($path) {
$datadir = OC_User::getHome($user).'/files';
$newpath = $path;
if (strncmp($newpath, $datadir, strlen($datadir)) == 0) {
$newpath = substr($path, strlen($datadir));
}
return $newpath;
}
/**
* check if the requested path is valid
* @param string path
* @return bool
*/
static public function isValidPath($path){
if(!$path || $path[0]!=='/'){
$path='/'.$path;
}
if(strstr($path,'/../') || strrchr($path, '/') === '/..' ){
return false;
}
return true;
}
/**
* checks if a file is blacklsited for storage in the filesystem
* Listens to write and rename hooks
* @param array $data from hook
*/
static public function isBlacklisted($data){
$blacklist = array('.htaccess');
if (isset($data['path'])) {
$path = $data['path'];
} else if (isset($data['newpath'])) {
$path = $data['newpath'];
}
if (isset($path)) {
$filename = strtolower(basename($path));
if (in_array($filename, $blacklist)) {
$data['run'] = false;
}
}
}
/**
* following functions are equivilent to their php buildin equivilents for arguments/return values.
*/
static public function mkdir($path){
return self::$defaultInstance->mkdir($path);
}
static public function rmdir($path){
return self::$defaultInstance->rmdir($path);
}
static public function opendir($path){
return self::$defaultInstance->opendir($path);
}
static public function readdir($path){
return self::$defaultInstance->readdir($path);
}
static public function is_dir($path){
return self::$defaultInstance->is_dir($path);
}
static public function is_file($path){
return self::$defaultInstance->is_file($path);
}
static public function stat($path){
return self::$defaultInstance->stat($path);
}
static public function filetype($path){
return self::$defaultInstance->filetype($path);
}
static public function filesize($path){
return self::$defaultInstance->filesize($path);
}
static public function readfile($path){
return self::$defaultInstance->readfile($path);
}
/**
* @deprecated Replaced by isReadable() as part of CRUDS
*/
static public function is_readable($path){
return self::$defaultInstance->is_readable($path);
}
/**
* @deprecated Replaced by isCreatable(), isUpdatable(), isDeletable() as part of CRUDS
*/
static public function is_writable($path){
return self::$defaultInstance->is_writable($path);
}
static public function isCreatable($path) {
return self::$defaultInstance->isCreatable($path);
}
static public function isReadable($path) {
return self::$defaultInstance->isReadable($path);
}
static public function isUpdatable($path) {
return self::$defaultInstance->isUpdatable($path);
}
static public function isDeletable($path) {
return self::$defaultInstance->isDeletable($path);
}
static public function isSharable($path) {
return self::$defaultInstance->isSharable($path);
}
static public function file_exists($path){
return self::$defaultInstance->file_exists($path);
}
static public function filectime($path){
return self::$defaultInstance->filectime($path);
}
static public function filemtime($path){
return self::$defaultInstance->filemtime($path);
}
static public function touch($path, $mtime=null){
return self::$defaultInstance->touch($path, $mtime);
}
static public function file_get_contents($path){
return self::$defaultInstance->file_get_contents($path);
}
static public function file_put_contents($path,$data){
return self::$defaultInstance->file_put_contents($path,$data);
}
static public function unlink($path){
return self::$defaultInstance->unlink($path);
}
static public function rename($path1,$path2){
return self::$defaultInstance->rename($path1,$path2);
}
static public function copy($path1,$path2){
return self::$defaultInstance->copy($path1,$path2);
}
static public function fopen($path,$mode){
return self::$defaultInstance->fopen($path,$mode);
}
static public function toTmpFile($path){
return self::$defaultInstance->toTmpFile($path);
}
static public function fromTmpFile($tmpFile,$path){
return self::$defaultInstance->fromTmpFile($tmpFile,$path);
}
static public function getMimeType($path){
return self::$defaultInstance->getMimeType($path);
}
static public function hash($type,$path, $raw = false){
return self::$defaultInstance->hash($type,$path, $raw);
}
static public function free_space($path='/'){
return self::$defaultInstance->free_space($path);
}
static public function search($query){
return OC_FileCache::search($query);
}
/**
* check if a file or folder has been updated since $time
* @param int $time
* @return bool
*/
static public function hasUpdated($path,$time){
return self::$defaultInstance->hasUpdated($path,$time);
}
static public function removeETagHook($params) {
if (isset($params['path'])) {
$path=$params['path'];
} else {
$path=$params['oldpath'];
}
OC_Connector_Sabre_Node::removeETagPropertyForPath($path);
}
/**
* normalize a path
* @param string path
* @param bool $stripTrailingSlash
* @return string
*/
public static function normalizePath($path,$stripTrailingSlash=true){
if($path==''){
return '/';
}
//no windows style slashes
$path=str_replace('\\','/',$path);
//add leading slash
if($path[0]!=='/'){
$path='/'.$path;
}
//remove trainling slash
if($stripTrailingSlash and strlen($path)>1 and substr($path,-1,1)==='/'){
$path=substr($path,0,-1);
}
//remove duplicate slashes
while(strpos($path,'//')!==false){
$path=str_replace('//','/',$path);
}
//normalize unicode if possible
if(class_exists('Normalizer')){
$path=Normalizer::normalize($path);
}
return $path;
}
}
OC_Hook::connect('OC_Filesystem','post_write', 'OC_Filesystem','removeETagHook');
OC_Hook::connect('OC_Filesystem','post_delete','OC_Filesystem','removeETagHook');
OC_Hook::connect('OC_Filesystem','post_rename','OC_Filesystem','removeETagHook');
OC_Util::setupFS();
require_once 'filecache.php';