freeswitcher 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/License.txt +20 -0
- data/NEWS +3 -0
- data/README +85 -0
- data/Rakefile +82 -0
- data/bin/cmd_demo.rb +19 -0
- data/bin/ies_demo.rb +19 -0
- data/bin/ies_demo_with_hook.rb +27 -0
- data/bin/oes_demo.rb +22 -0
- data/bin/oes_demo2.rb +26 -0
- data/lib/fsr.rb +86 -0
- data/lib/fsr/app.rb +72 -0
- data/lib/fsr/app/answer.rb +21 -0
- data/lib/fsr/app/bridge.rb +33 -0
- data/lib/fsr/app/conference.rb +29 -0
- data/lib/fsr/app/fifo.rb +38 -0
- data/lib/fsr/app/fs_break.rb +21 -0
- data/lib/fsr/app/fs_sleep.rb +24 -0
- data/lib/fsr/app/hangup.rb +22 -0
- data/lib/fsr/app/log.rb +24 -0
- data/lib/fsr/app/playback.rb +23 -0
- data/lib/fsr/app/set.rb +22 -0
- data/lib/fsr/app/speak.rb +25 -0
- data/lib/fsr/app/transfer.rb +24 -0
- data/lib/fsr/cmd.rb +57 -0
- data/lib/fsr/cmd/fsctl.rb +41 -0
- data/lib/fsr/cmd/originate.rb +46 -0
- data/lib/fsr/cmd/sofia.rb +40 -0
- data/lib/fsr/cmd/sofia/profile.rb +75 -0
- data/lib/fsr/cmd/sofia/status.rb +39 -0
- data/lib/fsr/cmd/sofia_contact.rb +31 -0
- data/lib/fsr/cmd/status.rb +25 -0
- data/lib/fsr/command_socket.rb +29 -0
- data/lib/fsr/database.rb +7 -0
- data/lib/fsr/database/call_limit.rb +21 -0
- data/lib/fsr/database/core.rb +30 -0
- data/lib/fsr/database/sofia_reg_external.rb +0 -0
- data/lib/fsr/database/sofia_reg_internal.rb +0 -0
- data/lib/fsr/database/voicemail_default.rb +0 -0
- data/lib/fsr/event_socket.rb +41 -0
- data/lib/fsr/fake_socket.rb +68 -0
- data/lib/fsr/listener.rb +10 -0
- data/lib/fsr/listener/header_and_content_response.rb +21 -0
- data/lib/fsr/listener/inbound.rb +61 -0
- data/lib/fsr/listener/inbound/event.rb +42 -0
- data/lib/fsr/listener/outbound.rb +62 -0
- data/lib/fsr/listener/outbound.rb.orig +131 -0
- data/spec/fsr/app.rb +8 -0
- data/spec/fsr/app/bridge.rb +17 -0
- data/spec/fsr/app/conference.rb +12 -0
- data/spec/fsr/app/fifo.rb +29 -0
- data/spec/fsr/cmd.rb +8 -0
- data/spec/fsr/cmd/originate.rb +12 -0
- data/spec/fsr/cmd/sofia.rb +69 -0
- data/spec/fsr/cmd/sofia/profile.rb +58 -0
- data/spec/fsr/listener.rb +12 -0
- data/spec/fsr/listener/inbound.rb +19 -0
- data/spec/fsr/listener/outbound.rb +19 -0
- data/spec/fsr/loading.rb +27 -0
- data/spec/helper.rb +14 -0
- data/tasks/package.rake +29 -0
- data/tasks/ride.rake +6 -0
- data/tasks/rspec.rake +21 -0
- data/tasks/spec.rake +59 -0
- metadata +229 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'fsr/listener'
|
4
|
+
require 'fsr/listener/header_and_content_response.rb'
|
5
|
+
|
6
|
+
module FSR
|
7
|
+
module Listener
|
8
|
+
class Inbound < EventMachine::Protocols::HeaderAndContentProtocol
|
9
|
+
HOOKS = {}
|
10
|
+
|
11
|
+
def initialize(args = {})
|
12
|
+
super
|
13
|
+
@auth = args[:auth] || "ClueCon"
|
14
|
+
end
|
15
|
+
|
16
|
+
def post_init
|
17
|
+
say("auth #{@auth}")
|
18
|
+
say('event plain ALL')
|
19
|
+
end
|
20
|
+
|
21
|
+
def receive_request(header, content)
|
22
|
+
hash_header = headers_2_hash(header)
|
23
|
+
hash_content = headers_2_hash(content)
|
24
|
+
event = HeaderAndContentResponse.new({:headers => hash_header, :content => hash_content})
|
25
|
+
event_name = event.content[:event_name].to_s.strip
|
26
|
+
unless event_name.empty?
|
27
|
+
HOOKS[event_name.to_sym].call(event) unless HOOKS[event_name.to_sym].nil?
|
28
|
+
end
|
29
|
+
on_event(event)
|
30
|
+
end
|
31
|
+
|
32
|
+
def say(line)
|
33
|
+
send_data("#{line}\r\n\r\n")
|
34
|
+
end
|
35
|
+
|
36
|
+
def on_event(event)
|
37
|
+
event
|
38
|
+
end
|
39
|
+
|
40
|
+
# Add or replace a block to execute when the specified event occurs
|
41
|
+
#
|
42
|
+
# <b>Parameters</b>
|
43
|
+
# - event : What event to trigger the block on. May be
|
44
|
+
# :CHANNEL_CREATE, :CHANNEL_DESTROY etc
|
45
|
+
# - block : Block to execute
|
46
|
+
#
|
47
|
+
# <b>Returns/<b>
|
48
|
+
# - nil
|
49
|
+
def self.add_event_hook(event, &block)
|
50
|
+
HOOKS[event] = block
|
51
|
+
end
|
52
|
+
|
53
|
+
# Delete the block that was to be executed for the specified event.
|
54
|
+
def self.del_event_hook(event)
|
55
|
+
HOOKS.delete(event)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'fsr/listener/inbound'
|
2
|
+
module FSR
|
3
|
+
module Listener
|
4
|
+
module Inbound
|
5
|
+
class Event
|
6
|
+
def self.from(data)
|
7
|
+
instance = new
|
8
|
+
|
9
|
+
capture = header = {}
|
10
|
+
body = {}
|
11
|
+
|
12
|
+
data.each_line do |line|
|
13
|
+
line.strip!
|
14
|
+
|
15
|
+
case line
|
16
|
+
when ''
|
17
|
+
capture = body
|
18
|
+
when /([a-zA-Z0-9-]+):\s*(.*)/
|
19
|
+
#capture[$1] = $2.strip
|
20
|
+
key, val = line.split(":")
|
21
|
+
capture[key] = val.to_s.strip
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
instance.header.merge!(header)
|
26
|
+
instance.body.merge!(body)
|
27
|
+
instance
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :header, :body
|
31
|
+
|
32
|
+
def initialize(header = {}, body = {})
|
33
|
+
@header, @body = header, body
|
34
|
+
end
|
35
|
+
|
36
|
+
def [](key)
|
37
|
+
@header.merge(@body)[key]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'fsr/listener'
|
4
|
+
require 'fsr/listener/header_and_content_response'
|
5
|
+
|
6
|
+
module FSR
|
7
|
+
load_all_applications
|
8
|
+
module Listener
|
9
|
+
class Outbound < EventMachine::Protocols::HeaderAndContentProtocol
|
10
|
+
|
11
|
+
# Include FSR::App to get all the applications defined as methods
|
12
|
+
include FSR::App
|
13
|
+
|
14
|
+
# Redefine the FSR::App methods to wrap sendmsg around them
|
15
|
+
SENDMSG_METHOD_DEFINITION = "def %s(*args, &block); sendmsg super; end"
|
16
|
+
APPLICATIONS.each { |app, obj| module_eval(SENDMSG_METHOD_DEFINITION % app.to_s) }
|
17
|
+
|
18
|
+
def post_init
|
19
|
+
@session = nil # holds the session object
|
20
|
+
send_data("connect\n\n")
|
21
|
+
FSR::Log.debug "Accepting connections."
|
22
|
+
end
|
23
|
+
|
24
|
+
def receive_request(header, content)
|
25
|
+
hash_header = headers_2_hash(header)
|
26
|
+
hash_content = headers_2_hash(content)
|
27
|
+
session = HeaderAndContentResponse.new({:headers => hash_header, :content => hash_content})
|
28
|
+
if @session.nil?
|
29
|
+
@session = self
|
30
|
+
session_initiated(session)
|
31
|
+
else
|
32
|
+
receive_reply(session)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Received data dispatches the data received by the EM socket,
|
37
|
+
# Either as a Session, a continuation of a Session, or as a Session's last CommandReply
|
38
|
+
|
39
|
+
def session_initiated(session)
|
40
|
+
session
|
41
|
+
end
|
42
|
+
|
43
|
+
def receive_reply(session)
|
44
|
+
session
|
45
|
+
end
|
46
|
+
|
47
|
+
# sendmsg sends data to the EM app socket via #send_data, or
|
48
|
+
# returns the string it would send if #send_data is not defined.
|
49
|
+
# It expects an object which responds to either #sendmsg or #to_s,
|
50
|
+
# which should return a EM Outbound Event Socket formatted instruction
|
51
|
+
|
52
|
+
def sendmsg(message)
|
53
|
+
text = message.respond_to?(:sendmsg) ? message.sendmsg : message.to_s
|
54
|
+
FSR::Log.debug "sending #{text}"
|
55
|
+
message = "sendmsg\n%s\n" % text
|
56
|
+
self.respond_to?(:send_data) ? send_data(message) : message
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "fsr/listener"
|
3
|
+
module FSR
|
4
|
+
load_all_applications
|
5
|
+
module Listener
|
6
|
+
module Outbound
|
7
|
+
include FSR::Listener
|
8
|
+
|
9
|
+
# Include FSR::App to get all the applications defined as methods
|
10
|
+
include FSR::App
|
11
|
+
|
12
|
+
# Redefine the FSR::App methods to wrap sendmsg around them
|
13
|
+
SENDMSG_METHOD_DEFINITION = "def %s(*args, &block); sendmsg super; end"
|
14
|
+
APPLICATIONS.each { |app, obj| module_eval(SENDMSG_METHOD_DEFINITION % app.to_s) }
|
15
|
+
|
16
|
+
def post_init
|
17
|
+
@session = nil # holds the session object
|
18
|
+
send_data("connect\n\n")
|
19
|
+
FSR::Log.debug "Accepting connections."
|
20
|
+
end
|
21
|
+
|
22
|
+
# Received data dispatches the data received by the EM socket,
|
23
|
+
# Either as a Session, a continuation of a Session, or as a Session's last CommandReply
|
24
|
+
def receive_data(data)
|
25
|
+
FSR::Log.debug("received #{data}")
|
26
|
+
if @session.nil? # if @session is nil, create a new Session object
|
27
|
+
@session = Session.new(data)
|
28
|
+
session_initiated(@session) if @session.initiated?
|
29
|
+
else
|
30
|
+
# If it's not nil, we add the data to this session, Session knows whether
|
31
|
+
# or not to create a CommandReply, complete a CommandReply, or simply add to
|
32
|
+
# its own @data array and @headers/@body structures
|
33
|
+
if @session.initiated?
|
34
|
+
@session << data
|
35
|
+
reply_received(@session.replies.last) if @session.replies.last.complete?
|
36
|
+
else
|
37
|
+
@session << data
|
38
|
+
session_initiated(@session) if @session.initiated?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
@session
|
42
|
+
end
|
43
|
+
|
44
|
+
def session_initiated(session)
|
45
|
+
session
|
46
|
+
end
|
47
|
+
|
48
|
+
def reply_received(command_reply)
|
49
|
+
command_reply
|
50
|
+
end
|
51
|
+
|
52
|
+
alias :receive_response :reply_received
|
53
|
+
# sendmsg sends data to the EM app socket via #send_data, or
|
54
|
+
# returns the string it would send if #send_data is not defined.
|
55
|
+
# It expects an object which responds to either #sendmsg or #to_s,
|
56
|
+
# which should return a EM Outbound Event Socket formatted instruction
|
57
|
+
def sendmsg(message)
|
58
|
+
text = message.respond_to?(:sendmsg) ? message.sendmsg : message.to_s
|
59
|
+
FSR::Log.debug "sending #{text}"
|
60
|
+
message = "sendmsg\n%s\n" % text
|
61
|
+
self.respond_to?(:send_data) ? send_data(message) : message
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
class SocketResponse
|
66
|
+
attr_accessor :headers, :body, :data
|
67
|
+
def initialize(data = "")
|
68
|
+
@data = [data]
|
69
|
+
@headers = {}
|
70
|
+
if data.match(/\n$/)
|
71
|
+
headers, @body = data.split("\n\n")
|
72
|
+
headers.each_line do |line|
|
73
|
+
key, value = line.split(":")
|
74
|
+
@headers[key] = value.to_s.strip
|
75
|
+
end
|
76
|
+
end
|
77
|
+
@body ||= ""
|
78
|
+
FSR::Log.debug("New #{self.class.name} created: #{self}")
|
79
|
+
end
|
80
|
+
|
81
|
+
def <<(data)
|
82
|
+
if data.match(/\n$/)
|
83
|
+
@data.last.match(/\n$/) ? @data << data : @data.last << data
|
84
|
+
extra_headers, more_body = @data.last.split("\n\n")
|
85
|
+
extra_headers.each_line do |line|
|
86
|
+
key, value = line.split(":")
|
87
|
+
@headers[key] = value.to_s.strip
|
88
|
+
end
|
89
|
+
@body << more_body unless more_body.nil?
|
90
|
+
else
|
91
|
+
@data.last.match(/\n$/) ? @data << data : @data.last << data
|
92
|
+
end
|
93
|
+
self
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class Session < SocketResponse
|
98
|
+
attr_accessor :replies
|
99
|
+
def initialize(data = "")
|
100
|
+
super
|
101
|
+
@replies = []
|
102
|
+
end
|
103
|
+
|
104
|
+
def <<(data)
|
105
|
+
if initiated?
|
106
|
+
if @replies.empty? or @replies.last.complete?
|
107
|
+
@replies << CommandReply.new(data)
|
108
|
+
else
|
109
|
+
@replies.last << data
|
110
|
+
end
|
111
|
+
else
|
112
|
+
super
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def initiated?
|
117
|
+
@headers.keys.include?("Control")
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
class CommandReply < SocketResponse
|
123
|
+
# Set this to true for now, fill it in when we know what completed a reply
|
124
|
+
def complete?
|
125
|
+
true
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
data/spec/fsr/app.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec/helper'
|
2
|
+
require "fsr/app"
|
3
|
+
FSR::App.load_application("bridge")
|
4
|
+
|
5
|
+
describe "Testing FSR::App::Bridge" do
|
6
|
+
# Utilize the [] shortcut to start a conference
|
7
|
+
it "Bridges a call, for FSR::Listener::Inbound" do
|
8
|
+
bridge = FSR::App::Bridge.new("user/bougyman")
|
9
|
+
bridge.raw.should == "bridge({}user/bougyman)"
|
10
|
+
end
|
11
|
+
|
12
|
+
it "Bridges a call, for FSR::Listener::Outbound" do
|
13
|
+
bridge = FSR::App::Bridge.new("user/bougyman")
|
14
|
+
bridge.sendmsg.should == "call-command: execute\nexecute-app-name: bridge\nexecute-app-arg: user/bougyman\n\n"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec/helper'
|
2
|
+
require "fsr/app"
|
3
|
+
FSR::App.load_application("conference")
|
4
|
+
|
5
|
+
describe "Testing FSR::App::Conference" do
|
6
|
+
# Utilize the [] shortcut to start a conference
|
7
|
+
it "Executes a conference using Conference[conf_spec]" do
|
8
|
+
conf = FSR::App::Conference["5290-192.168.6.30"]
|
9
|
+
conf.should == "conference(5290-192.168.6.30@ultrawideband)"
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec/helper'
|
2
|
+
require "fsr/app"
|
3
|
+
FSR::App.load_application("fifo")
|
4
|
+
|
5
|
+
describe "Testing FSR::App::Fifo" do
|
6
|
+
# Utilize the [] shortcut to start a conference
|
7
|
+
it "Puts a call into a queue, with #new" do
|
8
|
+
fifo = FSR::App::Fifo.new("myqueue", "in")
|
9
|
+
fifo.raw.should == "fifo(myqueue in)"
|
10
|
+
end
|
11
|
+
|
12
|
+
it "Puts a call into a queue, with <<" do
|
13
|
+
fifo = FSR::App::Fifo << "myqueue"
|
14
|
+
fifo.raw.should == "fifo(myqueue in)"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "Adds a consumer to a queue, with #new" do
|
18
|
+
fifo = FSR::App::Fifo.new("myqueue", "out")
|
19
|
+
fifo.raw.should == "fifo(myqueue out nowait)"
|
20
|
+
fifo = FSR::App::Fifo.new("myqueue", "out", :wait => true)
|
21
|
+
fifo.raw.should == "fifo(myqueue out wait)"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "Adds a consumer to a queue, with >>" do
|
25
|
+
fifo = FSR::App::Fifo >> "myqueue"
|
26
|
+
fifo.raw.should == "fifo(myqueue out wait)"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
data/spec/fsr/cmd.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec/helper'
|
2
|
+
require "fsr/cmd"
|
3
|
+
FSR::Cmd.load_command("originate")
|
4
|
+
|
5
|
+
describe "Testing FSR::Cmd::Originate" do
|
6
|
+
# Originate to an extension
|
7
|
+
it "Originates calls to extensions" do
|
8
|
+
originate = FSR::Cmd::Originate.new(nil, :target => "user/bougyman", :endpoint => "4000")
|
9
|
+
originate.raw.should == "originate {ignore_early_media=true,originate_timeout=30,origination_caller_id_name=FSR,origination_caller_id_number=8675309}user/bougyman 4000"
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec/helper'
|
2
|
+
require "fsr/cmd"
|
3
|
+
FSR::Cmd.load_command("sofia")
|
4
|
+
|
5
|
+
describe "Testing FSR::Cmd::Sofia" do
|
6
|
+
## Sofia ##
|
7
|
+
# Interface to sofia
|
8
|
+
it "FSR::Cmd::Sofia should interface to sofia" do
|
9
|
+
sofia = FSR::Cmd::Sofia.new
|
10
|
+
sofia.raw.should == "sofia"
|
11
|
+
end
|
12
|
+
|
13
|
+
## Sofia Status ##
|
14
|
+
it "FSR::Cmd::Sofia should allow status" do
|
15
|
+
sofia = FSR::Cmd::Sofia.new
|
16
|
+
status = sofia.status
|
17
|
+
status.raw.should == "sofia status"
|
18
|
+
end
|
19
|
+
# Sofia status profile internal
|
20
|
+
it "FSR::Cmd::Sofia should allow status profile internal" do
|
21
|
+
sofia = FSR::Cmd::Sofia.new
|
22
|
+
status = sofia.status(:status => 'profile', :name => 'internal')
|
23
|
+
status.raw.should == "sofia status profile internal"
|
24
|
+
end
|
25
|
+
# Sofia status gateway server
|
26
|
+
it "FSR::Cmd::Sofia should allow status gateway server" do
|
27
|
+
sofia = FSR::Cmd::Sofia.new
|
28
|
+
status = sofia.status(:status => 'gateway', :name => 'server')
|
29
|
+
status.raw.should == "sofia status gateway server"
|
30
|
+
end
|
31
|
+
|
32
|
+
## Sofia profile ##
|
33
|
+
it "FSR::Cmd::Sofia should allow profile" do
|
34
|
+
sofia = FSR::Cmd::Sofia.new
|
35
|
+
profile = sofia.profile
|
36
|
+
profile.raw.should == "sofia profile"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "FSR::Cmd::Sofia::Profile should allow raw string" do
|
40
|
+
sofia = FSR::Cmd::Sofia.new
|
41
|
+
profile = sofia.profile('internal stop')
|
42
|
+
profile.raw.should == "sofia profile internal stop"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "FSR::Cmd::Sofia::Profile should allow start" do
|
46
|
+
sofia = FSR::Cmd::Sofia.new
|
47
|
+
profile = sofia.profile.start('internal')
|
48
|
+
profile.raw.should == "sofia profile internal start"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "FSR::Cmd::Sofia::Profile should allow stop" do
|
52
|
+
sofia = FSR::Cmd::Sofia.new
|
53
|
+
profile = sofia.profile.stop('internal')
|
54
|
+
profile.raw.should == "sofia profile internal stop"
|
55
|
+
end
|
56
|
+
|
57
|
+
it "FSR::Cmd::Sofia::Profile should allow restart" do
|
58
|
+
sofia = FSR::Cmd::Sofia.new
|
59
|
+
profile = sofia.profile.restart('internal')
|
60
|
+
profile.raw.should == "sofia profile internal restart"
|
61
|
+
end
|
62
|
+
|
63
|
+
it "FSR::Cmd::Sofia::Profile should allow rescan" do
|
64
|
+
sofia = FSR::Cmd::Sofia.new
|
65
|
+
profile = sofia.profile.rescan('internal')
|
66
|
+
profile.raw.should == "sofia profile internal rescan"
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|