[0x00,'v',2], 'dnode'=>[0x02,'v',2], 'y'=>[0x04,'v',2], 'm'=>[0x06,'v',2], 'd'=>[0x08,'v',2], 'H'=>[0x0a,'v',2], 'M'=>[0x0c,'v',2], 'S'=>[0x0e,'v',2], 'baud'=>[0x10,'v',2], 'pktver'=>[0x12,'v',2], 'onet'=>[0x14,'v',2], 'dnet'=>[0x16,'v',2], 'prodcode-lo'=>[0x18,'C',1], 'prodrev-maj'=>[0x19,'C',1], ]; // Second part of header private $pack2 = [ 'qozone'=>[0x22,'v',2], 'qdzone'=>[0x24,'v',2], 'filler'=>[0x26,'v',2], 'capvalid'=>[0x28,'v',2], 'prodcode-hi'=>[0x2a,'C',1], 'prodrev-min'=>[0x2b,'C',1], 'capword'=>[0x2c,'v',1], 'ozone'=>[0x2e,'v',2], 'dzone'=>[0x30,'v',2], 'opoint'=>[0x32,'v',2], 'dpoint'=>[0x34,'v',2], ]; public function __construct(string $file=NULL) { $this->messages = collect(); if ($file) { $this->filename = $file; return $this->OpenFile($file); } } public function __get($k) { switch ($k) { case 'fz': return ftn_address_split($this->pktsrc,'z'); case 'fn': return ftn_address_split($this->pktsrc,'n'); case 'ff': return ftn_address_split($this->pktsrc,'f'); case 'fp': return ftn_address_split($this->pktsrc,'p'); case 'tz': return ftn_address_split($this->pktdst,'z'); case 'tn': return ftn_address_split($this->pktdst,'n'); case 'tf': return ftn_address_split($this->pktdst,'f'); case 'tp': return ftn_address_split($this->pktdst,'p'); default: return isset($this->{$k}) ? $this->{$k} : NULL; } } // @note - messages in this object have the same next destination 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 */ 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); } public function dump() { return hex_dump((string)$this); } /** * Open a packet file * * @param string $file * @throws InvalidFidoPacketException */ private function OpenFile(string $file) { $f = fopen($file,'r'); // $fstat = fstat($f); // PKT Header $header = fread($f,0x3a); // Could not read header if (strlen($header) != 0x3a) throw new InvalidFidoPacketException('Length of Header too short: '.$file); // Not a type 2 packet if (array_get(unpack('vv',substr($header,0x12)),'v') != 2) throw new InvalidFidoPacketException('Not a type 2 packet in file: '. $file); $this->parseHeader($header); while (! feof($f)) { $x = fread($f,2); // End of Packet? if (strlen($x) == 2 and $x == "\00\00") { break; } // Messages start with 02H 00H if (strlen($x) == 2 AND $x != "\02\00") throw new InvalidFidoPacketException('Not a valid packet: '.bin2hex($x)); // No message attached else if (! strlen($x)) break; $message = new FTNMessage(fread($f,0xc)); $message->date = Carbon::createFromFormat('d M y H:i:s',$this->readnullfield($f)); $message->to = $this->readnullfield($f); $message->from = $this->readnullfield($f); $message->subject = $this->readnullfield($f); $message->parsemessage($this->readnullfield($f)); $this->messages->push($message); } } private function readnullfield($f) { $result = ''; while (($x = fgetc($f) OR strlen($x)) AND $x !== "\00") { $result .= $x; } return $result; } private function parseHeader(string $header) { $result1 = unpack($this->unpackheader($this->pack1),substr($header,0,0x1a)); $this->password = array_get(unpack('a*p',substr($header,0x1a,8)),'p'); $result2 = unpack($this->unpackheader($this->pack2),substr($header,0x22,0x14)); $this->proddata = array_get(unpack('A*p',substr($header,0x36,4)),'p'); // @todo replcae these vars with the tz/fz $this->sz = array_get($result2,'ozone'); $this->sn = array_get($result1,'onet'); $this->sf = array_get($result1,'onode'); $this->sp = array_get($result2,'dpoint'); $this->pktsrc = sprintf('%s:%s/%s.%s', $this->sz, $this->sn, $this->sf, $this->sp ); $this->dz = array_get($result2,'dzone'); $this->dn = array_get($result1,'dnet'); $this->df = array_get($result1,'dnode'); $this->dp = array_get($result2,'dpoint'); $this->pktdst = sprintf('%s:%s/%s.%s', $this->dz, $this->dn, $this->df, $this->dp ); $this->date = Carbon::create( array_get($result1,'y'), array_get($result1,'m'), array_get($result1,'d'), array_get($result1,'H'), array_get($result1,'M'), array_get($result1,'S') ); $this->baud = array_get($result1,'baud'); $this->pktver = array_get($result1,'pktver'); $this->software['prodcode-lo'] = array_get($result1,'prodcode-lo'); $this->software['prodcode-hi'] = array_get($result2,'prodcode-hi'); $this->software['rev-maj'] = array_get($result1,'prodrev-maj'); $this->software['rev-min'] = array_get($result2,'prodrev-min'); $this->cap['valid'] = array_get($result2,'capvalid'); $this->cap['word'] = array_get($result2,'capword'); // @todo filler } }