xmpp4r 0.3.2 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. data/{ChangeLog → CHANGELOG} +33 -0
  2. data/LICENSE +9 -9
  3. data/README.rdoc +110 -0
  4. data/README_ruby19.txt +43 -0
  5. data/Rakefile +235 -74
  6. data/data/doc/xmpp4r/examples/advanced/adventure/README +0 -1
  7. data/data/doc/xmpp4r/examples/advanced/adventure/adventure.rb +1 -1
  8. data/data/doc/xmpp4r/examples/advanced/adventure/adventuremuc.rb +12 -12
  9. data/data/doc/xmpp4r/examples/advanced/adventure/world.rb +10 -11
  10. data/data/doc/xmpp4r/examples/advanced/fileserve.rb +11 -11
  11. data/data/doc/xmpp4r/examples/advanced/getonline.rb +7 -7
  12. data/data/doc/xmpp4r/examples/advanced/gtkmucclient.rb +3 -3
  13. data/data/doc/xmpp4r/examples/advanced/migrate.rb +21 -22
  14. data/data/doc/xmpp4r/examples/advanced/minimuc.rb +22 -22
  15. data/data/doc/xmpp4r/examples/advanced/pep-aggregator/index.xsl +235 -0
  16. data/data/doc/xmpp4r/examples/advanced/pep-aggregator/pep-aggregator.rb +147 -0
  17. data/data/doc/xmpp4r/examples/advanced/recvfile.rb +0 -1
  18. data/data/doc/xmpp4r/examples/advanced/rosterdiscovery.rb +2 -3
  19. data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr.rb +3 -3
  20. data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_jabber.rb +10 -10
  21. data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_test.rb +2 -2
  22. data/data/doc/xmpp4r/examples/advanced/versionpoll.rb +12 -12
  23. data/data/doc/xmpp4r/examples/advanced/xmpping.rb +2 -3
  24. data/data/doc/xmpp4r/examples/basic/change_password.rb +3 -3
  25. data/data/doc/xmpp4r/examples/basic/client.rb +5 -3
  26. data/data/doc/xmpp4r/examples/basic/component.rb +1 -1
  27. data/data/doc/xmpp4r/examples/basic/{echo_threaded.rb → echo.rb} +8 -7
  28. data/data/doc/xmpp4r/examples/basic/jabbersend.rb +5 -5
  29. data/data/doc/xmpp4r/examples/basic/mass_sender.rb +11 -11
  30. data/data/doc/xmpp4r/examples/basic/muc_owner_config.rb +0 -1
  31. data/data/doc/xmpp4r/examples/basic/mucinfo.rb +3 -1
  32. data/data/doc/xmpp4r/examples/basic/mucsimplebot.rb +0 -1
  33. data/data/doc/xmpp4r/examples/basic/register.rb +21 -4
  34. data/data/doc/xmpp4r/examples/basic/roster.rb +7 -5
  35. data/data/doc/xmpp4r/examples/basic/rosterprint.rb +0 -0
  36. data/data/doc/xmpp4r/examples/basic/rosterrename.rb +2 -2
  37. data/data/doc/xmpp4r/examples/basic/rosterwatch.rb +6 -7
  38. data/data/doc/xmpp4r/examples/basic/send_vcard.rb +3 -4
  39. data/data/doc/xmpp4r/examples/basic/tune_client.rb +56 -0
  40. data/data/doc/xmpp4r/examples/basic/tune_server.rb +58 -0
  41. data/data/doc/xmpp4r/examples/basic/versionbot.rb +1 -1
  42. data/lib/xmpp4r.rb +4 -4
  43. data/lib/xmpp4r/base64.rb +32 -0
  44. data/lib/xmpp4r/bytestreams/helper/filetransfer.rb +10 -11
  45. data/lib/xmpp4r/bytestreams/helper/ibb/base.rb +6 -8
  46. data/lib/xmpp4r/bytestreams/helper/ibb/initiator.rb +3 -6
  47. data/lib/xmpp4r/bytestreams/helper/ibb/target.rb +0 -1
  48. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb +8 -11
  49. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb +4 -7
  50. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/server.rb +7 -3
  51. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb +10 -5
  52. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb +14 -4
  53. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb.orig +62 -0
  54. data/lib/xmpp4r/bytestreams/iq/bytestreams.rb +0 -1
  55. data/lib/xmpp4r/callbacks.rb +5 -5
  56. data/lib/xmpp4r/caps.rb +1 -0
  57. data/lib/xmpp4r/caps/c.rb +53 -0
  58. data/lib/xmpp4r/caps/helper/generator.rb +160 -0
  59. data/lib/xmpp4r/caps/helper/helper.rb +87 -0
  60. data/lib/xmpp4r/client.rb +111 -40
  61. data/lib/xmpp4r/command/iq/command.rb +1 -1
  62. data/lib/xmpp4r/component.rb +8 -8
  63. data/lib/xmpp4r/connection.rb +25 -9
  64. data/lib/xmpp4r/dataforms/x/data.rb +17 -5
  65. data/lib/xmpp4r/debuglog.rb +13 -5
  66. data/lib/xmpp4r/delay/x/delay.rb +1 -1
  67. data/lib/xmpp4r/discovery.rb +3 -2
  68. data/lib/xmpp4r/discovery/helper/responder.rb +165 -0
  69. data/lib/xmpp4r/discovery/iq/discoinfo.rb +11 -12
  70. data/lib/xmpp4r/discovery/iq/discoitems.rb +12 -4
  71. data/lib/xmpp4r/{error.rb → errors.rb} +66 -10
  72. data/lib/xmpp4r/framework/base.rb +55 -0
  73. data/lib/xmpp4r/framework/bot.rb +148 -0
  74. data/lib/xmpp4r/httpbinding/client.rb +1 -1
  75. data/lib/xmpp4r/idgenerator.rb +1 -1
  76. data/lib/xmpp4r/iq.rb +34 -23
  77. data/lib/xmpp4r/jid.rb +2 -2
  78. data/lib/xmpp4r/message.rb +1 -1
  79. data/lib/xmpp4r/muc.rb +2 -0
  80. data/lib/xmpp4r/muc/helper/mucbrowser.rb +4 -19
  81. data/lib/xmpp4r/muc/helper/mucclient.rb +54 -23
  82. data/lib/xmpp4r/muc/helper/simplemucclient.rb +107 -1
  83. data/lib/xmpp4r/muc/iq/mucadmin.rb +23 -0
  84. data/lib/xmpp4r/muc/iq/mucadminitem.rb +20 -0
  85. data/lib/xmpp4r/muc/iq/mucowner.rb +5 -1
  86. data/lib/xmpp4r/muc/item.rb +143 -0
  87. data/lib/xmpp4r/muc/x/mucuserinvite.rb +2 -2
  88. data/lib/xmpp4r/muc/x/mucuseritem.rb +3 -117
  89. data/lib/xmpp4r/presence.rb +2 -1
  90. data/lib/xmpp4r/pubsub.rb +7 -0
  91. data/lib/xmpp4r/pubsub/children/configuration.rb +86 -0
  92. data/lib/xmpp4r/pubsub/{stanzas → children}/event.rb +13 -13
  93. data/lib/xmpp4r/pubsub/{stanzas → children}/item.rb +9 -1
  94. data/lib/xmpp4r/pubsub/{stanzas → children}/items.rb +10 -1
  95. data/lib/xmpp4r/pubsub/children/node_config.rb +48 -0
  96. data/lib/xmpp4r/pubsub/children/publish.rb +24 -0
  97. data/lib/xmpp4r/pubsub/{stanzas → children}/subscription.rb +16 -12
  98. data/lib/xmpp4r/pubsub/children/subscription_config.rb +67 -0
  99. data/lib/xmpp4r/pubsub/children/unsubscribe.rb +48 -0
  100. data/lib/xmpp4r/pubsub/helper/nodebrowser.rb +49 -93
  101. data/lib/xmpp4r/pubsub/helper/nodehelper.rb +39 -36
  102. data/lib/xmpp4r/pubsub/helper/servicehelper.rb +253 -162
  103. data/lib/xmpp4r/pubsub/iq/pubsub.rb +1 -1
  104. data/lib/xmpp4r/rexmladdons.rb +56 -28
  105. data/lib/xmpp4r/roster.rb +0 -1
  106. data/lib/xmpp4r/roster/helper/roster.rb +26 -15
  107. data/lib/xmpp4r/roster/iq/roster.rb +19 -19
  108. data/lib/xmpp4r/roster/x/roster.rb +10 -10
  109. data/lib/xmpp4r/rpc/helper/client.rb +17 -8
  110. data/lib/xmpp4r/rpc/helper/server.rb +1 -2
  111. data/lib/xmpp4r/rpc/helper/xmlrpcaddons.rb +14 -4
  112. data/lib/xmpp4r/rpc/iq/rpc.rb +0 -1
  113. data/lib/xmpp4r/sasl.rb +27 -6
  114. data/lib/xmpp4r/stream.rb +39 -44
  115. data/lib/xmpp4r/streamparser.rb +4 -4
  116. data/lib/xmpp4r/tune.rb +2 -0
  117. data/lib/xmpp4r/tune/helper/helper.rb +58 -0
  118. data/lib/xmpp4r/tune/tune.rb +113 -0
  119. data/lib/xmpp4r/vcard.rb +0 -1
  120. data/lib/xmpp4r/vcard/helper/vcard.rb +1 -3
  121. data/lib/xmpp4r/vcard/iq/vcard.rb +14 -0
  122. data/lib/xmpp4r/version/iq/version.rb +0 -1
  123. data/lib/xmpp4r/x.rb +1 -1
  124. data/lib/xmpp4r/xhtml.rb +1 -0
  125. data/lib/xmpp4r/xhtml/html.rb +115 -0
  126. data/lib/xmpp4r/xmpp4r.rb +7 -5
  127. data/lib/xmpp4r/xmppelement.rb +21 -5
  128. data/lib/xmpp4r/xmppstanza.rb +6 -6
  129. data/setup.rb +4 -4
  130. data/test/bytestreams/tc_ibb.rb +2 -2
  131. data/test/bytestreams/tc_socks5bytestreams.rb +12 -2
  132. data/test/caps/tc_helper.rb +156 -0
  133. data/test/dataforms/tc_data.rb +0 -0
  134. data/test/delay/tc_xdelay.rb +2 -2
  135. data/test/discovery/tc_responder.rb +91 -0
  136. data/test/lib/assert_equal_xml.rb +14 -0
  137. data/test/lib/clienttester.rb +31 -24
  138. data/test/muc/tc_muc_mucclient.rb +249 -13
  139. data/test/muc/tc_muc_simplemucclient.rb +41 -2
  140. data/test/muc/tc_mucowner.rb +0 -0
  141. data/test/pubsub/tc_helper.rb +486 -51
  142. data/test/pubsub/tc_nodeconfig.rb +54 -0
  143. data/test/pubsub/tc_subscriptionconfig.rb +41 -0
  144. data/test/roster/tc_helper.rb +6 -7
  145. data/test/roster/tc_iqqueryroster.rb +16 -16
  146. data/test/roster/tc_xroster.rb +1 -1
  147. data/test/rpc/tc_helper.rb +15 -3
  148. data/test/tc_callbacks.rb +7 -7
  149. data/test/tc_class_names.rb +15 -6
  150. data/test/tc_client.rb +3 -3
  151. data/test/{tc_error.rb → tc_errors.rb} +54 -12
  152. data/test/tc_idgenerator.rb +0 -0
  153. data/test/tc_iq.rb +32 -29
  154. data/test/tc_iqquery.rb +2 -2
  155. data/test/tc_jid.rb +44 -42
  156. data/test/tc_message.rb +13 -12
  157. data/test/tc_presence.rb +25 -24
  158. data/test/tc_rexml.rb +81 -2
  159. data/test/tc_stream.rb +15 -13
  160. data/test/tc_streamComponent.rb +6 -5
  161. data/test/tc_streamError.rb +21 -15
  162. data/test/tc_streamSend.rb +9 -9
  163. data/test/tc_streamparser.rb +112 -0
  164. data/test/tc_xmppstanza.rb +19 -9
  165. data/test/ts_xmpp4r.rb +10 -1
  166. data/test/tune/tc_helper_recv.rb +84 -0
  167. data/test/tune/tc_helper_send.rb +74 -0
  168. data/test/tune/tc_tune.rb +79 -0
  169. data/test/vcard/tc_helper.rb +2 -2
  170. data/test/vcard/tc_iqvcard.rb +13 -3
  171. data/test/version/tc_helper.rb +2 -2
  172. data/test/version/tc_iqqueryversion.rb +10 -10
  173. data/test/xhtml/tc_html.rb +41 -0
  174. data/tools/gen_requires.bash +29 -8
  175. data/tools/xmpp4r-gemspec-test.rb +11 -0
  176. data/xmpp4r.gemspec +291 -0
  177. metadata +277 -218
  178. data/README +0 -28
  179. data/UPDATING +0 -40
  180. data/data/doc/xmpp4r/examples/buggy/jabber2jabber/jabber2jabber.rb +0 -18
  181. data/data/doc/xmpp4r/examples/buggy/miniedgarr_cgi.rb +0 -192
  182. data/data/doc/xmpp4r/examples/buggy/miniedgarr_watch.rb +0 -82
  183. data/lib/xmpp4r/authenticationfailure.rb +0 -13
  184. data/lib/xmpp4r/errorexception.rb +0 -32
  185. data/tools/doctoweb.bash +0 -30
