xmpp4r 0.5 → 0.5.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.
- data/CHANGELOG +21 -0
- data/README.rdoc +29 -38
- data/Rakefile +11 -18
- data/data/doc/xmpp4r/examples/advanced/recvfile.rb +5 -4
- data/lib/xmpp4r/bytestreams/helper/filetransfer.rb +2 -0
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb +1 -0
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb +1 -1
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb +4 -1
- data/lib/xmpp4r/caps/helper/generator.rb +2 -2
- data/lib/xmpp4r/client.rb +18 -4
- data/lib/xmpp4r/connection.rb +8 -5
- data/lib/xmpp4r/delay/x/delay.rb +1 -1
- data/lib/xmpp4r/entity_time.rb +6 -0
- data/lib/xmpp4r/entity_time/iq.rb +45 -0
- data/lib/xmpp4r/entity_time/responder.rb +57 -0
- data/lib/xmpp4r/httpbinding/client.rb +178 -44
- data/lib/xmpp4r/message.rb +46 -0
- data/lib/xmpp4r/muc/helper/mucclient.rb +8 -1
- data/lib/xmpp4r/observable.rb +9 -0
- data/lib/xmpp4r/observable/contact.rb +61 -0
- data/lib/xmpp4r/observable/helper.rb +389 -0
- data/lib/xmpp4r/observable/observable_thing.rb +191 -0
- data/lib/xmpp4r/observable/pubsub.rb +211 -0
- data/lib/xmpp4r/observable/subscription.rb +57 -0
- data/lib/xmpp4r/observable/thread_store.rb +65 -0
- data/lib/xmpp4r/pubsub/children/unsubscribe.rb +16 -1
- data/lib/xmpp4r/pubsub/helper/servicehelper.rb +48 -14
- data/lib/xmpp4r/rexmladdons.rb +46 -6
- data/lib/xmpp4r/roster/helper/roster.rb +19 -9
- data/lib/xmpp4r/sasl.rb +1 -1
- data/lib/xmpp4r/stream.rb +2 -2
- data/lib/xmpp4r/xmpp4r.rb +1 -1
- data/test/bytestreams/tc_socks5bytestreams.rb +2 -2
- data/test/entity_time/tc_responder.rb +65 -0
- data/test/roster/tc_helper.rb +2 -3
- data/test/tc_message.rb +52 -1
- data/test/tc_stream.rb +2 -2
- data/test/tc_streamComponent.rb +11 -1
- data/test/ts_xmpp4r.rb +11 -2
- data/xmpp4r.gemspec +20 -9
- metadata +41 -35
@@ -0,0 +1,65 @@
|
|
1
|
+
# =XMPP4R - XMPP Library for Ruby
|
2
|
+
#
|
3
|
+
# This file's copyright (c) 2009 by Pablo Lorenzzoni <pablo@propus.com.br>
|
4
|
+
# License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
|
5
|
+
# Website::http://home.gna.org/xmpp4r/
|
6
|
+
#
|
7
|
+
# Class ThreadStore
|
8
|
+
class ThreadStore
|
9
|
+
attr_reader :cycles, :max
|
10
|
+
|
11
|
+
# Create a new ThreadStore.
|
12
|
+
#
|
13
|
+
# max:: max number of threads to store (when exhausted, older threads will
|
14
|
+
# just be killed and discarded). Default is 100. If 0 or negative no
|
15
|
+
# threads will be discarded until #keep is called.
|
16
|
+
def initialize(max = 100)
|
17
|
+
@store = []
|
18
|
+
@max = max > 0 ? max : 0
|
19
|
+
@cycles = 0
|
20
|
+
@killer_thread = Thread.new do
|
21
|
+
loop do
|
22
|
+
sleep 2 while @store.empty?
|
23
|
+
sleep 1
|
24
|
+
@store.each_with_index do |thread, i|
|
25
|
+
th = @store.delete_at(i) if thread.nil? or ! thread.alive?
|
26
|
+
th = nil
|
27
|
+
end
|
28
|
+
@cycles += 1
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def inspect # :nodoc:
|
34
|
+
sprintf("#<%s:0x%x @max=%d, @size=%d @cycles=%d>", self.class.name, __id__, @max, size, @cycles)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Add a new thread to the ThreadStore
|
38
|
+
def add(thread)
|
39
|
+
if thread.instance_of?(Thread) and thread.respond_to?(:alive?)
|
40
|
+
@store << thread
|
41
|
+
keep(@max) if @max > 0
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Keep only the number passed of threads
|
46
|
+
#
|
47
|
+
# n:: number of threads to keep (default to @max if @max > 0)
|
48
|
+
def keep(n = nil)
|
49
|
+
if n.nil?
|
50
|
+
raise ArgumentError, "You need to pass the number of threads to keep!" if @max == 0
|
51
|
+
n = @max
|
52
|
+
end
|
53
|
+
@store.shift.kill while @store.length > n
|
54
|
+
end
|
55
|
+
|
56
|
+
# Kill all threads
|
57
|
+
def kill!
|
58
|
+
@store.shift.kill while @store.length > 0
|
59
|
+
end
|
60
|
+
|
61
|
+
# How long is our ThreadStore
|
62
|
+
def size; @store.length; end
|
63
|
+
|
64
|
+
end # of class ThreadStore
|
65
|
+
|
@@ -10,10 +10,11 @@ module Jabber
|
|
10
10
|
# Unsubscribe
|
11
11
|
class Unsubscribe < XMPPElement
|
12
12
|
name_xmlns 'unsubscribe'
|
13
|
-
def initialize(myjid=nil,mynode=nil)
|
13
|
+
def initialize(myjid=nil,mynode=nil,mysubid=nil)
|
14
14
|
super()
|
15
15
|
jid = myjid
|
16
16
|
node = mynode
|
17
|
+
subid = mysubid
|
17
18
|
end
|
18
19
|
|
19
20
|
##
|
@@ -43,6 +44,20 @@ module Jabber
|
|
43
44
|
def node=(mynode)
|
44
45
|
attributes['node'] = mynode
|
45
46
|
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# shows the subid
|
50
|
+
# return:: [String]
|
51
|
+
def subid
|
52
|
+
attributes['subid']
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# sets the subid
|
57
|
+
# =:: [String]
|
58
|
+
def subid=(mysubid)
|
59
|
+
attributes['subid'] = mysubid
|
60
|
+
end
|
46
61
|
end
|
47
62
|
end
|
48
63
|
end
|
@@ -85,6 +85,20 @@ module Jabber
|
|
85
85
|
|
86
86
|
res
|
87
87
|
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# get subids for a passed node
|
91
|
+
# return:: [Array] of subids
|
92
|
+
def get_subids_for(node)
|
93
|
+
ret = []
|
94
|
+
get_subscriptions_from_all_nodes.each do |subscription|
|
95
|
+
if subscription.node == node
|
96
|
+
ret << subscription.subid
|
97
|
+
end
|
98
|
+
end
|
99
|
+
return ret
|
100
|
+
end
|
101
|
+
|
88
102
|
##
|
89
103
|
# subscribe to a node
|
90
104
|
# node:: [String]
|
@@ -113,15 +127,26 @@ module Jabber
|
|
113
127
|
# subid:: [String] or nil (not supported)
|
114
128
|
# return:: true
|
115
129
|
def unsubscribe_from(node, subid=nil)
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
130
|
+
ret = []
|
131
|
+
if subid.nil?
|
132
|
+
subids = get_subids_for(node)
|
133
|
+
else
|
134
|
+
subids = [ subid ]
|
135
|
+
end
|
136
|
+
subids << nil if subids.empty?
|
137
|
+
subids.each do |sid|
|
138
|
+
iq = basic_pubsub_query(:set)
|
139
|
+
unsub = PubSub::Unsubscribe.new
|
140
|
+
unsub.node = node
|
141
|
+
unsub.jid = @stream.jid.strip
|
142
|
+
unsub.subid = sid
|
143
|
+
iq.pubsub.add(unsub)
|
144
|
+
res = false
|
145
|
+
@stream.send_with_id(iq) { |reply|
|
146
|
+
res = reply.kind_of?(Jabber::Iq) and reply.type == :result
|
147
|
+
} # @stream.send_with_id(iq)
|
148
|
+
ret << res
|
149
|
+
end
|
125
150
|
ret
|
126
151
|
end
|
127
152
|
|
@@ -129,11 +154,21 @@ module Jabber
|
|
129
154
|
# gets all items from a pubsub node
|
130
155
|
# node:: [String]
|
131
156
|
# count:: [Fixnum]
|
157
|
+
# subid:: [String]
|
132
158
|
# return:: [Hash] { id => [Jabber::PubSub::Item] }
|
133
|
-
def get_items_from(node, count=nil)
|
159
|
+
def get_items_from(node, count=nil, subid=nil)
|
160
|
+
if subid.nil?
|
161
|
+
# Hm... no subid passed. Let's see if we can provide one.
|
162
|
+
subids = get_subids_for(node)
|
163
|
+
if ! subids.empty?
|
164
|
+
# If more than one, sorry. We'll just respect the first.
|
165
|
+
subid = subids[0]
|
166
|
+
end
|
167
|
+
end
|
134
168
|
iq = basic_pubsub_query(:get)
|
135
169
|
items = Jabber::PubSub::Items.new
|
136
170
|
items.max_items = count
|
171
|
+
items.subid = subid unless subid.nil? # if subid is still nil, we haven't any... why bother?
|
137
172
|
items.node = node
|
138
173
|
iq.pubsub.add(items)
|
139
174
|
res = nil
|
@@ -157,7 +192,7 @@ module Jabber
|
|
157
192
|
# return:: true
|
158
193
|
def publish_item_to(node,item)
|
159
194
|
iq = basic_pubsub_query(:set)
|
160
|
-
|
195
|
+
publish = iq.pubsub.add(REXML::Element.new('publish'))
|
161
196
|
publish.attributes['node'] = node
|
162
197
|
|
163
198
|
if item.kind_of?(Jabber::PubSub::Item)
|
@@ -212,13 +247,12 @@ module Jabber
|
|
212
247
|
@stream.send_with_id(iq)
|
213
248
|
end
|
214
249
|
|
215
|
-
|
216
250
|
##
|
217
251
|
# purges all items on a persistent node
|
218
252
|
# node:: [String]
|
219
253
|
# return:: true
|
220
254
|
def purge_items_from(node)
|
221
|
-
iq = basic_pubsub_query(:set)
|
255
|
+
iq = basic_pubsub_query(:set, true)
|
222
256
|
purge = REXML::Element.new('purge')
|
223
257
|
purge.attributes['node'] = node
|
224
258
|
iq.pubsub.add(purge)
|
@@ -354,7 +388,7 @@ module Jabber
|
|
354
388
|
res = []
|
355
389
|
if reply.pubsub.first_element('subscriptions').attributes['node'] == node
|
356
390
|
reply.pubsub.first_element('subscriptions').each_element('subscription') { |subscription|
|
357
|
-
|
391
|
+
res << PubSub::Subscription.import(subscription)
|
358
392
|
}
|
359
393
|
end
|
360
394
|
end
|
data/lib/xmpp4r/rexmladdons.rb
CHANGED
@@ -25,10 +25,11 @@ module REXML
|
|
25
25
|
|
26
26
|
##
|
27
27
|
# Replaces or adds a child element of name <tt>e</tt> with text <tt>t</tt>.
|
28
|
-
def replace_element_text(e, t)
|
29
|
-
el = first_element(e)
|
28
|
+
def replace_element_text(e, t, namespace = nil)
|
29
|
+
el = first_element(e, namespace)
|
30
30
|
if el.nil?
|
31
31
|
el = REXML::Element.new(e)
|
32
|
+
el.add_namespace(namespace)
|
32
33
|
add_element(el)
|
33
34
|
end
|
34
35
|
if t
|
@@ -36,18 +37,42 @@ module REXML
|
|
36
37
|
end
|
37
38
|
self
|
38
39
|
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Replaces or adds a child element of name <tt>e</tt> with content of <tt>t</tt>.
|
43
|
+
def replace_element_content(e, c, namespace = nil)
|
44
|
+
el = first_element(e, namespace)
|
45
|
+
if el.nil?
|
46
|
+
el = REXML::Element.new(e)
|
47
|
+
el.add_namespace(namespace)
|
48
|
+
add_element(el)
|
49
|
+
end
|
50
|
+
if c
|
51
|
+
el.children.each do |ch|
|
52
|
+
ch.remove
|
53
|
+
end
|
54
|
+
c.root.children.each do |ch|
|
55
|
+
el.add ch
|
56
|
+
end
|
57
|
+
end
|
58
|
+
self
|
59
|
+
end
|
39
60
|
|
40
61
|
##
|
41
62
|
# Returns first element of name <tt>e</tt>
|
42
|
-
def first_element(e)
|
43
|
-
|
63
|
+
def first_element(e, namespace = nil)
|
64
|
+
if namespace
|
65
|
+
each_element_with_attribute("xmlns", namespace, 1, e) { |el| return el }
|
66
|
+
else
|
67
|
+
each_element(e) { |el| return el }
|
68
|
+
end
|
44
69
|
return nil
|
45
70
|
end
|
46
71
|
|
47
72
|
##
|
48
73
|
# Returns text of first element of name <tt>e</tt>
|
49
|
-
def first_element_text(e)
|
50
|
-
el = first_element(e)
|
74
|
+
def first_element_text(e, namespace = nil)
|
75
|
+
el = first_element(e, namespace)
|
51
76
|
if el
|
52
77
|
return el.text
|
53
78
|
else
|
@@ -55,6 +80,21 @@ module REXML
|
|
55
80
|
end
|
56
81
|
end
|
57
82
|
|
83
|
+
##
|
84
|
+
# This method works like <tt>first_element_text</tt> except that it
|
85
|
+
# returns content of all children, not just the value of the first
|
86
|
+
# child text element.
|
87
|
+
#
|
88
|
+
# Returns content of first element of name <tt>e</tt>
|
89
|
+
def first_element_content(e, namespace = nil)
|
90
|
+
el = first_element(e, namespace)
|
91
|
+
if el
|
92
|
+
return el.children.join
|
93
|
+
else
|
94
|
+
return nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
58
98
|
# This method does exactly the same thing as add(), but it can be
|
59
99
|
# overriden by subclasses to provide on-the-fly object creations.
|
60
100
|
# For example, if you import a REXML::Element of name 'plop', and you
|
@@ -229,9 +229,9 @@ module Jabber
|
|
229
229
|
@presence_cbs.process(item, oldpres, pres)
|
230
230
|
}
|
231
231
|
else
|
232
|
-
oldpres = item.
|
232
|
+
oldpres = item.presence_of(pres.from).nil? ?
|
233
233
|
nil :
|
234
|
-
Presence.new.import(item.
|
234
|
+
Presence.new.import(item.presence_of(pres.from))
|
235
235
|
|
236
236
|
item.add_presence(pres)
|
237
237
|
@presence_cbs.process(item, oldpres, pres)
|
@@ -431,8 +431,8 @@ module Jabber
|
|
431
431
|
|
432
432
|
##
|
433
433
|
# Get specific presence
|
434
|
-
# jid:: [JID] Full JID
|
435
|
-
def
|
434
|
+
# jid:: [JID] Full JID with resource
|
435
|
+
def presence_of(jid)
|
436
436
|
@presences_lock.synchronize {
|
437
437
|
@presences.each { |pres|
|
438
438
|
return(pres) if pres.from == jid
|
@@ -441,15 +441,25 @@ module Jabber
|
|
441
441
|
nil
|
442
442
|
end
|
443
443
|
|
444
|
+
##
|
445
|
+
# Get presence of highest-priority available resource of this person
|
446
|
+
#
|
447
|
+
# Returns <tt>nil</tt> if contact is offline
|
448
|
+
def presence
|
449
|
+
@presences_lock.synchronize {
|
450
|
+
@presences.select { |pres|
|
451
|
+
pres.type.nil?
|
452
|
+
}.max { |pres1, pres2| (pres1.priority || 0) <=> (pres2.priority || 0) }
|
453
|
+
}
|
454
|
+
end
|
455
|
+
|
444
456
|
##
|
445
457
|
# Add presence and sort presences
|
446
458
|
# (unless type is :unavailable or :error)
|
447
459
|
#
|
448
460
|
# This overwrites previous stanzas with the same destination
|
449
|
-
# JID to keep track of resources.
|
450
|
-
# <tt>type == :unavailable</tt>
|
451
|
-
# be deleted as this indicates that this resource has gone
|
452
|
-
# offline.
|
461
|
+
# JID to keep track of resources. Old presence stanzas with
|
462
|
+
# <tt>type == :unavailable</tt> will be deleted.
|
453
463
|
#
|
454
464
|
# If <tt>type == :error</tt> and the presence's origin has no
|
455
465
|
# specific resource the contact is treated completely offline.
|
@@ -457,7 +467,7 @@ module Jabber
|
|
457
467
|
@presences_lock.synchronize {
|
458
468
|
# Delete old presences with the same JID
|
459
469
|
@presences.delete_if do |pres|
|
460
|
-
pres.from == newpres.from or pres.from.resource.nil?
|
470
|
+
pres.from == newpres.from or pres.from.resource.nil? or pres.type == :unavailable
|
461
471
|
end
|
462
472
|
|
463
473
|
if newpres.type == :error and newpres.from.resource.nil?
|
data/lib/xmpp4r/sasl.rb
CHANGED
@@ -177,7 +177,7 @@ module Jabber
|
|
177
177
|
response['nc'] = '00000001'
|
178
178
|
response['qop'] = 'auth'
|
179
179
|
response['digest-uri'] = "xmpp/#{@stream.jid.domain}"
|
180
|
-
response['response'] = response_value(@stream.jid.node,
|
180
|
+
response['response'] = response_value(@stream.jid.node, response['realm'], response['digest-uri'], password, @nonce, response['cnonce'], response['qop'], response['authzid'])
|
181
181
|
response.each { |key,value|
|
182
182
|
unless %w(nc qop response charset).include? key
|
183
183
|
response[key] = "\"#{value}\""
|
data/lib/xmpp4r/stream.rb
CHANGED
@@ -577,13 +577,13 @@ module Jabber
|
|
577
577
|
# In some cases, we might lost count of some stanzas
|
578
578
|
# (for example, if the handler raises an exception)
|
579
579
|
# so we can't block forever.
|
580
|
-
while pr > 0 and n <=
|
580
|
+
while pr > 0 and n <= 20
|
581
581
|
@tbcbmutex.synchronize { pr = @processing }
|
582
582
|
if pr > 0
|
583
583
|
n += 1
|
584
584
|
Jabber::debuglog("TRYING TO CLOSE, STILL PROCESSING #{pr} STANZAS")
|
585
585
|
#puts("TRYING TO CLOSE, STILL PROCESSING #{pr} STANZAS")
|
586
|
-
|
586
|
+
sleep 0.1
|
587
587
|
end
|
588
588
|
end
|
589
589
|
|
data/lib/xmpp4r/xmpp4r.rb
CHANGED
@@ -8,7 +8,7 @@ module Jabber
|
|
8
8
|
# XMPP4R Version number. This is the ONLY place where the version number
|
9
9
|
# should be specified. This constant is used to determine the version of
|
10
10
|
# package tarballs and generated gems.
|
11
|
-
XMPP4R_VERSION = '0.5'
|
11
|
+
XMPP4R_VERSION = '0.5.5'
|
12
12
|
end
|
13
13
|
|
14
14
|
require 'xmpp4r/client'
|
@@ -105,8 +105,8 @@ class SOCKS5BytestreamsTest < Test::Unit::TestCase
|
|
105
105
|
target = Bytestreams::SOCKS5BytestreamsTarget.new(@server, '1', '1@a.com/1', '1@a.com/2')
|
106
106
|
|
107
107
|
assert_nothing_raised do
|
108
|
-
Timeout::timeout(
|
109
|
-
target.connect_timeout =
|
108
|
+
Timeout::timeout(1) do
|
109
|
+
target.connect_timeout = 0.5
|
110
110
|
assert target.accept == false
|
111
111
|
end
|
112
112
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
$:.unshift File::dirname(__FILE__) + '/../../lib'
|
4
|
+
|
5
|
+
require 'test/unit'
|
6
|
+
require File::dirname(__FILE__) + '/../lib/clienttester'
|
7
|
+
|
8
|
+
require 'xmpp4r/entity_time/responder'
|
9
|
+
include Jabber
|
10
|
+
|
11
|
+
class EntityTime::ResponderTest < Test::Unit::TestCase
|
12
|
+
include ClientTester
|
13
|
+
|
14
|
+
def a_utc_time
|
15
|
+
Time.utc(2000,"jan",1,20,15,1) # xmlschema = "2000-01-01T20:15:01Z"
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_entity_time_iq
|
19
|
+
el_time = EntityTime::IqTime.new(a_utc_time)
|
20
|
+
|
21
|
+
assert_equal('time', el_time.name, "element has wrong name")
|
22
|
+
assert_equal('urn:xmpp:time', el_time.namespace, "element has wrong namespace")
|
23
|
+
|
24
|
+
assert(el_time.elements["utc"], "element does not have a utc child")
|
25
|
+
assert_equal('2000-01-01T20:15:01Z', el_time.elements["utc"].text, "element/utc has the wrong text")
|
26
|
+
|
27
|
+
assert(el_time.elements["tzo"], "element does not have a tzo child")
|
28
|
+
assert_equal("+00:00", el_time.elements["tzo"].text, "element/tzo has the wrong time zone offset")
|
29
|
+
end
|
30
|
+
|
31
|
+
=begin
|
32
|
+
http://xmpp.org/extensions/xep-0202.html
|
33
|
+
<iq type='get'
|
34
|
+
from='romeo@montague.net/orchard'
|
35
|
+
to='juliet@capulet.com/balcony'
|
36
|
+
id='time_1'>
|
37
|
+
<time xmlns='urn:xmpp:time'/>
|
38
|
+
</iq>
|
39
|
+
=end
|
40
|
+
def test_query
|
41
|
+
EntityTime::Responder.new(@client)
|
42
|
+
|
43
|
+
iq = Iq.new(:get)
|
44
|
+
t = REXML::Element.new('time')
|
45
|
+
t.add_namespace('urn:xmpp:time')
|
46
|
+
iq.add_element(t)
|
47
|
+
|
48
|
+
reply = @server.send_with_id(iq)
|
49
|
+
|
50
|
+
assert_equal(:result, reply.type)
|
51
|
+
assert_equal('time', reply[0].name)
|
52
|
+
assert_equal('urn:xmpp:time', reply[0].namespace)
|
53
|
+
|
54
|
+
assert(reply[0].elements["utc"].has_text?, "element must have a utc time (actual time should be check here)")
|
55
|
+
|
56
|
+
tmp = Time.now
|
57
|
+
local_offset = Time.now.utc_offset
|
58
|
+
hour_offset = local_offset / 3600
|
59
|
+
min_offset = local_offset % 60
|
60
|
+
offset = "%+03d:%02d"%[hour_offset, min_offset]
|
61
|
+
|
62
|
+
assert_equal(offset, reply[0].elements["tzo"].text, "element should has an incorrect time zone offset")
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|