xmpp4r 0.4 → 0.5

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 (68) hide show
  1. data/CHANGELOG +8 -0
  2. data/README.rdoc +4 -1
  3. data/Rakefile +10 -20
  4. data/data/doc/xmpp4r/examples/advanced/versionpoll.rb +20 -1
  5. data/lib/xmpp4r/bytestreams/helper/ibb/target.rb +7 -0
  6. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb +7 -1
  7. data/lib/xmpp4r/callbacks.rb +9 -0
  8. data/lib/xmpp4r/caps/c.rb +14 -0
  9. data/lib/xmpp4r/caps/helper/helper.rb +1 -4
  10. data/lib/xmpp4r/client.rb +42 -15
  11. data/lib/xmpp4r/connection.rb +7 -3
  12. data/lib/xmpp4r/debuglog.rb +22 -1
  13. data/lib/xmpp4r/discovery.rb +1 -0
  14. data/lib/xmpp4r/discovery/helper/helper.rb +58 -0
  15. data/lib/xmpp4r/discovery/iq/discoinfo.rb +2 -2
  16. data/lib/xmpp4r/discovery/iq/discoitems.rb +2 -2
  17. data/lib/xmpp4r/errors.rb +5 -2
  18. data/lib/xmpp4r/httpbinding/client.rb +9 -19
  19. data/lib/xmpp4r/last.rb +2 -0
  20. data/lib/xmpp4r/last/helper/helper.rb +37 -0
  21. data/lib/xmpp4r/last/iq/last.rb +67 -0
  22. data/lib/xmpp4r/location.rb +2 -0
  23. data/lib/xmpp4r/location/helper/helper.rb +56 -0
  24. data/lib/xmpp4r/location/location.rb +179 -0
  25. data/lib/xmpp4r/message.rb +32 -0
  26. data/lib/xmpp4r/presence.rb +1 -1
  27. data/lib/xmpp4r/pubsub/children/configuration.rb +1 -1
  28. data/lib/xmpp4r/pubsub/children/items.rb +11 -2
  29. data/lib/xmpp4r/pubsub/children/publish.rb +14 -0
  30. data/lib/xmpp4r/pubsub/children/retract.rb +41 -0
  31. data/lib/xmpp4r/pubsub/helper/nodebrowser.rb +2 -3
  32. data/lib/xmpp4r/pubsub/helper/nodehelper.rb +4 -4
  33. data/lib/xmpp4r/pubsub/helper/oauth_service_helper.rb +90 -0
  34. data/lib/xmpp4r/pubsub/helper/servicehelper.rb +58 -19
  35. data/lib/xmpp4r/reliable.rb +168 -0
  36. data/lib/xmpp4r/rexmladdons.rb +6 -0
  37. data/lib/xmpp4r/roster/helper/roster.rb +5 -2
  38. data/lib/xmpp4r/sasl.rb +19 -8
  39. data/lib/xmpp4r/stream.rb +133 -31
  40. data/lib/xmpp4r/streamparser.rb +9 -1
  41. data/lib/xmpp4r/test/listener_mocker.rb +118 -0
  42. data/lib/xmpp4r/xmpp4r.rb +3 -1
  43. data/test/bytestreams/tc_ibb.rb +6 -4
  44. data/test/bytestreams/tc_socks5bytestreams.rb +3 -2
  45. data/test/caps/tc_helper.rb +4 -2
  46. data/test/dataforms/tc_data.rb +1 -1
  47. data/test/last/tc_helper.rb +75 -0
  48. data/test/lib/clienttester.rb +43 -14
  49. data/test/muc/tc_muc_mucclient.rb +6 -2
  50. data/test/pubsub/tc_helper.rb +131 -8
  51. data/test/pubsub/tc_nodeconfig.rb +7 -0
  52. data/test/reliable/tc_disconnect_cleanup.rb +334 -0
  53. data/test/reliable/tc_disconnect_exception.rb +37 -0
  54. data/test/reliable/tc_listener_mocked_test.rb +68 -0
  55. data/test/reliable/tc_reliable_connection.rb +31 -0
  56. data/test/roster/tc_helper.rb +21 -11
  57. data/test/rpc/tc_helper.rb +2 -2
  58. data/test/tc_callbacks.rb +3 -3
  59. data/test/tc_message.rb +15 -0
  60. data/test/tc_stream.rb +59 -121
  61. data/test/tc_streamError.rb +2 -4
  62. data/test/tc_streamparser.rb +26 -13
  63. data/test/ts_xmpp4r.rb +0 -9
  64. data/test/tune/tc_helper_recv.rb +0 -2
  65. data/test/vcard/tc_helper.rb +1 -1
  66. data/xmpp4r.gemspec +31 -84
  67. metadata +116 -167
  68. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb.orig +0 -62
