# Adhearsion, open source technology integrator # Copyright 2006 Jay Phillips # # This program 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. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class ServletContainer # TODO: Port Mongrel server here. class NativeServer;end require 'gserver' class RubyServer < GServer def initialize(port, *args) @stdlog = $STDOUT log "Starting server on port #{port}" audit = true super(port, *args) end def read_variables io # When debugging, this method could actually not do any IO operations and just # return a development Hash of call variables call_variables = {} while(line = io.gets.chomp) break if line.empty? # Empty lines signify no more variables variable = line.split(/:\s*/) new_name, new_value = variable.first[4..-1].downcase, variable.last call_variables["#{new_name}"] = new_value =~ /^\d+$/ ? Integer(new_value) : new_value end call_variables end # GServer allows all functionality to be packed into def serve io io.sync = true Thread.current[:io] = io begin call_variables = read_variables io Thread.current[:VARS] = call_variables Thread.current[:container] = Contexts::Container.new if call_variables.extension == 'h' # We've received a notification that a channel's hung up! Thread.list.each do |thread| if thread[:VARS] && thread[:VARS]['uniqueid'] == call_variables['uniqueid'] thread[:hungup?] = true # Could kill() the Thread here but it may need to finish. We're merciful people. log "Received notification that channel #{call_variables['channel']} has been hung up." return end end elsif ['t', 'failed'].include? call_variables['extension'] # Timeout notifications. Not implemented yet... io.close return end log "Executing call with variables: " + call_variables.inspect # TODO Will perform cache checking here. call_variables.each do |k,v| Thread.current[:container].run_inside do meta_def k do v end end end # Execute all before_call hooks [$BEFORE_CALl_HIGH, $BEFORE_CALL, $BEFORE_CALL_LOW].flatten.compact.each &:call +lambda { answer if CONFIG.answer_before_call } # A call hook may decree that the call shouldn't be processed further (e.g. if # it processed the call itself). This is done be rewriting the context variable. unless Thread.current[:VARS]['context'] == :interrupted begin Contexts.new.instance_eval do # Interpret the extensions.rb file! eval File.read(File.join(Dir.pwd, "extensions.rb")) end rescue => detail log "Exception raised in extensions.rb! " << detail.message # TODO: Make error reports more intutive. Use notifications DSL? detail.backtrace.each do |msg| log " "*8 << msg end end log "Parsing of extensions.rb complete" begin target_context = call_variables['context'] if target_context Thread.current[:container].run_inside do begin +send(target_context.to_s.to_sym) rescue => e STDERR.puts e.inspect +lambda { play 'were-sorry' hangup } end end else io.close log "ASTERISK REQUESTED ROUTING FOR A CONTEXT UNDEFINED IN extensions.rb!!! REQUEST IGNORED!!!" return end rescue => detail log "ERROR: #{detail.class.name} => #{detail.inspect}" detail.backtrace.each do |msg| log " "*8 << msg end end log "Call routing complete" end rescue => detail log "Call thread raised an exception! #{detail.message}" detail.backtrace.each do |msg| log " "*8 << msg end end [$AFTER_CALl_HIGH, $AFTER_CALL, $AFTER_CALL_LOW].flatten.compact.each &:call +lambda { hangup if CONFIG.hangup_after_call } end end def initialize(port=4573, native=false) @server = (native ? NativeServer : RubyServer).new port, '0.0.0.0' @server.start end def shutdown @server.shutdown end attr_reader :server end