jicksta-adhearsion 0.8.0 → 0.8.2

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/EVENTS CHANGED
@@ -6,6 +6,6 @@
6
6
  - manager_interface
7
7
  - before_call
8
8
  - failed_call
9
- - call_hangup
9
+ - hungup_call
10
10
 
11
- # Note: This file is valid YAML for programatic parsing.
11
+ # Note: This file is valid YAML for programatic parsing.
data/adhearsion.gemspec CHANGED
@@ -4,14 +4,16 @@ ADHEARSION_FILES = %w{
4
4
  app_generators/ahn/templates/.ahnrc
5
5
  app_generators/ahn/templates/components/ami_remote/ami_remote.rb
6
6
  app_generators/ahn/templates/components/disabled/HOW_TO_ENABLE
7
- app_generators/ahn/templates/components/disabled/stomp_gateway/config.yml
7
+ app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.yml
8
8
  app_generators/ahn/templates/components/disabled/stomp_gateway/README.markdown
9
9
  app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.rb
10
- app_generators/ahn/templates/components/restful_rpc/config.yml
11
- app_generators/ahn/templates/components/restful_rpc/example-client.rb
12
- app_generators/ahn/templates/components/restful_rpc/README.markdown
13
- app_generators/ahn/templates/components/restful_rpc/restful_rpc.rb
14
- app_generators/ahn/templates/components/restful_rpc/spec/restful_rpc_spec.rb
10
+ app_generators/ahn/templates/components/disabled/sandbox/sandbox.rb
11
+ app_generators/ahn/templates/components/disabled/sandbox/sandbox.yml
12
+ app_generators/ahn/templates/components/disabled/restful_rpc/restful_rpc.yml
13
+ app_generators/ahn/templates/components/disabled/restful_rpc/example-client.rb
14
+ app_generators/ahn/templates/components/disabled/restful_rpc/README.markdown
15
+ app_generators/ahn/templates/components/disabled/restful_rpc/restful_rpc.rb
16
+ app_generators/ahn/templates/components/disabled/restful_rpc/spec/restful_rpc_spec.rb
15
17
  app_generators/ahn/templates/components/simon_game/simon_game.rb
16
18
  app_generators/ahn/templates/config/startup.rb
17
19
  app_generators/ahn/templates/dialplan.rb
@@ -106,12 +108,11 @@ ADHEARSION_FILES = %w{
106
108
  lib/theatre/version.rb
107
109
  LICENSE
108
110
  Rakefile
109
- README.txt
110
111
  }
111
112
 
112
113
  Gem::Specification.new do |s|
113
114
  s.name = "adhearsion"
114
- s.version = "0.8.0"
115
+ s.version = "0.8.2"
115
116
 
116
117
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
117
118
  s.authors = ["Jay Phillips"]
@@ -28,15 +28,18 @@ class AhnGenerator < RubiGen::Base
28
28
  m.file *["components/ami_remote/ami_remote.rb"]*2
29
29
 
30
30
  m.file *["components/disabled/stomp_gateway/stomp_gateway.rb"]*2
31
- m.file *["components/disabled/stomp_gateway/config.yml"]*2
31
+ m.file *["components/disabled/stomp_gateway/stomp_gateway.yml"]*2
32
32
  m.file *["components/disabled/stomp_gateway/README.markdown"]*2
33
33
 
34
34
  m.file *["components/disabled/restful_rpc/restful_rpc.rb"]*2
35
- m.file *["components/disabled/restful_rpc/config.yml"]*2
35
+ m.file *["components/disabled/restful_rpc/restful_rpc.yml"]*2
36
36
  m.file *["components/disabled/restful_rpc/README.markdown"]*2
37
37
  m.file *["components/disabled/restful_rpc/example-client.rb"]*2
38
38
  m.file *["components/disabled/restful_rpc/spec/restful_rpc_spec.rb"]*2
39
39
 
40
+ m.file *["components/disabled/sandbox/sandbox.yml"]*2
41
+ m.file *["components/disabled/sandbox/sandbox.rb"]*2
42
+
40
43
  m.file *["config/startup.rb"]*2
41
44
  m.file *["dialplan.rb"]*2
42
45
  m.file *["events.rb"]*2
@@ -80,6 +83,7 @@ EOS
80
83
  BASEDIRS = %w(
81
84
  components/simon_game
82
85
  components/disabled/stomp_gateway
86
+ components/disabled/sandbox
83
87
  components/ami_remote
84
88
  components/disabled/restful_rpc/spec
85
89
  config
@@ -1,3 +1,5 @@
1
+ # This file is for the "rake" tool which automates project-related tasks. If you need to automate things, you can create
2
+ # a new Rake task here. See http://rake.rubyforge.org for more info.
1
3
  require 'rubygems'
2
4
 
3
5
  begin
@@ -0,0 +1,11 @@
1
+ Adhearsion RESTful RPC Component
2
+ ================================
3
+
4
+ This is a component for people want to integrate their telephony systems with non-Ruby systems. When enabled, this component
5
+ will start up a HTTP server within the Adhearsion process and accept POST requests to invoke Ruby methods shared in the
6
+ `methods_for(:rpc)` context.
7
+
8
+ Protocol Notes
9
+ --------------
10
+
11
+ When POSTing your data to.
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rest_client'
3
+ require 'json'
4
+
5
+ # You must have the "rest-client" and "json" gems installed for this file to work.
6
+
7
+ class RESTfulAdhearsion
8
+
9
+ DEFAULT_OPTIONS = {
10
+ # Note: :user and :password are non-existent by default
11
+ :host => "localhost",
12
+ :port => "5000",
13
+ :path_nesting => "/"
14
+ }
15
+
16
+ def initialize(options={})
17
+ @options = DEFAULT_OPTIONS.merge options
18
+
19
+ @path_nesting = @options.delete :path_nesting
20
+ @host = @options.delete :host
21
+ @port = @options.delete :port
22
+
23
+ @url_beginning = "http://#{@host}:#{@port}#{@path_nesting}"
24
+ end
25
+
26
+ def method_missing(method_name, *args)
27
+ JSON.parse RestClient::Resource.new(@url_beginning + method_name.to_s, @options).post(args.to_json)
28
+ end
29
+
30
+ end
31
+
32
+ Adhearsion = RESTfulAdhearsion.new :host => "localhost", :port => 5000, :user => "jicksta", :password => "roflcopterz"
33
+
34
+ # ### Sample component code. Try doing "ahn create component testing123" and pasting this code in.
35
+ #
36
+ # methods_for :rpc do
37
+ # def i_like_hashes(options={})
38
+ # options.has_key?(:foo)
39
+ # end
40
+ # def i_like_arrays(*args)
41
+ # args.reverse
42
+ # end
43
+ # end
44
+
45
+ # Note: everything returned will be wrapped in an Array
46
+
47
+ p Adhearsion.i_like_hashes(:foo => "bar")
48
+ p Adhearsion.i_like_arrays(1,2,3,4,5)
@@ -0,0 +1,87 @@
1
+ require 'rack'
2
+ require 'json'
3
+
4
+ # Don't you love regular expressions? Matches only 0-255 octets. Recognizes "*" as an octet wildcard.
5
+ VALID_IP_ADDRESS = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|\*)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|\*)$/
6
+
7
+ def ip_allowed?(ip)
8
+ raise ArgumentError, "#{ip.inspect} is not a valid IP address!" unless ip.kind_of?(String) && ip =~ VALID_IP_ADDRESS
9
+
10
+ octets = ip.split "."
11
+
12
+ case COMPONENTS.restful_rpc["access"]
13
+ when "everyone"
14
+ true
15
+ when "whitelist"
16
+ whitelist = COMPONENTS.restful_rpc["whitelist"]
17
+ !! whitelist.find do |pattern|
18
+ pattern_octets = pattern.split "."
19
+ # Traverse both arrays in parallel
20
+ octets.zip(pattern_octets).map do |octet, octet_pattern|
21
+ octet_pattern == "*" ? true : (octet == octet_pattern)
22
+ end == [true, true, true, true]
23
+ end
24
+ when "blacklist"
25
+ blacklist = COMPONENTS.restful_rpc["blacklist"]
26
+ ! blacklist.find do |pattern|
27
+ pattern_octets = pattern.split "."
28
+ # Traverse both arrays in parallel
29
+ octets.zip(pattern_octets).map do |octet, octet_pattern|
30
+ octet_pattern == "*" ? true : (octet == octet_pattern)
31
+ end == [true, true, true, true]
32
+ end
33
+ else
34
+ raise Adhearsion::Components::ConfigurationError, 'Unrecognized "access" configuration value!'
35
+ end
36
+ end
37
+
38
+ RESTFUL_API_HANDLER = lambda do |env|
39
+ json = env["rack.input"].read
40
+
41
+ # Return "Bad Request" HTTP error if the client forgot
42
+ return [400, {}, "You must POST a valid JSON object!"] if json.blank?
43
+
44
+ json = JSON.parse json
45
+
46
+ nesting = COMPONENTS.restful_rpc["path_nesting"]
47
+ path = env["PATH_INFO"]
48
+
49
+ return [404, {}, "This resource does not respond to #{path.inspect}"] unless path[0...nesting.size] == nesting
50
+
51
+ path = path[nesting.size..-1]
52
+
53
+ return [404, {"Content-Type" => "application/json"}, "You cannot nest method names!"] if path.include?("/")
54
+
55
+ rpc_object = Adhearsion::Components.component_manager.extend_object_with(Object.new, :rpc)
56
+
57
+ # TODO: set the content-type and other HTTP headers
58
+ response_object = Array rpc_object.send(path, *json)
59
+ [200, {"Content-Type" => "application/json"}, response_object.to_json]
60
+
61
+ end
62
+
63
+ initialization do
64
+ config = COMPONENTS.restful_rpc
65
+
66
+ api = RESTFUL_API_HANDLER
67
+
68
+ port = config["port"] || 5000
69
+ authentication = config["authentication"]
70
+ show_exceptions = config["show_exceptions"]
71
+ handler = Rack::Handler.const_get(config["handler"] || "Mongrel")
72
+
73
+ if authentication
74
+ api = Rack::Auth::Basic.new(api) do |username, password|
75
+ authentication[username] == password
76
+ end
77
+ api.realm = "Adhearsion API"
78
+ end
79
+
80
+ if show_exceptions
81
+ api = Rack::ShowStatus.new(Rack::ShowExceptions.new(api))
82
+ end
83
+
84
+ Thread.new do
85
+ handler.run api, :Port => port
86
+ end
87
+ end
@@ -0,0 +1,34 @@
1
+ # Use path_nesting to specify an arbitrarily nested. Could be used for additional security or in HTTP reverse proxy server.
2
+ path_nesting: /
3
+
4
+ port: 5000
5
+
6
+ # The "handler" option here can be any valid Rack::Handler constant name.
7
+ # Other options: WEBrick, EventedMongrel.
8
+ # If you don't know the differences between these, "Mongrel" is definitely a good choice.
9
+ handler: Mongrel
10
+
11
+ # In a production system, you should make this "false" since
12
+ show_exceptions: true
13
+
14
+ # The "authentication" config option can either be "false" or key/value pairs representing allowed usernames and passwords.
15
+
16
+ #authentication: false
17
+ authentication:
18
+ jicksta: roflcopterz
19
+ foo: bar6213671682
20
+
21
+ access: everyone # When allowing "everyone" access, no IPs are blocked.
22
+ #access: whitelist # When using a whitelist, the "whitelist" data below will be used.
23
+ #access: blacklist # When using a blacklist, the "blacklist" data below will be used.
24
+
25
+ # This is a list of IPs which are exclusively allowed to call this web service.
26
+ # Note: whitelists are far more secure than blacklists.
27
+ whitelist:
28
+ - 127.0.0.1
29
+ - 192.168.*.*
30
+
31
+ # This is a list of the IPs which are explicitly NOT allowed to call this web service. This will only be used if "access" is
32
+ # set to "blacklist" above.
33
+ blacklist:
34
+ - 100.200.100.200
@@ -0,0 +1,263 @@
1
+ unless defined? Adhearsion
2
+ if File.exists? File.dirname(__FILE__) + "/../../../adhearsion/lib/adhearsion.rb"
3
+ # If you wish to freeze a copy of Adhearsion to this app, simply place a copy of Adhearsion
4
+ # into a folder named "adhearsion" within this app's main directory.
5
+ require File.dirname(__FILE__) + "/../../../adhearsion/lib/adhearsion.rb"
6
+ elsif File.exists? File.dirname(__FILE__) + "/../../../../../../lib/adhearsion.rb"
7
+ # This file may be ran from the within the Adhearsion framework code (before a project has been generated)
8
+ require File.dirname(__FILE__) + "/../../../../../../lib/adhearsion.rb"
9
+ else
10
+ require 'rubygems'
11
+ gem 'adhearsion', '>= 0.7.999'
12
+ require 'adhearsion'
13
+ end
14
+ end
15
+
16
+ require 'adhearsion/component_manager/spec_framework'
17
+
18
+ RESTFUL_RPC = ComponentTester.new("restful_rpc", File.dirname(__FILE__) + "/../..")
19
+
20
+ ##### This is here for a reference
21
+ #{"CONTENT_LENGTH" => "12",
22
+ # "CONTENT_TYPE" => "application/x-www-form-urlencoded",
23
+ # "GATEWAY_INTERFACE" => "CGI/1.1",
24
+ # "HTTP_ACCEPT" => "application/xml",
25
+ # "HTTP_ACCEPT_ENCODING" => "gzip, deflate",
26
+ # "HTTP_AUTHORIZATION" => "Basic amlja3N0YTpyb2ZsY29wdGVyeg==",
27
+ # "HTTP_HOST" => "localhost:5000",
28
+ # "HTTP_VERSION" => "HTTP/1.1",
29
+ # "PATH_INFO" => "/rofl",
30
+ # "QUERY_STRING" => "",
31
+ # "rack.errors" => StringIO.new(""),
32
+ # "rack.input" => StringIO.new('["o","hai!"]'),
33
+ # "rack.multiprocess" => false,
34
+ # "rack.multithread" => true,
35
+ # "rack.run_once" => false,
36
+ # "rack.url_scheme" => "http",
37
+ # "rack.version" => [0, 1],
38
+ # "REMOTE_ADDR" => "::1",
39
+ # "REMOTE_HOST" => "localhost",
40
+ # "REMOTE_USER" => "jicksta",
41
+ # "REQUEST_METHOD" => "POST"
42
+ # "REQUEST_PATH" => "/",
43
+ # "REQUEST_URI" => "http://localhost:5000/rofl",
44
+ # "SCRIPT_NAME" => "",
45
+ # "SERVER_NAME" => "localhost",
46
+ # "SERVER_PORT" => "5000",
47
+ # "SERVER_PROTOCOL" => "HTTP/1.1",
48
+ # "SERVER_SOFTWARE" => "WEBrick/1.3.1 (Ruby/1.8.6/2008-03-03)"}
49
+
50
+
51
+
52
+ describe "The VALID_IP_ADDRESS regular expression" do
53
+
54
+ it "should match only valid IP addresses" do
55
+ valid_ip_addresses = ["192.168.1.98", "10.0.1.200", "255.255.255.0", "123.*.4.*"]
56
+ invalid_ip_addresses = ["10.0.1.1 foo", "bar 255.255.255.0", "0*0*0*0", "1234"]
57
+
58
+ valid_ip_addresses. each { |ip| RESTFUL_RPC::VALID_IP_ADDRESS.should =~ ip }
59
+ invalid_ip_addresses.each { |ip| RESTFUL_RPC::VALID_IP_ADDRESS.should_not =~ ip }
60
+ end
61
+ end
62
+
63
+ describe "The initialization block" do
64
+
65
+ it "should create a new Thread" do
66
+ mock_component_config_with :restful_rpc => {}
67
+ mock(Thread).new { nil }
68
+ RESTFUL_RPC.initialize!
69
+ end
70
+
71
+ it "should run the Rack adapter specified in the configuration" do
72
+ mock(Thread).new.yields
73
+ mock_component_config_with :restful_rpc => {"adapter" => "Mongrel"}
74
+ mock(Rack::Handler::Mongrel).run is_a(Proc), :Port => 5000
75
+ RESTFUL_RPC.initialize!
76
+ end
77
+
78
+ it "should wrap the RESTFUL_API_HANDLER in an Rack::Auth::Basic object if authentication is enabled" do
79
+ mock(Thread).new.yields
80
+ mock_component_config_with :restful_rpc => {"authentication" => {"foo" => "bar"}}
81
+
82
+ proper_authenticator = lambda do |obj|
83
+ request = OpenStruct.new :credentials => ["foo", "bar"]
84
+ obj.is_a?(Rack::Auth::Basic) && obj.send(:valid?, request)
85
+ end
86
+
87
+ mock(Rack::Handler::Mongrel).run(satisfy(&proper_authenticator), :Port => 5000)
88
+ RESTFUL_RPC.initialize!
89
+ end
90
+
91
+ it 'should wrap the RESTFUL_API_HANDLER in ShowStatus and ShowExceptions objects when show_exceptions is enabled' do
92
+ mock(Thread).new.yields
93
+ mock_component_config_with :restful_rpc => {"show_exceptions" => true}
94
+
95
+ mock.proxy(Rack::ShowExceptions).new(is_a(Proc))
96
+ mock.proxy(Rack::ShowStatus).new is_a(Rack::ShowExceptions)
97
+
98
+ mock(Rack::Handler::Mongrel).run is_a(Rack::ShowStatus), :Port => 5000
99
+ RESTFUL_RPC.initialize!
100
+ end
101
+
102
+ end
103
+
104
+ describe 'Private helper methods' do
105
+
106
+ describe "the RESTFUL_API_HANDLER lambda" do
107
+
108
+ it "should return a 200 for requests which execute a method that has been defined in the methods_for(:rpc) context" do
109
+ component_manager = Adhearsion::Components::ComponentManager.new('/path/shouldnt/matter')
110
+
111
+ mock(Adhearsion::Components).component_manager { component_manager }
112
+ component_manager.load_code <<-RUBY
113
+ methods_for(:rpc) do
114
+ def testing_123456(one,two)
115
+ [two.reverse, one.reverse]
116
+ end
117
+ end
118
+ RUBY
119
+
120
+ input = StringIO.new %w[jay phillips].to_json
121
+
122
+ mock_component_config_with :restful_rpc => {"path_nesting" => "/"}
123
+
124
+ env = {"PATH_INFO" => "/testing_123456", "rack.input" => input}
125
+
126
+ response = RESTFUL_RPC::RESTFUL_API_HANDLER.call(env)
127
+ response.should be_kind_of(Array)
128
+ response.should have(3).items
129
+ response.first.should equal(200)
130
+ JSON.parse(response.last).should eql(%w[jay phillips].map(&:reverse).reverse)
131
+ end
132
+
133
+ it "should return a 400 when no data is POSTed" do
134
+ env = {"rack.input" => StringIO.new(""), "REQUEST_URI" => "/foobar"}
135
+ RESTFUL_RPC::RESTFUL_API_HANDLER.call(env).first.should equal(400)
136
+ end
137
+
138
+ it "should work with a high level test of a successful method invocation" do
139
+
140
+ component_manager = Adhearsion::Components::ComponentManager.new('/path/shouldnt/matter')
141
+
142
+ mock(Adhearsion::Components).component_manager { component_manager }
143
+
144
+ component_manager.load_code '
145
+ methods_for(:rpc) do
146
+ def rofl(one,two)
147
+ "Hai! #{one} #{two}"
148
+ end
149
+ end'
150
+
151
+ env = {
152
+ "CONTENT_LENGTH" => "12",
153
+ "CONTENT_TYPE" => "application/x-www-form-urlencoded",
154
+ "GATEWAY_INTERFACE" => "CGI/1.1",
155
+ "HTTP_ACCEPT" => "application/xml",
156
+ "HTTP_ACCEPT_ENCODING" => "gzip, deflate",
157
+ "HTTP_AUTHORIZATION" => "Basic amlja3N0YTpyb2ZsY29wdGVyeg==",
158
+ "HTTP_HOST" => "localhost:5000",
159
+ "HTTP_VERSION" => "HTTP/1.1",
160
+ "PATH_INFO" => "/rofl",
161
+ "QUERY_STRING" => "",
162
+ "rack.errors" => StringIO.new(""),
163
+ "rack.input" => StringIO.new('["o","hai!"]'),
164
+ "rack.multiprocess" => false,
165
+ "rack.multithread" => true,
166
+ "rack.run_once" => false,
167
+ "rack.url_scheme" => "http",
168
+ "rack.version" => [0, 1],
169
+ "REMOTE_ADDR" => "::1",
170
+ "REMOTE_HOST" => "localhost",
171
+ "REMOTE_USER" => "jicksta",
172
+ "REQUEST_METHOD" => "POST",
173
+ "REQUEST_PATH" => "/",
174
+ "REQUEST_URI" => "http://localhost:5000/rofl",
175
+ "SCRIPT_NAME" => "",
176
+ "SERVER_NAME" => "localhost",
177
+ "SERVER_PORT" => "5000",
178
+ "SERVER_PROTOCOL" => "HTTP/1.1",
179
+ "SERVER_SOFTWARE" => "WEBrick/1.3.1 (Ruby/1.8.6/2008-03-03)" }
180
+
181
+ response = RESTFUL_RPC::RESTFUL_API_HANDLER.call(env)
182
+ JSON.parse(response.last).should == ["Hai! o hai!"]
183
+
184
+ end
185
+
186
+ it "should contain backtrace information when show_errors is enabled and an exception occurs" do
187
+ mock_component_config_with :restful_api => {"show_errors" => true}
188
+ pending
189
+ end
190
+
191
+ end
192
+
193
+ describe 'the ip_allowed?() method' do
194
+
195
+ before :each do
196
+ @method = RESTFUL_RPC.helper_method :ip_allowed?
197
+ end
198
+
199
+ it 'should raise a ConfigurationError if "access" is not one of "everyone", "whitelist" or "blacklist"' do
200
+ good_access_values = %w[everyone whitelist blacklist]
201
+ bad_access_values = %w[foo bar qaz qwerty everone blaclist whitlist]
202
+
203
+ good_access_values.each do |access_value|
204
+ mock_component_config_with :restful_rpc => {"access" => access_value, "whitelist" => [], "blacklist" => []}
205
+ lambda { @method.call("10.0.0.1") }.should_not raise_error
206
+ end
207
+
208
+ bad_access_values.each do |access_value|
209
+ mock_component_config_with :restful_rpc => {"access" => access_value, "authentication" => false}
210
+ lambda { @method.call("10.0.0.1") }.should raise_error(Adhearsion::Components::ConfigurationError)
211
+ end
212
+ end
213
+
214
+ describe 'whitelists' do
215
+
216
+ it "should parse *'s as wildcards" do
217
+ mock_component_config_with :restful_rpc => {"access" => "whitelist", "whitelist" => ["10.*.*.*"]}
218
+ @method.call("10.1.2.3").should be_true
219
+ end
220
+
221
+ it "should allow IPs which are explictly specified" do
222
+ mock_component_config_with :restful_rpc => {"access" => "whitelist", "whitelist" => ["4.3.2.1"]}
223
+ @method.call("4.3.2.1").should be_true
224
+ end
225
+
226
+ it "should not allow IPs which are not explicitly specified" do
227
+ mock_component_config_with :restful_rpc => {"access" => "whitelist", "whitelist" => %w[ 1.2.3.4 4.3.2.1]}
228
+ @method.call("2.2.2.2").should be_false
229
+ end
230
+
231
+ end
232
+
233
+ describe 'blacklists' do
234
+
235
+ it "should parse *'s as wildcards" do
236
+ mock_component_config_with :restful_rpc => {"access" => "blacklist", "blacklist" => ["10.*.*.*"]}
237
+ @method.call("10.1.2.3").should be_false
238
+ end
239
+
240
+ it "should not allow IPs which are explicitly specified" do
241
+ mock_component_config_with :restful_rpc => {"access" => "blacklist", "blacklist" => ["9.8.7.6", "9.8.7.5"]}
242
+ @method.call("9.8.7.5").should be_false
243
+ end
244
+
245
+ it "should allow IPs which are not explicitly specified" do
246
+ mock_component_config_with :restful_rpc => {"access" => "blacklist", "blacklist" => ["10.20.30.40"]}
247
+ @method.call("1.1.1.1").should be_true
248
+ end
249
+
250
+ end
251
+
252
+ describe '"everyone" access' do
253
+ it "should return true for any IP given, irrespective of the configuration" do
254
+ ip_addresses = %w[100.200.100.200 0.0.0.0 *.0.0.*]
255
+ ip_addresses.each do |address|
256
+ RESTFUL_RPC.helper_method(:ip_allowed?).call(address).should equal(true)
257
+ end
258
+ end
259
+ end
260
+
261
+ end
262
+
263
+ end
@@ -0,0 +1,108 @@
1
+ require 'md5'
2
+ require 'open-uri'
3
+
4
+ SANDBOX_VERSION = 1.0
5
+
6
+ initialization do
7
+ # We shouldn't start initializing until after the AGI server has initialized.
8
+ Events.register_callback(:after_initialized) do
9
+ ahn_log.sandbox "Fetching sandbox connection information"
10
+
11
+ config = if COMPONENTS.sandbox.has_key? "connect_to"
12
+ {"connect_to" => COMPONENTS.sandbox["connect_to"]}
13
+ else
14
+ begin
15
+ yaml_data = open("http://sandbox.adhearsion.com/component/#{SANDBOX_VERSION}").read
16
+ YAML.load yaml_data
17
+ rescue SocketError
18
+ ahn_log.sandbox.error "Could not connect to the sandbox server! Skipping sandbox initialization!"
19
+ next
20
+ rescue => e
21
+ ahn_log.sandbox.error "COULD NOT RETRIEVE SANDBOX CONNECTION INFORMATION! Not initializing sandbox component!"
22
+ next
23
+ end
24
+ end
25
+
26
+ begin
27
+ # The "connect_to" key is what this version supports
28
+ if config.kind_of?(Hash) && config.has_key?("connect_to")
29
+ config = config['connect_to']
30
+
31
+ host, port = config.values_at "host", "port"
32
+
33
+ ahn_log.sandbox "Connecting to #{host}:#{port}"
34
+
35
+ username, password = COMPONENTS.sandbox["username"], COMPONENTS.sandbox["password"]
36
+
37
+ if username.blank? || password.blank? || username == "user123"
38
+ ahn_log.sandbox.error "You must specify your username and password in this component's config file!"
39
+ next
40
+ end
41
+
42
+ # Part of the AGI-superset protocol we use to log in.
43
+ identifying_hash = MD5.md5(username + ":" + password).to_s
44
+
45
+ if host.nil? || port.nil?
46
+ ahn_log.sandbox.error "Invalid YAML returned from server! Skipping sandbox initialization!"
47
+ next
48
+ end
49
+
50
+ Thread.new do
51
+ loop do
52
+ begin
53
+ ahn_log.sandbox.debug "Establishing outbound AGI socket"
54
+ socket = TCPSocket.new(host, port)
55
+ socket.puts identifying_hash
56
+ response = socket.gets
57
+ unless response
58
+ ahn_log.sandbox "Communication with the sandbox ended before receiving any response. Reconnecting."
59
+ next
60
+ end
61
+ response.chomp!
62
+ case
63
+ when "authentication accepted"
64
+ ahn_log.sandbox "Authentication accepted"
65
+
66
+ start_signal = socket.gets
67
+ next unless start_signal
68
+ start_signal.chomp!
69
+
70
+ if start_signal
71
+ ahn_log.sandbox "Incoming call from remote sandbox server!"
72
+ begin
73
+ Adhearsion::Initializer::AsteriskInitializer.agi_server.server.serve(socket)
74
+ rescue => e
75
+ ahn_log.error "Non-fatal exception in the AGI server: #{e.inspect} \n" + e.backtrace.join("\n")
76
+ ensure
77
+ socket.close
78
+ end
79
+ ahn_log.sandbox "AGI server finished serving call. Reconnecting to sandbox."
80
+ else
81
+ ahn_log.sandbox "Remote Asterisk server received no call. Reconnecting..."
82
+ end
83
+ when "authentication failed"
84
+ ahn_log.sandbox.error "Your username or password is invalid! Skipping sandbox initialization..."
85
+ break
86
+ when /^wait (\d+)$/
87
+ sleep response[/^wait (\d+)$/,1].to_i
88
+ else
89
+ ahn_log.sandbox.error "Invalid login acknowledgement! Skipping sandbox initialization!"
90
+ break
91
+ end
92
+ rescue Errno::ECONNREFUSED
93
+ ahn_log.sandbox.error "Could not connect to the sandbox server! Sandbox component stopping..."
94
+ break
95
+ rescue => e
96
+ ahn_log.error "Unrecognized error: #{e.inspect} \n" + e.backtrace.join("\n")
97
+ end
98
+ end
99
+ end
100
+
101
+ else
102
+ ahn_log.sandbox.error "COULD NOT RETRIEVE SANDBOX CONNECTION INFORMATION! Not initializing sandbox component!"
103
+ end
104
+ rescue => e
105
+ ahn_log.sandbox.error "Encountered an error when connecting to the sandbox! #{e.message}\n" + e.backtrace.join("\n")
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,2 @@
1
+ username: user123
2
+ password: pass123
@@ -35,7 +35,7 @@ Setting up a Ruby Stomp server
35
35
 
36
36
  Install the pure-Ruby Stomp server by doing "gem install stompserver". This will add the "stompserver" command to your system. When running it without any parameters, it starts without requiring authentication. If you're wanting to get a quick experiment running, I recommend simply doing that.
37
37
 
38
- Open the config.yml file in the stomp_gateway component folder. Comment out the four settings at the top of the file named "user", "pass", "host" and "port" by prepending a "#" to their line. This will cause the component to choose defaults for those properties. The component's defaults will match the expected credentials for the experimental stompserver you're already running on your computer.
38
+ Open the `stomp_gateway.yml` file in the stomp_gateway component folder. Comment out the four settings at the top of the file named "user", "pass", "host" and "port" by prepending a "#" to their line. This will cause the component to choose defaults for those properties. The component's defaults will match the expected credentials for the experimental stompserver you're already running on your computer.
39
39
 
40
40
  You also need specify a subscription name in
41
41
 
@@ -9,4 +9,4 @@ port: 61613
9
9
 
10
10
  # subscriptions:
11
11
  # - start_call
12
- # - hangup_call
12
+ # - hungup_call
@@ -25,7 +25,7 @@
25
25
  # - events.shutdown
26
26
  # - events.asterisk.before_call
27
27
  # - events.asterisk.failed_call
28
- # - events.asterisk.call_hangup
28
+ # - events.asterisk.hungup_call
29
29
  #
