diff options
Diffstat (limited to 'lib/private')
51 files changed, 1234 insertions, 279 deletions
diff --git a/lib/private/app.php b/lib/private/app.php index c6e235eda4d..abf12264c58 100644 --- a/lib/private/app.php +++ b/lib/private/app.php @@ -361,37 +361,6 @@ class OC_App { } /** - * marks a navigation entry as active - * - * @param string $id id of the entry - * @return bool - * - * This function sets a navigation entry as active and removes the 'active' - * property from all other entries. The templates can use this for - * highlighting the current position of the user. - * - * @deprecated Use \OC::$server->getNavigationManager()->setActiveEntry() instead - */ - public static function setActiveNavigationEntry($id) { - OC::$server->getNavigationManager()->setActiveEntry($id); - return true; - } - - /** - * gets the active Menu entry - * - * @return string id or empty string - * - * This function returns the id of the active navigation entry (set by - * setActiveNavigationEntry - * - * @deprecated Use \OC::$server->getNavigationManager()->getActiveEntry() instead - */ - public static function getActiveNavigationEntry() { - return OC::$server->getNavigationManager()->getActiveEntry(); - } - - /** * Returns the Settings Navigation * * @return string[] @@ -505,9 +474,13 @@ class OC_App { * search for an app in all app-directories * * @param string $appId - * @return mixed (bool|string) + * @return false|string */ protected static function findAppInDirectories($appId) { + $sanitizedAppId = self::cleanAppId($appId); + if($sanitizedAppId !== $appId) { + return false; + } static $app_dir = array(); if (isset($app_dir[$appId])) { diff --git a/lib/private/app/appmanager.php b/lib/private/app/appmanager.php index f826c8ba0c7..8ae93f98832 100644 --- a/lib/private/app/appmanager.php +++ b/lib/private/app/appmanager.php @@ -148,6 +148,13 @@ class AppManager implements IAppManager { return false; } else { $groupIds = json_decode($enabled); + + if (!is_array($groupIds)) { + $jsonError = json_last_error(); + \OC::$server->getLogger()->warning('AppManger::checkAppForUser - can\'t decode group IDs: ' . print_r($enabled, true) . ' - json error code: ' . $jsonError, ['app' => 'lib']); + return false; + } + $userGroups = $this->groupManager->getUserGroupIds($user); foreach ($userGroups as $groupId) { if (array_search($groupId, $groupIds) !== false) { diff --git a/lib/private/app/infoparser.php b/lib/private/app/infoparser.php index 789f01e5bc4..22f705884bc 100644 --- a/lib/private/app/infoparser.php +++ b/lib/private/app/infoparser.php @@ -61,7 +61,7 @@ class InfoParser { if ($xml == false) { return null; } - $array = $this->xmlToArray($xml, false); + $array = $this->xmlToArray($xml); if (is_null($array)) { return null; } diff --git a/lib/private/appframework/app.php b/lib/private/appframework/app.php index 0188d221be1..5cad32bbd09 100644 --- a/lib/private/appframework/app.php +++ b/lib/private/appframework/app.php @@ -26,6 +26,7 @@ namespace OC\AppFramework; +use OC\AppFramework\Http\Dispatcher; use OC_App; use OC\AppFramework\DependencyInjection\DIContainer; use OCP\AppFramework\QueryException; @@ -97,6 +98,7 @@ class App { } // initialize the dispatcher and run all the middleware before the controller + /** @var Dispatcher $dispatcher */ $dispatcher = $container['Dispatcher']; list( diff --git a/lib/private/appframework/http/request.php b/lib/private/appframework/http/request.php index 2bbb70db0f8..6ba1d8f644d 100644 --- a/lib/private/appframework/http/request.php +++ b/lib/private/appframework/http/request.php @@ -447,7 +447,7 @@ class Request implements \ArrayAccess, \Countable, IRequest { $deobfuscatedToken = base64_decode($obfuscatedToken) ^ $secret; // Check if the token is valid - if(\OCP\Security\StringUtils::equals($deobfuscatedToken, $this->items['requesttoken'])) { + if(hash_equals($deobfuscatedToken, $this->items['requesttoken'])) { return true; } else { return false; diff --git a/lib/private/appframework/middleware/security/securitymiddleware.php b/lib/private/appframework/middleware/security/securitymiddleware.php index d0b7202a360..725ce689b48 100644 --- a/lib/private/appframework/middleware/security/securitymiddleware.php +++ b/lib/private/appframework/middleware/security/securitymiddleware.php @@ -27,7 +27,6 @@ namespace OC\AppFramework\Middleware\Security; -use OC\AppFramework\Http; use OC\Appframework\Middleware\Security\Exceptions\AppNotEnabledException; use OC\Appframework\Middleware\Security\Exceptions\CrossSiteRequestForgeryException; use OC\Appframework\Middleware\Security\Exceptions\NotAdminException; diff --git a/lib/private/appframework/utility/simplecontainer.php b/lib/private/appframework/utility/simplecontainer.php index bcae351e023..e55d4e20a81 100644 --- a/lib/private/appframework/utility/simplecontainer.php +++ b/lib/private/appframework/utility/simplecontainer.php @@ -29,9 +29,7 @@ namespace OC\AppFramework\Utility; use ReflectionClass; use ReflectionException; use Closure; - use Pimple\Container; - use OCP\AppFramework\QueryException; use OCP\IContainer; diff --git a/lib/private/apphelper.php b/lib/private/apphelper.php index 9084d2b8ab4..478787b21d8 100644 --- a/lib/private/apphelper.php +++ b/lib/private/apphelper.php @@ -36,6 +36,12 @@ class AppHelper implements \OCP\IHelper { * @deprecated 8.1.0 Use \OCP\IServerContainer::getHTTPClientService */ public function getUrlContent($url) { - return \OC_Util::getUrlContent($url); + try { + $client = \OC::$server->getHTTPClientService()->newClient(); + $response = $client->get($url); + return $response->getBody(); + } catch (\Exception $e) { + return false; + } } } diff --git a/lib/private/comments/comment.php b/lib/private/comments/comment.php new file mode 100644 index 00000000000..219e7ec8e4b --- /dev/null +++ b/lib/private/comments/comment.php @@ -0,0 +1,357 @@ +<?php + +namespace OC\Comments; + +use OCP\Comments\IComment; +use OCP\Comments\IllegalIDChangeException; + +class Comment implements IComment { + + protected $data = [ + 'id' => '', + 'parentId' => '0', + 'topmostParentId' => '0', + 'childrenCount' => '0', + 'message' => '', + 'verb' => '', + 'actorType' => '', + 'actorId' => '', + 'objectType' => '', + 'objectId' => '', + 'creationDT' => null, + 'latestChildDT' => null, + ]; + + /** + * Comment constructor. + * + * @param [] $data optional, array with keys according to column names from + * the comments database scheme + */ + public function __construct(array $data = null) { + if(is_array($data)) { + $this->fromArray($data); + } + } + + /** + * returns the ID of the comment + * + * It may return an empty string, if the comment was not stored. + * It is expected that the concrete Comment implementation gives an ID + * by itself (e.g. after saving). + * + * @return string + * @since 9.0.0 + */ + public function getId() { + return $this->data['id']; + } + + /** + * sets the ID of the comment and returns itself + * + * It is only allowed to set the ID only, if the current id is an empty + * string (which means it is not stored in a database, storage or whatever + * the concrete implementation does), or vice versa. Changing a given ID is + * not permitted and must result in an IllegalIDChangeException. + * + * @param string $id + * @return IComment + * @throws IllegalIDChangeException + * @since 9.0.0 + */ + public function setId($id) { + if(!is_string($id)) { + throw new \InvalidArgumentException('String expected.'); + } + + $id = trim($id); + if($this->data['id'] === '' || ($this->data['id'] !== '' && $id === '')) { + $this->data['id'] = $id; + return $this; + } + + throw new IllegalIDChangeException('Not allowed to assign a new ID to an already saved comment.'); + } + + /** + * returns the parent ID of the comment + * + * @return string + * @since 9.0.0 + */ + public function getParentId() { + return $this->data['parentId']; + } + + /** + * sets the parent ID and returns itself + * + * @param string $parentId + * @return IComment + * @since 9.0.0 + */ + public function setParentId($parentId) { + if(!is_string($parentId)) { + throw new \InvalidArgumentException('String expected.'); + } + $this->data['parentId'] = trim($parentId); + return $this; + } + + /** + * returns the topmost parent ID of the comment + * + * @return string + * @since 9.0.0 + */ + public function getTopmostParentId() { + return $this->data['topmostParentId']; + } + + + /** + * sets the topmost parent ID and returns itself + * + * @param string $id + * @return IComment + * @since 9.0.0 + */ + public function setTopmostParentId($id) { + if(!is_string($id)) { + throw new \InvalidArgumentException('String expected.'); + } + $this->data['topmostParentId'] = trim($id); + return $this; + } + + /** + * returns the number of children + * + * @return int + * @since 9.0.0 + */ + public function getChildrenCount() { + return $this->data['childrenCount']; + } + + /** + * sets the number of children + * + * @param int $count + * @return IComment + * @since 9.0.0 + */ + public function setChildrenCount($count) { + if(!is_int($count)) { + throw new \InvalidArgumentException('Integer expected.'); + } + $this->data['childrenCount'] = $count; + return $this; + } + + /** + * returns the message of the comment + * + * @return string + * @since 9.0.0 + */ + public function getMessage() { + return $this->data['message']; + } + + /** + * sets the message of the comment and returns itself + * + * @param string $message + * @return IComment + * @since 9.0.0 + */ + public function setMessage($message) { + if(!is_string($message)) { + throw new \InvalidArgumentException('String expected.'); + } + $this->data['message'] = trim($message); + return $this; + } + + /** + * returns the verb of the comment + * + * @return string + * @since 9.0.0 + */ + public function getVerb() { + return $this->data['verb']; + } + + /** + * sets the verb of the comment, e.g. 'comment' or 'like' + * + * @param string $verb + * @return IComment + * @since 9.0.0 + */ + public function setVerb($verb) { + if(!is_string($verb) || !trim($verb)) { + throw new \InvalidArgumentException('Non-empty String expected.'); + } + $this->data['verb'] = trim($verb); + return $this; + } + + /** + * returns the actor type + * + * @return string + * @since 9.0.0 + */ + public function getActorType() { + return $this->data['actorType']; + } + + /** + * returns the actor ID + * + * @return string + * @since 9.0.0 + */ + public function getActorId() { + return $this->data['actorId']; + } + + /** + * sets (overwrites) the actor type and id + * + * @param string $actorType e.g. 'user' + * @param string $actorId e.g. 'zombie234' + * @return IComment + * @since 9.0.0 + */ + public function setActor($actorType, $actorId) { + if( + !is_string($actorType) || !trim($actorType) + || !is_string($actorId) || !trim($actorId) + ) { + throw new \InvalidArgumentException('String expected.'); + } + $this->data['actorType'] = trim($actorType); + $this->data['actorId'] = trim($actorId); + return $this; + } + + /** + * returns the creation date of the comment. + * + * If not explicitly set, it shall default to the time of initialization. + * + * @return \DateTime + * @since 9.0.0 + */ + public function getCreationDateTime() { + return $this->data['creationDT']; + } + + /** + * sets the creation date of the comment and returns itself + * + * @param \DateTime $timestamp + * @return IComment + * @since 9.0.0 + */ + public function setCreationDateTime(\DateTime $timestamp) { + $this->data['creationDT'] = $timestamp; + return $this; + } + + /** + * returns the timestamp of the most recent child + * + * @return int + * @since 9.0.0 + */ + public function getLatestChildDateTime() { + return $this->data['latestChildDT']; + } + + /** + * sets the date of the most recent child + * + * @param \DateTime $dateTime + * @return IComment + * @since 9.0.0 + */ + public function setLatestChildDateTime(\DateTime $dateTime = null) { + $this->data['latestChildDT'] = $dateTime; + return $this; + } + + /** + * returns the object type the comment is attached to + * + * @return string + * @since 9.0.0 + */ + public function getObjectType() { + return $this->data['objectType']; + } + + /** + * returns the object id the comment is attached to + * + * @return string + * @since 9.0.0 + */ + public function getObjectId() { + return $this->data['objectId']; + } + + /** + * sets (overwrites) the object of the comment + * + * @param string $objectType e.g. 'file' + * @param string $objectId e.g. '16435' + * @return IComment + * @since 9.0.0 + */ + public function setObject($objectType, $objectId) { + if( + !is_string($objectType) || !trim($objectType) + || !is_string($objectId) || !trim($objectId) + ) { + throw new \InvalidArgumentException('String expected.'); + } + $this->data['objectType'] = trim($objectType); + $this->data['objectId'] = trim($objectId); + return $this; + } + + /** + * sets the comment data based on an array with keys as taken from the + * database. + * + * @param [] $data + * @return IComment + */ + protected function fromArray($data) { + foreach(array_keys($data) as $key) { + // translate DB keys to internal setter names + $setter = 'set' . str_replace('_', '', ucwords($key,'_')); + $setter = str_replace('Timestamp', 'DateTime', $setter); + + if(method_exists($this, $setter)) { + $this->$setter($data[$key]); + } + } + + foreach(['actor', 'object'] as $role) { + if(isset($data[$role . '_type']) && isset($data[$role . '_id'])) { + $setter = 'set' . ucfirst($role); + $this->$setter($data[$role . '_type'], $data[$role . '_id']); + } + } + + return $this; + } +} diff --git a/lib/private/comments/manager.php b/lib/private/comments/manager.php new file mode 100644 index 00000000000..09e59f28370 --- /dev/null +++ b/lib/private/comments/manager.php @@ -0,0 +1,549 @@ +<?php + +namespace OC\Comments; + +use Doctrine\DBAL\Exception\DriverException; +use OCP\Comments\IComment; +use OCP\Comments\ICommentsManager; +use OCP\Comments\NotFoundException; +use OCP\IDBConnection; +use OCP\ILogger; + +class Manager implements ICommentsManager { + + /** @var IDBConnection */ + protected $dbConn; + + /** @var ILogger */ + protected $logger; + + /** @var IComment[] */ + protected $commentsCache = []; + + public function __construct( + IDBConnection $dbConn, + ILogger $logger + ) { + $this->dbConn = $dbConn; + $this->logger = $logger; + } + + /** + * converts data base data into PHP native, proper types as defined by + * IComment interface. + * + * @param array $data + * @return array + */ + protected function normalizeDatabaseData(array $data) { + $data['id'] = strval($data['id']); + $data['parent_id'] = strval($data['parent_id']); + $data['topmost_parent_id'] = strval($data['topmost_parent_id']); + $data['creation_timestamp'] = new \DateTime($data['creation_timestamp']); + $data['latest_child_timestamp'] = new \DateTime($data['latest_child_timestamp']); + $data['children_count'] = intval($data['children_count']); + return $data; + } + + /** + * prepares a comment for an insert or update operation after making sure + * all necessary fields have a value assigned. + * + * @param IComment $comment + * @return IComment returns the same updated IComment instance as provided + * by parameter for convenience + * @throws \UnexpectedValueException + */ + protected function prepareCommentForDatabaseWrite(IComment $comment) { + if( !$comment->getActorType() + || !$comment->getActorId() + || !$comment->getObjectType() + || !$comment->getObjectId() + || !$comment->getVerb() + ) { + throw new \UnexpectedValueException('Actor, Object and Verb information must be provided for saving'); + } + + if($comment->getId() === '') { + $comment->setChildrenCount(0); + $comment->setLatestChildDateTime(new \DateTime('0000-00-00 00:00:00', new \DateTimeZone('UTC'))); + $comment->setLatestChildDateTime(null); + } + + if(is_null($comment->getCreationDateTime())) { + $comment->setCreationDateTime(new \DateTime()); + } + + if($comment->getParentId() !== '0') { + $comment->setTopmostParentId($this->determineTopmostParentId($comment->getParentId())); + } else { + $comment->setTopmostParentId('0'); + } + + $this->cache($comment); + + return $comment; + } + + /** + * returns the topmost parent id of a given comment identified by ID + * + * @param string $id + * @return string + * @throws NotFoundException + */ + protected function determineTopmostParentId($id) { + $comment = $this->get($id); + if($comment->getParentId() === '0') { + return $comment->getId(); + } else { + return $this->determineTopmostParentId($comment->getId()); + } + } + + /** + * updates child information of a comment + * + * @param string $id + * @param \DateTime $cDateTime the date time of the most recent child + * @throws NotFoundException + */ + protected function updateChildrenInformation($id, \DateTime $cDateTime) { + $qb = $this->dbConn->getQueryBuilder(); + $query = $qb->select($qb->createFunction('COUNT(`id`)')) + ->from('comments') + ->where($qb->expr()->eq('parent_id', $qb->createParameter('id'))) + ->setParameter('id', $id); + + $resultStatement = $query->execute(); + $data = $resultStatement->fetch(\PDO::FETCH_NUM); + $resultStatement->closeCursor(); + $children = intval($data[0]); + + $comment = $this->get($id); + $comment->setChildrenCount($children); + $comment->setLatestChildDateTime($cDateTime); + $this->save($comment); + } + + /** + * Tests whether actor or object type and id parameters are acceptable. + * Throws exception if not. + * + * @param string $role + * @param string $type + * @param string $id + * @throws \InvalidArgumentException + */ + protected function checkRoleParameters($role, $type, $id) { + if( + !is_string($type) || empty($type) + || !is_string($id) || empty($id) + ) { + throw new \InvalidArgumentException($role . ' parameters must be string and not empty'); + } + } + + /** + * run-time caches a comment + * + * @param IComment $comment + */ + protected function cache(IComment $comment) { + $id = $comment->getId(); + if(empty($id)) { + return; + } + $this->commentsCache[strval($id)] = $comment; + } + + /** + * removes an entry from the comments run time cache + * + * @param mixed $id the comment's id + */ + protected function uncache($id) { + $id = strval($id); + if (isset($this->commentsCache[$id])) { + unset($this->commentsCache[$id]); + } + } + + /** + * returns a comment instance + * + * @param string $id the ID of the comment + * @return IComment + * @throws NotFoundException + * @throws \InvalidArgumentException + * @since 9.0.0 + */ + public function get($id) { + if(intval($id) === 0) { + throw new \InvalidArgumentException('IDs must be translatable to a number in this implementation.'); + } + + if(isset($this->commentsCache[$id])) { + return $this->commentsCache[$id]; + } + + $qb = $this->dbConn->getQueryBuilder(); + $resultStatement = $qb->select('*') + ->from('comments') + ->where($qb->expr()->eq('id', $qb->createParameter('id'))) + ->setParameter('id', $id, \PDO::PARAM_INT) + ->execute(); + + $data = $resultStatement->fetch(); + $resultStatement->closeCursor(); + if(!$data) { + throw new NotFoundException(); + } + + $comment = new Comment($this->normalizeDatabaseData($data)); + $this->cache($comment); + return $comment; + } + + /** + * returns the comment specified by the id and all it's child comments. + * At this point of time, we do only support one level depth. + * + * @param string $id + * @param int $limit max number of entries to return, 0 returns all + * @param int $offset the start entry + * @return array + * @since 9.0.0 + * + * The return array looks like this + * [ + * 'comment' => IComment, // root comment + * 'replies' => + * [ + * 0 => + * [ + * 'comment' => IComment, + * 'replies' => [] + * ] + * 1 => + * [ + * 'comment' => IComment, + * 'replies'=> [] + * ], + * … + * ] + * ] + */ + public function getTree($id, $limit = 0, $offset = 0) { + $tree = []; + $tree['comment'] = $this->get($id); + $tree['replies'] = []; + + $qb = $this->dbConn->getQueryBuilder(); + $query = $qb->select('*') + ->from('comments') + ->where($qb->expr()->eq('topmost_parent_id', $qb->createParameter('id'))) + ->orderBy('creation_timestamp', 'DESC') + ->setParameter('id', $id); + + if($limit > 0) { + $query->setMaxResults($limit); + } + if($offset > 0) { + $query->setFirstResult($offset); + } + + $resultStatement = $query->execute(); + while($data = $resultStatement->fetch()) { + $comment = new Comment($this->normalizeDatabaseData($data)); + $this->cache($comment); + $tree['replies'][] = [ + 'comment' => $comment, + 'replies' => [] + ]; + } + $resultStatement->closeCursor(); + + return $tree; + } + + /** + * returns comments for a specific object (e.g. a file). + * + * The sort order is always newest to oldest. + * + * @param string $objectType the object type, e.g. 'files' + * @param string $objectId the id of the object + * @param int $limit optional, number of maximum comments to be returned. if + * not specified, all comments are returned. + * @param int $offset optional, starting point + * @param \DateTime $notOlderThan optional, timestamp of the oldest comments + * that may be returned + * @return IComment[] + * @since 9.0.0 + */ + public function getForObject( + $objectType, + $objectId, + $limit = 0, + $offset = 0, + \DateTime $notOlderThan = null + ) { + $comments = []; + + $qb = $this->dbConn->getQueryBuilder(); + $query = $qb->select('*') + ->from('comments') + ->where($qb->expr()->eq('object_type', $qb->createParameter('type'))) + ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id'))) + ->orderBy('creation_timestamp', 'DESC') + ->setParameter('type', $objectType) + ->setParameter('id', $objectId); + + if($limit > 0) { + $query->setMaxResults($limit); + } + if($offset > 0) { + $query->setFirstResult($offset); + } + if(!is_null($notOlderThan)) { + $query + ->andWhere($qb->expr()->gt('creation_timestamp', $qb->createParameter('notOlderThan'))) + ->setParameter('notOlderThan', $notOlderThan, 'datetime'); + } + + $resultStatement = $query->execute(); + while($data = $resultStatement->fetch()) { + $comment = new Comment($this->normalizeDatabaseData($data)); + $this->cache($comment); + $comments[] = $comment; + } + $resultStatement->closeCursor(); + + return $comments; + } + + /** + * @param $objectType string the object type, e.g. 'files' + * @param $objectId string the id of the object + * @return Int + * @since 9.0.0 + */ + public function getNumberOfCommentsForObject($objectType, $objectId) { + $qb = $this->dbConn->getQueryBuilder(); + $query = $qb->select($qb->createFunction('COUNT(`id`)')) + ->from('comments') + ->where($qb->expr()->eq('object_type', $qb->createParameter('type'))) + ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id'))) + ->setParameter('type', $objectType) + ->setParameter('id', $objectId); + + $resultStatement = $query->execute(); + $data = $resultStatement->fetch(\PDO::FETCH_NUM); + $resultStatement->closeCursor(); + return intval($data[0]); + } + + /** + * creates a new comment and returns it. At this point of time, it is not + * saved in the used data storage. Use save() after setting other fields + * of the comment (e.g. message or verb). + * + * @param string $actorType the actor type (e.g. 'user') + * @param string $actorId a user id + * @param string $objectType the object type the comment is attached to + * @param string $objectId the object id the comment is attached to + * @return IComment + * @since 9.0.0 + */ + public function create($actorType, $actorId, $objectType, $objectId) { + $comment = new Comment(); + $comment + ->setActor($actorType, $actorId) + ->setObject($objectType, $objectId); + return $comment; + } + + /** + * permanently deletes the comment specified by the ID + * + * When the comment has child comments, their parent ID will be changed to + * the parent ID of the item that is to be deleted. + * + * @param string $id + * @return bool + * @throws \InvalidArgumentException + * @since 9.0.0 + */ + public function delete($id) { + if(!is_string($id)) { + throw new \InvalidArgumentException('Parameter must be string'); + } + + $qb = $this->dbConn->getQueryBuilder(); + $query = $qb->delete('comments') + ->where($qb->expr()->eq('id', $qb->createParameter('id'))) + ->setParameter('id', $id); + + try { + $affectedRows = $query->execute(); + $this->uncache($id); + } catch (DriverException $e) { + $this->logger->logException($e, ['app' => 'core_comments']); + return false; + } + return ($affectedRows > 0); + } + + /** + * saves the comment permanently and returns it + * + * if the supplied comment has an empty ID, a new entry comment will be + * saved and the instance updated with the new ID. + * + * Otherwise, an existing comment will be updated. + * + * Throws NotFoundException when a comment that is to be updated does not + * exist anymore at this point of time. + * + * @param IComment $comment + * @return bool + * @throws NotFoundException + * @since 9.0.0 + */ + public function save(IComment $comment) { + if($this->prepareCommentForDatabaseWrite($comment)->getId() === '') { + $result = $this->insert($comment); + } else { + $result = $this->update($comment); + } + + if($result && !!$comment->getParentId()) { + $this->updateChildrenInformation( + $comment->getParentId(), + $comment->getCreationDateTime() + ); + $this->cache($comment); + } + + return $result; + } + + /** + * inserts the provided comment in the database + * + * @param IComment $comment + * @return bool + */ + protected function insert(IComment &$comment) { + $qb = $this->dbConn->getQueryBuilder(); + $affectedRows = $qb + ->insert('comments') + ->values([ + 'parent_id' => $qb->createNamedParameter($comment->getParentId()), + 'topmost_parent_id' => $qb->createNamedParameter($comment->getTopmostParentId()), + 'children_count' => $qb->createNamedParameter($comment->getChildrenCount()), + 'actor_type' => $qb->createNamedParameter($comment->getActorType()), + 'actor_id' => $qb->createNamedParameter($comment->getActorId()), + 'message' => $qb->createNamedParameter($comment->getMessage()), + 'verb' => $qb->createNamedParameter($comment->getVerb()), + 'creation_timestamp' => $qb->createNamedParameter($comment->getCreationDateTime(), 'datetime'), + 'latest_child_timestamp' => $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'), + 'object_type' => $qb->createNamedParameter($comment->getObjectType()), + 'object_id' => $qb->createNamedParameter($comment->getObjectId()), + ]) + ->execute(); + + if ($affectedRows > 0) { + $comment->setId(strval($qb->getLastInsertId())); + } + + return $affectedRows > 0; + } + + /** + * updates a Comment data row + * + * @param IComment $comment + * @return bool + * @throws NotFoundException + */ + protected function update(IComment $comment) { + $qb = $this->dbConn->getQueryBuilder(); + $affectedRows = $qb + ->update('comments') + ->set('parent_id', $qb->createNamedParameter($comment->getParentId())) + ->set('topmost_parent_id', $qb->createNamedParameter($comment->getTopmostParentId())) + ->set('children_count', $qb->createNamedParameter($comment->getChildrenCount())) + ->set('actor_type', $qb->createNamedParameter($comment->getActorType())) + ->set('actor_id', $qb->createNamedParameter($comment->getActorId())) + ->set('message', $qb->createNamedParameter($comment->getMessage())) + ->set('verb', $qb->createNamedParameter($comment->getVerb())) + ->set('creation_timestamp', $qb->createNamedParameter($comment->getCreationDateTime(), 'datetime')) + ->set('latest_child_timestamp', $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime')) + ->set('object_type', $qb->createNamedParameter($comment->getObjectType())) + ->set('object_id', $qb->createNamedParameter($comment->getObjectId())) + ->where($qb->expr()->eq('id', $qb->createParameter('id'))) + ->setParameter('id', $comment->getId()) + ->execute(); + + if($affectedRows === 0) { + throw new NotFoundException('Comment to update does ceased to exist'); + } + + return $affectedRows > 0; + } + + /** + * removes references to specific actor (e.g. on user delete) of a comment. + * The comment itself must not get lost/deleted. + * + * @param string $actorType the actor type (e.g. 'user') + * @param string $actorId a user id + * @return boolean + * @since 9.0.0 + */ + public function deleteReferencesOfActor($actorType, $actorId) { + $this->checkRoleParameters('Actor', $actorType, $actorId); + + $qb = $this->dbConn->getQueryBuilder(); + $affectedRows = $qb + ->update('comments') + ->set('actor_type', $qb->createNamedParameter(ICommentsManager::DELETED_USER)) + ->set('actor_id', $qb->createNamedParameter(ICommentsManager::DELETED_USER)) + ->where($qb->expr()->eq('actor_type', $qb->createParameter('type'))) + ->andWhere($qb->expr()->eq('actor_id', $qb->createParameter('id'))) + ->setParameter('type', $actorType) + ->setParameter('id', $actorId) + ->execute(); + + $this->commentsCache = []; + + return is_int($affectedRows); + } + + /** + * deletes all comments made of a specific object (e.g. on file delete) + * + * @param string $objectType the object type (e.g. 'file') + * @param string $objectId e.g. the file id + * @return boolean + * @since 9.0.0 + */ + public function deleteCommentsAtObject($objectType, $objectId) { + $this->checkRoleParameters('Object', $objectType, $objectId); + + $qb = $this->dbConn->getQueryBuilder(); + $affectedRows = $qb + ->delete('comments') + ->where($qb->expr()->eq('object_type', $qb->createParameter('type'))) + ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id'))) + ->setParameter('type', $objectType) + ->setParameter('id', $objectId) + ->execute(); + + $this->commentsCache = []; + + return is_int($affectedRows); + } +} diff --git a/lib/private/comments/managerfactory.php b/lib/private/comments/managerfactory.php new file mode 100644 index 00000000000..41978d0cf4b --- /dev/null +++ b/lib/private/comments/managerfactory.php @@ -0,0 +1,23 @@ +<?php + +namespace OC\Comments; + +use OCP\Comments\ICommentsManager; +use OCP\Comments\ICommentsManagerFactory; + + +class ManagerFactory implements ICommentsManagerFactory { + + /** + * creates and returns an instance of the ICommentsManager + * + * @return ICommentsManager + * @since 9.0.0 + */ + public function getManager() { + return new Manager( + \OC::$server->getDatabaseConnection(), + \OC::$server->getLogger() + ); + } +} diff --git a/lib/private/console/application.php b/lib/private/console/application.php index 55c817d497f..e6d8677b72e 100644 --- a/lib/private/console/application.php +++ b/lib/private/console/application.php @@ -48,25 +48,31 @@ class Application { /** * @param OutputInterface $output + * @throws \Exception */ public function loadCommands(OutputInterface $output) { // $application is required to be defined in the register_command scripts $application = $this->application; - require_once \OC::$SERVERROOT . '/core/register_command.php'; + require_once __DIR__ . '/../../../core/register_command.php'; if ($this->config->getSystemValue('installed', false)) { - if (!\OCP\Util::needUpgrade()) { + if (\OCP\Util::needUpgrade()) { + $output->writeln("ownCloud or one of the apps require upgrade - only a limited number of commands are available"); + $output->writeln("You may use your browser or the occ upgrade command to do the upgrade"); + } elseif ($this->config->getSystemValue('maintenance', false)) { + $output->writeln("ownCloud is in maintenance mode - no app have been loaded"); + } else { OC_App::loadApps(); foreach (\OC::$server->getAppManager()->getInstalledApps() as $app) { $appPath = \OC_App::getAppPath($app); + if($appPath === false) { + continue; + } \OC::$loader->addValidRoot($appPath); $file = $appPath . '/appinfo/register_command.php'; if (file_exists($file)) { require $file; } } - } else { - $output->writeln("ownCloud or one of the apps require upgrade - only a limited number of commands are available"); - $output->writeln("You may use your browser or the occ upgrade command to do the upgrade"); } } else { $output->writeln("ownCloud is not installed - only a limited number of commands are available"); @@ -85,6 +91,11 @@ class Application { } } + /** + * Sets whether to automatically exit after a command execution or not. + * + * @param bool $boolean Whether to automatically exit after a command execution or not + */ public function setAutoExit($boolean) { $this->application->setAutoExit($boolean); } diff --git a/lib/private/db/querybuilder/querybuilder.php b/lib/private/db/querybuilder/querybuilder.php index 02d8ee4344d..a4cbb5abd76 100644 --- a/lib/private/db/querybuilder/querybuilder.php +++ b/lib/private/db/querybuilder/querybuilder.php @@ -40,6 +40,9 @@ class QueryBuilder implements IQueryBuilder { /** @var bool */ private $automaticTablePrefix = true; + /** @var string */ + protected $lastInsertedTable; + /** * Initializes a new QueryBuilder. * @@ -325,6 +328,28 @@ class QueryBuilder implements IQueryBuilder { } /** + * Specifies an item that is to be returned uniquely in the query result. + * + * <code> + * $qb = $conn->getQueryBuilder() + * ->selectDistinct('type') + * ->from('users'); + * </code> + * + * @param mixed $select The selection expressions. + * + * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + */ + public function selectDistinct($select) { + + $this->queryBuilder->addSelect( + 'DISTINCT ' . $this->helper->quoteColumnName($select) + ); + + return $this; + } + + /** * Adds an item that is to be returned in the query result. * * <code> @@ -423,6 +448,8 @@ class QueryBuilder implements IQueryBuilder { $this->getTableName($insert) ); + $this->lastInsertedTable = $insert; + return $this; } @@ -1024,14 +1051,57 @@ class QueryBuilder implements IQueryBuilder { } /** + * Used to get the id of the last inserted element + * @return int + * @throws \BadMethodCallException When being called before an insert query has been run. + */ + public function getLastInsertId() { + if ($this->getType() === \Doctrine\DBAL\Query\QueryBuilder::INSERT && $this->lastInsertedTable) { + // lastInsertId() needs the prefix but no quotes + $table = $this->prefixTableName($this->lastInsertedTable); + return (int) $this->connection->lastInsertId($table); + } + + throw new \BadMethodCallException('Invalid call to getLastInsertId without using insert() before.'); + } + + /** + * Returns the table name quoted and with database prefix as needed by the implementation + * + * @param string $table + * @return string + */ + public function getTableName($table) { + $table = $this->prefixTableName($table); + return $this->helper->quoteColumnName($table); + } + + /** + * Returns the table name with database prefix as needed by the implementation + * * @param string $table * @return string */ - private function getTableName($table) { + protected function prefixTableName($table) { if ($this->automaticTablePrefix === false || strpos($table, '*PREFIX*') === 0) { - return $this->helper->quoteColumnName($table); + return $table; + } + + return '*PREFIX*' . $table; + } + + /** + * Returns the column name quoted and with table alias prefix as needed by the implementation + * + * @param string $column + * @param string $tableAlias + * @return string + */ + public function getColumnName($column, $tableAlias = '') { + if ($tableAlias !== '') { + $tableAlias .= '.'; } - return $this->helper->quoteColumnName('*PREFIX*' . $table); + return $this->helper->quoteColumnName($tableAlias . $column); } } diff --git a/lib/private/db/querybuilder/quotehelper.php b/lib/private/db/querybuilder/quotehelper.php index 4b62fee6a6c..5ceb76bbf93 100644 --- a/lib/private/db/querybuilder/quotehelper.php +++ b/lib/private/db/querybuilder/quotehelper.php @@ -61,7 +61,7 @@ class QuoteHelper { } if (substr_count($string, '.')) { - list($alias, $columnName) = explode('.', $string); + list($alias, $columnName) = explode('.', $string, 2); if ($columnName === '*') { return $string; diff --git a/lib/private/files.php b/lib/private/files.php index af10f3e1e32..1cda28496cb 100644 --- a/lib/private/files.php +++ b/lib/private/files.php @@ -115,7 +115,7 @@ class OC_Files { self::lockFiles($view, $dir, $files); $streamer->sendHeaders($name); - $executionTime = intval(ini_get('max_execution_time')); + $executionTime = intval(OC::$server->getIniWrapper()->getNumeric('max_execution_time')); set_time_limit(0); if ($getType === self::ZIP_FILES) { foreach ($files as $file) { diff --git a/lib/private/files/fileinfo.php b/lib/private/files/fileinfo.php index 5b5e8697004..5ed65cd3795 100644 --- a/lib/private/files/fileinfo.php +++ b/lib/private/files/fileinfo.php @@ -100,6 +100,8 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { return $this->getType(); } else if ($offset === 'etag') { return $this->getEtag(); + } elseif ($offset === 'permissions') { + return $this->getPermissions(); } elseif (isset($this->data[$offset])) { return $this->data[$offset]; } else { @@ -193,7 +195,11 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { * @return int */ public function getPermissions() { - return $this->data['permissions']; + $perms = $this->data['permissions']; + if (\OCP\Util::isSharingDisabledForUser() || ($this->isShared() && !\OC\Share\Share::isResharingAllowed())) { + $perms = $perms & ~\OCP\Constants::PERMISSION_SHARE; + } + return $perms; } /** diff --git a/lib/private/files/filesystem.php b/lib/private/files/filesystem.php index 9f243c15a4c..9d1b7150687 100644 --- a/lib/private/files/filesystem.php +++ b/lib/private/files/filesystem.php @@ -58,7 +58,6 @@ namespace OC\Files; -use OC\Cache\File; use OC\Files\Config\MountProviderCollection; use OC\Files\Storage\StorageFactory; use OCP\Files\Config\IMountProvider; diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php index 0cd67e343ff..b06543d0a6a 100644 --- a/lib/private/files/storage/common.php +++ b/lib/private/files/storage/common.php @@ -141,10 +141,6 @@ abstract class Common implements Storage { } public function isSharable($path) { - if (\OCP\Util::isSharingDisabledForUser()) { - return false; - } - return $this->isReadable($path); } diff --git a/lib/private/files/storage/dav.php b/lib/private/files/storage/dav.php index dda163e41a0..9afebab1dd7 100644 --- a/lib/private/files/storage/dav.php +++ b/lib/private/files/storage/dav.php @@ -34,6 +34,7 @@ namespace OC\Files\Storage; use Exception; +use GuzzleHttp\Exception\RequestException; use OC\Files\Filesystem; use OC\Files\Stream\Close; use Icewind\Streams\IteratorDirectory; @@ -339,15 +340,20 @@ class DAV extends Common { switch ($mode) { case 'r': case 'rb': - if (!$this->file_exists($path)) { - return false; + try { + $response = $this->httpClientService + ->newClient() + ->get($this->createBaseUri() . $this->encodePath($path), [ + 'auth' => [$this->user, $this->password], + 'stream' => true + ]); + } catch (RequestException $e) { + if ($e->getResponse()->getStatusCode() === 404) { + return false; + } else { + throw $e; + } } - $response = $this->httpClientService - ->newClient() - ->get($this->createBaseUri() . $this->encodePath($path), [ - 'auth' => [$this->user, $this->password], - 'stream' => true - ]); if ($response->getStatusCode() !== Http::STATUS_OK) { if ($response->getStatusCode() === Http::STATUS_LOCKED) { diff --git a/lib/private/files/storage/wrapper/availability.php b/lib/private/files/storage/wrapper/availability.php index d6ce78f6e44..d2fbbbacf75 100644 --- a/lib/private/files/storage/wrapper/availability.php +++ b/lib/private/files/storage/wrapper/availability.php @@ -399,7 +399,6 @@ class Availability extends Wrapper { /** {@inheritdoc} */ public function getOwner($path) { - $this->checkAvailability(); try { return parent::getOwner($path); } catch (\OCP\Files\StorageNotAvailableException $e) { diff --git a/lib/private/files/view.php b/lib/private/files/view.php index 7854790d0e5..b8b1b8a50d6 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -1253,7 +1253,7 @@ class View { * @param boolean|string $includeMountPoints true to add mountpoint sizes, * 'ext' to add only ext storage mount point sizes. Defaults to true. * defaults to true - * @return \OC\Files\FileInfo|bool False if file does not exist + * @return \OC\Files\FileInfo|false False if file does not exist */ public function getFileInfo($path, $includeMountPoints = true) { $this->assertPathLength($path); @@ -1427,13 +1427,9 @@ class View { if ($mimetype_filter) { $files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) { if (strpos($mimetype_filter, '/')) { - if ($file->getMimetype() === $mimetype_filter) { - $result[] = $file; - } + return $file->getMimetype() === $mimetype_filter; } else { - if ($file->getMimePart() === $mimetype_filter) { - $result[] = $file; - } + return $file->getMimePart() === $mimetype_filter; } }); } diff --git a/lib/private/helper.php b/lib/private/helper.php index 3cad77e6667..78a567638ef 100644 --- a/lib/private/helper.php +++ b/lib/private/helper.php @@ -441,7 +441,7 @@ class OC_Helper { // Default check will be done with $path directories : $dirs = explode(PATH_SEPARATOR, $path); // WARNING : We have to check if open_basedir is enabled : - $obd = ini_get('open_basedir'); + $obd = OC::$server->getIniWrapper()->getString('open_basedir'); if ($obd != "none") { $obd_values = explode(PATH_SEPARATOR, $obd); if (count($obd_values) > 0 and $obd_values[0]) { @@ -701,8 +701,9 @@ class OC_Helper { * @return int PHP upload file size limit */ public static function uploadLimit() { - $upload_max_filesize = OCP\Util::computerFileSize(ini_get('upload_max_filesize')); - $post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size')); + $ini = \OC::$server->getIniWrapper(); + $upload_max_filesize = OCP\Util::computerFileSize($ini->get('upload_max_filesize')); + $post_max_size = OCP\Util::computerFileSize($ini->get('post_max_size')); if ((int)$upload_max_filesize === 0 and (int)$post_max_size === 0) { return INF; } elseif ((int)$upload_max_filesize === 0 or (int)$post_max_size === 0) { @@ -722,12 +723,13 @@ class OC_Helper { if (!function_exists($function_name)) { return false; } - $disabled = explode(',', ini_get('disable_functions')); + $ini = \OC::$server->getIniWrapper(); + $disabled = explode(',', $ini->get('disable_functions')); $disabled = array_map('trim', $disabled); if (in_array($function_name, $disabled)) { return false; } - $disabled = explode(',', ini_get('suhosin.executor.func.blacklist')); + $disabled = explode(',', $ini->get('suhosin.executor.func.blacklist')); $disabled = array_map('trim', $disabled); if (in_array($function_name, $disabled)) { return false; diff --git a/lib/private/l10n.php b/lib/private/l10n.php index 86335bce92f..7835285bd49 100644 --- a/lib/private/l10n.php +++ b/lib/private/l10n.php @@ -116,13 +116,17 @@ class OC_L10N implements \OCP\IL10N { $preferred_language = str_replace('-', '_', $preferred_language); foreach ($available as $available_language) { if ($preferred_language === strtolower($available_language)) { - self::$language = $available_language; + if (!self::$language) { + self::$language = $available_language; + } return $available_language; } } foreach ($available as $available_language) { if (substr($preferred_language, 0, 2) === $available_language) { - self::$language = $available_language; + if (!self::$language) { + self::$language = $available_language; + } return $available_language; } } @@ -407,7 +411,7 @@ class OC_L10N implements \OCP\IL10N { * If nothing works it returns 'en' */ public static function findLanguage($app = null) { - if(self::$language != '') { + if (self::$language != '' && self::languageExists($app, self::$language)) { return self::$language; } diff --git a/lib/private/lock/abstractlockingprovider.php b/lib/private/lock/abstractlockingprovider.php index c7a29380efe..db5f1c72dd7 100644 --- a/lib/private/lock/abstractlockingprovider.php +++ b/lib/private/lock/abstractlockingprovider.php @@ -28,6 +28,8 @@ use OCP\Lock\ILockingProvider; * to release any left over locks at the end of the request */ abstract class AbstractLockingProvider implements ILockingProvider { + const TTL = 3600; // how long until we clear stray locks in seconds + protected $acquiredLocks = [ 'shared' => [], 'exclusive' => [] diff --git a/lib/private/lock/dblockingprovider.php b/lib/private/lock/dblockingprovider.php index 90657e6725f..1b5142a90d2 100644 --- a/lib/private/lock/dblockingprovider.php +++ b/lib/private/lock/dblockingprovider.php @@ -51,8 +51,6 @@ class DBLockingProvider extends AbstractLockingProvider { private $sharedLocks = []; - const TTL = 3600; // how long until we clear stray locks in seconds - /** * Check if we have an open shared lock for a path * @@ -235,10 +233,10 @@ class DBLockingProvider extends AbstractLockingProvider { /** * cleanup empty locks */ - public function cleanEmptyLocks() { + public function cleanExpiredLocks() { $expire = $this->timeFactory->getTime(); $this->connection->executeUpdate( - 'DELETE FROM `*PREFIX*file_locks` WHERE `lock` = 0 AND `ttl` < ?', + 'DELETE FROM `*PREFIX*file_locks` WHERE `ttl` < ?', [$expire] ); } @@ -262,7 +260,7 @@ class DBLockingProvider extends AbstractLockingProvider { public function __destruct() { try { - $this->cleanEmptyLocks(); + $this->cleanExpiredLocks(); } catch (\Exception $e) { // If the table is missing, the clean up was successful if ($this->connection->tableExists('file_locks')) { diff --git a/lib/private/lock/memcachelockingprovider.php b/lib/private/lock/memcachelockingprovider.php index e4158dcdfdf..af95200d159 100644 --- a/lib/private/lock/memcachelockingprovider.php +++ b/lib/private/lock/memcachelockingprovider.php @@ -21,6 +21,7 @@ namespace OC\Lock; +use OCP\IMemcacheTTL; use OCP\Lock\LockedException; use OCP\IMemcache; @@ -37,6 +38,12 @@ class MemcacheLockingProvider extends AbstractLockingProvider { $this->memcache = $memcache; } + private function setTTL($path) { + if ($this->memcache instanceof IMemcacheTTL) { + $this->memcache->setTTL($path, self::TTL); + } + } + /** * @param string $path * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE @@ -69,6 +76,7 @@ class MemcacheLockingProvider extends AbstractLockingProvider { throw new LockedException($path); } } + $this->setTTL($path); $this->markAcquire($path, $type); } @@ -106,6 +114,7 @@ class MemcacheLockingProvider extends AbstractLockingProvider { throw new LockedException($path); } } + $this->setTTL($path); $this->markChange($path, $targetType); } } diff --git a/lib/private/log.php b/lib/private/log.php index ee5d61e98df..a722243dc69 100644 --- a/lib/private/log.php +++ b/lib/private/log.php @@ -227,7 +227,7 @@ class Log implements ILogger { $request = \OC::$server->getRequest(); // if token is found in the request change set the log condition to satisfied - if($request && StringUtils::equals($request->getParam('log_secret'), $logCondition['shared_secret'])) { + if($request && hash_equals($logCondition['shared_secret'], $request->getParam('log_secret'))) { $this->logConditionSatisfied = true; } } diff --git a/lib/private/memcache/apc.php b/lib/private/memcache/apc.php index f768cdc1c6e..e38b4bd3a39 100644 --- a/lib/private/memcache/apc.php +++ b/lib/private/memcache/apc.php @@ -115,9 +115,9 @@ class APC extends Cache implements IMemcache { static public function isAvailable() { if (!extension_loaded('apc')) { return false; - } elseif (!ini_get('apc.enabled')) { + } elseif (!\OC::$server->getIniWrapper()->getBool('apc.enabled')) { return false; - } elseif (!ini_get('apc.enable_cli') && \OC::$CLI) { + } elseif (!\OC::$server->getIniWrapper()->getBool('apc.enable_cli') && \OC::$CLI) { return false; } else { return true; diff --git a/lib/private/memcache/apcu.php b/lib/private/memcache/apcu.php index 9a8da2ae60c..84147233ef0 100644 --- a/lib/private/memcache/apcu.php +++ b/lib/private/memcache/apcu.php @@ -28,9 +28,9 @@ class APCu extends APC { static public function isAvailable() { if (!extension_loaded('apcu')) { return false; - } elseif (!ini_get('apc.enabled')) { + } elseif (!\OC::$server->getIniWrapper()->getBool('apc.enabled')) { return false; - } elseif (!ini_get('apc.enable_cli') && \OC::$CLI) { + } elseif (!\OC::$server->getIniWrapper()->getBool('apc.enable_cli') && \OC::$CLI) { return false; } elseif (version_compare(phpversion('apc'), '4.0.6') === -1) { return false; diff --git a/lib/private/memcache/redis.php b/lib/private/memcache/redis.php index 83be662eabf..68b62e7534a 100644 --- a/lib/private/memcache/redis.php +++ b/lib/private/memcache/redis.php @@ -25,9 +25,9 @@ namespace OC\Memcache; -use OCP\IMemcache; +use OCP\IMemcacheTTL; -class Redis extends Cache implements IMemcache { +class Redis extends Cache implements IMemcacheTTL { /** * @var \Redis $cache */ @@ -195,6 +195,10 @@ class Redis extends Cache implements IMemcache { return false; } + public function setTTL($key, $ttl) { + self::$cache->expire($this->getNamespace() . $key, $ttl); + } + static public function isAvailable() { return extension_loaded('redis') && version_compare(phpversion('redis'), '2.2.5', '>='); diff --git a/lib/private/memcache/xcache.php b/lib/private/memcache/xcache.php index a6265ed5622..0d2e43a1c18 100644 --- a/lib/private/memcache/xcache.php +++ b/lib/private/memcache/xcache.php @@ -118,13 +118,13 @@ class XCache extends Cache implements IMemcache { if (\OC::$CLI && !getenv('XCACHE_TEST')) { return false; } - if (!function_exists('xcache_unset_by_prefix') && ini_get('xcache.admin.enable_auth')) { + if (!function_exists('xcache_unset_by_prefix') && \OC::$server->getIniWrapper()->getBool('xcache.admin.enable_auth')) { // We do not want to use XCache if we can not clear it without // using the administration function xcache_clear_cache() // AND administration functions are password-protected. return false; } - $var_size = (int)ini_get('xcache.var_size'); + $var_size = \OC::$server->getIniWrapper()->getNumeric('xcache.var_size'); if (!$var_size) { return false; } diff --git a/lib/private/preview.php b/lib/private/preview.php index b2accdfd00f..38c043030fc 100644 --- a/lib/private/preview.php +++ b/lib/private/preview.php @@ -1250,7 +1250,7 @@ class Preview { * @param array $args * @param string $prefix */ - public static function prepare_delete($args, $prefix = '') { + public static function prepare_delete(array $args, $prefix = '') { $path = $args['path']; if (substr($path, 0, 1) === '/') { $path = substr($path, 1); @@ -1259,7 +1259,11 @@ class Preview { $view = new \OC\Files\View('/' . \OC_User::getUser() . '/' . $prefix); $absPath = Files\Filesystem::normalizePath($view->getAbsolutePath($path)); - self::addPathToDeleteFileMapper($absPath, $view->getFileInfo($path)); + $fileInfo = $view->getFileInfo($path); + if($fileInfo === false) { + return; + } + self::addPathToDeleteFileMapper($absPath, $fileInfo); if ($view->is_dir($path)) { $children = self::getAllChildren($view, $path); self::$deleteChildrenMapper[$absPath] = $children; diff --git a/lib/private/repair.php b/lib/private/repair.php index f6ac7ebe65b..d870b472c4f 100644 --- a/lib/private/repair.php +++ b/lib/private/repair.php @@ -40,7 +40,6 @@ use OC\Repair\SqliteAutoincrement; use OC\Repair\DropOldTables; use OC\Repair\FillETags; use OC\Repair\InnoDB; -use OC\Repair\RepairConfig; use OC\Repair\RepairLegacyStorages; use OC\Repair\RepairMimeTypes; use OC\Repair\SearchLuceneTables; @@ -107,7 +106,6 @@ class Repair extends BasicEmitter { return [ new RepairMimeTypes(\OC::$server->getConfig()), new RepairLegacyStorages(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection()), - new RepairConfig(), new AssetCache(), new FillETags(\OC::$server->getDatabaseConnection()), new CleanTags(\OC::$server->getDatabaseConnection()), @@ -138,13 +136,12 @@ class Repair extends BasicEmitter { * @return array of RepairStep instances */ public static function getBeforeUpgradeRepairSteps() { - $steps = array( + $steps = [ new InnoDB(), new Collation(\OC::$server->getConfig(), \OC_DB::getConnection()), new SqliteAutoincrement(\OC_DB::getConnection()), new SearchLuceneTables(), - new RepairConfig() - ); + ]; //There is no need to delete all previews on every single update //only 7.0.0 through 7.0.2 generated broken previews diff --git a/lib/private/security/crypto.php b/lib/private/security/crypto.php index 0bd34df3f36..46d0c750b2f 100644 --- a/lib/private/security/crypto.php +++ b/lib/private/security/crypto.php @@ -123,7 +123,7 @@ class Crypto implements ICrypto { $this->cipher->setIV($iv); - if(!\OCP\Security\StringUtils::equals($this->calculateHMAC($parts[0].$parts[1], $password), $hmac)) { + if(!hash_equals($this->calculateHMAC($parts[0].$parts[1], $password), $hmac)) { throw new \Exception('HMAC does not match.'); } diff --git a/lib/private/security/hasher.php b/lib/private/security/hasher.php index a5dd22e5dc8..318141b6852 100644 --- a/lib/private/security/hasher.php +++ b/lib/private/security/hasher.php @@ -109,7 +109,7 @@ class Hasher implements IHasher { // Verify whether it matches a legacy PHPass or SHA1 string $hashLength = strlen($hash); if($hashLength === 60 && password_verify($message.$this->legacySalt, $hash) || - $hashLength === 40 && StringUtils::equals($hash, sha1($message))) { + $hashLength === 40 && hash_equals($hash, sha1($message))) { $newHash = $this->hash($message); return true; } diff --git a/lib/private/security/securerandom.php b/lib/private/security/securerandom.php index 87dca68985e..24affbe8988 100644 --- a/lib/private/security/securerandom.php +++ b/lib/private/security/securerandom.php @@ -27,25 +27,15 @@ use Sabre\DAV\Exception; use OCP\Security\ISecureRandom; /** - * Class SecureRandom provides a layer around RandomLib to generate - * secure random strings. For PHP 7 the native CSPRNG is used. + * Class SecureRandom provides a wrapper around the random_int function to generate + * secure random strings. For PHP 7 the native CSPRNG is used, older versions do + * use a fallback. * * Usage: - * \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(10); - * + * \OC::$server->getSecureRandom()->generate(10); * @package OC\Security */ class SecureRandom implements ISecureRandom { - - /** @var \RandomLib\Factory */ - var $factory; - /** @var \RandomLib\Generator */ - var $generator; - - function __construct() { - $this->factory = new RandomLib\Factory; - } - /** * Convenience method to get a low strength random number generator. * @@ -53,10 +43,10 @@ class SecureRandom implements ISecureRandom { * in a non-cryptographical setting. They are not strong enough to be * used as keys or salts. They are however useful for one-time use tokens. * + * @deprecated 9.0.0 Use \OC\Security\SecureRandom::generate directly or random_bytes() / random_int() * @return $this */ public function getLowStrengthGenerator() { - $this->generator = $this->factory->getLowStrengthGenerator(); return $this; } @@ -67,10 +57,10 @@ class SecureRandom implements ISecureRandom { * They are strong enough to be used as keys and salts. However, they do * take some time and resources to generate, so they should not be over-used * + * @deprecated 9.0.0 Use \OC\Security\SecureRandom::generate directly or random_bytes() / random_int() * @return $this */ public function getMediumStrengthGenerator() { - $this->generator = $this->factory->getMediumStrengthGenerator(); return $this; } @@ -80,26 +70,17 @@ class SecureRandom implements ISecureRandom { * @param string $characters An optional list of characters to use if no character list is * specified all valid base64 characters are used. * @return string - * @throws \Exception If the generator is not initialized. */ public function generate($length, $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') { - if(is_null($this->generator)) { - throw new \Exception('Generator is not initialized.'); - } + $maxCharIndex = strlen($characters) - 1; + $randomString = ''; - if(function_exists('random_int')) { - $maxCharIndex = strlen($characters) - 1; - $randomString = ''; - - while($length > 0) { - $randomNumber = random_int(0, $maxCharIndex); - $randomString .= $characters[$randomNumber]; - $length--; - } - return $randomString; + while($length > 0) { + $randomNumber = random_int(0, $maxCharIndex); + $randomString .= $characters[$randomNumber]; + $length--; } - - return $this->generator->generateString($length, $characters); + return $randomString; } } diff --git a/lib/private/security/stringutils.php b/lib/private/security/stringutils.php deleted file mode 100644 index fa4342a2b45..00000000000 --- a/lib/private/security/stringutils.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php -/** - * @author Lukas Reschke <lukas@owncloud.com> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OC\Security; - -class StringUtils { - - /** - * Compares whether two strings are equal. To prevent guessing of the string - * length this is done by comparing two hashes against each other and afterwards - * a comparison of the real string to prevent against the unlikely chance of - * collisions. - * - * Be aware that this function may leak whether the string to compare have a different - * length. - * - * @param string $expected The expected value - * @param string $input The input to compare against - * @return bool True if the two strings are equal, otherwise false. - */ - public static function equals($expected, $input) { - - if(!is_string($expected) || !is_string($input)) { - return false; - } - - if(function_exists('hash_equals')) { - return hash_equals($expected, $input); - } - - $randomString = \OC::$server->getSecureRandom()->getLowStrengthGenerator()->generate(10); - - if(hash('sha512', $expected.$randomString) === hash('sha512', $input.$randomString)) { - if($expected === $input) { - return true; - } - } - - return false; - } -}
\ No newline at end of file diff --git a/lib/private/security/trusteddomainhelper.php b/lib/private/security/trusteddomainhelper.php index 6dbaadfdb60..c1a397dd52d 100644 --- a/lib/private/security/trusteddomainhelper.php +++ b/lib/private/security/trusteddomainhelper.php @@ -74,16 +74,11 @@ class TrustedDomainHelper { return false; } - // TODO: Workaround for older instances still with port applied. Remove for ownCloud 9. - if(in_array($domainWithPort, $trustedList)) { - return true; - } - // Always allow access from localhost if (preg_match(Request::REGEX_LOCALHOST, $domain) === 1) { return true; } - return in_array($domain, $trustedList); + return in_array($domain, $trustedList, true); } } diff --git a/lib/private/server.php b/lib/private/server.php index 6692e6f6bbf..8439500706d 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -528,6 +528,13 @@ class Server extends SimpleContainer implements IServerContainer { }); return $manager; }); + $this->registerService('CommentsManager', function(Server $c) { + $config = $c->getConfig(); + $factoryClass = $config->getSystemValue('comments.managerFactory', '\OC\Comments\ManagerFactory'); + /** @var \OCP\Comments\ICommentsManagerFactory $factory */ + $factory = new $factoryClass(); + return $factory->getManager(); + }); $this->registerService('EventDispatcher', function() { return new EventDispatcher(); }); @@ -1122,6 +1129,13 @@ class Server extends SimpleContainer implements IServerContainer { } /** + * @return \OCP\Comments\ICommentsManager + */ + public function getCommentsManager() { + return $this->query('CommentsManager'); + } + + /** * @return \OC\IntegrityCheck\Checker */ public function getIntegrityCodeChecker() { diff --git a/lib/private/session/cryptowrapper.php b/lib/private/session/cryptowrapper.php index c79778587e0..177f11ffb70 100644 --- a/lib/private/session/cryptowrapper.php +++ b/lib/private/session/cryptowrapper.php @@ -23,7 +23,6 @@ namespace OC\Session; -use OCP\AppFramework\Utility\ITimeFactory; use OCP\IConfig; use OCP\IRequest; use OCP\ISession; diff --git a/lib/private/setup.php b/lib/private/setup.php index 814d78679e2..4d11cb44a83 100644 --- a/lib/private/setup.php +++ b/lib/private/setup.php @@ -369,11 +369,9 @@ class Setup { // out that this is indeed an ownCloud data directory file_put_contents($config->getSystemValue('datadirectory', \OC::$SERVERROOT.'/data').'/.ocdata', ''); - // Update htaccess files for apache hosts - if (isset($_SERVER['SERVER_SOFTWARE']) && strstr($_SERVER['SERVER_SOFTWARE'], 'Apache')) { - self::updateHtaccess(); - self::protectDataDirectory(); - } + // Update .htaccess files + Setup::updateHtaccess(); + Setup::protectDataDirectory(); //try to write logtimezone if (date_default_timezone_get()) { @@ -395,32 +393,17 @@ class Setup { } /** - * Checks if the .htaccess contains the current version parameter - * - * @return bool - */ - private function isCurrentHtaccess() { - $version = \OC_Util::getVersion(); - unset($version[3]); - - return !strpos( - file_get_contents($this->pathToHtaccess()), - 'Version: '.implode('.', $version) - ) === false; - } - - /** * Append the correct ErrorDocument path for Apache hosts - * - * @throws \OC\HintException If .htaccess does not include the current version */ public static function updateHtaccess() { + // From CLI we don't know the defined web root. Thus we can't write any + // directives into the .htaccess file. + if(\OC::$CLI) { + return; + } $setupHelper = new \OC\Setup(\OC::$server->getConfig(), \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'), new \OC_Defaults(), \OC::$server->getLogger(), \OC::$server->getSecureRandom()); - if(!$setupHelper->isCurrentHtaccess()) { - throw new \OC\HintException('.htaccess file has the wrong version. Please upload the correct version. Maybe you forgot to replace it after updating?'); - } $htaccessContent = file_get_contents($setupHelper->pathToHtaccess()); $content = ''; @@ -439,6 +422,9 @@ class Setup { $content.="\n RewriteBase ".$webRoot; $content .= "\n <IfModule mod_env.c>"; $content .= "\n SetEnv front_controller_active true"; + $content .= "\n <IfModule mod_dir.c>"; + $content .= "\n DirectorySlash off"; + $content .= "\n </IfModule>"; $content.="\n </IfModule>"; $content.="\n</IfModule>"; diff --git a/lib/private/setup/mysql.php b/lib/private/setup/mysql.php index f2d2b15cd90..e8b88eb3489 100644 --- a/lib/private/setup/mysql.php +++ b/lib/private/setup/mysql.php @@ -89,15 +89,28 @@ class MySQL extends AbstractDatabase { * @throws \OC\DatabaseSetupException */ private function connect() { - $type = 'mysql'; + $connectionParams = array( - 'host' => $this->dbHost, - 'user' => $this->dbUser, - 'password' => $this->dbPassword, - 'tablePrefix' => $this->tablePrefix, + 'host' => $this->dbHost, + 'user' => $this->dbUser, + 'password' => $this->dbPassword, + 'tablePrefix' => $this->tablePrefix, ); + + // adding port support + if (strpos($this->dbHost, ':')) { + // Host variable may carry a port or socket. + list($host, $portOrSocket) = explode(':', $this->dbHost, 2); + if (ctype_digit($portOrSocket)) { + $connectionParams['port'] = $portOrSocket; + } else { + $connectionParams['unix_socket'] = $portOrSocket; + } + $connectionParams['host'] = $host; + } + $cf = new ConnectionFactory(); - return $cf->getConnection($type, $connectionParams); + return $cf->getConnection('mysql', $connectionParams); } /** diff --git a/lib/private/share/mailnotifications.php b/lib/private/share/mailnotifications.php index f45d80b37ca..f071c7f3a3c 100644 --- a/lib/private/share/mailnotifications.php +++ b/lib/private/share/mailnotifications.php @@ -170,16 +170,18 @@ class MailNotifications { * @param string $filename the shared file * @param string $link the public link * @param int $expiration expiration date (timestamp) - * @return array $result of failed recipients + * @return string[] $result of failed recipients */ public function sendLinkShareMail($recipient, $filename, $link, $expiration) { $subject = (string)$this->l->t('%s shared »%s« with you', [$this->senderDisplayName, $filename]); list($htmlBody, $textBody) = $this->createMailBody($filename, $link, $expiration); + $recipient = str_replace([', ', '; ', ',', ';', ' '], ',', $recipient); + $recipients = explode(',', $recipient); try { $message = $this->mailer->createMessage(); $message->setSubject($subject); - $message->setTo([$recipient]); + $message->setTo($recipients); $message->setHtmlBody($htmlBody); $message->setPlainBody($textBody); $message->setFrom([ @@ -230,8 +232,8 @@ class MailNotifications { } /** - * @param $itemSource - * @param $itemType + * @param string $itemSource + * @param string $itemType * @param IUser $recipient * @return array */ diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 70f9a6e8920..3edffba8a3f 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -635,7 +635,7 @@ class Share extends Constants { throw new \Exception($message_t); } // verify that the user has share permission - if (!\OC\Files\Filesystem::isSharable($path)) { + if (!\OC\Files\Filesystem::isSharable($path) || \OCP\Util::isSharingDisabledForUser()) { $message = 'You are not allowed to share %s'; $message_t = $l->t('You are not allowed to share %s', [$path]); \OCP\Util::writeLog('OCP\Share', sprintf($message, $path), \OCP\Util::DEBUG); @@ -745,10 +745,8 @@ class Share extends Constants { // The check for each user in the group is done inside the put() function if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_GROUP, $shareWith, null, self::FORMAT_NONE, null, 1, true, true)) { - // Only allow the same share to occur again if it is the same - // owner and is not a group share, this use case is for increasing - // permissions for a specific user - if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) { + + if ($checkExists['share_with'] === $shareWith && $checkExists['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) { $message = 'Sharing %s failed, because this item is already shared with %s'; $message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG); @@ -764,6 +762,11 @@ class Share extends Constants { $updateExistingShare = false; if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_links', 'yes') == 'yes') { + // IF the password is changed via the old ajax endpoint verify it before deleting the old share + if ($passwordChanged === true) { + self::verifyPassword($shareWith); + } + // when updating a link share // FIXME Don't delete link if we update it if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, diff --git a/lib/private/systemtag/systemtagmanager.php b/lib/private/systemtag/systemtagmanager.php index 8caf10d69da..7f239dc84cf 100644 --- a/lib/private/systemtag/systemtagmanager.php +++ b/lib/private/systemtag/systemtagmanager.php @@ -63,7 +63,7 @@ class SystemTagManager implements ISystemTagManager { /** * {@inheritdoc} */ - public function getTagsById($tagIds) { + public function getTagsByIds($tagIds) { if (!is_array($tagIds)) { $tagIds = [$tagIds]; } @@ -242,7 +242,7 @@ class SystemTagManager implements ISystemTagManager { $tagNotFoundException = null; try { - $this->getTagsById($tagIds); + $this->getTagsByIds($tagIds); } catch (TagNotFoundException $e) { $tagNotFoundException = $e; } diff --git a/lib/private/systemtag/systemtagobjectmapper.php b/lib/private/systemtag/systemtagobjectmapper.php index 75f2631a010..988fa66d77e 100644 --- a/lib/private/systemtag/systemtagobjectmapper.php +++ b/lib/private/systemtag/systemtagobjectmapper.php @@ -171,6 +171,10 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { public function haveTag($objIds, $objectType, $tagId, $all = true) { $this->assertTagsExist([$tagId]); + if (!is_array($objIds)) { + $objIds = [$objIds]; + } + $query = $this->connection->getQueryBuilder(); if (!$all) { @@ -209,7 +213,7 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { * @throws \OCP\SystemTag\TagNotFoundException if at least one tag did not exist */ private function assertTagsExist($tagIds) { - $tags = $this->tagManager->getTagsById($tagIds); + $tags = $this->tagManager->getTagsByIds($tagIds); if (count($tags) !== count($tagIds)) { // at least one tag missing, bail out $foundTagIds = array_map( diff --git a/lib/private/template.php b/lib/private/template.php index 1476a964ef3..d794dacac23 100644 --- a/lib/private/template.php +++ b/lib/private/template.php @@ -226,12 +226,12 @@ class OC_Template extends \OC\Template\Base { // Add custom headers $headers = ''; foreach(OC_Util::$headers as $header) { - $headers .= '<'.OC_Util::sanitizeHTML($header['tag']); + $headers .= '<'.\OCP\Util::sanitizeHTML($header['tag']); foreach($header['attributes'] as $name=>$value) { - $headers .= ' '.OC_Util::sanitizeHTML($name).'="'.OC_Util::sanitizeHTML($value).'"'; + $headers .= ' '.\OCP\Util::sanitizeHTML($name).'="'.\OCP\Util::sanitizeHTML($value).'"'; } if ($header['text'] !== null) { - $headers .= '>'.OC_Util::sanitizeHTML($header['text']).'</'.OC_Util::sanitizeHTML($header['tag']).'>'; + $headers .= '>'.\OCP\Util::sanitizeHTML($header['text']).'</'.\OCP\Util::sanitizeHTML($header['tag']).'>'; } else { $headers .= '/>'; } diff --git a/lib/private/template/functions.php b/lib/private/template/functions.php index 79d18632d2f..d156d26f9ce 100644 --- a/lib/private/template/functions.php +++ b/lib/private/template/functions.php @@ -33,7 +33,7 @@ * @param string $string the string which will be escaped and printed */ function p($string) { - print(OC_Util::sanitizeHTML($string)); + print(\OCP\Util::sanitizeHTML($string)); } /** @@ -262,7 +262,7 @@ function html_select_options($options, $selected, $params=array()) { $label = $label[$label_name]; } $select = in_array($value, $selected) ? ' selected="selected"' : ''; - $html .= '<option value="' . OC_Util::sanitizeHTML($value) . '"' . $select . '>' . OC_Util::sanitizeHTML($label) . '</option>'."\n"; + $html .= '<option value="' . \OCP\Util::sanitizeHTML($value) . '"' . $select . '>' . \OCP\Util::sanitizeHTML($label) . '</option>'."\n"; } return $html; } diff --git a/lib/private/tempmanager.php b/lib/private/tempmanager.php index 365d639389f..ac44b76d683 100644 --- a/lib/private/tempmanager.php +++ b/lib/private/tempmanager.php @@ -213,7 +213,7 @@ class TempManager implements ITempManager { if ($temp = $this->config->getSystemValue('tempdirectory', null)) { $directories[] = $temp; } - if ($temp = ini_get('upload_tmp_dir')) { + if ($temp = \OC::$server->getIniWrapper()->get('upload_tmp_dir')) { $directories[] = $temp; } if ($temp = getenv('TMP')) { diff --git a/lib/private/user/user.php b/lib/private/user/user.php index d827097ee39..6c89dd06f77 100644 --- a/lib/private/user/user.php +++ b/lib/private/user/user.php @@ -189,6 +189,8 @@ class User implements IUser { // Delete the users entry in the storage table \OC\Files\Cache\Storage::remove('home::' . $this->uid); + + \OC::$server->getCommentsManager()->deleteReferencesOfActor('user', $this->uid); } if ($this->emitter) { diff --git a/lib/private/util.php b/lib/private/util.php index ac42b96de2d..9016dc59751 100644 --- a/lib/private/util.php +++ b/lib/private/util.php @@ -642,13 +642,15 @@ class OC_Util { } // Check if config folder is writable. - if (!is_writable(OC::$configDir) or !is_readable(OC::$configDir)) { - $errors[] = array( - 'error' => $l->t('Cannot write into "config" directory'), - 'hint' => $l->t('This can usually be fixed by ' - . '%sgiving the webserver write access to the config directory%s.', - array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank">', '</a>')) - ); + if(!OC_Helper::isReadOnlyConfigEnabled()) { + if (!is_writable(OC::$configDir) or !is_readable(OC::$configDir)) { + $errors[] = array( + 'error' => $l->t('Cannot write into "config" directory'), + 'hint' => $l->t('This can usually be fixed by ' + . '%sgiving the webserver write access to the config directory%s.', + array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank">', '</a>')) + ); + } } // Check if there is a writable install folder. @@ -947,6 +949,14 @@ class OC_Util { $parameters['redirect_url'] = $_REQUEST['redirect_url']; } + $parameters['canResetPassword'] = true; + if (!\OC::$server->getSystemConfig()->getValue('lost_password_link')) { + $user = \OC::$server->getUserManager()->get($_REQUEST['user']); + if ($user instanceof IUser) { + $parameters['canResetPassword'] = $user->canChangePassword(); + } + } + $parameters['alt_login'] = OC_App::getAlternativeLogIns(); $parameters['rememberLoginAllowed'] = self::rememberLoginAllowed(); \OC_Hook::emit('OC_Util', 'pre_displayLoginPage', array('parameters' => $parameters)); @@ -1169,14 +1179,16 @@ class OC_Util { * This function is used to sanitize HTML and should be applied on any * string or array of strings before displaying it on a web page. * - * @param string|array &$value + * @param string|array $value * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter. */ - public static function sanitizeHTML(&$value) { + public static function sanitizeHTML($value) { if (is_array($value)) { - array_walk_recursive($value, 'OC_Util::sanitizeHTML'); + $value = array_map(function($value) { + return self::sanitizeHTML($value); + }, $value); } else { - //Specify encoding for PHP<5.4 + // Specify encoding for PHP<5.4 $value = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); } return $value; @@ -1237,7 +1249,11 @@ class OC_Util { // accessing the file via http $url = OC_Helper::makeURLAbsolute(OC::$WEBROOT . '/data' . $fileName); - $content = self::getUrlContent($url); + try { + $content = \OC::$server->getHTTPClientService()->newClient()->get($url)->getBody(); + } catch (\Exception $e) { + $content = false; + } // cleanup @unlink($testFile); @@ -1313,23 +1329,6 @@ class OC_Util { } /** - * Get URL content - * @param string $url Url to get content - * @throws Exception If the URL does not start with http:// or https:// - * @return string of the response or false on error - * This function get the content of a page via curl, if curl is enabled. - * If not, file_get_contents is used. - * @deprecated Use \OC::$server->getHTTPClientService()->newClient()->get($url); - */ - public static function getUrlContent($url) { - try { - return \OC::$server->getHTTPHelper()->getUrlContent($url); - } catch (\Exception $e) { - throw $e; - } - } - - /** * Checks whether the server is running on Windows * * @return bool true if running on Windows, false otherwise @@ -1418,7 +1417,7 @@ class OC_Util { } // XCache if (function_exists('xcache_clear_cache')) { - if (ini_get('xcache.admin.enable_auth')) { + if (\OC::$server->getIniWrapper()->getBool('xcache.admin.enable_auth')) { \OCP\Util::writeLog('core', 'XCache opcode cache will not be cleared because "xcache.admin.enable_auth" is enabled.', \OCP\Util::WARN); } else { @xcache_clear_cache(XC_TYPE_PHP, 0); |