1using Gee; 2 3namespace Xmpp.Xep.ServiceDiscovery { 4 5public const string NS_URI = "http://jabber.org/protocol/disco"; 6public const string NS_URI_INFO = NS_URI + "#info"; 7public const string NS_URI_ITEMS = NS_URI + "#items"; 8 9public class Module : XmppStreamModule, Iq.Handler { 10 public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "0030_service_discovery_module"); 11 12 private HashMap<Jid, Future<InfoResult?>> active_info_requests = new HashMap<Jid, Future<InfoResult?>>(Jid.hash_func, Jid.equals_func); 13 14 public Identity own_identity; 15 public CapsCache cache; 16 17 public Module.with_identity(string category, string type, string? name = null) { 18 this.own_identity = new Identity(category, type, name); 19 } 20 21 public void add_feature(XmppStream stream, string feature) { 22 stream.get_flag(Flag.IDENTITY).add_own_feature(feature); 23 } 24 25 public void remove_feature(XmppStream stream, string feature) { 26 stream.get_flag(Flag.IDENTITY).remove_own_feature(feature); 27 } 28 29 public void add_feature_notify(XmppStream stream, string feature) { 30 add_feature(stream, feature + "+notify"); 31 } 32 33 public void remove_feature_notify(XmppStream stream, string feature) { 34 remove_feature(stream, feature + "+notify"); 35 } 36 37 public void add_identity(XmppStream stream, Identity identity) { 38 stream.get_flag(Flag.IDENTITY).add_own_identity(identity); 39 } 40 41 public void remove_identity(XmppStream stream, Identity identity) { 42 stream.get_flag(Flag.IDENTITY).remove_own_identity(identity); 43 } 44 45 public async bool has_entity_feature(XmppStream stream, Jid jid, string feature) { 46 return yield this.cache.has_entity_feature(jid, feature); 47 } 48 49 public async Gee.Set<Identity>? get_entity_identities(XmppStream stream, Jid jid) { 50 return yield this.cache.get_entity_identities(jid); 51 } 52 53 public async InfoResult? request_info(XmppStream stream, Jid jid) { 54 var future = active_info_requests[jid]; 55 if (future == null) { 56 var promise = new Promise<InfoResult?>(); 57 future = promise.future; 58 active_info_requests[jid] = future; 59 60 Iq.Stanza iq = new Iq.Stanza.get(new StanzaNode.build("query", NS_URI_INFO).add_self_xmlns()) { to=jid }; 61 Iq.Stanza iq_response = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq); 62 InfoResult? result = InfoResult.create_from_iq(iq_response); 63 64 promise.set_value(result); 65 active_info_requests.unset(jid); 66 } 67 68 try { 69 InfoResult? res = yield future.wait_async(); 70 return res; 71 } catch (FutureError error) { 72 warning("Future error when waiting for info request result: %s", error.message); 73 return null; 74 } 75 } 76 77 public async ItemsResult? request_items(XmppStream stream, Jid jid) { 78 StanzaNode query_node = new StanzaNode.build("query", NS_URI_ITEMS).add_self_xmlns(); 79 Iq.Stanza iq = new Iq.Stanza.get(query_node) { to=jid }; 80 81 Iq.Stanza iq_result = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq); 82 ItemsResult? result = ItemsResult.create_from_iq(iq_result); 83 84 return result; 85 } 86 87 public async void on_iq_get(XmppStream stream, Iq.Stanza iq) { 88 StanzaNode? query_node = iq.stanza.get_subnode("query", NS_URI_INFO); 89 if (query_node != null) { 90 send_query_result(stream, iq); 91 } 92 } 93 94 public override void attach(XmppStream stream) { 95 stream.add_flag(new Flag()); 96 add_identity(stream, own_identity); 97 98 stream.get_module(Iq.Module.IDENTITY).register_for_namespace(NS_URI_INFO, this); 99 add_feature(stream, NS_URI_INFO); 100 } 101 102 public override void detach(XmppStream stream) { 103 active_info_requests.clear(); 104 105 remove_identity(stream, own_identity); 106 107 stream.get_module(Iq.Module.IDENTITY).unregister_from_namespace(NS_URI_INFO, this); 108 remove_feature(stream, NS_URI_INFO); 109 } 110 111 public static void require(XmppStream stream) { 112 if (stream.get_module(IDENTITY) == null) stream.add_module(new ServiceDiscovery.Module()); 113 } 114 115 public override string get_ns() { return NS_URI; } 116 public override string get_id() { return IDENTITY.id; } 117 118 private void send_query_result(XmppStream stream, Iq.Stanza iq_request) { 119 InfoResult query_result = new ServiceDiscovery.InfoResult(iq_request); 120 query_result.features = stream.get_flag(Flag.IDENTITY).own_features; 121 query_result.identities = stream.get_flag(Flag.IDENTITY).own_identities; 122 stream.get_module(Iq.Module.IDENTITY).send_iq(stream, query_result.iq); 123 } 124} 125 126public interface CapsCache : Object { 127 public abstract async bool has_entity_feature(Jid jid, string feature); 128 public abstract async Gee.Set<Identity> get_entity_identities(Jid jid); 129} 130 131} 132