xmpp4r 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/ChangeLog +12 -0
  2. data/Rakefile +2 -4
  3. data/data/doc/xmpp4r/examples/advanced/adventure/adventuremuc.rb +6 -6
  4. data/data/doc/xmpp4r/examples/advanced/adventure/world.rb +3 -3
  5. data/data/doc/xmpp4r/examples/advanced/minimuc.rb +5 -5
  6. data/data/doc/xmpp4r/examples/advanced/xmpping.rb +17 -4
  7. data/data/doc/xmpp4r/examples/advanced/xmppingrc.sample +5 -0
  8. data/data/doc/xmpp4r/examples/basic/echo_threaded.rb +6 -2
  9. data/data/doc/xmpp4r/examples/basic/muc_owner_config.rb +13 -0
  10. data/lib/xmpp4r.rb +0 -6
  11. data/lib/xmpp4r/bytestreams/helper/filetransfer.rb +6 -7
  12. data/lib/xmpp4r/bytestreams/helper/ibb/base.rb +5 -6
  13. data/lib/xmpp4r/bytestreams/helper/ibb/target.rb +3 -5
  14. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb +1 -1
  15. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/server.rb +10 -8
  16. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb +3 -5
  17. data/lib/xmpp4r/bytestreams/iq/bytestreams.rb +11 -17
  18. data/lib/xmpp4r/bytestreams/iq/si.rb +13 -32
  19. data/lib/xmpp4r/callbacks.rb +124 -0
  20. data/lib/xmpp4r/client.rb +2 -5
  21. data/lib/xmpp4r/command/helper/responder.rb +53 -0
  22. data/lib/xmpp4r/command/iq/command.rb +154 -0
  23. data/lib/xmpp4r/connection.rb +49 -12
  24. data/lib/xmpp4r/dataforms/x/data.rb +66 -29
  25. data/lib/xmpp4r/delay/x/delay.rb +2 -3
  26. data/lib/xmpp4r/discovery/iq/discoinfo.rb +20 -33
  27. data/lib/xmpp4r/discovery/iq/discoitems.rb +5 -28
  28. data/lib/xmpp4r/error.rb +5 -10
  29. data/lib/xmpp4r/feature_negotiation/iq/feature.rb +2 -20
  30. data/lib/xmpp4r/httpbinding.rb +5 -0
  31. data/lib/xmpp4r/httpbinding/client.rb +285 -0
  32. data/lib/xmpp4r/iq.rb +22 -41
  33. data/lib/xmpp4r/message.rb +8 -38
  34. data/lib/xmpp4r/muc.rb +2 -0
  35. data/lib/xmpp4r/muc/helper/mucclient.rb +50 -1
  36. data/lib/xmpp4r/muc/helper/simplemucclient.rb +2 -2
  37. data/lib/xmpp4r/muc/iq/mucowner.rb +11 -0
  38. data/lib/xmpp4r/muc/x/muc.rb +2 -30
  39. data/lib/xmpp4r/muc/x/mucuserinvite.rb +4 -2
  40. data/lib/xmpp4r/muc/x/mucuseritem.rb +4 -2
  41. data/lib/xmpp4r/presence.rb +7 -35
  42. data/lib/xmpp4r/pubsub.rb +1 -0
  43. data/lib/xmpp4r/pubsub/helper/nodebrowser.rb +174 -0
  44. data/lib/xmpp4r/pubsub/helper/nodehelper.rb +153 -0
  45. data/lib/xmpp4r/pubsub/helper/servicehelper.rb +326 -0
  46. data/lib/xmpp4r/pubsub/iq/pubsub.rb +19 -0
  47. data/lib/xmpp4r/pubsub/stanzas/event.rb +49 -0
  48. data/lib/xmpp4r/pubsub/stanzas/item.rb +27 -0
  49. data/lib/xmpp4r/pubsub/stanzas/items.rb +35 -0
  50. data/lib/xmpp4r/pubsub/stanzas/subscription.rb +58 -0
  51. data/lib/xmpp4r/query.rb +4 -32
  52. data/lib/xmpp4r/rexmladdons.rb +7 -772
  53. data/lib/xmpp4r/roster/helper/roster.rb +29 -50
  54. data/lib/xmpp4r/roster/iq/roster.rb +6 -35
  55. data/lib/xmpp4r/roster/x/roster.rb +13 -30
  56. data/lib/xmpp4r/rpc.rb +2 -0
  57. data/lib/xmpp4r/rpc/helper/client.rb +114 -0
  58. data/lib/xmpp4r/rpc/helper/server.rb +75 -0
  59. data/lib/xmpp4r/rpc/helper/xmlrpcaddons.rb +57 -0
  60. data/lib/xmpp4r/rpc/iq/rpc.rb +24 -0
  61. data/lib/xmpp4r/sasl.rb +1 -1
  62. data/lib/xmpp4r/semaphore.rb +38 -0
  63. data/lib/xmpp4r/stream.rb +104 -165
  64. data/lib/xmpp4r/streamparser.rb +2 -2
  65. data/lib/xmpp4r/vcard/iq/vcard.rb +5 -12
  66. data/lib/xmpp4r/version/helper/responder.rb +2 -1
  67. data/lib/xmpp4r/version/iq/version.rb +6 -18
  68. data/lib/xmpp4r/x.rb +20 -26
  69. data/lib/xmpp4r/xmpp4r.rb +1 -1
  70. data/lib/xmpp4r/xmppelement.rb +152 -0
  71. data/lib/xmpp4r/{xmlstanza.rb → xmppstanza.rb} +17 -29
  72. data/setup.rb +1 -0
  73. data/test/bytestreams/tc_ibb.rb +8 -8
  74. data/test/dataforms/tc_data.rb +81 -0
  75. data/test/lib/clienttester.rb +20 -17
  76. data/test/muc/tc_muc_mucclient.rb +48 -23
  77. data/test/muc/tc_muc_simplemucclient.rb +7 -4
  78. data/test/muc/tc_mucowner.rb +50 -0
  79. data/test/pubsub/tc_helper.rb +227 -0
  80. data/test/roster/tc_helper.rb +181 -55
  81. data/test/roster/tc_iqqueryroster.rb +33 -0
  82. data/test/roster/tc_xroster.rb +6 -3
  83. data/test/rpc/tc_helper.rb +84 -0
  84. data/test/tc_callbacks.rb +2 -1
  85. data/test/tc_class_names.rb +9 -1
  86. data/test/tc_error.rb +1 -0
  87. data/test/tc_iq.rb +13 -12
  88. data/test/tc_message.rb +1 -0
  89. data/test/tc_presence.rb +1 -0
  90. data/test/tc_rexml.rb +1 -1
  91. data/test/tc_stream.rb +147 -102
  92. data/test/tc_streamComponent.rb +94 -0
  93. data/test/tc_streamError.rb +67 -29
  94. data/test/tc_streamSend.rb +1 -1
  95. data/test/tc_xmppstanza.rb +125 -0
  96. data/test/ts_xmpp4r.rb +37 -28
  97. data/test/version/tc_helper.rb +14 -0
  98. data/test/version/tc_iqqueryversion.rb +4 -3
  99. metadata +163 -123
  100. data/data/doc/xmpp4r/examples/basic/echo_nonthreaded.rb +0 -32
  101. data/lib/callbacks.rb +0 -122
  102. data/test/tc_streamThreaded.rb +0 -168
  103. data/test/tc_xmlstanza.rb +0 -76
