diff --git a/Gemfile.lock b/Gemfile.lock index 267bd723462d258f015e5be041b0b15637b5ef49..eb65157c8cf59d13b93e5ad75a84c6b46cc95e0d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -19,6 +19,7 @@ GEM ffi (~> 1.9) rspec-expectations (>= 2.99) thor (>= 0.19, < 2.0) + ast (2.4.0) backports (3.17.1) builder (3.2.4) childprocess (3.0.0) @@ -44,20 +45,36 @@ GEM eventmachine (1.2.7) ffi (1.12.2) gherkin (5.1.0) + jaro_winkler (1.5.4) json (1.8.6) methadone (1.9.5) bundler multi_json (1.14.1) multi_test (0.1.2) ncursesw (1.4.10) + parallel (1.19.1) + parser (2.7.1.1) + ast (~> 2.4.0) + rainbow (3.0.0) rake (11.3.0) rdoc (4.3.0) require_all (2.0.0) + rexml (3.2.4) rspec-expectations (3.9.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) rspec-support (3.9.2) + rubocop (0.82.0) + jaro_winkler (~> 1.5.1) + parallel (~> 1.10) + parser (>= 2.7.0.1) + rainbow (>= 2.2.2, < 4.0) + rexml + ruby-progressbar (~> 1.7) + unicode-display_width (>= 1.4.0, < 2.0) + ruby-progressbar (1.10.1) thor (1.0.1) + unicode-display_width (1.7.0) wisper (2.0.1) PLATFORMS @@ -70,6 +87,7 @@ DEPENDENCIES json (~> 1.8) rake (~> 11.3) rdoc (~> 4.2) + rubocop (~> 0.82) BUNDLED WITH 2.1.4 diff --git a/aethyr.gemspec b/aethyr.gemspec index 2deda025e326729156d3284e99e02960a641f60e..7dcbf938e94edb111acc90199bfe47a21aa509b6 100644 --- a/aethyr.gemspec +++ b/aethyr.gemspec @@ -40,4 +40,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'rake', '~> 11.3' spec.add_development_dependency 'rdoc', '~> 4.2' spec.add_development_dependency 'aruba', '~> 0.14' + spec.add_development_dependency 'rubocop', '~> 0.82' end diff --git a/lib/aethyr/core/connection/telnet.rb b/lib/aethyr/core/connection/telnet.rb index 3000919204a2191abc06ca39b8760b5c2c665eaa..71a8af2ae800f08da943787a234a0d126abeffe6 100644 --- a/lib/aethyr/core/connection/telnet.rb +++ b/lib/aethyr/core/connection/telnet.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'socket' require 'aethyr/core/connection/telnet_codes' @@ -7,7 +9,7 @@ class TelnetScanner IAC + WILL + OPT_ECHO, IAC + WILL + OPT_MSSP, IAC + WONT + OPT_COMPRESS2, - IAC + DO + OPT_NAWS] + IAC + DO + OPT_NAWS].freeze def initialize(socket, display) @socket = socket @@ -26,196 +28,223 @@ class TelnetScanner def supports_naws(does_it) if does_it @supports_naws = true - log "Client supports NAWS" + log 'Client supports NAWS' else @supports_naws = false - log "Client does NOT support NAWS" + log 'Client does NOT support NAWS' end end def send_mssp - log "sending mssp" + log 'sending mssp' mssp_options = nil options = IAC + SB + OPT_MSSP - if File.exist? "conf/mssp.yaml" - File.open "conf/mssp.yaml" do |f| - mssp_options = YAML.load(f) + if File.exist? 'conf/mssp.yaml' + File.open 'conf/mssp.yaml' do |f| + mssp_options = YAML.safe_load(f) end - mssp_options.each do |k,v| + mssp_options.each do |k, v| options << (MSSP_VAR + k + MSSP_VAL + v.to_s) end end - options << (MSSP_VAR + "PLAYERS" + MSSP_VAL + $manager.find_all("class", Player).length.to_s) - options << (MSSP_VAR + "UPTIME" + MSSP_VAL + $manager.uptime.to_s) - options << (MSSP_VAR + "ROOMS" + MSSP_VAL + $manager.find_all("class", Room).length.to_s) - options << (MSSP_VAR + "AREAS" + MSSP_VAL + $manager.find_all("class", Area).length.to_s) - options << (MSSP_VAR + "ANSI" + MSSP_VAL + "1") - options << (MSSP_VAR + "FAMILY" + MSSP_VAL + "CUSTOM") - options << (MSSP_VAR + "CODEBASE" + MSSP_VAL + "Aethyr " + $AETHYR_VERSION) - options << (MSSP_VAR + "PORT" + MSSP_VAL + ServerConfig.port.to_s) - options << (MSSP_VAR + "MCCP" + MSSP_VAL + (ServerConfig[:mccp] ? "1" : "0")) + options << (MSSP_VAR + 'PLAYERS' + MSSP_VAL + $manager.find_all('class', Player).length.to_s) + options << (MSSP_VAR + 'UPTIME' + MSSP_VAL + $manager.uptime.to_s) + options << (MSSP_VAR + 'ROOMS' + MSSP_VAL + $manager.find_all('class', Room).length.to_s) + options << (MSSP_VAR + 'AREAS' + MSSP_VAL + $manager.find_all('class', Area).length.to_s) + options << (MSSP_VAR + 'ANSI' + MSSP_VAL + '1') + options << (MSSP_VAR + 'FAMILY' + MSSP_VAL + 'CUSTOM') + options << (MSSP_VAR + 'CODEBASE' + MSSP_VAL + 'Aethyr ' + $AETHYR_VERSION) + options << (MSSP_VAR + 'PORT' + MSSP_VAL + ServerConfig.port.to_s) + options << (MSSP_VAR + 'MCCP' + MSSP_VAL + (ServerConfig[:mccp] ? '1' : '0')) options << (IAC + SE) @display.send_raw options end def process_iac - log "doing process_iac" - - iac_state = :none if iac_state.nil? - while ch = @socket.recv(1, Socket::MSG_PEEK) do - ch = ch.chr - log "processing #{ch.ord}" - if iac_state == :none && ch == IAC - @socket.recv(1) - iac_state = :IAC - next - elsif iac_state == :none + log 'doing process_iac' + + @iac_state = :none if @iac_state.nil? + puts 'start' + ch = nil? + begin + ch = @socket.recv_nonblock(1, Socket::MSG_PEEK) + rescue Errno::EWOULDBLOCK + return false + end + return false if ch.nil? + + puts 'stop' + ch = ch.chr + log "processing #{ch.ord}" + if @iac_state == :none && ch == IAC + @socket.recv(1) + @iac_state = :IAC + return false + elsif @iac_state == :none + return true + else + @socket.recv(1) if @iac_state != IAC || ch != IAC + + case @iac_state + when :IAC + if ch == WILL + @iac_state = :IAC_WILL + elsif ch == SB + @iac_state = :IAC_SB + elsif ch == WONT + @iac_state = :IAC_WONT + elsif ch == DONT + @iac_state = :IAC_DONT + elsif ch == DO + @iac_state = :IAC_DO + elsif ch == IAC + @iac_state = :none return true - elsif iac_state != :none - @socket.recv(1) if iac_state != IAC || ch != IAC - case iac_state - - when :IAC - if ch == WILL - iac_state = :IAC_WILL - elsif ch == SB - iac_state = :IAC_SB - elsif ch == WONT - iac_state = :IAC_WONT - elsif ch == DONT - iac_state = :IAC_DONT - elsif ch == DO - iac_state = :IAC_DO - elsif ch == IAC - iac_state = :none - return true - else - iac_state = :none - end - - when :IAC_WILL - if OPT_BINARY == ch - @socket.puts(IAC + DO + OPT_BINARY) - elsif ch == OPT_NAWS - supports_naws(true) - elsif ch == OPT_LINEMODE - @linemode_supported = true - elsif OPT_ECHO == ch - @socket.puts(IAC + DONT + OPT_ECHO) - elsif OPT_SGA == ch - @socket.puts(IAC + DO + OPT_SGA) - else - @socket.puts(IAC + DONT + ch) - end - iac_state = :none - - when :IAC_WONT - if ch == OPT_LINEMODE - @linemode_supported = false - elsif ch == OPT_NAWS - supports_naws(false) - else - @socket.puts(IAC + DONT + ch) - end - iac_state = :none - - when :IAC_DO - if ch == OPT_BINARY - @socket.puts(IAC + WILL + OPT_BINARY) - elsif ch == OPT_ECHO - @echo_supported = true - elsif ch == OPT_MSSP - send_mssp - @mssp_supported = true - else - @socket.puts(IAC + WONT + ch) - end - iac_state = :none - - when :IAC_DONT - if ch == OPT_ECHO - @echo_supported = false - elsif ch == OPT_COMPRESS2 - #do nothing - elsif ch == OPT_MSSP - @mssp_supported = false - else - @socket.puts(IAC + WONT + ch) - end - iac_state = :none - - when :IAC_SB - if ch == OPT_NAWS - iac_state = :IAC_SB_NAWS - else - iac_state = :IAC_SB_SOMETHING - end - - when :IAC_SB_NAWS - lwidth = ch.ord - iac_state = :IAC_SB_NAWS_LWIDTH - if ch == IAC && @socket.getch != IAC - log "escaped IAC expected but not found" - break - end - - when :IAC_SB_NAWS_LWIDTH - hwidth = ch.ord - iac_state = :IAC_SB_NAWS_LWIDTH_HWIDTH - if ch == IAC && @socket.getch != IAC - log "escaped IAC expected but not found" - break - end - - when :IAC_SB_NAWS_LWIDTH_HWIDTH - lheight = ch.ord - iac_state = :IAC_SB_NAWS_LWIDTH_HWIDTH_LHEIGHT - if ch == IAC && @socket.getch != IAC - log "escaped IAC expected but not found" - break - end - - when :IAC_SB_NAWS_LWIDTH_HWIDTH_LHEIGHT - hheight = ch.ord - iac_state = :IAC_SB_NAWS_LWIDTH_HWIDTH_LHEIGHT_HHEIGHT - if ch == IAC && @socket.getch != IAC - log "escaped IAC expected but not found" - break - end - - when :IAC_SB_NAWS_LWIDTH_HWIDTH_LHEIGHT_HHEIGHT - if ch == IAC - iac_state = :IAC_SB_NAWS_LWIDTH_HWIDTH_LHEIGHT_HHEIGHT_IAC - else - log "invalid IAC" - iac_state = :none - end - - when :IAC_SB_NAWS_LWIDTH_HWIDTH_LHEIGHT_HHEIGHT_IAC - if ch == SE - new_width = lwidth*256 + hwidth - new_height = lheight*256 + hheight - log "setting resolution #{new_width} #{new_height}" - @display.resolution = [new_width, new_height] - else - log "invalid IAC" - end - iac_state = :none - when :IAC_SB_SOMETHING - iac_state = :IAC_SB_SOMETHING_IAC if ch == IAC - - when :IAC_SB_SOMETHING_IAC - iac_state = :IAC_SB_SOMETHING if ch == IAC - iac_state = :none if ch == SE else - iac_state = :none + @iac_state = :none + end + + when :IAC_WILL + if OPT_BINARY == ch + @socket.puts(IAC + DO + OPT_BINARY) + elsif ch == OPT_NAWS + supports_naws(true) + elsif ch == OPT_LINEMODE + @linemode_supported = true + elsif OPT_ECHO == ch + @socket.puts(IAC + DONT + OPT_ECHO) + elsif OPT_SGA == ch + @socket.puts(IAC + DO + OPT_SGA) + else + @socket.puts(IAC + DONT + ch) + end + @iac_state = :none + + when :IAC_WONT + if ch == OPT_LINEMODE + @linemode_supported = false + elsif ch == OPT_NAWS + supports_naws(false) + else + @socket.puts(IAC + DONT + ch) + end + @iac_state = :none + + when :IAC_DO + if ch == OPT_BINARY + @socket.puts(IAC + WILL + OPT_BINARY) + elsif ch == OPT_ECHO + @echo_supported = true + elsif ch == OPT_MSSP + send_mssp + @mssp_supported = true + else + @socket.puts(IAC + WONT + ch) + end + @iac_state = :none + + when :IAC_DONT + if ch == OPT_ECHO + @echo_supported = false + elsif ch == OPT_COMPRESS2 + # do nothing + elsif ch == OPT_MSSP + @mssp_supported = false + else + @socket.puts(IAC + WONT + ch) + end + @iac_state = :none + + when :IAC_SB + @iac_state = if ch == OPT_NAWS + :IAC_SB_NAWS + else + :IAC_SB_SOMETHING + end + + when :IAC_SB_NAWS + if ch != IAC + @lwidth = ch.ord + @iac_state = :IAC_SB_NAWS_LWIDTH + else + @iac_state = :IAC_SB_NAWS_IAC end + + when :IAC_SB_NAWS_IAC + if ch == IAC + @lwidth = IAC + @iac_state = :IAC_SB_NAWS_LWIDTH + else + raise "IAC escape expected" + end + + when :IAC_SB_NAWS_LWIDTH + if ch != IAC + @hwidth = ch.ord + @iac_state = :IAC_SB_NAWS_LWIDTH_HWIDTH + else + @iac_state = :IAC_SB_NAWS_LWIDTH_IAC + end + + when :IAC_SB_NAWS_LWIDTH_IAC + if ch == IAC + @hwidth = IAC + @iac_state = IAC_SB_NAWS_LWIDTH_HWIDTH + else + raise "IAC escape expected" + end + + when :IAC_SB_NAWS_LWIDTH_HWIDTH + @lheight = ch.ord + @iac_state = :IAC_SB_NAWS_LWIDTH_HWIDTH_LHEIGHT + if ch == IAC #&& @socket.getch != IAC + raise 'escaped IAC expected but not found' + end + + when :IAC_SB_NAWS_LWIDTH_HWIDTH_LHEIGHT + if ch != IAC + @hheight = ch.ord + @iac_state = :IAC_SB_NAWS_LWIDTH_HWIDTH_LHEIGHT_HHEIGHT + else + @iac_state = :IAC_SB_NAWS_LWIDTH_HWIDTH_LHEIGHT_IAC + end + + when :IAC_SB_NAWS_LWIDTH_HWIDTH_LHEIGHT_IAC + if ch == IAC + @hheight = IAC + @iac_state = :IAC_SB_NAWS_LWIDTH_HWIDTH_LHEIGHT_HHEIGHT + else + raise "IAC escape expected" + end + + when :IAC_SB_NAWS_LWIDTH_HWIDTH_LHEIGHT_HHEIGHT + if ch == IAC + @iac_state = :IAC_SB_NAWS_LWIDTH_HWIDTH_LHEIGHT_HHEIGHT_IAC + else + raise "invalid IAC" + end + + when :IAC_SB_NAWS_LWIDTH_HWIDTH_LHEIGHT_HHEIGHT_IAC + if ch == SE + new_width = @lwidth * 256 + @hwidth + new_height = @lheight * 256 + @hheight + log "setting resolution #{new_width} #{new_height}" + @display.resolution = [new_width, new_height] + else + raise 'invalid IAC' + end + @iac_state = :none + when :IAC_SB_SOMETHING + @iac_state = :IAC_SB_SOMETHING_IAC if ch == IAC + + when :IAC_SB_SOMETHING_IAC + @iac_state = :IAC_SB_SOMETHING if ch == IAC + @iac_state = :none if ch == SE else - log "Invalid IAC logic #{iac_state} #{ch}" - return false + @iac_state = :none end end diff --git a/lib/aethyr/core/render/display.rb b/lib/aethyr/core/render/display.rb index f5bf9882bcbed0ed5a66ae77093b2b537236d386..080102ce04b9aad50e84a7e05eec12d6bd45628c 100644 --- a/lib/aethyr/core/render/display.rb +++ b/lib/aethyr/core/render/display.rb @@ -329,6 +329,7 @@ CONF Ncurses.doupdate(); next if not @scanner.process_iac + ch = @windows[:input].window_text.getch puts ch