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
@@ -43,7 +43,11 @@ module Jabber
43
43
  e.add_attributes attributes
44
44
  @current = @current.nil? ? e : @current.add_element(e)
45
45
 
46
- if @current.name == 'stream' and !@started
46
+ # Handling <stream:stream> not only when it is being
47
+ # received as a top-level tag but also as a child of the
48
+ # top-level element itself. This way, we handle stream
49
+ # restarts (ie. after SASL authentication).
50
+ if @current.name == 'stream' and @current.parent.nil?
47
51
  @started = true
48
52
  @listener.receive(@current)
49
53
  @current = nil
@@ -60,6 +64,10 @@ module Jabber
60
64
  end
61
65
  end
62
66
 
67
+ parser.listen( :end_document ) do
68
+ raise Jabber::ServerDisconnected, "Server Disconnected!"
69
+ end
70
+
63
71
  parser.listen( :characters ) do | text |
64
72
  @current.add(REXML::Text.new(text.to_s, @current.whitespace, nil, true)) if @current
65
73
  end
@@ -0,0 +1,118 @@
1
+ module Jabber
2
+ module Test
3
+ class ListenerMocker
4
+
5
+ class << self
6
+ attr_accessor :with_socket_mocked_callback_proc
7
+ end
8
+
9
+ def self.with_socket_mocked(callback_proc)
10
+ TCPSocket.class_eval{ ListenerMocker.with_socket_mocked_callback_proc = callback_proc }
11
+ TCPSocket.class_eval do
12
+ alias_method :initialize_old, :initialize
13
+ def initialize(*args)
14
+ initialize_old(*args) if ListenerMocker.with_socket_mocked_callback_proc.call(args)
15
+ end
16
+ end
17
+ yield
18
+ ensure
19
+ TCPSocket.class_eval do
20
+ if method_defined?(:initialize_old)
21
+ alias_method :initialize, :initialize_old
22
+ end
23
+ end
24
+ end
25
+
26
+ class << self
27
+ attr_accessor :mock_clients, :tracker_of_callers
28
+ end
29
+
30
+ def self.mocker_proc
31
+ Proc.new do
32
+ attr_accessor :messagecbs, :connected
33
+ ListenerMocker.mock_clients ||= {}
34
+ ListenerMocker.tracker_of_callers ||= {}
35
+
36
+ def connect
37
+ Jabber::debuglog("(in mock) connected #{@jid.bare}")
38
+ self.connected = true
39
+ end
40
+
41
+ def close!
42
+ ListenerMocker.mock_clients[@jid.bare.to_s] = nil
43
+ ListenerMocker.tracker_of_callers[@jid.bare.to_s] = nil
44
+ self.connected = false
45
+ end
46
+
47
+ def auth(password)
48
+ auth_nonsasl(password)
49
+ end
50
+
51
+ def auth_nonsasl(password, digest=true)
52
+ Jabber::debuglog("(in mock) authed #{@jid.bare}")
53
+
54
+ if(ListenerMocker.mock_clients[@jid.bare.to_s])
55
+ #raise a stack trace about multiple clients
56
+ raise "\n\n ---> READ ME: this is actualy 2 stack traces: <---- \n\n"+
57
+ "There is already a client connected on that jid #{@jid.bare.to_s}. "+
58
+ "The mock library cannot support multiple listeners connected as the same user! originally called in:\n"+
59
+ ListenerMocker.tracker_of_callers[@jid.bare.to_s].backtrace.join("\n")+"\n\n second trace: \n"
60
+ else
61
+ #store a stack trace so that next time we have multiple client, we can alert about it...
62
+ begin
63
+ throw "just saving a stack trace"
64
+ rescue => e
65
+ ListenerMocker.tracker_of_callers[@jid.bare.to_s] = e
66
+ end
67
+ end
68
+
69
+ ListenerMocker.mock_clients[@jid.bare.to_s] = self
70
+ true
71
+ end
72
+
73
+ def send(xml, &block)
74
+ Jabber::debuglog("(in mock) sending #{xml} #{xml.class}")
75
+ if(xml.is_a?(Jabber::Message))
76
+ xml.from = @jid
77
+ # unless xml.to
78
+ # raise "no jid!"
79
+ # end
80
+ if ListenerMocker.mock_clients[xml.to.bare.to_s]
81
+ Jabber::debuglog("(in mock) to #{xml.to.bare.to_s}")
82
+ ListenerMocker.mock_clients[xml.to.bare.to_s].messagecbs.process(xml)
83
+ else
84
+ Jabber::debuglog("(in mock) no client listening as #{xml.to.bare.to_s}")
85
+ end
86
+ end
87
+ end
88
+
89
+ def is_connected?
90
+ self.connected
91
+ end
92
+ end
93
+ end
94
+
95
+ def self.mock_out_all_connections
96
+ Jabber::Reliable::Connection.class_eval(&Jabber::Test::ListenerMocker.mocker_proc)
97
+ end
98
+
99
+ def self.mock_out(listener)
100
+ listener.instance_eval do
101
+ class << self
102
+ def setup_connection
103
+ super
104
+ @connection.instance_eval do
105
+ class << self
106
+ self.class_eval(&Jabber::Test::ListenerMocker.mocker_proc)
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ listener
114
+ end
115
+
116
+ end
117
+ end
118
+ end
@@ -8,11 +8,13 @@ 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.4'
11
+ XMPP4R_VERSION = '0.5'
12
12
  end
