Attempts = 5; $this->Connected = false; $this->Delay = 3; $this->Port = 8728; $this->Timeout = 3; $this->Debug = false; $this->SSL = false; $this->ErrorNo = 0; $this->ErrorStr = ""; } function EncodeLength(int $Length): string { if ($Length < 0x80) { $Length = chr($Length); } else if ($Length < 0x4000) { $Length |= 0x8000; $Length = chr(($Length >> 8) & 0xFF).chr($Length & 0xFF); } else if ($Length < 0x200000) { $Length |= 0xC00000; $Length = chr(($Length >> 16) & 0xFF).chr(($Length >> 8) & 0xFF).chr($Length & 0xFF); } else if ($Length < 0x10000000) { $Length |= 0xE0000000; $Length = chr(($Length >> 24) & 0xFF).chr(($Length >> 16) & 0xFF).chr(($Length >> 8) & 0xFF).chr($Length & 0xFF); } else if ($Length >= 0x10000000) $Length = chr(0xF0).chr(($Length >> 24) & 0xFF).chr(($Length >> 16) & 0xFF).chr(($Length >> 8) & 0xFF).chr($Length & 0xFF); return $Length; } function ConnectOnce(string $IP, string $Login, string $Password): void { if ($this->Connected) $this->Disconnect(); if ($this->SSL) { $IP = 'ssl://'.$IP; } try { $LastErrorReporting = error_reporting(); error_reporting(0); $this->Socket = @fsockopen($IP, $this->Port, $this->ErrorNo, $this->ErrorStr, $this->Timeout); if ($this->Socket) { socket_set_timeout($this->Socket, $this->Timeout); $this->Write('/login', false); $this->Write('=name=' . $Login, false); $this->Write('=password='.$Password); $Response = $this->Read(false); if ((count($Response) > 0) and ($Response[0] == '!done')) $this->Connected = true; if (!$this->Connected) fclose($this->Socket); } } finally { error_reporting($LastErrorReporting); } } function Connect(string $IP, string $Login, string $Password): bool { for ($Attempt = 1; $Attempt <= $this->Attempts; $Attempt++) { $this->ConnectOnce($IP, $Login, $Password); if ($this->Connected) break; sleep($this->Delay); } return $this->Connected; } function Disconnect(): void { if ($this->Connected) { fclose($this->Socket); $this->Connected = false; } } function ParseResponse(array $Response): array { if (is_array($Response)) { $Parsed = array(); $Current = null; $SingleValue = null; $count = 0; foreach ($Response as $x) { if (in_array($x, array( '!fatal', '!re', '!trap' ))) { if ($x == '!re') { $Current =& $Parsed[]; } else $Current =& $Parsed[$x][]; } else if ($x != '!done') { if (preg_match_all('/[^=]+/i', $x, $Matches)) { if ($Matches[0][0] == 'ret') { $SingleValue = $Matches[0][1]; } $Current[$Matches[0][0]] = (isset($Matches[0][1]) ? $Matches[0][1] : ''); } } } if (empty($Parsed) && !is_null($SingleValue)) { $Parsed = $SingleValue; } return $Parsed; } else return array(); } function ArrayChangeKeyName(array &$array): array { if (is_array($array)) { foreach ($array as $k => $v) { $tmp = str_replace("-", "_", $k); $tmp = str_replace("/", "_", $tmp); if ($tmp) { $array_new[$tmp] = $v; } else { $array_new[$k] = $v; } } return $array_new; } else { return $array; } } function Read(bool $Parse = true): array { $Line = ''; $Response = array(); while (true) { // Read the first byte of input which gives us some or all of the length // of the remaining reply. $Byte = ord(fread($this->Socket, 1)); $Length = 0; // If the first bit is set then we need to remove the first four bits, shift left 8 // and then read another byte in. // We repeat this for the second and third bits. // If the fourth bit is set, we need to remove anything left in the first byte // and then read in yet another byte. if ($Byte & 0x80) { if (($Byte & 0xc0) == 0x80) { $Length = (($Byte & 63) << 8) + ord(fread($this->Socket, 1)); } else { if (($Byte & 0xe0) == 0xc0) { $Length = (($Byte & 31) << 8) + ord(fread($this->Socket, 1)); $Length = ($Length << 8) + ord(fread($this->Socket, 1)); } else { if (($Byte & 0xf0) == 0xe0) { $Length = (($Byte & 15) << 8) + ord(fread($this->Socket, 1)); $Length = ($Length << 8) + ord(fread($this->Socket, 1)); $Length = ($Length << 8) + ord(fread($this->Socket, 1)); } else { $Length = ord(fread($this->Socket, 1)); $Length = ($Length << 8) + ord(fread($this->Socket, 1)); $Length = ($Length << 8) + ord(fread($this->Socket, 1)); $Length = ($Length << 8) + ord(fread($this->Socket, 1)); } } } } else { $Length = $Byte; } // If we have got more characters to read, read them in. if ($Length > 0) { $Line = ''; $RetLen = 0; while ($RetLen < $Length) { $ToRead = $Length - $RetLen; $Line .= fread($this->Socket, $ToRead); $RetLen = strlen($Line); } $Response[] = $Line; } if ($this->Debug) echo($Line); // If we get a !done, make a note of it. if ($Line == "!done") $ReceivedDone = true; else $ReceivedDone = false; $Status = socket_get_status($this->Socket); if ((!$this->Connected && !$Status['unread_bytes']) || ($this->Connected && !$Status['unread_bytes'] && $ReceivedDone)) break; } if ($Parse) $Response = $this->ParseResponse($Response); return $Response; } function Write(string $Command, bool $Param2 = true): bool { if ($Command) { $Data = explode("\n", $Command); foreach ($Data as $Com) { $Com = trim($Com); fwrite($this->Socket, $this->EncodeLength(strlen($Com)).$Com); } if (gettype($Param2) == 'integer') { fwrite($this->Socket, $this->EncodeLength(strlen('.tag='.$Param2)).'.tag='.$Param2.chr(0)); } else if (gettype($Param2) == 'boolean') fwrite($this->Socket, ($Param2 ? chr(0) : '')); return true; } else return false; } function Comm(string $Com, array $Arr = array()): array { $Count = count($Arr); $this->write($Com, !$Arr); $i = 0; foreach ($Arr as $k => $v) { switch ($k[0]) { case "?": $el = "$k=$v"; break; case "~": $el = "$k~$v"; break; default: $el = "=$k=$v"; break; } $Last = ($i++ == $Count - 1); $this->Write($el, $Last); } return $this->Read(); } }