From c53eb4c7a695dc926969e477176a557eed2f4c41 Mon Sep 17 00:00:00 2001 From: Aldarien Date: Mon, 28 Nov 2022 22:56:21 -0300 Subject: [PATCH] Full implemantation --- NOTES.md | 10 +- README.md | 25 +- api/common/Controller/Attachments.php | 68 ++--- api/common/Controller/Base.php | 18 ++ api/common/Controller/Emails.php | 141 --------- api/common/Controller/Install.php | 28 -- api/common/Controller/Jobs.php | 45 +++ api/common/Controller/Mailboxes.php | 141 +++++---- api/common/Controller/Messages.php | 156 ++++------ api/common/Exception/Attachment/NotFound.php | 15 + api/common/Exception/Mailbox/EmptyMailbox.php | 7 +- api/common/Exception/Mailbox/Invalid.php | 4 +- api/common/Exception/Mailbox/Stateless.php | 15 + .../Exception/Message/NoAttachments.php | 15 + .../Exception/Request/MissingArgument.php | 14 + api/common/Implement/Repository.php | 12 +- api/common/Middleware/Auth.php | 1 + api/common/Middleware/CustomExceptions.php | 58 ++++ api/common/Service/Attachments.php | 289 +++++++++++------- api/common/Service/Emails.php | 158 ---------- api/common/Service/Install.php | 73 ----- api/common/Service/Jobs.php | 71 +++++ api/common/Service/Mailboxes.php | 194 ++++++------ api/common/Service/Messages.php | 168 +++++++++- api/common/Service/Remote/Attachments.php | 31 ++ api/common/Service/Remote/Base.php | 21 ++ api/common/Service/Remote/Mailboxes.php | 45 +++ api/common/Service/Remote/Messages.php | 67 ++++ api/resources/routes/01_emails.php | 12 - api/resources/routes/01_mailboxes.php | 11 +- api/resources/routes/02_attachments.php | 2 +- api/resources/routes/02_messages.php | 5 +- api/resources/routes/98_install.php | 4 - api/resources/routes/99_base.php | 4 + api/setup/middleware/02_cors.php | 2 +- api/setup/middleware/03_custom_exceptions.php | 2 + api/setup/middleware/97_auth.php | 2 +- api/setup/middleware/99_errors.php | 2 +- api/setup/setups/02_services.php | 41 +-- api/setup/setups/04_middlewares.php | 11 + api/src/Model/Attachment.php | 61 +++- api/src/Model/Job.php | 61 ++++ api/src/Model/Mailbox.php | 29 +- api/src/Model/Message.php | 7 +- api/src/Model/State/Attachment.php | 2 +- api/src/Repository/Attachment.php | 83 +++-- api/src/Repository/Job.php | 108 +++++++ api/src/Repository/Mailbox.php | 10 +- api/src/Repository/Message.php | 32 +- api/src/Repository/State/Attachment.php | 19 +- api/src/Repository/State/Message.php | 17 +- cli/common/Command/GrabAttachments.php | 7 +- ui/resources/views/emails/mailboxes.blade.php | 25 +- ui/resources/views/emails/messages.blade.php | 54 ++-- ui/resources/views/home.blade.php | 13 +- 55 files changed, 1505 insertions(+), 1011 deletions(-) create mode 100644 api/common/Controller/Base.php delete mode 100644 api/common/Controller/Emails.php delete mode 100644 api/common/Controller/Install.php create mode 100644 api/common/Controller/Jobs.php create mode 100644 api/common/Exception/Attachment/NotFound.php create mode 100644 api/common/Exception/Mailbox/Stateless.php create mode 100644 api/common/Exception/Message/NoAttachments.php create mode 100644 api/common/Exception/Request/MissingArgument.php create mode 100644 api/common/Middleware/CustomExceptions.php delete mode 100644 api/common/Service/Emails.php delete mode 100644 api/common/Service/Install.php create mode 100644 api/common/Service/Jobs.php create mode 100644 api/common/Service/Remote/Attachments.php create mode 100644 api/common/Service/Remote/Base.php create mode 100644 api/common/Service/Remote/Mailboxes.php create mode 100644 api/common/Service/Remote/Messages.php delete mode 100644 api/resources/routes/01_emails.php delete mode 100644 api/resources/routes/98_install.php create mode 100644 api/resources/routes/99_base.php create mode 100644 api/setup/middleware/03_custom_exceptions.php create mode 100644 api/setup/setups/04_middlewares.php create mode 100644 api/src/Model/Job.php create mode 100644 api/src/Repository/Job.php diff --git a/NOTES.md b/NOTES.md index 0ac9946..d20957b 100644 --- a/NOTES.md +++ b/NOTES.md @@ -3,7 +3,7 @@ * [x] Select which to *register* or unregister for watching. * [x] List *registered* `mailboxes`. * [x] List `messages` for selected `mailbox`. -* [ ] Schedule `attachments` downloads. +* [x] Schedule `attachments` downloads. * [ ] Download `attachments` (*encrypted* & *decrypted*). ## CLI @@ -12,12 +12,12 @@ ## API * [x] Grab all `mailboxes` from `Email Provider`, identifying those that are registered. -* [x] Register `mailboxes` into **[database]**. +* [x] Register `mailboxes` into **[database]** and grab latest `messages`. * [x] Grab new `messages` from `Email Provider` for selected `mailboxes` and store them in the `database`. * [x] Grab `messages` from **[database]** for selected `mailboxes`. -* [ ] Grab `attachments` from `Email Provider` for selected `messages`. -* [ ] Decrypt `attachments`. -* [ ] Register `messages` for `attachment` job. +* [x] Grab `attachments` from `Email Provider` for selected `messages`. +* [x] Register `messages` for `attachment` job. +* [x] Decrypt `attachments`. ## Workflow diff --git a/README.md b/README.md index ae99296..06f690c 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,8 @@ # Emails ProVM -## API - -| Concept | Model | Repository | Table | -|------------|--------------------|--------------------|--------------------| -| Message | | | | -| Attachment | | | | -| Mailbox | | | | - -## UI -+ [ ] Mailboxes - + [ ] Register -+ [ ] Messages -+ [ ] Attachments -## CLI -+ [ ] Mailboxes - + Scrub -+ [ ] Messages - + Grab Attachments -+ [ ] Attachments - + Decrypt \ No newline at end of file +## Description +Grab attachments from emails by inbox. +* Choose what mailboxes to watch. +* Select messages that you want to grab attachments from. +* Download (or view in browser) (decrypted) attachments from messages. diff --git a/api/common/Controller/Attachments.php b/api/common/Controller/Attachments.php index e7dd760..5b33445 100644 --- a/api/common/Controller/Attachments.php +++ b/api/common/Controller/Attachments.php @@ -1,11 +1,12 @@ getRealPath(); - if (isset($attachment['decrypted_attachment']) and $attachment['decrypted_attachment'] !== false) { - $attachment['decrypted_attachment'] = $attachment['decrypted_attachment']->getRealPath(); - } - return $attachment; - }, $service->fetchFullAttachments()); - return $this->withJson($response, compact('attachments')); - } - protected function fileToArray(\SplFileInfo $info): array - { - return [ - 'filename' => $info->getFilename(), - 'path' => $info->getPath(), - 'full_name' => $info->getRealPath(), - 'extension' => $info->getExtension() + $attachments = array_map(function(Attachment $attachment) { + return $attachment->toArray(); + },$service->getAll()); + $output = [ + 'total' => count($attachments), + 'attachments' => $attachments ]; - } - public function get(ServerRequestInterface $request, ResponseInterface $response, Service $service): ResponseInterface - { - $body = $request->getBody()->getContents(); - $json = json_decode($body); - if (!is_array($json)) { - $json = [$json]; - } - $output = ['input' => $json, 'attachments' => []]; - foreach ($json as $attachment) { - $output['attachments'] []= $this->fileToArray($service->getAttachment($json->attachment)); - } return $this->withJson($response, $output); } - public function decrypt(ServerRequestInterface $request, ResponseInterface $response, Service $service): ResponseInterface + public function grab(ServerRequestInterface $request, ResponseInterface $response, Service $service, \ProVM\Common\Service\Jobs $jobsService): ResponseInterface { - $body = $request->getBody()->getContents(); - $json = json_decode($body); - if (!is_array($json)) { - $json = [$json]; + $body = $request->getBody(); + $json = \Safe\json_decode($body->getContents()); + if (!isset($json->messages)) { + throw new MissingArgument('messages', 'array', 'message UIDs'); } - $output = ['input' => $json, 'attachments' => []]; - foreach ($json as $attachment) { - $output['attachments'] []= $this->fileToArray($service->removeEncryption($attachment)); + $output = [ + 'messages' => $json->messages, + 'total' => count($json->messages), + 'saved' => [ + 'attachments' => [], + 'total' => 0 + ] + ]; + foreach ($json->messages as $message_id) { + if (!$jobsService->isPending($message_id)) { + continue; + } + if ($service->grab($message_id)) { + $job = $jobsService->find($message_id); + $jobsService->execute($job->getId()); + $output['saved']['attachments'] []= $job->toArray(); + $output['saved']['total'] ++; + } } return $this->withJson($response, $output); } diff --git a/api/common/Controller/Base.php b/api/common/Controller/Base.php new file mode 100644 index 0000000..8611788 --- /dev/null +++ b/api/common/Controller/Base.php @@ -0,0 +1,18 @@ +withJson($response, [ + 'version' => '1.0.0' + ]); + } +} \ No newline at end of file diff --git a/api/common/Controller/Emails.php b/api/common/Controller/Emails.php deleted file mode 100644 index 5137841..0000000 --- a/api/common/Controller/Emails.php +++ /dev/null @@ -1,141 +0,0 @@ -setLogger($logger); - } - - protected LoggerInterface $logger; - public function getLogger(): LoggerInterface - { - return $this->logger; - } - public function setLogger(LoggerInterface $logger): Emails - { - $this->logger = $logger; - return $this; - } - - public function mailboxes(ServerRequestInterface $request, ResponseInterface $response, Service $service): ResponseInterface - { - $mailboxes = array_map(function(MailboxInterface $mailbox) { - return ['name' => $mailbox->getName(), 'message_count' => $mailbox->count()]; - }, array_values($service->getMailboxes()->getAll())); - return $this->withJson($response, compact('mailboxes')); - } - public function messages(ServerRequestInterface $request, ResponseInterface $response, Service $service, Mailbox $repository): ResponseInterface - { - $body = $request->getBody()->getContents(); - $json = \Safe\json_decode($body); - $mailbox = $repository->fetchById($json->mailbox); - $remote_mailbox = $service->getMailboxes()->get($mailbox->getName()); - $messages = $service->getMessages($remote_mailbox, $json->start ?? 0, $json->amount ?? null); - $mails = []; - foreach ($messages as $message) { - $mails []= [ - 'position' => $message->getPosition(), - 'uid' => $message->getUID(), - 'subject' => $message->getSubject(), - 'date' => $message->getDateTime()->format('Y-m-d H:i:s'), - 'from' => $message->getFrom(), - 'has_attachments' => $message->hasAttachments(), - 'valid_attachments' => $message->hasValidAttachments(), - 'downloaded_attachments' => $message->hasDownloadedAttachments() - ]; - } - return $this->withJson($response, [ - 'input' => $json, - 'total' => $mailbox->count(), - 'messages' => $mails - ]); - } - public function withAttachments(ServerRequestInterface $request, ResponseInterface $response, Service $service): ResponseInterface - { - $body = $request->getBody()->getContents(); - $json = \Safe\json_decode($body); - $mailbox = $service->getMailboxes()->get($json->mailbox); - $messages = $service->getValidMessages($mailbox, $json->start ?? 0, $json->amount ?? null); - $mails = []; - foreach ($messages as $message) { - $mails []= [ - 'position' => $message->getPosition(), - 'uid' => $message->getUID(), - 'subject' => $message->getSubject(), - 'date' => $message->getDateTime()->format('Y-m-d H:i:s'), - 'from' => $message->getFrom(), - 'has_attachments' => $message->hasAttachments(), - 'valid_attachments' => $message->hasValidAttachments(), - 'downloaded_attachments' => $message->hasDownloadedAttachments() - ]; - } - return $this->withJson($response, [ - 'input' => $json, - 'total' => $service->getMailboxes()->countValid($mailbox), - 'messages' => $mails - ]); - } - public function attachments(ServerRequestInterface $request, ResponseInterface $response, Service $service): ResponseInterface - { - $body = $request->getBody()->getContents(); - $json = \Safe\json_decode($body); - $mailbox = $service->getMailboxes()->get($json->mailbox); - $messages = $service->getMessages($mailbox, $json->start ?? 0, $json->amount ?? null); - $cnt = 0; - $attachments = []; - foreach ($messages as $message) { - $attachments = array_merge($attachments, $service->saveAttachments($message, $json->extension ?? null)); - $cnt ++; - } - return $this->withJson($response, [ - 'input' => $json, - 'messages_count' => $cnt, - 'attachments_count' => count($attachments), - 'attachments' => $attachments - ]); - } - public function attachment(ServerRequestInterface $request, ResponseInterface $response, Service $service): ResponseInterface - { - $body = $request->getBody()->getContents(); - $json = \Safe\json_decode($body); - $mailbox = $service->getMailbox($json->mailbox); - $cnt = 0; - $attachments = []; - $errors = []; - foreach ($json->messages as $uid) { - $message = $service->getMessage($mailbox, $uid); - try { - $attachments = array_merge($attachments, $service->saveAttachments($message, $json->extension ?? null)); - } catch (FilesystemException $e) { - $errors []= [ - 'message' => $uid, - 'error' => $e->getMessage() - ]; - } - $cnt ++; - } - $output = [ - 'input' => $json, - 'messages_count' => $cnt, - 'attachments_count' => count($attachments), - 'attachments' => $attachments - ]; - if (count($errors) > 0) { - $output['errors'] = $errors; - } - return $this->withJson($response, $output); - } -} diff --git a/api/common/Controller/Install.php b/api/common/Controller/Install.php deleted file mode 100644 index 238838b..0000000 --- a/api/common/Controller/Install.php +++ /dev/null @@ -1,28 +0,0 @@ -run(); - return $this->withJson($response, [ - 'message' => 'Install finished' - ]); - } catch (Exception $e) { - return $this->withJson($response, [ - 'message' => 'Install with error', - 'error' => $e->getMessage() - ]); - } - } -} \ No newline at end of file diff --git a/api/common/Controller/Jobs.php b/api/common/Controller/Jobs.php new file mode 100644 index 0000000..d0eca33 --- /dev/null +++ b/api/common/Controller/Jobs.php @@ -0,0 +1,45 @@ +getBody(); + $json = \Safe\json_decode($body->getContents()); + if (!isset($json->messages)) { + throw new MissingArgument('messages', 'array', 'messages ids'); + } + $output = [ + 'messages' => $json->messages, + 'total' => count($json->messages), + 'scheduled' => 0 + ]; + foreach ($json->messages as $message_id) { + if ($jobsService->schedule($message_id)) { + $output['scheduled'] ++; + } + } + return $this->withJson($response, $output); + } + public function pending(ServerRequestInterface $request, ResponseInterface $response, Service $jobsService): ResponseInterface + { + $pending = array_map(function(Job $job) { + return $job->toArray(); + }, $jobsService->getPending()); + $output = [ + 'total' => count($pending), + 'pending' => $pending + ]; + return $this->withJson($response, $output); + } +} \ No newline at end of file diff --git a/api/common/Controller/Mailboxes.php b/api/common/Controller/Mailboxes.php index d29dc26..97fb792 100644 --- a/api/common/Controller/Mailboxes.php +++ b/api/common/Controller/Mailboxes.php @@ -1,83 +1,100 @@ getAll(); - $mailboxes = []; - foreach ($source_mailboxes as $mailbox) { - $m = [ - 'name' => $mailbox->getName(), - 'registered' => false - ]; - try { - $s = $repository->fetchByName($mailbox->getName()); - $m['registered'] = true; - $m['id'] = $s->getId(); - } catch (BlankResult $e) { + $mailboxes = array_values(array_map(function(MailboxInterface $mailbox) use ($service) { + $arr = ['name' => $mailbox->getName(), 'registered' => false]; + if ($service->isRegistered($mailbox->getName())) { + $mb = $service->getLocalMailbox($mailbox->getName()); + $arr['id'] = $mb->getId(); + $arr['registered'] = true; } - $mailboxes []= $m; - } - return $this->withJson($response, compact('mailboxes')); - } - public function register(ServerRequestInterface $request, ResponseInterface $response, \ProVM\Common\Service\Mailboxes $service, Mailbox $repository): ResponseInterface - { - $body = $request->getBody(); - $json = \Safe\json_decode($body->getContents()); - $output = ['input' => $json, 'mailboxes' => []]; - foreach ($json->mailboxes as $mailbox) { - try { - $model = $repository->create([ - 'name' => $mailbox - ]); - $output['mailboxes'] []= ['name' => $mailbox, 'created' => true, 'id' => $model->getId()]; - } catch (\PDOException $e) { - $output['mailboxes'] []= ['name' => $mailbox, 'created' => false]; - } - } + return $arr; + }, $service->getAll())); + $output = [ + 'total' => count($mailboxes), + 'mailboxes' => $mailboxes + ]; return $this->withJson($response, $output); } - public function unregister(ServerRequestInterface $request, ResponseInterface $response, Mailbox $repository): ResponseInterface + public function registered(ServerRequestInterface $request, ResponseInterface $response, Service $service): ResponseInterface { - $body = $request->getBody(); - $json = \Safe\json_decode($body->getContents()); - $output = ['input' => $json, 'mailboxes' => []]; - foreach ($json->mailboxes as $id) { - try { - $mailbox = $repository->fetchById($id); - try { - $repository->delete($mailbox); - $output['mailboxes'] []= ['name' => $mailbox->getName(), 'id' => $id, 'removed' => true]; - } catch (\PDOException $e) { - $output['mailboxes'] []= ['name' => $mailbox->getName(), 'id' => $id, 'removed' => false]; - } - } catch (\PDOException | BlankResult $e) { - $output['mailboxes'] []= ['id' => $id, 'removed' => false]; - } - } - return $this->withJson($response, $output); - } - public function registered(ServerRequestInterface $request, ResponseInterface $response, Mailbox $repository): ResponseInterface - { - $data = $repository->fetchAll(); - $mailboxes = array_map(function(\ProVM\Emails\Model\Mailbox $mailbox) { + $mailboxes = array_map(function(Mailbox $mailbox) { return $mailbox->toArray(); - }, $data); - return $this->withJson($response, compact('mailboxes')); + }, $service->getRegistered()); + $output = [ + 'total' => count($mailboxes), + 'mailboxes' => $mailboxes + ]; + return $this->withJson($response, $output); } - public function get(ServerRequestInterface $request, ResponseInterface $response, Mailbox $repository, int $mailbox_id): ResponseInterface + public function get(ServerRequestInterface $request, ResponseInterface $response, Service $service, int $mailbox_id): ResponseInterface { - $mailbox = $repository->fetchById($mailbox_id); + $mailbox = $service->getRepository()->fetchById($mailbox_id); return $this->withJson($response, ['mailbox' => $mailbox->toArray()]); } + public function register(ServerRequestInterface $request, ResponseInterface $response, Service $service, \ProVM\Common\Service\Messages $messagesService): ResponseInterface + { + $body = $request->getBody(); + $json = \Safe\json_decode($body->getContents()); + if (!isset($json->mailboxes)) { + throw new MissingArgument('mailboxes', 'array', 'mailboxes names'); + } + $output = [ + 'mailboxes' => $json->mailboxes, + 'total' => count($json->mailboxes), + 'registered' => [ + 'total' => 0, + 'mailboxes' => [] + ] + ]; + foreach ($json->mailboxes as $mailbox_name) { + $arr = [ + 'id' => '', + 'name' => $mailbox_name, + 'registered' => false + ]; + if ($service->register($mailbox_name)) { + $mailbox = $service->getLocalMailbox($mailbox_name); + $arr['id'] = $mailbox->getId(); + $arr['registered'] = true; + $output['registered']['total'] ++; + $output['registered']['mailboxes'] []= $arr; + $messagesService->grab($mailbox_name); + } + } + return $this->withJson($response, $output, 201); + } + public function unregister(ServerRequestInterface $request, ResponseInterface $response, Service $service): ResponseInterface + { + $body = $request->getBody(); + $json = \Safe\json_decode($body->getContents()); + if (!isset($json->mailboxes)) { + throw new MissingArgument('mailboxes', 'array', 'mailboxes names'); + } + $output = [ + 'mailboxes' => $json->mailboxes, + 'total' => count($json->mailboxes), + 'unregistered' => 0 + ]; + foreach ($json->mailboxes as $mailbox_name) { + if ($service->unregister($mailbox_name)) { + $output['unregistered'] ++; + } + } + return $this->withJson($response, $output); + } } \ No newline at end of file diff --git a/api/common/Controller/Messages.php b/api/common/Controller/Messages.php index f4e2507..47425ef 100644 --- a/api/common/Controller/Messages.php +++ b/api/common/Controller/Messages.php @@ -1,117 +1,73 @@ getBody(); - $json = \Safe\json_decode($body->getContents()); - $messages = []; - foreach ($json->mailboxes as $mailbox_id) { - $messages = array_merge($messages, $repository->fetchByMailbox($mailbox_id) ?? []); - } - $messages = array_map(function(\ProVM\Emails\Model\Message $message) { + $mailbox = $service->getMailboxes()->get($mailbox_id); + $messages = array_map(function(Message $message) { return $message->toArray(); - }, $messages); - return $this->withJson($response, ['messages' => $messages, 'total' => count($messages)]); - } - public function valid(ServerRequestInterface $request, ResponseInterface $response, Message $repository): ResponseInterface - { - $body = $request->getBody(); - $json = \Safe\json_decode($body->getContents()); - $messages = []; - foreach ($json->mailboxes as $mailbox_id) { - try { - $messages = array_merge($messages, $repository->fetchByMailbox($mailbox_id) ?? []); - } catch (BlankResult $e) { - } - } - $messages = array_values(array_map(function(\ProVM\Emails\Model\Message $message) { - return $message->toArray(); - }, array_filter($messages, function(\ProVM\Emails\Model\Message $message) { - return $message->hasValidAttachments(); - }))); - return $this->withJson($response, ['messages' => $messages, 'total' => count($messages)]); - } - public function grab(ServerRequestInterface $request, ResponseInterface $response, Model $factory, Service $service): ResponseInterface - { - $body = $request->getBody(); - $json = \Safe\json_decode($body->getContents()); - $message_count = 0; - foreach ($json->mailboxes as $mailbox_name) { - $message_count += $this->grabFromMailbox($factory, $service, $mailbox_name); - } - return $this->withJson($response, compact('message_count')); - } - - protected function grabFromMailbox(Model $factory, Service $service, string $mailbox_name): int - { - $mailbox = $factory->find(\ProVM\Emails\Model\Mailbox::class)->fetchByName($mailbox_name); - $stored = array_reduce($mailbox->getStates(), function($count, Mailbox $state) { - return $count + $state->getCount(); - }) ?? 0; - $remote_mailbox = $service->get($mailbox->getName()); - $total = $remote_mailbox->count(); - if ($stored >= $total) { - return 0; - } - $added = 0; - $uids = []; - $amount = $total - $stored; - $messages = $service->getMessages($remote_mailbox, $stored, $amount); - foreach ($messages as $j => $m) { - if ($this->addMessage($factory->find(\ProVM\Emails\Model\Message::class), $service, $mailbox, $m, $j + 1)) { - $uids []= $m->getId(); - $added ++; - } - } - if ($added > 0 ) { - $data = [ - 'mailbox_id' => $mailbox->getId(), - 'date_time' => (new DateTimeImmutable('now'))->format('Y-m-d H:i:s'), - 'count' => $added, - 'uids' => serialize($uids) - ]; - $state = $factory->find(\ProVM\Emails\Model\State\Mailbox::class)->create($data); - $factory->find(\ProVM\Emails\Model\State\Mailbox::class)->save($state); - } - return $added; - } - protected function addMessage(Message $repository, Service $service, \ProVM\Emails\Model\Mailbox $mailbox, MessageInterface $remote_message, int $position): bool - { - $data = [ - 'mailbox_id' => $mailbox->getId(), - 'position' => $position, - 'uid' => $remote_message->getId(), - 'subject' => $remote_message->getSubject() ?? '', - 'from' => $remote_message->getFrom()->getFullAddress(), - 'date_time' => $remote_message->getDate()->format('Y-m-d H:i:s') + }, $service->getAll($mailbox->getName())); + $output = [ + 'mailbox' => $mailbox->toArray(), + 'total' => count($messages), + 'messages' => $messages ]; - $message = $repository->create($data); - if ($message->getId() === 0) { - if ($remote_message->hasAttachments()) { - $message->doesHaveAttachments(); - } - if ($service->validAttachments($remote_message)) { - $message->doesHaveValidAttachments(); - } - $repository->save($message); - return true; + return $this->withJson($response, $output); + } + public function valid(ServerRequestInterface $request, ResponseInterface $response, Service $service, \ProVM\Common\Service\Attachments $attachments, int $mailbox_id): ResponseInterface + { + $mailbox = $service->getMailboxes()->get($mailbox_id); + $messages = array_values(array_filter(array_map(function(Message $message) use ($service, $attachments) { + return $message->toArray(); + }, $service->getValid($mailbox->getName())), function($message) { + return $message !== null; + })); + $output = [ + 'mailbox' => $mailbox->toArray(), + 'total' => count($messages), + 'messages' => $messages + ]; + return $this->withJson($response, $output); + } + public function get(ServerRequestInterface $request, ResponseInterface $response, Service $service, int $message_id): ResponseInterface + { + $message = $service->getRepository()->fetchById($message_id); + return $this->withJson($response, ['message' => $message->toArray()]); + } + public function grab(ServerRequestInterface $request, ResponseInterface $response, Service $service, \ProVM\Common\Service\Attachments $attachmentsService): ResponseInterface + { + $body = $request->getBody(); + $json = \Safe\json_decode($body->getContents()); + if (!isset($json->mailboxes)) { + throw new MissingArgument('mailboxes', 'array', 'mailboxes names'); } - return false; + $output = [ + 'mailboxes' => $json->mailboxes, + 'messages' => [], + 'message_count' => 0 + ]; + foreach ($json->mailboxes as $mailbox_name) { + $messages = $service->grab($mailbox_name); + foreach ($messages as $message) { + if ($message->hasValidAttachments()) { + $attachmentsService->create($message); + } + } + $output['messages'] = array_merge($output['messages'], $messages); + $output['message_count'] += count($messages); + } + return $this->withJson($response, $output); } } \ No newline at end of file diff --git a/api/common/Exception/Attachment/NotFound.php b/api/common/Exception/Attachment/NotFound.php new file mode 100644 index 0000000..626a9f8 --- /dev/null +++ b/api/common/Exception/Attachment/NotFound.php @@ -0,0 +1,15 @@ +getId()}"; + $code = 120; + parent::__construct($message, $code, $previous); + } +} \ No newline at end of file diff --git a/api/common/Exception/Mailbox/EmptyMailbox.php b/api/common/Exception/Mailbox/EmptyMailbox.php index c5f1de1..50085fd 100644 --- a/api/common/Exception/Mailbox/EmptyMailbox.php +++ b/api/common/Exception/Mailbox/EmptyMailbox.php @@ -1,14 +1,15 @@ getName()}"; + $code = 101; parent::__construct($message, $code, $previous); } } \ No newline at end of file diff --git a/api/common/Exception/Mailbox/Invalid.php b/api/common/Exception/Mailbox/Invalid.php index 998a105..075ca3c 100644 --- a/api/common/Exception/Mailbox/Invalid.php +++ b/api/common/Exception/Mailbox/Invalid.php @@ -5,9 +5,9 @@ use Exception; class Invalid extends Exception { - public function __construct(?Throwable $previous = null) + public function __construct(string $mailbox_name, ?Throwable $previous = null) { - $message = "Mailbox not found"; + $message = "Mailbox {$mailbox_name} not found"; $code = 100; parent::__construct($message, $code, $previous); } diff --git a/api/common/Exception/Mailbox/Stateless.php b/api/common/Exception/Mailbox/Stateless.php new file mode 100644 index 0000000..ab18626 --- /dev/null +++ b/api/common/Exception/Mailbox/Stateless.php @@ -0,0 +1,15 @@ +getName()} has not loaded any emails."; + $code = 102; + parent::__construct($message, $code, $previous); + } +} \ No newline at end of file diff --git a/api/common/Exception/Message/NoAttachments.php b/api/common/Exception/Message/NoAttachments.php new file mode 100644 index 0000000..e36ef88 --- /dev/null +++ b/api/common/Exception/Message/NoAttachments.php @@ -0,0 +1,15 @@ +getSubject()} has no attachments"; + $code = 110; + parent::__construct($message, $code, $previous); + } +} \ No newline at end of file diff --git a/api/common/Exception/Request/MissingArgument.php b/api/common/Exception/Request/MissingArgument.php new file mode 100644 index 0000000..5466e0e --- /dev/null +++ b/api/common/Exception/Request/MissingArgument.php @@ -0,0 +1,14 @@ +valuesForUpdate($old); $columns = []; $values = []; - foreach ($this->fieldsForUpdate() as $column => $method) { - if (isset($model_values[$column]) and $old_values[$column] !== $model_values[$column]) { - $columns []= "`{$column}`"; - $values []= $model_values[$column]; + foreach ($this->fieldsForUpdate() as $i => $column) { + if (isset($model_values[$i]) and $old_values[$i] !== $model_values[$i]) { + $columns []= "`{$column}` = ?"; + $values []= $model_values[$i]; } } if (count($columns) === 0) { @@ -101,8 +102,9 @@ abstract class Repository } catch (BlankResult $e) { $this->insert($model); $model->setId($this->getConnection()->lastInsertId()); - } catch (\Error | \Exception $e) { + } catch(PDOException $e) { $this->getLogger()->error($e); + throw $e; } } abstract public function load(array $row): ModelInterface; diff --git a/api/common/Middleware/Auth.php b/api/common/Middleware/Auth.php index 0ff78ed..f62c8e4 100644 --- a/api/common/Middleware/Auth.php +++ b/api/common/Middleware/Auth.php @@ -63,6 +63,7 @@ class Auth } } } + $this->getLogger()->debug(sha1($this->getAPIKey())); $response = $this->getResponseFactory()->createResponse(401); $response->getBody()->write(\Safe\json_encode(['error' => 401, 'message' => 'Incorrect token'])); return $response diff --git a/api/common/Middleware/CustomExceptions.php b/api/common/Middleware/CustomExceptions.php new file mode 100644 index 0000000..6bece26 --- /dev/null +++ b/api/common/Middleware/CustomExceptions.php @@ -0,0 +1,58 @@ +setResponseFactory($factory) + ->setLogger($logger); + } + + protected ResponseFactoryInterface $factory; + protected LoggerInterface $logger; + public function getResponseFactory(): ResponseFactoryInterface + { + return $this->factory; + } + public function getLogger(): LoggerInterface + { + return $this->logger; + } + public function setResponseFactory(ResponseFactoryInterface $factory): CustomExceptions + { + $this->factory = $factory; + return $this; + } + public function setLogger(LoggerInterface $logger): CustomExceptions + { + $this->logger = $logger; + return $this; + } + + public function __invoke(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + try { + return $handler->handle($request); + } catch (BlankResult $e) { + $this->getLogger()->error($e); + return $this->getResponseFactory()->createResponse(204); + } catch (JsonException $e) { + $this->getLogger()->error($e); + return $this->getResponseFactory()->createResponse(415, 'Only JSON Media Type is supported for this request'); + } catch (MissingArgument $e) { + $this->getLogger()->error($e); + return $this->getResponseFactory()->createResponse(400, $e->getMessage()); + } + } +} \ No newline at end of file diff --git a/api/common/Service/Attachments.php b/api/common/Service/Attachments.php index 3843819..0c2a6a0 100644 --- a/api/common/Service/Attachments.php +++ b/api/common/Service/Attachments.php @@ -3,37 +3,65 @@ namespace ProVM\Common\Service; use Ddeboer\Imap\Message\AttachmentInterface; use Ddeboer\Imap\MessageInterface; -use ProVM\Emails\Model\Attachment; +use ProVM\Common\Exception\Message\NoAttachments; use ProVM\Emails\Model\Message; +use ProVM\Emails\Repository\Attachment; +use Psr\Log\LoggerInterface; +use Safe\Exceptions\FilesystemException; -class Attachments +class Attachments extends Base { - public function __construct(Decrypt $decrypt, \ProVM\Emails\Repository\Attachment $repository, string $attachments_folder) + public function __construct(Messages $messages, Attachment $repository, Remote\Attachments $remoteService, + Decrypt $decrypt, string $attachments_folder, LoggerInterface $logger) { - $this->setDecrypt($decrypt); - $this->setAttachmentsFolder($attachments_folder); + $this->setMessages($messages) + ->setRepository($repository) + ->setRemoteService($remoteService) + ->setDecrypt($decrypt) + ->setFolder($attachments_folder) + ->setLogger($logger); } - protected string $attachments_folder; + protected Messages $messages; + protected Attachment $repository; + protected Remote\Attachments $remoteService; protected Decrypt $decrypt; - protected \ProVM\Emails\Repository\Attachment $repository; + protected string $folder; - public function getAttachmentsFolder(): string + public function getMessages(): Messages { - return $this->attachments_folder; + return $this->messages; + } + public function getRepository(): Attachment + { + return $this->repository; + } + public function getRemoteService(): Remote\Attachments + { + return $this->remoteService; } public function getDecrypt(): Decrypt { return $this->decrypt; } - public function getRepository(): \ProVM\Emails\Repository\Attachment + public function getFolder(): string { - return $this->repository; + return $this->folder; } - public function setAttachmentsFolder(string $folder): Attachments + public function setMessages(Messages $messages): Attachments { - $this->attachments_folder = $folder; + $this->messages = $messages; + return $this; + } + public function setRepository(Attachment $repository): Attachments + { + $this->repository = $repository; + return $this; + } + public function setRemoteService(Remote\Attachments $service): Attachments + { + $this->remoteService = $service; return $this; } public function setDecrypt(Decrypt $decrypt): Attachments @@ -41,119 +69,164 @@ class Attachments $this->decrypt = $decrypt; return $this; } - public function setRepository(\ProVM\Emails\Repository\Attachment $repository): Attachments + public function setFolder(string $folder): Attachments { - $this->repository = $repository; + $this->folder = $folder; return $this; } - public function validateAttachment(AttachmentInterface $remote_attachment): bool + public function getLocalAttachment(Message $message, string $relative_filename): \ProVM\Emails\Model\Attachment { - return str_contains($remote_attachment->getFilename(), '.pdf'); + return $this->getRepository()->fetchByMessageAndFilename($message->getId(), $relative_filename); + } + public function getRemoteAttachment(Message $message, string $relative_filename): AttachmentInterface + { + $remote_message = $this->getMessages()->getRemoteMessage($message->getUID()); + return $this->getRemoteService()->get($remote_message, $relative_filename); } - public function buildAttachment(Message $message, AttachmentInterface $remote_attachment): Attachment + public function getAll(): array + { + return $this->getRepository()->fetchAll(); + } + public function create(int $message_id): array + { + $message = $this->getMessages()->getRepository()->fetchById($message_id); + $remote_message = $this->getMessages()->getRemoteMessage($message->getUID()); + if (!$remote_message->hasAttachments()) { + throw new NoAttachments($remote_message); + } + $attachments = []; + foreach ($remote_message->getAttachments() as $attachment) { + if (!$this->getMessages()->validateAttachment($attachment)) { + continue; + } + if ($this->save($message, $attachment, false)) { + $attachments []= $attachment->getFilename(); + } + } + return $attachments; + } + public function grab(int $message_id): array + { + $message = $this->getMessages()->getRepository()->fetchById($message_id); + $remote_message = $this->getMessages()->getRemoteMessage($message->getUID()); + if (!$remote_message->hasAttachments()) { + throw new NoAttachments($remote_message); + } + $attachments = []; + foreach ($remote_message->getAttachments() as $attachment) { + if (!$this->getMessages()->validateAttachment($attachment)) { + continue; + } + if ($this->save($message, $attachment)) { + $attachments []= $attachment->getFilename(); + } + } + return $attachments; + } + public function save(Message $message, AttachmentInterface $remote_attachment, bool $upload = true): bool { $data = [ 'message_id' => $message->getId(), - 'filename' => $this->buildFilename($message, $remote_attachment) + 'filename' => $remote_attachment->getFilename() ]; - return $this->getRepository()->create($data); - } + try { + $attachment = $this->getRepository()->create($data); + $this->getRepository()->save($attachment); + if ($upload and $this->upload($attachment, $remote_attachment)) { + $attachment->itIsDownloaded(); + $message->doesHaveDownloadedAttachments(); + $this->getMessages()->getRepository()->save($message); - public function exists(Attachment $attachment): bool - { - $filename = $this->buildFilename($attachment); - return file_exists($filename); - } + if ($this->isFileEncrypted($attachment->getMessage(), $attachment->getFilename())) { + $attachment->itIsEncrypted(); - public function buildFilename(Message $message, AttachmentInterface $attachment): string - { - $filename = implode(' - ', [ - $message->getSubject(), - $message->getDateTime()->format('Y-m-d'), - $attachment->getFilename() - ]); - return implode(DIRECTORY_SEPARATOR, [ - $this->getAttachmentsFolder(), - $filename - ]); - } - - public function checkEncrypted(): array - { - $output = []; - foreach ($this->fetchAttachments() as $attachment) { - $output[$attachment->getFilename()] = $this->getDecrypt()->isEncrypted($attachment->getRealPath()); - } - return $output; - } - - public function fetchAttachments(): array - { - $files = new \FilesystemIterator($this->getAttachmentsFolder()); - $attachments = []; - foreach ($files as $file) { - if ($file->isDir()) { - continue; + if ($this->isFileDecrypted($attachment->getMessage(), $attachment->getFilename())) { + $attachment->itIsDecrypted(); + } else { + if ($this->decrypt($attachment->getMessage(), $attachment->getFilename())) { + $attachment->itIsDecrypted(); + } + } + } } - $attachments []= $file; - } - return $attachments; - } - public function fetchDecryptedAttachments(): array - { - $folder = implode(DIRECTORY_SEPARATOR, [$this->getAttachmentsFolder(), 'decrypted']); - $files = new \FilesystemIterator($folder); - $attachments = []; - foreach ($files as $file) { - if ($file->isDir()) { - continue; - } - $attachments []= $file; - } - return $attachments; - } - public function fetchFullAttachments(): array - { - $attachments = $this->fetchAttachments(); - $output = []; - foreach ($attachments as $attachment) { - $att = [ - 'original_attachment' => $attachment, - 'encrypted' => $this->getDecrypt()->isEncrypted($attachment->getRealPath()) - ]; - if ($att['encrypted']) { - $att['decrypted_attachment'] = $this->getDecryptedAttachment($attachment); - } - $output []= $att; - } - return $output; - } - - public function getAttachment(string $filename): \SplFileInfo - { - if (!str_contains($filename, $this->getAttachmentsFolder())) { - $filename = implode(DIRECTORY_SEPARATOR, [$this->getAttachmentsFolder(), $filename]); - } - return new \SplFileInfo($filename); - } - public function getDecryptedAttachment(\SplFileInfo $info): \SplFileInfo|bool - { - $new_file = implode(DIRECTORY_SEPARATOR, [$info->getPath(), 'decrypted', $info->getFilename()]); - if (!file_exists($new_file)) { + $this->getRepository()->save($attachment); + return true; + } catch (PDOException $e) { + $this->getLogger()->error($e); return false; } - return new \SplFileInfo($new_file); } - - public function removeEncryption(string $filename): \SplFileInfo + public function upload(\ProVM\Emails\Model\Attachment $attachment, AttachmentInterface $remote_attachment): bool { - $attachment = $this->getAttachment($filename); - $new_file = implode(DIRECTORY_SEPARATOR, [$attachment->getPath(), 'decrypted', $attachment->getFilename()]); - if ($this->getDecrypt()->runCommand($attachment->getRealPath(), $new_file)) { - return new \SplFileInfo($new_file); + $destination = implode(DIRECTORY_SEPARATOR, [ + $this->getFolder(), + $attachment->getFullFilename() + ]); + try { + \Safe\file_put_contents($destination, $remote_attachment->getDecodedContent()); + return true; + } catch (FilesystemException $e) { + $this->getLogger()->error($e); + return false; } - return $attachment; + } + public function isFileEncrypted(Message $message, string $relative_filename): bool + { + $attachment = $this->getLocalAttachment($message, $relative_filename); + $filename = implode(DIRECTORY_SEPARATOR, [ + $this->getFolder(), + $attachment->getFullFilename() + ]); + try { + return $this->getDecrypt()->isEncrypted($filename); + } catch (\InvalidArgumentException $e) { + return false; + } + } + public function isFileDecrypted(Message $message, string $relative_filename): bool + { + $attachment = $this->getLocalAttachment($message, $relative_filename); + $filename = implode(DIRECTORY_SEPARATOR, [ + $this->getFolder(), + 'decrypted', + $attachment->getFullFilename() + ]); + try { + return !$this->getDecrypt()->isEncrypted($filename); + } catch (\InvalidArgumentException $e) { + return false; + } + } + public function decrypt(Message $message, string $relative_filename): bool + { + $attachment = $this->getLocalAttachment($message, $relative_filename); + $source = implode(DIRECTORY_SEPARATOR, [ + $this->getFolder(), + $attachment->getFullFilename() + ]); + $destination = implode(DIRECTORY_SEPARATOR, [ + $this->getFolder(), + 'decrypted', + $attachment->getFullFilename() + ]); + return $this->getDecrypt()->runCommand($source, $destination); + } + public function isDownloaded(Message $message, MessageInterface $remote_message): bool + { + if (!$message->hasValidAttachments()) { + return false; + } + foreach ($remote_message->getAttachments() as $attachment) { + if (!str_contains($attachment->getFilename(), '.pdf')) { + continue; + } + $attachment = $this->getLocalAttachment($message, $attachment->getFilename()); + if (!$attachment->isDownloaded()) { + return false; + } + } + return true; } } \ No newline at end of file diff --git a/api/common/Service/Emails.php b/api/common/Service/Emails.php deleted file mode 100644 index 85a3c40..0000000 --- a/api/common/Service/Emails.php +++ /dev/null @@ -1,158 +0,0 @@ -setMailboxes($mailboxService); - $this->setMessageRepository($messageRepository); - $this->setAttachmentsFolder($attachments_folder); - } - - protected Message $messageRepository; - protected Mailboxes $mailboxService; - protected string $attachments_folder; - - public function getMailboxes(): Mailboxes - { - return $this->mailboxService; - } - public function getMessageRepository(): Message - { - return $this->messageRepository; - } - public function getAttachmentsFolder(): string - { - return $this->attachments_folder; - } - - public function setMailboxes(Mailboxes $mailboxService): Emails - { - $this->mailboxService = $mailboxService; - return $this; - } - public function setMessageRepository(Message $messageRepository): Emails - { - $this->messageRepository = $messageRepository; - return $this; - } - public function setAttachmentsFolder(string $folder): Emails - { - $this->attachments_folder = $folder; - return $this; - } - - //---------------------------------------------------------------- - // Messages - - public function getMessages(MailboxInterface $mailbox, int $start = 0, ?int $amount = null): array - { - $output = []; - $cnt = 0; - $messages = $this->getMessageRepository()->fetchByMailboxAndPosition($mailbox->getName(), $start, $amount ?? 1); - if ($messages) { - foreach ($messages as $message) { - $output []= $message; - $cnt ++; - } - } - if ($amount === null or $cnt < $amount) { - $messages = $this->getMailboxes()->getMessages($mailbox, $cnt + $start, $amount ?? $mailbox->count()); - foreach ($messages as $m) { - $message = $this->saveMessage($mailbox, $m, $cnt + $start); - $cnt ++; - if ($message === null) { - continue; - } - $output []= $message; - } - } - return $output; - } - public function saveMessage(MailboxInterface $mailbox, MessageInterface $message, int $position): ?\ProVM\Emails\Model\Message - { - $data = [ - 'mailbox_id' => $mailbox->getId(), - 'position' => $position, - 'uid' => $message->getNumber(), - 'subject' => $message->getSubject(), - 'from' => $message->getFrom()->getFullAddress(), - 'date_time' => $message->getDate()->format('Y-m-d H:i:s'), - ]; - return $this->getMessageRepository()->create($data); - } - public function getValidMessages(MailboxInterface $mailbox, int $start = 0, ?int $amount = null): array - { - $messages = $this->getMessages($mailbox, $start, $amount); - $output = array_filter($messages, function(\ProVM\Emails\Model\Message $message) { - return ($message->hasAttachments() and $message->hasValidAttachments()); - }); - if ($amount === null or count($output) >= $amount) { - return $output; - } - $cnt = $start + $amount; - while (count($output) < $amount) { - \Safe\ini_set('max_execution_time', ((int) \Safe\ini_get('max_execution_time')) + $amount * 5); - $messages = $this->getMailboxes()->getMessages($mailbox, $start + $amount, $amount); - foreach ($messages as $m) { - $message = $this->saveMessage($mailbox, $m, $cnt + $start); - $cnt ++; - if ($message === null) { - continue; - } - if ($message->hasAttachments() and $message->hasValidAttachments() and count($output) < $amount) { - $output []= $message; - } - } - } - return $output; - } - - // Attachments - - public function saveAttachments(MessageInterface $message, ?string $extension = null): array - { - if (!$message->hasAttachments()) { - return []; - } - $attachments = []; - foreach ($message->getAttachments() as $attachment) { - $this->getLogger()->debug($attachment->getFilename()); - if ($extension !== null) { - $extension = trim($extension, '.'); - if (!str_contains($attachment->getFilename(), ".{$extension}")) { - continue; - } - } - $filename = implode(DIRECTORY_SEPARATOR, [ - $this->getAttachmentsFolder(), - "{$message->getSubject()} - {$message->getDate()->format('Y-m-d')} - {$attachment->getFilename()}" - ]); - $this->getLogger()->debug($filename); - \Safe\file_put_contents($filename, $attachment->getDecodedContent()); - $attachments []= $filename; - } - return $attachments; - } - public function getAttachments(?string $mailbox = null, int $start = 0, ?int $amount = null, ?string $extension = null): array - { - if ($mailbox === null) { - $mailbox = '/'; - } - $mb = $this->getMailboxes()->get($mailbox); - $messages = $this->getMessages($mb, $start, $amount); - $attachments = []; - foreach ($messages as $message) { - $attachments = array_merge($attachments, $this->saveAttachments($message, $extension)); - } - return $attachments; - } -} \ No newline at end of file diff --git a/api/common/Service/Install.php b/api/common/Service/Install.php deleted file mode 100644 index 8a5bb02..0000000 --- a/api/common/Service/Install.php +++ /dev/null @@ -1,73 +0,0 @@ -setLogger($logger); - $this->setConnection($pdo); - } - - protected LoggerInterface $logger; - protected PDO $connection; - - public function getLogger(): LoggerInterface - { - return $this->logger; - } - public function getConnection(): PDO - { - return $this->connection; - } - public function setLogger(LoggerInterface $logger): Install - { - $this->logger = $logger; - return $this; - } - public function setConnection(PDO $pdo): Install - { - $this->connection = $pdo; - return $this; - } - - public function run(): void - { - $tables = [ - 'messages' => [ - '`mailbox_id` int UNSIGNED NOT NULL', - '`position` int UNSIGNED NOT NULL', - '`uid` int UNSIGNED NOT NULL PRIMARY KEY', - '`subject` varchar(255) NOT NULL', - '`from` varchar(100) NOT NULL', - '`date_time` datetime NOT NULL', - '`has_attachments` int(1) DEFAULT 0', - '`valid_attachments` int(1) DEFAULT 0', - '`downloaded_attachments` int(1) DEFAULT 0' - ], - 'attachments' => [ - '`message_uid` int UNSIGNED NOT NULL', - '`filename` varchar(255) NOT NULL PRIMARY KEY', - '`encrypted` int(1) DEFAULT 0', - '`decrypted` int(1) DEFAULT 0', - 'CONSTRAINT `message_uid_fk` FOREIGN KEY (`message_uid`) REFERENCES `messages` (`uid`)' - ], - 'mailboxes' => [ - '`' - ] - ]; - foreach ($tables as $table => $definitions) { - $this->getConnection()->query($this->buildCreateTable($table, $definitions)); - } - } - protected function buildCreateTable(string $table_name, array $columns): string - { - $query = ["CREATE TABLE IF NOT EXISTS `{$table_name}` ("]; - $query []= implode(',' . PHP_EOL, $columns); - $query []= ')'; - return implode(PHP_EOL, $query); - } -} \ No newline at end of file diff --git a/api/common/Service/Jobs.php b/api/common/Service/Jobs.php new file mode 100644 index 0000000..13ef3d8 --- /dev/null +++ b/api/common/Service/Jobs.php @@ -0,0 +1,71 @@ +setRepository($repository); + } + + protected Job $repository; + + public function getRepository(): Job + { + return $this->repository; + } + + public function setRepository(Job $repository): Jobs + { + $this->repository = $repository; + return $this; + } + + public function schedule(int $message_id): bool + { + $data = [ + 'message_id' => $message_id, + 'date_time' => (new DateTimeImmutable())->format('Y-m-d H:i:s') + ]; + try { + $job = $this->getRepository()->create($data); + $this->getRepository()->save($job); + return true; + } catch (PDOException $e) { + return false; + } + } + public function getPending(): array + { + return $this->getRepository()->fetchAllPending(); + } + public function isPending(int $message_id): bool + { + try { + $this->getRepository()->fetchPendingByMessage($message_id); + return true; + } catch (BlankResult $e) { + return false; + } + } + public function find(int $message_id): \ProVM\Emails\Model\Job + { + return $this->getRepository()->fetchPendingByMessage($message_id); + } + public function execute(int $job_id): bool + { + try { + $job = $this->getRepository()->fetchById($job_id); + $job->wasExecuted(); + $this->getRepository()->save($job); + return true; + } catch (PDOException $e) { + return false; + } + } +} \ No newline at end of file diff --git a/api/common/Service/Mailboxes.php b/api/common/Service/Mailboxes.php index 3e55e04..58b5bf7 100644 --- a/api/common/Service/Mailboxes.php +++ b/api/common/Service/Mailboxes.php @@ -1,151 +1,143 @@ setConnection($connection) - ->setAttachments($attachments) - ->setUsername($username); + $this->setRepository($repository) + ->setRemoteService($remoteService) + ->setStatesRepository($states) + ->setLogger($logger); } - protected ConnectionInterface $connection; - protected Attachments $attachments; - protected string $username; + protected Mailbox $repository; + protected Remote\Mailboxes $remoteService; + protected State\Mailbox $statesRepository; - public function getConnection(): ConnectionInterface + public function getRepository(): Mailbox { - return $this->connection; + return $this->repository; } - public function getAttachments(): Attachments + public function getRemoteService(): Remote\Mailboxes { - return $this->attachments; + return $this->remoteService; } - public function getUsername(): string + public function getStatesRepository(): State\Mailbox { - return $this->username; + return $this->statesRepository; } - public function setConnection(ConnectionInterface $connection): Mailboxes + public function setRepository(Mailbox $repository): Mailboxes { - $this->connection = $connection; + $this->repository = $repository; return $this; } - public function setAttachments(Attachments $attachments): Mailboxes + public function setRemoteService(Remote\Mailboxes $service): Mailboxes { - $this->attachments = $attachments; + $this->remoteService = $service; return $this; } - public function setUsername(string $username): Mailboxes + public function setStatesRepository(State\Mailbox $repository): Mailboxes { - $this->username = $username; + $this->statesRepository = $repository; return $this; } - protected array $mailboxes; + public function getLocalMailbox(string $mailbox_name): \ProVM\Emails\Model\Mailbox + { + return $this->getRepository()->fetchByName($mailbox_name); + } + public function getRemoteMailbox(string $mailbox_name): MailboxInterface + { + return $this->getRemoteService()->get($mailbox_name); + } + public function getAll(): array { - if (!isset($this->mailboxes)) { - $this->mailboxes = $this->getConnection()->getMailboxes(); - } - return $this->mailboxes; + return $this->getRemoteService()->getAll(); } - public function get(string $mailbox): MailboxInterface + public function getRegistered(): array { - if (!$this->getConnection()->hasMailbox($mailbox)) { - throw new Invalid(); - } - return $this->getConnection()->getMailbox($mailbox); + return $this->getRepository()->fetchAll(); + } + public function get(int $mailbox_id): \ProVM\Emails\Model\Mailbox + { + return $this->getRepository()->fetchById($mailbox_id); } - // Messages - - protected function advanceIterator(Iterator $iterator, int $up_to): Iterator + public function isRegistered(string $mailbox_name): bool { - for ($i = 0; $i < $up_to; $i ++) { - $iterator->next(); + try { + $mailbox = $this->getRepository()->fetchByName($mailbox_name); + return true; + } catch (BlankResult $e) { + return false; } - return $iterator; } - public function getMessages(MailboxInterface $mailbox, int $start = 0, ?int $amount = null): Generator + public function register(string $mailbox_name): bool { - if ($mailbox->count() === 0) { - $this->getLogger()->notice('No mails found.'); - throw new EmptyMailbox(); - } - $it = $mailbox->getIterator(); - if ($amount === null) { - $amount = $mailbox->count() - $start; - } - $it = $this->advanceIterator($it, $start); - for ($i = $start; $i < min($start + $amount, $mailbox->count()); $i ++) { - yield $it->key() => $it->current(); - $it->next(); + $remote_mailbox = $this->getRemoteMailbox($mailbox_name); + $name = $remote_mailbox->getName(); + $validity = $remote_mailbox->getStatus()->uidvalidity; + try { + $mailbox = $this->getRepository()->create(compact('name', 'validity')); + $this->getRepository()->save($mailbox); + return true; + } catch (PDOException $e) { + return false; } } - public function countValid(MailboxInterface $mailbox): int + public function updateState(\ProVM\Emails\Model\Mailbox $mailbox, array $messages, \DateTimeInterface $dateTime): bool { - $cnt = 0; - foreach ($mailbox->getIterator() as $message) { - if ($this->hasAttachments($message) and $this->validAttachments($message)) { - $cnt ++; - } + $data = [ + 'mailbox_id' => $mailbox->getId(), + 'date_time' => $dateTime->format('Y-m-d H:i:s'), + 'count' => count($messages), + 'uids' => serialize($messages) + ]; + try { + $state = $this->getStatesRepository()->create($data); + $this->getStatesRepository()->save($state); + return true; + } catch (PDOException $e) { + return false; } - return $cnt; } - /** - * @param MailboxInterface $mailbox - * @param int $uid - * @return MessageInterface - */ - public function getMessage(MailboxInterface $mailbox, int $uid): MessageInterface + public function unregister(string $mailbox_name): bool { - return $mailbox->getMessage($uid); + try { + $mailbox = $this->getRepository()->fetchByName($mailbox_name); + } catch (BlankResult $e) { + // It's already unregistered + return true; + } + try { + $this->getRepository()->delete($mailbox); + return true; + } catch (PDOException $e) { + return false; + } } + public function validate(string $mailbox_name): bool + { + $mailbox = $this->getLocalMailbox($mailbox_name); - protected function validateAddress(array $to): bool - { - foreach ($to as $address) { - if (strtolower($address->getAddress()) === strtolower($this->getUsername())) { - return true; - } - } - return false; - } - public function hasAttachments(MessageInterface $message): bool - { - return ($message->hasAttachments() and $this->validateAddress($message->getTo())); - } - protected function validateAttachment(AttachmentInterface $attachment): bool - { - return str_contains($attachment->getFilename(), '.pdf'); - } - public function validAttachments(MessageInterface $message): bool - { - foreach ($message->getAttachments() as $attachment) { - if ($this->validateAttachment($attachment)) { - return true; - } - } - return false; - } - public function downloadAttachments(MessageInterface $message) - { - foreach ($message->getAttachments() as $attachment) { - if ($this->validateAttachment($attachment)) { - $attachment->getContent(); - } + if (!$this->getRemoteService()->validate($mailbox_name, $mailbox->getValidity())) { + $remote_mailbox = $this->getRemoteMailbox($mailbox_name); + $mailbox->setValidity($remote_mailbox->getStatus()->uidvalidity); + $this->getRepository()->save($mailbox); + return false; } + return true; } } \ No newline at end of file diff --git a/api/common/Service/Messages.php b/api/common/Service/Messages.php index d1b9552..c5d0f05 100644 --- a/api/common/Service/Messages.php +++ b/api/common/Service/Messages.php @@ -1,37 +1,177 @@ setFactory($factory) + $this->setMailboxes($mailboxes) + ->setRepository($repository) + ->setRemoteService($remoteService) ->setLogger($logger); } - protected Model $factory; - protected LoggerInterface $logger; + protected Mailboxes $mailboxes; + protected Message $repository; + protected Remote\Messages $remoteService; - public function getFactory(): Model + public function getMailboxes(): Mailboxes { - return $this->factory; + return $this->mailboxes; } - public function getLogger(): LoggerInterface + public function getRepository(): Message { - return $this->logger; + return $this->repository; + } + public function getRemoteService(): Remote\Messages + { + return $this->remoteService; } - public function setFactory(Model $factory): Messages + public function setMailboxes(Mailboxes $mailboxes): Messages { - $this->factory = $factory; + $this->mailboxes = $mailboxes; return $this; } - public function setLogger(LoggerInterface $logger): Messages + public function setRepository(Message $repository): Messages { - $this->logger = $logger; + $this->repository = $repository; return $this; } + public function setRemoteService(Remote\Messages $service): Messages + { + $this->remoteService = $service; + return $this; + } + + public function getLocalMessage(string $message_uid): \ProVM\Emails\Model\Message + { + return $this->getRepository()->fetchByUID($message_uid); + } + public function getRemoteMessage(string $message_uid): MessageInterface + { + $message = $this->getLocalMessage($message_uid); + $remote_mailbox = $this->getMailboxes()->getRemoteMailbox($message->getMailbox()->getName()); + return $this->getRemoteService()->get($remote_mailbox, $message->getSubject(), $message->getFrom(), $message->getDateTime()); + } + + public function getAll(string $mailbox_name): array + { + $mailbox = $this->getMailboxes()->getLocalMailbox($mailbox_name); + return $this->getRepository()->fetchByMailbox($mailbox->getId()); + } + public function getValid(string $mailbox_name): array + { + $mailbox = $this->getMailboxes()->getLocalMailbox($mailbox_name); + return $this->getRepository()->fetchValidByMailbox($mailbox->getId()); + } + public function grab(string $mailbox_name): array + { + $mailbox = $this->getMailboxes()->getLocalMailbox($mailbox_name); + if (!$this->getMailboxes()->validate($mailbox_name)) { + $this->restoreUIDs($mailbox_name); + } + try { + $start = $mailbox->lastPosition() + 1; + } catch (Stateless $e) { + $start = 0; + } + $remote_mailbox = $this->getMailboxes()->getRemoteMailbox($mailbox_name); + $total = $remote_mailbox->count(); + $total_amount = $total - $start; + $amount = min(100, $total_amount); + $messages = []; + for ($i = 0; $i < $total_amount; $i += $amount) { + if ($amount + $i > $total_amount) { + $amount = $total_amount - $i; + } + $remote_messages = $this->getRemoteService()->getAll($remote_mailbox, $start + $i, $amount); + foreach ($remote_messages as $p => $m) { + if ($this->save($mailbox, $m, $p)) { + $messages []= $m->getId(); + } + } + } + $this->getMailboxes()->updateState($mailbox, $messages, new DateTimeImmutable()); + return $messages; + } + public function restoreUIDs(string $mailbox_name): void + { + $mailbox = $this->getMailboxes()->getLocalMailbox($mailbox_name); + $remote_mailbox = $this->getMailboxes()->getRemoteMailbox($mailbox_name); + $total_amount = $mailbox->lastPosition(); + $amount = min(100, $total_amount); + for ($i = 0; $i < $total_amount; $i += $amount) { + if ($amount + $i > $total_amount) { + $amount = $total_amount - $i; + } + $remote_messages = $this->getRemoteService()->getAll($remote_mailbox, $i, $amount); + foreach ($remote_messages as $m) { + $this->update($mailbox, $m, $i); + } + } + } + public function save(Mailbox $mailbox, MessageInterface $remote_message, int $position): bool + { + $data = [ + 'mailbox_id' => $mailbox->getId(), + 'position' => $position, + 'uid' => $remote_message->getId(), + 'subject' => $remote_message->getSubject() ?? '', + 'from' => $remote_message->getFrom()->getAddress(), + 'date_time' => $remote_message->getDate()->format('Y-m-d H:i:s') + ]; + try { + $message = $this->getRepository()->create($data); + if ($message->getId() === 0) { + if ($remote_message->hasAttachments()) { + $message->doesHaveAttachments(); + } + if ($this->validAttachments($remote_message)) { + $message->doesHaveValidAttachments(); + } + } + $this->getRepository()->save($message); + return true; + } catch (PDOException $e) { + $this->getLogger()->error($e); + return false; + } + } + public function update(Mailbox $mailbox, MessageInterface $remote_message, int $position): bool + { + try { + $message = $this->getRepository()->fetchByMailboxSubjectFromAndDate($mailbox->getId(), $remote_message->getSubject(), $remote_message->getFrom()->getAddress(), $remote_message->getDate()); + $message->setUID($remote_message->getId()); + $message->setPosition($position); + $this->getRepository()->save($message); + return true; + } catch (PDOException $e) { + return false; + } + } + public function validateAttachment(AttachmentInterface $attachment): bool + { + return str_contains($attachment->getFilename(), '.pdf'); + } + public function validAttachments(MessageInterface $remote_message): bool + { + foreach ($remote_message->getAttachments() as $attachment) { + if ($this->validateAttachment($attachment)) { + return true; + } + } + return false; + } } \ No newline at end of file diff --git a/api/common/Service/Remote/Attachments.php b/api/common/Service/Remote/Attachments.php new file mode 100644 index 0000000..bfdb3ba --- /dev/null +++ b/api/common/Service/Remote/Attachments.php @@ -0,0 +1,31 @@ +setConnection($connection) + ->setLogger($logger); + } + + public function getAttachments(MessageInterface $message): array + { + return $message->getAttachments(); + } + public function get(MessageInterface $message, string $filename): AttachmentInterface + { + foreach ($message->getAttachments() as $attachment) { + if ($attachment->getFilename() === $filename) { + return $attachment; + } + } + throw new NotFound($message, $filename); + } +} \ No newline at end of file diff --git a/api/common/Service/Remote/Base.php b/api/common/Service/Remote/Base.php new file mode 100644 index 0000000..df2afa4 --- /dev/null +++ b/api/common/Service/Remote/Base.php @@ -0,0 +1,21 @@ +connection; + } + + public function setConnection(ConnectionInterface $connection): Base + { + $this->connection = $connection; + return $this; + } +} \ No newline at end of file diff --git a/api/common/Service/Remote/Mailboxes.php b/api/common/Service/Remote/Mailboxes.php new file mode 100644 index 0000000..0a83a56 --- /dev/null +++ b/api/common/Service/Remote/Mailboxes.php @@ -0,0 +1,45 @@ +setConnection($connection) + ->setLogger($logger); + } + + protected array $mailboxes; + public function getAll(): array + { + if (!isset($this->mailboxes)) { + $this->mailboxes = $this->getConnection()->getMailboxes(); + } + return $this->mailboxes; + } + + /** + * @throws Invalid + */ + public function get(string $mailbox_name): MailboxInterface + { + if (!$this->getConnection()->hasMailbox($mailbox_name)) { + throw new Invalid($mailbox_name); + } + return $this->getConnection()->getMailbox($mailbox_name); + } + + public function validate(string $mailbox_name, int $uidvalidity): bool + { + $mailbox = $this->get($mailbox_name); + return ($mailbox->getStatus()->uidvalidity === $uidvalidity); + } +} \ No newline at end of file diff --git a/api/common/Service/Remote/Messages.php b/api/common/Service/Remote/Messages.php new file mode 100644 index 0000000..2a252d0 --- /dev/null +++ b/api/common/Service/Remote/Messages.php @@ -0,0 +1,67 @@ +setConnection($connection) + ->setLogger($logger); + } + + public function getAll(MailboxInterface $mailbox, int $start = 0, ?int $amount = null): array + { + if ($mailbox->count() === 0) { + throw new EmptyMailbox($mailbox); + } + + if ($amount === null) { + $amount = $mailbox->count() - $start; + } + + $it = $mailbox->getIterator(); + for ($i = 0; $i < $start; $i ++) { + $it->next(); + } + + $messages = []; + for ($i = $start; $i < $start + $amount; $i ++) { + if (!$it->valid()) { + break; + } + $messages[$i] = $it->current(); + $it->next(); + } + return $messages; + } + public function get(MailboxInterface $mailbox, string $subject, string $from, DateTimeInterface $dateTime): MessageInterface + { + if ($mailbox->count() === 0) { + $this->getLogger()->notice("Mailbox {$mailbox->getName()} is empty"); + throw new EmptyMailbox($mailbox); + } + + $query = new SearchExpression(); + $query->addCondition(new Subject($subject)); + $query->addCondition(new From($from)); + $query->addCondition(new On($dateTime)); + + $result = $mailbox->getMessages($query); + if (count($result) === 0) { + throw new MessageDoesNotExistException("{$mailbox->getName()}: {$subject} - {$from} [{$dateTime->format('Y-m-d H:i:s')}]"); + } + return $result->current(); + } +} \ No newline at end of file diff --git a/api/resources/routes/01_emails.php b/api/resources/routes/01_emails.php deleted file mode 100644 index f86121a..0000000 --- a/api/resources/routes/01_emails.php +++ /dev/null @@ -1,12 +0,0 @@ -group('/emails', function($app) { - $app->get('/mailboxes', [Emails::class, 'mailboxes']); - $app->group('/messages', function($app) { - $app->post('/attachments[/]', [Emails::class, 'withAttachments']); - $app->post('[/]', [Emails::class, 'messages']); - }); - $app->post('/attachments', [Emails::class, 'attachments']); - $app->post('/attachment', [Emails::class, 'attachment']); -}); \ No newline at end of file diff --git a/api/resources/routes/01_mailboxes.php b/api/resources/routes/01_mailboxes.php index b39878e..c1b464d 100644 --- a/api/resources/routes/01_mailboxes.php +++ b/api/resources/routes/01_mailboxes.php @@ -1,13 +1,18 @@ group('/mailboxes', function($app) { - $app->post('/register', [Mailboxes::class, 'register']); - $app->delete('/unregister', [Mailboxes::class, 'unregister']); - $app->get('/registered', [Mailboxes::class, 'registered']); + $app->post('/register[/]', [Mailboxes::class, 'register']); + $app->delete('/unregister[/]', [Mailboxes::class, 'unregister']); + $app->get('/registered[/]', [Mailboxes::class, 'registered']); $app->get('[/]', Mailboxes::class); }); $app->group('/mailbox/{mailbox_id}', function($app) { + $app->group('/messages', function($app) { + $app->get('/valid[/]', [Messages::class, 'valid']); + $app->get('[/]', Messages::class); + }); $app->get('[/]', [Mailboxes::class, 'get']); }); diff --git a/api/resources/routes/02_attachments.php b/api/resources/routes/02_attachments.php index 5d68696..56c26c1 100644 --- a/api/resources/routes/02_attachments.php +++ b/api/resources/routes/02_attachments.php @@ -2,7 +2,7 @@ use ProVM\Common\Controller\Attachments; $app->group('/attachments', function($app) { - $app->post('/get', [Attachments::class, 'get']); + $app->put('/grab', [Attachments::class, 'grab']); $app->post('/decrypt', [Attachments::class, 'decrypt']); $app->get('[/]', Attachments::class); }); \ No newline at end of file diff --git a/api/resources/routes/02_messages.php b/api/resources/routes/02_messages.php index 1296b5a..9c3c07f 100644 --- a/api/resources/routes/02_messages.php +++ b/api/resources/routes/02_messages.php @@ -1,8 +1,9 @@ group('/messages', function($app) { - $app->post('/valid', [Messages::class, 'valid']); $app->put('/grab', [Messages::class, 'grab']); - $app->post('[/]', Messages::class); + $app->put('/schedule', [Jobs::class, 'schedule']); + $app->get('/pending', [Jobs::class, 'pending']); }); \ No newline at end of file diff --git a/api/resources/routes/98_install.php b/api/resources/routes/98_install.php deleted file mode 100644 index d446671..0000000 --- a/api/resources/routes/98_install.php +++ /dev/null @@ -1,4 +0,0 @@ -get('/install', Install::class); \ No newline at end of file diff --git a/api/resources/routes/99_base.php b/api/resources/routes/99_base.php new file mode 100644 index 0000000..022aa6f --- /dev/null +++ b/api/resources/routes/99_base.php @@ -0,0 +1,4 @@ +get('[/]', Base::class); \ No newline at end of file diff --git a/api/setup/middleware/02_cors.php b/api/setup/middleware/02_cors.php index 063dba6..12e122e 100644 --- a/api/setup/middleware/02_cors.php +++ b/api/setup/middleware/02_cors.php @@ -1,2 +1,2 @@ add($app->getContainer()->get(\ProVM\Common\Middleware\CORS::class)); +$app->add($app->getContainer()->get(ProVM\Common\Middleware\CORS::class)); diff --git a/api/setup/middleware/03_custom_exceptions.php b/api/setup/middleware/03_custom_exceptions.php new file mode 100644 index 0000000..9e38f2a --- /dev/null +++ b/api/setup/middleware/03_custom_exceptions.php @@ -0,0 +1,2 @@ +add($app->getContainer()->get(ProVM\Common\Middleware\CustomExceptions::class)); diff --git a/api/setup/middleware/97_auth.php b/api/setup/middleware/97_auth.php index 40efe2f..9e2f1be 100644 --- a/api/setup/middleware/97_auth.php +++ b/api/setup/middleware/97_auth.php @@ -1,2 +1,2 @@ add($app->getContainer()->get(\ProVM\Common\Middleware\Auth::class)); +$app->add($app->getContainer()->get(ProVM\Common\Middleware\Auth::class)); diff --git a/api/setup/middleware/99_errors.php b/api/setup/middleware/99_errors.php index c3f0b5f..29c59fa 100644 --- a/api/setup/middleware/99_errors.php +++ b/api/setup/middleware/99_errors.php @@ -1,2 +1,2 @@ add($app->getContainer()->get(\Zeuxisoo\Whoops\Slim\WhoopsMiddleware::class)); \ No newline at end of file +$app->add($app->getContainer()->get(Zeuxisoo\Whoops\Slim\WhoopsMiddleware::class)); diff --git a/api/setup/setups/02_services.php b/api/setup/setups/02_services.php index f2ea3ab..8fd0f0a 100644 --- a/api/setup/setups/02_services.php +++ b/api/setup/setups/02_services.php @@ -2,40 +2,21 @@ use Psr\Container\ContainerInterface; return [ - \ProVM\Common\Service\Mailboxes::class => function(ContainerInterface $container) { - return (new \ProVM\Common\Service\Mailboxes( - $container->get(\Ddeboer\Imap\ConnectionInterface::class), - $container->get(\ProVM\Common\Service\Attachments::class), - $container->get('emails')->username - ))->setLogger($container->get(\Psr\Log\LoggerInterface::class)); - }, - \ProVM\Common\Service\Emails::class => function(ContainerInterface $container) { - return (new \ProVM\Common\Service\Emails( - $container->get(\ProVM\Common\Service\Mailboxes::class), - $container->get(\ProVM\Emails\Repository\Message::class), - $container->get('attachments_folder') - ))->setLogger($container->get(\Psr\Log\LoggerInterface::class)); - }, - \ProVM\Common\Service\Attachments::class => function(ContainerInterface $container) { - return new \ProVM\Common\Service\Attachments( - $container->get(\ProVM\Common\Service\Decrypt::class), - $container->get(\ProVM\Emails\Repository\Attachment::class), - $container->get('attachments_folder') - ); - }, - \ProVM\Common\Service\Decrypt::class => function(ContainerInterface $container) { - return new \ProVM\Common\Service\Decrypt( - $container->get(\Psr\Log\LoggerInterface::class), + ProVM\Common\Service\Decrypt::class => function(ContainerInterface $container) { + return new ProVM\Common\Service\Decrypt( + $container->get(Psr\Log\LoggerInterface::class), $container->get('base_command'), $container->get('passwords') ); }, - \ProVM\Common\Service\Install::class => function(ContainerInterface $container) { - $database = $container->get('database'); - $pdo = new PDO("mysql:host={$database->host};dbname={$database->name}", $database->username, $database->password); - return new \ProVM\Common\Service\Install( - $container->get(\Psr\Log\LoggerInterface::class), - $pdo + ProVM\Common\Service\Attachments::class => function(ContainerInterface $container) { + return new ProVM\Common\Service\Attachments( + $container->get(ProVM\Common\Service\Messages::class), + $container->get(ProVM\Emails\Repository\Attachment::class), + $container->get(ProVM\Common\Service\Remote\Attachments::class), + $container->get(ProVM\Common\Service\Decrypt::class), + $container->get('attachments_folder'), + $container->get(Psr\Log\LoggerInterface::class) ); } ]; diff --git a/api/setup/setups/04_middlewares.php b/api/setup/setups/04_middlewares.php new file mode 100644 index 0000000..b94e9ec --- /dev/null +++ b/api/setup/setups/04_middlewares.php @@ -0,0 +1,11 @@ + function(ContainerInterface $container) { + return new ProVM\Common\Middleware\CustomExceptions( + $container->get(Nyholm\Psr7\Factory\Psr17Factory::class), + $container->get(Psr\Log\LoggerInterface::class) + ); + } +]; \ No newline at end of file diff --git a/api/src/Model/Attachment.php b/api/src/Model/Attachment.php index d6f562d..2fe5570 100644 --- a/api/src/Model/Attachment.php +++ b/api/src/Model/Attachment.php @@ -2,6 +2,7 @@ namespace ProVM\Emails\Model; use ProVM\Common\Define\Model; +use ProVM\Common\Exception\Database\BlankResult; class Attachment implements Model { @@ -53,13 +54,22 @@ class Attachment implements Model public function getStates(): array { if (!isset($this->states)) { - $this->setStates($this->getStateRepository()->fetchByAttachment($this->getId())); + try { + $this->setStates($this->getStateRepository()->fetchByAttachment($this->getId())); + } catch (BlankResult $e) { + return []; + } } return $this->states; } public function getState(string $name): State\Attachment { - return $this->getStates()[$name]; + try { + return $this->getStates()[$name]; + } catch (\Exception $e) { + $this->newState($name); + return $this->getStates()[$name]; + } } public function addState(State\Attachment $state): Attachment { @@ -76,19 +86,47 @@ class Attachment implements Model protected function newState(string $name): Attachment { $this->addState((new State\Attachment()) - ->setName($name) ->setAttachment($this) + ->setName($name) ); return $this; } + public function getFullFilename(): string + { + return implode(' - ', [ + $this->getMessage()->getSubject(), + $this->getMessage()->getDateTime()->format('Y-m-d His'), + $this->getFilename() + ]); + } + + public function isDownloaded(): bool + { + return $this->getState('downloaded')?->getValue() ?? false; + } public function isEncrypted(): bool { - return $this->getState('encrypted')->getValue() ?? false; + return $this->getState('encrypted')?->getValue() ?? false; } public function isDecrypted(): bool { - return $this->getState('encrypted')->getValue() ?? false; + try { + return $this->getState('decrypted')?->getValue() ?? false; + } catch (\Exception $e) { + $this->newState('decrypted'); + return $this->getState('decrypted')?->getValue() ?? false; + } + } + public function itIsDownloaded(): Attachment + { + try { + $this->getState('downloaded')->setValue(true); + } catch (\Exception $e) { + $this->newState('downloaded'); + $this->getState('downloaded')->setValue(true); + } + return $this; } public function itIsEncrypted(): Attachment { @@ -105,7 +143,7 @@ class Attachment implements Model try { $this->getState('decrypted')->setValue(true); } catch (\Exception $e) { - $this->newState('encrypted'); + $this->newState('decrypted'); $this->getState('decrypted')->setValue(true); } return $this; @@ -115,8 +153,17 @@ class Attachment implements Model { return [ 'id' => $this->getId(), - 'message' => $this->getMessage()->toArray(), + 'message' => [ + 'id' => $this->getMessage()->getId(), + 'mailbox' => $this->getMessage()->getMailbox()->toArray(), + 'position' => $this->getMessage()->getPosition(), + 'uid' => $this->getMessage()->getUID(), + 'subject' => $this->getMessage()->getSubject(), + 'from' => $this->getMessage()->getFrom(), + 'date_time' => $this->getMessage()->getDateTime()->format('Y-m-d H:i:s') + ], 'filename' => $this->getFilename(), + 'downloaded' => $this->isDownloaded(), 'encrypted' => $this->isEncrypted(), 'decrypted' => $this->isDecrypted() ]; diff --git a/api/src/Model/Job.php b/api/src/Model/Job.php new file mode 100644 index 0000000..421f79a --- /dev/null +++ b/api/src/Model/Job.php @@ -0,0 +1,61 @@ +id; + } + public function getMessage(): Message + { + return $this->message; + } + public function getDateTime(): DateTimeInterface + { + return $this->dateTime; + } + public function isExecuted(): bool + { + return $this->executed ?? false; + } + + public function setId(int $id): Job + { + $this->id = $id; + return $this; + } + public function setMessage(Message $message): Job + { + $this->message = $message; + return $this; + } + public function setDateTime(DateTimeInterface $dateTime): Job + { + $this->dateTime = $dateTime; + return $this; + } + public function wasExecuted(): Job + { + $this->executed = true; + return $this; + } + + public function toArray(): array + { + return [ + 'id' => $this->getId(), + 'message' => $this->getMessage()->toArray(), + 'date_time' => $this->getDateTime()->format('Y-m-d H:i:s'), + 'executed' => $this->isExecuted() + ]; + } +} \ No newline at end of file diff --git a/api/src/Model/Mailbox.php b/api/src/Model/Mailbox.php index 7b86bcf..0a4221a 100644 --- a/api/src/Model/Mailbox.php +++ b/api/src/Model/Mailbox.php @@ -4,12 +4,15 @@ namespace ProVM\Emails\Model; use DateTimeInterface; use ProVM\Common\Define\Model; use ProVM\Common\Exception\Database\BlankResult; +use ProVM\Common\Exception\EmptyMailbox; +use ProVM\Common\Exception\Mailbox\Stateless; use Safe\DateTimeImmutable; class Mailbox implements Model { protected int $id; protected string $name; + protected int $validity; public function getId(): int { @@ -19,6 +22,10 @@ class Mailbox implements Model { return $this->name; } + public function getValidity(): int + { + return $this->validity; + } public function setId(int $id): Mailbox { @@ -30,6 +37,11 @@ class Mailbox implements Model $this->name = $name; return $this; } + public function setValidity(int $uidvalidity): Mailbox + { + $this->validity = $uidvalidity; + return $this; + } protected \ProVM\Emails\Repository\State\Mailbox $stateRepository; @@ -68,19 +80,31 @@ class Mailbox implements Model return $this; } + public function lastState(): State\Mailbox + { + if (count($this->getStates()) === 0) { + throw new Stateless($this); + } + return $this->getStates()[array_key_last($this->getStates())]; + } public function lastChecked(): ?DateTimeInterface { if (count($this->getStates()) == 0) { return null; } - return $this->getStates()[array_key_last($this->getStates())]->getDateTime(); + return $this->lastState()->getDateTime(); } public function lastCount(): int { if (count($this->getStates()) == 0) { return 0; } - return $this->getStates()[array_key_last($this->getStates())]->getCount(); + return $this->lastState()->getCount(); + } + public function lastPosition(): int + { + $state = $this->lastState()->getUIDs(); + return array_key_last($state); } public function toArray(): array @@ -88,6 +112,7 @@ class Mailbox implements Model return [ 'id' => $this->getId(), 'name' => $this->getName(), + //'validity' => $this->getValidity(), 'last_checked' => [ 'date' => $this->lastChecked() ?? 'never', 'count' => $this->lastCount() ?? 0 diff --git a/api/src/Model/Message.php b/api/src/Model/Message.php index 2de4d68..a5f9af7 100644 --- a/api/src/Model/Message.php +++ b/api/src/Model/Message.php @@ -146,7 +146,7 @@ class Message implements Model public function doesHaveAttachments(): Message { - if (!isset($this->getStates()['has_attachments'])) { + if (!isset($this->getState()['has_attachments'])) { $this->newState('has_attachments'); } $this->getState('has_attachments')->setValue(true); @@ -206,7 +206,10 @@ class Message implements Model 'has_attachments' => $this->hasAttachments(), 'valid_attachments' => $this->hasValidAttachments(), 'downloaded_attachments' => $this->hasDownloadedAttachments() - ] + ], + 'attachments' => $this->hasValidAttachments() ? array_map(function(Attachment $attachment) { + return $attachment->toArray(); + }, $this->getAttachments()) : [] ]; } } \ No newline at end of file diff --git a/api/src/Model/State/Attachment.php b/api/src/Model/State/Attachment.php index 3838098..a50961b 100644 --- a/api/src/Model/State/Attachment.php +++ b/api/src/Model/State/Attachment.php @@ -24,7 +24,7 @@ class Attachment implements Model } public function getValue(): bool { - return $this->value; + return $this->value ?? false; } public function setId(int $id): Attachment diff --git a/api/src/Repository/Attachment.php b/api/src/Repository/Attachment.php index 59e0c06..568d4b6 100644 --- a/api/src/Repository/Attachment.php +++ b/api/src/Repository/Attachment.php @@ -2,64 +2,62 @@ namespace ProVM\Emails\Repository; use PDO; +use ProVM\Common\Exception\Database\BlankResult; use Psr\Log\LoggerInterface; use ProVM\Common\Define\Model as ModelInterface; +use ProVM\Common\Factory\Model; use ProVM\Common\Implement\Repository; class Attachment extends Repository { - public function __construct(PDO $connection, LoggerInterface $logger, State\Attachment $stateRepository) + public function __construct(PDO $connection, LoggerInterface $logger, Model $factory) { parent::__construct($connection, $logger); - $this->setStateRepository($stateRepository) + $this->setFactory($factory) ->setTable('attachments'); } - protected State\Attachment $stateRepository; - public function getStateRepository(): State\Attachment + protected Model $factory; + public function getFactory(): Model { - return $this->stateRepository; + return $this->factory; } - - public function setStateRepository(State\Attachment $repository): Attachment + public function setFactory(Model $factory): Attachment { - $this->stateRepository = $repository; + $this->factory = $factory; return $this; } - protected function idField(): string - { - return 'filename'; - } - protected function fieldsForInsert(): array { - return array_merge($this->fieldsForUpdate(), ['filename']); + return [ + 'message_id', + 'filename' + ]; } protected function fieldsForUpdate(): array { - return [ - 'message_id' - ]; + return $this->fieldsForInsert(); } protected function fieldsForCreate(): array { return $this->fieldsForInsert(); } protected function valuesForUpdate(ModelInterface $model): array + { + + return $this->valuesForInsert($model); + } + protected function valuesForInsert(ModelInterface $model): array { return [ $model->getMessage()->getId(), $model->getFilename() ]; } - protected function valuesForInsert(ModelInterface $model): array - { - return $this->valuesForUpdate($model); - } protected function defaultFind(ModelInterface $model): ModelInterface { - return $this->fetchByFilename($model->getFilename()); + return $this->fetchByMessageAndFilename($model->getMessage()->getId(), $model->getFilename()); } protected function valuesForCreate(array $data): array { @@ -70,22 +68,47 @@ class Attachment extends Repository } protected function defaultSearch(array $data): ModelInterface { - return $this->fetchByFilename($data['filename']); + return $this->fetchByMessageAndFilename($data['message_id'], $data['filename']); } public function load(array $row): \ProVM\Emails\Model\Attachment { - $model = new \ProVM\Emails\Model\Attachment(); - $model + return (new \ProVM\Emails\Model\Attachment()) + ->setId($row['id']) + ->setMessage($this->getFactory()->find(\ProVM\Emails\Model\Message::class)->fetchById($row['message_id'])) ->setFilename($row['filename']) - ->setStateRepository($this->getStateRepository()); - return $model; + ->setStateRepository($this->getFactory()->find(\ProVM\Emails\Model\State\Attachment::class)); } - public function fetchByFilename(string $filename): \ProVM\Emails\Model\Attachment + public function save(ModelInterface &$model): void { - $query = "SELECT * FROM {$this->getTable()} WHERE filename = ?"; - return $this->fetchOne($query, [$filename]); + parent::save($model); + $states_names = [ + 'downloaded', + 'encrypted', + 'decrypted' + ]; + foreach ($states_names as $name) { + try { + $state = $model->getState($name); + $this->getFactory()->find(\ProVM\Emails\Model\State\Attachment::class)->save($state); + } catch (BlankResult $e) { + $this->getLogger()->error($e); + $data = [ + 'attachment_id' => $model->getId(), + 'name' => $name, + 'value' => false + ]; + $state = $this->getFactory()->find(\ProVM\Emails\Model\State\Attachment::class)->create($data); + $this->getFactory()->find(\ProVM\Emails\Model\State\Attachment::class)->save($state); + } + } + } + + public function fetchByMessageAndFilename(int $message_id, string $filename): \ProVM\Emails\Model\Attachment + { + $query = "SELECT * FROM {$this->getTable()} WHERE message_id = ? AND filename = ?"; + return $this->fetchOne($query, [$message_id, $filename]); } public function fetchByMessage(int $message_id): array { diff --git a/api/src/Repository/Job.php b/api/src/Repository/Job.php new file mode 100644 index 0000000..164d900 --- /dev/null +++ b/api/src/Repository/Job.php @@ -0,0 +1,108 @@ +setFactory($factory) + ->setTable('attachments_jobs'); + } + + protected \ProVM\Common\Factory\Model $factory; + public function getFactory(): \ProVM\Common\Factory\Model + { + return $this->factory; + } + public function setFactory(\ProVM\Common\Factory\Model $factory): Job + { + $this->factory = $factory; + return $this; + } + + protected function fieldsForUpdate(): array + { + return $this->fieldsForInsert(); + } + protected function valuesForUpdate(ModelInterface $model): array + { + return $this->valuesForInsert($model); + } + protected function fieldsForInsert(): array + { + return [ + 'message_id', + 'date_time', + 'executed' + ]; + } + protected function valuesForInsert(ModelInterface $model): array + { + return [ + $model->getMessage()->getId(), + $model->getDateTime()->format('Y-m-d H:i:s'), + $model->isExecuted() ? 1 : 0 + ]; + } + protected function defaultFind(ModelInterface $model): ModelInterface + { + return $this->fetchByMessageAndDate($model->getMessage()->getId(), $model->getDateTime()->format('Y-m-d H:i:s')); + } + protected function fieldsForCreate(): array + { + return [ + 'message_id', + 'date_time', + 'executed' + ]; + } + protected function valuesForCreate(array $data): array + { + return [ + $data['message_id'], + $data['date_time'], + $data['executed'] ?? 0 + ]; + } + protected function defaultSearch(array $data): ModelInterface + { + return $this->fetchByMessageAndDate($data['message_id'], $data['date_time']); + } + + public function load(array $row): ModelInterface + { + $model = (new BaseModel()) + ->setId($row['id']) + ->setMessage($this->getFactory()->find(\ProVM\Emails\Model\Message::class)->fetchById($row['message_id'])) + ->setDateTime(new DateTimeImmutable($row['date_time'])); + if ($row['executed'] ?? 0 === 1) { + $model->wasExecuted(); + } + return $model; + } + + public function fetchAllPending(): array + { + $query = "SELECT * FROM {$this->getTable()} WHERE `executed` = 0"; + return $this->fetchMany($query); + } + public function fetchByMessageAndDate(int $message_id, string $date_time): \ProVM\Emails\Model\Job + { + $query = "SELECT * FROM {$this->getTable()} WHERE `message_id` = ? AND `date_time` = ?"; + return $this->fetchOne($query, [$message_id, $date_time]); + } + public function fetchPendingByMessage(int $message_id): \ProVM\Emails\Model\Job + { + $query = "SELECT * FROM {$this->getTable()} WHERE `message_id` = ? AND `executed` = 0"; + return $this->fetchOne($query, [$message_id]); + } +} \ No newline at end of file diff --git a/api/src/Repository/Mailbox.php b/api/src/Repository/Mailbox.php index 3f470e3..50a0b92 100644 --- a/api/src/Repository/Mailbox.php +++ b/api/src/Repository/Mailbox.php @@ -35,7 +35,8 @@ class Mailbox extends Repository protected function fieldsForInsert(): array { return [ - 'name' + 'name', + 'validity' ]; } protected function fieldsForCreate(): array @@ -45,7 +46,8 @@ class Mailbox extends Repository protected function valuesForInsert(Model $model): array { return [ - $model->getName() + $model->getName(), + $model->getValidity() ]; } protected function defaultFind(Model $model): Model @@ -61,7 +63,8 @@ class Mailbox extends Repository protected function valuesForCreate(array $data): array { return [ - $data['name'] + $data['name'], + $data['validity'] ]; } protected function defaultSearch(array $data): Model @@ -74,6 +77,7 @@ class Mailbox extends Repository return (new \ProVM\Emails\Model\Mailbox()) ->setId($row['id']) ->setName($row['name']) + ->setValidity($row['validity']) ->setStateRepository($this->getStates()); } diff --git a/api/src/Repository/Message.php b/api/src/Repository/Message.php index a0389e3..4976dd4 100644 --- a/api/src/Repository/Message.php +++ b/api/src/Repository/Message.php @@ -1,11 +1,11 @@ setId($row['id']) ->setUID($row['uid']) ->setMailbox($this->getFactory()->find(\ProVM\Emails\Model\Mailbox::class)->fetchById($row['mailbox_id'])) ->setPosition($row['position']) ->setSubject($row['subject']) ->setFrom($row['from']) + ->setDateTime(new DateTimeImmutable($row['date_time'])) ->setFactory($this->getFactory()); - try { - $model->setDateTime(new DateTimeImmutable($row['date_time'])); - } catch (Exception | ErrorfuncException $e) { - $this->getLogger()->error($e); - } - return $model; } public function save(Model &$model): void @@ -144,12 +138,26 @@ class Message extends Repository } public function fetchByMailbox(int $mailbox_id): array { - $query = "SELECT * FROM {$this->getTable()} WHERE mailbox_id = ?"; + $query = "SELECT * FROM `{$this->getTable()}` WHERE `mailbox_id` = ?"; return $this->fetchMany($query, [$mailbox_id]); } public function fetchByMailboxAndPosition(int $mailbox_id, int $start, int $amount): array { - $query = "SELECT * FROM {$this->getTable()} WHERE mailbox_id = ? AND position BETWEEN ? AND ? LIMIT {$amount}"; + $query = "SELECT * FROM `{$this->getTable()}` WHERE `mailbox_id` = ? AND `position` BETWEEN ? AND ? LIMIT {$amount}"; return $this->fetchMany($query, [$mailbox_id, $start, $start + $amount]); } + public function fetchValidByMailbox(int $mailbox_id): array + { + $query = "SELECT a.* + FROM `{$this->getTable()}` a + JOIN `messages_states` b ON b.`message_id` = a.`id` + WHERE a.`mailbox_id` = ? AND b.`name` = 'valid_attachments' AND b.`value` = 1"; + return $this->fetchMany($query, [$mailbox_id]); + } + public function fetchByMailboxSubjectFromAndDate(int $mailbox_id, string $subject, string $from, DateTimeInterface $dateTime): \ProVM\Emails\Model\Message + { + $query = "SELECT * FROM `{$this->getTable()}` + WHERE `mailbox_id` = ? `subject` = ? AND `from` = ? AND `date_time` = ?"; + return $this->fetchOne($query, [$mailbox_id, $subject, $from, $dateTime->format('Y-m-d H:i:s')]); + } } \ No newline at end of file diff --git a/api/src/Repository/State/Attachment.php b/api/src/Repository/State/Attachment.php index 9a40027..7da6b9b 100644 --- a/api/src/Repository/State/Attachment.php +++ b/api/src/Repository/State/Attachment.php @@ -8,10 +8,22 @@ use Psr\Log\LoggerInterface; class Attachment extends Repository { - public function __construct(PDO $connection, LoggerInterface $logger) + public function __construct(PDO $connection, LoggerInterface $logger, \ProVM\Common\Factory\Model $factory) { parent::__construct($connection, $logger); - $this->setTable('attachments_states'); + $this->setTable('attachments_states') + ->setFactory($factory); + } + + protected \ProVM\Common\Factory\Model $factory; + public function getFactory(): \ProVM\Common\Factory\Model + { + return $this->factory; + } + public function setFactory(\ProVM\Common\Factory\Model $factory): Attachment + { + $this->factory = $factory; + return $this; } protected function fieldsForInsert(): array @@ -40,7 +52,7 @@ class Attachment extends Repository } protected function valuesForUpdate(Model $model): array { - return array_merge($this->valuesForInsert($model), [$model->getId()]); + return $this->valuesForInsert($model); } protected function fieldsForCreate(): array { @@ -63,6 +75,7 @@ class Attachment extends Repository { return (new \ProVM\Emails\Model\State\Attachment()) ->setId($row['id']) + ->setAttachment($this->getFactory()->find(\ProVM\Emails\Model\Attachment::class)->fetchById($row['attachment_id'])) ->setName($row['name']) ->setValue($row['value'] !== 0); } diff --git a/api/src/Repository/State/Message.php b/api/src/Repository/State/Message.php index fda78e5..74acb5b 100644 --- a/api/src/Repository/State/Message.php +++ b/api/src/Repository/State/Message.php @@ -8,10 +8,22 @@ use ProVM\Common\Implement\Repository; class Message extends Repository { - public function __construct(PDO $connection, LoggerInterface $logger) + public function __construct(PDO $connection, LoggerInterface $logger, \ProVM\Common\Factory\Model $factory) { parent::__construct($connection, $logger); - $this->setTable('messages_states'); + $this->setTable('messages_states') + ->setFactory($factory); + } + + protected \ProVM\Common\Factory\Model $factory; + public function getFactory(): \ProVM\Common\Factory\Model + { + return $this->factory; + } + public function setFactory(\ProVM\Common\Factory\Model $factory): Message + { + $this->factory = $factory; + return $this; } protected function fieldsForUpdate(): array @@ -64,6 +76,7 @@ class Message extends Repository return (new \ProVM\Emails\Model\State\Message()) ->setId($row['id']) ->setName($row['name']) + ->setMessage($this->getFactory()->find(\ProVM\Emails\Model\Message::class)->fetchById($row['message_id'])) ->setValue(($row['value'] ?? 0) !== 0); } diff --git a/cli/common/Command/GrabAttachments.php b/cli/common/Command/GrabAttachments.php index dbf60dd..1055a8c 100644 --- a/cli/common/Command/GrabAttachments.php +++ b/cli/common/Command/GrabAttachments.php @@ -53,9 +53,10 @@ class GrabAttachments extends Command $messages = $this->getMessages(); $io->text('Found ' . count($messages) . ' messages.'); $io->section('Grabbing Attachments'); - foreach ($messages as $message) { - $attachments = $this->grabAttachments($message); - $io->text("Found {$attachments} attachments for message UID:{$message}."); + foreach ($messages as $job) { + $message = $job->message; + $attachments = $this->grabAttachments($message->uid); + $io->text("Found {$attachments} attachments for message UID:{$message->uid}."); } $io->success('Done.'); diff --git a/ui/resources/views/emails/mailboxes.blade.php b/ui/resources/views/emails/mailboxes.blade.php index fea91fd..fda295c 100644 --- a/ui/resources/views/emails/mailboxes.blade.php +++ b/ui/resources/views/emails/mailboxes.blade.php @@ -19,7 +19,7 @@ this.id = props.id } } - draw() { + draw(tr) { const name = $('') const register = $('') if (this.registered) { @@ -42,27 +42,32 @@ register.append( $('').addClass('ui mini circular icon button').append( $('').addClass('save icon') - ).click(() => { - this.register() + ).click((e) => { + this.register($(e.currentTarget)) }) ) } - return $('').append(name).append(register) + return tr.append(name).append(register) } - register() { + register(button) { const uri = '/mailboxes/register' const data = { mailboxes: [this.name] } + button.html('').append( + $('').addClass('redo loading icon') + ) return Send.post({ uri, data }).then(response => { - response.mailboxes.forEach(mb => { - if (mb.name === this.name && mb.created) { + response.registered.mailboxes.forEach(mb => { + if (mb.name === this.name && mb.registered) { this.id = mb.id this.registered = true - mailboxes.draw().table() + const tr = button.parent().parent() + tr.html('') + this.draw(tr) } }) }) @@ -91,7 +96,7 @@ mailboxes: [], get: function() { this.draw().loading() - return Send.get('/mailboxes').then(response => { + return Send.get('/mailboxes').then((response, status, jqXHR) => { response.mailboxes.forEach(mb => { this.mailboxes.push(new Mailbox(mb)) }) @@ -138,7 +143,7 @@ body: () => { const body = $('') this.mailboxes.forEach(mb => { - body.append(mb.draw()) + body.append(mb.draw($(''))) }) return body } diff --git a/ui/resources/views/emails/messages.blade.php b/ui/resources/views/emails/messages.blade.php index a16c0f3..138ac7a 100644 --- a/ui/resources/views/emails/messages.blade.php +++ b/ui/resources/views/emails/messages.blade.php @@ -19,14 +19,16 @@ valid: false, downloaded: false } + attachments - constructor({id, uid, subject, date_time, from, states}) { + constructor({id, uid, subject, date_time, from, states, attachments}) { this.set().id(id) .set().uid(uid) .set().subject(subject) .set().date(date_time) .set().from(from) .set().states(states) + .set().attachments(attachments) } get() { return { @@ -47,6 +49,9 @@ }, states: () => { return this.states + }, + attachments: () => { + return this.attachments } } } @@ -82,6 +87,9 @@ this.states[map_keys[k]] = states[k] }) return this + }, + attachments: attachments => { + this.attachments = attachments } } } @@ -123,25 +131,35 @@ } catch (e) { console.debug(e) } + const valid = $('').html($('').addClass(this.has().valid() ? 'green check circle icon' : 'red times circle icon')) + if (this.has().valid() && this.get().attachments().length > 0) { + const list = $('
').addClass('ui list') + this.get().attachments().forEach(attachment => { + list.append($('
').addClass('item').html(attachment.filename)) + }) + valid.append( + $('
').addClass('ui popup').append(list) + ) + } return $('').append( $('').html(this.get().subject()) ).append( $('').html(format.format(date)) ).append( - $('').html(this.get().from()) + $('').html(this.get().from().full) ).append( $('').html($('').addClass(this.has().attachments() ? 'green check circle icon' : 'red times circle icon')) ).append( - $('').html($('').addClass(this.has().valid() ? 'green check circle icon' : 'red times circle icon')) + valid ).append( $('').html($('').addClass(this.has().downloaded() ? 'green check circle icon' : 'red times circle icon')) ).append( $('').append( (this.has().attachments() && this.has().valid() && !this.has().downloaded()) ? $('').addClass('ui mini green circular icon button').append( - $('').addClass('paperclip icon') + $('').addClass('clock icon') ).click(() => { - this.download().attachments(this.get().uid()) + this.download().attachments(this.get().id()) }) : '' ) ) @@ -149,21 +167,19 @@ download() { return { - attachments: uid => { - const uri = '/emails/attachment' + attachments: id => { + const uri = '/messages/schedule' const data = { - mailbox: '{{$mailbox_id}}', messages: [ - uid - ], - extension: 'pdf' + id + ] } - return Send.post({ + return Send.put({ uri, data }).then(response => { - if (response.attachments_count > 0) { - alert('Grabbed Attachments') + if (response.scheduled > 0) { + alert('Scheduled Attachments Job') } }) } @@ -191,14 +207,8 @@ }) }, messages: () => { - const uri = '/messages/valid' - const data = { - mailboxes: ['{{$mailbox_id}}'] - } - return Send.post({ - uri, - data - }).then(response => { + const uri = '/mailbox/{{$mailbox_id}}/messages/valid' + return Send.get(uri).then(response => { if (this.total === null) { $(this.id.count).html(' (' + response.total + ')') this.total = response.total diff --git a/ui/resources/views/home.blade.php b/ui/resources/views/home.blade.php index d5f0851..6b50b7c 100644 --- a/ui/resources/views/home.blade.php +++ b/ui/resources/views/home.blade.php @@ -13,8 +13,13 @@ get: function() { const uri = '/mailboxes/registered' this.draw().loading() - return Send.get(uri).then(response => { - this.mailboxes = response.mailboxes + return Send.get(uri).then((response, status, jqXHR) => { + if (parseInt(jqXHR.status / 100) !== 2) { + return + } + if (jqXHR.status === 200) { + this.mailboxes = response.mailboxes + } this.draw().list() }) }, @@ -38,6 +43,10 @@ list: () => { const parent = $(this.div_id) parent.html('') + if (this.mailboxes.length === 0) { + parent.html('No mailboxes registered.') + return + } const list = $('
').addClass('ui list') this.mailboxes.forEach(mb => { list.append(