Skip to content

Commit 438efd4

Browse files
committed
Temp
Signed-off-by: Joas Schilling <[email protected]>
1 parent 76ee066 commit 438efd4

10 files changed

Lines changed: 319 additions & 7 deletions

File tree

appinfo/routes.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,5 @@
4545
include(__DIR__ . '/routes/routesSettingsController.php'),
4646
include(__DIR__ . '/routes/routesSignalingController.php'),
4747
include(__DIR__ . '/routes/routesTempAvatarController.php'),
48+
include(__DIR__ . '/routes/routesWebhookController.php'),
4849
);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* @copyright Copyright (c) 2023, Joas Schilling <[email protected]>
6+
*
7+
* @author Joas Schilling <[email protected]>
8+
*
9+
* @license GNU AGPL version 3 or any later version
10+
*
11+
* This program is free software: you can redistribute it and/or modify
12+
* it under the terms of the GNU Affero General Public License as
13+
* published by the Free Software Foundation, either version 3 of the
14+
* License, or (at your option) any later version.
15+
*
16+
* This program is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
* GNU Affero General Public License for more details.
20+
*
21+
* You should have received a copy of the GNU Affero General Public License
22+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
23+
*
24+
*/
25+
26+
$requirements = [
27+
'apiVersion' => 'v1',
28+
'token' => '[a-z0-9]{4,30}',
29+
];
30+
31+
return [
32+
'ocs' => [
33+
/** @see \OCA\Talk\Controller\WebhookController::sendMessage() */
34+
['name' => 'Webhook#sendMessage', 'url' => '/api/{apiVersion}/webhook/{token}', 'verb' => 'POST', 'requirements' => $requirements],
35+
],
36+
];

