aboutsummaryrefslogtreecommitdiff
path: root/libdino/src
diff options
context:
space:
mode:
Diffstat (limited to 'libdino/src')
-rw-r--r--libdino/src/application.vala1
-rw-r--r--libdino/src/entity/file_transfer.vala22
-rw-r--r--libdino/src/service/content_item_store.vala16
-rw-r--r--libdino/src/service/file_manager.vala298
-rw-r--r--libdino/src/service/jingle_file_manager.vala122
-rw-r--r--libdino/src/service/jingle_file_transfers.vala127
6 files changed, 407 insertions, 179 deletions
diff --git a/libdino/src/application.vala b/libdino/src/application.vala
index 396aa91f..da098fb4 100644
--- a/libdino/src/application.vala
+++ b/libdino/src/application.vala
@@ -37,7 +37,6 @@ public interface Dino.Application : GLib.Application {
RosterManager.start(stream_interactor, db);
ChatInteraction.start(stream_interactor);
FileManager.start(stream_interactor, db);
- JingleFileManager.start(stream_interactor);
ContentItemStore.start(stream_interactor, db);
NotificationEvents.start(stream_interactor);
SearchProcessor.start(stream_interactor, db);
diff --git a/libdino/src/entity/file_transfer.vala b/libdino/src/entity/file_transfer.vala
index 68234a48..4e4103b9 100644
--- a/libdino/src/entity/file_transfer.vala
+++ b/libdino/src/entity/file_transfer.vala
@@ -9,7 +9,7 @@ public class FileTransfer : Object {
public enum State {
COMPLETE,
- IN_PROCESS,
+ IN_PROGRESS,
NOT_STARTED,
FAILED
}
@@ -27,7 +27,7 @@ public class FileTransfer : Object {
public bool direction { get; set; }
public DateTime time { get; set; }
public DateTime? local_time { get; set; }
- public Encryption encryption { get; set; }
+ public Encryption encryption { get; set; default=Encryption.NONE; }
private InputStream? input_stream_ = null;
public InputStream input_stream {
@@ -54,9 +54,9 @@ public class FileTransfer : Object {
public string path { get; set; }
public string? mime_type { get; set; }
// TODO(hrxi): expand to 64 bit
- public int size { get; set; }
+ public int size { get; set; default=-1; }
- public State state { get; set; }
+ public State state { get; set; default=State.NOT_STARTED; }
public int provider { get; set; }
public string info { get; set; }
@@ -110,12 +110,15 @@ public class FileTransfer : Object {
.value(db.file_transfer.local_time, (long) local_time.to_unix())
.value(db.file_transfer.encryption, encryption)
.value(db.file_transfer.file_name, file_name)
- .value(db.file_transfer.path, path)
- .value(db.file_transfer.mime_type, mime_type)
.value(db.file_transfer.size, size)
.value(db.file_transfer.state, state)
.value(db.file_transfer.provider, provider)
.value(db.file_transfer.info, info);
+
+ if (file_name != null) builder.value(db.file_transfer.file_name, file_name);
+ if (path != null) builder.value(db.file_transfer.path, path);
+ if (mime_type != null) builder.value(db.file_transfer.mime_type, mime_type);
+
id = (int) builder.perform();
notify.connect(on_update);
}
@@ -142,7 +145,14 @@ public class FileTransfer : Object {
update_builder.set(db.file_transfer.encryption, encryption); break;
case "file-name":
update_builder.set(db.file_transfer.file_name, file_name); break;
+ case "path":
+ update_builder.set(db.file_transfer.path, path); break;
+ case "mime-type":
+ update_builder.set(db.file_transfer.mime_type, mime_type); break;
+ case "size":
+ update_builder.set(db.file_transfer.size, size); break;
case "state":
+ if (state == State.IN_PROGRESS) return;
update_builder.set(db.file_transfer.state, state); break;
case "provider":
update_builder.set(db.file_transfer.provider, provider); break;
diff --git a/libdino/src/service/content_item_store.vala b/libdino/src/service/content_item_store.vala
index 9f39ce59..3800f35d 100644
--- a/libdino/src/service/content_item_store.vala
+++ b/libdino/src/service/content_item_store.vala
@@ -257,18 +257,24 @@ public class FileItem : ContentItem {
public FileItem(FileTransfer file_transfer, int id) {
Jid jid = file_transfer.direction == FileTransfer.DIRECTION_SENT ? file_transfer.account.bare_jid.with_resource(file_transfer.account.resourcepart) : file_transfer.counterpart;
- base(id, TYPE, jid, file_transfer.local_time, file_transfer.time, file_transfer.encryption, file_to_message_state(file_transfer.state));
+ Entities.Message.Marked mark = Entities.Message.Marked.NONE;
+ if (file_transfer.direction == FileTransfer.DIRECTION_SENT) {
+ mark = file_to_message_state(file_transfer.state);
+ }
+ base(id, TYPE, jid, file_transfer.local_time, file_transfer.time, file_transfer.encryption, mark);
this.file_transfer = file_transfer;
- file_transfer.notify["state"].connect_after(() => {
- this.mark = file_to_message_state(file_transfer.state);
- });
+ if (file_transfer.direction == FileTransfer.DIRECTION_SENT) {
+ file_transfer.notify["state"].connect_after(() => {
+ this.mark = file_to_message_state(file_transfer.state);
+ });
+ }
}
private static Entities.Message.Marked file_to_message_state(FileTransfer.State state) {
switch (state) {
- case FileTransfer.State.IN_PROCESS:
+ case FileTransfer.State.IN_PROGRESS:
return Entities.Message.Marked.UNSENT;
case FileTransfer.State.COMPLETE:
return Entities.Message.Marked.NONE;
diff --git a/libdino/src/service/file_manager.vala b/libdino/src/service/file_manager.vala
index 7665936c..50b38f01 100644
--- a/libdino/src/service/file_manager.vala
+++ b/libdino/src/service/file_manager.vala
@@ -16,8 +16,9 @@ public class FileManager : StreamInteractionModule, Object {
private StreamInteractor stream_interactor;
private Database db;
private Gee.List<FileSender> file_senders = new ArrayList<FileSender>();
- public Gee.List<IncomingFileProcessor> incoming_processors = new ArrayList<IncomingFileProcessor>();
- private Gee.List<OutgoingFileProcessor> outgoing_processors = new ArrayList<OutgoingFileProcessor>();
+ private Gee.List<FileEncryptor> file_encryptors = new ArrayList<FileEncryptor>();
+ private Gee.List<FileDecryptor> file_decryptors = new ArrayList<FileDecryptor>();
+ private Gee.List<FileProvider> file_providers = new ArrayList<FileProvider>();
public static void start(StreamInteractor stream_interactor, Database db) {
FileManager m = new FileManager(stream_interactor, db);
@@ -32,6 +33,9 @@ public class FileManager : StreamInteractionModule, Object {
this.stream_interactor = stream_interactor;
this.db = db;
DirUtils.create_with_parents(get_storage_dir(), 0700);
+
+ this.add_provider(new JingleFileProvider(stream_interactor));
+ this.add_sender(new JingleFileSender(stream_interactor));
}
public async void send_file(string uri, Conversation conversation) {
@@ -43,6 +47,7 @@ public class FileManager : StreamInteractionModule, Object {
file_transfer.time = new DateTime.now_utc();
file_transfer.local_time = new DateTime.now_utc();
file_transfer.encryption = conversation.encryption;
+
try {
File file = File.new_for_path(uri);
FileInfo file_info = file.query_info("*", FileQueryInfoFlags.NONE);
@@ -50,26 +55,79 @@ public class FileManager : StreamInteractionModule, Object {
file_transfer.mime_type = file_info.get_content_type();
file_transfer.size = (int)file_info.get_size();
file_transfer.input_stream = yield file.read_async();
+
+ yield save_file(file_transfer);
+
+ file_transfer.persist(db);
+ received_file(file_transfer, conversation);
} catch (Error e) {
file_transfer.state = FileTransfer.State.FAILED;
+ warning("Error saving outgoing file: %s", e.message);
+ return;
}
- yield save_file(file_transfer);
- file_transfer.persist(db);
+ try {
+ var file_meta = new FileMeta();
+ file_meta.size = file_transfer.size;
+ file_meta.mime_type = file_transfer.mime_type;
+
+ bool encrypted = false;
+ foreach (FileEncryptor file_encryptor in file_encryptors) {
+ if (file_encryptor.can_encrypt_file(conversation, file_transfer)) {
+ file_meta = file_encryptor.encrypt_file(conversation, file_transfer);
+ encrypted = true;
+ break;
+ }
+ }
+ if (conversation.encryption != Encryption.NONE && !encrypted) {
+ throw new FileSendError.ENCRYPTION_FAILED("File was not encrypted");
+ }
+
+ FileSendData file_send_data = null;
+ foreach (FileSender file_sender in file_senders) {
+ if (file_sender.can_send(conversation, file_transfer)) {
+ file_send_data = yield file_sender.prepare_send_file(conversation, file_transfer);
+ break;
+ }
+ }
+
+ foreach (FileEncryptor file_encryptor in file_encryptors) {
+ if (file_encryptor.can_encrypt_file(conversation, file_transfer)) {
+ file_send_data = file_encryptor.preprocess_send_file(conversation, file_transfer, file_send_data, file_meta);
+ break;
+ }
+ }
- foreach (OutgoingFileProcessor processor in outgoing_processors) {
- if (processor.can_process(conversation, file_transfer)) {
- processor.process(conversation, file_transfer);
+ bool sent = false;
+ foreach (FileSender file_sender in file_senders) {
+ if (file_sender.can_send(conversation, file_transfer)) {
+ yield file_sender.send_file(conversation, file_transfer, file_send_data);
+ sent = true;
+ break;
+ }
}
+ if (!sent) {
+ throw new FileSendError.UPLOAD_FAILED("File was not sent");
+ }
+
+ conversation.last_active = file_transfer.time;
+ } catch (Error e) {
+ warning("Send file error: %s", e.message);
+ file_transfer.state = FileTransfer.State.FAILED;
}
+ }
- foreach (FileSender file_sender in file_senders) {
- if (file_sender.can_send(conversation, file_transfer)) {
- file_sender.send_file(conversation, file_transfer);
- break;
+ public async void download_file(FileTransfer file_transfer) {
+ Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account);
+
+ FileProvider? file_provider = null;
+ foreach (FileProvider fp in file_providers) {
+ if (file_transfer.provider == fp.get_id()) {
+ file_provider = fp;
}
}
- received_file(file_transfer, conversation);
+
+ yield download_file_internal(file_provider, file_transfer, conversation);
}
public bool is_upload_available(Conversation conversation) {
@@ -117,24 +175,28 @@ public class FileManager : StreamInteractionModule, Object {
}
public void add_provider(FileProvider file_provider) {
- file_provider.file_incoming.connect((file_transfer, conversation) => { handle_incoming_file.begin(file_provider, file_transfer, conversation); });
+ file_providers.add(file_provider);
+ file_provider.file_incoming.connect((info, from, time, local_time, conversation, receive_data, file_meta) => {
+ handle_incoming_file.begin(file_provider, info, from, time, local_time, conversation, receive_data, file_meta);
+ });
}
public void add_sender(FileSender file_sender) {
- // Order file_senders in reverse order of adding them -- HTTP is added
- // later than Jingle.
- file_senders.insert(0, file_sender);
+ file_senders.add(file_sender);
file_sender.upload_available.connect((account) => {
upload_available(account);
});
+ file_senders.sort((a, b) => {
+ return (int) (b.get_priority() - a.get_priority());
+ });
}
- public void add_incoming_processor(IncomingFileProcessor processor) {
- incoming_processors.add(processor);
+ public void add_file_encryptor(FileEncryptor encryptor) {
+ file_encryptors.add(encryptor);
}
- public void add_outgoing_processor(OutgoingFileProcessor processor) {
- outgoing_processors.add(processor);
+ public void add_file_decryptor(FileDecryptor decryptor) {
+ file_decryptors.add(decryptor);
}
public bool is_sender_trustworthy(FileTransfer file_transfer, Conversation conversation) {
@@ -143,29 +205,124 @@ public class FileManager : StreamInteractionModule, Object {
return file_transfer.direction == FileTransfer.DIRECTION_SENT || in_roster;
}
- private async void handle_incoming_file(FileProvider file_provider, FileTransfer file_transfer, Conversation conversation) {
- if (!is_sender_trustworthy(file_transfer, conversation)) return;
+ 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);
+
+ if (file_meta.size == -1) {
+ foreach (FileDecryptor file_decryptor in file_decryptors) {
+ if (file_decryptor.can_decrypt_file(conversation, file_transfer, receive_data)) {
+ receive_data = file_decryptor.prepare_get_meta_info(conversation, file_transfer, receive_data);
+ break;
+ }
+ }
+
+ file_meta = yield file_provider.get_meta_info(file_transfer, receive_data, file_meta);
- if (file_transfer.size == -1) {
- yield file_provider.get_meta_info(file_transfer);
+ file_transfer.size = (int)file_meta.size;
+ file_transfer.file_name = file_meta.file_name;
+ file_transfer.mime_type = file_meta.mime_type;
}
+ return file_meta;
+ }
- if (file_transfer.size >= 0 && file_transfer.size < 5000000) {
- string filename = Random.next_int().to_string("%x") + "_" + file_transfer.file_name;
+ private async void download_file_internal(FileProvider file_provider, FileTransfer file_transfer, Conversation conversation) {
+ try {
+ // Get meta info
+ FileReceiveData receive_data = file_provider.get_file_receive_data(file_transfer);
+ foreach (FileDecryptor file_decryptor in file_decryptors) {
+ if (file_decryptor.can_decrypt_file(conversation, file_transfer, receive_data)) {
+ receive_data = file_decryptor.prepare_get_meta_info(conversation, file_transfer, receive_data);
+ break;
+ }
+ }
+ FileMeta file_meta = yield get_file_meta(file_provider, file_transfer, conversation, receive_data);
+
+
+ InputStream? input_stream = null;
+
+ // Download and decrypt file
+ file_transfer.state = FileTransfer.State.IN_PROGRESS;
+
+ foreach (FileDecryptor file_decryptor in file_decryptors) {
+ if (file_decryptor.can_decrypt_file(conversation, file_transfer, receive_data)) {
+ file_meta = file_decryptor.prepare_download_file(conversation, file_transfer, receive_data, file_meta);
+ break;
+ }
+ }
+
+ input_stream = yield file_provider.download(file_transfer, receive_data, file_meta);
+
+ foreach (FileDecryptor file_decryptor in file_decryptors) {
+ if (file_decryptor.can_decrypt_file(conversation, file_transfer, receive_data)) {
+ input_stream = yield file_decryptor.decrypt_file(input_stream, conversation, file_transfer, receive_data);
+ break;
+ }
+ }
+
+ // Save file
+ string filename = Random.next_int().to_string("%x") + "_" + file_meta.file_name;
File file = File.new_for_path(Path.build_filename(get_storage_dir(), filename));
- yield file_provider.download(file_transfer, file);
- try {
- FileInfo file_info = file_transfer.get_file().query_info("*", FileQueryInfoFlags.NONE);
- file_transfer.mime_type = file_info.get_content_type();
- } catch (Error e) { }
+ if (file_transfer.encryption == Encryption.PGP || file.get_path().has_suffix(".pgp")) {
+ file = File.new_for_path(file.get_path().substring(0, file.get_path().length - 4));
+ }
- file_transfer.persist(db);
- received_file(file_transfer, conversation);
+ OutputStream os = file.create(FileCreateFlags.REPLACE_DESTINATION);
+ yield os.splice_async(input_stream, 0);
+ os.close();
+ file_transfer.size = (int)file_meta.size;
+ file_transfer.file_name = file_meta.file_name;
+ file_transfer.path = file.get_basename();
+ file_transfer.input_stream = yield file.read_async();
+
+ FileInfo file_info = file_transfer.get_file().query_info("*", FileQueryInfoFlags.NONE);
+ file_transfer.mime_type = file_info.get_content_type();
+
+ file_transfer.state = FileTransfer.State.COMPLETE;
+ } catch (Error e) {
+ warning("Error downloading file: %s", e.message);
+ file_transfer.state = FileTransfer.State.FAILED;
}
}
- private async void save_file(FileTransfer file_transfer) {
+ private async void handle_incoming_file(FileProvider file_provider, string info, Jid from, DateTime time, DateTime local_time, Conversation conversation, FileReceiveData receive_data, FileMeta file_meta) {
+ FileTransfer file_transfer = new FileTransfer();
+ file_transfer.account = conversation.account;
+ file_transfer.direction = from.bare_jid.equals(conversation.account.bare_jid) ? FileTransfer.DIRECTION_SENT : FileTransfer.DIRECTION_RECEIVED;
+ file_transfer.counterpart = file_transfer.direction == FileTransfer.DIRECTION_RECEIVED ? from : conversation.counterpart;
+ if (conversation.type_ in new Conversation.Type[]{Conversation.Type.GROUPCHAT, Conversation.Type.GROUPCHAT_PM}) {
+ file_transfer.ourpart = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account) ?? conversation.account.bare_jid;
+ } else {
+ file_transfer.ourpart = conversation.account.bare_jid.with_resource(conversation.account.resourcepart);
+ }
+ file_transfer.time = time;
+ file_transfer.local_time = local_time;
+ file_transfer.provider = file_provider.get_id();
+ file_transfer.file_name = file_meta.file_name;
+ file_transfer.size = (int)file_meta.size;
+ file_transfer.info = info;
+
+ file_transfer.persist(db);
+
+ if (is_sender_trustworthy(file_transfer, conversation)) {
+ try {
+ yield get_file_meta(file_provider, file_transfer, conversation, receive_data);
+
+ if (file_transfer.size >= 0 && file_transfer.size < 5000000) {
+ yield download_file_internal(file_provider, file_transfer, conversation);
+ }
+ } catch (Error e) {
+ warning("Error downloading file: %s", e.message);
+ file_transfer.state = FileTransfer.State.FAILED;
+ }
+ }
+
+ conversation.last_active = file_transfer.time;
+ 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));
@@ -176,33 +333,84 @@ public class FileManager : StreamInteractionModule, Object {
file_transfer.path = filename;
file_transfer.input_stream = yield file.read_async();
} catch (Error e) {
- file_transfer.state = FileTransfer.State.FAILED;
+ throw new FileSendError.SAVE_FAILED("Saving file error: %s".printf(e.message));
}
}
+}
+public errordomain FileSendError {
+ ENCRYPTION_FAILED,
+ UPLOAD_FAILED,
+ SAVE_FAILED
+}
+
+public errordomain FileReceiveError {
+ GET_METADATA_FAILED,
+ DECRYPTION_FAILED,
+ DOWNLOAD_FAILED
+}
+
+public class FileMeta {
+ public int64 size = -1;
+ public string? mime_type = null;
+ public string? file_name = null;
+ public Encryption encryption = Encryption.NONE;
+}
+
+public class HttpFileMeta : FileMeta {
+ public Message message;
+}
+
+public class FileSendData { }
+
+public class HttpFileSendData : FileSendData {
+ public string url_down { get; set; }
+ public string url_up { get; set; }
+
+ public bool encrypt_message { get; set; default=true; }
+}
+
+public class FileReceiveData { }
+
+public class HttpFileReceiveData : FileReceiveData {
+ public string url { get; set; }
}
public interface FileProvider : Object {
- public signal void file_incoming(FileTransfer file_transfer, Conversation conversation);
- public abstract async void get_meta_info(FileTransfer file_transfer);
- public abstract async void download(FileTransfer file_transfer, File file);
+ public signal void file_incoming(string info, Jid from, DateTime time, DateTime local_time, Conversation conversation, FileReceiveData receive_data, FileMeta file_meta);
+
+ public abstract FileMeta get_file_meta(FileTransfer file_transfer) throws FileReceiveError;
+ public abstract FileReceiveData? get_file_receive_data(FileTransfer file_transfer);
+
+ public abstract async FileMeta get_meta_info(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError;
+ public abstract async InputStream download(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError;
+
+ public abstract int get_id();
}
public interface FileSender : Object {
public signal void upload_available(Account account);
+
public abstract bool is_upload_available(Conversation conversation);
public abstract bool can_send(Conversation conversation, FileTransfer file_transfer);
- public abstract void send_file(Conversation conversation, FileTransfer file_transfer);
+ public abstract async FileSendData? prepare_send_file(Conversation conversation, FileTransfer file_transfer) throws FileSendError;
+ public abstract async void send_file(Conversation conversation, FileTransfer file_transfer, FileSendData file_send_data) throws FileSendError;
+
+ public abstract int get_id();
+ public abstract float get_priority();
}
-public interface IncomingFileProcessor : Object {
- public abstract bool can_process(FileTransfer file_transfer);
- public abstract void process(FileTransfer file_transfer);
+public interface FileEncryptor : Object {
+ public abstract bool can_encrypt_file(Conversation conversation, FileTransfer file_transfer);
+ public abstract FileMeta encrypt_file(Conversation conversation, FileTransfer file_transfer) throws FileSendError;
+ public abstract FileSendData? preprocess_send_file(Conversation conversation, FileTransfer file_transfer, FileSendData file_send_data, FileMeta file_meta) throws FileSendError;
}
-public interface OutgoingFileProcessor : Object {
- public abstract bool can_process(Conversation conversation, FileTransfer file_transfer);
- public abstract void process(Conversation conversation, FileTransfer file_transfer);
+public interface FileDecryptor : Object {
+ public abstract FileReceiveData prepare_get_meta_info(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data);
+ public abstract FileMeta prepare_download_file(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta);
+ public abstract bool can_decrypt_file(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data);
+ public abstract async InputStream decrypt_file(InputStream encrypted_stream, Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data) throws FileReceiveError;
}
}
diff --git a/libdino/src/service/jingle_file_manager.vala b/libdino/src/service/jingle_file_manager.vala
deleted file mode 100644
index 595afae0..00000000
--- a/libdino/src/service/jingle_file_manager.vala
+++ /dev/null
@@ -1,122 +0,0 @@
-using Gdk;
-using Gee;
-
-using Xmpp;
-using Dino.Entities;
-
-namespace Dino {
-
-public class JingleFileManager : StreamInteractionModule, FileProvider, FileSender, Object {
- public static ModuleIdentity<JingleFileManager> IDENTITY = new ModuleIdentity<JingleFileManager>("jingle_files");
- public string id { get { return IDENTITY.id; } }
-
- private StreamInteractor stream_interactor;
- private HashMap<string, Xmpp.Xep.JingleFileTransfer.FileTransfer> file_transfers
- = new HashMap<string, Xmpp.Xep.JingleFileTransfer.FileTransfer>();
-
- public static void start(StreamInteractor stream_interactor) {
- JingleFileManager m = new JingleFileManager(stream_interactor);
- stream_interactor.add_module(m);
- }
-
- private JingleFileManager(StreamInteractor stream_interactor) {
- this.stream_interactor = stream_interactor;
-
- stream_interactor.get_module(FileManager.IDENTITY).add_sender(this);
- stream_interactor.get_module(FileManager.IDENTITY).add_provider(this);
- stream_interactor.stream_negotiated.connect(on_stream_negotiated);
- }
-
- private void on_stream_negotiated(Account account, XmppStream stream) {
- stream_interactor.module_manager.get_module(account, Xmpp.Xep.JingleFileTransfer.Module.IDENTITY).file_incoming.connect((stream, jingle_file_transfer) => {
- Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jingle_file_transfer.peer.bare_jid, account);
- if (conversation == null) {
- // TODO(hrxi): What to do?
- return;
- }
- string id = random_uuid();
-
- FileTransfer file_transfer = new FileTransfer();
- file_transfer.account = account;
- file_transfer.counterpart = jingle_file_transfer.peer.bare_jid;
- file_transfer.ourpart = account.bare_jid;
- file_transfer.encryption = Encryption.NONE;
- file_transfer.time = new DateTime.now_utc();
- file_transfer.local_time = new DateTime.now_utc();
- file_transfer.direction = FileTransfer.DIRECTION_RECEIVED;
- file_transfer.file_name = jingle_file_transfer.file_name;
- file_transfer.size = (int)jingle_file_transfer.size;
- file_transfer.state = FileTransfer.State.NOT_STARTED;
- file_transfer.provider = 1;
- file_transfer.info = id;
- file_transfers[id] = jingle_file_transfer;
-
- file_incoming(file_transfer, conversation);
- });
- }
-
- async void get_meta_info(FileTransfer file_transfer) {
- // In Jingle, all the metadata is provided up-front, so there's no more
- // metadata to get.
- }
- async void download(FileTransfer file_transfer, File file_) {
- // TODO(hrxi) What should happen if `stream == null`?
- XmppStream? stream = stream_interactor.get_stream(file_transfer.account);
- Xmpp.Xep.JingleFileTransfer.FileTransfer jingle_file_transfer = file_transfers[file_transfer.info];
- jingle_file_transfer.accept(stream);
- file_transfer.input_stream = jingle_file_transfer.stream;
-
- // TODO(hrxi): BEGIN: Copied from plugins/http-files/src/file_provider.vala
- foreach (IncomingFileProcessor processor in stream_interactor.get_module(FileManager.IDENTITY).incoming_processors) {
- if (processor.can_process(file_transfer)) {
- processor.process(file_transfer);
- }
- }
-
- // TODO(hrxi): should this be an &&?
- File file = file_;
- if (file_transfer.encryption == Encryption.PGP || file.get_path().has_suffix(".pgp")) {
- file = File.new_for_path(file.get_path().substring(0, file.get_path().length - 4));
- }
- // TODO(hrxi): END: Copied from plugins/http-files/src/file_provider.vala
-
- try {
- OutputStream os = file.create(FileCreateFlags.REPLACE_DESTINATION);
- yield os.splice_async(file_transfer.input_stream, OutputStreamSpliceFlags.CLOSE_SOURCE|OutputStreamSpliceFlags.CLOSE_TARGET);
- file_transfer.path = file.get_basename();
- file_transfer.input_stream = yield file.read_async();
-
- file_transfer.state = FileTransfer.State.COMPLETE;
- } catch (Error e) {
- file_transfer.state = FileTransfer.State.FAILED;
- return;
- }
- }
-
- public bool is_upload_available(Conversation conversation) {
- // TODO(hrxi) Here and in `send_file`: What should happen if `stream == null`?
- XmppStream? stream = stream_interactor.get_stream(conversation.account);
- foreach (Jid full_jid in stream.get_flag(Presence.Flag.IDENTITY).get_resources(conversation.counterpart)) {
- if (stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).is_available(stream, full_jid)) {
- return true;
- }
- }
- return false;
- }
- public bool can_send(Conversation conversation, FileTransfer file_transfer) {
- return file_transfer.encryption != Encryption.OMEMO;
- }
- public void send_file(Conversation conversation, FileTransfer file_transfer) {
- XmppStream? stream = stream_interactor.get_stream(file_transfer.account);
- foreach (Jid full_jid in stream.get_flag(Presence.Flag.IDENTITY).get_resources(conversation.counterpart)) {
- // TODO(hrxi): Prioritization of transports (and resources?).
- if (!stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).is_available(stream, full_jid)) {
- continue;
- }
- stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).offer_file_stream.begin(stream, full_jid, file_transfer.input_stream, file_transfer.file_name, file_transfer.size);
- return;
- }
- }
-}
-
-}
diff --git a/libdino/src/service/jingle_file_transfers.vala b/libdino/src/service/jingle_file_transfers.vala
new file mode 100644
index 00000000..c90986d2
--- /dev/null
+++ b/libdino/src/service/jingle_file_transfers.vala
@@ -0,0 +1,127 @@
+using Gdk;
+using Gee;
+
+using Xmpp;
+using Dino.Entities;
+
+namespace Dino {
+
+public class JingleFileProvider : FileProvider, Object {
+
+ private StreamInteractor stream_interactor;
+ private HashMap<string, Xmpp.Xep.JingleFileTransfer.FileTransfer> file_transfers = new HashMap<string, Xmpp.Xep.JingleFileTransfer.FileTransfer>();
+
+ public JingleFileProvider(StreamInteractor stream_interactor) {
+ this.stream_interactor = stream_interactor;
+
+ stream_interactor.stream_negotiated.connect(on_stream_negotiated);
+ }
+
+ public FileMeta get_file_meta(FileTransfer file_transfer) throws FileReceiveError {
+ var file_meta = new FileMeta();
+ file_meta.file_name = file_transfer.file_name;
+ file_meta.size = file_transfer.size;
+ return file_meta;
+ }
+
+ public FileReceiveData? get_file_receive_data(FileTransfer file_transfer) {
+ return new FileReceiveData();
+ }
+
+ public async FileMeta get_meta_info(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError {
+ return file_meta;
+ }
+
+ public async InputStream download(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError {
+ // TODO(hrxi) What should happen if `stream == null`?
+ XmppStream? stream = stream_interactor.get_stream(file_transfer.account);
+ Xmpp.Xep.JingleFileTransfer.FileTransfer? jingle_file_transfer = file_transfers[file_transfer.info];
+ if (jingle_file_transfer == null) {
+ throw new FileReceiveError.DOWNLOAD_FAILED("Transfer data not available anymore");
+ }
+ jingle_file_transfer.accept(stream);
+ return jingle_file_transfer.stream;
+ }
+
+ public int get_id() {
+ return 1;
+ }
+
+ private void on_stream_negotiated(Account account, XmppStream stream) {
+ stream_interactor.module_manager.get_module(account, Xmpp.Xep.JingleFileTransfer.Module.IDENTITY).file_incoming.connect((stream, jingle_file_transfer) => {
+ Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jingle_file_transfer.peer.bare_jid, account);
+ if (conversation == null) {
+ // TODO(hrxi): What to do?
+ return;
+ }
+ string id = random_uuid();
+
+ file_transfers[id] = jingle_file_transfer;
+
+ FileMeta file_meta = new FileMeta();
+ file_meta.size = jingle_file_transfer.size;
+ file_meta.file_name = jingle_file_transfer.file_name;
+
+ var time = new DateTime.now_utc();
+ var from = jingle_file_transfer.peer.bare_jid;
+
+ file_incoming(id, from, time, time, conversation, new FileReceiveData(), file_meta);
+ });
+ }
+}
+
+public class JingleFileSender : FileSender, Object {
+
+ private StreamInteractor stream_interactor;
+
+ public JingleFileSender(StreamInteractor stream_interactor) {
+ this.stream_interactor = stream_interactor;
+ }
+
+ public bool is_upload_available(Conversation conversation) {
+ XmppStream? stream = stream_interactor.get_stream(conversation.account);
+ if (stream == null) return false;
+
+ foreach (Jid full_jid in stream.get_flag(Presence.Flag.IDENTITY).get_resources(conversation.counterpart)) {
+ if (stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).is_available(stream, full_jid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public bool can_send(Conversation conversation, FileTransfer file_transfer) {
+ XmppStream? stream = stream_interactor.get_stream(file_transfer.account);
+ if (stream == null) return false;
+
+ foreach (Jid full_jid in stream.get_flag(Presence.Flag.IDENTITY).get_resources(conversation.counterpart)) {
+ if (stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).is_available(stream, full_jid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public async FileSendData? prepare_send_file(Conversation conversation, FileTransfer file_transfer) throws FileSendError {
+ return new FileSendData();
+ }
+
+ public async void send_file(Conversation conversation, FileTransfer file_transfer, FileSendData file_send_data) throws FileSendError {
+ // TODO(hrxi) What should happen if `stream == null`?
+ XmppStream? stream = stream_interactor.get_stream(file_transfer.account);
+ foreach (Jid full_jid in stream.get_flag(Presence.Flag.IDENTITY).get_resources(conversation.counterpart)) {
+ // TODO(hrxi): Prioritization of transports (and resources?).
+ if (!stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).is_available(stream, full_jid)) {
+ continue;
+ }
+ stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).offer_file_stream.begin(stream, full_jid, file_transfer.input_stream, file_transfer.file_name, file_transfer.size);
+ return;
+ }
+ }
+
+ public int get_id() { return 1; }
+
+ public float get_priority() { return 50; }
+}
+
+}