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/
This commit is contained in:
parent
633f01050b
commit
b9b8567c9d
5
enums.js
5
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;
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
function SpiceFileXferTask(id, file)
|
||||
{
|
||||
this.id = id;
|
||||
this.file = file;
|
||||
}
|
95
main.js
95
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;
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
<script src="thirdparty/sha1.js"></script>
|
||||
<script src="ticket.js"></script>
|
||||
<script src="resize.js"></script>
|
||||
<script src="filexfer.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="spice.css" />
|
||||
|
||||
<script>
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
<script src="thirdparty/sha1.js"></script>
|
||||
<script src="ticket.js"></script>
|
||||
<script src="resize.js"></script>
|
||||
<script src="filexfer.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="spice.css" />
|
||||
|
||||
<script>
|
||||
|
|
107
spicemsg.js
107
spicemsg.js
|
@ -348,6 +348,29 @@ SpiceMsgMainMouseMode.prototype =
|
|||
},
|
||||
}
|
||||
|
||||
function SpiceMsgMainAgentData(a, at)
|
||||
{
|
||||
this.from_buffer(a, at);
|
||||
}
|
||||
|
||||
SpiceMsgMainAgentData.prototype =
|
||||
{
|
||||
from_buffer: function(a, at)
|
||||
{
|
||||
at = at || 0;
|
||||
var dv = new SpiceDataView(a);
|
||||
this.protocol = dv.getUint32(at, true); at += 4;
|
||||
this.type = dv.getUint32(at, true); at += 4;
|
||||
this.opaque = dv.getUint64(at, true); at += 8;
|
||||
this.size = dv.getUint32(at, true); at += 4;
|
||||
if (a.byteLength > at)
|
||||
{
|
||||
this.data = a.slice(at);
|
||||
at += this.data.byteLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function SpiceMsgMainAgentTokens(a, at)
|
||||
{
|
||||
this.from_buffer(a, at);
|
||||
|
@ -494,6 +517,90 @@ VDAgentMonitorsConfig.prototype =
|
|||
}
|
||||
}
|
||||
|
||||
function VDAgentFileXferStatusMessage(data, result)
|
||||
{
|
||||
if (result)
|
||||
{
|
||||
this.id = data;
|
||||
this.result = result;
|
||||
}
|
||||
else
|
||||
this.from_buffer(data);
|
||||
}
|
||||
|
||||
VDAgentFileXferStatusMessage.prototype =
|
||||
{
|
||||
to_buffer: function(a, at)
|
||||
{
|
||||
at = at || 0;
|
||||
var dv = new SpiceDataView(a);
|
||||
dv.setUint32(at, this.id, true); at += 4;
|
||||
dv.setUint32(at, this.result, true); at += 4;
|
||||
},
|
||||
from_buffer: function(a, at)
|
||||
{
|
||||
at = at || 0;
|
||||
var dv = new SpiceDataView(a);
|
||||
this.id = dv.getUint32(at, true); at += 4;
|
||||
this.result = dv.getUint32(at, true); at += 4;
|
||||
return at;
|
||||
},
|
||||
buffer_size: function()
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
function VDAgentFileXferStartMessage(id, name, size)
|
||||
{
|
||||
this.id = id;
|
||||
this.string = "[vdagent-file-xfer]\n"+"name="+name+"\nsize="+size+"\n";
|
||||
}
|
||||
|
||||
VDAgentFileXferStartMessage.prototype =
|
||||
{
|
||||
to_buffer: function(a, at)
|
||||
{
|
||||
at = at || 0;
|
||||
var dv = new SpiceDataView(a);
|
||||
dv.setUint32(at, this.id, true); at += 4;
|
||||
for (var i = 0; i < this.string.length; i++, at++)
|
||||
dv.setUint8(at, this.string.charCodeAt(i));
|
||||
},
|
||||
buffer_size: function()
|
||||
{
|
||||
return 4 + this.string.length + 1;
|
||||
}
|
||||
}
|
||||
|
||||
function VDAgentFileXferDataMessage(id, size, data)
|
||||
{
|
||||
this.id = id;
|
||||
this.size = size;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
VDAgentFileXferDataMessage.prototype =
|
||||
{
|
||||
to_buffer: function(a, at)
|
||||
{
|
||||
at = at || 0;
|
||||
var dv = new SpiceDataView(a);
|
||||
dv.setUint32(at, this.id, true); at += 4;
|
||||
dv.setUint64(at, this.size, true); at += 8;
|
||||
if (this.data && this.data.byteLength > 0)
|
||||
{
|
||||
var u8arr = new Uint8Array(this.data);
|
||||
for (var i = 0; i < u8arr.length; i++, at++)
|
||||
dv.setUint8(at, u8arr[i]);
|
||||
}
|
||||
},
|
||||
buffer_size: function()
|
||||
{
|
||||
return 12 + this.size;
|
||||
}
|
||||
}
|
||||
|
||||
function SpiceMsgNotify(a, at)
|
||||
{
|
||||
this.from_buffer(a, at);
|
||||
|
|
Loading…
Reference in New Issue