Complete rework of packet parsing and packet generation
This commit is contained in:
parent
1650d07d5c
commit
29710c37c2
@ -11,7 +11,7 @@ abstract class FTN
|
||||
public function __get(string $key)
|
||||
{
|
||||
switch ($key) {
|
||||
case 'fftn':
|
||||
case 'fftn_t':
|
||||
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':
|
||||
case 'tftn_t':
|
||||
return sprintf('%d:%d/%d.%d',
|
||||
$this->tz,
|
||||
$this->tn,
|
||||
@ -27,10 +27,10 @@ abstract class FTN
|
||||
$this->tp,
|
||||
).($this->zone ? sprintf('@%s',$this->zone->domain->name) : '');
|
||||
|
||||
case 'fftn_o':
|
||||
return Address::findFTN($this->fftn);
|
||||
case 'tftn_o':
|
||||
return Address::findFTN($this->tftn);
|
||||
case 'fftn':
|
||||
return Address::findFTN($this->fftn_t);
|
||||
case 'tftn':
|
||||
return Address::findFTN($this->tftn_t);
|
||||
|
||||
default:
|
||||
throw new \Exception('Unknown key: '.$key);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -10,23 +10,30 @@ use Illuminate\Support\Facades\Notification;
|
||||
use Symfony\Component\HttpFoundation\File\File;
|
||||
|
||||
use App\Classes\FTN as FTNBase;
|
||||
use App\Models\{Address,Domain,Software,System,Zone};
|
||||
use App\Exceptions\InvalidPacketException;
|
||||
use App\Models\{Address,Domain,Echomail,Netmail,Software,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 Message::class.
|
||||
* Thus this object is iterable as an array of Echomail::class or Netmail::class.
|
||||
*/
|
||||
class Packet extends FTNBase implements \Iterator, \Countable
|
||||
abstract 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,
|
||||
@ -35,115 +42,15 @@ class Packet extends FTNBase implements \Iterator, \Countable
|
||||
];
|
||||
|
||||
protected array $header; // Packet Header
|
||||
protected ?string $name; // Packet name
|
||||
protected ?string $name = NULL; // Packet name
|
||||
|
||||
public File $file; // Packet filename
|
||||
public Collection $messages; // Messages in the Packet
|
||||
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 $errors; // Messages that fail validation
|
||||
protected int $index; // Our array index
|
||||
|
||||
/**
|
||||
* @param string|null $header
|
||||
* @throws \Exception
|
||||
*/
|
||||
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 */
|
||||
|
||||
/**
|
||||
@ -164,10 +71,7 @@ class Packet extends FTNBase implements \Iterator, \Countable
|
||||
* @param string $header
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_type(string $header): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
abstract public static function is_type(string $header): bool;
|
||||
|
||||
/**
|
||||
* Process a packet file
|
||||
@ -224,7 +128,9 @@ class Packet extends FTNBase implements \Iterator, \Countable
|
||||
|
||||
// No message attached
|
||||
} else
|
||||
throw new InvalidPacketException('Not a valid packet, not EOP or SOM'.bin2hex($x));
|
||||
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));
|
||||
|
||||
// Work out the packet zone
|
||||
if ($o->fz && ($o->fd || $domain)) {
|
||||
@ -233,16 +139,10 @@ 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 (! $o->zone) {
|
||||
if (empty($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)
|
||||
@ -252,7 +152,7 @@ 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)))) {
|
||||
@ -269,7 +169,7 @@ class Packet extends FTNBase implements \Iterator, \Countable
|
||||
$msgbuf = substr($msgbuf,$end+3);
|
||||
continue;
|
||||
|
||||
// If we have more to read
|
||||
// If we have more to read
|
||||
} elseif ($read_ptr < $size) {
|
||||
continue;
|
||||
}
|
||||
@ -285,18 +185,103 @@ class Packet extends FTNBase implements \Iterator, \Countable
|
||||
}
|
||||
|
||||
/**
|
||||
* Location of the version
|
||||
*
|
||||
* @return int
|
||||
* @param string|null $header
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function version_offset(): int
|
||||
public function __construct(string $header=NULL)
|
||||
{
|
||||
return Arr::get(collect(static::HEADER)->get('type'),0);
|
||||
$this->messages = collect();
|
||||
$this->errors = collect();
|
||||
|
||||
if ($header)
|
||||
$this->header = unpack(self::unpackheader(static::HEADER),$header);
|
||||
}
|
||||
|
||||
public static function version_offset_len(): int
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
return Arr::get(collect(static::HEADER)->get('type'),2);
|
||||
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());
|
||||
|
||||
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)
|
||||
$return .= self::PACKED_MSG_LEAD.$o->packet($this->tftn_p);
|
||||
|
||||
$return .= "\00\00";
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/* INTERFACE */
|
||||
@ -309,7 +294,7 @@ class Packet extends FTNBase implements \Iterator, \Countable
|
||||
return $this->messages->count();
|
||||
}
|
||||
|
||||
public function current(): Message
|
||||
public function current(): Echomail|Netmail
|
||||
{
|
||||
return $this->messages->get($this->index);
|
||||
}
|
||||
@ -342,6 +327,7 @@ 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
|
||||
{
|
||||
@ -375,12 +361,38 @@ 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
|
||||
*
|
||||
* @param Collection $msgs
|
||||
* @return string
|
||||
* @throws InvalidPacketException
|
||||
*/
|
||||
public function generate(Collection $msgs): string
|
||||
{
|
||||
$this->messages = $msgs;
|
||||
|
||||
if (empty($this->tftn_p) || empty($this->fftn_p))
|
||||
throw new InvalidPacketException('Cannot generate a packet without a destination address');
|
||||
|
||||
return (string)$this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a message in a mail packet
|
||||
*
|
||||
@ -393,7 +405,8 @@ class Packet extends FTNBase implements \Iterator, \Countable
|
||||
|
||||
$msg = Message::parseMessage($message,$this->zone);
|
||||
|
||||
// If the message from domain is different to the packet address domain, we'll skip this message
|
||||
// @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 is invalid, we'll ignore it
|
||||
if ($msg->errors) {
|
||||
@ -401,14 +414,17 @@ 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 is from an invalid zone [%s], packet is from [%s] - ignoring it',self::LOGKEY,$msg->fftn,$msg->zone->domain->name));
|
||||
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));
|
||||
|
||||
if (! $msg->rescanned->count())
|
||||
Notification::route('netmail',$this->fftn_o)->notify(new EchomailBadAddress($msg));
|
||||
Notification::route('netmail',$this->fftn)->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 {
|
||||
@ -470,10 +486,13 @@ 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 ($msg->errors->messages()->has('user_from') || $msg->errors->messages()->has('user_to')) {
|
||||
// If the from/to user is missing
|
||||
if ($msg->errors->messages()->has('from') || $msg->errors->messages()->has('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;
|
||||
}
|
||||
}
|
||||
@ -481,8 +500,10 @@ class Packet extends FTNBase implements \Iterator, \Countable
|
||||
$this->messages->push($msg);
|
||||
}
|
||||
|
||||
/** @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);
|
||||
}
|
||||
}
|
@ -59,37 +59,40 @@ final class FSC48 extends Packet
|
||||
|
||||
/**
|
||||
* Create our message packet header
|
||||
* // @todo add the ability to overwrite the password
|
||||
*/
|
||||
protected function header(): string
|
||||
{
|
||||
$oldest = $this->messages->sortBy(function($item) { return $item->datetime; })->last();
|
||||
|
||||
try {
|
||||
return pack(collect(self::HEADER)->pluck(1)->join(''),
|
||||
$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->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->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
|
||||
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
|
||||
$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
|
||||
0, // Baud
|
||||
2, // Packet Version (should be 2)
|
||||
$this->fftn_p->point_id ? 0xffff : $this->fftn_p->node_id, // Orig Net (0xFFFF when OrigPoint != 0)
|
||||
$this->tftn_p->host_id, // Dest Net
|
||||
(Setup::PRODUCT_ID & 0xff), // Product Code Lo
|
||||
Setup::PRODUCT_VERSION_MAJ, // Product Version Major
|
||||
$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->node_id : 0x00, // Aux Net
|
||||
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
|
||||
strtoupper(hexstr(Setup::PRODUCT_ID)), // ProdData
|
||||
);
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Classes\FTN;
|
||||
|
||||
use App\Models\Echoarea;
|
||||
use App\Models\{Echoarea,Echomail,Netmail};
|
||||
|
||||
/**
|
||||
* Abstract class to hold the common functions for automatic responding to echomail/netmail messages
|
||||
@ -19,8 +19,8 @@ abstract class Process
|
||||
/**
|
||||
* Return TRUE if the process class handled the message.
|
||||
*
|
||||
* @param Message $msg
|
||||
* @param Echomail|Netmail $mo
|
||||
* @return bool
|
||||
*/
|
||||
abstract public static function handle(Message $msg): bool;
|
||||
abstract public static function handle(Echomail|Netmail $mo): bool;
|
||||
}
|
@ -5,7 +5,8 @@ namespace App\Classes\FTN\Process\Echomail;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
use App\Classes\FTN\{Message,Process};
|
||||
use App\Classes\FTN\Process;
|
||||
use App\Models\{Echomail,Netmail};
|
||||
use App\Notifications\Echomails\Test as TestNotification;
|
||||
|
||||
/**
|
||||
@ -19,16 +20,16 @@ final class Test extends Process
|
||||
|
||||
private const testing = ['test','testing'];
|
||||
|
||||
public static function handle(Message $msg): bool
|
||||
public static function handle(Echomail|Netmail $mo): bool
|
||||
{
|
||||
if (! self::canProcess($msg->echoarea)
|
||||
|| (strtolower($msg->user_to) !== 'all')
|
||||
|| (! in_array(strtolower($msg->subject),self::testing)))
|
||||
if (! self::canProcess($mo->echoarea)
|
||||
|| (strtolower($mo->to) !== 'all')
|
||||
|| (! in_array(strtolower($mo->subject),self::testing)))
|
||||
return FALSE;
|
||||
|
||||
Log::info(sprintf('%s:- Processing TEST message from (%s) [%s] in [%s]',self::LOGKEY,$msg->user_from,$msg->fftn,$msg->echoarea));
|
||||
|
||||
Notification::route('echomail',$msg->echoarea)->notify(new TestNotification($msg));
|
||||
Notification::route('echomail',$mo->echoarea)->notify(new TestNotification($msg));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ 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
|
||||
|
@ -50,8 +50,8 @@ class PacketAddress extends Command
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$o = $o->where('id',$this->argument('dbid'))->get();
|
||||
echo hex_dump($ao->system->packet($ao)->generate($o->where('id',$this->argument('dbid'))->get()));
|
||||
|
||||
dd(hex_dump($ao->getPacket($o)));
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ use Illuminate\Support\Facades\Storage;
|
||||
|
||||
use App\Classes\File;
|
||||
use App\Classes\FTN\Packet;
|
||||
use App\Models\Address;
|
||||
use App\Models\{Address,Echomail};
|
||||
|
||||
class PacketInfo extends Command
|
||||
{
|
||||
@ -31,7 +31,7 @@ class PacketInfo extends Command
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \App\Classes\FTN\InvalidPacketException
|
||||
* @throws \App\Exceptions\InvalidPacketException
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
@ -55,27 +55,38 @@ 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,$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('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('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) {
|
||||
try {
|
||||
$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));
|
||||
$this->warn(sprintf(' - Area : %s',$msg->echoarea));
|
||||
echo "\n";
|
||||
|
||||
if ($msg->errors)
|
||||
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(' - Subject: %s',$msg->subject));
|
||||
if ($msg instanceof Echomail)
|
||||
$this->warn(sprintf(' - Area : %s',$msg->echoarea->name));
|
||||
|
||||
if ($msg->errors) {
|
||||
echo "\n";
|
||||
$this->error("Errors:");
|
||||
foreach ($msg->errors->errors()->all() as $error)
|
||||
$this->line(' - '.$error);
|
||||
$this->error(' - '.$error);
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$this->error('! ERROR: '.$e->getMessage());
|
||||
|
@ -2,13 +2,12 @@
|
||||
|
||||
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\MessageProcess as Job;
|
||||
use App\Jobs\PacketProcess as Job;
|
||||
use App\Models\Address;
|
||||
|
||||
class PacketProcess extends Command
|
||||
@ -35,7 +34,7 @@ class PacketProcess extends Command
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
* @throws \App\Classes\FTN\InvalidPacketException
|
||||
* @throws \App\Exceptions\InvalidPacketException
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
@ -56,17 +55,8 @@ class PacketProcess extends Command
|
||||
exit(1);
|
||||
}
|
||||
|
||||
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()));
|
||||
$x = Job::dispatchSync($rel_name,$a->zone->domain,$this->option('dontqueue'));
|
||||
|
||||
// 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'));
|
||||
}
|
||||
}
|
||||
dd(['job completed'=>$x]);
|
||||
}
|
||||
}
|
@ -27,7 +27,7 @@ class PacketSystem extends Command
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \App\Classes\FTN\InvalidPacketException
|
||||
* @throws \App\Exceptions\InvalidPacketException
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\FTN;
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class NoReadSecurityException extends Exception
|
||||
{
|
||||
}
|
@ -13,7 +13,7 @@ use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
use App\Classes\FTN\Message;
|
||||
use App\Models\{Address,Echoarea,Echomail,Netmail,User};
|
||||
use App\Models\{Echoarea,Echomail,Netmail,User};
|
||||
use App\Notifications\Netmails\{EchoareaNotExist,EchoareaNotSubscribed,EchoareaNoWrite,NetmailForward,Reject};
|
||||
use App\Traits\ParseAddresses;
|
||||
|
||||
@ -23,20 +23,19 @@ class MessageProcess implements ShouldQueue
|
||||
|
||||
use Dispatchable,InteractsWithQueue,Queueable,SerializesModels,ParseAddresses;
|
||||
|
||||
private Address $sender;
|
||||
private Message $msg;
|
||||
private Address $pktsrc;
|
||||
private Carbon $recvtime;
|
||||
private Echomail|Netmail|string $mo;
|
||||
private bool $skipbot;
|
||||
private string $packet;
|
||||
|
||||
public function __construct(Message $msg,string $packet,Address $sender,Address $pktsrc,Carbon $recvtime,bool $skipbot=FALSE)
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->msg = $msg;
|
||||
$this->packet = $packet;
|
||||
$this->sender = $sender;
|
||||
$this->pktsrc = $pktsrc;
|
||||
$this->recvtime = $recvtime;
|
||||
// @todo We need to serialize this model here, because laravel has an error unserializing it (Model Not Found)
|
||||
$this->mo = serialize($mo);
|
||||
$this->skipbot = $skipbot;
|
||||
}
|
||||
|
||||
@ -44,7 +43,7 @@ class MessageProcess implements ShouldQueue
|
||||
{
|
||||
switch ($key) {
|
||||
case 'subject':
|
||||
return sprintf('%s-%s-%s',$this->packet,$this->sender->ftn,$this->msg->msgid);
|
||||
return sprintf('%s-%s-%s',$this->pktname,$this->mo->set->get('set_sender')->ftn,$this->mo->msgid);
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
@ -52,21 +51,25 @@ class MessageProcess implements ShouldQueue
|
||||
}
|
||||
|
||||
/**
|
||||
* When calling MessageProcess - we assume that the packet is from a valid source, and
|
||||
* the destination (netmail/echomail) is also valid
|
||||
* 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
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->mo = unserialize($this->mo);
|
||||
|
||||
// Load our details
|
||||
$ftns = our_address();
|
||||
|
||||
// If we are a netmail
|
||||
if ($this->msg->isNetmail()) {
|
||||
if ($this->mo instanceof Netmail) {
|
||||
// @todo generate exception when netmail to system that doesnt exist (node/point) and its this host's responsibility
|
||||
Log::info(sprintf('%s:- Processing Netmail [%s] to (%s) [%s] from (%s) [%s].',
|
||||
self::LOGKEY,
|
||||
$this->msg->msgid,
|
||||
$this->msg->user_to,$this->msg->tftn,
|
||||
$this->msg->user_from,$this->msg->fftn,
|
||||
$this->mo->msgid,
|
||||
$this->mo->to,$this->mo->tftn->ftn,
|
||||
$this->mo->from,$this->mo->fftn->ftn,
|
||||
));
|
||||
|
||||
// @todo Enable checks to reject old messages
|
||||
@ -74,89 +77,51 @@ class MessageProcess implements ShouldQueue
|
||||
// Check for duplicate messages
|
||||
|
||||
// FTS-0009.001
|
||||
if ($this->msg->msgid) {
|
||||
$o = Netmail::where('msgid',$this->msg->msgid)
|
||||
->where('fftn_id',($x=$this->msg->fboss_o) ? $x->id : NULL)
|
||||
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)
|
||||
->where('datetime','>',Carbon::now()->subYears(3))
|
||||
->single();
|
||||
|
||||
Log::debug(sprintf('%s:- Checking for duplicate from host id [%d].',self::LOGKEY,($x=$this->msg->fboss_o) ? $x->id : NULL));
|
||||
|
||||
if ($o) {
|
||||
Log::alert(sprintf('%s:! Duplicate netmail [%s] in [%s] from (%s) [%s] to (%s) - ignorning.',
|
||||
Log::alert(sprintf('%s:! Duplicate netmail #%d [%s] from (%s) [%s] to (%s) - ignoring.',
|
||||
self::LOGKEY,
|
||||
$this->msg->msgid,
|
||||
$this->msg->echoarea,
|
||||
$this->msg->user_from,$this->msg->fftn,
|
||||
$this->msg->user_to,
|
||||
$o->id,
|
||||
$this->mo->msgid,
|
||||
$this->mo->from,$this->mo->fftn->ftn,
|
||||
$this->mo->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;
|
||||
|
||||
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
|
||||
$o->flags &= ~(Message::FLAG_LOCAL|Message::FLAG_INTRANSIT);
|
||||
$this->mo->flags &= ~(Message::FLAG_LOCAL|Message::FLAG_INTRANSIT);
|
||||
|
||||
// Determine if the message is to this system, or in transit
|
||||
if ($ftns->search(function($item) { return $this->msg->tftn === $item->ftn; }) !== FALSE) {
|
||||
// @todo Check if it is a duplicate message
|
||||
// @todo Check if the message is from a system we know about
|
||||
|
||||
if ($ftns->contains($this->mo->tftn)) {
|
||||
$processed = FALSE;
|
||||
|
||||
// If the message is to a bot, we'll process it
|
||||
if (! $this->skipbot)
|
||||
foreach (config('process.robots') as $class) {
|
||||
if ($processed=$class::handle($this->msg)) {
|
||||
$o->flags |= Message::FLAG_RECD;
|
||||
$o->save();
|
||||
if ($processed=$class::handle($this->mo)) {
|
||||
$this->mo->flags |= Message::FLAG_RECD;
|
||||
$this->mo->save();
|
||||
|
||||
Log::info(sprintf('%s:= Netmail [%s] from (%s:%s) - was processed by us internally [%d]',
|
||||
self::LOGKEY,
|
||||
$this->msg->msgid,
|
||||
$this->msg->user_from,
|
||||
$this->msg->fftn,
|
||||
$o->id,
|
||||
$this->mo->msgid,
|
||||
$this->mo->from,
|
||||
$this->mo->fftn->ftn,
|
||||
$this->mo->id,
|
||||
));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -165,38 +130,40 @@ 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->msg->user_to)))
|
||||
->orWhereRaw(sprintf("LOWER(alias)='%s'",strtolower($this->msg->user_to)));
|
||||
return $query->whereRaw(sprintf("LOWER(name)='%s'",strtolower($this->mo->to)))
|
||||
->orWhereRaw(sprintf("LOWER(alias)='%s'",strtolower($this->mo->to)));
|
||||
})
|
||||
->whereNotNull('system_id')
|
||||
->single();
|
||||
|
||||
if ($uo && ($ao=$uo->system->match($this->msg->tftn_o->zone)?->pop())) {
|
||||
if ($uo && ($ao=$uo->system->match($this->mo->tftn->zone)?->pop())) {
|
||||
$note = "+--[ FORWARDED MESSAGE ]----------------------------------+\r";
|
||||
$note .= "+ This message has been forwarded to you, it was originally sent to you\r";
|
||||
$note .= sprintf("+ at [%s]\r",$this->msg->tftn_o->ftn);
|
||||
$note .= sprintf("+ at [%s]\r",$this->mo->tftn->ftn);
|
||||
$note .= "+---------------------------------------------------------+\r\r";
|
||||
$o->msg = $note.$this->msg->message;
|
||||
$o->tftn_id = $ao->id;
|
||||
$o->flags |= Message::FLAG_INTRANSIT;
|
||||
$o->save();
|
||||
|
||||
$this->mo->msg = $note.$this->mo->content;
|
||||
$this->mo->tftn_id = $ao->id;
|
||||
$this->mo->flags |= Message::FLAG_INTRANSIT;
|
||||
$this->mo->save();
|
||||
|
||||
$processed = TRUE;
|
||||
|
||||
// Dont send an advisement to an areabot
|
||||
if (! in_array(strtolower($this->msg->user_from),config('fido.areabots')))
|
||||
Notification::route('netmail',$this->msg->fftn_o)->notify(new NetmailForward($this->msg,$ao));
|
||||
if (! in_array(strtolower($this->mo->from),config('fido.areabots')))
|
||||
Notification::route('netmail',$this->mo->fftn)->notify(new NetmailForward($this->mo,$ao));
|
||||
|
||||
// We'll ignore messages from *fix users
|
||||
} elseif (in_array(strtolower($this->msg->user_from),config('fido.areabots'))) {
|
||||
$o->flags |= Message::FLAG_RECD;
|
||||
$o->save();
|
||||
} elseif (in_array(strtolower($this->mo->from),config('fido.areabots'))) {
|
||||
$this->mo->flags |= Message::FLAG_RECD;
|
||||
$this->mo->save();
|
||||
|
||||
Log::alert(sprintf('%s:! Ignoring Netmail [%s] to the Hub from (%s:%s) - its from a bot [%d]',
|
||||
self::LOGKEY,
|
||||
$this->msg->msgid,
|
||||
$this->msg->user_from,
|
||||
$this->msg->fftn,
|
||||
$o->id,
|
||||
$this->mo->msgid,
|
||||
$this->mo->from,
|
||||
$this->mo->fftn->ftn,
|
||||
$this->mo->id,
|
||||
));
|
||||
|
||||
$processed = TRUE;
|
||||
@ -205,99 +172,87 @@ 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->msg->user_from,$this->msg->fftn));
|
||||
Log::alert(sprintf('%s:! Netmail to the Hub from (%s) [%s] but no users here.',self::LOGKEY,$this->mo->from,$this->mo->fftn->ftn));
|
||||
|
||||
Notification::route('netmail',$this->msg->fftn_o)->notify(new Reject($this->msg));
|
||||
Notification::route('netmail',$this->mo->fftn)->notify(new Reject($this->mo));
|
||||
}
|
||||
|
||||
// If in transit, store for collection
|
||||
} else {
|
||||
// @todo Check if the message is to a system we know about
|
||||
// @todo In transit loop checking
|
||||
// @todo In transit TRACE response
|
||||
|
||||
$o->flags |= Message::FLAG_INTRANSIT;
|
||||
$o->save();
|
||||
$this->mo->flags |= Message::FLAG_INTRANSIT;
|
||||
$this->mo->save();
|
||||
|
||||
Log::info(sprintf('%s:= Netmail [%s] in transit to (%s:%s) from (%s:%s) [%d].',
|
||||
self::LOGKEY,
|
||||
$this->msg->msgid,
|
||||
$this->msg->user_to,$this->msg->tftn,
|
||||
$this->msg->user_from,$this->msg->fftn,
|
||||
$o->id,
|
||||
$this->mo->msgid,
|
||||
$this->mo->to,$this->mo->tftn->ftn,
|
||||
$this->mo->from,$this->mo->fftn->ftn,
|
||||
$this->mo->id,
|
||||
));
|
||||
}
|
||||
|
||||
// Else we are echomail
|
||||
} else {
|
||||
Log::debug(sprintf('%s:- Looking for echomail area [%s] for mail from [%s]',self::LOGKEY,$this->msg->echoarea,$this->msg->fboss));
|
||||
// The packet sender
|
||||
$sender = $this->mo->set->get('set_sender');
|
||||
|
||||
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));
|
||||
// @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));
|
||||
return;
|
||||
}
|
||||
|
||||
$ea = Echoarea::where('name',strtoupper($this->msg->echoarea))
|
||||
->where('domain_id',$this->msg->fboss_o->zone->domain_id)
|
||||
->single();
|
||||
Log::debug(sprintf('%s:- Processing echomail [%s] in [%s] from [%s].',self::LOGKEY,$this->mo->msgid,$this->mo->echoarea->name,$sender->ftn));
|
||||
|
||||
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)) {
|
||||
// Message from zone is incorrect for echoarea
|
||||
if (! $this->mo->echoarea->domain->zones->contains($this->mo->fftn->zone)) {
|
||||
Log::alert(sprintf('%s:! The message [%s] is from a different zone [%d] than the packet sender [%d] - not importing',
|
||||
self::LOGKEY,
|
||||
$this->msg->msgid,
|
||||
$this->msg->fboss_o->zone->zone_id,
|
||||
$this->pktsrc->zone->zone_id));
|
||||
$this->mo->msgid,
|
||||
$this->mo->fftn->zone->zone_id,
|
||||
$this->mo->fftn->zone->zone_id));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for duplicate messages
|
||||
// FTS-0009.001
|
||||
if ($this->msg->msgid) {
|
||||
$o = Echomail::where('msgid',$this->msg->msgid)
|
||||
->where('fftn_id',($x=$this->msg->fboss_o) ? $x->id : NULL)
|
||||
->where('datetime','>=',$this->msg->date->subYears(3))
|
||||
->where('datetime','<=',$this->msg->date)
|
||||
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)
|
||||
->single();
|
||||
|
||||
Log::debug(sprintf('%s:- Checking for duplicate from host id [%d].',self::LOGKEY,($x=$this->msg->fboss_o) ? $x->id : NULL));
|
||||
Log::debug(sprintf('%s:- Checking for duplicate from host id [%d].',self::LOGKEY,$this->mo->fftn->id));
|
||||
|
||||
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->msg->msgid,
|
||||
$this->msg->echoarea,
|
||||
$this->msg->user_from,$this->msg->fftn,
|
||||
$this->msg->user_to,
|
||||
$this->mo->msgid,
|
||||
$this->mo->echoarea->name,
|
||||
$this->mo->from,$this->mo->fftn->ftn,
|
||||
$this->mo->to,
|
||||
));
|
||||
|
||||
if (! $o->msg_crc)
|
||||
$o->msg_crc = md5($this->msg->message);
|
||||
|
||||
$o->save();
|
||||
//$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->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);
|
||||
}
|
||||
*/
|
||||
$path = $this->parseAddresses('path',$this->mo->path,$sender->zone,$dummy);
|
||||
|
||||
$ppoid = NULL;
|
||||
foreach ($path as $aoid) {
|
||||
@ -310,23 +265,24 @@ 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->msg->seenby,$this->pktsrc->zone,$o->rogue_seenby);
|
||||
$x = $o->seenby()->syncWithoutDetaching($seenby);
|
||||
|
||||
$seenby = $this->parseAddresses('seenby',$this->mo->seenby,$sender->zone,$o->rogue_seenby);
|
||||
$this->mo->seenby()->syncWithoutDetaching($seenby);
|
||||
|
||||
// In case our rogue_seenby changed
|
||||
if ($o->getDirty())
|
||||
$o->save();
|
||||
$this->mo->save();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Find another message with the same msg_crc
|
||||
if ($this->msg->message) {
|
||||
$o = Echomail::where('msg_crc',$xx=md5($this->msg->message))
|
||||
->where('fftn_id',($x=$this->msg->fboss_o) ? $x->id : NULL)
|
||||
if ($this->mo->msg_crc) {
|
||||
$o = Echomail::where('msg_crc',$xx=md5($this->mo->msg_crc))
|
||||
->where('fftn_id',$this->mo->fftn->id)
|
||||
->where('datetime','>',Carbon::now()->subWeek())
|
||||
->get();
|
||||
|
||||
@ -339,73 +295,38 @@ 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 ($sender->echoareas->search(function($item) { return $item->id === $this->mo->echoarea->id; }) === FALSE) {
|
||||
Log::alert(sprintf('%s:! FTN [%s] is not subscribed to [%s] for [%s].',self::LOGKEY,$sender->ftn,$this->mo->echoarea->name,$this->mo->msgid));
|
||||
|
||||
if (! $this->msg->rescanned->count())
|
||||
Notification::route('netmail',$this->pktsrc)->notify(new EchoareaNotSubscribed($this->msg));
|
||||
if (! $rescanned)
|
||||
Notification::route('netmail',$sender)->notify(new EchoareaNotSubscribed($this->mo));
|
||||
}
|
||||
|
||||
// Can the system send messages to this area?
|
||||
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));
|
||||
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));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We know about this area, store it
|
||||
$o = new Echomail;
|
||||
$o->init();
|
||||
|
||||
$o->to = $this->msg->user_to;
|
||||
$o->from = $this->msg->user_from;
|
||||
$o->subject = $this->msg->subject;
|
||||
$o->datetime = $this->msg->date;
|
||||
$o->tzoffset = $this->msg->date->utcOffset();
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
$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;
|
||||
$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();
|
||||
$this->mo->save();
|
||||
|
||||
Log::info(sprintf('%s:= Echomail [%s] in [%s] from (%s) [%s] to (%s) - [%s].',
|
||||
self::LOGKEY,
|
||||
$this->msg->msgid,
|
||||
$this->msg->echoarea,
|
||||
$this->msg->user_from,$this->msg->fftn,
|
||||
$this->msg->user_to,
|
||||
$o->id,
|
||||
$this->mo->msgid,
|
||||
$this->mo->echoarea->name,
|
||||
$this->mo->from,$this->mo->fftn->ftn,
|
||||
$this->mo->to,
|
||||
$this->mo->id,
|
||||
));
|
||||
|
||||
// If the message is to a bot, but not rescanned, or purposely skipbot set, we'll process it
|
||||
if ((! $this->skipbot) && (! $this->msg->rescanned->count()))
|
||||
if ((! $this->skipbot) && (! $rescanned))
|
||||
foreach (config('process.echomail') as $class) {
|
||||
if ($class::handle($this->msg)) {
|
||||
if ($class::handle($this->mo)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -15,9 +15,9 @@ use Illuminate\Support\Facades\Storage;
|
||||
use League\Flysystem\UnableToMoveFile;
|
||||
|
||||
use App\Classes\File;
|
||||
use App\Classes\File\Item;
|
||||
use App\Classes\FTN\{InvalidPacketException,Packet};
|
||||
use App\Models\Address;
|
||||
use App\Classes\FTN\Packet;
|
||||
use App\Exceptions\InvalidPacketException;
|
||||
use App\Models\{Domain,Echomail,Netmail};
|
||||
use App\Notifications\Netmails\PacketPasswordInvalid;
|
||||
|
||||
class PacketProcess implements ShouldQueue
|
||||
@ -26,22 +26,26 @@ class PacketProcess implements ShouldQueue
|
||||
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
private Item $file;
|
||||
private Address $ao;
|
||||
private string $filename;
|
||||
private Domain $do;
|
||||
private Carbon $rcvd_time;
|
||||
private bool $interactive;
|
||||
private bool $nobot;
|
||||
|
||||
public function __construct(Item $file,Address $ao,Carbon $rcvd_time)
|
||||
public function __construct(string $filename,Domain $do,bool $interactive=FALSE,Carbon $rcvd_time=NULL,bool $nobot=FALSE)
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->ao = $ao;
|
||||
$this->rcvd_time = $rcvd_time;
|
||||
$this->filename = $filename;
|
||||
$this->do = $do;
|
||||
$this->interactive = $interactive;
|
||||
$this->rcvd_time = $rcvd_time ?: Carbon::now();
|
||||
$this->nobot = $nobot;
|
||||
}
|
||||
|
||||
public function __get($key): mixed
|
||||
{
|
||||
switch ($key) {
|
||||
case 'subject':
|
||||
return $this->file->name;
|
||||
return $this->filename;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
@ -54,30 +58,47 @@ class PacketProcess implements ShouldQueue
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Log::info(sprintf('%s:- Processing mail %s [%s]',self::LOGKEY,$this->file->whatType() === Item::IS_PKT ? 'PACKET' : 'ARCHIVE',$this->file->nameas));
|
||||
Log::info(sprintf('%s:- Processing mail [%s]',self::LOGKEY,$this->filename));
|
||||
|
||||
$fs = Storage::disk(config('fido.local_disk'));
|
||||
|
||||
// @todo Catch files that we cannot process, eg: ARJ bundles.
|
||||
try {
|
||||
$f = new File($this->file->full_name);
|
||||
$f = new File($fs->path($this->filename));
|
||||
|
||||
$processed = FALSE;
|
||||
|
||||
foreach ($f as $packet) {
|
||||
$pkt = Packet::process($packet,Arr::get(stream_get_meta_data($packet),'uri'),$f->itemSize(),$this->ao->zone->domain);
|
||||
$pkt = Packet::process($packet,Arr::get(stream_get_meta_data($packet),'uri'),$f->itemSize(),$this->do);
|
||||
|
||||
// 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));
|
||||
// Check that the packet is from a system that is defined in the DB
|
||||
if (! $pkt->fftn) {
|
||||
Log::error(sprintf('%s:! Packet [%s] is not from a system we know about? [%s]',self::LOGKEY,$this->filename,$pkt->fftn_t));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Check the packet 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));
|
||||
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));
|
||||
|
||||
Notification::route('netmail',$this->ao)->notify(new PacketPasswordInvalid($pkt->password,$this->file->nameas));
|
||||
// @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));
|
||||
|
||||
Notification::route('netmail',$pkt->fftn)->notify(new PacketPasswordInvalid($pkt->password,$this->filename));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -89,37 +110,34 @@ class PacketProcess implements ShouldQueue
|
||||
|
||||
$count = 0;
|
||||
foreach ($pkt as $msg) {
|
||||
Log::info(sprintf('%s:- Mail from [%s] to [%s]',self::LOGKEY,$msg->fftn,$msg->tftn));
|
||||
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));
|
||||
|
||||
// @todo Quick check that the packet should be processed by us.
|
||||
// @todo validate that the packet's zone is in the domain.
|
||||
if ($msg->errors) {
|
||||
Log::error(sprintf('%s:! Message [%s] has [%d] errors, unable to process',self::LOGKEY,$msg->msgid,$msg->errors->errors()->count()));
|
||||
|
||||
/*
|
||||
* // @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
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
|
||||
$msg->set_sender = $pkt->fftn->withoutRelations();
|
||||
// Record receiving packet and sender
|
||||
$msg->set_pkt = $f->pktName();
|
||||
$msg->set_recvtime = $this->rcvd_time;
|
||||
|
||||
try {
|
||||
// Dispatch job.
|
||||
if ($queue)
|
||||
MessageProcess::dispatch($msg,$f->pktName(),$this->ao->withoutRelations(),$pkt->fftn_o->withoutRelations(),$this->rcvd_time);
|
||||
if ($queue || (! $this->interactive))
|
||||
MessageProcess::dispatch($msg->withoutRelations(),$this->nobot);
|
||||
else
|
||||
MessageProcess::dispatchSync($msg,$f->pktName(),$this->ao->withoutRelations(),$pkt->fftn_o->withoutRelations(),$this->rcvd_time);
|
||||
MessageProcess::dispatchSync($msg->withoutRelations(),$this->nobot);
|
||||
|
||||
$count++;
|
||||
|
||||
} 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())
|
||||
@ -127,42 +145,41 @@ class PacketProcess implements ShouldQueue
|
||||
}
|
||||
|
||||
if (! $processed) {
|
||||
Log::alert(sprintf('%s:- Not deleting packet [%s], it doesnt seem to be processed?',self::LOGKEY,$this->file->nameas));
|
||||
Log::alert(sprintf('%s:- Not deleting packet [%s], it doesnt seem to be processed?',self::LOGKEY,$this->filename));
|
||||
|
||||
} else {
|
||||
// If we want to keep the packet, we could do that logic here
|
||||
if (config('fido.packet_keep')) {
|
||||
$dir = sprintf('%s/%s/%s/%s',config('fido.dir'),($x=Carbon::now())->format('Y'),$x->format('m'),$x->format('d'));
|
||||
Log::debug(sprintf('%s:- Moving processed packet [%s] to [%s]',self::LOGKEY,$this->file->rel_name,$dir));
|
||||
Log::debug(sprintf('%s:- Moving processed packet [%s] to [%s]',self::LOGKEY,$this->filename,$dir));
|
||||
|
||||
try {
|
||||
if ($fs->makeDirectory($dir)) {
|
||||
$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));
|
||||
$fs->move($this->filename,$x=sprintf('%s/%s',$dir,$f->itemName()));
|
||||
Log::info(sprintf('%s:- Moved processed packet [%s] to [%s]',self::LOGKEY,$this->filename,$x));
|
||||
|
||||
} else
|
||||
Log::error(sprintf('%s:! Unable to create dir [%s]',self::LOGKEY,$dir));
|
||||
|
||||
} catch (UnableToMoveFile $e) {
|
||||
Log::error(sprintf('%s:! Unable to move packet [%s] to [%s] (%s)',self::LOGKEY,$this->file->full_name,$dir,$e->getMessage()));
|
||||
Log::error(sprintf('%s:! Unable to move packet [%s] to [%s] (%s)',self::LOGKEY,$this->filename,$dir,$e->getMessage()));
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error(sprintf('%s:! Failed moving packet [%s] to [%s] (%s)',self::LOGKEY,$this->file->full_name,$dir,$e->getMessage()));
|
||||
Log::error(sprintf('%s:! Failed moving packet [%s] to [%s] (%s)',self::LOGKEY,$this->filename,$dir,$e->getMessage()));
|
||||
}
|
||||
|
||||
} else {
|
||||
Log::debug(sprintf('%s:- Deleting processed packet [%s]',self::LOGKEY,$this->file->full_name));
|
||||
Log::debug(sprintf('%s:- Deleting processed packet [%s]',self::LOGKEY,$this->filename));
|
||||
|
||||
// @todo Change this to use Storage::disk()
|
||||
unlink($this->file->full_name);
|
||||
$fs->delete($this->filename);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (InvalidPacketException $e) {
|
||||
Log::error(sprintf('%s:- Not deleting packet [%s], as it generated an InvalidPacketException',self::LOGKEY,$this->file->nameas),['e'=>$e->getMessage()]);
|
||||
Log::error(sprintf('%s:- Not deleting packet [%s], as it generated an InvalidPacketException',self::LOGKEY,$this->filename),['e'=>$e->getMessage()]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error(sprintf('%s:- Not deleting packet [%s], as it generated an uncaught exception',self::LOGKEY,$this->file->nameas),['e'=>$e->getMessage()]);
|
||||
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()]);
|
||||
}
|
||||
}
|
||||
}
|
@ -1053,6 +1053,8 @@ class Address extends Model
|
||||
if ($passpos > 8)
|
||||
Log::alert(sprintf('%s:! Password would be greater than 8 chars? [%d]',self::LOGKEY,$passpos));
|
||||
|
||||
// @todo Do the strip pass where, if we dont want the password in the netmail
|
||||
|
||||
$pkt = $this->getPacket($x,substr($x->last()->subject,0,$passpos));
|
||||
|
||||
if ($pkt && $pkt->count() && $update)
|
||||
@ -1091,8 +1093,9 @@ class Address extends Model
|
||||
* @param string|null $passwd Override password used in packet
|
||||
* @return Packet|null
|
||||
* @throws \Exception
|
||||
* @deprecated
|
||||
*/
|
||||
public function getPacket(Collection $msgs,string $passwd=NULL): ?Packet
|
||||
private function getPacket(Collection $msgs,string $passwd=NULL): ?Packet
|
||||
{
|
||||
$s = Setup::findOrFail(config('app.id'));
|
||||
$ao = our_address($this);
|
||||
@ -1104,7 +1107,7 @@ class Address extends Model
|
||||
}
|
||||
|
||||
// Get packet type
|
||||
$o = $ao->system->packet();
|
||||
$o = $ao->system->packet($this);
|
||||
$o->addressHeader($ao,$this,$passwd);
|
||||
|
||||
// $oo = Netmail/Echomail Model
|
||||
|
@ -10,54 +10,77 @@ 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\{EncodeUTF8,MsgID,ParseAddresses,QueryCacheableConfig};
|
||||
use App\Traits\{MessageAttributes,MsgID,ParseAddresses,QueryCacheableConfig};
|
||||
|
||||
final class Echomail extends Model implements Packet
|
||||
{
|
||||
use SoftDeletes,EncodeUTF8,MsgID,ParseAddresses,QueryCacheableConfig;
|
||||
use SoftDeletes,MessageAttributes,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,
|
||||
];
|
||||
|
||||
private const cast_utf8 = [
|
||||
'to',
|
||||
'from',
|
||||
'subject',
|
||||
'msg',
|
||||
'msg_src',
|
||||
'origin',
|
||||
'tearline',
|
||||
'tagline',
|
||||
'rogue_path' => CollectionOrNull::class, // @deprecated?
|
||||
];
|
||||
|
||||
public function __set($key,$value)
|
||||
{
|
||||
switch ($key) {
|
||||
case 'kludges':
|
||||
if (! count($value))
|
||||
return;
|
||||
|
||||
if (array_key_exists($value[0],self::kludges)) {
|
||||
$this->{self::kludges[$value[0]]} = $value[1];
|
||||
|
||||
} else {
|
||||
$this->kludges->put($value[0],$value[1]);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'no_export':
|
||||
case 'set_path':
|
||||
case 'set_pkt':
|
||||
case 'set_sender':
|
||||
case 'set_recvtime':
|
||||
case 'set_seenby':
|
||||
$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_seenby':
|
||||
if (! $this->set->has($key))
|
||||
$this->set->put($key,collect());
|
||||
|
||||
$this->set->get($key)->push($value);
|
||||
break;
|
||||
|
||||
default:
|
||||
parent::__set($key,$value);
|
||||
}
|
||||
@ -67,6 +90,12 @@ 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();
|
||||
@ -74,8 +103,10 @@ final class Echomail extends Model implements Packet
|
||||
$path = collect();
|
||||
|
||||
// Parse PATH
|
||||
if ($model->set_path->count())
|
||||
$path = self::parseAddresses('path',$model->set_path,$model->fftn->zone,$rogue);
|
||||
if ($model->set->has('set_path'))
|
||||
$path = self::parseAddresses('path',$model->set->get('set_path'),$model->fftn->zone,$rogue);
|
||||
|
||||
Log::debug(sprintf('%s:^ Message [%d] from point address is [%d]',self::LOGKEY,$model->id,$model->fftn->point_id));
|
||||
|
||||
// Make sure our sender is first in the path
|
||||
if (! $path->contains($model->fftn_id)) {
|
||||
@ -84,9 +115,9 @@ final class Echomail extends Model implements Packet
|
||||
}
|
||||
|
||||
// Make sure our pktsrc is last in the path
|
||||
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);
|
||||
if ($model->set->has('set_sender') && (! $path->contains($model->set->get('set_sender')->id))) {
|
||||
Log::alert(sprintf('%s:? Echomail adding pktsrc to end of PATH [%s].',self::LOGKEY,$model->set->get('set_sender')->ftn));
|
||||
$path->push($model->set->get('set_sender')->id);
|
||||
}
|
||||
|
||||
// Save the Path
|
||||
@ -105,8 +136,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_seenby->count())
|
||||
$seenby = self::parseAddresses('seenby',$model->set_seenby,$model->fftn->zone,$rogue);
|
||||
if ($model->set->has('set_seenby'))
|
||||
$seenby = self::parseAddresses('seenby',$model->set->get('set_seenby'),$model->fftn->zone,$rogue);
|
||||
|
||||
// Make sure our sender is in the seenby
|
||||
if (! $seenby->contains($model->fftn_id)) {
|
||||
@ -115,9 +146,9 @@ final class Echomail extends Model implements Packet
|
||||
}
|
||||
|
||||
// Make sure our pktsrc is in the seenby
|
||||
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 ($model->set->has('set_sender') && (! $seenby->contains($model->set->get('set_sender')->id))) {
|
||||
Log::alert(sprintf('%s:? Echomail adding pktsrc to SEENBY [%s].',self::LOGKEY,$model->set->get('set_sender')->ftn));
|
||||
$seenby->push($model->set->get('set_sender')->id);
|
||||
}
|
||||
|
||||
if (count($rogue)) {
|
||||
@ -129,26 +160,26 @@ final class Echomail extends Model implements Packet
|
||||
$model->seenby()->sync($seenby);
|
||||
|
||||
// Our last node in the path is our sender
|
||||
if (isset($model->set_pkt) && isset($model->set_recvtime)) {
|
||||
if ($model->set->has('set_pkt') && $model->set->has('set_recvtime')) {
|
||||
if ($path->count()) {
|
||||
DB::update('UPDATE echomail_path set recv_pkt=?,recv_at=? where address_id=? and echomail_id=?',[
|
||||
$model->set_pkt,
|
||||
$model->set_recvtime,
|
||||
$model->set->get('set_pkt'),
|
||||
$model->set->get('set_recvtime'),
|
||||
$path->last(),
|
||||
$model->id,
|
||||
]);
|
||||
|
||||
} else {
|
||||
Log::critical(sprintf('%s:! Wasnt able to set packet details for [%d] to [%s] to [%s], no path information',self::LOGKEY,$model->id,$model->set_pkt,$model->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->get('set_pkt'),$model->set->get('set_recvtime')));
|
||||
}
|
||||
}
|
||||
|
||||
// See if we need to export this message.
|
||||
if ($model->echoarea->sec_read) {
|
||||
$exportto = ($x=$model
|
||||
$exportto = $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);
|
||||
|
||||
@ -193,90 +224,19 @@ final class Echomail extends Model implements Packet
|
||||
->withPivot(['id','parent_id','recv_pkt','recv_at']);
|
||||
}
|
||||
|
||||
/* METHODS */
|
||||
/* ATTRIBUTES */
|
||||
|
||||
public function init(): void
|
||||
public function getSeenByAttribute(): Collection
|
||||
{
|
||||
$this->set_path = collect();
|
||||
$this->set_seenby = collect();
|
||||
return ((! $this->exists) && $this->set->has('set_seenby'))
|
||||
? $this->set->get('set_seenby')
|
||||
: $this->getRelationValue('seenby');
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
public function getPathAttribute(): Collection
|
||||
{
|
||||
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();
|
||||
return ((! $this->exists) && $this->set->has('set_path'))
|
||||
? $this->set->get('set_path')
|
||||
: $this->getRelationValue('path');
|
||||
}
|
||||
}
|
@ -10,35 +10,30 @@ use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
use App\Casts\CompressedString;
|
||||
use App\Classes\FTN\Message;
|
||||
use App\Casts\{CollectionOrNull,CompressedString};
|
||||
use App\Interfaces\Packet;
|
||||
use App\Traits\{EncodeUTF8,MsgID};
|
||||
use App\Pivots\ViaPivot;
|
||||
use App\Traits\{MessageAttributes,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+(.*)$/';
|
||||
|
||||
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',
|
||||
/**
|
||||
* Kludges that we absorb in this model
|
||||
*/
|
||||
private const kludges = [
|
||||
'MSGID:'=>'msgid',
|
||||
'REPLY:'=>'replyid',
|
||||
'Via' => 'set_path',
|
||||
];
|
||||
|
||||
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',
|
||||
@ -47,11 +42,36 @@ final class Netmail extends Model implements Packet
|
||||
public function __set($key,$value)
|
||||
{
|
||||
switch ($key) {
|
||||
case 'set_path':
|
||||
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_pkt':
|
||||
case 'set_recvtime':
|
||||
case 'set_sender':
|
||||
$this->{$key} = $value;
|
||||
// @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);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -63,43 +83,48 @@ 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
|
||||
// @todo dont save us in the path, we'll add it dynamically when we send out.
|
||||
// <FTN Address> @YYYYMMDD.HHMMSS[.Precise][.Time Zone] <Program Name> <Version> [Serial Number]
|
||||
if (isset($model->set_path)) {
|
||||
if ($model->set_path->count()) {
|
||||
foreach ($model->set_path as $line) {
|
||||
$m = [];
|
||||
if ($model->set->has('set_path')) {
|
||||
foreach ($model->set->get('set_path') as $line) {
|
||||
$m = [];
|
||||
|
||||
if (preg_match('/^([0-9]+:[0-9]+\/[0-9]+(\..*)?)\s+@([0-9.a-zA-Z]+)\s+(.*)$/',$line,$m)) {
|
||||
// Address
|
||||
$ao = Address::findFTN($m[1]);
|
||||
if (preg_match(self::PATH_REGEX,$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
|
||||
$t = [];
|
||||
$datetime = '';
|
||||
// Time
|
||||
$t = [];
|
||||
$datetime = '';
|
||||
|
||||
if (! preg_match('/^([0-9]+\.[0-9]+)(\.?(.*))?$/',$m[3],$t))
|
||||
Log::alert(sprintf('%s:! Unable to determine time from [%s]',self::LOGKEY,$m[3]));
|
||||
else
|
||||
$datetime = Carbon::createFromFormat('Ymd.His',$t[1],$t[3] ?? '');
|
||||
if (! preg_match('/^([0-9]+\.[0-9]+)(\.?(.*))?$/',$m[3],$t))
|
||||
Log::alert(sprintf('%s:! Unable to determine time from [%s]',self::LOGKEY,$m[3]));
|
||||
else
|
||||
$datetime = Carbon::createFromFormat('Ymd.His',$t[1],$t[3] ?? '');
|
||||
|
||||
if (! $ao) {
|
||||
Log::alert(sprintf('%s:! Undefined Node [%s] for Netmail.',self::LOGKEY,$m[1]));
|
||||
//$rogue->push(['node'=>$m[1],'datetime'=>$datetime,'program'=>$m[4]]);
|
||||
if (! $ao) {
|
||||
Log::alert(sprintf('%s:! Undefined Node [%s] in netmail path.',self::LOGKEY,$m[1]));
|
||||
//$rogue->push(['node'=>$m[1],'datetime'=>$datetime,'program'=>$m[4]]);
|
||||
|
||||
} else {
|
||||
$nodes->push(['node'=>$ao,'datetime'=>$datetime,'program'=>$m[4]]);
|
||||
}
|
||||
} else {
|
||||
$nodes->push(['node'=>$ao,'datetime'=>$datetime,'program'=>$m[4]]);
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no details (Mystic), we'll create a blank
|
||||
} else {
|
||||
$nodes->push(['node'=>$model->set_sender,'datetime'=>Carbon::now(),'program'=>'Unknown']);
|
||||
}
|
||||
|
||||
// If there are no details (Mystic), we'll create a blank
|
||||
} else {
|
||||
$nodes->push(['node'=>$model->set->get('set_sender'),'datetime'=>Carbon::now(),'program'=>sprintf('%s (%04X)',Setup::PRODUCT_NAME,Setup::PRODUCT_ID)]);
|
||||
}
|
||||
|
||||
// Save the Path
|
||||
@ -118,15 +143,25 @@ final class Netmail extends Model implements Packet
|
||||
}
|
||||
|
||||
// Our last node in the path is our sender
|
||||
if ($nodes->count() && isset($model->set_pkt) && isset($model->set_sender) && isset($model->set_recvtime)) {
|
||||
if ($nodes->count() && $model->set->has('set_pkt') && $model->set->has('set_sender') && $model->set->has('set_recvtime')) {
|
||||
DB::update('UPDATE netmail_path set recv_pkt=?,recv_at=?,recv_id=? where address_id=? and netmail_id=?',[
|
||||
$model->set_pkt,
|
||||
$model->set_recvtime,
|
||||
$model->set_sender->id,
|
||||
$model->set->get('set_pkt'),
|
||||
$model->set->get('set_recvtime'),
|
||||
$model->set->get('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();
|
||||
});
|
||||
}
|
||||
|
||||
@ -142,13 +177,8 @@ 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']);
|
||||
}
|
||||
|
||||
public function received()
|
||||
{
|
||||
return $this->belongsToMany(Address::class,'netmail_path','netmail_id','recv_id')
|
||||
->withPivot(['id','parent_id','datetime','program','recv_pkt','recv_id']);
|
||||
->withPivot(['id','parent_id','datetime','program','recv_pkt','recv_id'])
|
||||
->using(ViaPivot::class);
|
||||
}
|
||||
|
||||
public function tftn()
|
||||
@ -158,89 +188,41 @@ 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 */
|
||||
|
||||
/**
|
||||
* Return this model as a packet
|
||||
* Render the via line
|
||||
*
|
||||
* @param Address $ao
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function packet(Address $ao,string $strippass=NULL): Message
|
||||
public function via(Address $ao): string
|
||||
{
|
||||
Log::debug(sprintf('%s:+ Bundling [%s]',self::LOGKEY,$this->id));
|
||||
if (! $ao->pivot)
|
||||
throw new \Exception('Cannot render the via line without an address record without a path pivot');
|
||||
|
||||
// @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;
|
||||
$o->origin = $this->origin;
|
||||
|
||||
// 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();
|
||||
return sprintf('%s @%s.UTC %s',
|
||||
$ao->ftn3d,
|
||||
$ao->pivot->datetime->format('Ymd.His'),
|
||||
$ao->pivot->program);
|
||||
}
|
||||
}
|
@ -251,12 +251,15 @@ class System extends Model
|
||||
/**
|
||||
* Return the packet that this system uses
|
||||
*
|
||||
* @param Address $ao
|
||||
* @return Packet
|
||||
*/
|
||||
public function packet(): Packet
|
||||
public function packet(Address $ao): Packet
|
||||
{
|
||||
return new (collect(Packet::PACKET_TYPES)
|
||||
->get($this->pkt_type ?: config('fido.packet_default')));
|
||||
// @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);
|
||||
}
|
||||
|
||||
public function poll(): ?Job
|
||||
|
@ -41,6 +41,6 @@ class EchomailChannel
|
||||
|
||||
$o = $notification->toEchomail($notifiable);
|
||||
|
||||
Log::info(sprintf('%s:= Posted echomail [%s] to [%s]',self::LOGKEY,$o->msgid,$echoarea));
|
||||
Log::info(sprintf('%s:= Posted echomail (%d) [%s] to [%s]',self::LOGKEY,$o->id,$o->msgid,$echoarea));
|
||||
}
|
||||
}
|
@ -41,6 +41,6 @@ class NetmailChannel
|
||||
|
||||
$o = $notification->toNetmail($notifiable);
|
||||
|
||||
Log::info(sprintf('%s:= Sent netmail [%s] to [%s]',self::LOGKEY,$o->msgid,$ao->ftn));
|
||||
Log::info(sprintf('%s:= Sent netmail (%d) [%s] to [%s]',self::LOGKEY,$o->id,$o->msgid,$ao->ftn));
|
||||
}
|
||||
}
|
@ -52,13 +52,14 @@ abstract class Netmails extends Notification //implements ShouldQueue
|
||||
$ao = $notifiable->routeNotificationFor(static::via);
|
||||
|
||||
$o = new Netmail;
|
||||
$o->set_sender = our_address($ao);
|
||||
$o->to = $ao->system->sysop;
|
||||
$o->from = Setup::PRODUCT_NAME;
|
||||
|
||||
$o->datetime = Carbon::now();
|
||||
$o->tzoffset = $o->datetime->utcOffset();
|
||||
|
||||
$o->fftn_id = our_address($ao)->id;
|
||||
$o->fftn_id = $o->set->get('set_sender')->id;
|
||||
$o->tftn_id = $ao->id;
|
||||
$o->flags = (Message::FLAG_LOCAL|Message::FLAG_PRIVATE);
|
||||
$o->cost = 0;
|
||||
|
@ -7,7 +7,7 @@ use Illuminate\Support\Facades\Log;
|
||||
|
||||
use App\Classes\FTN\Message;
|
||||
use App\Notifications\Netmails;
|
||||
use App\Models\{Netmail,System};
|
||||
use App\Models\{Echomail,Netmail};
|
||||
use App\Traits\{MessagePath,PageTemplate};
|
||||
|
||||
class EchoareaNotExist extends Netmails
|
||||
@ -16,14 +16,14 @@ class EchoareaNotExist extends Netmails
|
||||
|
||||
private const LOGKEY = 'NNW';
|
||||
|
||||
private Message $mo;
|
||||
private Echomail $mo;
|
||||
|
||||
/**
|
||||
* Send a sysop a message if they attempt to write to an area that doesnt exist.
|
||||
*
|
||||
* @param Message $mo
|
||||
*/
|
||||
public function __construct(Message $mo)
|
||||
public function __construct(Echomail $mo)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
@ -44,7 +44,7 @@ class EchoareaNotExist extends Netmails
|
||||
|
||||
Log::info(sprintf('%s:+ Creating ECHOMAIL NOT EXIST netmail to [%s]',self::LOGKEY,$ao->ftn));
|
||||
|
||||
$o->subject = 'Echoarea doesnt exist - '.$this->mo->echoarea;
|
||||
$o->subject = 'Echoarea doesnt exist - '.$this->mo->set->get('set_echoarea');
|
||||
|
||||
// Message
|
||||
$msg = $this->page(FALSE,'nothere');
|
||||
@ -52,7 +52,7 @@ 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->user_to,
|
||||
$this->mo->to,
|
||||
Carbon::now()->utc()->toDateTimeString(),
|
||||
$this->mo->date->utc()->toDateTimeString(),
|
||||
)
|
||||
|
12
app/Pivots/ViaPivot.php
Normal file
12
app/Pivots/ViaPivot.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Pivots;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
|
||||
class ViaPivot extends Pivot
|
||||
{
|
||||
protected $casts = [
|
||||
'datetime' => 'datetime',
|
||||
];
|
||||
}
|
184
app/Traits/MessageAttributes.php
Normal file
184
app/Traits/MessageAttributes.php
Normal file
@ -0,0 +1,184 @@
|
||||
<?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 $val is not set, then it may be an unsaved object
|
||||
return ((! $this->exists) && $this->set->has('set_origin'))
|
||||
? sprintf(' * Origin: %s',$this->set->get('set_origin'))
|
||||
: $val;
|
||||
}
|
||||
|
||||
public function getTaglineAttribute(string $val=NULL): ?string
|
||||
{
|
||||
// If $val is not set, then it may be an unsaved object
|
||||
return ((! $this->exists) && $this->set->has('set_tagline'))
|
||||
? sprintf('... %s',$this->set->get('set_tagline'))
|
||||
: $val;
|
||||
}
|
||||
|
||||
public function getTearlineAttribute(string $val=NULL): ?string
|
||||
{
|
||||
// If $val is not set, then it may be an unsaved object
|
||||
return ((! $this->exists) && $this->set->has('set_tearline'))
|
||||
? sprintf('--- %s',$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),
|
||||
])->filter();
|
||||
}
|
||||
|
||||
private 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();
|
||||
}
|
||||
}
|
@ -5,32 +5,32 @@
|
||||
*/
|
||||
namespace App\Traits;
|
||||
|
||||
use App\Classes\FTN\Message;
|
||||
use App\Models\{Echomail,Netmail};
|
||||
|
||||
trait MessagePath
|
||||
{
|
||||
protected function message_path(Message $mo): string
|
||||
protected function message_path(Echomail|Netmail $mo): string
|
||||
{
|
||||
$reply = "This is your original message:\r\r";
|
||||
|
||||
$reply .= "+--[ BEGIN MESSAGE ]----------------------------------+\r";
|
||||
$reply .= sprintf("TO: %s\r",$mo->user_to);
|
||||
$reply .= sprintf("TO: %s\r",$mo->to);
|
||||
$reply .= sprintf("SUBJECT: %s\r",$mo->subject);
|
||||
$reply .= str_replace("\r---","\r#--",$mo->message)."\r";
|
||||
$reply .= str_replace("\r---","\r#--",$mo->msg)."\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->kludge as $k=>$v)
|
||||
$reply .= sprintf("@%s: %s\r",strtoupper($k),$v);
|
||||
foreach ($mo->kludges as $k=>$v)
|
||||
$reply .= sprintf("%s %s\r",$k,$v);
|
||||
|
||||
$reply .= "+--[ PATH ]-------------------------------------------+\r";
|
||||
|
||||
if ($mo->isNetmail()) {
|
||||
if ($mo instanceof Netmail) {
|
||||
if ($mo->via->count())
|
||||
foreach ($mo->via as $via)
|
||||
$reply .= sprintf("VIA: %s\r",$via);
|
||||
foreach ($mo->via as $ao)
|
||||
$reply .= sprintf("VIA: %s\r",$mo->via($ao));
|
||||
else
|
||||
$reply .= "No path information? This would be normal if this message came directly to the hub\r";
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
"keywords": ["framework","laravel"],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.1|8.2|8.3",
|
||||
"php": "^8.2|8.3",
|
||||
"ext-bz2": "*",
|
||||
"ext-pcntl": "*",
|
||||
"ext-sockets": "*",
|
||||
|
@ -0,0 +1,34 @@
|
||||
<?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');
|
||||
});
|
||||
}
|
||||
};
|
@ -1,3 +1,8 @@
|
||||
@php
|
||||
use App\Models\Netmail;
|
||||
use App\Classes\FTN\Message;
|
||||
@endphp
|
||||
|
||||
@extends('layouts.app')
|
||||
@section('htmlheader_title')
|
||||
Verify Packet
|
||||
@ -82,7 +87,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->isNetmail()) Netmail @else Echomail <strong>{{ $msg->echoarea }}</strong> @endif : {{ $msg->msgid }}
|
||||
@if($msg instanceof Netmail) Netmail @else Echomail <strong>{{ $msg->echoarea->name }}</strong> @endif : {{ $msg->msgid }}
|
||||
</span>
|
||||
</h4>
|
||||
|
||||
@ -98,48 +103,40 @@
|
||||
|
||||
<div class="row pb-2">
|
||||
<div class="col-4">
|
||||
DATE: <strong class="highlight">{{ $msg->date }}</strong>
|
||||
DATE: <strong class="highlight">{{ $msg->datetime }}</strong>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
FLAGS: <strong class="highlight">{{ $msg->flags()->filter()->keys()->join(', ') }}</strong>
|
||||
FLAGS: <strong class="highlight">{{ $msg->flags()->keys()->join(', ') }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row pb-2">
|
||||
<div class="col-4">
|
||||
FROM: <strong class="highlight">{!! \App\Classes\FTN\Message::tr($msg->user_from) !!}</strong> (<strong class="highlight">{{ $msg->fftn }}</strong>)
|
||||
FROM: <strong class="highlight">{!! Message::tr($msg->from) !!}</strong> (<strong class="highlight">{{ $msg->fftn->ftn }}</strong>)
|
||||
</div>
|
||||
<div class="col-4">
|
||||
TO: <strong class="highlight">{!! \App\Classes\FTN\Message::tr($msg->user_to) !!}</strong>@if($msg->isNetmail()) (<strong class="highlight">{{ $msg->tftn }}</strong>) @endif
|
||||
TO: <strong class="highlight">{!! Message::tr($msg->to) !!}</strong>@if($msg instanceof Netmail) (<strong class="highlight">{{ $msg->tftn->ftn }}</strong>) @endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row pb-2">
|
||||
<div class="col-8">
|
||||
SUBJECT: <strong class="highlight">{!! \App\Classes\FTN\Message::tr($msg->subject) !!}</strong>
|
||||
SUBJECT: <strong class="highlight">{!! Message::tr($msg->subject) !!}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row pb-2">
|
||||
<div class="col-8">
|
||||
<div class="pad pb-0">
|
||||
<pre class="highlight">{!! \App\Classes\FTN\Message::tr($msg->message).sprintf("\r * Origin: %s",$msg->origin) !!}</pre>
|
||||
<pre class="highlight">{!! Message::tr($msg->msg_src) !!}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($msg->tagline)
|
||||
@if($msg instanceof Netmail)
|
||||
<div class="row pb-2">
|
||||
<div class="col-8">
|
||||
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>
|
||||
VIA: <br><strong class="highlight">{!! $msg->path->join('</strong> -> <strong class="highlight">') !!}</strong>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
@ -159,7 +156,7 @@
|
||||
<div class="row pb-2">
|
||||
<div class="col-8">
|
||||
<strong>KLUDGES:</strong> <br>
|
||||
@foreach ($msg->kludge->sort(function($v,$k) { return $k; })->reverse() as $k => $v)
|
||||
@foreach ($msg->kludges->sort(function($v,$k) { return $k; })->reverse() as $k => $v)
|
||||
<strong class="highlight">{{ $k }}</strong> {{ $v }}<br>
|
||||
@endforeach
|
||||
</div>
|
||||
|
@ -1,10 +1,11 @@
|
||||
@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 \App\Models\Netmail)(<strong class="highlight">{{ $msg->tftn->ftn }}</strong>)@endif
|
||||
TO: <strong class="highlight">{!! Message::tr($msg->to) !!}</strong> @if ($msg instanceof 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>
|
||||
@ -16,19 +17,30 @@ use App\Classes\FTN\Message;
|
||||
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=\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
|
||||
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
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row pt-1 pb-2">
|
||||
<div class="col-4">
|
||||
SUBJECT: <strong class="highlight">{!! Message::tr($msg->subject) !!}</strong>
|
||||
@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>
|
||||
@if ($msg instanceof \App\Models\Echomail)
|
||||
<div class="col-4">
|
||||
@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>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="row pt-1 pb-2">
|
||||
<div class="col-8">
|
||||
SUBJECT: <strong class="highlight">{!! Message::tr($msg->subject) !!}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row pb-2">
|
||||
@ -39,63 +51,63 @@ use App\Classes\FTN\Message;
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($msg instanceof \App\Models\Echomail)
|
||||
<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)
|
||||
<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->join(',') }})]</small>
|
||||
@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>
|
||||
@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>
|
||||
<!-- @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())
|
||||
<br><small>[<strong>NOTE</strong>: Some path values couldnt be identified - ({{ $msg->rogue_path->join(',') }})]</small>
|
||||
@endif
|
||||
</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 \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>
|
||||
</div>
|
||||
|
||||
<div class="row pb-2">
|
||||
<div class="col-8">
|
||||
<div class="row pb-2">
|
||||
<div class="col-8">
|
||||
@if($msg instanceof Netmail)
|
||||
RECEIVED:<br>
|
||||
@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 \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>
|
||||
@foreach ($msg->path 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>
|
||||
<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>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@section('page-scripts')
|
||||
<script type="text/javascript" src="{{ asset('ansilove/ansilove.js') }}"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
var msg = new Uint8Array({!! json_encode(array_values(unpack('C*',str_replace("\r","\n",$msg->msg)))) !!});
|
||||
var msg = new Uint8Array({!! json_encode(array_values(unpack('C*',str_replace("\r","\n",$msg->content)))) !!});
|
||||
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}
|
||||
|
Loading…
Reference in New Issue
Block a user