class Vines::Stanza::Iq::Roster

Constants

NS

Public Instance Methods

process() click to toggle source
# File lib/vines/stanza/iq/roster.rb, line 11
def process
  validate_to_address
  get? ? roster_query : update_roster
end

Private Instance Methods

presence(to, type) click to toggle source
# File lib/vines/stanza/iq/roster.rb, line 115
def presence(to, type)
  doc = Document.new
  doc.create_element('presence',
    'from' => stream.user.jid.bare.to_s,
    'id'   => Kit.uuid,
    'to'   => to.to_s,
    'type' => type)
end
push_roster_updates(to, follower) click to toggle source

Send an iq set stanza to the user's interested resources, letting them know their roster has been updated.

# File lib/vines/stanza/iq/roster.rb, line 126
def push_roster_updates(to, follower)
  stream.interested_resources(to).each do |recipient|
    follower.send_roster_push(recipient)
  end
end
remove_follower(jid) click to toggle source

Remove the follower with this JID from the user's roster and send roster pushes to the user's interested resources. This is triggered by receiving an iq set with an item element like <item jid=“alice@wonderland.lit” subscription=“remove”/>. RFC 6121 section 2.5.

# File lib/vines/stanza/iq/roster.rb, line 74
def remove_follower(jid)
  follower = stream.user.follower(jid)
  raise StanzaErrors::ItemNotFound.new(self, 'modify') unless follower
  if local_jid?(follower.jid)
    user = storage(follower.jid.domain).find_user(follower.jid)
  end

  if user && user.follower(stream.user.jid)
    user.follower(stream.user.jid).subscription = 'none'
    user.follower(stream.user.jid).ask = nil
  end
  stream.user.remove_follower(follower.jid)
  [user, stream.user].compact.each do |save|
    storage(save.jid.domain).save_user(save)
    stream.update_user_streams(save)
  end

  send_result_iq
  push_roster_updates(stream.user.jid,
    Follower.new(jid: follower.jid, subscription: 'remove'))

  if local_jid?(follower.jid)
    send_unavailable(stream.user.jid, follower.jid) if follower.subscribed_from?
    send_unsubscribe(follower)
    if user && user.follower(stream.user.jid)
      push_roster_updates(follower.jid, user.follower(stream.user.jid))
    end
  else
    send_unsubscribe(follower)
  end
end
roster_query() click to toggle source

Send an iq result stanza containing roster items to the user in response to their roster get request. Requesting the roster makes this stream an “interested resource” that can now receive roster updates.

# File lib/vines/stanza/iq/roster.rb, line 22
def roster_query
  stream.requested_roster!
  stream.write(stream.user.to_roster_xml(self['id']))
end
send_result_iq() click to toggle source
# File lib/vines/stanza/iq/roster.rb, line 132
def send_result_iq
  node = to_result
  node.remove_attribute('from')
  stream.write(node)
end
send_unsubscribe(follower) click to toggle source

Notify the follower that it's been removed from the user's roster and no longer has any presence relationship with the user.

# File lib/vines/stanza/iq/roster.rb, line 108
def send_unsubscribe(follower)
  presence = [%w[to unsubscribe], %w[from unsubscribed]].map do |meth, type|
    presence(follower.jid, type) if follower.send("subscribed_#{meth}?")
  end.compact
  broadcast_to_interested_resources(presence, follower.jid)
end
update_roster() click to toggle source

Add, update, or delete the roster item contained in the iq set stanza received from the client. RFC 6121 sections 2.3, 2.4, 2.5.

# File lib/vines/stanza/iq/roster.rb, line 38
def update_roster
  items = self.xpath('ns:query/ns:item', 'ns' => NS)
  raise StanzaErrors::BadRequest.new(self, 'modify') if items.size != 1
  item = items.first

  jid = JID.new(item['jid']) rescue (raise StanzaErrors::JidMalformed.new(self, 'modify'))
  raise StanzaErrors::BadRequest.new(self, 'modify') if jid.empty? || !jid.bare?

  if item['subscription'] == 'remove'
    remove_follower(jid)
    return
  end

  raise StanzaErrors::NotAllowed.new(self, 'modify') if jid == stream.user.jid.bare
  groups = item.xpath('ns:group', 'ns' => NS).map {|g| g.text.strip }
  raise StanzaErrors::BadRequest.new(self, 'modify') if groups.uniq!
  raise StanzaErrors::NotAcceptable.new(self, 'modify') if groups.include?('')

  follower = stream.user.follower(jid)
  unless follower
    follower = Follower.new(jid: jid)
    stream.user.roster << follower
  end
  follower.name = item['name']
  follower.groups = groups
  storage.save_user(stream.user)
  stream.update_user_streams(stream.user)
  send_result_iq
  push_roster_updates(stream.user.jid, follower)
end
validate_to_address() click to toggle source

Roster sets must have no 'to' address or be addressed to the same JID that sent the stanza. RFC 6121 sections 2.1.5 and 2.3.3.

# File lib/vines/stanza/iq/roster.rb, line 29
def validate_to_address
  to = validate_to
  unless to.nil? || to.bare == stream.user.jid.bare
    raise StanzaErrors::Forbidden.new(self, 'auth')
  end
end