using Gee; namespace Xmpp.Xep.Coin { private const string NS_RFC = "urn:ietf:params:xml:ns:conference-info"; public class Module : XmppStreamModule, Iq.Handler { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_RFC, "0298_coin"); public signal void coin_info_received(Jid jid, ConferenceInfo info); public async override void on_iq_set(XmppStream stream, Iq.Stanza iq) { ConferenceInfo? info = parse_node(iq.stanza.get_subnode("conference-info", NS_RFC), null); if (info == null) return; coin_info_received(iq.from, info); } public override void attach(XmppStream stream) { stream.get_module(Iq.Module.IDENTITY).register_for_namespace(NS_RFC, this); } public override void detach(XmppStream stream) { } public override string get_ns() { return NS_RFC; } public override string get_id() { return IDENTITY.id; } } public ConferenceInfo? parse_node(StanzaNode conference_node, ConferenceInfo? previous_conference_info) { string? version_str = conference_node.get_attribute("version"); string? conference_state = conference_node.get_attribute("state"); if (version_str == null || conference_state == null) return null; int version = int.parse(version_str); if (previous_conference_info != null && version <= previous_conference_info.version) return null; ConferenceInfo conference_info = previous_conference_info ?? new ConferenceInfo(); conference_info.version = version; Gee.List user_nodes = conference_node.get_deep_subnodes(NS_RFC + ":users", NS_RFC + ":user"); foreach (StanzaNode user_node in user_nodes) { string? jid_string = user_node.get_attribute("entity"); if (jid_string == null) continue; // if (!jid_string.has_prefix("xmpp:")) continue; // silk does this wrong Jid? jid = null; try { jid = new Jid(jid_string.substring(4)); } catch (Error e) { continue; } string user_state = user_node.get_attribute("state"); if (conference_state == "full" && user_state != "full") return null; if (user_state == "deleted") { conference_info.users.unset(jid); continue; } ConferenceUser user = new ConferenceUser(); user.jid = jid; user.display_text = user_node.get_deep_string_content(NS_RFC + ":display-text"); Gee.List endpoint_nodes = user_node.get_subnodes("endpoint"); foreach (StanzaNode entpoint_node in endpoint_nodes) { Gee.List media_nodes = entpoint_node.get_subnodes("media"); foreach (StanzaNode media_node in media_nodes) { string? id = media_node.get_attribute("id"); string? ty = media_node.get_deep_string_content(NS_RFC + ":type"); string? src_id_str = media_node.get_deep_string_content(NS_RFC + ":src-id"); if (id == null) continue; ConferenceMedia media = new ConferenceMedia(); media.id = id; media.src_id = src_id_str != null ? int.parse(src_id_str) : -1; media.ty = ty; user.medias[id] = media; } conference_info.users[user.jid] = user; } } return conference_info; } public class ConferenceInfo { public int version = -1; public HashMap users = new HashMap(Jid.hash_func, Jid.equals_func); public StanzaNode to_xml() { StanzaNode ret = new StanzaNode.build("conference-info", NS_RFC).add_self_xmlns() .put_attribute("version", this.version.to_string()) .put_attribute("state", "full"); StanzaNode users_node = new StanzaNode.build("users", NS_RFC); foreach (ConferenceUser user in this.users.values) { users_node.put_node(user.to_xml()); } ret.put_node(users_node); return ret; } } public class ConferenceUser { public Jid jid; public string? display_text; public HashMap medias = new HashMap(); public StanzaNode to_xml() { StanzaNode user_node = new StanzaNode.build("user", NS_RFC) .put_attribute("entity", jid.to_string()); foreach (ConferenceMedia media in medias.values) { user_node.put_node(media.to_xml()); } return user_node; } } public class ConferenceMedia { public string id; public string? ty; public int src_id = -1; public StanzaNode to_xml() { StanzaNode media_node = new StanzaNode.build("media", NS_RFC) .put_attribute("id", id); if (ty != null) { media_node.put_node(new StanzaNode.build("type", NS_RFC).put_node(new StanzaNode.text(ty))); } if (src_id != -1) { media_node.put_node(new StanzaNode.build("src-id", NS_RFC).put_node(new StanzaNode.text(src_id.to_string()))); } return media_node; } } }