13
13
 
14
14
  require 'xmpp4r/client'
15
+ require 'xmpp4r/reliable'
15
16
  require 'xmpp4r/component'
16
17
  require 'xmpp4r/debuglog'
17
18
  require 'xmpp4r/errors'
18
19
 
20
+ require 'xmpp4r/test/listener_mocker'
@@ -30,6 +30,7 @@ class IBBTest < Test::Unit::TestCase
30
30
  target.close
31
31
  end
32
32
 
33
+ target.accept_wait
33
34
 
34
35
  initiator.open
35
36
 
@@ -50,10 +51,9 @@ class IBBTest < Test::Unit::TestCase
50
51
  buffer = create_buffer(9999)
51
52
 
52
53
  Thread.new do
53
- Thread.pass
54
+ target.accept_wait
54
55
  initiator.open
55
56
  initiator.write(buffer)
56
- Thread.pass
57
57
  initiator.close
58
58
  end
59
59
 
@@ -100,6 +100,7 @@ class IBBTest < Test::Unit::TestCase
100
100
  assert_equal(1, ignored_stanzas)
101
101
 
102
102
 
103
+ target.accept_wait
103
104
  initiator.open
104
105
 
105
106
 
@@ -116,8 +117,8 @@ class IBBTest < Test::Unit::TestCase
116
117
  assert_equal(3, ignored_stanzas)
117
118
 
118
119
 
119
- 10.times do
120
- buf = create_buffer(9999)
120
+ 5.times do
121
+ buf = create_buffer(100)
121
122
  initiator.write(buf)
122
123
  initiator.flush
123
124
 
@@ -147,6 +148,7 @@ class IBBTest < Test::Unit::TestCase
147
148
  end
148
149
 
149
150
 
151
+ target.accept_wait
150
152
  initiator.open
151
153
 
152
154
  assert_nil(initiator.read)
@@ -44,6 +44,8 @@ class SOCKS5BytestreamsTest < Test::Unit::TestCase
44
44
  target2.close
45
45
  end
46
46
 
47
+ target1.accept_wait
48
+ target2.accept_wait
47
49
  initiator1.open
48
50
  initiator2.open
49
51
 
@@ -70,7 +72,6 @@ class SOCKS5BytestreamsTest < Test::Unit::TestCase
70
72
  initiator = Bytestreams::SOCKS5BytestreamsInitiator.new(@client, '1', '1@a.com/1', '1@a.com/2')
71
73
  initiator.add_streamhost(@@server)
72
74
 
73
-
74
75
  Thread.new do
75
76
  target.accept
76
77
 
@@ -81,7 +82,7 @@ class SOCKS5BytestreamsTest < Test::Unit::TestCase
81
82
 
82
83
  target.close
83
84
  end
84
-
85
+ target.accept_wait
85
86
 
86
87
  initiator.open
87
88
 
@@ -1,3 +1,4 @@
1
+ # coding: utf-8
1
2
 
2
3
  $:.unshift File::dirname(__FILE__) + '/../../lib'
3
4
 
@@ -17,6 +18,7 @@ class Caps::HelperTest < Test::Unit::TestCase
17
18
  # in http://www.xmpp.org/extensions/xep-0115.html#usecases
18
19
  # and assert conformance.
19
20
  def test_caps_reply
