diff --git a/NOTES.md b/NOTES.md index d20957b..ec9035b 100644 --- a/NOTES.md +++ b/NOTES.md @@ -25,4 +25,14 @@ -> **[API]** Register selected `mailboxes` and get `messages` for recently registered. * **[Cron]** Get registered `mailboxes` -> **[API]** Get `messages` * **[User]** Check messages found -> **[API]** Schedule `attachments` -* **[Cron]** Get `attachment download` jobs -> **[API]** grab `attachments` \ No newline at end of file +* **[Cron]** Get `attachment download` jobs -> **[API]** grab `attachments` + +### Jobs +#### Automatic +* [ ] Check registered `mailboxes` for new `messages`, logging last check. +* [ ] Check if `attachments` are `encrypted`. +* [ ] Check for new scheduled jobs. +#### Scheduled +* [ ] Grab `messages`. +* [ ] Grab `attachments`. +* [ ] Decrypt `attachments`. diff --git a/api/common/Controller/Jobs.php b/api/common/Controller/Jobs.php index 9320eae..437555f 100644 --- a/api/common/Controller/Jobs.php +++ b/api/common/Controller/Jobs.php @@ -7,6 +7,7 @@ use Psr\Http\Message\ServerRequestInterface; use ProVM\Common\Exception\Request\MissingArgument; use ProVM\Common\Implement\Controller\Json; use ProVM\Common\Service\Jobs as Service; +use function Safe\json_decode; class Jobs { @@ -15,7 +16,7 @@ class Jobs public function schedule(ServerRequestInterface $request, ResponseInterface $response, Service $service, \ProVM\Common\Service\Messages $messagesService): ResponseInterface { $body = $request->getBody(); - $json = \Safe\json_decode($body->getContents()); + $json = json_decode($body->getContents()); if (!isset($json->messages)) { throw new MissingArgument('messages', 'array', 'messages ids'); } diff --git a/api/common/Controller/Messages.php b/api/common/Controller/Messages.php index 47425ef..db86073 100644 --- a/api/common/Controller/Messages.php +++ b/api/common/Controller/Messages.php @@ -1,13 +1,13 @@ toArray(); }, $service->getAll($mailbox->getName())); + usort($messages, function($a, $b) { + $d = $a['date_time'] - $b['date_time']; + if ($d->days === 0) { + $f = strcmp($a['from'], $b['from']); + if ($f === 0) { + return strcmp($a['subject'], $b['subject']); + } + return $f; + } + return $d->format('%r%a'); + }); $output = [ 'mailbox' => $mailbox->toArray(), 'total' => count($messages), @@ -34,6 +45,17 @@ class Messages }, $service->getValid($mailbox->getName())), function($message) { return $message !== null; })); + usort($messages, function($a, $b) { + $d = strcmp($a['date_time'], $b['date_time']); + if ($d === 0) { + $f = strcmp($a['from'], $b['from']); + if ($f === 0) { + return strcmp($a['subject'], $b['subject']); + } + return $f; + } + return $d; + }); $output = [ 'mailbox' => $mailbox->toArray(), 'total' => count($messages), @@ -49,7 +71,7 @@ class Messages public function grab(ServerRequestInterface $request, ResponseInterface $response, Service $service, \ProVM\Common\Service\Attachments $attachmentsService): ResponseInterface { $body = $request->getBody(); - $json = \Safe\json_decode($body->getContents()); + $json = json_decode($body->getContents()); if (!isset($json->mailboxes)) { throw new MissingArgument('mailboxes', 'array', 'mailboxes names'); } @@ -70,4 +92,4 @@ class Messages } return $this->withJson($response, $output); } -} \ No newline at end of file +} diff --git a/api/common/Define/Model.php b/api/common/Define/Model.php index 28a9b45..9af5971 100644 --- a/api/common/Define/Model.php +++ b/api/common/Define/Model.php @@ -1,6 +1,8 @@ getContainer()->get($repository_class); } -} \ No newline at end of file +} diff --git a/api/common/Implement/Repository.php b/api/common/Implement/Repository.php index f8a7b7a..7cbe22d 100644 --- a/api/common/Implement/Repository.php +++ b/api/common/Implement/Repository.php @@ -3,11 +3,11 @@ namespace ProVM\Common\Implement; use PDO; use PDOException; -use ProVM\Common\Exception\Database\BlankResult; use Psr\Log\LoggerInterface; -use ProVM\Common\Define\Model as ModelInterface; +use ProVM\Common\Exception\Database\BlankResult; +use ProVM\Common\Define; -abstract class Repository +abstract class Repository implements Define\Repository { public function __construct(PDO $connection, LoggerInterface $logger) { @@ -32,33 +32,32 @@ abstract class Repository return $this->logger; } - public function setConnection(PDO $pdo): Repository + public function setConnection(PDO $pdo): Define\Repository { $this->connection = $pdo; return $this; } - public function setTable(string $table): Repository + public function setTable(string $table): Define\Repository { $this->table = $table; return $this; } - public function setLogger(LoggerInterface $logger): Repository + public function setLogger(LoggerInterface $logger): Define\Repository { $this->logger = $logger; return $this; } - abstract protected function fieldsForUpdate(): array; - abstract protected function valuesForUpdate(ModelInterface $model): array; - protected function idProperty(): string + public function isInstalled(): bool { - return 'getId'; + $query = "SHOW TABLES LIKE '{$this->getTable()}'"; + $st = $this->getConnection()->query($query); + if ($st === false) { + throw new PDOException("Could not run query {$query}"); + } + return $st->rowCount() > 0; } - protected function idField(): string - { - return 'id'; - } - public function update(ModelInterface $model, ModelInterface $old): void + public function update(Define\Model $model, Define\Model $old): void { $query = "UPDATE `{$this->getTable()}` SET "; $model_values = $this->valuesForUpdate($model); @@ -79,22 +78,7 @@ abstract class Repository $st = $this->getConnection()->prepare($query); $st->execute($values); } - abstract protected function fieldsForInsert(): array; - abstract protected function valuesForInsert(ModelInterface $model): array; - protected function insert(ModelInterface $model): void - { - $fields = $this->fieldsForInsert(); - $fields_string = implode(', ', array_map(function($field) { - return "`{$field}`"; - }, $fields)); - $fields_questions = implode(', ', array_fill(0, count($fields), '?')); - $query = "INSERT INTO `{$this->getTable()}` ({$fields_string}) VALUES ({$fields_questions})"; - $values = $this->valuesForInsert($model); - $st = $this->getConnection()->prepare($query); - $st->execute($values); - } - abstract protected function defaultFind(ModelInterface $model): ModelInterface; - public function save(ModelInterface &$model): void + public function save(Define\Model &$model): void { try { $old = $this->defaultFind($model); @@ -107,12 +91,7 @@ abstract class Repository throw $e; } } - abstract public function load(array $row): ModelInterface; - - abstract protected function fieldsForCreate(): array; - abstract protected function valuesForCreate(array $data): array; - abstract protected function defaultSearch(array $data): ModelInterface; - public function create(array $data): ModelInterface + public function create(array $data): Define\Model { try { return $this->defaultSearch($data); @@ -121,11 +100,6 @@ abstract class Repository return $this->load($data); } } - - protected function getId(ModelInterface $model): int - { - return $model->getId(); - } public function resetIndex(): void { $query = "ALTER TABLE `{$this->getTable()}` AUTO_INCREMENT = 1"; @@ -136,7 +110,7 @@ abstract class Repository $query = "OPTIMIZE TABLE `{$this->getTable()}`"; $this->getConnection()->query($query); } - public function delete(ModelInterface $model): void + public function delete(Define\Model $model): void { $query = "DELETE FROM `{$this->getTable()}` WHERE `{$this->idField()}` = ?"; $st = $this->getConnection()->prepare($query); @@ -144,8 +118,42 @@ abstract class Repository $this->resetIndex(); $this->optimize(); } + public function fetchAll(): array + { + $query = "SELECT * FROM `{$this->getTable()}`"; + return $this->fetchMany($query); + } + public function fetchById(int $id): Define\Model + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `{$this->idField()}` = ?"; + return $this->fetchOne($query, [$id]); + } - protected function fetchOne(string $query, ?array $values = null): ModelInterface + protected function idProperty(): string + { + return 'getId'; + } + protected function idField(): string + { + return 'id'; + } + protected function insert(Define\Model $model): void + { + $fields = $this->fieldsForInsert(); + $fields_string = implode(', ', array_map(function($field) { + return "`{$field}`"; + }, $fields)); + $fields_questions = implode(', ', array_fill(0, count($fields), '?')); + $query = "INSERT INTO `{$this->getTable()}` ({$fields_string}) VALUES ({$fields_questions})"; + $values = $this->valuesForInsert($model); + $st = $this->getConnection()->prepare($query); + $st->execute($values); + } + protected function getId(Define\Model $model): int + { + return $model->getId(); + } + protected function fetchOne(string $query, ?array $values = null): Define\Model { if ($values !== null) { $st = $this->getConnection()->prepare($query); @@ -174,14 +182,14 @@ abstract class Repository return array_map([$this, 'load'], $rows); } - public function fetchAll(): array - { - $query = "SELECT * FROM `{$this->getTable()}`"; - return $this->fetchMany($query); - } - public function fetchById(int $id): ModelInterface - { - $query = "SELECT * FROM `{$this->getTable()}` WHERE `{$this->idField()}` = ?"; - return $this->fetchOne($query, [$id]); - } -} \ No newline at end of file + abstract public function install(): void; + abstract public function load(array $row): Define\Model; + abstract protected function fieldsForUpdate(): array; + abstract protected function valuesForUpdate(Define\Model $model): array; + abstract protected function fieldsForInsert(): array; + abstract protected function valuesForInsert(Define\Model $model): array; + abstract protected function defaultFind(Define\Model $model): Define\Model; + abstract protected function fieldsForCreate(): array; + abstract protected function valuesForCreate(array $data): array; + abstract protected function defaultSearch(array $data): Define\Model; +} diff --git a/api/common/Middleware/Attachments.php b/api/common/Middleware/Attachments.php new file mode 100644 index 0000000..1baabca --- /dev/null +++ b/api/common/Middleware/Attachments.php @@ -0,0 +1,25 @@ +service->checkDownloaded(); + $this->service->checkEncryption(); + } catch (BlankResult $e) { + $this->logger->notice($e); + } + return $handler->handle($request); + } +} diff --git a/api/common/Middleware/Install.php b/api/common/Middleware/Install.php new file mode 100644 index 0000000..b9a8053 --- /dev/null +++ b/api/common/Middleware/Install.php @@ -0,0 +1,20 @@ +service->check()) { + $this->service->install(); + } + return $handler->handle($request); + } +} diff --git a/api/common/Middleware/Logging.php b/api/common/Middleware/Logging.php index a95c37b..2e91a6d 100644 --- a/api/common/Middleware/Logging.php +++ b/api/common/Middleware/Logging.php @@ -5,34 +5,21 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Psr\Log\LoggerInterface; +use function Safe\json_encode; class Logging { - public function __construct(LoggerInterface $logger) { - $this->setLogger($logger); - } - - protected LoggerInterface $logger; - - public function getLogger(): LoggerInterface - { - return $this->logger; - } - - public function setLogger(LoggerInterface $logger): Logging - { - $this->logger = $logger; - return $this; - } + public function __construct(protected LoggerInterface $logger) {} public function __invoke(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $response = $handler->handle($request); $output = [ 'uri' => var_export($request->getUri(), true), - 'body' => $request->getBody()->getContents() + 'body' => $request->getBody()->getContents(), + 'response' => (clone $response)->getBody()->getContents() ]; - $this->getLogger()->info(\Safe\json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); + $this->logger->info(json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); return $response; } } diff --git a/api/common/Middleware/Mailboxes.php b/api/common/Middleware/Mailboxes.php new file mode 100644 index 0000000..0808f7f --- /dev/null +++ b/api/common/Middleware/Mailboxes.php @@ -0,0 +1,24 @@ +service->checkUpdate(); + } catch (BlankResult $e) { + $this->logger->notice($e); + } + return $handler->handle($request); + } +} diff --git a/api/common/Middleware/Messages.php b/api/common/Middleware/Messages.php new file mode 100644 index 0000000..13c13cb --- /dev/null +++ b/api/common/Middleware/Messages.php @@ -0,0 +1,24 @@ +service->checkSchedule(); + } catch (BlankResult $e) { + $this->logger->notice($e); + } + return $handler->handle($request); + } +} diff --git a/api/common/Service/Attachments.php b/api/common/Service/Attachments.php index 6c92915..1487b77 100644 --- a/api/common/Service/Attachments.php +++ b/api/common/Service/Attachments.php @@ -1,15 +1,15 @@ getFullFilename() ]); } - return \Safe\file_get_contents($filename); + return file_get_contents($filename); } public function getAll(): array { return $this->getRepository()->fetchAll(); } + public function getDownloadedFiles(): array + { + $downloaded = []; + $folder = $this->getFolder(); + $files = new \FilesystemIterator($folder); + foreach ($files as $file) { + if ($file->isDir()) { + continue; + } + $name = $file->getBasename(".{$file->getExtension()}"); + list($subject, $date, $filename) = explode(' - ', $name); + try { + $message = $this->getMessages()->find($subject, $date)[0]; + $filename = "{$filename}.{$file->getExtension()}"; + $downloaded []= compact('message', 'filename'); + } catch (BlankResult $e) { + } + } + return $downloaded; + } public function create(int $message_id): array { $message = $this->getMessages()->getRepository()->fetchById($message_id); @@ -183,7 +203,7 @@ class Attachments extends Base $attachment->getFullFilename() ]); try { - \Safe\file_put_contents($destination, $remote_attachment->getDecodedContent()); + file_put_contents($destination, $remote_attachment->getDecodedContent()); return true; } catch (FilesystemException $e) { $this->getLogger()->error($e); @@ -236,6 +256,9 @@ class Attachments extends Base if (!$message->hasValidAttachments()) { return false; } + if ($message->hasDownloadedAttachments()) { + return true; + } foreach ($remote_message->getAttachments() as $attachment) { if (!str_contains($attachment->getFilename(), '.pdf')) { continue; @@ -247,4 +270,59 @@ class Attachments extends Base } return true; } -} \ No newline at end of file + public function find(Message $message, string $filename): \ProVM\Emails\Model\Attachment + { + return $this->getRepository()->fetchByMessageAndFilename($message->getId(), $filename); + } + public function exists(Message $message, string $filename): bool + { + try { + $this->find($message, $filename); + return true; + } catch (BlankResult $e) { + return false; + } + } + public function add(Message $message, string $filename): bool + { + $data = [ + 'message_id' => $message->getId(), + 'filename' => $filename + ]; + try { + $attachment = $this->getRepository()->create($data); + $attachment->itIsDownloaded(); + $this->getRepository()->save($attachment); + $message->doesHaveDownloadedAttachments(); + $this->getMessages()->getRepository()->save($message); + return true; + } catch (PDOException $e) { + $this->getLogger()->error($e); + return false; + } + } + public function checkDownloaded(): void + { + $data = $this->getDownloadedFiles(); + foreach ($data as $info) { + if (!$this->exists($info['message'], $info['filename'])) { + $this->logger->info("Updating attachment {$info['filename']} for message {$info['message']->getSubject()}"); + $this->add($info['message'], $info['filename']); + } + } + } + public function getDownloaded(): array + { + return $this->getRepository()->fetchDownloaded(); + } + public function checkEncryption(): void + { + $attachments = $this->getDownloaded(); + foreach ($attachments as $attachment) { + if ($attachment->isEncrypted() and !$attachment->isDecrypted()) { + $this->logger->notice("Schedule decrypt for {$attachment->getFullFilename()}"); + $this->decrypt($attachment->getMessage(), $attachment->getFilename()); + } + } + } +} diff --git a/api/common/Service/Install.php b/api/common/Service/Install.php new file mode 100644 index 0000000..2ae9574 --- /dev/null +++ b/api/common/Service/Install.php @@ -0,0 +1,40 @@ +model_list as $model_class) { + $repository = $this->factory->find($model_class); + if (!$repository->isInstalled()) { + return false; + } + } + return true; + } + public function install(): void + { + $check = true; + $repository = null; + foreach ($this->model_list as $model_class) { + $repository = $this->factory->find($model_class); + if ($check) { + $query = "SET FOREIGN_KEY_CHECKS = 0"; + $repository->getConnection()->query($query); + $check = false; + } + if (!$repository->isInstalled()) { + $repository->install(); + } + } + if (!$check) { + $query = "SET FOREIGN_KEY_CHECKS = 1"; + $repository->getConnection()->query($query); + } + } +} diff --git a/api/common/Service/Mailboxes.php b/api/common/Service/Mailboxes.php index 908cf00..3eda6e6 100644 --- a/api/common/Service/Mailboxes.php +++ b/api/common/Service/Mailboxes.php @@ -11,7 +11,7 @@ use ProVM\Emails\Repository\State; class Mailboxes extends Base { - public function __construct(Mailbox $repository, Remote\Mailboxes $remoteService, State\Mailbox $states, LoggerInterface $logger) + public function __construct(Mailbox $repository, Remote\Mailboxes $remoteService, State\Mailbox $states, LoggerInterface $logger, protected int $max_update_days) { $this->setRepository($repository) ->setRemoteService($remoteService) @@ -35,7 +35,6 @@ class Mailboxes extends Base { return $this->statesRepository; } - public function setRepository(Mailbox $repository): Mailboxes { $this->repository = $repository; @@ -110,6 +109,7 @@ class Mailboxes extends Base $this->getStatesRepository()->save($state); return true; } catch (PDOException $e) { + $this->getLogger()->error($e); return false; } } @@ -141,4 +141,13 @@ class Mailboxes extends Base } return true; } -} \ No newline at end of file + public function isUpdated(\ProVM\Emails\Model\Mailbox $mailbox): bool + { + $states = $mailbox->getStates(); + if (count($states) === 0) { + return false; + } + $last = $states[count($states) - 1]; + return abs((int) $last->getDateTime()->diff(new \DateTimeImmutable())->format('%r%a')) < $this->max_update_days; + } +} diff --git a/api/common/Service/Messages.php b/api/common/Service/Messages.php index c5d0f05..a0cd5e0 100644 --- a/api/common/Service/Messages.php +++ b/api/common/Service/Messages.php @@ -4,6 +4,7 @@ namespace ProVM\Common\Service; use Ddeboer\Imap\Exception\MessageDoesNotExistException; use Ddeboer\Imap\MailboxInterface; use PDOException; +use ProVM\Common\Exception\Database\BlankResult; use ProVM\Common\Exception\Mailbox\Stateless; use Psr\Log\LoggerInterface; use Ddeboer\Imap\MessageInterface; @@ -14,17 +15,19 @@ use Safe\DateTimeImmutable; class Messages extends Base { - public function __construct(Mailboxes $mailboxes, Message $repository, Remote\Messages $remoteService, LoggerInterface $logger) + public function __construct(Mailboxes $mailboxes, Message $repository, Remote\Messages $remoteService, Jobs $jobsService, LoggerInterface $logger) { $this->setMailboxes($mailboxes) ->setRepository($repository) ->setRemoteService($remoteService) + ->setJobsService($jobsService) ->setLogger($logger); } protected Mailboxes $mailboxes; protected Message $repository; protected Remote\Messages $remoteService; + protected Jobs $jobsService; public function getMailboxes(): Mailboxes { @@ -38,6 +41,10 @@ class Messages extends Base { return $this->remoteService; } + public function getJobsService(): Jobs + { + return $this->jobsService; + } public function setMailboxes(Mailboxes $mailboxes): Messages { @@ -54,6 +61,11 @@ class Messages extends Base $this->remoteService = $service; return $this; } + public function setJobsService(Jobs $service): Messages + { + $this->jobsService = $service; + return $this; + } public function getLocalMessage(string $message_uid): \ProVM\Emails\Model\Message { @@ -142,6 +154,7 @@ class Messages extends Base $message->doesHaveValidAttachments(); } } + error_log(json_encode(compact('message')).PHP_EOL,3,'/logs/debug'); $this->getRepository()->save($message); return true; } catch (PDOException $e) { @@ -174,4 +187,30 @@ class Messages extends Base } return false; } -} \ No newline at end of file + public function find(string $subject, string $date): array + { + return $this->repository->fetchAllBySubjectAndDate($subject, new DateTimeImmutable($date)); + } + public function checkUpdate(): void + { + $registered = $this->getMailboxes()->getRegistered(); + foreach ($registered as $mailbox) { + if (!$this->getMailboxes()->isUpdated($mailbox)) { + $this->logger->info("Updating messages from {$mailbox->getName()}"); + $this->grab($mailbox->getName()); + } + } + } + public function checkSchedule(): void + { + $messages = $this->getRepository()->fetchAll(); + foreach ($messages as $message) { + if ($message->hasAttachments() and $message->hasValidAttachments() and !$message->hasDownloadedAttachments() and !$message->hasScheduledDownloads()) { + if ($this->getJobsService()->schedule($message->getId())) { + $message->doesHaveDownloadedAttachments(); + $this->getRepository()->save($message); + } + } + } + } +} diff --git a/api/public/index.php b/api/public/index.php index 9e2bc0e..2e6a761 100644 --- a/api/public/index.php +++ b/api/public/index.php @@ -7,12 +7,8 @@ $app = require_once implode(DIRECTORY_SEPARATOR, [ Monolog\ErrorHandler::register($app->getContainer()->get(Psr\Log\LoggerInterface::class)); try { $app->run(); -} catch (Error | Exception $e) { - $logger = $app->getContainer()->get(Psr\Log\LoggerInterface::class); - if (isset($_REQUEST)) { - $logger->debug(Safe\json_encode(compact('_REQUEST'))); - } - $logger->debug(Safe\json_encode(compact('_SERVER'))); - $logger->error($e); - throw $e; +} catch (Error $e) { + $app->getContainer()->get(Psr\Log\LoggerInterface::class)->error($e); +} catch (Exception $e) { + $app->getContainer()->get(Psr\Log\LoggerInterface::class)->debug($e); } diff --git a/api/setup/middleware/04_db.php b/api/setup/middleware/04_db.php new file mode 100644 index 0000000..9d831e8 --- /dev/null +++ b/api/setup/middleware/04_db.php @@ -0,0 +1,5 @@ +add($app->getContainer()->get(ProVM\Common\Middleware\Attachments::class)); +$app->add($app->getContainer()->get(ProVM\Common\Middleware\Messages::class)); +$app->add($app->getContainer()->get(ProVM\Common\Middleware\Mailboxes::class)); +$app->add($app->getContainer()->get(ProVM\Common\Middleware\Install::class)); diff --git a/api/setup/settings/01_env.php b/api/setup/settings/01_env.php index 6e61213..89deb37 100644 --- a/api/setup/settings/01_env.php +++ b/api/setup/settings/01_env.php @@ -5,7 +5,7 @@ return [ 'host' => $_ENV['EMAIL_HOST'], 'username' => $_ENV['EMAIL_USERNAME'], 'password' => $_ENV['EMAIL_PASSWORD'], - 'folder' => $_ENV['EMAIL_FOLDER'], + //'folder' => $_ENV['EMAIL_FOLDER'], ]; if (isset($_ENV['EMAIL_PORT'])) { $data['port'] = $_ENV['EMAIL_PORT']; @@ -27,5 +27,6 @@ return [ $arr['port'] = $_ENV['MYSQL_PORT']; } return (object) $arr; - } + }, + 'max_update_days' => 7 ]; diff --git a/api/setup/settings/04_db.php b/api/setup/settings/04_db.php new file mode 100644 index 0000000..62415ba --- /dev/null +++ b/api/setup/settings/04_db.php @@ -0,0 +1,33 @@ + function() { + function getClassesFromFolder(string $folder): array { + $classes = []; + $files = new FilesystemIterator($folder); + foreach ($files as $file) { + if ($file->isDir()) { + $classes = array_merge($classes, getClassesFromFolder($file->getRealPath())); + continue; + } + $classes []= ltrim(str_replace("\\\\", "\\", implode("\\", [ + 'ProVM', + 'Emails', + 'Model', + str_replace([implode(DIRECTORY_SEPARATOR, [ + dirname(__FILE__, 3), + 'src', + 'Model' + ]), '/'], ['', "\\"], $folder), + $file->getBasename(".{$file->getExtension()}") + ])), "\\"); + } + return $classes; + } + $folder = implode(DIRECTORY_SEPARATOR, [ + dirname(__FILE__, 3), + 'src', + 'Model' + ]); + return getClassesFromFolder($folder); + } +]; diff --git a/api/setup/settings/98_log.php b/api/setup/settings/98_log.php index 55328ea..3c1e3f0 100644 --- a/api/setup/settings/98_log.php +++ b/api/setup/settings/98_log.php @@ -1,4 +1,5 @@ '/logs/php.log' -]; \ No newline at end of file + 'log_file' => '/logs/php.log', + 'logstash_socket' => 'localhost:50000' +]; diff --git a/api/setup/setups/01_emails.php b/api/setup/setups/01_emails.php index 23c62b3..1428bae 100644 --- a/api/setup/setups/01_emails.php +++ b/api/setup/setups/01_emails.php @@ -5,13 +5,13 @@ return [ Ddeboer\Imap\ServerInterface::class => function(ContainerInterface $container) { $emails = $container->get('emails'); if (isset($emails->port)) { - return new \Ddeboer\Imap\Server($emails->host, $emails->port); + return new Ddeboer\Imap\Server($emails->host, $emails->port); } - return new \Ddeboer\Imap\Server($emails->host); + return new Ddeboer\Imap\Server($emails->host); }, - \Ddeboer\Imap\ConnectionInterface::class => function(ContainerInterface $container) { + Ddeboer\Imap\ConnectionInterface::class => function(ContainerInterface $container) { $emails = $container->get('emails'); - $server = $container->get(\Ddeboer\Imap\ServerInterface::class); + $server = $container->get(Ddeboer\Imap\ServerInterface::class); return $server->authenticate($emails->username, $emails->password); }, PDO::class => function(ContainerInterface $container) { diff --git a/api/setup/setups/02_services.php b/api/setup/setups/02_services.php index 8fd0f0a..2a09318 100644 --- a/api/setup/setups/02_services.php +++ b/api/setup/setups/02_services.php @@ -18,5 +18,20 @@ return [ $container->get('attachments_folder'), $container->get(Psr\Log\LoggerInterface::class) ); + }, + ProVM\Common\Service\Mailboxes::class => function(ContainerInterface $container) { + return new ProVM\Common\Service\Mailboxes( + $container->get(ProVM\Emails\Repository\Mailbox::class), + $container->get(ProVM\Common\Service\Remote\Mailboxes::class), + $container->get(ProVM\Emails\Repository\State\Mailbox::class), + $container->get(Psr\Log\LoggerInterface::class), + $container->get('max_update_days') + ); + }, + ProVM\Common\Service\Install::class => function(ContainerInterface $container) { + return new ProVM\Common\Service\Install( + $container->get(ProVM\Common\Factory\Model::class), + $container->get('model_list') + ); } ]; diff --git a/api/setup/setups/03_factories.php b/api/setup/setups/03_factories.php index 654ca74..281eafa 100644 --- a/api/setup/setups/03_factories.php +++ b/api/setup/setups/03_factories.php @@ -2,16 +2,15 @@ use Psr\Container\ContainerInterface; return [ - \ProVM\Common\Factory\Model::class => function(ContainerInterface $container) { - $factory = new \ProVM\Common\Factory\Model($container); - $repositories = [ - 'Mailbox' => \ProVM\Emails\Repository\Mailbox::class, - 'Message' => \ProVM\Emails\Repository\Message::class, - 'Attachment' => \ProVM\Emails\Repository\Attachment::class, - "State\\Mailbox" => \ProVM\Emails\Repository\State\Mailbox::class, - "State\\Message" => \ProVM\Emails\Repository\State\Message::class, - "State\\Attachment" => \ProVM\Emails\Repository\State\Attachment::class - ]; - return $factory->setRepositories($repositories); + ProVM\Common\Factory\Model::class => function(ContainerInterface $container) { + $factory = new ProVM\Common\Factory\Model($container); + return $factory->setRepositories([ + 'Mailbox' => ProVM\Emails\Repository\Mailbox::class, + 'Message' => ProVM\Emails\Repository\Message::class, + 'Attachment' => ProVM\Emails\Repository\Attachment::class, + "State\\Mailbox" => ProVM\Emails\Repository\State\Mailbox::class, + "State\\Message" => ProVM\Emails\Repository\State\Message::class, + "State\\Attachment" => ProVM\Emails\Repository\State\Attachment::class + ]); } -]; \ No newline at end of file +]; diff --git a/api/setup/setups/04_middlewares.php b/api/setup/setups/04_middlewares.php index 1ec3b45..10775dd 100644 --- a/api/setup/setups/04_middlewares.php +++ b/api/setup/setups/04_middlewares.php @@ -10,5 +10,5 @@ return [ }, ProVM\Common\Middleware\Logging::class => function(ContainerInterface $container) { return new ProVM\Common\Middleware\Logging($container->get('request_logger')); - } + }, ]; diff --git a/api/setup/setups/97_auth.php b/api/setup/setups/97_auth.php index 17e45ac..03fa0f3 100644 --- a/api/setup/setups/97_auth.php +++ b/api/setup/setups/97_auth.php @@ -2,10 +2,10 @@ use Psr\Container\ContainerInterface; return [ - \ProVM\Common\Middleware\Auth::class => function(ContainerInterface $container) { - return new \ProVM\Common\Middleware\Auth( - $container->get(\Nyholm\Psr7\Factory\Psr17Factory::class), - $container->get(\Psr\Log\LoggerInterface::class), + ProVM\Common\Middleware\Auth::class => function(ContainerInterface $container) { + return new ProVM\Common\Middleware\Auth( + $container->get(Nyholm\Psr7\Factory\Psr17Factory::class), + $container->get(Psr\Log\LoggerInterface::class), $container->get('api_key') ); } diff --git a/api/setup/setups/98_log.php b/api/setup/setups/98_log.php index ec2c9a7..4c6a66e 100644 --- a/api/setup/setups/98_log.php +++ b/api/setup/setups/98_log.php @@ -2,31 +2,61 @@ use Psr\Container\ContainerInterface; return [ - Monolog\Handler\DeduplicationHandler::class => function(ContainerInterface $container) { - return new Monolog\Handler\DeduplicationHandler($container->get(Monolog\Handler\RotatingFileHandler::class)); + 'log_processors' => function(ContainerInterface $container) { + return [ + $container->get(Monolog\Processor\PsrLogMessageProcessor::class), + $container->get(Monolog\Processor\IntrospectionProcessor::class), + $container->get(Monolog\Processor\MemoryPeakUsageProcessor::class), + ]; }, - Monolog\Handler\RotatingFileHandler::class => function(ContainerInterface $container) { - $handler = new Monolog\Handler\RotatingFileHandler($container->get('log_file')); - $handler->setFormatter($container->get(Monolog\Formatter\SyslogFormatter::class)); - return $handler; + 'request_log_handler' => function(ContainerInterface $container) { + return (new Monolog\Handler\RotatingFileHandler(implode(DIRECTORY_SEPARATOR, [$container->get('logs_folder'), 'requests.log']))) + ->setFormatter(new Monolog\Formatter\LineFormatter(null, null, true)); }, 'request_logger' => function(ContainerInterface $container) { - $logger = new Monolog\Logger('request_logger'); - $handler = new Monolog\Handler\RotatingFileHandler(implode(DIRECTORY_SEPARATOR, [$container->get('logs_folder'), 'requests.log'])); - $handler->setFormatter($container->get(Monolog\Formatter\SyslogFormatter::class)); - $dedupHandler = new Monolog\Handler\DeduplicationHandler($handler, null, Monolog\Level::Info); - $logger->pushHandler($dedupHandler); - $logger->pushProcessor($container->get(Monolog\Processor\PsrLogMessageProcessor::class)); - $logger->pushProcessor($container->get(Monolog\Processor\IntrospectionProcessor::class)); - $logger->pushProcessor($container->get(Monolog\Processor\MemoryUsageProcessor::class)); - return $logger; + return new Monolog\Logger( + 'request_logger', + [$container->get('request_log_handler')], + $container->get('log_processors') + ); + }, + 'file_log_handler' => function(ContainerInterface $container) { + return new Monolog\Handler\FilterHandler( + (new Monolog\Handler\RotatingFileHandler($container->get('log_file'))) + ->setFormatter(new Monolog\Formatter\LineFormatter(null, null, true)), + Monolog\Level::Error + ); + }, + 'debug_log_handler' => function(ContainerInterface $container) { + return new Monolog\Handler\FilterHandler( + (new Monolog\Handler\RotatingFileHandler(implode(DIRECTORY_SEPARATOR, [$container->get('logs_folder'), 'debug.log']))) + ->setFormatter(new Monolog\Formatter\LineFormatter(null, null, true)), + Monolog\Level::Debug, + Monolog\Level::Warning + ); }, Psr\Log\LoggerInterface::class => function(ContainerInterface $container) { - $logger = new Monolog\Logger('file_logger'); - $logger->pushHandler($container->get(Monolog\Handler\DeduplicationHandler::class)); - $logger->pushProcessor($container->get(Monolog\Processor\PsrLogMessageProcessor::class)); - $logger->pushProcessor($container->get(Monolog\Processor\IntrospectionProcessor::class)); - $logger->pushProcessor($container->get(Monolog\Processor\MemoryUsageProcessor::class)); - return $logger; + return $container->get('elk_logger'); + }, + 'file_logger' => function(ContainerInterface $container) { + return new Monolog\Logger( + 'file', + [ + $container->get('file_log_handler'), + $container->get('debug_log_handler') + ], + $container->get('log_processors') + ); + }, + 'elk_logger' => function(ContainerInterface $container) { + return new Monolog\Logger('elk', [ + (new Monolog\Handler\SocketHandler($container->get('logstash_socket'))) + ->setFormatter(new Monolog\Formatter\LogstashFormatter('emails', 'docker')) + ], [ + new Monolog\Processor\PsrLogMessageProcessor(), + new Monolog\Processor\WebProcessor(), + new Monolog\Processor\IntrospectionProcessor(), + new Monolog\Processor\MemoryPeakUsageProcessor() + ]); } ]; diff --git a/api/src/Model/Attachment.php b/api/src/Model/Attachment.php index 49bf820..912a2e8 100644 --- a/api/src/Model/Attachment.php +++ b/api/src/Model/Attachment.php @@ -169,4 +169,8 @@ class Attachment implements Model 'decrypted' => $this->isDecrypted() ]; } -} \ No newline at end of file + public function jsonSerialize(): mixed + { + return $this->toArray(); + } +} diff --git a/api/src/Model/Job.php b/api/src/Model/Job.php index 421f79a..b4a9cc5 100644 --- a/api/src/Model/Job.php +++ b/api/src/Model/Job.php @@ -58,4 +58,8 @@ class Job implements Model 'executed' => $this->isExecuted() ]; } -} \ No newline at end of file + public function jsonSerialize(): mixed + { + return $this->toArray(); + } +} diff --git a/api/src/Model/Mailbox.php b/api/src/Model/Mailbox.php index 0a4221a..770f715 100644 --- a/api/src/Model/Mailbox.php +++ b/api/src/Model/Mailbox.php @@ -104,6 +104,9 @@ class Mailbox implements Model public function lastPosition(): int { $state = $this->lastState()->getUIDs(); + if (count($state) === 0) { + return 0; + } return array_key_last($state); } @@ -119,4 +122,8 @@ class Mailbox implements Model ] ]; } -} \ No newline at end of file + public function jsonSerialize(): mixed + { + return $this->toArray(); + } +} diff --git a/api/src/Model/Message.php b/api/src/Model/Message.php index d9a4373..5992956 100644 --- a/api/src/Model/Message.php +++ b/api/src/Model/Message.php @@ -99,14 +99,14 @@ class Message implements Model { if (!isset($this->states)) { try { - $this->setStates($this->getFactory()->find(\ProVM\Emails\Model\State\Message::class)->fetchByMessage($this->getId())); + $this->setStates($this->getFactory()->find(State\Message::class)->fetchByMessage($this->getId())); } catch (BlankResult $e) { return []; } } return $this->states; } - public function getState(string $name): \ProVM\Emails\Model\State\Message + public function getState(string $name): State\Message { try { return $this->getStates()[$name]; @@ -115,7 +115,7 @@ class Message implements Model return $this->getStates()[$name]; } } - public function addState(\ProVM\Emails\Model\State\Message $state): Message + public function addState(State\Message $state): Message { $this->states[$state->getName()] = $state; return $this; @@ -129,7 +129,7 @@ class Message implements Model } protected function newState(string $name): Message { - $this->addState((new \ProVM\Emails\Model\State\Message()) + $this->addState((new State\Message()) ->setName($name) ->setMessage($this) ->setValue(false) @@ -219,4 +219,8 @@ class Message implements Model }, $this->getAttachments()) : [] ]; } + public function jsonSerialize(): mixed + { + return $this->toArray(); + } } diff --git a/api/src/Model/State/Attachment.php b/api/src/Model/State/Attachment.php index a50961b..edb5e86 100644 --- a/api/src/Model/State/Attachment.php +++ b/api/src/Model/State/Attachment.php @@ -47,4 +47,18 @@ class Attachment implements Model $this->value = $value; return $this; } -} \ No newline at end of file + + public function toArray(): array + { + return [ + 'id' => $this->getId(), + 'attachment_id' => $this->getAttachment()->getId(), + 'name' => $this->getName(), + 'value' => $this->getValue() + ]; + } + public function jsonSerialize(): mixed + { + return $this->toArray(); + } +} diff --git a/api/src/Model/State/Mailbox.php b/api/src/Model/State/Mailbox.php index 35d5df6..35e66d3 100644 --- a/api/src/Model/State/Mailbox.php +++ b/api/src/Model/State/Mailbox.php @@ -30,7 +30,7 @@ class Mailbox implements Model } public function getUIDs(): array { - return $this->uids; + return $this->uids ?? []; } public function setId(int $id): Mailbox @@ -65,4 +65,19 @@ class Mailbox implements Model } return $this; } -} \ No newline at end of file + + public function toArray(): array + { + return [ + 'id' => $this->getId(), + 'mailbox_id' => $this->getMailbox()->getId(), + 'date_time' => $this->getDateTime()->format('Y-m-d H:i:s'), + 'count' => $this->getCount(), + 'uids' => $this->getUIDs() + ]; + } + public function jsonSerialize(): mixed + { + return $this->toArray(); + } +} diff --git a/api/src/Model/State/Message.php b/api/src/Model/State/Message.php index c1fab5c..304cdd6 100644 --- a/api/src/Model/State/Message.php +++ b/api/src/Model/State/Message.php @@ -47,4 +47,18 @@ class Message implements Model $this->value = $value; return $this; } -} \ No newline at end of file + + public function toArray(): array + { + return [ + 'id' => $this->getId(), + 'message_id' => $this->getMessage()->getId(), + 'name' => $this->getName(), + 'value' => $this->getValue() + ]; + } + public function jsonSerialize(): mixed + { + return $this->toArray(); + } +} diff --git a/api/src/Repository/Attachment.php b/api/src/Repository/Attachment.php index 568d4b6..d27f59c 100644 --- a/api/src/Repository/Attachment.php +++ b/api/src/Repository/Attachment.php @@ -28,6 +28,20 @@ class Attachment extends Repository return $this; } + public function install(): void + { + $query = " +CREATE TABLE {$this->getTable()} ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `message_id` INT UNSIGNED NOT NULL, + `filename` VARCHAR(255) NOT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY `fk_messages_{$this->getTable()}` (`message_id`) + REFERENCES `messages` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +)"; + $this->getConnection()->query($query); + } + protected function fieldsForInsert(): array { return [ @@ -115,4 +129,11 @@ class Attachment extends Repository $query = "SELECT * FROM {$this->getTable()} WHERE message_id = ?"; return $this->fetchMany($query, [$message_id]); } -} \ No newline at end of file + public function fetchDownloaded(): array + { + $query = "SELECT a.* +FROM `{$this->getTable()}` a JOIN `attachments_states` `as` ON `as`.attachment_id = a.id +WHERE `as`.name = 'downloaded' AND `as`.value = 1"; + return $this->fetchMany($query); + } +} diff --git a/api/src/Repository/Job.php b/api/src/Repository/Job.php index 164d900..0f47ee3 100644 --- a/api/src/Repository/Job.php +++ b/api/src/Repository/Job.php @@ -29,6 +29,21 @@ class Job extends Repository return $this; } + public function install(): void + { + $query = " +CREATE TABLE {$this->getTable()} ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `message_id` INT UNSIGNED NOT NULL, + `date_time` DATETIME NOT NULL, + `executed` INT(1) UNSIGNED DEFAULT 0, + PRIMARY KEY (`id`), + FOREIGN KEY `fk_messages_{$this->getTable()}` (`message_id`) + REFERENCES `messages` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +)"; + $this->getConnection()->query($query); + } + protected function fieldsForUpdate(): array { return $this->fieldsForInsert(); @@ -105,4 +120,4 @@ class Job extends Repository $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 50a0b92..212355d 100644 --- a/api/src/Repository/Mailbox.php +++ b/api/src/Repository/Mailbox.php @@ -28,6 +28,18 @@ class Mailbox extends Repository return $this; } + public function install(): void + { + $query = " +CREATE TABLE {$this->getTable()} ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `name` VARCHAR(100) NOT NULL, + `validity` INT UNSIGNED NOT NULL, + PRIMARY KEY (`id`) +)"; + $this->getConnection()->query($query); + } + protected function fieldsForUpdate(): array { return $this->fieldsForInsert(); @@ -86,4 +98,4 @@ class Mailbox extends Repository $query = "SELECT * FROM `{$this->getTable()}` WHERE `name` = ?"; return $this->fetchOne($query, [$name]); } -} \ No newline at end of file +} diff --git a/api/src/Repository/Message.php b/api/src/Repository/Message.php index 45d192f..11ed659 100644 --- a/api/src/Repository/Message.php +++ b/api/src/Repository/Message.php @@ -3,34 +3,50 @@ namespace ProVM\Emails\Repository; use DateTimeInterface; use PDO; -use PDOException; -use Exception; -use ProVM\Common\Define\Model; use Psr\Log\LoggerInterface; -use ProVM\Common\Implement\Repository; use Safe\DateTimeImmutable; -use Safe\Exceptions\ErrorfuncException; +use ProVM\Common\Define\Model; +use ProVM\Common\Implement\Repository; +use ProVM\Common\Factory; class Message extends Repository { - public function __construct(PDO $connection, LoggerInterface $logger, \ProVM\Common\Factory\Model $factory) + public function __construct(PDO $connection, LoggerInterface $logger, Factory\Model $factory) { parent::__construct($connection, $logger); $this->setTable('messages') ->setFactory($factory); } - protected \ProVM\Common\Factory\Model $factory; - public function getFactory(): \ProVM\Common\Factory\Model + protected Factory\Model $factory; + public function getFactory(): Factory\Model { return $this->factory; } - public function setFactory(\ProVM\Common\Factory\Model $factory): Message + public function setFactory(Factory\Model $factory): Message { $this->factory = $factory; return $this; } + public function install(): void + { + $query = " +CREATE TABLE {$this->getTable()} ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `uid` VARCHAR(255) NOT NULL, + `mailbox_id` INT UNSIGNED NOT NULL, + `position` INT UNSIGNED NOT NULL, + `subject` VARCHAR(255) NOT NULL, + `from` VARCHAR(255) NOT NULL, + `date_time` DATETIME NOT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY `fk_mailboxes_{$this->getTable()}` (`mailbox_id`) + REFERENCES `mailboxes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +)"; + $this->getConnection()->query($query); + } + protected function fieldsForUpdate(): array { return $this->fieldsForInsert(); @@ -114,21 +130,23 @@ class Message extends Repository 'downloaded_attachments', 'scheduled_downloads' ]; + $stateRepository = $this->getFactory()->find(\ProVM\Emails\Model\State\Message::class); foreach ($valid_states as $state_name) { try { $model->getState($state_name); } catch (\Exception $e) { + $this->getLogger()->warning($e); $data = [ 'message_id' => $model->getId(), 'name' => $state_name ]; - $state = $this->getFactory()->find(\ProVM\Emails\Model\State\Message::class)->create($data); + $state = $stateRepository->create($data); $model->addState($state); } } foreach ($model->getStates() as $state) { - $state->setMessage($model); - $this->getFactory()->find(\ProVM\Emails\Model\State\Message::class)->save($state); + //$state->setMessage($model); + $stateRepository->save($state); } } @@ -161,4 +179,9 @@ class Message extends Repository 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')]); } + public function fetchAllBySubjectAndDate(string $subject, DateTimeInterface $dateTime): array + { + $query = "SELECT * FROM `{$this->getTable()}` WHERE `subject` = ? AND `date_time` BETWEEN ? AND ?"; + return $this->fetchMany($query, [$subject, $dateTime->format('Y-m-d 00:00:00'), $dateTime->format('Y-m-d 23:59:59')]); + } } diff --git a/api/src/Repository/State/Attachment.php b/api/src/Repository/State/Attachment.php index 7da6b9b..2240eda 100644 --- a/api/src/Repository/State/Attachment.php +++ b/api/src/Repository/State/Attachment.php @@ -26,6 +26,22 @@ class Attachment extends Repository return $this; } + public function install(): void + { + $query = " +CREATE TABLE {$this->getTable()} ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `attachment_id` INT UNSIGNED NOT NULL, + `name` VARCHAR(100) NOT NULL, + `value` INT(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + FOREIGN KEY `fk_attachments_{$this->getTable()}` (`attachment_id`) + REFERENCES `attachments` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) +"; + $this->getConnection()->query($query); + } + protected function fieldsForInsert(): array { return [ @@ -90,4 +106,4 @@ class Attachment extends Repository $query = "SELECT * FROM `{$this->getTable()}` WHERE `attachment_id` = ? AND `name` = ?"; return $this->fetchOne($query, [$attachment_id, $name]); } -} \ No newline at end of file +} diff --git a/api/src/Repository/State/Mailbox.php b/api/src/Repository/State/Mailbox.php index 7d498d0..e6e03a5 100644 --- a/api/src/Repository/State/Mailbox.php +++ b/api/src/Repository/State/Mailbox.php @@ -28,6 +28,23 @@ class Mailbox extends Repository return $this; } + public function install(): void + { + $query = " +CREATE TABLE {$this->getTable()} ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `mailbox_id` INT UNSIGNED NOT NULL, + `date_time` DATETIME NOT NULL, + `count` INT UNSIGNED NOT NULL, + `uids` TEXT NOT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY `fk_mailboxes_{$this->getTable()}` (`mailbox_id`) + REFERENCES `mailboxes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) +"; + $this->getConnection()->query($query); + } + protected function fieldsForUpdate(): array { return $this->fieldsForInsert(); @@ -99,4 +116,4 @@ class Mailbox extends Repository $query = "SELECT * FROM `{$this->getTable()}` WHERE `mailbox_id` = ? AND `date_time` = ?"; return $this->fetchOne($query, [$mailbox_id, $date_time]); } -} \ No newline at end of file +} diff --git a/api/src/Repository/State/Message.php b/api/src/Repository/State/Message.php index 74acb5b..8859eb8 100644 --- a/api/src/Repository/State/Message.php +++ b/api/src/Repository/State/Message.php @@ -26,6 +26,22 @@ class Message extends Repository return $this; } + public function install(): void + { + $query = " +CREATE TABLE {$this->getTable()} ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `message_id` INT UNSIGNED NOT NULL, + `name` VARCHAR(100) NOT NULL, + `value` INT(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + FOREIGN KEY `fk_messages_{$this->getTable()}` (`message_id`) + REFERENCES `messages` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) +"; + $this->getConnection()->query($query); + } + protected function fieldsForUpdate(): array { return $this->fieldsForInsert(); @@ -90,4 +106,4 @@ class Message extends Repository $query = "SELECT * FROM `{$this->getTable()}` WHERE `message_id` = ? AND `name` = ?"; return $this->fetchOne($query, [$message_id, $name]); } -} \ No newline at end of file +} diff --git a/cli/common/Command/DecryptPdf.php b/cli/common/Command/DecryptPdf.php index 92027b6..e868843 100644 --- a/cli/common/Command/DecryptPdf.php +++ b/cli/common/Command/DecryptPdf.php @@ -1,12 +1,13 @@ getCommunicator()->get('/attachments/pending'); - return \Safe\json_decode($response->getBody()->getContents())->attachments; + return json_decode($response->getBody()->getContents())->attachments; } protected function decrypt(string $attachment): bool { $response = $this->getCommunicator()->put('/attachments/decrypt', ['attachments' => [$attachment]]); - return \Safe\json_decode($response->getBody()->getContents())->status; + return json_decode($response->getBody()->getContents())->status; } public function execute(InputInterface $input, OutputInterface $output) @@ -64,4 +65,4 @@ class DecryptPdf extends Command return Command::SUCCESS; } -} \ No newline at end of file +} diff --git a/cli/common/Command/GrabAttachments.php b/cli/common/Command/GrabAttachments.php index 1055a8c..23295e1 100644 --- a/cli/common/Command/GrabAttachments.php +++ b/cli/common/Command/GrabAttachments.php @@ -1,12 +1,13 @@ getCommunicator()->get('/messages/pending'); - return \Safe\json_decode($response->getBody()->getContents())->messages; + return json_decode($response->getBody()->getContents())->messages; } protected function grabAttachments(int $message_uid): int { $response = $this->getCommunicator()->put('/attachments/grab', ['messages' => [$message_uid]]); - return \Safe\json_decode($response->getBody()->getContents())->attachment_count; + return json_decode($response->getBody()->getContents())->attachment_count; } public function execute(InputInterface $input, OutputInterface $output): int @@ -62,4 +63,4 @@ class GrabAttachments extends Command return Command::SUCCESS; } -} \ No newline at end of file +} diff --git a/cli/common/Command/Messages.php b/cli/common/Command/Messages.php index a09b493..848436e 100644 --- a/cli/common/Command/Messages.php +++ b/cli/common/Command/Messages.php @@ -1,12 +1,13 @@ getCommunicator()->get('/mailboxes/registered'); - return \Safe\json_decode($response->getBody()->getContents())->mailboxes; + return json_decode($response->getBody()->getContents())->mailboxes; } protected function grabMessages(string $mailbox): int { $response = $this->getCommunicator()->put('/messages/grab', ['mailboxes' => [$mailbox]]); - $body = \Safe\json_decode($response->getBody()->getContents()); + $body = json_decode($response->getBody()->getContents()); return $body->message_count; } @@ -63,4 +64,4 @@ class Messages extends Command return Command::SUCCESS; } -} \ No newline at end of file +} diff --git a/emails-2022-11-30-03-59-49.sql b/emails-2022-11-30-03-59-49.sql new file mode 100644 index 0000000..3fd22d9 --- /dev/null +++ b/emails-2022-11-30-03-59-49.sql @@ -0,0 +1,94 @@ +-- Adminer 4.8.1 MySQL 5.5.5-10.9.3-MariaDB-1:10.9.3+maria~ubu2204 dump + +SET NAMES utf8; +SET time_zone = '+00:00'; +SET foreign_key_checks = 0; +SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO'; + +SET NAMES utf8mb4; + +DROP TABLE IF EXISTS `attachments`; +CREATE TABLE `attachments` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `message_id` int(10) unsigned NOT NULL, + `filename` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + KEY `message_id` (`message_id`), + CONSTRAINT `attachments_ibfk_2` FOREIGN KEY (`message_id`) REFERENCES `messages` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + + +DROP TABLE IF EXISTS `attachments_jobs`; +CREATE TABLE `attachments_jobs` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `message_id` int(10) unsigned NOT NULL, + `date_time` datetime NOT NULL, + `executed` int(1) DEFAULT 0, + PRIMARY KEY (`id`), + KEY `message_id` (`message_id`), + CONSTRAINT `attachments_jobs_ibfk_2` FOREIGN KEY (`message_id`) REFERENCES `messages` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + + +DROP TABLE IF EXISTS `attachments_states`; +CREATE TABLE `attachments_states` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `attachment_id` int(10) unsigned NOT NULL, + `name` varchar(100) NOT NULL, + `value` int(1) unsigned NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `attachment_id` (`attachment_id`), + CONSTRAINT `attachments_states_ibfk_1` FOREIGN KEY (`attachment_id`) REFERENCES `attachments` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + + +DROP TABLE IF EXISTS `mailboxes`; +CREATE TABLE `mailboxes` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(100) NOT NULL, + `validity` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + + +DROP TABLE IF EXISTS `mailboxes_states`; +CREATE TABLE `mailboxes_states` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `mailbox_id` int(10) unsigned NOT NULL, + `date_time` datetime NOT NULL, + `count` int(10) unsigned NOT NULL, + `uids` text NOT NULL, + PRIMARY KEY (`id`), + KEY `mailbox_id` (`mailbox_id`), + CONSTRAINT `mailboxes_states_ibfk_2` FOREIGN KEY (`mailbox_id`) REFERENCES `mailboxes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + + +DROP TABLE IF EXISTS `messages`; +CREATE TABLE `messages` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `mailbox_id` int(10) unsigned NOT NULL, + `position` int(10) unsigned NOT NULL, + `uid` varchar(255) NOT NULL, + `subject` varchar(255) NOT NULL, + `from` varchar(100) NOT NULL, + `date_time` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `mailbox_id` (`mailbox_id`), + CONSTRAINT `messages_ibfk_1` FOREIGN KEY (`mailbox_id`) REFERENCES `mailboxes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + + +DROP TABLE IF EXISTS `messages_states`; +CREATE TABLE `messages_states` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `message_id` int(10) unsigned NOT NULL, + `name` varchar(100) NOT NULL, + `value` int(1) unsigned NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `message_id` (`message_id`), + CONSTRAINT `messages_states_ibfk_2` FOREIGN KEY (`message_id`) REFERENCES `messages` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + + +-- 2022-11-30 03:59:49