diff options
Diffstat (limited to 'xmpp-vala/src/module')
8 files changed, 191 insertions, 152 deletions
diff --git a/xmpp-vala/src/module/xep/0030_service_discovery/flag.vala b/xmpp-vala/src/module/xep/0030_service_discovery/flag.vala index d4874fca..0ecd98fb 100644 --- a/xmpp-vala/src/module/xep/0030_service_discovery/flag.vala +++ b/xmpp-vala/src/module/xep/0030_service_discovery/flag.vala @@ -23,8 +23,8 @@ public class Flag : XmppStreamFlag { owned get { return own_identities_.read_only_view; } } - public Gee.Set<Identity>? get_entity_categories(Jid jid) { - return entity_identities.has_key(jid) ? entity_identities[jid] : null; // TODO isn’t this default for hashmap + public Gee.Set<Identity>? get_entity_identities(Jid jid) { + return entity_identities.has_key(jid) ? entity_identities[jid].read_only_view : null; // TODO isn’t this default for hashmap } public bool? has_entity_identity(Jid jid, string category, string type) { diff --git a/xmpp-vala/src/module/xep/0030_service_discovery/module.vala b/xmpp-vala/src/module/xep/0030_service_discovery/module.vala index 332e8fc3..e63dee74 100644 --- a/xmpp-vala/src/module/xep/0030_service_discovery/module.vala +++ b/xmpp-vala/src/module/xep/0030_service_discovery/module.vala @@ -9,6 +9,8 @@ public const string NS_URI_ITEMS = NS_URI + "#items"; public class Module : XmppStreamModule, Iq.Handler { public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "0030_service_discovery_module"); + private HashMap<Jid, Future<InfoResult?>> active_info_requests = new HashMap<Jid, Future<InfoResult?>>(Jid.hash_func, Jid.equals_func); + public Identity own_identity; public Module.with_identity(string category, string type, string? name = null) { @@ -39,36 +41,63 @@ public class Module : XmppStreamModule, Iq.Handler { stream.get_flag(Flag.IDENTITY).remove_own_identity(identity); } - public delegate void HasEntryCategoryRes(XmppStream stream, Gee.Set<Identity>? identities); - public void get_entity_categories(XmppStream stream, Jid jid, owned HasEntryCategoryRes listener) { - Gee.Set<Identity>? res = stream.get_flag(Flag.IDENTITY).get_entity_categories(jid); - if (res != null) listener(stream, res); - request_info(stream, jid, (stream, query_result) => { - listener(stream, query_result != null ? query_result.identities : null); - }); - } - - public delegate void OnInfoResult(XmppStream stream, InfoResult? query_result); - public void request_info(XmppStream stream, Jid jid, owned OnInfoResult listener) { - Iq.Stanza iq = new Iq.Stanza.get(new StanzaNode.build("query", NS_URI_INFO).add_self_xmlns()); - iq.to = jid; - stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq, (stream, iq) => { - InfoResult? result = InfoResult.create_from_iq(iq); - stream.get_flag(Flag.IDENTITY).set_entity_features(iq.from, result != null ? result.features : null); - stream.get_flag(Flag.IDENTITY).set_entity_identities(iq.from, result != null ? result.identities : null); - listener(stream, result); - }); - } - - public delegate void OnItemsResult(XmppStream stream, ItemsResult query_result); - public void request_items(XmppStream stream, Jid jid, owned OnItemsResult listener) { - Iq.Stanza iq = new Iq.Stanza.get(new StanzaNode.build("query", NS_URI_ITEMS).add_self_xmlns()); - iq.to = jid; - stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq, (stream, iq) => { - ItemsResult? result = ItemsResult.create_from_iq(iq); - stream.get_flag(Flag.IDENTITY).set_entity_items(iq.from, result != null ? result.items : null); - listener(stream, result); - }); + public async bool has_entity_feature(XmppStream stream, Jid jid, string feature) { + Flag flag = stream.get_flag(Flag.IDENTITY); + + if (flag.has_entity_feature(jid, feature) == null) { + InfoResult? info_result = yield request_info(stream, jid); + stream.get_flag(Flag.IDENTITY).set_entity_features(info_result.iq.from, info_result != null ? info_result.features : null); + stream.get_flag(Flag.IDENTITY).set_entity_identities(info_result.iq.from, info_result != null ? info_result.identities : null); + } + + return flag.has_entity_feature(jid, feature); + } + + public async Gee.Set<Identity>? get_entity_identities(XmppStream stream, Jid jid) { + Flag flag = stream.get_flag(Flag.IDENTITY); + + if (flag.get_entity_identities(jid) == null) { + InfoResult? info_result = yield request_info(stream, jid); + stream.get_flag(Flag.IDENTITY).set_entity_features(info_result.iq.from, info_result != null ? info_result.features : null); + stream.get_flag(Flag.IDENTITY).set_entity_identities(info_result.iq.from, info_result != null ? info_result.identities : null); + } + + return flag.get_entity_identities(jid); + } + + public async InfoResult? request_info(XmppStream stream, Jid jid) { + var future = active_info_requests[jid]; + if (future == null) { + var promise = new Promise<InfoResult?>(); + future = promise.future; + active_info_requests[jid] = future; + + Iq.Stanza iq = new Iq.Stanza.get(new StanzaNode.build("query", NS_URI_INFO).add_self_xmlns()) { to=jid }; + Iq.Stanza iq_response = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq); + InfoResult? result = InfoResult.create_from_iq(iq_response); + + promise.set_value(result); + active_info_requests.unset(jid); + } + + try { + InfoResult? res = yield future.wait_async(); + return res; + } catch (FutureError error) { + warning("Future error when waiting for info request result: %s", error.message); + return null; + } + } + + public async ItemsResult? request_items(XmppStream stream, Jid jid) { + StanzaNode query_node = new StanzaNode.build("query", NS_URI_ITEMS).add_self_xmlns(); + Iq.Stanza iq = new Iq.Stanza.get(query_node) { to=jid }; + + Iq.Stanza iq_result = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq); + ItemsResult? result = ItemsResult.create_from_iq(iq_result); + stream.get_flag(Flag.IDENTITY).set_entity_items(iq_result.from, result != null ? result.items : null); + + return result; } public void on_iq_get(XmppStream stream, Iq.Stanza iq) { diff --git a/xmpp-vala/src/module/xep/0045_muc/module.vala b/xmpp-vala/src/module/xep/0045_muc/module.vala index 3a15a19d..3434c138 100644 --- a/xmpp-vala/src/module/xep/0045_muc/module.vala +++ b/xmpp-vala/src/module/xep/0045_muc/module.vala @@ -99,7 +99,7 @@ public class Module : XmppStreamModule { stream.get_flag(Flag.IDENTITY).start_muc_enter(bare_jid, presence.id); - query_room_info(stream, bare_jid); + query_room_info.begin(stream, bare_jid); stream.get_module(Presence.Module.IDENTITY).send_presence(stream, presence); var promise = new Promise<JoinResult?>(); @@ -273,7 +273,7 @@ public class Module : XmppStreamModule { if (status_codes.contains(StatusCode.CONFIG_CHANGE_NON_PRIVACY) || status_codes.contains(StatusCode.NON_ANONYMOUS) || status_codes.contains(StatusCode.SEMI_ANONYMOUS)) { - query_room_info(stream, message.from.bare_jid); + query_room_info.begin(stream, message.from.bare_jid); } } } @@ -398,45 +398,43 @@ public class Module : XmppStreamModule { } } - private void query_room_info(XmppStream stream, Jid jid) { - stream.get_module(ServiceDiscovery.Module.IDENTITY).request_info(stream, jid, (stream, query_result) => { + private async void query_room_info(XmppStream stream, Jid jid) { + ServiceDiscovery.InfoResult? info_result = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).request_info(stream, jid); + if (info_result == null) return; - Gee.List<Feature> features = new ArrayList<Feature>(); - if (query_result != null) { + Gee.List<Feature> features = new ArrayList<Feature>(); - foreach (ServiceDiscovery.Identity identity in query_result.identities) { - if (identity.category == "conference") { - stream.get_flag(Flag.IDENTITY).set_room_name(jid, identity.name); - } - } + foreach (ServiceDiscovery.Identity identity in info_result.identities) { + if (identity.category == "conference") { + stream.get_flag(Flag.IDENTITY).set_room_name(jid, identity.name); + } + } - foreach (string feature in query_result.features) { - Feature? parsed = null; - switch (feature) { - case "http://jabber.org/protocol/muc#register": parsed = Feature.REGISTER; break; - case "http://jabber.org/protocol/muc#roomconfig": parsed = Feature.ROOMCONFIG; break; - case "http://jabber.org/protocol/muc#roominfo": parsed = Feature.ROOMINFO; break; - case "http://jabber.org/protocol/muc#stable_id": parsed = Feature.STABLE_ID; break; - case "muc_hidden": parsed = Feature.HIDDEN; break; - case "muc_membersonly": parsed = Feature.MEMBERS_ONLY; break; - case "muc_moderated": parsed = Feature.MODERATED; break; - case "muc_nonanonymous": parsed = Feature.NON_ANONYMOUS; break; - case "muc_open": parsed = Feature.OPEN; break; - case "muc_passwordprotected": parsed = Feature.PASSWORD_PROTECTED; break; - case "muc_persistent": parsed = Feature.PERSISTENT; break; - case "muc_public": parsed = Feature.PUBLIC; break; - case "muc_rooms": parsed = Feature.ROOMS; break; - case "muc_semianonymous": parsed = Feature.SEMI_ANONYMOUS; break; - case "muc_temporary": parsed = Feature.TEMPORARY; break; - case "muc_unmoderated": parsed = Feature.UNMODERATED; break; - case "muc_unsecured": parsed = Feature.UNSECURED; break; - } - if (parsed != null) features.add(parsed); - } + foreach (string feature in info_result.features) { + Feature? parsed = null; + switch (feature) { + case "http://jabber.org/protocol/muc#register": parsed = Feature.REGISTER; break; + case "http://jabber.org/protocol/muc#roomconfig": parsed = Feature.ROOMCONFIG; break; + case "http://jabber.org/protocol/muc#roominfo": parsed = Feature.ROOMINFO; break; + case "http://jabber.org/protocol/muc#stable_id": parsed = Feature.STABLE_ID; break; + case "muc_hidden": parsed = Feature.HIDDEN; break; + case "muc_membersonly": parsed = Feature.MEMBERS_ONLY; break; + case "muc_moderated": parsed = Feature.MODERATED; break; + case "muc_nonanonymous": parsed = Feature.NON_ANONYMOUS; break; + case "muc_open": parsed = Feature.OPEN; break; + case "muc_passwordprotected": parsed = Feature.PASSWORD_PROTECTED; break; + case "muc_persistent": parsed = Feature.PERSISTENT; break; + case "muc_public": parsed = Feature.PUBLIC; break; + case "muc_rooms": parsed = Feature.ROOMS; break; + case "muc_semianonymous": parsed = Feature.SEMI_ANONYMOUS; break; + case "muc_temporary": parsed = Feature.TEMPORARY; break; + case "muc_unmoderated": parsed = Feature.UNMODERATED; break; + case "muc_unsecured": parsed = Feature.UNSECURED; break; } - stream.get_flag(Flag.IDENTITY).set_room_features(jid, features); - room_info_updated(stream, jid); - }); + if (parsed != null) features.add(parsed); + } + stream.get_flag(Flag.IDENTITY).set_room_features(jid, features); + room_info_updated(stream, jid); } public delegate void OnAffiliationResult(XmppStream stream, Gee.List<Jid> jids); diff --git a/xmpp-vala/src/module/xep/0065_socks5_bytestreams.vala b/xmpp-vala/src/module/xep/0065_socks5_bytestreams.vala index a1be00d4..2631040d 100644 --- a/xmpp-vala/src/module/xep/0065_socks5_bytestreams.vala +++ b/xmpp-vala/src/module/xep/0065_socks5_bytestreams.vala @@ -23,7 +23,7 @@ public class Module : XmppStreamModule, Iq.Handler { public override void attach(XmppStream stream) { stream.add_flag(new Flag()); - query_availability(stream); + query_availability.begin(stream); } public override void detach(XmppStream stream) { } @@ -33,41 +33,36 @@ public class Module : XmppStreamModule, Iq.Handler { return stream.get_flag(Flag.IDENTITY).proxies; } - private void query_availability(XmppStream stream) { - stream.get_module(ServiceDiscovery.Module.IDENTITY).request_items(stream, stream.remote_name, (stream, items_result) => { - foreach (Xep.ServiceDiscovery.Item item in items_result.items) { - stream.get_module(ServiceDiscovery.Module.IDENTITY).request_info(stream, item.jid, (stream, info_result) => { - foreach (string feature in info_result.features) { - if (feature == NS_URI) { - StanzaNode query_ = new StanzaNode.build("query", NS_URI).add_self_xmlns(); - Iq.Stanza iq = new Iq.Stanza.get(query_) { to=item.jid }; - stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq, (stream, iq) => { - if (iq.is_error()) { - return; - } - StanzaNode? query = iq.stanza.get_subnode("query", NS_URI); - StanzaNode? stream_host = query != null ? query.get_subnode("streamhost", NS_URI) : null; - if (query == null || stream_host == null) { - return; - } - string? host = stream_host.get_attribute("host"); - string? jid_str = stream_host.get_attribute("jid"); - Jid? jid = null; - try { - jid = jid_str != null ? new Jid(jid_str) : null; - } catch (InvalidJidError ignored) { - } - int port = stream_host.get_attribute_int("port"); - if (host == null || jid == null || port <= 0 || port > 65535) { - return; - } - stream.get_flag(Flag.IDENTITY).proxies.add(new Proxy(host, jid, port)); - }); - } - } - }); + private async void query_availability(XmppStream stream) { + ServiceDiscovery.ItemsResult? items_result = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).request_items(stream, stream.remote_name); + + foreach (Xep.ServiceDiscovery.Item item in items_result.items) { + bool has_feature = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, item.jid, NS_URI); + if (!has_feature) continue; + + StanzaNode query_node = new StanzaNode.build("query", NS_URI).add_self_xmlns(); + Iq.Stanza iq = new Iq.Stanza.get(query_node) { to=item.jid }; + + Iq.Stanza iq_result = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq); + if (iq_result.is_error()) continue; + + StanzaNode? query_result_node = iq_result.stanza.get_subnode("query", NS_URI); + StanzaNode? stream_host = query_result_node != null ? query_result_node.get_subnode("streamhost", NS_URI) : null; + if (query_result_node == null || stream_host == null) { + return; + } + string? host = stream_host.get_attribute("host"); + string? jid_str = stream_host.get_attribute("jid"); + Jid? jid = null; + try { + jid = jid_str != null ? new Jid(jid_str) : null; + } catch (InvalidJidError ignored) { } + int port = stream_host.get_attribute_int("port"); + if (host == null || jid == null || port <= 0 || port > 65535) { + continue; } - }); + stream.get_flag(Flag.IDENTITY).proxies.add(new Proxy(host, jid, port)); + } } public override string get_ns() { return NS_URI; } diff --git a/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala b/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala index 1d5d10c9..60d13211 100644 --- a/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala +++ b/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala @@ -5,10 +5,15 @@ namespace Xmpp.Xep.EntityCapabilities { private Regex? sha1_base64_regex = null; - public string? get_caps_hash(Presence.Stanza presence) { + private Regex get_sha1_base64_regex() { if (sha1_base64_regex == null) { sha1_base64_regex = /^[A-Za-z0-9+\/]{27}=$/; } + return sha1_base64_regex; + } + + public string? get_caps_hash(Presence.Stanza presence) { + Regex sha1_base64_regex = get_sha1_base64_regex(); StanzaNode? c_node = presence.stanza.get_subnode("c", NS_URI); if (c_node == null) return null; string? ver_attribute = c_node.get_attribute("ver", NS_URI); @@ -37,6 +42,8 @@ namespace Xmpp.Xep.EntityCapabilities { stream.get_module(Presence.Module.IDENTITY).pre_send_presence_stanza.connect(on_pre_send_presence_stanza); stream.get_module(Presence.Module.IDENTITY).received_presence.connect(on_received_presence); stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI); + + check_features_node_ver(stream); } public override void detach(XmppStream stream) { @@ -61,14 +68,27 @@ namespace Xmpp.Xep.EntityCapabilities { string? caps_hash = get_caps_hash(presence); if (caps_hash == null) return; + process_hash.begin(stream, presence.from, caps_hash); + } + + private void check_features_node_ver(XmppStream stream) { + StanzaNode? node = stream.features.get_subnode("c", NS_URI); + if (node == null) return; + + string? ver_attribute = node.get_attribute("ver", NS_URI); + if (ver_attribute == null) return; + + process_hash.begin(stream, stream.remote_name, ver_attribute); + } + + private async void process_hash(XmppStream stream, Jid jid_from, string caps_hash) { Gee.List<string> capabilities = storage.get_features(caps_hash); ServiceDiscovery.Identity identity = storage.get_identities(caps_hash); if (identity == null) { - stream.get_module(ServiceDiscovery.Module.IDENTITY).request_info(stream, presence.from, (stream, query_result) => { - store_entity_result(stream, caps_hash, query_result); - }); + ServiceDiscovery.InfoResult? info_result = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).request_info(stream, jid_from); + store_entity_result(stream, caps_hash, info_result); } else { - stream.get_flag(ServiceDiscovery.Flag.IDENTITY).set_entity_features(presence.from, capabilities); + stream.get_flag(ServiceDiscovery.Flag.IDENTITY).set_entity_features(jid_from, capabilities); } } diff --git a/xmpp-vala/src/module/xep/0191_blocking_command.vala b/xmpp-vala/src/module/xep/0191_blocking_command.vala index 2fc1d3b4..fbedc8b3 100644 --- a/xmpp-vala/src/module/xep/0191_blocking_command.vala +++ b/xmpp-vala/src/module/xep/0191_blocking_command.vala @@ -80,29 +80,24 @@ public class Module : XmppStreamModule, Iq.Handler { public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } - private void on_stream_negotiated(XmppStream stream) { - stream.get_module(Xep.ServiceDiscovery.Module.IDENTITY).request_info(stream, stream.remote_name, (stream, info_result) => { - if (info_result.features.contains(NS_URI)) { - stream.add_flag(new Flag()); - get_blocklist(stream, (stream, jids) => { - stream.get_flag(Flag.IDENTITY).blocklist = jids; - }); - return; - } - }); + private async void on_stream_negotiated(XmppStream stream) { + bool has_feature = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, stream.remote_name, NS_URI); + if (has_feature) { + stream.add_flag(new Flag()); + stream.get_flag(Flag.IDENTITY).blocklist = yield get_blocklist(stream); + } } - private delegate void OnBlocklist(XmppStream stream, Gee.List<string> jids); - private void get_blocklist(XmppStream stream, owned OnBlocklist listener) { + private async Gee.List<string> get_blocklist(XmppStream stream) { StanzaNode blocklist_node = new StanzaNode.build("blocklist", NS_URI).add_self_xmlns(); Iq.Stanza iq = new Iq.Stanza.get(blocklist_node); - stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq, (stream, iq) => { - StanzaNode? node = iq.stanza.get_subnode("blocklist", NS_URI); - if (node != null) { - Gee.List<string> jids = get_jids_from_items(node); - listener(stream, jids); - } - }); + + Iq.Stanza result_iq = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq); + StanzaNode? node = result_iq.stanza.get_subnode("blocklist", NS_URI); + if (node != null) { + return get_jids_from_items(node); + } + return new ArrayList<string>(); } private Gee.List<string> get_jids_from_items(StanzaNode node) { diff --git a/xmpp-vala/src/module/xep/0313_message_archive_management.vala b/xmpp-vala/src/module/xep/0313_message_archive_management.vala index 78d0aaff..bba52702 100644 --- a/xmpp-vala/src/module/xep/0313_message_archive_management.vala +++ b/xmpp-vala/src/module/xep/0313_message_archive_management.vala @@ -108,17 +108,22 @@ public class Module : XmppStreamModule { return result_iq; } - private void query_availability(XmppStream stream) { - stream.get_module(Xep.ServiceDiscovery.Module.IDENTITY).request_info(stream, stream.get_flag(Bind.Flag.IDENTITY).my_jid.bare_jid, (stream, info_result) => { - if (info_result == null) return; - if (info_result.features.contains(NS_URI)) { - stream.add_flag(new Flag(NS_URI)); - feature_available(stream); - } else if (info_result.features.contains(NS_URI_1)) { - stream.add_flag(new Flag(NS_URI_1)); - feature_available(stream); - } - }); + private async void query_availability(XmppStream stream) { + Jid own_jid = stream.get_flag(Bind.Flag.IDENTITY).my_jid.bare_jid; + + bool ver_2_available = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, own_jid, NS_URI); + if (ver_2_available) { + stream.add_flag(new Flag(NS_URI)); + feature_available(stream); + return; + } + + bool ver_1_available = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, own_jid, NS_URI_1); + if (ver_1_available) { + stream.add_flag(new Flag(NS_URI_1)); + feature_available(stream); + return; + } } } diff --git a/xmpp-vala/src/module/xep/0363_http_file_upload.vala b/xmpp-vala/src/module/xep/0363_http_file_upload.vala index d3faae2d..64fbbebc 100644 --- a/xmpp-vala/src/module/xep/0363_http_file_upload.vala +++ b/xmpp-vala/src/module/xep/0363_http_file_upload.vala @@ -105,25 +105,22 @@ public class Module : XmppStreamModule { } public override void detach(XmppStream stream) { - stream.get_module(Bind.Module.IDENTITY).bound_to_resource.disconnect(query_availability); + stream.stream_negotiated.disconnect(query_availability); } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } - private void query_availability(XmppStream stream) { - stream.get_module(ServiceDiscovery.Module.IDENTITY).request_info(stream, stream.remote_name, (stream, info_result) => { - bool available = check_ns_in_info(stream, stream.remote_name, info_result); - if (!available) { - stream.get_module(ServiceDiscovery.Module.IDENTITY).request_items(stream, stream.remote_name, (stream, items_result) => { - foreach (Xep.ServiceDiscovery.Item item in items_result.items) { - stream.get_module(ServiceDiscovery.Module.IDENTITY).request_info(stream, item.jid, (stream, info_result) => { - check_ns_in_info(stream, item.jid, info_result); - }); - } - }); + private async void query_availability(XmppStream stream) { + ServiceDiscovery.InfoResult? info_result = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).request_info(stream, stream.remote_name); + bool available = check_ns_in_info(stream, stream.remote_name, info_result); + if (!available) { + ServiceDiscovery.ItemsResult? items_result = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).request_items(stream, stream.remote_name); + foreach (Xep.ServiceDiscovery.Item item in items_result.items) { + ServiceDiscovery.InfoResult? info_result2 = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).request_info(stream, item.jid); + check_ns_in_info(stream, item.jid, info_result2); } - }); + } } private bool check_ns_in_info(XmppStream stream, Jid jid, Xep.ServiceDiscovery.InfoResult info_result) { |