Compare commits

..

2 Commits

Author SHA1 Message Date
6c7da7a220 When decompressing compressed messages, dont barf if we try to decompress the same attribute twice
All checks were successful
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 39s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 1m47s
Create Docker Image / Final Docker Image Manifest (push) Successful in 9s
2024-05-13 17:06:51 +10:00
b008cb20d2 Fix recording of netmails, when they contain taglines and origin lines 2024-05-13 17:06:51 +10:00
63 changed files with 1709 additions and 1898 deletions

View File

@ -93,13 +93,6 @@ jobs:
- name: Code Checkout
uses: actions/checkout@v4
- name: Record version
run: |
pwd
ls -al
echo ${GITHUB_SHA::8} > VERSION
cat VERSION
- name: Build and Push Docker Image
uses: docker/build-push-action@v5
with:

View File

@ -11,7 +11,7 @@ abstract class FTN
public function __get(string $key)
{
switch ($key) {
case 'fftn_t':
case 'fftn':
return sprintf('%d:%d/%d.%d',
$this->fz,
$this->fn,
@ -19,7 +19,7 @@ abstract class FTN
$this->fp,
).($this->zone ? sprintf('@%s',$this->zone->domain->name) : '');
case 'tftn_t':
case 'tftn':
return sprintf('%d:%d/%d.%d',
$this->tz,
$this->tn,
@ -27,16 +27,30 @@ abstract class FTN
$this->tp,
).($this->zone ? sprintf('@%s',$this->zone->domain->name) : '');
case 'fftn':
return Address::findFTN($this->fftn_t);
case 'tftn':
return Address::findFTN($this->tftn_t);
case 'fftn_o':
return Address::findFTN($this->fftn);
case 'tftn_o':
return Address::findFTN($this->tftn);
default:
throw new \Exception('Unknown key: '.$key);
}
}
/**
* Determine if a line is a kludge line.
*
* @param string $kludge
* @param string $string
* @return string
*/
protected function kludge(string $kludge,string $string)
{
return (preg_match("/^{$kludge}/",$string))
? chop(preg_replace("/^{$kludge}/",'',$string),"\r")
: FALSE;
}
/**
* This function creates our unpack header
*

View File

@ -1,6 +1,6 @@
<?php
namespace App\Exceptions;
namespace App\Classes\FTN;
use Exception;

File diff suppressed because it is too large Load Diff

View File

@ -10,30 +10,23 @@ use Illuminate\Support\Facades\Notification;
use Symfony\Component\HttpFoundation\File\File;
use App\Classes\FTN as FTNBase;
use App\Exceptions\InvalidPacketException;
use App\Models\{Address,Domain,Echomail,Netmail,Software,Zone};
use App\Models\{Address,Domain,Software,System,Zone};
use App\Notifications\Netmails\EchomailBadAddress;
/**
* Represents a Fidonet Packet, that contains an array of messages.
*
* Thus this object is iterable as an array of Echomail::class or Netmail::class.
* Thus this object is iterable as an array of Message::class.
*/
abstract class Packet extends FTNBase implements \Iterator, \Countable
class Packet extends FTNBase implements \Iterator, \Countable
{
private const LOGKEY = 'PKT';
protected const PACKED_MSG_LEAD = "\02\00";
protected const PACKED_END = "\00\00";
// @todo Rename this regex to something more descriptive, ie: FILENAME_REGEX
public const regex = '([[:xdigit:]]{4})(?:-(\d{4,10}))?-(.+)';
/**
* Packet types we support, in specific order for auto-detection to work
*
* @var string[]
*/
public const PACKET_TYPES = [
'2.2' => FTNBase\Packet\FSC45::class,
'2+' => FTNBase\Packet\FSC48::class,
@ -42,28 +35,114 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
];
protected array $header; // Packet Header
protected ?string $name = NULL; // Packet name
protected ?string $name; // Packet name
public File $file; // Packet filename
protected Address $fftn_p; // Address the packet is from (when packing messages)
protected Address $tftn_p; // Address the packet is to (when packing messages)
protected Collection $messages; // Messages in the Packet
public Collection $messages; // Messages in the Packet
public Collection $errors; // Messages that fail validation
protected int $index; // Our array index
protected $pass_p = NULL; // Overwrite the packet password (when packing messages)
/* ABSTRACT */
/**
* This function is intended to be implemented in child classes to test if the packet
* is defined by the child object
*
* @see self::PACKET_TYPES
* @param string $header
* @return bool
* @param string|null $header
* @throws \Exception
*/
abstract public static function is_type(string $header): bool;
abstract protected function header(): string;
public function __construct(string $header=NULL)
{
$this->messages = collect();
$this->errors = collect();
$this->domain = NULL;
$this->name = NULL;
if ($header)
$this->header = unpack(self::unpackheader(static::HEADER),$header);
}
/**
* @throws \Exception
*/
public function __get($key)
{
switch ($key) {
// From Addresses
case 'fz': return Arr::get($this->header,'ozone');
case 'fn': return Arr::get($this->header,'onet');
case 'ff': return Arr::get($this->header,'onode');
case 'fp': return Arr::get($this->header,'opoint');
case 'fd': return rtrim(Arr::get($this->header,'odomain',"\x00"));
// To Addresses
case 'tz': return Arr::get($this->header,'dzone');
case 'tn': return Arr::get($this->header,'dnet');
case 'tf': return Arr::get($this->header,'dnode');
case 'tp': return Arr::get($this->header,'dpoint');
case 'td': return rtrim(Arr::get($this->header,'ddomain',"\x00"));
case 'date':
return Carbon::create(
Arr::get($this->header,'y'),
Arr::get($this->header,'m')+1,
Arr::get($this->header,'d'),
Arr::get($this->header,'H'),
Arr::get($this->header,'M'),
Arr::get($this->header,'S')
);
case 'password':
return rtrim(Arr::get($this->header,$key),"\x00");
case 'fftn':
case 'fftn_o':
case 'tftn':
case 'tftn_o':
return parent::__get($key);
case 'software':
$code = Arr::get($this->header,'prodcode-hi')<<8|Arr::get($this->header,'prodcode-lo');
Software::unguard();
$o = Software::singleOrNew(['code'=>$code,'type'=>Software::SOFTWARE_TOSSER]);
Software::reguard();
return $o;
case 'software_ver':
return sprintf('%d.%d',Arr::get($this->header,'prodrev-maj'),Arr::get($this->header,'prodrev-min'));
case 'capability':
// This needs to be defined in child classes, since not all children have it
return NULL;
// Packet Type
case 'type':
return static::TYPE;
// Packet name:
case 'name':
return $this->{$key} ?: sprintf('%08x',timew());
default:
throw new \Exception('Unknown key: '.$key);
}
}
/**
* Return the packet
*
* @return string
* @throws \Exception
*/
public function __toString(): string
{
$return = $this->header();
foreach ($this->messages as $o) {
if ($o->packed)
$return .= self::PACKED_MSG_LEAD.$o;
}
$return .= "\00\00";
return $return;
}
/* STATIC */
@ -77,6 +156,19 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
return collect(static::HEADER)->sum(function($item) { return Arr::get($item,2); });
}
/**
* This function is intended to be implemented in child classes to test if the packet
* is defined by the child object
*
* @see self::PACKET_TYPES
* @param string $header
* @return bool
*/
public static function is_type(string $header): bool
{
return FALSE;
}
/**
* Process a packet file
*
@ -132,9 +224,7 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
// No message attached
} else
throw new InvalidPacketException('Not a valid packet, not EOP or SOM:'.bin2hex($x));
Log::info(sprintf('%s:- Packet [%s] is a [%s] packet, dated [%s]',self::LOGKEY,$o->name,get_class($o),$o->date));
throw new InvalidPacketException('Not a valid packet, not EOP or SOM'.bin2hex($x));
// Work out the packet zone
if ($o->fz && ($o->fd || $domain)) {
@ -143,10 +233,16 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
->where('zone_id',$o->fz)
->where('name',$o->fd ?: $domain->name)
->single();
// We need not knowing the domain, we use the default zone
} else {
$o->zone = Zone::where('zone_id',$o->fz)
->where('default',TRUE)
->single();
}
// If zone is not set, then we need to use a default zone - the messages may not be from this zone.
if (empty($o->zone)) {
if (! $o->zone) {
Log::alert(sprintf('%s:! We couldnt work out the packet zone, so we have fallen back to the default for [%d]',self::LOGKEY,$o->fz));
$o->zone = Zone::where('zone_id',$o->fz)
@ -156,7 +252,7 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
$message = ''; // Current message we are building
$msgbuf = '';
$leader = Message::header_len()+strlen(self::PACKED_MSG_LEAD);
$leader = Message::HEADER_LEN+strlen(self::PACKED_MSG_LEAD);
// We loop through reading from the buffer, to find our end of message tag
while ((! feof($f) && ($readbuf=fread($f,$leader)))) {
@ -189,112 +285,18 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
}
/**
* @param string|null $header
* @throws \Exception
*/
public function __construct(string $header=NULL)
{
$this->messages = collect();
$this->errors = collect();
if ($header)
$this->header = unpack(self::unpackheader(static::HEADER),$header);
}
/**
* @throws \Exception
*/
public function __get($key)
{
Log::debug(sprintf('%s:/ Requesting key for Packet::class [%s]',self::LOGKEY,$key));
switch ($key) {
// From Addresses
case 'fz': return Arr::get($this->header,'ozone');
case 'fn': return Arr::get($this->header,'onet');
case 'ff': return Arr::get($this->header,'onode');
case 'fp': return Arr::get($this->header,'opoint');
case 'fd': return rtrim(Arr::get($this->header,'odomain',"\x00"));
// To Addresses
case 'tz': return Arr::get($this->header,'dzone');
case 'tn': return Arr::get($this->header,'dnet');
case 'tf': return Arr::get($this->header,'dnode');
case 'tp': return Arr::get($this->header,'dpoint');
case 'td': return rtrim(Arr::get($this->header,'ddomain',"\x00"));
case 'date':
return Carbon::create(
Arr::get($this->header,'y'),
Arr::get($this->header,'m')+1,
Arr::get($this->header,'d'),
Arr::get($this->header,'H'),
Arr::get($this->header,'M'),
Arr::get($this->header,'S')
);
case 'password':
return rtrim(Arr::get($this->header,$key),"\x00");
case 'fftn_t':
case 'fftn':
case 'tftn_t':
case 'tftn':
return parent::__get($key);
case 'software':
$code = Arr::get($this->header,'prodcode-hi')<<8|Arr::get($this->header,'prodcode-lo');
Software::unguard();
$o = Software::singleOrNew(['code'=>$code,'type'=>Software::SOFTWARE_TOSSER]);
Software::reguard();
return $o;
case 'software_ver':
return sprintf('%d.%d',Arr::get($this->header,'prodrev-maj'),Arr::get($this->header,'prodrev-min'));
case 'capability':
// This needs to be defined in child classes, since not all children have it
return NULL;
// Packet Type
case 'type':
return static::TYPE;
// Packet name:
case 'name':
return $this->{$key} ?: sprintf('%08x',timew());
case 'messages':
return $this->{$key};
default:
throw new \Exception('Unknown key: '.$key);
}
}
/**
* Return the packet
* Location of the version
*
* @return string
* @throws \Exception
* @return int
*/
public function __toString(): string
public static function version_offset(): int
{
if (empty($this->messages))
throw new InvalidPacketException('Refusing to make an empty packet');
return Arr::get(collect(static::HEADER)->get('type'),0);
}
if (empty($this->tftn_p) || empty($this->fftn_p))
throw new InvalidPacketException('Cannot generate a packet without a destination address');
$return = $this->header();
foreach ($this->messages as $o)
$return .= self::PACKED_MSG_LEAD.$o->packet($this->tftn_p);
$return .= "\00\00";
return $return;
public static function version_offset_len(): int
{
return Arr::get(collect(static::HEADER)->get('type'),2);
}
/* INTERFACE */
@ -307,7 +309,7 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
return $this->messages->count();
}
public function current(): Echomail|Netmail
public function current(): Message
{
return $this->messages->get($this->index);
}
@ -340,7 +342,6 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
* @param Address $oo
* @param Address $o
* @param string|null $passwd Override the password used in the packet
* @deprecated Use Packet::generate(), which should generate a packet of the right type
*/
public function addressHeader(Address $oo,Address $o,string $passwd=NULL): void
{
@ -374,38 +375,12 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
* Add a message to this packet
*
* @param Message $o
* @deprecated No longer used when Address::class is updated
*/
public function addMail(Message $o): void
{
$this->messages->push($o);
}
public function for(Address $ao): self
{
$this->tftn_p = $ao;
$this->fftn_p = our_address($ao);
return $this;
}
/**
* Generate a packet
*
* @return string
*/
public function generate(): string
{
return (string)$this;
}
public function mail(Collection $msgs): self
{
$this->messages = $msgs;
return $this;
}
/**
* Parse a message in a mail packet
*
@ -418,8 +393,7 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
$msg = Message::parseMessage($message,$this->zone);
// @todo If the message from domain (eg: $msg->fftn->zone->domain) is different to the packet address domain ($pkt->fftn->zone->domain), we'll skip this message
Log::debug(sprintf('%s:^ Message [%s] - Packet from domain [%d], Message domain [%d]',self::LOGKEY,$msg->msgid,$this->fftn->zone->domain_id,$msg->fftn->zone->domain_id));
// If the message from domain is different to the packet address domain, we'll skip this message
// If the message is invalid, we'll ignore it
if ($msg->errors) {
@ -427,17 +401,14 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
// If the messages is not for the right zone, we'll ignore it
if ($msg->errors->messages()->has('invalid-zone')) {
Log::alert(sprintf('%s:! Message [%s] is from an invalid zone [%s], packet is from [%s] - ignoring it',self::LOGKEY,$msg->msgid,$msg->fftn->zone->zone_id,$this->fftn->zone->zone_id));
Log::alert(sprintf('%s:! Message is from an invalid zone [%s], packet is from [%s] - ignoring it',self::LOGKEY,$msg->fftn,$msg->zone->domain->name));
if (! $msg->rescanned->count())
Notification::route('netmail',$this->fftn)->notify(new EchomailBadAddress($msg));
Notification::route('netmail',$this->fftn_o)->notify(new EchomailBadAddress($msg));
return;
}
// @todo If the $msg->fftn doesnt exist, we'll need to create it
// @todo If the $msg->tftn doesnt exist (netmail), we'll need to create it (ergo intransit)
/*
// If the to address doenst exist, we'll create a new entry
if ($msg->errors->messages()->has('to') && $msg->tzone) {
try {
@ -499,13 +470,10 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
Log::alert(sprintf('%s:- From FTN is not defined, creating new entry for [%s] (%d)',self::LOGKEY,$msg->fboss,$ao->id));
}
*/
// If the from/to user is missing
if ($msg->errors->messages()->has('from') || $msg->errors->messages()->has('to')) {
if ($msg->errors->messages()->has('user_from') || $msg->errors->messages()->has('user_to')) {
Log::error(sprintf('%s:! Skipping message [%s] due to errors (%s)...',self::LOGKEY,$msg->msgid,join(',',$msg->errors->messages()->keys())));
$this->errors->push($msg);
return;
}
}
@ -513,24 +481,8 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
$this->messages->push($msg);
}
/**
* Overwrite the packet password
*
* @param string|null $password
* @return self
*/
public function password(string $password=NULL): self
{
if ($password && (strlen($password) < 9))
$this->pass_p = $password;
return $this;
}
/** @deprecated Is this used? */
public function pluck(string $key): Collection
{
throw new \Exception(sprintf('%s:! This function is deprecated - [%s]',self::LOGKEY,$key));
return $this->messages->pluck($key);
}
}

View File

