diff options
author | fiaxh <git@lightrise.org> | 2021-03-19 22:46:39 +0100 |
---|---|---|
committer | fiaxh <git@lightrise.org> | 2021-03-21 12:40:04 +0100 |
commit | 2b90fcc39a1079346d6c5e2bfff8987104da737a (patch) | |
tree | e9c645a82010392623ec2cd3333e8dd1c8d4431a /xmpp-vala/src/module/xep/0166_jingle/content.vala | |
parent | 148cf48d2b68354881066e2587e2673c91d2619a (diff) | |
download | dino-2b90fcc39a1079346d6c5e2bfff8987104da737a.tar.gz dino-2b90fcc39a1079346d6c5e2bfff8987104da737a.zip |
Improve & refactor Jingle base implementation
Co-authored-by: Marvin W <git@larma.de>
Diffstat (limited to 'xmpp-vala/src/module/xep/0166_jingle/content.vala')
-rw-r--r-- | xmpp-vala/src/module/xep/0166_jingle/content.vala | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/xmpp-vala/src/module/xep/0166_jingle/content.vala b/xmpp-vala/src/module/xep/0166_jingle/content.vala new file mode 100644 index 00000000..67c13dd8 --- /dev/null +++ b/xmpp-vala/src/module/xep/0166_jingle/content.vala @@ -0,0 +1,236 @@ +using Gee; +using Xmpp; + +public class Xmpp.Xep.Jingle.Content : Object { + + public signal void senders_modify_incoming(Senders proposed_senders); + + // INITIATE_SENT -> CONNECTING -> [REPLACING_TRANSPORT -> CONNECTING ->]... ACTIVE -> ENDED + // INITIATE_RECEIVED -> CONNECTING -> [WAITING_FOR_TRANSPORT_REPLACE -> CONNECTING ->].. ACTIVE -> ENDED + public enum State { + PENDING, + WANTS_TO_BE_ACCEPTED, + ACCEPTED, + REPLACING_TRANSPORT, + WAITING_FOR_TRANSPORT_REPLACE + } + + public State state { get; set; } + + public Role role { get; private set; } + public Jid local_full_jid { get; private set; } + public Jid peer_full_jid { get; private set; } + public Role content_creator { get; private set; } + public string content_name { get; private set; } + public Senders senders { get; private set; } + + public ContentType content_type; + public ContentParameters content_params; + public Transport transport; + public TransportParameters? transport_params; + public SecurityPrecondition security_precondition; + public SecurityParameters? security_params; + + public weak Session session; + public Map<uint8, ComponentConnection> component_connections = new HashMap<uint8, ComponentConnection>(); // TODO private + + // INITIATE_SENT | INITIATE_RECEIVED | CONNECTING + public Set<string> tried_transport_methods = new HashSet<string>(); + + + public Content.initiate_sent(string content_name, Senders senders, + ContentType content_type, ContentParameters content_params, + Transport transport, TransportParameters? transport_params, + SecurityPrecondition? security_precondition, SecurityParameters? security_params, + Jid local_full_jid, Jid peer_full_jid) { + this.content_name = content_name; + this.senders = senders; + this.role = Role.INITIATOR; + this.local_full_jid = local_full_jid; + this.peer_full_jid = peer_full_jid; + this.content_creator = Role.INITIATOR; + + this.content_type = content_type; + this.content_params = content_params; + this.transport = transport; + this.transport_params = transport_params; + this.security_precondition = security_precondition; + this.security_params = security_params; + + this.tried_transport_methods.add(transport.ns_uri); + + state = State.PENDING; + } + + public Content.initiate_received(string content_name, Senders senders, + ContentType content_type, ContentParameters content_params, + Transport transport, TransportParameters? transport_params, + SecurityPrecondition? security_precondition, SecurityParameters? security_params, + Jid local_full_jid, Jid peer_full_jid) throws Error { + this.content_name = content_name; + this.senders = senders; + this.role = Role.RESPONDER; + this.local_full_jid = local_full_jid; + this.peer_full_jid = peer_full_jid; + this.content_creator = Role.INITIATOR; + + this.content_type = content_type; + this.content_params = content_params; + this.transport = transport; + this.transport_params = transport_params; + this.security_precondition = security_precondition; + this.security_params = security_params; + + if (transport != null) { + this.tried_transport_methods.add(transport.ns_uri); + } + + state = State.PENDING; + } + + public void set_session(Session session) { + this.session = session; + this.transport_params.set_content(this); + } + + public void accept() { + state = State.WANTS_TO_BE_ACCEPTED; + + session.accept_content(this); + } + + public void reject() { + session.reject_content(this); + } + + public void terminate(bool we_terminated, string? reason_name, string? reason_text) { + content_params.terminate(we_terminated, reason_name, reason_text); + + foreach (ComponentConnection connection in component_connections.values) { + connection.terminate(we_terminated, reason_name, reason_text); + } + } + + public void modify(Senders new_sender) { + session.send_content_modify(this, new_sender); + this.senders = new_sender; + } + + public void accept_content_modify(Senders senders) { + this.senders = senders; + } + + internal void handle_content_modify(XmppStream stream, Senders proposed_senders) { + senders_modify_incoming(proposed_senders); + } + + internal void on_accept(XmppStream stream) { + this.transport_params.create_transport_connection(stream, this); + this.content_params.accept(stream, session, this); + } + + internal void handle_accept(XmppStream stream, ContentNode content_node) { + this.transport_params.handle_transport_accept(content_node.transport); + this.transport_params.create_transport_connection(stream, this); + this.content_params.handle_accept(stream, this.session, this, content_node.description); + } + + private async void select_new_transport() { + XmppStream stream = session.stream; + Transport? new_transport = yield stream.get_module(Module.IDENTITY).select_transport(stream, transport.type_, transport_params.components, peer_full_jid, tried_transport_methods); + if (new_transport == null) { + session.terminate(ReasonElement.FAILED_TRANSPORT, null, "failed transport"); + // TODO should we only terminate this content or really the whole session? + return; + } + tried_transport_methods.add(new_transport.ns_uri); + transport_params = new_transport.create_transport_parameters(stream, transport_params.components, local_full_jid, peer_full_jid); + set_transport_params(transport_params); + session.send_transport_replace(this, transport_params); + state = State.REPLACING_TRANSPORT; + } + + public void handle_transport_accept(XmppStream stream, StanzaNode transport_node, StanzaNode jingle, Iq.Stanza iq) throws IqError { + if (state != State.REPLACING_TRANSPORT) { + throw new IqError.OUT_OF_ORDER("no outstanding transport-replace request"); + } + if (transport_node.ns_uri != transport.ns_uri) { + throw new IqError.BAD_REQUEST("transport-accept with unnegotiated transport method"); + } + transport_params.handle_transport_accept(transport_node); + stream.get_module(Iq.Module.IDENTITY).send_iq(stream, new Iq.Stanza.result(iq)); + transport_params.create_transport_connection(stream, this); + } + + public void handle_transport_reject(XmppStream stream, StanzaNode jingle, Iq.Stanza iq) throws IqError { + if (state != State.REPLACING_TRANSPORT) { + throw new IqError.OUT_OF_ORDER("no outstanding transport-replace request"); + } + stream.get_module(Iq.Module.IDENTITY).send_iq(stream, new Iq.Stanza.result(iq)); + select_new_transport.begin(); + } + + public void handle_transport_replace(XmppStream stream, StanzaNode transport_node, StanzaNode jingle, Iq.Stanza iq) throws IqError { + Transport? transport = stream.get_module(Module.IDENTITY).get_transport(transport_node.ns_uri); + TransportParameters? parameters = null; + if (transport != null) { + // Just parse the transport info for the errors. + parameters = transport.parse_transport_parameters(stream, content_type.required_components, local_full_jid, peer_full_jid, transport_node); + } + stream.get_module(Iq.Module.IDENTITY).send_iq(stream, new Iq.Stanza.result(iq)); + if (state != State.WAITING_FOR_TRANSPORT_REPLACE || transport == null) { + session.send_transport_reject(this, transport_node); + return; + } + set_transport_params(parameters); + session.send_transport_accept(this, parameters); + + this.transport_params.create_transport_connection(stream, this); + } + + public void handle_transport_info(XmppStream stream, StanzaNode transport, StanzaNode jingle, Iq.Stanza iq) throws IqError { + this.transport_params.handle_transport_info(transport); + stream.get_module(Iq.Module.IDENTITY).send_iq(stream, new Iq.Stanza.result(iq)); + } + + public void on_description_info(XmppStream stream, StanzaNode description, StanzaNode jinglq, Iq.Stanza iq) throws IqError { + // TODO: do something. + stream.get_module(Iq.Module.IDENTITY).send_iq(stream, new Iq.Stanza.result(iq)); + } + + void verify_content(ContentNode content) throws IqError { + if (content.name != content_name || content.creator != content_creator) { + throw new IqError.BAD_REQUEST("unknown content"); + } + } + + public void set_transport_connection(ComponentConnection? conn, uint8 component = 1) { + debug(@"set_transport_connection: %s, %s, %i, %s, overwrites: %s", this.content_name, this.state.to_string(), component, (conn != null).to_string(), component_connections.has_key(component).to_string()); + + if (conn != null) { + component_connections[component] = conn; + if (transport_params.components == component) { + state = State.ACCEPTED; + tried_transport_methods.clear(); + } + } else { + if (role == Role.INITIATOR) { + select_new_transport.begin(); + } else { + state = State.WAITING_FOR_TRANSPORT_REPLACE; + } + } + } + + private void set_transport_params(TransportParameters transport_params) { + this.transport_params = transport_params; + } + + public ComponentConnection? get_transport_connection(uint8 component = 1) { + return component_connections[component]; + } + + public void send_transport_info(StanzaNode transport) { + session.send_transport_info(this, transport); + } +}
\ No newline at end of file |