@@ -11,24 +11,8 @@ module Jabber
11
11
  # Feature negotiation,
12
12
  # can appear as direct child to Iq
13
13
  # or as child of IqSi
14
- class IqFeature < REXML::Element
15
- def initialize
16
- super('feature')
17
-
18
- add_namespace 'http://jabber.org/protocol/feature-neg'
19
- end
20
-
21
- def IqFeature.import(element)
22
- IqFeature::new.import(element)
23
- end
24
-
25
- def typed_add(element)
26
- if element.name == 'x' and element.namespace == 'jabber:x:data'
27
- super Dataforms::XData.new.import(element)
28
- else
29
- super element
30
- end
31
- end
14
+ class IqFeature < XMPPElement
15
+ name_xmlns 'feature', 'http://jabber.org/protocol/feature-neg'
32
16
 
33
17
  ##
34
18
  # First <x/> child with xmlns='jabber:x:data'
@@ -40,7 +24,5 @@ module Jabber
40
24
  res
41
25
  end
42
26
  end
43
-
44
- Iq.add_elementclass('feature', IqFeature)
45
27
  end
46
28
  end
@@ -0,0 +1,5 @@
1
+ # =XMPP4R - XMPP Library for Ruby
2
+ # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
+ # Website::http://home.gna.org/xmpp4r/
4
+
5
+ require 'xmpp4r/httpbinding/client'
@@ -0,0 +1,285 @@
1
+ # =XMPP4R - XMPP Library for Ruby
2
+ # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
+ # Website::http://home.gna.org/xmpp4r/
4
+
5
+
6
+ require 'xmpp4r/client'
7
+ require 'net/http'
8
+
9
+ module Jabber
10
+ module HTTPBinding
11
+ ##
12
+ # This class implements an alternative Client
13
+ # using HTTP Binding (JEP0124).
14
+ #
15
+ # This class is designed to be a drop-in replacement
16
+ # for Jabber::Client, except for the
17
+ # Jabber::HTTP::Client#connect method which takes an URI
18
+ # as argument.
19
+ #
20
+ # HTTP requests are buffered to not exceed the negotiated
21
+ # 'polling' and 'requests' parameters.
22
+ #
23
+ # Stanzas in HTTP resonses may be delayed to arrive in the
24
+ # order defined by 'rid' parameters.
25
+ #
26
+ # =Debugging
27
+ # Turning Jabber::debug to true will make debug output
28
+ # not only spit out stanzas but HTTP request/response
29
+ # bodies, too.
30
+ class Client < Jabber::Client
31
+
32
+ # Content-Type to be used for communication
33
+ # (you can set this to "text/html")
34
+ attr_accessor :http_content_type
35
+ # The server should wait this value seconds if
36
+ # there is no stanza to be received
37
+ attr_accessor :http_wait
38
+ # The server may hold this amount of stanzas
39
+ # to reduce number of HTTP requests
40
+ attr_accessor :http_hold
41
+
42
+ ##
43
+ # Initialize
44
+ # jid:: [JID or String]
45
+ def initialize(jid)
46
+ super
47
+
48
+ @lock = Mutex.new
49
+ @pending_requests = 0
50
+ @last_send = Time.at(0)
51
+ @send_buffer = ''
52
+
53
+ @http_wait = 20
54
+ @http_hold = 1
55
+ @http_content_type = 'text/xml; charset=utf-8'
56
+ end
57
+
58
+ ##
59
+ # Set up the stream using uri as the HTTP Binding URI
60
+ #
61
+ # You may optionally pass host and port parameters
62
+ # to make use of the JEP0124 'route' feature.
63
+ #
64
+ # uri:: [URI::Generic or String]
65
+ # host:: [String] Optional host to route to
66
+ # port:: [Fixnum] Port for route feature
67
+ def connect(uri, host=nil, port=5222)
68
+ uri = URI::parse(uri) unless uri.kind_of? URI::Generic
69
+ @uri = uri
70
+
71
+ @allow_tls = false # Shall be done at HTTP level
72
+ @stream_mechanisms = []
73
+ @stream_features = {}
74
+ @http_rid = IdGenerator.generate_id.to_i
75
+ @pending_rid = @http_rid
76
+ @pending_rid_lock = Mutex.new
77
+
78
+ req_body = REXML::Element.new('body')
79
+ req_body.attributes['rid'] = @http_rid
80
+ req_body.attributes['content'] = @http_content_type
81
+ req_body.attributes['hold'] = @http_hold.to_s
82
+ req_body.attributes['wait'] = @http_wait.to_s
83
+ req_body.attributes['to'] = @jid.domain
84
+ if host
85
+ req_body.attributes['route'] = 'xmpp:#{host}:#{port}'
86
+ end
87
+ req_body.attributes['secure'] = 'true'
88
+ req_body.attributes['xmlns'] = 'http://jabber.org/protocol/httpbind'
89
+ res_body = post(req_body)
90
+ unless res_body.name == 'body'
91
+ raise 'Response body is no <body/> element'
92
+ end
93
+
94
+ @streamid = res_body.attributes['authid']
95
+ @status = CONNECTED
96
+ @http_sid = res_body.attributes['sid']
97
+ @http_wait = res_body.attributes['wait'].to_i if res_body.attributes['wait']
98
+ @http_hold = res_body.attributes['hold'].to_i if res_body.attributes['hold']
99
+ @http_inactivity = res_body.attributes['inactivity'].to_i
100
+ @http_polling = res_body.attributes['polling'].to_i
101
+ @http_polling = 5 if @http_polling == 0
102
+ @http_requests = res_body.attributes['requests'].to_i
103
+ @http_requests = 1 if @http_requests == 0
104
+
105
+ receive_elements_with_rid(@http_rid, res_body.children)
106
+
107
+ @features_sem.run
108
+ end
109
+
110
+ ##
111
+ # Send a stanza, additionally with block
112
+ #
113
+ # This method ensures a 'jabber:client' namespace for
114
+ # the stanza
115
+ def send(xml, &block)
116
+ if xml.kind_of? REXML::Element
117
+ xml.add_namespace('jabber:client')
118
+ end
119
+
120
+ super
121
+ end
122
+
123
+ ##
124
+ # Ensure that there is one pending request
125
+ #
126
+ # Will be automatically called if you've sent
127
+ # a stanza.
128
+ def ensure_one_pending_request
129
+ return if is_disconnected?
130
+
131
+ if @lock.synchronize { @pending_requests } < 1
132
+ send_data('')
133
+ end
134
+ end
135
+
136
+ ##
137
+ # Close the session by sending
138
+ # <presence type='unavailable'/>
139
+ def close
140
+ @status = DISCONNECTED
141
+ send(Jabber::Presence.new.set_type(:unavailable))
142
+ end
143
+
144
+ private
145
+
146
+ ##
147
+ # Receive stanzas ensuring that the 'rid' order is kept
148
+ # result:: [REXML::Element]
149
+ def receive_elements_with_rid(rid, elements)
150
+ while rid > @pending_rid
151
+ @pending_rid_lock.lock
152
+ end
153
+ @pending_rid = rid + 1
154
+
155
+ elements.each { |e|
156
+ receive(e)
157
+ }
158
+
159
+ @pending_rid_lock.unlock
160
+ end
161
+
162
+ ##
163
+ # Do a POST request
164
+ def post(body)
165
+ body = body.to_s
166
+ request = Net::HTTP::Post.new(@uri.path)
167
+ request.content_length = body.size
168
+ request.body = body
169
+ request['Content-Type'] = @http_content_type
170
+ Jabber::debuglog("HTTP REQUEST (#{@pending_requests}/#{@http_requests}):\n#{request.body}")
171
+ response = Net::HTTP.start(@uri.host, @uri.port) { |http|
172
+ http.use_ssl = true if @uri.kind_of? URI::HTTPS
173
+ http.request(request)
174
+ }
175
+ Jabber::debuglog("HTTP RESPONSE (#{@pending_requests}/#{@http_requests}):\n#{response.body}")
176
+
177
+ unless response.kind_of? Net::HTTPSuccess
178
+ # Unfortunately, HTTPResponses aren't exceptions
179
+ # TODO: rescue'ing code should be able to distinguish
180
+ raise Net::HTTPBadResponse, "#{response.class}"
181
+ end
182
+
183
+ body = REXML::Document.new(response.body).root
184
+ if body.name != 'body' and body.namespace != 'http://jabber.org/protocol/httpbind'
185
+ raise REXML::ParseException.new('Malformed body')
186
+ end
187
+ body
188
+ end
189
+
190
+ ##
191
+ # Prepare data to POST and
192
+ # handle the result
193
+ def post_data(data)
194
+ req_body = nil
195
+ current_rid = nil
196
+
197
+ begin
198
+ begin
199
+ @lock.synchronize {
200
+ # Do not send unneeded requests
201
+ if data.size < 1 and @pending_requests > 0
202
+ return
203
+ end
204
+
205
+ req_body = "<body"
206
+ req_body += " rid='#{@http_rid += 1}'"
207
+ req_body += " sid='#{@http_sid}'"
208
+ req_body += " xmlns='http://jabber.org/protocol/httpbind'"
209
+ req_body += ">"
210
+ req_body += data
211
+ req_body += "</body>"
212
+ current_rid = @http_rid
213
+
214
+ @pending_requests += 1
215
+ @last_send = Time.now
216
+ }
217
+
218
+ res_body = post(req_body)
219
+
220
+ ensure
221
+ @lock.synchronize { @pending_requests -= 1 }
222
+ end
223
+
224
+ receive_elements_with_rid(current_rid, res_body.children)
225
+ ensure_one_pending_request
226
+
227
+ rescue REXML::ParseException
228
+ if @exception_block
229
+ Thread.new do
230
+ Thread.current.abort_on_exception = true
231
+ close; @exception_block.call(e, self, :parser)
232
+ end
233
+ else
234
+ puts "Exception caught when parsing HTTP response!"
235
+ close
236
+ raise
237
+ end
238
+
239
+ rescue StandardError => e
240
+ Jabber::debuglog("POST error (will retry): #{e.class}: #{e}")
241
+ receive_elements_with_rid(current_rid, [])
242
+ # It's not good to resend on *any* exception,
243
+ # but there are too many cases (Timeout, 404, 502)
244
+ # where resending is appropriate
245
+ # TODO: recognize these conditions and act appropriate
246
+ send_data(data)
247
+ end
248
+ end
249
+
250
+ ##
251
+ # Send data,
252
+ # buffered and obeying 'polling' and 'requests' limits
253
+ def send_data(data)
254
+ @lock.synchronize do
255
+
256
+ @send_buffer += data
257
+ limited_by_polling = (@last_send + @http_polling >= Time.now)
258
+ limited_by_requests = (@pending_requests + 1 > @http_requests)
259
+
260
+ # Can we send?
261
+ if !limited_by_polling and !limited_by_requests
262
+ data = @send_buffer
263
+ @send_buffer = ''
264
+
265
+ Thread.new do
266
+ Thread.current.abort_on_exception = true
267
+ post_data(data)
268
+ end
269
+
270
+ elsif !limited_by_requests
271
+ Thread.new do
272
+ Thread.current.abort_on_exception = true
273
+ # Defer until @http_polling has expired
274
+ wait = @last_send + @http_polling - Time.now
275
+ sleep(wait) if wait > 0
276
+ # Ignore locking, it's already threaded ;-)
277
+ send_data('')
278
+ end
279
+ end
280
+
281
+ end
282
+ end
283
+ end
284
+ end
285
+ end
@@ -2,17 +2,23 @@
2
2
  # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
