tp-blather 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. data/.autotest +13 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +19 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +8 -0
  6. data/CHANGELOG.md +249 -0
  7. data/Gemfile +4 -0
  8. data/Guardfile +5 -0
  9. data/LICENSE +22 -0
  10. data/README.md +413 -0
  11. data/Rakefile +20 -0
  12. data/TODO.md +2 -0
  13. data/blather.gemspec +51 -0
  14. data/examples/certs/README +20 -0
  15. data/examples/certs/ca-bundle.crt +3987 -0
  16. data/examples/echo.rb +19 -0
  17. data/examples/execute.rb +17 -0
  18. data/examples/ping_pong.rb +38 -0
  19. data/examples/print_hierarchy.rb +77 -0
  20. data/examples/rosterprint.rb +15 -0
  21. data/examples/stream_only.rb +28 -0
  22. data/examples/trusted_echo.rb +21 -0
  23. data/examples/xmpp4r/echo.rb +36 -0
  24. data/lib/blather.rb +112 -0
  25. data/lib/blather/cert_store.rb +53 -0
  26. data/lib/blather/client.rb +95 -0
  27. data/lib/blather/client/client.rb +345 -0
  28. data/lib/blather/client/dsl.rb +320 -0
  29. data/lib/blather/client/dsl/pubsub.rb +174 -0
  30. data/lib/blather/core_ext/eventmachine.rb +125 -0
  31. data/lib/blather/core_ext/ipaddr.rb +20 -0
  32. data/lib/blather/errors.rb +69 -0
  33. data/lib/blather/errors/sasl_error.rb +44 -0
  34. data/lib/blather/errors/stanza_error.rb +110 -0
  35. data/lib/blather/errors/stream_error.rb +84 -0
  36. data/lib/blather/file_transfer.rb +107 -0
  37. data/lib/blather/file_transfer/ibb.rb +68 -0
  38. data/lib/blather/file_transfer/s5b.rb +114 -0
  39. data/lib/blather/jid.rb +141 -0
  40. data/lib/blather/roster.rb +118 -0
  41. data/lib/blather/roster_item.rb +146 -0
  42. data/lib/blather/stanza.rb +167 -0
  43. data/lib/blather/stanza/disco.rb +32 -0
  44. data/lib/blather/stanza/disco/capabilities.rb +161 -0
  45. data/lib/blather/stanza/disco/disco_info.rb +205 -0
  46. data/lib/blather/stanza/disco/disco_items.rb +134 -0
  47. data/lib/blather/stanza/iq.rb +144 -0
  48. data/lib/blather/stanza/iq/command.rb +339 -0
  49. data/lib/blather/stanza/iq/ibb.rb +86 -0
  50. data/lib/blather/stanza/iq/ping.rb +50 -0
  51. data/lib/blather/stanza/iq/query.rb +53 -0
  52. data/lib/blather/stanza/iq/roster.rb +185 -0
  53. data/lib/blather/stanza/iq/s5b.rb +208 -0
  54. data/lib/blather/stanza/iq/si.rb +415 -0
  55. data/lib/blather/stanza/iq/vcard.rb +149 -0
  56. data/lib/blather/stanza/message.rb +428 -0
  57. data/lib/blather/stanza/message/muc_user.rb +119 -0
  58. data/lib/blather/stanza/muc/muc_user_base.rb +54 -0
  59. data/lib/blather/stanza/presence.rb +172 -0
  60. data/lib/blather/stanza/presence/c.rb +100 -0
  61. data/lib/blather/stanza/presence/muc.rb +35 -0
  62. data/lib/blather/stanza/presence/muc_user.rb +147 -0
  63. data/lib/blather/stanza/presence/status.rb +218 -0
  64. data/lib/blather/stanza/presence/subscription.rb +100 -0
  65. data/lib/blather/stanza/pubsub.rb +119 -0
  66. data/lib/blather/stanza/pubsub/affiliations.rb +79 -0
  67. data/lib/blather/stanza/pubsub/create.rb +65 -0
  68. data/lib/blather/stanza/pubsub/errors.rb +18 -0
  69. data/lib/blather/stanza/pubsub/event.rb +139 -0
  70. data/lib/blather/stanza/pubsub/items.rb +103 -0
  71. data/lib/blather/stanza/pubsub/publish.rb +103 -0
  72. data/lib/blather/stanza/pubsub/retract.rb +92 -0
  73. data/lib/blather/stanza/pubsub/subscribe.rb +68 -0
  74. data/lib/blather/stanza/pubsub/subscription.rb +135 -0
  75. data/lib/blather/stanza/pubsub/subscriptions.rb +83 -0
  76. data/lib/blather/stanza/pubsub/unsubscribe.rb +84 -0
  77. data/lib/blather/stanza/pubsub_owner.rb +51 -0
  78. data/lib/blather/stanza/pubsub_owner/delete.rb +52 -0
  79. data/lib/blather/stanza/pubsub_owner/purge.rb +52 -0
  80. data/lib/blather/stanza/x.rb +416 -0
  81. data/lib/blather/stream.rb +266 -0
  82. data/lib/blather/stream/client.rb +32 -0
  83. data/lib/blather/stream/component.rb +39 -0
  84. data/lib/blather/stream/features.rb +70 -0
  85. data/lib/blather/stream/features/register.rb +38 -0
  86. data/lib/blather/stream/features/resource.rb +63 -0
  87. data/lib/blather/stream/features/sasl.rb +190 -0
  88. data/lib/blather/stream/features/session.rb +45 -0
  89. data/lib/blather/stream/features/tls.rb +29 -0
  90. data/lib/blather/stream/parser.rb +102 -0
  91. data/lib/blather/version.rb +3 -0
  92. data/lib/blather/xmpp_node.rb +94 -0
  93. data/spec/blather/client/client_spec.rb +687 -0
  94. data/spec/blather/client/dsl/pubsub_spec.rb +492 -0
  95. data/spec/blather/client/dsl_spec.rb +266 -0
  96. data/spec/blather/errors/sasl_error_spec.rb +33 -0
  97. data/spec/blather/errors/stanza_error_spec.rb +129 -0
  98. data/spec/blather/errors/stream_error_spec.rb +108 -0
  99. data/spec/blather/errors_spec.rb +33 -0
  100. data/spec/blather/file_transfer_spec.rb +135 -0
  101. data/spec/blather/jid_spec.rb +87 -0
  102. data/spec/blather/roster_item_spec.rb +134 -0
  103. data/spec/blather/roster_spec.rb +107 -0
  104. data/spec/blather/stanza/discos/disco_info_spec.rb +247 -0
  105. data/spec/blather/stanza/discos/disco_items_spec.rb +154 -0
  106. data/spec/blather/stanza/iq/command_spec.rb +206 -0
  107. data/spec/blather/stanza/iq/ibb_spec.rb +124 -0
  108. data/spec/blather/stanza/iq/ping_spec.rb +45 -0
  109. data/spec/blather/stanza/iq/query_spec.rb +64 -0
  110. data/spec/blather/stanza/iq/roster_spec.rb +139 -0
  111. data/spec/blather/stanza/iq/s5b_spec.rb +57 -0
  112. data/spec/blather/stanza/iq/si_spec.rb +98 -0
  113. data/spec/blather/stanza/iq/vcard_spec.rb +93 -0
  114. data/spec/blather/stanza/iq_spec.rb +61 -0
  115. data/spec/blather/stanza/message/muc_user_spec.rb +152 -0
  116. data/spec/blather/stanza/message_spec.rb +282 -0
  117. data/spec/blather/stanza/presence/c_spec.rb +56 -0
  118. data/spec/blather/stanza/presence/muc_spec.rb +37 -0
  119. data/spec/blather/stanza/presence/muc_user_spec.rb +83 -0
  120. data/spec/blather/stanza/presence/status_spec.rb +144 -0
  121. data/spec/blather/stanza/presence/subscription_spec.rb +102 -0
  122. data/spec/blather/stanza/presence_spec.rb +125 -0
  123. data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
  124. data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
  125. data/spec/blather/stanza/pubsub/event_spec.rb +98 -0
  126. data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
  127. data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
  128. data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
  129. data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
  130. data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
  131. data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
  132. data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +74 -0
  133. data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
  134. data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
  135. data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
  136. data/spec/blather/stanza/pubsub_spec.rb +68 -0
  137. data/spec/blather/stanza/x_spec.rb +231 -0
  138. data/spec/blather/stanza_spec.rb +134 -0
  139. data/spec/blather/stream/client_spec.rb +1090 -0
  140. data/spec/blather/stream/component_spec.rb +108 -0
  141. data/spec/blather/stream/parser_spec.rb +152 -0
  142. data/spec/blather/stream/ssl_spec.rb +32 -0
  143. data/spec/blather/xmpp_node_spec.rb +47 -0
  144. data/spec/blather_spec.rb +34 -0
  145. data/spec/fixtures/pubsub.rb +311 -0
  146. data/spec/spec_helper.rb +17 -0
  147. data/yard/templates/default/class/html/handlers.erb +18 -0
  148. data/yard/templates/default/class/setup.rb +10 -0
  149. data/yard/templates/default/class/text/handlers.erb +1 -0
  150. metadata +459 -0
