diff --git a/lib/Controller/AvatarsController.php b/lib/Controller/AvatarsController.php index 431600bfb9..a3b8baedef 100644 --- a/lib/Controller/AvatarsController.php +++ b/lib/Controller/AvatarsController.php @@ -85,9 +85,13 @@ public function image(string $email): Response { } $imageData = $this->avatarService->getAvatarImage($email, $this->uid); + + if ($imageData === null) { + return $this->noAvatarFoundResponse(); + } [$avatar, $image] = $imageData; - if (is_null($imageData) || !$avatar->isExternal()) { + if (!$avatar->isExternal()) { // This could happen if the cache invalidated meanwhile return $this->noAvatarFoundResponse(); } diff --git a/lib/Controller/MessageApiController.php b/lib/Controller/MessageApiController.php index 475deca64f..063c486373 100644 --- a/lib/Controller/MessageApiController.php +++ b/lib/Controller/MessageApiController.php @@ -296,10 +296,14 @@ public function get(int $id): DataResponse { $json['rawUrl'] = $this->urlGenerator->linkToOCSRouteAbsolute('mail.messageApi.getRaw', ['id' => $id]); if (!$loadBody) { - return new DataResponse($json, Http::STATUS_PARTIAL_CONTENT); + /** @var DataResponse $response */ + $response = new DataResponse($json, Http::STATUS_PARTIAL_CONTENT); + return $response; } - return new DataResponse($json, Http::STATUS_OK); + /** @var DataResponse $response */ + $response = new DataResponse($json, Http::STATUS_OK); + return $response; } /** @@ -382,6 +386,10 @@ private function enrichDownloadUrl(int $id, array $attachment): array { #[NoAdminRequired] #[TrapError] public function getAttachment(int $id, string $attachmentId): DataResponse { + if ($this->userId === null) { + return new DataResponse('Account not found.', Http::STATUS_NOT_FOUND); + } + try { $message = $this->mailManager->getMessage($this->userId, $id); $mailbox = $this->mailManager->getMailbox($this->userId, $message->getMailboxId()); @@ -407,20 +415,24 @@ public function getAttachment(int $id, string $attachmentId): DataResponse { // Body party and embedded messages do not have a name if ($attachment->getName() === null) { - return new DataResponse([ + /** @var DataResponse $response */ + $response = new DataResponse([ 'name' => $attachmentId . '.eml', 'mime' => $attachment->getType(), 'size' => $attachment->getSize(), 'content' => $attachment->getContent() ]); + return $response; } - return new DataResponse([ + /** @var DataResponse $response */ + $response = new DataResponse([ 'name' => $attachment->getName(), 'mime' => $attachment->getType(), 'size' => $attachment->getSize(), 'content' => $attachment->getContent() ]); + return $response; } /** diff --git a/lib/Controller/MessagesController.php b/lib/Controller/MessagesController.php index c462c91f03..b58d8d3689 100755 --- a/lib/Controller/MessagesController.php +++ b/lib/Controller/MessagesController.php @@ -380,7 +380,7 @@ public function getThread(int $id): JSONResponse { return new JSONResponse([], Http::STATUS_NOT_FOUND); } - return new JSONResponse($this->mailManager->getThread($account, $message->getThreadRootId())); + return new JSONResponse($this->mailManager->getThread($account, (string)$message->getThreadRootId())); } /** @@ -589,7 +589,7 @@ public function export(int $id): Response { } return new AttachmentDownloadResponse( - $source, + $source ?? '', $message->getSubject() . '.eml', 'message/rfc822', ); @@ -721,7 +721,8 @@ public function downloadAttachment(int $id, ); // Body party and embedded messages do not have a name - if ($attachment->getName() === null) { + $attachmentName = $attachment->getName(); + if ($attachmentName === null) { return new AttachmentDownloadResponse( $attachment->getContent(), $this->l10n->t('Embedded message %s', [ @@ -732,7 +733,7 @@ public function downloadAttachment(int $id, } return new AttachmentDownloadResponse( $attachment->getContent(), - $attachment->getName(), + $attachmentName, $attachment->getType() ); } @@ -766,7 +767,7 @@ public function downloadAttachments(int $id): Response { $zip = new ZipResponse($this->request, 'attachments'); foreach ($attachments as $attachment) { - $fileName = $attachment->getName(); + $fileName = $attachment->getName() ?? ''; $fh = fopen('php://temp', 'r+'); if ($fh === false) { continue; @@ -800,6 +801,9 @@ public function saveAttachment(int $id, if ($this->currentUserId === null) { return new JSONResponse([], Http::STATUS_UNAUTHORIZED); } + if ($this->userFolder === null) { + return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR); + } try { $message = $this->mailManager->getMessage($this->currentUserId, $id); $mailbox = $this->mailManager->getMailbox($this->currentUserId, $message->getMailboxId()); @@ -831,7 +835,7 @@ public function saveAttachment(int $id, ]) . '.eml'; $fileParts = pathinfo($fileName); $fileName = $fileParts['filename']; - $fileExtension = $fileParts['extension']; + $fileExtension = $fileParts['extension'] ?? ''; $fullPath = "$targetPath/$fileName.$fileExtension"; $counter = 2; while ($this->userFolder->nodeExists($fullPath)) { @@ -995,7 +999,7 @@ public function smartReply(int $messageId):JSONResponse { return new JSONResponse([], Http::STATUS_FORBIDDEN); } try { - $replies = array_values($this->aiIntegrationService->getSmartReply($account, $mailbox, $message, $this->currentUserId)); + $replies = array_values($this->aiIntegrationService->getSmartReply($account, $mailbox, $message, $this->currentUserId) ?? []); } catch (ServiceException $e) { $this->logger->error('Smart reply failed: ' . $e->getMessage(), [ 'exception' => $e, diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index f5c32384c6..2073b9fc02 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -135,6 +135,17 @@ public function __construct( * @return TemplateResponse renders the index page */ public function index(): TemplateResponse { + if ($this->currentUserId === null) { + // This should not happen as the route requires authentication, + // but handle it defensively. + return new TemplateResponse($this->appName, 'index'); + } + + $user = $this->userSession->getUser(); + if ($user === null) { + return new TemplateResponse($this->appName, 'index'); + } + if (class_exists(LoadViewer::class)) { $this->dispatcher->dispatchTyped(new LoadViewer()); } @@ -216,7 +227,6 @@ public function index(): TemplateResponse { $passwordIsUnavailable, ); - $user = $this->userSession->getUser(); $response = new TemplateResponse($this->appName, 'index'); $this->initialStateService->provideInitialState('preferences', [ 'attachment-size-limit' => $this->config->getSystemValue('app.mail.attachment-size-limit', 0), @@ -456,14 +466,14 @@ public function filteredDraft(string $filter, int $mailboxId, int $draftId): Tem public function compose(string $uri): RedirectResponse { $parts = parse_url($uri); $params = []; - if (isset($parts['path'])) { + if (is_array($parts) && isset($parts['path'])) { $params['to'] = $parts['path']; } - if (isset($parts['query'])) { - $parts = explode('&', $parts['query']); - foreach ($parts as $part) { + if (is_array($parts) && isset($parts['query'])) { + $queryParts = explode('&', $parts['query']); + foreach ($queryParts as $part) { $pair = explode('=', $part, 2); - $params[strtolower($pair[0])] = urldecode($pair[1]); + $params[strtolower($pair[0])] = urldecode($pair[1] ?? ''); } } diff --git a/lib/Controller/SmimeCertificatesController.php b/lib/Controller/SmimeCertificatesController.php index a73c339246..e26fbe221a 100644 --- a/lib/Controller/SmimeCertificatesController.php +++ b/lib/Controller/SmimeCertificatesController.php @@ -105,7 +105,7 @@ public function create(): JsonResponse { } $certificateFile = new UploadedFile($attachedCertificate); - $certificateData = file_get_contents($certificateFile->getTempPath()); + $certificateData = file_get_contents($certificateFile->getTempPath() ?? ''); if ($certificateData === false) { return JsonResponse::fail( 'Could not read certificate file', @@ -116,7 +116,7 @@ public function create(): JsonResponse { $privateKeyData = null; if ($attachedPrivateKey !== null) { $privateKeyFile = new UploadedFile($attachedPrivateKey); - $privateKeyData = file_get_contents($privateKeyFile->getTempPath()); + $privateKeyData = file_get_contents($privateKeyFile->getTempPath() ?? ''); if ($privateKeyData === false) { return JsonResponse::fail( 'Could not read private key file', diff --git a/lib/Controller/ThreadController.php b/lib/Controller/ThreadController.php index 9265a5f62f..ef15ad530f 100755 --- a/lib/Controller/ThreadController.php +++ b/lib/Controller/ThreadController.php @@ -73,12 +73,16 @@ public function move(int $id, int $destMailboxId): JSONResponse { return new JSONResponse([], Http::STATUS_FORBIDDEN); } + $threadRootId = $message->getThreadRootId(); + if ($threadRootId === null) { + return new JSONResponse([], Http::STATUS_NOT_FOUND); + } $this->mailManager->moveThread( $srcAccount, $srcMailbox, $dstAccount, $dstMailbox, - $message->getThreadRootId() + $threadRootId ); return new JSONResponse(); @@ -149,14 +153,15 @@ public function summarize(int $id): JSONResponse { } catch (DoesNotExistException $e) { return new JSONResponse([], Http::STATUS_FORBIDDEN); } - if (empty($message->getThreadRootId())) { + if ($message->getThreadRootId() === null || $message->getThreadRootId() === '') { return new JSONResponse([], Http::STATUS_NOT_FOUND); } - $thread = $this->mailManager->getThread($account, $message->getThreadRootId()); + $threadRootId = $message->getThreadRootId(); + $thread = $this->mailManager->getThread($account, $threadRootId); try { $summary = $this->aiIntergrationsService->summarizeThread( $account, - $message->getThreadRootId(), + $threadRootId, $thread, $this->currentUserId, ); @@ -181,13 +186,14 @@ public function generateEventData(int $id): JSONResponse { } catch (DoesNotExistException $e) { return new JSONResponse([], Http::STATUS_FORBIDDEN); } - if (empty($message->getThreadRootId())) { + if ($message->getThreadRootId() === null || $message->getThreadRootId() === '') { return new JSONResponse([], Http::STATUS_NOT_FOUND); } - $thread = $this->mailManager->getThread($account, $message->getThreadRootId()); + $threadRootId = $message->getThreadRootId(); + $thread = $this->mailManager->getThread($account, $threadRootId); $data = $this->aiIntergrationsService->generateEventData( $account, - $message->getThreadRootId(), + $threadRootId, $thread, $this->currentUserId, ); @@ -214,10 +220,14 @@ public function delete(int $id): JSONResponse { return new JSONResponse([], Http::STATUS_FORBIDDEN); } + $threadRootId = $message->getThreadRootId(); + if ($threadRootId === null) { + return new JSONResponse([], Http::STATUS_NOT_FOUND); + } $this->mailManager->deleteThread( $account, $mailbox, - $message->getThreadRootId() + $threadRootId ); return new JSONResponse(); diff --git a/tests/psalm-baseline.xml b/tests/psalm-baseline.xml index 4fa8f01e01..9760277d70 100644 --- a/tests/psalm-baseline.xml +++ b/tests/psalm-baseline.xml @@ -18,154 +18,11 @@ _slicemap[$mailbox]]]> - - - - - - - - - - - - - - - - uid]]> - - - - - - - - currentUserId]]> - - - - - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - - - - - - - $attachmentId . '.eml', - 'mime' => $attachment->getType(), - 'size' => $attachment->getSize(), - 'content' => $attachment->getContent() - ])]]> - $attachment->getName(), - 'mime' => $attachment->getType(), - 'size' => $attachment->getSize(), - 'content' => $attachment->getContent() - ])]]> - - - |DataResponse]]> - |DataResponse]]> - - - userId]]> - userId]]> - userId]]> - - - - - getName()]]> - - getThreadRootId()]]> - - aiIntegrationService->getSmartReply($account, $mailbox, $message, $this->currentUserId)]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - currentUserId]]> - - - - - - - - - - - - currentUserId]]> - userManager->getDisplayName($this->currentUserId)]]> - - - - - - - - - - - - getTempPath()]]> - getTempPath()]]> - userId]]> - userId]]> - userId]]> - - - - - getThreadRootId()]]> - getThreadRootId()]]> - getThreadRootId()]]> - getThreadRootId()]]> - getThreadRootId()]]> - getThreadRootId()]]> - - - - - uid]]> - uid]]> - uid]]> - - getLabel() : '']]>