From b9b8567c9d12f8d4162a777f36cf77980f647bb0 Mon Sep 17 00:00:00 2001 From: Pavel Grunt Date: Wed, 14 Jan 2015 17:44:40 +0100 Subject: Implement methods for transfering files from client to guest It is possible to transfer files from the client to the guest using File API [0] when a spice vd agent is connected. Methods for the transfer are based on spice-gtk implementation. [0] http://www.w3.org/TR/file-upload/ --- enums.js | 5 +++ filexfer.js | 25 +++++++++++++ main.js | 95 +++++++++++++++++++++++++++++++++++++++++++++++++ spice.html | 1 + spice_auto.html | 1 + spicemsg.js | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 234 insertions(+) create mode 100644 filexfer.js diff --git a/enums.js b/enums.js index 4b678df..07aa343 100644 --- a/enums.js +++ b/enums.js @@ -328,3 +328,8 @@ var VD_AGENT_MOUSE_STATE = 1, VD_AGENT_FILE_XFER_DATA =12, VD_AGENT_CLIENT_DISCONNECTED =13, VD_AGENT_MAX_CLIPBOARD =14; + +var VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA = 0, + VD_AGENT_FILE_XFER_STATUS_CANCELLED = 1, + VD_AGENT_FILE_XFER_STATUS_ERROR = 2, + VD_AGENT_FILE_XFER_STATUS_SUCCESS = 3; diff --git a/filexfer.js b/filexfer.js new file mode 100644 index 0000000..d472240 --- /dev/null +++ b/filexfer.js @@ -0,0 +1,25 @@ +"use strict"; +/* + Copyright (C) 2014 Red Hat, Inc. + + This file is part of spice-html5. + + spice-html5 is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + spice-html5 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with spice-html5. If not, see . +*/ + +function SpiceFileXferTask(id, file) +{ + this.id = id; + this.file = file; +} diff --git a/main.js b/main.js index fce2f41..eba8599 100644 --- a/main.js +++ b/main.js @@ -56,6 +56,8 @@ function SpiceMainConn() SpiceConn.apply(this, arguments); this.agent_msg_queue = []; + this.file_xfer_tasks = {}; + this.file_xfer_task_id = 0; } SpiceMainConn.prototype = Object.create(SpiceConn.prototype); @@ -171,6 +173,18 @@ SpiceMainConn.prototype.process_channel_message = function(msg) return true; } + if (msg.type == SPICE_MSG_MAIN_AGENT_DATA) + { + var agent_data = new SpiceMsgMainAgentData(msg.data); + if (agent_data.type == VD_AGENT_FILE_XFER_STATUS) + { + this.handle_file_xfer_status(new VDAgentFileXferStatusMessage(agent_data.data)); + return true; + } + + return false; + } + return false; } @@ -245,6 +259,87 @@ SpiceMainConn.prototype.resize_window = function(flags, width, height, depth, x, this.send_agent_message(VD_AGENT_MONITORS_CONFIG, monitors_config); } +SpiceMainConn.prototype.file_xfer_start = function(file) +{ + var task_id, xfer_start, task; + + task_id = this.file_xfer_task_id++; + task = new SpiceFileXferTask(task_id, file); + this.file_xfer_tasks[task_id] = task; + xfer_start = new VDAgentFileXferStartMessage(task_id, file.name, file.size); + this.send_agent_message(VD_AGENT_FILE_XFER_START, xfer_start); +} + +SpiceMainConn.prototype.handle_file_xfer_status = function(file_xfer_status) +{ + var xfer_error, xfer_task; + if (!this.file_xfer_tasks[file_xfer_status.id]) + { + return; + } + xfer_task = this.file_xfer_tasks[file_xfer_status.id]; + switch (file_xfer_status.result) + { + case VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA: + this.file_xfer_read(xfer_task); + return; + case VD_AGENT_FILE_XFER_STATUS_CANCELLED: + xfer_error = "transfer is cancelled by spice agent"; + break; + case VD_AGENT_FILE_XFER_STATUS_ERROR: + xfer_error = "some errors occurred in the spice agent"; + break; + case VD_AGENT_FILE_XFER_STATUS_SUCCESS: + break; + default: + xfer_error = "unhandled status type: " + file_xfer_status.result; + break; + } + + this.file_xfer_completed(xfer_task, xfer_error) +} + +SpiceMainConn.prototype.file_xfer_read = function(file_xfer_task, start_byte) +{ + var FILE_XFER_CHUNK_SIZE = 32 * VD_AGENT_MAX_DATA_SIZE; + var _this = this; + var sb, eb; + var slice, reader; + + if (!file_xfer_task || + !this.file_xfer_tasks[file_xfer_task.id] || + (start_byte > 0 && start_byte == file_xfer_task.file.size)) + { + return; + } + + sb = start_byte || 0, + eb = Math.min(sb + FILE_XFER_CHUNK_SIZE, file_xfer_task.file.size); + + reader = new FileReader(); + reader.onload = function(e) + { + var xfer_data = new VDAgentFileXferDataMessage(file_xfer_task.id, + e.target.result.byteLength, + e.target.result); + _this.send_agent_message(VD_AGENT_FILE_XFER_DATA, xfer_data); + _this.file_xfer_read(file_xfer_task, eb); + }; + + slice = file_xfer_task.file.slice(sb, eb); + reader.readAsArrayBuffer(slice); +} + +SpiceMainConn.prototype.file_xfer_completed = function(file_xfer_task, error) +{ + if (error) + this.log_err(error); + else + this.log_info("transfer of '" + file_xfer_task.file.name +"' was successful"); + + delete this.file_xfer_tasks[file_xfer_task.id]; +} + SpiceMainConn.prototype.connect_agent = function() { this.agent_connected = true; diff --git a/spice.html b/spice.html index e830eb3..fc53a2a 100644 --- a/spice.html +++ b/spice.html @@ -55,6 +55,7 @@ + +