Add ICE-UDP Jingle transport (XEP-0176) to xmpp-vala
Co-authored-by: fiaxh <git@lightrise.org>
This commit is contained in:
parent
2b90fcc39a
commit
5bd719a919
93
xmpp-vala/src/module/xep/0176_jingle_ice_udp/candidate.vala
Normal file
93
xmpp-vala/src/module/xep/0176_jingle_ice_udp/candidate.vala
Normal file
|
@ -0,0 +1,93 @@
|
|||
using Gee;
|
||||
using Xmpp.Xep;
|
||||
using Xmpp;
|
||||
|
||||
public class Xmpp.Xep.JingleIceUdp.Candidate {
|
||||
public uint8 component;
|
||||
public uint8 foundation;
|
||||
public uint8 generation;
|
||||
public string id;
|
||||
public string ip;
|
||||
public uint8 network;
|
||||
public uint16 port;
|
||||
public uint32 priority;
|
||||
public string protocol;
|
||||
public string? rel_addr;
|
||||
public uint16 rel_port;
|
||||
public Type type_;
|
||||
|
||||
public static Candidate parse(StanzaNode node) throws Jingle.IqError {
|
||||
Candidate candidate = new Candidate();
|
||||
candidate.component = (uint8) node.get_attribute_uint("component");
|
||||
candidate.foundation = (uint8) node.get_attribute_uint("foundation");
|
||||
candidate.generation = (uint8) node.get_attribute_uint("generation");
|
||||
candidate.id = node.get_attribute("id");
|
||||
candidate.ip = node.get_attribute("ip");
|
||||
candidate.network = (uint8) node.get_attribute_uint("network");
|
||||
candidate.port = (uint16) node.get_attribute_uint("port");
|
||||
candidate.priority = (uint32) node.get_attribute_uint("priority");
|
||||
candidate.protocol = node.get_attribute("protocol");
|
||||
candidate.rel_addr = node.get_attribute("rel-addr");
|
||||
candidate.rel_port = (uint16) node.get_attribute_uint("rel-port");
|
||||
candidate.type_ = Type.parse(node.get_attribute("type"));
|
||||
return candidate;
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
HOST, PRFLX, RELAY, SRFLX;
|
||||
public static Type parse(string str) throws Jingle.IqError {
|
||||
switch (str) {
|
||||
case "host": return HOST;
|
||||
case "prflx": return PRFLX;
|
||||
case "relay": return RELAY;
|
||||
case "srflx": return SRFLX;
|
||||
default: throw new Jingle.IqError.BAD_REQUEST("Illegal ICE-UDP candidate type");
|
||||
}
|
||||
}
|
||||
public string to_string() {
|
||||
switch (this) {
|
||||
case HOST: return "host";
|
||||
case PRFLX: return "prflx";
|
||||
case RELAY: return "relay";
|
||||
case SRFLX: return "srflx";
|
||||
default: assert_not_reached();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public StanzaNode to_xml() {
|
||||
StanzaNode node = new StanzaNode.build("candidate", NS_URI)
|
||||
.put_attribute("component", component.to_string())
|
||||
.put_attribute("foundation", foundation.to_string())
|
||||
.put_attribute("generation", generation.to_string())
|
||||
.put_attribute("id", id)
|
||||
.put_attribute("ip", ip)
|
||||
.put_attribute("network", network.to_string())
|
||||
.put_attribute("port", port.to_string())
|
||||
.put_attribute("priority", priority.to_string())
|
||||
.put_attribute("protocol", protocol)
|
||||
.put_attribute("type", type_.to_string());
|
||||
if (rel_addr != null) node.put_attribute("rel-addr", rel_addr);
|
||||
if (rel_port != 0) node.put_attribute("rel-port", rel_port.to_string());
|
||||
return node;
|
||||
}
|
||||
|
||||
public bool equals(Candidate c) {
|
||||
return equals_func(this, c);
|
||||
}
|
||||
|
||||
public static bool equals_func(Candidate c1, Candidate c2) {
|
||||
return c1.component == c2.component &&
|
||||
c1.foundation == c2.foundation &&
|
||||
c1.generation == c2.generation &&
|
||||
c1.id == c2.id &&
|
||||
c1.ip == c2.ip &&
|
||||
c1.network == c2.network &&
|
||||
c1.port == c2.port &&
|
||||
c1.priority == c2.priority &&
|
||||
c1.protocol == c2.protocol &&
|
||||
c1.rel_addr == c2.rel_addr &&
|
||||
c1.rel_port == c2.rel_port &&
|
||||
c1.type_ == c2.type_;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using Gee;
|
||||
using Xmpp.Xep;
|
||||
using Xmpp;
|
||||
|
||||
namespace Xmpp.Xep.JingleIceUdp {
|
||||
|
||||
private const string NS_URI = "urn:xmpp:jingle:transports:ice-udp:1";
|
||||
|
||||
public abstract class Module : XmppStreamModule, Jingle.Transport {
|
||||
public static Xmpp.ModuleIdentity<Module> IDENTITY = new Xmpp.ModuleIdentity<Module>(NS_URI, "0176_jingle_ice_udp");
|
||||
|
||||
public override void attach(XmppStream stream) {
|
||||
stream.get_module(Jingle.Module.IDENTITY).register_transport(this);
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI);
|
||||
}
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||
}
|
||||
|
||||
public override string get_ns() { return NS_URI; }
|
||||
public override string get_id() { return IDENTITY.id; }
|
||||
|
||||
public async bool is_transport_available(XmppStream stream, uint8 components, Jid full_jid) {
|
||||
return yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, full_jid, NS_URI);
|
||||
}
|
||||
|
||||
public string ns_uri{ get { return NS_URI; } }
|
||||
public Jingle.TransportType type_{ get { return Jingle.TransportType.DATAGRAM; } }
|
||||
public int priority { get { return 1; } }
|
||||
|
||||
public abstract Jingle.TransportParameters create_transport_parameters(XmppStream stream, uint8 components, Jid local_full_jid, Jid peer_full_jid);
|
||||
|
||||
public abstract Jingle.TransportParameters parse_transport_parameters(XmppStream stream, uint8 components, Jid local_full_jid, Jid peer_full_jid, StanzaNode transport) throws Jingle.IqError;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
using Gee;
|
||||
using Xmpp.Xep;
|
||||
using Xmpp;
|
||||
|
||||
public abstract class Xmpp.Xep.JingleIceUdp.IceUdpTransportParameters : Jingle.TransportParameters, Object {
|
||||
public string ns_uri { get { return NS_URI; } }
|
||||
public string remote_pwd { get; private set; }
|
||||
public string remote_ufrag { get; private set; }
|
||||
public string local_pwd { get; private set; }
|
||||
public string local_ufrag { get; private set; }
|
||||
|
||||
public ConcurrentList<Candidate> local_candidates = new ConcurrentList<Candidate>(Candidate.equals_func);
|
||||
public ConcurrentList<Candidate> unsent_local_candidates = new ConcurrentList<Candidate>(Candidate.equals_func);
|
||||
public Gee.List<Candidate> remote_candidates = new ArrayList<Candidate>(Candidate.equals_func);
|
||||
|
||||
public Jid local_full_jid { get; private set; }
|
||||
public Jid peer_full_jid { get; private set; }
|
||||
private uint8 components_;
|
||||
public uint8 components { get { return components_; } }
|
||||
|
||||
public bool incoming { get; private set; default = false; }
|
||||
private bool connection_created = false;
|
||||
|
||||
private weak Jingle.Content? content = null;
|
||||
|
||||
protected IceUdpTransportParameters(uint8 components, Jid local_full_jid, Jid peer_full_jid, StanzaNode? node = null) {
|
||||
this.components_ = components;
|
||||
this.local_full_jid = local_full_jid;
|
||||
this.peer_full_jid = peer_full_jid;
|
||||
if (node != null) {
|
||||
incoming = true;
|
||||
remote_pwd = node.get_attribute("pwd");
|
||||
remote_ufrag = node.get_attribute("ufrag");
|
||||
foreach (StanzaNode candidateNode in node.get_subnodes("candidate")) {
|
||||
remote_candidates.add(Candidate.parse(candidateNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void init(string ufrag, string pwd) {
|
||||
this.local_ufrag = ufrag;
|
||||
this.local_pwd = pwd;
|
||||
debug("Initialized for %s", pwd);
|
||||
}
|
||||
|
||||
public void set_content(Jingle.Content content) {
|
||||
this.content = content;
|
||||
this.content.weak_ref(unset_content);
|
||||
}
|
||||
|
||||
public void unset_content() {
|
||||
this.content = null;
|
||||
}
|
||||
|
||||
public StanzaNode to_transport_stanza_node() {
|
||||
var node = new StanzaNode.build("transport", NS_URI)
|
||||
.add_self_xmlns()
|
||||
.put_attribute("ufrag", local_ufrag)
|
||||
.put_attribute("pwd", local_pwd);
|
||||
foreach (Candidate candidate in unsent_local_candidates) {
|
||||
node.put_node(candidate.to_xml());
|
||||
}
|
||||
unsent_local_candidates.clear();
|
||||
return node;
|
||||
}
|
||||
|
||||
public virtual void handle_transport_accept(StanzaNode node) throws Jingle.IqError {
|
||||
string? pwd = node.get_attribute("pwd");
|
||||
string? ufrag = node.get_attribute("ufrag");
|
||||
if (pwd != null) remote_pwd = pwd;
|
||||
if (ufrag != null) remote_ufrag = ufrag;
|
||||
foreach (StanzaNode candidateNode in node.get_subnodes("candidate")) {
|
||||
remote_candidates.add(Candidate.parse(candidateNode));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void handle_transport_info(StanzaNode node) throws Jingle.IqError {
|
||||
string? pwd = node.get_attribute("pwd");
|
||||
string? ufrag = node.get_attribute("ufrag");
|
||||
if (pwd != null) remote_pwd = pwd;
|
||||
if (ufrag != null) remote_ufrag = ufrag;
|
||||
uint8 components = 0;
|
||||
foreach (StanzaNode candidateNode in node.get_subnodes("candidate")) {
|
||||
remote_candidates.add(Candidate.parse(candidateNode));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void create_transport_connection(XmppStream stream, Jingle.Content content) {
|
||||
connection_created = true;
|
||||
|
||||
check_send_transport_info();
|
||||
}
|
||||
|
||||
public void add_local_candidate_threadsafe(Candidate candidate) {
|
||||
if (local_candidates.contains(candidate)) return;
|
||||
|
||||
debug("New local candidate %u %s %s:%u", candidate.component, candidate.type_.to_string(), candidate.ip, candidate.port);
|
||||
unsent_local_candidates.add(candidate);
|
||||
local_candidates.add(candidate);
|
||||
|
||||
if (this.content != null && (this.connection_created || !this.incoming)) {
|
||||
Timeout.add(50, () => {
|
||||
check_send_transport_info();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void check_send_transport_info() {
|
||||
if (this.content != null && unsent_local_candidates.size > 0) {
|
||||
content.send_transport_info(to_transport_stanza_node());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue