jicksta-adhearsion 0.7.999

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 (107) hide show
  1. data/CHANGELOG +6 -0
  2. data/EVENTS +11 -0
  3. data/LICENSE +456 -0
  4. data/README.txt +5 -0
  5. data/Rakefile +120 -0
  6. data/adhearsion.gemspec +146 -0
  7. data/app_generators/ahn/USAGE +5 -0
  8. data/app_generators/ahn/ahn_generator.rb +87 -0
  9. data/app_generators/ahn/templates/.ahnrc +34 -0
  10. data/app_generators/ahn/templates/README +8 -0
  11. data/app_generators/ahn/templates/Rakefile +23 -0
  12. data/app_generators/ahn/templates/components/ami_remote/ami_remote.rb +15 -0
  13. data/app_generators/ahn/templates/components/disabled/HOW_TO_ENABLE +7 -0
  14. data/app_generators/ahn/templates/components/disabled/stomp_gateway/README.markdown +47 -0
  15. data/app_generators/ahn/templates/components/disabled/stomp_gateway/config.yml +12 -0
  16. data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.rb +34 -0
  17. data/app_generators/ahn/templates/components/restful_rpc/README.markdown +11 -0
  18. data/app_generators/ahn/templates/components/restful_rpc/config.yml +34 -0
  19. data/app_generators/ahn/templates/components/restful_rpc/example-client.rb +48 -0
  20. data/app_generators/ahn/templates/components/restful_rpc/restful_rpc.rb +87 -0
  21. data/app_generators/ahn/templates/components/simon_game/simon_game.rb +56 -0
  22. data/app_generators/ahn/templates/config/startup.rb +53 -0
  23. data/app_generators/ahn/templates/dialplan.rb +3 -0
  24. data/app_generators/ahn/templates/events.rb +32 -0
  25. data/bin/ahn +28 -0
  26. data/bin/ahnctl +68 -0
  27. data/bin/jahn +42 -0
  28. data/examples/asterisk_manager_interface/standalone.rb +51 -0
  29. data/lib/adhearsion/cli.rb +223 -0
  30. data/lib/adhearsion/component_manager/spec_framework.rb +24 -0
  31. data/lib/adhearsion/component_manager.rb +208 -0
  32. data/lib/adhearsion/events_support.rb +84 -0
  33. data/lib/adhearsion/foundation/all.rb +9 -0
  34. data/lib/adhearsion/foundation/blank_slate.rb +5 -0
  35. data/lib/adhearsion/foundation/custom_daemonizer.rb +45 -0
  36. data/lib/adhearsion/foundation/event_socket.rb +203 -0
  37. data/lib/adhearsion/foundation/future_resource.rb +36 -0
  38. data/lib/adhearsion/foundation/global.rb +1 -0
  39. data/lib/adhearsion/foundation/metaprogramming.rb +17 -0
  40. data/lib/adhearsion/foundation/numeric.rb +13 -0
  41. data/lib/adhearsion/foundation/pseudo_guid.rb +10 -0
  42. data/lib/adhearsion/foundation/relationship_properties.rb +42 -0
  43. data/lib/adhearsion/foundation/string.rb +26 -0
  44. data/lib/adhearsion/foundation/synchronized_hash.rb +96 -0
  45. data/lib/adhearsion/foundation/thread_safety.rb +7 -0
  46. data/lib/adhearsion/host_definitions.rb +67 -0
  47. data/lib/adhearsion/initializer/asterisk.rb +81 -0
  48. data/lib/adhearsion/initializer/configuration.rb +254 -0
  49. data/lib/adhearsion/initializer/database.rb +49 -0
  50. data/lib/adhearsion/initializer/drb.rb +31 -0
  51. data/lib/adhearsion/initializer/freeswitch.rb +22 -0
  52. data/lib/adhearsion/initializer/rails.rb +40 -0
  53. data/lib/adhearsion/initializer.rb +373 -0
  54. data/lib/adhearsion/logging.rb +92 -0
  55. data/lib/adhearsion/tasks/database.rb +5 -0
  56. data/lib/adhearsion/tasks/deprecations.rb +59 -0
  57. data/lib/adhearsion/tasks/generating.rb +20 -0
  58. data/lib/adhearsion/tasks/lint.rb +4 -0
  59. data/lib/adhearsion/tasks/testing.rb +37 -0
  60. data/lib/adhearsion/tasks.rb +16 -0
  61. data/lib/adhearsion/version.rb +9 -0
  62. data/lib/adhearsion/voip/asterisk/agi_server.rb +81 -0
  63. data/lib/adhearsion/voip/asterisk/commands.rb +1284 -0
  64. data/lib/adhearsion/voip/asterisk/config_generators/agents.conf.rb +140 -0
  65. data/lib/adhearsion/voip/asterisk/config_generators/config_generator.rb +101 -0
  66. data/lib/adhearsion/voip/asterisk/config_generators/queues.conf.rb +250 -0
  67. data/lib/adhearsion/voip/asterisk/config_generators/voicemail.conf.rb +240 -0
  68. data/lib/adhearsion/voip/asterisk/config_manager.rb +71 -0
  69. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +1754 -0
  70. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +286 -0
  71. data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +78 -0
  72. data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +87 -0
  73. data/lib/adhearsion/voip/asterisk/manager_interface.rb +562 -0
  74. data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +80 -0
  75. data/lib/adhearsion/voip/asterisk/super_manager.rb +19 -0
  76. data/lib/adhearsion/voip/asterisk.rb +4 -0
  77. data/lib/adhearsion/voip/call.rb +440 -0
  78. data/lib/adhearsion/voip/call_routing.rb +64 -0
  79. data/lib/adhearsion/voip/commands.rb +9 -0
  80. data/lib/adhearsion/voip/constants.rb +39 -0
  81. data/lib/adhearsion/voip/conveniences.rb +18 -0
  82. data/lib/adhearsion/voip/dial_plan.rb +218 -0
  83. data/lib/adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches.rb +37 -0
  84. data/lib/adhearsion/voip/dsl/dialing_dsl.rb +151 -0
  85. data/lib/adhearsion/voip/dsl/dialplan/control_passing_exception.rb +27 -0
  86. data/lib/adhearsion/voip/dsl/dialplan/dispatcher.rb +124 -0
  87. data/lib/adhearsion/voip/dsl/dialplan/parser.rb +71 -0
  88. data/lib/adhearsion/voip/dsl/dialplan/thread_mixin.rb +16 -0
  89. data/lib/adhearsion/voip/dsl/numerical_string.rb +117 -0
  90. data/lib/adhearsion/voip/freeswitch/basic_connection_manager.rb +48 -0
  91. data/lib/adhearsion/voip/freeswitch/event_handler.rb +58 -0
  92. data/lib/adhearsion/voip/freeswitch/freeswitch_dialplan_command_factory.rb +129 -0
  93. data/lib/adhearsion/voip/freeswitch/inbound_connection_manager.rb +38 -0
  94. data/lib/adhearsion/voip/freeswitch/oes_server.rb +195 -0
  95. data/lib/adhearsion/voip/menu_state_machine/calculated_match.rb +80 -0
  96. data/lib/adhearsion/voip/menu_state_machine/matchers.rb +123 -0
  97. data/lib/adhearsion/voip/menu_state_machine/menu_builder.rb +58 -0
  98. data/lib/adhearsion/voip/menu_state_machine/menu_class.rb +149 -0
  99. data/lib/adhearsion.rb +37 -0
  100. data/lib/theatre/README.markdown +64 -0
  101. data/lib/theatre/callback_definition_loader.rb +84 -0
  102. data/lib/theatre/guid.rb +23 -0
  103. data/lib/theatre/invocation.rb +121 -0
  104. data/lib/theatre/namespace_manager.rb +153 -0
  105. data/lib/theatre/version.rb +2 -0
  106. data/lib/theatre.rb +151 -0
  107. metadata +177 -0
