# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://home.gna.org/xmpp4r/ require 'xmpp4r' require 'xmpp4r/discovery' require 'xmpp4r/dataforms' require 'xmpp4r/caps/c' require 'digest' require 'xmpp4r/base64' module Jabber module Caps def self.generate_ver_str(identities, features, forms=[]) # 1. Initialize an empty string S. s = '' # 2. Sort the service discovery identities [14] 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, :iname].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, :iname].collect do |field| identity.send(field).to_s end.join('/') + '<' end.join # 4. Sort the supported service discovery features. [15] features.sort! do |feature1,feature2| feature1.var <=> feature2.var end # 5. For each feature, append the feature to S, followed by the # '<' character. s += features.collect do |feature| feature.var.to_s + '<' end.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 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 # element, followed by the '<' character. fform_type = form.field('FORM_TYPE') form_type = fform_type ? fform_type.first_element_text('value') : nil s += "#{form_type}<" # 7.2. Sort the fields by the value of the "var" attribute. fields = form.fields(false).sort do |field1,field2| field1.var <=> field2.var end # 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 # element. values = field.values.sort do |value1,value2| value1 <=> value2 end # 7.3.3. For each element, append the XML character # data, followed by the '<' character. s += values.collect do |value| "#{value}<" end.join end end # 8. Ensure that S is encoded according to the UTF-8 encoding # (RFC 3269 [16]). # (given in XMPP4R) s end ## # Implementation of the algorithm defined at: # http://www.xmpp.org/extensions/xep-0115.html#ver-gen def self.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 [17]). The hashed data MUST be generated # with binary output and encoded using Base64 as specified in # Section 4 of RFC 4648 [18] (note: the Base64 output MUST NOT # include whitespace and MUST set padding bits to zero). [19] # 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 if hash_klass Base64::encode64(hash_klass::digest(s)).strip else nil end end ## # Generate a ver hash from a Jabber::Discovery::IqQueryDiscoInfo result # query:: [Jabber::Discovery::IqQueryDiscoInfo] def self.generate_ver_from_discoinfo(query, hash='sha-1') identities = [] features = [] forms = [] query.each_element do |element| if element.kind_of? Discovery::Identity identities << element elsif element.kind_of? Discovery::Feature features << element elsif element.kind_of? Dataforms::XData forms << element end end generate_ver(identities, features, forms, hash) end end end