[0x00,'v',2], // Originating Node 'dnode' => [0x02,'v',2], // Destination Node 'y' => [0x04,'v',2], // Year 'm' => [0x06,'v',2], // Month 'd' => [0x08,'v',2], // Day 'H' => [0x0a,'v',2], // Hour 'M' => [0x0c,'v',2], // Minute 'S' => [0x0e,'v',2], // Second 'baud' => [0x10,'v',2], // Baud 'pktver' => [0x12,'v',2], // Packet Version 'onet' => [0x14,'v',2], // Originating Net (0xffff when origPoint !=0 2+) 'dnet' => [0x16,'v',2], // Destination Net 'prodcode-lo' => [0x18,'C',1], 'prodrev-maj' => [0x19,'C',1], // Product Version Major (serialNum 2) 'password' => [0x1a,'Z8',8], // Packet Password 'qozone' => [0x22,'v',2], 'qdzone' => [0x24,'v',2], 'filler' => [0x26,'v',2], // Reserved (auxnet 2+ - contains Orignet if Origin is a point) fsc-0048.001 'capvalid' => [0x28,'n',2], // fsc-0039.004 (Not used 2) (copy of 0x2c) 'prodcode-hi' => [0x2a,'C',1], // (Not used 2) 'prodrev-min' => [0x2b,'C',1], // (Not used 2) 'capword' => [0x2c,'v',2], // fsc-0039.001 (Not used 2) 'ozone' => [0x2e,'v',2], // Originating Zone (Not used 2) 'dzone' => [0x30,'v',2], // Destination Zone (Not used 2) 'opoint' => [0x32,'v',2], // Originating Point (Not used 2) 'dpoint' => [0x34,'v',2], // Destination Point (Not used 2) 'proddata' => [0x36,'A4',4], // ProdData (Not used 2) // FSC-39/FSC-48 ]; public function __construct(File $file) { $this->messages = collect(); if ($file) { $this->file = $file; $this->open($file); } } /** * @throws \Exception */ public function __get($key) { switch ($key) { // From Addresses case 'fz': return Arr::get($this->header,'ozone'); case 'fn': return Arr::get($this->header,'onet'); case 'ff': return Arr::get($this->header,'onode'); case 'fp': return Arr::get($this->header,'opoint'); case 'fd': return Arr::get($this->header,'odomain'); // To Addresses case 'tz': return Arr::get($this->header,'dzone'); case 'tn': return Arr::get($this->header,'dnet'); case 'tf': return Arr::get($this->header,'dnode'); case 'tp': return Arr::get($this->header,'dpoint'); case 'td': return Arr::get($this->header,'ddomain'); case 'date': return Carbon::create( Arr::get($this->header,'y'), Arr::get($this->header,'m')+1, Arr::get($this->header,'d'), Arr::get($this->header,'H'), Arr::get($this->header,'M'), Arr::get($this->header,'S') ); case 'capability': return Arr::get($this->header,'capword') == Arr::get($this->header,'capvalid') ? sprintf('%016b',Arr::get($this->header,'capword')) : 'FTS-1'; case 'password': return Arr::get($this->header,$key); case 'fftn': case 'tftn': return parent::__get($key); case 'software': $code = Arr::get($this->header,'prodcode-hi')<<8|Arr::get($this->header,'prodcode-lo'); Software::unguard(); $o = Software::singleOrNew(['code'=>$code,'type'=>Software::SOFTWARE_TOSSER]); Software::reguard(); return $o; case 'software_ver': return sprintf('%d.%d',Arr::get($this->header,'prodrev-maj'),Arr::get($this->header,'prodrev-min')); // Packet Type case 'type': if ((Arr::get($this->header,'onet') == 0xffff) && (Arr::get($this->header,'opoint') != 0) && Arr::get($this->header,'filler')) return '2+'; elseif (Arr::get($this->header,'prodrev-maj') && ! Arr::get($this->header,'capword')) return '2'; else return '2e'; default: throw new \Exception('Unknown key: '.$key); } } // @note - messages in this object have the same next destination // @todo To rework /* public function __toString(): string { // @todo - is this appropriate to set here $this->date = now(); $this->pktsrc = (string)$this->get_node(ftn_address_split('10:1/5.0'),TRUE); // @todo if ($this->messages->first()->type == 'echomail') $this->pktdst = (string)$this->messages->first()->fqfa->uplink; else $this->pktdst = (string)$this->messages->first()->fqda->uplink; $this->software['prodcode-lo'] = 0x00; $this->software['prodcode-hi'] = 0xde; $this->software['rev-maj'] = 0x00; $this->software['rev-min'] = 0x01; // Type 2+ Packet $this->cap['valid'] = 0x0100; $this->cap['word'] = 0x0001; $this->pktver = 0x0002; $return = $this->createHeader(); foreach ($this->messages as $o) $return .= "\02\00".(string)$o; $return .= "\00\00"; return $return; } */ /** * Create our message packet header * @todo To rework */ /* private function createHeader(): string { try { $a = pack(join('',collect($this->pack1)->pluck(1)->toArray()), $this->ff, $this->tf, $this->date->year, $this->date->month, $this->date->day, $this->date->hour, $this->date->minute, $this->date->second, $this->baud, $this->pktver, $this->fn, // @todo if point, this needs to be 0xff $this->tn, $this->software['prodcode-lo'], // @todo change to this software $this->software['rev-maj'] // @todo change to this software ); $b = pack(join('',collect($this->pack2)->pluck(1)->toArray()), 0x0000, // @note: Type 2 packet this is $this->sz, 0x0000, // @note: Type 2 packet this is $this->dz, 0x0000, // Filler $this->>sn if message to point. $this->cap['valid'], // @todo to check $this->software['prodcode-hi'], // @todo change to this software $this->software['rev-min'], // @todo change to this software $this->cap['word'], // @todo to check $this->fz, $this->tz, $this->fp, // @note: point address, type 2+ packets $this->tp // @note: point address, type 2+ packets ); return $a.pack('a8',strtoupper($this->password)).$b."mbse"; // @todo change to this software } catch (\Exception $e) { return $e->getMessage(); } } public function addMessage(FTNMessage $o) { // @todo Check that this message is for the same uplink. $this->messages->push($o); } */ /** * Open a packet file * * @param string $file * @throws InvalidPacketException */ private function open(string $file) { Log::debug(sprintf('%s:Opening Packet [%s]',self::LOGKEY,$file)); $f = fopen($file,'r'); $fstat = fstat($f); // PKT Header $header = fread($f,self::HEADER_LEN); Log::debug(sprintf("%s:\n%s",self::LOGKEY,hex_dump($header))); // Could not read header if (strlen($header) != self::HEADER_LEN) throw new InvalidPacketException(sprintf('Length of header [%d] too short'.strlen($header))); // Not a type 2 packet $version = Arr::get(unpack('vv',substr($header,self::VERSION_OFFSET)),'v'); if ($version != 2) throw new InvalidPacketException('Not a type 2 packet: '.$version); $this->header = unpack($this->unpackheader(self::v2header),$header); $x = fread($f,2); // End of Packet? if (strlen($x) == 2 and $x == "\00\00") return; // Messages start with 02H 00H if (strlen($x) == 2 AND $x != "\02\00") 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)); $buf_ptr = 0; $message = ''; $readbuf = ''; while ($buf_ptr || (! feof($f) && ($readbuf=fread($f,self::BLOCKSIZE)))) { // A message header is atleast 0x22 chars long if (strlen($readbuf) < self::PACKED_MSG_HEADER_LEN) { $message .= $readbuf; $buf_ptr = 0; continue; } elseif (strlen($message) < self::PACKED_MSG_HEADER_LEN) { $addchars = self::PACKED_MSG_HEADER_LEN-strlen($message); $message .= substr($readbuf,$buf_ptr,$addchars); $buf_ptr += $addchars; } // If we didnt find a packet end, perhaps there are no more if (($end=strpos($readbuf,"\x00\x02\x00",$buf_ptr)) === FALSE) $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 && (ftell($f) < $fstat['size'])) { $message .= substr($readbuf,$buf_ptr); $buf_ptr = 0; continue; } else { $message .= substr($readbuf,$buf_ptr,$end-$buf_ptr); $buf_ptr += $end-$buf_ptr+3; if ($buf_ptr >= strlen($readbuf)) $buf_ptr = 0; } // Look for the next message $this->messages->push(new Message($message)); $message = ''; } } }