30
30
  #
31
31
  # Note: events are mostly for components to register and expose to you.
@@ -115,7 +115,7 @@ USAGE
115
115
  # look at the examples in newly-created projects.
116
116
  RUBY
117
117
  end
118
- File.open(new_component_dir + "/config.yml","w") do |file|
118
+ File.open(new_component_dir + "/#{component_name}.yml","w") do |file|
119
119
  file.puts '# You can use this file for component-specific configuration.'
120
120
  end
121
121
  puts "Created blank component '#{component_name}' at components/#{component_name}"
@@ -18,7 +18,6 @@ module Adhearsion
18
18
  end
19
19
 
20
20
  SCOPE_NAMES = [:dialplan, :events, :generators, :rpc, :global]
21
- DEFAULT_CONFIG_NAME = "config.yml"
22
21
 
23
22
  attr_reader :scopes, :lazy_config_loader
24
23
  def initialize(path_to_container_directory)
@@ -61,7 +60,7 @@ module Adhearsion
61
60
  #
62
61
  def configuration_for_component_named(component_name)
63
62
  component_dir = File.join(@path_to_container_directory, component_name)
64
- config_file = File.join component_dir, DEFAULT_CONFIG_NAME
63
+ config_file = File.join component_dir, "#{component_name}.yml"
65
64
  if File.exists?(config_file)
