# encoding: UTF-8 module Vines class Stream class Client class Bind < State NS = NAMESPACES[:bind] MAX_ATTEMPTS = 5 def initialize(stream, success=Ready) super @attempts = 0 end def node(node) @attempts += 1 raise StreamErrors::NotAuthorized unless bind?(node) raise StreamErrors::PolicyViolation.new('max bind attempts reached') if @attempts > MAX_ATTEMPTS raise StanzaErrors::ResourceConstraint.new(node, 'wait') if resource_limit_reached? stream.bind!(resource(node)) doc = Document.new result = doc.create_element('iq', 'id' => node['id'], 'type' => 'result') do |el| el << doc.create_element('bind') do |bind| bind.default_namespace = NS bind << doc.create_element('jid', stream.user.jid.to_s) end end stream.write(result) send_empty_features advance end private # Write the final element to the stream, indicating # stream negotiation is complete and the client is cleared to send # stanzas. def send_empty_features stream.write('') end def bind?(node) node.name == 'iq' && node['type'] == 'set' && node.xpath('ns:bind', 'ns' => NS).any? end def resource(node) el = node.xpath('ns:bind/ns:resource', 'ns' => NS).first resource = el ? el.text.strip : '' generate = resource.empty? || !resource_valid?(resource) || resource_used?(resource) generate ? Kit.uuid : resource end def resource_limit_reached? used = stream.connected_resources(stream.user.jid.bare).size used >= stream.max_resources_per_account end def resource_used?(resource) stream.available_resources(stream.user.jid).any? do |c| c.user.jid.resource == resource end end def resource_valid?(resource) jid = stream.user.jid JID.new(jid.node, jid.domain, resource) rescue false end end end end end