From 50062f3b9faf983ad64f301ed65a87cfd9582f80 Mon Sep 17 00:00:00 2001 From: paul Date: Thu, 15 Sep 2005 14:47:25 +0000 Subject: [PATCH] Implemented parts of the server and protocol handler. git-svn-id: svn+ssh://svn.luon.net/svn/ildus/trunk@3 65a33f86-aa00-0410-91be-cd1bf5efb309 --- lib/ildus/server.rb | 42 +++++++++++++++ lib/ildus/server/handler.rb | 104 ++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 lib/ildus/server/handler.rb diff --git a/lib/ildus/server.rb b/lib/ildus/server.rb index f988932..77c8e25 100644 --- a/lib/ildus/server.rb +++ b/lib/ildus/server.rb @@ -8,23 +8,65 @@ # any later version. require 'gserver' +require 'yaml' + +require 'ildus/server/handler' module Ildus class Server < GServer + # The interface to listen specified default by the hostname. + DEFAULT_HOST = '::' + + # The maximal number of connections. + MAX_CONNECTIONS = 20 + + # The server configuration. + attr_reader :config + # Create a Ildus server instance that is a derivate of a generic # server. Accepts an alternative _config_file_ as optional argument. def initialize(config_file=nil) + @config = nil + parse_config(config_file) + + super(config['port'], DEFAULT_HOST, 20, $stderr, true, false) + end + + def serve(io) + hdl = Handler.new(self, io) + hdl.handle_client + rescue => e + $stderr.puts "#{e.class}: #{e}" + $stderr.puts e.backtrace end # Start the Ildus server. def start + super + join end # Stop and shutdown the Ildus server. An _status_ code (defaults to 0) # can be used as an optional argument. def shutdown(status=0) + super() + exit(status) + end + + ######### + private + ######### + + # Parses the configuration file _filename_ and stores the parsed + # contents. + def parse_config(filename) + raise ArgumentError unless FileTest.exists? filename + + File.open(filename, "r") do |io| + @config = YAML.load(io) + end end end # class Server diff --git a/lib/ildus/server/handler.rb b/lib/ildus/server/handler.rb new file mode 100644 index 0000000..f4572c2 --- /dev/null +++ b/lib/ildus/server/handler.rb @@ -0,0 +1,104 @@ +# = ildus/server/handler - Ildus server command handler +# +# Copyright (C) 2005 Paul van Tilburg +# +# Ildus is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free +# Software Foundation; either version 2 of the License, or (at your option) +# any later version. + +module Ildus + + class Server < GServer + + class Handler + + class ProtocolException < StandardError; end + class CmdUnknownError < ProtocolException; end + class ArgsSyntaxError < ProtocolException; end + class NotImplementedError < ProtocolException; end + class AlreadyAuthError < ProtocolException; end + class TooManyUnknownError < ProtocolException; end + + MaxCmdErrs = 3 + + ProtocolCodeMapping = { + 200 => "Command OK.", + # 210, 211, 212, 213 + 214 => "Direct comments to %s.", + 215 => "Listing done.", + 220 => "%s %s %s %s, welcome.", + 221 => "%s closed connection, goodbye!", + 230 => "User %s authenticated, proceed.", + 240 => "Updated host %s to IP %s.", + # 330 + 331 => "User name OK, need password.", + # 420 + 421 => "%s forcibly closed connection.", + 422 => "connection timed out, %s forcibly closed connection.", + # 423, 424 + 425 => "Can't find host %s.", + 426 => "Can't find %s-record of host %s.", + 500 => "Syntax error, command `%s' unrecognized.", + 501 => "Syntax error in arguments.", + 502 => "Command not implemented.", + 503 => "Too many unrecognized commands!", + 504 => "You are already authenticated!", + 505 => "Server error: %s!", + 506 => "Server error, update failed: %s", + 530 => "Not authenticated!" + } + + def intialize(server, io) + @server = server + @io = io + end + + def handle_client + io.puts prot_msg(220, "localhost", Program, Version, Time.now.to_s) + + cmd_errs = 0 + io.each_line do |line| + begin + cmd, *args = line.split /\s+/ + handle_command(io, cmd, args) + rescue CmdUnknownError # 500 + raise TooManyUnknownError if cmd_errs > MaxCmdErrs + io.puts prot_msg(500, cmd) + cmd_errs = cmd_errs + 1 + rescue ArgsSyntaxError # 501 + io.puts prot_msg(501) + rescue NotImplementedError # 502 + io.puts prot_msg(502) + rescue AlreadyAuthError # 504 + io.puts prot_msg(504) + end + end + rescue TooManyUnknownError # 503 + io.puts prot_msg(503) + rescue RuntimeError => msg # 505 + io.puts prot_msg(505, msg) + raise + end + + ######### + private + ######### + + def handle_command(io, cmd, args) + method_name = cmd.downcase + "_cmd" + meth = method(method_name) + meth.call(*args) + rescue NameError + raise CmdUnknownError + end + + def prot_msg(code, *args) + code.to_s << " " << ProtocolCodeMapping[code] % args + end + + end # class Handler + + end # class Server + +end # module Ildus