aboutsummaryrefslogtreecommitdiff
path: root/xmpp-vala
diff options
context:
space:
mode:
authorfiaxh <git@lightrise.org>2024-11-02 22:24:59 +0100
committerfiaxh <git@lightrise.org>2024-11-15 14:40:08 -0600
commit79f792e090330a05753f9edb27332a946eb0840d (patch)
tree5a6f1ad3ac0af0beea44ca9e83e7a9b052263025 /xmpp-vala
parentaaf4542e6208460c305db4be36b15dc832ddc95a (diff)
downloaddino-79f792e090330a05753f9edb27332a946eb0840d.tar.gz
dino-79f792e090330a05753f9edb27332a946eb0840d.zip
Fix and improve stateless file-sharing
Diffstat (limited to 'xmpp-vala')
-rw-r--r--xmpp-vala/CMakeLists.txt2
-rw-r--r--xmpp-vala/meson.build6
-rw-r--r--xmpp-vala/src/module/message/stanza.vala6
-rw-r--r--xmpp-vala/src/module/xep/0104_http_scheme_url_data.vala19
-rw-r--r--xmpp-vala/src/module/xep/0264_jingle_content_thumbnails.vala41
-rw-r--r--xmpp-vala/src/module/xep/0300_cryptographic_hashes.vala152
-rw-r--r--xmpp-vala/src/module/xep/0367_message_attaching.vala15
-rw-r--r--xmpp-vala/src/module/xep/0446_file_metadata_element.vala128
-rw-r--r--xmpp-vala/src/module/xep/0447_stateless_file_sharing.vala267
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; }
}
}