diff options
Diffstat (limited to 'xmpp-vala')
-rw-r--r-- | xmpp-vala/CMakeLists.txt | 2 | ||||
-rw-r--r-- | xmpp-vala/meson.build | 6 | ||||
-rw-r--r-- | xmpp-vala/src/module/message/stanza.vala | 6 | ||||
-rw-r--r-- | xmpp-vala/src/module/xep/0104_http_scheme_url_data.vala | 19 | ||||
-rw-r--r-- | xmpp-vala/src/module/xep/0264_jingle_content_thumbnails.vala | 41 | ||||
-rw-r--r-- | xmpp-vala/src/module/xep/0300_cryptographic_hashes.vala | 152 | ||||
-rw-r--r-- | xmpp-vala/src/module/xep/0367_message_attaching.vala | 15 | ||||
-rw-r--r-- | xmpp-vala/src/module/xep/0446_file_metadata_element.vala | 128 | ||||
-rw-r--r-- | xmpp-vala/src/module/xep/0447_stateless_file_sharing.vala | 267 |
9 files changed, 257 insertions, 379 deletions
diff --git a/xmpp-vala/CMakeLists.txt b/xmpp-vala/CMakeLists.txt index 824b667e..1fc5e436 100644 --- a/xmpp-vala/CMakeLists.txt +++ b/xmpp-vala/CMakeLists.txt @@ -81,6 +81,7 @@ SOURCES "src/module/xep/0082_date_time_profiles.vala" "src/module/xep/0084_user_avatars.vala" "src/module/xep/0085_chat_state_notifications.vala" + "src/module/xep/0104_http_scheme_url_data.vala" "src/module/xep/0115_entity_capabilities.vala" "src/module/xep/0166_jingle/content.vala" @@ -139,6 +140,7 @@ SOURCES "src/module/xep/0353_jingle_message_initiation.vala" "src/module/xep/0359_unique_stable_stanza_ids.vala" "src/module/xep/0363_http_file_upload.vala" + "src/module/xep/0367_message_attaching.vala" "src/module/xep/0380_explicit_encryption.vala" "src/module/xep/0391_jingle_encrypted_transports.vala" "src/module/xep/0410_muc_self_ping.vala" diff --git a/xmpp-vala/meson.build b/xmpp-vala/meson.build index 4d8c0493..fdcb54dd 100644 --- a/xmpp-vala/meson.build +++ b/xmpp-vala/meson.build @@ -66,6 +66,7 @@ sources = files( 'src/module/xep/0082_date_time_profiles.vala', 'src/module/xep/0084_user_avatars.vala', 'src/module/xep/0085_chat_state_notifications.vala', + 'src/module/xep/0104_http_scheme_url_data.vala', 'src/module/xep/0115_entity_capabilities.vala', 'src/module/xep/0166_jingle/component.vala', 'src/module/xep/0166_jingle/content.vala', @@ -99,10 +100,12 @@ sources = files( 'src/module/xep/0249_direct_muc_invitations.vala', 'src/module/xep/0260_jingle_socks5_bytestreams.vala', 'src/module/xep/0261_jingle_in_band_bytestreams.vala', + 'src/module/xep/0264_jingle_content_thumbnails.vala', 'src/module/xep/0272_muji.vala', 'src/module/xep/0280_message_carbons.vala', 'src/module/xep/0297_stanza_forwarding.vala', 'src/module/xep/0298_coin.vala', + 'src/module/xep/0300_cryptographic_hashes.vala', 'src/module/xep/0308_last_message_correction.vala', 'src/module/xep/0313_2_message_archive_management.vala', 'src/module/xep/0313_message_archive_management.vala', @@ -111,6 +114,7 @@ sources = files( 'src/module/xep/0353_jingle_message_initiation.vala', 'src/module/xep/0359_unique_stable_stanza_ids.vala', 'src/module/xep/0363_http_file_upload.vala', + 'src/module/xep/0367_message_attaching.vala', 'src/module/xep/0380_explicit_encryption.vala', 'src/module/xep/0384_omemo/omemo_decryptor.vala', 'src/module/xep/0384_omemo/omemo_encryptor.vala', @@ -122,6 +126,8 @@ sources = files( 'src/module/xep/0421_occupant_ids.vala', 'src/module/xep/0428_fallback_indication.vala', 'src/module/xep/0444_reactions.vala', + 'src/module/xep/0446_file_metadata_element.vala', + 'src/module/xep/0447_stateless_file_sharing.vala', 'src/module/xep/0461_replies.vala', 'src/module/xep/0482_call_invites.vala', 'src/module/xep/pixbuf_storage.vala', diff --git a/xmpp-vala/src/module/message/stanza.vala b/xmpp-vala/src/module/message/stanza.vala index 053c44dd..cb07ab2a 100644 --- a/xmpp-vala/src/module/message/stanza.vala +++ b/xmpp-vala/src/module/message/stanza.vala @@ -15,13 +15,17 @@ public class MessageStanza : Xmpp.Stanza { public bool rerun_parsing = false; private ArrayList<MessageFlag> flags = new ArrayList<MessageFlag>(); - public string body { + public string? body { get { StanzaNode? body_node = stanza.get_subnode(NODE_BODY); return body_node == null ? null : body_node.get_string_content(); } set { StanzaNode? body_node = stanza.get_subnode(NODE_BODY); + if (value == null) { + if (body_node != null) stanza.sub_nodes.remove(body_node); + return; + } if (body_node == null) { body_node = new StanzaNode.build(NODE_BODY); stanza.put_node(body_node); diff --git a/xmpp-vala/src/module/xep/0104_http_scheme_url_data.vala b/xmpp-vala/src/module/xep/0104_http_scheme_url_data.vala new file mode 100644 index 00000000..b177a1ef --- /dev/null +++ b/xmpp-vala/src/module/xep/0104_http_scheme_url_data.vala @@ -0,0 +1,19 @@ +using Xmpp; + +namespace Xmpp.Xep.HttpSchemeForUrlData { + public const string NS_URI = "http://jabber.org/protocol/url-data"; + + // If there are multiple URLs, this will only return the first one + public static string? get_url(StanzaNode node) { + StanzaNode? url_data_node = node.get_subnode("url-data", NS_URI); + if (url_data_node == null) return null; + + return url_data_node.get_attribute("target"); + } + + public static StanzaNode to_stanza_node(string url) { + return new StanzaNode.build("url-data", NS_URI).add_self_xmlns() + .put_attribute("target", url, NS_URI); + + } +}
\ No newline at end of file diff --git a/xmpp-vala/src/module/xep/0264_jingle_content_thumbnails.vala b/xmpp-vala/src/module/xep/0264_jingle_content_thumbnails.vala index cb281f80..053fb7d5 100644 --- a/xmpp-vala/src/module/xep/0264_jingle_content_thumbnails.vala +++ b/xmpp-vala/src/module/xep/0264_jingle_content_thumbnails.vala @@ -1,6 +1,16 @@ namespace Xmpp.Xep.JingleContentThumbnails { public const string NS_URI = "urn:xmpp:thumbs:1"; - public const string STANZA_NAME = "thumbnail"; + + public Gee.List<Thumbnail> get_thumbnails(StanzaNode node) { + var thumbnails = new Gee.ArrayList<Thumbnail>(); + foreach (StanzaNode thumbnail_node in node.get_subnodes("thumbnail", Xep.JingleContentThumbnails.NS_URI)) { + var thumbnail = Thumbnail.from_stanza_node(thumbnail_node); + if (thumbnail != null) { + thumbnails.add(thumbnail); + } + } + return thumbnails; + } public class Thumbnail { public string uri; @@ -8,41 +18,30 @@ namespace Xmpp.Xep.JingleContentThumbnails { public int width; public int height; - const string URI_ATTRIBUTE = "uri"; - const string MIME_ATTRIBUTE = "media-type"; - const string WIDTH_ATTRIBUTE = "width"; - const string HEIGHT_ATTRIBUTE = "height"; - public StanzaNode to_stanza_node() { - StanzaNode node = new StanzaNode.build(STANZA_NAME, NS_URI).add_self_xmlns() - .put_attribute(URI_ATTRIBUTE, this.uri); + StanzaNode node = new StanzaNode.build("thumbnail", NS_URI).add_self_xmlns() + .put_attribute("uri", this.uri); if (this.media_type != null) { - node.put_attribute(MIME_ATTRIBUTE, this.media_type); + node.put_attribute("media-type", this.media_type); } if (this.width != -1) { - node.put_attribute(WIDTH_ATTRIBUTE, this.width.to_string()); + node.put_attribute("width", this.width.to_string()); } if (this.height != -1) { - node.put_attribute(HEIGHT_ATTRIBUTE, this.height.to_string()); + node.put_attribute("height", this.height.to_string()); } return node; } public static Thumbnail? from_stanza_node(StanzaNode node) { Thumbnail thumbnail = new Thumbnail(); - thumbnail.uri = node.get_attribute(URI_ATTRIBUTE); + thumbnail.uri = node.get_attribute("uri"); if (thumbnail.uri == null) { return null; } - thumbnail.media_type = node.get_attribute(MIME_ATTRIBUTE); - string? width = node.get_attribute(WIDTH_ATTRIBUTE); - if (width != null) { - thumbnail.width = int.parse(width); - } - string? height = node.get_attribute(HEIGHT_ATTRIBUTE); - if (height != null) { - thumbnail.height = int.parse(height); - } + thumbnail.media_type = node.get_attribute("media-type"); + thumbnail.width = node.get_attribute_int("width"); + thumbnail.height = node.get_attribute_int("height"); return thumbnail; } } diff --git a/xmpp-vala/src/module/xep/0300_cryptographic_hashes.vala b/xmpp-vala/src/module/xep/0300_cryptographic_hashes.vala index 00f9e2ee..b73e63a5 100644 --- a/xmpp-vala/src/module/xep/0300_cryptographic_hashes.vala +++ b/xmpp-vala/src/module/xep/0300_cryptographic_hashes.vala @@ -4,69 +4,54 @@ using Gee; namespace Xmpp.Xep.CryptographicHashes { public const string NS_URI = "urn:xmpp:hashes:2"; - public enum HashCmp { - Match, - Mismatch, - None, + public Gee.List<Hash> get_hashes(StanzaNode node) { + var hashes = new ArrayList<Hash>(); + foreach (StanzaNode hash_node in node.get_subnodes("hash", NS_URI)) { + hashes.add(new Hash.from_stanza_node(hash_node)); + } + return hashes; + } + + public Gee.List<Hash> get_supported_hashes(Gee.List<Hash> hashes) { + var ret = new ArrayList<Hash>(); + foreach (Hash hash in hashes) { + ChecksumType? hash_type = hash_string_to_type(hash.algo); + if (hash_type != null) { + ret.add(hash); + } + } + return ret; + } + + public bool has_supported_hashes(Gee.List<Hash> hashes) { + foreach (Hash hash in hashes) { + ChecksumType? hash_type = hash_string_to_type(hash.algo); + if (hash_type != null) return true; + } + return false; } - public class Hash { + public class Hash : Object { public string algo; // hash encoded in Base64 public string val; - public static string hash_name(ChecksumType type) { - switch(type) { - case ChecksumType.MD5: - return "md5"; - case ChecksumType.SHA1: - return "sha-1"; - case ChecksumType.SHA256: - return "sha-256"; - case ChecksumType.SHA384: - return "sha-384"; - case ChecksumType.SHA512: - return "sha-512"; - } - return "(null)"; - } - - public static ChecksumType? supported_hash(string hash) { - switch (hash) { - case "sha-1": - return ChecksumType.SHA1; - case "sha-256": - return ChecksumType.SHA256; - case "sha-384": - return ChecksumType.SHA384; - case "sha-512": - return ChecksumType.SHA512; - } - return null; + public Hash.with_checksum(ChecksumType checksum_type, string hash) { + algo = hash_type_to_string(checksum_type); + val = hash; } - public Hash.from_data(GLib.ChecksumType type, uint8[] data) { + public Hash.compute(GLib.ChecksumType type, uint8[] data) { GLib.Checksum checksum = new GLib.Checksum(type); checksum.update(data, data.length); // 64 * 8 = 512 (sha-512 is the longest hash variant) uint8[] digest = new uint8[64]; size_t length = digest.length; checksum.get_digest(digest, ref length); - this.algo = hash_name(type); + this.algo = hash_type_to_string(type); this.val = GLib.Base64.encode(digest[0:length]); } - public HashCmp compare(Hash other) { - if (this.algo != other.algo) { - return HashCmp.None; - } - if (this.val == other.val) { - return HashCmp.Match; - } else { - return HashCmp.Mismatch; - } - } - public StanzaNode to_stanza_node() { return new StanzaNode.build("hash", NS_URI).add_self_xmlns() .put_attribute("algo", this.algo) @@ -79,58 +64,33 @@ namespace Xmpp.Xep.CryptographicHashes { } } - public class Hashes { - public Gee.List<Hash> hashes = new ArrayList<Hash>(); - - public Gee.List<ChecksumType> supported_hashes() { - Gee.List<ChecksumType> supported = new ArrayList<ChecksumType>(); - foreach (Hash hash in this.hashes) { - ChecksumType? hash_type = Hash.supported_hash(hash.algo); - if (hash_type != null) { - supported.add(hash_type); - } - } - return supported; - } - - public Hashes.from_data(Gee.List<ChecksumType> types, uint8[] data) { - foreach (ChecksumType type in types) { - this.hashes.add(new Hash.from_data(type, data)); - } - } - - public HashCmp compare(Hashes other) { - HashCmp cmp = HashCmp.None; - foreach (Hash this_hash in this.hashes) { - foreach (Hash other_hash in other.hashes) { - switch (this_hash.compare(other_hash)) { - case HashCmp.Mismatch: - return HashCmp.Mismatch; - case HashCmp.Match: - cmp = HashCmp.Match; - break; - case HashCmp.None: - continue; - } - } - } - return cmp; - } - - public Gee.List<StanzaNode> to_stanza_nodes() { - Gee.List<StanzaNode> nodes = new ArrayList<StanzaNode>(); - foreach (Hash hash in this.hashes) { - nodes.add(hash.to_stanza_node()); - } - return nodes; + public static string hash_type_to_string(ChecksumType type) { + switch(type) { + case ChecksumType.MD5: + return "md5"; + case ChecksumType.SHA1: + return "sha-1"; + case ChecksumType.SHA256: + return "sha-256"; + case ChecksumType.SHA384: + return "sha-384"; + case ChecksumType.SHA512: + return "sha-512"; } + return "(null)"; + } - public Hashes.from_stanza_subnodes(StanzaNode node) { - Gee.List<StanzaNode> subnodes = node.get_subnodes("hash", NS_URI); - this.hashes = new ArrayList<Hash>(); - foreach (StanzaNode subnode in subnodes) { - this.hashes.add(new Hash.from_stanza_node(subnode)); - } + public static ChecksumType? hash_string_to_type(string hash) { + switch (hash) { + case "sha-1": + return ChecksumType.SHA1; + case "sha-256": + return ChecksumType.SHA256; + case "sha-384": + return ChecksumType.SHA384; + case "sha-512": + return ChecksumType.SHA512; } + return null; } } diff --git a/xmpp-vala/src/module/xep/0367_message_attaching.vala b/xmpp-vala/src/module/xep/0367_message_attaching.vala new file mode 100644 index 00000000..7441cd19 --- /dev/null +++ b/xmpp-vala/src/module/xep/0367_message_attaching.vala @@ -0,0 +1,15 @@ +namespace Xmpp.Xep.MessageAttaching { + public const string NS_URI = "urn:xmpp:message-attaching:1"; + + public static string? get_attach_to(StanzaNode node) { + StanzaNode? attach_to = node.get_subnode("attach-to", NS_URI); + if (attach_to == null) return null; + + return attach_to.get_attribute("id", NS_URI); + } + + public static StanzaNode to_stanza_node(string id) { + return new StanzaNode.build("attach-to", NS_URI).add_self_xmlns() + .put_attribute("id", id, NS_URI); + } +}
\ No newline at end of file diff --git a/xmpp-vala/src/module/xep/0446_file_metadata_element.vala b/xmpp-vala/src/module/xep/0446_file_metadata_element.vala index d7fbb06f..56b3d379 100644 --- a/xmpp-vala/src/module/xep/0446_file_metadata_element.vala +++ b/xmpp-vala/src/module/xep/0446_file_metadata_element.vala @@ -1,23 +1,24 @@ -using Xmpp.Xep.CryptographicHashes; - namespace Xmpp.Xep.FileMetadataElement { public const string NS_URI = "urn:xmpp:file:metadata:0"; public class FileMetadata { - public string name { get; set; } + public string? name { get; set; } public string? mime_type { get; set; } public int64 size { get; set; default=-1; } public string? desc { get; set; } public DateTime? date { get; set; } public int width { get; set; default=-1; } // Width of image in pixels - public int height { get; set; default=-1; } // Height of image in pixels - public CryptographicHashes.Hashes hashes = new CryptographicHashes.Hashes(); - public int64 length { get; set; default=-1; } // Length of audio/video in milliseconds - public Gee.List<Xep.JingleContentThumbnails.Thumbnail> thumbnails = new Gee.ArrayList<Xep.JingleContentThumbnails.Thumbnail>(); + public int height { get; set; default=-1; } // Height of image in pixels + public Gee.List<CryptographicHashes.Hash> hashes = new Gee.ArrayList<CryptographicHashes.Hash>(); + public int64 length { get; set; default=-1; } // Length of audio/video in milliseconds + public Gee.List<Xep.JingleContentThumbnails.Thumbnail> thumbnails = new Gee.ArrayList<Xep.JingleContentThumbnails.Thumbnail>(); public StanzaNode to_stanza_node() { - StanzaNode node = new StanzaNode.build("file", NS_URI).add_self_xmlns() - .put_node(new StanzaNode.build("name", NS_URI).put_node(new StanzaNode.text(this.name))); + StanzaNode node = new StanzaNode.build("file", NS_URI).add_self_xmlns(); + + if (this.name != null) { + node.put_node(new StanzaNode.build("name", NS_URI).put_node(new StanzaNode.text(this.name))); + } if (this.mime_type != null) { node.put_node(new StanzaNode.build("media_type", NS_URI).put_node(new StanzaNode.text(this.mime_type))); } @@ -39,82 +40,57 @@ namespace Xmpp.Xep.FileMetadataElement { if (this.length != -1) { node.put_node(new StanzaNode.build("length", NS_URI).put_node(new StanzaNode.text(this.length.to_string()))); } - node.sub_nodes.add_all(this.hashes.to_stanza_nodes()); + foreach (var hash in hashes) { + node.put_node(hash.to_stanza_node()); + } foreach (Xep.JingleContentThumbnails.Thumbnail thumbnail in this.thumbnails) { node.put_node(thumbnail.to_stanza_node()); } return node; } + } - public void add_to_message(MessageStanza message) { - StanzaNode node = this.to_stanza_node(); - printerr("Attaching file metadata:\n"); - printerr("%s\n", node.to_ansi_string(true)); - message.stanza.put_node(node); - } + public static FileMetadata? get_file_metadata(StanzaNode node) { + StanzaNode? file_node = node.get_subnode("file", Xep.FileMetadataElement.NS_URI); + if (file_node == null) return null; - public static FileMetadata? from_stanza_node(StanzaNode node) { - FileMetadata metadata = new FileMetadata(); - // TODO: null checks on final values - StanzaNode? name_node = node.get_subnode("name"); - if (name_node == null || name_node.get_string_content() == null) { - return null; - } else { - metadata.name = name_node.get_string_content(); - } - StanzaNode? desc_node = node.get_subnode("desc"); - if (desc_node != null && desc_node.get_string_content() != null) { - metadata.desc = desc_node.get_string_content(); - } - StanzaNode? mime_node = node.get_subnode("media_type"); - if (mime_node != null && mime_node.get_string_content() != null) { - metadata.mime_type = mime_node.get_string_content(); - } - StanzaNode? size_node = node.get_subnode("size"); - if (size_node != null && size_node.get_string_content() != null) { - metadata.size = int64.parse(size_node.get_string_content()); - } - StanzaNode? date_node = node.get_subnode("date"); - if (date_node != null && date_node.get_string_content() != null) { - metadata.date = new DateTime.from_iso8601(date_node.get_string_content(), null); - } - StanzaNode? width_node = node.get_subnode("width"); - if (width_node != null && width_node.get_string_content() != null) { - metadata.width = int.parse(width_node.get_string_content()); - } - StanzaNode? height_node = node.get_subnode("height"); - if (height_node != null && height_node.get_string_content() != null) { - metadata.height = int.parse(height_node.get_string_content()); - } - StanzaNode? length_node = node.get_subnode("length"); - if (length_node != null && length_node.get_string_content() != null) { - metadata.length = int.parse(length_node.get_string_content()); - } - foreach (StanzaNode thumbnail_node in node.get_subnodes(Xep.JingleContentThumbnails.STANZA_NAME, Xep.JingleContentThumbnails.NS_URI)) { - Xep.JingleContentThumbnails.Thumbnail? thumbnail = Xep.JingleContentThumbnails.Thumbnail.from_stanza_node(thumbnail_node); - if (thumbnail != null) { - metadata.thumbnails.add(thumbnail); - } - } - metadata.hashes = new CryptographicHashes.Hashes.from_stanza_subnodes(node); - return metadata; + FileMetadata metadata = new FileMetadata(); + + StanzaNode? name_node = file_node.get_subnode("name"); + if (name_node != null && name_node.get_string_content() != null) { + metadata.name = name_node.get_string_content(); } - public static FileMetadata? from_message(MessageStanza message) { - StanzaNode? node = message.stanza.get_subnode("file", NS_URI); - if (node == null) { - return null; - } - printerr("Parsing metadata from message:\n"); - printerr("%s\n", node.to_xml()); - FileMetadata metadata = FileMetadata.from_stanza_node(node); - if (metadata != null) { - printerr("Parsed metadata:\n"); - printerr("%s\n", metadata.to_stanza_node().to_ansi_string(true)); - } else { - printerr("Failed to parse metadata!\n"); - } - return FileMetadata.from_stanza_node(node); + StanzaNode? desc_node = file_node.get_subnode("desc"); + if (desc_node != null && desc_node.get_string_content() != null) { + metadata.desc = desc_node.get_string_content(); + } + StanzaNode? mime_node = file_node.get_subnode("media_type"); + if (mime_node != null && mime_node.get_string_content() != null) { + metadata.mime_type = mime_node.get_string_content(); + } + StanzaNode? size_node = file_node.get_subnode("size"); + if (size_node != null && size_node.get_string_content() != null) { + metadata.size = int64.parse(size_node.get_string_content()); + } + StanzaNode? date_node = file_node.get_subnode("date"); + if (date_node != null && date_node.get_string_content() != null) { + metadata.date = new DateTime.from_iso8601(date_node.get_string_content(), null); + } + StanzaNode? width_node = file_node.get_subnode("width"); + if (width_node != null && width_node.get_string_content() != null) { + metadata.width = int.parse(width_node.get_string_content()); + } + StanzaNode? height_node = file_node.get_subnode("height"); + if (height_node != null && height_node.get_string_content() != null) { + metadata.height = int.parse(height_node.get_string_content()); + } + StanzaNode? length_node = file_node.get_subnode("length"); + if (length_node != null && length_node.get_string_content() != null) { + metadata.length = int.parse(length_node.get_string_content()); } + metadata.thumbnails = Xep.JingleContentThumbnails.get_thumbnails(file_node); + metadata.hashes = CryptographicHashes.get_hashes(file_node); + return metadata; } } diff --git a/xmpp-vala/src/module/xep/0447_stateless_file_sharing.vala b/xmpp-vala/src/module/xep/0447_stateless_file_sharing.vala index 285bfe78..c63f7ea6 100644 --- a/xmpp-vala/src/module/xep/0447_stateless_file_sharing.vala +++ b/xmpp-vala/src/module/xep/0447_stateless_file_sharing.vala @@ -1,225 +1,122 @@ +using Gee; using Xmpp; namespace Xmpp.Xep.StatelessFileSharing { + public const string NS_URI = "urn:xmpp:sfs:0"; - public const string STANZA_NAME = "file-transfer"; + public static Gee.List<FileShare> get_file_shares(MessageStanza message) { + var ret = new ArrayList<FileShare>(); + foreach (StanzaNode file_sharing_node in message.stanza.get_subnodes("file-sharing", NS_URI)) { + var metadata = Xep.FileMetadataElement.get_file_metadata(file_sharing_node); + if (metadata == null) continue; - 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; + var sources_node = message.stanza.get_subnode("sources", NS_URI); - 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; + ret.add(new FileShare() { + id = file_sharing_node.get_attribute("id", NS_URI), + metadata = Xep.FileMetadataElement.get_file_metadata(file_sharing_node), + sources = sources_node != null ? get_sources(sources_node) : null + }); } - public string serialize() { - return this.to_stanza_node().to_xml(); - } + if (ret.size == 0) return null; - 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; - } + return ret; + } - 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 Gee.List<SourceAttachment>? get_source_attachments(MessageStanza message) { + Gee.List<StanzaNode> sources_nodes = message.stanza.get_subnodes("sources", NS_URI); + if (sources_nodes.is_empty) return null; - 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; - } + string? attach_to_id = MessageAttaching.get_attach_to(message.stanza); + if (attach_to_id == null) return null; - 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; - } - } + var ret = new ArrayList<SourceAttachment>(); - 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; + foreach (StanzaNode sources_node in sources_nodes) { + ret.add(new SourceAttachment() { + to_message_id = attach_to_id, + to_file_transfer_id = sources_node.get_attribute("id", NS_URI), + sources = get_sources(sources_node) + }); } + return ret; } - 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; - } + // Currently only returns a single http source + private static Gee.List<Source>? get_sources(StanzaNode sources_node) { + string? url = HttpSchemeForUrlData.get_url(sources_node); + if (url == null) return null; - 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); + var http_source = new HttpSource() { url=url }; + var sources = new Gee.ArrayList<Source>(); + sources.add(http_source); - 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; - } + return sources; } - 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 static void set_sfs_element(MessageStanza message, string file_sharing_id, FileMetadataElement.FileMetadata metadata, Gee.List<Xep.StatelessFileSharing.Source>? sources) { + var file_sharing_node = new StanzaNode.build("file-sharing", NS_URI).add_self_xmlns() + .put_attribute("id", file_sharing_id, NS_URI) + .put_node(metadata.to_stanza_node()); + if (sources != null && !sources.is_empty) { + file_sharing_node.put_node(create_sources_node(file_sharing_id, sources)); } + message.stanza.put_node(file_sharing_node); + } - public override string get_ns() { - return NS_URI; - } + public static void set_sfs_attachment(MessageStanza message, string attach_to_id, string attach_to_file_id, Gee.List<Xep.StatelessFileSharing.Source> sources) { + message.stanza.put_node(MessageAttaching.to_stanza_node(attach_to_id)); + message.stanza.put_node(create_sources_node(attach_to_file_id, sources).add_self_xmlns()); + } - public override string get_id() { - return ID; + private static StanzaNode create_sources_node(string file_sharing_id, Gee.List<Xep.StatelessFileSharing.Source> sources) { + StanzaNode sources_node = new StanzaNode.build("sources", NS_URI) + .put_attribute("id", file_sharing_id, NS_URI); + foreach (var source in sources) { + sources_node.put_node(source.to_stanza_node()); } + return sources_node; } - 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)); + public class FileShare : Object { + public string? id { get; set; } + public Xep.FileMetadataElement.FileMetadata metadata { get; set; } + public Gee.List<Source>? sources { get; set; } + } - 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 class SourceAttachment : Object { + public string to_message_id { get; set; } + public string? to_file_transfer_id { get; set; } + public Gee.List<Source>? sources { get; set; } + } - 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); + public interface Source: Object { + public abstract string type(); + public abstract StanzaNode to_stanza_node(); + public abstract bool equals(Source source); - printerr("Sending message:\n"); - printerr(message.stanza.to_ansi_string(true)); - stream.get_module(MessageModule.IDENTITY).send_message.begin(stream, message); + public static bool equals_func(Source s1, Source s2) { + return s1.equals(s2); } + } - 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 class HttpSource : Object, Source { + public string url { get; set; } + public string type() { + return "http"; } - public override void attach(XmppStream stream) { - stream.get_module(MessageModule.IDENTITY).received_message.connect(on_received_message); + public StanzaNode to_stanza_node() { + return HttpSchemeForUrlData.to_stanza_node(url); } - public override void detach(XmppStream stream) { - stream.get_module(MessageModule.IDENTITY).received_message.disconnect(on_received_message); + public bool equals(Source source) { + HttpSource? http_source = source as HttpSource; + if (http_source == null) return false; + return http_source.url == this.url; } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return IDENTITY.id; } } } |