#!/usr/bin/ruby require 'socket' require 'thread' require 'time' require 'kconv' CRLF = "\r\n" def filter(str) return str end # analyse command line option while l = ARGV.shift case l when /^--help$/ print "usage: #{$PROGRAM_NAME} [-p PORT] [-m /path/to/motd] [-l /path/to/log]\n" exit when /^-l$/ $stderr = File.open(ARGV.shift, "a") when /^-m$/ Motd = ARGV.shift when /^-p$/ Port = ARGV.shift.to_i else end end # set default value Port = 36800 unless defined? Port Motd = "./motd" unless defined? Motd TCPServer.open(Port) do |gs| q = Queue.new clientHash = Hash.new # if caught SIGINT shutdown ther server Signal.trap(:INT) do $stderr.print "[" + Time.now.to_s + "] caught SIGINT, shutting down.\n" exit end # if caught SIGTERM shutdown the server Signal.trap(:TERM) do $stderr.print "[" + Time.now.to_s + "] caught SIGTERM, shutting down.\n" exit end # if caught SIGHUP flush all clients and queue Signal.trap(:HUP) do $stderr.print "[" + Time.now.to_s + "] caught SIGHUP, flusing all of clients.\n" begin clientHash.each_value do |cs| cs.close end rescue nil end clientHash = Hash.new q = Queue.new end # chat server started $stderr.print "[" + Time.now.to_s + "] Chat server started.\n" loop do ns = gs.accept Thread.start(ns) do |s| # initialize value user = nil # print motd to client s.write "+OK Welcome to MECH chat server." + CRLF if FileTest.exist?(Motd) File.open(Motd) do |f| while l = f.gets l.chomp! s.write Kconv.toutf8(l) + CRLF end s.write "." + CRLF end end # thread to push messages to client tpush = Thread.start do while str = q.pop clientHash.each_value do |cs| cs.write str end end end # command operations while l = s.gets l.chomp! l = Kconv.toutf8(l) case l when /^USER \w+/i if user == nil user = l.split(/ /)[1] else s.write "-ERR You have already logged in." + CRLF next end if clientHash.has_key?(user) != true clientHash.store(user, s) s.write "+OK Hello, " + user + "." + CRLF now = Time.now str2cli = "*INFO [" + now.to_s + "] " + user + " logged in." + CRLF str2log = "[" + now.to_s + "] " + user + " logged in from " + ns.peeraddr[2] + "." + "\n" $stderr.print str2log #tpush.run q.push str2cli else s.write "-ERR Nickname already in use." + CRLF end when /^MEMBER$/i num_members = clientHash.size s.write "+OK " + num_members.to_s + " members in the room." + CRLF if num_members != 0 clientHash.each_key do |u| s.write u + CRLF end s.write "." + CRLF end when /^TALK .+/i if user == nil s.write "-ERR You are not logged in." + CRLF next else message = l.split(/ /, 2)[1] s.write "+OK Your message has received." + CRLF q.push("*MSG [" + Time.now.to_s + "] " + user + " " + filter(message) + CRLF) end when /^QUIT$/i s.write "+OK Have a nice day!" + CRLF if user != nil clientHash.delete(user) now = Time.now str2cli = "*INFO [" + now.to_s + "] " + user + " logged out." + CRLF str2log = "[" + now.to_s + "] " + user + " logged out from " + ns.peeraddr[2] + "." + "\n" $stderr.print str2log q.push str2cli end break when /^NOOP$/i s.write "+OK I'm here saying nothing." + CRLF else s.write "-ERR Invalid command." + CRLF end end s.close end end end