bougyman-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/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/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/spec.rake +59 -0
- metadata +229 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
require "fsr/app"
|
2
|
+
module FSR
|
3
|
+
module Cmd
|
4
|
+
class Sofia < Command
|
5
|
+
def initialize(fs_socket = nil)
|
6
|
+
@fs_socket = fs_socket # FSR::CommandSocket obj
|
7
|
+
end
|
8
|
+
|
9
|
+
# sofia status
|
10
|
+
def status(args = {})
|
11
|
+
require "fsr/cmd/sofia/status" # Require sofia/status
|
12
|
+
Status.new(@fs_socket, args)
|
13
|
+
end
|
14
|
+
|
15
|
+
# sofia profile
|
16
|
+
def profile(args = nil)
|
17
|
+
require "fsr/cmd/sofia/profile" # Require sofia/profile
|
18
|
+
if args == nil
|
19
|
+
Profile.new(@fs_socket, :command_string => "")
|
20
|
+
else
|
21
|
+
Profile.new(@fs_socket, args)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Send the command to the event socket, using api by default.
|
26
|
+
def run(api_method = :api)
|
27
|
+
orig_command = "%s %s" % [api_method, raw]
|
28
|
+
Log.debug "saying #{orig_command}"
|
29
|
+
@fs_socket.say(orig_command)
|
30
|
+
end
|
31
|
+
|
32
|
+
# This method builds the API command to send to the freeswitch event socket
|
33
|
+
def raw
|
34
|
+
orig_command = "sofia"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
register(:sofia, Sofia)
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "fsr/app"
|
2
|
+
module FSR
|
3
|
+
module Cmd
|
4
|
+
class Sofia
|
5
|
+
class Profile < Command
|
6
|
+
attr_reader :options
|
7
|
+
attr_accessor :fs_socket, :command_string, :name, :action
|
8
|
+
|
9
|
+
VALID_ACTIONS = [:start, :stop, :restart, :rescan]
|
10
|
+
def initialize(fs_socket = nil, options = nil)
|
11
|
+
@fs_socket = fs_socket # FSR::CommandSocket object
|
12
|
+
if options.kind_of?(String)
|
13
|
+
@command_string = options
|
14
|
+
else
|
15
|
+
raise "options must be a String or Hash" unless options.kind_of?(Hash)
|
16
|
+
@options = options
|
17
|
+
@action = @options[:action]
|
18
|
+
if @action
|
19
|
+
raise "Invalid action, must specify one of #{VALID_ACTIONS.inspect}" unless VALID_ACTIONS.include?(@action)
|
20
|
+
@name = @options[:name]
|
21
|
+
raise "Invalid profile name" unless @name.to_s.match(/\w/)
|
22
|
+
else
|
23
|
+
@command_string = @options[:command_string] # If user wants to send a raw "sofia profile"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
@command_string ||= ""
|
27
|
+
end
|
28
|
+
|
29
|
+
VALID_ACTIONS.each do |action|
|
30
|
+
define_method(action, lambda { |name| @action, @name = action, name;self })
|
31
|
+
end
|
32
|
+
|
33
|
+
# Send the command to the event socket, using api by default.
|
34
|
+
def run(api_method = :api)
|
35
|
+
orig_command = "%s %s" % [api_method, raw]
|
36
|
+
Log.debug "saying #{orig_command}"
|
37
|
+
@fs_socket.say(orig_command)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.start(profile, socket = FSR::CommandSocket.new)
|
41
|
+
new(socket, :name => profile, :action => :start)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Restart a sip_profile
|
45
|
+
def self.restart(profile, socket = FSR::CommandSocket.new)
|
46
|
+
new(socket, :name => profile, :action => :restart)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Stop a sip_profile
|
50
|
+
def self.stop(profile, socket = FSR::CommandSocket.new)
|
51
|
+
new(socket, :name => profile, :action => :stop)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Rescan a sip_profile
|
55
|
+
def self.rescan(profile, socket = FSR::CommandSocket.new)
|
56
|
+
new(socket, :name => profile, :action => :rescan)
|
57
|
+
end
|
58
|
+
|
59
|
+
# This method builds the API command to send to the freeswitch event socket
|
60
|
+
def raw
|
61
|
+
raise "Invalid action, must specify (start|stop|restart|rescan) as an action or pass a command_string" unless @command_string or @action
|
62
|
+
if @action
|
63
|
+
if @action_options
|
64
|
+
"sofia profile %s %s %s" % [@name, @action, @action_options]
|
65
|
+
else
|
66
|
+
"sofia profile %s %s" % [@name, @action]
|
67
|
+
end
|
68
|
+
else
|
69
|
+
"sofia profile %s" % @command_string
|
70
|
+
end.to_s.strip
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "fsr/app"
|
2
|
+
module FSR
|
3
|
+
module Cmd
|
4
|
+
class Sofia
|
5
|
+
class Status < Command
|
6
|
+
attr_reader :fs_socket
|
7
|
+
|
8
|
+
def initialize(fs_socket = nil, args = {})
|
9
|
+
@fs_socket = fs_socket # FSR::CommandSocket object
|
10
|
+
@status = args[:status] # Status type; profile or gateway
|
11
|
+
@name = args[:name] # Name of profile or gateway
|
12
|
+
# If status is given, make sure it's profile or gateway
|
13
|
+
unless @status.nil?
|
14
|
+
raise "status must be profile or gateway" unless @status =~ /profile|gateway/i
|
15
|
+
end
|
16
|
+
if @status
|
17
|
+
raise "must provide a profile or gateway name" unless @name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Send the command to the event socket, using api by default.
|
22
|
+
def run(api_method = :api)
|
23
|
+
orig_command = "%s %s" % [api_method, raw]
|
24
|
+
Log.debug "saying #{orig_command}"
|
25
|
+
@fs_socket.say(orig_command)
|
26
|
+
end
|
27
|
+
|
28
|
+
# This method builds the API command to send to the freeswitch event socket
|
29
|
+
def raw
|
30
|
+
if @status and @name
|
31
|
+
orig_command = "sofia status #{@status} #{@name}"
|
32
|
+
else
|
33
|
+
orig_command = "sofia status"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# A lot of methods are missing here. The only one implemented is max_sessions
|
2
|
+
# The max_sessions getter currently returns the raw result but could instead return an Integer
|
3
|
+
|
4
|
+
require "fsr/app"
|
5
|
+
module FSR
|
6
|
+
module Cmd
|
7
|
+
class SofiaContact < Command
|
8
|
+
attr_reader :contact
|
9
|
+
|
10
|
+
def initialize(fs_socket = nil, contact = {})
|
11
|
+
@fs_socket = fs_socket # FSR::CommandSocket obj
|
12
|
+
@contact = contact[:contact]
|
13
|
+
puts @contact
|
14
|
+
end
|
15
|
+
|
16
|
+
# Send the command to the event socket, using api by default.
|
17
|
+
def run(api_method = :api)
|
18
|
+
orig_command = "%s %s" % [api_method, raw]
|
19
|
+
Log.debug "saying #{orig_command}"
|
20
|
+
@fs_socket.say(orig_command)
|
21
|
+
end
|
22
|
+
|
23
|
+
# This method builds the API command to send to the freeswitch event socket
|
24
|
+
def raw
|
25
|
+
orig_command = "sofia_contact #{@contact}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
register(:sofia_contact, SofiaContact)
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "fsr/app"
|
2
|
+
module FSR
|
3
|
+
module Cmd
|
4
|
+
class Status < Command
|
5
|
+
|
6
|
+
def initialize(fs_socket = nil)
|
7
|
+
@fs_socket = fs_socket # FSR::CommandSocket obj
|
8
|
+
end
|
9
|
+
|
10
|
+
# Send the command to the event socket, using bgapi by default.
|
11
|
+
def run(api_method = :api)
|
12
|
+
orig_command = "%s %s" % [api_method, raw]
|
13
|
+
Log.debug "saying #{orig_command}"
|
14
|
+
@fs_socket.say(orig_command)
|
15
|
+
end
|
16
|
+
|
17
|
+
# This method builds the API command to send to the freeswitch event socket
|
18
|
+
def raw
|
19
|
+
orig_command = "status"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
register(:status, Status)
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "fsr/event_socket"
|
2
|
+
require "fsr/cmd"
|
3
|
+
module FSR
|
4
|
+
class CommandSocket < EventSocket
|
5
|
+
include Cmd
|
6
|
+
|
7
|
+
def initialize(args = {})
|
8
|
+
@server = args[:server] || "127.0.0.1"
|
9
|
+
@port = args[:port] || "8021"
|
10
|
+
@auth = args[:auth] || "ClueCon"
|
11
|
+
@socket = TCPSocket.new(@server, @port)
|
12
|
+
super(@socket)
|
13
|
+
# Attempt to login or raise an exception
|
14
|
+
unless login
|
15
|
+
raise "Unable to login, check your password!"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Method to authenticate to FreeSWITCH
|
20
|
+
def login
|
21
|
+
#Clear buf from initial socket creation/opening
|
22
|
+
response
|
23
|
+
# Send auth string to FreeSWITCH
|
24
|
+
self << "auth #{@auth}"
|
25
|
+
#Return response, clear buf for rest of commands
|
26
|
+
response
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/fsr/database.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "fsr/database"
|
2
|
+
module FSR
|
3
|
+
module Database
|
4
|
+
module CallLimit
|
5
|
+
DB = Sequel.connect("sqlite:///" + File.join(FSR::FS_DB_PATH, "call_limit.db"))
|
6
|
+
class LimitData < Sequel::Model
|
7
|
+
end
|
8
|
+
LimitData.set_dataset :limit_data
|
9
|
+
|
10
|
+
class DbData < Sequel::Model
|
11
|
+
end
|
12
|
+
DbData.set_dataset :db_data
|
13
|
+
|
14
|
+
class GroupData < Sequel::Model
|
15
|
+
end
|
16
|
+
GroupData.set_dataset :group_data
|
17
|
+
|
18
|
+
LimitData.db, DbData.db, GroupData.db = [DB] * 3
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "fsr/database"
|
2
|
+
# This module maps to the Free Switch core.db database
|
3
|
+
#
|
4
|
+
# TODO Separate these models into their own subdirectories
|
5
|
+
module FSR
|
6
|
+
module Database
|
7
|
+
module Core
|
8
|
+
DB = Sequel.connect("sqlite://" + File.join(FSR::FS_DB_PATH, "core.db"))
|
9
|
+
|
10
|
+
class Complete < Sequel::Model
|
11
|
+
end
|
12
|
+
Complete.set_dataset :complete
|
13
|
+
|
14
|
+
class Alias < Sequel::Model
|
15
|
+
end
|
16
|
+
|
17
|
+
class Channel < Sequel::Model
|
18
|
+
end
|
19
|
+
|
20
|
+
class Call < Sequel::Model
|
21
|
+
end
|
22
|
+
|
23
|
+
class Interface < Sequel::Model
|
24
|
+
end
|
25
|
+
|
26
|
+
class Task < Sequel::Model
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module FSR
|
2
|
+
class EventSocket
|
3
|
+
attr_reader :socket
|
4
|
+
|
5
|
+
def initialize(socket)
|
6
|
+
@socket = socket
|
7
|
+
end
|
8
|
+
|
9
|
+
# Send a command and return response
|
10
|
+
def say(cmd)
|
11
|
+
@socket.send("#{cmd}\n\n",0)
|
12
|
+
response
|
13
|
+
end
|
14
|
+
|
15
|
+
# Send a command, do not return response
|
16
|
+
def <<(cmd)
|
17
|
+
@socket.send("#{cmd}\n\n",0)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Grab result from command and create a hash, simple but works.
|
21
|
+
def get_header_and_body
|
22
|
+
headers, body = {}, ""
|
23
|
+
until line = @socket.gets and line.chomp.empty?
|
24
|
+
if (kv = line.chomp.split(/:\s+/,2)).size == 2
|
25
|
+
headers.store *kv
|
26
|
+
end
|
27
|
+
end
|
28
|
+
if (content_length = headers["Content-Length"].to_i) > 0
|
29
|
+
Log.debug "content_length is #{content_length}, grabbing from socket"
|
30
|
+
body << @socket.read(content_length)
|
31
|
+
end
|
32
|
+
headers.merge("body" => body.strip)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Scrub result into a hash
|
36
|
+
def response
|
37
|
+
get_header_and_body
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
## Fake socket used for mock testing ##
|
2
|
+
#
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
module FSR
|
6
|
+
class FakeSocket
|
7
|
+
def initialize(remote_host, remote_port)
|
8
|
+
@remote_host, @remote_port = remote_host, remote_port
|
9
|
+
|
10
|
+
@input = StringIO.new('')
|
11
|
+
@buffer = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def hostname
|
15
|
+
'localhost'
|
16
|
+
end
|
17
|
+
|
18
|
+
def address
|
19
|
+
'127.0.0.1'
|
20
|
+
end
|
21
|
+
|
22
|
+
def eof?
|
23
|
+
@input.eof?
|
24
|
+
end
|
25
|
+
alias closed? eof?
|
26
|
+
|
27
|
+
def close
|
28
|
+
end
|
29
|
+
|
30
|
+
def print(*args)
|
31
|
+
@buffer << args.join
|
32
|
+
end
|
33
|
+
|
34
|
+
def read(len)
|
35
|
+
@input.read(len)
|
36
|
+
end
|
37
|
+
|
38
|
+
def fake_input
|
39
|
+
@input
|
40
|
+
end
|
41
|
+
|
42
|
+
def fake_buffer
|
43
|
+
@buffer
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
require 'bacon'
|
49
|
+
Bacon.summary_at_exit
|
50
|
+
|
51
|
+
describe FSR::FakeSocket do
|
52
|
+
it 'can be initialized' do
|
53
|
+
@socket = FSR::FakeSocket.new('google.com', 80)
|
54
|
+
@socket.should.not.be.nil
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'can be filled with input which is then read' do
|
58
|
+
@socket.fake_input.write('foobar')
|
59
|
+
@socket.fake_input.pos = 0
|
60
|
+
@socket.read(6).should == 'foobar'
|
61
|
+
@socket.read(1).should == nil
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'can receive input' do
|
65
|
+
@socket.print('foo')
|
66
|
+
@socket.fake_buffer.should == ['foo']
|
67
|
+
end
|
68
|
+
end
|
data/lib/fsr/listener.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'fsr/listener'
|
2
|
+
|
3
|
+
module FSR
|
4
|
+
module Listener
|
5
|
+
class HeaderAndContentResponse
|
6
|
+
|
7
|
+
attr_reader :headers, :content
|
8
|
+
|
9
|
+
def initialize(args = {})
|
10
|
+
@headers = args[:headers]
|
11
|
+
@content = args[:content]
|
12
|
+
end
|
13
|
+
|
14
|
+
# Keep backward compat with the other 2 people who use FSR
|
15
|
+
def body
|
16
|
+
@content
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|