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
@@ -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|