diff options
author | Patiga <dev@patiga.eu> | 2022-06-28 12:11:17 +0200 |
---|---|---|
committer | fiaxh <git@lightrise.org> | 2024-11-14 10:20:12 -0600 |
commit | aaf4542e6208460c305db4be36b15dc832ddc95a (patch) | |
tree | ec7b60b0f0ea74e21403788e8345336bd0f3939b /xmpp-vala/src/module/xep/0447_stateless_file_sharing.vala | |
parent | 909f569318835d11703c49fba7dbe49996759f38 (diff) | |
download | dino-aaf4542e6208460c305db4be36b15dc832ddc95a.tar.gz dino-aaf4542e6208460c305db4be36b15dc832ddc95a.zip |
Implement XEP-0447: Stateless file sharing
Diffstat (limited to 'xmpp-vala/src/module/xep/0447_stateless_file_sharing.vala')
-rw-r--r-- | xmpp-vala/src/module/xep/0447_stateless_file_sharing.vala | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/xmpp-vala/src/module/xep/0447_stateless_file_sharing.vala b/xmpp-vala/src/module/xep/0447_stateless_file_sharing.vala new file mode 100644 index 00000000..285bfe78 --- /dev/null +++ b/xmpp-vala/src/module/xep/0447_stateless_file_sharing.vala @@ -0,0 +1,225 @@ +using Xmpp; + +namespace Xmpp.Xep.StatelessFileSharing { + + + public const string STANZA_NAME = "file-transfer"; + + public interface SfsSource: Object { + public abstract string type(); + public abstract string serialize(); + + public abstract StanzaNode to_stanza_node(); + } + + public class HttpSource: Object, SfsSource { + public string url; + + public const string HTTP_NS_URI = "http://jabber.org/protocol/url-data"; + public const string HTTP_STANZA_NAME = "url-data"; + public const string HTTP_URL_ATTRIBUTE = "target"; + public const string SOURCE_TYPE = "http"; + + public string type() { + return SOURCE_TYPE; + } + + public string serialize() { + return this.to_stanza_node().to_xml(); + } + + public StanzaNode to_stanza_node() { + StanzaNode node = new StanzaNode.build(HTTP_STANZA_NAME, HTTP_NS_URI).add_self_xmlns(); + node.put_attribute(HTTP_URL_ATTRIBUTE, this.url); + return node; + } + + public static async HttpSource deserialize(string data) { + StanzaNode node = yield new StanzaReader.for_string(data).read_stanza_node(); + HttpSource source = HttpSource.from_stanza_node(node); + assert(source != null); + return source; + } + + public static HttpSource? from_stanza_node(StanzaNode node) { + string? url = node.get_attribute(HTTP_URL_ATTRIBUTE); + if (url == null) { + return null; + } + HttpSource source = new HttpSource(); + source.url = url; + return source; + } + + public static Gee.List<HttpSource> extract_sources(StanzaNode node) { + Gee.List<HttpSource> sources = new Gee.ArrayList<HttpSource>(); + foreach (StanzaNode http_node in node.get_subnodes(HTTP_STANZA_NAME, HTTP_NS_URI)) { + HttpSource? source = HttpSource.from_stanza_node(http_node); + if (source != null) { + sources.add(source); + } + } + return sources; + } + } + + public class SfsElement { + public Xep.FileMetadataElement.FileMetadata metadata = new Xep.FileMetadataElement.FileMetadata(); + public Gee.List<SfsSource> sources = new Gee.ArrayList<SfsSource>(); + + public static SfsElement? from_stanza_node(StanzaNode node) { + SfsElement element = new SfsElement(); + StanzaNode? metadata_node = node.get_subnode("file", Xep.FileMetadataElement.NS_URI); + if (metadata_node == null) { + return null; + } + Xep.FileMetadataElement.FileMetadata metadata = Xep.FileMetadataElement.FileMetadata.from_stanza_node(metadata_node); + if (metadata == null) { + return null; + } + element.metadata = metadata; + StanzaNode? sources_node = node.get_subnode("sources"); + if (sources_node == null) { + return null; + } + Gee.List<HttpSource> sources = HttpSource.extract_sources(sources_node); + if (sources.is_empty) { + return null; + } + element.sources = sources; + return element; + } + + public StanzaNode to_stanza_node() { + StanzaNode node = new StanzaNode.build(STANZA_NAME, NS_URI).add_self_xmlns(); + node.put_node(this.metadata.to_stanza_node()); + StanzaNode sources_node = new StanzaNode.build("sources", NS_URI); + Gee.List<StanzaNode> sources = new Gee.ArrayList<StanzaNode>(); + foreach (SfsSource source in this.sources) { + sources.add(source.to_stanza_node()); + } + sources_node.sub_nodes = sources; + node.put_node(sources_node); + return node; + } + } + + public class SfsSourceAttachment { + public string sfs_id; + public Gee.List<SfsSource> sources = new Gee.ArrayList<SfsSource>(); + + public const string ATTACHMENT_NS_URI = "urn:xmpp:message-attaching:1"; + public const string ATTACH_TO_STANZA_NAME = "attach-to"; + public const string SOURCES_STANZA_NAME = "sources"; + public const string ID_ATTRIBUTE_NAME = "id"; + + + public static SfsSourceAttachment? from_message_stanza(MessageStanza stanza) { + StanzaNode? attach_to = stanza.stanza.get_subnode(ATTACH_TO_STANZA_NAME, ATTACHMENT_NS_URI); + StanzaNode? sources = stanza.stanza.get_subnode(SOURCES_STANZA_NAME, NS_URI); + if (attach_to == null || sources == null) { + return null; + } + string? id = attach_to.get_attribute(ID_ATTRIBUTE_NAME, ATTACHMENT_NS_URI); + if (id == null) { + return null; + } + SfsSourceAttachment attachment = new SfsSourceAttachment(); + attachment.sfs_id = id; + Gee.List<HttpSource> http_sources = HttpSource.extract_sources(sources); + if (http_sources.is_empty) { + return null; + } + attachment.sources = http_sources; + return attachment; + } + + public MessageStanza to_message_stanza(Jid to, string message_type) { + MessageStanza stanza = new MessageStanza() { to=to, type_=message_type }; + Xep.MessageProcessingHints.set_message_hint(stanza, Xep.MessageProcessingHints.HINT_STORE); + + StanzaNode attach_to = new StanzaNode.build(ATTACH_TO_STANZA_NAME, ATTACHMENT_NS_URI); + attach_to.add_attribute(new StanzaAttribute.build(ATTACHMENT_NS_URI, "id", this.sfs_id)); + stanza.stanza.put_node(attach_to); + + StanzaNode sources = new StanzaNode.build(SOURCES_STANZA_NAME, NS_URI); + Gee.List<StanzaNode> sources_nodes = new Gee.ArrayList<StanzaNode>(); + foreach (SfsSource source in this.sources) { + sources_nodes.add(source.to_stanza_node()); + } + sources.sub_nodes = sources_nodes; + stanza.stanza.put_node(sources); + + return stanza; + } + } + + public class MessageFlag : Xmpp.MessageFlag { + public const string ID = "stateless_file_sharing"; + + public static MessageFlag? get_flag(MessageStanza message) { + return (MessageFlag) message.get_flag(NS_URI, ID); + } + + public override string get_ns() { + return NS_URI; + } + + public override string get_id() { + return ID; + } + } + + public class Module : XmppStreamModule { + public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "stateless_file_sharing"); + + public signal void received_sfs(Jid from, Jid to, SfsElement sfs_element, MessageStanza message); + public signal void received_sfs_attachment(Jid from, Jid to, SfsSourceAttachment attachment, MessageStanza message); + + public void send_stateless_file_transfer(XmppStream stream, MessageStanza sfs_message, SfsElement sfs_element) { + StanzaNode sfs_node = sfs_element.to_stanza_node(); + printerr(sfs_node.to_ansi_string(true)); + + sfs_message.stanza.put_node(sfs_node); + printerr("Sending message:\n"); + printerr(sfs_message.stanza.to_ansi_string(true)); + stream.get_module(MessageModule.IDENTITY).send_message.begin(stream, sfs_message); + } + + public void send_stateless_file_transfer_attachment(XmppStream stream, Jid to, string message_type, SfsSourceAttachment attachment) { + MessageStanza message = attachment.to_message_stanza(to, message_type); + + printerr("Sending message:\n"); + printerr(message.stanza.to_ansi_string(true)); + stream.get_module(MessageModule.IDENTITY).send_message.begin(stream, message); + } + + private void on_received_message(XmppStream stream, MessageStanza message) { + StanzaNode? sfs_node = message.stanza.get_subnode(STANZA_NAME, NS_URI); + if (sfs_node != null) { + SfsElement? sfs_element = SfsElement.from_stanza_node(sfs_node); + if (sfs_element == null) { + return; + } + message.add_flag(new MessageFlag()); + received_sfs(message.from, message.to, sfs_element, message); + } + SfsSourceAttachment? attachment = SfsSourceAttachment.from_message_stanza(message); + if (attachment != null) { + received_sfs_attachment(message.from, message.to, attachment, message); + } + + } + + public override void attach(XmppStream stream) { + stream.get_module(MessageModule.IDENTITY).received_message.connect(on_received_message); + } + + public override void detach(XmppStream stream) { + stream.get_module(MessageModule.IDENTITY).received_message.disconnect(on_received_message); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return IDENTITY.id; } + } +} |