mirror of
https://github.com/nextcloud/spreed.git
synced 2025-12-17 21:12:20 +01:00
fix(performance): Parse file shares inaccurately to prevent setupFS
In some cases we only use the "plain text" version of a chat message. We can render the conversation list subline without knowing the exact path of a file. All other information is still available on the cache entry. The path is only needed in some special cases when afterwards the viewer is proposed to the user. Signed-off-by: Joas Schilling <coding@schilljs.com>
This commit is contained in:
parent
b21659365b
commit
fa7d240896
12 changed files with 56 additions and 30 deletions
|
|
@ -66,7 +66,12 @@ class MessageParser {
|
||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function parseMessage(Message $message): void {
|
/**
|
||||||
|
* @param bool $allowInaccurate File share messages will not have fully correct data for the file object
|
||||||
|
* E.g. path is only the file name and preview generation is estimated by
|
||||||
|
* mimetype only. This is done to prevent a filesystem setup.
|
||||||
|
*/
|
||||||
|
public function parseMessage(Message $message, bool $allowInaccurate = false): void {
|
||||||
$message->setMessage($message->getComment()->getMessage(), []);
|
$message->setMessage($message->getComment()->getMessage(), []);
|
||||||
|
|
||||||
$verb = $message->getComment()->getVerb();
|
$verb = $message->getComment()->getVerb();
|
||||||
|
|
@ -77,7 +82,7 @@ class MessageParser {
|
||||||
$this->setMessageActor($message);
|
$this->setMessageActor($message);
|
||||||
$this->setLastEditInfo($message);
|
$this->setLastEditInfo($message);
|
||||||
|
|
||||||
$event = new MessageParseEvent($message->getRoom(), $message);
|
$event = new MessageParseEvent($message->getRoom(), $message, $allowInaccurate);
|
||||||
$this->dispatcher->dispatchTyped($event);
|
$this->dispatcher->dispatchTyped($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ use OCP\Comments\ICommentsManager;
|
||||||
use OCP\EventDispatcher\Event;
|
use OCP\EventDispatcher\Event;
|
||||||
use OCP\EventDispatcher\IEventListener;
|
use OCP\EventDispatcher\IEventListener;
|
||||||
use OCP\Federation\ICloudIdManager;
|
use OCP\Federation\ICloudIdManager;
|
||||||
|
use OCP\Files\FileInfo;
|
||||||
use OCP\Files\InvalidPathException;
|
use OCP\Files\InvalidPathException;
|
||||||
use OCP\Files\IRootFolder;
|
use OCP\Files\IRootFolder;
|
||||||
use OCP\Files\Node;
|
use OCP\Files\Node;
|
||||||
|
|
@ -92,7 +93,7 @@ class SystemMessage implements IEventListener {
|
||||||
|
|
||||||
if ($event->getMessage()->getMessageType() === ChatManager::VERB_SYSTEM) {
|
if ($event->getMessage()->getMessageType() === ChatManager::VERB_SYSTEM) {
|
||||||
try {
|
try {
|
||||||
$this->parseMessage($event->getMessage());
|
$this->parseMessage($event->getMessage(), $event->allowInaccurate());
|
||||||
// Disabled so we can parse mentions in captions: $event->stopPropagation();
|
// Disabled so we can parse mentions in captions: $event->stopPropagation();
|
||||||
} catch (\OutOfBoundsException $e) {
|
} catch (\OutOfBoundsException $e) {
|
||||||
// Unknown message, ignore
|
// Unknown message, ignore
|
||||||
|
|
@ -111,7 +112,7 @@ class SystemMessage implements IEventListener {
|
||||||
* @param Message $chatMessage
|
* @param Message $chatMessage
|
||||||
* @throws \OutOfBoundsException
|
* @throws \OutOfBoundsException
|
||||||
*/
|
*/
|
||||||
protected function parseMessage(Message $chatMessage): void {
|
protected function parseMessage(Message $chatMessage, $allowInaccurate): void {
|
||||||
$this->l = $chatMessage->getL10n();
|
$this->l = $chatMessage->getL10n();
|
||||||
$comment = $chatMessage->getComment();
|
$comment = $chatMessage->getComment();
|
||||||
$room = $chatMessage->getRoom();
|
$room = $chatMessage->getRoom();
|
||||||
|
|
@ -515,7 +516,7 @@ class SystemMessage implements IEventListener {
|
||||||
}
|
}
|
||||||
} elseif ($message === 'file_shared') {
|
} elseif ($message === 'file_shared') {
|
||||||
try {
|
try {
|
||||||
$parsedParameters['file'] = $this->getFileFromShare($room, $participant, $parameters['share']);
|
$parsedParameters['file'] = $this->getFileFromShare($room, $participant, $parameters['share'], $allowInaccurate);
|
||||||
$parsedMessage = '{file}';
|
$parsedMessage = '{file}';
|
||||||
$metaData = $parameters['metaData'] ?? [];
|
$metaData = $parameters['metaData'] ?? [];
|
||||||
if (isset($metaData['messageType'])) {
|
if (isset($metaData['messageType'])) {
|
||||||
|
|
@ -759,18 +760,24 @@ class SystemMessage implements IEventListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ?Participant $participant
|
|
||||||
* @param string $shareId
|
|
||||||
* @return array
|
|
||||||
* @throws InvalidPathException
|
* @throws InvalidPathException
|
||||||
* @throws NotFoundException
|
* @throws NotFoundException
|
||||||
* @throws ShareNotFound
|
* @throws ShareNotFound
|
||||||
*/
|
*/
|
||||||
protected function getFileFromShare(Room $room, ?Participant $participant, string $shareId): array {
|
protected function getFileFromShare(Room $room, ?Participant $participant, string $shareId, bool $allowInaccurate): array {
|
||||||
$share = $this->shareProvider->getShareById((int)$shareId);
|
$share = $this->shareProvider->getShareById((int)$shareId);
|
||||||
|
|
||||||
if ($participant && $participant->getAttendee()->getActorType() === Attendee::ACTOR_USERS) {
|
if ($participant && $participant->getAttendee()->getActorType() === Attendee::ACTOR_USERS) {
|
||||||
if ($share->getShareOwner() !== $participant->getAttendee()->getActorId()) {
|
if ($allowInaccurate) {
|
||||||
|
$node = $share->getNodeCacheEntry();
|
||||||
|
if ($node === null) {
|
||||||
|
throw new ShareNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = $node->getName();
|
||||||
|
$size = $node->getSize();
|
||||||
|
$path = $name;
|
||||||
|
} elseif ($share->getShareOwner() !== $participant->getAttendee()->getActorId()) {
|
||||||
$userFolder = $this->rootFolder->getUserFolder($participant->getAttendee()->getActorId());
|
$userFolder = $this->rootFolder->getUserFolder($participant->getAttendee()->getActorId());
|
||||||
if (!$userFolder instanceof Node) {
|
if (!$userFolder instanceof Node) {
|
||||||
throw new ShareNotFound();
|
throw new ShareNotFound();
|
||||||
|
|
@ -814,7 +821,12 @@ class SystemMessage implements IEventListener {
|
||||||
} elseif ($participant && $room->getType() !== Room::TYPE_PUBLIC && $participant->getAttendee()->getActorType() === Attendee::ACTOR_FEDERATED_USERS) {
|
} elseif ($participant && $room->getType() !== Room::TYPE_PUBLIC && $participant->getAttendee()->getActorType() === Attendee::ACTOR_FEDERATED_USERS) {
|
||||||
throw new ShareNotFound();
|
throw new ShareNotFound();
|
||||||
} else {
|
} else {
|
||||||
$node = $share->getNode();
|
if ($allowInaccurate) {
|
||||||
|
$node = $share->getNodeCacheEntry();
|
||||||
|
} else {
|
||||||
|
$node = $share->getNode();
|
||||||
|
}
|
||||||
|
|
||||||
$name = $node->getName();
|
$name = $node->getName();
|
||||||
$size = $node->getSize();
|
$size = $node->getSize();
|
||||||
$path = $name;
|
$path = $name;
|
||||||
|
|
@ -825,7 +837,11 @@ class SystemMessage implements IEventListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
$fileId = $node->getId();
|
$fileId = $node->getId();
|
||||||
$isPreviewAvailable = $this->previewManager->isAvailable($node);
|
if ($node instanceof FileInfo) {
|
||||||
|
$isPreviewAvailable = $this->previewManager->isAvailable($node);
|
||||||
|
} else {
|
||||||
|
$isPreviewAvailable = $size > 0 && $this->previewManager->isMimeSupported($node->getMimeType());
|
||||||
|
}
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'type' => 'file',
|
'type' => 'file',
|
||||||
|
|
|
||||||
|
|
@ -469,7 +469,7 @@ class Listener implements IEventListener {
|
||||||
try {
|
try {
|
||||||
$parentComment = $this->chatManager->getParentComment($room, (string)$replyTo);
|
$parentComment = $this->chatManager->getParentComment($room, (string)$replyTo);
|
||||||
$parentMessage = $this->messageParser->createMessage($room, $participant, $parentComment, $this->l);
|
$parentMessage = $this->messageParser->createMessage($room, $participant, $parentComment, $this->l);
|
||||||
$this->messageParser->parseMessage($parentMessage);
|
$this->messageParser->parseMessage($parentMessage, true);
|
||||||
if ($parentMessage->isReplyable()) {
|
if ($parentMessage->isReplyable()) {
|
||||||
$parent = $parentComment;
|
$parent = $parentComment;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ class TalkReferenceProvider extends ADiscoverableReferenceProvider implements IS
|
||||||
throw new RoomNotFoundException();
|
throw new RoomNotFoundException();
|
||||||
}
|
}
|
||||||
$message = $this->messageParser->createMessage($room, $participant, $comment, $this->l);
|
$message = $this->messageParser->createMessage($room, $participant, $comment, $this->l);
|
||||||
$this->messageParser->parseMessage($message);
|
$this->messageParser->parseMessage($message, true);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
$proxy = $this->proxyCacheMessageMapper->findById($room, (int)$messageId);
|
$proxy = $this->proxyCacheMessageMapper->findById($room, (int)$messageId);
|
||||||
|
|
|
||||||
|
|
@ -553,7 +553,7 @@ class ChatController extends AEnvironmentAwareOCSController {
|
||||||
foreach ($comments as $comment) {
|
foreach ($comments as $comment) {
|
||||||
$nextOffset = (int)$comment->getId();
|
$nextOffset = (int)$comment->getId();
|
||||||
$message = $this->messageParser->createMessage($this->room, $this->participant, $comment, $this->l);
|
$message = $this->messageParser->createMessage($this->room, $this->participant, $comment, $this->l);
|
||||||
$this->messageParser->parseMessage($message);
|
$this->messageParser->parseMessage($message, true);
|
||||||
|
|
||||||
if (!$message->getVisibility()) {
|
if (!$message->getVisibility()) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -281,7 +281,7 @@ class TalkWidget implements IAPIWidget, IIconWidget, IButtonWidget, IOptionWidge
|
||||||
}
|
}
|
||||||
} elseif ($room->getLastMessageId() && $room->getLastMessage() && !$room->isFederatedConversation()) {
|
} elseif ($room->getLastMessageId() && $room->getLastMessage() && !$room->isFederatedConversation()) {
|
||||||
$message = $this->messageParser->createMessage($room, $participant, $room->getLastMessage(), $this->l10n);
|
$message = $this->messageParser->createMessage($room, $participant, $room->getLastMessage(), $this->l10n);
|
||||||
$this->messageParser->parseMessage($message);
|
$this->messageParser->parseMessage($message, true);
|
||||||
$subtitle = $this->getSubtitleFromMessage($message);
|
$subtitle = $this->getSubtitleFromMessage($message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ class MessageParseEvent extends ARoomEvent {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
Room $room,
|
Room $room,
|
||||||
protected Message $message,
|
protected Message $message,
|
||||||
|
protected bool $allowInaccurate,
|
||||||
) {
|
) {
|
||||||
parent::__construct($room);
|
parent::__construct($room);
|
||||||
}
|
}
|
||||||
|
|
@ -22,4 +23,8 @@ class MessageParseEvent extends ARoomEvent {
|
||||||
public function getMessage(): Message {
|
public function getMessage(): Message {
|
||||||
return $this->message;
|
return $this->message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function allowInaccurate(): bool {
|
||||||
|
return $this->allowInaccurate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -525,7 +525,7 @@ class Notifier implements INotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
$message = $this->messageParser->createMessage($room, $participant, $comment, $l);
|
$message = $this->messageParser->createMessage($room, $participant, $comment, $l);
|
||||||
$this->messageParser->parseMessage($message);
|
$this->messageParser->parseMessage($message, true);
|
||||||
|
|
||||||
if (!$message->getVisibility()) {
|
if (!$message->getVisibility()) {
|
||||||
throw new AlreadyProcessedException();
|
throw new AlreadyProcessedException();
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ class BotService {
|
||||||
$parent,
|
$parent,
|
||||||
$this->l10nFactory->get('spreed', 'en', 'en')
|
$this->l10nFactory->get('spreed', 'en', 'en')
|
||||||
);
|
);
|
||||||
$messageParser->parseMessage($parentMessage);
|
$messageParser->parseMessage($parentMessage, true);
|
||||||
$parentMessageData = [
|
$parentMessageData = [
|
||||||
'message' => $parentMessage->getMessage(),
|
'message' => $parentMessage->getMessage(),
|
||||||
'parameters' => $parentMessage->getMessageParameters(),
|
'parameters' => $parentMessage->getMessageParameters(),
|
||||||
|
|
@ -125,7 +125,7 @@ class BotService {
|
||||||
$event->getComment(),
|
$event->getComment(),
|
||||||
$this->l10nFactory->get('spreed', 'en', 'en')
|
$this->l10nFactory->get('spreed', 'en', 'en')
|
||||||
);
|
);
|
||||||
$messageParser->parseMessage($message);
|
$messageParser->parseMessage($message, true);
|
||||||
$messageData = [
|
$messageData = [
|
||||||
'message' => $message->getMessage(),
|
'message' => $message->getMessage(),
|
||||||
'parameters' => $message->getMessageParameters(),
|
'parameters' => $message->getMessageParameters(),
|
||||||
|
|
|
||||||
|
|
@ -435,7 +435,7 @@ class RoomFormatter {
|
||||||
IComment $lastMessage,
|
IComment $lastMessage,
|
||||||
): ?array {
|
): ?array {
|
||||||
$message = $this->messageParser->createMessage($room, $participant, $lastMessage, $this->l10n);
|
$message = $this->messageParser->createMessage($room, $participant, $lastMessage, $this->l10n);
|
||||||
$this->messageParser->parseMessage($message);
|
$this->messageParser->parseMessage($message, true);
|
||||||
|
|
||||||
if (!$message->getVisibility()) {
|
if (!$message->getVisibility()) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -632,7 +632,7 @@ class SystemMessageTest extends TestCase {
|
||||||
'parameters' => $parameters,
|
'parameters' => $parameters,
|
||||||
]), [], $message);
|
]), [], $message);
|
||||||
|
|
||||||
self::invokePrivate($parser, 'parseMessage', [$chatMessage]);
|
self::invokePrivate($parser, 'parseMessage', [$chatMessage, false]);
|
||||||
|
|
||||||
$this->assertSame($expectedMessage, $chatMessage->getMessage());
|
$this->assertSame($expectedMessage, $chatMessage->getMessage());
|
||||||
$this->assertSame($expectedParameters, $chatMessage->getMessageParameters());
|
$this->assertSame($expectedParameters, $chatMessage->getMessageParameters());
|
||||||
|
|
@ -672,7 +672,7 @@ class SystemMessageTest extends TestCase {
|
||||||
$chatMessage->setMessage($return, []);
|
$chatMessage->setMessage($return, []);
|
||||||
|
|
||||||
$this->expectException(\OutOfBoundsException::class);
|
$this->expectException(\OutOfBoundsException::class);
|
||||||
self::invokePrivate($parser, 'parseMessage', [$chatMessage]);
|
self::invokePrivate($parser, 'parseMessage', [$chatMessage, false]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetFileFromShareForGuest(): void {
|
public function testGetFileFromShareForGuest(): void {
|
||||||
|
|
@ -743,7 +743,7 @@ class SystemMessageTest extends TestCase {
|
||||||
'preview-available' => 'yes',
|
'preview-available' => 'yes',
|
||||||
'width' => '1234',
|
'width' => '1234',
|
||||||
'height' => '4567',
|
'height' => '4567',
|
||||||
], self::invokePrivate($parser, 'getFileFromShare', [$room, $participant, '23']));
|
], self::invokePrivate($parser, 'getFileFromShare', [$room, $participant, '23', false]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetFileFromShareForGuestWithBlurhash(): void {
|
public function testGetFileFromShareForGuestWithBlurhash(): void {
|
||||||
|
|
@ -820,7 +820,7 @@ class SystemMessageTest extends TestCase {
|
||||||
'width' => '1234',
|
'width' => '1234',
|
||||||
'height' => '4567',
|
'height' => '4567',
|
||||||
'blurhash' => 'LEHV9uae2yk8pyo0adR*.7kCMdnj',
|
'blurhash' => 'LEHV9uae2yk8pyo0adR*.7kCMdnj',
|
||||||
], self::invokePrivate($parser, 'getFileFromShare', [$room, $participant, '23']));
|
], self::invokePrivate($parser, 'getFileFromShare', [$room, $participant, '23', false]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetFileFromShareForOwner(): void {
|
public function testGetFileFromShareForOwner(): void {
|
||||||
|
|
@ -897,7 +897,7 @@ class SystemMessageTest extends TestCase {
|
||||||
'permissions' => '27',
|
'permissions' => '27',
|
||||||
'mimetype' => 'httpd/unix-directory',
|
'mimetype' => 'httpd/unix-directory',
|
||||||
'preview-available' => 'no',
|
'preview-available' => 'no',
|
||||||
], self::invokePrivate($parser, 'getFileFromShare', [$room, $participant, '23']));
|
], self::invokePrivate($parser, 'getFileFromShare', [$room, $participant, '23', false]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetFileFromShareForRecipient(): void {
|
public function testGetFileFromShareForRecipient(): void {
|
||||||
|
|
@ -982,7 +982,7 @@ class SystemMessageTest extends TestCase {
|
||||||
'permissions' => '27',
|
'permissions' => '27',
|
||||||
'mimetype' => 'application/octet-stream',
|
'mimetype' => 'application/octet-stream',
|
||||||
'preview-available' => 'no',
|
'preview-available' => 'no',
|
||||||
], self::invokePrivate($parser, 'getFileFromShare', [$room, $participant, '23']));
|
], self::invokePrivate($parser, 'getFileFromShare', [$room, $participant, '23', false]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetFileFromShareForRecipientThrows(): void {
|
public function testGetFileFromShareForRecipientThrows(): void {
|
||||||
|
|
@ -1026,7 +1026,7 @@ class SystemMessageTest extends TestCase {
|
||||||
|
|
||||||
$parser = $this->getParser();
|
$parser = $this->getParser();
|
||||||
$this->expectException(NotFoundException::class);
|
$this->expectException(NotFoundException::class);
|
||||||
self::invokePrivate($parser, 'getFileFromShare', [$room, $participant, '23']);
|
self::invokePrivate($parser, 'getFileFromShare', [$room, $participant, '23', false]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetFileFromShareThrows(): void {
|
public function testGetFileFromShareThrows(): void {
|
||||||
|
|
@ -1046,7 +1046,7 @@ class SystemMessageTest extends TestCase {
|
||||||
->willReturn($attendee);
|
->willReturn($attendee);
|
||||||
$parser = $this->getParser();
|
$parser = $this->getParser();
|
||||||
$this->expectException(ShareNotFound::class);
|
$this->expectException(ShareNotFound::class);
|
||||||
self::invokePrivate($parser, 'getFileFromShare', [$room, $participant, '23']);
|
self::invokePrivate($parser, 'getFileFromShare', [$room, $participant, '23', false]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function dataGetActor(): array {
|
public static function dataGetActor(): array {
|
||||||
|
|
|
||||||
|
|
@ -421,8 +421,8 @@ class ChatControllerTest extends TestCase {
|
||||||
|
|
||||||
$i = 0;
|
$i = 0;
|
||||||
$expectedCalls = [
|
$expectedCalls = [
|
||||||
[$parentMessage],
|
[$parentMessage, false],
|
||||||
[$chatMessage],
|
[$chatMessage, false],
|
||||||
];
|
];
|
||||||
$this->messageParser->expects($this->exactly(2))
|
$this->messageParser->expects($this->exactly(2))
|
||||||
->method('parseMessage')
|
->method('parseMessage')
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue