agent_xmpp 0.1.0

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 (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,36 @@
1
+ ##############################################################################################################
2
+ module AgentXmpp
3
+
4
+ #####-------------------------------------------------------------------------------------------------------
5
+ module Xmpp
6
+
7
+ #####-------------------------------------------------------------------------------------------------------
8
+ class Entry < Element
9
+
10
+ #.....................................................................................................
11
+ name_xmlns 'entry', 'http://www.w3.org/2005/Atom'
12
+
13
+ #.......................................................................................................
14
+ def initialize(t = nil)
15
+ super()
16
+ add_element(REXML::Element.new("title").add_text(t)) if t
17
+ end
18
+
19
+ #.......................................................................................................
20
+ def title
21
+ first_element_text('title')
22
+ end
23
+
24
+ #.......................................................................................................
25
+ def title=(t)
26
+ replace_element_text('title', t)
27
+ end
28
+
29
+ #### Entry
30
+ end
31
+
32
+ #### XMPP
33
+ end
34
+
35
+ #### AgentXmpp
36
+ end
@@ -0,0 +1,189 @@
1
+ # Original from XMPP4R - XMPP Library for Ruby Website::http://home.gna.org/xmpp4r/
2
+ ##############################################################################################################
3
+ module AgentXmpp
4
+
5
+ #####-------------------------------------------------------------------------------------------------------
6
+ module Xmpp
7
+
8
+ #####-------------------------------------------------------------------------------------------------------
9
+ class NoNameXmlnsRegistered < ArgumentError
10
+ def initialize(klass)
11
+ super "Class #{klass} has not set name and xmlns"
12
+ end
13
+ end
14
+
15
+ #####-------------------------------------------------------------------------------------------------------
16
+ class ErrorResponse < Element
17
+
18
+ #.........................................................................................................
19
+ name_xmlns 'error'
20
+ xmpp_attribute :type, :sym => true
21
+
22
+ #####-------------------------------------------------------------------------------------------------------
23
+ class << self
24
+
25
+ #.........................................................................................................
26
+ def bad_request(params, msg)
27
+ command_error(params, 'bad-request', msg)
28
+ end
29
+
30
+ #.........................................................................................................
31
+ def unsupported_payload(params)
32
+ command_error(params, 'bad-request', 'unsupported payload')
33
+ end
34
+
35
+ #.........................................................................................................
36
+ def no_route(params)
37
+ command_error(params, 'item-not-found', 'no route for specified command node')
38
+ end
39
+
40
+ #.........................................................................................................
41
+ def forbidden(params)
42
+ command_error(params, 'forbidden', 'the requesting JID is not allowed to execute the command')
43
+ end
44
+
45
+ #.........................................................................................................
46
+ def feature_not_implemented(request)
47
+ iq_error(request, 'feature-not-implemented', 'feature not implemented')
48
+ end
49
+
50
+ #.........................................................................................................
51
+ def service_unavailable(request)
52
+ query_error(request, 'service-unavailable', 'service unavailable')
53
+ end
54
+
55
+ #.........................................................................................................
56
+ def item_not_found(request)
57
+ query_error(request, 'item-not-found', 'item not found')
58
+ end
59
+
60
+ ####........................................................................................................
61
+ private
62
+
63
+ #.........................................................................................................
64
+ def iq_error(request, condition, text)
65
+ iq = Xmpp::Iq.new(:error, request.from)
66
+ iq.id = request.id unless request.id.nil?
67
+ iq.error = Xmpp::ErrorResponse.new(condition, text)
68
+ Send(iq)
69
+ end
70
+
71
+ #.........................................................................................................
72
+ def query_error(request, condition, text)
73
+ iq = Xmpp::Iq.new(:error, request.from)
74
+ iq.id = request.id unless request.id.nil?
75
+ iq.query = Xmpp::IqQuery.new
76
+ iq.query.add_namespace(request.query.namespace)
77
+ iq.query.attributes['node'] = request.query.node if request.query.respond_to?(:node) and request.query.node
78
+ iq.error = Xmpp::ErrorResponse.new(condition, text)
79
+ Send(iq)
80
+ end
81
+
82
+ #.........................................................................................................
83
+ def command_error(params, condition, text)
84
+ iq = Xmpp::Iq.new(:error, params[:from])
85
+ iq.id = params[:id] unless params[:id].nil?
86
+ iq.command = Xmpp::IqCommand.new(params[:node], params[:action])
87
+ iq.command = Xmpp::ErrorResponse.new(condition, text)
88
+ Send(iq)
89
+ end
90
+
91
+ #### self
92
+ end
93
+
94
+ #.......................................................................................................
95
+ def initialize(errorcondition=nil, text=nil)
96
+ if errorcondition.nil?
97
+ super()
98
+ else
99
+ errortype = nil
100
+ errorcode = nil
101
+ @@Errors.each do |cond,type,code|
102
+ if errorcondition == cond
103
+ errortype = type
104
+ errorcode = code
105
+ end
106
+ end
107
+ raise ArgumentError, "Unknown error condition when initializing ErrorReponse" if errortype.nil? || errorcode.nil?
108
+ super()
109
+ self.error = errorcondition unless errorcondition.nil?
110
+ self.type = errortype unless errortype.nil?
111
+ self.code = errorcode unless errorcode.nil?
112
+ end
113
+ self.text = text unless text.nil?
114
+ end
115
+
116
+ #.......................................................................................................
117
+ def code
118
+ (c = attributes['code']).nil? ? nil : c.to_i
119
+ end
120
+
121
+ #.......................................................................................................
122
+ def code=(i)
123
+ attributes['code'] = i.nil? ? nil : i.to_s
124
+ end
125
+
126
+ #.......................................................................................................
127
+ def error
128
+ name = nil
129
+ each_element {|e| name = e.name if (e.namespace == 'urn:ietf:params:xml:ns:xmpp-stanzas') && (e.name != 'text') }
130
+ name
131
+ end
132
+
133
+ #.......................................................................................................
134
+ def error=(s)
135
+ xe = nil
136
+ each_element {|e| xe = e if (e.namespace == 'urn:ietf:params:xml:ns:xmpp-stanzas') && (e.name != 'text') }
137
+ unless xe.nil?
138
+ delete_element(xe)
139
+ end
140
+ add_element(s).add_namespace('urn:ietf:params:xml:ns:xmpp-stanzas')
141
+ end
142
+
143
+ #.......................................................................................................
144
+ def text
145
+ first_element_text('text') || super
146
+ end
147
+
148
+ #.......................................................................................................
149
+ def text=(s)
150
+ delete_elements('text')
151
+ unless s.nil?
152
+ e = add_element('text')
153
+ e.add_namespace('urn:ietf:params:xml:ns:xmpp-stanzas')
154
+ e.text = s
155
+ end
156
+ end
157
+
158
+ #.......................................................................................................
159
+ @@Errors = [['bad-request', :modify, 400],
160
+ ['conflict', :cancel, 409],
161
+ ['feature-not-implemented', :cancel, 501],
162
+ ['forbidden', :auth, 403],
163
+ ['gone', :modify, 302],
164
+ ['internal-server-error', :wait, 500],
165
+ ['item-not-found', :cancel, 404],
166
+ ['jid-malformed', :modify, 400],
167
+ ['not-acceptable', :modify, 406],
168
+ ['not-allowed', :cancel, 405],
169
+ ['not-authorized', :auth, 401],
170
+ ['payment-required', :auth, 402],
171
+ ['recipient-unavailable', :wait, 404],
172
+ ['redirect', :modify, 302],
173
+ ['registration-required', :auth, 407],
174
+ ['remote-server-not-found', :cancel, 404],
175
+ ['remote-server-timeout', :wait, 504],
176
+ ['resource-constraint', :wait, 500],
177
+ ['service-unavailable', :cancel, 503],
178
+ ['subscription-required', :auth, 407],
179
+ ['undefined-condition', nil, 500],
180
+ ['unexpected-request', :wait, 400]]
181
+
182
+ #### ErrorResponse
183
+ end
184
+
185
+ #### XMPP
186
+ end
187
+
188
+ #### AgentXmpp
189
+ end
@@ -0,0 +1,90 @@
1
+ # Original from XMPP4R - XMPP Library for Ruby Website::http://home.gna.org/xmpp4r/
2
+ ##############################################################################################################
3
+ module AgentXmpp
4
+
5
+ #####-------------------------------------------------------------------------------------------------------
6
+ module Xmpp
7
+
8
+ #####-------------------------------------------------------------------------------------------------------
9
+ class Iq < Stanza
10
+
11
+ #.....................................................................................................
12
+ name_xmlns 'iq', 'jabber:client'
13
+ xmpp_child :query, :error, :pubsub, :command, :bind, :session
14
+
15
+ #####-----------------------------------------------------------------------------------------------------
16
+ class << self
17
+
18
+ #.........................................................................................................
19
+ def bind(pipe)
20
+ iq = new_bind(AgentXmpp.jid)
21
+ Send(iq) do |r|
22
+ if r.type == :result and full_jid = r.first_element('//jid') and full_jid.text
23
+ AgentXmpp.jid = Jid.new(full_jid.text)
24
+ pipe.broadcast_to_delegates(:on_bind, pipe)
25
+ elsif r.type.eql?(:error) and r.bind
26
+ raise AgentXmppError, "resource bind failed"
27
+ end
28
+ end
29
+ end
30
+
31
+ #.........................................................................................................
32
+ def session(pipe)
33
+ iq = new_session
34
+ Send(iq) do |r|
35
+ if r.type == :result
36
+ pipe.broadcast_to_delegates(:on_start_session, pipe)
37
+ elsif r.type.eql?(:error) and r.session
38
+ raise AgentXmppError, "session start failed"
39
+ end
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ #.......................................................................................................
46
+ def new_bind(jid)
47
+ iq = Iq.new(:set)
48
+ iq.bind = REXML::Element.new('bind')
49
+ iq.bind.add_namespace('urn:ietf:params:xml:ns:xmpp-bind')
50
+ resource = iq.bind.add(REXML::Element.new('resource'))
51
+ resource.text = jid.resource
52
+ iq
53
+ end
54
+
55
+ #.......................................................................................................
56
+ def new_session
57
+ iq = Iq.new(:set)
58
+ iq.session =REXML::Element.new('session')
59
+ iq.session.add_namespace('urn:ietf:params:xml:ns:xmpp-session')
60
+ iq
61
+ end
62
+
63
+ #### self
64
+ end
65
+
66
+ #.......................................................................................................
67
+ def initialize(type = nil, to = nil)
68
+ super()
69
+ self.to = to unless to.nil?
70
+ self.type = type unless type.nil?
71
+ end
72
+
73
+ #.......................................................................................................
74
+ def queryns
75
+ (e = first_element('query')).nil? ? nil : e.namespace
76
+ end
77
+
78
+ #### Iq
79
+ end
80
+
81
+ #####-------------------------------------------------------------------------------------------------------
82
+ class IqQuery < Element
83
+ name_xmlns 'query'
84
+ end
85
+
86
+ #### XMPP
87
+ end
88
+
89
+ #### AgentXmpp
90
+ end
@@ -0,0 +1,54 @@
1
+ # Original from XMPP4R - XMPP Library for Ruby Website::http://home.gna.org/xmpp4r/
2
+ ##############################################################################################################
3
+ module AgentXmpp
4
+
5
+ #####-------------------------------------------------------------------------------------------------------
6
+ module Xmpp
7
+
8
+ #####-------------------------------------------------------------------------------------------------------
9
+ class IqCommand < Element
10
+
11
+ #.....................................................................................................
12
+ name_xmlns 'command', 'http://jabber.org/protocol/commands'
13
+ xmpp_attribute :node, :sessionid
14
+ xmpp_attribute :action, :status, :sym => true
15
+ xmpp_child :actions, :x
16
+
17
+ #####-------------------------------------------------------------------------------------------------------
18
+ class << self
19
+
20
+ #.........................................................................................................
21
+ def send_command(args, &blk)
22
+ iq = Xmpp::Iq.new(args[:iq_type] || :set, args[:to])
23
+ iq.id = args[:id] unless args[:id].nil?
24
+ iq.command = new(args[:node])
25
+ iq.command.action = args[:action] unless args[:action].nil?
26
+ iq.command.status = args[:status] unless args[:status].nil?
27
+ iq.command.sessionid = args[:sessionid] unless args[:sessionid].nil?
28
+ iq.command << args[:payload] unless args[:payload].nil?
29
+ if blk
30
+ Send(iq) do |r|
31
+ AgentXmpp.logger.info "RECEIVED RESPONSE: #{r.type} from #{r.from}"
32
+ blk.call(r.type, (r.type.eql?(:result) and r.command and r.command.x) ? r.command.x.to_native : nil)
33
+ end
34
+ else; Send(iq); end
35
+ end
36
+
37
+ #### self
38
+ end
39
+
40
+ #.....................................................................................................
41
+ def initialize(node=nil, action=nil)
42
+ super()
43
+ self.node = node if node
44
+ self.action = action if action
45
+ end
46
+
47
+ #### IqCommand
48
+ end
49
+
50
+ #### XMPP
51
+ end
52
+
53
+ #### AgentXmpp
54
+ end
@@ -0,0 +1,206 @@
1
+ # Original from XMPP4R - XMPP Library for Ruby Website::http://home.gna.org/xmpp4r/
2
+ ##############################################################################################################
3
+ module AgentXmpp
4
+
5
+ #####-------------------------------------------------------------------------------------------------------
6
+ module Xmpp
7
+
8
+ #####-------------------------------------------------------------------------------------------------------
9
+ class IqDiscoItems < IqQuery
10
+
11
+ #.........................................................................................................
12
+ name_xmlns 'query', 'http://jabber.org/protocol/disco#items'
13
+ xmpp_attribute :node
14
+
15
+ #####-----------------------------------------------------------------------------------------------------
16
+ class << self
17
+
18
+ #.........................................................................................................
19
+ def get(pipe, to=nil, node=nil)
20
+ iq = Iq.new(:get, to)
21
+ iq.query = new
22
+ iq.query.node = node if node
23
+ Send(iq) do |r|
24
+ if (r.type == :result) && r.query.kind_of?(Xmpp::IqDiscoItems)
25
+ pipe.broadcast_to_delegates(:on_discoitems_result, pipe, r)
26
+ elsif r.type.eql?(:error)
27
+ pipe.broadcast_to_delegates(:on_discoitems_error, pipe, r)
28
+ end
29
+ end
30
+ end
31
+
32
+ #.........................................................................................................
33
+ def result(pipe, request)
34
+ iq = Xmpp::Iq.new(:result, request.from.to_s)
35
+ iq.id = request.id unless request.id.nil?
36
+ iq.query = new
37
+ Send(iq)
38
+ end
39
+
40
+ #.........................................................................................................
41
+ def result_command_nodes(pipe, request)
42
+ from_jid = request.from.to_s
43
+ iq = Xmpp::Iq.new(:result, from_jid)
44
+ iq.id = request.id unless request.id.nil?
45
+ iq.query = new
46
+ iq.query.node = 'http://jabber.org/protocol/commands'
47
+ iq.query.items = BaseController.command_nodes(from_jid).map{|n| {:node => n, :name => n.humanize, :jid => AgentXmpp.jid.to_s}}
48
+ Send(iq)
49
+ end
50
+
51
+ #### self
52
+ end
53
+
54
+ #.........................................................................................................
55
+ def items
56
+ elements.to_a('item')
57
+ end
58
+
59
+ #.........................................................................................................
60
+ def items=(its)
61
+ its.each{|i| self << DiscoItem.new(i[:jid], i[:name], i[:node])}
62
+ end
63
+
64
+ #### IqDiscoItems
65
+ end
66
+
67
+ #####-------------------------------------------------------------------------------------------------------
68
+ class IqDiscoInfo < IqQuery
69
+
70
+ #.........................................................................................................
71
+ name_xmlns 'query', 'http://jabber.org/protocol/disco#info'
72
+ xmpp_attribute :node
73
+
74
+ #####-------------------------------------------------------------------------------------------------------
75
+ class << self
76
+
77
+ #.........................................................................................................
78
+ def get(pipe, to=nil, node=nil)
79
+ iq = Iq.new(:get, to)
80
+ iq.query = new
81
+ iq.query.node = node if node
82
+ Send(iq) do |r|
83
+ if (r.type == :result) && r.query.kind_of?(Xmpp::IqDiscoInfo)
84
+ pipe.broadcast_to_delegates(:on_discoinfo_result, pipe, r)
85
+ elsif r.type.eql?(:error)
86
+ pipe.broadcast_to_delegates(:on_discoinfo_error, pipe, r)
87
+ end
88
+ end
89
+ end
90
+
91
+ #.........................................................................................................
92
+ def result(pipe, request)
93
+ iq = Xmpp::Iq.new(:result, request.from.to_s)
94
+ iq.id = request.id unless request.id.nil?
95
+ iq.query = IqDiscoInfo.new
96
+ iq.query << DiscoIdentity.new(AgentXmpp::IDENTITY[:category], AgentXmpp::IDENTITY[:name], AgentXmpp::IDENTITY[:type])
97
+ iq.query.features = AgentXmpp::FEATURES
98
+ Send(iq)
99
+ end
100
+
101
+ #### self
102
+ end
103
+
104
+ #.........................................................................................................
105
+ def identities
106
+ elements.to_a('identity')
107
+ end
108
+
109
+ #.........................................................................................................
110
+ def features
111
+ elements.to_a('feature')
112
+ end
113
+
114
+ #.........................................................................................................
115
+ def features=(feats)
116
+ feats.each{|f| self << DiscoFeature.new(f)}
117
+ end
118
+
119
+ #### IqDiscoInfo
120
+ end
121
+
122
+ #####-------------------------------------------------------------------------------------------------------
123
+ class DiscoItem < Element
124
+
125
+ #.........................................................................................................
126
+ name_xmlns 'item', 'http://jabber.org/protocol/disco#items'
127
+ xmpp_attribute :node
128
+
129
+ #.........................................................................................................
130
+ def initialize(jid=nil, iname=nil, node=nil)
131
+ super()
132
+ self.jid = jid if jid
133
+ self.iname = iname if iname
134
+ self.node = node if node
135
+ end
136
+
137
+ #.........................................................................................................
138
+ def jid
139
+ Jid.new(attributes['jid'])
140
+ end
141
+
142
+ #.........................................................................................................
143
+ def jid=(val)
144
+ attributes['jid'] = val.to_s
145
+ end
146
+
147
+ def iname
148
+ attributes['name']
149
+ end
150
+
151
+ #.........................................................................................................
152
+ def iname=(val)
153
+ attributes['name'] = val
154
+ end
155
+
156
+ #### DiscoItem
157
+ end
158
+
159
+ #####-------------------------------------------------------------------------------------------------------
160
+ class DiscoIdentity < Element
161
+
162
+ #.........................................................................................................
163
+ name_xmlns 'identity', 'http://jabber.org/protocol/disco#info'
164
+ xmpp_attribute :node, :category, :type
165
+
166
+ #.........................................................................................................
167
+ def initialize(category=nil, iname=nil, type=nil)
168
+ super()
169
+ self.category = category if category
170
+ self.iname = iname if iname
171
+ self.type = type if type
172
+ end
173
+
174
+ def iname
175
+ attributes['name']
176
+ end
177
+
178
+ #.........................................................................................................
179
+ def iname=(val)
180
+ attributes['name'] = val
181
+ end
182
+
183
+ #### DiscoIdentity
184
+ end
185
+
186
+ #####-------------------------------------------------------------------------------------------------------
187
+ class DiscoFeature < Element
188
+
189
+ #.........................................................................................................
190
+ name_xmlns 'feature', 'http://jabber.org/protocol/disco#info'
191
+ xmpp_attribute :var
192
+
193
+ #.........................................................................................................
194
+ def initialize(var=nil)
195
+ super()
196
+ self.var = var
197
+ end
198
+
199
+ #### DiscoFeature
200
+ end
201
+
202
+ #### Xmpp
203
+ end
204
+
205
+ #### AgentXmpp
206
+ end