More complete rework of packet parsing and packet generation with 29710c

This commit is contained in:
Deon George 2024-05-19 23:28:45 +10:00
parent 46f52dd56d
commit f279d85b08
43 changed files with 412 additions and 291 deletions

View File

@ -12,9 +12,9 @@ use Illuminate\Validation\Validator as ValidatorResult;
use App\Classes\FTN as FTNBase; use App\Classes\FTN as FTNBase;
use App\Exceptions\InvalidPacketException; use App\Exceptions\InvalidPacketException;
use App\Models\{Address,Domain,Echomail,Netmail,Zone}; use App\Models\{Address,Domain,Echomail,Netmail,Setup,Zone};
use App\Rules\{TwoByteInteger,TwoByteIntegerWithZero}; use App\Rules\{TwoByteInteger,TwoByteIntegerWithZero};
use App\Traits\{EncodeUTF8,ObjectIssetFix}; use App\Traits\ObjectIssetFix;
/** /**
* Class Message * Class Message
@ -91,6 +91,7 @@ class Message extends FTNBase
private array $header; // Message Header private array $header; // Message Header
private int $tzutc = 0; // TZUTC that needs to be converted to be used by Carbon @see self::kludges private int $tzutc = 0; // TZUTC that needs to be converted to be used by Carbon @see self::kludges
private Echomail|Netmail $mo; // The object storing this packet message private Echomail|Netmail $mo; // The object storing this packet message
private Address $us; // Our address for this message
/** @deprecated Not sure why this is needed? */ /** @deprecated Not sure why this is needed? */
public bool $packed = FALSE; // Has the message been packed successfully public bool $packed = FALSE; // Has the message been packed successfully
@ -156,6 +157,7 @@ class Message extends FTNBase
{ {
$oo = new self($o->fftn->zone); $oo = new self($o->fftn->zone);
$oo->mo = $o; $oo->mo = $o;
$oo->us = our_address($o->tftn);
return $oo; return $oo;
} }
@ -507,6 +509,8 @@ class Message extends FTNBase
*/ */
public function __toString(): string public function __toString(): string
{ {
$s = Setup::findOrFail(config('app.id'));
$return = pack(collect(self::HEADER)->pluck(1)->join(''), $return = pack(collect(self::HEADER)->pluck(1)->join(''),
$this->mo->fftn->node_id, // Originating Node $this->mo->fftn->node_id, // Originating Node
$this->mo->tftn->node_id, // Destination Node $this->mo->tftn->node_id, // Destination Node
@ -521,22 +525,26 @@ class Message extends FTNBase
$return .= $this->mo->from."\00"; $return .= $this->mo->from."\00";
$return .= $this->mo->subject."\00"; $return .= $this->mo->subject."\00";
if (($this->mo instanceof Netmail) && $this->mo->isFlagSet(self::FLAG_LOCAL)) {
// If there isnt an INTL kludge, we'll add it
if (! $this->mo->kludges->has('INTL'))
$this->mo->kludges->put('INTL',sprintf('%s %s',$this->mo->tftn->ftn3d,$this->mo->fftn->ftn3d));
if ((! $this->mo->kludges->has('FMPT')) && $this->mo->fftn->point_id)
$this->mo->kludges->put('FMPT',$this->mo->fftn->point_id);
if ((! $this->mo->kludges->has('TOPT')) && $this->mo->tftn->point_id)
$this->mo->kludges->put('TOPT',$this->mo->tftn->point_id);
}
if ($this->mo->isFlagSet(self::FLAG_LOCAL))
$this->mo->kludges->put('PID',sprintf('clrghouz %s',$s->version));
else
$this->mo->kludges->put('TID',sprintf('clrghouz %s',$s->version));
if ($this->mo instanceof Echomail) if ($this->mo instanceof Echomail)
$return .= sprintf("AREA:%s\r",strtoupper($this->mo->echoarea->name)); $return .= sprintf("AREA:%s\r",strtoupper($this->mo->echoarea->name));
if ($this->mo instanceof Netmail) {
// @todo If the message is local, then our kludges are not in the msg itself, we'll add them
if ($this->isFlagSet(self::FLAG_LOCAL))
Log::debug(sprintf('%s:^ Local message, we may need to set our INTL/FMPT/TOPT',self::LOGKEY));
$return .= sprintf("\01INTL %s\r",$this->mo->kludges->get('INTL'));
if ($this->mo->kludges->has('FMPT'))
$return .= sprintf("\01FMPT %d\r",$this->mo->kludges->get('FMPT'));
if ($this->mo->kludges->has('TOPT'))
$return .= sprintf("\01TOPT %d\r",$this->mo->kludges->get('TOPT'));
}
// Add some kludges // Add some kludges
$return .= sprintf("\01TZUTC: %s\r",str_replace('+','',$this->mo->date->getOffsetString(''))); $return .= sprintf("\01TZUTC: %s\r",str_replace('+','',$this->mo->date->getOffsetString('')));
@ -551,11 +559,15 @@ class Message extends FTNBase
$return .= $this->mo->content; $return .= $this->mo->content;
if ($this->mo instanceof Netmail) { if ($this->mo instanceof Netmail) {
// @todo automatically include our address in the path
foreach ($this->mo->path as $ao) foreach ($this->mo->path as $ao)
$return .= sprintf("\x01Via %s\r",$this->mo->via($ao)); $return .= sprintf("\x01Via %s\r",$this->mo->via($ao));
// Add our address
$return .= sprintf("\x01Via %s @%s.UTC %s (%04X)\r",
$this->us->ftn3d,
Carbon::now()->format('Ymd.His'),
Setup::PRODUCT_NAME,Setup::PRODUCT_ID);
} else { } else {
// FTS-0004.001/FSC-0068.001 The message SEEN-BY lines // FTS-0004.001/FSC-0068.001 The message SEEN-BY lines
// FTS-0004.001/FSC-0068.001 The message PATH lines // FTS-0004.001/FSC-0068.001 The message PATH lines

View File

@ -50,6 +50,20 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
protected Collection $messages; // Messages in the Packet protected Collection $messages; // Messages in the Packet
public Collection $errors; // Messages that fail validation public Collection $errors; // Messages that fail validation
protected int $index; // Our array index protected int $index; // Our array index
protected $pass_p = NULL; // Overwrite the packet password (when packing messages)
/* ABSTRACT */
/**
* This function is intended to be implemented in child classes to test if the packet
* is defined by the child object
*
* @see self::PACKET_TYPES
* @param string $header
* @return bool
*/
abstract public static function is_type(string $header): bool;
abstract protected function header(): string;
/* STATIC */ /* STATIC */
@ -63,16 +77,6 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
return collect(static::HEADER)->sum(function($item) { return Arr::get($item,2); }); return collect(static::HEADER)->sum(function($item) { return Arr::get($item,2); });
} }
/**
* This function is intended to be implemented in child classes to test if the packet
* is defined by the child object
*
* @see self::PACKET_TYPES
* @param string $header
* @return bool
*/
abstract public static function is_type(string $header): bool;
/** /**
* Process a packet file * Process a packet file
* *
@ -261,6 +265,9 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
case 'name': case 'name':
return $this->{$key} ?: sprintf('%08x',timew()); return $this->{$key} ?: sprintf('%08x',timew());
case 'messages':
return $this->{$key};
default: default:
throw new \Exception('Unknown key: '.$key); throw new \Exception('Unknown key: '.$key);
} }
@ -274,6 +281,12 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
*/ */
public function __toString(): string public function __toString(): string
{ {
if (empty($this->messages))
throw new InvalidPacketException('Refusing to make an empty packet');
if (empty($this->tftn_p) || empty($this->fftn_p))
throw new InvalidPacketException('Cannot generate a packet without a destination address');
$return = $this->header(); $return = $this->header();
foreach ($this->messages as $o) foreach ($this->messages as $o)
@ -379,18 +392,18 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
/** /**
* Generate a packet * Generate a packet
* *
* @param Collection $msgs
* @return string * @return string
* @throws InvalidPacketException
*/ */
public function generate(Collection $msgs): string public function generate(): string
{
return (string)$this;
}
public function mail(Collection $msgs): self
{ {
$this->messages = $msgs; $this->messages = $msgs;
if (empty($this->tftn_p) || empty($this->fftn_p)) return $this;
throw new InvalidPacketException('Cannot generate a packet without a destination address');
return (string)$this;
} }
/** /**
@ -500,6 +513,20 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
$this->messages->push($msg); $this->messages->push($msg);
} }
/**
* Overwrite the packet password
*
* @param string|null $password
* @return self
*/
public function password(string $password=NULL): self
{
if ($password && (strlen($password) < 9))
$this->pass_p = $password;
return $this;
}
/** @deprecated Is this used? */ /** @deprecated Is this used? */
public function pluck(string $key): Collection public function pluck(string $key): Collection
{ {

View File

@ -59,11 +59,10 @@ final class FSC48 extends Packet
/** /**
* Create our message packet header * Create our message packet header
* // @todo add the ability to overwrite the password
*/ */
protected function header(): string protected function header(): string
{ {
$oldest = $this->messages->sortBy(function($item) { return $item->datetime; })->last(); $oldest = $this->messages->sortBy('datetime')->last();
try { try {
return pack(collect(self::HEADER)->pluck(1)->join(''), return pack(collect(self::HEADER)->pluck(1)->join(''),
@ -77,14 +76,14 @@ final class FSC48 extends Packet
$oldest->datetime->format('s'), // Second $oldest->datetime->format('s'), // Second
0, // Baud 0, // Baud
2, // Packet Version (should be 2) 2, // Packet Version (should be 2)
$this->fftn_p->point_id ? 0xffff : $this->fftn_p->node_id, // Orig Net (0xFFFF when OrigPoint != 0) $this->fftn_p->point_id ? 0xffff : $this->fftn_p->host_id, // Orig Net (0xFFFF when OrigPoint != 0)
$this->tftn_p->host_id, // Dest Net $this->tftn_p->host_id, // Dest Net
(Setup::PRODUCT_ID & 0xff), // Product Code Lo (Setup::PRODUCT_ID & 0xff), // Product Code Lo
Setup::PRODUCT_VERSION_MAJ, // Product Version Major Setup::PRODUCT_VERSION_MAJ, // Product Version Major
$this->tftn_p->session('pktpass'), // Packet Password $this->pass_p ?: $this->tftn_p->session('pktpass'), // Packet Password
$this->fftn_p->zone->zone_id, // Orig Zone $this->fftn_p->zone->zone_id, // Orig Zone
$this->tftn_p->zone->zone_id, // Dest Zone $this->tftn_p->zone->zone_id, // Dest Zone
$this->fftn_p->point_id ? $this->fftn_p->node_id : 0x00, // Aux Net $this->fftn_p->point_id ? $this->fftn_p->host_id : 0x00, // Aux Net
1<<0, // fsc-0039.004 (copy of 0x2c) 1<<0, // fsc-0039.004 (copy of 0x2c)
((Setup::PRODUCT_ID >> 8) & 0xff), // Product Code Hi ((Setup::PRODUCT_ID >> 8) & 0xff), // Product Code Hi
Setup::PRODUCT_VERSION_MIN, // Product Version Minor Setup::PRODUCT_VERSION_MIN, // Product Version Minor

View File

@ -9,11 +9,9 @@ use App\Models\{Echoarea,Echomail,Netmail};
*/ */
abstract class Process abstract class Process
{ {
public static function canProcess(string $echoarea): bool public static function canProcess(Echoarea $eao): bool
{ {
$eao = Echoarea::where('name',$echoarea)->single(); return $eao->automsgs;
return $eao && $eao->automsgs;
} }
/** /**

View File

@ -27,9 +27,9 @@ final class Test extends Process
|| (! in_array(strtolower($mo->subject),self::testing))) || (! in_array(strtolower($mo->subject),self::testing)))
return FALSE; return FALSE;
Log::info(sprintf('%s:- Processing TEST message from (%s) [%s] in [%s]',self::LOGKEY,$msg->user_from,$msg->fftn,$msg->echoarea)); Log::info(sprintf('%s:- Processing TEST message from (%s) [%s] in [%s]',self::LOGKEY,$mo->from,$mo->fftn->ftn,$mo->echoarea->name));
Notification::route('echomail',$mo->echoarea)->notify(new TestNotification($msg)); Notification::route('echomail',$mo->echoarea->withoutRelations())->notify(new TestNotification($mo));
return TRUE; return TRUE;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1009,6 +1009,9 @@ final class Binkp extends BaseProtocol
$this->send->close(TRUE,$this->node); $this->send->close(TRUE,$this->node);
} }
} else {
Log::error(sprintf('%s:! M_got[skip] not for our file? [%s]',self::LOGKEY,$buf));
} }
} else { } else {

View File

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

View File

@ -99,8 +99,8 @@ class PacketInfo extends Command
foreach ($pkt->errors as $msg) { foreach ($pkt->errors as $msg) {
$this->error(sprintf('- Date: %s',$msg->date)); $this->error(sprintf('- Date: %s',$msg->date));
$this->error(sprintf(' - FLAGS: %s',$msg->flags()->filter()->keys()->join(', '))); $this->error(sprintf(' - FLAGS: %s',$msg->flags()->filter()->keys()->join(', ')));
$this->error(sprintf(' - From: %s (%s)',$msg->user_from,$msg->fftn)); $this->error(sprintf(' - From: %s (%s)',$msg->from,$msg->fftn));
$this->error(sprintf(' - To: %s (%s)',$msg->user_to,$msg->tftn)); $this->error(sprintf(' - To: %s (%s)',$msg->to,$msg->tftn));
$this->error(sprintf(' - Subject: %s',$msg->subject)); $this->error(sprintf(' - Subject: %s',$msg->subject));
foreach ($msg->errors->errors()->all() as $error) foreach ($msg->errors->errors()->all() as $error)

View File

@ -10,6 +10,33 @@ use App\Classes\FTN\Packet;
use App\Jobs\PacketProcess as Job; use App\Jobs\PacketProcess as Job;
use App\Models\Address; use App\Models\Address;
/**
* Things to test
* + Packet
* - Sender doesnt exist (try and send a bounce message in the same session)
* - Sender defined in DB by not ours
* - Sender has wrong password
* - Packet too old
*
* + Echomail
* - Area doesnt exist (to uplink)
* - Sender not subscribed (to uplink)
* - Sender cannot post (to uplink)
* - Sender has wrong address for echorea domain (to uplink)
* - Test message in echoarea
* - Echomail from address doesnt match packet envelope (to uplink)
* - Echomail too old (to uplink)
* - Rescanned dont generate notifications
* - Rescanned dont trigger bots
* - Some Notifications to an uplink should go to the admin instead?
*
* + Netmail
* - To hub, and user not defined (reject)
* - To hub, but user redirect (redirected)
* - To areafix (processed)
* - To ping (respond)
* - With trace turned on (respond)
*/
class PacketProcess extends Command class PacketProcess extends Command
{ {
/** /**
@ -38,25 +65,25 @@ class PacketProcess extends Command
*/ */
public function handle() public function handle()
{ {
$fs = Storage::disk(config('fido.local_disk')); //$fs = Storage::disk(config('fido.local_disk'));
$rel_name = sprintf('%s/%s',config('fido.dir'),$this->argument('file')); $rel_name = sprintf('%s/%s',config('fido.dir'),$this->argument('file'));
$f = new File($fs->path($rel_name)); //$f = new File($fs->path($rel_name));
$m = []; $m = [];
if ($this->argument('ftn')) { if ($this->argument('ftn')) {
$a = Address::findFTN($this->argument('ftn')); $ao = Address::findFTN($this->argument('ftn'));
} elseif (preg_match(sprintf('/^%s\.(.{3})$/',Packet::regex),$this->argument('file'),$m)) { } elseif (preg_match(sprintf('/^%s\.(.{3})$/',Packet::regex),$this->argument('file'),$m)) {
$a = Address::findOrFail(hexdec($m[1])); $ao = Address::findOrFail(hexdec($m[1]));
} else { } else {
$this->error('Unable to determine sender FTN address'); $this->error('Unable to determine sender FTN address');
exit(1); exit(1);
} }
$x = Job::dispatchSync($rel_name,$a->zone->domain,$this->option('dontqueue')); Job::dispatchSync($rel_name,$ao->zone->domain,$this->option('dontqueue'));
dd(['job completed'=>$x]); return Command::SUCCESS;
} }
} }

View File

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

View File

@ -16,7 +16,7 @@ use Illuminate\Support\Facades\Notification;
use Illuminate\Support\ViewErrorBag; use Illuminate\Support\ViewErrorBag;
use App\Classes\FTN\Message; use App\Classes\FTN\Message;
use App\Http\Requests\{AddressMerge,AreafixRequest,SystemRegister,SystemSessionRequest}; use App\Http\Requests\{AddressMerge,AreafixRequest,SystemEchoareaRequest,SystemRegister,SystemSessionRequest};
use App\Jobs\AddressPoll; use App\Jobs\AddressPoll;
use App\Models\{Address,Echoarea,Echomail,Filearea,Netmail,Setup,System,Zone}; use App\Models\{Address,Echoarea,Echomail,Filearea,Netmail,Setup,System,Zone};
use App\Notifications\Netmails\AddressLink; use App\Notifications\Netmails\AddressLink;
@ -694,30 +694,20 @@ class SystemController extends Controller
/** /**
* Update the systems echoareas * Update the systems echoareas
* *
* @param Request $request * @param SystemEchoareaRequest $request
* @param System $o * @param System $o
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
*/ */
public function echoareas(Request $request,System $o) public function echoareas(SystemEchoareaRequest $request,System $o)
{ {
$ao = $o->addresses->firstWhere('id',$request->address_id); $ao = $o->addresses->firstWhere('id',$request->address_id);
if (($request->method() === 'POST') && $request->post()) { if (($request->method() === 'POST') && $request->validated()) {
session()->flash('accordion','echoarea'); $ao->echoareas()->syncWithPivotValues($request->validated('id',[]),['subscribed'=>Carbon::now()]);
if ($ao->trashed() && collect($request->get('id'))->diff($ao->echoareas->pluck('id'))->count())
return redirect()->back()->withErrors(sprintf('Address [%s] has been deleted, cannot add additional echos',$ao->ftn3d));
// Ensure we have session details for this address.
if (! $ao->session('sespass'))
return redirect()->back()->withErrors('System doesnt belong to this network');
$ao->echoareas()->syncWithPivotValues($request->get('id',[]),['subscribed'=>Carbon::now()]);
return redirect()->back()->with('success','Echoareas updated'); return redirect()->back()->with('success','Echoareas updated');
} }
// @todo Allow a NC/RC/ZC to override
$eo = Echoarea::active() $eo = Echoarea::active()
->where('domain_id',$ao->zone->domain_id) ->where('domain_id',$ao->zone->domain_id)
->where(function($query) use ($ao) { ->where(function($query) use ($ao) {
@ -869,7 +859,7 @@ class SystemController extends Controller
*/ */
public function session_del(System $o,Zone $zo) public function session_del(System $o,Zone $zo)
{ {
$this->authorize('admin',$zo); $this->authorize('update_nn',$o);
session()->flash('accordion','session'); session()->flash('accordion','session');
$o->sessions()->detach($zo); $o->sessions()->detach($zo);

View File

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

View File

@ -8,13 +8,12 @@ use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification; use Illuminate\Support\Facades\Notification;
use App\Classes\FTN\Message; use App\Classes\FTN\Message;
use App\Models\{Echoarea,Echomail,Netmail,User}; use App\Models\{Echomail,Netmail,User};
use App\Notifications\Netmails\{EchoareaNotExist,EchoareaNotSubscribed,EchoareaNoWrite,NetmailForward,Reject}; use App\Notifications\Netmails\{EchoareaNotExist,EchoareaNotSubscribed,EchoareaNoWrite,NetmailForward,NetmailHubNoUser};
use App\Traits\ParseAddresses; use App\Traits\ParseAddresses;
class MessageProcess implements ShouldQueue class MessageProcess implements ShouldQueue
@ -174,7 +173,7 @@ class MessageProcess implements ShouldQueue
if (! $processed) { if (! $processed) {
Log::alert(sprintf('%s:! Netmail to the Hub from (%s) [%s] but no users here.',self::LOGKEY,$this->mo->from,$this->mo->fftn->ftn)); Log::alert(sprintf('%s:! Netmail to the Hub from (%s) [%s] but no users here.',self::LOGKEY,$this->mo->from,$this->mo->fftn->ftn));
Notification::route('netmail',$this->mo->fftn)->notify(new Reject($this->mo)); Notification::route('netmail',$this->mo->fftn)->notify(new NetmailHubNoUser($this->mo));
} }
// If in transit, store for collection // If in transit, store for collection
@ -269,11 +268,11 @@ class MessageProcess implements ShouldQueue
// @todo if we have an export for any of the seenby addresses, remove it // @todo if we have an export for any of the seenby addresses, remove it
$seenby = $this->parseAddresses('seenby',$this->mo->seenby,$sender->zone,$o->rogue_seenby); //$seenby = $this->parseAddresses('seenby',$this->mo->seenby,$sender->zone,$o->rogue_seenby);
$this->mo->seenby()->syncWithoutDetaching($seenby); //$this->mo->seenby()->syncWithoutDetaching($seenby);
// In case our rogue_seenby changed // In case our rogue_seenby changed
$this->mo->save(); //$this->mo->save();
return; return;
} }
@ -294,14 +293,6 @@ class MessageProcess implements ShouldQueue
)); ));
} }
// If the node is not subscribed
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 (! $rescanned)
Notification::route('netmail',$sender)->notify(new EchoareaNotSubscribed($this->mo));
}
// Can the system send messages to this area? // Can the system send messages to this area?
if (! $this->mo->echoarea->can_write($sender->security)) { 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)); 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));
@ -311,6 +302,14 @@ class MessageProcess implements ShouldQueue
return; return;
} }
// If the node is not subscribed, we'll accept it, but let them know
if (! $sender->echoareas->contains($this->mo->echoarea)) {
Log::alert(sprintf('%s:! FTN [%s] is not subscribed to [%s] for [%s].',self::LOGKEY,$sender->ftn,$this->mo->echoarea->name,$this->mo->msgid));
if (! $rescanned)
Notification::route('netmail',$sender)->notify(new EchoareaNotSubscribed($this->mo));
}
// We know about this area, store it // We know about this area, store it
$this->mo->save(); $this->mo->save();

View File

@ -32,7 +32,7 @@ class PacketProcess implements ShouldQueue
private bool $interactive; private bool $interactive;
private bool $nobot; private bool $nobot;
public function __construct(string $filename,Domain $do,bool $interactive=FALSE,Carbon $rcvd_time=NULL,bool $nobot=FALSE) public function __construct(string $filename,Domain $do,bool $interactive=TRUE,Carbon $rcvd_time=NULL,bool $nobot=FALSE)
{ {
$this->filename = $filename; $this->filename = $filename;
$this->do = $do; $this->do = $do;
@ -98,7 +98,7 @@ class PacketProcess implements ShouldQueue
if (strtoupper($pkt->fftn->session('pktpass')) !== strtoupper($pkt->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)); 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)); Notification::route('netmail',$pkt->fftn)->notify(new PacketPasswordInvalid($pkt->password,$f->pktName()));
break; break;
} }
@ -126,6 +126,9 @@ class PacketProcess implements ShouldQueue
$msg->set_pkt = $f->pktName(); $msg->set_pkt = $f->pktName();
$msg->set_recvtime = $this->rcvd_time; $msg->set_recvtime = $this->rcvd_time;
if ($queue || (! $this->interactive))
Log::info(sprintf('%s:! Message [%s] will be sent to the queue to process',self::LOGKEY,$msg->msgid));
try { try {
// Dispatch job. // Dispatch job.
if ($queue || (! $this->interactive)) if ($queue || (! $this->interactive))

View File

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

View File

@ -92,7 +92,6 @@ final class Netmail extends Model implements Packet
$nodes = collect(); $nodes = collect();
// Parse PATH // 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] // <FTN Address> @YYYYMMDD.HHMMSS[.Precise][.Time Zone] <Program Name> <Version> [Serial Number]
if ($model->set->has('set_path')) { if ($model->set->has('set_path')) {
foreach ($model->set->get('set_path') as $line) { foreach ($model->set->get('set_path') as $line) {
@ -123,8 +122,8 @@ final class Netmail extends Model implements Packet
} }
// If there are no details (Mystic), we'll create a blank // If there are no details (Mystic), we'll create a blank
} else { } elseif ($model->set->has('set_sender')) {
$nodes->push(['node'=>$model->set->get('set_sender'),'datetime'=>Carbon::now(),'program'=>sprintf('%s (%04X)',Setup::PRODUCT_NAME,Setup::PRODUCT_ID)]); $nodes->push(['node'=>$model->set->get('set_sender'),'datetime'=>Carbon::now(),'program'=>'Unknown']);
} }
// Save the Path // Save the Path

View File

@ -253,14 +253,18 @@ class System extends Model
* Return the packet that this system uses * Return the packet that this system uses
* *
* @param Address $ao * @param Address $ao
* @param string|null $password
* @return Packet * @return Packet
*/ */
public function packet(Address $ao): Packet public function packet(Address $ao,string $password=NULL): Packet
{ {
// @todo Check that the address is one of the system's addresses // @todo Check that the address is one of the system's addresses
return (new (collect(Packet::PACKET_TYPES) return
->get($this->pkt_type ?: config('fido.packet_default'))))->for($ao); (new (collect(Packet::PACKET_TYPES)
->get($this->pkt_type ?: config('fido.packet_default'))))
->for($ao)
->password($password);
} }
public function poll(): ?Job public function poll(): ?Job

View File

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

View File

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

View File

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

View File

@ -52,14 +52,13 @@ abstract class Netmails extends Notification //implements ShouldQueue
$ao = $notifiable->routeNotificationFor(static::via); $ao = $notifiable->routeNotificationFor(static::via);
$o = new Netmail; $o = new Netmail;
$o->set_sender = our_address($ao);
$o->to = $ao->system->sysop; $o->to = $ao->system->sysop;
$o->from = Setup::PRODUCT_NAME; $o->from = Setup::PRODUCT_NAME;
$o->datetime = Carbon::now(); $o->datetime = Carbon::now();
$o->tzoffset = $o->datetime->utcOffset(); $o->tzoffset = $o->datetime->utcOffset();
$o->fftn_id = $o->set->get('set_sender')->id; $o->fftn_id = our_address($ao)->id;
$o->tftn_id = $ao->id; $o->tftn_id = $ao->id;
$o->flags = (Message::FLAG_LOCAL|Message::FLAG_PRIVATE); $o->flags = (Message::FLAG_LOCAL|Message::FLAG_PRIVATE);
$o->cost = 0; $o->cost = 0;

View File

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

View File

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

View File

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

View File

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

View File

@ -42,7 +42,7 @@ class EchoareaNotExist extends Netmails
$o = $this->setupNetmail($notifiable); $o = $this->setupNetmail($notifiable);
$ao = $notifiable->routeNotificationFor(static::via); $ao = $notifiable->routeNotificationFor(static::via);
Log::info(sprintf('%s:+ Creating ECHOMAIL NOT EXIST netmail to [%s]',self::LOGKEY,$ao->ftn)); Log::info(sprintf('%s:+ Creating ECHOAREA NOT EXIST netmail to [%s]',self::LOGKEY,$ao->ftn));
$o->subject = 'Echoarea doesnt exist - '.$this->mo->set->get('set_echoarea'); $o->subject = 'Echoarea doesnt exist - '.$this->mo->set->get('set_echoarea');
@ -58,7 +58,7 @@ class EchoareaNotExist extends Netmails
) )
); );
$msg->addText("It appears that the echoarea that this message is for doesnt exist, so the message from your system was rejected.\r\r"); $msg->addText(sprintf("It appears that the echoarea [%s] that this message is for doesnt exist, so the message from your system was rejected.\r\r",$this->mo->set->get('set_echoarea')));
$msg->addText("Please contact the ZC if you think this is a mistake.\r\r"); $msg->addText("Please contact the ZC if you think this is a mistake.\r\r");
$msg->addText($this->message_path($this->mo)); $msg->addText($this->message_path($this->mo));

View File

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

View File

@ -7,7 +7,7 @@ use Illuminate\Support\Facades\Log;
use App\Classes\FTN\Message; use App\Classes\FTN\Message;
use App\Notifications\Netmails; use App\Notifications\Netmails;
use App\Models\{Netmail,System}; use App\Models\Netmail;
use App\Traits\{MessagePath,PageTemplate}; use App\Traits\{MessagePath,PageTemplate};
class EchomailBadAddress extends Netmails class EchomailBadAddress extends Netmails
@ -52,7 +52,7 @@ class EchomailBadAddress extends Netmails
$msg->addText( $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", 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->msgid,
$this->mo->user_to, $this->mo->to,
Carbon::now()->utc()->toDateTimeString(), Carbon::now()->utc()->toDateTimeString(),
$this->mo->date->utc()->toDateTimeString(), $this->mo->date->utc()->toDateTimeString(),
) )

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -70,26 +70,38 @@ trait MessageAttributes
public function getOriginAttribute(string $val=NULL): ?string public function getOriginAttribute(string $val=NULL): ?string
{ {
if ($this->exists && (! $val))
return $val;
// If $val is not set, then it may be an unsaved object // If $val is not set, then it may be an unsaved object
return ((! $this->exists) && $this->set->has('set_origin')) return sprintf(' * Origin: %s',
? sprintf(' * Origin: %s',$this->set->get('set_origin')) ((! $this->exists) && $this->set->has('set_origin'))
: $val; ? $this->set->get('set_origin')
: $val);
} }
public function getTaglineAttribute(string $val=NULL): ?string public function getTaglineAttribute(string $val=NULL): ?string
{ {
if ($this->exists && (! $val))
return $val;
// If $val is not set, then it may be an unsaved object // If $val is not set, then it may be an unsaved object
return ((! $this->exists) && $this->set->has('set_tagline')) return sprintf('... %s',
? sprintf('... %s',$this->set->get('set_tagline')) ((! $this->exists) && $this->set->has('set_tagline'))
: $val; ? $this->set->get('set_tagline')
: $val);
} }
public function getTearlineAttribute(string $val=NULL): ?string public function getTearlineAttribute(string $val=NULL): ?string
{ {
if ($this->exists && (! $val))
return $val;
// If $val is not set, then it may be an unsaved object // If $val is not set, then it may be an unsaved object
return ((! $this->exists) && $this->set->has('set_tearline')) return sprintf('--- %s',
? sprintf('--- %s',$this->set->get('set_tearline')) ((! $this->exists) && $this->set->has('set_tearline'))
: $val; ? $this->set->get('set_tearline')
: $val);
} }
/* METHODS */ /* METHODS */
@ -141,10 +153,11 @@ trait MessageAttributes
'receipt' => $this->isFlagSet(Message::FLAG_ISRETRECEIPT), 'receipt' => $this->isFlagSet(Message::FLAG_ISRETRECEIPT),
'audit' => $this->isFlagSet(Message::FLAG_AUDITREQ), 'audit' => $this->isFlagSet(Message::FLAG_AUDITREQ),
'fileupdate' => $this->isFlagSet(Message::FLAG_FILEUPDATEREQ), 'fileupdate' => $this->isFlagSet(Message::FLAG_FILEUPDATEREQ),
'pktpasswd' => $this->isFlagSet(Message::FLAG_PKTPASSWD),
])->filter(); ])->filter();
} }
private function isFlagSet($flag): bool public function isFlagSet($flag): bool
{ {
return ($this->flags & $flag); return ($this->flags & $flag);
} }