66
65
  YAML.load_file config_file
67
66
  else
@@ -11,7 +11,8 @@ module Adhearsion
11
11
  require_dependencies
12
12
  require_models
13
13
  @@config = Adhearsion::AHN_CONFIG.database
14
- ActiveRecord::Base.allow_concurrency = true
14
+ # You may need to uncomment the following line for older versions of ActiveRecord
15
+ # ActiveRecord::Base.allow_concurrency = true
15
16
  establish_connection
16
17
  create_call_hook_for_connection_cleanup
17
18
  end
@@ -17,7 +17,8 @@ module Adhearsion
17
17
  "It's not a directory!" unless File.directory?(rails_root)
18
18
  load_rails
19
19
  if defined? ActiveRecord
20
- ActiveRecord::Base.allow_concurrency = true
20
+ # You may need to uncomment the following line for older versions of ActiveRecord
21
+ # ActiveRecord::Base.allow_concurrency = true
21
22
  Events.register_callback([:asterisk, :before_call]) do
22
23
  ActiveRecord::Base.verify_active_connections!
23
24
  end
@@ -2,7 +2,7 @@ module Adhearsion #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0 unless defined? MAJOR
4
4
  MINOR = 8 unless defined? MINOR
5
- TINY = 0 unless defined? TINY
5
+ TINY = 2 unless defined? TINY
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.') unless defined? STRING
8
8
  end
