aboutsummaryrefslogtreecommitdiff
path: root/libdino/src/service/stateless_file_sharing.vala
diff options
context:
space:
mode:
authorfrosty <frostinator33@airmail.cc>2026-10-26 10:21:57 -0400
committerfrosty <frostinator33@airmail.cc>2026-10-26 10:21:57 -0400
commit74aecd8b0986d07b50b7ce310d64e645b8b54e38 (patch)
tree560e6434f183eb54cd779936f3a4c9575146a066 /libdino/src/service/stateless_file_sharing.vala
parent3d7e90d401da02d430a87db0ec7e1db3a6f1a68f (diff)
parent4436001bb361ad4f9e4e05819214ecf0ea77c063 (diff)
Merge 'upstream/master'
Diffstat (limited to 'libdino/src/service/stateless_file_sharing.vala')
-rw-r--r--libdino/src/service/stateless_file_sharing.vala177
1 files changed, 99 insertions, 78 deletions
diff --git a/libdino/src/service/stateless_file_sharing.vala b/libdino/src/service/stateless_file_sharing.vala
index 15e1ddfe..6e97a5ce 100644
--- a/libdino/src/service/stateless_file_sharing.vala
+++ b/libdino/src/service/stateless_file_sharing.vala
@@ -16,11 +16,16 @@ public class Dino.StatelessFileSharing : StreamInteractionModule, Object {
private set { }
}
- public FileManager file_manager {
+ public FileManager fm {
owned get { return stream_interactor.get_module(FileManager.IDENTITY); }
private set { }
}
+ public FileManager2 fm2 {
+ owned get { return stream_interactor.get_module(FileManager2.IDENTITY); }
+ private set { }
+ }
+
public Database db {
owned get { return Application.get_default().db; }
private set { }
@@ -30,7 +35,7 @@ public class Dino.StatelessFileSharing : StreamInteractionModule, Object {
this.stream_interactor = stream_interactor;
this.db = db;
- stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(new ReceivedMessageListener(this));
+ stream_interactor.get_module(MessageProcessor.IDENTITY).build_message_stanza.connect(check_add_sfs_element);
}
public static void start(StreamInteractor stream_interactor, Database db) {
@@ -38,7 +43,81 @@ public class Dino.StatelessFileSharing : StreamInteractionModule, Object {
stream_interactor.add_module(m);
}
- public async void create_file_transfer(Conversation conversation, Message message, string? file_sharing_id, Xep.FileMetadataElement.FileMetadata metadata, Gee.List<Xep.StatelessFileSharing.Source>? sources) {
+ public async Entities.Message announce_files(FileTransferGroup file_group, Conversation conversation) {
+ Entities.Message file_share_message = stream_interactor.get_module(MessageProcessor.IDENTITY).create_out_message(null, conversation);
+ stream_interactor.get_module(MessageStorage.IDENTITY).add_message(file_share_message, conversation);
+ file_group.message_id = file_share_message.id;
+
+ foreach (var file_transfer in file_group.file_transfers) {
+ file_transfer.provider = SFS_PROVIDER_ID;
+ file_transfer.info = file_share_message.id.to_string();
+ file_transfer.file_sharing_id = Xmpp.random_uuid();
+ }
+
+ stream_interactor.get_module(MessageProcessor.IDENTITY).send_xmpp_message(file_share_message, conversation);
+
+ return file_share_message;
+ }
+
+ public async void attach_source(FileTransfer file_transfer, Conversation conversation, string http_url, string to_sfs_id) {
+ MessageStanza stanza = new MessageStanza() { to = conversation.counterpart, type_ = conversation.type_ == GROUPCHAT ? MessageStanza.TYPE_GROUPCHAT : MessageStanza.TYPE_CHAT };
+ stanza.body = http_url;
+ Xep.OutOfBandData.add_url_to_message(stanza, http_url);
+ var sources = new ArrayList<Xep.StatelessFileSharing.Source>();
+ sources.add(new Xep.StatelessFileSharing.HttpSource() { url = http_url });
+ Xep.StatelessFileSharing.set_sfs_attachment(stanza, to_sfs_id, file_transfer.file_sharing_id, sources);
+
+ var stream = stream_interactor.get_stream(conversation.account);
+ if (stream == null) throw new FileSendError.UPLOAD_FAILED("No stream");
+
+ stream.get_module(MessageModule.IDENTITY).send_message.begin(stream, stanza);
+ }
+
+ public async bool try_process_sfs(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) {
+ Gee.List<Xep.StatelessFileSharing.FileShare> file_shares = Xep.StatelessFileSharing.get_file_shares(stanza);
+ if (file_shares != null) {
+ // For now, only accept file shares that have at least one supported hash
+ foreach (Xep.StatelessFileSharing.FileShare file_share in file_shares) {
+ if (!Xep.CryptographicHashes.has_supported_hashes(file_share.metadata.hashes)) {
+ return false;
+ }
+ }
+
+ if (file_shares.size > 0) {
+ var file_transfers = new ArrayList<FileTransfer>();
+ foreach (Xep.StatelessFileSharing.FileShare file_share in file_shares) {
+ var file_transfer = yield create_incoming_file_transfer(conversation, message, file_share.id, file_share.metadata, file_share.sources);
+ file_transfers.add(file_transfer);
+ stream_interactor.get_module(FileTransferStorage.IDENTITY).add_file(file_transfer);
+ }
+ var file_group = new FileTransferGroup() {
+ message_id = message.id,
+ file_transfers = file_transfers
+ };
+ stream_interactor.get_module(FileTransferStorage.IDENTITY).add_file_group(file_group);
+
+ fm2.received_file_group(file_group, conversation);
+ }
+ return true;
+ }
+
+ var source_attachments = Xep.StatelessFileSharing.get_source_attachments(stanza);
+ if (source_attachments != null) {
+ foreach (var source_attachment in source_attachments) {
+ on_received_sources(stanza.from, conversation, source_attachment.to_message_id, source_attachment.to_file_transfer_id, source_attachment.sources);
+ return true;
+ }
+ }
+
+ // Don't process messages that are fallback for legacy clients
+ if (Xep.StatelessFileSharing.is_sfs_fallback_message(stanza)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public async FileTransfer create_incoming_file_transfer(Conversation conversation, Message message, string? file_sharing_id, Xep.FileMetadataElement.FileMetadata metadata, Gee.List<Xep.StatelessFileSharing.Source>? sources) {
FileTransfer file_transfer = new FileTransfer();
file_transfer.file_sharing_id = file_sharing_id;
file_transfer.account = message.account;
@@ -57,7 +136,8 @@ public class Dino.StatelessFileSharing : StreamInteractionModule, Object {
stream_interactor.get_module(FileTransferStorage.IDENTITY).add_file(file_transfer);
conversation.last_active = file_transfer.time;
- file_manager.received_file(file_transfer, conversation);
+
+ return file_transfer;
}
public void on_received_sources(Jid from, Conversation conversation, string attach_to_message_id, string? attach_to_file_id, Gee.List<Xep.StatelessFileSharing.Source> sources) {
@@ -74,9 +154,9 @@ public class Dino.StatelessFileSharing : StreamInteractionModule, Object {
// "If no <hash/> is provided or the <hash/> elements provided use unsupported algorithms, receiving clients MUST ignore
// any attached sources from other senders and only obtain the file from the sources announced by the original sender."
- // For now we only allow the original sender
- if (from.equals(file_transfer.from) && Xep.CryptographicHashes.get_supported_hashes(file_transfer.hashes).is_empty) {
- warning("Ignoring sfs source: Not from original sender or no known file hashes");
+ // For now we don't allow source attachments for file shares without hashes. TODO extend this
+ if (Xep.CryptographicHashes.get_supported_hashes(file_transfer.hashes).is_empty) {
+ warning("Ignoring sfs source: No known file hashes");
return;
}
@@ -84,85 +164,26 @@ public class Dino.StatelessFileSharing : StreamInteractionModule, Object {
file_transfer.add_sfs_source(source);
}
- if (file_manager.is_sender_trustworthy(file_transfer, conversation) && file_transfer.state == FileTransfer.State.NOT_STARTED && file_transfer.size >= 0 && file_transfer.size < 5000000) {
- file_manager.download_file(file_transfer);
+ if (fm2.is_sender_trustworthy(file_transfer, conversation) && file_transfer.state == FileTransfer.State.NOT_STARTED && file_transfer.size >= 0 && file_transfer.size < 5000000) {
+ fm.download_file(file_transfer);
}
}
- /*
- public async void create_sfs_for_legacy_transfer(FileProvider file_provider, string info, Jid from, DateTime time, DateTime local_time, Conversation conversation, FileReceiveData receive_data, FileMeta file_meta) {
- FileTransfer file_transfer = file_manager.create_file_transfer_from_provider_incoming(file_provider, info, from, time, local_time, conversation, receive_data, file_meta);
-
- HttpFileReceiveData? http_receive_data = receive_data as HttpFileReceiveData;
- if (http_receive_data == null) return;
+ private void check_add_sfs_element(Entities.Message message, Xmpp.MessageStanza message_stanza, Conversation conversation) {
+ if (message.encryption != Encryption.NONE) return;
- var sources = new ArrayList<Xep.StatelessFileSharing.Source>();
- Xep.StatelessFileSharing.HttpSource source = new Xep.StatelessFileSharing.HttpSource();
- source.url = http_receive_data.url;
- sources.add(source);
-
- if (file_manager.is_jid_trustworthy(from, conversation)) {
- try {
- file_meta = yield file_provider.get_meta_info(file_transfer, http_receive_data, file_meta);
- } catch (Error e) {
- warning("Http meta request failed: %s", e.message);
+ // Check if it's a FileTransferGroup
+ FileTransferGroup? file_group = stream_interactor.get_module(FileTransferStorage.IDENTITY).get_file_group_by_message_id(message.id, conversation);
+ if (file_group != null) {
+ print("check added file group\n");
+ foreach (FileTransfer file_transfer in file_group.file_transfers) {
+ Xep.StatelessFileSharing.set_sfs_element(message_stanza, file_transfer.file_sharing_id, file_transfer.file_metadata, file_transfer.sfs_sources);
}
- }
- var metadata = new Xep.FileMetadataElement.FileMetadata();
- metadata.size = file_meta.size;
- metadata.name = file_meta.file_name;
- metadata.mime_type = file_meta.mime_type;
+ Xep.MessageProcessingHints.set_message_hint(message_stanza, Xep.MessageProcessingHints.HINT_STORE);
- file_transfer.provider = SFS_PROVIDER_ID;
- file_transfer.file_metadata = metadata;
- file_transfer.sfs_sources = sources;
- }
- */
-
- private class ReceivedMessageListener : MessageListener {
-
- public string[] after_actions_const = new string[]{ "STORE" };
- public override string action_group { get { return "MESSAGE_REINTERPRETING"; } }
- public override string[] after_actions { get { return after_actions_const; } }
-
- private StatelessFileSharing outer;
- private StreamInteractor stream_interactor;
-
- public ReceivedMessageListener(StatelessFileSharing outer) {
- this.outer = outer;
- this.stream_interactor = outer.stream_interactor;
+ return;
}
- public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) {
- Gee.List<Xep.StatelessFileSharing.FileShare> file_shares = Xep.StatelessFileSharing.get_file_shares(stanza);
- if (file_shares != null) {
- // For now, only accept file shares that have at least one supported hash
- foreach (Xep.StatelessFileSharing.FileShare file_share in file_shares) {
- if (!Xep.CryptographicHashes.has_supported_hashes(file_share.metadata.hashes)) {
- return false;
- }
- }
- foreach (Xep.StatelessFileSharing.FileShare file_share in file_shares) {
- outer.create_file_transfer(conversation, message, file_share.id, file_share.metadata, file_share.sources);
- }
- return true;
- }
-
- var source_attachments = Xep.StatelessFileSharing.get_source_attachments(stanza);
- if (source_attachments != null) {
- foreach (var source_attachment in source_attachments) {
- outer.on_received_sources(stanza.from, conversation, source_attachment.to_message_id, source_attachment.to_file_transfer_id, source_attachment.sources);
- return true;
- }
- }
-
- // Don't process messages that are fallback for legacy clients
- if (Xep.StatelessFileSharing.is_sfs_fallback_message(stanza)) {
- return true;
- }
-
- return false;
- }
}
} \ No newline at end of file