Software /
code /
verse
File
plugins/roster.lua @ 498:50d0bd035bb7
util.sasl.oauthbearer: Don't send authzid
It's not needed and not recommended in XMPP unless we want to act as
someone other than who we authenticate as. We find out the JID during
resource binding.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Fri, 23 Jun 2023 12:09:49 +0200 |
parent | 490:6b2f31da9610 |
line wrap: on
line source
local verse = require "verse"; local bare_jid = require "prosody.util.jid".bare; local xmlns_roster = "jabber:iq:roster"; local xmlns_rosterver = "urn:xmpp:features:rosterver"; local t_insert = table.insert; function verse.plugins.roster(stream) local ver_supported = false; local roster = { items = {}; ver = ""; -- TODO: -- groups = {}; }; stream.roster = roster; stream:hook("stream-features", function(features_stanza) if features_stanza:get_child("ver", xmlns_rosterver) then ver_supported = true; end end); local function item_lua2xml(item_table) local xml_item = verse.stanza("item", { xmlns = xmlns_roster }); for k, v in pairs(item_table) do if k ~= "groups" then xml_item.attr[k] = v; else for i = 1,#v do xml_item:tag("group"):text(v[i]):up(); end end end return xml_item; end local function item_xml2lua(xml_item) local item_table = { }; local groups = {}; item_table.groups = groups; for k, v in pairs(xml_item.attr) do if k ~= "xmlns" then item_table[k] = v end end for group in xml_item:childtags("group") do t_insert(groups, group:get_text()) end return item_table; end function roster:load(r) roster.ver, roster.items = r.ver, r.items; end function roster:dump() return { ver = roster.ver, items = roster.items, }; end -- should this be add_contact(item, callback) instead? function roster:add_contact(jid, name, groups, callback) local item = { jid = jid, name = name, groups = groups }; local stanza = verse.iq({ type = "set" }) :tag("query", { xmlns = xmlns_roster }) :add_child(item_lua2xml(item)); stream:send_iq(stanza, function (reply) if not callback then return end if reply.attr.type == "result" then callback(true); else callback(nil, reply); end end); end -- What about subscriptions? function roster:delete_contact(jid, callback) jid = (type(jid) == "table" and jid.jid) or jid; local item = { jid = jid, subscription = "remove" } if not roster.items[jid] then return false, "item-not-found"; end stream:send_iq(verse.iq({ type = "set" }) :tag("query", { xmlns = xmlns_roster }) :add_child(item_lua2xml(item)), function (reply) if not callback then return end if reply.attr.type == "result" then callback(true); else callback(nil, reply); end end); end local function add_item(item) -- Takes one roster <item/> local roster_item = item_xml2lua(item); roster.items[roster_item.jid] = roster_item; end -- Private low level local function delete_item(jid) local deleted_item = roster.items[jid]; roster.items[jid] = nil; return deleted_item; end function roster:fetch(callback) stream:send_iq(verse.iq({type="get"}):tag("query", { xmlns = xmlns_roster, ver = ver_supported and roster.ver or nil }), function (result) if result.attr.type == "result" then local query = result:get_child("query", xmlns_roster); if query then roster.items = {}; for item in query:childtags("item") do add_item(item) end roster.ver = query.attr.ver or ""; end callback(roster); else callback(nil, result); end end); end stream:hook("iq/"..xmlns_roster, function(stanza) local type, from = stanza.attr.type, stanza.attr.from; if type == "set" and (not from or from == bare_jid(stream.jid)) then local query = stanza:get_child("query", xmlns_roster); local item = query and query:get_child("item"); if item then local event, target; local jid = item.attr.jid; if item.attr.subscription == "remove" then event = "removed" target = delete_item(jid); else event = roster.items[jid] and "changed" or "added"; add_item(item) target = roster.items[jid]; end roster.ver = query.attr.ver; if target then stream:event("roster/item-"..event, target); end -- TODO else return error? Events? end stream:send(verse.reply(stanza)) return true; end end); end