From aae551aacf60866adf2b5cd66b00945c00117494 Mon Sep 17 00:00:00 2001 From: Deon George Date: Wed, 13 Dec 2023 23:00:47 +1100 Subject: [PATCH] Simplify packet processing. Re-enable pkt processing tests. --- app/Classes/FTN/Message.php | 2 +- app/Classes/FTN/Packet.php | 131 +++----- .../test/mail/018A-1701955391-71c7a400.pkt | Bin 0 -> 107487 bytes .../test/mail/018A-1702393055-78754407.pkt | Bin 0 -> 1085 bytes .../test/mail/0B39-1701919239-65713a06.pkt | Bin 0 -> 11325 bytes .../app/test/mail}/test_binary_content-2.pkt | Bin .../app/test/mail}/test_binary_content.pkt | Bin .../mail}/test_msg_with_soh_in_origin.pkt | Bin .../app/test/mail}/test_msgid_origin.pkt | Bin .../app/test/mail}/test_nomsgid_noorigin.pkt | Bin .../app/test/mail}/test_nomsgid_origin.pkt | Bin 495 -> 495 bytes tests/Feature/PacketTest.php | 285 +++++++++++++----- 12 files changed, 255 insertions(+), 163 deletions(-) create mode 100644 storage/app/test/mail/018A-1701955391-71c7a400.pkt create mode 100644 storage/app/test/mail/018A-1702393055-78754407.pkt create mode 100644 storage/app/test/mail/0B39-1701919239-65713a06.pkt rename {tests/Feature/data => storage/app/test/mail}/test_binary_content-2.pkt (100%) rename {tests/Feature/data => storage/app/test/mail}/test_binary_content.pkt (100%) rename {tests/Feature/data => storage/app/test/mail}/test_msg_with_soh_in_origin.pkt (100%) rename {tests/Feature/data => storage/app/test/mail}/test_msgid_origin.pkt (100%) rename {tests/Feature/data => storage/app/test/mail}/test_nomsgid_noorigin.pkt (100%) rename {tests/Feature/data => storage/app/test/mail}/test_nomsgid_origin.pkt (61%) diff --git a/app/Classes/FTN/Message.php b/app/Classes/FTN/Message.php index e6f3e35..5fad2e0 100644 --- a/app/Classes/FTN/Message.php +++ b/app/Classes/FTN/Message.php @@ -565,7 +565,7 @@ class Message extends FTNBase $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)); + Log::error(sprintf('%s:! Error bad packet header',self::LOGKEY),['e'=>$e->getMessage(),'header'=>substr($msg,0,self::HEADER_LEN)]); $validator = Validator::make([ 'header' => substr($msg,0,self::HEADER_LEN), ],[ diff --git a/app/Classes/FTN/Packet.php b/app/Classes/FTN/Packet.php index be9f7b3..ffda3d5 100644 --- a/app/Classes/FTN/Packet.php +++ b/app/Classes/FTN/Packet.php @@ -22,8 +22,8 @@ class Packet extends FTNBase implements \Iterator, \Countable { private const LOGKEY = 'PKT'; - private const BLOCKSIZE = 1024; protected const PACKED_MSG_LEAD = "\02\00"; + protected const PACKED_END = "\00\00"; public const regex = '([[:xdigit:]]{4})(?:-(\d{4,10}))?-(.+)'; @@ -172,7 +172,7 @@ class Packet extends FTNBase implements \Iterator, \Countable /** * Process a packet file * - * @param mixed $f + * @param mixed $f File handler returning packet data * @param string $name * @param int $size * @param Domain|null $domain @@ -213,18 +213,18 @@ class Packet extends FTNBase implements \Iterator, \Countable $o->name = $name; $x = fread($f,2); + if (strlen($x) === 2) { + // End of Packet? + if ($x === "\00\00") + return $o; - // End of Packet? - if ((strlen($x) === 2) && ($x === "\00\00")) - return $o; - - // Messages start with self::PACKED_MSG_LEAD - if ((strlen($x) === 2) && ($x !== self::PACKED_MSG_LEAD)) - throw new InvalidPacketException('Not a valid packet: '.bin2hex($x)); + // Messages start with self::PACKED_MSG_LEAD + elseif ($x !== self::PACKED_MSG_LEAD) + throw new InvalidPacketException('Not a valid packet: '.bin2hex($x)); // No message attached - else if (! strlen($x)) - throw new InvalidPacketException('No message in packet: '.bin2hex($x)); + } else + throw new InvalidPacketException('Not a valid packet, not EOP or SOM'.bin2hex($x)); // Work out the packet zone if ($o->fz && ($o->fd || $domain)) { @@ -250,98 +250,36 @@ class Packet extends FTNBase implements \Iterator, \Countable ->singleOrFail(); } - $buf_ptr = 0; // Pointer to the end of the current message $message = ''; // Current message we are building - $readbuf = ''; // What we are reading to determine the message contents - $last = ''; // If during buffer reads, the end of the last buffer had our NULL end of message marker + $msgbuf = ''; + $leader = Message::HEADER_LEN+strlen(self::PACKED_MSG_LEAD); // We loop through reading from the buffer, to find our end of message tag - while ($buf_ptr || (! feof($f) && ($readbuf=fread($f,self::BLOCKSIZE)))) { - if (! $buf_ptr) - $read_ptr = ftell($f); + while ((! feof($f) && ($readbuf=fread($f,$leader)))) { + $read_ptr = ftell($f); + $msgbuf .= $readbuf; - // Packed messages are Message::HEADER_LEN, prefixed with self::PACKED_MSG_LEAD - // If we havent got our header yet - if (strlen($message) < (Message::HEADER_LEN+strlen(self::PACKED_MSG_LEAD))) { - $addchars = (Message::HEADER_LEN+strlen(self::PACKED_MSG_LEAD))-strlen($message); - $message .= substr($readbuf,$buf_ptr,$addchars); - $buf_ptr += $addchars; + // See if we have our EOM/EOP marker + if ((($end=strpos($msgbuf,"\x00".self::PACKED_MSG_LEAD,$leader)) !== FALSE) + || (($end=strpos($msgbuf,"\x00".self::PACKED_END,$leader)) !== FALSE)) + { + // Parse our message + $o->parseMessage(substr($msgbuf,0,$end)); - // If our buffer wasnt big enough, and thus $addchars didnt have enough chars to add. - if ($buf_ptr >= strlen($readbuf)) { - $buf_ptr = 0; - continue; - } + $msgbuf = substr($msgbuf,$end+3); + continue; + + // If we have more to read + } elseif ($read_ptr < $size) { + continue; } - // Take 2 chars from the buffer and check if we have our end packet signature - // $last is set, because we detected a NULL, so we'll add two more chars and see if we have our EOM - // signature. Some of those chars may belong to the next message, if $last has more than 1 NULL. - if ($last && ($buf_ptr === 0)) { - $last .= substr($readbuf,0,2); - - if (($end=strpos($last,"\x00".self::PACKED_MSG_LEAD,$buf_ptr)) !== FALSE) { - $o->parseMessage(substr($message,0,$end-2)); - $last = ''; - $message = ''; - $buf_ptr = 1+$end; - - // Loop to rebuild our header for the next message - continue; - } - - // We didnt have an EOM marker - $last = ''; - } - - // See if our EOM marker is in the read buffer - if (($end=strpos($readbuf,"\x00".self::PACKED_MSG_LEAD,$buf_ptr)) === FALSE) { - // Just in case our packet break is at the end of the buffer - $last = substr($readbuf,-2); - - // We have an EOM or EOP marker here, so loop around to get the next read - if (str_contains($last,"\x00") && ($size < $read_ptr)) { - $message .= substr($readbuf,$buf_ptr); - $buf_ptr = 0; - - continue; - } - - // No EOM marker - $last = ''; - - // See if we have an EOP marker - $end = strpos($readbuf,"\x00\x00\x00",$buf_ptr); - } - - // See if we have found the end of the packet, if not read more. - if ($end === FALSE) { - if ($read_ptr < $size) { - $message .= substr($readbuf,$buf_ptr); - $buf_ptr = 0; - - continue; - - // No more to read, so the packet is bad - } else - throw new InvalidPacketException(sprintf('Cannot determine END of message/packet: %s|%s',get_class($o),hex_dump($message))); - - } else { - $message .= substr($readbuf,$buf_ptr,$end-$buf_ptr); - $buf_ptr = $end+3; - - if ($buf_ptr >= strlen($readbuf)) - $buf_ptr = 0; - } - - // Look for the next message - $o->parseMessage($message); - $message = ''; + // If we get here + throw new InvalidPacketException(sprintf('Cannot determine END of message/packet: %s|%s',get_class($o),hex_dump($message)));; } - // If our message is still set, then we have an unprocessed message - if ($message) - $o->parseMessage($message); + if ($msgbuf) + throw new InvalidPacketException(sprintf('Unprocessed data in packet: %s|%s',get_class($o),hex_dump($msgbuf))); return $o; } @@ -546,4 +484,9 @@ class Packet extends FTNBase implements \Iterator, \Countable $this->messages->push($msg); } + + public function pluck(string $key): Collection + { + return $this->messages->pluck($key); + } } \ No newline at end of file diff --git a/storage/app/test/mail/018A-1701955391-71c7a400.pkt b/storage/app/test/mail/018A-1701955391-71c7a400.pkt new file mode 100644 index 0000000000000000000000000000000000000000..fa3b352397e8b311513aeb6fd460589f2fa9be8c GIT binary patch literal 107487 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 Y*D;HESw-L1{rzgzzGm%zVC@@?FZu&U?EnA( literal 0 HcmV?d00001 diff --git a/storage/app/test/mail/018A-1702393055-78754407.pkt b/storage/app/test/mail/018A-1702393055-78754407.pkt new file mode 100644 index 0000000000000000000000000000000000000000..eeb52025e202cdff8d1c8d687238587c0e7c8dc2 GIT binary patch literal 1085 zcmaJ=O>fgc6tv3$0a}S;k0WtuqbA-DJ8Sy@o&-T~5HttP!Ow6o9E~S0PS5TI_m~aB z#}7hAZS2>?9kSMA{Yq(!-5lBgGRu=B*I97ks(M!ir)BN%(z!2g8x(a}f8Am831@=~ zY|BNpLAAz`IQ#H+Hp=xnhluWT&B%gLKp?{lbJ>nw_O3aRv58m&e6#&ZKs3*gcb``|(%2Jqx(6OX||9%n%(0v=Fp3YKN zGBA?oLEo5&ThOVzWowhEf=Oe<>?h_`ipiSDjP*3oD$Sl!D5DiS_x$99VdEygLu4)6 z@>B6`a8nx|KuONRUlNXpJzFGwZ~#hW6lnKje=`QNvwV*ugg}+FvuO{0GnBpdzF#SL G>3;z%!uT=( literal 0 HcmV?d00001 diff --git a/storage/app/test/mail/0B39-1701919239-65713a06.pkt b/storage/app/test/mail/0B39-1701919239-65713a06.pkt new file mode 100644 index 0000000000000000000000000000000000000000..f28a37d8e3bbc9f593a6f6da4ac423229abf8bbe GIT binary patch literal 11325 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 diff --git a/tests/Feature/PacketTest.php b/tests/Feature/PacketTest.php index 66b2fca..860f0ee 100644 --- a/tests/Feature/PacketTest.php +++ b/tests/Feature/PacketTest.php @@ -2,132 +2,279 @@ namespace Tests\Feature; +use Illuminate\Foundation\Testing\DatabaseTransactions; +use Illuminate\Support\Facades\Storage; use Tests\TestCase; use App\Classes\File; -use App\Classes\FTN\Packet; +use App\Classes\FTN\{Message,Packet}; use App\Models\{Address,Domain,System,Zone}; class PacketTest extends TestCase { - private System $so; + use DatabaseTransactions; + + //private System $so; private Domain $do; - private function init() + private function init_fidonet(): void { - System::unguard(); - Domain::unguard(); - $this->so = System::firstOrCreate(['name'=>'test','sysop'=>'sysop','location'=>'location','active'=>TRUE]); - $this->do = Domain::firstOrCreate(['name'=>'packets','active'=>TRUE]); + $do = new Domain; + $do->active = TRUE; + $do->name = 'fidonet'; + $do->flatten = TRUE; + $do->save(); + + $zo = new Zone; + $zo->zone_id = 1; + $zo->active = TRUE; + $zo->system_id = 2; + $zo->default = TRUE; + $do->zones()->save($zo); + + $zo = new Zone; + $zo->zone_id = 2; + $zo->active = TRUE; + $zo->system_id = 3; + $zo->default = TRUE; + $do->zones()->save($zo); + + $zo = new Zone; + $zo->zone_id = 3; + $zo->active = TRUE; + $zo->system_id = 1; + $zo->default = TRUE; + $do->zones()->save($zo); + + Address::createFTN('3:633/280@fidonet',System::findOrFail(1)); + Address::createFTN('3:633/2744@fidonet',System::findOrFail(2)); + + $this->do = $do; } - public function test_null() + private function init_fsxnet(): void { - return $this->assertTrue(TRUE); + $do = new Domain; + $do->active = TRUE; + $do->name = 'fsxnet'; + $do->flatten = TRUE; + $do->save(); + + $zo = new Zone; + $zo->zone_id = 21; + $zo->active = TRUE; + $zo->system_id = 1; + $zo->default = TRUE; + $do->zones()->save($zo); + + Address::createFTN('21:3/100@fsxnet',System::findOrFail(2)); + Address::createFTN('21:3/2744@fsxnet',System::findOrFail(3)); + Address::createFTN('21:3/184@fsxnet',System::findOrFail(4)); + + $this->do = $do; + } + + private function init_private(): void + { + $do = new Domain; + $do->active = TRUE; + $do->name = 'private'; + $do->flatten = TRUE; + $do->save(); + + $zo = new Zone; + $zo->zone_id = 10; + $zo->active = TRUE; + $zo->system_id = 1; + $zo->default = TRUE; + $do->zones()->save($zo); + + Address::createFTN('10:1/1@private',System::findOrFail(3)); + Address::createFTN('10:1/2@private',System::findOrFail(4)); + + $this->do = $do; + } + + private function mail_file(string $file) + { + $fs = Storage::disk(config('fido.local_disk')); + + return new File($fs->path(sprintf('%s/%s',config('fido.dir'),$file))); + } + + public function test_packet_1() + { + $this->init_fidonet(); + + $f = $this->mail_file('mail/018A-1702393055-78754407.pkt'); + + foreach ($f as $packet) { + $pkt = Packet::process($packet,$x=$f->itemName(),$f->itemSize(),$this->do); + + $this->assertInstanceOf(Packet\FSC39::class,$pkt); + $this->assertEquals('ABCDEFGH',$pkt->password); + $this->assertEquals(1,count($pkt)); + $this->assertEquals('3634/27 12 154/10 221/6 218/840 770/1 633/280',$pkt->messages[0]->path[0]); + $this->assertEquals('1/120 18/0 116/116 120/616 123/0 10 25 126 160 180 200 525 755 3001',$pkt->messages[0]->seenby[0]); + $this->assertEquals('*The Gate BBS*Shelby, NC USA*thegateb.synchro.net* (1:3634/27)',$pkt->messages[0]->origin); + $this->assertEquals('1:3634/27.0@fidonet',$pkt->messages[0]->fftn); + $this->assertEquals('1:633/2744.0@fidonet',$pkt->messages[0]->tftn); + $this->assertEquals('Gate Keeper',$pkt->messages[0]->user_from); + $this->assertEquals('Meitsi',$pkt->messages[0]->user_to); + $this->assertEquals('Status of HAM radio in FidoNet',$pkt->messages[0]->subject); + $this->assertEquals('275.fidonet_ham@1:3634/27 29ddab74',$pkt->messages[0]->msgid); + $this->assertEquals('1:229/428 012c0322',$pkt->messages[0]->replyid); + $this->assertEquals('bc291b3ea15750c2d0c39c9221093901',md5($pkt->messages[0]->message)); + } + } + + public function test_packet_2() + { + $this->init_fidonet(); + + $f = $this->mail_file('mail/018A-1701955391-71c7a400.pkt'); + + foreach ($f as $packet) { + $pkt = Packet::process($packet,$x=$f->itemName(),$f->itemSize(),$this->do); + + $this->assertInstanceOf(Packet\FSC39::class,$pkt); + $this->assertEquals('ABCDEFGH',$pkt->password); + $this->assertEquals(43,count($pkt)); + $this->assertEquals('229/664 426 633/280',$pkt->messages[0]->path[0]); + $this->assertEquals('15/0 106/201 128/260 129/305 153/7715 218/700 221/6 226/30 227/114',$pkt->messages[0]->seenby[0]); + $this->assertEquals('Northern Realms (1:229/664)',$pkt->messages[0]->origin); + $this->assertEquals('1:229/664.0@fidonet',$pkt->messages[0]->fftn); + $this->assertEquals('1:633/2744.0@fidonet',$pkt->messages[0]->tftn); + $this->assertEquals('Northern Realms',$pkt->messages[0]->user_from); + $this->assertEquals('All',$pkt->messages[0]->user_to); + $this->assertEquals('NYPost Daily Horoscope',$pkt->messages[0]->subject); + $this->assertEquals('1:229/664 56db6f89',$pkt->messages[0]->msgid); + $this->assertEquals('',$pkt->messages[0]->replyid); + $this->assertEquals('b8c33987647e88f86456f0e571e98398',md5($pkt->messages[0]->message)); + } + } + + public function test_packet_3() + { + $this->init_fsxnet(); + + $f = $this->mail_file('mail/0B39-1701919239-65713a06.pkt'); + + foreach ($f as $packet) { + $pkt = Packet::process($packet,$x=$f->itemName(),$f->itemSize(),$this->do); + + $this->assertInstanceOf(Packet\FSC48::class,$pkt); + $this->assertEquals('ABCDEFG#',$pkt->password); + $this->assertEquals(4,count($pkt)); + $this->assertInstanceOf(Message::class,$pkt->messages[0]); + $this->assertEquals('3/165 100 184',$pkt->messages[0]->path[0]); + $this->assertEquals('1/100 179 2/100 116 3/100 105 107 108 109 110 111 113 119 120 126 127',$pkt->messages[0]->seenby[0]); + $this->assertEquals('www.theunderground.us Telnet 10023 SSH 7771 (21:3/165)',$pkt->messages[0]->origin); + $this->assertEquals('21:3/165.0@fsxnet',$pkt->messages[0]->fftn); + $this->assertEquals('21:3/2744.0@fsxnet',$pkt->messages[0]->tftn); + $this->assertEquals('ibbslastcall',$pkt->messages[0]->user_from); + $this->assertEquals('All',$pkt->messages[0]->user_to); + $this->assertEquals('ibbslastcall-data',$pkt->messages[0]->subject); + $this->assertEquals('21:3/165 6b42fe09',$pkt->messages[0]->msgid); + $this->assertEquals('',$pkt->messages[0]->replyid); + $this->assertEquals('aa6bc82b63355ab68889f61822305072',md5($pkt->messages[0]->message)); + } } - /* - * @todo This packet doesnt have an origin line, need a new one with nomsg and an origin line public function test_nomsgid_origin() { - $this->init(); + $this->init_private(); - Zone::unguard(); - Address::unguard(); - $zo = Zone::firstOrCreate(['zone_id'=>21,'default'=>TRUE,'active'=>TRUE,'domain_id'=>$this->do->id,'system_id'=>$this->so->id]); - $src = Address::firstOrCreate(['zone_id'=>$zo->id,'region_id'=>0,'host_id'=>3,'node_id'=>151,'point_id'=>0,'role'=>Address::NODE_ACTIVE,'active'=>TRUE,'system_id'=>$this->so->id]); - $hub = Address::firstOrCreate(['zone_id'=>$zo->id,'region_id'=>0,'host_id'=>1,'node_id'=>1,'point_id'=>0,'role'=>Address::NODE_ACTIVE,'active'=>TRUE,'system_id'=>$this->so->id]); - $ao = Address::firstOrCreate(['zone_id'=>$zo->id,'region_id'=>0,'host_id'=>1,'node_id'=>4,'point_id'=>0,'role'=>Address::NODE_ACTIVE,'active'=>TRUE,'system_id'=>$this->so->id]); - - // This packet has an incorrect zone in the Origin - $f = new File(__DIR__.'/data/test_nomsgid_origin.pkt'); + $f = $this->mail_file('mail/test_nomsgid_origin.pkt'); foreach ($f as $packet) { $pkt = Packet::process($packet,$f->itemName(),$f->itemSize()); + $this->assertInstanceOf(Packet\FSC39::class,$pkt); $this->assertEquals(1,$pkt->count()); - $messages = FALSE; foreach ($pkt as $msg) { - $messages = TRUE; $this->assertNotTrue($msg->isNetmail()); - $this->assertNotFalse($msg->pathaddress->search($hub->id)); - $this->assertCount(0,$msg->rogue_seenby); + $this->assertEquals('PVT_TEST',$msg->echoarea); } - $this->assertTrue($messages); + $this->assertInstanceOf(Message::class,$pkt->messages[0]); + $this->assertEquals('1/1 999/1',$pkt->messages[0]->path[0]); + $this->assertEquals('Daytona BBS (Netherlands) (10:3/151)',$pkt->messages[0]->origin); + $this->assertEquals('10:3/151.0@private',$pkt->messages[0]->fftn); + $this->assertEquals('10:999/999.0@private',$pkt->messages[0]->tftn); + $this->assertEquals('Hub Robot',$pkt->messages[0]->user_from); + $this->assertEquals('Henk Hilgersum',$pkt->messages[0]->user_to); + $this->assertEquals('LEMEIN',$pkt->messages[0]->subject); + $this->assertEquals('',$pkt->messages[0]->msgid); + $this->assertEquals('',$pkt->messages[0]->replyid); + $this->assertEquals('6bbe3d0475cf60804ca4e942212d3456',md5($pkt->messages[0]->message)); } } - */ /* * @todo We are not correctly setup to parse messages without an origin line public function test_nomsgid_noorigin() { - $this->init(); - - Zone::unguard(); - Address::unguard(); - $zo = Zone::firstOrCreate(['zone_id'=>10,'default'=>TRUE,'active'=>TRUE,'domain_id'=>$this->do->id,'system_id'=>$this->so->id]); - $src = Address::firstOrCreate(['zone_id'=>$zo->id,'region_id'=>0,'host_id'=>999,'node_id'=>1,'point_id'=>0,'role'=>Address::NODE_ACTIVE,'active'=>TRUE,'system_id'=>$this->so->id]); + $this->init_private(); // This packet has no Origin Line - $f = new File(__DIR__.'/data/test_nomsgid_noorigin.pkt'); + $f = $this->mail_file('mail/test_nomsgid_noorigin.pkt'); foreach ($f as $packet) { $pkt = Packet::process($packet,$f->itemName(),$f->itemSize(),$this->do); + $this->assertInstanceOf(Packet\FSC39::class,$pkt); $this->assertEquals(1,$pkt->count()); - $messages = FALSE; foreach ($pkt as $msg) { - $messages = TRUE; + dump($msg); $this->assertNotTrue($msg->isNetmail()); $this->assertNotFalse($msg->path->search('1/1 999/1')); $this->assertNotFalse($msg->seenby->search('1/1 4 3/0 999/1 999')); } - - $this->assertTrue($messages); } } */ - /* public function test_msgid_origin() { - $this->init(); - - Zone::unguard(); - Address::unguard(); - $zo = Zone::firstOrCreate(['zone_id'=>10,'default'=>TRUE,'active'=>TRUE,'domain_id'=>$this->do->id,'system_id'=>$this->so->id]); - $src = Address::firstOrCreate(['zone_id'=>$zo->id,'region_id'=>0,'host_id'=>999,'node_id'=>1,'point_id'=>0,'role'=>Address::NODE_ACTIVE,'active'=>TRUE,'system_id'=>$this->so->id]); + $this->init_private(); // This packet has an incorrect zone in the Origin - $f = new File(__DIR__.'/data/test_msgid_origin.pkt'); + $f = $this->mail_file('mail/test_msgid_origin.pkt'); foreach ($f as $packet) { $pkt = Packet::process($packet,$f->itemName(),$f->itemSize(),$this->do); + $this->assertInstanceOf(Packet\FSC39::class,$pkt); $this->assertEquals(1,$pkt->count()); - $messages = FALSE; foreach ($pkt as $msg) { - $messages = TRUE; $this->assertNotTrue($msg->isNetmail()); + $this->assertEquals('PVT_TEST',$msg->echoarea); $this->assertNotFalse($msg->seenby->search('1/1 999/1 999')); } - $this->assertTrue($messages); + $this->assertInstanceOf(Message::class,$pkt->messages[0]); + $this->assertEquals('999/1',$pkt->messages[0]->path[0]); + $this->assertEquals('Alterant MailHUB at your service (10:999/1)',$pkt->messages[0]->origin); + $this->assertEquals('10:999/1.0@private',$pkt->messages[0]->fftn); + $this->assertEquals('10:999/999.0@private',$pkt->messages[0]->tftn); + $this->assertEquals('Hub Robot',$pkt->messages[0]->user_from); + $this->assertEquals('deon',$pkt->messages[0]->user_to); + $this->assertEquals('Hub Robot Report',$pkt->messages[0]->subject); + $this->assertEquals('10:999/1 612aecda',$pkt->messages[0]->msgid); + $this->assertEquals('',$pkt->messages[0]->replyid); + $this->assertEquals('61078e680cda04c8b5eba0f712582e70',md5($pkt->messages[0]->message)); } } - public function test_packet_parse() + public function test_soh_char_soa() { - $this->init(); - - $zo = Zone::firstOrCreate(['zone_id'=>21,'default'=>TRUE,'active'=>TRUE,'domain_id'=>$this->do->id,'system_id'=>$this->so->id]); + $this->init_fsxnet(); // This packet has a SOHSOH sequence - $f = new File(__DIR__.'/data/test_binary_content-2.pkt'); + $f = $this->mail_file('mail/test_binary_content-2.pkt'); foreach ($f as $packet) { - $pkt = Packet::process($packet,$f->itemName(),$f->itemSize(),$zo->domain); + $pkt = Packet::process($packet,$f->itemName(),$f->itemSize(),$this->do); $this->assertEquals(1,$pkt->count()); @@ -147,9 +294,14 @@ class PacketTest extends TestCase $this->assertTrue($messages); } + } + + public function test_soh_message() + { + $this->init_fsxnet(); // This packet has SOH in the message content - $f = new File(__DIR__.'/data/test_binary_content.pkt'); + $f = $this->mail_file('mail/test_binary_content.pkt'); foreach ($f as $packet) { $pkt = Packet::process($packet,$f->itemName(),$f->itemSize()); @@ -171,9 +323,14 @@ class PacketTest extends TestCase $this->assertTrue($messages); } + } + + public function test_bad_zone() + { + $this->init_private(); // This packet has an incorrect zone in the Origin - $f = new File(__DIR__.'/data/test_msgid_origin.pkt'); + $f = $this->mail_file('mail/test_msgid_origin.pkt'); foreach ($f as $packet) { $pkt = Packet::process($packet,$f->itemName(),$f->itemSize()); @@ -199,17 +356,10 @@ class PacketTest extends TestCase public function test_soh_in_origin() { - $this->init(); - - Zone::unguard(); - Address::unguard(); - - $zo = Zone::firstOrCreate(['zone_id'=>3,'default'=>TRUE,'active'=>TRUE,'domain_id'=>$this->do->id,'system_id'=>$this->so->id]); - $src = Address::firstOrCreate(['zone_id'=>$zo->id,'region_id'=>0,'host_id'=>712,'node_id'=>886,'point_id'=>0,'role'=>Address::NODE_ACTIVE,'active'=>TRUE,'system_id'=>$this->so->id]); + $this->init_fidonet(); // This packet has a SOHSOH sequence - $f = new File(__DIR__.'/data/test_msg_with_soh_in_origin.pkt'); - + $f = $this->mail_file('mail/test_msg_with_soh_in_origin.pkt'); foreach ($f as $packet) { $pkt = Packet::process($packet,$f->itemName(),$f->itemSize()); @@ -251,5 +401,4 @@ class PacketTest extends TestCase $this->assertTrue($messages); } } - */ -} +} \ No newline at end of file