show banner and ask for calling again later
This commit is contained in:
parent
cdaa7ecbaf
commit
17e8e006fd
11
.gitignore
vendored
11
.gitignore
vendored
|
@ -1,16 +1,7 @@
|
|||
# ---> Rust
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
/target
|
||||
|
|
396
Cargo.lock
generated
Normal file
396
Cargo.lock
generated
Normal file
|
@ -0,0 +1,396 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
|
||||
dependencies = [
|
||||
"humantime",
|
||||
"is-terminal",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno-dragonfly"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-streaming-iterator"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"allocator-api2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "koko-bbs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"mio",
|
||||
"rusqlite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.147"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326"
|
||||
dependencies = [
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"fallible-iterator",
|
||||
"fallible-streaming-iterator",
|
||||
"hashlink",
|
||||
"libsqlite3-sys",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
11
Cargo.toml
Normal file
11
Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "koko-bbs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
env_logger = "0.10.0"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4.19"
|
||||
mio = { version = "0.8.8", features = ["net", "os-poll"] }
|
||||
rusqlite = "0.29.0"
|
304
src/ansithings.org
Normal file
304
src/ansithings.org
Normal file
|
@ -0,0 +1,304 @@
|
|||
#+title: Ansithings
|
||||
|
||||
The ANSI-BBS Specification
|
||||
This "specification" is designed to standardize the meaning of ANSI-BBS in the communications world. It is split into three parts. General definitions are contained in the core document, specifications for the handling of server to client communications and saved ANSI-BBS files are contained in this document, and client to server specicifications are contained in the Client Communications document.
|
||||
|
||||
This is not a comprehensive ANSI specification, but a compilation of current common practices.
|
||||
|
||||
ANSI-BBS terminal chariteristics:
|
||||
Output area
|
||||
Almost all ANSI-BBS terminals are 80 columns wide and usually default to 24 rows high. Is is widely considered acceptable to assume 80 columns, but not considered acceptable to assume 24 rows. Other common hights include 23, 25, 42, and 49 rows.
|
||||
|
||||
Font issues
|
||||
Most english BBS software assumes that IBM CodePage 437 is in use at all times, and extensive use is made of the extended ASCII characters in the 128-255 range. In many cases, if the user asserts that he supports ANSI, that user is assumed to be using this codepage. This is flawed but exceedingly common. In many countries not in North America, the most common codepage will not be IBM CodePage 437, which will cause visual glitches in a significant number of BBS programs.
|
||||
|
||||
Also common is the usage of some of the so-called "low ASCII" characters which IBM systems will use when control characters (those from zero to 31) are displayed. Only five are generally interpreted as control characters. Using terminals intended for non-BBS use will often cause glitches and outright malfunctions when used with BBS software. Many of these terminals will not display anything for these characters. Another group will display different glyphs for these. Most will cause exceedingly bad effects when certain control characters are sent to the terminal. The worst control sequences, their effects, and work-arounds are as follows:
|
||||
|
||||
SO (0x0e): Shift Out
|
||||
Effects: Terminals which properly support the Shift Out control character will cause the meanings of all following characters to be changed. This will result in "gibberish" being displayed after this character is sent to the terminal.
|
||||
Work-around: After sending an SO character, send the "Shift In" character (0x0f) followed by a backspace, a space, then another backspace. The SI character "undoes" what the SO character does. The backspace space backspace sequence erases the SI character on terminals which display it as the "white sun with rays" character.
|
||||
ENQ (0x05): Enquiry
|
||||
Effects: Terminals which properly support the Enquiry will send an arbitrary identification string back to the sender. This will be interpreted as characters which were entered by the user. It is quite common to encounter a loop by which one or more of the reply characters trigger another ENQ to be sent.
|
||||
Work-around: Send a CSI6n after the ENQ then discard all input until the response is recieved.
|
||||
End of line behaviour (wrapping)
|
||||
This behaviour is a major weakness in the current ANSI-BBS world. Because there is no officially correct behaviour at the end of a line, many different methods have been implemented by different terminal programs. The end result is that you cannot rely on what happens after a character is placed at the end of a line. In general, people simply avoid using the last column. This isn't necessary however since you can work-around this by using an absolute character positioning after writing to the last column. It is encouraged that programs which create ANSI art handle writes to the last column in this manner. The best method to use is as follows:
|
||||
|
||||
At the beginning of a line which contains a character in column 80, add a line feed (0x0a) followed by the ESC [ A sequence to move the cursor back to the previous line. This forces a scroll if the current line is the last one on the screen.
|
||||
Add an ESC [ s sequence to save the cursor position.
|
||||
After the character is written to column 80, add an ESC [ u sequence to restore the cursor position
|
||||
Add a carriage return (0x0d) then line feed (0x0a). The CR/LF combination is used to allow proper line ending detection on Microsoft operating systems.
|
||||
Attributes
|
||||
This document will often speak of attributes. An attribute is a complete combination of four different factors which effect visual presentation of a character. These factors are:
|
||||
|
||||
Blink Characters blink while displayed. On some terminals, this selects a Bold/Bright background colour. This most often happens with Windows programs that use the Win32 console for output and are running in windowed mode. With these terminals, the text will correctly blink while in full-screen mode.
|
||||
Bold/Bright Characters are displayed with a more intense foreground colour
|
||||
Foreground colour The colour the characters themselves are displayed in
|
||||
Background colour The colour of unset pixels behind a character
|
||||
Control characters:
|
||||
While control characters have well defined meanings intended for terminals, BBS tradition has used these to display IBMs "low ascii" glyphs. Because if this historical usage, we must therefore support this if we intend to be compatible with a larger number of door games. In the standards, these are referred to as the C0 control function set.
|
||||
|
||||
This is a list of control characters and either their effect or the glyph which they display
|
||||
|
||||
NUL (0x00):
|
||||
No action. The NULL character is silently discarded. Traditionally, the NUL character has been used as a pad character by UNIX systems. Because of this historical usage, this must be retained. DoorWay mode also makes extensive use of the NULL character.
|
||||
SOL (0x01):
|
||||
This will display the "while smiling face" (☺) character which is unicode 9786 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
STX (0x02):
|
||||
This will display the "black smiling face" (☻) character which is unicode 9787 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
ETX (0x03):
|
||||
This will display the "black heart suit" (♥) character which is unicode 9829 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
EOT (0x04):
|
||||
This will display the "black diamond suit" (♦) character which is unicode 9830 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
ENQ (0x05):
|
||||
This will display the "black club suit" (♣) character which is unicode 9827 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
ACK (0x06):
|
||||
This will display the "black spade suit" (♠) character which is unicode 9824 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
BEL (0x07):
|
||||
Beep - Causes the terminal to make a beep sound. The duration and frequency of the beep is up to the implementor and a system "beep" should be used when possible. However, as a rough guide, 440Hz, 100ms duration has been used by some terminals.
|
||||
BS (0x08):
|
||||
Backspace. Moves cursor position to the previous column unless the current column is the first, in which case no operation is performed. This may or may not be implemented as a "destructive" backspace which will remove the character from that position too and possibly change the current background colour to the current one. Because of this ambuguity, it is strongly suggested that you always use the backspace space backspace sequence. The specification calls for a NON destructive backspace.
|
||||
TAB (0x09): (Subject to discussion)
|
||||
Moves to the next tab stop. This may or may not erase characters from the current location to the new one. The specification indicates that the characters should NOT be erased.
|
||||
LF (0x0a):
|
||||
Move cursor position to same column of the next row. If current row is the last row, scrolls the screen up and fills the new row with the current attribute.
|
||||
VT (0x0b):
|
||||
This will display the "male sign" (♂) character which is unicode 9794 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
FF (0x0c):
|
||||
Clears screen and homes cursor. Same as CSI2J.
|
||||
CR (0x0d):
|
||||
Move cursor position to column 1 of the current line
|
||||
SO (0x0e):
|
||||
This will display the "beamed eighth notes" (♫) character which is unicode 9835 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character. Not supported by mTelnet
|
||||
SI (0x0f):
|
||||
This will display the "white sun with rays" (☼) character which is unicode 9788 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character. Not supported by mTelnet
|
||||
DLE (0x10):
|
||||
This will display the "black right-pointing pointer" (▶) character which is unicode 9654 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
DC1 (0x11):
|
||||
This will display the "black left-pointing pointer" (◀) character which is unicode 9664 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
DC2 (0x12):
|
||||
This will display the "up down arrow" (↕) character which is unicode 8597 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
DC3 (0x13):
|
||||
This will display the "double exclamation mark" (‼) character which is unicode 8252 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
DC4 (0x14):
|
||||
This will display the "pilcrow sign" (¶) character which is unicode 182 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
NAK (0x15):
|
||||
This will display the "section sign" (§) character which is unicode 167 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
SYN (0x16):
|
||||
This will display the "black rectangle" (▬) character which is unicode 9644 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
ETB (0x17):
|
||||
This will display the "up down arrow with base" (↨) character which is unicode 8616 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
CAN (0x18):
|
||||
This will display the "upwards arrow" (↑) character which is unicode 8593 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
EM (0x19):
|
||||
This will display the "downwards arrow" (↓) character which is unicode 8595 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
SUB (0x1a):
|
||||
This will display the "rightwards arrow" (→) character which is unicode 8594 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
ESC (0x1b): (Subject to discussion)
|
||||
This chacter is reserved for use to introduce control codes and control characters.
|
||||
FS (0x1c): (Subject to discussion)
|
||||
This will display the "turned not sign" (⌙) character which is unicode 8985 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
DISCUSSION:
|
||||
It has been suggested that the far more common "right angle" (∟) which is unicode 8735 be used here. Apparently, IBM now uses this character in their maps.
|
||||
GS (0x1d):
|
||||
This will display the "left right arrow" (↔) character which is unicode 8596 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
RS (0x1e):
|
||||
This will display the "black up-pointing triangle" (▲) character which is unicode 9650 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
US (0x1f):
|
||||
This will display the "black down-pointing triangle" (▼) character which is unicode 9660 and occupies this slot on IBMs running codepage 437. This will be treated as a normal character.
|
||||
Two-byte control functions
|
||||
Two-byte control function format
|
||||
Known as the "7-bit C1 control function set" for '@' to '_' and the "Independant control functions" for '`' to '~' in standards, these are two-byte combinations which do not take any parameters. The first byte is always an ESC (0x1b) character. The second byte is from the range '@' (0x40) to '~' (0x7e) inclusive.
|
||||
|
||||
Unhandled functions
|
||||
If a legal function which the terminal does not support is encountered, that function should be silently ignored.
|
||||
|
||||
Illegal functions
|
||||
An illegal function is defined as any ESC character followed by any character which is not in the range of '@' (0x40) to '~' (0x5f) inclusive.
|
||||
|
||||
When an illegal function is encountered, the terminal should attempt to make this known to the user in some manner. Commonly all or part of the sequence itself is displayed
|
||||
|
||||
Defined Control Functions (in alphabetical order by the second byte)
|
||||
ESC[
|
||||
Control sequence Introducer (CSI)
|
||||
This indicates the start of a control sequence. See the section on control sequences below for more details.
|
||||
Control Sequences:
|
||||
Control Sequence Format
|
||||
Control sequences make up the bulk of the ANSI-BBS spec. And contain four distinct parts which are, in order:
|
||||
|
||||
Control Sequence Introducer (CSI)
|
||||
The two-character CSI consists of the ESC character (0x1b) followed by the '[' character (0x5b)
|
||||
Parameter Bytes (PBs)
|
||||
Parameter bytes consist of zero of more of the characters from '0' (0x30) to '?' (0x3f)
|
||||
Intermediate Bytes (IBs)
|
||||
Intermediate bytes consist of zero or more of the charaters from ' ' (0x20) to '/' (0x2f)
|
||||
Final Byte (FB)
|
||||
The final byte consists of one of the characters from '@' (0x40) to '~' (0x7e)
|
||||
All control sequences begin with a CSI, are followed by a series of zero or more PBs which are followed by zero or more IBs and terminated with a FB. The IBs and the FB together make a string which defines the Control Function CF.
|
||||
|
||||
Parameter Byte Formats
|
||||
If a parameter byte string consists of only characters from '0' to ';' inclusive ("0123456789:;") then it is interpreted as a list of semi-colon (';') separated numeric values. The meaning of a colon in a value must be defined by the control function. If it is not so defined, anything between a colon and the end of that sub-parameter is ignored (ie: up to the next semi-colon, IB, or FB).
|
||||
|
||||
If a parameter byte string begins with one of the chrarcters from '<' to '?' inclusive ("<=>?") then it is part of an extension which will be defined in another document.
|
||||
|
||||
If a parameter byte string begins with one of the characters from '0' to ';' inclusive ("01234567890:;") but contains one of the characters from '<' to '?' ("<=>?"), it is deemed to be an illegal sequence and should be handled accordingly.
|
||||
|
||||
Unhandled sequences
|
||||
If a legal sequence which the terminal does not support is encountered, that sequence should be silently ignored. If one item in a parameter list is not handled, only that single unhandled parameter should be ignored. All other parameters from the list should be handled.
|
||||
|
||||
Illegal sequences
|
||||
An illegal sequence is defined as:
|
||||
|
||||
A sequence which begins with a CSI but has any IB character before any PB character.
|
||||
A sequence which begins with a CSI and contains a character which is not a legal PB, IB, or FB
|
||||
A sequence which begins with a CSI, is followed by a PB from the set of charaters from '0' to ';' inclusive, and contains a PB from the set of characters from '<' to '?' inclusive.
|
||||
When an illegal sequence is encountered, the terminal should attempt to make this known to the user in some manner. Commonly all or part of the sequence itself is displayed
|
||||
|
||||
Defined Control Sequences (in alphabetical order by FB)
|
||||
The following is a list of commonly defined control sequences for ANSI-BBS terminals. Parameters will be indicated by one of:
|
||||
|
||||
p# where # is replaced with a number. This is used to indicate an order-specific parameter. That is to say, p2 *must* be the second parameter. Defaults will be defined for each so-indicated parameter. If this parameter is omitted or empty, the default will be used.
|
||||
pX indicates that multiple numeric parameters are accepted in any order.
|
||||
? where '?' is one of the characters from '<' to '?' inclusive. This inducates that this charater MUST be used in this position.
|
||||
IBs and FBs will be shown Like this.
|
||||
CSIp1@
|
||||
Insert Character(s)
|
||||
Defaults: p1 = 1
|
||||
Moves text from the current position to the right edge p1 characters to the right, with rightmost charaters going off-screen and the resulting hole being filled with the current attribute.
|
||||
Source: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
|
||||
Not supported by mTelnet
|
||||
CSIp1A
|
||||
Cursor Up
|
||||
Defaults: p1 = 1
|
||||
Moves the cursor position up p1 lines from the current position. Attempting to move past the screen boundaries stops the cursor at the screen boundary.
|
||||
Source: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
|
||||
CSIp1B
|
||||
Cursor Down
|
||||
Defaults: p1 = 1
|
||||
Moves the cursor position down p1 lines from the current position. Attempting to move past the screen boundaries stops the cursor at the screen boundary.
|
||||
Source: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
|
||||
CSIp1C
|
||||
Cursor Right
|
||||
Defaults: p1 = 1
|
||||
Moves the cursor position right p1 columns from the current position. Attempting to move past the screen boundaries stops the cursor at the screen boundary.
|
||||
Source: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
|
||||
CSIp1D
|
||||
Cursor Left
|
||||
Defaults: p1 = 1
|
||||
Moves the cursor position left p1 columns from the current position. Attempting to move past the screen boundaries stops the cursor at the screen boundary.
|
||||
Source: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
|
||||
CSIp1;p2H
|
||||
CSIp1;p2f
|
||||
Cusror Position
|
||||
Defaults: p1 = 1 p2 = 1
|
||||
Moves the cursor to the p2th column of the p1th line.
|
||||
Source: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
|
||||
CSIp1J (Subject to discussion)
|
||||
Erase in Page
|
||||
Defaults: p1 = 0
|
||||
Erases from the current screen according to the value of p1
|
||||
Erase from the current position to the end of the screen.
|
||||
Erase from the current position to the start of the screen.
|
||||
Erase entire screen. As a violation of ECMA-048, also moves the cursor to position 1/1 as a number of BBS programs assume this behaviour.
|
||||
Erased characters are set to the current attribute.
|
||||
DISCUSSION:
|
||||
Should ESC 2 J violate the spec?
|
||||
Source: BANSI.TXT
|
||||
CSIp1K
|
||||
Erase in Line
|
||||
Defaults: p1 = 0
|
||||
Erases from the current line according to the value of p1
|
||||
Erase from the current position to the end of the line.
|
||||
Erase from the current position to the start of the line.
|
||||
Erase entire line.
|
||||
Erased characters are set to the current attribute.
|
||||
Source: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
|
||||
mTelnet interprets any sequence ending in 'K' as though p1 is zero
|
||||
CSIp1L
|
||||
Insert Line(s)
|
||||
Defaults: p1 = 1
|
||||
Inserts p1 lines at the current line position. The current line and those after it are scrolled down and the new empty lines are filled with the current attribute.
|
||||
Source: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
|
||||
CSIp1M
|
||||
Delete Line(s)
|
||||
Defaults: p1 = 1
|
||||
Deletes the current line and the p1 - 1 lines after it scrolling the first non-deleted line up to the current line and filling the newly empty lines at the end of the screen with the current attribute. Source: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
|
||||
CSIp1P
|
||||
Delete Character
|
||||
Defaults: p1 = 1
|
||||
Deletes the character at the current position by shifting all characters from the current column + p1 left to the current column. Opened blanks at the end of the line are filled with the current attribute.
|
||||
Source: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
|
||||
Not supported by mTelnet
|
||||
CSIp1S
|
||||
Scroll Up
|
||||
Defaults: p1 = 1
|
||||
Scrolls all text on the screen up p1 lines. New lines emptied at the bottom are filled with the current attribute.
|
||||
Source: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
|
||||
Not supported by mTelnet
|
||||
CSIp1T
|
||||
Scroll Down
|
||||
Defaults: p1 = 1
|
||||
Scrolls all text on the screen down p1 lines. New lines emptied at the top are filled with the current attribute.
|
||||
Source: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
|
||||
Not supported by mTelnet
|
||||
CSIp1X
|
||||
Erase Character
|
||||
Defaults: p1 = 1
|
||||
Erases p1 characters starting at the corrent position. Will not go past the end of the line.
|
||||
Source: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
|
||||
CSIp1Z
|
||||
Cursor Backward Tabulation
|
||||
Defaults: p1 = 1
|
||||
Move the cursor to the p1th preceeding tab stop. Will not go past the start of the line.
|
||||
Source: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
|
||||
Not supported by mTelnet
|
||||
CSIpXm
|
||||
Select Graphic Rendition
|
||||
Defaults: pX = 0
|
||||
Sets or clears one or more text attributes. Unlimited parameters are supported and are applied in received order. The following are supoprted:
|
||||
Modifies
|
||||
pX Description Blink Bold FG BG
|
||||
0 Default attribute, white on black X X X X
|
||||
1 Bright Intensity X
|
||||
2 Dim Intensity X
|
||||
5 Blink (slow) X
|
||||
6 Blink (fast) X
|
||||
7 Negative Image - Reverses FG and BG X X
|
||||
8 Concealed characters. Sets the FG to the BG X
|
||||
22 Normal intensity X
|
||||
25 Steady (not blinking) X
|
||||
30 Black foreground X
|
||||
31 Red foreground X
|
||||
32 Green foreground X
|
||||
33 Yellow foreground X
|
||||
34 Blue foreground X
|
||||
35 Magenta foreground X
|
||||
36 Cyan foreground X
|
||||
37 White foreground X
|
||||
40 Black background X
|
||||
41 Red background X
|
||||
42 Green background X
|
||||
43 Yellow background X
|
||||
44 Blue background X
|
||||
45 Magenta background X
|
||||
46 Cyan background X
|
||||
47 White background X
|
||||
Source: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
|
||||
CSIp1n
|
||||
Device Status Report
|
||||
Defaults: p1 = 0
|
||||
A request for a status report. ANSI-BBS terminals should handle at least the following values for p1
|
||||
Request active cursor position
|
||||
terminal must reply with CSIp1;p2R where p1 is the current line number counting from one, and p2 is the current column.
|
||||
NOTE: This sequences should not be present in saved ANSI-BBS files
|
||||
Source: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
|
||||
CSIs
|
||||
Save Current Cursor Position
|
||||
No parameters
|
||||
Saves the current cursor position for later restoring with CSIu.
|
||||
Source: DOS ANSI.SYS Documentation
|
||||
CSIu
|
||||
Restore Cursor Position
|
||||
No parameters
|
||||
Move the cursor to the last saved position. If no position has been saved, the cursor is not moved.
|
||||
Source: DOS ANSI.SYS Documentation
|
||||
Please feel free to Comment on this page.
|
137
src/connection.rs
Normal file
137
src/connection.rs
Normal file
|
@ -0,0 +1,137 @@
|
|||
use log::{debug, info};
|
||||
|
||||
use mio::net::TcpStream;
|
||||
use mio::event::Event;
|
||||
use mio::{Interest, Registry};
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use crate::state::State;
|
||||
use crate::system::System;
|
||||
use crate::user::User;
|
||||
use crate::utils::{would_block, interrupted};
|
||||
|
||||
pub struct Connection<'a> {
|
||||
pub system: &'a System,
|
||||
pub ts: TcpStream,
|
||||
pub user: Option<User>,
|
||||
pub state: State,
|
||||
}
|
||||
|
||||
impl Connection<'_> {
|
||||
pub fn handle_event(&mut self, reg: &Registry, event: &Event) -> io::Result<bool> {
|
||||
if event.is_writable() {
|
||||
self.handle_write_event(reg, event)?;
|
||||
}
|
||||
|
||||
if event.is_readable() {
|
||||
self.handle_read_event(reg, event)?;
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn set_state(&mut self, s: State) {
|
||||
debug!("state change {:?} -> {:?}", self.state, s);
|
||||
self.state = s;
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<bool> {
|
||||
loop {
|
||||
match self.ts.write(&buf) {
|
||||
// This shouldn't happen since the TCP send buffer is big enough
|
||||
Ok(n) if n < buf.len() => return Err(io::ErrorKind::WriteZero.into()),
|
||||
Ok(_) => return Ok(true),
|
||||
Err(ref err) if would_block(err) => return Ok(false),
|
||||
Err(ref err) if interrupted(err) => continue,
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_write_event(&mut self, reg: &Registry, event: &Event) -> io::Result<()> {
|
||||
// TODO just write everything that changed in the framebuffer
|
||||
// we update the fb after user input, there is no change until the user
|
||||
// did something (for now), so we don't need to match on the current state here
|
||||
// a special case might be after the user logged out, but this could also be
|
||||
// represented as a new state
|
||||
// ... actually... this works only after the user has seen the banner since
|
||||
// it scrolls the screen, so we still need to match the state but only check
|
||||
// for the Connected and maybe the Logout state, everything else just prints
|
||||
// updates of the fb
|
||||
match self.state {
|
||||
// query term info (size etc.)
|
||||
State::Connected => {
|
||||
if self.write(&self.system.theme.banner)? {
|
||||
self.set_state(State::Banner);
|
||||
reg.reregister(&mut self.ts, event.token(), Interest::READABLE)?;
|
||||
}
|
||||
},
|
||||
// send banner and wait for keypress
|
||||
_ => todo!("handle write for all the other states")
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&self, buf: &mut [u8]) -> io::Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_read_event(&mut self, reg: &Registry, event: &Event) -> io::Result<bool> {
|
||||
let mut received_data = vec![0; 4096];
|
||||
let mut connection_closed = false;
|
||||
let mut bytes_read = 0;
|
||||
|
||||
|
||||
loop {
|
||||
match self.ts.read(&mut received_data[bytes_read..]) {
|
||||
Ok(0) => {
|
||||
connection_closed = true;
|
||||
break;
|
||||
}
|
||||
Ok(n) => {
|
||||
bytes_read += n;
|
||||
// TODO: we should probably fail when our recv buffer is too
|
||||
// small and not just increase it indefinitely
|
||||
if bytes_read == received_data.len() {
|
||||
received_data.resize(received_data.len() + 1024, 0);
|
||||
}
|
||||
}
|
||||
Err(ref err) if would_block(err) => break,
|
||||
Err(ref err) if interrupted(err) => continue,
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if bytes_read != 0 {
|
||||
let received_data = &received_data[..bytes_read];
|
||||
debug!("data: {:?}", received_data);
|
||||
|
||||
match self.state {
|
||||
// parse term info
|
||||
State::Connected => {
|
||||
|
||||
},
|
||||
// one keypress to show the login screen, ignore the rest
|
||||
State::Banner => {},
|
||||
_ => {},
|
||||
};
|
||||
|
||||
// if let Ok(str_buf) = std::str::from_utf8(received_data) {
|
||||
// debug!("Received data: {}", str_buf.trim_end());
|
||||
// } else {
|
||||
// debug!("Received (none UTF-8) data: {:?}", received_data);
|
||||
// }
|
||||
}
|
||||
|
||||
if connection_closed {
|
||||
info!("Connection closed");
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
}
|
47
src/framebuffer.rs
Normal file
47
src/framebuffer.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
enum Colour {
|
||||
None,
|
||||
Black,
|
||||
Red,
|
||||
Green,
|
||||
Yellow,
|
||||
Blue,
|
||||
Magenta,
|
||||
Cyan,
|
||||
White,
|
||||
}
|
||||
|
||||
enum Modifier {
|
||||
None,
|
||||
Bright
|
||||
}
|
||||
|
||||
struct Cell {
|
||||
char: u8,
|
||||
colour: Option<Colour>,
|
||||
modifier: Modifier,
|
||||
changed: bool,
|
||||
}
|
||||
|
||||
impl Default for Cell {
|
||||
fn default() -> Cell {
|
||||
Cell {
|
||||
char: b' ',
|
||||
colour: None,
|
||||
modifier: Modifier::None,
|
||||
changed: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cell {
|
||||
pub fn new(char: u8) -> Self {
|
||||
Self {
|
||||
char,
|
||||
.. Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct framebuffer {
|
||||
|
||||
}
|
101
src/main.rs
Normal file
101
src/main.rs
Normal file
|
@ -0,0 +1,101 @@
|
|||
use log::info;
|
||||
use mio::net::TcpListener;
|
||||
use mio::{Events, Interest, Poll, Token};
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
|
||||
mod connection;
|
||||
mod system;
|
||||
mod state;
|
||||
mod theme;
|
||||
mod user;
|
||||
mod utils;
|
||||
|
||||
use self::connection::Connection;
|
||||
use self::system::System;
|
||||
use self::state::State;
|
||||
use self::utils::interrupted;
|
||||
|
||||
const SERVER: Token = Token(0);
|
||||
const LISTEN: &str = "127.0.0.1:9000";
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let system = System::new("dachgeschoss")?;
|
||||
let mut poll = Poll::new()?;
|
||||
let mut events = Events::with_capacity(128);
|
||||
|
||||
let addr = LISTEN.parse().unwrap();
|
||||
let mut server = TcpListener::bind(addr)?;
|
||||
|
||||
poll.registry()
|
||||
.register(&mut server, SERVER, Interest::READABLE)?;
|
||||
|
||||
let mut connections = HashMap::new();
|
||||
let mut unique_token = Token(SERVER.0 + 1);
|
||||
|
||||
loop {
|
||||
if let Err(err) = poll.poll(&mut events, None) {
|
||||
if interrupted(&err) {
|
||||
continue;
|
||||
}
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
for event in events.iter() {
|
||||
match event.token() {
|
||||
SERVER => loop {
|
||||
let (mut connection, address) = match server.accept() {
|
||||
Ok((connection, address)) => (connection, address),
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
info!("Connection from: {}", address);
|
||||
|
||||
let token = next(&mut unique_token);
|
||||
poll.registry().register(
|
||||
&mut connection,
|
||||
token,
|
||||
Interest::READABLE.add(Interest::WRITABLE),
|
||||
)?;
|
||||
|
||||
connections.insert(
|
||||
token,
|
||||
Connection {
|
||||
system: &system,
|
||||
ts: connection,
|
||||
state: State::Connected,
|
||||
user: None,
|
||||
},
|
||||
);
|
||||
},
|
||||
token => {
|
||||
let done = if let Some(connection) = connections.get_mut(&token) {
|
||||
connection.handle_event(poll.registry(), event)?
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if done {
|
||||
if let Some(mut connection) = connections.remove(&token) {
|
||||
poll.registry().deregister(&mut connection.ts)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn next(current: &mut Token) -> Token {
|
||||
let next = current.0;
|
||||
current.0 += 1;
|
||||
Token(next)
|
||||
}
|
||||
|
24
src/state.rs
Normal file
24
src/state.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
#[derive(Debug)]
|
||||
pub struct LoginState {
|
||||
name: Option<String>,
|
||||
password: Option<String>,
|
||||
}
|
||||
|
||||
impl LoginState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
name: None,
|
||||
password: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum State {
|
||||
Connected,
|
||||
Banner,
|
||||
Login(LoginState),
|
||||
Landing,
|
||||
Messages,
|
||||
OneLiners,
|
||||
}
|
15
src/system.rs
Normal file
15
src/system.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
use std::io;
|
||||
|
||||
use crate::theme::Theme;
|
||||
|
||||
pub struct System {
|
||||
pub theme: Theme,
|
||||
}
|
||||
|
||||
impl System {
|
||||
pub fn new(t: &str) -> io::Result<Self> {
|
||||
Ok(Self {
|
||||
theme: Theme::new(t)?
|
||||
})
|
||||
}
|
||||
}
|
31
src/theme.rs
Normal file
31
src/theme.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use std::fs;
|
||||
use std::io;
|
||||
|
||||
const GREETINGS: &[u8] = b"Greetings!\r\n\r\nUse a BBS terminal like syncterm \
|
||||
to connect by\r\ntelnet port 23\r\ndialin number TBD\r\nssh with user bbs and \
|
||||
password doesntmatter\r\n\r\n";
|
||||
//const PROMPT_ANY_KEY: &[u8] = b"\r\n<Apply adequate pressure to your favorite key...>";
|
||||
const PROMPT_ANY_KEY: &[u8] = b"\r\nStill hacking on this thing, come back later...";
|
||||
|
||||
pub struct Theme {
|
||||
pub banner: Vec<u8>,
|
||||
pub header: Vec<u8>,
|
||||
pub bye: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Theme {
|
||||
pub fn new(name: &str) -> io::Result<Self> {
|
||||
let mut banner = Vec::new();
|
||||
banner.extend_from_slice(&GREETINGS);
|
||||
banner.extend(fs::read(format!("themes/{}/camp-logo.ans", name))?);
|
||||
banner.extend_from_slice(&PROMPT_ANY_KEY);
|
||||
let bye = fs::read(format!("themes/{}/htp.ans", name))?;
|
||||
let header = fs::read(format!("themes/{}/header.ans", name))?;
|
||||
|
||||
Ok(Self {
|
||||
banner,
|
||||
header,
|
||||
bye,
|
||||
})
|
||||
}
|
||||
}
|
3
src/user.rs
Normal file
3
src/user.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub struct User {
|
||||
name: String,
|
||||
}
|
14
src/utils.rs
Normal file
14
src/utils.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use std::io;
|
||||
|
||||
pub fn would_block(err: &io::Error) -> bool {
|
||||
err.kind() == io::ErrorKind::WouldBlock
|
||||
}
|
||||
|
||||
pub fn interrupted(err: &io::Error) -> bool {
|
||||
err.kind() == io::ErrorKind::Interrupted
|
||||
}
|
||||
|
||||
pub fn trim_crlf(s: &mut String) {
|
||||
let len = s.trim_end_matches(&['\r', '\n'][..]).len();
|
||||
s.truncate(len);
|
||||
}
|
44
themes/dachgeschoss/camp-logo.ans
Normal file
44
themes/dachgeschoss/camp-logo.ans
Normal file
|
@ -0,0 +1,44 @@
|
|||
[0m[1;30moriginal by Veith Y<>er CC-BY-SA 4.0[35m ワワロロワ[0m
|
||||
[35m [1mワワワ ワワロロロワ ゙ロロロロロロワ[0m
|
||||
[35m [1mワロロロロロワ[0;35m [1mロロロロロロロロン ゙ロロロロロロロ ワロロロロワ[0m
|
||||
[35m [1mロロロロロロロロ[0;35mワ[1mロロロロロロロロン ゙ロロロロロロロン ロロロロロロロワ[0m
|
||||
[35mーワワ ワワワーロロローロロンワワワー[1mロロロロロロロロ[0;35mロ[1mロロロロロロロロ[0;35mロロ[1mロロロロロロロロロ[0;35mロ[1mロロロロロロロロ[0;35mロロロロン[37m
|
||||
[35mーロロン アロロ゚ロロロ ロロンロロロン[1mロロロロロロロロ[0;35mロ[1mロロロロロロロロ[0;35mロロ[1mロロロロロロロロロ[0;35mロ[1mロロロロロロロロロ[0;35m゚゚゚゚[37m
|
||||
[35m [1m゚゚゚゚゚゚゚[0;35m [1m゚゚゚゚゚゚゚[0;35m [1mロロロロロロロロン[0;35m [1m゙ロロロロロロロン[0m
|
||||
[35m [1mワワワワワワワワワワワワワワワ[0;35m [1mロロロロロロロロロ[0;35m [1mロロロロロロロン[0;35mワワワ ワー ワワ ワワ ワワー[37m
|
||||
[1;30m [0;35mワワ ワワ アワワ ワワ ロロン ロロ[1mワロロロロロロロロロロロロロロロロロン[0;35m [1m゙ロロロロロロロイ[0;35mー[1m ロロロロロロロ[0;35mロロロロン アロロ ロロ イロ゚ ロロイ[37m
|
||||
[1;30m [0;35mー ー ーー ア ゚゚ ゚[1mロロロロロロロロロロロロロロロロロロロ ゙ロロロロロロロン[0;35m [1mロロロロロロロ[0;35mワーワワー ワワ ワア ワロロ ロロー[37m
|
||||
[1;30m [0;35mロロロー ロロ アロー ロロ ロロ[1mワロロロロロロロロロロロロロロロロロ゚ [0;35mワ[1m ゚ロロロロロ゚ ゚ロロロロ゚ [0;35m゚ロロロー ロロ イイーアロ゚ ロロロ[37m
|
||||
[1;30m [0;35mワ ー[1mロロロロロロロロロロロロロ゚[0;35m ー゚゚゚ [1m゚゚゚ [0;35mワワーワワアイロロ ロロロ[37m
|
||||
[35m ロロロローーロロ ロロ ーロア [1mロロロロロロロロロロロロロン [0;35mロ_ _ワ_[37m [35mー _ー___ __ーロロロロロロア ゚イ ゚゚ ゚゚ ゚ロア[37m
|
||||
[35m ワワワワワワ ワワ ワ_ ー[1mロロロロロロロロロロロロロン [0;35mー ロ ア ー ゚゚゚゚ー アー ロロアロロアアロロ ロロイ[37m
|
||||
[35m ロロロロロローアロロ ロロ_ [1mロロロロロロロロロロロロロロ [0;35mア_ イ_ ワ_ _アワワワ__ _アロロロロロ゚ ーア ロロロ[37m
|
||||
[35m ワワワー ーワワワー ー[1mロロロロロロロロロロロロロ゚ [0;35m゚゚゚ー アー ロ ロロ ロロロロロアーロロ ロロロ[37m
|
||||
[35m ゚゚゚ ゚゚ロア ア[1mロロロロロロロロロロロロ゚ ワワワワワワワロワワワワロロロロワワワワロロロワワワロ[0m
|
||||
[35m [1mロロロロロロロロロ゚ ワロロロロロロロロロロロロロロロロロロロロロロロロロロロ゚[0m
|
||||
[35m [1mロロロロロロロ゚ ワロロロロロロロロロロロロロロロロロロロロロロ゚゚゚゚゚゚[0m
|
||||
[35m [1mロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロ゚[0m
|
||||
[35m [1mロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロ[0m
|
||||
[35m [1mロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロ[0m
|
||||
[35m [1mワロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロワワワワワワワワワワワワ[0m
|
||||
[35m [1mロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロ゚[0m
|
||||
[35m [1mワロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロ゚ロ[0m
|
||||
[35m [1mロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロ゚[0m
|
||||
[35m [1m゚゚゚゚゚゚゚゚゚゚゚゚゚ロロロロロロロロロロロロロロロロロロロロロロロロ゚[0m
|
||||
[35m [1mロロロロロロロロロロロロロロロロロロロロロロロ゚[0m
|
||||
[35m [1mロロロロロロロロロロロロロロロロロロロロロ゚[0m
|
||||
[35m [1mロロロロロロロロロロロロロロロロロロロロ゚[0m
|
||||
[35m [1mロロロロロロロロロロロロロロロロロロ゚[0m
|
||||
[35m [1mロロロロロロロロロロロロロロロロロ [0;35mワワワ[1m [0;35mワワワワワワ[37m
|
||||
[35m [1mワロロロロロロロロロロロロロロロ゚ [0;35mワ゚ ゚ワ[1m [0;35mワ゚[37m
|
||||
[35m [1mロロロロロロロロロロロロロロ゚ [0;35m゚ワ ワ゚[1m [0;35m゚ワ[37m
|
||||
[35m [1mロロロロロロロロロロロロ゚ [0;35mワ゚[1m [0;35mワワワワロワ[37m
|
||||
[35m [1mワロロロロロロロロロロ゚ [0;35mワ゚ワ[1m [0;35mロ[1m [0;35m゚ワ[37m
|
||||
[35m [1mロロロロロロロロロ゚ [0;35mワロワワワロワワワ[1m [0;35mロ゚゚[37m
|
||||
[35m [1mロロロロロロロロ゚[0m
|
||||
[1;35mロロロロロロ゚[0m
|
||||
[1;35mロロロロ゚[0m
|
||||
[1;35mワロロロ゚[0m
|
||||
[1;35mロロ゚[0m
|
||||
[1;35mロ゚[0m
|
||||
[1;35mン[0m
|
6
themes/dachgeschoss/header.ans
Normal file
6
themes/dachgeschoss/header.ans
Normal file
|
@ -0,0 +1,6 @@
|
|||
[0m[1m<31>[30m \ [37m<37>[0;35m [37m/[1m`<60><><EFBFBD><EFBFBD>[0m \[1;30m [35m/[37m<37>[0m [1m'[0m [35m\[37m [35m/ [37m\ / [1m<31>[0m [1;30m\[0;35m [1;30m________[0m
|
||||
[1;30m \___[37m__[30m_/[0;35m [1;37m<37>'[0m \[1;35m_[37m___[35m_[0;35m_/ [1;37m<37>[0m [1;35m\[37m<37>[35m_[0;35m____/ [37m\______/ [1m<31>[0m [1;30m\/[0;35m __ ___[1;30m\[0m
|
||||
[1m<31>[30m / [37m<37><6D>[0m [1;30m\[0m [1m<31>[0m [35m/[37m [1m<31>[0m [35m\[37m [1;35m/[37m<37>[0;35m \ / [37m\[35m [1;30m/[0;35m ( ) /[37m [1;30m\[0m
|
||||
[1m<31><6D>[30m/ [0;36m<36><6D><EFBFBD>[37m [36m<36><6D>[1m<31>[0m\[36m<36><6D>[37m_[36m<36>[37m_[1;36m<36>[0;35m/<2F><><EFBFBD>[1;37m<37>[0;35m<35><6D><EFBFBD>[1;37m<37>[0;35m<35><6D><EFBFBD>_<EFBFBD><5F>_<EFBFBD>[37m [1;35m<35>[0m [35m<35><6D><EFBFBD>[37m [35m<35><6D><EFBFBD>_<EFBFBD><5F><EFBFBD>_/ [37m\[1m_<6D><5F><EFBFBD>_[30m_/ [0;35m/ __\[37m [1;30m\[37m<37><6D>[30m\[0;35m [36m<36>[37m [1;36m<36>[0m [36m<36>[1m_<6D>[0m/[36m<36> <20><><EFBFBD>[35m\<5C><><EFBFBD>[37m [35m<35><6D>[37m [1m.[0;35m<35><6D><EFBFBD>[37m [35m<35>[37m [35m<35><6D><EFBFBD>[37m [35m<35> [1m<31>[0m [35m<35><6D><EFBFBD>[37m [35m<35><6D><EFBFBD> \ [37m/ [1;30m\[0m [35m/\ <20>)[37m [1;30m/[37m<37><6D>[30m \[0;36m<36><6D><EFBFBD>[1;30m_[0;36m<36><6D><EFBFBD>[37m [36m<36><6D>[37m [36m<36> <20>[37m [35m<35><6D>[1m<31>[37m_[0;35m<35><6D><EFBFBD>/<2F><>[1m<31>[37m<37>[0;35m<35><6D>[1;30m [0;35m<35>\<5C>[1;37m_[0;35m<35><6D>[1m<31>_[0;35m<35><6D>[1m<31>[0m [35m<35><6D>[1m<31>[0m [35m\__[37m____/[35m [1;30m\[0;35m /__\_ <20> [1;30m/[0m
|
||||
[1m<31><6D>[0;35m [1;30m\ [0;35m/[37m [35m\ [1;37m<37>[0m [35m/[37m [1m<31>[0m [35m\ [1;37m<37>[0m [35m/[37m [1;30m\________/[0m
|
||||
[1m<31><6D>[0m [1;30m\_[0m____[35m_/ \_[1m_[37m_[35m_[0;35m__/[37m [35m\__[1m_[37m_<6D>_[35m/[0m
|
43
themes/dachgeschoss/htp.ans
Normal file
43
themes/dachgeschoss/htp.ans
Normal file
|
@ -0,0 +1,43 @@
|
|||
[0m
|
||||
[1;33mワワワワワワロロロロロロロワワワワ[0m
|
||||
[1;33mワロロロロロロロロロロロロロロロロロロロロロロロワワ[0m
|
||||
[1;33m゚゚ロロロロロロロロロロロロロロロロロロロロロロロロロロロロワ[0m
|
||||
[1;33mロロワワワ[0m [1;33m゚゚ロロロロロロロロロロロロロロロロロロロロロロロロワ[0m
|
||||
[1;33mロロロロロロロロロワワワ[0m [1;33m゚゚ロロロロロロロロロロロロロロロロロロロロロワ[0m
|
||||
[1;33mロロロロロロロロロロロロロロロロロワワ[0m [1;33m゚゚゚゚゚ロロロロロロロロロロワ[0m
|
||||
[1;33mロロロロロロロロロロ゚[0m [1;33m゚ロロロロロロロ[0m [1;33m゚゚ロロロロロロロ[0m
|
||||
[1;33mロロロロロロロロロロ[0m [1;33mロロロロロロ[0m [1;33m゚ロロロロロ[0m
|
||||
[1;33mロロロロロロロロロロ[0m [1;33mロロロロ[0m [1;33m゚ロロロロ[0m
|
||||
[1;33mロロロロロロロロロロロ[0m [1;33mロロロロロ[0m [1;33m゚゚゚[0m
|
||||
[1;33mロロロロロロロロロロロロ[0m [1;33mロロロロロ[0m [1;33mワロワワ[0m
|
||||
[1;33mロロロロロロロロロロロロ[0m [1;33mロロロロロロ[0m [1;33mワロロロロロロワ[0m
|
||||
[1;33mロロロロロロロロロロロロロロ[0m [1;33mロロロロロロロロワ[0m [1;33mワロロロロロロロロロ[0m
|
||||
[1;33mロロロロロロロロロロロロロロロワ[0m [1;33mワロロロロロロロロロロロロワワ[0m [1;33mワワワワロロロロロロロロロロロ[0m
|
||||
[1;33mロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロ[0m
|
||||
[1;33mロロロロロロロロ゚[0m [1;33m゚ロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロ[0m [1;33m゚ロロロロロロロロ[0m
|
||||
[1;33mロロロロロロロロ[0m [1;33mロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロ[0m [1;33mロロロロロロロロ[0m
|
||||
[1;33m゚ロロロロロロロワ[0m [1;33m゚ロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロ゚[0m [1;33mワロロロロロロロ゚[0m
|
||||
[1;33mロロロロロロロロ[0m [1;33mロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロ[0m [1;33mロロロロロロロロ[0m
|
||||
[1;33m゚ロロロロロロロワ[0m [1;33mロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロ[0m [1;33mロロロロロロロロ゚[0m
|
||||
[1;33mロロロロロロロロワ[0m [1;33m゚ロロロロロロロロロロロロロロロロロロロロロロロロロロロロ゚[0m [1;33mロロロロロロロロロ[0m
|
||||
[1;33mロロロロロロロロロワ[0m [1;33m゚ロロロロロロロロロロロロロロロロロロロロロロロロ゚[0m [1;33mワロロロロロロロロロ[0m
|
||||
[1;33m゚ロロロロロロロロワ ゚ロロロロロロロロロロロロロロロロロロロ゚゚[0m [1;33mワロロロロロロロロロロ[0m
|
||||
[1;33m゚ロロロロロロロロロロワ[0m [1;33m゚゚゚ロロロロロロロロ゚゚゚[0m [1;33mワロロロロロロロロロロ゚[0m
|
||||
[1;33m゚ロロロロロロロロロロロワワ[0m [1;33mワワロロロロロロロロロロロ゚[0m
|
||||
[1;33m゚ロロロロロロロロロロロロロロワワワワワワワワワロロロロロロロロロロロロロロロ゚[0m
|
||||
[1;33m゚ロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロロ゚゚[0m
|
||||
[1;33m゚゚ロロロロロロロロロロロロロロロロロロロロロロロロロロ゚゚[0m
|
||||
[1;33m゚゚゚ロロロロロロロロロロロロロロロ゚゚゚[0m
|
||||
[1;33m゚゚゚゚゚゚゚゚゚[0m
|
||||
|
||||
|
||||
[1;33mロロ[0m [1;33mロロ[0m [1;33mロロロロロロロロロ[0m [1;33mロロロロロロロロ ロロ[0m [1;33mワロロ゚[0m [1;33mロロロロロロロロ[0m [1;33mロロ[0m [1;33mロロ ロロロロロロロロ[0m
|
||||
[1;33mワワ[0m [1;33mワワ[0m [1;33mワワ[0m [1;33mワワ[0m [1;33mワワ[0m [1;33mワワ[0m [1;33m゚゚゚[0m [1;33mワワ[0m [1;33mワワ[0m [1;33mワワ[0m [1;33mワワ[0m
|
||||
[1;33mロロ[0m [1;33mロロロロロロ[0m [1;33mロロ[0m [1;33mロロロロロロ[0m [1;33mロロ[0m [1;33mロロ[0m [1;33mロロロロワ[0m [1;33mロロ[0m [1;33mロロ[0m [1;33mロロロロロロ[0m [1;33mロロロロロロロロ[0m
|
||||
[1;33mロロ[0m [1;33mロロ[0m [1;33mロロ[0m [1;33mロロ[0m [1;33mロロワワワワワワ ロロ[0m [1;33m゚ロロワ[0m [1;33mロロ[0m [1;33mロロ[0m [1;33mロロ[0m [1;33mロロワワワワワワ[0m
|
||||
[1;33m゚゚[0m [1;33m゚゚[0m [1;33m゚゚[0m [1;33m゚゚[0m [1;33m゚゚゚゚゚゚゚゚ ゚゚[0m [1;33m゚゚[0m [1;33m゚゚[0m [1;33m゚゚[0m [1;33m゚゚[0m [1;33m゚゚゚゚゚゚゚゚[0m
|
||||
[1;33mロロロロロロロロロ[0m [1;33mロロ[0m [1;33mロロロロロロロロロ[0m [1;33mロロロロロロロロ ロロロロロロロロ[0m [1;33mロロロロロロロロ[0m
|
||||
[1;33mワワ[0m [1;33mワワ[0m [1;33mワワ[0m [1;33mワワ[0m [1;33mワワ[0m [1;33mワワ[0m [1;33mワワ[0m [1;33mワワ[0m [1;33mワワ[0m
|
||||
[1;33mロロ[0m [1;33mロロロロロロ[0m [1;33mロロ[0m [1;33mロロ[0m [1;33mロロロロロロ[0m [1;33mロロ[0m [1;33mロロ[0m [1;33mロロロロロロロロ[0m [1;33mロロ[0m
|
||||
[1;33mロロ[0m [1;33mロロワワワワワワ[0m [1;33mロロ[0m [1;33mロロ[0m [1;33mロロ[0m [1;33mロロ[0m [1;33mロロワワワワワワ[0m [1;33mロロ[0m
|
||||
[1;33m゚゚[0m [1;33m゚゚゚゚゚゚゚゚[0m [1;33m゚゚[0m [1;33m゚゚[0m [1;33m゚゚[0m [1;33m゚゚[0m [1;33m゚゚゚゚゚゚゚゚[0m [1;33m゚゚[0m
|
Loading…
Reference in a new issue