Implement NEW-ENVIRON / MNES
This commit is contained in:
parent
de00a4de66
commit
ad3fd0207e
@ -318,6 +318,7 @@ pub fn install_lua_globals(
|
||||
register_class_function!(mud_class_table, mudoutput_dont);
|
||||
register_stateless_class_function!(mud_class_table, mudoutput_subnegotiation);
|
||||
register_class_function!(mud_class_table, mudoutput_subnegotiation_termtype);
|
||||
register_class_function!(mud_class_table, mudoutput_subnegotiation_environ);
|
||||
register_class_function!(mud_class_table, mudinput_line);
|
||||
register_class_function!(mud_class_table, "closed", mudclass_closed);
|
||||
register_stateless_class_function!(mud_class_table, "new", new_mud);
|
||||
@ -328,6 +329,11 @@ pub fn install_lua_globals(
|
||||
"local_termtype_enabled",
|
||||
opt_enabled_noop
|
||||
);
|
||||
register_stateless_class_function!(
|
||||
mud_class_table,
|
||||
"local_environ_enabled",
|
||||
opt_enabled_noop
|
||||
);
|
||||
register_stateless_class_function!(
|
||||
mud_class_table,
|
||||
"remote_eor_enabled",
|
||||
|
@ -12,8 +12,6 @@ use piccolo::{
|
||||
SequenceReturn, StashedTable, StashedUserData, StashedValue, Table, UserData, Value, Variadic,
|
||||
};
|
||||
use std::{rc::Rc, str};
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::console;
|
||||
use yew::UseStateSetter;
|
||||
|
||||
use super::call_checking_metatable;
|
||||
|
@ -1,10 +1,13 @@
|
||||
use anyhow::Error;
|
||||
use core::str;
|
||||
use std::mem::swap;
|
||||
|
||||
use anyhow::{bail, Error};
|
||||
use gc_arena::{Gc, Rootable};
|
||||
use piccolo::{
|
||||
self, async_sequence, Callback, CallbackReturn, Context, FromValue, Function, IntoValue,
|
||||
SequenceReturn, StashedTable, StashedUserData, StashedValue, Table, UserData, Value,
|
||||
};
|
||||
use telopt::{EOR_TELOPT, TERMTYPE_TELOPT};
|
||||
use telopt::{ENVIRON_TELOPT, EOR_TELOPT, TERMTYPE_TELOPT};
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::{console, window};
|
||||
use yew::UseStateSetter;
|
||||
@ -242,6 +245,7 @@ pub(super) fn connect_mud<'gc>(
|
||||
}
|
||||
set_option_supported(ctx, &conntab, &TERMTYPE_TELOPT, &Side::Us);
|
||||
set_option_supported(ctx, &conntab, &EOR_TELOPT, &Side::Him);
|
||||
set_option_supported(ctx, &conntab, &ENVIRON_TELOPT, &Side::Us);
|
||||
|
||||
// Call conntab:new...
|
||||
let seq = async_sequence(&ctx, |locals, mut seq| {
|
||||
@ -698,11 +702,14 @@ pub(super) fn mudoutput_subnegotiation_termtype<'gc>(
|
||||
};
|
||||
let supported_termtypes: Table = mud.get(ctx, "supported_termtypes")?;
|
||||
let optlen = supported_termtypes.length() as u64;
|
||||
if negidx > optlen {
|
||||
if negidx > optlen + 1 {
|
||||
negidx = 1;
|
||||
}
|
||||
|
||||
let termtype: String = supported_termtypes.get(ctx, negidx as i64)?;
|
||||
// We repeat the last one before cycling.
|
||||
let effective_negidx = if negidx > optlen { optlen } else { negidx };
|
||||
|
||||
let termtype: String = supported_termtypes.get(ctx, effective_negidx as i64)?;
|
||||
|
||||
send_subnegotiation_if_allowed(
|
||||
ctx,
|
||||
@ -725,6 +732,154 @@ pub(super) fn mudoutput_subnegotiation_termtype<'gc>(
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_environ_sendcmd(mut input: &[u8]) -> anyhow::Result<Vec<Vec<u8>>> {
|
||||
let mut args: Vec<Vec<u8>> = vec![];
|
||||
let mut curstr: Vec<u8> = vec![];
|
||||
|
||||
const VAR: u8 = 0;
|
||||
const ESC: u8 = 2;
|
||||
const USERVAR: u8 = 3;
|
||||
loop {
|
||||
match input.first() {
|
||||
None => {
|
||||
if !curstr.is_empty() {
|
||||
args.push(curstr);
|
||||
}
|
||||
return Ok(args);
|
||||
}
|
||||
Some(c) => {
|
||||
input = &input[1..];
|
||||
match *c {
|
||||
ESC => match input.first() {
|
||||
None => {
|
||||
bail!("new-environ SEND command ended with escape, which is invalid");
|
||||
}
|
||||
Some(c) => {
|
||||
input = &input[1..];
|
||||
curstr.push(*c);
|
||||
}
|
||||
},
|
||||
VAR | USERVAR => {
|
||||
if !curstr.is_empty() {
|
||||
let mut laststr: Vec<u8> = vec![];
|
||||
swap(&mut curstr, &mut laststr);
|
||||
args.push(laststr);
|
||||
}
|
||||
}
|
||||
_ => curstr.push(*c),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn escape_environ(input: &[u8]) -> Vec<u8> {
|
||||
let mut buf: Vec<u8> = vec![];
|
||||
const VAR: u8 = 0;
|
||||
const VALUE: u8 = 1;
|
||||
const ESC: u8 = 2;
|
||||
const USERVAR: u8 = 3;
|
||||
for c in input {
|
||||
match *c {
|
||||
VAR => {
|
||||
buf.push(ESC);
|
||||
buf.push(VAR);
|
||||
}
|
||||
VALUE => {
|
||||
buf.push(ESC);
|
||||
buf.push(VALUE);
|
||||
}
|
||||
ESC => {
|
||||
buf.push(ESC);
|
||||
buf.push(ESC);
|
||||
}
|
||||
USERVAR => {
|
||||
buf.push(ESC);
|
||||
buf.push(USERVAR);
|
||||
}
|
||||
c => buf.push(c),
|
||||
}
|
||||
}
|
||||
buf
|
||||
}
|
||||
|
||||
pub(super) fn mudoutput_subnegotiation_environ<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
) -> Callback<'gc> {
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let (mud, mut msg): (Table, Vec<u8>) = stack.consume(ctx)?;
|
||||
if msg.is_empty() {
|
||||
return Ok(CallbackReturn::Return);
|
||||
}
|
||||
let cmd = msg.remove(0);
|
||||
|
||||
let socket: Value = mud.get(ctx, "socket")?;
|
||||
let socket: &WebSocketId =
|
||||
UserData::from_value(ctx, socket)?.downcast::<Rootable![Gc<'_, WebSocketId>]>()?;
|
||||
|
||||
let environ: Table = mud.get(ctx, "environ_for_remote")?;
|
||||
|
||||
const IS_CMD: u8 = 0;
|
||||
const SEND_CMD: u8 = 1;
|
||||
|
||||
const VAR: u8 = 0;
|
||||
const VALUE: u8 = 1;
|
||||
match cmd {
|
||||
SEND_CMD => {
|
||||
let mut requested_env = parse_environ_sendcmd(&msg)?;
|
||||
console::log_1(&JsValue::from_str(&format!(
|
||||
"Environment request of length: {}",
|
||||
requested_env.len()
|
||||
)));
|
||||
if requested_env.is_empty() {
|
||||
requested_env = environ
|
||||
.iter()
|
||||
.map(|(k, _v)| {
|
||||
Ok::<_, anyhow::Error>(
|
||||
piccolo::String::from_value(ctx, k)?.as_bytes().to_owned(),
|
||||
)
|
||||
})
|
||||
.collect::<anyhow::Result<Vec<_>>>()?;
|
||||
}
|
||||
console::log_1(&JsValue::from_str(&format!(
|
||||
"After expansion, environment request of length: {}",
|
||||
requested_env.len()
|
||||
)));
|
||||
|
||||
let mut buf: Vec<u8> = vec![];
|
||||
buf.push(IS_CMD);
|
||||
for env in &requested_env {
|
||||
if let Ok(envstr) = environ.get::<_, piccolo::String>(
|
||||
ctx,
|
||||
piccolo::String::from_slice(&ctx, env.as_slice()),
|
||||
) {
|
||||
buf.push(VAR);
|
||||
buf.extend_from_slice(escape_environ(env).as_slice());
|
||||
buf.push(VALUE);
|
||||
buf.extend_from_slice(escape_environ(envstr.as_bytes()).as_slice());
|
||||
}
|
||||
}
|
||||
send_subnegotiation_if_allowed(
|
||||
ctx,
|
||||
&mud,
|
||||
&ENVIRON_TELOPT,
|
||||
&Side::Us,
|
||||
&mut MudWithMemo {
|
||||
memo: global_memo.clone(),
|
||||
mud: socket.clone(),
|
||||
},
|
||||
buf.as_slice(),
|
||||
);
|
||||
|
||||
Ok(CallbackReturn::Return)
|
||||
}
|
||||
_ => Ok(CallbackReturn::Return),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn mudinput_line<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
_global_memo: &GlobalMemoCell,
|
||||
@ -796,6 +951,14 @@ pub(super) fn new_mud<'gc>(ctx: Context<'gc>) -> Callback<'gc> {
|
||||
termtypes.set(ctx, 3_i64, "MTTS 815")?;
|
||||
mud.set(ctx, ctx.intern_static(b"supported_termtypes"), termtypes)?;
|
||||
|
||||
let environ: Table = Table::new(&ctx);
|
||||
environ.set(ctx, "CLIENT_NAME", "worldwideportal")?;
|
||||
environ.set(ctx, "CLIENT_VERSION", "0.0.1")?;
|
||||
environ.set(ctx, "CHARSET", "UTF-8")?;
|
||||
environ.set(ctx, "MTTS", "815")?;
|
||||
environ.set(ctx, "TERMINAL_TYPE", "XTERM")?;
|
||||
mud.set(ctx, ctx.intern_static(b"environ_for_remote"), environ)?;
|
||||
|
||||
let curr_frame: Value = ctx
|
||||
.get_global::<Table>("info")?
|
||||
.get(ctx, ctx.intern_static(b"current_frame"))?;
|
||||
|
@ -430,6 +430,7 @@ pub fn handle_incoming_dont<'gc, T: SendOptNeg>(
|
||||
pub const TERMTYPE_TELOPT: Telopt = Telopt(24);
|
||||
pub const EOR_TELOPT: Telopt = Telopt(25);
|
||||
pub const NAWS_TELOPT: Telopt = Telopt(31);
|
||||
pub const ENVIRON_TELOPT: Telopt = Telopt(39);
|
||||
pub const GMCP_TELOPT: Telopt = Telopt(201);
|
||||
|
||||
fn negotiate_option_on<'gc, T: SendOptNeg>(
|
||||
@ -533,6 +534,9 @@ pub fn configure_telopt_table<'gc>(ctx: Context<'gc>, table: &Table<'gc>) {
|
||||
table
|
||||
.set(ctx, "termtype", TERMTYPE_TELOPT.0)
|
||||
.expect("Can't set TERMTYPE in telopt table");
|
||||
table
|
||||
.set(ctx, "environ", ENVIRON_TELOPT.0)
|
||||
.expect("Can't set ENVIRON in telopt table");
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
Loading…
Reference in New Issue
Block a user