@@ -151,14 +151,18 @@ module Jabber
151
151
  ##
152
152
  # Start handshake process
153
153
  def start
154
- auth_ver = @socket.getc
154
+ if !@socket.respond_to? :getbyte
155
+ class << @socket; alias getbyte getc; end
156
+ end
157
+
158
+ auth_ver = @socket.getbyte
155
159
  if auth_ver != 5
156
160
  # Unsupported version
157
161
  @socket.close
158
162
  return
159
163
  end
160
164
 
161
- auth_nmethods = @socket.getc
165
+ auth_nmethods = @socket.getbyte
162
166
  auth_methods = @socket.read(auth_nmethods)
163
167
  unless auth_methods.index("\x00")
164
168
  # Client won't accept no authentication
@@ -175,7 +179,7 @@ module Jabber
175
179
  @socket.close
176
180
  return
177
181
  end
178
- req_addrlen = @socket.getc
182
+ req_addrlen = @socket.getbyte
179
183
  req_addr = @socket.read(req_addrlen)
180
184
  req_port = @socket.read(2)
181
185
  if req_port != "\x00\x00"
@@ -6,10 +6,6 @@ require 'socket'
6
6
 
7
7
  module Jabber
8
8
  module Bytestreams
9
- ##
10
- # Can be thrown upon communication error with
11
- # a SOCKS5 proxy
12
- class SOCKS5Error < RuntimeError; end
13
9
 
14
10
  ##
15
11
  # A SOCKS5 client implementation
@@ -47,12 +43,21 @@ module Jabber
47
43
  # port:: [Fixnum] Port number
48
44
  def connect_domain(domain, port)
49
45
  write("\x05\x01\x00\x03#{domain.size.chr}#{domain}#{[port].pack("n")}")
50
- buf = read(7 + domain.size)
46
+ buf = read(4)
51
47
  if buf.nil? or buf[0..1] != "\005\000"
52
48
  close
53
49
  raise SOCKS5Error.new("Invalid SOCKS5 connect: #{buf.inspect}")
54
50
  end
55
51
 
52
+ case buf.respond_to?(:bytes) ? buf.bytes.to_a[3] : buf[3]
53
+ when 1 then read(6) # IPv4 addr
54
+ when 3 then read(3 + domain.size) # Domain
55
+ when 4 then read(18) # IPv6 addr
56
+ else
57
+ close
58
+ raise SOCKS5Error.new("Invalid SOCKS5 address type #{buf[3].to_s}")
59
+ end
60
+
56
61
  self
57
62
  end
58
63
  end
@@ -7,6 +7,12 @@ module Jabber
7
7
  ##
8
8
  # SOCKS5 Bytestreams implementation of the target site
9
9
  class SOCKS5BytestreamsTarget < SOCKS5Bytestreams