21
+
20
22
  # This will be invoked by 'wait_state' below...
21
23
  state { |presence|
22
24
  assert_kind_of(Jabber::Presence, presence)
@@ -30,8 +32,6 @@ class Caps::HelperTest < Test::Unit::TestCase
30
32
  assert_equal('sha-1', c.hash)
31
33
 
32
34
  assert_equal("http://home.gna.org/xmpp4r/##{Jabber::XMPP4R_VERSION}", c.node)
33
-
34
- send(iq_discovering_capabilities)
35
35
  }
36
36
 
37
37
  # Construct Caps::Helper which will send a <presence>
@@ -64,6 +64,8 @@ class Caps::HelperTest < Test::Unit::TestCase
64
64
  assert_equal(iq.query.features.sort, features.map(&get_var).sort)
65
65
  }
66
66
 
67
+ send(iq_discovering_capabilities)
68
+
67
69
  # The 'server' will receive the <iq> result from the
68
70
  # 'client' and yield it to the block above. Wait here
69
71
  # until that block exits.
@@ -76,6 +76,6 @@ class DataFormsTest < Test::Unit::TestCase
76
76
  assert_equal(f, v.field('test'))
77
77
  assert_nil(v.field('wrong field'))
78
78
  assert_equal([f], v.fields)
79
- end
79
+ end
80
80
 
81
81
  end