@@ -0,0 +1,286 @@
1
+ require File.join(File.dirname(__FILE__), 'ami_messages.rb')
2
+
3
+ module Adhearsion
4
+ module VoIP
5
+ module Asterisk
6
+ module Manager
7
+ class AbstractAsteriskManagerInterfaceStreamLexer
8
+
9
+ BUFFER_SIZE = 8.kilobytes unless defined? BUFFER_SIZE
10
+
11
+ %%{
12
+ machine ami_protocol_parser;
13
+
14
+ # All required Ragel actions are implemented as Ruby methods.
15
+
16
+ # Executed after a "Respone: Success" or "Response: Pong"
17
+ action init_success { init_success }
18
+
19
+ action init_response_follows { init_response_follows }
20
+
21
+ action init_error { init_error }
22
+
23
+ action message_received { message_received @current_message }
24
+ action error_received { error_received @current_message }
25
+
26
+ action version_starts { version_starts }
27
+ action version_stops { version_stops }
28
+
29
+ action key_starts { key_starts }
30
+ action key_stops { key_stops }
31
+
32
+ action value_starts { value_starts }
33
+ action value_stops { value_stops }
34
+
35
+ action error_reason_starts { error_reason_starts }
36
+ action error_reason_stops { error_reason_stops }
37
+
38
+ action syntax_error_starts { syntax_error_starts }
39
+ action syntax_error_stops { syntax_error_stops }
40
+
41
+ action immediate_response_starts { immediate_response_starts }
42
+ action immediate_response_stops { immediate_response_stops }
43
+
44
+ action follows_text_starts { follows_text_starts }
45
+ action follows_text_stops { follows_text_stops }
46
+
47
+ action event_name_starts { event_name_starts }
48
+ action event_name_stops { event_name_stops }
49
+
50
+ include ami_protocol_parser_machine "ami_protocol_lexer_machine.rl";
51
+
52
+ }%%##
53
+
54
+ attr_accessor(:ami_version)
55
+ def initialize
56
+
57
+ @data = ""
58
+ @current_pointer = 0
59
+ @ragel_stack = []
60
+
61
+ %%{
62
+ # All other variables become local, letting Ruby garbage collect them. This
63
+ # prevents us from having to manually reset them.
64
+
65
+ variable data @data;
66
+ variable p @current_pointer;
67
+ variable pe @data_ending_pointer;
68
+ variable cs @current_state;
69
+ variable ts @token_start;
70
+ variable te @token_end;
71
+ variable stack @stack;
72
+ variable act @ragel_act;
73
+ variable eof @eof;
74
+ variable stack @ragel_stack;
75
+ variable top @ragel_stack_top;
76
+
77
+ write data;
78
+ write init;
79
+ }%%##
80
+
81
+ end
82
+
83
+ def <<(new_data)
84
+ extend_buffer_with new_data
85
+ resume!
86
+ end
87
+
88
+ def resume!
89
+ %%{ write exec; }%%##
90
+ end
91
+
92
+ def extend_buffer_with(new_data)
93
+ if new_data.size + @data.size > BUFFER_SIZE
94
+ @data.slice! 0...new_data.size
95
+ # TODO: What if the current_pointer wasn't at the end of the data for some reason?
96
+ @current_pointer = @data.size
97
+ end
98
+ @data << new_data
99
+ @data_ending_pointer = @data.size
100
+ end
101
+
102
+ protected
103
+
104
+ ##
105
+ # Called after a response or event has been successfully parsed.
106
+ #
107
+ # @param [ManagerInterfaceResponse, ManagerInterfaceEvent] message The message just received
108
+ #
109
+ def message_received(message)
110
+ raise NotImplementedError, "Must be implemented in subclass!"
111
+ end
112
+
113
+ ##
114
+ # Called when there is an Error: stanza on the socket. Could be caused by executing an unrecognized command, trying
115
+ # to originate into an invalid priority, etc. Note: many errors' responses are actually tightly coupled to a
116
+ # ManagerInterfaceEvent which comes directly after it. Often the message will say something like "Channel status
117
+ # will follow".
118
+ #
119
+ # @param [String] reason The reason given in the Message: header for the error stanza.
120
+ #
121
+ def error_received(reason)
122
+ raise NotImplementedError, "Must be implemented in subclass!"
123
+ end
124
+
125
+ ##
126
+ # Called when there's a syntax error on the socket. This doesn't happen as often as it should because, in many cases,
127
+ # it's impossible to distinguish between a syntax error and an immediate packet.
128
+ #
129
+ # @param [String] ignored_chunk The offending text which caused the syntax error.
130
+ def syntax_error_encountered(ignored_chunk)
131
+ raise NotImplementedError, "Must be implemented in subclass!"
132
+ end
133
+
134
+ def init_success
135
+ @current_message = ManagerInterfaceResponse.new
136
+ end
137
+
138
+ def init_response_follows
139
+ @current_message = ManagerInterfaceResponse.new
140
+ end
141
+
142
+ def init_error
143
+ @current_message = ManagerInterfaceError.new()
144
+ end
145
+
146
+ def version_starts
147
+ @start_of_version = @current_pointer
148
+ end
149
+
150
+ def version_stops
151
+ self.ami_version = @data[@start_of_version...@current_pointer].to_f
152
+ @start_of_version = nil
153
+ end
154
+
155
+ def event_name_starts
156
+ @event_name_start = @current_pointer
157
+ end
158
+
159
+ def event_name_stops
160
+ event_name = @data[@event_name_start...@current_pointer]
161
+ @event_name_start = nil
162
+ @current_message = ManagerInterfaceEvent.new(event_name)
163
+ end
164
+
165
+ def key_starts
166
+ @current_key_position = @current_pointer
167
+ end
168
+
169
+ def key_stops
170
+ @current_key = @data[@current_key_position...@current_pointer]
171
+ end
172
+
173
+ def value_starts
174
+ @current_value_position = @current_pointer
175
+ end
176
+
177
+ def value_stops
178
+ @current_value = @data[@current_value_position...@current_pointer]
179
+ @last_seen_value_end = @current_pointer + 2 # 2 for \r\n
180
+ add_pair_to_current_message
181
+ end
182
+
183
+ def error_reason_starts
184
+ @error_reason_start = @current_pointer
185
+ end
186
+
187
+ def error_reason_stops
188
+ @current_message.message = @data[@error_reason_start...@current_pointer]
189
+ end
190
+
191
+ def follows_text_starts
192
+ @follows_text_start = @current_pointer
193
+ end
194
+
195
+ def follows_text_stops
196
+ text = @data[@last_seen_value_end..(@current_pointer - "\r\n--END COMMAND--".size)]
197
+ @current_message.text_body = text
198
+ @follows_text_start = nil
199
+ end
200
+
201
+ def add_pair_to_current_message
202
+ @current_message[@current_key] = @current_value
203
+ reset_key_and_value_positions
204
+ end
205
+
206
+ def reset_key_and_value_positions
207
+ @current_key, @current_value, @current_key_position, @current_value_position = nil
208
+ end
209
+
210
+ def syntax_error_starts
211
+ @current_syntax_error_start = @current_pointer # Adding 1 since the pointer is still set to the last successful match
212
+ end
213
+
214
+ def syntax_error_stops
215
+ # Subtracting 3 from @current_pointer below for "\r\n" which separates a stanza
216
+ offending_data = @data[@current_syntax_error_start...@current_pointer - 1]
217
+ syntax_error_encountered offending_data
218
+ @current_syntax_error_start = nil
219
+ end
220
+
221
+ def immediate_response_starts
222
+ @immediate_response_start = @current_pointer
223
+ end
224
+
225
+ def immediate_response_stops
226
+ message = @data[@immediate_response_start...(@current_pointer -1)]
227
+ message_received ManagerInterfaceResponse.from_immediate_response(message)
228
+ end
229
+
230
+ ##
231
+ # This method is used primarily in debugging.
232
+ #
233
+ def view_buffer(message=nil)
234
+
235
+ message ||= "Viewing the buffer"
236
+
237
+ buffer = @data.clone
238
+ buffer.insert(@current_pointer, "\033[0;31m\033[1;31m^\033[0m")
239
+
240
+ buffer.gsub!("\r", "\\\\r")
241
+ buffer.gsub!("\n", "\\n\n")
242
+
243
+ puts <<-INSPECTION
244
+ VVVVVVVVVVVVVVVVVVVVVVVVVVVVV
245
+ #### #{message}
246
+ #############################
247
+ #{buffer}
248
+ #############################
249
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
250
+ INSPECTION
251
+
252
+ end
253
+ end
254
+ class DelegatingAsteriskManagerInterfaceLexer < AbstractAsteriskManagerInterfaceStreamLexer
255
+
256
+ def initialize(delegate, method_delegation_map=nil)
257
+ super()
258
+ @delegate = delegate
259
+
260
+ @message_received_method = method_delegation_map && method_delegation_map.has_key?(:message_received) ?
261
+ method_delegation_map[:message_received] : :message_received
262
+
263
+ @error_received_method = method_delegation_map && method_delegation_map.has_key?(:error_received) ?
264
+ method_delegation_map[:error_received] : :error_received
265
+
266
+ @syntax_error_method = method_delegation_map && method_delegation_map.has_key?(:syntax_error_encountered) ?
267
+ method_delegation_map[:syntax_error_encountered] : :syntax_error_encountered
268
+ end
269
+
270
+ def message_received(message)
271
+ @delegate.send(@message_received_method, message)
272
+ end
273
+
274
+ def error_received(message)
275
+ @delegate.send(@error_received_method, message)
276
+ end
277
+
278
+ def syntax_error_encountered(ignored_chunk)
279
+ @delegate.send(@syntax_error_method, ignored_chunk)
280
+ end
281
+
282
+ end
283
+ end
284
+ end
285
+ end
286
+ end
@@ -0,0 +1,78 @@
1
+ module Adhearsion
2
+ module VoIP
3
+ module Asterisk
4
+ module Manager
5
+
6
+
7
+ ##
8
+ # This is the object containing a response from Asterisk.
9
+ #
10
+ # Note: not all responses have an ActionID!
11
+ #
12
+ class ManagerInterfaceResponse
13
+
14
+ class << self
15
+ def from_immediate_response(text)
16
+ returning new do |instance|
17
+ instance.text_body = text
18
+ end
19
+ end
20
+ end
21
+
22
+ attr_accessor :action,
23
+ :action_id,
24
+ :text_body # For "Response: Follows" sections
25
+ attr_reader :events
26
+
27
+ def initialize
28
+ @headers = HashWithIndifferentAccess.new
29
+ end
30
+
31
+ def has_text_body?
32
+ !! @text_body
33
+ end
34
+
35
+ def headers
36
+ @headers.clone
37
+ end
38
+
39
+ def [](arg)
40
+ @headers[arg]
41
+ end
42
+
43
+ def []=(key,value)
44
+ @headers[key] = value
45
+ end
46
+
47
+ end
48
+
49
+ class ManagerInterfaceError < Exception
50
+
51
+ attr_accessor :message
52
+ def initialize
53
+ @headers = HashWithIndifferentAccess.new
54
+ end
55
+
56
+ def [](key)
57
+ @headers[key]
58
+ end
59
+
60
+ def []=(key,value)
61
+ @headers[key] = value
62
+ end
63
+
64
+ end
65
+
66
+ class ManagerInterfaceEvent < ManagerInterfaceResponse
67
+
68
+ attr_reader :name
69
+ def initialize(name)
70
+ super()
71
+ @name = name
72
+ end
73
+
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,87 @@
1
+ %%{ #%
2
+
3
+ #########
4
+ ## This file is written with the Ragel programming language and parses the Asterisk Manager Interface protocol. It depends
5
+ ## upon Ragel actions which should be implemented in another Ragel-parsed file which includes this file.
6
+ ##
7
+ ## Ragel was used because the AMI protocol is extremely non-deterministic and, in the edge cases, requires something both
8
+ ## very robust and something which can recover from syntax errors.
9
+ ##
10
+ ## Note: This file is language agnostic. From this AMI parsers in many other languages can be generated.
11
+ #########
12
+
13
+ machine ami_protocol_parser_machine;
14
+
15
+ cr = "\r"; # A carriage return. Used before (almost) every newline character.
16
+ lf = "\n"; # Newline. Used (with cr) to separate key/value pairs and stanzas.
17
+ crlf = cr lf; # Means "carriage return and line feed". Used to separate key/value pairs and stanzas
18
+ loose_newline = cr? lf; # Used sometimes when the AMI protocol is nondeterministic about the delimiter
19
+
20
+ white = [\t ]; # Single whitespace character, either a tab or a space
21
+ colon = ":" [ ]**; # Separates keys from values. "A colon followed by any number of spaces"
22
+ stanza_break = crlf crlf; # The seperator between two stanzas.
23
+ rest_of_line = (any* -- crlf); # Match all characters until the next line seperator.
24
+
25
+ Prompt = "Asterisk Call Manager/" digit+ >version_starts "." digit+ %version_stops crlf;
26
+
27
+ Key = ((alnum | print) -- (cr | lf))+;
28
+ KeyValuePair = Key >key_starts %key_stops colon rest_of_line >value_starts %value_stops crlf;
29
+
30
+ FollowsDelimiter = crlf "--END COMMAND--";
31
+
32
+ Response = "Response"i colon;
33
+
34
+ Success = Response "Success"i %init_success crlf @{ fgoto success; };
35
+ Pong = Response "Pong"i %init_success crlf @{ fgoto success; };
36
+ Event = "Event"i colon %event_name_starts rest_of_line %event_name_stops crlf @{ fgoto success; };
37
+ Error = Response "Error"i %init_error crlf (("Message"i colon rest_of_line >error_reason_starts crlf >error_reason_stops) | KeyValuePair)+ crlf @error_received;
38
+ Follows = Response "Follows"i crlf @init_response_follows @{ fgoto response_follows; };
39
+
40
+ # For "Response: Follows"
41
+ FollowsBody = (any* -- FollowsDelimiter) >follows_text_starts FollowsDelimiter @follows_text_stops crlf;
42
+
43
+ ImmediateResponse = (any+ -- (loose_newline | ":")) >immediate_response_starts loose_newline @immediate_response_stops @{fret;};
44
+ SyntaxError = (any+ -- crlf) >syntax_error_starts crlf @syntax_error_stops;
45
+
46
+ irregularity := |*
47
+ ImmediateResponse; # Performs the fret in the ImmediateResponse FSM
48
+ SyntaxError => { fret; };
49
+ *|;
50
+
51
+ # When a new socket is established, Asterisk will send the version of the protocol per the Prompt machine. Because it's
52
+ # tedious for unit tests to always send this, we'll put some intelligence into this parser to support going straight into
53
+ # the protocol-parsing machine. It's also conceivable that a variant of AMI would not send this initial information.
54
+ main := |*
55
+ Prompt => { fgoto protocol; };
56
+ any => {
57
+ # If this scanner's look-ahead capability didn't match the prompt, let's ignore the need for a prompt
58
+ fhold;
59
+ fgoto protocol;
60
+ };
61
+ *|;
62
+
63
+ protocol := |*
64
+ Prompt;
65
+ Success;
66
+ Pong;
67
+ Event;
68
+ Error;
69
+ Follows crlf;
70
+ crlf => { fgoto protocol; }; # If we get a crlf out of place, let's just ignore it.
71
+ any => {
72
+ # If NONE of the above patterns match, we consider this a syntax error. The irregularity machine can recover gracefully.
73
+ fhold;
74
+ fcall irregularity;
75
+ };
76
+ *|;
77
+
78
+ success := KeyValuePair* crlf @message_received @{fgoto protocol;};
79
+
80
+ # For the "Response: Follows" protocol abnormality. What happens if there's a protocol irregularity in this state???
81
+ response_follows := |*
82
+ KeyValuePair+;
83
+ FollowsBody;
84
+ crlf @{ message_received; fgoto protocol; };
85
+ *|;
86
+
87
+ }%%