Internal rework pending editframe

This commit is contained in:
Deon George 2019-07-12 10:42:01 +07:00
parent 4f79a1a997
commit 264747e2f3
14 changed files with 611 additions and 428 deletions

View File

@ -2,18 +2,36 @@
namespace App\Classes;
use App\Classes\Control\EditFrame;
use App\Classes\Control\Register;
use App\Classes\Control\Telnet;
abstract class Control
{
// Has this control class finished with input
protected $complete = FALSE;
// The server object that is running this control class
protected $so = NULL;
// The frame applicable for this control (not the current rendered frame, thats in $so)
protected $fo = NULL;
/**
* What is the state of the server outside of this control.
* Should only contain
* + mode = Mode to follow outside of the control method
* + action = Action to run after leaving the control method
*
* @var array
*/
public $state = [];
public function __construct(Server $so) {
public function __construct(Server $so,Frame $fo=NULL) {
$this->so = $so;
$this->fo = $fo;
// Boot control, preparing anything before keyboard entry
$this->boot();
}
@ -31,8 +49,11 @@ abstract class Control
}
// @todo Change to Dynamic Calls by the existence of files in App\Classes\Control
public static function factory(string $name, Server $so) {
public static function factory(string $name,Server $so,Frame $fo=NULL) {
switch ($name) {
case 'editframe':
return new EditFrame($so,$fo);
case 'register':
return new Register($so);
@ -44,5 +65,5 @@ abstract class Control
}
}
abstract public function handle(string $char);
abstract public function handle(string $read);
}

View File

@ -12,7 +12,6 @@ use Illuminate\Support\Facades\Validator;
* Class Register handles registration
*
* @todo REMOVE the force .WHITE at the end of each sendBaseline()
*
* @package App\Classes\Control
*/
class Register extends Control
@ -21,7 +20,7 @@ class Register extends Control
protected function boot()
{
$this->so->sendBaseline($this->so->client(),GREEN.'Select User Name'.WHITE);
$this->so->sendBaseline($this->so->co,GREEN.'Select User Name'.WHITE);
}
/**
@ -37,124 +36,114 @@ class Register extends Control
*/
public function handle(string $read,array $current=[])
{
// Ignore CR
if ($read == CR)
return '';
// If we got a # we'll be completing field input.
if ($read == HASH OR $read == LF) {
// Does our field have data...
if (array_get($current['fielddata'],$current['fieldnum'])) {
switch ($current['fieldnum']) {
// Username
case 0:
// See if the requested username already exists
if (User::where('login', $current['fielddata'][$current['fieldnum']])->exists()) {
$this->so->sendBaseline($this->so->co,RED.'USER ALREADY EXISTS'.WHITE);
// Our registration page
// @todo get this from the DB
if ($current['page']['frame'] == '981') {
return '';
}
// Does our field have data...
if (array_get($current['fielddata'],$current['fieldnum'])) {
switch ($current['fieldnum']) {
// Username
case 0:
// See if the requested username already exists
if (User::where('login', $current['fielddata'][$current['fieldnum']])->exists()) {
$this->so->sendBaseline($this->so->client(), RED . 'USER ALREADY EXISTS'.WHITE);
$this->data['user'] = $current['fielddata'][$current['fieldnum']];
$this->so->sendBaseline($this->so->co,GREEN.'Enter Real Name'.WHITE);
return '';
}
break;
$this->data['user'] = $current['fielddata'][$current['fieldnum']];
$this->so->sendBaseline($this->so->client(), GREEN . 'Enter Real Name'.WHITE);
// Real Name
case 1:
$this->data['name'] = $current['fielddata'][$current['fieldnum']];
$this->so->sendBaseline($this->so->co,GREEN.'Enter Email Address'.WHITE);
break;
break;
// Real Name
case 1:
$this->data['name'] = $current['fielddata'][$current['fieldnum']];
$this->so->sendBaseline($this->so->client(), GREEN . 'Enter Email Address'.WHITE);
// Email Address
case 2:
if (Validator::make(['email'=>$current['fielddata'][$current['fieldnum']]],[
'email'=>'email',
])->fails()) {
$this->so->sendBaseline($this->so->co,RED.'INVALID EMAIL ADDRESS'.WHITE);
break;
return '';
};
// Email Address
case 2:
if (Validator::make(['email'=>$current['fielddata'][$current['fieldnum']]],[
'email'=>'email',
])->fails()) {
$this->so->sendBaseline($this->so->client(), RED . 'INVALID EMAIL ADDRESS'.WHITE);
// See if the requested email already exists
if (User::where('email', $current['fielddata'][$current['fieldnum']])->exists()) {
$this->so->sendBaseline($this->so->co,RED.'USER ALREADY EXISTS'.WHITE);
return '';
};
return '';
}
// See if the requested email already exists
if (User::where('email', $current['fielddata'][$current['fieldnum']])->exists()) {
$this->so->sendBaseline($this->so->client(), RED . 'USER ALREADY EXISTS'.WHITE);
$this->data['email'] = $current['fielddata'][$current['fieldnum']];
$this->data['token'] = sprintf('%06.0f',rand(0,999999));
return '';
}
$this->so->sendBaseline($this->so->co,YELLOW.'PROCESSING...'.WHITE);
Mail::to($this->data['email'])->sendNow(new SendToken($this->data['token']));
$this->data['email'] = $current['fielddata'][$current['fieldnum']];
$this->data['token'] = sprintf('%06.0f',rand(0,999999));
if (Mail::failures()) {
dump('Failure?');
$this->so->sendBaseline($this->so->client(), YELLOW . 'PROCESSING...'.WHITE);
Mail::to($this->data['email'])->sendNow(new SendToken($this->data['token']));
dump(Mail::failures());
}
if (Mail::failures()) {
dump('Failure?');
$this->so->sendBaseline($this->so->co,GREEN.'Enter Password'.WHITE);
dump(Mail::failures());
}
break;
$this->so->sendBaseline($this->so->client(), GREEN . 'Enter Password'.WHITE);
// Enter Password
case 3:
$this->data['password'] = $current['fielddata'][$current['fieldnum']];
$this->so->sendBaseline($this->so->co,GREEN.'Confirm Password'.WHITE);
break;
break;
// Enter Password
case 3:
$this->data['password'] = $current['fielddata'][$current['fieldnum']];
$this->so->sendBaseline($this->so->client(), GREEN . 'Confirm Password'.WHITE);
// Confirm Password
case 4:
if ($this->data['password'] !== $current['fielddata'][$current['fieldnum']]) {
$this->so->sendBaseline($this->so->co,RED.'PASSWORD DOESNT MATCH, *09 TO START AGAIN'.WHITE);
break;
return '';
}
// Confirm Password
case 4:
if ($this->data['password'] !== $current['fielddata'][$current['fieldnum']]) {
$this->so->sendBaseline($this->so->client(), RED . 'PASSWORD DOESNT MATCH, *09 TO START AGAIN'.WHITE);
$this->so->sendBaseline($this->so->co,GREEN.'Enter Location'.WHITE);
return '';
}
break;
$this->so->sendBaseline($this->so->client(), GREEN . 'Enter Location'.WHITE);
// Enter Location
case 5:
$this->data['location'] = $current['fielddata'][$current['fieldnum']];
$this->so->sendBaseline($this->so->co,GREEN.'Enter TOKEN emailed to you'.WHITE);
break;
break;
// Enter Location
case 5:
$this->data['location'] = $current['fielddata'][$current['fieldnum']];
$this->so->sendBaseline($this->so->client(), GREEN . 'Enter TOKEN emailed to you'.WHITE);
// Enter Token
case 6:
if ($this->data['token'] !== $current['fielddata'][$current['fieldnum']]) {
$this->so->sendBaseline($this->so->co,RED.'TOKEN DOESNT MATCH, *09 TO START AGAIN'.WHITE);
break;
return '';
}
// Enter Token
case 6:
if ($this->data['token'] !== $current['fielddata'][$current['fieldnum']]) {
$this->so->sendBaseline($this->so->client(), RED . 'TOKEN DOESNT MATCH, *09 TO START AGAIN'.WHITE);
break;
return '';
}
default:
$this->so->sendBaseline($this->so->co,RED.'HUH?');
}
break;
default:
$this->so->sendBaseline($this->so->client(), RED . 'HUH?');
}
} else {
// If we are MODE_BL, we need to return the HASH, otherwise nothing.
if (in_array($this->state['mode'],[MODE_BL,MODE_SUBMITRF,MODE_RFNOTSENT])) {
return $read;
} else {
// If we are MODE_BL, we need to return the HASH, otherwise nothing.
if (in_array($this->state['mode'],[MODE_BL,MODE_SUBMITRF,MODE_RFNOTSENT])) {
return $read;
$this->so->sendBaseline($this->so->co,RED.'FIELD REQUIRED...'.WHITE);
} else {
$this->so->sendBaseline($this->so->client(), RED . 'FIELD REQUIRED...'.WHITE);
return '';
}
return '';
}
}
}
@ -174,14 +163,14 @@ class Register extends Control
$o->location = $this->data['location'];
$o->save();
$this->so->sendBaseline($this->so->client(), GREEN . 'ACCOUNT CREATED, PRESS '.HASH.' TO CONTINUE...'.WHITE);
$this->so->sendBaseline($this->so->co,GREEN.'ACCOUNT CREATED, PRESS '.HASH.' TO CONTINUE...'.WHITE);
$this->state['action'] = ACTION_NEXT;
// Add to CUG 0
$o->cugs()->attach(0);
} catch (\Exception $e) {
$this->so->sendBaseline($this->so->client(), RED . 'SOMETHING WENT WRONG...'.WHITE);
$this->so->sendBaseline($this->so->co,RED.'SOMETHING WENT WRONG...'.WHITE);
$this->so->log('error',$e->getMessage());
$this->state['action'] = ACTION_RELOAD;
}

View File

@ -2,11 +2,12 @@
namespace App\Classes;
use App\Models\Mode;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use App\User;
use App\Models\CUG;
use App\Models\{CUG,Mode};
use App\Models\Frame as FrameModel;
/**
* Handles all aspects of frame
@ -34,12 +35,14 @@ use App\Models\CUG;
*/
abstract class Frame
{
protected $frame = NULL;
protected $output = '';
protected $startline = 1;
// This holds the parser object for this frame.
protected $po = NULL;
// This holds the frame object as retrieved from the DB
protected $fo = NULL;
// All this vars should be overridden in the child class
/*
// All this vars should be overridden in the child class
protected $frame_length = 22;
protected $frame_width = 40;
@ -54,9 +57,8 @@ abstract class Frame
const FRAMETYPE_LOGIN = 'l';
const FRAMETYPE_TERMINATE = 't';
public $fields = NULL; // The fields in this frame.
// Fields that are editable
// @todo This needs rework.
private $fieldoptions = [
'p'=>['edit'=>TRUE,'mask'=>'*'], // Password
't'=>['edit'=>TRUE], // Text
@ -65,38 +67,44 @@ abstract class Frame
// @todo Move this to the database
private $header = RED.'T'.BLUE.'E'.GREEN.'S'.YELLOW.'T'.MAGENTA.'!';
public function __construct(\App\Models\Frame $o)
public function __construct(FrameModel $o)
{
$this->frame = $o;
$this->fo = $o;
$startline = 1;
$this->output = $this->frame->cls ? CLS : HOME;
if ($this->fo->exists) {
if (! $this->hasFlag('ip') AND (! $this->isCUG(0) OR $this->type() !== self::FRAMETYPE_LOGIN)) {
$startline = 2;
if (! $this->hasFlag('ip') AND (! $this->isCUG(0) OR $this->type() !== self::FRAMETYPE_LOGIN)) {
// Set the page header: CUG/Site Name | Page # | Cost
$this->output .= $this->render_header($this->header).
$this->render_page($this->frame->frame,$this->frame->index).
$this->render_cost($this->frame->cost);
$this->startline = 2;
} elseif ($this->isCUG(0) AND $this->type() === self::FRAMETYPE_LOGIN) {
$this->startline = 2;
$this->output .= str_repeat(DOWN,$this->startline-1);
} elseif ($this->isCUG(0) AND $this->type() === self::FRAMETYPE_LOGIN) {
$startline = 2;
}
}
// Calculate fields and render output.
$this->fields = collect(); // Fields in this frame.
$this->fields($this->startline);
// Our parser object
$this->po = $this->parser($startline);
}
/**
* Render the frame
*
* @return null|string
* @throws \Exception
*/
public function __toString()
{
return $this->output;
$output = $this->fo->cls ? CLS : HOME;
if (! $this->hasFlag('ip') AND (! $this->isCUG(0) OR $this->type() !== self::FRAMETYPE_LOGIN)) {
$output .= $this->render_header($this->header).
$this->render_page($this->fo->frame,$this->fo->index).
$this->render_cost($this->fo->cost);
} elseif ($this->isCUG(0) AND $this->type() === self::FRAMETYPE_LOGIN) {
$output .= str_repeat(DOWN,1);
}
return $output.(string)$this->po;
}
/**
@ -106,9 +114,9 @@ abstract class Frame
*/
public function alts(Mode $o)
{
return \App\Models\Frame::where('frame',$this->frame())
return FrameModel::where('frame',$this->fo->frame)
->where('index',$this->index())
->where('id','<>',$this->frame->id)
->where('id','<>',$this->fo->id)
->where('mode_id',$o->id)
->where('access',1)
->limit(9);
@ -119,23 +127,33 @@ abstract class Frame
*/
public function created()
{
return $this->frame->created_at;
return $this->fo->created_at;
}
/**
* Convert the frame from Binary to Output
* Look for fields within the frame.
*
* @param int $startline
* Return fields within the frame.
*/
abstract public function fields($startline=0);
public function fields()
{
return $this->po->fields;
}
/**
* Returns the current frame.
*/
public function frame()
{
return $this->frame->frame;
return $this->fo->frame;
}
public function frame_length()
{
return static::$frame_length;
}
public function frame_width()
{
return static::$frame_width;
}
/**
@ -149,7 +167,7 @@ abstract class Frame
public function getCUG()
{
$co = NULL;
$frame = $this->frame->frame;
$frame = $this->fo->frame;
while (! $co)
{
@ -171,7 +189,7 @@ abstract class Frame
*/
public function getField(int $id)
{
return $this->fields->get($id);
return $this->fields()->get($id);
}
/**
@ -183,7 +201,7 @@ abstract class Frame
*/
public function getFieldId($type='edit',$after=0)
{
return $this->fields
return $this->fields()
->search(function($item,$key) use ($type,$after) {
return $key >= $after AND $this->isFieldEditable($item->type);
});
@ -204,7 +222,7 @@ abstract class Frame
*/
public function hasFlag($flag)
{
return $this->frame->hasFlag($flag);
return $this->fo->hasFlag($flag);
}
/**
@ -214,7 +232,7 @@ abstract class Frame
*/
public function id()
{
return $this->frame->id;
return $this->fo->id;
}
/**
@ -224,7 +242,7 @@ abstract class Frame
*/
public function index()
{
return $this->frame->index;
return $this->fo->index;
}
/**
@ -232,16 +250,25 @@ abstract class Frame
*/
public function index_next()
{
return chr(ord($this->frame->index)+1);
return chr(ord($this->fo->index)+1);
}
/**
* Return the previous index
*/
public function index_prev()
{
return $this->fo->index == 'a' ? 'a' : chr(ord($this->fo->index)-1);
}
public function isAccessible():bool
{
return $this->frame->access ? TRUE : FALSE;
return $this->fo->access ? TRUE : FALSE;
}
/**
* Determine if the frame is a particular CUG
*
* @param int $cug
* @return bool
*/
@ -273,10 +300,10 @@ abstract class Frame
*/
public function isFramePublic(): bool
{
return $this->frame->public ? TRUE : FALSE;
return $this->fo->public ? TRUE : FALSE;
}
// @todo To implement
// @todo To implement
public function isOwner(User $o):bool
{
return FALSE;
@ -287,7 +314,7 @@ abstract class Frame
*/
public function page(bool $as_array=FALSE)
{
return $as_array ? ['frame'=>$this->frame->frame,'index'=>$this->frame->index] : $this->frame->page;
return $as_array ? ['frame'=>$this->fo->frame,'index'=>$this->fo->index] : $this->fo->page;
}
/**
@ -296,11 +323,19 @@ abstract class Frame
* @param bool $as_array
* @return mixed
*/
public function pagenext(bool $as_array=FALSE)
public function page_next(bool $as_array=FALSE)
{
return $as_array ? ['frame'=>$this->frame->frame,'index'=>$this->index_next()] : $this->frame->frame.$this->index_next();
return $as_array ? ['frame'=>$this->fo->frame,'index'=>$this->index_next()] : $this->fo->frame.$this->index_next();
}
/**
* Load the parser
*
* @param int $startline
* @return Parser
*/
abstract protected function parser(int $startline): Parser;
/**
* Render the cost of the frame
*
@ -359,6 +394,8 @@ abstract class Frame
* Get the route for the key press
*
* @param string $read
* @return string
* @throws \Exception
*/
public function route(string $read)
{
@ -366,11 +403,11 @@ abstract class Frame
throw new \Exception('Routes are single digit');
// If we dont have a route record...
if (! $this->frame->route)
if (! $this->fo->route)
return '*';
$key = 'r'.$read;
return $this->frame->route->{$key};
return $this->fo->route->{$key};
}
/**
@ -381,12 +418,12 @@ abstract class Frame
* @param $text
* @return int
*/
abstract function strlenv($text):int;
abstract public static function strlenv($text):int;
public static function testFrame(Server $so)
public static function testFrame()
{
// Simulate a DB load
$o = new \App\Models\Frame;
$o = new FrameModel;
$content = '';
$o->flags = ['ip'];
@ -399,7 +436,7 @@ abstract class Frame
// Header
$sid = R_RED.'T'.R_BLUE.'E'.R_GREEN.'S'.R_YELLOW.'T';
$content .= substr($sid.'-'.str_repeat('12345678901234567890',4),0,static::$header_length+(strlen($sid)-$so->strlenv($sid))).
$content .= substr($sid.'-'.str_repeat('12345678901234567890',4),0,static::$header_length+(strlen($sid)-static::strlenv($sid))).
R_WHITE.str_repeat('9',static::$pagenum_length).'a'.R_RED.sprintf('%07.0f',999).'u';
$content .= R_WHITE.str_repeat('+-',static::$frame_width/2-3).' '.R_RED.'01';
@ -410,7 +447,7 @@ abstract class Frame
$o->content = $content;
return $o;
return new static($o);
}
/**
@ -418,6 +455,6 @@ abstract class Frame
*/
public function type()
{
return $this->frame->type();
return $this->fo->type();
}
}

View File

@ -24,7 +24,7 @@ class Login extends Action
$this->mode = 2; // MODE_FIELD
// $this->action = 2; // ACTION_GOTO
$this->so->sendBaseline($this->so->client(),RED.'INVALID DETAILS, TRY AGAIN *00');
$this->so->sendBaseline($this->so->co,RED.'INVALID DETAILS, TRY AGAIN *00');
return FALSE;
}
@ -33,7 +33,7 @@ class Login extends Action
$this->uo = User::where('login',array_get($fielddata,0))->firstOrFail();
} catch (ModelNotFoundException $e) {
$this->so->sendBaseline($this->so->client(),RED.'USER NOT FOUND, TRY AGAIN *00');
$this->so->sendBaseline($this->so->co,RED.'USER NOT FOUND, TRY AGAIN *00');
return FALSE;
}
@ -41,7 +41,7 @@ class Login extends Action
if ($this->uo->password != array_get($fielddata,1))
{
$this->uo = new User;
$this->so->sendBaseline($this->so->client(),RED.'INVALID PASSWORD, TRY AGAIN *00');
$this->so->sendBaseline($this->so->co,RED.'INVALID PASSWORD, TRY AGAIN *00');
return FALSE;
}

View File

@ -2,12 +2,14 @@
namespace App\Classes\Frame;
use App\Classes\Parser;
use Illuminate\Support\Facades\Log;
use App\Classes\Frame as AbstractFrame;
use App\Classes\Frame;
use App\Classes\Parser\Ansi as AnsiParser;
use App\Models\Frame as FrameModel;
class Ansi extends AbstractFrame
class Ansi extends Frame
{
public static $frame_length = 22;
public static $frame_width = 80;
@ -19,7 +21,7 @@ class Ansi extends AbstractFrame
public static $if_filler = '.';
public function __construct(\App\Models\Frame $o,string $msg='')
public function __construct(FrameModel $o,string $msg='')
{
parent::__construct($o);
@ -28,15 +30,12 @@ class Ansi extends AbstractFrame
$this->output .= ESC.'[24;0f'.$msg.HOME;
}
public function fields($startline=1)
protected function parser(int $startline): Parser
{
$o = new AnsiParser($this->frame->content,$startline);
$this->output .= (string)$o;
$this->fields = $o->fields;
return new AnsiParser($this->fo->content,self::$frame_width,$startline);
}
public function strlenv($text):int {
public static function strlenv($text):int {
return strlen($text ? preg_replace('/'.ESC.'\[[0-9;?]+[a-zA-Z]/','',$text) : $text);
}
}

View File

@ -4,10 +4,12 @@ namespace App\Classes\Frame;
use Illuminate\Support\Facades\Log;
use App\Classes\Frame as AbstractFrame;
use App\Classes\Frame;
use App\Classes\Parser;
use App\Classes\Parser\Videotex as VideotexParser;
use App\Models\Frame as FrameModel;
class Videotex extends AbstractFrame
class Videotex extends Frame
{
public static $frame_length = 22;
public static $frame_width = 40;
@ -19,7 +21,7 @@ class Videotex extends AbstractFrame
public static $if_filler = '.';
public function __construct(\App\Models\Frame $o,string $msg='')
public function __construct(FrameModel $o,string $msg='')
{
parent::__construct($o);
@ -28,17 +30,12 @@ class Videotex extends AbstractFrame
$this->output .= HOME.UP.$msg.HOME;
}
// @todo Change to use a Parser, like we do for ANSI
public function fields($startline=1)
protected function parser(int $startline): Parser
{
$o = new VideotexParser($this->frame->content,$startline);
$this->output .= (string)$o;
$this->fields = $o->fields;
return new VideotexParser($this->fo->content,self::$frame_width,$startline);
}
public function strlenv($text):int {
public static function strlenv($text):int {
return strlen($text)-substr_count($text,ESC);
}
}

View File

@ -2,11 +2,22 @@
namespace App\Classes;
/**
* The Frame Parser looks into frames for ESC codes that renders dynamic information
*/
abstract class Parser
{
protected $content = '';
protected $startline = 0;
public $fields = NULL;
// Fields in the frame
public $fields = [];
// Parsed frame, ready to send to client
public $output = '';
// Position array of frame control chars
protected $frame_data = [];
// Position array of frame chars
protected $frame_content = [];
// Magic Fields that are pre-filled
protected $fieldmap = [
@ -14,17 +25,16 @@ abstract class Parser
'd'=>'%date',
];
public function __construct(string $content,int $startline=1)
public function __construct(string $content,int $width,int $startline=1)
{
$this->content = $content;
$this->startline = $startline;
$this->fields = collect();
$this->output = $this->parse($startline,$content,$width);
}
public function __toString(): string
{
return $this->parse($this->startline);
return $this->output;
}
abstract protected function parse($startline): string;
abstract protected function parse(int $startline,string $content,int $width): string;
}

View File

@ -16,11 +16,11 @@ class Ansi extends AbstractParser {
* @param int $start
* @return bool|int
*/
private function findEOF(string $char,int $start)
private function findEOF(string $char,int $start,string $content)
{
for ($c=$start;$c <= strlen($this->content);$c++)
for ($c=$start;$c <= strlen($content);$c++)
{
if ($this->content{$c} != $char)
if ($content{$c} != $char)
return $c-$start;
}
@ -32,7 +32,7 @@ class Ansi extends AbstractParser {
* @param int $offset
* @return string
*/
protected function parse($startline): string
protected function parse(int $startline,string $content,int $width): string
{
// Our starting coordinates
$x = 1;
@ -40,10 +40,10 @@ class Ansi extends AbstractParser {
$output = '';
// Scan the frame for a field start
for ($c=0; $c<=strlen($this->content); $c++)
for ($c=0; $c<=strlen($content); $c++)
{
// If the frame is not big enough, fill it with spaces.
$byte = isset($this->content{$c}) ? $this->content{$c} : ' ';
$byte = isset($content{$c}) ? $content{$c} : ' ';
$advance = 0;
switch ($byte) {
@ -58,7 +58,7 @@ class Ansi extends AbstractParser {
case ESC:
$advance = 1;
// Is the next byte something we know about
$nextbyte = isset($this->content{$c+$advance}) ? $this->content{$c+$advance} : ' ';
$nextbyte = isset($content{$c+$advance}) ? $content{$c+$advance} : ' ';
switch ($nextbyte) {
case '[':
@ -68,7 +68,7 @@ class Ansi extends AbstractParser {
// Find our end CSI param
$matches = [];
$a = preg_match('/([0-9]+[;]?)+([a-zA-Z])/',$this->content,$matches,NULL,$c+$advance);
$a = preg_match('/([0-9]+[;]?)+([a-zA-Z])/',$content,$matches,NULL,$c+$advance);
if (! $a)
break;
@ -76,6 +76,13 @@ class Ansi extends AbstractParser {
$advance += strlen($matches[0])-1;
$chars .= $matches[0];
if (! isset($this->frame_data[$y][$x]))
$this->frame_data[$y][$x] = '';
else
$this->frame_data[$y][$x] .= '|';
$this->frame_data[$y][$x] .= $matches[0];
switch ($matches[2]) {
// We ignore 'm' they are color CSIs
case 'm': break;
@ -96,10 +103,11 @@ class Ansi extends AbstractParser {
break;
default:
$c--; // Allow for the original ESC
// Allow for the original ESC
$c--;
$advance++;
$fieldtype = ord($nextbyte)%128; // @todo Do we need the %128 for ANSI?
$fieldlength = $this->findEOF(chr($fieldtype),$c+2)+1;
$fieldtype = ord($nextbyte);
$fieldlength = $this->findEOF(chr($fieldtype),$c+2,$content)+1;
$byte = '';
@ -122,6 +130,7 @@ class Ansi extends AbstractParser {
$x++;
}
$this->frame_content[$y][$x] = $byte;
$output .= $byte;
if ($advance) {
@ -129,7 +138,7 @@ class Ansi extends AbstractParser {
$c += $advance;
}
if ($x > 80) {
if ($x > $width) {
$x = 1;
$y++;
}

View File

@ -10,7 +10,7 @@ use App\Classes\Frame\Videotex as VideotexFrame;
class Videotex extends AbstractParser
{
protected function parse($startline): string
protected function parse(int $startline,string $content,int $width): string
{
// Our starting coordinates
$output = '';
@ -32,13 +32,13 @@ class Videotex extends AbstractParser
$posn = $y*VideotexFrame::$frame_width+$x;
// If the frame is not big enough, fill it with spaces.
$byte = ord(isset($this->content{$posn}) ? $this->content{$posn} : ' ')%128;
$byte = ord(isset($content{$posn}) ? $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->content,$posn+1,1))%128;
$fieldtype = ord(substr($content,$posn+1,1))%128;
$output .= VideotexFrame::$if_filler;
} else {
@ -107,8 +107,8 @@ class Videotex extends AbstractParser
}
// truncate end of lines @todo havent validated this code or used it?
if (isset($pageflags['tru']) && substr($this->content,$posn,40-$x) === str_repeat(' ',40-$x)) {
$output .= CR . LF;
if (isset($pageflags['tru']) && substr($content,$posn,$width-$x) === str_repeat(' ',$width-$x)) {
$output .= CR.LF;
break;
}

View File

@ -13,11 +13,23 @@ use App\Models\Frame as FrameModel;
use App\Models\Mode;
abstract class Server {
private $mo = NULL; // Our Mode object
private $co = NULL;
protected $blp = 0; // Size of Bottom Line Pollution
protected $baseline = ''; // Whats on the baseline currently
protected $pid = NULL; // Client PID
// Our Mode object, this determines if we are an ANSI or VIDEOTEX server
private $mo = NULL;
// The client connection object of the currently connected client.
public $co = NULL;
// The currently rendered Frame to the client of Frame/*
public $fo = NULL;
// Size of Bottom Line Pollution
protected $blp = 0;
// Whats on the baseline currently
protected $baseline = '';
// Client PID
protected $pid = NULL;
public function __construct(Mode $o)
{
@ -31,6 +43,7 @@ abstract class Server {
define('MODE_RFSENT', 6);
define('MODE_RFERROR', 7);
define('MODE_RFNOTSENT', 8);
define('MODE_CONTROL', 9); // Do nothing, a method is controlling input
define('ACTION_RELOAD', 1);
define('ACTION_GOTO', 2);
@ -40,16 +53,11 @@ abstract class Server {
define('ACTION_TERMINATE', 6);
define('ACTION_SUBMITRF', 7); // Offer to submit a response frame
define('ACTION_STAR', 8);
define('ACTION_EDIT', 9); // Edit current frame
define('CONTROL_TELNET', 1); // Telnet session control
define('CONTROL_METHOD', 2); // Send input to an external method
// Keyboard presses
define('KEY_DELETE', chr(8));
define('KEY_LEFT', chr(136));
define('KEY_RIGHT', chr(137));
define('KEY_DOWN', chr(138));
define('KEY_UP', chr(139));
define('CONTROL_EDIT', 3); // Controller to edit frame
define('TCP_IAC', chr(255));
define('TCP_DONT', chr(254));
@ -67,11 +75,12 @@ abstract class Server {
define('TCP_OPT_WINDOWSIZE', chr(31));
define('TCP_OPT_LINEMODE', chr(34));
// Status messages
define('MSG_SENDORNOT', GREEN.'KEY 1 TO SEND, 2 NOT TO SEND');
define('MSG_SENT', GREEN.'MESSAGE SENT - KEY '.HASH.' TO CONTINUE');
define('MSG_NOTSENT', GREEN.'MESSAGE NOT SENT - KEY '.HASH.' TO CONTINUE');
define('ERR_DATABASE', RED.'UNAVAILABLE AT PRESENT - PLSE TRY LATER');
define('ERR_DATABASE', RED.'UNAVAILABLE AT PRESENT - PLS TRY LATER');
define('ERR_NOTSENT', WHITE.'MESSAGE NOT SENT DUE TO AN ERROR');
define('ERR_PRIVATE', WHITE.'PRIVATE PAGE'.GREEN.'- FOR EXPLANATION *37'.HASH.'..');
define('ERR_ROUTE', WHITE.'MISTAKE?'.GREEN.'TRY AGAIN OR TELL US ON *08');
@ -84,11 +93,13 @@ abstract class Server {
define('MSG_TIMEWARP', WHITE.'OTHER VERSIONS EXIST'.GREEN.'KEY *02 TO VIEW');
}
public function client()
{
return $this->co;
}
/**
* Write something to the system log.
*
* @param string $mode
* @param string $message
* @param array $data
*/
public function log(string $mode,string $message,array $data=[])
{
Log::$mode(sprintf('%s: %s',$this->pid,$message),$data);
@ -111,7 +122,9 @@ abstract class Server {
elseif ($pid)
return;
$fo = NULL;
// The next page we will load - this should have frame=,index=
$next_page = NULL;
$this->co = $client;
$this->pid = getmypid();
$this->log('info','Connection from: ',['client'=>$client->getAddress(),'server'=>$this->mo->name]);
@ -136,7 +149,6 @@ abstract class Server {
$action = ACTION_GOTO; // Initial action.
$control = FALSE; // Logic in control
$mode = FALSE; // Current mode.
$save = FALSE; //
$cmd = ''; // Current *command being typed in
$user = new User; // The logged in user
$method = collect(); // Method in control for CONTROL_METHOD
@ -147,19 +159,28 @@ abstract class Server {
$current['fieldpos'] = 0; // For current field, position within.
$current['prevmode'] = FALSE; // Previous mode - in case we need to go back to MODE_FIELD
$fielddata = [];
// @todo Get the login/start page, and if it is not available, throw the ERR_DATEBASE error.
if (isset($config['loginpage'])) {
$page = ['frame'=>$config['loginpage']];
$next_page = ['frame'=>$config['loginpage']];
} else if (!empty($service['start_page'])) {
$page = ['frame'=>$service['start_page']];
$next_page = ['frame'=>$service['start_page']];
} else {
$page = ['frame'=>'980']; // next page
$next_page = ['frame'=>'980']; // Default Login Page
}
while ($action != ACTION_TERMINATE) {
// Read a character from the client session
// @todo Add a timeout to read, and log user off if timeout exceeded.
$read = $client->read(1);
printf(". Got: %s (%s): Mode: [%s], Action: [%s], Control: [%s]\n",$read,ord($read),$mode,$action,$control);
echo "\n";
printf(". Got: %s (%s): Mode: [%s], Action: [%s], Control: [%s]\n",
(ord($read) < 32 ? '.' : $read),
ord($read),
serialize($mode),
serialize($action),
$control);
// It appears that read will return '' instead of false when a disconnect has occurred.
// We'll set it to NULL so its caught later
@ -174,17 +195,15 @@ abstract class Server {
$control = CONTROL_TELNET;
// Remember our Telnet Session Object
// @todo We might need to clear out the old mode/action states
if (! $session) {
if (! $session)
$session = Control::factory('telnet',$this);
}
$method->push($session);
}
}
if ($control AND $method->count()) {
printf("= Control going to method: %s\n", get_class($method->last()));
printf("= Start CONTROL: Going to method: %s\n",get_class($method->last()));
// Capture our state when we enter this method.
if (! array_key_exists('control',$method->last()->state)) {
@ -200,7 +219,7 @@ abstract class Server {
$mode = $method->last()->state['mode'];
if ($method->last()->complete()) {
printf("- Control complete: %s\n",get_class($method->last()));
printf("- Complete CONTROL: %s\n",get_class($method->last()));
$save = $method->pop();
if ($method->count()) {
@ -214,28 +233,34 @@ abstract class Server {
dump(sprintf('End: Control is now: %s: Method Count: %s',is_object($control) ? get_class($control) : serialize($control),$method->count()));
}
printf("- End CONTROL: Read %s (%s): Mode: [%s], Action: [%s], Control: [%s]\n",
(ord($read) < 32 ? '.' : $read),
ord($read),
serialize($mode),
serialize($action),
$control);
}
printf("- End Control: Read %s (%s): Mode: [%s], Action: [%s], Control: [%s]\n",$read,ord($read),$mode,$action,$control);
printf("= Start MODE: %s\n",serialize($mode));
switch ($mode) {
// Key presses during field input.
case MODE_FIELD:
$cmd = '';
$action = FALSE;
switch ($fo->type()) {
switch ($this->fo->type()) {
// Login frame.
case Frame::FRAMETYPE_LOGIN:
switch ($read) {
case HASH:
// If we are the main login screen, see if it is a new user
if ($fo->isCUG(0))
if ($this->fo->isCUG(0))
{
if ($current['field']->type == 't' AND array_get($fielddata,$current['fieldnum']) == 'NEW')
{
$action = ACTION_GOTO;
$page = ['frame'=>'981']; // @todo This should be in the DB.
$next_page = ['frame'=>'981']; // @todo This should be in the DB.
break 2;
}
@ -254,13 +279,13 @@ abstract class Server {
$current['fieldnum']++;
$current['fieldpos'] = 0;
if ($current['fieldnum'] < $fo->fields->count()) {
$current['fieldnum'] = $fo->getFieldId('edit',$current['fieldnum']);
if ($current['fieldnum'] < $this->fo->fields()->count()) {
$current['fieldnum'] = $this->fo->getFieldId('edit',$current['fieldnum']);
if ($current['fieldnum'] !== FALSE) {
$current['field'] = $fo->getField($current['fieldnum']);
$current['field'] = $this->fo->getField($current['fieldnum']);
$client->send($this->outputPosition($current['field']->x,$current['field']->y).CON);
$client->send($this->moveCursor($current['field']->x,$current['field']->y).CON);
$mode = MODE_FIELD;
// There were no (more) editable fields.
@ -288,7 +313,7 @@ abstract class Server {
if ($current['fieldpos'])
{
$current['fieldpos']--;
$client->send(LEFT.$fo::$if_filler.LEFT);
$client->send(LEFT.$this->fo::$if_filler.LEFT);
$fielddata[$current['fieldnum']] = substr($fielddata[$current['fieldnum']],0,-1);
$current['fielddata'][$current['fieldnum']] = substr($current['fielddata'][$current['fieldnum']],0,-1);
}
@ -332,7 +357,7 @@ abstract class Server {
$current['fieldpos'] = (($current['fieldpos'] + $current['field']->x) % 40) * 40;
} else {
$client->send($this->outputPosition($current['field']->x,$current['field']->y).CON);
$client->send($this->moveCursor($current['field']->x,$current['field']->y).CON);
$current['fieldpos'] = 0;
}
@ -354,7 +379,7 @@ abstract class Server {
$current['fieldpos']++;
$client->send($fo->isFieldMasked($current['field']->type) ?: $read);
$client->send($this->fo->isFieldMasked($current['field']->type) ?: $read);
}
}
@ -364,7 +389,7 @@ abstract class Server {
default:
$client->close();
throw new \Exception('Shouldnt get here', 500);
throw new \Exception('Shouldnt get here',500);
}
break;
@ -372,29 +397,32 @@ abstract class Server {
// Form submission: 1 to send, 2 not to send.
case MODE_SUBMITRF:
switch ($read) {
// @todo Input received, process it.
case '1':
$route = $fo->route(1);
// If we are in a control method, complete it
if ($control AND $method->count()) {
$method->last()->process();
} elseif ($route == '*' OR is_numeric($route)) {
} elseif ($this->fo->route(1) == '*' OR is_numeric($this->fo->route(1))) {
$this->sendBaseline($client,RED.'NO ACTION PERFORMED');
$mode = MODE_RFSENT;
} elseif ($ao = FrameClass\Action::factory($fo->route(1),$this,$user,$action,$mode)) {
} elseif ($ao = FrameClass\Action::factory($this->fo->route(1),$this,$user,$action,$mode)) {
$ao->handle($fielddata);
$mode = $ao->mode;
$action = $ao->action;
$user = $ao->uo;
// Is this a user logging in?
if ($ao->uo->exists AND $ao instanceof FrameClass\Action\Login)
{
$user = $ao->uo;
$history = collect();
}
if ($ao->page)
$page = $ao->page;
$next_page = $ao->page;
} else {
$this->sendBaseline($client, RED.'NO method exists...');
$this->sendBaseline($client,RED.'NO method exists...');
$mode = MODE_RFSENT;
}
@ -434,18 +462,18 @@ abstract class Server {
$client->send(COFF);
if ($read == HASH) {
if ($route = $fo->route(2) AND $route !== '*' AND is_numeric($route)) {
$page = ['frame'=>$route];
if ($x = $this->fo->route(2) AND $x !== '*' AND is_numeric($x)) {
$next_page = ['frame'=>$x];
} elseif (FrameModel::where('frame',$fo->frame())->where('index',$fo->index_next())->exists()) {
$page = ['frame'=>$fo->frame(),'index'=>$fo->index_next()];
} elseif (FrameModel::where('frame',$this->fo->frame())->where('index',$this->fo->index_next())->exists()) {
$next_page = ['frame'=>$this->fo->frame(),'index'=>$this->fo->index_next()];
} elseif ($route = $fo->route(0) AND $route !== '*' AND is_numeric($route)) {
$page = ['frame'=>$route];
} elseif ($x = $this->fo->route(0) AND $x !== '*' AND is_numeric($x)) {
$next_page = ['frame'=>$x];
// No further routes defined, go home.
} else {
$page = ['frame'=>0];
$next_page = ['frame'=>0];
}
$action = ACTION_GOTO;
@ -461,23 +489,23 @@ abstract class Server {
// Response form after NOT sending
case MODE_RFNOTSENT:
// Response form ERROR
// Response form ERROR
case MODE_RFERROR:
$client->send(COFF);
if ($read == HASH) {
if ($route = $fo->route(2) AND $route !== '*' AND is_numeric($route)) {
$page = ['frame'=>$route];
if ($x = $this->fo->route(2) AND $x !== '*' AND is_numeric($x)) {
$next_page = ['frame'=>$x];
} elseif (FrameModel::where('frame',$fo->frame())->where('index',$fo->index_next())->exists()) {
$page = ['frame'=>$fo->frame(),'index'=>$fo->index_next()];
} elseif (FrameModel::where('frame',$this->fo->frame())->where('index',$this->fo->index_next())->exists()) {
$next_page = ['frame'=>$this->fo->frame(),'index'=>$this->fo->index_next()];
} elseif ($route = $fo->route(0) AND $route !== '*' AND is_numeric($route)) {
$page = ['frame'=>$route];
} elseif ($x = $this->fo->route(0) AND $x !== '*' AND is_numeric($x)) {
$next_page = ['frame'=>$x];
// No further routes defined, go home.
} else {
$page = ['frame'=>0];
$next_page = ['frame'=>0];
}
$action = ACTION_GOTO;
@ -528,8 +556,8 @@ abstract class Server {
case '7':
case '8':
case '9':
if (is_numeric($fo->route($read))) {
$page = ['frame'=>$fo->route($read),'index'=>'a'];
if (is_numeric($this->fo->route($read))) {
$next_page = ['frame'=>$this->fo->route($read)];
$action = ACTION_GOTO;
@ -548,10 +576,18 @@ abstract class Server {
echo "- Waiting for Page Number\n";
// if it's a number, continue entry
if (strpos('0123456789', $read) !== FALSE) {
$cmd .= $read;
if (strpos('0123456789',$read) !== FALSE) {
$client->send($read);
$this->blp++;
$cmd .= $read;
}
// If its a backspace, delete last input
if ($read === KEY_DELETE AND strlen($cmd))
{
$client->send(BS.' '.BS);
$this->blp--;
$cmd = substr($cmd,0,-1);
}
// if we hit a special numeric command, deal with it.
@ -569,7 +605,7 @@ abstract class Server {
if ($cmd === '01') {
$client->send(COFF);
$timewarp = !$timewarp;
$this->sendBaseline($client, ($timewarp ? MSG_TIMEWARP_ON : MSG_TIMEWARP_OFF));
$this->sendBaseline($client,($timewarp ? MSG_TIMEWARP_ON : MSG_TIMEWARP_OFF));
$cmd = '';
$action = $mode = FALSE;
@ -586,9 +622,22 @@ abstract class Server {
break;
}
// Edit frame
// Catch if we are going to edit a child frame
if (preg_match('/^04/',$cmd) AND preg_match('/^[a-z]$/',$read))
{
$client->send(COFF);
$next_page = ['frame'=>substr($cmd,2),'index'=>$read];
$cmd = '';
$action = ACTION_EDIT;
break;
}
// Bookmark page
if ($cmd === '05') {
$this->sendBaseline($client, RED.'NOT IMPLEMENTED YET?');
$this->sendBaseline($client,RED.'NOT IMPLEMENTED YET?');
$mode = FALSE;
break;
@ -596,7 +645,7 @@ abstract class Server {
// Report a problem
if ($cmd === '08') {
$this->sendBaseline($client, RED.'NOT IMPLEMENTED YET?');
$this->sendBaseline($client,RED.'NOT IMPLEMENTED YET?');
$mode = FALSE;
break;
@ -607,6 +656,7 @@ abstract class Server {
$client->send(COFF);
$action = ACTION_GOTO;
$cmd = '';
$next_page = $this->fo->page(TRUE);
break;
}
@ -622,8 +672,8 @@ abstract class Server {
$current['prevmode'] = FALSE;
// @todo The cursor color could be wrong
$client->send($this->outputPosition($current['field']->x,$current['field']->y).CON);
$client->send(str_repeat($fo::$if_filler, $current['field']->length));
$client->send($this->moveCursor($current['field']->x,$current['field']->y).CON);
$client->send(str_repeat($this->fo::$if_filler,$current['field']->length));
$current['fieldreset'] = TRUE;
} else {
@ -638,18 +688,36 @@ abstract class Server {
$client->send(COFF);
$timewarpalt = FALSE;
// If input is in a control, terminate it
if ($control)
{
$method->pop();
$control = FALSE;
// Our method count should be zero
if ($method->count())
dd($method);
}
// Nothing typed between * and #
// *# means go back
if ($cmd === '') {
$action = ACTION_BACKUP;
} elseif ($cmd === '0') {
$page = $user->exists ? ['frame'=>1,'index'=>'a'] : ['frame'=>980,'index'=>'a']; // @todo Get from DB.
$next_page = ['frame'=>$user->exists ? 1 : 980]; // @todo Get from DB.
$action = ACTION_GOTO;
// Edit Frame
} elseif (preg_match('/^04/',$cmd)) {
$client->send(COFF);
$action = ACTION_EDIT;
$next_page = [
'frame' => substr($cmd,2) ?: $this->fo->frame(),
];
} else {
$page['frame'] = $cmd;
$page['index'] = 'a';
$next_page = ['frame'=>$cmd];
$action = ACTION_GOTO;
}
@ -661,17 +729,24 @@ abstract class Server {
break;
// Control is taking input
case MODE_CONTROL:
break;
default:
$this->log('debug','Not sure what we were doing?');
}
}
printf("- End Mode: Read %s (%s): Mode: [%s], Action: [%s], Control: [%s]\n",$read,ord($read),$mode,$action,$control);
printf("- End MODE: Read %s (%s): Mode: [%s], Action: [%s], Control: [%s]\n",
(ord($read) < 32 ? '.' : $read),
ord($read),
serialize($mode),
serialize($action),
$control);
}
// This section performs some action if it is deemed necessary
if ($action) {
printf("+ Performing action: %s\n",$action);
}
printf("= Start ACTION: %s\n",serialize($action));
switch ($action) {
case ACTION_STAR:
@ -679,7 +754,10 @@ abstract class Server {
// If there is something on the baseline, lets preserve it
if ($this->blp)
{
printf(". Preserving Baseline: %s\n",$this->baseline);
$current['baseline'] = $this->baseline;
}
$this->sendBaseline($client,GREEN.STAR,TRUE);
$client->send(CON);
@ -696,6 +774,30 @@ abstract class Server {
break;
// Edit Frame
case ACTION_EDIT:
$this->log('debug','Editing frame:',[$next_page]);
$next_fo = NULL;
// If we are editing a different frame, load it
try {
$next_fo = $this->mo->framePage($next_page['frame'],array_get($next_page,'index','a'),$this);
} catch (ModelNotFoundException $e) {
$next_fo = $this->mo->frameNew($this,$next_page['frame'],$next_page['index']);
}
$control = CONTROL_EDIT;
$method->push(Control::factory('editframe',$this,$next_fo));
$next_fo = NULL;
$method->last()->state['control'] = $control;
$method->last()->state['action'] = FALSE;
$method->last()->state['mode'] = MODE_FIELD;
$mode = MODE_CONTROL;
$action = FALSE;
break;
// GO Backwards
case ACTION_BACKUP:
// Do we have anywhere to go, drop the current page from the history.
@ -716,116 +818,126 @@ abstract class Server {
}
}
$page = $history->last();
$next_page = $history->last();
$this->log('debug','Backing up to:',$page);
// If there is no next page, we'll refresh the current page.
if ($next_page)
$this->log('debug','Backing up to:',$next_page);
// Go to next index frame.
case ACTION_NEXT:
// We need this extra test in case we come from ACTION_BACKUP
if ($action == ACTION_NEXT)
{
$current['page']['index'] = $fo->index();
$page['index'] = $fo->index_next();
$current['page']['index'] = $this->fo->index();
$next_page = ['frame'=>$this->fo->frame(),'index'=>$this->fo->index_next()];
}
// Look for requested page
// Look for requested page - charge for it to be loaded.
case ACTION_GOTO:
$current['frame'] = $fo;
// If we wanted a "Searching..." message, this is where to put it.
try {
$fo = $timewarpalt
? $this->mo->frame(FrameModel::findOrFail($timewarpalt))
: $this->mo->frameLoad($page['frame'],array_get($page,'index','a'),$this);
$this->log('debug',sprintf('Fetched frame: %s (%s)',$fo->id(),$fo->page()));
} catch (ModelNotFoundException $e) {
$this->sendBaseline($client,ERR_PAGE);
$mode = $action = FALSE;
$fo = $current['frame'] ?: $this->mo->frame($this->testFrame());
break;
}
// Is there a user logged in
if ($user)
if ($next_page OR $timewarpalt)
{
if ($fo->isFramePublic() AND $fo->isAccessible())
{
if ($fo->type() == Frame::FRAMETYPE_LOGIN AND $user->isMemberCUG($fo->getCUG()))
{
$this->sendBaseline($client,ERR_USER_ALREADYMEMBER);
$fo = $current['frame'];
$page = $history->last();
$mode = $action = FALSE;
$this->log('debug',sprintf('Frame Denied - Already Member: %s (%s)',$fo->id(),$fo->page()));
// If we wanted a "Searching..." message, this is where to put it.
try {
// Store our next frame in a temporary var while we determine if it can be displayed
$fo = $timewarpalt
? $this->mo->frameID($timewarpalt)
: $this->mo->framePage($next_page['frame'],array_get($next_page,'index','a'),$this);
break;
}
$this->log('debug',sprintf('Fetched frame: %s (%s)',$fo->id(),$fo->page()));
// If this is a login frame and the user is already a member.
} else {
if (! $fo->isOwner($user))
{
if (! $fo->isAccessible())
{
$this->sendBaseline($client,ERR_PAGE);
$fo = $current['frame'];
$page = $history->last();
$mode = $action = FALSE;
$this->log('debug',sprintf('Frame Denied - In Accessible: %s (%s)',$fo->id(),$fo->page()));
break;
}
if (! $user->isMemberCUG($fo->getCUG()))
{
$this->sendBaseline($client, ERR_PRIVATE);
$fo = $current['frame'];
$page = $history->last();
$mode = $action = FALSE;
$this->log('debug',sprintf('Frame Denied - Not in CUG [%s]: %s (%s)',$fo->getCUG()->id,$fo->id(),$fo->page()));
break;
}
}
}
} else {
// Is this a public frame in CUG 0?
if (! $fo->isCUG(0) OR ! $fo->isFramePublic())
{
} catch (ModelNotFoundException $e) {
// @todo Make sure parent frame exists, or display error
$this->sendBaseline($client,ERR_PAGE);
$mode = $action = FALSE;
if (! $fo)
$fo = $this->mo->frameTest($this);
break;
}
// Is there a user logged in
if ($user)
{
if ($fo->isFramePublic() AND $fo->isAccessible())
{
if ($fo->type() == Frame::FRAMETYPE_LOGIN AND $user->isMemberCUG($fo->getCUG()))
{
$this->sendBaseline($client,ERR_USER_ALREADYMEMBER);
$next_page = $history->last();
$mode = $action = FALSE;
$this->log('debug',sprintf('Frame Denied - Already Member: %s (%s)',$fo->id(),$fo->page()));
break;
}
// If this is a login frame and the user is already a member.
} else {
if (! $fo->isOwner($user))
{
if (! $fo->isAccessible())
{
$this->sendBaseline($client,ERR_PAGE);
$next_page = $history->last();
$mode = $action = FALSE;
$this->log('debug',sprintf('Frame Denied - In Accessible: %s (%s)',$fo->id(),$fo->page()));
break;
}
if (! $user->isMemberCUG($fo->getCUG()))
{
$this->sendBaseline($client,ERR_PRIVATE);
$next_page = $history->last();
$mode = $action = FALSE;
$this->log('debug',sprintf('Frame Denied - Not in CUG [%s]: %s (%s)',$fo->getCUG()->id,$fo->id(),$fo->page()));
break;
}
}
}
} else {
// Is this a public frame in CUG 0?
if (! $fo->isCUG(0) OR ! $fo->isFramePublic())
{
$this->sendBaseline($client,ERR_PAGE);
$next_page = $history->last();
$mode = $action = FALSE;
break;
}
}
}
$current['page'] = $fo->page(TRUE);
$current['fieldpos'] = 0;
// Only if new location, not going backwards
if (($history->last() != $page) AND ($action == ACTION_GOTO || $action == ACTION_NEXT)) {
$history->push($page);
if (($history->last() != $next_page) AND ($action == ACTION_GOTO || $action == ACTION_NEXT)) {
$history->push($next_page);
}
$current['fieldpos'] = 0;
$this->fo = $fo;
$fo = NULL;
$next_page = NULL;
$timewarpalt = NULL;
printf("+ Mode is: %s\n",$mode);
// drop into
case ACTION_RELOAD:
// Clear the baseline history
$this->sendBaseline($client,'');
$output = (string)$fo;
$current['baseline'] = '';
$output = (string)$this->fo;
if ($timewarpalt) {
$this->sendBaseline($client,sprintf(MSG_TIMEWARP_TO,$fo->created() ? $fo->created()->format('Y-m-d H:i:s') : 'UNKNOWN'));
$this->sendBaseline($client,sprintf(MSG_TIMEWARP_TO,$this->fo->created() ? $this->fo->created()->format('Y-m-d H:i:s') : 'UNKNOWN'));
}
switch ($fo->type()) {
switch ($this->fo->type()) {
default:
// Standard Frame
case Frame::FRAMETYPE_INFO:
@ -842,7 +954,7 @@ abstract class Server {
// If this is the registration page
// @todo Should be evaluated out of the DB
if ($fo->page() == '981a') {
if ($this->fo->page() == '981a') {
$control = CONTROL_METHOD;
$method->push(Control::factory('register',$this));
$method->last()->state['control'] = $control;
@ -857,10 +969,10 @@ abstract class Server {
$fielddata = [];
$current['fielddata'] = [];
if (count($fo->fields)) {
if ($this->fo->fields()->count()) {
// Get our first editable field.
$current['fieldnum'] = $fo->getFieldId('edit',0);
$current['field'] = $fo->getField($current['fieldnum']);
$current['fieldnum'] = $this->fo->getFieldId('edit',0);
$current['field'] = $this->fo->getField($current['fieldnum']);
$current['fieldreset'] = TRUE;
if ($current['fieldnum'] !== FALSE) {
@ -896,17 +1008,17 @@ abstract class Server {
$cmd = '';
$y = 0;
$output = $this->outputPosition(0, $y++) . WHITE . NEWBG . RED . 'TIMEWARP INFO FOR Pg.' . BLUE . $fo->page(). WHITE;
//$output .= $this->outputPosition(0, $y++) . WHITE . NEWBG . BLUE . 'Service : ' . substr($service['service_name'] . str_repeat(' ', 27), 0, 27);
//$output .= $this->outputPosition(0, $y++) . WHITE . NEWBG . BLUE . 'Varient : ' . substr($varient['varient_name'] . str_repeat(' ', 27), 0, 27);
$output .= $this->outputPosition(0, $y++) . WHITE . NEWBG . BLUE . 'Dated : ' .substr(($fo->created() ? $fo->created()->format('j F Y') : 'Unknown').str_repeat(' ', 27), 0, 27);
$output = $this->moveCursor(0,$y++).WHITE.NEWBG.RED.'TIMEWARP INFO FOR Pg.'.BLUE.$this->fo->page().WHITE;
//$output .= $this->moveCursor(0,$y++).WHITE.NEWBG.BLUE.'Service : '.substr($service['service_name'].str_repeat(' ',27),0,27);
//$output .= $this->moveCursor(0,$y++).WHITE.NEWBG.BLUE.'Varient : '.substr($varient['varient_name'].str_repeat(' ',27),0,27);
$output .= $this->moveCursor(0,$y++).WHITE.NEWBG.BLUE.'Dated : ' .substr(($this->fo->created() ? $this->fo->created()->format('j F Y') : 'Unknown').str_repeat(' ',27),0,27);
$alts = $fo->alts($this->mo)->get();
$alts = $this->fo->alts($this->mo)->get();
if (count($alts)) {
$n = 1;
$output .= $this->outputPosition(0, $y++) . WHITE . NEWBG . RED . 'ALTERNATIVE VERSIONS:' . str_repeat(' ', 16);
$output .= $this->moveCursor(0,$y++).WHITE.NEWBG.RED.'ALTERNATIVE VERSIONS:'.str_repeat(' ',16);
foreach ($alts as $o) {
$date = $o->created_at->format('d M Y');
@ -919,7 +1031,7 @@ abstract class Server {
$line .= BLUE.$date.' '.$o->note;
$output .= $this->outputPosition(0,$y++).$line.str_repeat(' ',40-$this->strlenv($line)); // @todo should use frame::page_length
$output .= $this->moveCursor(0,$y++).$line.str_repeat(' ',40-$this->fo->strlenv($line)); // @todo should use frame::page_length
}
if ($timewarp) {
@ -933,11 +1045,16 @@ abstract class Server {
break;
}
printf("- End Action: Read %s (%s): Mode: [%s], Action: [%s], Control: [%s]\n",$read,ord($read),$mode,$action,$control);
printf("- End ACTION: Read %s (%s): Mode: [%s], Action: [%s], Control: [%s]\n",
(ord($read) < 32 ? '.' : $read),
ord($read),
serialize($mode),
serialize($action),
$control);
// We need to reposition the cursor to the current field.
if ($current['fieldreset'] !== FALSE) {
$client->send($this->outputPosition($current['field']->x,$current['field']->y).CON);
$client->send($this->moveCursor($current['field']->x,$current['field']->y).CON);
$current['fieldreset'] = FALSE;
}
@ -962,7 +1079,7 @@ abstract class Server {
/**
* Move the cursor via the shortest path.
*/
abstract function outputPosition($x,$y);
abstract function moveCursor($x,$y);
/**
* Send a message to the base line
@ -973,20 +1090,4 @@ abstract class Server {
* @param bool $reposition
*/
abstract function sendBaseline($client,$text,$reposition=FALSE);
/**
* Calculate the length of text
*
* ESC characters are two chars, and need to be counted as one.
*
* @param $text
* @return int
*/
abstract function strlenv($text):int;
/**
* Return a test frame appropriate for this server.
* @return mixed
*/
abstract public function testFrame();
}

View File

@ -2,8 +2,6 @@
namespace App\Classes\Server;
use Illuminate\Support\Facades\Log;
use App\Classes\Server as AbstractServer;
use App\Models\Mode;
@ -22,6 +20,7 @@ class Ansi extends AbstractServer {
define('UP', ESC.'[A'); // Move Cursor
define('CR', chr(13));
define('LF', chr(10));
define('BS', chr(8));
define('CLS', ESC.'[2J');
define('HASH', '#'); // Enter
define('STAR', '*'); // Star Entry
@ -47,34 +46,32 @@ class Ansi extends AbstractServer {
define('R_WHITE', WHITE);
//define('FLASH',chr(8));
// Keyboard presses
// @todo Check where these are used vs the keys defined above?
define('KEY_DELETE', chr(8));
define('KEY_LEFT', chr(136));
define('KEY_RIGHT', chr(137));
define('KEY_DOWN', chr(138));
define('KEY_UP', chr(139));
parent::__construct($o);
}
function outputPosition($x,$y) {
function moveCursor($x,$y) {
printf(". Move cursor %s,%s\n",$x,$y);
return ESC.'['.$y.';'.$x.'f';
}
// Abstract function
public function sendBaseline($client,$text,$reposition=FALSE) {
$client->send(CSAVE.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)) : CRESTORE)
($this->blp > $this->fo->strlenv($text)
? str_repeat(' ',$this->blp-$this->fo->strlenv($text)).
($reposition ? ESC.'[24;0f'.str_repeat(RIGHT,$this->fo->strlenv($text)) : CRESTORE)
: ($reposition ? '' : CRESTORE))
);
$this->blp = $this->strlenv($text);
$this->blp = $this->fo->strlenv($text);
$this->baseline = $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);
}
}

View File

@ -48,7 +48,7 @@ class Videotex extends AbstractServer {
parent::__construct($o);
}
public function outputPosition($x,$y) {
public function moveCursor($x,$y) {
// Take the shortest path.
if ($y < 12) {
return HOME.
@ -74,15 +74,4 @@ class Videotex extends AbstractServer {
$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);
}
}

View File

@ -20,6 +20,11 @@ class Mode extends Model
return $this->hasMany(Frame::class);
}
public function frameId(int $id)
{
return $this->frameLoad(Frame::findOrFail($id));
}
/**
* Return a frame class for the Model
*
@ -27,7 +32,7 @@ class Mode extends Model
* @return FrameClass
* @throws \Exception
*/
public function frame(Model $o): FrameClass
private function frameLoad(Model $o): FrameClass
{
switch (strtolower($this->name)) {
case 'ansi':
@ -35,10 +40,28 @@ class Mode extends Model
case 'videotex':
return new VideotexFrame($o);
default:
throw new \Exception('Unknown Frame type: '.$mo->name);
throw new \Exception('Unknown Frame type: '.$this->name);
}
}
public function frameNew(Server $so,int $frame,string $index='a'): FrameClass
{
$o = new Frame;
$o->frame = $frame;
$o->index = $index;
$o->mode_id = $this->id;
// Make sure parent frame exists
// @todo make sure not trying to edit test frames
if ($o->index != 'a' AND ! FrameModel::where('frame',$fo->frame())->where('index',$fo->index_prev())->exists())
{
$so->sendBaseline($so->co,ERR_ROUTE);
return new Frame;
}
return $this->frameLoad($o);
}
/**
* Fetch a specific frame from the DB
*
@ -48,17 +71,28 @@ class Mode extends Model
* @return FrameClass
* @throws \Exception
*/
public function frameLoad(int $frame,string $index,Server $so): FrameClass
public function framePage(int $frame,string $index='a',Server $so): FrameClass
{
return $this->frame(
return ($frame == '999' and $index == 'a')
// Return our internal test frame.
($frame == '999' and $index == 'a')
? $so->testFrame()
: $this->frames()
->where('frame','=',$frame)
->where('index','=',$index)
->firstOrFail()
);
? $this->frameTest($so)
: $this->frameLoad($this->frames()
->where('frame','=',$frame)
->where('index','=',$index)
->firstOrFail()
);
}
public function frameTest(Server $so): FrameClass
{
switch (strtolower($this->name)) {
case 'ansi':
return AnsiFrame::testFrame($so);
case 'videotex':
return VideotexFrame::testFrame($so);
default:
throw new \Exception('Unknown Frame type: '.$this->name);
}
}
/**

View File

@ -98,7 +98,7 @@ return [
// otherwise you can them out of the configuration array
#'sslcert' => env('DB_SSLCERT', 'client.crt'),
#'sslkey' => env('DB_SSLKEY', 'client.key'),
#'sslrootcert' => env('DB_SSLROOTCERT', 'ca.crt'),
'sslrootcert' => env('DB_SSLROOTCERT', 'ca.crt'),
],
],