@ -62,36 +62,34 @@ final class FSC48 extends Packet
*/
protected function header(): string
{
$oldest = $this->messages->sortBy('datetime')->last();
try {
return pack(collect(self::HEADER)->pluck(1)->join(''),
$this->fftn_p->node_id, // Orig Node
$this->tftn_p->node_id, // Dest Node
$oldest->datetime->format('Y'), // Year
$oldest->datetime->format('m')-1, // Month
$oldest->datetime->format('d'), // Day
$oldest->datetime->format('H'), // Hour
$oldest->datetime->format('i'), // Minute
$oldest->datetime->format('s'), // Second
$this->ff, // Orig Node
$this->tf, // Dest Node
Arr::get($this->header,'y'), // Year
Arr::get($this->header,'m'), // Month
Arr::get($this->header,'d'), // Day
Arr::get($this->header,'H'), // Hour
Arr::get($this->header,'M'), // Minute
Arr::get($this->header,'S'), // Second
0, // Baud
2, // Packet Version (should be 2)
$this->fftn_p->point_id ? 0xffff : $this->fftn_p->host_id, // Orig Net (0xFFFF when OrigPoint != 0)
$this->tftn_p->host_id, // Dest Net
$this->fp ? 0xffff : $this->fn, // Orig Net (0xFFFF when OrigPoint != 0)
$this->tn, // Dest Net
(Setup::PRODUCT_ID & 0xff), // Product Code Lo
Setup::PRODUCT_VERSION_MAJ, // Product Version Major
$this->pass_p ?: $this->tftn_p->session('pktpass'), // Packet Password
$this->fftn_p->zone->zone_id, // Orig Zone
$this->tftn_p->zone->zone_id, // Dest Zone
$this->fftn_p->point_id ? $this->fftn_p->host_id : 0x00, // Aux Net
1<<0, // fsc-0039.004 (copy of 0x2c)
$this->password, // Packet Password
$this->fz, // Orig Zone
$this->tz, // Dest Zone
$this->fp ? $this->fn : 0x00, // Aux Net
Arr::get($this->header,'capvalid',1<<0), // fsc-0039.004 (copy of 0x2c)
((Setup::PRODUCT_ID >> 8) & 0xff), // Product Code Hi
Setup::PRODUCT_VERSION_MIN, // Product Version Minor
1<<0, // Capability Word
$this->fftn_p->zone->zone_id, // Orig Zone
$this->tftn_p->zone->zone_id, // Dest Zone
$this->fftn_p->point_id, // Orig Point
$this->tftn_p->point_id, // Dest Point
Arr::get($this->header,'capword',1<<0), // Capability Word
$this->fz, // Orig Zone
$this->tz, // Dest Zone
$this->fp, // Orig Point
$this->tp, // Dest Point
strtoupper(hexstr(Setup::PRODUCT_ID)), // ProdData
);

View File

@ -2,23 +2,25 @@
namespace App\Classes\FTN;
use App\Models\{Echoarea,Echomail,Netmail};
use App\Models\Echoarea;
/**
* Abstract class to hold the common functions for automatic responding to echomail/netmail messages
*/
abstract class Process
{
public static function canProcess(Echoarea $eao): bool
public static function canProcess(string $echoarea): bool
{
return $eao->automsgs;
$eao = Echoarea::where('name',$echoarea)->single();
return $eao && $eao->automsgs;
}
/**
* Return TRUE if the process class handled the message.
*
* @param Echomail|Netmail $mo
* @param Message $msg
* @return bool
*/
abstract public static function handle(Echomail|Netmail $mo): bool;
abstract public static function handle(Message $msg): bool;
}

View File

@ -5,8 +5,7 @@ namespace App\Classes\FTN\Process\Echomail;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Log;
use App\Classes\FTN\Process;
use App\Models\{Echomail,Netmail};
use App\Classes\FTN\{Message,Process};
use App\Notifications\Echomails\Test as TestNotification;
/**
@ -20,16 +19,16 @@ final class Test extends Process
private const testing = ['test','testing'];
public static function handle(Echomail|Netmail $mo): bool
public static function handle(Message $msg): bool
{
if (! self::canProcess($mo->echoarea)
|| (strtolower($mo->to) !== 'all')
|| (! in_array(strtolower($mo->subject),self::testing)))
if (! self::canProcess($msg->echoarea)
|| (strtolower($msg->user_to) !== 'all')
|| (! in_array(strtolower($msg->subject),self::testing)))
return FALSE;
Log::info(sprintf('%s:- Processing TEST message from (%s) [%s] in [%s]',self::LOGKEY,$mo->from,$mo->fftn->ftn,$mo->echoarea->name));
Log::info(sprintf('%s:- Processing TEST message from (%s) [%s] in [%s]',self::LOGKEY,$msg->user_from,$msg->fftn,$msg->echoarea));
Notification::route('echomail',$mo->echoarea->withoutRelations())->notify(new TestNotification($mo));
Notification::route('echomail',$msg->echoarea)->notify(new TestNotification($msg));
return TRUE;
}

View File

@ -5,8 +5,7 @@ namespace App\Classes\FTN\Process\Netmail;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Log;
use App\Classes\FTN\Process;
use App\Models\{Echomail,Netmail};
use App\Classes\FTN\{Message,Process};
use App\Notifications\Netmails\Areafix as AreafixNotification;
use App\Notifications\Netmails\Areafix\NotConfiguredHere as AreafixNotConfiguredHereNotification;
@ -19,18 +18,18 @@ final class Areafix extends Process
{
private const LOGKEY = 'RP-';
public static function handle(Echomail|Netmail $mo): bool
public static function handle(Message $msg): bool
{
if (strtolower($mo->to) !== 'areafix')
if (strtolower($msg->user_to) !== 'areafix')
return FALSE;
Log::info(sprintf('%s:- Processing AREAFIX message from (%s) [%s]',self::LOGKEY,$mo->from,$mo->fftn));
Log::info(sprintf('%s:- Processing AREAFIX message from (%s) [%s]',self::LOGKEY,$msg->user_from,$msg->fftn));
// If this is not a node we manage, then respond with a sorry can help you
if ($mo->fftn->system->sessions->count())
Notification::route('netmail',$mo->fftn)->notify(new AreafixNotification($mo));
if ($msg->fftn_o->system->sessions->count())
Notification::route('netmail',$msg->fftn_o)->notify(new AreafixNotification($msg));
else
Notification::route('netmail',$mo->fftn)->notify(new AreafixNotConfiguredHereNotification($mo));
Notification::route('netmail',$msg->fftn_o)->notify(new AreafixNotConfiguredHereNotification($msg));
return TRUE;
}

View File

@ -5,8 +5,7 @@ namespace App\Classes\FTN\Process\Netmail;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Log;
use App\Classes\FTN\Process;
use App\Models\{Echomail,Netmail};
use App\Classes\FTN\{Message,Process};
use App\Notifications\Netmails\Ping as PingNotification;
/**
@ -18,14 +17,14 @@ final class Ping extends Process
{
private const LOGKEY = 'RP-';
public static function handle(Echomail|Netmail $mo): bool
public static function handle(Message $msg): bool
{
if (strtolower($mo->to) !== 'ping')
if (strtolower($msg->user_to) !== 'ping')
return FALSE;
Log::info(sprintf('%s:- Processing PING message from (%s) [%s]',self::LOGKEY,$mo->from,$mo->fftn->ftn));
Log::info(sprintf('%s:- Processing PING message from (%s) [%s]',self::LOGKEY,$msg->user_from,$msg->fftn));
Notification::route('netmail',$mo->fftn)->notify(new PingNotification($mo));
Notification::route('netmail',$msg->fftn_o)->notify(new PingNotification($msg));
return TRUE;
}

View File

@ -32,7 +32,7 @@ final class Mail extends Send
public function __get($key) {
switch ($key) {
case 'dbids':
return $this->f->messages->pluck('id');
return $this->f->messages->pluck('dbid');
case 'name':
return sprintf('%08x',timew($this->youngest()));
@ -111,7 +111,7 @@ final class Mail extends Send
return TRUE;
}
private function youngest(): Carbon
public function youngest(): Carbon
{
return $this->f->messages->pluck('date')->sort()->last();
}

View File

@ -128,12 +128,13 @@ class Receive extends Base
// If packet is greater than a size, lets queue it
if ($this->queue || ($this->receiving->size > config('fido.queue_size',0))) {
Log::info(sprintf('%s:- Packet [%s] will be sent to the queue for processing because its [%d] size, or queue forced',self::LOGKEY,$this->receiving->full_name,$this->receiving->size));
PacketProcess::dispatch($this->receiving->rel_name,$this->ao->zone->domain,FALSE,$rcvd_time);
PacketProcess::dispatch($this->receiving,$this->ao->withoutRelations(),$rcvd_time);
} else
PacketProcess::dispatchSync($this->receiving->rel_name,$this->ao->zone->domain,TRUE,$rcvd_time);
PacketProcess::dispatchSync($this->receiving,$this->ao->withoutRelations(),$rcvd_time);
} catch (\Exception $e) {
Log::error(sprintf('%s:! Got error dispatching packet [%s] (%d:%s-%s).',self::LOGKEY,$this->receiving->rel_name,$e->getLine(),$e->getFile(),$e->getMessage()));
Log::error(sprintf('%s:! Got error dispatching packet [%s] (%d:%s-%s).',self::LOGKEY,$this->receiving->full_name,$e->getLine(),$e->getFile(),$e->getMessage()));
}
break;

View File

@ -249,10 +249,11 @@ class Send extends Base
* Add our mail to the send queue
*
* @param Address $ao
* @param bool $update
* @return bool
* @throws Exception
*/
public function mail(Address $ao): bool
public function mail(Address $ao,bool $update=TRUE): bool
{
$mail = FALSE;
@ -264,7 +265,7 @@ class Send extends Base
}
// Netmail
if ($x=$ao->getNetmail()) {
if ($x=$ao->getNetmail($update)) {
Log::debug(sprintf('%s:- Netmail(s) added for sending to [%s]',self::LOGKEY,$ao->ftn));
$this->list->push(new Mail($x,self::T_NETMAIL));
@ -272,7 +273,7 @@ class Send extends Base
}
// Echomail
if ($x=$ao->getEchomail()) {
if ($x=$ao->getEchomail($update)) {
Log::debug(sprintf('%s:- Echomail(s) added for sending to [%s]',self::LOGKEY,$ao->ftn));
$this->list->push(new Mail($x,self::T_ECHOMAIL));

View File

@ -11,8 +11,6 @@ use App\Classes\Sock\SocketClient;
use App\Classes\Sock\SocketException;
use App\Models\{Address,Mailer,Setup,System,SystemLog};
// @todo after receiving a mail packet/file, dont acknowledge it until we can validate that we can read it properly.
abstract class Protocol
{
// Enable extra debugging

View File

@ -696,13 +696,8 @@ final class Binkp extends BaseProtocol
$this->node->ftn_other = $rem_aka;
continue;
// If we only present limited AKAs dont validate password against akas outside of the domains we present
} elseif (is_null(our_address($o))) {
Log::alert(sprintf('%s:/ AKA domain [%s] is not in our domain(s) [%s] - ignoring',self::LOGKEY,$o->zone->domain->name,our_address()->pluck('zone.domain.name')->unique()->join(',')));
continue;
} elseif (! $o->active) {
Log::alert(sprintf('%s:/ AKA is not active [%s] - ignoring',self::LOGKEY,$rem_aka));
Log::alert(sprintf('%s:/ AKA is not active [%s], ignoring',self::LOGKEY,$rem_aka));
continue;
} else {
@ -710,7 +705,7 @@ final class Binkp extends BaseProtocol
}
} catch (InvalidFTNException $e) {
Log::error(sprintf('%s:! AKA is INVALID [%s] (%s) - ignoring',self::LOGKEY,$rem_aka,$e->getMessage()));
Log::error(sprintf('%s:! AKA is INVALID [%s] (%s), ignoring',self::LOGKEY,$rem_aka,$e->getMessage()));
continue;
@ -1009,9 +1004,6 @@ final class Binkp extends BaseProtocol
$this->send->close(TRUE,$this->node);
}
} else {
Log::error(sprintf('%s:! M_got[skip] not for our file? [%s]',self::LOGKEY,$buf));
}
} else {

View File

@ -319,23 +319,11 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
Log::debug(sprintf('%s: - Parsing AKA [%s]',self::LOGKEY,$rem_aka));
try {
if (! ($o = Address::findFTN($rem_aka,TRUE))) {
if (! ($o = Address::findFTN($rem_aka))) {
Log::debug(sprintf('%s: ? AKA is UNKNOWN [%s]',self::LOGKEY,$rem_aka));
$this->node->ftn_other = $rem_aka;
continue;
// If we only present limited AKAs dont validate password against akas outside of the domains we present
} elseif (is_null(our_address($o))) {
Log::alert(sprintf('%s:/ AKA domain [%s] is not in our domain(s) [%s] - ignoring',self::LOGKEY,$o->zone->domain->name,our_address()->pluck('zone.domain.name')->unique()->join(',')));
continue;
} elseif (! $o->active) {
Log::alert(sprintf('%s:/ AKA is not active [%s] - ignoring',self::LOGKEY,$rem_aka));
continue;
} else {
Log::info(sprintf('%s:- Got AKA [%s]',self::LOGKEY,$rem_aka));
}
} catch (InvalidFTNException $e) {

View File

@ -50,13 +50,8 @@ class PacketAddress extends Command
exit(1);
}
echo hex_dump($ao
->system
->packet($ao)
->mail($o->where('id',$this->argument('dbid'))->get())
->generate()
);
$o = $o->where('id',$this->argument('dbid'))->get();
return Command::SUCCESS;
dd(hex_dump($ao->getPacket($o)));
}
}

View File

@ -7,7 +7,7 @@ use Illuminate\Support\Facades\Storage;
use App\Classes\File;
use App\Classes\FTN\Packet;
use App\Models\{Address,Echomail};
use App\Models\Address;
class PacketInfo extends Command
{
@ -31,7 +31,7 @@ class PacketInfo extends Command
* Execute the console command.
*
* @return mixed
* @throws \App\Exceptions\InvalidPacketException
* @throws \App\Classes\FTN\InvalidPacketException
*/
public function handle()
{
@ -55,38 +55,27 @@ class PacketInfo extends Command
$this->alert(sprintf('File Name: %s',$x));
$this->info(sprintf('Packet Type : %s (%s)',$pkt->type,get_class($pkt)));
$this->info(sprintf('From : %s to %s',$pkt->fftn->ftn,$pkt->tftn->ftn));
$this->info(sprintf('Dated : %s (%s) [%s]',$pkt->date,$pkt->date->timestamp,$pkt->date->tz->toOffsetName()));
$this->info(sprintf('Password : %s (%s)',$pkt->password ?: '-',$pkt->password ? 'SET' : 'NOT set'));
$this->info(sprintf('Messages : %d',$pkt->count()));
$this->info(sprintf('From : %s to %s',$pkt->fftn,$pkt->tftn));
$this->info(sprintf('Dated : %s (%s)',$pkt->date,$pkt->date->timestamp));
$this->info(sprintf('Password : %s (%s)',$pkt->password,$pkt->password ? 'SET' : 'NOT set'));
$this->info(sprintf('Messages : %d',$pkt->messages->count()));
$this->info(sprintf('Tosser : %d (%s) version %s',$pkt->software->code,$pkt->software->name,$pkt->software_ver));
$this->info(sprintf('Capabilities: %x',$pkt->capability));
$this->info(sprintf('Has Errors : %s',$pkt->errors->count() ? 'YES' : 'No'));
$this->info(sprintf('Messages : %d',$pkt->count()));
foreach ($pkt as $msg) {
echo "\n";
try {
$this->warn(sprintf('- Date : %s (%s)',$msg->datetime,$msg->datetime->tz->toOffsetName()));
$this->warn(sprintf(' - Errors : %s',$msg->errors?->errors()->count() ? 'YES' : 'No'));
$this->warn(sprintf(' - Flags : %s',$msg->flags()->keys()->join(', ')));
$this->warn(sprintf(' - Cost : %d',$msg->cost));
$this->warn(sprintf(' - From : %s (%s)',$msg->from,$msg->fftn->ftn));
if ($msg instanceof Echomail)
$this->warn(sprintf(' - To : %s',$msg->to));
else
$this->warn(sprintf(' - To : %s (%s)',$msg->to,$msg->tftn->ftn));
$this->warn(sprintf('- Date : %s',$msg->date));
$this->warn(sprintf(' - Flags : %s',$msg->flags()->filter()->keys()->join(', ')));
$this->warn(sprintf(' - From : %s (%s)',$msg->user_from,$msg->fftn));
$this->warn(sprintf(' - To : %s (%s)',$msg->user_to,$msg->tftn));
$this->warn(sprintf(' - Subject: %s',$msg->subject));
if ($msg instanceof Echomail)
$this->warn(sprintf(' - Area : %s',$msg->echoarea->name));
$this->warn(sprintf(' - Area : %s',$msg->echoarea));
if ($msg->errors) {
echo "\n";
$this->error("Errors:");
if ($msg->errors)
foreach ($msg->errors->errors()->all() as $error)
$this->error(' - '.$error);
}
$this->line(' - '.$error);
} catch (\Exception $e) {
$this->error('! ERROR: '.$e->getMessage());
@ -99,8 +88,8 @@ class PacketInfo extends Command
foreach ($pkt->errors as $msg) {
$this->error(sprintf('- Date: %s',$msg->date));
$this->error(sprintf(' - FLAGS: %s',$msg->flags()->filter()->keys()->join(', ')));
$this->error(sprintf(' - From: %s (%s)',$msg->from,$msg->fftn));
$this->error(sprintf(' - To: %s (%s)',$msg->to,$msg->tftn));
$this->error(sprintf(' - From: %s (%s)',$msg->user_from,$msg->fftn));
$this->error(sprintf(' - To: %s (%s)',$msg->user_to,$msg->tftn));
$this->error(sprintf(' - Subject: %s',$msg->subject));
foreach ($msg->errors->errors()->all() as $error)

View File

@ -2,41 +2,15 @@
namespace App\Console\Commands;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
use App\Classes\File;
use App\Classes\FTN\Packet;
use App\Jobs\PacketProcess as Job;
use App\Jobs\MessageProcess as Job;
use App\Models\Address;
/**
* Things to test
* + Packet
* - Sender doesnt exist (try and send a bounce message in the same session)
* - Sender defined in DB by not ours
* - Sender has wrong password
* - Packet too old
*
* + Echomail
* - Area doesnt exist (to uplink)
* - Sender not subscribed (to uplink)
* - Sender cannot post (to uplink)
* - Sender has wrong address for echorea domain (to uplink)
* - Test message in echoarea
* - Echomail from address doesnt match packet envelope (to uplink)
* - Echomail too old (to uplink)
* - Rescanned dont generate notifications
* - Rescanned dont trigger bots
* - Some Notifications to an uplink should go to the admin instead?
*
* + Netmail
* - To hub, and user not defined (reject)
* - To hub, but user redirect (redirected)
* - To areafix (processed)
* - To ping (respond)
* - With trace turned on (respond)
*/
class PacketProcess extends Command
{
/**
@ -61,29 +35,38 @@ class PacketProcess extends Command
* Execute the console command.
*
* @return void
* @throws \App\Exceptions\InvalidPacketException
* @throws \App\Classes\FTN\InvalidPacketException
*/
public function handle()
{
//$fs = Storage::disk(config('fido.local_disk'));
$fs = Storage::disk(config('fido.local_disk'));
$rel_name = sprintf('%s/%s',config('fido.dir'),$this->argument('file'));
//$f = new File($fs->path($rel_name));
$f = new File($fs->path($rel_name));
$m = [];
if ($this->argument('ftn')) {
$ao = Address::findFTN($this->argument('ftn'));
$a = Address::findFTN($this->argument('ftn'));
} elseif (preg_match(sprintf('/^%s\.(.{3})$/',Packet::regex),$this->argument('file'),$m)) {
$ao = Address::findOrFail(hexdec($m[1]));
$a = Address::findOrFail(hexdec($m[1]));
} else {
$this->error('Unable to determine sender FTN address');
exit(1);
}
Job::dispatchSync($rel_name,$ao->zone->domain,$this->option('dontqueue'));
foreach ($f as $packet) {
foreach ($pkt = Packet::process($packet,$f->itemName(),$f->itemSize(),$a?->zone->domain) as $msg) {
// @todo Quick check that the packet should be processed by us.
$this->info(sprintf('Processing message from [%s] with msgid [%s] in (%s)',$msg->fboss,$msg->msgid,$f->pktName()));
return Command::SUCCESS;
// Dispatch job.
if ($this->option('dontqueue'))
Job::dispatchSync($msg,$f->pktName(),$a,$pkt->fftn_o,Carbon::now(),$this->option('nobot'));
else
Job::dispatch($msg,$f->pktName(),$a,$pkt->fftn_o,Carbon::now(),$this->option('nobot'));
}
}
}
}

View File

@ -4,7 +4,7 @@ namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Address;
use App\Models\System;
class PacketSystem extends Command
{
@ -14,7 +14,7 @@ class PacketSystem extends Command
* @var string
*/
protected $signature = 'packet:system'
.' {ftn : System address}';
.' {sid : System ID}';
/**
* The console command description.
@ -27,19 +27,19 @@ class PacketSystem extends Command
* Execute the console command.
*
* @return mixed
* @throws \App\Exceptions\InvalidPacketException
* @throws \App\Classes\FTN\InvalidPacketException
*/
public function handle()
{
$ao = Address::findFTN($this->argument('ftn'));
$so = System::findOrFail($this->argument('sid'));
foreach ($ao->system->addresses as $o) {
$pkt = $o->getEchomail();
foreach ($so->addresses as $ao) {
$pkt = $ao->getEchomail(FALSE);
$this->info(sprintf('System address [%s] has [%d] messages.',$ao->ftn,$pkt?->count()));
if ($pkt) {
foreach ($pkt as $msg)
$this->warn(sprintf('- %s (%s)',$msg->msgid,$msg->id));
$this->warn(sprintf('- %s',$msg->msgid));
}
}
}

View File

@ -1,9 +0,0 @@
<?php
namespace App\Exceptions;
use Exception;
class NoReadSecurityException extends Exception
{
}

View File

@ -3,9 +3,7 @@
namespace App\Http\Controllers;
use Carbon\Carbon;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\QueryException;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
@ -16,7 +14,7 @@ use Illuminate\Support\Facades\Notification;
use Illuminate\Support\ViewErrorBag;
use App\Classes\FTN\Message;
use App\Http\Requests\{AddressMerge,AreafixRequest,SystemEchoareaRequest,SystemRegister,SystemSessionRequest};
use App\Http\Requests\{AddressMerge,AreafixRequest,SystemRegister};
use App\Jobs\AddressPoll;
use App\Models\{Address,Echoarea,Echomail,Filearea,Netmail,Setup,System,Zone};
use App\Notifications\Netmails\AddressLink;
@ -31,6 +29,8 @@ class SystemController extends Controller
*/
public function add_edit(SystemRegister $request,System $o)
{
$this->authorize('update',$o);
if ($request->post()) {
foreach (['name','location','sysop','hold','phone','address','port','active','method','notes','zt_id','pkt_type','heartbeat'] as $key)
$o->{$key} = $request->post($key);
@ -694,20 +694,30 @@ class SystemController extends Controller
/**
* Update the systems echoareas
*
* @param SystemEchoareaRequest $request
* @param Request $request
* @param System $o
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
*/
public function echoareas(SystemEchoareaRequest $request,System $o)
public function echoareas(Request $request,System $o)
{
$ao = $o->addresses->firstWhere('id',$request->address_id);
if (($request->method() === 'POST') && $request->validated()) {
$ao->echoareas()->syncWithPivotValues($request->validated('id',[]),['subscribed'=>Carbon::now()]);
if (($request->method() === 'POST') && $request->post()) {
session()->flash('accordion','echoarea');
if ($ao->trashed() && collect($request->get('id'))->diff($ao->echoareas->pluck('id'))->count())
return redirect()->back()->withErrors(sprintf('Address [%s] has been deleted, cannot add additional echos',$ao->ftn3d));
// Ensure we have session details for this address.
if (! $ao->session('sespass'))
return redirect()->back()->withErrors('System doesnt belong to this network');
$ao->echoareas()->syncWithPivotValues($request->get('id',[]),['subscribed'=>Carbon::now()]);
return redirect()->back()->with('success','Echoareas updated');
}
// @todo Allow a NC/RC/ZC to override
$eo = Echoarea::active()
->where('domain_id',$ao->zone->domain_id)
->where(function($query) use ($ao) {
@ -826,14 +836,26 @@ class SystemController extends Controller
/**
* Add Session details
*
* @param SystemSessionRequest $request
* @param Request $request
* @param System $o
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function session_add(SystemSessionRequest $request,System $o)
public function session_add(Request $request,System $o)
{
$zo = Zone::findOrFail($request->zone_id);
// @todo This should be admin of the zone
$this->authorize('update',$o);
session()->flash('accordion','session');
$validate = $request->validate([
'zone_id' => 'required|exists:zones,id',
'sespass' => 'required|string|min:4',
'pktpass' => 'required|string|min:4|max:8',
'ticpass' => 'required|string|min:4',
'fixpass' => 'required|string|min:4',
]);
$zo = Zone::findOrFail($validate['zone_id']);
/*
// @todo Disabling this, it needs improvement. If the new node is the ZC it becomes the default for the zone (and therefore remove all defaults from other addresses in the same zone), otherwise default should be false
@ -844,7 +866,7 @@ class SystemController extends Controller
}
*/
$o->sessions()->attach($zo,$request->validated());
$o->sessions()->attach($zo,$validate);
return redirect()->to(sprintf('system/addedit/%d',$o->id));
}
@ -852,14 +874,13 @@ class SystemController extends Controller
/**
* Delete address assigned to a host
*
* @param System $o
* @param Zone $zo
* @return RedirectResponse
* @throws AuthorizationException
* @param Address $o
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function session_del(System $o,Zone $zo)
{
$this->authorize('update_nn',$o);
$this->authorize('admin',$zo);
session()->flash('accordion','session');
$o->sessions()->detach($zo);

View File

@ -1,71 +0,0 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use App\Models\Domain;
/**
* Validation to register echoareas for a system
* o = System::class
*
* @note This validation only expects to be used with POST
* Variables:
* + "address_id" => "address" // address_id that will receive the echos
* + "domain_id" => "domain" // Of the echoareas
* + "id" => array of IDs // The echos being subscribed (or if absent, are removed)
*
* Rules:
* @see AddressAdd::class for description of authorisation
*/
class SystemEchoareaRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return request()->isMethod('get')
|| Gate::allows('update_nn',$this->route('o'));
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(Request $request): array
{
if (request()->isMethod('get'))
return [];
session()->flash('accordion','echoarea');
return [
'address_id'=>[
'exists:addresses,id',
// Make sure we have session details for the area
function ($attribute,$value,$fail) use ($request) {
$ao = request()->route('o')->addresses->firstWhere('id',$request->address_id);
if ((! $ao) || (! $ao->active) || (! $ao->system->zones->pluck('domain_id')->contains($request->domain_id)))
$fail('Address must be ACTIVE, and have session details for the domain');
},
],
'domain_id'=>'exists:domains,id',
'id'=>[
'array',
'min:1',
// Make sure the echoareas are in the domain
function ($attribute,$value,$fail) use ($request) {
$do = Domain::findOrFail($request->domain_id);
if ($do->echoareas->pluck('id')->intersect($value)->count() !== count($value))
$fail('Some echaoreas dont exist in the domain?');
},
]
];
}
}

View File

@ -22,14 +22,17 @@ class SystemRegister extends FormRequest
*/
public function authorize(Request $request)
{
if (! $request->post())
return TRUE;
$this->so = new System;
if (is_numeric($request->name)) {
$this->so = System::findOrNew($request->name);
// Cannot claim this site
if ($this->route('o')->id === Setup::findOrFail(config('app.id'))->system_id)
if ($this->so->id === Setup::findOrFail(config('app.id'))->system_id)
return FALSE;
}
return Gate::allows($this->route('o')->users->count() ? 'update_nn' : 'register',$this->route('o'));
return Gate::allows(is_numeric($request->name) && $this->so->users->count() ? 'update' : 'register',$this->so);
}
public function messages(): array

View File

@ -1,35 +0,0 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
class SystemSessionRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
session()->flash('accordion','session');
return Gate::allows('update_nn',$this->route('o'));
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'zone_id' => 'required|exists:zones,id',
'sespass' => 'required|string|min:4',
'pktpass' => 'required|string|min:4|max:8',
'ticpass' => 'required|string|min:4',
'fixpass' => 'required|string|min:4',
];
}
}

View File

@ -8,12 +8,13 @@ use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;
use App\Classes\FTN\Message;
use App\Models\{Echomail,Netmail,User};
use App\Notifications\Netmails\{EchoareaNotExist,EchoareaNotSubscribed,EchoareaNoWrite,NetmailForward,NetmailHubNoUser};
use App\Models\{Address,Echoarea,Echomail,Netmail,User};
use App\Notifications\Netmails\{EchoareaNotExist,EchoareaNotSubscribed,EchoareaNoWrite,NetmailForward,Reject};
use App\Traits\ParseAddresses;
class MessageProcess implements ShouldQueue
@ -22,19 +23,20 @@ class MessageProcess implements ShouldQueue
use Dispatchable,InteractsWithQueue,Queueable,SerializesModels,ParseAddresses;
private Echomail|Netmail|string $mo;
private Address $sender;
private Message $msg;
private Address $pktsrc;
private Carbon $recvtime;
private bool $skipbot;
private string $packet;
/**
* Process a message from a packet
*
* @param Echomail|Netmail $mo The message object
* @param bool $skipbot Dont trigger bot actions
*/
public function __construct(Echomail|Netmail $mo,bool $skipbot=FALSE)
public function __construct(Message $msg,string $packet,Address $sender,Address $pktsrc,Carbon $recvtime,bool $skipbot=FALSE)
{
// @todo We need to serialize this model here, because laravel has an error unserializing it (Model Not Found)
$this->mo = serialize($mo);
$this->msg = $msg;
$this->packet = $packet;
$this->sender = $sender;
$this->pktsrc = $pktsrc;
$this->recvtime = $recvtime;
$this->skipbot = $skipbot;
}
@ -42,7 +44,7 @@ class MessageProcess implements ShouldQueue
{
switch ($key) {
case 'subject':
return sprintf('%s-%s-%s',$this->pktname,$this->mo->set->get('set_sender')->ftn,$this->mo->msgid);
return sprintf('%s-%s-%s',$this->packet,$this->sender->ftn,$this->msg->msgid);
default:
return NULL;
@ -50,25 +52,21 @@ class MessageProcess implements ShouldQueue
}
/**
* At this point, we know that the packet is from a system we know about, and the packet is to us:
* + From a system that is configured with us, and the password has been validated
* + From a system that is not configured with us, and it may have netmails for us
* When calling MessageProcess - we assume that the packet is from a valid source, and
* the destination (netmail/echomail) is also valid
*/
public function handle()
{
$this->mo = unserialize($this->mo);
// Load our details
$ftns = our_address();
// If we are a netmail
if ($this->mo instanceof Netmail) {
// @todo generate exception when netmail to system that doesnt exist (node/point) and its this host's responsibility
if ($this->msg->isNetmail()) {
Log::info(sprintf('%s:- Processing Netmail [%s] to (%s) [%s] from (%s) [%s].',
self::LOGKEY,
$this->mo->msgid,
$this->mo->to,$this->mo->tftn->ftn,
$this->mo->from,$this->mo->fftn->ftn,
$this->msg->msgid,
$this->msg->user_to,$this->msg->tftn,
$this->msg->user_from,$this->msg->fftn,
));
// @todo Enable checks to reject old messages
@ -76,51 +74,89 @@ class MessageProcess implements ShouldQueue
// Check for duplicate messages
// FTS-0009.001
if ($this->mo->msgid) {
Log::debug(sprintf('%s:- Checking for duplicate from host [%s].',self::LOGKEY,$this->mo->fftn->ftn));
$o = Netmail::where('msgid',$this->mo->msgid)
->where('fftn_id',$this->mo->fftn->id)
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 #%d [%s] from (%s) [%s] to (%s) - ignoring.',
Log::alert(sprintf('%s:! Duplicate netmail [%s] in [%s] from (%s) [%s] to (%s) - ignorning.',
self::LOGKEY,
$o->id,
$this->mo->msgid,
$this->mo->from,$this->mo->fftn->ftn,
$this->mo->to,
$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->tagline = $this->msg->tagline;
$o->tearline = $this->msg->tearline;
$o->origin = $this->msg->origin;
$o->subject = $this->msg->subject;
$o->msg = $this->msg->message_src."\r";
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->via;
$o->set_recvtime = $this->recvtime;
// Strip any local/transit flags
$this->mo->flags &= ~(Message::FLAG_LOCAL|Message::FLAG_INTRANSIT);
$o->flags &= ~(Message::FLAG_LOCAL|Message::FLAG_INTRANSIT);
// Determine if the message is to this system, or in transit
if ($ftns->contains($this->mo->tftn)) {
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->mo)) {
$this->mo->flags |= Message::FLAG_RECD;
$this->mo->save();
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->mo->msgid,
$this->mo->from,
$this->mo->fftn->ftn,
$this->mo->id,
$this->msg->msgid,
$this->msg->user_from,
$this->msg->fftn,
$o->id,
));
break;
}
}
@ -129,40 +165,38 @@ class MessageProcess implements ShouldQueue
// 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->mo->to)))
->orWhereRaw(sprintf("LOWER(alias)='%s'",strtolower($this->mo->to)));
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->mo->tftn->zone)?->pop())) {
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->mo->tftn->ftn);
$note .= sprintf("+ at [%s]\r",$this->msg->tftn_o->ftn);
$note .= "+---------------------------------------------------------+\r\r";
$this->mo->msg = $note.$this->mo->content;
$this->mo->tftn_id = $ao->id;
$this->mo->flags |= Message::FLAG_INTRANSIT;
$this->mo->save();
$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->mo->from),config('fido.areabots')))
Notification::route('netmail',$this->mo->fftn)->notify(new NetmailForward($this->mo,$ao));
if (! in_array(strtolower($this->msg->user_from),config('fido.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->mo->from),config('fido.areabots'))) {
$this->mo->flags |= Message::FLAG_RECD;
$this->mo->save();
} elseif (in_array(strtolower($this->msg->user_from),config('fido.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->mo->msgid,
$this->mo->from,
$this->mo->fftn->ftn,
$this->mo->id,
$this->msg->msgid,
$this->msg->user_from,
$this->msg->fftn,
$o->id,
));
$processed = TRUE;
@ -171,87 +205,99 @@ class MessageProcess implements ShouldQueue
// 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->mo->from,$this->mo->fftn->ftn));
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->mo->fftn)->notify(new NetmailHubNoUser($this->mo));
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
$this->mo->flags |= Message::FLAG_INTRANSIT;
$this->mo->save();
$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->mo->msgid,
$this->mo->to,$this->mo->tftn->ftn,
$this->mo->from,$this->mo->fftn->ftn,
$this->mo->id,
$this->msg->msgid,
$this->msg->user_to,$this->msg->tftn,
$this->msg->user_from,$this->msg->fftn,
$o->id,
));
}
// Else we are echomail
} else {
// The packet sender
$sender = $this->mo->set->get('set_sender');
Log::debug(sprintf('%s:- Looking for echomail area [%s] for mail from [%s]',self::LOGKEY,$this->msg->echoarea,$this->msg->fboss));
// @todo Check that this does evaulate to true if a message has been rescanned
$rescanned = $this->mo->kludges->get('RESCANNED',FALSE);
// Echoarea doesnt exist, cant import the message
if (! $this->mo->echoarea) {
Log::alert(sprintf('%s:! Echoarea [%s] doesnt exist for zone [%d@%s]',self::LOGKEY,$this->mo->set->get('set_echoarea'),$sender->zone->zone_id,$sender->zone->domain->name));
Notification::route('netmail',$sender)->notify(new EchoareaNotExist($this->mo));
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;
}
Log::debug(sprintf('%s:- Processing echomail [%s] in [%s] from [%s].',self::LOGKEY,$this->mo->msgid,$this->mo->echoarea->name,$sender->ftn));
$ea = Echoarea::where('name',strtoupper($this->msg->echoarea))
->where('domain_id',$this->msg->fboss_o->zone->domain_id)
->single();
// Message from zone is incorrect for echoarea
if (! $this->mo->echoarea->domain->zones->contains($this->mo->fftn->zone)) {
if (! $ea) {
Log::alert(sprintf('%s:! Echoarea [%s] doesnt exist for zone [%d-%d]',self::LOGKEY,$this->msg->echoarea,$this->msg->fboss_o->zone->domain_id,$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));
if (! $this->pktsrc->zone->domain->zones->pluck('zone_id')->contains($this->msg->fboss_o->zone->zone_id)) {
Log::alert(sprintf('%s:! The message [%s] is from a different zone [%d] than the packet sender [%d] - not importing',
self::LOGKEY,
$this->mo->msgid,
$this->mo->fftn->zone->zone_id,
$this->mo->fftn->zone->zone_id));
$this->msg->msgid,
$this->msg->fboss_o->zone->zone_id,
$this->pktsrc->zone->zone_id));
return;
}
// Check for duplicate messages
// FTS-0009.001
if ($this->mo->msgid) {
$o = Echomail::where('msgid',$this->mo->msgid)
->where('fftn_id',$this->mo->fftn->id)
->where('datetime','>=',$this->mo->date->subYears(3))
->where('datetime','<=',$this->mo->date)
if ($this->msg->msgid) {
$o = Echomail::where('msgid',$this->msg->msgid)
->where('fftn_id',($x=$this->msg->fboss_o) ? $x->id : NULL)
->where('datetime','>=',$this->msg->date->subYears(3))
->where('datetime','<=',$this->msg->date)
->single();
Log::debug(sprintf('%s:- Checking for duplicate from host id [%d].',self::LOGKEY,$this->mo->fftn->id));
Log::debug(sprintf('%s:- Checking for duplicate from host id [%d].',self::LOGKEY,($x=$this->msg->fboss_o) ? $x->id : NULL));
if ($o) {
// @todo Actually update seenby
Log::alert(sprintf('%s:! Duplicate echomail [%s] in [%s] from (%s) [%s] to (%s) - updating seenby.',
self::LOGKEY,
$this->mo->msgid,
$this->mo->echoarea->name,
$this->mo->from,$this->mo->fftn->ftn,
$this->mo->to,
$this->msg->msgid,
$this->msg->echoarea,
$this->msg->user_from,$this->msg->fftn,
$this->msg->user_to,
));
//$o->save();
if (! $o->msg_crc)
$o->msg_crc = md5($this->msg->message);
$o->save();
// @todo This duplicate message may have gone via a different path, be nice to record it.
/*
// If we didnt get the path on the original message, we'll override it
if (! $o->path->count()) {
$dummy = collect();
$path = $this->parseAddresses('path',$this->mo->path,$sender->zone,$dummy);
$path = $this->parseAddresses('path',$this->msg->path,$this->pktsrc->zone,$dummy);
/*
// If our sender is not in the path, add it
if (! $path->contains($this->sender->id)) {
Log::alert(sprintf('%s:? Echomail adding sender to PATH [%s] for [%d].',self::LOGKEY,$x->ftn,$o->id));
$path->push($this->sender->id);
}
*/
$ppoid = NULL;
foreach ($path as $aoid) {
@ -264,24 +310,23 @@ class MessageProcess implements ShouldQueue
$ppoid = $po[0]->id;
}
}
*/
// @todo if we have an export for any of the seenby addresses, remove it
//$seenby = $this->parseAddresses('seenby',$this->mo->seenby,$sender->zone,$o->rogue_seenby);
//$this->mo->seenby()->syncWithoutDetaching($seenby);
$seenby = $this->parseAddresses('seenby',$this->msg->seenby,$this->pktsrc->zone,$o->rogue_seenby);
$x = $o->seenby()->syncWithoutDetaching($seenby);
// In case our rogue_seenby changed
//$this->mo->save();
if ($o->getDirty())
$o->save();
return;
}
}
// Find another message with the same msg_crc
if ($this->mo->msg_crc) {
$o = Echomail::where('msg_crc',$xx=md5($this->mo->msg_crc))
->where('fftn_id',$this->mo->fftn->id)
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();
@ -293,39 +338,74 @@ class MessageProcess implements ShouldQueue
));
}
// If the node is not subscribed
if ($this->pktsrc->echoareas->search(function($item) use ($ea) { return $item->id === $ea->id; }) === FALSE) {
Log::alert(sprintf('%s:! FTN [%s] is not subscribed to [%s] for [%s].',self::LOGKEY,$this->pktsrc->ftn,$ea->name,$this->msg->msgid));
if (! $this->msg->rescanned->count())
Notification::route('netmail',$this->pktsrc)->notify(new EchoareaNotSubscribed($this->msg));
}
// Can the system send messages to this area?
if (! $this->mo->echoarea->can_write($sender->security)) {
Log::alert(sprintf('%s:! FTN [%s] is not allowed to post [%s] to [%s].',self::LOGKEY,$sender->ftn,$this->mo->msgid,$this->mo->echoarea->name));
if (! $rescanned)
Notification::route('netmail',$sender)->notify(new EchoareaNoWrite($this->mo));
if (! $ea->can_write($this->pktsrc->security)) {
Log::alert(sprintf('%s:! FTN [%s] is not allowed to post [%s] to [%s].',self::LOGKEY,$this->pktsrc->ftn,$this->msg->msgid,$ea->name));
if (! $this->msg->rescanned->count())
Notification::route('netmail',$this->pktsrc)->notify(new EchoareaNoWrite($this->msg));
return;
}
// If the node is not subscribed, we'll accept it, but let them know
if (! $sender->echoareas->contains($this->mo->echoarea)) {
Log::alert(sprintf('%s:! FTN [%s] is not subscribed to [%s] for [%s].',self::LOGKEY,$sender->ftn,$this->mo->echoarea->name,$this->mo->msgid));
// We know about this area, store it
$o = new Echomail;
$o->init();
if (! $rescanned)
Notification::route('netmail',$sender)->notify(new EchoareaNotSubscribed($this->mo));
$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();
if ($x=$this->msg->fboss_o) {
$o->fftn_id = $x->id;
} else {
$o->fftn_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
}
// We know about this area, store it
$this->mo->save();
$o->echoarea_id = $ea->id;
$o->msgid = $this->msg->msgid;
$o->replyid = $this->msg->replyid;
$o->tagline = $this->msg->tagline;
$o->tearline = $this->msg->tearline;
$o->origin = $this->msg->origin;
$o->msg = $this->msg->message_src."\r";
$o->msg_src = $this->msg->message_src;
$o->msg_crc = md5($this->msg->message);
$o->set_path = $this->msg->path;
$o->set_seenby = $this->msg->seenby;
$o->set_recvtime = $this->recvtime;
$o->set_sender = $this->pktsrc->id;
// 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->mo->msgid,
$this->mo->echoarea->name,
$this->mo->from,$this->mo->fftn->ftn,
$this->mo->to,
$this->mo->id,
$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) && (! $rescanned))
if ((! $this->skipbot) && (! $this->msg->rescanned->count()))
foreach (config('process.echomail') as $class) {
if ($class::handle($this->mo)) {
if ($class::handle($this->msg)) {
break;
}
}

View File

@ -15,9 +15,9 @@ 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\{Domain,Echomail,Netmail};
use App\Classes\File\Item;
use App\Classes\FTN\{InvalidPacketException,Packet};
use App\Models\Address;
use App\Notifications\Netmails\PacketPasswordInvalid;
class PacketProcess implements ShouldQueue
@ -26,26 +26,22 @@ class PacketProcess implements ShouldQueue
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private string $filename;
private Domain $do;
private Item $file;
private Address $ao;
private Carbon $rcvd_time;
private bool $interactive;
private bool $nobot;
public function __construct(string $filename,Domain $do,bool $interactive=TRUE,Carbon $rcvd_time=NULL,bool $nobot=FALSE)
public function __construct(Item $file,Address $ao,Carbon $rcvd_time)
{
$this->filename = $filename;
$this->do = $do;
$this->interactive = $interactive;
$this->rcvd_time = $rcvd_time ?: Carbon::now();
$this->nobot = $nobot;
$this->file = $file;
$this->ao = $ao;
$this->rcvd_time = $rcvd_time;
}
public function __get($key): mixed
{
switch ($key) {
case 'subject':
return $this->filename;
return $this->file->name;
default:
return NULL;
@ -58,47 +54,30 @@ class PacketProcess implements ShouldQueue
*/
public function handle()
{
Log::info(sprintf('%s:- Processing mail [%s]',self::LOGKEY,$this->filename));
Log::info(sprintf('%s:- Processing mail %s [%s]',self::LOGKEY,$this->file->whatType() === Item::IS_PKT ? 'PACKET' : 'ARCHIVE',$this->file->nameas));
$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));
$f = new File($this->file->full_name);
$processed = FALSE;
foreach ($f as $packet) {
$pkt = Packet::process($packet,Arr::get(stream_get_meta_data($packet),'uri'),$f->itemSize(),$this->do);
$pkt = Packet::process($packet,Arr::get(stream_get_meta_data($packet),'uri'),$f->itemSize(),$this->ao->zone->domain);
// 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));
// Check the messages are from the uplink
if ($this->ao->system->addresses->search(function($item) use ($pkt) { return $item->id === $pkt->fftn_o->id; }) === FALSE) {
Log::error(sprintf('%s:! Packet [%s] is not from this link? [%d]',self::LOGKEY,$pkt->fftn_o->ftn,$this->ao->system_id));
break;
}
if (! our_nodes($this->do)->contains($pkt->fftn)) {
Log::error(sprintf('%s:! Packet [%s] is from a system that is not configured with us? [%s] for [%s]',self::LOGKEY,$this->filename,$pkt->fftn_t,$this->do->name));
// @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;
}
// Check the packet is to our address, if not we'll reject it.
if (! our_address($this->do)->contains($pkt->tftn)) {
Log::error(sprintf('%s:! Packet [%s] is not to our address? [%s]',self::LOGKEY,$this->filename,$pkt->tftn));
// @todo Notification::route('netmail',$pkt->fftn)->notify(new UnexpectedPacketToUs($this->filename));
break;
}
// Check the packet password
if (strtoupper($pkt->fftn->session('pktpass')) !== strtoupper($pkt->password)) {
Log::error(sprintf('%s:! Packet from [%s] with password [%s] is invalid.',self::LOGKEY,$pkt->fftn->ftn,$pkt->password));
if (strtoupper($this->ao->session('pktpass')) !== strtoupper($pkt->password)) {
Log::error(sprintf('%s:! Packet from [%s] with password [%s] is invalid.',self::LOGKEY,$this->ao->ftn,$pkt->password));
Notification::route('netmail',$pkt->fftn)->notify(new PacketPasswordInvalid($pkt->password,$f->pktName()));
Notification::route('netmail',$this->ao)->notify(new PacketPasswordInvalid($pkt->password,$this->file->nameas));
break;
}
@ -110,37 +89,37 @@ class PacketProcess implements ShouldQueue
$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));
Log::info(sprintf('%s:- Mail from [%s] to [%s]',self::LOGKEY,$msg->fftn,$msg->tftn));
if ($msg->errors) {
Log::error(sprintf('%s:! Message [%s] has [%d] errors, unable to process',self::LOGKEY,$msg->msgid,$msg->errors->errors()->count()));
// @todo Quick check that the packet should be processed by us.
// @todo validate that the packet's zone is in the domain.
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));
/*
* // @todo generate exception when echomail for an area that doesnt exist
* // @todo generate exception when echomail for an area sender cannot post to
* // @todo generate exception when echomail for an area sender not subscribed to
* // @todo generate exception when echomail comes from a system not defined here
* // @todo generate exception when echomail comes from a system doesnt exist
*
* // @todo generate exception when netmail to system that doesnt exist (node/point)
* // @todo generate exception when netmail from system that doesnt exist (node/point)
* // @todo generate warning when netmail comes from a system not defined here
*
* // @todo generate exception when packet has wrong password
*/
try {
// Dispatch job.
if ($queue || (! $this->interactive))
MessageProcess::dispatch($msg->withoutRelations(),$this->nobot);
if ($queue)
MessageProcess::dispatch($msg,$f->pktName(),$this->ao->withoutRelations(),$pkt->fftn_o->withoutRelations(),$this->rcvd_time);
else
MessageProcess::dispatchSync($msg->withoutRelations(),$this->nobot);
$count++;
MessageProcess::dispatchSync($msg,$f->pktName(),$this->ao->withoutRelations(),$pkt->fftn_o->withoutRelations(),$this->rcvd_time);
} catch (\Exception $e) {
Log::error(sprintf('%s:! Got error dispatching message [%s] (%d:%s-%s).',self::LOGKEY,$msg->msgid,$e->getLine(),$e->getFile(),$e->getMessage()));
}
$count++;
}
if ($count === $pkt->count())
@ -148,41 +127,42 @@ class PacketProcess implements ShouldQueue
}
if (! $processed) {
Log::alert(sprintf('%s:- Not deleting packet [%s], it doesnt seem to be processed?',self::LOGKEY,$this->filename));
Log::alert(sprintf('%s:- Not deleting packet [%s], it doesnt seem to be processed?',self::LOGKEY,$this->file->nameas));
} 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));
Log::debug(sprintf('%s:- Moving processed packet [%s] to [%s]',self::LOGKEY,$this->file->rel_name,$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));
$fs->move($this->file->rel_name,$x=sprintf('%s/%s',$dir,$this->file->pref_name));
Log::info(sprintf('%s:- Moved processed packet [%s] to [%s]',self::LOGKEY,$this->file->rel_name,$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()));
Log::error(sprintf('%s:! Unable to move packet [%s] to [%s] (%s)',self::LOGKEY,$this->file->full_name,$dir,$e->getMessage()));
} catch (\Exception $e) {
Log::error(sprintf('%s:! Failed moving packet [%s] to [%s] (%s)',self::LOGKEY,$this->filename,$dir,$e->getMessage()));
Log::error(sprintf('%s:! Failed moving packet [%s] to [%s] (%s)',self::LOGKEY,$this->file->full_name,$dir,$e->getMessage()));
}
} else {
Log::debug(sprintf('%s:- Deleting processed packet [%s]',self::LOGKEY,$this->filename));
Log::debug(sprintf('%s:- Deleting processed packet [%s]',self::LOGKEY,$this->file->full_name));
$fs->delete($this->filename);
// @todo Change this to use Storage::disk()
unlink($this->file->full_name);
}
}
} catch (InvalidPacketException $e) {
Log::error(sprintf('%s:- Not deleting packet [%s], as it generated an InvalidPacketException',self::LOGKEY,$this->filename),['e'=>$e->getMessage()]);
Log::error(sprintf('%s:- Not deleting packet [%s], as it generated an InvalidPacketException',self::LOGKEY,$this->file->nameas),['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()]);
Log::error(sprintf('%s:- Not deleting packet [%s], as it generated an uncaught exception',self::LOGKEY,$this->file->nameas),['e'=>$e->getMessage()]);
}
}
}

