1using Gee; 2 3namespace Xmpp.Xep.BlockingCommand { 4 5private const string NS_URI = "urn:xmpp:blocking"; 6 7public class Module : XmppStreamModule, Iq.Handler { 8 public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "0191_blocking_command"); 9 10 public signal void block_push_received(XmppStream stream, Gee.List<string> jids); 11 public signal void unblock_push_received(XmppStream stream, Gee.List<string> jids); 12 public signal void unblock_all_received(XmppStream stream); 13 14 public bool is_blocked(XmppStream stream, string jid) { 15 return stream.get_flag(Flag.IDENTITY).blocklist.contains(jid); 16 } 17 18 public bool block(XmppStream stream, Gee.List<string> jids) { 19 if (jids.size == 0) return false; // This would otherwise be a bad-request error. 20 21 StanzaNode block_node = new StanzaNode.build("block", NS_URI).add_self_xmlns(); 22 fill_node_with_items(block_node, jids); 23 Iq.Stanza iq = new Iq.Stanza.set(block_node); 24 stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq, null); 25 return true; 26 } 27 28 public bool unblock(XmppStream stream, Gee.List<string> jids) { 29 if (jids.size == 0) return false; // This would otherwise unblock all blocked JIDs. 30 31 StanzaNode unblock_node = new StanzaNode.build("unblock", NS_URI).add_self_xmlns(); 32 fill_node_with_items(unblock_node, jids); 33 Iq.Stanza iq = new Iq.Stanza.set(unblock_node); 34 stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq, null); 35 return true; 36 } 37 38 public void unblock_all(XmppStream stream) { 39 StanzaNode unblock_node = new StanzaNode.build("unblock", NS_URI).add_self_xmlns(); 40 Iq.Stanza iq = new Iq.Stanza.set(unblock_node); 41 stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq, null); 42 } 43 44 public bool is_supported(XmppStream stream) { 45 return stream.has_flag(Flag.IDENTITY); 46 } 47 48 private async void on_iq_set(XmppStream stream, Iq.Stanza iq) { 49 StanzaNode? block_node = iq.stanza.get_subnode("block", NS_URI); 50 StanzaNode? unblock_node = iq.stanza.get_subnode("unblock", NS_URI); 51 Gee.List<string> jids; 52 if (block_node != null) { 53 jids = get_jids_from_items(block_node); 54 stream.get_flag(Flag.IDENTITY).blocklist.add_all(jids); 55 block_push_received(stream, jids); 56 } else if (unblock_node != null) { 57 jids = get_jids_from_items(unblock_node); 58 if (jids.size > 0) { 59 stream.get_flag(Flag.IDENTITY).blocklist.remove_all(jids); 60 unblock_push_received(stream, jids); 61 } else { 62 stream.get_flag(Flag.IDENTITY).blocklist.clear(); 63 unblock_all_received(stream); 64 } 65 } 66 } 67 68 public override void attach(XmppStream stream) { 69 stream.get_module(Iq.Module.IDENTITY).register_for_namespace(NS_URI, this); 70 stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI); 71 stream.stream_negotiated.connect(on_stream_negotiated); 72 } 73 74 public override void detach(XmppStream stream) { 75 stream.get_module(Iq.Module.IDENTITY).unregister_from_namespace(NS_URI, this); 76 stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI); 77 stream.stream_negotiated.disconnect(on_stream_negotiated); 78 } 79 80 public override string get_ns() { return NS_URI; } 81 public override string get_id() { return IDENTITY.id; } 82 83 private async void on_stream_negotiated(XmppStream stream) { 84 bool has_feature = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, stream.remote_name, NS_URI); 85 if (has_feature) { 86 stream.add_flag(new Flag()); 87 stream.get_flag(Flag.IDENTITY).blocklist = yield get_blocklist(stream); 88 } 89 } 90 91 private async Gee.List<string> get_blocklist(XmppStream stream) { 92 StanzaNode blocklist_node = new StanzaNode.build("blocklist", NS_URI).add_self_xmlns(); 93 Iq.Stanza iq = new Iq.Stanza.get(blocklist_node); 94 95 Iq.Stanza result_iq = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq); 96 StanzaNode? node = result_iq.stanza.get_subnode("blocklist", NS_URI); 97 if (node != null) { 98 return get_jids_from_items(node); 99 } 100 return new ArrayList<string>(); 101 } 102 103 private Gee.List<string> get_jids_from_items(StanzaNode node) { 104 Gee.List<StanzaNode> item_nodes = node.get_subnodes("item", NS_URI); 105 Gee.List<string> jids = new ArrayList<string>(); 106 foreach (StanzaNode item_node in item_nodes) { 107 string? jid = item_node.get_attribute("jid", NS_URI); 108 if (jid != null) { 109 jids.add(jid); 110 } 111 } 112 return jids; 113 } 114 115 private void fill_node_with_items(StanzaNode node, Gee.List<string> jids) { 116 foreach (string jid in jids) { 117 StanzaNode item_node = new StanzaNode.build("item", NS_URI).add_self_xmlns(); 118 item_node.set_attribute("jid", jid, NS_URI); 119 node.put_node(item_node); 120 } 121 } 122} 123 124public class Flag : XmppStreamFlag { 125 public static FlagIdentity<Flag> IDENTITY = new FlagIdentity<Flag>(NS_URI, "blocking_command"); 126 127 public Gee.List<string> blocklist; 128 129 public override string get_ns() { return NS_URI; } 130 public override string get_id() { return IDENTITY.id; } 131} 132 133} 134