'CHRS: ', 'charset' => 'CHARSET: ', 'codepage' => 'CODEPAGE: ', 'pid' => 'PID: ', 'tid' => 'TID: ', ]; public function __construct(string $header) { // Initialise vars $this->kludge = collect(); // The message kludge lines $this->path = collect(); // The message PATH lines $this->seenby = collect(); // The message SEEN-BY lines $this->via = collect(); // The path the message has gone using Via lines $this->_other = collect(); // Temporarily hold attributes we dont process yet. $this->unknown = collect(); // Temporarily hold attributes we have no logic for. // FTS-0001.016 Message header 12 bytes // node, net, flags, cost $struct = [ 'onode'=>[0x00,'v',2], 'dnode'=>[0x02,'v',2], 'onet'=>[0x04,'v',2], 'dnet'=>[0x06,'v',2], 'flags'=>[0x08,'v',2], 'cost'=>[0x0a,'v',2], ]; $result = unpack($this->unpackheader($struct),$header); $this->src = sprintf('%s/%s',array_get($result,'onet'),array_get($result,'onode')); $this->dst = sprintf('%s/%s',array_get($result,'dnet'),array_get($result,'dnode')); $this->flags = array_get($result,'flags'); $this->cost = array_get($result,'cost'); } public function __get($k) { return $this->{$k}; } public function __set($k,$v) { switch ($k) { case 'message': // Remove DOS \n\r $v = preg_replace("/\n\r/","\r",$v); $this->parsemessage($v); break; case 'origin': $this->parseorigin($v); break; default: $this->{$k} = $v; } } public function parsemessage(string $message) { // Split out the lines $result = collect(explode("\01",$message))->filter(); foreach ($result as $k => $v) { // Search for \r - if that is the end of the line, then its a kludge $x = strpos($v,"\r"); // If there are more characters, then put the kludge back into the result, so that we process it. if ($x != strlen($v)-1) { /** * Anything after the origin line is also kludge data. */ if ($y = strpos($v,"\r * Origin: ")) { $this->message .= substr($v,$x+1,$y-$x-1); $this->__set('origin',substr($v,$y)); $matches = []; preg_match('/^.*\((.*)\)$/',$this->origin,$matches); if (($this->type() == 'Netmail' AND array_get($matches,1) != $this->fqfa) OR ! array_get($matches,1)) throw new InvalidFidoPacketException(sprintf('Source address mismatch? [%s,%s]',$this->fqfa,array_get($matches,1))); $this->fqfa = array_get($matches,1); } $v = substr($v,0,$x+1); } foreach ($this->_kludge as $a => $b) { if ($t = $this->kludge($b, $v)) { $this->kludge->put($a,$t); break; } } if ($t) continue; if ($t = $this->kludge('AREA:',$v)) $this->echoarea = $t; // From point: "FMPT elseif ($t = $this->kludge('FMPT ',$v)) $this->_other->push($t); /* * 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. * * "INTL "" " */ elseif ($t = $this->kludge('INTL ',$v)) { $this->intl = $t; list($this->fqfd,$this->fqfa) = explode(' ',$t); } elseif ($t = $this->kludge('MSGID: ',$v)) $this->msgid = $t; elseif ($t = $this->kludge('PATH: ',$v)) $this->path->push($t); elseif ($t = $this->kludge('REPLY: ',$v)) $this->reply = $t; // To Point: TOPT elseif ($t = $this->kludge('TOPT ',$v)) $this->_other->push($t); // Time Zone of the sender. elseif ($t = $this->kludge('TZUTC: ',$v)) $this->tzutc= $t; // Via @YYYYMMDD.HHMMSS[.Precise][.Time Zone] [Serial Number] elseif ($t = $this->kludge('Via ',$v)) $this->via->push($t); // We got a kludge line we dont know about else { $this->unknown->push(chop($v,"\r")); //dd(['v'=>$v,'t'=>$t]); } } } /** * Process the data after the ORIGIN * There may be kludge lines after the origin - notably SEEN-BY * * @param string $message */ public function parseorigin(string $message) { // Split out each line $result = collect(explode("\r",$message))->filter(); foreach ($result as $k => $v) { 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); elseif ($t = $this->kludge(' \* Origin: ',$v)) $this->origin = $t; // We got unknown Kludge lines in the origin else { $this->unknown->push($v); //dd(['v'=>$v,'t'=>$t,'message'=>$message]); } } } public function description() { switch ($this->type()) { case 'Echomail': return sprintf('Echomail: '.$this->echoarea); case 'Netmail': return sprintf('Netmail: %s->%s',$this->fqfa,$this->fqfd); default: return 'UNKNOWN'; } } public function type() { if ($this->echoarea) return 'Echomail'; if ($this->intl) return 'Netmail'; return 'UNKNOWN'; } }