@@ -11,6 +11,8 @@ module Jabber
11
11
  # which is used for all messaging communication.
12
12
  class Message < XMPPStanza
13
13
 
14
+ CHAT_STATES = %w(active composing gone inactive paused).freeze
15
+
14
16
  name_xmlns 'message', 'jabber:client'
15
17
  force_xmlns true
16
18
 
@@ -144,5 +146,35 @@ module Jabber
144
146
  def thread
145
147
  first_element_text('thread')
146
148
  end
149
+
150
+ ##
151
+ # Returns the current chat state, or nil if no chat state is set
152
+ def chat_state
153
+ each_elements(*CHAT_STATES) { |el| return el.name.to_sym }
154
+ return nil
155
+ end
156
+
157
+ ##
158
+ # Sets the chat state :active, :composing, :gone, :inactive, :paused
159
+ def chat_state=(s)
160
+ s = s.to_s
161
+ raise InvalidChatState, "Chat state must be one of #{CHAT_STATES.join(', ')}" unless CHAT_STATES.include?(s)
162
+ CHAT_STATES.each { |state| delete_elements(state) }
163
+ add_element(REXML::Element.new(s).add_namespace('http://jabber.org/protocol/chatstates'))
164
+ end
165
+
166
+ ##
167
+ # Sets the message's chat state
168
+ def set_chat_state(s)
169
+ self.state = s
170
+ self
171
+ end
172
+
173
+ CHAT_STATES.each do |state|
174
+ define_method("#{state}?") do
175
+ chat_state == state.to_sym
176
+ end
177
+ end
178
+
147
179
  end
148
180
  end
@@ -18,7 +18,7 @@ module Jabber
18
18
 
19
19
  ##
20
20
  # Create presence stanza
21
- # show:: [String] Initial Availability Status
21
+ # show:: [Symbol] Initial Availability Status (see show)
22
22
  # status:: [String] Initial status message
23
23
  # priority:: [Fixnum] Initial priority value
24
24
  def initialize(show=nil, status=nil, priority=nil)
@@ -75,7 +75,7 @@ module Jabber
75
75
  form.add(pubsub_config)
76
76
  options.each_pair do |key, value|
77
77
  f = Jabber::Dataforms::XDataField.new(key)
78
- f.values = [value]
78
+ f.values = value.is_a?(Array) ? value : [value]
79
79
  form.add(f)
80
80
  end
81
81
 
@@ -10,6 +10,7 @@ module Jabber
10
10
  # Items
11
11
  # a collection of Items
12
12
  class Items < XMPPElement
13
+ include Enumerable
13
14
  name_xmlns 'items', NS_PUBSUB
14
15
 
15
16
  def node
@@ -22,13 +23,21 @@ module Jabber
22
23
  attributes['subid']
23
24
  end
24
25
  def subid=(mysubid)
25
- attributes['subid'] = mysubid
26
+ attributes['subid'] = mysubid.to_s
26
27
  end
27
28
  def max_items
28
29
  attributes['max_items']
29
30
  end
30
31
  def max_items=(mymaxitems)
