using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; namespace TELTest { public abstract class ConnectionHandler { public static List AllHandlers { get; } = new List(); public ConnectionHandler() { ID = AllHandlers.Count; AllHandlers.Add(this); } public int ID { get; protected set; } public abstract Task HandleMe(NetworkStream stream); public async Task Handle(NetworkStream stream) { await Task.Delay(500); // PBX flashbacks await WriteASCII(stream, "Processing.\r\n"); await Task.Delay(100); Flush(stream); //await ReadTimeout(stream, 100); // Catch loose buffered chars await TryClear(stream); await WriteASCII(stream, "Hello!\r\n"); await WriteASCII(stream, (ID + 1) + " concurrent connections right now.\r\n"); await WriteASCII(stream, $"Server time is {DateTime.Now}\r\n"); await HandleMe(stream); await WriteASCII(stream, "\r\n\r\nBye!\r\n"); AllHandlers.Remove(this); } public void Flush(NetworkStream stream) { while (stream.DataAvailable) stream.ReadByte(); } public int ReadOneIfAvailable(NetworkStream stream) { if (!stream.DataAvailable) return -1; return stream.ReadByte(); } public async Task TryClear(NetworkStream stream) { await WriteASCII(stream, "\x1b[2J\x1b[H"); } public async Task Choice(NetworkStream stream, char[] choice, string caption = null, string prompt = null, char? def = null, bool echo = true, bool noNewLine = false, bool caseSensitive = false, CancellationToken? token = null) { if (choice.Length <= 0) throw new ArgumentOutOfRangeException(nameof(choice)); if (prompt == null) { StringBuilder sb = new StringBuilder(); sb.Append('['); for (int i = 0; i < choice.Length; i++) { var item = choice[i]; string cs = (item + (i == choice.Length - 1 ? "" : "/")); cs = caseSensitive ? cs : cs.ToUpper(); sb.Append(cs); } sb.Append(']'); if (def != null) { string cs = " (" + def.Value + ")"; cs = caseSensitive ? cs : cs.ToUpper(); sb.Append(cs); } sb.Append(" > "); prompt = sb.ToString(); } char selection = '\0'; while (true) { if (caption != null) await WriteASCII(stream, "\r\n" + caption + "\r\n"); await WriteASCII(stream, prompt); char c = await ReadChar(stream,echo); if (!noNewLine) await WriteASCII(stream, "\r\n"); for (int i = 0; i < choice.Length; i++) { if (caseSensitive) { if (choice[i] == c) { selection = c; goto ChoiceDone; } } else { if (Char.ToUpper(choice[i]) == Char.ToUpper(c)) { selection = choice[i]; goto ChoiceDone; } } } if (def != null) { if (c == '\r' || c == '\n') { selection = def.Value; //while (stream.DataAvailable) stream.ReadByte(); goto ChoiceDone; } } Flush(stream); } ChoiceDone:; Flush(stream); return selection; } public async Task Prompt(NetworkStream stream, string prompt, ReadConfiguration? config = null, Nullable token = null) { await WriteASCII(stream, prompt); if (config == null) config = ReadConfiguration.Default; string ret = await Read(stream, config.Value, token); await WriteASCII(stream, "\r\n"); return ret; } public async Task Read(NetworkStream stream, char terminator = '\r', bool echo = true, Nullable token = null) { ReadConfiguration rc = ReadConfiguration.Default; rc.Terminator = terminator; rc.Echo = echo; return await Read(stream, rc, token); } public async Task Read(NetworkStream stream, ReadConfiguration config, Nullable token = null) { byte[] buf = new byte[512]; int i = 0; int con = 0; while (true) { if (token == null) await stream.ReadAsync(buf, i, 1); else await stream.ReadAsync(buf, i, 1, token.Value); if (buf[i] == 0x7F) { buf[i] = (byte)'\b'; await WriteASCII(stream, "\b"); } if (buf[i] == (byte)config.Terminator) { i++; break; } if (buf[i] == (byte)'\b') { i--; con--; i = i < 0 ? 0 : i; if (con >= 0) { await WriteASCII(stream, " \b"); } else if (con < 0) { await WriteASCII(stream, " "); con++; } continue; } else { i++; if (!config.Echo) { await WriteASCII(stream, "\b \b"); } else { con++; } } char cb = (char)buf[i - 1]; if (config.Whitelist != null && config.Whitelist.IndexOf(cb) == -1 && (cb != '\b' && cb != '\n' && cb != '\r')) { //Console.WriteLine("Forbidden char - \"" + (char)buf[i] + "\""); await WriteASCII(stream, "\b \b"); i--; con--; continue; } if (config.Echo && config.EchoPasswordChars) { await WriteASCII(stream, "\b" + config.PasswordChar); } if (config.MaxLength + 1 == i) { i--; con--; await WriteASCII(stream, "\b \b"); } //Console.WriteLine(buf[i > 0 ? i - 1 : 0].ToString("X") + ", " + i + " curs " + con); } string ret = System.Text.Encoding.ASCII.GetString(buf, 0, i - 1); if (config.FlushAfterReading) Flush(stream); return ret.Trim(); } protected int Columns { get; set; } = 120; public String PlayerName { get; set; } public bool PlayerReady { get; set; } public object SharedTempUserObject { get; set; } public async Task ReadTimeout(NetworkStream stream, int timeout, string def = null, char terminator = '\n') { CancellationTokenSource cts = new CancellationTokenSource(); Task ts = Read(stream, terminator, true, cts.Token); await Task.WhenAny(ts, Task.Delay(timeout)); cts.Cancel(); cts.Dispose(); return ts.IsCompleted ? ts.Result : def; } public async Task ReadChar(NetworkStream stream, bool echo = true) { byte[] buf = new byte[512]; await stream.ReadAsync(buf, 0, 1); if (!echo && buf[0] != 8) await WriteASCII(stream, "\b \b"); //string ret = System.Text.Encoding.ASCII.GetString(buf, 0, i - 1); return (char)buf[0]; } public async Task WriteBar(NetworkStream stream, char chr = '=') { await WriteASCII(stream, new string(chr, Columns) + "\r\n"); } public async Task WriteASCII(NetworkStream stream, string text) { var bytes = Encoding.ASCII.GetBytes(text); await stream.WriteAsync(bytes, 0, bytes.Length); } } }