3
  # Website::http://home.gna.org/xmpp4r/
4
4
 
5
- require 'xmpp4r/xmlstanza'
5
+ require 'xmpp4r/xmppstanza'
6
6
  require 'xmpp4r/jid'
7
7
  require 'digest/sha1'
8
8
 
9
+ require 'xmpp4r/query'
10
+ require 'xmpp4r/vcard/iq/vcard'
11
+
9
12
  module Jabber
10
13
  ##
11
14
  # IQ: Information/Query
12
15
  # (see RFC3920 - 9.2.3
13
16
  #
14
17
  # A class used to build/parse IQ requests/responses
15
- class Iq < XMLStanza
18
+ class Iq < XMPPStanza
19
+ name_xmlns 'iq', 'jabber:client'
20
+ force_xmlns true
21
+
16
22
  @@element_classes = {}
17
23
 
18
24
  ##
@@ -20,13 +26,14 @@ module Jabber
20
26
  # type:: [Symbol] or nil, see Iq#type
21
27
  # to:: [JID] Recipient
22
28
  def initialize(type = nil, to = nil)
23
- super("iq")
29
+ super()
30
+
24
31
  if not to.nil?
25
32
  set_to(to)
26
- end
33
+ end
27
34
  if not type.nil?
28
35
  set_type(type)