lib/Chat/ChatManager.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ public function addChangelogMessage(Room $chat, string $message): IComment {
246246
* Sends a new message to the given chat.
247247
*
248248
* @param Room $chat
249-
* @param Participant $participant
249+
* @param ?Participant $participant
250250
* @param string $actorType
251251
* @param string $actorId
252252
* @param string $message
@@ -255,7 +255,7 @@ public function addChangelogMessage(Room $chat, string $message): IComment {
255255
* @param string $referenceId
256256
* @return IComment
257257
*/
258-
public function sendMessage(Room $chat, Participant $participant, string $actorType, string $actorId, string $message, \DateTime $creationDateTime, ?IComment $replyTo, string $referenceId, bool $silent): IComment {
258+
public function sendMessage(Room $chat, ?Participant $participant, string $actorType, string $actorId, string $message, \DateTime $creationDateTime, ?IComment $replyTo, string $referenceId, bool $silent): IComment {
259259
$comment = $this->commentsManager->create($actorType, $actorId, 'chat', (string) $chat->getId());
260260
$comment->setMessage($message, self::MAX_CHAT_LENGTH);
261261
$comment->setCreationDateTime($creationDateTime);
@@ -273,15 +273,19 @@ public function sendMessage(Room $chat, Participant $participant, string $actorT
273273
}
274274
$this->setMessageExpiration($chat, $comment);
275275

276-
$event = new ChatParticipantEvent($chat, $comment, $participant, $silent);
276+
if ($participant instanceof Participant) {
277+
$event = new ChatParticipantEvent($chat, $comment, $participant, $silent);
278+
} else {
279+
$event = new ChatEvent($chat, $comment, false, $silent);
280+
}
277281
$this->dispatcher->dispatch(self::EVENT_BEFORE_MESSAGE_SEND, $event);
278282

279283
$shouldFlush = $this->notificationManager->defer();
280284
try {
281285
$this->commentsManager->save($comment);
282286

283287
// Update last_message
284-
if ($comment->getActorType() !== 'bots' || $comment->getActorId() === 'changelog') {
288+
if ($comment->getActorType() !== 'bots' || $comment->getActorId() === 'changelog' || str_starts_with($comment->getActorId(), 'webhook-')) {
285289
$this->roomService->setLastMessage($chat, $comment);
286290
$this->unreadCountCache->clear($chat->getId() . '-');
287291
} else {
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
*
6+
* @copyright Copyright (c) 2017, Daniel Calviño Sánchez ([email protected])
7+
*
8+
* @license GNU AGPL version 3 or any later version
9+
*
10+
* This program is free software: you can redistribute it and/or modify
11+
* it under the terms of the GNU Affero General Public License as
12+
* published by the Free Software Foundation, either version 3 of the
13+
* License, or (at your option) any later version.
14+
*
15+
* This program is distributed in the hope that it will be useful,
16+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
* GNU Affero General Public License for more details.
19+
*
20+
* You should have received a copy of the GNU Affero General Public License
21+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
22+
*
23+
*/
24+
25+
namespace OCA\Talk\Controller;
26+
27+
use OCA\Talk\Chat\AutoComplete\SearchPlugin;
28+
use OCA\Talk\Chat\AutoComplete\Sorter;
29+
use OCA\Talk\Chat\ChatManager;
30+
use OCA\Talk\Chat\MessageParser;
31+
use OCA\Talk\Chat\ReactionManager;
32+
use OCA\Talk\Exceptions\UnauthorizedException;
33+
use OCA\Talk\GuestManager;
34+
use OCA\Talk\Manager;
35+
use OCA\Talk\MatterbridgeManager;
36+
use OCA\Talk\Middleware\Attribute\RequireModeratorOrNoLobby;
37+
use OCA\Talk\Middleware\Attribute\RequireModeratorParticipant;
38+
use OCA\Talk\Middleware\Attribute\RequireParticipant;
39+
use OCA\Talk\Middleware\Attribute\RequirePermission;
40+
use OCA\Talk\Middleware\Attribute\RequireReadWriteConversation;
41+
use OCA\Talk\Middleware\Attribute\RequireRoom;
42+
use OCA\Talk\Model\Attachment;
43+
use OCA\Talk\Model\Attendee;
44+
use OCA\Talk\Model\Message;
45+
use OCA\Talk\Model\Session;
46+
use OCA\Talk\Model\Webhook;
47+
use OCA\Talk\Participant;
48+
use OCA\Talk\Room;
49+
use OCA\Talk\Service\AttachmentService;
50+
use OCA\Talk\Service\AvatarService;
51+
use OCA\Talk\Service\ChecksumVerificationService;
52+
use OCA\Talk\Service\ParticipantService;
53+
use OCA\Talk\Service\SessionService;
54+
use OCA\Talk\Service\WebhookService;
55+
use OCA\Talk\Share\RoomShareProvider;
56+
use OCP\App\IAppManager;
57+
use OCP\AppFramework\Http;
58+
use OCP\AppFramework\Http\Attribute\BruteForceProtection;
59+
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
60+
use OCP\AppFramework\Http\Attribute\PublicPage;
61+
use OCP\AppFramework\Http\DataResponse;
62+
use OCP\AppFramework\Utility\ITimeFactory;
63+
use OCP\Collaboration\AutoComplete\IManager;
64+
use OCP\Collaboration\Collaborators\ISearchResult;
65+
use OCP\Comments\IComment;
66+
use OCP\Comments\MessageTooLongException;
67+
use OCP\Comments\NotFoundException;
68+
use OCP\EventDispatcher\IEventDispatcher;
69+
use OCP\IL10N;
70+
use OCP\IRequest;
71+
use OCP\IUserManager;
72+
use OCP\RichObjectStrings\InvalidObjectExeption;
73+
use OCP\RichObjectStrings\IValidator;
74+
use OCP\Security\ITrustedDomainHelper;
75+
use OCP\Share\Exceptions\ShareNotFound;
76+
use OCP\User\Events\UserLiveStatusEvent;
77+
use OCP\UserStatus\IManager as IUserStatusManager;
78+
use OCP\UserStatus\IUserStatus;
79+
80+
class WebhookController extends AEnvironmentAwareController {
81+
private ?string $userId;
82+
private IUserManager $userManager;
83+
private IAppManager $appManager;
84+
private ChatManager $chatManager;
85+
private ReactionManager $reactionManager;
86+
private ParticipantService $participantService;
87+
private SessionService $sessionService;
88+
protected AttachmentService $attachmentService;
89+
protected avatarService $avatarService;
90+
private GuestManager $guestManager;
91+
/** @var string[] */
92+
protected array $guestNames;
93+
private MessageParser $messageParser;
94+
protected RoomShareProvider $shareProvider;
95+
private IManager $autoCompleteManager;
96+
private IUserStatusManager $statusManager;
97+
protected MatterbridgeManager $matterbridgeManager;
98+
private SearchPlugin $searchPlugin;
99+
private ISearchResult $searchResult;
100+
protected ITimeFactory $timeFactory;
101+
protected IEventDispatcher $eventDispatcher;
102+
protected IValidator $richObjectValidator;
103+
protected ITrustedDomainHelper $trustedDomainHelper;
104+
private IL10N $l;
105+
106+
public function __construct(
107+
string $appName,
108+
IRequest $request,
109+
ChatManager $chatManager,
110+
ParticipantService $participantService,
111+
AttachmentService $attachmentService,
112+
avatarService $avatarService,
113+
MessageParser $messageParser,
114+
RoomShareProvider $shareProvider,
115+
MatterbridgeManager $matterbridgeManager,
116+
ITimeFactory $timeFactory,
117+
IEventDispatcher $eventDispatcher,
118+
IValidator $richObjectValidator,
119+
ITrustedDomainHelper $trustedDomainHelper,
120+
IL10N $l,
121+
protected ChecksumVerificationService $checksumVerificationService,
122+
protected WebhookService $webhookService,
123+
protected Manager $manager,
124+
) {
125+
parent::__construct($appName, $request);
126+
$this->chatManager = $chatManager;
127+
$this->participantService = $participantService;
128+
$this->attachmentService = $attachmentService;
129+
$this->avatarService = $avatarService;
130+
$this->messageParser = $messageParser;
131+
$this->shareProvider = $shareProvider;
132+
$this->matterbridgeManager = $matterbridgeManager;
133+
$this->timeFactory = $timeFactory;
134+
$this->eventDispatcher = $eventDispatcher;
135+
$this->richObjectValidator = $richObjectValidator;
136+
$this->trustedDomainHelper = $trustedDomainHelper;
137+
$this->l = $l;
138+
}
139+
140+
141+
public function parseCommentToResponse(IComment $comment, Message $parentMessage = null): DataResponse {
142+
$chatMessage = $this->messageParser->createMessage($this->room, $this->participant, $comment, $this->l);
143+
$this->messageParser->parseMessage($chatMessage);
144+
145+
if (!$chatMessage->getVisibility()) {
146+
$response = new DataResponse([], Http::STATUS_CREATED);
147+
if ($this->participant->getAttendee()->getReadPrivacy() === Participant::PRIVACY_PUBLIC) {
148+
$response->addHeader('X-Chat-Last-Common-Read', (string) $this->chatManager->getLastCommonReadMessage($this->room));
149+
}
150+
return $response;
151+
}
152+
153+
$this->participantService->updateLastReadMessage($this->participant, (int) $comment->getId());
154+
155+
$data = $chatMessage->toArray($this->getResponseFormat());
156+
if ($parentMessage instanceof Message) {
157+
$data['parent'] = $parentMessage->toArray($this->getResponseFormat());
158+
}
159+
160+
$response = new DataResponse($data, Http::STATUS_CREATED);
161+
if ($this->participant->getAttendee()->getReadPrivacy() === Participant::PRIVACY_PUBLIC) {
162+
$response->addHeader('X-Chat-Last-Common-Read', (string) $this->chatManager->getLastCommonReadMessage($this->room));
163+
}
164+
return $response;
165+
}
166+
167+
/**
168+
* Sends a new chat message to the given room.
169+
*
170+
* The author and timestamp are automatically set to the current user/guest
171+
* and time.
172+
*
173+
* @param string $token conversation token
174+
* @param string $message the message to send
175+
* @param string $referenceId for the message to be able to later identify it again
176+
* @param int $replyTo Parent id which this message is a reply to
177+
* @param bool $silent If sent silent the chat message will not create any notifications
178+
* @return DataResponse the status code is "201 Created" if successful, and
179+
* "404 Not found" if the room or session for a guest user was not
180+
* found".
181+
*/
182+
#[BruteForceProtection(action: 'webhook')]
183+
#[PublicPage]
184+
public function sendMessage(string $token, string $message, string $referenceId = '', int $replyTo = 0, bool $silent = false): DataResponse {
185+
186+
187+
\OC::$server->getLogger()->error('Entry');
188+
$random = $this->request->getHeader('Talk-Webhook-Random');
189+
if (empty($random) || strlen($random) < 32) {
190+
\OC::$server->getLogger()->error('Wrong Random');
191+
return new DataResponse([], Http::STATUS_BAD_REQUEST);
192+
}
193+
$checksum = $this->request->getHeader('Talk-Webhook-Checksum');
194+
if (empty($checksum)) {
195+
\OC::$server->getLogger()->error('Wrong Checksum');
196+
return new DataResponse([], Http::STATUS_BAD_REQUEST);
197+
}
198+
199+
$webhooks = $this->webhookService->getWebhooksForToken($token);
200+
$webhook = null;
201+
foreach ($webhooks as $webhookAttempt) {
202+
try {
203+
$this->checksumVerificationService->validateRequest(
204+
$random,
205+
$checksum,
206+
$webhookAttempt->getSecret(),
207+
$message
208+
);
209+
$webhook = $webhookAttempt;
210+
break;
211+
} catch (UnauthorizedException) {
212+
}
213+
}
214+
215+
if (!$webhook instanceof Webhook) {
216+
\OC::$server->getLogger()->error('No Webhook found');
217+
$response = new DataResponse([], Http::STATUS_UNAUTHORIZED);
218+
$response->throttle(['action' => 'webhook']);
219+
return $response;
220+
}
221+
222+
$room = $this->manager->getRoomByToken($token);
223+
224+
$actorType = 'bots';
225+
$actorId = 'webhook-' . $webhook->getUrlHash();
226+
227+
$parent = $parentMessage = null;
228+
// if ($replyTo !== 0) {
229+
// try {
230+
// $parent = $this->chatManager->getParentComment($this->room, (string) $replyTo);
231+
// } catch (NotFoundException $e) {
232+
// // Someone is trying to reply cross-rooms or to a non-existing message
233+
// return new DataResponse([], Http::STATUS_BAD_REQUEST);
234+
// }
235+
//
236+
// $parentMessage = $this->messageParser->createMessage($this->room, $this->participant, $parent, $this->l);
237+
// $this->messageParser->parseMessage($parentMessage);
238+
// if (!$parentMessage->isReplyable()) {
239+
// return new DataResponse([], Http::STATUS_BAD_REQUEST);
240+
// }
241+
// }
242+
243+
// $this->participantService->ensureOneToOneRoomIsFilled($this->room);
244+
$creationDateTime = $this->timeFactory->getDateTime('now', new \DateTimeZone('UTC'));
245+
246+
try {
247+
$comment = $this->chatManager->sendMessage($room, $this->participant, $actorType, $actorId, $message, $creationDateTime, $parent, $referenceId, $silent);
248+
} catch (MessageTooLongException) {
249+
return new DataResponse([], Http::STATUS_REQUEST_ENTITY_TOO_LARGE);
250+
} catch (\Exception) {
251+
return new DataResponse([], Http::STATUS_BAD_REQUEST);
252+
}
253+
254+
return new DataResponse([], Http::STATUS_CREATED);
255+
return $this->parseCommentToResponse($comment, $parentMessage);
256+
}
257+
}

lib/Events/ChatEvent.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,18 @@ class ChatEvent extends RoomEvent {
3030
protected IComment $comment;
3131

3232
protected bool $skipLastActivityUpdate;
33+
protected bool $silent;
3334

3435
public function __construct(
3536
Room $room,
3637
IComment $comment,
3738
bool $skipLastActivityUpdate = false,
39+
bool $silent = false,
3840
) {
3941
parent::__construct($room);
4042
$this->comment = $comment;
4143
$this->skipLastActivityUpdate = $skipLastActivityUpdate;
44+
$this->silent = $silent;
4245
}
4346

4447
public function getComment(): IComment {

lib/Events/ChatParticipantEvent.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,15 @@
2929

3030
class ChatParticipantEvent extends ChatEvent {
3131
protected Participant $participant;
32-
protected bool $silent;
3332

3433
public function __construct(
3534
Room $room,
3635
IComment $message,
3736
Participant $participant,
3837
bool $silent,
3938
) {
40-
parent::__construct($room, $message);
39+
parent::__construct($room, $message, false, $silent);
4140
$this->participant = $participant;
42-
$this->silent = $silent;
4341
}
4442

4543
public function getParticipant(): Participant {

lib/Middleware/InjectionMiddleware.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ public function beforeController(Controller $controller, string $methodName): vo
9494
if (!$controller instanceof AEnvironmentAwareController) {
9595
return;
9696
}
97+
\OC::$server->getLogger()->error('beforeController');
9798

9899
$reflectionMethod = new \ReflectionMethod($controller, $methodName);
99100

lib/Migration/Version18000Date20230504205823.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt
5454
$table->addColumn('url', Types::STRING, [
5555
'length' => 4000,
5656
]);
57+
$table->addColumn('url_hash', Types::STRING, [
58+
'length' => 64,
59+
]);
5760
$table->addColumn('description', Types::STRING, [
5861
'length' => 4000,
5962
]);

0 commit comments

Comments
 (0)