mirror of
https://github.com/nextcloud/server.git
synced 2026-10-22 10:16:56 +02:00
Hooks are deprecated for > 10 versions, but also this specific hook is not listened anymore by any of our apps. Alternatives are available for many years: - `OCP\User\Events\UserLoggedInEvent` - `OCP\User\Events\UserLoggedInWithCookieEvent` Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
205 lines
6.6 KiB
PHP
205 lines
6.6 KiB
PHP
<?php
|
|
|
|
/**
|
|
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
|
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
use OC\Files\Cache\Scanner;
|
|
use OC\Files\Filesystem;
|
|
use OC\ServerNotAvailableException;
|
|
use OCA\Files_Trashbin\Trashbin;
|
|
use OCP\HintException;
|
|
use OCP\Server;
|
|
use OCP\Share;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
class OC_Hook {
|
|
public static $thrownExceptions = [];
|
|
|
|
private static array $registered = [];
|
|
|
|
private static array $allowList = [
|
|
[Filesystem::CLASSNAME, Filesystem::signal_read],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_create],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_post_create],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_update],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_post_update],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_write],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_post_write],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_delete],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_post_delete],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_rename],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_post_rename],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_copy],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_post_copy],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_touch],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_post_touch],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_delete_mount],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_create_mount],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_setup],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_pre_setup],
|
|
[Filesystem::CLASSNAME, Filesystem::signal_post_init_mountpoints],
|
|
[Filesystem::CLASSNAME, 'umount'],
|
|
[Filesystem::CLASSNAME, 'post_umount'],
|
|
[Share::class,'share_link_access'],
|
|
[Share::class,'pre_unshare'],
|
|
[Share::class,'post_unshare'],
|
|
[Share::class,'post_unshareFromSelf'],
|
|
[Share::class,'pre_shared'],
|
|
[Share::class,'post_shared'],
|
|
[Share::class,'post_set_expiration_date'],
|
|
[Share::class,'post_update_password'],
|
|
[Share::class,'post_update_permissions'],
|
|
['OC\Files\Storage\Shared','fopen'],
|
|
['OC\Files\Storage\Shared','file_get_contents'],
|
|
['OC\Files\Storage\Shared','file_put_contents'],
|
|
[Trashbin::class,'post_moveToTrash'],
|
|
[Trashbin::class,'post_restore'],
|
|
['OCP\Trashbin','delete'],
|
|
['OCP\Trashbin','preDelete'],
|
|
['OCP\Trashbin','preDeleteAll'],
|
|
['OCP\Trashbin','deleteAll'],
|
|
['OCP\Versions','preDelete'],
|
|
['OCP\Versions','delete'],
|
|
['OC\User','assignedUserId'],
|
|
['OC\User','preUnassignedUserId'],
|
|
['OC\User','postUnassignedUserId'],
|
|
[Scanner::class,'scan_file'],
|
|
[Scanner::class,'post_scan_file'],
|
|
['Scanner','removeFromCache'],
|
|
['Scanner','addToCache'],
|
|
['Scanner','correctFolderSize'],
|
|
/* Only used by tests */
|
|
['LegacyHookTest', 'error'],
|
|
['LegacyHookTest', 'hint'],
|
|
];
|
|
|
|
/**
|
|
* connects a function to a hook
|
|
*
|
|
* @param string $signalClass class name of emitter
|
|
* @param string $signalName name of signal
|
|
* @param string|object $slotClass class name of slot
|
|
* @param string $slotName name of slot
|
|
*
|
|
* This function makes it very easy to connect to use hooks.
|
|
*
|
|
* TODO: write example
|
|
*/
|
|
public static function connect(string $signalClass, string $signalName, string|object $slotClass, string $slotName): bool {
|
|
if (str_starts_with($signalClass, '\\')) {
|
|
$signalClass = substr($signalClass, 1);
|
|
}
|
|
$found = array_find(self::$allowList, function ($allowed) use ($signalClass, $signalName) {
|
|
[$allowedClass, $allowedSignal] = $allowed;
|
|
return $allowedClass === $signalClass && $allowedSignal === $signalName;
|
|
}) !== null;
|
|
|
|
if (!$found) {
|
|
throw new \RuntimeException("The signal $signalClass::$signalName is no longer emitted in server. Listening to it is NOOP.");
|
|
}
|
|
// If we're trying to connect to an emitting class that isn't
|
|
// yet registered, register it
|
|
if (!array_key_exists($signalClass, self::$registered)) {
|
|
self::$registered[$signalClass] = [];
|
|
}
|
|
// If we're trying to connect to an emitting method that isn't
|
|
// yet registered, register it with the emitting class
|
|
if (!array_key_exists($signalName, self::$registered[$signalClass])) {
|
|
self::$registered[$signalClass][$signalName] = [];
|
|
}
|
|
|
|
// don't connect hooks twice
|
|
foreach (self::$registered[$signalClass][$signalName] as $hook) {
|
|
if ($hook['class'] === $slotClass && $hook['name'] === $slotName) {
|
|
return false;
|
|
}
|
|
}
|
|
// Connect the hook handler to the requested emitter
|
|
self::$registered[$signalClass][$signalName][] = [
|
|
'class' => $slotClass,
|
|
'name' => $slotName
|
|
];
|
|
|
|
// No chance for failure ;-)
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* emits a signal
|
|
*
|
|
* @param string $signalClass class name of emitter
|
|
* @param string $signalName name of signal
|
|
* @param mixed $params default: array() array with additional data
|
|
* @return bool true if slots exists or false if not
|
|
* @throws HintException
|
|
* @throws ServerNotAvailableException Emits a signal. To get data from the slot use references!
|
|
*
|
|
* TODO: write example
|
|
*/
|
|
public static function emit(string $signalClass, string $signalName, $params = []): bool {
|
|
if (str_starts_with($signalClass, '\\')) {
|
|
$signalClass = substr($signalClass, 1);
|
|
}
|
|
// Return false if no hook handlers are listening to this
|
|
// emitting class
|
|
if (!array_key_exists($signalClass, self::$registered)) {
|
|
return false;
|
|
}
|
|
|
|
// Return false if no hook handlers are listening to this
|
|
// emitting method
|
|
if (!array_key_exists($signalName, self::$registered[$signalClass])) {
|
|
return false;
|
|
}
|
|
|
|
// Call all slots
|
|
foreach (self::$registered[$signalClass][$signalName] as $i) {
|
|
try {
|
|
call_user_func([ $i['class'], $i['name'] ], $params);
|
|
} catch (Throwable $e) {
|
|
// a failing hook handler must not break the operation that emitted
|
|
// the signal, this includes Errors like TypeError
|
|
self::$thrownExceptions[] = $e;
|
|
Server::get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]);
|
|
if ($e instanceof HintException) {
|
|
throw $e;
|
|
}
|
|
if ($e instanceof ServerNotAvailableException) {
|
|
throw $e;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Clear hooks
|
|
*/
|
|
public static function clear(string $signalClass = '', string $signalName = ''): void {
|
|
if ($signalClass) {
|
|
if (str_starts_with($signalClass, '\\')) {
|
|
$signalClass = substr($signalClass, 1);
|
|
}
|
|
if ($signalName) {
|
|
self::$registered[$signalClass][$signalName] = [];
|
|
} else {
|
|
self::$registered[$signalClass] = [];
|
|
}
|
|
} else {
|
|
self::$registered = [];
|
|
self::$thrownExceptions = [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* DO NOT USE!
|
|
* For unit tests ONLY!
|
|
*/
|
|
public static function getHooks(): array {
|
|
return self::$registered;
|
|
}
|
|
}
|