10
+ ##
11
+ # See SOCKS5Bytestreams#initialize
12
+ def initialize(stream, session_id, initiator_jid, target_jid)
13
+ @connect_timeout = 60
14
+ super
15
+ end
10
16
  ##
11
17
  # Wait until the stream has been established
12
18
  #
@@ -39,13 +45,13 @@ module Jabber
39
45
  reply.query.add(StreamHostUsed.new(@streamhost_used.jid))
40
46
  else
41
47
  reply.type = :error
42
- reply.add(Error.new('item-not-found'))
48
+ reply.add(ErrorResponse.new('item-not-found'))
43
49
  end
44
50
  @stream.send(reply)
45
51
  rescue Exception => e
46
52
  error = e
47
53
  end
48
-
54
+
49
55
  connect_sem.run
50
56
  true
51
57
  else
@@ -53,11 +59,15 @@ module Jabber
53
59
  end
54
60
  }
55
61
 
56
- connect_sem.wait
62
+ begin
63
+ Timeout::timeout(@connect_timeout) { connect_sem.wait }
64
+ rescue Timeout::Error
65
+ @stream.delete_iq_callback(self)
66
+ end
67
+
57
68
  raise error if error
58
69
  (@socks != nil)
59
70
  end
60
71
  end
61
72
  end
62
73
  end
63
-
@@ -0,0 +1,62 @@
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
+ module Jabber
6
+ module Bytestreams
7
+ ##
8
+ # SOCKS5 Bytestreams implementation of the target site
9
+ class SOCKS5BytestreamsTarget < SOCKS5Bytestreams
10
+ ##
11
+ # Wait until the stream has been established
12
+ #
13
+ # May raise various exceptions
14
+ def accept
15
+ error = nil
16
+ connect_sem = Semaphore.new
17
+
18
+ @stream.add_iq_callback(200, self) { |iq|
19
+ if iq.type == :set and iq.from == @initiator_jid and iq.to == @target_jid and iq.query.kind_of?(IqQueryBytestreams)
20
+ begin
21
+ @stream.delete_iq_callback(self)
22
+
23
+ iq.query.each_element('streamhost') { |streamhost|
24
+ if streamhost.host and streamhost.port and not @socks
25
+ begin
26
+ @socks = connect_socks(streamhost)
27
+ @streamhost_used = streamhost
28
+ rescue Exception => e
29
+ Jabber::debuglog("SOCKS5 Bytestreams: #{e.class}: #{e}\n#{e.backtrace.join("\n")}")
30
+ @streamhost_cbs.process(streamhost, :failure, e)
31
+ end
32
+ end
33
+ }
34
+
35
+ reply = iq.answer(false)
36
+ if @streamhost_used
37
+ reply.type = :result
38
+ reply.add(IqQueryBytestreams.new)
39
+ reply.query.add(StreamHostUsed.new(@streamhost_used.jid))
40
+ else
41
+ reply.type = :error
42
+ reply.add(ErrorResponse.new('item-not-found'))
43
+ end
44
+ @stream.send(reply)
45
+ rescue Exception => e
46
+ error = e
47
+ end
48
+
49
+ connect_sem.run
50
+ true
51
+ else
52
+ false
53
+ end
54
+ }
55
+
56
+ connect_sem.wait
57
+ raise error if error
58
+ (@socks != nil)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -168,4 +168,3 @@ module Jabber
168
168
  end
169
169
  end
170
170
  end
171
-
@@ -15,7 +15,7 @@ module Jabber
15
15
  #
16
16
  # Callbacks are processed for a given set of objects as long as they return
17
17
  # false. If you want to stop processing, you must return true. Example :
18
- # cbl = CallbackList::new
18
+ # cbl = CallbackList.new
19
19
  # c1 = false
20
20
  # c2 = false
21
21
  # c3 = false
@@ -30,7 +30,7 @@ module Jabber
30
30
  # In XMPP4R, callbacks' priorities are quite normalized since we want to be
31
31
  # able to "cascade" callbacks in a clean way. Here are values your code should
32
32
  # take into account :
