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.
Files changed (60) hide show
  1. data/License.txt +20 -0
  2. data/NEWS +3 -0
  3. data/README +85 -0
  4. data/Rakefile +82 -0
  5. data/bin/cmd_demo.rb +19 -0
  6. data/bin/ies_demo.rb +19 -0
  7. data/bin/ies_demo_with_hook.rb +27 -0
  8. data/bin/oes_demo.rb +22 -0
  9. data/lib/fsr.rb +86 -0
  10. data/lib/fsr/app.rb +72 -0
  11. data/lib/fsr/app/answer.rb +21 -0
  12. data/lib/fsr/app/bridge.rb +33 -0
  13. data/lib/fsr/app/conference.rb +29 -0
  14. data/lib/fsr/app/fifo.rb +38 -0
  15. data/lib/fsr/app/fs_break.rb +21 -0
  16. data/lib/fsr/app/fs_sleep.rb +24 -0
  17. data/lib/fsr/app/hangup.rb +22 -0
  18. data/lib/fsr/app/log.rb +24 -0
  19. data/lib/fsr/app/playback.rb +23 -0
  20. data/lib/fsr/app/set.rb +22 -0
  21. data/lib/fsr/app/speak.rb +25 -0
  22. data/lib/fsr/app/transfer.rb +24 -0
  23. data/lib/fsr/cmd.rb +57 -0
  24. data/lib/fsr/cmd/fsctl.rb +41 -0
  25. data/lib/fsr/cmd/originate.rb +46 -0
  26. data/lib/fsr/cmd/sofia.rb +40 -0
  27. data/lib/fsr/cmd/sofia/profile.rb +75 -0
  28. data/lib/fsr/cmd/sofia/status.rb +39 -0
  29. data/lib/fsr/cmd/sofia_contact.rb +31 -0
  30. data/lib/fsr/cmd/status.rb +25 -0
  31. data/lib/fsr/command_socket.rb +29 -0
  32. data/lib/fsr/database.rb +7 -0
  33. data/lib/fsr/database/call_limit.rb +21 -0
  34. data/lib/fsr/database/core.rb +30 -0
  35. data/lib/fsr/database/sofia_reg_external.rb +0 -0
  36. data/lib/fsr/database/sofia_reg_internal.rb +0 -0
  37. data/lib/fsr/database/voicemail_default.rb +0 -0
  38. data/lib/fsr/event_socket.rb +41 -0
  39. data/lib/fsr/fake_socket.rb +68 -0
  40. data/lib/fsr/listener.rb +10 -0
  41. data/lib/fsr/listener/header_and_content_response.rb +21 -0
  42. data/lib/fsr/listener/inbound.rb +61 -0
  43. data/lib/fsr/listener/inbound/event.rb +42 -0
  44. data/lib/fsr/listener/outbound.rb +62 -0
  45. data/spec/fsr/app.rb +8 -0
  46. data/spec/fsr/app/bridge.rb +17 -0
  47. data/spec/fsr/app/conference.rb +12 -0
  48. data/spec/fsr/app/fifo.rb +29 -0
  49. data/spec/fsr/cmd.rb +8 -0
  50. data/spec/fsr/cmd/originate.rb +12 -0
  51. data/spec/fsr/cmd/sofia.rb +69 -0
  52. data/spec/fsr/cmd/sofia/profile.rb +58 -0
  53. data/spec/fsr/listener.rb +12 -0
  54. data/spec/fsr/listener/inbound.rb +19 -0
  55. data/spec/fsr/listener/outbound.rb +19 -0
  56. data/spec/fsr/loading.rb +27 -0
  57. data/spec/helper.rb +14 -0
  58. data/tasks/package.rake +29 -0
  59. data/tasks/spec.rake +59 -0
  60. 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
@@ -0,0 +1,7 @@
1
+ # Logic to initialize the database here
2
+ require "sequel"
3
+ module FSR
4
+ module Database
5
+ end
6
+ end
7
+
@@ -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
@@ -0,0 +1,10 @@
1
+ module FSR
2
+ module Listener
3
+
4
+ def receive_data(data)
5
+ FSR::Log.debug "Received #{data}"
6
+ end
7
+
8
+ end
9
+ end
10
+ FSL = FSR::Listener
@@ -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