/ app / Jobs / ProcessCertificationRequestMailbox.php
ProcessCertificationRequestMailbox.php
  1  <?php
  2  
  3  namespace App\Jobs;
  4  
  5  use App\Enums\InboxFileTypeEnum;
  6  use App\Helpers\InboxFileSaver;
  7  use App\Http\Requests\InboxSftpRequest;
  8  use App\Http\Services\AutoRequirementService;
  9  use App\Http\Services\CertificationRequestService;
 10  use App\Http\Services\TaskService;
 11  use App\Mail\CertificationRequestProcessed;
 12  use App\Mail\CertificationRequestResponse;
 13  use App\Models\User;
 14  use Carbon\Carbon;
 15  use Exception;
 16  use Illuminate\Support\Facades\Mail;
 17  use Illuminate\Support\Facades\Storage;
 18  use Illuminate\Support\Facades\Validator;
 19  use Webklex\IMAP\Facades\Client;
 20  
 21  class ProcessCertificationRequestMailbox
 22  {
 23      public string $sender = "";
 24      public string $filename = "";
 25      public string $username = "";
 26  
 27      protected function rules()
 28      {
 29          return (new InboxSftpRequest())->rules();
 30      }
 31  
 32      private function sendError($message, $errors)
 33      {
 34          $subject = $message->getSubject();
 35          $attachmentNames = [];
 36          foreach ($message->getAttachments() as $attachment) {
 37              $attachmentNames[] = $attachment->getName();
 38          }
 39  
 40          $mailable = new CertificationRequestResponse($this->sender, $subject, $attachmentNames, $errors);
 41          Mail::to($this->sender)->send($mailable);
 42      }
 43  
 44      public function validateMailAttachments($message)
 45      {
 46          $attachment = null;
 47  
 48          try {
 49              $attachments = $message->getAttachments();
 50  
 51              if (count($attachments) === 0) {
 52                  throw new Exception('No attachments were found');
 53              }
 54  
 55              if (count($attachments) > 1) {
 56                  throw new Exception('Only one attachment is allowed');
 57              }
 58  
 59              $attachment = $attachments[0];
 60              $extension = pathinfo($attachment->getName(), PATHINFO_EXTENSION);
 61              if (!in_array($extension, ['zip', 'gpg', 'pgp'])) {
 62                  throw new Exception('Only zip files are accepted');
 63              }
 64          } catch (Exception $e) {
 65              $this->sendError($message, [$e->getMessage()]);
 66              $attachment = null;
 67          }
 68  
 69          return $attachment;
 70      }
 71  
 72      public function processMessage($message)
 73      {
 74          // do not mark as seen when testing
 75          if (config('app.env') === 'production') {
 76              $message->setFlag('Seen');
 77          }
 78  
 79          // get the sender
 80          $this->sender = $message->getFrom()[0]->mail;
 81          $user = User::where('email', $this->sender)->first();
 82          if (!$user) {
 83              $this->sendError($message, ['User ' . $this->sender . ' is not enabled to perform this action']);
 84              return null;
 85          }
 86  
 87          // get the attachment
 88          $attachment = $this->validateMailAttachments($message);
 89          if (!$attachment) {
 90              return;
 91          }
 92  
 93          $path = 'inbox_files/' . $attachment->getName();
 94          $attachment->save(Storage::path('inbox_files/'));
 95  
 96          // validate the document
 97          $validator = Validator::make(
 98              [
 99                  'username' => $user->username,
100                  'filename' => $path,
101              ],
102              $this->rules()
103          );
104          if ($validator->fails()) {
105              $this->sendError($message, $validator->errors()->all());
106              return;
107          }
108  
109          // TODO: save the file and create the dossier if necessary
110          $dossierId = (new InboxSftpRequest)->getDossierId($path);
111          $inboxFileSaver = new InboxFileSaver($path, $user->id, $dossierId);
112          $inboxFileSaver->decrypt();
113  
114          if ($inboxFileSaver->type === InboxFileTypeEnum::certification_request()  && !$dossierId) {
115              $certificationRequest = new CertificationRequestService($inboxFileSaver->path, $user->id);
116              $errors = $certificationRequest->readAndValidate();
117              if (count($errors) > 0) {
118                  $this->sendError($message, $errors);
119                  return null;
120              }
121  
122              $dossier = $certificationRequest->build(false);
123              $dossierId = $dossier->id;
124          }
125  
126          $inboxFile = $inboxFileSaver->save($dossierId);
127  
128          AutoRequirementService::run($inboxFile->validation);
129          TaskService::create(
130              [
131                  'title' => 'New validation received',
132                  'description' => 'Please, assign a reviewer to validation.',
133                  'start_date' => Carbon::now(),
134                  'end_date' => null
135              ],
136              [
137                  'user_id' => $inboxFile->reviewer_id
138              ]
139          );
140  
141  
142          Mail::to($this->sender)->send(
143              new CertificationRequestProcessed(
144                  $this->sender,
145                  $message->getSubject(),
146                  [$attachment->getName()]
147              )
148          );
149      }
150  
151      public function handle()
152      {
153          try {
154              $client = Client::account('default');
155              $client->connect();
156          } catch (Exception $e) {
157              logger()->info('Error connecting to mailbox');
158              return;
159          }
160  
161          $messages = $client->getFolderByPath('INBOX')
162              ->messages()
163              ->where(['subject' => 'Certification Request'])
164              ->unseen()
165              ->get();
166  
167          logger()->info('Found ' . count($messages) . ' messages');
168  
169          foreach ($messages as $message) {
170              $this->processMessage($message);
171          }
172      }
173  }