# session.rb -- Session manager for BugCommunicator # # Copyright (C) 2002 Yoshinori K. Okuji # # This file is part of BugCommunicator. # # BugCommunicator 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. # # BugCommunicator is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Foobar; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA require 'socket' require 'timeout' require 'bts/system' require 'bts/exception' module BTS class Session PROTOCOL_VERSION = '1.0' def initialize(socket, config) @socket = socket @config = config System.debug('from %s: connected', @socket.peeraddr[2]) end def ok(msg) begin @socket << 'OK ' << msg << "\r\n" rescue SystemCallError end System.info('to %s: %s', @socket.peeraddr[2], msg) end private :ok def error(msg) begin @socket << 'ERROR ' << msg << "\r\n" rescue SystemCallError end System.error('to %s: %s', @socket.peeraddr[2], msg) end private :error def gets begin timeout(30) do return @socket.gets end rescue TimeoutError error 'terminated due to a time out' return nil end end private :gets def handle_mail # Read the whole mail. Is it necessary to be afraid of too long # strings for security? str = '' while true if @socket.eof? error 'unexpected EOF' return end line = gets return if line.nil? if /\r?\n/ !~ line error "no trailing CRLF found in `#{line}'" return end break if /^\.\r?\n/ =~ line line.sub!(/^\./, '') line.chop! str << line + "\r\n" end System.debug('from %s: %s', @socket.peeraddr[2], str) # FIXME: do something useful here... ok "dummy" end private :handle_mail def handle_cgi # Read metavariables. name = nil value = nil metavars = {} while true if @socket.eof? error 'unexpected EOF' return end line = gets return if line.nil? if /\r?\n/ !~ line error "no trailing CRLF found in `#{line}'" end break if /^\.\r?\n/ =~ line line.chop! if line[0] != ?. and /^([^=]+)=(.*)/ =~ line name = $1 value = $2 else if name.nil? or value.nil? error "malformed input line `#{line}'" return end value << "\r\n" + line end metavars[name] = value end System.debug('from %s: %s', @socket.peeraddr[2], metavars.collect do |name, value| name + '=' + value end.join("\n")) body = '' while true if @socket.eof? error 'unexpected EOF' return end line = gets return if line.nil? if /\r?\n/ !~ line error "no trailing CRLF found in `#{line}'" end break if /^\.\r?\n/ =~ line line.sub!(/^\./, '') line.chop! body << line + "\r\n" end System.debug('from %s: %s', @socket.peeraddr[2], body) # FIXME: do something useful here... ok "dummy" tmp = body.gsub(/^\./, '..') @socket << "Content-Type: text/plain\r\n\r\n#{tmp}" @socket << ".\r\n" end private :handle_cgi def run until @socket.eof? line = gets return if line.nil? if /\r?\n/ !~ line error "no trailing CRLF found in `#{line}'" return end System.info('from %s: %s', @socket.peeraddr[2], line.chop) cmd, *args = line.chop.split(/\s+/) case cmd when 'VERSION' if args.size != 1 error 'one argument is required for VERSION' return end if args[0] != PROTOCOL_VERSION error "unsupported protocol version `#{args[0]}'" return end ok "use the protocol version #{PROTOCOL_VERSION}" when 'EXPIRE' if args.size != 0 error 'no argument is required for EXPIRE' return end # FIXME: do something useful here... ok "dummy" when 'OPTIMIZE' if args.size != 0 error 'no argument is required for OPTIMIZE' return end # FIXME: do something useful here... ok "dummy" when 'MAIL' if args.size != 0 error 'no argument is required for MAIL' return end handle_mail when 'CGI' if args.size != 0 error 'no argument is required for CGI' return end handle_cgi when 'BYE' if args.size != 0 error 'no argument is required for BYE' return end ok "good-bye!" return else error "unknown command `#{cmd}'" return end end end def close @socket.close end end end