clrghouz/app/Models/Echomail.php
Deon George 81f59dcbb8
All checks were successful
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 42s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 1m50s
Create Docker Image / Final Docker Image Manifest (push) Successful in 11s
Remove EncodeUTF8 infavour of using attribute casting only. The implementation of EncodeUTF8 was not correct, essentially removing any previous casting causing issues when saving a record.
2024-06-01 10:46:02 +10:00

251 lines
7.4 KiB
PHP

<?php
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Casts\{CollectionOrNull,CompressedStringOrNull,UTF8StringOrNull};
use App\Classes\FTN\Message;
use App\Interfaces\Packet;
use App\Traits\{MessageAttributes,MsgID,ParseAddresses,QueryCacheableConfig};
final class Echomail extends Model implements Packet
{
use SoftDeletes,MessageAttributes,MsgID,ParseAddresses,QueryCacheableConfig;
private const LOGKEY = 'ME-';
private bool $no_export = FALSE;
private const kludges = [
'MSGID:'=>'msgid',
'PATH:'=>'set_path',
'REPLY:'=>'replyid',
'SEEN-BY:'=>'set_seenby',
];
// When generating a packet for this echomail, the packet recipient is our tftn
public Address $tftn;
protected $casts = [
'to' => UTF8StringOrNull::class,
'from' => UTF8StringOrNull::class,
'subject' => UTF8StringOrNull::class,
'origin' => UTF8StringOrNull::class,
'tearline' => UTF8StringOrNull::class,
'tagline' => UTF8StringOrNull::class,
'datetime' => 'datetime:Y-m-d H:i:s',
'kludges' => CollectionOrNull::class,
'msg' => CompressedStringOrNull::class,
'msg_src' => CompressedStringOrNull::class,
'rogue_seenby' => CollectionOrNull::class,
'rogue_path' => CollectionOrNull::class, // @deprecated?
];
public function __set($key,$value)
{
switch ($key) {
case 'kludges':
if (! count($value))
return;
if (array_key_exists($value[0],self::kludges)) {
$this->{self::kludges[$value[0]]} = $value[1];
} else {
$this->kludges->put($value[0],$value[1]);
}
break;
case 'no_export':
$this->{$key} = $value;
break;
case 'set_fftn':
// Values that we pass to boot() to record how we got this echomail
case 'set_pkt':
case 'set_recvtime':
case 'set_sender':
// @todo We'll normalise these values when saving the netmail
case 'set_tagline':
case 'set_tearline':
case 'set_origin':
// For us to record the echoarea the message is for, if the area isnt defined (eg: packet dump)
case 'set_echoarea':
$this->set->put($key,$value);
break;
// The path and seenby the echomail went through to get here
case 'set_path':
case 'set_seenby':
if (! $this->set->has($key))
$this->set->put($key,collect());
$this->set->get($key)->push($value);
break;
default:
parent::__set($key,$value);
}
}
public static function boot()
{
parent::boot();
static::creating(function($model) {
if (isset($model->errors) && $model->errors->count())
throw new \Exception('Cannot save, validation errors exist');
});
// @todo if the message is updated with new SEEN-BY's from another route, we'll delete the pending export for systems (if there is one)
static::created(function($model) {
$rogue = collect();
$seenby = collect();
$path = collect();
// Parse PATH
if ($model->set->has('set_path'))
$path = self::parseAddresses('path',$model->set->get('set_path'),$model->fftn->zone,$rogue);
Log::debug(sprintf('%s:^ Message [%d] from point address is [%d]',self::LOGKEY,$model->id,$model->fftn->point_id));
// Make sure our sender is first in the path
if ((! $model->isFlagSet(Message::FLAG_LOCAL)) && (! $path->contains($model->fftn_id))) {
Log::alert(sprintf('%s:? Echomail adding sender to start of PATH [%s].',self::LOGKEY,$model->fftn_id));
$path->prepend($model->fftn_id);
}
// Make sure our pktsrc is last in the path
if ($model->set->has('set_sender') && (! $path->contains($model->set->get('set_sender')->id))) {
Log::alert(sprintf('%s:? Echomail adding pktsrc to end of PATH [%s].',self::LOGKEY,$model->set->get('set_sender')->ftn));
$path->push($model->set->get('set_sender')->id);
}
// Save the Path
$ppoid = NULL;
foreach ($path as $aoid) {
$po = DB::select('INSERT INTO echomail_path (echomail_id,address_id,parent_id) VALUES (?,?,?) RETURNING id',[
$model->id,
$aoid,
$ppoid,
]);
$ppoid = $po[0]->id;
}
$rogue = collect();
// @todo move the parseAddress processing into Message::class, and our address to the seenby (and thus no need to add it when we export)
// Parse SEEN-BY
if ($model->set->has('set_seenby'))
$seenby = self::parseAddresses('seenby',$model->set->get('set_seenby'),$model->fftn->zone,$rogue);
// Make sure our sender is in the seenby
if ((! $model->isFlagSet(Message::FLAG_LOCAL)) && (! $seenby->contains($model->fftn_id))) {
Log::alert(sprintf('%s:? Echomail adding sender to SEENBY [%s].',self::LOGKEY,$model->fftn_id));
$seenby->push($model->fftn_id);
}
// Make sure our pktsrc is in the seenby
if ($model->set->has('set_sender') && (! $seenby->contains($model->set->get('set_sender')->id))) {
Log::alert(sprintf('%s:? Echomail adding pktsrc to SEENBY [%s].',self::LOGKEY,$model->set->get('set_sender')->ftn));
$seenby->push($model->set->get('set_sender')->id);
}
if (count($rogue)) {
$model->rogue_seenby = $rogue;
$model->save();
}
if ($seenby)
$model->seenby()->sync($seenby);
// Our last node in the path is our sender
if ($model->set->has('set_pkt') && $model->set->has('set_recvtime')) {
if ($path->count()) {
DB::update('UPDATE echomail_path set recv_pkt=?,recv_at=? where address_id=? and echomail_id=?',[
$model->set->get('set_pkt'),
$model->set->get('set_recvtime'),
$path->last(),
$model->id,
]);
} else {
Log::critical(sprintf('%s:! Wasnt able to set packet details for [%d] to [%s] to [%s], no path information',self::LOGKEY,$model->id,$model->set->get('set_pkt'),$model->set->get('set_recvtime')));
}
}
// See if we need to export this message.
if ($model->echoarea->sec_read) {
$exportto = $model
->echoarea
->addresses
->filter(function($item) use ($model) { return $model->echoarea->can_read($item->security); })
->pluck('id')
->diff(our_address($model->fftn->zone->domain)->pluck('id'))
->diff($seenby);
if ($exportto->count()) {
if ($model->no_export) {
Log::alert(sprintf('%s:- NOT processing exporting of message by configuration [%s] to [%s]',self::LOGKEY,$model->id,$exportto->join(',')));
return;
}
Log::info(sprintf('%s:- Exporting message [%s] to [%s]',self::LOGKEY,$model->id,$exportto->join(',')));
// Save the seenby for the exported systems
$model->seenby()->syncWithPivotValues($exportto,['export_at'=>Carbon::now()],FALSE);
}
}
});
}
/* RELATIONS */
public function echoarea()
{
return $this->belongsTo(Echoarea::class);
}
public function fftn()
{
return $this->belongsTo(Address::class)
->withTrashed();
}
public function seenby()
{
return $this->belongsToMany(Address::class,'echomail_seenby')
->withPivot(['export_at','sent_at','sent_pkt'])
->FTN2DOrder();
}
public function path()
{
return $this->belongsToMany(Address::class,'echomail_path')
->withPivot(['id','parent_id','recv_pkt','recv_at'])
->orderBy('id','DESC');
}
/* ATTRIBUTES */
public function getSeenByAttribute(): Collection
{
return ((! $this->exists) && $this->set->has('set_seenby'))
? $this->set->get('set_seenby')
: $this->getRelationValue('seenby');
}
public function getPathAttribute(): Collection
{
return ((! $this->exists) && $this->set->has('set_path'))
? $this->set->get('set_path')
: $this->getRelationValue('path');
}
}