xmpp4r 0.4 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
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