33
- #
33
+ #
34
34
  # >= 200:: logging & debugging callbacks. Those callbacks should not consume
35
35
  # elements.
36
36
  # 100-199:: Where Helpers register their callbacks. The normal value is 100,
@@ -56,7 +56,7 @@ module Jabber
56
56
  # return:: [Jabber::CallbackList] The list, for chaining
57
57
  def add(prio = 0, ref = nil, proc = nil, &block)
58
58
  block = proc if proc
59
- @list.push(Callback::new(prio, ref, block))
59
+ @list.push(Callback.new(prio, ref, block))
60
60
  @list.sort! { |a, b| b.priority <=> a.priority }
61
61
  self
62
62
  end
@@ -107,7 +107,7 @@ module Jabber
107
107
  # The Callback's reference, using for deleting purposes
108
108
  attr_reader :ref
109
109
 
110
- # The Callback's block to execute
110
+ # The Callback's block to execute
111
111
  attr_reader :block
112
112
 
113
113
  ##
@@ -115,7 +115,7 @@ module Jabber
115
115
  # priority:: [Integer] the callback's priority. The higher, the sooner it
116
116
  # will be executed
117
117
  # ref:: [String] The callback's reference
118
- def initialize(priority = 0, ref = nil, block = Proc::new {})
118
+ def initialize(priority = 0, ref = nil, block = Proc.new {})
119
119
  @priority = priority
120
120
  @ref = ref
121
121
  @block = block
