FreeSWITCHeR 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- 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 +224 -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
|