2021-06-29 10:43:29 +00:00
< ? php
namespace App\Classes\FTN ;
2021-07-15 14:54:23 +00:00
use Carbon\Carbon ;
2022-11-05 08:03:33 +00:00
use Carbon\Exceptions\InvalidFormatException ;
2021-06-29 10:43:29 +00:00
use Illuminate\Support\Arr ;
use Illuminate\Support\Collection ;
use Illuminate\Support\Facades\Log ;
use Illuminate\Support\Facades\Validator ;
use Illuminate\Validation\Validator as ValidatorResult ;
use App\Classes\FTN as FTNBase ;
2023-09-20 10:29:23 +00:00
use App\Models\ { Address , Domain , Zone };
2022-01-15 02:06:15 +00:00
use App\Rules\ { TwoByteInteger , TwoByteIntegerWithZero };
2023-11-27 04:56:28 +00:00
use App\Traits\ { EncodeUTF8 , ObjectIssetFix };
2021-06-29 10:43:29 +00:00
/**
* Class Message
2023-06-22 07:36:22 +00:00
* Represents the structure of a message in a packet
2021-06-29 10:43:29 +00:00
* NOTE : FTN Echomail Messages are ZONE agnostic .
*
* @ package App\Classes
*/
class Message extends FTNBase
{
2023-11-27 04:56:28 +00:00
use EncodeUTF8 , ObjectIssetFix ;
2021-08-11 13:45:30 +00:00
2021-09-12 13:06:17 +00:00
private const LOGKEY = 'FM-' ;
2021-07-15 14:54:23 +00:00
private const cast_utf8 = [
2021-12-01 11:45:51 +00:00
'user_to' ,
'user_from' ,
2021-08-24 14:15:09 +00:00
'subject' ,
2021-07-15 14:54:23 +00:00
'message' ,
2021-07-30 14:35:52 +00:00
'message_src' ,
2021-09-12 13:06:17 +00:00
'origin' ,
'tearline' ,
'tagline' ,
2023-01-11 02:08:59 +00:00
'dump' ,
2021-07-15 14:54:23 +00:00
];
2021-06-29 10:43:29 +00:00
// Single value kludge items
private array $_kludge = [
'chrs' => 'CHRS: ' ,
'charset' => 'CHARSET: ' ,
'codepage' => 'CODEPAGE: ' ,
2022-01-22 12:08:46 +00:00
'dbid' => 'DBID: ' ,
2021-06-29 10:43:29 +00:00
'pid' => 'PID: ' ,
'tid' => 'TID: ' ,
];
// Flags for messages
2023-07-20 13:12:26 +00:00
/** @var int Private message */
2023-06-27 07:39:11 +00:00
public const FLAG_PRIVATE = 1 << 0 ;
2023-07-20 13:12:26 +00:00
/** @var int Crash priority message (Crash + Hold = Direct) */
2023-06-27 07:39:11 +00:00
public const FLAG_CRASH = 1 << 1 ;
2023-07-20 13:12:26 +00:00
/** @var int Read by addressee */
2023-06-27 07:39:11 +00:00
public const FLAG_RECD = 1 << 2 ;
2023-07-20 13:12:26 +00:00
/** @var int Message has been sent */
2023-06-27 07:39:11 +00:00
public const FLAG_SENT = 1 << 3 ;
2023-07-20 13:12:26 +00:00
/** @var int File attached (filename in subject) */
2023-06-27 07:39:11 +00:00
public const FLAG_FILEATTACH = 1 << 4 ;
2023-07-20 13:12:26 +00:00
/** @var int Message in transit to another destination */
2023-06-27 07:39:11 +00:00
public const FLAG_INTRANSIT = 1 << 5 ;
2023-07-20 13:12:26 +00:00
/** @var int Unknown destination - node not in nodelist */
2023-06-27 07:39:11 +00:00
public const FLAG_ORPHAN = 1 << 6 ;
2023-07-20 13:12:26 +00:00
/** @var int Kill after mailing */
2023-06-27 07:39:11 +00:00
public const FLAG_KILLSENT = 1 << 7 ;
2023-07-20 13:12:26 +00:00
/** @var int Message originated here */
2023-06-27 07:39:11 +00:00
public const FLAG_LOCAL = 1 << 8 ;
2023-07-20 13:12:26 +00:00
/** @var int Hold message here to be collected (Crash + Hold = Direct) */
2023-06-27 07:39:11 +00:00
public const FLAG_HOLD = 1 << 9 ;
2023-07-20 13:12:26 +00:00
/** @var int Reserved for future use by FTS-0001 */
2023-06-27 07:39:11 +00:00
public const FLAG_UNUSED_10 = 1 << 10 ;
2023-07-20 13:12:26 +00:00
/** @var int Requesting a file (filename in subject) */
2023-06-27 07:39:11 +00:00
public const FLAG_FREQ = 1 << 11 ;
2023-07-20 13:12:26 +00:00
/** @var int Return Receipt requested */
public const FLAG_RETRECEIPT = 1 << 12 ; // (RRQ)
/** @var int Return Receipt message in response to an RRQ */
2023-06-27 07:39:11 +00:00
public const FLAG_ISRETRECEIPT = 1 << 13 ;
2023-07-20 13:12:26 +00:00
/** @var int Request audit trail */
public const FLAG_AUDITREQ = 1 << 14 ; // (ARQ)
/** @var int Requesting a file update (filename in subject) */
public const FLAG_FILEUPDATEREQ = 1 << 15 ; // (URQ)
2023-07-23 07:27:52 +00:00
/** @var int Echomail has been scanned out */
2021-06-29 10:43:29 +00:00
public const FLAG_ECHOMAIL = 1 << 16 ;
2023-07-23 07:27:52 +00:00
/** @var int Use packet password on the subject line for this message */
public const FLAG_PKTPASSWD = 1 << 17 ;
2021-06-29 10:43:29 +00:00
// FTS-0001.016 Message header 32 bytes node, net, flags, cost, date
2023-06-25 10:45:02 +00:00
public const HEADER_LEN = 0x20 ; // Length of message header
2021-06-29 10:43:29 +00:00
private const header = [ // Struct of message header
'onode' => [ 0x00 , 'v' , 2 ], // Originating Node
'dnode' => [ 0x02 , 'v' , 2 ], // Destination Node
'onet' => [ 0x04 , 'v' , 2 ], // Originating Net
'dnet' => [ 0x06 , 'v' , 2 ], // Destination Net
'flags' => [ 0x08 , 'v' , 2 ], // Message Flags
'cost' => [ 0x0a , 'v' , 2 ], // Send Cost
2021-07-15 14:54:23 +00:00
'date' => [ 0x0c , 'a20' , 20 ] // Message Date FTS-0001.016 Date: upto 20 chars null terminated
2021-06-29 10:43:29 +00:00
];
private const USER_FROM_LEN = 36 ; // FTS-0001.016 From Name: upto 36 chars null terminated
private const USER_TO_LEN = 36 ; // FTS-0001.016 To Name: upto 36 chars null terminated
2021-07-01 11:56:55 +00:00
private const SUBJECT_LEN = 71 ; // FTS-0001.016 Subject: upto 72 chars null terminated
2021-06-29 10:43:29 +00:00
private const AREATAG_LEN = 35 ; //
2021-08-08 12:49:38 +00:00
private ? ValidatorResult $errors = NULL ; // Packet validation
2021-06-29 10:43:29 +00:00
private array $header ; // Message Header
private Collection $kludge ; // Hold kludge items
2021-07-15 14:54:23 +00:00
2023-01-11 02:08:59 +00:00
public string $dump ; // Raw message
2021-06-29 10:43:29 +00:00
private string $user_from ; // User message is From
private string $user_to ; // User message is To
private string $subject ; // Message subject
2021-07-30 14:35:52 +00:00
2021-07-15 14:54:23 +00:00
private string $msgid ; // MSG ID
2022-02-11 23:21:46 +00:00
private string $replyid ; // Reply ID
2023-06-18 13:33:26 +00:00
private string $gateid ; // MSG ID if the message came via gate
2021-07-30 14:35:52 +00:00
2021-07-15 14:54:23 +00:00
private string $echoarea ; // FTS-0004.001
private string $intl ; // Netmail details
2021-07-30 14:35:52 +00:00
private string $message ; // The parsed message content
private string $message_src ; // The actual message part
2021-09-08 12:53:27 +00:00
private ? string $tagline ;
private ? string $tearline ;
2021-06-29 10:43:29 +00:00
private string $origin ; // FTS-0004.001
2021-07-15 14:54:23 +00:00
2021-07-30 14:35:52 +00:00
private int $tzutc ;
2021-06-29 13:23:59 +00:00
private array $point ; // Point the message belongs to (Netmail)
2021-08-20 14:33:41 +00:00
private array $src ; // Address the message is from
private array $dst ; // Address the message is to
2021-06-29 10:43:29 +00:00
2021-09-13 13:02:39 +00:00
private Collection $rescanned ; // Message was created as a result of a rescan
2021-06-29 10:43:29 +00:00
private Collection $path ; // FTS-0004.001 The message PATH lines
private Collection $seenby ; // FTS-0004.001 The message SEEN-BY lines
private Collection $via ; // The path the message has gone using Via lines (Netmail)
private Collection $unknown ; // Temporarily hold attributes we have no logic for.
2021-08-16 12:30:34 +00:00
public bool $packed = FALSE ; // Has the message been packed successfully
2021-07-05 11:31:04 +00:00
// Convert characters into printable chars
// https://int10h.org/oldschool-pc-fonts/readme/#437_charset
private const CP437 = [
0x01 => 0x263a , 0x02 => 0x263b , 0x03 => 0x2665 , 0x04 => 0x2666 ,
0x05 => 0x2663 , 0x06 => 0x2660 , 0x07 => 0x2022 , 0x08 => 0x25d8 ,
0x09 => 0x25cb , 0x0a => 0x2509 , 0x0b => 0x2642 , 0x0c => 0x2640 ,
0x0d => 0x266a , 0x0e => 0x266b , 0x0f => 0x263c ,
0x10 => 0x25ba , 0x11 => 0x25ca , 0x12 => 0x2195 , 0x13 => 0x203c ,
0x14 => 0x00b6 , 0x15 => 0x00a7 , 0x16 => 0x25ac , 0x17 => 0x21a8 ,
0x18 => 0x2191 , 0x19 => 0x2193 , 0x1a => 0x2192 , 0x1b => 0x2190 ,
0x1c => 0x221f , 0x1d => 0x2194 , 0x1e => 0x25bc , 0x1f => 0x25bc ,
0x7f => 0x2302 ,
0x80 => 0x00c7 , 0x81 => 0x00fc , 0x82 => 0x00e9 , 0x83 => 0x00e2 ,
0x84 => 0x00e4 , 0x85 => 0x00e0 , 0x86 => 0x00e5 , 0x87 => 0x00e7 ,
0x88 => 0x00ea , 0x89 => 0x00eb , 0x8a => 0x00e8 , 0x8b => 0x00ef ,
0x8c => 0x00ee , 0x8d => 0x00ec , 0x8e => 0x00c4 , 0x8f => 0x00c5 ,
0x90 => 0x00c9 , 0x91 => 0x00e6 , 0x92 => 0x00c6 , 0x93 => 0x00f4 ,
0x94 => 0x00f6 , 0x95 => 0x00f2 , 0x96 => 0x00fb , 0x97 => 0x00f9 ,
0x98 => 0x00ff , 0x99 => 0x00d6 , 0x9a => 0x00dc , 0x9b => 0x00a2 ,
0x9c => 0x00a3 , 0x9d => 0x00a5 , 0x9e => 0x20a7 , 0x9f => 0x0192 ,
0xa0 => 0x00e1 , 0xa1 => 0x00ed , 0xa2 => 0x00f3 , 0xa3 => 0x00fa ,
0xa4 => 0x00f1 , 0xa5 => 0x00d1 , 0xa6 => 0x00aa , 0xa7 => 0x00ba ,
0xa8 => 0x00bf , 0xa9 => 0x2310 , 0xaa => 0x00ac , 0xab => 0x00bd ,
0xac => 0x00bc , 0xad => 0x00a1 , 0xae => 0x00ab , 0xaf => 0x00bb ,
0xb0 => 0x2591 , 0xb1 => 0x2592 , 0xb2 => 0x2593 , 0xb3 => 0x2502 ,
0xb4 => 0x2524 , 0xb5 => 0x2561 , 0xb6 => 0x2562 , 0xb7 => 0x2556 ,
0xb8 => 0x2555 , 0xb9 => 0x2563 , 0xba => 0x2551 , 0xbb => 0x2557 ,
0xbc => 0x255d , 0xbd => 0x255c , 0xbe => 0x255b , 0xbf => 0x2510 ,
0xc0 => 0x2514 , 0xc1 => 0x2534 , 0xc2 => 0x252c , 0xc3 => 0x251c ,
0xc4 => 0x2500 , 0xc5 => 0x253c , 0xc6 => 0x255e , 0xc7 => 0x255f ,
0xc8 => 0x255a , 0xc9 => 0x2554 , 0xca => 0x2569 , 0xcb => 0x2566 ,
0xcc => 0x2560 , 0xcd => 0x2550 , 0xce => 0x256c , 0xcf => 0x2567 ,
0xd0 => 0x2568 , 0xd1 => 0x2564 , 0xd2 => 0x2565 , 0xd3 => 0x2559 ,
0xd4 => 0x2558 , 0xd5 => 0x2552 , 0xd6 => 0x2553 , 0xd7 => 0x256b ,
0xd8 => 0x256a , 0xd9 => 0x2518 , 0xda => 0x250c , 0xdb => 0x2588 ,
0xdc => 0x2584 , 0xdd => 0x258c , 0xde => 0x2590 , 0xdf => 0x2580 ,
0xe0 => 0x03b1 , 0xe1 => 0x00df , 0xe2 => 0x0393 , 0xe3 => 0x03c0 ,
0xe4 => 0x03a3 , 0xe5 => 0x03c3 , 0xe6 => 0x00b5 , 0xe7 => 0x03c4 ,
0xe8 => 0x03a6 , 0xe9 => 0x0398 , 0xea => 0x03a9 , 0xeb => 0x03b4 ,
0xec => 0x221e , 0xed => 0x03c6 , 0xee => 0x03b5 , 0xef => 0x2229 ,
0xf0 => 0x2261 , 0xf1 => 0x00b1 , 0xf2 => 0x2265 , 0xf3 => 0x2264 ,
0xf4 => 0x2320 , 0xf5 => 0x2321 , 0xf6 => 0x00f7 , 0xf7 => 0x2248 ,
0xf8 => 0x00b0 , 0xf9 => 0x2219 , 0xfa => 0x00b7 , 0xfb => 0x221a ,
0xfc => 0x207f , 0xfd => 0x00b2 , 0xfe => 0x25a0 , 0xff => 0x00a0 ,
];
2021-08-29 13:58:12 +00:00
public function __construct ( Zone $zone = NULL )
2021-06-29 10:43:29 +00:00
{
2021-08-29 13:58:12 +00:00
$this -> zone = $zone ;
2021-07-15 14:54:23 +00:00
2021-08-16 12:30:34 +00:00
$this -> header = [];
2021-07-30 14:35:52 +00:00
2021-10-25 10:09:57 +00:00
$this -> user_from = '' ;
$this -> user_to = '' ;
$this -> subject = '' ;
$this -> message = '' ;
2021-07-30 14:35:52 +00:00
$this -> msgid = '' ;
2022-02-11 23:21:46 +00:00
$this -> gateid = '' ;
2022-01-01 05:59:35 +00:00
$this -> replyid = '' ;
2021-07-30 14:35:52 +00:00
$this -> echoarea = '' ;
$this -> intl = '' ;
2023-08-04 12:06:29 +00:00
$this -> tearline = NULL ;
$this -> tagline = NULL ;
2021-07-30 14:35:52 +00:00
$this -> origin = '' ;
$this -> tzutc = 0 ;
2021-08-20 14:33:41 +00:00
$this -> src = [];
$this -> dst = [];
2021-07-30 14:35:52 +00:00
$this -> point = [];
2021-09-14 13:14:13 +00:00
$this -> kludge = collect ();
2021-09-13 13:02:39 +00:00
$this -> rescanned = collect ();
2021-06-29 10:43:29 +00:00
$this -> path = collect ();
$this -> seenby = collect ();
$this -> via = collect ();
$this -> unknown = collect ();
2021-07-15 14:54:23 +00:00
}
2021-06-29 10:43:29 +00:00
public function __get ( $key )
{
switch ( $key ) {
// From Addresses
2021-08-20 14:33:41 +00:00
case 'fz' : return Arr :: get ( $this -> src , 'z' );
2023-06-27 07:39:11 +00:00
case 'fn' : return $this -> src ? Arr :: get ( $this -> src , 'n' ) : Arr :: get ( $this -> header , 'onet' );
case 'ff' : return $this -> src ? Arr :: get ( $this -> src , 'f' ) : Arr :: get ( $this -> header , 'onode' );
2023-07-20 10:04:41 +00:00
case 'fp' : return Arr :: get ( $this -> point , 'src' , Arr :: get ( $this -> src , 'p' , Arr :: get ( $this -> header , 'opoint' , 0 )));
2021-09-11 13:32:10 +00:00
case 'fd' : return Arr :: get ( $this -> src , 'd' );
case 'fdomain' :
// We'll use the zone's domain if this method class was called with a zone
2023-06-27 07:39:11 +00:00
if ( $this -> zone && (( $this -> zone -> domain -> name === Arr :: get ( $this -> src , 'd' )) || ! Arr :: get ( $this -> src , 'd' )))
2021-09-11 13:32:10 +00:00
return $this -> zone -> domain ;
// If we get the domain from the packet, we'll find it
if ( $x = Arr :: get ( $this -> src , 'd' )) {
return Domain :: where ( 'name' , $x ) -> single ();
}
return NULL ;
2022-11-11 11:57:40 +00:00
case 'tdomain' :
// We'll use the zone's domain if this method class was called with a zone
2023-06-27 07:39:11 +00:00
if ( $this -> zone && (( $this -> zone -> domain -> name === Arr :: get ( $this -> dst , 'd' )) || ! Arr :: get ( $this -> dst , 'd' )))
2022-11-11 11:57:40 +00:00
return $this -> zone -> domain ;
// If we get the domain from the packet, we'll find it
if ( $x = Arr :: get ( $this -> dst , 'd' )) {
return Domain :: where ( 'name' , $x ) -> single ();
}
// Otherwise we'll assume the same as the source domain
return $this -> fdomain ? : NULL ;
2021-09-11 13:32:10 +00:00
case 'fzone' :
// Use the zone if this class was called with it.
2023-06-27 07:39:11 +00:00
if ( $this -> zone && ( $this -> fz === $this -> zone -> zone_id ))
2021-09-11 13:32:10 +00:00
return $this -> zone ;
2022-12-03 05:00:38 +00:00
if ( $this -> fdomain ) {
2023-06-27 07:39:11 +00:00
if (( $x = $this -> fdomain -> zones -> search ( function ( $item ) { return $item -> zone_id === $this -> fz ; })) !== FALSE )
2021-09-11 13:32:10 +00:00
return $this -> fdomain -> zones -> get ( $x );
}
// No domain, so we'll use the default zone
return Zone :: where ( 'zone_id' , $this -> fz )
-> where ( 'default' , TRUE )
-> single ();
2021-06-29 10:43:29 +00:00
2022-11-11 11:57:40 +00:00
case 'tzone' :
// Use the zone if this class was called with it.
2023-06-27 07:39:11 +00:00
if ( $this -> zone && ( $this -> tz === $this -> zone -> zone_id ))
2022-11-11 11:57:40 +00:00
return $this -> zone ;
2022-12-03 05:00:38 +00:00
if ( $this -> tdomain ) {
2023-06-27 07:39:11 +00:00
if (( $x = $this -> tdomain -> zones -> search ( function ( $item ) { return $item -> zone_id === $this -> tz ; })) !== FALSE )
2022-11-11 11:57:40 +00:00
return $this -> tdomain -> zones -> get ( $x );
}
// No domain, so we'll use the default zone
return Zone :: where ( 'zone_id' , $this -> tz )
-> where ( 'default' , TRUE )
-> single ();
2021-06-29 10:43:29 +00:00
// To Addresses
// Echomail doesnt have a zone, so we'll use the source zone
2021-08-20 14:33:41 +00:00
case 'tz' : return Arr :: get ( $this -> echoarea ? $this -> src : $this -> dst , 'z' );
2022-02-19 05:33:14 +00:00
case 'tn' : return Arr :: get ( $this -> header , 'dnet' );
case 'tf' : return Arr :: get ( $this -> header , 'dnode' );
2023-07-20 10:04:41 +00:00
case 'tp' : return Arr :: get ( $this -> point , 'dst' , Arr :: get ( $this -> header , 'dpoint' , 0 ));
2021-06-29 10:43:29 +00:00
case 'fftn' :
2021-07-15 14:54:23 +00:00
case 'fftn_o' :
2021-08-18 14:20:34 +00:00
case 'tftn' :
2021-07-15 14:54:23 +00:00
case 'tftn_o' :
2021-08-18 14:20:34 +00:00
return parent :: __get ( $key );
2021-07-15 14:54:23 +00:00
2023-01-01 14:05:44 +00:00
// For 5D we need to include the domain
2021-08-20 14:33:41 +00:00
case 'fboss' :
2023-01-01 14:05:44 +00:00
return sprintf ( '%d:%d/%d' , $this -> fz , $this -> fn , $this -> ff ) . (( $x = $this -> fdomain ) ? '@' . $x -> name : '' );
2021-08-20 14:33:41 +00:00
case 'tboss' :
2023-01-01 14:05:44 +00:00
return sprintf ( '%d:%d/%d' , $this -> tz , $this -> tn , $this -> tf ) . (( $x = $this -> tdomain ) ? '@' . $x -> name : '' );
2021-08-20 14:33:41 +00:00
case 'fboss_o' :
return Address :: findFTN ( $this -> fboss );
case 'tboss_o' :
return Address :: findFTN ( $this -> tboss );
2021-06-29 10:43:29 +00:00
case 'date' :
2021-08-27 13:16:39 +00:00
try {
2022-11-05 08:03:33 +00:00
if ( str_contains ( $x = chop ( Arr :: get ( $this -> header , $key )), " \x00 " ))
throw new \Exception ( 'Date contains null values.' );
2021-08-27 13:16:39 +00:00
return Carbon :: createFromFormat ( 'd M y H:i:s O' ,
2022-11-05 08:03:33 +00:00
sprintf ( '%s %s%04d' , $x ,( $this -> tzutc < 0 ) ? '-' : '+' , abs ( $this -> tzutc )));
2021-08-27 13:16:39 +00:00
2022-11-05 08:03:33 +00:00
} catch ( InvalidFormatException | \Exception $e ) {
2023-07-20 10:04:41 +00:00
Log :: error ( sprintf ( '%s:! Date doesnt parse [%s] (%s)' , self :: LOGKEY , $e -> getMessage (), Arr :: get ( $this -> header , $key )));
2023-01-11 02:08:59 +00:00
throw new \Exception ( sprintf ( '%s (%s)' , $e -> getMessage (), hex_dump ( Arr :: get ( $this -> header , $key ))));
2021-08-27 13:16:39 +00:00
}
2021-06-29 10:43:29 +00:00
case 'flags' :
2021-07-17 07:15:00 +00:00
case 'cost' :
return Arr :: get ( $this -> header , $key );
2021-06-29 10:43:29 +00:00
2021-07-30 14:35:52 +00:00
case 'tzutc' :
case 'user_to' :
case 'user_from' :
case 'subject' :
case 'echoarea' :
case 'msgid' :
2022-01-01 05:59:35 +00:00
case 'replyid' :
2022-02-11 23:21:46 +00:00
case 'gateid' :
2021-07-30 14:35:52 +00:00
2021-06-29 10:43:29 +00:00
case 'message' :
2021-07-30 14:35:52 +00:00
case 'message_src' :
2021-07-18 12:10:21 +00:00
case 'tearline' :
2021-07-30 14:35:52 +00:00
case 'tagline' :
2021-07-18 12:10:21 +00:00
case 'origin' :
2021-07-30 14:35:52 +00:00
2021-06-29 10:43:29 +00:00
case 'kludge' :
2021-09-13 13:02:39 +00:00
case 'rescanned' :
2021-06-29 10:43:29 +00:00
case 'path' :
case 'seenby' :
2021-11-28 03:02:50 +00:00
case 'unknown' :
2021-07-05 11:31:04 +00:00
case 'via' :
2021-07-30 14:35:52 +00:00
2021-06-29 10:43:29 +00:00
case 'errors' :
return $this -> { $key };
2023-07-15 12:10:05 +00:00
case 'dbid' :
return $this -> kludge -> get ( $key );
2021-07-15 14:54:23 +00:00
default :
throw new \Exception ( 'Unknown key: ' . $key );
}
}
2022-02-12 23:24:31 +00:00
/**
* When we serialise this object , we ' ll need to utf8_encode some values
*
* @ return array
*/
public function __serialize () : array
{
return $this -> encode ();
}
2021-07-15 14:54:23 +00:00
public function __set ( $key , $value )
{
switch ( $key ) {
2021-07-30 14:35:52 +00:00
case 'flags' :
2021-07-15 14:54:23 +00:00
case 'header' :
2021-07-30 14:35:52 +00:00
case 'tzutc' :
case 'user_from' :
case 'user_to' :
case 'subject' :
2022-02-11 23:21:46 +00:00
case 'gateid' :
2021-07-30 14:35:52 +00:00
case 'msgid' :
2022-01-01 05:59:35 +00:00
case 'replyid' :
2021-07-30 14:35:52 +00:00
case 'echoarea' :
2021-07-15 14:54:23 +00:00
case 'intl' :
2021-07-30 14:35:52 +00:00
2021-07-15 14:54:23 +00:00
case 'message' :
2021-07-30 14:35:52 +00:00
2021-09-14 13:14:13 +00:00
case 'kludge' :
2021-07-30 14:35:52 +00:00
case 'tagline' :
2021-07-18 12:10:21 +00:00
case 'tearline' :
case 'origin' :
2022-01-22 12:08:46 +00:00
case 'seenby' :
case 'path' :
2021-07-15 14:54:23 +00:00
case 'via' :
$this -> { $key } = $value ;
break ;
2021-06-29 10:43:29 +00:00
default :
throw new \Exception ( 'Unknown key: ' . $key );
}
}
/**
* Export an FTN message , ready for sending .
*
* @ return string
*/
public function __toString () : string
{
2021-08-16 12:30:34 +00:00
$return = pack ( collect ( self :: header ) -> pluck ( 1 ) -> join ( '' ),
2023-10-09 10:54:46 +00:00
$this -> ff , // Originating Node
$this -> tf , // Destination Node
$this -> fn , // Originating Net
$this -> tn , // Destination Net
2023-07-20 10:04:41 +00:00
$this -> flags &~ ( self :: FLAG_INTRANSIT | self :: FLAG_LOCAL ), // Turn off our local/intransit bits
2021-07-15 14:54:23 +00:00
$this -> cost ,
$this -> date -> format ( 'd M y H:i:s' ),
2021-06-29 10:43:29 +00:00
);
2021-07-15 14:54:23 +00:00
$return .= $this -> user_to . " \00 " ;
$return .= $this -> user_from . " \00 " ;
2021-06-29 10:43:29 +00:00
$return .= $this -> subject . " \00 " ;
2021-07-30 14:35:52 +00:00
if ( ! $this -> isNetmail ())
2023-09-12 07:20:09 +00:00
$return .= sprintf ( " AREA:%s \r " , strtoupper ( $this -> echoarea ));
2021-06-29 10:43:29 +00:00
2021-07-30 14:35:52 +00:00
// If the message is local, then our kludges are not in the msg itself, we'll add them
if ( $this -> isFlagSet ( self :: FLAG_LOCAL )) {
2023-07-20 10:04:41 +00:00
if ( $this -> isNetmail ()) {
2021-07-30 14:35:52 +00:00
$return .= sprintf ( " \01 INTL %s \r " , $this -> intl );
2021-06-29 10:43:29 +00:00
2023-07-20 10:04:41 +00:00
if ( $this -> fp )
$return .= sprintf ( " \01 FMPT %d \r " , $this -> fp );
if ( $this -> tp )
$return .= sprintf ( " \01 TOPT %d \r " , $this -> tp );
}
2021-07-30 14:35:52 +00:00
$return .= sprintf ( " \01 TZUTC: %s \r " , str_replace ( '+' , '' , $this -> date -> getOffsetString ( '' )));
2021-06-29 10:43:29 +00:00
2021-07-30 14:35:52 +00:00
// Add some kludges
$return .= sprintf ( " \01 MSGID: %s \r " , $this -> msgid );
2022-01-01 05:59:35 +00:00
if ( $this -> replyid )
$return .= sprintf ( " \01 REPLY: %s \r " , $this -> replyid );
2021-07-30 14:35:52 +00:00
2022-02-11 23:21:46 +00:00
if ( $this -> gateid )
$return .= sprintf ( " \01 GATE: %s \r " , $this -> gateid );
2021-07-30 14:35:52 +00:00
foreach ( $this -> _kludge as $k => $v ) {
if ( $x = $this -> kludge -> get ( $k ))
$return .= sprintf ( " \01 %s%s \r " , $v , $x );
}
2022-02-16 12:01:55 +00:00
$return .= $this -> message ;
2021-07-30 14:35:52 +00:00
if ( $this -> tagline )
$return .= sprintf ( " ... %s \r " , $this -> tagline );
if ( $this -> tearline )
$return .= sprintf ( " --- %s \r " , $this -> tearline );
if ( $this -> origin )
$return .= sprintf ( " * Origin: %s \r " , $this -> origin );
} else {
2023-07-20 10:04:41 +00:00
if ( $this -> isFlagSet ( self :: FLAG_INTRANSIT ) && $this -> isNetmail ()) {
$return .= sprintf ( " \01 INTL %s \r " , $this -> intl );
if ( $this -> fp )
$return .= sprintf ( " \01 FMPT %d \r " , $this -> fp );
if ( $this -> tp )
$return .= sprintf ( " \01 TOPT %d \r " , $this -> tp );
}
2022-02-16 12:01:55 +00:00
$return .= $this -> message ;
2021-07-30 14:35:52 +00:00
}
2021-07-15 14:54:23 +00:00
if ( $this -> isNetmail ()) {
2022-02-16 12:01:55 +00:00
foreach ( $this -> via as $via )
$return .= sprintf ( " \01 Via %s \r " , $via );
2021-07-30 14:35:52 +00:00
} else {
2022-01-22 12:08:46 +00:00
// Seenby & PATH - FSC-0068
2023-11-26 22:00:32 +00:00
$return .= $this -> optimise_path ( $this -> seenby , 'SEEN-BY:' ) . " \r " ;
$return .= " \x01 " . $this -> optimise_path ( $this -> path , 'PATH:' ) . " \r " ;
2021-06-29 10:43:29 +00:00
}
$return .= " \00 " ;
return $return ;
}
2022-02-12 23:24:31 +00:00
/**
* When we unserialize , we ' ll restore ( utf8_decode ) some values
*
* @ param array $values
*/
public function __unserialize ( array $values ) : void
{
$this -> decode ( $values );
}
2023-11-26 22:00:32 +00:00
/**
* Reduce our PATH / SEEN - BY for messages as per FSC - 006 8
*
* @ param Collection $path
* @ param string $prefix
* @ param int $len
* @ param string $delim
* @ return string
*/
function optimise_path ( Collection $path , string $prefix , int $len = 79 , string $delim = " \r " ) : string
{
$cur = NULL ;
$result = $prefix ;
$c = strlen ( $prefix );
foreach ( $path as $address ) {
[ $host , $node ] = explode ( '/' , $address );
if (( $c + strlen ( ' ' . $host )) > $len ) {
$result .= ( $x = $delim . $prefix );
$c = strlen ( $x );
$cur = NULL ;
}
if ( $host !== $cur ) {
$cur = $host ;
$result .= ( $x = ' ' . $address );
} else {
$result .= ( $x = ' ' . $node );
}
$c += strlen ( $x );
}
return $result ;
}
2022-02-12 23:24:31 +00:00
/**
* Parse a message from a packet
*
* @ param string $msg
* @ param Zone | null $zone
* @ return Message
* @ throws \Exception
*/
public static function parseMessage ( string $msg , Zone $zone = NULL ) : self
{
2023-07-20 10:04:41 +00:00
Log :: info ( sprintf ( '%s:= Processing message [%d] bytes from zone [%d]' , self :: LOGKEY , strlen ( $msg ), $zone ? -> zone_id ));
2022-02-12 23:24:31 +00:00
$o = new self ( $zone );
2023-01-11 02:08:59 +00:00
$o -> dump = $msg ;
2022-02-12 23:24:31 +00:00
try {
$o -> header = unpack ( self :: unpackheader ( self :: header ), substr ( $msg , 0 , self :: HEADER_LEN ));
} catch ( \Exception $e ) {
Log :: error ( sprintf ( '%s:! Error bad packet header' , self :: LOGKEY ));
$validator = Validator :: make ([
'header' => substr ( $msg , 0 , self :: HEADER_LEN ),
],[
'header' => [ function ( $attribute , $value , $fail ) use ( $e ) { return $fail ( $e -> getMessage ()); }]
]);
if ( $validator -> fails ())
$o -> errors = $validator ;
return $o ;
}
$ptr = 0 ;
2023-09-20 10:29:23 +00:00
2022-02-12 23:24:31 +00:00
// To User
$o -> user_to = strstr ( substr ( $msg , self :: HEADER_LEN + $ptr ), " \x00 " , TRUE );
$ptr += strlen ( $o -> user_to ) + 1 ;
// From User
$o -> user_from = strstr ( substr ( $msg , self :: HEADER_LEN + $ptr ), " \x00 " , TRUE );
$ptr += strlen ( $o -> user_from ) + 1 ;
// Subject
$o -> subject = strstr ( substr ( $msg , self :: HEADER_LEN + $ptr ), " \x00 " , TRUE );
$ptr += strlen ( $o -> subject ) + 1 ;
// Check if this is an Echomail
if ( ! strncmp ( substr ( $msg , self :: HEADER_LEN + $ptr ), 'AREA:' , 5 )) {
2023-09-12 07:20:09 +00:00
$o -> echoarea = strtoupper ( substr ( $msg , self :: HEADER_LEN + $ptr + 5 , strpos ( $msg , " \r " , self :: HEADER_LEN + $ptr + 5 ) - ( self :: HEADER_LEN + $ptr + 5 )));
2022-02-12 23:24:31 +00:00
$ptr += strlen ( $o -> echoarea ) + 5 + 1 ;
}
$o -> unpackMessage ( substr ( $msg , self :: HEADER_LEN + $ptr ));
if (( $x = $o -> validate ()) -> fails ()) {
2023-07-20 10:04:41 +00:00
Log :: debug ( sprintf ( '%s:! Message fails validation (%s@%s->%s@%s)' , self :: LOGKEY , $o -> user_from , $o -> fftn , $o -> user_to , $o -> tftn ),[ 'result' => $x -> errors ()]);
2022-02-12 23:24:31 +00:00
//throw new \Exception('Message validation fails:'.join(' ',$x->errors()->all()));
}
return $o ;
}
/**
* Translate the string into something printable via the web
*
* @ param string $string
* @ param array $skip
* @ return string
*/
public static function tr ( string $string , array $skip = [ 0x0a , 0x0d ]) : string
{
$tr = [];
foreach ( self :: CP437 as $k => $v ) {
if ( in_array ( $k , $skip ))
continue ;
$tr [ chr ( $k )] = '&#' . $v ;
}
return strtr ( $string , $tr );
}
2021-07-15 14:54:23 +00:00
/**
* If this message doesnt have an AREATAG , then its a netmail .
*
* @ return bool
*/
public function isNetmail () : bool
{
return ! $this -> echoarea ;
}
2021-06-29 10:43:29 +00:00
/**
* Return an array of flag descriptions
*
2021-09-11 13:32:10 +00:00
* @ return Collection
2021-06-29 10:43:29 +00:00
*
* http :// ftsc . org / docs / fsc - 0001.000
* AttributeWord bit meaning
2021-07-15 14:54:23 +00:00
* --- --------------------
* 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
2021-06-29 10:43:29 +00:00
*/
2021-07-17 07:15:00 +00:00
public function flags () : Collection
2021-06-29 10:43:29 +00:00
{
2021-07-17 07:15:00 +00:00
return collect ([
'private' => $this -> isFlagSet ( self :: FLAG_PRIVATE ),
'crash' => $this -> isFlagSet ( self :: FLAG_CRASH ),
'recd' => $this -> isFlagSet ( self :: FLAG_RECD ),
'sent' => $this -> isFlagSet ( self :: FLAG_SENT ),
'fileattach' => $this -> isFlagSet ( self :: FLAG_FILEATTACH ),
'intransit' => $this -> isFlagSet ( self :: FLAG_INTRANSIT ),
'orphan' => $this -> isFlagSet ( self :: FLAG_ORPHAN ),
'killsent' => $this -> isFlagSet ( self :: FLAG_KILLSENT ),
'local' => $this -> isFlagSet ( self :: FLAG_LOCAL ),
'hold' => $this -> isFlagSet ( self :: FLAG_HOLD ),
'unused-10' => $this -> isFlagSet ( self :: FLAG_UNUSED_10 ),
'filereq' => $this -> isFlagSet ( self :: FLAG_FREQ ),
'receipt-req' => $this -> isFlagSet ( self :: FLAG_RETRECEIPT ),
'receipt' => $this -> isFlagSet ( self :: FLAG_ISRETRECEIPT ),
'audit' => $this -> isFlagSet ( self :: FLAG_AUDITREQ ),
'fileupdate' => $this -> isFlagSet ( self :: FLAG_FILEUPDATEREQ ),
]);
2021-06-29 10:43:29 +00:00
}
2021-07-17 07:15:00 +00:00
private function isFlagSet ( $flag ) : bool
2021-06-29 10:43:29 +00:00
{
2021-07-17 07:15:00 +00:00
return ( $this -> flags & $flag );
2021-06-29 10:43:29 +00:00
}
/**
2021-07-15 14:54:23 +00:00
* Process the data after the ORIGIN
* There may be kludge lines after the origin - notably SEEN - BY
2021-06-29 10:43:29 +00:00
*
2021-07-15 14:54:23 +00:00
* @ param string $message
2021-06-29 10:43:29 +00:00
*/
2021-07-15 14:54:23 +00:00
private function parseOrigin ( string $message )
2021-06-29 10:43:29 +00:00
{
2021-07-15 14:54:23 +00:00
// Split out each line
$result = collect ( explode ( " \r " , $message )) -> filter ();
2023-08-04 12:06:29 +00:00
while ( $result -> count ()) {
$v = $result -> shift ();
2021-07-15 14:54:23 +00:00
foreach ( $this -> _kludge as $a => $b ) {
if ( $t = $this -> kludge ( $b , $v )) {
$this -> kludge -> put ( $a , $t );
break ;
}
}
if ( $t = $this -> kludge ( 'SEEN-BY: ' , $v ))
$this -> seenby -> push ( $t );
elseif ( $t = $this -> kludge ( 'PATH: ' , $v ))
$this -> path -> push ( $t );
2023-08-04 12:06:29 +00:00
elseif ( $t = $this -> kludge ( ' \* Origin: ' , $v )) {
// Check in case there is a kludge that starts with SOH
if ( $soh = strpos ( $t , " \x01 " )) {
$this -> origin = substr ( $t , 0 , $soh );
$result -> push ( substr ( $t , $soh + 1 ));
} else {
$this -> origin = $t ;
}
}
2021-07-15 14:54:23 +00:00
// We got unknown Kludge lines in the origin
else
$this -> unknown -> push ( $v );
}
2021-06-29 10:43:29 +00:00
}
/**
* Extract information out of the message text .
*
* @ param string $message
2021-08-29 13:58:12 +00:00
* @ throws \Exception
2021-06-29 10:43:29 +00:00
*/
2021-07-15 14:54:23 +00:00
public function unpackMessage ( string $message ) : void
2021-06-29 10:43:29 +00:00
{
// Remove DOS \n\r
$message = preg_replace ( " / \n \r / " , " \r " , $message );
2021-08-27 13:16:39 +00:00
$message = preg_replace ( " / \r \n / " , " \r " , $message );
2021-06-29 10:43:29 +00:00
// Split out the <SOH> lines
2021-07-30 14:35:52 +00:00
$result = collect ( explode ( " \x01 " , $message )) -> filter ();
2021-06-29 10:43:29 +00:00
$this -> message = '' ;
2021-07-30 14:35:52 +00:00
$this -> message_src = '' ;
2021-11-28 03:02:50 +00:00
$msgpos = 0 ;
2023-08-04 12:06:29 +00:00
$haveOrigin = FALSE ;
2021-11-28 03:02:50 +00:00
2023-08-04 12:06:29 +00:00
while ( $result -> count ()) {
2021-11-28 03:02:50 +00:00
// $kl is our line starting with a \x01 kludge delimiter
2023-08-04 12:06:29 +00:00
$kl = $result -> shift ();
2021-06-29 10:43:29 +00:00
// Search for \r - if that is the end of the line, then its a kludge
2021-11-28 03:02:50 +00:00
$retpos = strpos ( $kl , " \r " );
$msgpos += 1 + strlen ( $kl ); // SOH+text
2021-06-29 10:43:29 +00:00
$t = '' ;
2023-08-04 12:06:29 +00:00
// If there is a return in this middle of this line, that means the text message starts after the return,
// ie: SOH+text\rmessage
2021-11-28 13:12:37 +00:00
2023-08-04 12:06:29 +00:00
// Just in case we already parsed this as part of our message, we'll continue, since its probably a
// binary message with a SOH inside it.
if ( strlen ( $this -> message ) || ( $retpos !== strlen ( $kl ) - 1 )) {
// If there was no return, its part of the message, so we need to add back the SOH.
2021-11-28 13:12:37 +00:00
if ( $retpos === FALSE ) {
$this -> message .= " \x01 " . $kl ;
2021-12-01 11:45:51 +00:00
2021-11-28 13:12:37 +00:00
continue ;
}
2023-08-04 12:06:29 +00:00
// Check if this has the origin line. Anything before is the message, anything after the origin
// line is also kludge data.
if ( $originpos = strrpos ( $kl , " \r * Origin: " )) {
// Anything after the return (from the kludge) is a message.
2021-11-28 03:02:50 +00:00
if ( ! $this -> message ) {
$this -> message .= substr ( $kl , $retpos + 1 , $originpos - $retpos - 1 );
2023-08-04 12:06:29 +00:00
// But if we are already sourcing a message, then its part of it message.
2021-11-28 03:02:50 +00:00
} else {
$this -> message .= " \x01 " . substr ( $kl , 0 , $originpos );
2021-12-01 11:45:51 +00:00
$retpos = 0 ;
2021-11-28 03:02:50 +00:00
}
2021-07-30 14:35:52 +00:00
2023-08-04 12:06:29 +00:00
// See if we have a tagline
if ( $tl = strrpos ( $kl , " \r ... " )) {
$tlr = strpos ( substr ( $kl , $tl + 6 ), " \r " );
$this -> tagline = substr ( $kl , $tl + 5 , $tlr );
}
// Message is finished, now we are parsing origin data (and more kludges)
2021-12-01 11:45:51 +00:00
$this -> parseOrigin ( substr ( $kl , $originpos + 1 ));
2023-08-04 12:06:29 +00:00
$haveOrigin = TRUE ;
// Our message source (for resending, is everything up to the end of the origin line.
2021-12-01 11:45:51 +00:00
$this -> message_src = substr ( $message , 0 , $msgpos - ( 1 + strlen ( $kl )) + $originpos + 12 + strlen ( $this -> origin ) + 1 );
$kl = substr ( $kl , 0 , $retpos );
2021-06-29 10:43:29 +00:00
// The message is the rest?
2023-08-04 12:06:29 +00:00
// Netmails dont generally have an origin line
2021-11-28 03:02:50 +00:00
} elseif ( strlen ( $kl ) > $retpos + 1 ) {
2023-08-04 12:06:29 +00:00
// We still have some text to process, add it to the list
if ( $haveOrigin && ( $retpos + 1 < strlen ( $kl ))) {
$result -> push ( substr ( $kl , $retpos + 1 ));
// If this was the overflow from echomail, then our message_src would be defined, and thus its not part of the message
} else {
$this -> message .= substr ( $kl , $retpos + 1 );
$this -> message_src = substr ( $message , 0 , $msgpos );
}
2022-02-19 05:33:14 +00:00
2021-11-28 03:02:50 +00:00
$kl = substr ( $kl , 0 , $retpos );
2021-06-29 10:43:29 +00:00
}
2021-11-28 03:02:50 +00:00
if ( ! $kl )
continue ;
2021-06-29 10:43:29 +00:00
}
2023-09-11 11:52:48 +00:00
foreach ( $this -> _kludge as $a => $b )
2021-11-28 03:02:50 +00:00
if ( $t = $this -> kludge ( $b , $kl )) {
2021-06-29 10:43:29 +00:00
$this -> kludge -> put ( $a , $t );
break ;
}
// There is more text.
if ( $t )
continue ;
2021-07-15 14:54:23 +00:00
// From point: <SOH>"FMPT <point number><CR>
2021-11-28 03:02:50 +00:00
if ( $t = $this -> kludge ( 'FMPT ' , $kl ))
2021-06-29 13:23:59 +00:00
$this -> point [ 'src' ] = $t ;
2021-06-29 10:43:29 +00:00
/*
* The INTL control paragraph shall be used to give information about
* the zone numbers of the original sender and the ultimate addressee
* of a message .
*
* < SOH > " INTL " < destination address > " " < origin address >< CR >
*/
2021-11-28 03:02:50 +00:00
elseif ( $t = $this -> kludge ( 'INTL ' , $kl )) {
2021-07-15 14:54:23 +00:00
$this -> intl = $t ;
2021-06-29 10:43:29 +00:00
2021-06-29 13:23:59 +00:00
// INTL kludge is in Netmail, so we'll do some validation:
2021-07-15 14:54:23 +00:00
list ( $dst , $src ) = explode ( ' ' , $t );
2021-08-20 14:33:41 +00:00
$this -> src = Address :: parseFTN ( $src );
if (( $this -> src [ 'n' ] !== $this -> fn ) || ( $this -> src [ 'f' ] !== $this -> ff )) {
2023-07-20 10:04:41 +00:00
Log :: error ( sprintf ( '%s:! INTL src address [%s] doesnt match packet' , self :: LOGKEY , $src ),[ 'src' => $this -> src , 'fn' => $this -> fn , 'ff' => $this -> ff ]);
2021-06-29 13:23:59 +00:00
}
2021-08-20 14:33:41 +00:00
$this -> dst = Address :: parseFTN ( $dst );
if (( $this -> dst [ 'n' ] !== $this -> tn ) || ( $this -> dst [ 'f' ] !== $this -> tf )) {
2023-07-20 10:04:41 +00:00
Log :: error ( sprintf ( '%s:! INTL dst address [%s] doesnt match packet' , self :: LOGKEY , $dst ),[ 'dst' => $this -> dst , 'tn' => $this -> tn , 'tf' => $this -> tf ]);
2021-06-29 13:23:59 +00:00
}
2021-06-29 10:43:29 +00:00
}
2023-01-01 01:02:28 +00:00
elseif (( $t = $this -> kludge ( 'TZUTC: ' , $kl )) && is_numeric ( $t ))
2021-07-30 14:35:52 +00:00
$this -> tzutc = $t ;
2021-11-28 03:02:50 +00:00
elseif ( $t = $this -> kludge ( 'MSGID: ' , $kl ))
2021-07-15 14:54:23 +00:00
$this -> msgid = $t ;
2022-11-11 11:57:40 +00:00
elseif ( $t = $this -> kludge ( 'REPLY: ' , $kl ))
$this -> replyid = $t ;
2022-02-11 23:21:46 +00:00
elseif ( $t = $this -> kludge ( 'GATE: ' , $kl ))
$this -> gateid = $t ;
2021-11-28 03:02:50 +00:00
elseif ( $t = $this -> kludge ( 'PATH: ' , $kl ))
2021-06-29 10:43:29 +00:00
$this -> path -> push ( $t );
2021-11-28 03:02:50 +00:00
elseif ( $t = $this -> kludge ( 'RESCANNED ' , $kl ))
2021-09-13 13:02:39 +00:00
$this -> rescanned -> push ( $t );
2021-11-28 03:02:50 +00:00
elseif ( $t = $this -> kludge ( 'SEEN-BY: ' , $kl ))
2021-08-29 13:58:12 +00:00
$this -> seenby -> push ( $t );
2021-06-29 10:43:29 +00:00
// To Point: <SOH>TOPT <point number><CR>
2021-11-28 03:02:50 +00:00
elseif ( $t = $this -> kludge ( 'TOPT ' , $kl ))
2021-06-29 13:23:59 +00:00
$this -> point [ 'dst' ] = $t ;
2021-06-29 10:43:29 +00:00
// <SOH>Via <FTN Address> @YYYYMMDD.HHMMSS[.Precise][.Time Zone] <Program Name> <Version> [Serial Number]<CR>
2023-09-22 04:45:44 +00:00
// @todo The via line is still showing in the main message? https://clrghouz.bbs.dege.au/netmail/view/707
// @todo Need to make sure that the CRC doesnt include this
2021-11-28 03:02:50 +00:00
elseif ( $t = $this -> kludge ( 'Via ' , $kl ))
2021-06-29 10:43:29 +00:00
$this -> via -> push ( $t );
// We got a kludge line we dont know about
else
2021-11-28 03:02:50 +00:00
$this -> unknown -> push ( chop ( $kl , " \r " ));
2021-06-29 10:43:29 +00:00
}
2021-08-19 13:35:48 +00:00
2021-08-20 14:33:41 +00:00
// Work out our zone/point
// http://ftsc.org/docs/fsc-0068.001
2023-09-11 11:52:48 +00:00
// MSGID should be the basis of the source, if it cannot be obtained from the origin
2022-02-11 23:21:46 +00:00
// If the message was gated, we'll use the gateid
2021-08-22 01:00:41 +00:00
$m = [];
2023-09-11 11:52:48 +00:00
if ( $this -> origin && preg_match ( '#\(([0-9]+:[0-9]+/[0-9]+)?\.?([0-9]+)?@?([A-Za-z-_~]+)?\)$#' , $this -> origin , $m )) {
$this -> src = Address :: parseFTN ( $m [ 1 ] . (( isset ( $m [ 2 ]) && $m [ 2 ] != '' ) ? '.' . $m [ 2 ] : '' ) . ( isset ( $m [ 3 ]) ? '@' . $m [ 3 ] : '' ));
} elseif (( $this -> msgid || $this -> gateid ) && preg_match ( '#([0-9]+:[0-9]+/[0-9]+)?\.?([0-9]+)?(\.[0-9]+)?@?([A-Za-z-_~]+)?\ +#' , $this -> gateid ? : $this -> msgid , $m )) {
2021-12-01 12:41:20 +00:00
try {
$this -> src = Address :: parseFTN ( $m [ 1 ] . (( isset ( $m [ 2 ]) && $m [ 2 ] != '' ) ? '.' . $m [ 2 ] : '' ) . ( isset ( $m [ 4 ]) ? '@' . $m [ 4 ] : '' ));
} catch ( \Exception $e ) {
Log :: error ( sprintf ( '%s:! MSGID [%s] address is invalid [%s]' , self :: LOGKEY , $this -> msgid , $e -> getMessage ()));
}
2021-08-29 13:58:12 +00:00
// Otherwise get it from our zone object and packet header
} elseif ( $this -> zone ) {
2021-09-11 13:32:10 +00:00
$this -> src = Address :: parseFTN ( sprintf ( '%d:%d/%d.%d@%s' , $this -> zone -> zone_id , $this -> fn , $this -> ff , $this -> fp , $this -> zone -> domain -> name ));
2021-08-22 01:00:41 +00:00
}
2021-06-29 10:43:29 +00:00
}
/**
* Validate details about this message
*
* @ return \Illuminate\Contracts\Validation\Validator
*/
2021-08-29 13:58:12 +00:00
public function validate () : ValidatorResult
2021-06-29 10:43:29 +00:00
{
// Check lengths
$validator = Validator :: make ([
'user_from' => $this -> user_from ,
'user_to' => $this -> user_to ,
'subject' => $this -> subject ,
2022-01-15 02:06:15 +00:00
'onode' => $this -> ff ,
'dnode' => $this -> tf ,
'onet' => $this -> fn ,
'dnet' => $this -> tn ,
2021-06-29 10:43:29 +00:00
'flags' => $this -> flags ,
'cost' => $this -> cost ,
'echoarea' => $this -> echoarea ,
2021-07-15 14:54:23 +00:00
'ozone' => $this -> fz ,
'dzone' => $this -> tz ,
2021-06-29 10:43:29 +00:00
],[
'user_from' => 'required|min:1|max:' . self :: USER_FROM_LEN ,
'user_to' => 'required|min:1|max:' . self :: USER_TO_LEN ,
2021-07-18 12:10:21 +00:00
'subject' => 'present|max:' . self :: SUBJECT_LEN ,
2022-01-15 02:06:15 +00:00
'onode' => [ 'required' , new TwoByteIntegerWithZero ],
'dnode' => [ 'required' , new TwoByteIntegerWithZero ],
2021-06-29 10:43:29 +00:00
'onet' => [ 'required' , new TwoByteInteger ],
'dnet' => [ 'required' , new TwoByteInteger ],
'flags' => 'required|numeric' ,
'cost' => 'required|numeric' ,
'echoarea' => 'nullable|max:' . self :: AREATAG_LEN ,
2021-08-29 13:58:12 +00:00
'ozone' => [ 'required' ],
'dzone' => [ 'required' ]
2021-06-29 10:43:29 +00:00
]);
2021-08-29 13:58:12 +00:00
$validator -> after ( function ( $validator ) {
2023-09-20 10:29:23 +00:00
if ( $this -> zone -> domain -> flatten ) {
if ( ! $this -> zone -> domain -> zones -> pluck ( 'zone_id' ) -> contains ( $this -> fz ))
$validator -> errors () -> add ( 'invalid-zone' , sprintf ( 'Message zone [%d] doesnt match any zone in domain for packet zone [%d].' , $this -> fz , $this -> zone -> zone_id ));
} else {
if ( $this -> zone -> zone_id !== $this -> fz )
$validator -> errors () -> add ( 'invalid-zone' , sprintf ( 'Message zone [%d] doesnt match packet zone [%d].' , $this -> fz , $this -> zone -> zone_id ));
}
2021-08-29 13:58:12 +00:00
if ( ! $this -> fboss_o )
$validator -> errors () -> add ( 'from' , sprintf ( 'Undefined Node [%s] sent message.' , $this -> fboss ));
if ( ! $this -> tboss_o )
$validator -> errors () -> add ( 'to' , sprintf ( 'Undefined Node [%s] for destination.' , $this -> tboss ));
});
2021-07-15 14:54:23 +00:00
2021-06-29 10:43:29 +00:00
if ( $validator -> fails ())
$this -> errors = $validator ;
return $validator ;
}
}