@@ -0,0 +1 @@
1
+ require 'xmpp4r/caps/helper/helper'
@@ -0,0 +1,53 @@
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 Caps
9
+ NS_CAPS='http://jabber.org/protocol/caps'
10
+
11
+ ##
12
+ # The <c/> XMPP element, used to advertise entity capabilities.
13
+ #
14
+ # See http://www.xmpp.org/extensions/xep-0115.html#protocol.
15
+ #
16
+ # You should not need to construct this element directly, see
17
+ # Jabber::Caps::Helper.
18
+ class C < XMPPElement
19
+ name_xmlns 'c', NS_CAPS
20
+ force_xmlns true
21
+
22
+ def initialize(node = nil, ver = nil)
23
+ super()
24
+ add_attribute('node', node) if node
25
+ if ver
26
+ add_attribute('ver', ver)
27
+ add_attribute('hash', 'sha-1')
28
+ end
29
+ end
30
+
31
+ ##
32
+ # Get the value of this element's 'ver' attribute,
33
+ # an opaque hash representing this entity's capabilities.
34
+ def ver
35
+ attributes['ver']
36
+ end
37
+
38
+ ##
39
+ # Get the value of this element's 'node' attribute,
40
+ # a 'unique identifier for the software underlying the entity'
41
+ def node
42
+ attributes['node']
43
+ end
44
+
45
+ ##
46
+ # Get the value of this element's 'hash' attribute,
47
+ # the algorithm used in generating the 'ver' attribute
48
+ def hash
49
+ attributes['hash']
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,160 @@
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'
6
+ require 'xmpp4r/discovery'
7
+ require 'xmpp4r/dataforms'
8
+ require 'xmpp4r/caps/c'
9
+ require 'digest'
10
+ require 'xmpp4r/base64'
11
+
12
+ module Jabber
13
+ module Caps
14
+ def self.generate_ver_str(identities, features, forms=[])
15
+ # 1. Initialize an empty string S.
16
+ s = ''
17
+
18
+ # 2. Sort the service discovery identities [14] by category and
19
+ # then by type (if it exists) and then by xml:lang (if it
20
+ # exists), formatted as CATEGORY '/' [TYPE] '/' [LANG] '/'
21
+ # [NAME]. Note that each slash is included even if the TYPE,
22
+ # LANG, or NAME is not included.
23
+ identities.sort! do |identity1,identity2|
24
+ cmp_result = nil
25
+ [:category, :type, :xml_lang, :iname].each do |field|
26
+ value1 = identity1.send(field)
27
+ value2 = identity2.send(field)
28
+
29
+ if value1 != value2
30
+ cmp_result = value1 <=> value2
31
+ break
32
+ end
33
+ end
34
+
35
+
36
+ cmp_result
37
+ end
38
+
39
+ # 3. For each identity, append the 'category/type/lang/name' to
40
+ # S, followed by the '<' character.
41
+ s += identities.collect do |identity|
42
+ [:category, :type, :xml_lang, :iname].collect do |field|
43
+ identity.send(field).to_s
44
+ end.join('/') + '<'
45
+ end.join
46
+
47
+ # 4. Sort the supported service discovery features. [15]
48
+ features.sort! do |feature1,feature2|
49
+ feature1.var <=> feature2.var
50
+ end
51
+
52
+ # 5. For each feature, append the feature to S, followed by the
53
+ # '<' character.
54
+ s += features.collect do |feature|
55
+ feature.var.to_s + '<'
56
+ end.join
57
+
58
+ # 6. If the service discovery information response includes
59
+ # XEP-0128 data forms, sort the forms by the FORM_TYPE (i.e., by
60
+ # the XML character data of the <value/> element).
61
+ forms.sort! do |form1,form2|
62
+ fform_type1 = form1.field('FORM_TYPE')
63
+ fform_type2 = form2.field('FORM_TYPE')
64
+ form_type1 = fform_type1 ? fform_type1.values.to_s : nil
65
+ form_type2 = fform_type2 ? fform_type2.values.to_s : nil
66
+ form_type1 <=> form_type2
67
+ end
68
+
69
+ # 7. For each extended service discovery information form:
70
+ forms.each do |form|
71
+ # 7.1. Append the XML character data of the FORM_TYPE field's
72
+ # <value/> element, followed by the '<' character.
73
+ fform_type = form.field('FORM_TYPE')
74
+ form_type = fform_type ? fform_type.values.to_s : nil
75
+ s += "#{form_type}<"
76
+
77
+ # 7.2. Sort the fields by the value of the "var" attribute.
78
+ fields = form.fields(false).sort do |field1,field2|
79
+ field1.var <=> field2.var
80
+ end
81
+
82
+ # 7.3. For each field:
83
+
84
+ fields.each do |field|
85
+ # 7.3.1. Append the value of the "var" attribute, followed by
86
+ # the '<' character.
87
+ s += "#{field.var}<"
88
+
89
+ # 7.3.2. Sort values by the XML character data of the <value/>
90
+ # element.
91
+ values = field.values.sort do |value1,value2|
92
+ value1 <=> value2
93
+ end
94
+
95
+ # 7.3.3. For each <value/> element, append the XML character
96
+ # data, followed by the '<' character.
97
+ s += values.collect do |value|
98
+ "#{value}<"
99
+ end.join
100
+ end
101
+ end
102
+
103
+ # 8. Ensure that S is encoded according to the UTF-8 encoding
104
+ # (RFC 3269 [16]).
105
+
106
+ # (given in XMPP4R)
107
+
108
+ s
109
+ end
110
+
111
+ ##
112
+ # Implementation of the algorithm defined at:
113
+ # http://www.xmpp.org/extensions/xep-0115.html#ver-gen
114
+ def self.generate_ver(identities, features, forms=[], hash='sha-1')
115
+ s = generate_ver_str(identities, features, forms)
116
+
117
+ # 9. Compute the verification string by hashing S using the
118
+ # algorithm specified in the 'hash' attribute (e.g., SHA-1 as
119
+ # defined in RFC 3174 [17]). The hashed data MUST be generated
120
+ # with binary output and encoded using Base64 as specified in
121
+ # Section 4 of RFC 4648 [18] (note: the Base64 output MUST NOT
122
+ # include whitespace and MUST set padding bits to zero). [19]
123
+
124
+ # See http://www.iana.org/assignments/hash-function-text-names
125
+ hash_klass = case hash
126
+ when 'md2' then nil
127
+ when 'md5' then Digest::MD5
128
+ when 'sha-1' then Digest::SHA1
129
+ when 'sha-224' then nil
130
+ when 'sha-256' then Digest::SHA256
131
+ when 'sha-384' then Digest::SHA384
132
+ when 'sha-512' then Digest::SHA512
133
+ end
134
+ if hash_klass
135
+ Base64::encode64(hash_klass::digest(s)).strip
136
+ else
137
+ nil
138
+ end
139
+ end
140
+
141
+ ##
142
+ # Generate a ver hash from a Jabber::Discovery::IqQueryDiscoInfo result
143
+ # query:: [Jabber::Discovery::IqQueryDiscoInfo]
144
+ def self.generate_ver_from_discoinfo(query, hash='sha-1')
145
+ identities = []
146
+ features = []
147
+ forms = []
148
+ query.each_element do |element|
149
+ if element.kind_of? Discovery::Identity
150
+ identities << element
151
+ elsif element.kind_of? Discovery::Feature
152
+ features << element
153
+ elsif element.kind_of? Dataforms::XData
154
+ forms << element
155
+ end
156
+ end
157
+ generate_ver(identities, features, forms, hash)
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,87 @@
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'
6
+ require 'xmpp4r/discovery'
7
+ require 'xmpp4r/caps/c'
8
+ require 'xmpp4r/caps/helper/generator'
9
+
10
+ module Jabber
11
+ module Caps
12
+ ##
13
+ # A Helper to manage advertising and discovery of entity capabilities.
14
+ #
15
+ # Following XEP-0115 (ver 1.4 http://www.xmpp.org/extensions/xep-0115.html).
16
+ # you can use this Helper to, for example, advertise that your client
17
+ # wishes to receive XEP-0118 User Tune notifications, eg:
18
+ #
19
+ # caps_helper=Jabber::Caps::Helper(cl,
20
+ # [Jabber::Discovery::Identity.new('client',nil,'bot')],
21
+ # [Jabber::Discovery::Feature.new('http://jabber.org/protocol/tune+notify')]
22
+ # )
23
+ class Helper
24
+ attr_accessor :identities, :features, :node
25
+
26
+ ##
27
+ # Construct a new Caps Helper.
28
+ #
29
+ # This will send a <presence> message containing
30
+ # a <c/> (Jabber::Caps::C) stanza to your server.
31
+ # client:: [Jabber::Stream]
32
+ # i:: [Array] of [Jabber::Discovery::Identity] objects that this entity will advertise
33
+ # f:: [Array] of [Jabber::Discovery::Feature] objects that this entity will advertise
34
+ # n:: [String] an identifier representing the software underlying this entity
35
+ def initialize(client,i=[],f=[],n="http://home.gna.org/xmpp4r/##{Jabber::XMPP4R_VERSION}")
36
+ @stream = client
37
+ @identities = i
38
+ @features = f
39
+ @node = n
40
+
41
+ @stream.add_iq_callback(250) do |iq|
42
+ if iq.type == :get and iq.query.kind_of? Jabber::Discovery::IqQueryDiscoInfo
43
+ Thread.new do
44
+ Thread.abort_on_exception = true
45
+ handle_discoinfo_query(iq)
46
+ end
47
+ true
48
+ else
49
+ false
50
+ end
51
+ end
52
+
53
+ p = Jabber::Presence.new()
54
+ p.add(c)
55
+ @stream.send(p)
56
+ end
57
+
58
+ ##
59
+ # Return a <c/> element for inclusion in your own <presence>
60
+ # stanzas.
61
+ def c
62
+ Jabber::Caps::C.new(node, ver)
63
+ end
64
+
65
+ ##
66
+ # Send actual identities/ features back to a requesting entity
67
+ def handle_discoinfo_query(iq)
68
+ caps_reply = Jabber::XMPPStanza.answer(iq)
69
+ caps_reply.type = :result
70
+ caps_reply.query = Jabber::Discovery::IqQueryDiscoInfo.new
71
+ @identities.each { |i| caps_reply.query.add(i) }
72
+ @features.each { |f| caps_reply.query.add(f) }
73
+
74
+ @stream.send(caps_reply)
75
+ end
76
+
77
+ ##
78
+ # Generate 'ver', an opaque hash used to represent this entity's
79
+ # capabilities
80
+ #
81
+ # See http://www.xmpp.org/extensions/xep-0115.html#ver
82
+ def ver
83
+ Caps::generate_ver(@identities, @features)
84
+ end
85
+ end
86
+ end
87
+ end