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