@@ -34,8 +34,8 @@ module Adhearsion
34
34
  end
35
35
  rescue HungupExtensionCallException => hungup_call
36
36
  begin
37
- ahn_log.agi "Received \"h\" meta-call. Executing /asterisk/call_hangup event callbacks."
38
- Events.trigger [:asterisk, :call_hangup], hungup_call.call
37
+ ahn_log.agi "Received \"h\" meta-call. Executing /asterisk/hungup_call event callbacks."
38
+ Events.trigger [:asterisk, :hungup_call], hungup_call.call
39
39
  call.hangup!
40
40
  rescue => e
41
41
  ahn_log.agi.error e
@@ -78,4 +78,4 @@ module Adhearsion
78
78
  end
79
79
  end
80
80
  end
81
- end
81
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jicksta-adhearsion
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jay Phillips
@@ -46,14 +46,16 @@ files:
46
46
  - app_generators/ahn/templates/.ahnrc
47
47
  - app_generators/ahn/templates/components/ami_remote/ami_remote.rb
48
48
  - app_generators/ahn/templates/components/disabled/HOW_TO_ENABLE
49
- - app_generators/ahn/templates/components/disabled/stomp_gateway/config.yml
49
+ - app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.yml
50
50
  - app_generators/ahn/templates/components/disabled/stomp_gateway/README.markdown
