2013-07-24 16:41:12 +00:00
|
|
|
/*
|
|
|
|
* synergy -- mouse and keyboard sharing utility
|
2016-09-07 14:24:00 +00:00
|
|
|
* Copyright (C) 2013-2016 Symless Ltd.
|
2013-07-24 16:41:12 +00:00
|
|
|
*
|
|
|
|
* This package is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
2015-05-03 02:33:52 +00:00
|
|
|
* found in the file LICENSE that should have accompanied this file.
|
2013-07-24 16:41:12 +00:00
|
|
|
*
|
|
|
|
* This package 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2015-05-18 17:17:22 +00:00
|
|
|
#include "synergy/StreamChunker.h"
|
2014-02-28 12:36:45 +00:00
|
|
|
|
2016-07-05 11:30:08 +00:00
|
|
|
#include "mt/Lock.h"
|
|
|
|
#include "mt/Mutex.h"
|
2015-05-19 16:37:15 +00:00
|
|
|
#include "synergy/FileChunk.h"
|
|
|
|
#include "synergy/ClipboardChunk.h"
|
2014-02-28 12:36:45 +00:00
|
|
|
#include "synergy/protocol_types.h"
|
|
|
|
#include "base/EventTypes.h"
|
|
|
|
#include "base/Event.h"
|
|
|
|
#include "base/IEventQueue.h"
|
|
|
|
#include "base/EventTypes.h"
|
|
|
|
#include "base/Log.h"
|
|
|
|
#include "base/Stopwatch.h"
|
2015-05-19 16:37:15 +00:00
|
|
|
#include "base/String.h"
|
2014-03-14 20:33:18 +00:00
|
|
|
#include "common/stdexcept.h"
|
2014-02-28 12:36:45 +00:00
|
|
|
|
2013-07-24 16:41:12 +00:00
|
|
|
#include <fstream>
|
|
|
|
|
2015-06-05 20:48:06 +00:00
|
|
|
#define SEND_THRESHOLD 0.005f
|
2013-07-26 14:10:06 +00:00
|
|
|
|
2013-07-24 16:41:12 +00:00
|
|
|
using namespace std;
|
|
|
|
|
2015-05-22 20:30:50 +00:00
|
|
|
#define SOCKET_CHUNK_SIZE 512 * 1024; // 512kb
|
|
|
|
#define SECURE_SOCKET_CHUNK_SIZE 2 * 1024; // 2kb
|
|
|
|
|
|
|
|
size_t StreamChunker::s_chunkSize = SOCKET_CHUNK_SIZE;
|
2015-05-22 23:34:00 +00:00
|
|
|
bool StreamChunker::s_isChunkingClipboard = false;
|
|
|
|
bool StreamChunker::s_interruptClipboard = false;
|
|
|
|
bool StreamChunker::s_isChunkingFile = false;
|
|
|
|
bool StreamChunker::s_interruptFile = false;
|
2016-07-05 11:30:08 +00:00
|
|
|
Mutex* StreamChunker::s_interruptMutex = NULL;
|
2013-07-24 16:41:12 +00:00
|
|
|
|
|
|
|
void
|
2015-05-19 16:37:15 +00:00
|
|
|
StreamChunker::sendFile(
|
|
|
|
char* filename,
|
|
|
|
IEventQueue* events,
|
|
|
|
void* eventTarget)
|
2013-07-24 16:41:12 +00:00
|
|
|
{
|
2015-05-22 23:34:00 +00:00
|
|
|
s_isChunkingFile = true;
|
|
|
|
|
2013-07-24 16:41:12 +00:00
|
|
|
std::fstream file(reinterpret_cast<char*>(filename), std::ios::in | std::ios::binary);
|
|
|
|
|
|
|
|
if (!file.is_open()) {
|
|
|
|
throw runtime_error("failed to open file");
|
|
|
|
}
|
|
|
|
|
|
|
|
// check file size
|
|
|
|
file.seekg (0, std::ios::end);
|
|
|
|
size_t size = (size_t)file.tellg();
|
|
|
|
|
|
|
|
// send first message (file size)
|
2015-05-19 21:23:43 +00:00
|
|
|
String fileSize = synergy::string::sizeTypeToString(size);
|
2015-05-19 16:37:15 +00:00
|
|
|
FileChunk* sizeMessage = FileChunk::start(fileSize);
|
|
|
|
|
2015-06-05 20:48:06 +00:00
|
|
|
events->addEvent(Event(events->forFile().fileChunkSending(), eventTarget, sizeMessage));
|
2013-07-24 16:41:12 +00:00
|
|
|
|
|
|
|
// send chunk messages with a fixed chunk size
|
|
|
|
size_t sentLength = 0;
|
2015-05-22 20:30:50 +00:00
|
|
|
size_t chunkSize = s_chunkSize;
|
2015-06-05 20:48:06 +00:00
|
|
|
Stopwatch sendStopwatch;
|
|
|
|
sendStopwatch.start();
|
2013-07-24 16:41:12 +00:00
|
|
|
file.seekg (0, std::ios::beg);
|
2015-06-05 20:48:06 +00:00
|
|
|
|
2013-07-24 16:41:12 +00:00
|
|
|
while (true) {
|
2015-05-22 23:34:00 +00:00
|
|
|
if (s_interruptFile) {
|
|
|
|
s_interruptFile = false;
|
2015-07-09 20:46:17 +00:00
|
|
|
LOG((CLOG_DEBUG "file transmission interrupted"));
|
2015-05-22 23:34:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-07-22 14:24:51 +00:00
|
|
|
if (sendStopwatch.getTime() > SEND_THRESHOLD) {
|
2015-12-08 20:27:55 +00:00
|
|
|
events->addEvent(Event(events->forFile().keepAlive(), eventTarget));
|
|
|
|
|
2013-07-26 14:10:06 +00:00
|
|
|
// make sure we don't read too much from the mock data.
|
|
|
|
if (sentLength + chunkSize > size) {
|
|
|
|
chunkSize = size - sentLength;
|
|
|
|
}
|
|
|
|
|
2015-05-19 16:37:15 +00:00
|
|
|
char* chunkData = new char[chunkSize];
|
|
|
|
file.read(chunkData, chunkSize);
|
|
|
|
UInt8* data = reinterpret_cast<UInt8*>(chunkData);
|
|
|
|
FileChunk* fileChunk = FileChunk::data(data, chunkSize);
|
|
|
|
delete[] chunkData;
|
2013-07-24 16:41:12 +00:00
|
|
|
|
2015-06-05 20:48:06 +00:00
|
|
|
events->addEvent(Event(events->forFile().fileChunkSending(), eventTarget, fileChunk));
|
2013-07-24 16:41:12 +00:00
|
|
|
|
2013-07-26 14:10:06 +00:00
|
|
|
sentLength += chunkSize;
|
|
|
|
file.seekg (sentLength, std::ios::beg);
|
2013-07-24 16:41:12 +00:00
|
|
|
|
2013-07-26 14:10:06 +00:00
|
|
|
if (sentLength == size) {
|
|
|
|
break;
|
|
|
|
}
|
2013-07-24 16:41:12 +00:00
|
|
|
|
2015-06-05 20:48:06 +00:00
|
|
|
sendStopwatch.reset();
|
2013-07-24 16:41:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// send last message
|
2015-05-19 16:37:15 +00:00
|
|
|
FileChunk* end = FileChunk::end();
|
2013-07-24 16:41:12 +00:00
|
|
|
|
2015-06-05 20:48:06 +00:00
|
|
|
events->addEvent(Event(events->forFile().fileChunkSending(), eventTarget, end));
|
2013-07-24 16:41:12 +00:00
|
|
|
|
|
|
|
file.close();
|
2015-05-22 23:34:00 +00:00
|
|
|
|
|
|
|
s_isChunkingFile = false;
|
2013-07-24 16:41:12 +00:00
|
|
|
}
|
|
|
|
|
2015-05-19 16:37:15 +00:00
|
|
|
void
|
|
|
|
StreamChunker::sendClipboard(
|
|
|
|
String& data,
|
|
|
|
size_t size,
|
|
|
|
ClipboardID id,
|
|
|
|
UInt32 sequence,
|
|
|
|
IEventQueue* events,
|
|
|
|
void* eventTarget)
|
2013-07-24 16:41:12 +00:00
|
|
|
{
|
2015-05-22 23:34:00 +00:00
|
|
|
s_isChunkingClipboard = true;
|
|
|
|
|
2015-05-19 16:37:15 +00:00
|
|
|
// send first message (data size)
|
2015-05-19 21:23:43 +00:00
|
|
|
String dataSize = synergy::string::sizeTypeToString(size);
|
2015-05-19 16:37:15 +00:00
|
|
|
ClipboardChunk* sizeMessage = ClipboardChunk::start(id, sequence, dataSize);
|
|
|
|
|
2015-05-20 18:09:54 +00:00
|
|
|
events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, sizeMessage));
|
2015-05-19 16:37:15 +00:00
|
|
|
|
|
|
|
// send clipboard chunk with a fixed size
|
|
|
|
size_t sentLength = 0;
|
2015-05-22 20:30:50 +00:00
|
|
|
size_t chunkSize = s_chunkSize;
|
2015-06-05 20:48:06 +00:00
|
|
|
Stopwatch sendStopwatch;
|
|
|
|
sendStopwatch.start();
|
2015-05-22 23:34:00 +00:00
|
|
|
|
2015-05-19 16:37:15 +00:00
|
|
|
while (true) {
|
2016-07-05 11:30:08 +00:00
|
|
|
{
|
|
|
|
if (s_interruptMutex == NULL) {
|
|
|
|
s_interruptMutex = new Mutex();
|
|
|
|
}
|
|
|
|
Lock lock(s_interruptMutex);
|
|
|
|
if (s_interruptClipboard) {
|
|
|
|
LOG((CLOG_DEBUG "clipboard transmission interrupted"));
|
|
|
|
break;
|
|
|
|
}
|
2015-05-22 23:34:00 +00:00
|
|
|
}
|
2015-06-05 20:48:06 +00:00
|
|
|
|
|
|
|
if (sendStopwatch.getTime() > SEND_THRESHOLD) {
|
2015-12-08 20:27:55 +00:00
|
|
|
events->addEvent(Event(events->forFile().keepAlive(), eventTarget));
|
|
|
|
|
2015-05-19 16:37:15 +00:00
|
|
|
// make sure we don't read too much from the mock data.
|
|
|
|
if (sentLength + chunkSize > size) {
|
|
|
|
chunkSize = size - sentLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
String chunk(data.substr(sentLength, chunkSize).c_str(), chunkSize);
|
|
|
|
ClipboardChunk* dataChunk = ClipboardChunk::data(id, sequence, chunk);
|
|
|
|
|
2015-05-20 18:09:54 +00:00
|
|
|
events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, dataChunk));
|
2015-05-19 16:37:15 +00:00
|
|
|
|
|
|
|
sentLength += chunkSize;
|
|
|
|
if (sentLength == size) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-06-05 20:48:06 +00:00
|
|
|
sendStopwatch.reset();
|
2015-05-19 16:37:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// send last message
|
|
|
|
ClipboardChunk* end = ClipboardChunk::end(id, sequence);
|
|
|
|
|
2015-05-20 18:09:54 +00:00
|
|
|
events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, end));
|
2015-05-22 23:34:00 +00:00
|
|
|
|
2016-07-05 11:30:08 +00:00
|
|
|
LOG((CLOG_DEBUG "sent clipboard size=%d", sentLength));
|
|
|
|
|
2015-05-22 23:34:00 +00:00
|
|
|
s_isChunkingClipboard = false;
|
2013-07-24 16:41:12 +00:00
|
|
|
}
|
2015-05-22 20:30:50 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
StreamChunker::updateChunkSize(bool useSecureSocket)
|
|
|
|
{
|
|
|
|
if (useSecureSocket) {
|
|
|
|
s_chunkSize = SECURE_SOCKET_CHUNK_SIZE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
s_chunkSize = SOCKET_CHUNK_SIZE;
|
|
|
|
}
|
|
|
|
}
|
2015-05-22 23:34:00 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
StreamChunker::interruptFile()
|
|
|
|
{
|
|
|
|
if (s_isChunkingFile) {
|
|
|
|
s_interruptFile = true;
|
|
|
|
LOG((CLOG_INFO "previous dragged file has become invalid"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-07-05 11:30:08 +00:00
|
|
|
StreamChunker::setClipboardInterrupt(bool interrupt)
|
2015-05-22 23:34:00 +00:00
|
|
|
{
|
2016-07-05 11:30:08 +00:00
|
|
|
if (s_interruptMutex == NULL) {
|
|
|
|
s_interruptMutex = new Mutex();
|
|
|
|
}
|
|
|
|
Lock lock(s_interruptMutex);
|
|
|
|
|
|
|
|
if (interrupt) {
|
|
|
|
if (s_isChunkingClipboard) {
|
|
|
|
s_interruptClipboard = interrupt;
|
|
|
|
LOG((CLOG_INFO "previous clipboard data has become invalid"));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
LOG((CLOG_DEBUG "no clipboard to interrupt"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
s_interruptClipboard = interrupt;
|
|
|
|
LOG((CLOG_DEBUG "reset clipboard interrupt"));
|
2015-05-22 23:34:00 +00:00
|
|
|
}
|
|
|
|
}
|