@@ -0,0 +1,75 @@
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'
9
+ require 'xmpp4r/last/helper/helper'
10
+ include Jabber
11
+
12
+
13
+ class LastActivity::HelperTest < Test::Unit::TestCase
14
+ include ClientTester
15
+
16
+ def test_simple_query
17
+ state { |iq|
18
+ assert_kind_of(Iq, iq)
19
+ assert_equal(JID.new('juliet@capulet.com'), iq.to)
20
+ assert_equal(:get, iq.type)
21
+ assert_kind_of(LastActivity::IqQueryLastActivity, iq.query)
22
+ send("
23
+ <iq type='result' from='#{iq.to}' to='#{iq.from}' id='#{iq.id}'>
24
+ <query xmlns='jabber:iq:last' seconds='903'/>
25
+ </iq>")
26
+ }
27
+
28
+ res = LastActivity::Helper.new(@client).get_last_activity_from('juliet@capulet.com')
29
+ wait_state
30
+ assert_equal(903, res.seconds)
31
+ assert_nil(res.text)
32
+ end
33
+
34
+ def test_text_query
35
+ state { |iq|
36
+ send("
37
+ <iq type='result' from='#{iq.to}' to='#{iq.from}' id='#{iq.id}'>
38
+ <query xmlns='jabber:iq:last' seconds='903'>Heading Home</query>
39
+ </iq>")
40
+ }
41
+
42
+ res = LastActivity::Helper.new(@client).get_last_activity_from('juliet@capulet.com')
43
+ wait_state
44
+ assert_equal(903, res.seconds)
45
+ assert_equal('Heading Home', res.text)
46
+ end
47
+
48
+ def test_empty_query
49
+ state { |iq|
50
+ send("
51
+ <iq type='result' from='#{iq.to}' to='#{iq.from}' id='#{iq.id}'>
52
+ <query xmlns='jabber:iq:last'/>
53
+ </iq>")
54
+ }
55
+
56
+ res = LastActivity::Helper.new(@client).get_last_activity_from('juliet@capulet.com')
57
+ wait_state
58
+ assert_nil(res.seconds)
59
+ assert_nil(res.text)
60
+ end
61
+
62
+ def test_forbidden_query
63
+ state { |iq|
64
+ send("
65
+ <iq type='error' from='#{iq.to}' to='#{iq.from}' id='#{iq.id}'>
66
+ <error type='auth'>
67
+ <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
68
+ </error>
69
+ </iq>")
70
+ }
71
+
72
+ assert_raises(ServerError) { LastActivity::Helper.new(@client).get_last_activity_from('juliet@capulet.com') }
73
+ end
74
+
75
+ end
@@ -8,6 +8,10 @@ require 'test/unit'
8
8
  require 'socket'
9
9
  require 'xmpp4r/semaphore'
10
10
 
11
+ # Jabber::debug = true
12
+ $ctdebug = false
13
+ # $ctdebug = true
14
+
11
15
  # This is sane for tests:
12
16
  Thread::abort_on_exception = true
13
17
 
@@ -33,6 +37,9 @@ module Jabber
33
37
  serverwait = Semaphore.new
34
38
  stream = '<stream:stream xmlns:stream="http://etherx.jabber.org/streams">'
35
39
 
40
+ @state = 0
41
+ @states = []
42
+
36
43
  Thread.new do
37
44
  Thread.current.abort_on_exception = true
38
45
  serversock = servlisten.accept
@@ -68,31 +75,53 @@ module Jabber
68
75
  end
69
76
  #=end
70
77
  @client.start(clientsock)
71
- @client.send(stream) { |reply| true }
72
78
 
73
- @state = 0
74
- @states = []
75
- @state_wait = Semaphore.new
76
- @state_wait2 = Semaphore.new
79
+ @processdone_wait = Semaphore.new
80
+ @nextstate_wait = Semaphore.new
81
+ serverwait.wait
77
82
  @server.add_stanza_callback { |stanza|
78
- if @state < @states.size
83
+ # Client prepares everything, then calls wait_state. Problem: because
84
+ # of a race condition, it is possible that we receive the stanza before
85
+ # what to do with it is defined. We busy-wait on @states here.
86
+ n = 0
87
+ while @state >= @states.size and n < 1000
88
+ Thread.pass
89
+ n += 1
90
+ end
91
+ if n == 1000
92
+ puts "Unmanaged stanza in state. Maybe processed by helper?" if $ctdebug
93
+ else
79
94
  begin
95
+ puts "Calling #{@states[@state]} for #{stanza.to_s}" if $ctdebug
80
96
  @states[@state].call(stanza)
81
- rescue
82
- puts "Exception in state: #{$!.class}: #{$!}\n#{$!.join("\n")}"
97
+ rescue Exception => e
98
+ puts "Exception in state: #{e.class}: #{e}\n#{e.backtrace.join("\n")}"
83
99
  end
84
100
  @state += 1
85
- @state_wait2.wait
86
- @state_wait.run
101
+ @nextstate_wait.wait
102
+ @processdone_wait.run
87
103
  end
88
104
 
89
105
  false
90
106
  }
107
+ @client.send(stream) { |reply| true }
91
108
 
92
- serverwait.wait
93
109
  end
94
110
 
95
111
  def teardown
112
+ # In some cases, we might lost count of some stanzas
113
+ # (for example, if the handler raises an exception)
114
+ # so we can't block forever.
115
+ n = 0
116
+ while @client.processing > 0 and n < 1000
117
+ Thread::pass
118
+ n += 1
119
+ end
120
+ n = 0
121
+ while @server.processing > 0 and n < 1000
122
+ Thread::pass
123
+ n += 1
124
+ end
96
125
  @client.close
97
126
  @server.close
98
127
  end
@@ -106,12 +135,12 @@ module Jabber
106
135
  end
107
136
 
108
137
  def wait_state
109
- @state_wait2.run
110
- @state_wait.wait
138
+ @nextstate_wait.run
139
+ @processdone_wait.wait
111
140
  end
112
141
 
113
142
  def skip_state
114
- @state_wait2.run
143
+ @nextstate_wait.run
115
144
  end
116
145
  end
117
146
  end
@@ -86,7 +86,11 @@ class MUCClientTest < Test::Unit::TestCase
86
86
  send("<presence from='darkcave@macbeth.shakespeare.lit/thirdwitch' to='crone1@shakespeare.lit/desktop'>" +
87
87
  "<x xmlns='http://jabber.org/protocol/muc#user'><item affiliation='none' jid='hag66@shakespeare.lit/pda' role='participant'/></x>" +
88
88
  "</presence>")
89
- sleep 0.1
89
+ n = 0
90
+ while m.roster.size != 3 and n < 1000
91
+ Thread::pass
92
+ n += 1
93
+ end
90
94
  assert_equal(3, m.roster.size)
91
95
  assert_equal(:none, m.roster['thirdwitch'].x.items[0].affiliation)
92
96
  assert_equal(:participant, m.roster['thirdwitch'].x.items[0].role)
@@ -408,7 +412,7 @@ class MUCClientTest < Test::Unit::TestCase
408
412
  assert_equal(1, messages_muc_private)
409
413
 
410
414
  wait_state
411
- end
415
+ end
412
416
 
413
417
  def test_presence_callbacks
414
418
  state { |pres|