31
- attributes['max_items'] = mymaxitems
32
+ attributes['max_items'] = mymaxitems.to_s
33
+ end
34
+
35
+ def items
36
+ get_elements("item")
37
+ end
38
+
39
+ def each(&block)
40
+ items.each(&block)
32
41
  end
33
42
  end
34
43
 
@@ -13,7 +13,21 @@ module Jabber
13
13
  # A <publish> XMPP element, see example 1 in
14
14
  # http://www.xmpp.org/extensions/xep-0060.html#intro-howitworks
15
15
  class Publish < XMPPElement
16
+ include Enumerable
16
17
  name_xmlns 'publish', NS_PUBSUB
18
+
19
+ ##
20
+ # support for enumerating <item> elements
21
+ def each(&block)
22
+ items.each(&block)
23
+ end
24
+
25
+ ##
26
+ # return child <item> elements
27
+ def items
28
+ get_elements("item")
29
+ end
30
+
17
31
  ##
18
32
  # return the node for this publication
19
33
  def node
@@ -0,0 +1,41 @@
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/xmppelement'
6
+
7
+ module Jabber
8
+ module PubSub
9
+
10
+ ##
11
+ # Retract
12
+ #
13
+ # A <retract> XMPP element, see example 103 in
14
+ # http://xmpp.org/extensions/xep-0060.html#publisher-delete
15
+ class Retract < XMPPElement
16
+ name_xmlns 'retract', NS_PUBSUB
17
+ ##
18
+ # get the node for this retraction
19
+ def node
20
+ attributes['node']
21
+ end
22
+
23
+ ##
24
+ # set the node for this retraction
25
+ def node=(s)
26
+ attributes['node'] = s
27
+ end
28
+
29
+ ##
30
+ # Get <item/> children
31
+ def items
32
+ res = []
33
+ each_element('item') { |item|
34
+ res << item
35
+ }
36
+ res
37
+ end
38
+ end
39
+ end
40
+ end
41
+
@@ -45,10 +45,10 @@ module Jabber
45
45
  iq.from = @stream.jid
46
46
  iq.add(Discovery::IqQueryDiscoItems.new)
47
47
  nodes = []
48
- @stream.send_with_id(iq) { |answer|
48
+ @stream.send_with_id(iq) do |answer|
49
49
  answer.query.each_element('item') do |item|
50
50
  nodes.push( {'node' => item.node,'name' => item.iname } )
51
- }
51
+ end
52
52
  end
53
53
  nodes
54
54
  end
@@ -95,7 +95,6 @@ module Jabber
95
95
  info['type'] = identity.type
96
96
  info['category'] = identity.category
97
97
  info['features'] = answer.query.features
98
-
99
98
  answer.query.each_element('x') { |x|
100
99
  info['nodeinformation'] = x
101
100
  }
@@ -30,7 +30,7 @@ module Jabber
30
30
  @jid = jid
31
31
  @stream = stream
32
32
 
33
- if create_if_not_exist and not node_exist?
33
+ if create_if_not_exist and !node_exist?
34
34
  # if no nodename is given a instant node will created
35
35
  # (if the service supports instant nodes)
36
36
  @nodename = create_node
@@ -87,7 +87,7 @@ module Jabber
87
87
  # gets all items from the node
88
88
  # get_all_items
89
89
  def get_all_items
90
- items(@nodename)
90
+ get_items_from(@nodename)
91
91
  end
92
92
 
93
93
  ##
@@ -95,7 +95,7 @@ module Jabber
95
95
  # get_items(count)
96
96
  # count:: [Fixnum]
97
97
  def get_items(count)
98
- items(@nodename,count)
98
+ get_items_from(@nodename,count)
99
99
  end
100
100
 
101
101
  ##
@@ -153,4 +153,4 @@ module Jabber
153
153
 
154
154
  end #class
155
155
  end #module