29
- end
36
+ end
30
37
  end
31
38
 
32
39
  ##
@@ -88,7 +95,7 @@ module Jabber
88
95
  ##
89
96
  # Returns the iq's query's namespace, or nil
90
97
  # result:: [String]
91
- def queryns
98
+ def queryns
92
99
  e = first_element('query')
93
100
  if e
94
101
  return e.namespace
@@ -100,30 +107,22 @@ module Jabber
100
107
  ##
101
108
  # Returns the iq's <vCard/> child, or nil
102
109
  # result:: [IqVcard]
103
- def vcard
110
+ def vcard
104
111
  first_element('vCard')
105
112
  end
106
113
 
107
114
  ##
108
- # Create a new iq from a stanza,
109
- # copies all attributes and children from xmlstanza
110
- # xmlstanza:: [REXML::Element] Source stanza
111
- # return:: [Iq] New stanza
112
- def Iq.import(xmlstanza)
113
- Iq::new.import(xmlstanza)
115
+ # Returns the iq's <pubsub/> child, or nil
116
+ # result:: [IqVcard]
117
+ def pubsub
118
+ first_element('pubsub')
114
119
  end
115
120
 
116
121
  ##
117
- # Add an element to the Iq stanza
118
- # element:: [REXML::Element] Element to add.
119
- # Will be automatically converted (imported) to
120
- # a class registered with add_elementclass
121
- def typed_add(element)
122
- if element.kind_of?(REXML::Element) && @@element_classes.has_key?(element.name)
123
- super(@@element_classes[element.name]::import(element))
124
- else
125
- super(element)
126
- end
122
+ # Returns the iq's <command/> child, or nil
123
+ # resulte:: [IqCommand]
124
+ def command
125
+ first_element("command")
127
126
  end