@@ -0,0 +1,54 @@
1
+ module Blather
2
+ class Stanza
3
+ class MUC
4
+
5
+ module MUCUserBase
6
+ MUC_USER_NAMESPACE = "http://jabber.org/protocol/muc#user"
7
+
8
+ def self.included(klass)
9
+ klass.extend ClassMethods
10
+ end
11
+
12
+ module ClassMethods
13
+ def new(*args)
14
+ super.tap { |e| e.muc_user }
15
+ end
16
+ end
17
+
18
+ def inherit(node)
19
+ muc_user.remove
20
+ super
21
+ self
22
+ end
23
+
24
+ def password
25
+ find_password_node && password_node.content
26
+ end
27
+
28
+ def password=(var)
29
+ password_node.content = var
30
+ end
31
+
32
+ def muc_user
33
+ unless muc_user = find_first('ns:x', :ns => MUC_USER_NAMESPACE)
34
+ self << (muc_user = XMPPNode.new('x', self.document))
35
+ muc_user.namespace = self.class.registered_ns
36
+ end
37
+ muc_user
38
+ end
39
+
40
+ def password_node
41
+ unless pw = find_password_node
42
+ muc_user << (pw = XMPPNode.new('password', self.document))
43
+ end
44
+ pw
45
+ end
46
+
47
+ def find_password_node
48
+ muc_user.find_first 'ns:password', :ns => MUC_USER_NAMESPACE
49
+ end
50
+ end # MUCUserBase
51
+
52
+ end # MUC
53
+ end # Stanza
54
+ end # Blather
@@ -0,0 +1,172 @@
1
+ module Blather
2
+ class Stanza
3
+
4
+ # # Presence Stanza
5
+ #
6
+ # [RFC 3921 Section 2.2 - Presence Syntax](http://xmpp.org/rfcs/rfc3921.html#stanzas-presence)
7
+ #
8
+ # Within Blather most of the interaction with Presence stanzas will be
9
+ # through one of its child classes: Status or Subscription.
10
+ #
11
+ # Presence stanzas are used to express an entity's current network
12
+ # availability (offline or online, along with various sub-states of the
13
+ # latter and optional user-defined descriptive text), and to notify other
14
+ # entities of that availability. Presence stanzas are also used to negotiate
15
+ # and manage subscriptions to the presence of other entities.
16
+ #
17
+ # ## "Type" Attribute
18
+ #
19
+ # The `type` attribute of a presence stanza is optional. A presence stanza
20
+ # that does not possess a `type` attribute is used to signal to the server
21
+ # that the sender is online and available for communication. If included,
22
+ # the `type` attribute specifies a lack of availability, a request to manage
23
+ # a subscription to another entity's presence, a request for another
24
+ # entity's current presence, or an error related to a previously-sent
25
+ # presence stanza. If included, the `type` attribute must have one of the
26
+ # following values:
27
+ #
28
+ # * `:unavailable` -- Signals that the entity is no longer available for
29
+ # communication
30
+ #
31
+ # * `:subscribe` -- The sender wishes to subscribe to the recipient's
32
+ # presence.
33
+ #
34
+ # * `:subscribed` -- The sender has allowed the recipient to receive their
35
+ # presence.
36
+ #
37
+ # * `:unsubscribe` -- The sender is unsubscribing from another entity's
38
+ # presence.
39
+ #
40
+ # * `:unsubscribed` -- The subscription request has been denied or a
41
+ # previously-granted subscription has been cancelled.
42
+ #
43
+ # * `:probe` -- A request for an entity's current presence; should be
44
+ # generated only by a server on behalf of a user.
45
+ #
46
+ # * `:error` -- An error has occurred regarding processing or delivery of a
47
+ # previously-sent presence stanza.
48
+ #
49
+ # Blather provides a helper for each possible type:
50
+ #
51
+ # Presence#unavailabe?
52
+ # Presence#unavailable?
53
+ # Presence#subscribe?
54
+ # Presence#subscribed?
55
+ # Presence#unsubscribe?
56
+ # Presence#unsubscribed?
57
+ # Presence#probe?
58
+ # Presence#error?
59
+ #
60
+ # Blather treats the `type` attribute like a normal ruby object attribute
61
+ # providing a getter and setter. The default `type` is nil.
62
+ #
63
+ # presence = Presence.new
64
+ # presence.type # => nil
65
+ # presence.type = :unavailable
66
+ # presence.unavailable? # => true
67
+ # presence.error? # => false
68
+ #
69
+ # presence.type = :invalid # => RuntimeError
70
+ #
71
+ # @handler :presence
72
+ class Presence < Stanza
73
+ # @private
74
+ VALID_TYPES = [ :unavailable,
75
+ :subscribe,
76
+ :subscribed,
77
+ :unsubscribe,
78
+ :unsubscribed,
79
+ :probe,
80
+ :error].freeze
81
+
82
+ register :presence
83
+
84
+ # Creates a class based on the presence type
85
+ # either a Status or Subscription object is created based
86
+ # on the type attribute.
87
+ # If neither is found it instantiates a Presence object
88
+ def self.import(node, *decorators) # :nodoc:
89
+ node.children.each do |e|
90
+ ns = e.namespace ? e.namespace.href : nil
91
+ klass = class_from_registration e.element_name, ns
92
+ decorators << klass if klass
93
+ end
94
+
95
+ case node['type']
96
+ when nil, 'unavailable'
97
+ decorators << Status
98
+ when /subscribe/
99
+ decorators << Subscription
100
+ end
101
+
102
+ super node, *decorators
103
+ end
104
+
105
+ # Ensure element_name is "presence" for all subclasses
106
+ def self.new(*args)
107
+ super :presence
108
+ end
109
+
110
+ # Check if the IQ is of type :unavailable
111
+ #
112
+ # @return [true, false]
113
+ def unavailable?
114
+ self.type == :unavailable
115
+ end
116
+
117
+ # Check if the IQ is of type :subscribe
118
+ #
119
+ # @return [true, false]
120
+ def subscribe?
121
+ self.type == :subscribe
122
+ end
123
+
124
+ # Check if the IQ is of type :subscribed
125
+ #
126
+ # @return [true, false]
127
+ def subscribed?
128
+ self.type == :subscribed
129
+ end
130
+
131
+ # Check if the IQ is of type :unsubscribe
132
+ #
133
+ # @return [true, false]
134
+ def unsubscribe?
135
+ self.type == :unsubscribe
136
+ end
137
+
138
+ # Check if the IQ is of type :unsubscribed
139
+ #
140
+ # @return [true, false]
141
+ def unsubscribed?
142
+ self.type == :unsubscribed
143
+ end
144
+
145
+ # Check if the IQ is of type :probe
146
+ #
147
+ # @return [true, false]
148
+ def probe?
149
+ self.type == :probe
150
+ end
151
+
152
+ # Check if the IQ is of type :error
153
+ #
154
+ # @return [true, false]
155
+ def error?
156
+ self.type == :error
157
+ end
158
+
159
+ # Ensures type is one of Blather::Stanza::Presence::VALID_TYPES
160
+ #
161
+ # @param [#to_sym] type the Presence type. Must be one of VALID_TYPES
162
+ def type=(type)
163
+ if type && !VALID_TYPES.include?(type.to_sym)
164
+ raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}"
165
+ end
166
+ super
167
+ end
168
+
169
+ end
170
+
171
+ end #Stanza
172
+ end
@@ -0,0 +1,100 @@
1
+ module Blather
2
+ class Stanza
3
+ class Presence
4
+
5
+ # # Entity Capabilities Stanza
6
+ #
7
+ # [XEP-0115 - Entity Capabilities](http://http://xmpp.org/extensions/xep-0115.html)
8
+ #
9
+ # Blather handles c nodes through this class. It provides a set of helper methods
10
+ # to quickly deal with capabilites presence stanzas.
11
+ #
12
+ # @handler :c
13
+ class C < Presence
14
+ register :c, :c, 'http://jabber.org/protocol/caps'
15
+
16
+ # @private
17
+ VALID_HASH_TYPES = %w[md2 md5 sha-1 sha-224 sha-256 sha-384 sha-512].freeze
18
+
19
+ def self.new(node = nil, ver = nil, hash = 'sha-1')
20
+ new_node = super()
21
+ new_node.c
22
+ new_node.hash = hash
23
+ new_node.node = node
24
+ new_node.ver = ver
25
+ parse new_node.to_xml
26
+ end
27
+
28
+ module InstanceMethods
29
+
30
+ # @private
31
+ def inherit(node)
32
+ c.remove
33
+ super
34
+ self
35
+ end
36
+
37
+ # Get the name of the node
38
+ #
39
+ # @return [String, nil]
40
+ def node
41
+ c[:node]
42
+ end
43
+
44
+ # Set the name of the node
45
+ #
46
+ # @param [String, nil] node the new node name
47
+ def node=(node)
48
+ c[:node] = node
49
+ end
50
+
51
+ # Get the name of the hash
52
+ #
53
+ # @return [Symbol, nil]
54
+ def hash
55
+ c[:hash] && c[:hash].to_sym
56
+ end
57
+
58
+ # Set the name of the hash
59
+ #
60
+ # @param [String, nil] hash the new hash name
61
+ def hash=(hash)
62
+ if hash && !VALID_HASH_TYPES.include?(hash.to_s)
63
+ raise ArgumentError, "Invalid Hash Type (#{hash}), use: #{VALID_HASH_TYPES*' '}"
64
+ end
65
+ c[:hash] = hash
66
+ end
67
+
68
+ # Get the ver
69
+ #
70
+ # @return [String, nil]
71
+ def ver
72
+ c[:ver]
73
+ end
74
+
75
+ # Set the ver
76
+ #
77
+ # @param [String, nil] ver the new ver
78
+ def ver=(ver)
79
+ c[:ver] = ver
80
+ end
81
+
82
+ # C node accessor
83
+ # If a c node exists it will be returned.
84
+ # Otherwise a new node will be created and returned
85
+ #
86
+ # @return [Blather::XMPPNode]
87
+ def c
88
+ unless c = find_first('ns:c', :ns => C.registered_ns)
89
+ self << (c = XMPPNode.new('c', self.document))
90
+ c.namespace = self.class.registered_ns
91
+ end
92
+ c
93
+ end
94
+ end
95
+
96
+ include InstanceMethods
97
+ end # C
98
+ end #Presence
99
+ end #Stanza
100
+ end
@@ -0,0 +1,35 @@
1
+ module Blather
2
+ class Stanza
3
+ class Presence
4
+
5
+ class MUC < Status
6
+ register :muc_join, :x, "http://jabber.org/protocol/muc"
7
+
8
+ def self.new(*args)
9
+ new_node = super
10
+ new_node.muc
11
+ new_node
12
+ end
13
+
14
+ module InstanceMethods
15
+ def inherit(node)
16
+ muc.remove
17
+ super
18
+ self
19
+ end
20
+
21
+ def muc
22
+ unless muc = find_first('ns:x', :ns => MUC.registered_ns)
23
+ self << (muc = XMPPNode.new('x', self.document))
24
+ muc.namespace = self.class.registered_ns
25
+ end
26
+ muc
27
+ end
28
+ end
29
+
30
+ include InstanceMethods
31
+ end # MUC
32
+
33
+ end # Presence
34
+ end # Stanza
35
+ end # Blather
@@ -0,0 +1,147 @@
1
+ require 'blather/stanza/muc/muc_user_base'
2
+
3
+ module Blather
4
+ class Stanza
5
+ class Presence
6
+
7
+ class MUCUser < Presence
8
+ include Blather::Stanza::MUC::MUCUserBase
9
+
10
+ def self.decorator_modules
11
+ super + [Blather::Stanza::MUC::MUCUserBase]
12
+ end
13
+
14
+ register :muc_user_presence, :x, MUC_USER_NAMESPACE
15
+
16
+ module InstanceMethods
17
+
18
+ def affiliation
19
+ item.affiliation
20
+ end
21
+
22
+ def affiliation=(val)
23
+ item.affiliation = val
24
+ end
25
+
26
+ def role
27
+ item.role
28
+ end
29
+
30
+ def role=(val)
31
+ item.role = val
32
+ end
33
+
34
+ def jid
35
+ item.jid
36
+ end
37
+
38
+ def jid=(val)
39
+ item.jid = val
40
+ end
41
+
42
+ def status_codes
43
+ status.map &:code
44
+ end
45
+
46
+ def status_codes=(val)
47
+ muc_user.remove_children :status
48
+ val.each do |code|
49
+ muc_user << Status.new(code)
50
+ end
51
+ end
52
+
53
+ def item
54
+ if item = muc_user.find_first('ns:item', :ns => MUCUser.registered_ns)
55
+ Item.new item
56
+ else
57
+ muc_user << (item = Item.new nil, nil, nil, self.document)
58
+ item
59
+ end
60
+ end
61
+
62
+ def status
63
+ muc_user.find('ns:status', :ns => MUCUser.registered_ns).map do |status|
64
+ Status.new status
65
+ end
66
+ end
67
+ end
68
+
69
+ include InstanceMethods
70
+
71
+ class Item < XMPPNode
72
+ def self.new(affiliation = nil, role = nil, jid = nil, document = nil)
73
+ new_node = super :item, document
74
+
75
+ case affiliation
76
+ when self
77
+ affiliation.document ||= document
78
+ return affiliation
79
+ when Nokogiri::XML::Node
80
+ new_node.inherit affiliation
81
+ when Hash
82
+ new_node.affiliation = affiliation[:affiliation]
83
+ new_node.role = affiliation[:role]
84
+ new_node.jid = affiliation[:jid]
85
+ else
86
+ new_node.affiliation = affiliation
87
+ new_node.role = role
88
+ new_node.jid = jid
89
+ end
90
+ new_node
91
+ end
92
+
93
+ def affiliation
94
+ read_attr :affiliation, :to_sym
95
+ end
96
+
97
+ def affiliation=(val)
98
+ write_attr :affiliation, val
99
+ end
100
+
101
+ def role
102
+ read_attr :role, :to_sym
103
+ end
104
+
105
+ def role=(val)
106
+ write_attr :role, val
107
+ end
108
+
109
+ def jid
110
+ read_attr :jid
111
+ end
112
+
113
+ def jid=(val)
114
+ write_attr :jid, val
115
+ end
116
+ end
117
+
118
+ class Status < XMPPNode
119
+ def self.new(code = nil)
120
+ new_node = super :status
121
+
122
+ case code
123
+ when self.class
124
+ return code
125
+ when Nokogiri::XML::Node
126
+ new_node.inherit code
127
+ when Hash
128
+ new_node.code = code[:code]
129
+ else
130
+ new_node.code = code
131
+ end
132
+ new_node
133
+ end
134
+
135
+ def code
136
+ read_attr :code, :to_i
137
+ end
138
+
139
+ def code=(val)
140
+ write_attr :code, val
141
+ end
142
+ end
143
+ end # MUC
144
+
145
+ end # Presence
146
+ end # Stanza
147
+ end # Blather