156
- end #module
156
+ end #module
@@ -0,0 +1,90 @@
1
+ module Jabber
2
+ module PubSub
3
+ # Jabber::Stream helper that will transparently sign PubSub requests
4
+ module OAuthPubSubStreamHelper
5
+ attr_accessor :pubsubjid, :oauth_consumer, :oauth_token, :oauth_options
6
+
7
+ # enhanced #send_with_id method that signs stanzas
8
+ def send_with_id(iq)
9
+ if iq.first_element("pubsub")
10
+ oauth = OAuthServiceHelper.create_oauth_node(self.jid, self.pubsubjid, self.oauth_consumer, self.oauth_token, self.oauth_options)
11
+ iq.pubsub.add(oauth)
12
+ end
13
+
14
+ super(iq)
15
+ end
16
+ end
17
+
18
+ # PubSub service helper for use with OAuth-authenticated nodes
19
+ class OAuthServiceHelper < ServiceHelper
20
+ def initialize(stream, pubsubjid, oauth_consumer, oauth_token, options = {})
21
+ # imbue the stream with magical OAuth signing powers
22
+ stream.extend(OAuthPubSubStreamHelper)
23
+ stream.oauth_consumer = oauth_consumer
24
+ stream.oauth_token = oauth_token
25
+ stream.oauth_options = options
26
+ stream.pubsubjid = pubsubjid
27
+
28
+ super(stream, pubsubjid)
29
+ end
30
+
31
+ # add the OAuth sauce (XEP-0235)
32
+ # The `options` hash may contain the following parameters:
33
+ # :oauth_nonce => nonce (one will be generated otherwise)
34
+ # :oauth_timestamp => timestamp (one will be generated otherwise)
35
+ # :oauth_signature_method => signature method (defaults to HMAC-SHA1)
36
+ # :oauth_version => OAuth version (defaults to "1.0")
37
+ def self.create_oauth_node(jid, pubsubjid, oauth_consumer, oauth_token, options = {})
38
+ require 'oauth'
39
+
40
+ request = OAuth::RequestProxy.proxy \
41
+ "method" => "iq",
42
+ "uri" => [jid.strip.to_s, pubsubjid.strip.to_s] * "&",
43
+ "parameters" => {
44
+ "oauth_consumer_key" => oauth_consumer.key,
45
+ "oauth_nonce" => options[:oauth_nonce] || OAuth::Helper.generate_nonce,
46
+ "oauth_timestamp" => options[:oauth_timestamp] || OAuth::Helper.generate_timestamp,
47
+ "oauth_token" => oauth_token.token,
48
+ "oauth_signature_method" => options[:oauth_signature_method] || "HMAC-SHA1",
49
+ "oauth_version" => options[:oauth_version] || "1.0"
50
+ }
51
+
52
+ request.sign!(:consumer => oauth_consumer, :token => oauth_token)
53
+
54
+ # TODO create XMPPElements for OAuth elements
55
+ oauth = REXML::Element.new("oauth")
56
+ oauth.attributes['xmlns'] = 'urn:xmpp:oauth:0'
57
+
58
+ oauth_consumer_key = REXML::Element.new("oauth_consumer_key")
59
+ oauth_consumer_key.text = request.oauth_consumer_key
60
+ oauth.add(oauth_consumer_key)
61
+
62
+ oauth_token_node = REXML::Element.new("oauth_token")
63
+ oauth_token_node.text = request.oauth_token
64
+ oauth.add(oauth_token_node)
65
+
66
+ oauth_signature_method = REXML::Element.new("oauth_signature_method")
67
+ oauth_signature_method.text = request.oauth_signature_method
68
+ oauth.add(oauth_signature_method)
69
+
70
+ oauth_signature = REXML::Element.new("oauth_signature")
71
+ oauth_signature.text = request.oauth_signature
72
+ oauth.add(oauth_signature)
73
+
74
+ oauth_timestamp = REXML::Element.new("oauth_timestamp")
75
+ oauth_timestamp.text = request.oauth_timestamp
76
+ oauth.add(oauth_timestamp)
77
+
78
+ oauth_nonce = REXML::Element.new("oauth_nonce")
79
+ oauth_nonce.text = request.oauth_nonce
80
+ oauth.add(oauth_nonce)
81
+
82
+ oauth_version = REXML::Element.new("oauth_version")
83
+ oauth_version.text = request.oauth_version
84
+ oauth.add(oauth_version)
85
+
86
+ oauth
87
+ end
88
+ end
89
+ end
90
+ end
@@ -2,11 +2,11 @@
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
- # It's recommented to read the XEP-0060 before you use this Helper. (Maybe its
5
+ # It's recommented to read the XEP-0060 before you use this Helper. (Maybe its
6
6
  # better not use the helper for now ) ;)