51
51
  - app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.rb
52
- - app_generators/ahn/templates/components/restful_rpc/config.yml
53
- - app_generators/ahn/templates/components/restful_rpc/example-client.rb
54
- - app_generators/ahn/templates/components/restful_rpc/README.markdown
55
- - app_generators/ahn/templates/components/restful_rpc/restful_rpc.rb
56
- - app_generators/ahn/templates/components/restful_rpc/spec/restful_rpc_spec.rb
52
+ - app_generators/ahn/templates/components/disabled/sandbox/sandbox.rb
53
+ - app_generators/ahn/templates/components/disabled/sandbox/sandbox.yml
54
+ - app_generators/ahn/templates/components/disabled/restful_rpc/restful_rpc.yml
55
+ - app_generators/ahn/templates/components/disabled/restful_rpc/example-client.rb
56
+ - app_generators/ahn/templates/components/disabled/restful_rpc/README.markdown
57
+ - app_generators/ahn/templates/components/disabled/restful_rpc/restful_rpc.rb
58
+ - app_generators/ahn/templates/components/disabled/restful_rpc/spec/restful_rpc_spec.rb
57
59
  - app_generators/ahn/templates/components/simon_game/simon_game.rb
58
60
  - app_generators/ahn/templates/config/startup.rb
59
61
  - app_generators/ahn/templates/dialplan.rb
@@ -148,7 +150,6 @@ files:
148
150
  - lib/theatre/version.rb
149
151
  - LICENSE
150
152
  - Rakefile
151
- - README.txt
152
153
  has_rdoc: false
153
154
  homepage: http://adhearsion.com
154
155
  post_install_message:
data/README.txt DELETED
@@ -1,5 +0,0 @@
1
- Adhearsion
2
- by Jay Phillips, et. al
3
- http://adhearsion.com
4
-
5
- Adhearsion is an open-source framework written in Ruby designed to vastly improve Voice over IP development. It rests atop established telephony platforms (such as Asterisk and Freeswitch) and abstracts their complexity with the beauty of a well-designed, high-level suite of solutions.