agent_xmpp 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/.document +5 -0
  2. data/.gitignore +11 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +417 -0
  5. data/Rakefile +75 -0
  6. data/VERSION +1 -0
  7. data/agent_xmpp.gemspec +144 -0
  8. data/lib/agent_xmpp.rb +22 -0
  9. data/lib/agent_xmpp/admin.rb +113 -0
  10. data/lib/agent_xmpp/client.rb +7 -0
  11. data/lib/agent_xmpp/client/boot.rb +83 -0
  12. data/lib/agent_xmpp/client/client.rb +64 -0
  13. data/lib/agent_xmpp/client/connection.rb +108 -0
  14. data/lib/agent_xmpp/client/controller.rb +394 -0
  15. data/lib/agent_xmpp/client/message_delegate.rb +720 -0
  16. data/lib/agent_xmpp/client/message_pipe.rb +193 -0
  17. data/lib/agent_xmpp/client/response.rb +102 -0
  18. data/lib/agent_xmpp/config.rb +48 -0
  19. data/lib/agent_xmpp/main.rb +175 -0
  20. data/lib/agent_xmpp/models.rb +7 -0
  21. data/lib/agent_xmpp/models/contact.rb +85 -0
  22. data/lib/agent_xmpp/models/message.rb +152 -0
  23. data/lib/agent_xmpp/models/publication.rb +53 -0
  24. data/lib/agent_xmpp/models/roster.rb +107 -0
  25. data/lib/agent_xmpp/models/service.rb +91 -0
  26. data/lib/agent_xmpp/models/subscription.rb +61 -0
  27. data/lib/agent_xmpp/models/table_definitions.rb +107 -0
  28. data/lib/agent_xmpp/patches.rb +7 -0
  29. data/lib/agent_xmpp/patches/array.rb +32 -0
  30. data/lib/agent_xmpp/patches/float.rb +10 -0
  31. data/lib/agent_xmpp/patches/hash.rb +13 -0
  32. data/lib/agent_xmpp/patches/object.rb +15 -0
  33. data/lib/agent_xmpp/patches/rexml.rb +69 -0
  34. data/lib/agent_xmpp/patches/string.rb +15 -0
  35. data/lib/agent_xmpp/xmpp.rb +18 -0
  36. data/lib/agent_xmpp/xmpp/element.rb +158 -0
  37. data/lib/agent_xmpp/xmpp/entry.rb +36 -0
  38. data/lib/agent_xmpp/xmpp/error_response.rb +189 -0
  39. data/lib/agent_xmpp/xmpp/iq.rb +90 -0
  40. data/lib/agent_xmpp/xmpp/iq_command.rb +54 -0
  41. data/lib/agent_xmpp/xmpp/iq_disco.rb +206 -0
  42. data/lib/agent_xmpp/xmpp/iq_pubsub.rb +270 -0
  43. data/lib/agent_xmpp/xmpp/iq_roster.rb +183 -0
  44. data/lib/agent_xmpp/xmpp/iq_version.rb +89 -0
  45. data/lib/agent_xmpp/xmpp/jid.rb +150 -0
  46. data/lib/agent_xmpp/xmpp/message.rb +82 -0
  47. data/lib/agent_xmpp/xmpp/presence.rb +127 -0
  48. data/lib/agent_xmpp/xmpp/sasl.rb +241 -0
  49. data/lib/agent_xmpp/xmpp/stanza.rb +107 -0
  50. data/lib/agent_xmpp/xmpp/x_data.rb +357 -0
  51. data/test/app/app.rb +339 -0
  52. data/test/cases/test_application_message_processing.rb +65 -0
  53. data/test/cases/test_errors.rb +24 -0
  54. data/test/cases/test_presence_management.rb +139 -0
  55. data/test/cases/test_roster_management.rb +214 -0
  56. data/test/cases/test_service_discovery.rb +168 -0
  57. data/test/cases/test_session_management.rb +120 -0
  58. data/test/cases/test_version_discovery.rb +67 -0
  59. data/test/helpers/matchers.rb +23 -0
  60. data/test/helpers/mocks.rb +82 -0
  61. data/test/helpers/test_case_extensions.rb +45 -0
  62. data/test/helpers/test_client.rb +44 -0
  63. data/test/helpers/test_delegate.rb +60 -0
  64. data/test/helpers/test_helper.rb +91 -0
  65. data/test/messages/application_messages.rb +206 -0
  66. data/test/messages/error_messages.rb +35 -0
  67. data/test/messages/presence_messages.rb +66 -0
  68. data/test/messages/roster_messages.rb +126 -0
  69. data/test/messages/service_discovery_messages.rb +201 -0
  70. data/test/messages/session_messages.rb +158 -0
  71. data/test/messages/version_discovery_messages.rb +69 -0
  72. data/test/peer/peer.rb +21 -0
  73. metadata +187 -0
@@ -0,0 +1,64 @@
1
+ ##############################################################################################################
2
+ module AgentXmpp
3
+
4
+ #####-------------------------------------------------------------------------------------------------------
5
+ class Client
6
+
7
+ #####-------------------------------------------------------------------------------------------------------
8
+ class << self
9
+
10
+ #### self
11
+ end
12
+
13
+ #---------------------------------------------------------------------------------------------------------
14
+ attr_reader :connection
15
+ #---------------------------------------------------------------------------------------------------------
16
+
17
+ #.........................................................................................................
18
+ def initialize
19
+ end
20
+
21
+ #.........................................................................................................
22
+ def connect
23
+ while (true)
24
+ EventMachine.run do
25
+ @connection = EventMachine.connect(AgentXmpp.jid.domain, AgentXmpp.port, Connection, self)
26
+ end
27
+ Boot.call_if_implemented(:call_restarting_client, pipe)
28
+ sleep(10.0)
29
+ AgentXmpp.logger.warn "RESTARTING CLIENT"
30
+ end
31
+ end
32
+
33
+ #.........................................................................................................
34
+ def close_connection
35
+ AgentXmpp.logger.info "CLOSE CONNECTION"
36
+ connection.close_connection_after_writing unless connection.nil?
37
+ end
38
+
39
+ #.........................................................................................................
40
+ def reconnect
41
+ AgentXmpp.logger.info "RECONNECTING"
42
+ connection.reconnect(jid.domain, port) unless connection.nil?
43
+ end
44
+
45
+ #.........................................................................................................
46
+ def pipe
47
+ connection.pipe
48
+ end
49
+
50
+ #.........................................................................................................
51
+ def add_delegate(delegate)
52
+ connection.pipe.add_delegate(delegate)
53
+ end
54
+
55
+ #.........................................................................................................
56
+ def remove_delegate(delegate)
57
+ connection.pipe.remove_delegate(delegate)
58
+ end
59
+
60
+ #### Client
61
+ end
62
+
63
+ #### AgentXmpp
64
+ end
@@ -0,0 +1,108 @@
1
+ ##############################################################################################################
2
+ module AgentXmpp
3
+
4
+ #####-------------------------------------------------------------------------------------------------------
5
+ class Connection < EventMachine::Connection
6
+
7
+ #---------------------------------------------------------------------------------------------------------
8
+ include EventMachine::XmlPushParser
9
+ #---------------------------------------------------------------------------------------------------------
10
+
11
+ #---------------------------------------------------------------------------------------------------------
12
+ attr_reader :client, :delegates, :keepalive, :pipe
13
+ #---------------------------------------------------------------------------------------------------------
14
+
15
+ #.........................................................................................................
16
+ def initialize(client)
17
+ @client = client
18
+ @pipe = MessagePipe.new(self)
19
+ end
20
+
21
+ #---------------------------------------------------------------------------------------------------------
22
+ # EventMachine::Connection callbacks
23
+ #.........................................................................................................
24
+ def connection_completed
25
+ @keepalive = EventMachine::PeriodicTimer.new(60) do
26
+ send_data("\n")
27
+ end
28
+ AgentXmpp.start_garbage_collection(pipe)
29
+ pipe.connection_completed
30
+ end
31
+
32
+ #.........................................................................................................
33
+ def receive_data(data)
34
+ super(data)
35
+ end
36
+
37
+ #.........................................................................................................
38
+ def unbind
39
+ if @keepalive
40
+ @keepalive.cancel
41
+ @keepalive = nil
42
+ end
43
+ pipe.unbind
44
+ end
45
+
46
+ #---------------------------------------------------------------------------------------------------------
47
+ # EventMachine::XmlPushParser callbacks
48
+ #.........................................................................................................
49
+ def start_document
50
+ end
51
+
52
+ #.........................................................................................................
53
+ def start_element(name, attrs)
54
+ e = REXML::Element.new(name)
55
+ e.add_attributes(attrs)
56
+ @current = @current.nil? ? e : @current.add_element(e)
57
+ if @current.xpath == 'stream:stream'
58
+ process
59
+ @current = nil
60
+ end
61
+ end
62
+
63
+ #.........................................................................................................
64
+ def end_element(name)
65
+ if @current and @current.parent
66
+ @current = @current.parent
67
+ else
68
+ process
69
+ @current = nil
70
+ end
71
+ end
72
+
73
+ #.........................................................................................................
74
+ def characters(text)
75
+ @current.text = @current.text.to_s + text if @current
76
+ end
77
+
78
+ #.........................................................................................................
79
+ def error(*args)
80
+ AgentXmpp.logger.error *args
81
+ end
82
+
83
+ #.........................................................................................................
84
+ def process
85
+ if @current
86
+ @current.add_namespace(@streamns) if @current.namespace('').to_s.eql?('')
87
+ begin
88
+ stanza = Xmpp::Stanza::import(@current)
89
+ rescue Xmpp::NoNameXmlnsRegistered
90
+ stanza = @current
91
+ end
92
+ if @current.xpath.eql?('stream:stream')
93
+ @streamns = @current.namespace('') if @current.namespace('')
94
+ end
95
+ receive(stanza) if respond_to?(:receive)
96
+ end
97
+ end
98
+
99
+ #.........................................................................................................
100
+ def receive(stanza)
101
+ pipe.receive(stanza)
102
+ end
103
+
104
+ #### Connection
105
+ end
106
+
107
+ #### AgentXmpp
108
+ end
@@ -0,0 +1,394 @@
1
+ ##############################################################################################################
2
+ module AgentXmpp
3
+
4
+ #####-------------------------------------------------------------------------------------------------------
5
+ class BaseController
6
+
7
+ #---------------------------------------------------------------------------------------------------------
8
+ @routes = {}
9
+ @before_filters = {}
10
+ @commands_list = {}
11
+ @commands_list_mutex = Mutex.new
12
+
13
+ #---------------------------------------------------------------------------------------------------------
14
+ class << self
15
+
16
+ #.........................................................................................................
17
+ attr_reader :routes, :before_filters, :commands_list, :commands_list_mutex
18
+
19
+ #.........................................................................................................
20
+ # application interface
21
+ #.........................................................................................................
22
+ def command(node, opts = {}, &blk)
23
+ route(:command, {:node => node, :opts => opts, :blk => blk})
24
+ end
25
+
26
+ #.........................................................................................................
27
+ def event(jid, node, opts = {}, &blk)
28
+ j = Xmpp::Jid.new(jid)
29
+ route(:event, {:node => "/home/#{j.domain}/#{j.node}/#{node}", :domain => j.domain, :opts => opts, :blk => blk})
30
+ end
31
+
32
+ #.........................................................................................................
33
+ def chat(opts = {}, &blk)
34
+ route(:chat, {:opts => opts, :blk => blk})
35
+ end
36
+
37
+ #.........................................................................................................
38
+ def before(args=nil, &blk)
39
+ args = {:command => :all, :event => :all, :chat => :all} if args.nil? or args.eql?(:all)
40
+ args.each {|(msg_type, nodes)| add_before_filter(msg_type, {:nodes => nodes, :blk => blk})}
41
+ end
42
+
43
+ #.........................................................................................................
44
+ def include_module(mod)
45
+ include(mod)
46
+ end
47
+
48
+ #.........................................................................................................
49
+ # managment
50
+ #.........................................................................................................
51
+ def route(msg_type, nroute)
52
+ (routes[msg_type] ||= []).push(nroute).last
53
+ end
54
+
55
+ #.........................................................................................................
56
+ def add_before_filter(msg_type, nodes)
57
+ (before_filters[msg_type] ||= []).push(nodes).last
58
+ end
59
+
60
+ #.........................................................................................................
61
+ def command_nodes(jid=nil)
62
+ (routes[:command] ||= []).inject([]) do |nodes, route|
63
+ if jid and not AgentXmpp.is_account_jid?(jid)
64
+ groups, access = Contact.find_by_jid(jid)[:groups], [route[:opts][:access] || []].flatten
65
+ (access.empty? or access.any?{|a| groups.include?(a)}) ? nodes << route[:node] : nodes
66
+ else; nodes << route[:node]; end
67
+ end
68
+ end
69
+
70
+ #.........................................................................................................
71
+ def subscriptions(service)
72
+ (routes[:event] ||= []).inject([]){|s,r| /#{r[:domain]}/.match(service) ? s << r[:node] : s}
73
+ end
74
+
75
+ #.........................................................................................................
76
+ def event_domains
77
+ (routes[:event] ||= []).map{|r| r[:domain]}.uniq
78
+ end
79
+
80
+ #.........................................................................................................
81
+ def add_command_to_list(session_id, controller)
82
+ commands_list_mutex.synchronize do
83
+ commands_list[session_id] = {:controller=>controller, :created_at=>Time.now}
84
+ end
85
+ end
86
+
87
+ #.........................................................................................................
88
+ def remove_command_from_list(session_id)
89
+ if commands_list[session_id]
90
+ commands_list_mutex.synchronize{commands_list.delete(session_id)}
91
+ end
92
+ end
93
+
94
+ #### self
95
+ end
96
+
97
+ #.........................................................................................................
98
+ attr_reader :params, :pipe, :route, :params_list, :submits
99
+
100
+ #.........................................................................................................
101
+ def initialize(pipe, params)
102
+ @params, @pipe = params, pipe
103
+ @params_list, @submits = [params], []
104
+ end
105
+
106
+ #.........................................................................................................
107
+ def next(params)
108
+ @params = params
109
+ @params_list << params
110
+ self
111
+ end
112
+
113
+ #.........................................................................................................
114
+ # internal interface
115
+ #.......................................................................................................
116
+ def invoke_command
117
+ params[:sessionid] = Xmpp::IdGenerator.generate_id
118
+ invoke_command_on_route do
119
+ define_meta_class_method(:request, &route[:blk])
120
+ define_meta_class_method(:request_handler) do
121
+ run_command(request)
122
+ end
123
+ define_meta_class_method(:request_callback) do |*resp|
124
+ resp = resp.length.eql?(1) ? resp.first : resp
125
+ add_payload_to_container(resp)
126
+ end
127
+ process_request
128
+ end
129
+ end
130
+
131
+ #.......................................................................................................
132
+ def invoke_command_next
133
+ invoke_command_on_route do
134
+ define_meta_class_method(:request_handler) do
135
+ if params[:x_data_type].eql?(:submit)
136
+ on_submit
137
+ end
138
+ end
139
+ process_request
140
+ end
141
+ end
142
+
143
+ #.......................................................................................................
144
+ def invoke_event
145
+ @route = get_route(:event)
146
+ unless route.nil?
147
+ define_meta_class_method(:request, &route[:blk])
148
+ define_meta_class_method(:request_handler) do
149
+ request; delegate_methods.delegate(pipe, self); flush_messages
150
+ end
151
+ process_request
152
+ else
153
+ AgentXmpp.logger.error "ROUTING ERROR: no route for {:node => '#{params[:node]}'}."
154
+ end
155
+ end
156
+
157
+ #.......................................................................................................
158
+ def invoke_chat
159
+ @route = chat_route
160
+ unless route.nil?
161
+ define_meta_class_method(:request_handler, &route[:blk])
162
+ else
163
+ define_meta_class_method(:request_handler) do
164
+ "#{AgentXmpp::AGENT_XMPP_NAME} #{AgentXmpp::VERSION}, #{AgentXmpp::OS_VERSION}"
165
+ end
166
+ end
167
+ define_meta_class_method(:request_callback) do |result|
168
+ add_payload_to_container(result) if result.kind_of?(String)
169
+ end
170
+ process_request
171
+ end
172
+
173
+ #.......................................................................................................
174
+ def delegate_methods
175
+ @delegate_methods ||= AgentXmpp::Delegate.new
176
+ end
177
+
178
+ #.......................................................................................................
179
+ def messages
180
+ @messages ||= []
181
+ end
182
+
183
+ #.......................................................................................................
184
+ def flush_messages
185
+ pipe.send_resp(messages); messages.clear
186
+ end
187
+
188
+ #.........................................................................................................
189
+ # application interface
190
+ #.......................................................................................................
191
+ def error(err, *args)
192
+ AgentXmpp::Error.new(err, *args)
193
+ end
194
+
195
+ #.......................................................................................................
196
+ def on(action, opts= {}, &blk)
197
+ if action.eql?(:submit)
198
+ @submits << {:opts=>opts, :blk=>blk}
199
+ else
200
+ define_meta_class_method(("on_"+action.to_s).to_sym, &blk)
201
+ end
202
+ end
203
+
204
+ #.......................................................................................................
205
+ def xmpp_msg(msg)
206
+ messages << msg
207
+ end
208
+
209
+ #.......................................................................................................
210
+ def delegate_to(methods)
211
+ delegate_methods.add_delegate_methods(methods); delegate_methods
212
+ end
213
+
214
+ #.........................................................................................................
215
+ def command_completed
216
+ Xmpp::IqCommand.send_command(:to=>params[:from], :node=>params[:node], :iq_type=>:result, :status=>:completed,
217
+ :id => params[:id], :sessionid => params[:sessionid])
218
+ end
219
+
220
+ #.........................................................................................................
221
+ def command_request(args, &blk)
222
+ raise ArgmentError ':to and :node are required' unless args[:to] and args[:node]
223
+ Xmpp::IqCommand.send_command(:to=>args[:to], :node=>args[:node], :iq_type=>:set, :action=>:execute, :payload=>args[:payload], &blk)
224
+ end
225
+
226
+ #.........................................................................................................
227
+ # managment
228
+ #.......................................................................................................
229
+ def on_submit
230
+ unless(sub = submits.shift).nil?
231
+ update_command_list
232
+ blk, guard = sub[:blk], sub[:opts][:guard]
233
+ call_blk = lambda{|| blk.arity.eql?(1) ? blk.call(Xmpp::XData.new('form')) : blk.call}
234
+ result = if guard.nil?
235
+ call_blk[]
236
+ else
237
+ (guard.arity.eql?(1) ? guard.call(self) : guard.call) ? call_blk[] : on_submit
238
+ end
239
+ result.nil? ? on_submit : result
240
+ end
241
+ end
242
+
243
+ ####......................................................................................................
244
+ # private
245
+ ####......................................................................................................
246
+ def run_command(req)
247
+ request_method = ("on_"+(params[:x_data_type] || params[:action]).to_s).to_sym
248
+ if respond_to?(request_method)
249
+ if params[:action].eql?(:execute) and params[:x_data_type].nil?
250
+ form = Xmpp::XData.new('form')
251
+ on_execute(form); form
252
+ elsif params[:action].eql?(:cancel)
253
+ on_cancel
254
+ else
255
+ send(request_method)
256
+ end
257
+ else
258
+ req
259
+ end
260
+ end
261
+
262
+ #.........................................................................................................
263
+ def process_request
264
+ if route and route[:opts] and route[:opts][:defer]
265
+ EventMachine.defer(method(:request_handler).to_proc, respond_to?(:request_callback) ? method(:request_callback).to_proc : nil)
266
+ else
267
+ respond_to?(:request_callback) ? request_callback(request_handler) : request_handler
268
+ end
269
+ end
270
+
271
+ #.........................................................................................................
272
+ # add payloads
273
+ #.........................................................................................................
274
+ def result_jabber_x_data(payload)
275
+ delegate_methods.delegate(pipe, self)
276
+ flush_messages
277
+ if params[:action].eql?(:cancel)
278
+ command_canceled
279
+ elsif payload.kind_of?(AgentXmpp::Error)
280
+ payload.responce
281
+ elsif payload.kind_of?(AgentXmpp::Delegate)
282
+ nil
283
+ elsif payload.kind_of?(AgentXmpp::Response)
284
+ payload
285
+ else
286
+ command_result(payload.nil? ? nil : payload.to_x_data, params[:sessionid])
287
+ end
288
+ end
289
+
290
+ #.........................................................................................................
291
+ def result_message_chat(payload)
292
+ Xmpp::Message.chat(params[:from], payload)
293
+ end
294
+
295
+ #.........................................................................................................
296
+ def add_payload_to_container(payload)
297
+ meth = "result_#{params[:xmlns].gsub(/:/, "_")}".to_sym
298
+ if respond_to?(meth, true)
299
+ if res = send(meth, payload)
300
+ pipe.send_resp(res)
301
+ end
302
+ else
303
+ AgentXmpp.logger.error "PAYLOAD ERROR: unsupported payload {:xmlns => '#{params[:xmlns]}', :node => '#{params[:node]}', :action => '#{params[:action]}'}."
304
+ Xmpp::ErrorResponse.unsupported_payload(params)
305
+ end
306
+ end
307
+
308
+ #.........................................................................................................
309
+ # commands
310
+ #.........................................................................................................
311
+ def update_command_list
312
+ if not BaseController.commands_list[params[:sessionid]] and submits.length > 0
313
+ BaseController.add_command_to_list(params[:sessionid], self)
314
+ elsif BaseController.commands_list[params[:sessionid]] and submits.length.eql?(0)
315
+ BaseController.remove_command_from_list(params[:sessionid])
316
+ end
317
+ end
318
+
319
+ #.........................................................................................................
320
+ def command_result(payload, sessionid)
321
+ if payload
322
+ Xmpp::IqCommand.send_command(:to=>params[:from], :node=>params[:node], :status=>payload.type.eql?(:form) ? :executing : :completed,
323
+ :id => params[:id], :sessionid => sessionid, :payload => payload, :iq_type=>:result)
324
+ else
325
+ Xmpp::IqCommand.send_command(:to=>params[:from], :node=>params[:node], :status=> :completed, :id => params[:id],
326
+ :sessionid => sessionid, :iq_type=>:result)
327
+ end
328
+ end
329
+
330
+ #.........................................................................................................
331
+ def command_canceled
332
+ BaseController.remove_command_from_list(params[:sessionid])
333
+ Xmpp::IqCommand.send_command(:to=>params[:from], :node=>params[:node], :status=>:canceled, :id => params[:id],
334
+ :sessionid => params[:sessionid], :iq_type=>:result)
335
+ end
336
+
337
+ #.......................................................................................................
338
+ def invoke_command_on_route
339
+ @route = get_route(:command)
340
+ unless route.nil?
341
+ if apply_before_filters(:command, params[:node])
342
+ yield
343
+ else
344
+ AgentXmpp.logger.error "ACCESS ERROR: before_filter prevented '#{params[:from]}' access {:node => '#{params[:node]}', :action => '#{params[:action]}'}."
345
+ Xmpp::ErrorResponse.forbidden(params)
346
+ end
347
+ else
348
+ AgentXmpp.logger.error "ROUTING ERROR: no route for {:node => '#{params[:node]}', :action => '#{params[:action]}'}."
349
+ Xmpp::ErrorResponse.no_route(params)
350
+ end
351
+ end
352
+
353
+ #.........................................................................................................
354
+ # routes
355
+ #.........................................................................................................
356
+ def get_route(msg_type)
357
+ (BaseController.routes[msg_type] || []).select{|r| r[:node].eql?(params[:node].to_s)}.first
358
+ end
359
+
360
+ #.........................................................................................................
361
+ def chat_route
362
+ (BaseController.routes[:chat] ||= []).first
363
+ end
364
+
365
+ #.........................................................................................................
366
+ # filters
367
+ #.........................................................................................................
368
+ def apply_before_filters(msg_type, node=nil)
369
+ (BaseController.before_filters[msg_type] || []).inject([]) do |fs, f|
370
+ nodes = [f[:nodes]].flatten
371
+ (nodes.include?(node) or nodes.include?(:all)) ? fs << f : fs
372
+ end.inject(true) do |r,f|
373
+ define_meta_class_method(:filter, &f[:blk])
374
+ r and filter
375
+ end
376
+ end
377
+
378
+ #.........................................................................................................
379
+ private :add_payload_to_container, :chat_route, :get_route, :result_jabber_x_data, :result_message_chat,
380
+ :process_request, :run_command, :command_result, :invoke_command_on_route, :update_command_list,
381
+ :command_canceled
382
+
383
+ #### BaseController
384
+ end
385
+
386
+ ##############################################################################################################
387
+ class Controller < BaseController
388
+
389
+ #### Controller
390
+ end
391
+
392
+ #### AgentXmpp
393
+ end
394
+