7
7
  # The whole code is getting better, but may still contain bugs - be careful!
8
- #
9
- # Maybe the following structure is good
8
+ #
9
+ # Maybe the following structure is good
10
10
  # ( taken from the xep-0060 )
11
11
  #
12
12
  # entity usecases
@@ -31,12 +31,12 @@
31
31
  # process pending subscriptions
32
32
  # manage subscriptions
33
33
  # manage affiliations
34
- #
34
+ #
35
35
  # collection nodes
36
- #
37
- # If someone want to implement something i think its better to do this in
36
+ #
37
+ # If someone want to implement something i think its better to do this in
38
38
  # this order because everyone who reads the xep-0060 do know where to search in the file
39
- #
39
+ #
40
40
  require 'xmpp4r/pubsub/iq/pubsub'
41
41
  require 'xmpp4r/pubsub/children/event'
42
42
  require 'xmpp4r/pubsub/children/item'
@@ -45,6 +45,7 @@ require 'xmpp4r/pubsub/children/subscription'
45
45
  require 'xmpp4r/pubsub/children/unsubscribe'
46
46
  require 'xmpp4r/pubsub/children/node_config'
47
47
  require 'xmpp4r/pubsub/children/subscription_config'
48
+ require 'xmpp4r/pubsub/children/retract'
48
49
  require 'xmpp4r/dataforms'
49
50
 
50
51
  module Jabber
@@ -118,12 +119,12 @@ module Jabber
118
119
  unsub.jid = @stream.jid.strip
119
120
  iq.pubsub.add(unsub)
120
121
  ret = false