128
127
 
129
128
  ##
@@ -207,23 +206,5 @@ module Jabber
207
206
  iq.add(query)
208
207
  iq
209
208
  end
210
-
211
- ##
212
- # Add a class by name.
213
- # Elements with this name will be automatically converted
214
- # to the specific class.
215
- # Used for <query/>, <vCard>, <pubsub> etc.
216
- # name:: [String] Element name
217
- # elementclass:: [Class] Target class
218
- def Iq.add_elementclass(name, elementclass)
219
- @@element_classes[name] = elementclass
220
- end
221
209
  end
222
210
  end
223
-
224
- # Actually these should be included at the top,
225
- # but then they would be unable to call Iq.add_elementclass
226
- # because it hasn't just been defined.
227
-
228
- require 'xmpp4r/query'
229
- require 'xmpp4r/vcard/iq/vcard'
@@ -2,21 +2,26 @@
2
2
  # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
3
  # Website::http://home.gna.org/xmpp4r/
4
4
 
5
- require 'xmpp4r/xmlstanza'
5
+ require 'xmpp4r/xmppstanza'
6
6
  require 'xmpp4r/x'
7
7
 
8
8
  module Jabber
9
9
  ##
10
10
  # The Message class manages the <message/> stanzas,
11
11
  # which is used for all messaging communication.