View File

@ -27,20 +27,18 @@ trait MessagePath
$reply .= "+--[ PATH ]-------------------------------------------+\r"; $reply .= "+--[ PATH ]-------------------------------------------+\r";
if ($mo->path->count())
if ($mo instanceof Netmail) { if ($mo instanceof Netmail) {
if ($mo->via->count()) foreach ($mo->path as $o)
foreach ($mo->via as $ao) $reply .= sprintf("VIA: %s\r",$mo->via($o));
$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";
} else { } else {
if ($mo->path->count()) foreach ($mo->path as $o)
foreach ($mo->path as $via) $reply .= sprintf("VIA: %s\r",$o->ftn);
$reply .= sprintf("VIA: %s\r",$via); }
else else
$reply .= "No path information? This would be normal if this message came directly to the hub\r"; $reply .= "No path information? This would be normal if this message came directly to the hub\r";
}
$reply .= "+--[ END MESSAGE ]------------------------------------+\r\r"; $reply .= "+--[ END MESSAGE ]------------------------------------+\r\r";

View File

@ -97,6 +97,17 @@ use App\Models\{Echomail,Netmail};
</div> </div>
</div> </div>
@if($msg instanceof Netmail)
<div class="col-8">
SENT:
@if($msg->sent_pkt && $msg->sent_at)
<strong class="highlight">{{ $msg->sent_at }}</strong> (<strong class="highlight">{{ $msg->sent_pkt }}.pkt</strong>)
@else
<strong class="highlight">NOT SENT</strong>
@endif
</div>
@endif
@section('page-scripts') @section('page-scripts')
<script type="text/javascript" src="{{ asset('ansilove/ansilove.js') }}"></script> <script type="text/javascript" src="{{ asset('ansilove/ansilove.js') }}"></script>
@ -114,5 +125,4 @@ use App\Models\{Echomail,Netmail};
); );
}); });
</script> </script>
@append @append