121
- @stream.send_with_id(iq) { |reply|
122
+ @stream.send_with_id(iq) { |reply|
122
123
  ret = reply.kind_of?(Jabber::Iq) and reply.type == :result
123
124
  } # @stream.send_with_id(iq)
124
125
  ret
125
126
  end
126
-
127
+
127
128
  ##
128
129
  # gets all items from a pubsub node
129
130
  # node:: [String]
@@ -132,6 +133,7 @@ module Jabber
132
133
  def get_items_from(node, count=nil)
133
134
  iq = basic_pubsub_query(:get)
134
135
  items = Jabber::PubSub::Items.new
136
+ items.max_items = count
135
137
  items.node = node
136
138
  iq.pubsub.add(items)
137
139
  res = nil
@@ -148,19 +150,17 @@ module Jabber
148
150
  end
149
151
 
150
152
  ##
151
- # NOTE: this method sends only one item per publish request because some services
153
+ # NOTE: this method sends only one item per publish request because some services
152
154
  # may not allow batch processing. Maybe this will changed in the future?
153
155
  # node:: [String]
154
156
  # item:: [Jabber::PubSub::Item]
155
157
  # return:: true
156
- # it automatically generates an id for the item
157
158
  def publish_item_to(node,item)
158
159
  iq = basic_pubsub_query(:set)
159
160
  publish = iq.pubsub.add(REXML::Element.new('publish'))
160
161
  publish.attributes['node'] = node
161
-
162
+
162
163
  if item.kind_of?(Jabber::PubSub::Item)
163
- item.id = Jabber::IdGenerator.generate_id
164
164
  publish.add(item)
165
165
  @stream.send_with_id(iq)
166
166
  end
@@ -175,7 +175,7 @@ module Jabber
175
175
  iq = basic_pubsub_query(:set)
176
176
  publish = iq.pubsub.add(REXML::Element.new('publish'))
177
177
  publish.attributes['node'] = node
178
-
178
+
179
179
  if item.kind_of?(REXML::Element)
180
180
  xmlitem = Jabber::PubSub::Item.new
181
181
  xmlitem.id = id
@@ -187,6 +187,32 @@ module Jabber
187
187
  @stream.send_with_id(iq)
188
188
  end
189
189
 
190
+ ##
191
+ # deletes an item from a persistent node
192
+ # node:: [String]
193
+ # item_id:: [String] or [Array] of [String]
194
+ # return:: true
195
+ def delete_item_from(node, item_id)
196
+ iq = basic_pubsub_query(:set)
197
+ retract = iq.pubsub.add(Jabber::PubSub::Retract.new)
198
+ retract.node = node
199
+
200
+ if item_id.kind_of? Array
201
+ item_id.each { |id|
202
+ xmlitem = Jabber::PubSub::Item.new
203
+ xmlitem.id = id
204
+ retract.add(xmlitem)
205
+ }
206
+ else
207
+ xmlitem = Jabber::PubSub::Item.new
208
+ xmlitem.id = item_id
209
+ retract.add(xmlitem)
210
+ end
211
+
212
+ @stream.send_with_id(iq)
213
+ end
214
+
215
+
190
216
  ##
191
217
  # purges all items on a persistent node
192
218
  # node:: [String]
@@ -256,8 +282,8 @@ module Jabber
256
282
  # options:: [Jabber::PubSub::NodeConfig]
257
283
  # return:: true on success
258
284
  def set_config_for(node, config)
259
- iq = basic_pubsub_query( :set )
260
- iq.pubsub.add(config.form)
285
+ iq = basic_pubsub_query(:set, true)
286
+ iq.pubsub.add(config)
261
287
  @stream.send_with_id(iq)
262
288
  end
263
289
 
@@ -271,6 +297,19 @@ module Jabber
271
297
  @stream.send_with_id(iq)
272
298
  end
273
299
 
300
+ def set_affiliations(node, jid, role = 'publisher')
301
+ iq = basic_pubsub_query(:set, true)
302
+ affiliations = iq.pubsub.add(REXML::Element.new('affiliations'))
303
+ affiliations.attributes['node'] = node
304
+ affiliation = affiliations.add(REXML::Element.new('affiliation'))
305
+ affiliation.attributes['jid'] = jid.to_s
306
+ affiliation.attributes['affiliation'] = role.to_s
307
+ res = nil
308
+ @stream.send_with_id(iq) { |reply|
309
+ true
310
+ }
311
+ res
312
+ end
274
313
 
275
314
  ##
276
315
  # shows the affiliations on a pubsub service
@@ -316,7 +355,7 @@ module Jabber
316
355
  if reply.pubsub.first_element('subscriptions').attributes['node'] == node
317
356
  reply.pubsub.first_element('subscriptions').each_element('subscription') { |subscription|
318
357
  res << PubSub::Subscription.import(subscription)
319
- }
358
+ }
320
359
  end
321
360
  end
322
361
  true
@@ -359,7 +398,7 @@ module Jabber
359
398
  # jid:: [Jabber::JID] or [String]
360
399
  # options:: [Jabber::PubSub::SubscriptionConfig} specifying configuration options
361
400
  # subid:: [String] or nil
362
- # return:: true
401
+ # return:: true
363
402
  def set_options_for(node, jid, options, subid = nil)
364
403
  iq = basic_pubsub_query(:set)
365
404
  iq.pubsub.add(Jabber::PubSub::SubscriptionConfig.new(node, jid.kind_of?(String) ? Jabber::JID.new(jid).strip: jid.strip, options, subid))
@@ -370,7 +409,7 @@ module Jabber
370
409
 
371
410
  ret
372
411
  end
373
-
412
+
374
413
  ##
375
414
  # String representation
376
415
  # result:: [String] The PubSub service's JID