diff --git a/app/Classes/Control.php b/app/Classes/Control.php new file mode 100644 index 0000000..db5531a --- /dev/null +++ b/app/Classes/Control.php @@ -0,0 +1,48 @@ +so = $so; + + $this->boot(); + } + + // Default boot method if a child class doesnt have one. + protected function boot() { + $this->state['mode'] = FALSE; + } + + /** + * Has control completed? + */ + public function complete() + { + return $this->complete; + } + + // @todo Change to Dynamic Calls by the existence of files in App\Classes\Control + public static function factory(string $name, Server $so) { + switch ($name) { + case 'register': + return new Register($so); + + case 'telnet': + return new Telnet($so); + + default: + throw new \Exception('Unknown control method: '.$name); + } + } + + abstract public function handle(string $char); +} \ No newline at end of file diff --git a/app/Classes/Control/Register.php b/app/Classes/Control/Register.php new file mode 100644 index 0000000..0416fb6 --- /dev/null +++ b/app/Classes/Control/Register.php @@ -0,0 +1,190 @@ +so->sendBaseline($this->so->client(),GREEN.'Select User Name'.WHITE); + } + + /** + * Handle Registration Form Input + * + * This function assumes the form has 7 fields in a specific order. + * + * @todo Make this form more dynamic, or put some configuration in a config file, so that there is flexibility + * in field placement. + * @param string $read + * @param array $current + * @return string + */ + 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) { + + // Our registration page + // @todo get this from the DB + if ($current['page']['frame'] == '981') { + + // 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); + + return ''; + } + + $this->data['user'] = $current['fielddata'][$current['fieldnum']]; + $this->so->sendBaseline($this->so->client(), GREEN . 'Enter Real Name'.WHITE); + + break; + + // Real Name + case 1: + $this->data['name'] = $current['fielddata'][$current['fieldnum']]; + $this->so->sendBaseline($this->so->client(), GREEN . 'Enter Email Address'.WHITE); + + break; + + // 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); + + 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); + + return ''; + } + + $this->data['email'] = $current['fielddata'][$current['fieldnum']]; + $this->data['token'] = sprintf('%06.0f',rand(0,999999)); + + $this->so->sendBaseline($this->so->client(), YELLOW . 'PROCESSING...'.WHITE); + Mail::to($this->data['email'])->sendNow(new SendToken($this->data['token'])); + + if (Mail::failures()) { + dump('Failure?'); + + dump(Mail::failures()); + } + + $this->so->sendBaseline($this->so->client(), GREEN . 'Enter Password'.WHITE); + + break; + + // Enter Password + case 3: + $this->data['password'] = $current['fielddata'][$current['fieldnum']]; + $this->so->sendBaseline($this->so->client(), GREEN . 'Confirm Password'.WHITE); + + break; + + // 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); + + return ''; + } + + $this->so->sendBaseline($this->so->client(), GREEN . 'Enter Location'.WHITE); + + 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); + + break; + + // 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); + + return ''; + } + + 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 { + $this->so->sendBaseline($this->so->client(), RED . 'FIELD REQUIRED...'.WHITE); + + return ''; + } + } + } + } + + return $read; + } + + public function process() + { + $o = new User; + $o->login = $this->data['user']; + $o->email = $this->data['email']; + $o->password = $this->data['password']; + $o->name = $this->data['name']; + $o->location = $this->data['location']; + + try { + $o->save(); + $this->so->sendBaseline($this->so->client(), 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->log('error',$e->getMessage()); + $this->state['action'] = ACTION_RELOAD; + } + + $this->complete = TRUE; + } +} \ No newline at end of file diff --git a/app/Classes/Control/Telnet.php b/app/Classes/Control/Telnet.php new file mode 100644 index 0000000..9c34be3 --- /dev/null +++ b/app/Classes/Control/Telnet.php @@ -0,0 +1,101 @@ +state['mode'] = FALSE; + $this->so->log('debug',sprintf('Session Char (%s)',ord($read)),['complete'=>$this->complete,'option'=>$this->option]); + + switch ($read) { + // Command being sent. + case TCP_IAC: + $this->complete = FALSE; + $this->note = 'IAC '; + + break; + + case TCP_SB: + $this->option = TRUE; + + break; + + case TCP_SE: + $this->option = FALSE; + $this->complete = TRUE; + $this->so->log('debug',sprintf('Session Terminal: %s',$this->terminal)); + + break; + + case TCP_DO: + $this->note .= 'DO '; + + break; + + case TCP_WILL: + $this->note .= 'WILL '; + + break; + + case TCP_WONT: + $this->note .= 'WONT '; + + break; + + case TCP_OPT_TERMTYPE: + + break; + + case TCP_OPT_ECHO: + $this->note .= 'ECHO'; + $this->complete = TRUE; + + $this->so->log('debug',sprintf('Session Note: %s',$this->note)); + + break; + + case TCP_OPT_SUP_GOAHEAD: + $this->note .= 'SUPPRESS GO AHEAD'; + $this->complete = TRUE; + + $this->so->log('debug',sprintf('Session Note: %s',$this->note)); + + break; + + case TCP_OPT_WINDOWSIZE: + $this->note .= 'WINDOWSIZE'; + $this->complete = TRUE; + + $this->so->log('debug',sprintf('Session Note: %s',$this->note)); + + break; + + default: + if ($this->option AND $read) { + $this->terminal .= $read; + + } else { + $this->so->log('debug',sprintf('Unhandled char in session_init: %s (%s)',$read,ord($read))); + } + } + + return ''; + } +} \ No newline at end of file diff --git a/app/Classes/Frame.php b/app/Classes/Frame.php index e62afaa..d29ac89 100644 --- a/app/Classes/Frame.php +++ b/app/Classes/Frame.php @@ -280,7 +280,7 @@ abstract class Frame */ public function isFramePublic(): bool { - return $this->frame->closed ? FALSE : TRUE; + return $this->frame->public ? TRUE : FALSE; } // @todo To implement @@ -401,7 +401,7 @@ abstract class Frame $o->frame = 999; $o->index = 'a'; $o->access = 1; - $o->closed = 0; + $o->public = 1; $o->cls = 1; // Header diff --git a/app/Classes/Parser.php b/app/Classes/Parser.php index 58f1f19..264962e 100644 --- a/app/Classes/Parser.php +++ b/app/Classes/Parser.php @@ -2,6 +2,6 @@ namespace App\Classes; -class Parser +abstract class Parser { } \ No newline at end of file diff --git a/app/Classes/Server.php b/app/Classes/Server.php index 3ce4f55..f33766d 100644 --- a/app/Classes/Server.php +++ b/app/Classes/Server.php @@ -16,6 +16,7 @@ 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 public function __construct(Mode $o) @@ -40,6 +41,9 @@ abstract class Server { define('ACTION_SUBMITRF', 7); // Offer to submit a response frame define('ACTION_STAR', 8); + 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)); @@ -114,9 +118,7 @@ abstract class Server { // We are now the child. try { - $session_init = $session_option = FALSE; - $session_note = ''; // TCP Session Notice - $session_term = ''; // TCP Terminal Type + $session = NULL; // TCP Session Details $client->send(TCP_IAC . TCP_DO . TCP_OPT_SUP_GOAHEAD); // DO SUPPRES GO AHEAD $client->send(TCP_IAC . TCP_WONT . TCP_OPT_LINEMODE); // WONT LINEMODE @@ -134,6 +136,8 @@ abstract class Server { $cmd = ''; // Current *command being typed in $mode = FALSE; // Current mode. $user = new User; // The logged in user + $control = FALSE; // Logic in control + $method = collect(); // Method in control for CONTROL_METHOD $current = []; // Attributes about the current page // field/fieldnum indexes are for fields on the active page @@ -161,85 +165,52 @@ abstract class Server { $read = NULL; if ($read != '') { - // Client initiation input - // TELNET http://pcmicro.com/netfoss/telnet.html - if ($read == TCP_IAC OR $session_init OR $session_option) { - $this->log('debug',sprintf('Session Char (%s)',ord($read)),['init'=>$session_init,'option'=>$session_option]); + if ($read == TCP_IAC) { - switch ($read) { - // Command being sent. - case TCP_IAC: - $session_init = TRUE; - $session_note = 'IAC '; + // If we are not already in a TELNET LOOP + if ($control !== CONTROL_TELNET) { + $control = CONTROL_TELNET; - continue 2; + // Remember our Telnet Session Object + // @todo We might need to clear out the old mode/action states + if (! $session) { + $session = Control::factory('telnet',$this); + } - case TCP_SB: - $session_option = TRUE; + $method->push($session); + } + } - continue 2; + if ($control AND $method->count()) { + printf("= Control going to method: %s\n", get_class($method->last())); - case TCP_SE: - $session_option = $session_init = FALSE; - $this->log('debug',sprintf('Session Terminal: %s',$session_term)); - $read = ''; + // Capture our state when we enter this method. + if (! array_key_exists('control',$method->last()->state)) { + $method->last()->state['control'] = $control; + $method->last()->state['action'] = $action; + } - break; + $method->last()->state['mode'] = $mode; + $action = FALSE; - case TCP_DO: - $session_note .= 'DO '; + // Pass Control to Method + $read = $method->last()->handle($read,$current); + $mode = $method->last()->state['mode']; - continue 2; + if ($method->last()->complete()) { + printf("- Control complete: %s\n",get_class($method->last())); + $save = $method->pop(); - case TCP_WILL: - $session_note .= 'WILL '; + if ($method->count()) { + $control = $method->last()->state['control']; - continue 2; + } else { + $mode = $save->state['mode']; + $action = $save->state['action']; + $control = FALSE; + } - case TCP_WONT: - $session_note .= 'WONT '; - - continue 2; - - case TCP_OPT_TERMTYPE: - - continue 2; - - case TCP_OPT_ECHO: - $session_note .= 'ECHO'; - $session_init = FALSE; - $read = ''; - - $this->log('debug',sprintf('Session Note: %s',$session_note)); - - continue; - - case TCP_OPT_SUP_GOAHEAD: - $session_note .= 'SUPPRESS GO AHEAD'; - $session_init = FALSE; - $read = ''; - - $this->log('debug',sprintf('Session Note: %s',$session_note)); - - continue; - - case TCP_OPT_WINDOWSIZE: - $session_note .= 'WINDOWSIZE'; - $session_init = FALSE; - $read = ''; - - $this->log('debug',sprintf('Session Note: %s',$session_note)); - - continue; - - default: - if ($session_option AND $read) { - $session_term .= $read; - $read = ''; - - } else { - $this->log('debug',sprintf('Unhandled char in session_init: %s (%s)',$read,ord($read))); - } + dump(sprintf('End: Control is now: %s: Method Count: %s',is_object($control) ? get_class($control) : serialize($control),$method->count())); } } @@ -261,6 +232,8 @@ abstract class Server { { $action = ACTION_GOTO; $page = ['frame'=>'981']; // @todo This should be in the DB. + + break 2; } } @@ -303,6 +276,7 @@ abstract class Server { $action = ACTION_STAR; $current['fieldpos'] = 0; $fielddata[$current['fieldnum']] = ''; + $current['fielddata'][$current['fieldnum']] = ''; break; @@ -312,6 +286,7 @@ abstract class Server { $current['fieldpos']--; $client->send(LEFT.$fo::$if_filler.LEFT); $fielddata[$current['fieldnum']] = substr($fielddata[$current['fieldnum']],0,-1); + $current['fielddata'][$current['fieldnum']] = substr($current['fielddata'][$current['fieldnum']],0,-1); } break; @@ -360,15 +335,19 @@ abstract class Server { break; case ESC: - break;; + break; // Record Data Entry default: if (ord($read) > 31 && $current['fieldpos'] < $current['field']->length) { - if (! array_get($fielddata,$current['fieldnum'])) + if (! array_key_exists($current['fieldnum'],$current['fielddata'])) { + $current['fielddata'][$current['fieldnum']] = ''; $fielddata[$current['fieldnum']] = ''; + } + + $fielddata[$current['fieldnum']]{$current['fieldpos']} = $read; // @todo delete + $current['fielddata'][$current['fieldnum']]{$current['fieldpos']} = $read; - $fielddata[$current['fieldnum']]{$current['fieldpos']} = $read; $current['fieldpos']++; $client->send($fo->isFieldMasked($current['field']->type) ?: $read); @@ -393,7 +372,11 @@ abstract class Server { case '1': $route = $fo->route(1); - if ($route == '*' OR is_numeric($route)) { + // If we are in a control method, complete it + if ($control AND $method->count()) { + $method->last()->process(); + + } elseif ($route == '*' OR is_numeric($route)) { $this->sendBaseline($client,RED.'NO ACTION PERFORMED'); $mode = MODE_RFSENT; @@ -415,9 +398,13 @@ abstract class Server { break; case '2': - $this->sendBaseline($client,MSG_NOTSENT);; + $this->sendBaseline($client,MSG_NOTSENT); $mode = MODE_RFNOTSENT; + // If a Control method was rejected, we can clear it + if ($control AND $method->count()) + $method->pop(); + break; case STAR: @@ -564,10 +551,11 @@ abstract class Server { } // Toggle Timewarp Mode + // @todo in forms, the cursor is in the wrong location for ANSI 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; @@ -575,6 +563,7 @@ abstract class Server { } // Present Timewarp Frames + // @todo in forms, the cursor is in the wrong location for ANSI if ($cmd === '02') { $client->send(COFF); $action = ACTION_INFO; @@ -583,6 +572,14 @@ abstract class Server { break; } + // Report a problem + if ($cmd === '08') { + $this->sendBaseline($client, RED.'NOT IMPLEMENTED YET?'); + $read = STAR; + + break; + } + // Reload page if ($cmd === '09') { $client->send(COFF); @@ -595,12 +592,14 @@ abstract class Server { // Another star aborts the command. if ($read === STAR) { $action = FALSE; - $this->sendBaseline($client,''); + $this->sendBaseline($client,array_get($current,'baseline','')); $cmd = ''; if ($current['prevmode'] == MODE_FIELD) { $mode = $current['prevmode']; $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)); $current['fieldreset'] = TRUE; @@ -654,6 +653,10 @@ abstract class Server { case ACTION_STAR: echo "+ Star command...\n"; + // If there is something on the baseline, lets preserve it + if ($this->blp) + $current['baseline'] = $this->baseline; + $this->sendBaseline($client,GREEN.STAR,TRUE); $client->send(CON); $action = FALSE; @@ -773,6 +776,8 @@ abstract class Server { $history->push($page); } + printf("+ Mode is: %s\n",$mode); + // drop into case ACTION_RELOAD: $this->sendBaseline($client,''); @@ -793,11 +798,25 @@ abstract class Server { // Login Frame. case Frame::FRAMETYPE_LOGIN: + $client->send($output); + $output = ''; + + // If this is the registration page + // @todo Should be evaluated out of the DB + if ($fo->page() == '981a') { + $control = CONTROL_METHOD; + $method->push(Control::factory('register',$this)); + $method->last()->state['control'] = $control; + $method->last()->state['action'] = $action; + $method->last()->state['mode'] = MODE_FIELD; + } + // Active Frame. Prestel uses this for a Response Frame. case Frame::FRAMETYPE_ACTION: $client->send($output); // holds data entered by user. $fielddata = []; + $current['fielddata'] = []; if (count($fo->fields)) { // Get our first editable field. diff --git a/app/Classes/Server/Ansi.php b/app/Classes/Server/Ansi.php index 75e88a6..1e02394 100644 --- a/app/Classes/Server/Ansi.php +++ b/app/Classes/Server/Ansi.php @@ -13,6 +13,8 @@ class Ansi extends AbstractServer { define('ESC', chr(27)); define('CON', ESC.'[?25h'); // Cursor On define('COFF', ESC.'[?25l'); // Cursor Off + define('CSAVE', ESC.'[s'); // Save Cursor position + define('CRESTORE',ESC.'[u'); // Restore to saved position define('HOME', ESC.'[0;0f'); define('LEFT', ESC.'[D'); // Move Cursor define('RIGHT', ESC.'[C'); // Move Cursor @@ -54,14 +56,15 @@ class Ansi extends AbstractServer { // Abstract function public function sendBaseline($client,$text,$reposition=FALSE) { - $client->send(ESC.'[24;0f'.$text. + $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)) : '') - : '') + ($reposition ? ESC.'[24;0f'.str_repeat(RIGHT,$this->strlenv($text)) : CRESTORE) + : ($reposition ? '' : CRESTORE)) ); $this->blp = $this->strlenv($text); + $this->baseline = $text; } // Abstract function diff --git a/app/Console/Commands/FrameImport.php b/app/Console/Commands/FrameImport.php index 7a6194a..93a1747 100644 --- a/app/Console/Commands/FrameImport.php +++ b/app/Console/Commands/FrameImport.php @@ -3,6 +3,7 @@ namespace App\Console\Commands; use App\Models\Frame; +use App\Models\Mode; use Illuminate\Console\Command; use Illuminate\Database\Eloquent\ModelNotFoundException; @@ -15,9 +16,9 @@ class FrameImport extends Command */ protected $signature = 'frame:import {frame} {index} {file} '. '{--access=0 : Is frame accessible }'. - '{--closed=1 : Is frame limited to CUG }'. + '{--public=0 : Is frame limited to CUG }'. '{--cost=0 : Frame Cost }'. - '{--mode=1 : Frame Emulation Mode }'. + '{--mode=videotex : Frame Emulation Mode }'. '{--replace : Replace existing frame}'. '{--type=i : Frame Type}'. '{--trim : Trim off header (first 40 chars)}'; @@ -56,12 +57,14 @@ class FrameImport extends Command if (! file_exists($this->argument('file'))) throw new \Exception('File not found: '.$this->argument('file')); + $mo = Mode::where('name',$this->option('mode'))->firstOrFail(); + $o = new Frame; if ($this->option('replace')) { try { $o = $o->where('frame',$this->argument('frame')) ->where('index',$this->argument('index')) - ->where('mode_id',$this->option('mode')) + ->where('mode_id',$mo->id) ->firstOrFail(); } catch (ModelNotFoundException $e) { @@ -72,7 +75,7 @@ class FrameImport extends Command } else { $o->frame = $this->argument('frame'); $o->index = $this->argument('index'); - $o->mode_id = $this->option('mode'); + $o->mode_id = $mo->id; } $o->content = ($this->option('trim')) @@ -80,10 +83,10 @@ class FrameImport extends Command : file_get_contents($this->argument('file')); $o->access = $this->option('access'); - $o->closed = $this->option('closed'); + $o->public = $this->option('public'); $o->cost = $this->option('cost'); $o->type = $this->option('type'); $o->save(); } -} +} \ No newline at end of file diff --git a/app/Mail/SendToken.php b/app/Mail/SendToken.php new file mode 100644 index 0000000..3016beb --- /dev/null +++ b/app/Mail/SendToken.php @@ -0,0 +1,40 @@ +token = $token; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + return $this + ->markdown('email.sendtoken') + ->subject('Token to complete registration') + ->with(['token'=>$this->token]); + } +} diff --git a/app/Models/Frame.php b/app/Models/Frame.php index 7571cbf..9c4fc7a 100644 --- a/app/Models/Frame.php +++ b/app/Models/Frame.php @@ -25,6 +25,18 @@ class Frame extends Model }); } + /** + * For cockroachDB, content is a "resource stream" + * + * @return bool|string + */ + public function getContentAttribute() + { + return is_resource($this->attributes['content']) + ? stream_get_contents($this->attributes['content']) + : $this->attributes['content']; + } + /** * Return the Page Number * diff --git a/composer.json b/composer.json index 811f848..a209a6c 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,8 @@ "fideloper/proxy": "^4.0", "laravel/framework": "5.7.*", "laravel/tinker": "^1.0", - "lukaszkujawa/php-multithreaded-socket-server": "0.0.*" + "lukaszkujawa/php-multithreaded-socket-server": "0.0.*", + "nbj/cockroachdb-laravel": "^0.0.4@alpha" }, "require-dev": { "beyondcode/laravel-dump-server": "^1.0", diff --git a/composer.lock b/composer.lock index 314246a..9d7b082 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "ff208b67c5f938a92c08bec4d362e0b4", + "content-hash": "d320d65a20b1a9c380331a5c8b043c98", "packages": [ { "name": "dnoegel/php-xdg-base-dir", @@ -116,16 +116,16 @@ }, { "name": "doctrine/dbal", - "version": "v2.9.0", + "version": "v2.9.1", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "21fdabe2fc01e004e1966f200d900554876bc63c" + "reference": "ec74d6e300d78fbc896669c3ca57ef9719adc9c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/21fdabe2fc01e004e1966f200d900554876bc63c", - "reference": "21fdabe2fc01e004e1966f200d900554876bc63c", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/ec74d6e300d78fbc896669c3ca57ef9719adc9c6", + "reference": "ec74d6e300d78fbc896669c3ca57ef9719adc9c6", "shasum": "" }, "require": { @@ -194,7 +194,7 @@ "php", "queryobject" ], - "time": "2018-12-04T04:39:48+00:00" + "time": "2018-12-14T04:51:13+00:00" }, { "name": "doctrine/event-manager", @@ -1065,6 +1065,51 @@ ], "time": "2018-11-05T09:00:11+00:00" }, + { + "name": "nbj/cockroachdb-laravel", + "version": "0.0.4-alpha", + "source": { + "type": "git", + "url": "https://github.com/nbj/cockroachdb-laravel.git", + "reference": "852ac919fc57e4a483a027287000c6e37234d3c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nbj/cockroachdb-laravel/zipball/852ac919fc57e4a483a027287000c6e37234d3c8", + "reference": "852ac919fc57e4a483a027287000c6e37234d3c8", + "shasum": "" + }, + "type": "package", + "extra": { + "laravel": { + "providers": [ + "Nbj\\Cockroach\\CockroachServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Nbj\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nikolaj Boel Jensen", + "email": "nbj@cego.dk" + } + ], + "description": "CockroachDB driver for Laravel 5.6", + "keywords": [ + "cockroach", + "cockroachdb", + "laravel" + ], + "time": "2018-09-08T19:28:36+00:00" + }, { "name": "nesbot/carbon", "version": "1.36.1", @@ -4370,7 +4415,9 @@ ], "aliases": [], "minimum-stability": "dev", - "stability-flags": [], + "stability-flags": { + "nbj/cockroachdb-laravel": 15 + }, "prefer-stable": true, "prefer-lowest": false, "platform": { diff --git a/config/database.php b/config/database.php index 22347a4..5c23ae0 100644 --- a/config/database.php +++ b/config/database.php @@ -82,6 +82,25 @@ return [ 'prefix_indexes' => true, ], + 'cockroach' => [ + 'driver' => 'cockroach', + 'host' => env('DB_HOST', 'HOSTNAME-OF-COCKROACH-SERVER'), + 'port' => env('DB_PORT', '26257'), + 'database' => env('DB_DATABASE', 'DATABASE-NAME'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => 'utf8', + 'prefix' => '', + 'schema' => 'public', + 'sslmode' => 'prefer', + + // Only set these keys if you want to run en secure mode + // 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'), + ], + ], /* diff --git a/config/mail.php b/config/mail.php new file mode 100644 index 0000000..f400645 --- /dev/null +++ b/config/mail.php @@ -0,0 +1,136 @@ + env('MAIL_DRIVER', 'smtp'), + + /* + |-------------------------------------------------------------------------- + | SMTP Host Address + |-------------------------------------------------------------------------- + | + | Here you may provide the host address of the SMTP server used by your + | applications. A default option is provided that is compatible with + | the Mailgun mail service which will provide reliable deliveries. + | + */ + + 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), + + /* + |-------------------------------------------------------------------------- + | SMTP Host Port + |-------------------------------------------------------------------------- + | + | This is the SMTP port used by your application to deliver e-mails to + | users of the application. Like the host we have set this value to + | stay compatible with the Mailgun e-mail application by default. + | + */ + + 'port' => env('MAIL_PORT', 587), + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all e-mails sent by your application to be sent from + | the same address. Here, you may specify a name and address that is + | used globally for all e-mails that are sent by your application. + | + */ + + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), + 'name' => env('MAIL_FROM_NAME', 'Example'), + ], + + /* + |-------------------------------------------------------------------------- + | E-Mail Encryption Protocol + |-------------------------------------------------------------------------- + | + | Here you may specify the encryption protocol that should be used when + | the application send e-mail messages. A sensible default using the + | transport layer security protocol should provide great security. + | + */ + + 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + + /* + |-------------------------------------------------------------------------- + | SMTP Server Username + |-------------------------------------------------------------------------- + | + | If your SMTP server requires a username for authentication, you should + | set it here. This will get used to authenticate with your server on + | connection. You may also set the "password" value below this one. + | + */ + + 'username' => env('MAIL_USERNAME'), + + 'password' => env('MAIL_PASSWORD'), + + /* + |-------------------------------------------------------------------------- + | Sendmail System Path + |-------------------------------------------------------------------------- + | + | When using the "sendmail" driver to send e-mails, we will need to know + | the path to where Sendmail lives on this server. A default path has + | been provided here, which will work well on most of your systems. + | + */ + + 'sendmail' => '/usr/sbin/sendmail -bs', + + /* + |-------------------------------------------------------------------------- + | Markdown Mail Settings + |-------------------------------------------------------------------------- + | + | If you are using Markdown based email rendering, you may configure your + | theme and component paths here, allowing you to customize the design + | of the emails. Or, you may simply stick with the Laravel defaults! + | + */ + + 'markdown' => [ + 'theme' => 'default', + + 'paths' => [ + resource_path('views/vendor/mail'), + ], + ], + + /* + |-------------------------------------------------------------------------- + | Log Channel + |-------------------------------------------------------------------------- + | + | If you are using the "log" driver, you may specify the logging channel + | if you prefer to keep mail messages separate from other log entries + | for simpler reading. Otherwise, the default channel will be used. + | + */ + + 'log_channel' => env('MAIL_LOG_CHANNEL'), + +]; diff --git a/data/1a-main.ans b/data/1a-main.ans new file mode 100644 index 0000000..60ea90a --- /dev/null +++ b/data/1a-main.ans @@ -0,0 +1,19 @@ + ÚÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄ¿ +ͳù Õ¸Õ¸ú³ ͳù Õ͸ù³ ÍÔ͸ù Õ; ͳù Õ͸ú³ ddddddddddddddddd ³ ³Ô¾³ ³  ³ ÀÄÙ ³  ²²³ ³°²  ³ ³²³ ³ +°³ ³²±³ ³ °³ Õ͸ ³ °°°³ ³²Û °³ ³±³ ³ +±³ ³±°³ ³ ±³ ³°³ ³ ±±±³ ³°± ±³ ³°³ ³ +ͳù ³Íͳù³ ͳù ³Í³ù³ ÍÚÄÙù ÀÄ¿ ͳù ³Í³ù³  *0# to get back here anytime.  ÔÍ; Ô; ÔÍÍ; Ô; ÔÍÍÍÍÍÍ; ÔÍÍ; Ô; + + ß ß ßßß ßßÜ ßßß ßßß + Ü Ü Ü Ü Ü Ü Üß  ÜßÜ + ßßß ß ß ßß ßßß ß ß +ßßß ßßß ßßß ßßß ßßß ßßß ß ß ßßß ßßß ß ßßß ßßß +Ü Ü Ü Ü Ü ßßÜ Ü ÜßÜ Ü Ü ÜÜ Ü Ü Ü Ü Ü +ßßß ßßß ß ß ßßß ß ß ß ßßß ßßß ß ß ßßß ß ß + + + 1 Messages*90# User Menu + 3 Fido Networks*91# Messages + 5 Directory*95# Help + 8 Help*97# Directory + 9 About this System*99# Log Off \ No newline at end of file diff --git a/data/980a-login.ans b/data/980a-login.ans new file mode 100644 index 0000000..682b880 --- /dev/null +++ b/data/980a-login.ans @@ -0,0 +1,20 @@ + ÚÄÄ¿ ÚÄ¿ ÚÄÄÄÄÄÄÄ¿ ÚÄÄÄ¿ÚÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄ¿ +ͳù ³Íͳù³Í³ù Õ͸ù³Í³ù ³ÍÍÍÍͳù Õ͸ù³Í³ù Õ͸ù³Í³ù Õ¸Õ¸ú³Í³ù Õ͸ù³Í +°³ ³²Û³ ³ ³ À¿ÀÄÙ°³ ³Û²²² ³ ³²ÀÄÙ°³ ³Û³ ³ ³ ³Ô¾³ ³ ³ À¿ÀÄÙ° +±³ ³Õ¸³ ³°³ ÚÙÚÄ¿±³ ³²ÚÄ¿°³ ³°ÚÄ¿±³ ³²³ ³°³ ³²±³ ³°³ ÚÙÚÄ¿± +²³ ÀÙÀÙ ³±³ ³°³ ³²³ ÀÄÙ ³±³ ³°³ ³²³ ÀÄÙ ³±³ ³±°³ ³±³ ³°³ ³² +ͳùù³Í³ù Ô;ù³Í³ùù³Í³ù Ô;ù³Í³ùù³Í³ù ³Íͳù³Í³ù Ô;ù³Í + ÔÍÍÍÍÍÍ; ÔÍÍÍÍÍÍ; ÔÍÍÍÍÍÍ; ÔÍÍÍÍÍÍ; ÔÍÍÍÍÍÍ; ÔÍ; Ô; ÔÍÍÍÍÍÍ; + A vBBS by ....deon  + You have connected to VBBS. + + This is a public access ANSItex BBS. + + While this system is currently under development, you are welcome to use it + and make recommendations. + + If you would like to host your own pages on this system, please let deon + know. + + User: uuuuuuuuuuu [New Accounts use NEW#] + Pass: ppppppppppp diff --git a/data/981a-register.ans b/data/981a-register.ans new file mode 100644 index 0000000..2eda8ba --- /dev/null +++ b/data/981a-register.ans @@ -0,0 +1,18 @@ + ÚÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄ¿ ÚÄÄ¿ ÚÄ¿ ÚÄÄÄ¿ ÚÄ¿ ÚÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄ¿ +ͳù Õ͸ú³Í³ù Õ͸ù³Í³ù ³Íͳù³ÍÍͳù ³Í³ù³Í³ùÕÍÍÍÍ;ͳù Õ͸ù³Í³ù Õ͸ù³Í + ³ ³²³ ³ ³ À¿ÀÄÙ°³ ³²Û³ ³°°°³ ³Û³ ³²³ ÀÄÄÄÄÄ¿ ³ À¿ÀÄÙ ³ ÀÄÙÚÙ° +°³ ³±³ ³°³ ÚÙÚÄ¿±³ ³Õ¸³ ³±±±³ ³²³ ³ÛÔÍÍ͸ ù³°³ ÚÙÚÄ¿°³ Õ͸À¿± +±³ ³°³ ³±³ ³°³ ³²³ ÀÙÀÙ ³²²²³ ÀÄÙ ³²ÚÄÄÄÙ ³±³ ³°³ ³±³ ³°³ ³² +ͳù ³Í³ù³Í³ù Ô;ù³Í³ùù³ÍÍͳùù³Í³ùù³Í³ù Ô;ù³Í³ù ³Í³ù³Í + ÔÍÍ; Ô; ÔÍÍÍÍÍÍ; ÔÍÍÍÍÍÍ; ÔÍÍÍÍÍÍ; ÔÍÍÍÍÍÍ; ÔÍÍÍÍÍÍ; ÔÍÍ; Ô; + Thanks for your interest! To get started we just need a few details:  + + + Preferred User Name: ttttttttttt +Real Name: ttttttttttttttttttttttttttttt +Email Address: ttttttttttttttttttttttttttttt +Password: ppppppppppp +Repeat Password: ppppppppppp +Location: ttttttttttttttttttttttttttttt + +Token: ttttt diff --git a/data/under-construction.ans b/data/under-construction.ans new file mode 100644 index 0000000..a85178b --- /dev/null +++ b/data/under-construction.ans @@ -0,0 +1,10 @@ + + ß ß ßßß ßßÜ ßßß ßßß + Ü Ü Ü Ü Ü Ü Üß  ÜßÜ + ßßß ß ß ßß ßßß ß ß +ßßß ßßß ßßß ßßß ßßß ßßß ß ß ßßß ßßß ß ßßß ßßß +Ü Ü Ü Ü Ü ßßÜ Ü ÜßÜ Ü Ü ÜÜ Ü Ü Ü Ü Ü +ßßß ßßß ß ß ßßß ß ß ß ßßß ßßß ß ß ßßß ß ß + Lets hope that this frame is installed soon! Please check back another time... + +*# to go back... \ No newline at end of file diff --git a/database/migrations/2018_12_03_104758_init_database.php b/database/migrations/2018_12_03_104758_init_database.php index a1b68cb..c8d4f4d 100644 --- a/database/migrations/2018_12_03_104758_init_database.php +++ b/database/migrations/2018_12_03_104758_init_database.php @@ -17,7 +17,7 @@ class InitDatabase extends Migration Schema::create('modes', function (Blueprint $table) { $table->timestamps(); - $table->integer('id')->autoIncrement(); + $table->integer('id')->autoIncrement()->index(); $table->string('name',16); $table->string('note',255); @@ -25,43 +25,38 @@ class InitDatabase extends Migration }); Schema::create('cugs', function (Blueprint $table) { - $table->timestamps(); - $table->integer('id'); - $table->string('name',16); - $table->string('note',255); - $table->integer('parent_id')->nullable(); + $table->timestamps(); + $table->integer('id')->index()->unique(); + $table->string('name', 16); + $table->string('note', 255); + $table->integer('parent_id')->nullable()->index(); + }); - $table->primary('id'); + Schema::table('cugs', function (Blueprint $table) { $table->foreign('parent_id')->references('id')->on('cugs'); $table->unique(['name']); }); Schema::create('frames', function (Blueprint $table) { - $table->timestamps(); - $table->integer('id')->autoIncrement(); - $table->integer('frame'); - $table->char('index',1); - $table->integer('mode_id'); - $table->char('type',2); - $table->smallInteger('cost')->default(0); - $table->integer('cug_id')->default(0); - $table->boolean('public')->default(FALSE); - $table->binary('content'); - $table->string('note',255)->nullable(); + $table->timestamps(); + $table->integer('id')->autoIncrement()->index(); + $table->integer('frame'); + $table->char('index', 1); + $table->integer('mode_id')->index(); + $table->char('type', 2); + $table->smallInteger('cost')->default(0); + $table->integer('cug_id')->default(0)->index(); + $table->boolean('public')->default(FALSE); + $table->binary('content'); + $table->string('note', 255)->nullable(); - //$table->unique(['frame','index','mode_id']); // Not needed since we have timewarp + //$table->unique(['frame','index','mode_id']); // Not needed since we have timewarp + }); + Schema::table('frames', function (Blueprint $table) { $table->foreign('mode_id')->references('id')->on('modes'); $table->foreign('cug_id')->references('id')->on('cugs'); }); - - Schema::create('routes', function (Blueprint $table) { - $table->integer('id')->autoIncrement(); - $table->char('key',1); - $table->integer('route'); - - $table->foreign('route')->references('id')->on('frames'); - }); } /** diff --git a/database/migrations/2018_12_09_103357_create_framemeta.php b/database/migrations/2018_12_09_103357_create_framemeta.php index c90496b..d430b31 100644 --- a/database/migrations/2018_12_09_103357_create_framemeta.php +++ b/database/migrations/2018_12_09_103357_create_framemeta.php @@ -16,18 +16,21 @@ class CreateFramemeta extends Migration $this->down(); Schema::create('framemeta', function (Blueprint $table) { - $table->integer('frame_id')->primary(); - $table->string('r0')->default('*'); - $table->string('r1')->default('*'); - $table->string('r2')->default('*'); - $table->string('r3')->default('*'); - $table->string('r4')->default('*'); - $table->string('r5')->default('*'); - $table->string('r6')->default('*'); - $table->string('r7')->default('*'); - $table->string('r8')->default('*'); - $table->string('r9')->default('*'); + $table->integer('frame_id')->index(); + $table->string('r0')->default('*'); + $table->string('r1')->default('*'); + $table->string('r2')->default('*'); + $table->string('r3')->default('*'); + $table->string('r4')->default('*'); + $table->string('r5')->default('*'); + $table->string('r6')->default('*'); + $table->string('r7')->default('*'); + $table->string('r8')->default('*'); + $table->string('r9')->default('*'); + }); + + Schema::table('framemeta', function (Blueprint $table) { $table->foreign('frame_id')->references('id')->on('frames'); }); } diff --git a/database/migrations/2018_12_10_211904_create_user_cug.php b/database/migrations/2018_12_10_211904_create_user_cug.php index bf5e125..df8a2f7 100644 --- a/database/migrations/2018_12_10_211904_create_user_cug.php +++ b/database/migrations/2018_12_10_211904_create_user_cug.php @@ -16,9 +16,11 @@ class CreateUserCug extends Migration $this->down(); Schema::create('cug_users', function (Blueprint $table) { - $table->integer('cug_id'); - $table->integer('user_id')->unsigned(); + $table->integer('cug_id')->index(); + $table->integer('user_id')->unsigned()->index(); + }); + Schema::table('cug_users', function (Blueprint $table) { $table->foreign('user_id')->references('id')->on('users'); $table->foreign('cug_id')->references('id')->on('cugs'); $table->unique(['user_id','cug_id']); diff --git a/database/migrations/2018_12_10_211936_create_cug_owners.php b/database/migrations/2018_12_10_211936_create_cug_owners.php index a854694..cf225a2 100644 --- a/database/migrations/2018_12_10_211936_create_cug_owners.php +++ b/database/migrations/2018_12_10_211936_create_cug_owners.php @@ -16,9 +16,11 @@ class CreateCugOwners extends Migration $this->down(); Schema::create('cug_owners', function (Blueprint $table) { - $table->integer('cug_id'); - $table->integer('user_id')->unsigned(); + $table->integer('cug_id')->index(); + $table->integer('user_id')->unsigned()->index(); + }); + Schema::table('cug_owners', function (Blueprint $table) { $table->foreign('user_id')->references('id')->on('users'); $table->foreign('cug_id')->references('id')->on('cugs'); $table->unique(['user_id','cug_id']); diff --git a/database/migrations/2018_12_10_214547_frame_add_access.php b/database/migrations/2018_12_10_214547_frame_add_access.php index 049b9af..f333c96 100644 --- a/database/migrations/2018_12_10_214547_frame_add_access.php +++ b/database/migrations/2018_12_10_214547_frame_add_access.php @@ -14,7 +14,6 @@ class FrameAddAccess extends Migration public function up() { Schema::table('frames', function (Blueprint $table) { - $table->renameColumn('public','closed'); $table->boolean('access')->default(FALSE); $table->dropForeign(['cug_id']); $table->dropColumn('cug_id'); @@ -30,9 +29,8 @@ class FrameAddAccess extends Migration { Schema::table('frames', function (Blueprint $table) { $table->dropColumn('access'); - $table->renameColumn('closed','public'); $table->integer('cug_id')->default(0); $table->foreign('cug_id')->references('id')->on('cugs'); }); } -} +} \ No newline at end of file diff --git a/database/seeds/SeedCUG.php b/database/seeds/SeedCUG.php index bd82ef6..b615c59 100644 --- a/database/seeds/SeedCUG.php +++ b/database/seeds/SeedCUG.php @@ -18,4 +18,4 @@ class SeedCUG extends Seeder 'note' => 'All frames belong to this CUG if not any other.', ]); } -} +} \ No newline at end of file diff --git a/database/seeds/SeedMode.php b/database/seeds/SeedMode.php index 0515f14..01530a4 100644 --- a/database/seeds/SeedMode.php +++ b/database/seeds/SeedMode.php @@ -11,10 +11,15 @@ class SeedMode extends Seeder */ public function run() { - DB::table('modes')->insert([ - 'created_at' => now(), - 'name' => 'VideoTex', - 'note' => 'Original ViewData/VideoTex 40x25 char mode.', - ]); + DB::table('modes')->insert([ + 'created_at' => now(), + 'name' => 'videotex', + 'note' => 'Original ViewData/VideoTex 40x25 char mode.', + ]); + DB::table('modes')->insert([ + 'created_at' => now(), + 'name' => 'ansi', + 'note' => 'ANSItex 80x25 char mode.', + ]); } } diff --git a/resources/views/email/sendtoken.blade.php b/resources/views/email/sendtoken.blade.php new file mode 100644 index 0000000..63c767a --- /dev/null +++ b/resources/views/email/sendtoken.blade.php @@ -0,0 +1,14 @@ +@component('mail::message') +# New User Token + +Use this token to sign into ANSItex + +If this email is a surprise to you, you can ignore it. + +@component('mail::panel') + TOKEN: {{ $token }} +@endcomponent + +Thanks, +{{ config('app.name') }} +@endcomponent