clrghouz/app/Classes/BBS/Control/Telnet.php
2023-09-13 20:59:40 +10:00

199 lines
5.0 KiB
PHP

<?php
namespace App\Classes\BBS\Control;
use App\Classes\BBS\Control;
/**
* Class Telnet
*
* This class looks after any telnet session commands
*
* TELNET http://pcmicro.com/netfoss/telnet.html
*
* @package App\Classes\Control
*/
final class Telnet extends Control
{
protected const LOGKEY = 'CT-';
/** @var int Data Byte */
public const TCP_IAC = 0xff;
/** @var int Indicates the demand that the other party stop performing, or confirmation that you are no
longer expecting the other party to perform, the indicated option */
public const TCP_DONT = 0xfe;
/** @var int Indicates the request that the other party perform, or confirmation that you are expecting
the other party to perform, the indicated option. */
public const TCP_DO = 0xfd;
/** @var int Indicates the refusal to perform, or continue performing, the indicated option. */
public const TCP_WONT = 0xfc;
/** @var int Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option. */
public const TCP_WILL = 0xfb;
/** @var int Indicates that what follows is sub-negotiation of the indicated option. */
public const TCP_SB = 0xfa;
/** @var int The GA signal. */
public const TCP_GA = 0xf9;
/** @var int Erase Line. */
public const TCP_EL = 0xf8;
/** @var int Erase character. */
public const TCP_EC = 0xf7;
/** @var int Are you there? */
public const TCP_AYT = 0xf6;
/** @var int About output */
public const TCP_AO = 0xf5;
/** @var int Interrupt Process. */
public const TCP_IP = 0xf4;
/** @var int Break. */
public const TCP_BREAK = 0xf3;
/** @var int The data stream portion of a Synch. This should always be accompanied by a TCP Urgent notification. */
public const TCP_DM = 0xf2;
/** @var int No operation. */
public const TCP_NOPT = 0xf1;
/** @var int End of sub-negotiation parameters. */
public const TCP_SE = 0xf0;
public const TCP_BINARY = 0x00;
public const TCP_OPT_ECHO = 0x01;
public const TCP_OPT_SUP_GOAHEAD = 0x03;
public const TCP_OPT_TERMTYPE = 0x18;
public const TCP_OPT_WINDOWSIZE = 0x1f;
public const TCP_OPT_LINEMODE = 0x22;
private bool $option = FALSE;
private string $note;
private string $terminal = '';
public static function send_iac($key): string
{
$send = chr(self::TCP_IAC);
switch ($key) {
case 'are_you_there':
$send .= chr(self::TCP_AYT);
break;
case 'do_echo':
$send .= chr(self::TCP_DO).chr(self::TCP_OPT_ECHO);
break;
case 'dont_echo':
$send .= chr(self::TCP_DONT).chr(self::TCP_OPT_ECHO);
break;
case 'will_echo':
$send .= chr(self::TCP_WILL).chr(self::TCP_OPT_ECHO);
break;
case 'wont_echo':
$send .= chr(self::TCP_WONT).chr(self::TCP_OPT_ECHO);
break;
case 'do_opt_termtype':
$send .= chr(self::TCP_DO).chr(self::TCP_OPT_TERMTYPE);
break;
case 'do_suppress_goahead':
$send .= chr(self::TCP_DO).chr(self::TCP_OPT_SUP_GOAHEAD);
break;
case 'sn_end':
$send .= chr(self::TCP_SE);
break;
case 'sn_start':
$send .= chr(self::TCP_SB);
break;
case 'wont_linemode':
$send .= chr(self::TCP_WONT).chr(self::TCP_OPT_LINEMODE);
break;
case 'will_xmit_binary':
$send .= chr(self::TCP_WILL).chr(self::TCP_BINARY);
break;
default:
throw new \Exception(sprintf('%s:! Unknown key: %s',$key));
}
return $send;
}
public function handle(string $read): string
{
$this->so->log('debug',sprintf('%s:+ Session Char [%02x] (%c)',self::LOGKEY,ord($read),$read),['complete'=>$this->complete,'option'=>$this->option]);
switch (ord($read)) {
// Command being sent.
case self::TCP_IAC:
$this->complete = FALSE;
$this->note = 'IAC ';
break;
case self::TCP_SB:
$this->option = TRUE;
break;
case self::TCP_SE:
$this->option = FALSE;
$this->complete = TRUE;
$this->so->log('debug',sprintf('%s:%% Session Terminal: %s',self::LOGKEY,$this->terminal));
break;
case self::TCP_DO:
$this->note .= 'DO ';
break;
case self::TCP_WILL:
$this->note .= 'WILL ';
break;
case self::TCP_WONT:
$this->note .= 'WONT ';
break;
case self::TCP_OPT_TERMTYPE:
break;
case self::TCP_OPT_ECHO:
$this->note .= 'ECHO';
$this->complete = TRUE;
$this->so->log('debug',sprintf('%s:%% Session Note: [%s]',self::LOGKEY,$this->note));
break;
case self::TCP_OPT_SUP_GOAHEAD:
$this->note .= 'SUPPRESS GO AHEAD';
$this->complete = TRUE;
$this->so->log('debug',sprintf('%s:%% Session Note: [%s]',self::LOGKEY,$this->note));
break;
case self::TCP_OPT_WINDOWSIZE:
$this->note .= 'WINDOWSIZE';
$this->complete = TRUE;
$this->so->log('debug',sprintf('%s:%% Session Note: [%s]',self::LOGKEY,$this->note));
break;
default:
if ($this->option && $read)
$this->terminal .= $read;
else
$this->so->log('debug',sprintf('%s:= Unhandled char in session_init: [%02x] (%c)',self::LOGKEY,ord($read),$read));
}
if ($this->complete)
$this->so->log('debug',sprintf('%s:= TELNET control COMPLETE',self::LOGKEY));
return '';
}
}