aboutsummaryrefslogtreecommitdiff
path: root/xmpp-vala/src/module/xep/0166_jingle/content.vala
diff options
context:
space:
mode:
authorfiaxh <git@lightrise.org>2021-03-19 22:46:39 +0100
committerfiaxh <git@lightrise.org>2021-03-21 12:40:04 +0100
commit2b90fcc39a1079346d6c5e2bfff8987104da737a (patch)
treee9c645a82010392623ec2cd3333e8dd1c8d4431a /xmpp-vala/src/module/xep/0166_jingle/content.vala
parent148cf48d2b68354881066e2587e2673c91d2619a (diff)
downloaddino-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.vala236
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