2017-03-02 14:37:32 +00:00
|
|
|
using Gee;
|
|
|
|
|
2023-02-06 20:13:33 +00:00
|
|
|
public abstract class Xmpp.XmppStream : Object {
|
2017-03-02 14:37:32 +00:00
|
|
|
|
|
|
|
public signal void received_node(XmppStream stream, StanzaNode node);
|
|
|
|
public signal void received_root_node(XmppStream stream, StanzaNode node);
|
|
|
|
public signal void received_features_node(XmppStream stream);
|
|
|
|
public signal void received_message_stanza(XmppStream stream, StanzaNode node);
|
|
|
|
public signal void received_presence_stanza(XmppStream stream, StanzaNode node);
|
|
|
|
public signal void received_iq_stanza(XmppStream stream, StanzaNode node);
|
|
|
|
public signal void received_nonza(XmppStream stream, StanzaNode node);
|
|
|
|
public signal void stream_negotiated(XmppStream stream);
|
2017-08-12 21:14:50 +00:00
|
|
|
public signal void attached_modules(XmppStream stream);
|
2017-03-02 14:37:32 +00:00
|
|
|
|
2020-11-16 14:55:33 +00:00
|
|
|
public const string NS_URI = "http://etherx.jabber.org/streams";
|
2017-03-02 14:37:32 +00:00
|
|
|
|
2020-11-16 14:55:33 +00:00
|
|
|
public Gee.List<XmppStreamFlag> flags { get; private set; default=new ArrayList<XmppStreamFlag>(); }
|
|
|
|
public Gee.List<XmppStreamModule> modules { get; private set; default=new ArrayList<XmppStreamModule>(); }
|
2017-03-02 14:37:32 +00:00
|
|
|
|
2020-11-16 14:55:33 +00:00
|
|
|
public StanzaNode? features { get; private set; default = new StanzaNode.build("features", NS_URI); }
|
|
|
|
public Jid remote_name;
|
2020-07-21 13:48:42 +00:00
|
|
|
|
2020-11-16 14:55:33 +00:00
|
|
|
public XmppLog log = new XmppLog();
|
|
|
|
public bool negotiation_complete { get; set; default=false; }
|
|
|
|
protected bool non_negotiation_modules_attached = false;
|
|
|
|
protected bool setup_needed = false;
|
|
|
|
protected bool disconnected = false;
|
2017-03-02 14:37:32 +00:00
|
|
|
|
2020-12-31 18:00:54 +00:00
|
|
|
protected XmppStream(Jid remote_name) {
|
|
|
|
this.remote_name = remote_name;
|
|
|
|
}
|
|
|
|
|
2023-01-31 14:12:39 +00:00
|
|
|
public abstract async void connect() throws IOError;
|
2017-03-02 14:37:32 +00:00
|
|
|
|
2023-01-31 14:12:39 +00:00
|
|
|
public abstract async void disconnect() throws IOError;
|
2017-03-02 14:37:32 +00:00
|
|
|
|
2023-01-31 14:12:39 +00:00
|
|
|
public abstract async StanzaNode read() throws IOError;
|
2017-03-02 14:37:32 +00:00
|
|
|
|
2019-12-22 18:28:40 +00:00
|
|
|
[Version (deprecated = true, deprecated_since = "0.1", replacement = "write_async")]
|
2023-01-31 14:13:12 +00:00
|
|
|
public abstract void write(StanzaNode node, int io_priority = Priority.DEFAULT);
|
2017-11-22 19:06:50 +00:00
|
|
|
|
2023-01-31 14:13:12 +00:00
|
|
|
public abstract async void write_async(StanzaNode node, int io_priority = Priority.DEFAULT, Cancellable? cancellable = null) throws IOError;
|
2020-11-16 14:55:33 +00:00
|
|
|
|
2023-01-31 14:12:39 +00:00
|
|
|
public abstract async void setup() throws IOError;
|
2020-11-16 14:55:33 +00:00
|
|
|
|
|
|
|
public void require_setup() {
|
|
|
|
setup_needed = true;
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
2020-11-16 14:55:33 +00:00
|
|
|
public bool is_setup_needed() {
|
|
|
|
return setup_needed;
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void add_flag(XmppStreamFlag flag) {
|
|
|
|
flags.add(flag);
|
|
|
|
}
|
|
|
|
|
2017-03-19 11:55:36 +00:00
|
|
|
public bool has_flag<T>(FlagIdentity<T>? identity) {
|
|
|
|
return get_flag(identity) != null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public T? get_flag<T>(FlagIdentity<T>? identity) {
|
|
|
|
if (identity == null) return null;
|
2017-03-02 14:37:32 +00:00
|
|
|
foreach (var flag in flags) {
|
2017-04-18 15:53:25 +00:00
|
|
|
if (((!)identity).matches(flag)) return ((!)identity).cast(flag);
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void remove_flag(XmppStreamFlag flag) {
|
|
|
|
flags.remove(flag);
|
|
|
|
}
|
|
|
|
|
|
|
|
public XmppStream add_module(XmppStreamModule module) {
|
2018-03-10 18:46:08 +00:00
|
|
|
foreach (XmppStreamModule m in modules) {
|
|
|
|
if (m.get_ns() == module.get_ns() && m.get_id() == module.get_id()) {
|
2019-03-15 19:56:19 +00:00
|
|
|
warning("[%p] Adding already added module: %s\n", this, module.get_id());
|
2018-03-10 18:46:08 +00:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
}
|
2017-03-02 14:37:32 +00:00
|
|
|
modules.add(module);
|
2017-08-12 21:14:50 +00:00
|
|
|
if (negotiation_complete) module.attach(this);
|
2017-03-02 14:37:32 +00:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2017-09-26 15:01:06 +00:00
|
|
|
public void detach_modules() {
|
2018-01-04 20:13:44 +00:00
|
|
|
foreach (XmppStreamModule module in modules) {
|
|
|
|
module.detach(this);
|
|
|
|
}
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
2017-03-10 20:13:35 +00:00
|
|
|
public T? get_module<T>(ModuleIdentity<T>? identity) {
|
|
|
|
if (identity == null) return null;
|
2017-03-02 14:37:32 +00:00
|
|
|
foreach (var module in modules) {
|
2017-04-18 15:53:25 +00:00
|
|
|
if (((!)identity).matches(module)) return ((!)identity).cast(module);
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2023-01-31 14:12:39 +00:00
|
|
|
public async void loop() throws IOError {
|
2017-04-03 13:09:30 +00:00
|
|
|
while (true) {
|
2017-03-02 14:37:32 +00:00
|
|
|
if (setup_needed) {
|
2017-11-11 20:29:13 +00:00
|
|
|
yield setup();
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
2017-11-11 20:29:13 +00:00
|
|
|
StanzaNode node = yield read();
|
|
|
|
|
|
|
|
Idle.add(loop.callback);
|
|
|
|
yield;
|
|
|
|
|
2019-11-27 17:46:29 +00:00
|
|
|
if (disconnected) break;
|
|
|
|
|
2020-11-16 14:55:33 +00:00
|
|
|
yield handle_stanza(node);
|
2017-03-02 14:37:32 +00:00
|
|
|
|
2017-08-12 21:14:50 +00:00
|
|
|
if (!non_negotiation_modules_attached && negotiation_modules_done()) {
|
2017-03-02 14:37:32 +00:00
|
|
|
attach_non_negotation_modules();
|
2017-08-12 21:14:50 +00:00
|
|
|
non_negotiation_modules_attached = true;
|
|
|
|
if (!negotiation_complete) {
|
|
|
|
stream_negotiated(this);
|
|
|
|
negotiation_complete = true;
|
|
|
|
}
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-16 14:55:33 +00:00
|
|
|
private async void handle_stanza(StanzaNode node) {
|
|
|
|
received_node(this, node);
|
|
|
|
|
|
|
|
if (node.ns_uri == NS_URI && node.name == "features") {
|
|
|
|
features = node;
|
|
|
|
received_features_node(this);
|
|
|
|
} else if (node.ns_uri == NS_URI && node.name == "stream" && node.pseudo) {
|
|
|
|
debug("[%p] Server closed stream", this);
|
|
|
|
try {
|
|
|
|
yield disconnect();
|
|
|
|
} catch (Error e) {}
|
|
|
|
return;
|
|
|
|
} else if (node.ns_uri == JABBER_URI) {
|
|
|
|
if (node.name == "message") {
|
|
|
|
received_message_stanza(this, node);
|
|
|
|
} else if (node.name == "presence") {
|
|
|
|
received_presence_stanza(this, node);
|
|
|
|
} else if (node.name == "iq") {
|
|
|
|
received_iq_stanza(this, node);
|
|
|
|
} else {
|
|
|
|
received_nonza(this, node);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
received_nonza(this, node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool is_negotiation_active() {
|
|
|
|
foreach (XmppStreamModule module in modules) {
|
|
|
|
if (module is XmppStreamNegotiationModule) {
|
|
|
|
XmppStreamNegotiationModule negotiation_module = (XmppStreamNegotiationModule) module;
|
|
|
|
if (negotiation_module.negotiation_active(this)) return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-01-31 14:12:39 +00:00
|
|
|
private bool negotiation_modules_done() throws IOError {
|
2018-01-04 20:13:44 +00:00
|
|
|
if (setup_needed) return false;
|
|
|
|
if (is_negotiation_active()) return false;
|
|
|
|
|
|
|
|
foreach (XmppStreamModule module in modules) {
|
|
|
|
if (module is XmppStreamNegotiationModule) {
|
|
|
|
XmppStreamNegotiationModule negotiation_module = (XmppStreamNegotiationModule) module;
|
|
|
|
if (negotiation_module.mandatory_outstanding(this)) {
|
2023-01-31 14:12:39 +00:00
|
|
|
throw new IOError.FAILED("mandatory-to-negotiate feature not negotiated: " + negotiation_module.get_id());
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-01-04 20:13:44 +00:00
|
|
|
return true;
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private void attach_non_negotation_modules() {
|
|
|
|
foreach (XmppStreamModule module in modules) {
|
|
|
|
if (module as XmppStreamNegotiationModule == null) {
|
|
|
|
module.attach(this);
|
|
|
|
}
|
2017-08-12 21:14:50 +00:00
|
|
|
}
|
|
|
|
attached_modules(this);
|
|
|
|
}
|
|
|
|
|
2020-11-16 14:55:33 +00:00
|
|
|
public void attach_negotation_modules() {
|
2017-08-12 21:14:50 +00:00
|
|
|
foreach (XmppStreamModule module in modules) {
|
|
|
|
if (module as XmppStreamNegotiationModule != null) {
|
|
|
|
module.attach(this);
|
|
|
|
}
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-16 14:55:33 +00:00
|
|
|
}
|