View File

@ -978,26 +978,47 @@ class Address extends Model
/**
* Get echomail for this node
*
* @param bool $update
* @param Collection|null $echomail
* @return Packet|null
* @throws \Exception
* @todo If we export to uplink hubs without our address in the seenby, they should send the message back to
* us with their seenby's.
*/
public function getEchomail(): ?Packet
public function getEchomail(bool $update=TRUE,Collection $echomail=NULL): ?Packet
{
if (($num=$this->echomailWaiting())->count()) {
$pkt = NULL;
if ($echomail)
return $this->getPacket($echomail);
$s = Setup::findOrFail(config('app.id'));
$num = self::UncollectedEchomail()
->select('echomails.id')
->where('addresses.id',$this->id)
->groupBy(['echomails.id'])
->get();
if ($num->count()) {
// Limit to max messages
Log::info(sprintf('%s:= Got [%d] echomails for [%s] for sending',self::LOGKEY,$num->count(),$this->ftn));
// Limit to max messages
if ($num->count() > $s->msgs_pkt)
Log::notice(sprintf('%s:= Only sending [%d] echomails for [%s]',self::LOGKEY,$s->msgs_pkt,$this->ftn));
return $this->system->packet($this)->mail($num->take($s->msgs_pkt));
$x = $this->echomailWaiting($s->msgs_pkt);
$pkt = $this->getPacket($x);
if ($pkt && $pkt->count() && $update)
DB::table('echomail_seenby')
->whereIn('echomail_id',$x->pluck('id'))
->where('address_id',$this->id)
->whereNull('sent_at')
->whereNotNull('export_at')
->update(['sent_pkt'=>$pkt->name]);
}
return NULL;
return $pkt;
}
/**
@ -1015,47 +1036,52 @@ class Address extends Model
/**
* Get netmail for this node (including it's children)
*
* @param bool $update
* @return Packet|null
* @throws \Exception
*/
public function getNetmail(): ?Packet
public function getNetmail(bool $update=FALSE): ?Packet
{
if (($num=$this->netmailAlertWaiting())->count()) {
Log::debug(sprintf('%s:= Packaging [%d] netmail alerts to [%s]',self::LOGKEY,$num->count(),$this->ftn));
$pkt = NULL;
// Find any message that defines a packet password
$pass = $num
->map(function($item) {
$passpos = strpos($item->subject,':');
return (($passpos > 0) && ($passpos < 8)) ? substr($item->subject,0,$passpos) : NULL;
})
->filter()
->pop();
Log::debug(sprintf('%s:= Overwriting system packet password with [%s] for [%s]',self::LOGKEY,$pass,$this->ftn));
return $this->system->packet($this,$pass)->mail(
$num->filter(fn($item)=>preg_match("/^{$pass}:/",$item->subject))
->transform(function($item) use ($pass) {
$item->subject = preg_replace("/^{$pass}:/",'',$item->subject);
return $item;
})
);
}
if (($num=$this->netmailWaiting())->count()) {
$s = Setup::findOrFail(config('app.id'));
Log::debug(sprintf('%s:= Got [%d] netmails for [%s] for sending',self::LOGKEY,$num->count(),$this->ftn));
if (($x=$this->netmailAlertWaiting())->count()) {
Log::debug(sprintf('%s:= Packaging [%d] netmail alerts to [%s]',self::LOGKEY,$x->count(),$this->ftn));
$passpos = strpos($x->last()->subject,':');
// Limit to max messages
if ($num->count() > $s->msgs_pkt)
Log::alert(sprintf('%s:= Only sending [%d] netmails for [%s]',self::LOGKEY,$num->count(),$this->ftn));
if ($passpos > 8)
Log::alert(sprintf('%s:! Password would be greater than 8 chars? [%d]',self::LOGKEY,$passpos));
return $this->system->packet($this)->mail($num->take($s->msgs_pkt));
$pkt = $this->getPacket($x,substr($x->last()->subject,0,$passpos));
if ($pkt && $pkt->count() && $update)
DB::table('netmails')
->whereIn('id',$x->pluck('id'))
->update(['sent_pkt'=>$pkt->name]);
return $pkt;
}
return NULL;
if (($x=$this->netmailWaiting())
->count())
{
Log::debug(sprintf('%s:= Got [%d] netmails for [%s] for sending',self::LOGKEY,$x->count(),$this->ftn));
if ($x->count() > $s->msgs_pkt) {
$x = $x->take($s->msgs_pkt);
Log::alert(sprintf('%s:= Only sending [%d] netmails for [%s]',self::LOGKEY,$x->count(),$this->ftn));
}
$pkt = $this->getPacket($x);
if ($pkt && $pkt->count() && $update)
DB::table('netmails')
->whereIn('id',$x->pluck('id'))
->update(['sent_pkt'=>$pkt->name]);
}
return $pkt;
}
/**
@ -1065,9 +1091,8 @@ class Address extends Model
* @param string|null $passwd Override password used in packet
* @return Packet|null
* @throws \Exception
* @deprecated
*/
private function getPacket(Collection $msgs,string $passwd=NULL): ?Packet
public function getPacket(Collection $msgs,string $passwd=NULL): ?Packet
{
$s = Setup::findOrFail(config('app.id'));
$ao = our_address($this);
@ -1079,7 +1104,7 @@ class Address extends Model
}
// Get packet type
$o = $ao->system->packet($this);
$o = $ao->system->packet();
$o->addressHeader($ao,$this,$passwd);
// $oo = Netmail/Echomail Model
@ -1126,7 +1151,6 @@ class Address extends Model
*
* @return Collection
* @throws \Exception
* @note The packet password to use is on the subject line for these alerts
*/
public function netmailAlertWaiting(): Collection
{

View File

@ -10,75 +10,52 @@ use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Casts\{CollectionOrNull,CompressedString};
use App\Classes\FTN\Message;
use App\Interfaces\Packet;
use App\Traits\{MessageAttributes,MsgID,ParseAddresses,QueryCacheableConfig};
use App\Traits\{EncodeUTF8,MsgID,ParseAddresses,QueryCacheableConfig};
final class Echomail extends Model implements Packet
{
use SoftDeletes,MessageAttributes,MsgID,ParseAddresses,QueryCacheableConfig;
use SoftDeletes,EncodeUTF8,MsgID,ParseAddresses,QueryCacheableConfig;
private const LOGKEY = 'ME-';
private Collection $set_seenby;
private Collection $set_path;
private Carbon $set_recvtime;
private string $set_pkt;
private string $set_sender;
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 = [
'datetime' => 'datetime:Y-m-d H:i:s',
'kludges' => CollectionOrNull::class,
'msg' => CompressedString::class,
'msg_src' => CompressedString::class,
'rogue_seenby' => CollectionOrNull::class,
'rogue_path' => CollectionOrNull::class, // @deprecated?
'rogue_path' => CollectionOrNull::class,
];
private const cast_utf8 = [
'to',
'from',
'subject',
'msg',
'msg_src',
'origin',
'tearline',
'tagline',
];
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;
// 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_pkt':
case 'set_sender':
case 'set_recvtime':
case 'set_seenby':
if (! $this->set->has($key))
$this->set->put($key,collect());
$this->set->get($key)->push($value);
$this->{$key} = $value;
break;
default:
@ -90,12 +67,6 @@ final class Echomail extends Model implements Packet
{
parent::boot();
static::creating(function($model) {
if (! is_null($model->errors))
throw new \Exception('Cannot save, validation errors exist');
});
// @todo dont save us in the seenby/path, we'll add it dynamically when we send out.
// @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();
@ -103,10 +74,8 @@ final class Echomail extends Model implements Packet
$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));
if ($model->set_path->count())
$path = self::parseAddresses('path',$model->set_path,$model->fftn->zone,$rogue);
// Make sure our sender is first in the path
if (! $path->contains($model->fftn_id)) {
@ -115,9 +84,9 @@ final class Echomail extends Model implements Packet
}
// 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);
if (isset($model->set_sender) && (! $path->contains($model->set_sender))) {
Log::alert(sprintf('%s:? Echomail adding pktsrc to end of PATH [%s].',self::LOGKEY,$model->set_sender));
$path->push($model->set_sender);
}
// Save the Path
@ -136,8 +105,8 @@ final class Echomail extends Model implements Packet
// @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);
if ($model->set_seenby->count())
$seenby = self::parseAddresses('seenby',$model->set_seenby,$model->fftn->zone,$rogue);
// Make sure our sender is in the seenby
if (! $seenby->contains($model->fftn_id)) {
@ -146,9 +115,9 @@ final class Echomail extends Model implements Packet
}
// 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 (isset($model->set_sender) && (! $seenby->contains($model->set_sender))) {
Log::alert(sprintf('%s:? Echomail adding pktsrc to SEENBY [%s].',self::LOGKEY,$model->set_sender));
$seenby->push($model->set_sender);
}
if (count($rogue)) {
@ -160,26 +129,26 @@ final class Echomail extends Model implements Packet
$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 (isset($model->set_pkt) && isset($model->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'),
$model->set_pkt,
$model->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')));
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_pkt,$model->set_recvtime));
}
}
// See if we need to export this message.
if ($model->echoarea->sec_read) {
$exportto = $model
$exportto = ($x=$model
->echoarea
->addresses
->filter(function($item) use ($model) { return $model->echoarea->can_read($item->security); })
->filter(function($item) use ($model) { return $model->echoarea->can_read($item->security); }))
->pluck('id')
->diff($seenby);
@ -224,19 +193,90 @@ final class Echomail extends Model implements Packet
->withPivot(['id','parent_id','recv_pkt','recv_at']);
}
/* ATTRIBUTES */
/* METHODS */
public function getSeenByAttribute(): Collection
public function init(): void
{
return ((! $this->exists) && $this->set->has('set_seenby'))
? $this->set->get('set_seenby')
: $this->getRelationValue('seenby');
$this->set_path = collect();
$this->set_seenby = collect();
}
public function getPathAttribute(): Collection
public function jsonSerialize(): array
{
return ((! $this->exists) && $this->set->has('set_path'))
? $this->set->get('set_path')
: $this->getRelationValue('path');
return $this->encode();
}
/**
* Return this model as a packet
*/
public function packet(Address $ao): Message
{
Log::info(sprintf('%s:+ Bundling [%s]',self::LOGKEY,$this->id));
$sysaddress = our_address($this->fftn);
if (! $sysaddress)
throw new \Exception(sprintf('%s:! We dont have an address in this network? (%s)',self::LOGKEY,$this->fftn->zone->domain->name));
// @todo Dont bundle mail to nodes that have been disabled, or addresses that have been deleted
$o = new Message;
$o->header = [
'onode' => $sysaddress->node_id,
'dnode' => $ao->node_id,
'onet' => $sysaddress->host_id,
'dnet' => $ao->host_id,
'flags' => 0,
'cost' => 0,
'date'=>$this->datetime->format('d M y H:i:s'),
];
$o->tzutc = $this->datetime->utcOffset($this->tzoffset)->getOffsetString('');
$o->user_to = $this->to;
$o->user_from = $this->from;
$o->subject = $this->subject;
$o->echoarea = $this->echoarea->name;
$o->flags = $this->flags;
if ($this->kludges)
$o->kludge = collect($this->kludges);
$o->kludge->put('dbid',$this->id);
$o->msgid = $this->msgid;
if ($this->replyid)
$o->replyid = $this->replyid;
$o->message = $this->msg;
if ($this->tagline)
$o->tagline = $this->tagline;
if ($this->tearline)
$o->tearline = $this->tearline;
if ($this->origin)
$o->origin = $this->origin;
$o->seenby = $this->seenby->push($sysaddress)->unique()->pluck('ftn2d');
// Add our address to the path and seenby
$o->path = $this->pathorder()->merge($sysaddress->ftn2d);
$o->packed = TRUE;
return $o;
}
public function pathorder(string $display='ftn2d',int $start=NULL): Collection
{
$result = collect();
if ($x=$this->path->firstWhere('pivot.parent_id',$start)) {
$result->push($x->$display);
$result->push($this->pathorder($display,$x->pivot->id));
}
return $result->flatten()->filter();
}
}

View File

@ -10,30 +10,35 @@ use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Casts\{CollectionOrNull,CompressedString};
use App\Casts\CompressedString;
use App\Classes\FTN\Message;
use App\Interfaces\Packet;
use App\Pivots\ViaPivot;
use App\Traits\{MessageAttributes,MsgID};
use App\Traits\{EncodeUTF8,MsgID};
final class Netmail extends Model implements Packet
{
use SoftDeletes,MsgID,MessageAttributes;
private const LOGKEY = 'MN-';
private const PATH_REGEX = '/^([0-9]+:[0-9]+\/[0-9]+(\..*)?)\s+@([0-9.a-zA-Z]+)\s+(.*)$/';
/**
* Kludges that we absorb in this model
*/
private const kludges = [
'MSGID:'=>'msgid',
'REPLY:'=>'replyid',
'Via' => 'set_path',
use SoftDeletes,EncodeUTF8,MsgID;
private Collection $set_path;
private Address $set_sender;
private Carbon $set_recvtime;
private string $set_pkt;
private const cast_utf8 = [
'to',
'from',
'subject',
'msg',
'msg_src',
'origin',
'tearline',
'tagline',
];
protected $casts = [
'datetime' => 'datetime:Y-m-d H:i:s',
'kludges' => CollectionOrNull::class,
'msg' => CompressedString::class,
'msg_src' => CompressedString::class,
'sent_at' => 'datetime:Y-m-d H:i:s',
@ -42,36 +47,11 @@ final class Netmail extends Model implements Packet
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;
// Values that we pass to boot() to record how we got this netmail
case 'set_path':
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':
$this->set->put($key,$value);
break;
// The path the netmail went through to get here
case 'set_path':
if (! $this->set->has($key))
$this->set->put($key,collect());
$this->set->get($key)->push($value);
$this->{$key} = $value;
break;
default:
@ -83,23 +63,18 @@ final class Netmail extends Model implements Packet
{
parent::boot();
static::creating(function($model) {
if (! is_null($model->errors))
throw new \Exception('Cannot save, validation errors exist');
});
static::created(function($model) {
$nodes = collect();
// Parse PATH
// <FTN Address> @YYYYMMDD.HHMMSS[.Precise][.Time Zone] <Program Name> <Version> [Serial Number]
if ($model->set->has('set_path')) {
foreach ($model->set->get('set_path') as $line) {
if (isset($model->set_path)) {
if ($model->set_path->count()) {
foreach ($model->set_path as $line) {
$m = [];
if (preg_match(self::PATH_REGEX,$line,$m)) {
if (preg_match('/^([0-9]+:[0-9]+\/[0-9]+(\..*)?)\s+@([0-9.a-zA-Z]+)\s+(.*)$/',$line,$m)) {
// Address
// @todo Do we need to add a domain here, since the path line may not include one
$ao = Address::findFTN($m[1]);
// Time
@ -112,7 +87,7 @@ final class Netmail extends Model implements Packet
$datetime = Carbon::createFromFormat('Ymd.His',$t[1],$t[3] ?? '');
if (! $ao) {
Log::alert(sprintf('%s:! Undefined Node [%s] in netmail path.',self::LOGKEY,$m[1]));
Log::alert(sprintf('%s:! Undefined Node [%s] for Netmail.',self::LOGKEY,$m[1]));
//$rogue->push(['node'=>$m[1],'datetime'=>$datetime,'program'=>$m[4]]);
} else {
@ -122,8 +97,9 @@ final class Netmail extends Model implements Packet
}
// If there are no details (Mystic), we'll create a blank
} elseif ($model->set->has('set_sender')) {
$nodes->push(['node'=>$model->set->get('set_sender'),'datetime'=>Carbon::now(),'program'=>'Unknown']);
} else {
$nodes->push(['node'=>$model->set_sender,'datetime'=>Carbon::now(),'program'=>'Unknown']);
}
}
// Save the Path
@ -142,25 +118,15 @@ final class Netmail extends Model implements Packet
}
// Our last node in the path is our sender
if ($nodes->count() && $model->set->has('set_pkt') && $model->set->has('set_sender') && $model->set->has('set_recvtime')) {
if ($nodes->count() && isset($model->set_pkt) && isset($model->set_sender) && isset($model->set_recvtime)) {
DB::update('UPDATE netmail_path set recv_pkt=?,recv_at=?,recv_id=? where address_id=? and netmail_id=?',[
$model->set->get('set_pkt'),
$model->set->get('set_recvtime'),
$model->set->get('set_sender')->id,
$model->set_pkt,
$model->set_recvtime,
$model->set_sender->id,
Arr::get($nodes->last(),'node')->id,
$model->id,
]);
}
// Save our origin, tearline & tagline
if ($model->set->has('set_tagline'))
$model->tagline = $model->set->get('set_tagline');
if ($model->set->has('set_tearline'))
$model->tearline = $model->set->get('set_tearline');
if ($model->set->has('set_origin'))
$model->origin = $model->set->get('set_origin');
$model->save();
});
}
@ -176,8 +142,13 @@ final class Netmail extends Model implements Packet
public function path()
{
return $this->belongsToMany(Address::class,'netmail_path')
->withPivot(['id','parent_id','datetime','program','recv_pkt','recv_id'])
->using(ViaPivot::class);
->withPivot(['id','parent_id','datetime','program','recv_pkt','recv_id']);
}
public function received()
{
return $this->belongsToMany(Address::class,'netmail_path','netmail_id','recv_id')
->withPivot(['id','parent_id','datetime','program','recv_pkt','recv_id']);
}
public function tftn()
@ -187,41 +158,88 @@ final class Netmail extends Model implements Packet
->withTrashed();
}
/* ATTRIBUTES */
/**
* Enable rendering the path even if the model hasnt been saved
*
* @return Collection
*/
public function getPathAttribute(): Collection
{
return ((! $this->exists) && $this->set->has('set_path'))
? $this->set->get('set_path')->map(function($item) {
$m = [];
preg_match(self::PATH_REGEX,$item,$m);
return $m[1];
})
: $this->getRelationValue('path');
}
/* METHODS */
/**
* Render the via line
*
* @param Address $ao
* @return string
* @throws \Exception
* Return this model as a packet
*/
public function via(Address $ao): string
public function packet(Address $ao,string $strippass=NULL): Message
{
if (! $ao->pivot)
throw new \Exception('Cannot render the via line without an address record without a path pivot');
Log::debug(sprintf('%s:+ Bundling [%s]',self::LOGKEY,$this->id));
return sprintf('%s @%s.UTC %s',
$ao->ftn3d,
$ao->pivot->datetime->format('Ymd.His'),
$ao->pivot->program);
// @todo Dont bundle mail to nodes that have been disabled, or addresses that have been deleted
$o = new Message;
try {
$o->header = [
'onode' => $this->fftn->node_id,
'dnode' => $this->tftn->node_id,
'onet' => $this->fftn->host_id,
'dnet' => $this->tftn->host_id,
'opoint' => $this->fftn->point_id,
'dpoint' => $this->tftn->point_id,
'flags' => 0,
'cost' => 0,
'date'=>$this->datetime->format('d M y H:i:s'),
];
$o->tzutc = $this->datetime->utcOffset($this->tzoffset)->getOffsetString('');
$o->user_to = $this->to;
$o->user_from = $this->from;
$o->subject = (! is_null($strippass)) ? preg_replace('/^'.$strippass.':/','',$this->subject) : $this->subject;
// INTL kludge
$o->intl = sprintf('%s %s',$this->tftn->ftn3d,$this->fftn->ftn3d);
$o->flags = $this->flags;
$o->msgid = $this->msgid
? $this->msgid
: sprintf('%s %08x',$this->fftn->ftn4d,timew($this->datetime));
if ($this->replyid)
$o->replyid = $this->replyid;
$o->kludge->put('dbid',$this->id);
$o->message = $this->msg;
$o->tagline = $this->tagline;
$o->tearline = $this->tearline;
// VIA kludge
$via = $this->via ?: collect();
// Add our address to the VIA line
$via->push(
sprintf('%s @%s.UTC %s %d.%d/%s %s',
our_address($this->fftn)->ftn3d,
Carbon::now()->utc()->format('Ymd.His'),
str_replace(' ','_',Setup::PRODUCT_NAME),
Setup::PRODUCT_VERSION_MAJ,
Setup::PRODUCT_VERSION_MIN,
(new Setup)->version,
Carbon::now()->format('Y-m-d'),
));
$o->via = $via;
$o->packed = TRUE;
} catch (\Exception $e) {
Log::error(sprintf('%s:! Error converting netmail [%s] to a message (%d:%s)',self::LOGKEY,$this->id,$e->getLine(),$e->getMessage()));
dump($this);
}
return $o;
}
public function pathorder(string $display='ftn2d',int $start=NULL): Collection
{
$result = collect();
if ($x=$this->path->firstWhere('pivot.parent_id',$start)) {
$result->push($x->$display);
$result->push($this->pathorder($display,$x->pivot->id));
}
return $result->flatten()->filter();
}
}

View File

@ -110,8 +110,7 @@ class System extends Model
public function sessions()
{
return $this->belongsToMany(Zone::class)
->withPivot(['sespass','pktpass','ticpass','fixpass','zt_ipv4','zt_ipv6','default'])
->dontCache();
->withPivot(['sespass','pktpass','ticpass','fixpass','zt_ipv4','zt_ipv6','default']);
}
/**
@ -252,19 +251,12 @@ class System extends Model
/**
* Return the packet that this system uses
*
* @param Address $ao
* @param string|null $password
* @return Packet
*/
public function packet(Address $ao,string $password=NULL): Packet
public function packet(): Packet
{
// @todo Check that the address is one of the system's addresses
return
(new (collect(Packet::PACKET_TYPES)
->get($this->pkt_type ?: config('fido.packet_default'))))
->for($ao)
->password($password);
return new (collect(Packet::PACKET_TYPES)
->get($this->pkt_type ?: config('fido.packet_default')));
}
public function poll(): ?Job

View File

@ -41,6 +41,6 @@ class EchomailChannel
$o = $notification->toEchomail($notifiable);
Log::info(sprintf('%s:= Posted echomail (%d) [%s] to [%s]',self::LOGKEY,$o->id,$o->msgid,$echoarea->name));
Log::info(sprintf('%s:= Posted echomail [%s] to [%s]',self::LOGKEY,$o->msgid,$echoarea));
}
}

View File

@ -41,6 +41,6 @@ class NetmailChannel
$o = $notification->toNetmail($notifiable);
Log::info(sprintf('%s:= Sent netmail (%d) [%s] to [%s]',self::LOGKEY,$o->id,$o->msgid,$ao->ftn));
Log::info(sprintf('%s:= Sent netmail [%s] to [%s]',self::LOGKEY,$o->msgid,$ao->ftn));
}
}

View File

@ -8,7 +8,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
use App\Classes\FTN\Message;
use App\Models\{Echoarea,Echomail,Setup};
use App\Models\{Echoarea, Echomail, Setup, System};
abstract class Echomails extends Notification //implements ShouldQueue
{
@ -46,24 +46,26 @@ abstract class Echomails extends Notification //implements ShouldQueue
*/
abstract public function toEchomail(object $notifiable): Echomail;
protected function setupEchomail(Echomail $mo,object $notifiable): Echomail
protected function setupEchomail(Message $mo,object $notifiable): Echomail
{
$echoarea = $notifiable->routeNotificationFor(static::via);
$eo = Echoarea::where('name',$echoarea)->singleOrFail();
$o = new Echomail;
$o->init();
$o->from = Setup::PRODUCT_NAME;
$o->replyid = $mo->msgid;
$o->echoarea_id = $echoarea->id;
$o->echoarea_id = $eo->id;
$o->datetime = Carbon::now();
$o->tzoffset = $o->datetime->utcOffset();
$o->fftn_id = ($x=our_address($mo->fftn))->id;
$o->fftn_id = ($x=our_address($mo->fboss_o))->id;
$o->flags = (Message::FLAG_LOCAL);
$o->tearline = sprintf('%s (%04X)',Setup::PRODUCT_NAME,Setup::PRODUCT_ID);
$o->origin = sprintf('%s (%s)',Setup::PRODUCT_NAME,$x->ftn4d);
$o->kludges->put('CHRS:',$mo->kludges->get('chrs') ?: 'CP437 2');
$o->kludges = collect(['chrs'=>$mo->kludge->get('chrs') ?: 'CP437 2']);
return $o;
}

View File

@ -6,8 +6,8 @@ use Carbon\Carbon;
use Carbon\CarbonInterface;
use Illuminate\Support\Facades\Log;
use App\Classes\{Fonts\Thick,Fonts\Thin,Page};
use App\Models\Echomail;
use App\Classes\{Fonts\Thick,Fonts\Thin,FTN\Message,Page};
use App\Models\{Echomail,System};
use App\Notifications\Echomails;
use App\Traits\MessagePath;
@ -17,14 +17,14 @@ class Test extends Echomails
private const LOGKEY = 'NNP';
private Echomail $mo;
private Message $mo;
/**
* Reply to a netmail ping request.
*
* @param Echomail $mo
* @param Message $mo
*/
public function __construct(Echomail $mo)
public function __construct(Message $mo)
{
parent::__construct();
@ -43,9 +43,9 @@ class Test extends Echomails
$o = $this->setupEchomail($this->mo,$notifiable);
$echoarea = $notifiable->routeNotificationFor(static::via);
Log::info(sprintf('%s:+ Creating TEST echomail in [%s]',self::LOGKEY,$echoarea->name));
Log::info(sprintf('%s:+ Creating test echomail to [%s]',self::LOGKEY,$echoarea));
$o->to = $this->mo->from;
$o->to = $this->mo->user_from;
$o->subject = 'Test Reply';
// Message

View File

@ -6,7 +6,7 @@ use Illuminate\Support\Facades\Log;
use App\Classes\FTN\Message;
use App\Notifications\Netmails;
use App\Models\{Netmail,User};
use App\Models\{Netmail,System,User};
use App\Traits\PageTemplate;
class AddressLink extends Netmails

View File

@ -4,8 +4,9 @@ namespace App\Notifications\Netmails;
use Illuminate\Support\Facades\Log;
use App\Classes\FTN\Message;
use App\Notifications\Netmails;
use App\Models\Netmail;
use App\Models\{Netmail,System};
use App\Traits\{MessagePath,PageTemplate};
class Areafix extends Netmails
@ -14,14 +15,14 @@ class Areafix extends Netmails
private const LOGKEY = 'NAF';
private Netmail $mo;
private Message $mo;
/**
* Reply to an areafix request.
*
* @param Netmail $mo
* @param Message $mo
*/
public function __construct(Netmail $mo)
public function __construct(Message $mo)
{
parent::__construct();
@ -42,7 +43,7 @@ class Areafix extends Netmails
Log::info(sprintf('%s:+ Responding to areafix with netmail to [%s]',self::LOGKEY,$ao->ftn));
$o->to = $this->mo->from;
$o->to = $this->mo->user_from;
$o->replyid = $this->mo->msgid;
$o->subject = 'Areafix Reply';

View File

@ -4,8 +4,9 @@ namespace App\Notifications\Netmails\Areafix;
use Illuminate\Support\Facades\Log;
use App\Classes\FTN\Message;
use App\Notifications\Netmails;
use App\Models\Netmail;
use App\Models\{Netmail,System};
use App\Traits\{MessagePath,PageTemplate};
class NotConfiguredHere extends Netmails
@ -14,14 +15,14 @@ class NotConfiguredHere extends Netmails
private const LOGKEY = 'NCH';
private Netmail $mo;
private Message $mo;
/**
* Reply to a areafix, but the system isnt configured here.
*
* @param Netmail $mo
* @param Message $mo
*/
public function __construct(Netmail $mo)
public function __construct(Message $mo)
{
parent::__construct();
@ -42,7 +43,7 @@ class NotConfiguredHere extends Netmails
Log::info(sprintf('%s:+ Responding to areafix for a node [%s] not configured here',self::LOGKEY,$ao->ftn));
$o->to = $this->mo->from;
$o->to = $this->mo->user_from;
$o->replyid = $this->mo->msgid;
$o->subject = 'Areafix - Not Configured Here';

View File

@ -5,8 +5,9 @@ namespace App\Notifications\Netmails;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use App\Classes\FTN\Message;
use App\Notifications\Netmails;
use App\Models\{Echomail,Netmail};
use App\Models\{Netmail,System};
use App\Traits\{MessagePath,PageTemplate};
class EchoareaNoWrite extends Netmails
@ -15,14 +16,14 @@ class EchoareaNoWrite extends Netmails
private const LOGKEY = 'NNW';
private Echomail $mo;
private Message $mo;
/**
* Send a sysop a message if they attempt to write to an area that they dont have permission.
*
* @param Echomail $mo
* @param Message $mo
*/
public function __construct(Echomail $mo)
public function __construct(Message $mo)
{
parent::__construct();
@ -41,9 +42,9 @@ class EchoareaNoWrite extends Netmails
$o = $this->setupNetmail($notifiable);
$ao = $notifiable->routeNotificationFor(static::via);
Log::info(sprintf('%s:+ Creating ECHOAREA NO WRITE netmail to [%s]',self::LOGKEY,$ao->ftn));
Log::info(sprintf('%s:+ Creating ECHOMAIL NO WRITE netmail to [%s]',self::LOGKEY,$ao->ftn));
$o->subject = sprintf('Echomail #%s rejected to %s',$this->mo->msgid,$this->mo->echoarea->name);
$o->subject = 'Echomail rejected - '.$this->mo->msgid;
// Message
$msg = $this->page(FALSE,'nowrite');
@ -51,13 +52,13 @@ class EchoareaNoWrite extends Netmails
$msg->addText(
sprintf("Your echomail with ID [%s] to [%s] here was received here on [%s] and it looks like you sent it on [%s].\r\r",
$this->mo->msgid,
$this->mo->to,
$this->mo->user_to,
Carbon::now()->utc()->toDateTimeString(),
$this->mo->date->utc()->toDateTimeString(),
)
);
$msg->addText(sprintf("It appears that you do not have permission to post in [%s] using the address [%s], so the message from your system was rejected.\r\r",$this->mo->echoarea->name,$ao->ftn));
$msg->addText(sprintf("It appears that you do not have permission to post in this echoarea using the address [%s], so the message from your system was rejected.\r\r",$ao->ftn));
$msg->addText("Please contact the ZC if you think this is a mistake.\r\r");
$msg->addText($this->message_path($this->mo));

View File

@ -7,7 +7,7 @@ use Illuminate\Support\Facades\Log;
use App\Classes\FTN\Message;
use App\Notifications\Netmails;
use App\Models\{Echomail,Netmail};
use App\Models\{Netmail,System};
use App\Traits\{MessagePath,PageTemplate};
class EchoareaNotExist extends Netmails
@ -16,14 +16,14 @@ class EchoareaNotExist extends Netmails
private const LOGKEY = 'NNW';
private Echomail $mo;
private Message $mo;
/**
* Send a sysop a message if they attempt to write to an area that doesnt exist.
*
* @param Message $mo
*/
public function __construct(Echomail $mo)
public function __construct(Message $mo)
{
parent::__construct();
@ -42,9 +42,9 @@ class EchoareaNotExist extends Netmails
$o = $this->setupNetmail($notifiable);
$ao = $notifiable->routeNotificationFor(static::via);
Log::info(sprintf('%s:+ Creating ECHOAREA NOT EXIST netmail to [%s]',self::LOGKEY,$ao->ftn));
Log::info(sprintf('%s:+ Creating ECHOMAIL NOT EXIST netmail to [%s]',self::LOGKEY,$ao->ftn));
$o->subject = 'Echoarea doesnt exist - '.$this->mo->set->get('set_echoarea');
$o->subject = 'Echoarea doesnt exist - '.$this->mo->echoarea;
// Message
$msg = $this->page(FALSE,'nothere');
@ -52,13 +52,13 @@ class EchoareaNotExist extends Netmails
$msg->addText(
sprintf("Your echomail with ID [%s] to [%s] here was received here on [%s] and it looks like you sent it on [%s].\r\r",
$this->mo->msgid,
$this->mo->to,
$this->mo->user_to,
Carbon::now()->utc()->toDateTimeString(),
$this->mo->date->utc()->toDateTimeString(),
)
);
$msg->addText(sprintf("It appears that the echoarea [%s] that this message is for doesnt exist, so the message from your system was rejected.\r\r",$this->mo->set->get('set_echoarea')));
$msg->addText("It appears that the echoarea that this message is for doesnt exist, so the message from your system was rejected.\r\r");
$msg->addText("Please contact the ZC if you think this is a mistake.\r\r");
$msg->addText($this->message_path($this->mo));

View File

@ -5,8 +5,9 @@ namespace App\Notifications\Netmails;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use App\Classes\FTN\Message;
use App\Notifications\Netmails;
use App\Models\{Echomail,Netmail};
use App\Models\{Netmail,System};
use App\Traits\{MessagePath,PageTemplate};
class EchoareaNotSubscribed extends Netmails
@ -15,14 +16,14 @@ class EchoareaNotSubscribed extends Netmails
private const LOGKEY = 'NNW';
private Echomail $mo;
private Message $mo;
/**
* Send a sysop a message if they write to an area that they hadnt previously subscribed to.
*
* @param Echomail $mo
* @param Message $mo
*/
public function __construct(Echomail $mo)
public function __construct(Message $mo)
{
parent::__construct();
@ -41,17 +42,17 @@ class EchoareaNotSubscribed extends Netmails
$o = $this->setupNetmail($notifiable);
$ao = $notifiable->routeNotificationFor(static::via);
Log::info(sprintf('%s:+ Creating ECHOAREA NOT SUBSCRIBED netmail to [%s]',self::LOGKEY,$ao->ftn));
Log::info(sprintf('%s:+ Creating ECHOMAIL NOT SUBSCRIBED netmail to [%s]',self::LOGKEY,$ao->ftn));
$o->subject = 'Echoarea not subscribed - '.$this->mo->echoarea->name;
$o->subject = 'Echoarea not subscribed - '.$this->mo->echoarea;
// Message
$msg = $this->page(FALSE,'nosub');
$msg = $this->page(FALSE,'nothere');
$msg->addText(
sprintf("Your echomail with ID [%s] to [%s] here was received here on [%s] and it looks like you sent it on [%s].\r\r",
$this->mo->msgid,
$this->mo->to,
$this->mo->user_to,
Carbon::now()->utc()->toDateTimeString(),
$this->mo->date->utc()->toDateTimeString(),
)

View File

@ -7,7 +7,7 @@ use Illuminate\Support\Facades\Log;
use App\Classes\FTN\Message;
use App\Notifications\Netmails;
use App\Models\Netmail;
use App\Models\{Netmail,System};
use App\Traits\{MessagePath,PageTemplate};
class EchomailBadAddress extends Netmails
@ -52,7 +52,7 @@ class EchomailBadAddress extends Netmails
$msg->addText(
sprintf("Your echomail with ID [%s] to [%s] here was received here on [%s] and it looks like you sent it on [%s].\r\r",
$this->mo->msgid,
$this->mo->to,
$this->mo->user_to,
Carbon::now()->utc()->toDateTimeString(),
$this->mo->date->utc()->toDateTimeString(),
)

View File

@ -4,8 +4,9 @@ namespace App\Notifications\Netmails;
use Illuminate\Support\Facades\Log;
use App\Classes\FTN\Message;
use App\Notifications\Netmails;
use App\Models\{Address,Netmail};
use App\Models\{Address,Netmail,System};
use App\Traits\{MessagePath,PageTemplate};
class NetmailForward extends Netmails
@ -15,15 +16,15 @@ class NetmailForward extends Netmails
private const LOGKEY = 'NNP';
private Address $ao;
private Netmail $mo;
private Message $mo;
/**
* Reply to a netmail ping request.
*
* @param Netmail $mo
* @param Message $mo
* @param Address $ao
*/
public function __construct(Netmail $mo,Address $ao)
public function __construct(Message $mo,Address $ao)
{
parent::__construct();
@ -43,9 +44,9 @@ class NetmailForward extends Netmails
$o = $this->setupNetmail($notifiable);
$ao = $notifiable->routeNotificationFor(static::via);
Log::info(sprintf('%s:+ Advising [%s@%s] that netmail to [%s] will be forwarded to [%s].',self::LOGKEY,$this->mo->from,$ao->ftn,$this->mo->to,$this->ao->ftn));
Log::info(sprintf('%s:+ Advising [%s@%s] that netmail to [%s] will be forwarded to [%s].',self::LOGKEY,$this->mo->user_from,$ao->ftn,$this->mo->user_to,$this->ao->ftn));
$o->to = $this->mo->from;
$o->to = $this->mo->user_from;
$o->replyid = $this->mo->msgid;
$o->subject = sprintf('Your netmail is being forwarded to %s',$this->ao->ftn3d);
@ -55,10 +56,10 @@ class NetmailForward extends Netmails
$msg->addText("Howdy, Clrghouz is not a BBS, so users cannot login to collect netmail.\r\r\r");
$msg->addText(sprintf("Never fear, your msg [%s] to [%s] has been forwarded, to [%s].\r\r",
$this->mo->msgid,
$this->mo->to,
$this->mo->user_to,
$this->ao->ftn3d,
));
$msg->addText(sprintf("To avoid receiving this netmail, send messages to [%s] to [%s].\r\r",$this->mo->to,$this->ao->ftn3d));
$msg->addText(sprintf("To avoid receiving this netmail, send messages to [%s] to [%s].\r\r",$this->mo->user_to,$this->ao->ftn3d));
$o->msg = $msg->render();
$o->tagline = 'Thank you so much for your mail. I love it already.';

View File

@ -6,7 +6,7 @@ use Illuminate\Support\Facades\Log;
use App\Notifications\Netmails;
use App\Classes\FTN\Message;
use App\Models\Netmail;
use App\Models\{Netmail,System};
use App\Traits\PageTemplate;
class PacketPasswordInvalid extends Netmails

View File

@ -8,7 +8,7 @@ use Illuminate\Support\Facades\Log;
use App\Classes\FTN\Message;
use App\Notifications\Netmails;
use App\Models\Netmail;
use App\Models\{Netmail,System};
use App\Traits\{MessagePath,PageTemplate};
class Ping extends Netmails
@ -17,14 +17,14 @@ class Ping extends Netmails
private const LOGKEY = 'NNP';
private Netmail $mo;
private Message $mo;
/**
* Reply to a netmail ping request.
*
* @param Message $mo
*/
public function __construct(Netmail $mo)
public function __construct(Message $mo)
{
parent::__construct();
@ -45,7 +45,7 @@ class Ping extends Netmails
Log::info(sprintf('%s:+ Creating PING netmail to [%s]',self::LOGKEY,$ao->ftn));
$o->to = $this->mo->from;
$o->to = $this->mo->user_from;
$o->replyid = $this->mo->msgid;
$o->subject = 'Ping Reply';

View File

@ -5,8 +5,9 @@ namespace App\Notifications\Netmails;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use App\Classes\FTN\Message;
use App\Notifications\Netmails;
use App\Models\Netmail;
use App\Models\{Netmail,System};
use App\Traits\{MessagePath,PageTemplate};
class PollingFailed extends Netmails

View File

@ -5,24 +5,25 @@ namespace App\Notifications\Netmails;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use App\Classes\FTN\Message;
use App\Notifications\Netmails;
use App\Models\Netmail;
use App\Models\{Netmail,System};
use App\Traits\{MessagePath,PageTemplate};
class NetmailHubNoUser extends Netmails
class Reject extends Netmails
{
use MessagePath,PageTemplate;
private const LOGKEY = 'NNP';
private Netmail $mo;
private Message $mo;
/**
* Reply to a netmail ping request.
*
* @param Netmail $mo
* @param Message $mo
*/
public function __construct(Netmail $mo)
public function __construct(Message $mo)
{
parent::__construct();
@ -41,9 +42,9 @@ class NetmailHubNoUser extends Netmails
$o = $this->setupNetmail($notifiable);
$ao = $notifiable->routeNotificationFor(static::via);
Log::info(sprintf('%s:+ Creating HUB NO USER netmail to [%s]',self::LOGKEY,$ao->ftn));
Log::info(sprintf('%s:+ Creating reject netmail to [%s]',self::LOGKEY,$ao->ftn));
$o->to = $this->mo->from;
$o->to = $this->mo->user_from;
$o->replyid = $this->mo->msgid;
$o->subject = 'Message Undeliverable - '.$this->mo->msgid;
@ -53,7 +54,7 @@ class NetmailHubNoUser extends Netmails
$msg->addText(
sprintf("Your netmail with ID [%s] to [%s] here was received here on [%s] and it looks like you sent it on [%s].\r\r",
$this->mo->msgid,
$this->mo->to,
$this->mo->user_to,
Carbon::now()->utc()->toDateTimeString(),
$this->mo->date->utc()->toDateTimeString(),
)

View File

@ -5,7 +5,7 @@ namespace App\Notifications\Netmails;
use Illuminate\Support\Facades\Log;
use App\Notifications\Netmails;
use App\Models\Netmail;
use App\Models\{Netmail,System};
use App\Traits\PageTemplate;
class Test extends Netmails

View File

@ -1,12 +0,0 @@
<?php
namespace App\Pivots;
use Illuminate\Database\Eloquent\Relations\Pivot;
class ViaPivot extends Pivot
{
protected $casts = [
'datetime' => 'datetime',
];
}

View File

@ -6,21 +6,6 @@ use Illuminate\Auth\Access\HandlesAuthorization;
use App\Models\{System,User};
/**
* This handles updating system records
*
* Authorisation is defined by function_role_only, where
* - function = create,delete,update
* - role = admin,zc,rc,nc,hc,nn,pt
* - only = only that role can do (no hierarchy permission)
* ie:
* - admin - only site admin can do (user = admin)
* - zc - only ZC can perform (user has an address that is a ZC)
* - rc - only RC (or ZC) ...
* - hc - only HC (or ZC/RC) ...
* - nn - only NN (or ZC/RC/HC) ...
* - pt - only PT (or ZC/RC/HC/NN) ...
*/
class SystemPolicy
{
use HandlesAuthorization;
@ -64,7 +49,7 @@ class SystemPolicy
* @param System $system
* @return bool
*/
public function update_nn(User $user,System $system): bool
public function update(User $user, System $system): bool
{
// Site Admins can always edit
if ($user->isAdmin())
@ -74,8 +59,7 @@ class SystemPolicy
if (! $system->exists)
return FALSE;
// @todo Permit ZC, RC, NC, HUB user
return $system->users->contains($user) && $system->akas->count();
return $system->users->contains($user)
&& (($system->addresses->count() === 0) || ($system->addresses->where('validated',TRUE)->count()));
}
}

View File

@ -89,7 +89,7 @@ trait EncodeUTF8
return $this->attributes[$key];
}
return Arr::get($this->_encoded,$key) ? $this->attributes[$key] : parent::getAttribute($key);
return parent::getAttribute($key);
}
public function setAttribute($key,$value)

View File

@ -1,197 +0,0 @@
<?php
/**
* Common Attributes used by message packets (and thus their Models)
*/
namespace App\Traits;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Validator as ValidatorResult;
use App\Classes\FTN\Message;
use App\Models\Address;
trait MessageAttributes
{
use EncodeUTF8;
// Items we need to set when creating()
public Collection $set;
// Validation Errors
public ?ValidatorResult $errors = NULL;
private const cast_utf8 = [
'to',
'from',
'subject',
'msg',
'msg_src',
'origin',
'tearline',
'tagline',
];
public function __construct()
{
parent::__construct();
// Init
$this->set = collect();
}
/* ATTRIBUTES */
public function getContentAttribute(): string
{
if ($this->msg_src)
return $this->msg_src."\r";
// If we have a msg_src attribute, we'll use that
$result = $this->msg."\r\r";
if ($this->tagline)
$result .= sprintf("%s\r",$this->tagline);
if ($this->tearline)
$result .= sprintf("%s\r",$this->tearline);
if ($this->origin)
$result .= sprintf("%s",$this->origin);
return rtrim($result,"\r")."\r";
}
public function getDateAttribute(): Carbon
{
return $this->datetime->utcOffset($this->tzoffset);
}
public function getOriginAttribute(string $val=NULL): ?string
{
if ($this->exists && (! $val))
return $val;
// If $val is not set, then it may be an unsaved object
return sprintf(' * Origin: %s',
((! $this->exists) && $this->set->has('set_origin'))
? $this->set->get('set_origin')
: $val);
}
public function getTaglineAttribute(string $val=NULL): ?string
{
if ($this->exists && (! $val))
return $val;
// If $val is not set, then it may be an unsaved object
return sprintf('... %s',
((! $this->exists) && $this->set->has('set_tagline'))
? $this->set->get('set_tagline')
: $val);
}
public function getTearlineAttribute(string $val=NULL): ?string
{
if ($this->exists && (! $val))
return $val;
// If $val is not set, then it may be an unsaved object
return sprintf('--- %s',
((! $this->exists) && $this->set->has('set_tearline'))
? $this->set->get('set_tearline')
: $val);
}
/* METHODS */
/**
* Return an array of flag descriptions
*
* @return Collection
*
* http://ftsc.org/docs/fsc-0001.000
* AttributeWord bit meaning
* --- --------------------
* 0 + Private
* 1 + s Crash
* 2 Recd
* 3 Sent
* 4 + FileAttached
* 5 InTransit
* 6 Orphan
* 7 KillSent
* 8 Local
* 9 s HoldForPickup
* 10 + unused
* 11 s FileRequest
* 12 + s ReturnReceiptRequest
* 13 + s IsReturnReceipt
* 14 + s AuditRequest
* 15 s FileUpdateReq
*
* s - this bit is supported by SEAdog only
* + - this bit is not zeroed before packeting
*/
public function flags(): Collection
{
return collect([
'private' => $this->isFlagSet(Message::FLAG_PRIVATE),
'crash' => $this->isFlagSet(Message::FLAG_CRASH),
'recd' => $this->isFlagSet(Message::FLAG_RECD),
'sent' => $this->isFlagSet(Message::FLAG_SENT),
'fileattach' => $this->isFlagSet(Message::FLAG_FILEATTACH),
'intransit' => $this->isFlagSet(Message::FLAG_INTRANSIT),
'orphan' => $this->isFlagSet(Message::FLAG_ORPHAN),
'killsent' => $this->isFlagSet(Message::FLAG_KILLSENT),
'local' => $this->isFlagSet(Message::FLAG_LOCAL),
'hold' => $this->isFlagSet(Message::FLAG_HOLD),
'unused-10' => $this->isFlagSet(Message::FLAG_UNUSED_10),
'filereq' => $this->isFlagSet(Message::FLAG_FREQ),
'receipt-req' => $this->isFlagSet(Message::FLAG_RETRECEIPT),
'receipt' => $this->isFlagSet(Message::FLAG_ISRETRECEIPT),
'audit' => $this->isFlagSet(Message::FLAG_AUDITREQ),
'fileupdate' => $this->isFlagSet(Message::FLAG_FILEUPDATEREQ),
'pktpasswd' => $this->isFlagSet(Message::FLAG_PKTPASSWD),
])->filter();
}
public function isFlagSet($flag): bool
{
return ($this->flags & $flag);
}
/**
* Return this model as a packet
*/
public function packet(Address $ao): Message
{
Log::debug(sprintf('%s:+ Bundling [%s]',self::LOGKEY,$this->id),['type'=>get_class($this)]);
// For netmails, our tftn is the next hop
$this->tftn = $ao;
// @todo Dont bundle mail to nodes that have been disabled, or addresses that have been deleted
return Message::packMessage($this);
}
/**
* Return our path in order
*
* @param string $display
* @param int|NULL $start
* @return Collection
*/
public function pathorder(string $display='ftn2d',int $start=NULL): Collection
{
$result = collect();
if ($x=$this->path->firstWhere('pivot.parent_id',$start)) {
$result->push($x->$display);
$result->push($this->pathorder($display,$x->pivot->id));
}
return $result->flatten()->filter();
}
}

View File

@ -5,41 +5,43 @@
*/
namespace App\Traits;
use App\Models\{Echomail,Netmail};
use App\Classes\FTN\Message;
trait MessagePath
{
protected function message_path(Echomail|Netmail $mo): string
protected function message_path(Message $mo): string
{
$reply = "This is your original message:\r\r";
$reply .= "+--[ BEGIN MESSAGE ]----------------------------------+\r";
$reply .= sprintf("TO: %s\r",$mo->to);
$reply .= sprintf("TO: %s\r",$mo->user_to);
$reply .= sprintf("SUBJECT: %s\r",$mo->subject);
$reply .= str_replace("\r---","\r#--",$mo->msg)."\r";
$reply .= str_replace("\r---","\r#--",$mo->message)."\r";
$reply .= "+--[ CONTROL LINES ]----------------------------------+\r";
$reply .= sprintf("DATE: %s\r",$mo->date->format('Y-m-d H:i:s'));
$reply .= sprintf("MSGID: %s\r",$mo->msgid);
foreach ($mo->kludges as $k=>$v)
$reply .= sprintf("%s %s\r",$k,$v);
foreach ($mo->kludge as $k=>$v)
$reply .= sprintf("@%s: %s\r",strtoupper($k),$v);
$reply .= "+--[ PATH ]-------------------------------------------+\r";
if ($mo->path->count())
if ($mo instanceof Netmail) {
foreach ($mo->path as $o)
$reply .= sprintf("VIA: %s\r",$mo->via($o));
} else {
foreach ($mo->path as $o)
$reply .= sprintf("VIA: %s\r",$o->ftn);
}
if ($mo->isNetmail()) {
if ($mo->via->count())
foreach ($mo->via as $via)
$reply .= sprintf("VIA: %s\r",$via);
else
$reply .= "No path information? This would be normal if this message came directly to the hub\r";
} else {
if ($mo->path->count())
foreach ($mo->path as $via)
$reply .= sprintf("VIA: %s\r",$via);
else
$reply .= "No path information? This would be normal if this message came directly to the hub\r";
}
$reply .= "+--[ END MESSAGE ]------------------------------------+\r\r";
return $reply;

View File

@ -5,7 +5,7 @@
"keywords": ["framework","laravel"],
"license": "MIT",
"require": {
"php": "^8.2|8.3",
"php": "^8.1|8.2|8.3",
"ext-bz2": "*",
"ext-pcntl": "*",
"ext-sockets": "*",

View File

@ -21,7 +21,7 @@ return new class extends Migration
*/
public function down(): void
{
Schema::table('netmails', function (Blueprint $table) {
Schema::table('netmail', function (Blueprint $table) {
$table->dropColumn('origin');
});
}

View File

@ -1,34 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('netmails', function (Blueprint $table) {
$table->json('kludges')->nullable();
});
Schema::table('echomails', function (Blueprint $table) {
$table->tinyInteger('cost')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('netmails', function (Blueprint $table) {
$table->dropColumn('kludges');
});
Schema::table('echomails', function (Blueprint $table) {
$table->dropColumn('cost');
});
}
};

View File

@ -1,8 +1,3 @@
@php
use App\Models\Netmail;
use App\Classes\FTN\Message;
@endphp
@extends('layouts.app')
@section('htmlheader_title')
Verify Packet
@ -87,7 +82,7 @@
<div class="col-12">
<h4 class="accordion-header">
<span class="accordion-button" id="pktmsg" data-bs-toggle="collapse" data-bs-target="#collapse_msg_{{ $loop->parent->parent->index }}_{{ $loop->index }}" aria-expanded="false">
@if($msg instanceof Netmail) Netmail @else Echomail&nbsp;<strong>{{ $msg->echoarea->name }}</strong> @endif : {{ $msg->msgid }}
@if($msg->isNetmail()) Netmail @else Echomail&nbsp;<strong>{{ $msg->echoarea }}</strong> @endif : {{ $msg->msgid }}
</span>
</h4>
@ -103,40 +98,48 @@
<div class="row pb-2">
<div class="col-4">
DATE: <strong class="highlight">{{ $msg->datetime }}</strong>
DATE: <strong class="highlight">{{ $msg->date }}</strong>
</div>
<div class="col-4">
FLAGS: <strong class="highlight">{{ $msg->flags()->keys()->join(', ') }}</strong>
FLAGS: <strong class="highlight">{{ $msg->flags()->filter()->keys()->join(', ') }}</strong>
</div>
</div>
<div class="row pb-2">
<div class="col-4">
FROM: <strong class="highlight">{!! Message::tr($msg->from) !!}</strong> (<strong class="highlight">{{ $msg->fftn->ftn }}</strong>)
FROM: <strong class="highlight">{!! \App\Classes\FTN\Message::tr($msg->user_from) !!}</strong> (<strong class="highlight">{{ $msg->fftn }}</strong>)
</div>
<div class="col-4">
TO: <strong class="highlight">{!! Message::tr($msg->to) !!}</strong>@if($msg instanceof Netmail) (<strong class="highlight">{{ $msg->tftn->ftn }}</strong>) @endif
TO: <strong class="highlight">{!! \App\Classes\FTN\Message::tr($msg->user_to) !!}</strong>@if($msg->isNetmail()) (<strong class="highlight">{{ $msg->tftn }}</strong>) @endif
</div>
</div>
<div class="row pb-2">
<div class="col-8">
SUBJECT: <strong class="highlight">{!! Message::tr($msg->subject) !!}</strong>
SUBJECT: <strong class="highlight">{!! \App\Classes\FTN\Message::tr($msg->subject) !!}</strong>
</div>
</div>
<div class="row pb-2">
<div class="col-8">
<div class="pad pb-0">
<pre class="highlight">{!! Message::tr($msg->msg_src) !!}</pre>
<pre class="highlight">{!! \App\Classes\FTN\Message::tr($msg->message).sprintf("\r * Origin: %s",$msg->origin) !!}</pre>
</div>
</div>
</div>
@if($msg instanceof Netmail)
@if($msg->tagline)
<div class="row pb-2">
<div class="col-8">
VIA: <br><strong class="highlight">{!! $msg->path->join('</strong> -> <strong class="highlight">') !!}</strong>
TAGLINE: <br><strong class="highlight">{{ $msg->tagline }}</strong>
</div>
</div>
@endif
@if($msg->isNetmail())
<div class="row pb-2">
<div class="col-8">
VIA: <br><strong class="highlight">{!! $msg->via->join('</strong><br><strong class="highlight">') !!}</strong>
</div>
</div>
@else
@ -156,7 +159,7 @@
<div class="row pb-2">
<div class="col-8">
<strong>KLUDGES:</strong> <br>
@foreach ($msg->kludges->sort(function($v,$k) { return $k; })->reverse() as $k => $v)
@foreach ($msg->kludge->sort(function($v,$k) { return $k; })->reverse() as $k => $v)
<strong class="highlight">{{ $k }}</strong> {{ $v }}<br>
@endforeach
</div>

View File

@ -217,9 +217,7 @@
@if(($x=$oo->systems->where('pivot.default',TRUE))->count() && ($x->first()->id !== $o->id))
<i class="bi bi-dash-square"></i>
@else
@can('admin')
<span class="default" itemid="{{ $oo->id }}"><i class="bi bi-{{ $x->count() ? 'check-square' : 'square' }}"></i></span>
@endcan
@endif
</td>
<td>{{ $oo->pivot->sespass }}</td>
@ -241,9 +239,7 @@
<p>No session details exist</p>
@endif
@can('update_nn',$o)
@include('system.widget.form-session')
@endcan
</div>
</div>
</div>
@ -607,7 +603,6 @@
@section('page-scripts')
<script type="text/javascript">
$(document).ready(function () {
@can('admin')
$('.default').click(function () {
var item = this;
icon = $(item).find('i');
@ -639,7 +634,6 @@
cache: false
})
});
@endcan
$('data.validated').on('click',function(item) {
that = $(this);
var values = item.delegateTarget.value.split(':');

View File

@ -42,7 +42,7 @@
<tbody>
@foreach (\App\Models\System::active()->with(['addresses.zone.domain'])->get() as $oo)
<tr>
<td><a href="{{ url('system/addedit',[$oo->id]) }}" @cannot('update_nn',$oo)class="disabled" @endcannot>{{ $oo->id }}</a></td>
<td><a href="{{ url('system/addedit',[$oo->id]) }}" @cannot('update',$oo)class="disabled" @endcannot>{{ $oo->id }}</a></td>
<td>{{ $oo->name }} @if(! $oo->active)<span class="float-end"><small>[i]</small></span>@endif</td>
<td>{{ $oo->sysop }}</td>
<td>{{ $oo->location }}</td>

View File

@ -36,7 +36,7 @@
@endif
</span>
@can('update_nn',$o)
@can('update',$o)
<div class="col-2">
<button type="submit" name="submit" class="btn btn-success float-end">Add</button>
</div>

View File

@ -36,7 +36,7 @@
@endif
</span>
@can('update_nn',$o)
@can('update',$o)
<div class="col-2">
<button type="submit" name="submit" class="btn btn-success float-end">Add</button>
</div>

View File

@ -1,10 +1,6 @@
<!-- $o = System::class -->
@php
use App\Models\Zone;
@endphp
@if(($x=Zone::active()
->whereIn('id',$o->addresses->pluck('zone.id'))
@if(($x=\App\Models\Zone::active()
->whereIn('id',$o->zones->pluck('id'))
->whereNotIn('id',$o->sessions->pluck('id'))
->with(['domain'])
->get())->count())
@ -46,7 +42,7 @@ use App\Models\Zone;
<label for="sespass" class="form-label">Session Password</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-lock"></i></span>
<input type="text" style="width: 35%;" class="form-control @error('sespass') is-invalid @enderror" id="sespass" placeholder="Session" name="sespass" value="{{ old('sespass') }}" required>
<input type="text" style="width: 35%;" class="form-control @error('sespass') is-invalid @enderror" id="sespass" placeholder="Session" name="sespass" value="{{ old('sespass') }}" @cannot('update',$o)disabled @endcannot required>
<span class="invalid-feedback" role="alert">
@error('sespass')
{{ $message }}
@ -62,7 +58,7 @@ use App\Models\Zone;
<label for="pktpass" class="form-label">Packet Password</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-box"></i></span>
<input type="text" style="width: 35%;" class="form-control @error('pktpass') is-invalid @enderror" id="pktpass" placeholder="Packet" name="pktpass" value="{{ old('pktpass') }}" required>
<input type="text" style="width: 35%;" class="form-control @error('pktpass') is-invalid @enderror" id="pktpass" placeholder="Packet" name="pktpass" value="{{ old('pktpass') }}" @cannot('update',$o)disabled @endcannot required>
<span class="invalid-feedback" role="alert">
@error('pktpass')
{{ $message }}
@ -80,7 +76,7 @@ use App\Models\Zone;
<label for="fixpass" class="form-label">Areafix Password</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-card-text"></i></span>
<input type="text" style="width: 35%;" class="form-control @error('fixpass') is-invalid @enderror" id="fixpass" placeholder="Areafix" name="fixpass" value="{{ old('fixpass') }}" required>
<input type="text" style="width: 35%;" class="form-control @error('fixpass') is-invalid @enderror" id="fixpass" placeholder="Areafix" name="fixpass" value="{{ old('fixpass') }}" @cannot('update',$o)disabled @endcannot required>
<span class="invalid-feedback" role="alert">
@error('fixpass')
{{ $message }}
@ -96,7 +92,7 @@ use App\Models\Zone;
<label for="ticpass" class="form-label">TIC Password</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-archive"></i></span>
<input type="text" style="width: 35%;" class="form-control @error('ticpass') is-invalid @enderror" id="ticpass" placeholder="TIC" name="ticpass" value="{{ old('ticpass') }}" required>
<input type="text" style="width: 35%;" class="form-control @error('ticpass') is-invalid @enderror" id="ticpass" placeholder="TIC" name="ticpass" value="{{ old('ticpass') }}" @cannot('update',$o)disabled @endcannot required>
<span class="invalid-feedback" role="alert">
@error('ticpass')
{{ $message }}
@ -122,9 +118,11 @@ use App\Models\Zone;
@endif
</span>
@can('update',$o)
<div class="col-2">
<button type="submit" name="submit" class="btn btn-success float-end">Add</button>
</div>
@endcan
</div>
</div>
</div>

View File

@ -1,11 +1,10 @@
@php
use App\Classes\FTN\Message;
use App\Models\{Echomail,Netmail};
@endphp
<div class="row">
<div class="col-4">
TO: <strong class="highlight">{!! Message::tr($msg->to) !!}</strong> @if ($msg instanceof Netmail)(<strong class="highlight">{{ $msg->tftn->ftn }}</strong>)@endif
TO: <strong class="highlight">{!! Message::tr($msg->to) !!}</strong> @if ($msg instanceof \App\Models\Netmail)(<strong class="highlight">{{ $msg->tftn->ftn }}</strong>)@endif
</div>
<div class="col-4">
DATE: <strong class="highlight">{{ $msg->datetime->format('Y-m-d H:i:s') }}</strong>
@ -17,30 +16,19 @@ use App\Models\{Echomail,Netmail};
FROM: <strong class="highlight">{!! Message::tr($msg->from) !!}</strong> (<strong class="highlight">{{ $msg->fftn->ftn }}</strong>)
</div>
<div class="col-4">
MSGID: <strong class="highlight">{{ $msg->msgid }}</strong>@if($x=Echomail::where('replyid',$msg->msgid)->count()) (<strong class="highlight">{{$x}}</strong> replies)@endif @if($msg->replyid)<br>REPLY: <strong class="highlight">{{ $msg->replyid }}</strong>@endif
MSGID: <strong class="highlight">{{ $msg->msgid }}</strong>@if($x=\App\Models\Echomail::where('replyid',$msg->msgid)->count()) (<strong class="highlight">{{$x}}</strong> replies)@endif @if($msg->replyid)<br>REPLY: <strong class="highlight">{{ $msg->replyid }}</strong>@endif
</div>
</div>
@if($msg->flags()->count())
<div class="row pt-1">
<div class="offset-4 col-8">
FLAGS: <strong class="highlight">{!! $msg->flags()->keys()->map(fn($item)=>strtoupper($item))->join('</strong>, <strong class="highlight">') !!}</strong>
</div>
</div>
@endif
@if ($msg instanceof Echomail)
<div class="row pt-1 pb-2">
<div class="offset-4 col-4">
ECHOAREA: <strong class="highlight">{{ $msg->echoarea->name }}</strong> (<strong class="highlight">{{ $msg->echoarea->domain->name }}</strong>)
</div>
</div>
@endif
<div class="row pt-1 pb-2">
<div class="col-8">
<div class="col-4">
SUBJECT: <strong class="highlight">{!! Message::tr($msg->subject) !!}</strong>
</div>
@if ($msg instanceof \App\Models\Echomail)
<div class="col-4">
ECHOAREA: <strong class="highlight">{{ $msg->echoarea->name }}</strong> (<strong class="highlight">{{ $msg->echoarea->domain->name }}</strong>)
</div>
@endif
</div>
<div class="row pb-2">
@ -51,33 +39,32 @@ use App\Models\{Echomail,Netmail};
</div>
</div>
<div class="row pb-2">
<div class="col-8">
KLUDGES: <br>
@foreach($msg->kludges as $k=>$v)
<strong class="highlight">{{ $k }}</strong> {{ $v }}<br>
@endforeach
</div>
</div>
@if ($msg instanceof Echomail)
@if ($msg instanceof \App\Models\Echomail)
<div class="row pb-2">
<div class="col-8">
SEENBY: <br><strong class="highlight">{!! $msg->seenby->pluck('ftn2d')->join('</strong>, <strong class="highlight">') !!}</strong>
</div>
@if ($msg->rogue_seenby->count())
<br><small>[<strong>NOTE</strong>: Some seen-by values couldnt be identified - ({{ $msg->rogue_seenby->transform(fn($item)=>str_replace('0:','',$item))->join(',') }})]</small>
<br><small>[<strong>NOTE</strong>: Some seen-by values couldnt be identified - ({{ $msg->rogue_seenby->join(',') }})]</small>
@endif
</div>
@endif
@if ($msg->flags & Message::FLAG_LOCAL)
<div class="row pb-2">
<div class="col-8">
<strong class="highlight">Local message</strong>
</div>
</div>
@elseif ((! $msg->flags) || ($msg->flags & (Message::FLAG_INTRANSIT|Message::FLAG_RECD)))
<!-- @todo for the nodes we export to, highlight those that we have actually sent it, vs those that havent received it yet -->
<div class="row pb-2">
<div class="col-8">
PATH: <br><strong class="highlight">{!! $msg->pathorder()->join('</strong> -> <strong class="highlight">') !!}</strong>
@if(($msg instanceof Echomail) && $msg->rogue_path->count())
@if (($msg instanceof \App\Models\Echomail) && $msg->rogue_path->count())
<br><small>[<strong>NOTE</strong>: Some path values couldnt be identified - ({{ $msg->rogue_path->join(',') }})]</small>
@endif
</div>
@ -85,27 +72,16 @@ use App\Models\{Echomail,Netmail};
<div class="row pb-2">
<div class="col-8">
@if($msg instanceof Netmail)
RECEIVED:<br>
@foreach ($msg->path as $path)
@if ($msg instanceof \App\Models\Netmail)
@foreach ($msg->received as $path)
<strong class="highlight">{{ $path->pivot->recv_pkt }}</strong> from <strong class="highlight">{{ $path->ftn }}</strong> {{ $msg->created_at }}
@endforeach
@elseif ($msg instanceof Echomail)
RECEIVED:<br>
@elseif ($msg instanceof \App\Models\Echomail)
<strong class="highlight">{{ ($x=$msg->path->sortBy('pivot.parent_id')->last())->pivot->recv_pkt }}</strong> from <strong class="highlight">{{ $x->ftn }}</strong> {{ $x->pivot->recv_at }}
@endif
</div>
</div>
@if($msg instanceof Netmail)
<div class="col-8">
SENT:
@if($msg->sent_pkt && $msg->sent_at)
<strong class="highlight">{{ $msg->sent_at }}</strong> (<strong class="highlight">{{ $msg->sent_pkt }}.pkt</strong>)
@else
<strong class="highlight">NOT SENT</strong>
@endif
</div>
@endif
@section('page-scripts')
@ -113,16 +89,18 @@ use App\Models\{Echomail,Netmail};
<script type="text/javascript">
$(document).ready(function() {
var msg = new Uint8Array({!! json_encode(array_values(unpack('C*',str_replace("\r","\n",$msg->content)))) !!});
var msg = new Uint8Array({!! json_encode(array_values(unpack('C*',str_replace("\r","\n",$msg->msg)))) !!});
retina = window.devicePixelRatio > 1;
AnsiLove.renderBytes(
msg,
function (canvas, sauce) {
console.log(canvas);
document.getElementById("canvas").appendChild(canvas);
},
{'font': '80x25', 'bits': 8, 'icecolors': 0, 'columns': 80}
);
});
</script>
@append