From 09f92476e0ab91a8767c051c52dc0d6d5c6f4f27 Mon Sep 17 00:00:00 2001 From: Maciek Borzecki Date: Wed, 11 Oct 2017 23:42:17 +0200 Subject: [PATCH] transfer-upload: device upload wrapper --- meson.build | 1 + src/mconnect/transfer-upload.vala | 184 ++++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 src/mconnect/transfer-upload.vala diff --git a/meson.build b/meson.build index 4328731..fff5416 100644 --- a/meson.build +++ b/meson.build @@ -54,6 +54,7 @@ mconnect_src = [ 'src/mconnect/transfer-interface.vala', 'src/mconnect/transfer-proxy.vala', 'src/mconnect/transfer-download.vala', + 'src/mconnect/transfer-upload.vala', 'src/mconnect/logging.vala', 'src/crypt/certificate.vala', ] diff --git a/src/mconnect/transfer-upload.vala b/src/mconnect/transfer-upload.vala new file mode 100644 index 0000000..b5fa484 --- /dev/null +++ b/src/mconnect/transfer-upload.vala @@ -0,0 +1,184 @@ +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * AUTHORS + * Maciek Borzecki + */ + +class UploadTransfer : TransferInterface, Object { + + private FileInputStream finstream = null; + private Cancellable cancellable = null; + private Device device = null; + private TlsConnection tls_conn = null; + private SocketService listener = null; + private uint timeout_source = 0; + private IOCopyJob job = null; + private SocketConnection conn = null; + private uint64 transferred = 0; + private uint64 size; + + private const int WAIT_TIMEOUT = 30; + + public UploadTransfer(Device dev, SocketService listener, + FileInputStream source, uint64 size) { + this.listener = listener; + this.cancellable = new Cancellable(); + this.device = dev; + this.finstream = source; + this.size = size; + } + + public async bool start_async() { + debug("start transfer from to device %s", + this.device.to_string()); + + this.listener.incoming.connect(this.client_connected); + debug("wait for client"); + this.timeout_source = Timeout.add_seconds(WAIT_TIMEOUT, + this.wait_timeout); + this.listener.start(); + + return true; + } + + private bool wait_timeout() { + warning("timeout waiting for client"); + this.listener.stop(); + this.cleanup_error("timeout waiting for client"); + return false; + } + + private bool client_connected(SocketConnection conn, Object? source) { + if (this.timeout_source != 0) { + Source.remove(this.timeout_source); + this.timeout_source = 0; + } + + this.handle_client.begin(conn); + return false; + } + + private async void handle_client(SocketConnection conn) { + debug("client connected: %s", conn.get_remote_address().to_string()); + + this.conn = conn; + + var sock = this.conn.get_socket(); + Utils.socket_set_keepalive(sock); + + // enable TLS + this.tls_conn = Utils.make_tls_connection(this.conn, + Core.instance().certificate, + this.device.certificate, + Utils.TlsConnectionMode.SERVER); + try { + debug("attempt TLS handshake"); + var tls_res = yield this.tls_conn.handshake_async(); + debug("TLS handshake complete"); + } catch (Error e) { + var err ="TLS handshake failed: %s".printf(e.message); + warning(err); + this.cleanup_error(err); + return; + } + + this.start_transfer(); + } + + private void start_transfer() { + debug("connected, start transfer"); + this.job = new IOCopyJob(this.finstream, + this.tls_conn.output_stream); + this.job.progress.connect((t, done) => { + int percent = (int) (100.0 * ((double)done / (double)this.size)); + debug("progress: %s/%s %d%%", + format_size(done), format_size(this.size), percent); + this.transferred = done; + }); + + this.started(); + + this.job.start_async.begin(this.cancellable, + this.job_complete); + } + + private void job_complete(Object? obj, AsyncResult res) { + info("transfer finished"); + try { + var rcvd_bytes = this.job.start_async.end(res); + debug("transfer done, got %s", format_size(rcvd_bytes)); + + this.cleanup_success(); + + } catch (Error err) { + warning("transfer failed: %s", err.message); + + this.cleanup_error(err.message); + } + } + + private void cleanup() { + if (this.finstream != null) { + try { + this.finstream.close(); + } catch (IOError e) { + warning("failed to close file input: %s", + e.message); + } + } + + if (this.tls_conn != null) { + try { + this.tls_conn.close(); + } catch (IOError e) { + warning("failed to close TLS connection: %s", + e.message); + } + } + if (this.conn != null) { + try { + this.conn.close(); + } catch (IOError e) { + warning("failed to close connection: %s", + e.message); + } + } + + this.listener.stop(); + this.listener.close(); + this.finstream = null; + this.conn = null; + this.tls_conn = null; + this.job = null; + } + + private void cleanup_error(string reason) { + + this.cleanup(); + + this.error(reason); + } + + private void cleanup_success() { + this.cleanup(); + + this.finished(); + } + + public void cancel() { + debug("cancel called"); + this.cancellable.cancel(); + } +} \ No newline at end of file