clrghouz/app/Jobs/MessageProcess.php

344 lines
11 KiB
PHP

<?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\Facades\Log;
use Illuminate\Support\Facades\Notification;
use App\Classes\FTN\Message;
use App\Models\{Address,Echoarea,Echomail,Netmail,Setup,User};
use App\Notifications\Netmails\{EchoareaNotExist,EchoareaNotSubscribed,EchoareaNoWrite,NetmailForward,Reject};
class MessageProcess implements ShouldQueue
{
private const LOGKEY = 'JMP';
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private Address $sender;
private Message $msg;
private Address $pktsrc;
private Carbon $recvtime;
private bool $skipbot;
private string $packet;
public function __construct(Message $msg,string $packet,Address $sender,Address $pktsrc,Carbon $recvtime,bool $skipbot=FALSE)
{
$this->msg = $msg;
$this->packet = $packet;
$this->sender = $sender;
$this->pktsrc = $pktsrc;
$this->recvtime = $recvtime;
$this->skipbot = $skipbot;
}
/**
* When calling MessageProcess - we assume that the packet is from a valid source, and
* the destination (netmail/echomail) is also valid
*/
public function handle()
{
// Load our details
$ftns = Setup::findOrFail(config('app.id'))->system->addresses;
// If we are a netmail
if ($this->msg->isNetmail()) {
Log::info(sprintf('%s:- Processing Netmail [%s] to (%s) [%s] from (%s) [%s].',
self::LOGKEY,
$this->msg->msgid,
$this->msg->user_to,$this->msg->tftn,
$this->msg->user_from,$this->msg->fftn,
));
// @todo Enable checks to reject old messages
// Check for duplicate messages
// FTS-0009.001
if ($this->msg->msgid) {
$o = Netmail::where('msgid',$this->msg->msgid)
->where('fftn_id',($x=$this->msg->fboss_o) ? $x->id : NULL)
->where('datetime','>',Carbon::now()->subYears(3))
->single();
Log::debug(sprintf('%s:- Checking for duplicate from host id [%d].',self::LOGKEY,($x=$this->msg->fboss_o) ? $x->id : NULL));
if ($o) {
Log::alert(sprintf('%s:! Duplicate netmail [%s] in [%s] from (%s) [%s] to (%s) - ignorning.',
self::LOGKEY,
$this->msg->msgid,
$this->msg->echoarea,
$this->msg->user_from,$this->msg->fftn,
$this->msg->user_to,
));
if (! $o->msg_crc)
$o->msg_crc = md5($this->msg->message);
$o->save();
return;
}
}
// @todo Enable checks to see if this is a file request or file send
$o = new Netmail;
$o->to = $this->msg->user_to;
$o->from = $this->msg->user_from;
$o->fftn_id = ($x=$this->msg->fftn_o) ? $x->id : NULL;
$o->tftn_id = ($x=$this->msg->tftn_o) ? $x->id : NULL;
$o->datetime = $this->msg->date;
$o->tzoffset = $this->msg->date->utcOffset();
$o->flags = $this->msg->flags;
$o->cost = $this->msg->cost;
$o->msgid = $this->msg->msgid;
$o->subject = $this->msg->subject;
$o->msg = $this->msg->message;
foreach ($this->msg->via as $v)
$o->msg .= sprintf("\01Via %s\r",$v);
$o->msg_src = $this->msg->message_src;
$o->msg_crc = md5($this->msg->message);
$o->set_pkt = $this->packet;
$o->set_sender = $this->sender;
$o->set_path = $this->msg->pathaddress;
$o->set_recvtime = $this->recvtime;
// Strip any local/transit flags
$o->flags &= ~(Message::FLAG_LOCAL|Message::FLAG_INTRANSIT);
// Determine if the message is to this system, or in transit
if ($ftns->search(function($item) { return $this->msg->tftn === $item->ftn; }) !== FALSE) {
// @todo Check if it is a duplicate message
// @todo Check if the message is from a system we know about
$processed = FALSE;
// If the message is to a bot, we'll process it
if (! $this->skipbot)
foreach (config('process.robots') as $class) {
if ($processed=$class::handle($this->msg)) {
$o->flags |= Message::FLAG_RECD;
$o->save();
Log::info(sprintf('%s:= Netmail [%s] from (%s:%s) - was processed by us internally [%d]',
self::LOGKEY,
$this->msg->msgid,
$this->msg->user_from,
$this->msg->fftn,
$o->id,
));
break;
}
}
if (! $processed) {
// Check if the netmail is to a user, with netmail forwarding enabled
$uo = User::active()
->where(function($query) {
return $query->whereRaw(sprintf("LOWER(name)='%s'",strtolower($this->msg->user_to)))
->orWhereRaw(sprintf("LOWER(alias)='%s'",strtolower($this->msg->user_to)));
})
->whereNotNull('system_id')
->single();
if ($uo && ($ao=$uo->system->match($this->msg->tftn_o->zone)?->pop())) {
$note = "+--[ FORWARDED MESSAGE ]----------------------------------+\r";
$note .= "+ This message has been forwarded to you, it was originally sent to you\r";
$note .= sprintf("+ at [%s]\r",$this->msg->tftn_o->ftn);
$note .= "+---------------------------------------------------------+\r\r";
$o->msg = $note.$this->msg->message;
$o->tftn_id = $ao->id;
$o->flags |= Message::FLAG_INTRANSIT;
$o->save();
$processed = TRUE;
// Dont send an advisement to an areabot
if (! in_array(strtolower($this->msg->user_from),config('app.areabots')))
Notification::route('netmail',$this->msg->fftn_o)->notify(new NetmailForward($this->msg,$ao));
// We'll ignore messages from *fix users
} elseif (in_array(strtolower($this->msg->user_from),config('app.areabots'))) {
$o->flags |= Message::FLAG_RECD;
$o->save();
Log::alert(sprintf('%s:! Ignoring Netmail [%s] to the Hub from (%s:%s) - its from a bot [%d]',
self::LOGKEY,
$this->msg->msgid,
$this->msg->user_from,
$this->msg->fftn,
$o->id,
));
$processed = TRUE;
}
}
// If not processed, no users here!
if (! $processed) {
Log::alert(sprintf('%s:! Netmail to the Hub from (%s) [%s] but no users here.',self::LOGKEY,$this->msg->user_from,$this->msg->fftn));
Notification::route('netmail',$this->msg->fftn_o)->notify(new Reject($this->msg));
}
// If in transit, store for collection
} else {
// @todo Check if the message is to a system we know about
// @todo In transit loop checking
// @todo In transit TRACE response
$o->flags |= Message::FLAG_INTRANSIT;
$o->save();
Log::info(sprintf('%s:= Netmail [%s] in transit to (%s:%s) from (%s:%s) [%d].',
self::LOGKEY,
$this->msg->msgid,
$this->msg->user_to,$this->msg->tftn,
$this->msg->user_from,$this->msg->fftn,
$o->id,
));
}
// Else we are echomail
} else {
Log::debug(sprintf('%s:- Looking for echomail area [%s] for mail from [%s]',self::LOGKEY,$this->msg->echoarea,$this->msg->fboss));
if (! $this->msg->fboss_o) {
Log::error(sprintf('%s:! Cannot process message for echomail area [%s] for mail from [%s] with msgid [%s] - no boss object?',self::LOGKEY,$this->msg->echoarea,$this->msg->fboss,$this->msg->msgid));
return;
}
$ea = Echoarea::where('name',strtoupper($this->msg->echoarea))
->where('domain_id',$this->msg->fboss_o->zone->domain_id)
->single();
if (! $ea) {
Log::alert(sprintf('%s:! Echoarea [%s] doesnt exist for zone [%d]',self::LOGKEY,$this->msg->echoarea,$this->msg->fboss_o->zone->zone_id));
Notification::route('netmail',$this->pktsrc)->notify(new EchoareaNotExist($this->msg));
return;
}
Log::debug(sprintf('%s:- Processing echomail [%s] in [%s].',self::LOGKEY,$this->msg->msgid,$this->msg->echoarea));
// Check for duplicate messages
// FTS-0009.001
if ($this->msg->msgid) {
$o = Echomail::where('msgid',$this->msg->msgid)
->where('fftn_id',($x=$this->msg->fboss_o) ? $x->id : NULL)
->where('datetime','>',Carbon::now()->subYears(3))
->single();
Log::debug(sprintf('%s:- Checking for duplicate from host id [%d].',self::LOGKEY,($x=$this->msg->fboss_o) ? $x->id : NULL));
if ($o) {
Log::alert(sprintf('%s:! Duplicate echomail [%s] in [%s] from (%s) [%s] to (%s) - updating seenby.',
self::LOGKEY,
$this->msg->msgid,
$this->msg->echoarea,
$this->msg->user_from,$this->msg->fftn,
$this->msg->user_to,
));
if (! $o->msg_crc)
$o->msg_crc = md5($this->msg->message);
$o->save();
// If the path is empty, then its probably because of the previous bug, we'll replace it.
// @todo This duplicate message may have gone via a different path, be nice to record it.
//$o->path()->sync($o->path->pluck('id')->merge($this->msg->pathaddress)->toArray());
// @todo if we have an export for any of the seenby addresses, remove it
// @todo add received packet details
$o->seenby()->sync($o->seenby->pluck('id')->merge($this->msg->seenaddress)->filter()->toArray());
return;
}
}
// Find another message with the same msg_crc
if ($this->msg->message) {
$o = Echomail::where('msg_crc',$xx=md5($this->msg->message))
->where('fftn_id',($x=$this->msg->fboss_o) ? $x->id : NULL)
->where('datetime','>',Carbon::now()->subWeek())
->get();
if ($o->count())
Log::alert(sprintf('%s:! Duplicate message CRC [%s] in [%s].',
self::LOGKEY,
$xx,
$o->pluck('id')->join('|')
));
}
// @todo Can the sender create it if it doesnt exist?
// Can the system send messages to this area?
if (! $ea->sec_write || ($this->pktsrc->security < $ea->sec_write)) {
Notification::route('netmail',$this->pktsrc)->notify(new EchoareaNoWrite($this->msg));
return;
}
// If the node is not subscribed
if ($this->pktsrc->echoareas->search(function($item) use ($ea) { return $item->id === $ea->id; }) === FALSE)
Notification::route('netmail',$this->pktsrc)->notify(new EchoareaNotSubscribed($this->msg));
// We know about this area, store it
$o = new Echomail;
$o->init();
$o->to = $this->msg->user_to;
$o->from = $this->msg->user_from;
$o->subject = $this->msg->subject;
$o->datetime = $this->msg->date;
$o->tzoffset = $this->msg->date->utcOffset();
$o->fftn_id = ($x=$this->msg->fboss_o) ? $x->id : NULL; // @todo This should be the node that originated the message - but since that node is not in the DB it would be null
$o->echoarea_id = $ea->id;
$o->msgid = $this->msg->msgid;
$o->replyid = $this->msg->replyid;
$o->msg = $this->msg->message_src."\r";
$o->msg_src = $this->msg->message_src;
$o->msg_crc = md5($this->msg->message);
$o->rogue_seenby = $this->msg->rogue_seenby;
$o->rogue_path = $this->msg->rogue_path;
$o->set_path = $this->msg->pathaddress;
$o->set_seenby = $this->msg->seenaddress;
$o->set_recvtime = $this->recvtime;
// Record receiving packet and sender
$o->set_pkt = $this->packet;
$o->save();
Log::info(sprintf('%s:= Echomail [%s] in [%s] from (%s) [%s] to (%s) - [%s].',
self::LOGKEY,
$this->msg->msgid,
$this->msg->echoarea,
$this->msg->user_from,$this->msg->fftn,
$this->msg->user_to,
$o->id,
));
// If the message is to a bot, but not rescanned, or purposely skipbot set, we'll process it
if ((! $this->skipbot) && (! $this->msg->rescanned->count()))
foreach (config('process.echomail') as $class) {
if ($class::handle($this->msg)) {
break;
}
}
}
}
}