12
- class Message < XMLStanza
12
+ class Message < XMPPStanza
13
+
14
+ name_xmlns 'message', 'jabber:client'
15
+ force_xmlns true
16
+
17
+ include XParent
13
18
 
14
19
  ##
15
20
  # Create a new message
16
21
  # >to:: a JID or a String object to send the message to.
17
22
  # >body:: the message's body
18
23
  def initialize(to = nil, body = nil)
19
- super("message")
24
+ super()
20
25
  if not to.nil?
21
26
  set_to(to)
22
27
  end
@@ -25,19 +30,6 @@ module Jabber
25
30
  end
26
31
  end
27
32
 
28
- ##
29
- # Add a sub-element
30
- #
31
- # Will be converted to [X] if named "x"
32
- # element:: [REXML::Element] to add
33
- def typed_add(element)
34
- if element.kind_of?(REXML::Element) && (element.name == 'x')
35
- super(X::import(element))
36
- else
37
- super(element)
38
- end
39
- end
40
-
41
33
  ##
42
34
  # Get the type of the Message stanza
43
35
  #
@@ -81,19 +73,6 @@ module Jabber
81
73
  self
82
74
  end
83
75
 
84
- ##
85
- # Get the first <x/> element in this stanza, or nil if none found.
86
- # namespace:: [String] Optional, find the first <x/> element having this xmlns
87
- # result:: [REXML::Element] or nil
88
- def x(namespace=nil)
89
- each_element('x') { |x|
90
- if namespace.nil? or namespace == x.namespace
91
- return x
92
- end
93
- }
94
- nil
95
- end
96
-
97
76
  ##
98
77
  # Returns the message's body, or nil.
99
78
  # This is the message's plain-text content.
@@ -101,15 +80,6 @@ module Jabber
101
80
  first_element_text('body')
102
81
  end
103
82
 
104
- ##
105
- # Create a new message from a stanza,
106
- # by copying all attributes and children from it.
107
- # xmlstanza:: [REXML::Element] Source
108
- # return:: [Message] Result
109
- def Message.import(xmlstanza)
110
- Message::new.import(xmlstanza)
111
- end
112
-
113
83
  ##
114
84
  # Sets the message's body
115
85
  #