diff options
| author | fiaxh <git@lightrise.org> | 2026-08-14 11:45:08 +0200 |
|---|---|---|
| committer | fiaxh <git@lightrise.org> | 2026-08-21 18:18:03 +0200 |
| commit | 8d49d83b3b45ab22d7b9d945c4b32296b07cb49e (patch) | |
| tree | 5a0bf3d777c4efd806388163fc9dc74c87edd07b /libdino/src/service | |
| parent | 6d0706bcaa6d54354e416174e3d2654a2bff81fe (diff) | |
Add initial support for multi-file transfers
Diffstat (limited to 'libdino/src/service')
| -rw-r--r-- | libdino/src/service/content_item_store.vala | 34 | ||||
| -rw-r--r-- | libdino/src/service/database.vala | 30 | ||||
| -rw-r--r-- | libdino/src/service/file_manager.vala | 44 | ||||
| -rw-r--r-- | libdino/src/service/file_transfer_storage.vala | 57 | ||||
| -rw-r--r-- | libdino/src/service/stateless_file_sharing.vala | 177 |
5 files changed, 226 insertions, 116 deletions
diff --git a/libdino/src/service/content_item_store.vala b/libdino/src/service/content_item_store.vala index d74f9b05..cf4c6975 100644 --- a/libdino/src/service/content_item_store.vala +++ b/libdino/src/service/content_item_store.vala @@ -26,6 +26,8 @@ public class ContentItemStore : StreamInteractionModule, Object { this.db = db; stream_interactor.get_module(FileManager.IDENTITY).received_file.connect(insert_file_transfer); + stream_interactor.get_module(FileManager2.IDENTITY).received_file.connect(insert_file_transfer); + stream_interactor.get_module(FileManager2.IDENTITY).received_file_group.connect(insert_file_group); stream_interactor.get_module(MessageProcessor.IDENTITY).message_received.connect(announce_message); stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent.connect(announce_message); stream_interactor.get_module(Calls.IDENTITY).call_incoming.connect(insert_call); @@ -88,6 +90,13 @@ public class ContentItemStore : StreamInteractionModule, Object { return call_item; } break; + case 4: + FileTransferGroup? file_group = stream_interactor.get_module(FileTransferStorage.IDENTITY).get_file_group_by_id(foreign_id, conversation); + if (file_group != null) { + var file_group_item = new FileGroupItem(file_group, conversation, id); + return file_group_item; + } + break; default: warning("Unknown content item type: %i", content_type); break; @@ -300,6 +309,16 @@ public class ContentItemStore : StreamInteractionModule, Object { new_item(item, conversation); } + private void insert_file_group(FileTransferGroup file_group, Conversation conversation) { + FileGroupItem item = new FileGroupItem(file_group, conversation, file_group.id); + // TODO fix local time + item.id = db.add_content_item(conversation, item.time, item.time, 4, file_group.id, false); + if (collection_conversations.has_key(conversation)) { + collection_conversations.get(conversation).insert_item(item); + } + new_item(item, conversation); + } + private void insert_call(Call call, CallState call_state, Conversation conversation) { CallItem item = new CallItem(call, conversation, -1); item.id = db.add_content_item(conversation, call.time, call.local_time, 3, call.id, false); @@ -415,6 +434,21 @@ public class FileItem : ContentItem { } } +public class FileGroupItem : ContentItem { + public const string TYPE = "file_group"; + + public FileTransferGroup file_group; + public Conversation conversation; + + public FileGroupItem(FileTransferGroup file_group, Conversation conversation, int id) { + var file_transfer = file_group.file_transfers.get(0); + base(id, TYPE, file_transfer.from, file_transfer.time, file_transfer.encryption, Message.Marked.NONE); + + this.file_group = file_group; + this.conversation = conversation; + } +} + public class CallItem : ContentItem { public const string TYPE = "call"; diff --git a/libdino/src/service/database.vala b/libdino/src/service/database.vala index b38ccfca..8432ff40 100644 --- a/libdino/src/service/database.vala +++ b/libdino/src/service/database.vala @@ -7,7 +7,7 @@ using Dino.Entities; namespace Dino { public class Database : Qlite.Database { - private const int VERSION = 33; + private const int VERSION = 34; public class AccountTable : Table { public Column<int> id = new Column.Integer("id") { primary_key = true, auto_increment = true }; @@ -225,6 +225,28 @@ public class Database : Qlite.Database { } } + public class FileTransferGroupTable : Table { + public Column<int> id = new Column.Integer("id") { primary_key = true, auto_increment = true }; + public Column<int> message_id = new Column.Integer("message_id"); + + internal FileTransferGroupTable(Database db) { + base(db, "file_transfer_group"); + init({id, message_id}); + index("file_transfer_group_message_id_idx", {message_id}); + } + } + + public class FileTransferGroupFileTransferTable : Table { + public Column<int> file_transfer_group_id = new Column.Integer("file_transfer_group_id"); + public Column<int> file_transfer_id = new Column.Integer("file_transfer_id"); + + internal FileTransferGroupFileTransferTable(Database db) { + base(db, "file_transfer_group_file_transfer"); + init({file_transfer_group_id, file_transfer_id}); + index("file_transfer_group_file_transfer_id_idx", {file_transfer_group_id}); + } + } + public class FileHashesTable : Table { public Column<int> id = new Column.Integer("id"); public Column<string> algo = new Column.Text("algo") { not_null = true }; @@ -489,6 +511,8 @@ public class Database : Qlite.Database { public RealJidTable real_jid { get; private set; } public OccupantIdTable occupantid { get; private set; } public FileTransferTable file_transfer { get; private set; } + public FileTransferGroupTable file_transfer_group { get; private set; } + public FileTransferGroupFileTransferTable file_transfer_group_file_transfer { get; private set; } public FileHashesTable file_hashes { get; private set; } public FileThumbnailsTable file_thumbnails { get; private set; } public SourcesTable sfs_sources { get; private set; } @@ -525,6 +549,8 @@ public class Database : Qlite.Database { occupantid = new OccupantIdTable(this); real_jid = new RealJidTable(this); file_transfer = new FileTransferTable(this); + file_transfer_group = new FileTransferGroupTable(this); + file_transfer_group_file_transfer = new FileTransferGroupFileTransferTable(this); file_hashes = new FileHashesTable(this); file_thumbnails = new FileThumbnailsTable(this); sfs_sources = new SourcesTable(this); @@ -542,7 +568,7 @@ public class Database : Qlite.Database { settings = new SettingsTable(this); account_settings = new AccountSettingsTable(this); conversation_settings = new ConversationSettingsTable(this); - init({ account, jid, entity, content_item, message, message_occupant_id, body_meta, message_correction, reply, real_jid, occupantid, file_transfer, file_hashes, file_thumbnails, sfs_sources, call, call_counterpart, conversation, avatar, entity_identity, entity_feature, roster, mam_catchup, reaction, muc_affiliation, muc_affiliation_version, settings, account_settings, conversation_settings }); + init({ account, jid, entity, content_item, message, message_occupant_id, body_meta, message_correction, reply, real_jid, occupantid, file_transfer, file_transfer_group, file_transfer_group_file_transfer, file_hashes, file_thumbnails, sfs_sources, call, call_counterpart, conversation, avatar, entity_identity, entity_feature, roster, mam_catchup, reaction, muc_affiliation, muc_affiliation_version, settings, account_settings, conversation_settings }); try { exec("PRAGMA journal_mode = WAL"); diff --git a/libdino/src/service/file_manager.vala b/libdino/src/service/file_manager.vala index 964278a5..60b7fafa 100644 --- a/libdino/src/service/file_manager.vala +++ b/libdino/src/service/file_manager.vala @@ -16,17 +16,22 @@ public class FileManager : StreamInteractionModule, Object { private StreamInteractor stream_interactor; private Database db; - private Gee.List<FileSender> file_senders = new ArrayList<FileSender>(); + public Gee.List<FileSender> file_senders = new ArrayList<FileSender>(); private Gee.List<FileEncryptor> file_encryptors = new ArrayList<FileEncryptor>(); - private Gee.List<FileDecryptor> file_decryptors = new ArrayList<FileDecryptor>(); + public Gee.List<FileDecryptor> file_decryptors = new ArrayList<FileDecryptor>(); private Gee.List<FileProvider> file_providers = new ArrayList<FileProvider>(); - private Gee.List<FileMetadataProvider> file_metadata_providers = new ArrayList<FileMetadataProvider>(); + public Gee.List<FileMetadataProvider> file_metadata_providers = new ArrayList<FileMetadataProvider>(); public StatelessFileSharing sfs { owned get { return stream_interactor.get_module(StatelessFileSharing.IDENTITY); } private set { } } + public FileManager2 fm2 { + owned get { return stream_interactor.get_module(FileManager2.IDENTITY); } + private set { } + } + public static void start(StreamInteractor stream_interactor, Database db) { FileManager m = new FileManager(stream_interactor, db); stream_interactor.add_module(m); @@ -97,7 +102,7 @@ public class FileManager : StreamInteractionModule, Object { try { file_transfer.input_stream = yield file.read_async(); - yield save_file(file_transfer); + yield fm2.save_file(file_transfer); stream_interactor.get_module(FileTransferStorage.IDENTITY).add_file(file_transfer); conversation.last_active = file_transfer.time; @@ -151,7 +156,7 @@ public class FileManager : StreamInteractionModule, Object { file_transfer.state = FileTransfer.State.IN_PROGRESS; - // Update current download progress in the FileTransfer + // Update current upload progress in the FileTransfer LimitInputStream? limit_stream = file_transfer.input_stream as LimitInputStream; if (limit_stream == null) { limit_stream = new LimitInputStream(file_transfer.input_stream, file_meta.size); @@ -216,19 +221,6 @@ public class FileManager : StreamInteractionModule, Object { file_metadata_providers.add(file_metadata_provider); } - public bool is_sender_trustworthy(FileTransfer file_transfer, Conversation conversation) { - if (file_transfer.direction == FileTransfer.DIRECTION_SENT) return true; - - Jid relevant_jid = conversation.counterpart; - if (conversation.type_ == Conversation.Type.GROUPCHAT) { - relevant_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(file_transfer.from, conversation.account); - } - if (relevant_jid == null) return false; - - bool in_roster = stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(conversation.account, relevant_jid) != null; - return in_roster; - } - private async FileMeta get_file_meta(FileProvider file_provider, FileTransfer file_transfer, Conversation conversation, FileReceiveData receive_data_) throws FileReceiveError { FileReceiveData receive_data = receive_data_; FileMeta file_meta = file_provider.get_file_meta(file_transfer); @@ -390,7 +382,7 @@ public class FileManager : StreamInteractionModule, Object { FileTransfer file_transfer = create_file_transfer_from_provider_incoming(file_provider, info, from, time, local_time, conversation, receive_data, file_meta); stream_interactor.get_module(FileTransferStorage.IDENTITY).add_file(file_transfer); - if (is_sender_trustworthy(file_transfer, conversation)) { + if (fm2.is_sender_trustworthy(file_transfer, conversation)) { try { yield get_file_meta(file_provider, file_transfer, conversation, receive_data); } catch (Error e) { @@ -408,20 +400,6 @@ public class FileManager : StreamInteractionModule, Object { received_file(file_transfer, conversation); } - private async void save_file(FileTransfer file_transfer) throws FileSendError { - try { - string filename = Random.next_int().to_string("%x") + "_" + file_transfer.file_name; - File file = File.new_for_path(Path.build_filename(get_storage_dir(), filename)); - OutputStream os = file.create(FileCreateFlags.REPLACE_DESTINATION); - yield os.splice_async(file_transfer.input_stream, OutputStreamSpliceFlags.CLOSE_SOURCE | OutputStreamSpliceFlags.CLOSE_TARGET); - file_transfer.state = FileTransfer.State.COMPLETE; - file_transfer.path = filename; - file_transfer.input_stream = new LimitInputStream(yield file.read_async(), file_transfer.size); - } catch (Error e) { - throw new FileSendError.SAVE_FAILED("Saving file error: %s".printf(e.message)); - } - } - public Message? get_message_for_file_transfer(FileTransfer file_transfer, Conversation conversation) { if ((file_transfer.provider == 0 || file_transfer.provider == 2) && file_transfer.info != null) { return stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(int.parse(file_transfer.info), conversation); diff --git a/libdino/src/service/file_transfer_storage.vala b/libdino/src/service/file_transfer_storage.vala index 64bb6b81..b4a4db3e 100644 --- a/libdino/src/service/file_transfer_storage.vala +++ b/libdino/src/service/file_transfer_storage.vala @@ -15,6 +15,8 @@ namespace Dino { private WeakMap<int, FileTransfer> files_by_db_id = new WeakMap<int, FileTransfer>(); private WeakMap<int, FileTransfer> files_by_message_id = new WeakMap<int, FileTransfer>(); + private WeakMap<int, FileTransferGroup> file_group_by_db_id = new WeakMap<int, FileTransferGroup>(); + private WeakMap<int, FileTransferGroup> file_group_by_message_id = new WeakMap<int, FileTransferGroup>(); private WeakMap<string, FileTransfer> files_by_message_and_file_id = new WeakMap<string, FileTransfer>(); public static void start(StreamInteractor stream_interactor, Database db) { @@ -32,7 +34,13 @@ namespace Dino { cache_file(file_transfer); } - public FileTransfer? get_file_by_id(int id, Conversation conversation) { + public void add_file_group(FileTransferGroup file_group) { + file_group.persist(db); + cache_file_group(file_group); + } + + // `conversation` should be provided, otherwise the FileTransfer.ourpart might be wrong + public FileTransfer? get_file_by_id(int id, Conversation? conversation) { FileTransfer? file_transfer = files_by_db_id[id]; if (file_transfer != null) { return file_transfer; @@ -58,6 +66,29 @@ namespace Dino { return create_file_from_row_opt(row_option, conversation); } + public FileTransferGroup? get_file_group_by_id(int id, Conversation conversation) { + FileTransferGroup? file_group = file_group_by_db_id[id]; + if (file_group != null) { + return file_group; + } + + RowOption row_option = db.file_transfer_group.select().with(db.file_transfer_group.id, "=", id).row(); + return row_option.is_present() ? create_file_group_from_row(row_option.inner, conversation) : null; + } + + public FileTransferGroup get_file_group_by_message_id(int id, Conversation conversation) { + FileTransferGroup? file_group = file_group_by_message_id[id]; + if (file_group != null) { + return file_group; + } + + foreach (Row row in db.file_transfer_group.select().with(db.file_transfer_group.message_id, "=", id)) { + return create_file_group_from_row(row, conversation); + } + + return null; + } + public FileTransfer get_files_by_message_and_file_id(int message_id, string file_sharing_id, Conversation conversation) { string combined_identifier = message_id.to_string() + file_sharing_id; FileTransfer? file_transfer = files_by_message_and_file_id[combined_identifier]; @@ -79,13 +110,13 @@ namespace Dino { return null; } - private FileTransfer? create_file_from_row_opt(RowOption row_opt, Conversation conversation) { + private FileTransfer? create_file_from_row_opt(RowOption row_opt, Conversation? conversation) { if (!row_opt.is_present()) return null; try { FileTransfer file_transfer = new FileTransfer.from_row(db, row_opt.inner, FileManager.get_storage_dir()); - if (conversation.type_.is_muc_semantic()) { + if (conversation != null && conversation.type_.is_muc_semantic()) { file_transfer.ourpart = conversation.counterpart.with_resource(file_transfer.ourpart.resourcepart); } @@ -97,6 +128,21 @@ namespace Dino { return null; } + private FileTransferGroup? create_file_group_from_row(Row file_transfer_group_row, Conversation conversation) { + QueryBuilder query = db.file_transfer_group_file_transfer.select() + .with(db.file_transfer_group_file_transfer.file_transfer_group_id, "=", file_transfer_group_row[db.file_transfer_group.id]); + var file_transfers = new ArrayList<FileTransfer>(); + foreach (Qlite.Row row in query) { + FileTransfer file_transfer = get_file_by_id(row[db.file_transfer_group_file_transfer.file_transfer_id], conversation); + file_transfers.add(file_transfer); + } + if (file_transfers.size == 0) return null; + + FileTransferGroup file_group = new FileTransferGroup.from_row(db, file_transfer_group_row, file_transfers); + cache_file_group(file_group); + return file_group; + } + private void cache_file(FileTransfer file_transfer) { files_by_db_id[file_transfer.id] = file_transfer; @@ -109,5 +155,10 @@ namespace Dino { } } } + + private void cache_file_group(FileTransferGroup file_group) { + file_group_by_db_id[file_group.id] = file_group; +// file_group_by_message_id[file_group.message_id] = file_group; + } } }
\ No newline at end of file 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 |
