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 (64) 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/bin/oes_demo2.rb +26 -0
  10. data/lib/fsr.rb +86 -0
  11. data/lib/fsr/app.rb +72 -0
  12. data/lib/fsr/app/answer.rb +21 -0
  13. data/lib/fsr/app/bridge.rb +33 -0
  14. data/lib/fsr/app/conference.rb +29 -0
  15. data/lib/fsr/app/fifo.rb +38 -0
  16. data/lib/fsr/app/fs_break.rb +21 -0
  17. data/lib/fsr/app/fs_sleep.rb +24 -0
  18. data/lib/fsr/app/hangup.rb +22 -0
  19. data/lib/fsr/app/log.rb +24 -0
  20. data/lib/fsr/app/playback.rb +23 -0
  21. data/lib/fsr/app/set.rb +22 -0
  22. data/lib/fsr/app/speak.rb +25 -0
  23. data/lib/fsr/app/transfer.rb +24 -0
  24. data/lib/fsr/cmd.rb +57 -0
  25. data/lib/fsr/cmd/fsctl.rb +41 -0
  26. data/lib/fsr/cmd/originate.rb +46 -0
  27. data/lib/fsr/cmd/sofia.rb +40 -0
  28. data/lib/fsr/cmd/sofia/profile.rb +75 -0
  29. data/lib/fsr/cmd/sofia/status.rb +39 -0
  30. data/lib/fsr/cmd/sofia_contact.rb +31 -0
  31. data/lib/fsr/cmd/status.rb +25 -0
  32. data/lib/fsr/command_socket.rb +29 -0
  33. data/lib/fsr/database.rb +7 -0
  34. data/lib/fsr/database/call_limit.rb +21 -0
  35. data/lib/fsr/database/core.rb +30 -0
  36. data/lib/fsr/database/sofia_reg_external.rb +0 -0
  37. data/lib/fsr/database/sofia_reg_internal.rb +0 -0
  38. data/lib/fsr/database/voicemail_default.rb +0 -0
  39. data/lib/fsr/event_socket.rb +41 -0
  40. data/lib/fsr/fake_socket.rb +68 -0
  41. data/lib/fsr/listener.rb +10 -0
  42. data/lib/fsr/listener/header_and_content_response.rb +21 -0
  43. data/lib/fsr/listener/inbound.rb +61 -0
  44. data/lib/fsr/listener/inbound/event.rb +42 -0
  45. data/lib/fsr/listener/outbound.rb +62 -0
  46. data/lib/fsr/listener/outbound.rb.orig +131 -0
  47. data/spec/fsr/app.rb +8 -0
  48. data/spec/fsr/app/bridge.rb +17 -0
  49. data/spec/fsr/app/conference.rb +12 -0
  50. data/spec/fsr/app/fifo.rb +29 -0
  51. data/spec/fsr/cmd.rb +8 -0
  52. data/spec/fsr/cmd/originate.rb +12 -0
  53. data/spec/fsr/cmd/sofia.rb +69 -0
  54. data/spec/fsr/cmd/sofia/profile.rb +58 -0
  55. data/spec/fsr/listener.rb +12 -0
  56. data/spec/fsr/listener/inbound.rb +19 -0
  57. data/spec/fsr/listener/outbound.rb +19 -0
  58. data/spec/fsr/loading.rb +27 -0
  59. data/spec/helper.rb +14 -0
  60. data/tasks/package.rake +29 -0
  61. data/tasks/ride.rake +6 -0
  62. data/tasks/rspec.rake +21 -0
  63. data/tasks/spec.rake +59 -0
  64. 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,8 @@
1
+ require "spec/helper"
2
+ describe "Basic FSR::App module" do
3
+ it "Aliases itself as FSA" do
4
+ require "fsr/app"
5
+ FSA.should == FSR::App
6
+ end
7
+ end
8
+
@@ -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,8 @@
1
+ require "spec/helper"
2
+ describe "Basic FSR::Cmd module" do
3
+ it "Aliases itself as FSC" do
4
+ require "fsr/cmd"
5
+ FSC.should == FSR::Cmd
6
+ end
7
+ end
8
+
@@ -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