<?php namespace App\Jobs; use Carbon\Carbon; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; use Illuminate\Support\Facades\Storage; use League\Flysystem\UnableToMoveFile; use App\Classes\File; use App\Classes\FTN\Packet; use App\Exceptions\InvalidPacketException; use App\Models\{Echomail,Netmail,System}; use App\Notifications\Netmails\PacketPasswordInvalid; class PacketProcess implements ShouldQueue { private const LOGKEY = 'JPP'; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; private string $filename; private System $so; private Carbon $rcvd_time; private bool $interactive; private bool $nobot; public function __construct(string $filename,System $so,bool $interactive=TRUE,Carbon $rcvd_time=NULL,bool $nobot=FALSE) { $this->filename = $filename; $this->so = $so->withoutRelations(); $this->interactive = $interactive; $this->rcvd_time = $rcvd_time ?: Carbon::now(); $this->nobot = $nobot; } public function __get($key): mixed { switch ($key) { case 'jobname': return $this->filename; default: return NULL; } } /** * When calling MessageProcess - we assume that the packet is from a valid source, and * the destination (netmail/echomail) is also valid */ public function handle() { Log::info(sprintf('%s:- Processing mail [%s]',self::LOGKEY,$this->filename)); $fs = Storage::disk(config('fido.local_disk')); // @todo Catch files that we cannot process, eg: ARJ bundles. try { $f = new File($fs->path($this->filename)); $processed = FALSE; $bad_archive = FALSE; foreach ($f as $packet) { try { $pkt = Packet::process($packet,Arr::get(stream_get_meta_data($packet),'uri'),$f->itemSize(),$this->so); // Check that the packet is from a system that is defined in the DB if (! $pkt->fftn) { Log::error(sprintf('%s:! Packet [%s] is not from a system we know about? [%s]',self::LOGKEY,$this->filename,$pkt->fftn_t)); break; } if (! our_nodes($pkt->fftn->zone->domain)->contains($pkt->fftn)) { Log::error(sprintf('%s:! Packet [%s] is from a system that is not configured with us? [%s]',self::LOGKEY,$this->filename,$pkt->fftn_t)); // @todo Notification::route('netmail',$pkt->fftn)->notify(new UnexpectedPacketFromYou($this->filename)); // @todo Parse the packet for netmails and process them. We'll only accept netmails to us, and ignore all others break; } // If we dont have the tftn in the DB, then packet cannot be to us if (! $pkt->tftn) { Log::error(sprintf('%s:! Packet [%s] is from a system [%s] we dont know about?',self::LOGKEY,$this->filename,$pkt->tftn_t)); // @todo Notification::route('netmail',$pkt->fftn)->notify(new UnexpectedPacketToUs($this->filename)); break; } // Check the packet is to our address, if not we'll reject it. if (! our_address($pkt->tftn->zone->domain)->contains($pkt->tftn)) { Log::error(sprintf('%s:! Packet [%s] is not to our address? [%s]',self::LOGKEY,$this->filename,$pkt->tftn->ftn)); // @todo Notification::route('netmail',$pkt->fftn)->notify(new UnexpectedPacketToUs($this->filename)); break; } // Check the packet password if ($pkt->fftn->pass_packet !== strtoupper($pkt->password)) { Log::error(sprintf('%s:! Packet from [%s] with password [%s] is invalid.',self::LOGKEY,$pkt->fftn->ftn,$pkt->password)); Notification::route('netmail',$pkt->fftn)->notify(new PacketPasswordInvalid($pkt->password,$f->pktName())); break; } Log::info(sprintf('%s:- Packet has [%d] messages',self::LOGKEY,$pkt->count())); // Queue messages if there are too many in the packet. if ($queue = ($pkt->count() > config('fido.queue_msgs'))) Log::info(sprintf('%s:- Messages will be sent to the queue for processing',self::LOGKEY)); $count = 0; foreach ($pkt as $msg) { if ($msg instanceof Netmail) Log::info(sprintf('%s:- Netmail from [%s] to [%s]',self::LOGKEY,$msg->fftn->ftn,$msg->tftn->ftn)); elseif ($msg instanceof Echomail) Log::info(sprintf('%s:- Echomail from [%s]',self::LOGKEY,$msg->fftn->ftn)); if ($msg->errors->count()) { Log::error(sprintf('%s:! Message [%s] has [%d] errors, unable to process',self::LOGKEY,$msg->msgid,$msg->errors->count())); continue; } $msg->set_sender = $pkt->fftn->withoutRelations(); // Record receiving packet and sender $msg->set_pkt = $f->pktName(); $msg->set_recvtime = $this->rcvd_time; if ($queue || (! $this->interactive)) Log::info(sprintf('%s:! Message [%s] will be sent to the queue to process',self::LOGKEY,$msg->msgid)); try { // Dispatch job. if ($queue || (! $this->interactive)) MessageProcess::dispatch($msg->withoutRelations(),$this->nobot); else MessageProcess::dispatchSync($msg->withoutRelations(),$this->nobot); $count++; } catch (\Exception $e) { Log::error(sprintf('%s:! Got error [%s] dispatching message [%s] (%d:%s-%s).',self::LOGKEY,get_class($e),$msg->msgid,$e->getLine(),$e->getFile(),$e->getMessage())); } } if ($count === $pkt->count()) $processed = TRUE; } catch (\Exception $e) { Log::error(sprintf('%s:! Got an exception [%s] processing packet',self::LOGKEY,$e->getMessage())); $bad_archive = TRUE; } } if ((! $processed) || $bad_archive) { Log::alert(sprintf('%s:- Not deleting packet [%s], it doesnt seem to be processed?',self::LOGKEY,$this->filename)); } else { // If we want to keep the packet, we could do that logic here if (config('fido.packet_keep')) { $dir = sprintf('%s/%s/%s/%s',config('fido.dir'),($x=Carbon::now())->format('Y'),$x->format('m'),$x->format('d')); Log::debug(sprintf('%s:- Moving processed packet [%s] to [%s]',self::LOGKEY,$this->filename,$dir)); try { if ($fs->makeDirectory($dir)) { $fs->move($this->filename,$x=sprintf('%s/%s',$dir,$f->itemName())); Log::info(sprintf('%s:- Moved processed packet [%s] to [%s]',self::LOGKEY,$this->filename,$x)); } else Log::error(sprintf('%s:! Unable to create dir [%s]',self::LOGKEY,$dir)); } catch (UnableToMoveFile $e) { Log::error(sprintf('%s:! Unable to move packet [%s] to [%s] (%s)',self::LOGKEY,$this->filename,$dir,$e->getMessage())); } catch (\Exception $e) { Log::error(sprintf('%s:! Failed moving packet [%s] to [%s] (%s)',self::LOGKEY,$this->filename,$dir,$e->getMessage())); } } else { Log::debug(sprintf('%s:- Deleting processed packet [%s]',self::LOGKEY,$this->filename)); $fs->delete($this->filename); } } } catch (InvalidPacketException $e) { Log::error(sprintf('%s:- Not deleting packet [%s], as it generated an InvalidPacketException',self::LOGKEY,$this->filename),['e'=>$e->getMessage()]); } catch (\Exception $e) { Log::error(sprintf('%s:- Not deleting packet [%s], as it generated an uncaught exception',self::LOGKEY,$this->filename),['e'=>$e->getMessage(),'l'=>$e->getLine(),'f'=>$e->getFile()]); } } }