Enabled ANSI and Videotex servers and frames
This commit is contained in:
parent
6c91c69bd9
commit
0eb71ef7c6
@ -28,22 +28,26 @@ use Illuminate\Support\Facades\Log;
|
|||||||
*
|
*
|
||||||
* @package App\Classes
|
* @package App\Classes
|
||||||
*/
|
*/
|
||||||
class Frame
|
abstract class Frame
|
||||||
{
|
{
|
||||||
private $frame = NULL;
|
protected $frame = NULL;
|
||||||
private $output = NULL;
|
protected $output = NULL;
|
||||||
private $frame_length = 22;
|
|
||||||
private $frame_width = 40;
|
|
||||||
|
|
||||||
private $header_length = 20; // 20
|
// All this vars should be overridden in the child class
|
||||||
private $pagenum_length = 9; // 11 (prefixed with a color, suffixed with frame)
|
/*
|
||||||
private $cost_length = 7; // 9 (prefixed with a color, suffixed with unit)
|
protected $frame_length = 22;
|
||||||
private $cost_unit = 'u';
|
protected $frame_width = 40;
|
||||||
|
|
||||||
|
protected $header_length = 20; // 20
|
||||||
|
protected $pagenum_length = 9; // 11 (prefixed with a color, suffixed with frame)
|
||||||
|
protected $cost_length = 7; // 9 (prefixed with a color, suffixed with unit)
|
||||||
|
protected $cost_unit = 'u';
|
||||||
|
*/
|
||||||
|
|
||||||
public $fields = NULL; // The fields in this frame.
|
public $fields = NULL; // The fields in this frame.
|
||||||
|
|
||||||
// Magic Fields that are pre-filled
|
// Magic Fields that are pre-filled
|
||||||
private $fieldmap = [
|
protected $fieldmap = [
|
||||||
'a'=>'address#',
|
'a'=>'address#',
|
||||||
'd'=>'%date',
|
'd'=>'%date',
|
||||||
];
|
];
|
||||||
@ -80,6 +84,7 @@ class Frame
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculate fields and render output.
|
// Calculate fields and render output.
|
||||||
|
$this->fields = collect(); // Fields in this frame.
|
||||||
$this->fields($startline);
|
$this->fields($startline);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,115 +123,7 @@ class Frame
|
|||||||
*
|
*
|
||||||
* @param int $startline
|
* @param int $startline
|
||||||
*/
|
*/
|
||||||
public function fields($startline=0,$fieldchar='.')
|
abstract public function fields($startline=0,$fieldchar='.');
|
||||||
{
|
|
||||||
$infield = FALSE; // In a field
|
|
||||||
$fieldtype = NULL; // Type of field
|
|
||||||
$fieldlength = 0; // Length of field
|
|
||||||
$this->fields = collect(); // Fields in this frame.
|
|
||||||
|
|
||||||
if ($startline)
|
|
||||||
$this->output .= str_repeat(DOWN,$startline);
|
|
||||||
|
|
||||||
// $fieldadrline = 1;
|
|
||||||
|
|
||||||
// Scan the frame for a field start
|
|
||||||
for ($y=$startline;$y<=$this->frame_length;$y++)
|
|
||||||
{
|
|
||||||
// Fields can only be on a single line
|
|
||||||
$fieldx = $fieldy = FALSE;
|
|
||||||
|
|
||||||
for ($x=0;$x<$this->frame_width;$x++)
|
|
||||||
{
|
|
||||||
$posn = $y*40+$x;
|
|
||||||
|
|
||||||
// If the frame is not big enough, fill it with spaces.
|
|
||||||
$byte = ord(isset($this->frame->content{$posn}) ? $this->frame->content{$posn} : ' ')%128;
|
|
||||||
|
|
||||||
// Check for start-of-field
|
|
||||||
if ($byte == ord(ESC)) { // Esc designates start of field (Esc-K is end of edit)
|
|
||||||
$infield = TRUE;
|
|
||||||
$fieldlength = 1;
|
|
||||||
$fieldtype = ord(substr($this->frame->content,$posn+1,1))%128;
|
|
||||||
$this->output .= $fieldchar;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if ($infield) {
|
|
||||||
if ($byte == $fieldtype) {
|
|
||||||
$fieldlength++;
|
|
||||||
$byte = ord($fieldchar); // Replace field with $fieldchar.
|
|
||||||
|
|
||||||
if ($fieldx === FALSE) {
|
|
||||||
$fieldx = $x;
|
|
||||||
$fieldy = $y;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is this a magic field?
|
|
||||||
// @todo For page redisplay *00, we should show entered contents - for refresh *09 we should show updated contents
|
|
||||||
if (array_get($this->fieldmap,chr($fieldtype)) ) {
|
|
||||||
$field = $this->fieldmap[chr($fieldtype)];
|
|
||||||
//dump(['infield','byte'=>$byte,'fieldtype'=>$fieldtype,'field'=>$field,'strpos'=>strpos($field,'#')]);
|
|
||||||
|
|
||||||
/*
|
|
||||||
// address field has many lines. increment when hit on first character.
|
|
||||||
if ($fieldlength == 1 && strpos($field,'#') !== false) {
|
|
||||||
$field = str_replace('#',$fieldadrline,$field);
|
|
||||||
dump(['field'=>$field,'fieldadrline'=>$fieldadrline,'fieldadrline'=>$fieldadrline]);
|
|
||||||
$fieldadrline++;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Replace field with Date
|
|
||||||
if ($field == '%date') {
|
|
||||||
// Drop the last dot and replace it.
|
|
||||||
if ($fieldlength == 2) {
|
|
||||||
$datetime = date('D d M H:ia');
|
|
||||||
$this->output = rtrim($this->output,$fieldchar);
|
|
||||||
$this->output .= $datetime{0};
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($fieldlength > 1 AND $fieldlength <= strlen($datetime))
|
|
||||||
$byte = ord($datetime{$fieldlength-1});
|
|
||||||
}
|
|
||||||
|
|
||||||
// @todo user data
|
|
||||||
/* else if (isset($user[$field])) {
|
|
||||||
if ($fieldlength <= strlen($user[$field])) {
|
|
||||||
$byte = ord($user[$field]{$fieldlength-1});
|
|
||||||
}
|
|
||||||
} /*else // pre-load field contents. PAM or *00 ?
|
|
||||||
if (isset($fields[what]['value'])) {
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$this->fields->push(new FrameFields([
|
|
||||||
'type'=>chr($fieldtype),
|
|
||||||
'length'=>$fieldlength,
|
|
||||||
'x'=>$fieldx-1, // Adjust for the ESC char
|
|
||||||
'y'=>$fieldy,
|
|
||||||
]));
|
|
||||||
|
|
||||||
Log::debug(sprintf('Frame: %s, Field found at [%s,%s], Type: %s, Length: %s',$this->page(),$fieldx-1,$fieldy,$fieldtype,$fieldlength));
|
|
||||||
|
|
||||||
$infield = FALSE;
|
|
||||||
$fieldx = $fieldy = FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// truncate end of lines @todo havent validated this code or used it?
|
|
||||||
if (isset($pageflags['tru']) && substr($this->frame->content,$posn,40-$x) === str_repeat(' ',40-$x)) {
|
|
||||||
$this->output .= CR . LF;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $infield OR $fieldlength > 1)
|
|
||||||
$this->output .= ($byte < 32) ? ESC.chr($byte+64) : chr($byte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current frame.
|
* Returns the current frame.
|
||||||
@ -349,7 +246,7 @@ class Frame
|
|||||||
else
|
else
|
||||||
$color = GREEN;
|
$color = GREEN;
|
||||||
|
|
||||||
return sprintf($color.'% '.$this->cost_length.'.0f%s',$cost,$this->cost_unit);
|
return sprintf($color.'% '.static::$cost_length.'.0f%s',$cost,static::$cost_unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -360,9 +257,9 @@ class Frame
|
|||||||
*/
|
*/
|
||||||
private function render_header(string $header)
|
private function render_header(string $header)
|
||||||
{
|
{
|
||||||
$filler = ($this->strlenv($header) < $this->header_length) ? str_repeat(' ',$this->header_length-$this->strlenv($header)) : '';
|
$filler = ($this->strlenv($header) < static::$header_length) ? str_repeat(' ',static::$header_length-$this->strlenv($header)) : '';
|
||||||
|
|
||||||
return substr($header.$filler,0,$this->header_length+substr_count($this->header,ESC));
|
return substr($header.$filler,0,static::$header_length+strlen($header)-$this->strlenv($header));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -381,7 +278,7 @@ class Frame
|
|||||||
if (strlen($frame) !== 1)
|
if (strlen($frame) !== 1)
|
||||||
throw new \Exception('Frame invalid',500);
|
throw new \Exception('Frame invalid',500);
|
||||||
|
|
||||||
return sprintf(WHITE.'% '.$this->pagenum_length.'.0f%s',$num,$frame);
|
return sprintf(WHITE.'% '.static::$pagenum_length.'.0f%s',$num,$frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -403,11 +300,9 @@ class Frame
|
|||||||
* @param $text
|
* @param $text
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
function strlenv($text):int {
|
abstract function strlenv($text):int;
|
||||||
return strlen($text)-substr_count($text,ESC);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function testFrame()
|
public static function testFrame(Server $so)
|
||||||
{
|
{
|
||||||
// Simulate a DB load
|
// Simulate a DB load
|
||||||
$o = new \App\Models\Frame;
|
$o = new \App\Models\Frame;
|
||||||
@ -419,7 +314,8 @@ class Frame
|
|||||||
$o->index = 'a';
|
$o->index = 'a';
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
$o->content .= substr(R_RED.'T'.R_BLUE.'E'.R_GREEN.'S'.R_YELLOW.'T-12345678901234567890',0,20).
|
$sid = R_RED.'T'.R_BLUE.'E'.R_GREEN.'S'.R_YELLOW.'T';
|
||||||
|
$o->content .= substr($sid.'-'.str_repeat('12345678901234567890',4),0,static::$header_length+(strlen($sid)-$so->strlenv($sid))).
|
||||||
R_WHITE.'999999999a'.R_RED.sprintf('%07.0f',999).'u';
|
R_WHITE.'999999999a'.R_RED.sprintf('%07.0f',999).'u';
|
||||||
|
|
||||||
$o->content .= str_repeat('+-',18).' '.R_RED.'01';
|
$o->content .= str_repeat('+-',18).' '.R_RED.'01';
|
||||||
|
27
app/Classes/Frame/Ansi.php
Normal file
27
app/Classes/Frame/Ansi.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\Frame;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\Frame as AbstractFrame;
|
||||||
|
|
||||||
|
class Ansi extends AbstractFrame
|
||||||
|
{
|
||||||
|
public static $frame_length = 22;
|
||||||
|
public static $frame_width = 80;
|
||||||
|
|
||||||
|
public static $header_length = 60;
|
||||||
|
public static $pagenum_length = 9;
|
||||||
|
public static $cost_length = 7;
|
||||||
|
public static $cost_unit = 'u';
|
||||||
|
|
||||||
|
public function fields($startline=0,$fieldchar='.')
|
||||||
|
{
|
||||||
|
$this->output .= str_replace(LF,CR.LF,$this->frame->content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function strlenv($text):int {
|
||||||
|
return strlen($text ? preg_replace('/'.ESC.'\[[0-9;?]+[a-zA-Z]/','',$text) : $text);
|
||||||
|
}
|
||||||
|
}
|
132
app/Classes/Frame/Videotex.php
Normal file
132
app/Classes/Frame/Videotex.php
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\Frame;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\Frame as AbstractFrame;
|
||||||
|
use App\Classes\FrameFields;
|
||||||
|
|
||||||
|
class Videotex extends AbstractFrame
|
||||||
|
{
|
||||||
|
public static $frame_length = 22;
|
||||||
|
public static $frame_width = 40;
|
||||||
|
|
||||||
|
public static $header_length = 20;
|
||||||
|
public static $pagenum_length = 9;
|
||||||
|
public static $cost_length = 7;
|
||||||
|
public static $cost_unit = 'u';
|
||||||
|
|
||||||
|
public function fields($startline=0,$fieldchar='.')
|
||||||
|
{
|
||||||
|
$infield = FALSE; // In a field
|
||||||
|
$fieldtype = NULL; // Type of field
|
||||||
|
$fieldlength = 0; // Length of field
|
||||||
|
|
||||||
|
if ($startline)
|
||||||
|
$this->output .= str_repeat(DOWN,$startline);
|
||||||
|
|
||||||
|
// $fieldadrline = 1;
|
||||||
|
|
||||||
|
// Scan the frame for a field start
|
||||||
|
for ($y=$startline;$y<=static::$frame_length;$y++)
|
||||||
|
{
|
||||||
|
// Fields can only be on a single line
|
||||||
|
$fieldx = $fieldy = FALSE;
|
||||||
|
|
||||||
|
for ($x=0;$x<static::$frame_width;$x++)
|
||||||
|
{
|
||||||
|
$posn = $y*40+$x;
|
||||||
|
|
||||||
|
// If the frame is not big enough, fill it with spaces.
|
||||||
|
$byte = ord(isset($this->frame->content{$posn}) ? $this->frame->content{$posn} : ' ')%128;
|
||||||
|
|
||||||
|
// Check for start-of-field
|
||||||
|
if ($byte == ord(ESC)) { // Esc designates start of field (Esc-K is end of edit)
|
||||||
|
$infield = TRUE;
|
||||||
|
$fieldlength = 1;
|
||||||
|
$fieldtype = ord(substr($this->frame->content,$posn+1,1))%128;
|
||||||
|
$this->output .= $fieldchar;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if ($infield) {
|
||||||
|
if ($byte == $fieldtype) {
|
||||||
|
$fieldlength++;
|
||||||
|
$byte = ord($fieldchar); // Replace field with $fieldchar.
|
||||||
|
|
||||||
|
if ($fieldx === FALSE) {
|
||||||
|
$fieldx = $x;
|
||||||
|
$fieldy = $y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is this a magic field?
|
||||||
|
// @todo For page redisplay *00, we should show entered contents - for refresh *09 we should show updated contents
|
||||||
|
if (array_get($this->fieldmap,chr($fieldtype)) ) {
|
||||||
|
$field = $this->fieldmap[chr($fieldtype)];
|
||||||
|
//dump(['infield','byte'=>$byte,'fieldtype'=>$fieldtype,'field'=>$field,'strpos'=>strpos($field,'#')]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
// address field has many lines. increment when hit on first character.
|
||||||
|
if ($fieldlength == 1 && strpos($field,'#') !== false) {
|
||||||
|
$field = str_replace('#',$fieldadrline,$field);
|
||||||
|
dump(['field'=>$field,'fieldadrline'=>$fieldadrline,'fieldadrline'=>$fieldadrline]);
|
||||||
|
$fieldadrline++;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Replace field with Date
|
||||||
|
if ($field == '%date') {
|
||||||
|
// Drop the last dot and replace it.
|
||||||
|
if ($fieldlength == 2) {
|
||||||
|
$datetime = date('D d M H:ia');
|
||||||
|
$this->output = rtrim($this->output,$fieldchar);
|
||||||
|
$this->output .= $datetime{0};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($fieldlength > 1 AND $fieldlength <= strlen($datetime))
|
||||||
|
$byte = ord($datetime{$fieldlength-1});
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo user data
|
||||||
|
/* else if (isset($user[$field])) {
|
||||||
|
if ($fieldlength <= strlen($user[$field])) {
|
||||||
|
$byte = ord($user[$field]{$fieldlength-1});
|
||||||
|
}
|
||||||
|
} /*else // pre-load field contents. PAM or *00 ?
|
||||||
|
if (isset($fields[what]['value'])) {
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$this->fields->push(new FrameFields([
|
||||||
|
'type'=>chr($fieldtype),
|
||||||
|
'length'=>$fieldlength,
|
||||||
|
'x'=>$fieldx-1, // Adjust for the ESC char
|
||||||
|
'y'=>$fieldy,
|
||||||
|
]));
|
||||||
|
|
||||||
|
Log::debug(sprintf('Frame: %s, Field found at [%s,%s], Type: %s, Length: %s',$this->page(),$fieldx-1,$fieldy,$fieldtype,$fieldlength));
|
||||||
|
|
||||||
|
$infield = FALSE;
|
||||||
|
$fieldx = $fieldy = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// truncate end of lines @todo havent validated this code or used it?
|
||||||
|
if (isset($pageflags['tru']) && substr($this->frame->content,$posn,40-$x) === str_repeat(' ',40-$x)) {
|
||||||
|
$this->output .= CR . LF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $infield OR $fieldlength > 1)
|
||||||
|
$this->output .= ($byte < 32) ? ESC.chr($byte+64) : chr($byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function strlenv($text):int {
|
||||||
|
return strlen($text)-substr_count($text,ESC);
|
||||||
|
}
|
||||||
|
}
|
@ -9,17 +9,30 @@ use Sock\{SocketClient,SocketException};
|
|||||||
|
|
||||||
use App\Classes\Frame as FrameClass;
|
use App\Classes\Frame as FrameClass;
|
||||||
use App\Models\Frame as FrameModel;
|
use App\Models\Frame as FrameModel;
|
||||||
|
use App\Models\Mode;
|
||||||
|
|
||||||
abstract class Server {
|
abstract class Server {
|
||||||
// Size of Bottom Line Pollution
|
private $mo = NULL; // Our Mode object
|
||||||
private $blp = 0;
|
protected $blp = 0; // Size of Bottom Line Pollution
|
||||||
|
|
||||||
public function __construct()
|
public function __construct(Mode $o)
|
||||||
{
|
{
|
||||||
define('MSG_TIMEWARP_ON', WHITE . 'TIMEWARP ON' . GREEN . 'VIEW INFO WITH *02');
|
$this->mo = $o;
|
||||||
define('MSG_TIMEWARP_OFF', WHITE . 'TIMEWARP OFF' . GREEN . 'VIEWING DATE IS FIXED');
|
|
||||||
define('MSG_TIMEWARP_TO', GREEN . 'TIMEWARP TO %s');
|
define('MSG_SENDORNOT', GREEN.'KEY 1 TO SEND, 2 NOT TO SEND');
|
||||||
define('MSG_TIMEWARP', WHITE . 'OTHER VERSIONS EXIST' . GREEN . 'KEY *02 TO VIEW');
|
define('MSG_SENT', GREEN.'MESSAGE SENT - KEY _ TO CONTINUE');
|
||||||
|
define('MSG_NOTSENT', GREEN.'MESSAGE NOT SENT - KEY _ TO CONTINUE');
|
||||||
|
|
||||||
|
define('ERR_DATABASE', RED.'UNAVAILABLE AT PRESENT - PLSE TRY LATER');
|
||||||
|
define('ERR_NOTSENT', WHITE.'MESSAGE NOT SENT DUE TO AN ERROR');
|
||||||
|
define('ERR_PRIVATE', WHITE.'PRIVATE PAGE'.GREEN.'- FOR EXPLANATION *37_..');
|
||||||
|
define('ERR_ROUTE', WHITE.'MISTAKE?'.GREEN.'TRY AGAIN OR TELL US ON *08');
|
||||||
|
define('ERR_PAGE',ERR_ROUTE);
|
||||||
|
|
||||||
|
define('MSG_TIMEWARP_ON', WHITE.'TIMEWARP ON'.GREEN.'VIEW INFO WITH *02');
|
||||||
|
define('MSG_TIMEWARP_OFF', WHITE.'TIMEWARP OFF'.GREEN.'VIEWING DATE IS FIXED');
|
||||||
|
define('MSG_TIMEWARP_TO', GREEN.'TIMEWARP TO %s');
|
||||||
|
define('MSG_TIMEWARP', WHITE.'OTHER VERSIONS EXIST'.GREEN.'KEY *02 TO VIEW');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,7 +68,7 @@ abstract class Server {
|
|||||||
// $client->send(TCP_IAC.TCP_AYT); // AYT
|
// $client->send(TCP_IAC.TCP_AYT); // AYT
|
||||||
$client->send(TCP_IAC . TCP_DO . TCP_OPT_TERMTYPE . TCP_IAC . TCP_SB . TCP_OPT_TERMTYPE . TCP_OPT_ECHO . TCP_IAC . TCP_SE); // Request Term Type
|
$client->send(TCP_IAC . TCP_DO . TCP_OPT_TERMTYPE . TCP_IAC . TCP_SB . TCP_OPT_TERMTYPE . TCP_OPT_ECHO . TCP_IAC . TCP_SE); // Request Term Type
|
||||||
|
|
||||||
$client->send(CLS);
|
$client->send(CLS.COFF);
|
||||||
|
|
||||||
// Setup VARS
|
// Setup VARS
|
||||||
$timewarp = FALSE; // Is timewarp active.
|
$timewarp = FALSE; // Is timewarp active.
|
||||||
@ -151,6 +164,14 @@ abstract class Server {
|
|||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
case TCP_OPT_WINDOWSIZE:
|
||||||
|
$session_note .= 'WINDOWSIZE';
|
||||||
|
$session_init = FALSE;
|
||||||
|
$read = '';
|
||||||
|
Log::debug($session_note);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if ($session_option AND $read) {
|
if ($session_option AND $read) {
|
||||||
$session_term .= $read;
|
$session_term .= $read;
|
||||||
@ -575,16 +596,18 @@ abstract class Server {
|
|||||||
// Look for requested page
|
// Look for requested page
|
||||||
case ACTION_GOTO:
|
case ACTION_GOTO:
|
||||||
// If we wanted a "Searching..." message, this is where to put it.
|
// If we wanted a "Searching..." message, this is where to put it.
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$fo = $timewarpalt
|
$fo = $timewarpalt
|
||||||
? new FrameClass(FrameModel::findOrFail($timewarpalt))
|
? $this->mo->frame(FrameModel::findOrFail($timewarpalt))
|
||||||
: (new FrameModel)->fetch($page['frame'],$page['index']);
|
: $this->mo->frameLoad($page['frame'],$page['index'],$this);
|
||||||
|
|
||||||
} catch (ModelNotFoundException $e) {
|
} catch (ModelNotFoundException $e) {
|
||||||
$this->sendBaseline($client,ERR_PAGE);
|
$this->sendBaseline($client,ERR_PAGE);
|
||||||
$mode = $action = FALSE;
|
$mode = $action = FALSE;
|
||||||
|
|
||||||
|
// We initialise $fo anyway
|
||||||
|
$fo = $this->mo->frame($this->testFrame());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -649,6 +672,7 @@ abstract class Server {
|
|||||||
case 'i':
|
case 'i':
|
||||||
$client->send($output);
|
$client->send($output);
|
||||||
$mode = $action = false;
|
$mode = $action = false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Active Frame. Prestel uses this for a Response Frame.
|
// Active Frame. Prestel uses this for a Response Frame.
|
||||||
@ -684,8 +708,8 @@ abstract class Server {
|
|||||||
case 't':
|
case 't':
|
||||||
$client->send($output);
|
$client->send($output);
|
||||||
$action = ACTION_TERMINATE;
|
$action = ACTION_TERMINATE;
|
||||||
break;
|
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -719,7 +743,7 @@ abstract class Server {
|
|||||||
|
|
||||||
$line .= BLUE.$date.' '.$o->note;
|
$line .= BLUE.$date.' '.$o->note;
|
||||||
|
|
||||||
$output .= $this->outputPosition(0,$y++).$line.str_repeat(' ',40-$this->strlenv($line));
|
$output .= $this->outputPosition(0,$y++).$line.str_repeat(' ',40-$this->strlenv($line)); // @todo should use frame::page_length
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($timewarp) {
|
if ($timewarp) {
|
||||||
@ -750,7 +774,7 @@ abstract class Server {
|
|||||||
|
|
||||||
// Something bad happened. We'll log it and then disconnect.
|
// Something bad happened. We'll log it and then disconnect.
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
Log::emergency($e->getMessage());
|
Log::emergency($e->getMessage(),['line'=>$e->getLine(),'file'=>$e->getFile()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$client->close();
|
$client->close();
|
||||||
@ -784,16 +808,7 @@ abstract class Server {
|
|||||||
* @param $text
|
* @param $text
|
||||||
* @param bool $reposition
|
* @param bool $reposition
|
||||||
*/
|
*/
|
||||||
function sendBaseline($client,$text,$reposition=FALSE) {
|
abstract function sendBaseline($client,$text,$reposition=FALSE);
|
||||||
$client->send(HOME.UP.$text.
|
|
||||||
($this->blp > $this->strlenv($text)
|
|
||||||
? str_repeat(' ',$this->blp-$this->strlenv($text)).
|
|
||||||
($reposition ? HOME.UP.str_repeat(RIGHT,$this->strlenv($text)) : '')
|
|
||||||
: '')
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->blp = $this->strlenv($text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the length of text
|
* Calculate the length of text
|
||||||
@ -802,9 +817,12 @@ abstract class Server {
|
|||||||
*
|
*
|
||||||
* @param $text
|
* @param $text
|
||||||
* @return int
|
* @return int
|
||||||
* @todo Implement this as a helper
|
|
||||||
*/
|
*/
|
||||||
function strlenv($text):int {
|
abstract function strlenv($text):int;
|
||||||
return strlen($text)-substr_count($text,ESC);
|
|
||||||
}
|
/**
|
||||||
|
* Return a test frame appropriate for this server.
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
abstract public function testFrame();
|
||||||
}
|
}
|
@ -2,10 +2,72 @@
|
|||||||
|
|
||||||
namespace App\Classes\Server;
|
namespace App\Classes\Server;
|
||||||
|
|
||||||
use App\Classes\Server as AbstractServer;
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class Ansi extends AbstractServer {
|
use App\Classes\Server as AbstractServer;
|
||||||
|
use App\Models\Mode;
|
||||||
|
|
||||||
|
class Ansi extends AbstractServer {
|
||||||
|
public function __construct(Mode $o)
|
||||||
|
{
|
||||||
|
define('ESC', chr(27));
|
||||||
|
define('CON', ESC.'[?25h'); // Cursor On
|
||||||
|
define('COFF', ESC.'[?25l'); // Cursor Off
|
||||||
|
define('HOME', ESC.'[0;0f');
|
||||||
|
define('LEFT', ESC.'[D'); // Move Cursor
|
||||||
|
define('RIGHT', ESC.'[C'); // Move Cursor
|
||||||
|
define('DOWN', chr(10)); // Move Cursor
|
||||||
|
define('UP', chr(11)); // Move Cursor
|
||||||
|
define('CR', chr(13));
|
||||||
|
define('LF', chr(10));
|
||||||
|
define('CLS', ESC.'[2J');
|
||||||
|
define('HASH', '#'); // Enter
|
||||||
|
define('STAR', '*'); // Star Entry
|
||||||
|
define('SPACE', ' '); // Space
|
||||||
|
|
||||||
|
// NOTE: This consts are effective output
|
||||||
|
define('RED', ESC.'[0;31m'.SPACE);
|
||||||
|
define('GREEN', ESC.'[0;32m'.SPACE);
|
||||||
|
define('YELLOW', ESC.'[1;33m'.SPACE);
|
||||||
|
define('BLUE', ESC.'[0;34m'.SPACE);
|
||||||
|
define('MAGENTA', ESC.'[0;35m'.SPACE);
|
||||||
|
define('CYAN', ESC.'[0;36m'.SPACE);
|
||||||
|
define('WHITE', ESC.'[1;37m'.SPACE);
|
||||||
|
define('NEWBG', '');
|
||||||
|
|
||||||
|
// Raw attributes - used when storing frames.
|
||||||
|
define('R_RED', RED);
|
||||||
|
define('R_GREEN', GREEN);
|
||||||
|
define('R_YELLOW', YELLOW);
|
||||||
|
define('R_BLUE', BLUE);
|
||||||
|
define('R_MAGENTA', MAGENTA);
|
||||||
|
define('R_CYAN', CYAN);
|
||||||
|
define('R_WHITE', WHITE);
|
||||||
|
//define('FLASH',chr(8));
|
||||||
|
|
||||||
|
parent::__construct($o);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abstract function
|
||||||
|
public function sendBaseline($client,$text,$reposition=FALSE) {
|
||||||
|
$client->send(ESC.'[24;0f'.$text.
|
||||||
|
($this->blp > $this->strlenv($text)
|
||||||
|
? str_repeat(' ',$this->blp-$this->strlenv($text)).
|
||||||
|
($reposition ? ESC.'[24;0f'.str_repeat(RIGHT,$this->strlenv($text)) : '')
|
||||||
|
: '')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->blp = $this->strlenv($text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abstract function
|
||||||
|
public function strlenv($text):int {
|
||||||
|
return strlen($text ? preg_replace('/'.ESC.'\[[0-9;?]+[a-zA-Z]/','',$text) : $text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abstract function
|
||||||
|
public function testFrame()
|
||||||
|
{
|
||||||
|
return \App\Classes\Frame\Ansi::testFrame($this);
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,11 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Classes\Server;
|
|
||||||
|
|
||||||
use App\Classes\Server as AbstractServer;
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
class VideoTex extends AbstractServer {
|
|
||||||
|
|
||||||
}
|
|
72
app/Classes/Server/Videotex.php
Normal file
72
app/Classes/Server/Videotex.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\Server;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\Server as AbstractServer;
|
||||||
|
use App\Models\Mode;
|
||||||
|
|
||||||
|
class Videotex extends AbstractServer {
|
||||||
|
public function __construct(Mode $o)
|
||||||
|
{
|
||||||
|
define('ESC', chr(27));
|
||||||
|
define('CON', chr(17)); // Cursor On
|
||||||
|
define('COFF', chr(20)); // Cursor Off
|
||||||
|
define('HOME', chr(30));
|
||||||
|
define('LEFT', chr(8)); // Move Cursor
|
||||||
|
define('RIGHT', chr(9)); // Move Cursor
|
||||||
|
define('DOWN', chr(10)); // Move Cursor
|
||||||
|
define('UP', chr(11)); // Move Cursor
|
||||||
|
define('CR', chr(13));
|
||||||
|
define('LF', chr(10));
|
||||||
|
define('CLS', chr(12));
|
||||||
|
define('HASH', '_'); // Enter
|
||||||
|
define('STAR', '*'); // Star Entry
|
||||||
|
define('SPACE', ''); // Space
|
||||||
|
|
||||||
|
// NOTE: This consts are effective output
|
||||||
|
define('RED', ESC.'A');
|
||||||
|
define('GREEN', ESC.'B');
|
||||||
|
define('YELLOW', ESC.'C');
|
||||||
|
define('BLUE', ESC.'D');
|
||||||
|
define('MAGENTA', ESC.'E');
|
||||||
|
define('CYAN', ESC.'F');
|
||||||
|
define('WHITE', ESC.'G');
|
||||||
|
define('NEWBG', ESC.']');
|
||||||
|
|
||||||
|
// Raw attributes - used when storing frames.
|
||||||
|
define('R_RED', chr(1));
|
||||||
|
define('R_GREEN', chr(2));
|
||||||
|
define('R_YELLOW', chr(3));
|
||||||
|
define('R_BLUE', chr(4));
|
||||||
|
define('R_MAGENTA', chr(5));
|
||||||
|
define('R_CYAN', chr(6));
|
||||||
|
define('R_WHITE', chr(7));
|
||||||
|
define('FLASH', chr(8));
|
||||||
|
|
||||||
|
parent::__construct($o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sendBaseline($client,$text,$reposition=FALSE) {
|
||||||
|
$client->send(HOME.UP.$text.
|
||||||
|
($this->blp > $this->strlenv($text)
|
||||||
|
? str_repeat(' ',$this->blp-$this->strlenv($text)).
|
||||||
|
($reposition ? HOME.UP.str_repeat(RIGHT,$this->strlenv($text)) : '')
|
||||||
|
: '')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->blp = $this->strlenv($text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abstract function
|
||||||
|
public function strlenv($text):int {
|
||||||
|
return strlen($text)-substr_count($text,ESC);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abstract function
|
||||||
|
public function testFrame()
|
||||||
|
{
|
||||||
|
return \App\Classes\Frame\Videotex::testFrame($this);
|
||||||
|
}
|
||||||
|
}
|
@ -60,7 +60,7 @@ class Server extends Command
|
|||||||
throw new SocketException(SocketException::CANT_ACCEPT,'Missing pcntl extension');
|
throw new SocketException(SocketException::CANT_ACCEPT,'Missing pcntl extension');
|
||||||
}
|
}
|
||||||
|
|
||||||
$mo = Mode::where('name',$this->option('mode'))->firstOrFail();
|
$mo = Mode::where('name','LIKE',$this->option('mode'))->firstOrFail();
|
||||||
|
|
||||||
$server = new SocketServer(config('app.port'),config('app.bind'));
|
$server = new SocketServer(config('app.port'),config('app.bind'));
|
||||||
$server->init();
|
$server->init();
|
||||||
|
@ -25,22 +25,6 @@ class Frame extends Model
|
|||||||
return $this->frame.$this->index;
|
return $this->frame.$this->index;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch a specific frame from the database
|
|
||||||
*
|
|
||||||
* @param int $page
|
|
||||||
* @param string $frame
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function fetch(int $frame,string $index='a'): \App\Classes\Frame
|
|
||||||
{
|
|
||||||
// Return our internal test frame.
|
|
||||||
if ($frame == '999' and $index == 'a')
|
|
||||||
return new \App\Classes\Frame(\App\Classes\Frame::testFrame());
|
|
||||||
|
|
||||||
return new \App\Classes\Frame($this->where('frame',$frame)->where('index',$index)->firstOrFail());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hasFlag(string $flag)
|
public function hasFlag(string $flag)
|
||||||
{
|
{
|
||||||
// @todo When flags is in the DB update this.
|
// @todo When flags is in the DB update this.
|
||||||
|
@ -5,24 +5,73 @@ namespace App\Models;
|
|||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
use App\Classes\Frame as FrameClass;
|
||||||
|
use App\Classes\Frame\Ansi as AnsiFrame;
|
||||||
|
use App\Classes\Frame\Videotex as VideotexFrame;
|
||||||
|
|
||||||
|
use App\Classes\Server;
|
||||||
|
use App\Classes\Server\Ansi as AnsiServer;
|
||||||
|
use App\Classes\Server\Videotex as VideotexServer;
|
||||||
|
|
||||||
class Mode extends Model
|
class Mode extends Model
|
||||||
{
|
{
|
||||||
|
public function frames()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Frame::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a frame class for the Model
|
||||||
|
*
|
||||||
|
* @param Model $o
|
||||||
|
* @return FrameClass
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function frame(Model $o): FrameClass
|
||||||
|
{
|
||||||
|
switch (strtolower($this->name)) {
|
||||||
|
case 'ansi':
|
||||||
|
return new AnsiFrame($o);
|
||||||
|
case 'videotex':
|
||||||
|
return new VideotexFrame($o);
|
||||||
|
default:
|
||||||
|
throw new \Exception('Unknown Frame type: '.$mo->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a specific frame from the DB
|
||||||
|
*
|
||||||
|
* @param int $frame
|
||||||
|
* @param string $index
|
||||||
|
* @return FrameClass
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
//@todo Move Server $so first
|
||||||
|
public function frameLoad(int $frame,string $index='a',Server $so): FrameClass
|
||||||
|
{
|
||||||
|
return $this->frame(
|
||||||
|
// Return our internal test frame.
|
||||||
|
($frame == '999' and $index == 'a')
|
||||||
|
? $so->testFrame()
|
||||||
|
: $this->frames()
|
||||||
|
->where('frame','=',$frame)
|
||||||
|
->where('index','=',$index)
|
||||||
|
->firstOrFail()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return our server instance
|
* Return our server instance
|
||||||
*/
|
*/
|
||||||
public function server()
|
public function server()
|
||||||
{
|
{
|
||||||
switch ($this->name) {
|
switch (strtolower($this->name)) {
|
||||||
case 'Ansi':
|
case 'ansi': return new AnsiServer($this);
|
||||||
case 'VideoTex':
|
case 'videotex': return new VideotexServer($this);
|
||||||
$class = 'App\\Classes\\Server\\'.$this->name;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new \Exception('Unknown server type: '.$this->name);
|
throw new \Exception('Unknown server type: '.$this->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new $class;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,46 +18,12 @@ define('ACTION_TERMINATE',6);
|
|||||||
define('ACTION_SUBMITRF',7); // Offer to submit a response frame
|
define('ACTION_SUBMITRF',7); // Offer to submit a response frame
|
||||||
define('ACTION_STAR',8);
|
define('ACTION_STAR',8);
|
||||||
|
|
||||||
define('CON', chr(17)); // Cursor On
|
|
||||||
define('COFF', chr(20)); // Cursor Off
|
|
||||||
define('HOME', chr(30));
|
|
||||||
define('LEFT', chr(8)); // Move Cursor
|
|
||||||
define('RIGHT', chr(9)); // Move Cursor
|
|
||||||
define('DOWN', chr(10)); // Move Cursor
|
|
||||||
define('UP', chr(11)); // Move Cursor
|
|
||||||
define('CR', chr(13));
|
|
||||||
define('LF', chr(10));
|
|
||||||
define('CLS', chr(12));
|
|
||||||
define('ESC', chr(27));
|
|
||||||
define('HASH','_'); // Enter
|
|
||||||
define('STAR','*'); // Star Entry
|
|
||||||
|
|
||||||
// Keyboard presses
|
// Keyboard presses
|
||||||
define('KEY_LEFT',chr(136));
|
define('KEY_LEFT',chr(136));
|
||||||
define('KEY_RIGHT',chr(137));
|
define('KEY_RIGHT',chr(137));
|
||||||
define('KEY_DOWN',chr(138));
|
define('KEY_DOWN',chr(138));
|
||||||
define('KEY_UP',chr(139));
|
define('KEY_UP',chr(139));
|
||||||
|
|
||||||
// NOTE: This consts are effective output
|
|
||||||
define('RED', ESC . 'A');
|
|
||||||
define('GREEN', ESC . 'B');
|
|
||||||
define('YELLOW', ESC . 'C');
|
|
||||||
define('BLUE', ESC . 'D');
|
|
||||||
define('MAGENTA', ESC . 'E');
|
|
||||||
define('CYAN', ESC . 'F');
|
|
||||||
define('WHITE', ESC . 'G');
|
|
||||||
define('NEWBG', ESC . ']');
|
|
||||||
|
|
||||||
// Raw attributes - used when storing frames.
|
|
||||||
define('R_RED',chr(1));
|
|
||||||
define('R_GREEN',chr(2));
|
|
||||||
define('R_YELLOW',chr(3));
|
|
||||||
define('R_BLUE',chr(4));
|
|
||||||
define('R_MAGENT',chr(5));
|
|
||||||
define('R_CYAN',chr(6));
|
|
||||||
define('R_WHITE',chr(7));
|
|
||||||
define('FLASH',chr(8));
|
|
||||||
|
|
||||||
define('TCP_IAC',chr(255));
|
define('TCP_IAC',chr(255));
|
||||||
define('TCP_DONT',chr(254));
|
define('TCP_DONT',chr(254));
|
||||||
define('TCP_DO',chr(253));
|
define('TCP_DO',chr(253));
|
||||||
@ -67,18 +33,9 @@ define('TCP_SB',chr(250));
|
|||||||
define('TCP_AYT',chr(246));
|
define('TCP_AYT',chr(246));
|
||||||
define('TCP_SE',chr(240));
|
define('TCP_SE',chr(240));
|
||||||
|
|
||||||
define('TCP_OPT_LINEMODE',chr(34));
|
|
||||||
define('TCP_OPT_TERMTYPE',chr(24));
|
|
||||||
define('TCP_OPT_SUP_GOAHEAD',chr(3));
|
|
||||||
define('TCP_OPT_ECHO',chr(1));
|
|
||||||
define('TCP_BINARY',chr(0));
|
define('TCP_BINARY',chr(0));
|
||||||
|
define('TCP_OPT_ECHO',chr(1));
|
||||||
define('MSG_SENDORNOT', GREEN . 'KEY 1 TO SEND, 2 NOT TO SEND');
|
define('TCP_OPT_SUP_GOAHEAD',chr(3));
|
||||||
define('MSG_SENT', GREEN . 'MESSAGE SENT - KEY _ TO CONTINUE');
|
define('TCP_OPT_TERMTYPE',chr(24));
|
||||||
define('MSG_NOTSENT', GREEN . 'MESSAGE NOT SENT - KEY _ TO CONTINUE');
|
define('TCP_OPT_WINDOWSIZE',chr(31));
|
||||||
|
define('TCP_OPT_LINEMODE',chr(34));
|
||||||
define('ERR_ROUTE', WHITE . 'MISTAKE?' . GREEN . 'TRY AGAIN OR TELL US ON *08');
|
|
||||||
define('ERR_PAGE', ERR_ROUTE);
|
|
||||||
define('ERR_PRIVATE', WHITE . 'PRIVATE PAGE' . GREEN . '- FOR EXPLANATION *37_..');
|
|
||||||
define('ERR_DATABASE', RED . 'UNAVAILABLE AT PRESENT - PLSE TRY LATER');
|
|
||||||
define('ERR_NOTSENT', WHITE . 'MESSAGE NOT SENT DUE TO AN ERROR');
|
|
Reference in New Issue
Block a user