lib/blather/client/client.rb in blather-0.4.15 vs lib/blather/client/client.rb in blather-0.4.16
- old
+ new
@@ -31,11 +31,12 @@
# client.write Blather::Stanza::Message.new(m.from, "You sent: #{m.body}")
# end
#
class Client
attr_reader :jid,
- :roster
+ :roster,
+ :caps
# Create a new client and set it up
#
# @param [Blather::JID, #to_s] jid the JID to authorize with
# @param [String] password the password to authorize with
@@ -54,10 +55,11 @@
@status = Stanza::Presence::Status.new
@handlers = {}
@tmp_handlers = {}
@filters = {:before => [], :after => []}
@roster = Roster.new self
+ @caps = Caps.new
setup_initial_handlers
end
# Get the current status. Taken from the `state` attribute of Status
@@ -311,8 +313,144 @@
else
raise "Bad guard: #{guard.inspect}"
end
end
end
+
+ class Caps < Blather::Stanza::DiscoInfo
+ def self.new
+ super :result
+ end
+
+ def ver
+ generate_ver identities, features
+ end
+
+ def node=(node)
+ @bare_node = node
+ super "#{node}##{ver}"
+ end
+
+ def identities=(identities)
+ super identities
+ regenerate_full_node
+ end
+
+ def features=(features)
+ super features
+ regenerate_full_node
+ end
+
+ def c
+ Blather::Stanza::Presence::C.new @bare_node, ver
+ end
+
+ private
+
+ def regenerate_full_node
+ self.node = @bare_node
+ end
+
+ def generate_ver_str(identities, features, forms = [])
+ # 1. Initialize an empty string S.
+ s = ''
+
+ # 2. Sort the service discovery identities by category and
+ # then by type (if it exists) and then by xml:lang (if it
+ # exists), formatted as CATEGORY '/' [TYPE] '/' [LANG] '/'
+ # [NAME]. Note that each slash is included even if the TYPE,
+ # LANG, or NAME is not included.
+ identities.sort! do |identity1, identity2|
+ cmp_result = nil
+ [:category, :type, :xml_lang, :name].each do |field|
+ value1 = identity1.send(field)
+ value2 = identity2.send(field)
+
+ if value1 != value2
+ cmp_result = value1 <=> value2
+ break
+ end
+ end
+ cmp_result
+ end
+
+ # 3. For each identity, append the 'category/type/lang/name' to
+ # S, followed by the '<' character.
+ s += identities.collect do |identity|
+ [:category, :type, :xml_lang, :name].collect do |field|
+ identity.send(field).to_s
+ end.join('/') + '<'
+ end.join
+
+ # 4. Sort the supported service discovery features.
+ features.sort! { |feature1, feature2| feature1.var <=> feature2.var }
+
+ # 5. For each feature, append the feature to S, followed by the
+ # '<' character.
+ s += features.collect { |feature| feature.var.to_s + '<' }.join
+
+ # 6. If the service discovery information response includes
+ # XEP-0128 data forms, sort the forms by the FORM_TYPE (i.e., by
+ # the XML character data of the <value/> element).
+ forms.sort! do |form1, form2|
+ fform_type1 = form1.field 'FORM_TYPE'
+ fform_type2 = form2.field 'FORM_TYPE'
+ form_type1 = fform_type1 ? fform_type1.values.to_s : nil
+ form_type2 = fform_type2 ? fform_type2.values.to_s : nil
+ form_type1 <=> form_type2
+ end
+
+ # 7. For each extended service discovery information form:
+ forms.each do |form|
+ # 7.1. Append the XML character data of the FORM_TYPE field's
+ # <value/> element, followed by the '<' character.
+ fform_type = form.field 'FORM_TYPE'
+ form_type = fform_type ? fform_type.values.to_s : nil
+ s += "#{form_type}<"
+
+ # 7.2. Sort the fields by the value of the "var" attribute
+ fields = form.fields.sort { |field1, field2| field1.var <=> field2.var }
+
+ # 7.3. For each field:
+ fields.each do |field|
+ # 7.3.1. Append the value of the "var" attribute, followed by
+ # the '<' character.
+ s += "#{field.var}<"
+
+ # 7.3.2. Sort values by the XML character data of the <value/> element
+ # values = field.values.sort { |value1, value2| value1 <=> value2 }
+
+ # 7.3.3. For each <value/> element, append the XML character
+ # data, followed by the '<' character.
+ # s += values.collect { |value| "#{value}<" }.join
+ s += "#{field.value}<"
+ end
+ end
+ s
+ end
+
+ def generate_ver(identities, features, forms = [], hash = 'sha-1')
+ s = generate_ver_str identities, features, forms
+
+ # 9. Compute the verification string by hashing S using the
+ # algorithm specified in the 'hash' attribute (e.g., SHA-1 as
+ # defined in RFC 3174). The hashed data MUST be generated
+ # with binary output and encoded using Base64 as specified in
+ # Section 4 of RFC 4648 (note: the Base64 output MUST NOT
+ # include whitespace and MUST set padding bits to zero).
+
+ # See http://www.iana.org/assignments/hash-function-text-names
+ hash_klass = case hash
+ when 'md2' then nil
+ when 'md5' then Digest::MD5
+ when 'sha-1' then Digest::SHA1
+ when 'sha-224' then nil
+ when 'sha-256' then Digest::SHA256
+ when 'sha-384' then Digest::SHA384
+ when 'sha-512' then Digest::SHA512
+ end
+ hash_klass ? [hash_klass::digest(s)].pack('m').strip : nil
+ end
+ end # Caps
end # Client
end # Blather