class SimpleTelnetServer::Connection

A basic Telnet server implemented with EventMachine.

Constants

DEFAULT_OPTIONS

@return [Hash<Symbol, Object>] default values for (telnet) options

Attributes

custom_handler[RW]

custom handler of buffer content @return [Proc, call] will be passed the content of the buffer

Public Class Methods

commands() click to toggle source

Returns the Hash of registered commands. If they are not initialized yet (@commands), the one from superclass is used. If we’re TelnetServer itself, an empty Hash is used.

@return [Hash{String, Regexp => Symbol, Proc}]

# File lib/em-simple_telnet_server.rb, line 60
def commands
  @commands ||= if top_class?
    {}
  else
    superclass.commands.dup # copy from superclass
  end
end
has_command(cmd, action = nil, &blk) click to toggle source

Registers an action for the command cmd (which can be a Regexp, === will be used). action can be a Symbol referring to an instance method or an instance of Proc. If action is not specified, the given block is used.

If cmd is a Regexp, all captures (MatchData#captures) will be passed to the block/method call (see {#run_command}).

@param cmd [String, Regexp] fixed command or regular expression that

matches a command and its arguments in capture groups

@param action [Symbol] the method to call. if given, the block passed is

ignored
# File lib/em-simple_telnet_server.rb, line 51
def has_command(cmd, action = nil, &blk)
  commands[cmd] = action || blk
end
has_option(opt, value) click to toggle source

Sets the option opt to value. @param opt [Symbol] option key @param value [Object] anything

# File lib/em-simple_telnet_server.rb, line 25
def has_option(opt, value)
  options[opt] = value
end
options() click to toggle source

Returns the (telnet) options for this class. If they are not initialized yet, the ones from superclass (or SimpleTelnetServer) are duplicated.

# File lib/em-simple_telnet_server.rb, line 31
def options
  @options ||= if top_class?
    DEFAULT_OPTIONS
  else
    superclass.options
  end.dup
end
start_server(addr = 'localhost', port = options[:port]) click to toggle source

Starts the server on address addr and port port.

# File lib/em-simple_telnet_server.rb, line 18
def start_server(addr = 'localhost', port = options[:port])
  EventMachine.start_server(addr, port, self)
end
top_class?() click to toggle source

@return [Boolean] whether we’ve reached the top of the relevant

hieararchy
# File lib/em-simple_telnet_server.rb, line 70
def top_class?
  self == SimpleTelnetServer::Connection
end

Public Instance Methods

authorized?() click to toggle source

@return [Boolean] whether the user is logged in

# File lib/em-simple_telnet_server.rb, line 122
def authorized?
  @connection_state == :authorized
end
command_not_known(command) click to toggle source

@abstract Called automatically when a received command is not known (no matching entry in @commands) and sends back an error message.

@param command [String] the command that is not known

# File lib/em-simple_telnet_server.rb, line 136
def command_not_known(command)
  send_output "Command #{command.inspect} is not known."
end
commands() click to toggle source

Returns the recognized commands for this telnet server.

# File lib/em-simple_telnet_server.rb, line 146
def commands
  self.class.commands
end
needs_authentication?() click to toggle source

@abstract If authentication is not required, there won’t be a login procedure and any peer is automatically logged in after connecting. @return [Boolean] whether this telnet server requires authentication

# File lib/em-simple_telnet_server.rb, line 106
def needs_authentication?
  false
end
options() click to toggle source

Returns the telnet options for this telnet server.

# File lib/em-simple_telnet_server.rb, line 141
def options
  self.class.options
end
post_init() click to toggle source

Called by EventMachine when a new connection attempt is made to this server (immediately after calling {#initialize}).

Checks if {#needs_authentication?}, which returns false if not overridden. If it authentication is needed, it’ll initiate the login procedure (send login prompt, get username, get password, …).

Otherwise, any peer is authorized right away.

# File lib/em-simple_telnet_server.rb, line 93
def post_init
  @buffer = ""
  if needs_authentication?
    initiate_authentication
  else
    authorize # login anybody
  end
end
receive_data(data) click to toggle source

Called by EventMachine when new data is received. Appends data to the buffer (@buffer). Calls {#process_buffer} if @buffer content ends with newline.

# File lib/em-simple_telnet_server.rb, line 113
  def receive_data data
#    warn "Server: <<< #{data.inspect}"
    @buffer << data

    # work only with complete commands (ending with newline)
    process_buffer if @buffer.end_with? "\n"
  end
unbind() click to toggle source

@abstract Called by EventMachine after the connection has been closed.

# File lib/em-simple_telnet_server.rb, line 128
def unbind
end

Private Instance Methods

authorize() click to toggle source

Authorizes the user. This is done by setting @connection_state to :authorized, calling the {#on_authorization} hook method, and sendng him the command prompt.

# File lib/em-simple_telnet_server.rb, line 196
def authorize
  @connection_state = :authorized
  on_authorization
  send_command_prompt
end
execute_action(action, args = nil) click to toggle source

Invokes action along with the given arguments.

@param action [Proc, Symbol] code or a method on this server to call @param params [Array<Object>, nil] arguments for action (passed with splat

operator)

@raise [ArgumentError] if action is invalid

# File lib/em-simple_telnet_server.rb, line 258
def execute_action(action, args = nil)
  case action
  when Proc
    self.instance_exec(*args, &action)
  when Symbol
    self.send(action, *args)
  else
    raise ArgumentError, "invalid action #{action.inspect}"
  end
end
on_authorization() click to toggle source

@abstract Called right after authorization. You can override this method to initialize your code.

Using {#initialize} instead is a bit clunky because it’s expected to take EventMachine-specific arguments and happens before the user is authorized. Same goes for {#post_init}. For both you’d have to remember to call super.

# File lib/em-simple_telnet_server.rb, line 210
def on_authorization
end
process_buffer() click to toggle source

Processes the content of @buffer.

If a {#custom_handler} is defined, it’s called with the current buffer contents. The handler will be removed, so if it has to stay, it has to re-add itself.

If the user is authorized, the commands in the buffer are executed.

If the user isn’t authorized, {#process_spam} is called, which does nothing by default.

Ensures that the buffer is cleared.

# File lib/em-simple_telnet_server.rb, line 179
def process_buffer
  if handler = custom_handler
    self.custom_handler = nil
    handler.(@buffer)

  elsif authorized?
    run_commands
  else
    process_spam
  end
ensure
  @buffer.clear
end
process_spam() click to toggle source

@abstract Called when data has been received while user isn’t authorized. This could be used to {#close_connection}.

# File lib/em-simple_telnet_server.rb, line 272
def process_spam
end
run_command(command) click to toggle source

Runs a command.

Stores command into @current_command for later use and looks it up. If it finds an action for it, executes the action.

If the matching command pattern is a Regexp, the captures are passed to the action.

@param command [String] command to run @raise [UnknownCommand] if the command is unknown

# File lib/em-simple_telnet_server.rb, line 241
def run_command(command)
  @current_command = command
  if pair = commands.find { |pattern,| pattern === command }
    pattern, action = pair
    args = $~.captures if pattern.is_a? Regexp
    execute_action(action, args) if action
  else
    raise UnknownCommand, command
  end
end
run_commands() click to toggle source

Runs the commands in the buffer. Will call {#run_command} for each command (line), no matter if it is recognized or not. If {UnknownCommand} is raised, it’s handled using {#command_not_known}.

# File lib/em-simple_telnet_server.rb, line 223
def run_commands
  @buffer.lines.each do |command|
    run_command(command.chomp)
  end
rescue UnknownCommand
  command_not_known $!.command
end
send_command_prompt() click to toggle source

Sends the command prompt.

# File lib/em-simple_telnet_server.rb, line 153
def send_command_prompt
  send_data options[:command_prompt]
end
send_output(output) click to toggle source

Sends output and then the command prompt. Appens new line to output first, if it doesn’t have one yet, unless it’s the empty string. @param output [String] output to send before command prompt

# File lib/em-simple_telnet_server.rb, line 161
def send_output(output)
  output += "\n" unless output.end_with? "\n" or output.empty?
  send_data output
  send_command_prompt
end