Patch by Jerry for issue 46:

- Unit test for sending file data from server to client.
- Removed singleton pattern from CSocketMultiplexer for easier unit testing.
- Incremented protocol version from 1.4 to 1.5 (new file chunk message).
- Storing pointer to CConfig instead of copying in CServer (so we can mock it).
- Created a common event queue for testing (timeout, quit event, etc).
- Fixed code style.
This commit is contained in:
Nick Bolton 2013-07-16 19:02:30 +00:00
parent 6f97f1d186
commit c368013f13
56 changed files with 830 additions and 165 deletions

View File

@ -180,6 +180,8 @@ REGISTER_EVENT(IScreen, shapeChanged)
REGISTER_EVENT(IScreen, clipboardGrabbed) REGISTER_EVENT(IScreen, clipboardGrabbed)
REGISTER_EVENT(IScreen, suspend) REGISTER_EVENT(IScreen, suspend)
REGISTER_EVENT(IScreen, resume) REGISTER_EVENT(IScreen, resume)
REGISTER_EVENT(IScreen, fileChunkSending)
REGISTER_EVENT(IScreen, fileRecieveComplete)
// //
// ISecondaryScreen // ISecondaryScreen

View File

@ -647,7 +647,9 @@ public:
m_shapeChanged(CEvent::kUnknown), m_shapeChanged(CEvent::kUnknown),
m_clipboardGrabbed(CEvent::kUnknown), m_clipboardGrabbed(CEvent::kUnknown),
m_suspend(CEvent::kUnknown), m_suspend(CEvent::kUnknown),
m_resume(CEvent::kUnknown) { } m_resume(CEvent::kUnknown),
m_fileChunkSending(CEvent::kUnknown),
m_fileRecieveComplete(CEvent::kUnknown) { }
//! @name accessors //! @name accessors
//@{ //@{
@ -683,11 +685,17 @@ public:
//! Get resume event type //! Get resume event type
/*! /*!
Returns the suspend event type. This is sent whenever the system wakes Returns the resume event type. This is sent whenever the system wakes
up or a user session is activated (fast user switching). up or a user session is activated (fast user switching).
*/ */
CEvent::Type resume(); CEvent::Type resume();
//! Sending a file chunk
CEvent::Type fileChunkSending();
//! Completed receiving a file
CEvent::Type fileRecieveComplete();
//@} //@}
private: private:
@ -696,6 +704,8 @@ private:
CEvent::Type m_clipboardGrabbed; CEvent::Type m_clipboardGrabbed;
CEvent::Type m_suspend; CEvent::Type m_suspend;
CEvent::Type m_resume; CEvent::Type m_resume;
CEvent::Type m_fileChunkSending;
CEvent::Type m_fileRecieveComplete;
}; };
class ISecondaryScreenEvents : public CEventTypes { class ISecondaryScreenEvents : public CEventTypes {

View File

@ -30,11 +30,12 @@
#include "CLog.h" #include "CLog.h"
#include "IEventQueue.h" #include "IEventQueue.h"
#include "TMethodEventJob.h" #include "TMethodEventJob.h"
#include <cstring>
#include <cstdlib>
#include "CArch.h" #include "CArch.h"
#include "IPlatformScreen.h" #include "IPlatformScreen.h"
#include "CCryptoStream.h" #include "CCryptoStream.h"
#include <cstring>
#include <cstdlib>
#include <sstream>
// //
// CClient // CClient
@ -83,6 +84,10 @@ CClient::CClient(IEventQueue* events,
getEventTarget(), getEventTarget(),
new TMethodEventJob<CClient>(this, new TMethodEventJob<CClient>(this,
&CClient::handleGameDeviceFeedback)); &CClient::handleGameDeviceFeedback));
m_events->adoptHandler(m_events->forIScreen().fileChunkSending(),
this,
new TMethodEventJob<CClient>(this,
&CClient::handleFileChunkSending));
} }
CClient::~CClient() CClient::~CClient()
@ -728,3 +733,33 @@ CClient::handleGameDeviceFeedback(const CEvent& event, void*)
m_server->onGameDeviceFeedback(info->m_id, info->m_m1, info->m_m2); m_server->onGameDeviceFeedback(info->m_id, info->m_m1, info->m_m2);
} }
void
CClient::handleFileChunkSending(const CEvent& event, void*)
{
}
void
CClient::clearReceivedFileData()
{
m_receivedFileData.clear();
}
void
CClient::setExpectedFileSize(CString data)
{
std::istringstream iss(data);
iss >> m_expectedFileSize;
}
void
CClient::fileChunkReceived(CString data)
{
m_receivedFileData += data;
}
bool
CClient::isReceivedFileSizeValid()
{
return m_expectedFileSize == m_receivedFileData.size();
}

View File

@ -64,7 +64,7 @@ public:
~CClient(); ~CClient();
#ifdef TEST_ENV #ifdef TEST_ENV
CClient() : m_mock(true), m_events(NULL) { } CClient() : m_mock(true) { }
#endif #endif
//! @name manipulators //! @name manipulators
@ -92,6 +92,18 @@ public:
//! Set crypto IV for decryption //! Set crypto IV for decryption
virtual void setDecryptIv(const UInt8* iv); virtual void setDecryptIv(const UInt8* iv);
//! Clears the file buffer
void clearReceivedFileData();
//! Set the expected size of receiving file
void setExpectedFileSize(CString data);
//! Received a chunk of file data
void fileChunkReceived(CString data);
//! Return true if recieved file size is valid
bool isReceivedFileSizeValid();
//@} //@}
//! @name accessors //! @name accessors
//@{ //@{
@ -175,6 +187,7 @@ private:
void handleResume(const CEvent& event, void*); void handleResume(const CEvent& event, void*);
void handleGameDeviceTimingResp(const CEvent& event, void*); void handleGameDeviceTimingResp(const CEvent& event, void*);
void handleGameDeviceFeedback(const CEvent& event, void*); void handleGameDeviceFeedback(const CEvent& event, void*);
void handleFileChunkSending(const CEvent&, void*);
public: public:
bool m_mock; bool m_mock;
@ -199,6 +212,8 @@ private:
IEventQueue* m_events; IEventQueue* m_events;
CCryptoStream* m_cryptoStream; CCryptoStream* m_cryptoStream;
CCryptoOptions m_crypto; CCryptoOptions m_crypto;
std::size_t m_expectedFileSize;
CString m_receivedFileData;
}; };
#endif #endif

View File

@ -309,6 +309,10 @@ CServerProxy::parseMessage(const UInt8* code)
cryptoIv(); cryptoIv();
} }
else if (memcmp(code, kMsgDFileTransfer, 4) == 0) {
fileChunkReceived();
}
else if (memcmp(code, kMsgCClose, 4) == 0) { else if (memcmp(code, kMsgCClose, 4) == 0) {
// server wants us to hangup // server wants us to hangup
LOG((CLOG_DEBUG1 "recv close")); LOG((CLOG_DEBUG1 "recv close"));
@ -930,3 +934,29 @@ CServerProxy::infoAcknowledgment()
LOG((CLOG_DEBUG1 "recv info acknowledgment")); LOG((CLOG_DEBUG1 "recv info acknowledgment"));
m_ignoreMouse = false; m_ignoreMouse = false;
} }
void CServerProxy::fileChunkReceived()
{
// parse
UInt8 mark;
CString content;
CProtocolUtil::readf(m_stream, kMsgDFileTransfer + 4, &mark, &content);
switch (mark) {
case '0':
LOG((CLOG_DEBUG2 "recv file data: file size = %s", content));
m_client->clearReceivedFileData();
m_client->setExpectedFileSize(content);
break;
case '1':
LOG((CLOG_DEBUG2 "recv file data: chunck size = %i", content.size()));
m_client->fileChunkReceived(content);
break;
case '2':
LOG((CLOG_DEBUG2 "file data transfer finished"));
m_events->addEvent(CEvent(m_events->forIScreen().fileRecieveComplete(), m_client));
break;
}
}

View File

@ -105,6 +105,7 @@ private:
void setOptions(); void setOptions();
void queryInfo(); void queryInfo();
void infoAcknowledgment(); void infoAcknowledgment();
void fileChunkReceived();
private: private:
typedef EResult (CServerProxy::*MessageParser)(const UInt8*); typedef EResult (CServerProxy::*MessageParser)(const UInt8*);

View File

@ -26,19 +26,19 @@
// CIpcClient // CIpcClient
// //
CIpcClient::CIpcClient(IEventQueue* events) : CIpcClient::CIpcClient(IEventQueue* events, CSocketMultiplexer* socketMultiplexer) :
m_serverAddress(CNetworkAddress(IPC_HOST, IPC_PORT)), m_serverAddress(CNetworkAddress(IPC_HOST, IPC_PORT)),
m_server(nullptr), m_server(nullptr),
m_socket(events), m_socket(events, socketMultiplexer),
m_events(events) m_events(events)
{ {
init(); init();
} }
CIpcClient::CIpcClient(IEventQueue* events, int port) : CIpcClient::CIpcClient(IEventQueue* events, CSocketMultiplexer* socketMultiplexer, int port) :
m_serverAddress(CNetworkAddress(IPC_HOST, port)), m_serverAddress(CNetworkAddress(IPC_HOST, port)),
m_server(nullptr), m_server(nullptr),
m_socket(events), m_socket(events, socketMultiplexer),
m_events(events) m_events(events)
{ {
init(); init();

View File

@ -25,6 +25,7 @@
class CIpcServerProxy; class CIpcServerProxy;
class CIpcMessage; class CIpcMessage;
class IEventQueue; class IEventQueue;
class CSocketMultiplexer;
//! IPC client for communication between daemon and GUI. //! IPC client for communication between daemon and GUI.
/*! /*!
@ -32,8 +33,8 @@ class IEventQueue;
*/ */
class CIpcClient { class CIpcClient {
public: public:
CIpcClient(IEventQueue* events); CIpcClient(IEventQueue* events, CSocketMultiplexer* socketMultiplexer);
CIpcClient(IEventQueue* events, int port); CIpcClient(IEventQueue* events, CSocketMultiplexer* socketMultiplexer, int port);
virtual ~CIpcClient(); virtual ~CIpcClient();
//! @name manipulators //! @name manipulators

View File

@ -31,17 +31,17 @@
// CIpcServer // CIpcServer
// //
CIpcServer::CIpcServer(IEventQueue* events) : CIpcServer::CIpcServer(IEventQueue* events, CSocketMultiplexer* socketMultiplexer) :
m_events(events), m_events(events),
m_socket(events), m_socket(events, socketMultiplexer),
m_address(CNetworkAddress(IPC_HOST, IPC_PORT)) m_address(CNetworkAddress(IPC_HOST, IPC_PORT))
{ {
init(); init();
} }
CIpcServer::CIpcServer(IEventQueue* events, int port) : CIpcServer::CIpcServer(IEventQueue* events, CSocketMultiplexer* socketMultiplexer, int port) :
m_events(events), m_events(events),
m_socket(events), m_socket(events, socketMultiplexer),
m_address(CNetworkAddress(IPC_HOST, port)) m_address(CNetworkAddress(IPC_HOST, port))
{ {
init(); init();

View File

@ -29,6 +29,7 @@ class CEvent;
class CIpcClientProxy; class CIpcClientProxy;
class CIpcMessage; class CIpcMessage;
class IEventQueue; class IEventQueue;
class CSocketMultiplexer;
//! IPC server for communication between daemon and GUI. //! IPC server for communication between daemon and GUI.
/*! /*!
@ -39,8 +40,8 @@ and allows the daemon and client/server to send log data to the GUI.
*/ */
class CIpcServer { class CIpcServer {
public: public:
CIpcServer(IEventQueue* events); CIpcServer(IEventQueue* events, CSocketMultiplexer* socketMultiplexer);
CIpcServer(IEventQueue* events, int port); CIpcServer(IEventQueue* events, CSocketMultiplexer* socketMultiplexer, int port);
virtual ~CIpcServer(); virtual ~CIpcServer();
//! @name manipulators //! @name manipulators

View File

@ -32,8 +32,6 @@
// CSocketMultiplexer // CSocketMultiplexer
// //
CSocketMultiplexer* CSocketMultiplexer::s_instance = NULL;
CSocketMultiplexer::CSocketMultiplexer() : CSocketMultiplexer::CSocketMultiplexer() :
m_mutex(new CMutex), m_mutex(new CMutex),
m_thread(NULL), m_thread(NULL),
@ -44,8 +42,6 @@ CSocketMultiplexer::CSocketMultiplexer() :
m_jobListLocker(NULL), m_jobListLocker(NULL),
m_jobListLockLocker(NULL) m_jobListLockLocker(NULL)
{ {
assert(s_instance == NULL);
// this pointer just has to be unique and not NULL. it will // this pointer just has to be unique and not NULL. it will
// never be dereferenced. it's used to identify cursor nodes // never be dereferenced. it's used to identify cursor nodes
// in the jobs list. // in the jobs list.
@ -54,8 +50,6 @@ CSocketMultiplexer::CSocketMultiplexer() :
// start thread // start thread
m_thread = new CThread(new TMethodJob<CSocketMultiplexer>( m_thread = new CThread(new TMethodJob<CSocketMultiplexer>(
this, &CSocketMultiplexer::serviceThread)); this, &CSocketMultiplexer::serviceThread));
s_instance = this;
} }
CSocketMultiplexer::~CSocketMultiplexer() CSocketMultiplexer::~CSocketMultiplexer()
@ -76,15 +70,6 @@ CSocketMultiplexer::~CSocketMultiplexer()
i != m_socketJobMap.end(); ++i) { i != m_socketJobMap.end(); ++i) {
delete *(i->second); delete *(i->second);
} }
s_instance = NULL;
}
CSocketMultiplexer*
CSocketMultiplexer::getInstance()
{
assert(s_instance != NULL);
return s_instance;
} }
void void

View File

@ -108,8 +108,6 @@ private:
CSocketJobs m_socketJobs; CSocketJobs m_socketJobs;
CSocketJobMap m_socketJobMap; CSocketJobMap m_socketJobMap;
ISocketMultiplexerJob* m_cursorMark; ISocketMultiplexerJob* m_cursorMark;
static CSocketMultiplexer* s_instance;
}; };
#endif #endif

View File

@ -33,8 +33,9 @@
// CTCPListenSocket // CTCPListenSocket
// //
CTCPListenSocket::CTCPListenSocket(IEventQueue* events) : CTCPListenSocket::CTCPListenSocket(IEventQueue* events, CSocketMultiplexer* socketMultiplexer) :
m_events(events) m_events(events),
m_socketMultiplexer(socketMultiplexer)
{ {
m_mutex = new CMutex; m_mutex = new CMutex;
try { try {
@ -49,7 +50,7 @@ CTCPListenSocket::~CTCPListenSocket()
{ {
try { try {
if (m_socket != NULL) { if (m_socket != NULL) {
CSocketMultiplexer::getInstance()->removeSocket(this); m_socketMultiplexer->removeSocket(this);
ARCH->closeSocket(m_socket); ARCH->closeSocket(m_socket);
} }
} }
@ -67,7 +68,7 @@ CTCPListenSocket::bind(const CNetworkAddress& addr)
ARCH->setReuseAddrOnSocket(m_socket, true); ARCH->setReuseAddrOnSocket(m_socket, true);
ARCH->bindSocket(m_socket, addr.getAddress()); ARCH->bindSocket(m_socket, addr.getAddress());
ARCH->listenOnSocket(m_socket); ARCH->listenOnSocket(m_socket);
CSocketMultiplexer::getInstance()->addSocket(this, m_socketMultiplexer->addSocket(this,
new TSocketMultiplexerMethodJob<CTCPListenSocket>( new TSocketMultiplexerMethodJob<CTCPListenSocket>(
this, &CTCPListenSocket::serviceListening, this, &CTCPListenSocket::serviceListening,
m_socket, true, false)); m_socket, true, false));
@ -88,7 +89,7 @@ CTCPListenSocket::close()
throw XIOClosed(); throw XIOClosed();
} }
try { try {
CSocketMultiplexer::getInstance()->removeSocket(this); m_socketMultiplexer->removeSocket(this);
ARCH->closeSocket(m_socket); ARCH->closeSocket(m_socket);
m_socket = NULL; m_socket = NULL;
} }
@ -108,9 +109,9 @@ CTCPListenSocket::accept()
{ {
IDataSocket* socket = NULL; IDataSocket* socket = NULL;
try { try {
socket = new CTCPSocket(m_events, ARCH->acceptSocket(m_socket, NULL)); socket = new CTCPSocket(m_events, m_socketMultiplexer, ARCH->acceptSocket(m_socket, NULL));
if (socket != NULL) { if (socket != NULL) {
CSocketMultiplexer::getInstance()->addSocket(this, m_socketMultiplexer->addSocket(this,
new TSocketMultiplexerMethodJob<CTCPListenSocket>( new TSocketMultiplexerMethodJob<CTCPListenSocket>(
this, &CTCPListenSocket::serviceListening, this, &CTCPListenSocket::serviceListening,
m_socket, true, false)); m_socket, true, false));

View File

@ -25,6 +25,7 @@
class CMutex; class CMutex;
class ISocketMultiplexerJob; class ISocketMultiplexerJob;
class IEventQueue; class IEventQueue;
class CSocketMultiplexer;
//! TCP listen socket //! TCP listen socket
/*! /*!
@ -32,7 +33,7 @@ A listen socket using TCP.
*/ */
class CTCPListenSocket : public IListenSocket { class CTCPListenSocket : public IListenSocket {
public: public:
CTCPListenSocket(IEventQueue* events); CTCPListenSocket(IEventQueue* events, CSocketMultiplexer* socketMultiplexer);
~CTCPListenSocket(); ~CTCPListenSocket();
// ISocket overrides // ISocket overrides
@ -52,6 +53,7 @@ private:
CArchSocket m_socket; CArchSocket m_socket;
CMutex* m_mutex; CMutex* m_mutex;
IEventQueue* m_events; IEventQueue* m_events;
CSocketMultiplexer* m_socketMultiplexer;
}; };
#endif #endif

View File

@ -35,11 +35,12 @@
// CTCPSocket // CTCPSocket
// //
CTCPSocket::CTCPSocket(IEventQueue* events) : CTCPSocket::CTCPSocket(IEventQueue* events, CSocketMultiplexer* socketMultiplexer) :
IDataSocket(events), IDataSocket(events),
m_events(events), m_events(events),
m_mutex(), m_mutex(),
m_flushed(&m_mutex, true) m_flushed(&m_mutex, true),
m_socketMultiplexer(socketMultiplexer)
{ {
try { try {
m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM); m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM);
@ -51,12 +52,13 @@ CTCPSocket::CTCPSocket(IEventQueue* events) :
init(); init();
} }
CTCPSocket::CTCPSocket(IEventQueue* events, CArchSocket socket) : CTCPSocket::CTCPSocket(IEventQueue* events, CSocketMultiplexer* socketMultiplexer, CArchSocket socket) :
IDataSocket(events), IDataSocket(events),
m_events(events), m_events(events),
m_mutex(), m_mutex(),
m_socket(socket), m_socket(socket),
m_flushed(&m_mutex, true) m_flushed(&m_mutex, true),
m_socketMultiplexer(socketMultiplexer)
{ {
assert(m_socket != NULL); assert(m_socket != NULL);
@ -316,10 +318,10 @@ CTCPSocket::setJob(ISocketMultiplexerJob* job)
{ {
// multiplexer will delete the old job // multiplexer will delete the old job
if (job == NULL) { if (job == NULL) {
CSocketMultiplexer::getInstance()->removeSocket(this); m_socketMultiplexer->removeSocket(this);
} }
else { else {
CSocketMultiplexer::getInstance()->addSocket(this, job); m_socketMultiplexer->addSocket(this, job);
} }
} }

View File

@ -29,6 +29,7 @@ class CMutex;
class CThread; class CThread;
class ISocketMultiplexerJob; class ISocketMultiplexerJob;
class IEventQueue; class IEventQueue;
class CSocketMultiplexer;
//! TCP data socket //! TCP data socket
/*! /*!
@ -36,8 +37,8 @@ A data socket using TCP.
*/ */
class CTCPSocket : public IDataSocket { class CTCPSocket : public IDataSocket {
public: public:
CTCPSocket(IEventQueue* events); CTCPSocket(IEventQueue* events, CSocketMultiplexer* socketMultiplexer);
CTCPSocket(IEventQueue* events, CArchSocket socket); CTCPSocket(IEventQueue* events, CSocketMultiplexer* socketMultiplexer, CArchSocket socket);
~CTCPSocket(); ~CTCPSocket();
// ISocket overrides // ISocket overrides
@ -87,6 +88,7 @@ private:
bool m_readable; bool m_readable;
bool m_writable; bool m_writable;
IEventQueue* m_events; IEventQueue* m_events;
CSocketMultiplexer* m_socketMultiplexer;
}; };
#endif #endif

View File

@ -24,8 +24,9 @@
// CTCPSocketFactory // CTCPSocketFactory
// //
CTCPSocketFactory::CTCPSocketFactory(IEventQueue* events) : CTCPSocketFactory::CTCPSocketFactory(IEventQueue* events, CSocketMultiplexer* socketMultiplexer) :
m_events(events) m_events(events),
m_socketMultiplexer(socketMultiplexer)
{ {
// do nothing // do nothing
} }
@ -38,11 +39,11 @@ CTCPSocketFactory::~CTCPSocketFactory()
IDataSocket* IDataSocket*
CTCPSocketFactory::create() const CTCPSocketFactory::create() const
{ {
return new CTCPSocket(m_events); return new CTCPSocket(m_events, m_socketMultiplexer);
} }
IListenSocket* IListenSocket*
CTCPSocketFactory::createListen() const CTCPSocketFactory::createListen() const
{ {
return new CTCPListenSocket(m_events); return new CTCPListenSocket(m_events, m_socketMultiplexer);
} }

View File

@ -22,11 +22,12 @@
#include "ISocketFactory.h" #include "ISocketFactory.h"
class IEventQueue; class IEventQueue;
class CSocketMultiplexer;
//! Socket factory for TCP sockets //! Socket factory for TCP sockets
class CTCPSocketFactory : public ISocketFactory { class CTCPSocketFactory : public ISocketFactory {
public: public:
CTCPSocketFactory(IEventQueue* events); CTCPSocketFactory(IEventQueue* events, CSocketMultiplexer* socketMultiplexer);
virtual ~CTCPSocketFactory(); virtual ~CTCPSocketFactory();
// ISocketFactory overrides // ISocketFactory overrides
@ -35,6 +36,7 @@ public:
private: private:
IEventQueue* m_events; IEventQueue* m_events;
CSocketMultiplexer* m_socketMultiplexer;
}; };
#endif #endif

View File

@ -83,6 +83,7 @@ public:
virtual void screensaver(bool activate) = 0; virtual void screensaver(bool activate) = 0;
virtual void resetOptions() = 0; virtual void resetOptions() = 0;
virtual void setOptions(const COptionsList& options) = 0; virtual void setOptions(const COptionsList& options) = 0;
virtual void fileChunkSending(UInt8 mark, const UInt8* data) = 0;
virtual CString getName() const; virtual CString getName() const;
private: private:

View File

@ -63,6 +63,9 @@ public:
*/ */
CClientProxy* getNextClient(); CClientProxy* getNextClient();
//! Get server which owns this listener
CServer* getServer() { return m_server; }
//@} //@}
private: private:

View File

@ -89,6 +89,7 @@ public:
virtual void gameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2) = 0; virtual void gameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2) = 0;
virtual void gameDeviceTimingReq() = 0; virtual void gameDeviceTimingReq() = 0;
virtual void cryptoIv(const UInt8* iv) = 0; virtual void cryptoIv(const UInt8* iv) = 0;
virtual void fileChunkSending(UInt8 mark, const UInt8* data) = 0;
private: private:
synergy::IStream* m_stream; synergy::IStream* m_stream;

View File

@ -393,6 +393,13 @@ CClientProxy1_0::cryptoIv(const UInt8* iv)
LOG((CLOG_DEBUG "cryptoIv not supported")); LOG((CLOG_DEBUG "cryptoIv not supported"));
} }
void
CClientProxy1_0::fileChunkSending(UInt8 mark, const UInt8* iv)
{
// ignore -- not supported in protocol 1.0
LOG((CLOG_DEBUG "fileChunkSending not supported"));
}
void void
CClientProxy1_0::screensaver(bool on) CClientProxy1_0::screensaver(bool on)
{ {

View File

@ -64,6 +64,7 @@ public:
virtual void gameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2); virtual void gameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2);
virtual void gameDeviceTimingReq(); virtual void gameDeviceTimingReq();
virtual void cryptoIv(const UInt8* iv); virtual void cryptoIv(const UInt8* iv);
virtual void fileChunkSending(UInt8 mark, const UInt8* data);
protected: protected:
virtual bool parseHandshakeMessage(const UInt8* code); virtual bool parseHandshakeMessage(const UInt8* code);

View File

@ -0,0 +1,56 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* 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/>.
*/
#include "CClientProxy1_5.h"
#include "CProtocolUtil.h"
#include "CLog.h"
#include "IStream.h"
//
// CClientProxy1_5
//
CClientProxy1_5::CClientProxy1_5(const CString& name, synergy::IStream* stream, CServer* server, IEventQueue* events) :
CClientProxy1_4(name, stream, server, events)
{
}
CClientProxy1_5::~CClientProxy1_5()
{
}
void
CClientProxy1_5::fileChunkSending(UInt8 mark, const UInt8* data)
{
CString chunk(reinterpret_cast<const char*>(data));
switch (mark) {
case '0':
LOG((CLOG_DEBUG2 "file sending start: file size = %s", data));
break;
case '1':
LOG((CLOG_DEBUG2 "file chunk sending: %s", data));
break;
case '2':
LOG((CLOG_DEBUG2 "file sending finished"));
break;
}
CProtocolUtil::writef(getStream(), kMsgDFileTransfer, mark, &chunk);
}

View File

@ -0,0 +1,32 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* 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/>.
*/
#pragma once
#include "CClientProxy1_4.h"
class CServer;
class IEventQueue;
//! Proxy for client implementing protocol version 1.5
class CClientProxy1_5 : public CClientProxy1_4 {
public:
CClientProxy1_5(const CString& name, synergy::IStream* adoptedStream, CServer* server, IEventQueue* events);
~CClientProxy1_5();
virtual void fileChunkSending(UInt8 mark, const UInt8* data);
};

View File

@ -22,6 +22,7 @@
#include "CClientProxy1_2.h" #include "CClientProxy1_2.h"
#include "CClientProxy1_3.h" #include "CClientProxy1_3.h"
#include "CClientProxy1_4.h" #include "CClientProxy1_4.h"
#include "CClientProxy1_5.h"
#include "ProtocolTypes.h" #include "ProtocolTypes.h"
#include "CProtocolUtil.h" #include "CProtocolUtil.h"
#include "XSynergy.h" #include "XSynergy.h"
@ -221,6 +222,10 @@ CClientProxyUnknown::handleData(const CEvent&, void*)
case 4: case 4:
m_proxy = new CClientProxy1_4(name, m_stream, m_server, m_events); m_proxy = new CClientProxy1_4(name, m_stream, m_server, m_events);
break; break;
case 5:
m_proxy = new CClientProxy1_5(name, m_stream, m_server, m_events);
break;
} }
} }

View File

@ -176,6 +176,10 @@ public:
CConfig(IEventQueue* events); CConfig(IEventQueue* events);
virtual ~CConfig(); virtual ~CConfig();
#ifdef TEST_ENV
CConfig() : m_inputFilter(NULL) { }
#endif
//! @name manipulators //! @name manipulators
//@{ //@{
@ -313,7 +317,8 @@ public:
Returns the hot key input filter. Clients can modify hotkeys using Returns the hot key input filter. Clients can modify hotkeys using
that object. that object.
*/ */
CInputFilter* getInputFilter(); virtual CInputFilter*
getInputFilter();
//@} //@}
//! @name accessors //! @name accessors
@ -339,7 +344,7 @@ public:
/*! /*!
Returns true iff \c name names a screen. Returns true iff \c name names a screen.
*/ */
bool isScreen(const CString& name) const; virtual bool isScreen(const CString& name) const;
//! Test for canonical screen name //! Test for canonical screen name
/*! /*!

View File

@ -321,6 +321,10 @@ public:
CInputFilter(const CInputFilter&); CInputFilter(const CInputFilter&);
virtual ~CInputFilter(); virtual ~CInputFilter();
#ifdef TEST_ENV
CInputFilter() : m_primaryClient(NULL) { }
#endif
CInputFilter& operator=(const CInputFilter&); CInputFilter& operator=(const CInputFilter&);
// add rule, adopting the condition and the actions // add rule, adopting the condition and the actions
@ -334,7 +338,7 @@ public:
// enable event filtering using the given primary client. disable // enable event filtering using the given primary client. disable
// if client is NULL. // if client is NULL.
void setPrimaryClient(CPrimaryClient* client); virtual void setPrimaryClient(CPrimaryClient* client);
// convert rules to a string // convert rules to a string
CString format(const CString& linePrefix) const; CString format(const CString& linePrefix) const;

View File

@ -23,6 +23,7 @@ set(inc
CClientProxy1_2.h CClientProxy1_2.h
CClientProxy1_3.h CClientProxy1_3.h
CClientProxy1_4.h CClientProxy1_4.h
CClientProxy1_5.h
CClientProxyUnknown.h CClientProxyUnknown.h
CConfig.h CConfig.h
CInputFilter.h CInputFilter.h
@ -39,6 +40,7 @@ set(src
CClientProxy1_2.cpp CClientProxy1_2.cpp
CClientProxy1_3.cpp CClientProxy1_3.cpp
CClientProxy1_4.cpp CClientProxy1_4.cpp
CClientProxy1_5.cpp
CClientProxyUnknown.cpp CClientProxyUnknown.cpp
CConfig.cpp CConfig.cpp
CInputFilter.cpp CInputFilter.cpp

View File

@ -28,7 +28,8 @@
CPrimaryClient::CPrimaryClient(const CString& name, CScreen* screen) : CPrimaryClient::CPrimaryClient(const CString& name, CScreen* screen) :
CBaseClientProxy(name), CBaseClientProxy(name),
m_screen(screen), m_screen(screen),
m_fakeInputCount(0) m_fakeInputCount(0),
m_mock(false)
{ {
// all clipboards are clean // all clipboards are clean
for (UInt32 i = 0; i < kClipboardEnd; ++i) { for (UInt32 i = 0; i < kClipboardEnd; ++i) {
@ -272,6 +273,12 @@ CPrimaryClient::screensaver(bool)
// ignore // ignore
} }
void
CPrimaryClient::fileChunkSending(UInt8 mark, const UInt8* data)
{
// ignore
}
void void
CPrimaryClient::resetOptions() CPrimaryClient::resetOptions()
{ {

View File

@ -38,6 +38,10 @@ public:
CPrimaryClient(const CString& name, CScreen* screen); CPrimaryClient(const CString& name, CScreen* screen);
~CPrimaryClient(); ~CPrimaryClient();
#ifdef TEST_ENV
CPrimaryClient() : CBaseClientProxy(""), m_mock(true) { }
#endif
//! @name manipulators //! @name manipulators
//@{ //@{
@ -45,20 +49,20 @@ public:
/*! /*!
Handles reconfiguration of jump zones. Handles reconfiguration of jump zones.
*/ */
void reconfigure(UInt32 activeSides); virtual void reconfigure(UInt32 activeSides);
//! Register a system hotkey //! Register a system hotkey
/*! /*!
Registers a system-wide hotkey for key \p key with modifiers \p mask. Registers a system-wide hotkey for key \p key with modifiers \p mask.
Returns an id used to unregister the hotkey. Returns an id used to unregister the hotkey.
*/ */
UInt32 registerHotKey(KeyID key, KeyModifierMask mask); virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask);
//! Unregister a system hotkey //! Unregister a system hotkey
/*! /*!
Unregisters a previously registered hot key. Unregisters a previously registered hot key.
*/ */
void unregisterHotKey(UInt32 id); virtual void unregisterHotKey(UInt32 id);
//! Prepare to synthesize input on primary screen //! Prepare to synthesize input on primary screen
/*! /*!
@ -98,7 +102,8 @@ public:
/*! /*!
Returns the primary screen's current toggle modifier key state. Returns the primary screen's current toggle modifier key state.
*/ */
KeyModifierMask getToggleMask() const; virtual KeyModifierMask
getToggleMask() const;
//! Get screen lock state //! Get screen lock state
/*! /*!
@ -143,11 +148,13 @@ public:
virtual void screensaver(bool activate); virtual void screensaver(bool activate);
virtual void resetOptions(); virtual void resetOptions();
virtual void setOptions(const COptionsList& options); virtual void setOptions(const COptionsList& options);
virtual void fileChunkSending(UInt8 mark, const UInt8* data);
private: private:
CScreen* m_screen; CScreen* m_screen;
bool m_clipboardDirty[kClipboardEnd]; bool m_clipboardDirty[kClipboardEnd];
SInt32 m_fakeInputCount; SInt32 m_fakeInputCount;
bool m_mock;
}; };
#endif #endif

View File

@ -41,7 +41,7 @@
// CServer // CServer
// //
CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient, CScreen* screen, IEventQueue* events) : CServer::CServer(CConfig& config, CPrimaryClient* primaryClient, CScreen* screen, IEventQueue* events) :
m_events(events), m_events(events),
m_mock(false), m_mock(false),
m_primaryClient(primaryClient), m_primaryClient(primaryClient),
@ -51,8 +51,8 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient, CScreen*
m_yDelta(0), m_yDelta(0),
m_xDelta2(0), m_xDelta2(0),
m_yDelta2(0), m_yDelta2(0),
m_config(events), m_config(&config),
m_inputFilter(m_config.getInputFilter()), m_inputFilter(config.getInputFilter()),
m_activeSaver(NULL), m_activeSaver(NULL),
m_switchDir(kNoDirection), m_switchDir(kNoDirection),
m_switchScreen(NULL), m_switchScreen(NULL),
@ -173,6 +173,10 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient, CScreen*
m_inputFilter, m_inputFilter,
new TMethodEventJob<CServer>(this, new TMethodEventJob<CServer>(this,
&CServer::handleFakeInputEndEvent)); &CServer::handleFakeInputEndEvent));
m_events->adoptHandler(m_events->forIScreen().fileChunkSending(),
this,
new TMethodEventJob<CServer>(this,
&CServer::handleFileChunkSendingEvent));
// add connection // add connection
addClient(m_primaryClient); addClient(m_primaryClient);
@ -185,8 +189,8 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient, CScreen*
m_inputFilter->setPrimaryClient(m_primaryClient); m_inputFilter->setPrimaryClient(m_primaryClient);
// Determine if scroll lock is already set. If so, lock the cursor to the primary screen // Determine if scroll lock is already set. If so, lock the cursor to the primary screen
int keyValue = m_primaryClient->getToggleMask (); int keyValue = m_primaryClient->getToggleMask();
if (m_primaryClient->getToggleMask () & KeyModifierScrollLock) { if (m_primaryClient->getToggleMask() & KeyModifierScrollLock) {
LOG((CLOG_DEBUG "scroll lock on initially. locked to screen")); LOG((CLOG_DEBUG "scroll lock on initially. locked to screen"));
m_lockedToScreen = true; m_lockedToScreen = true;
} }
@ -259,7 +263,6 @@ CServer::setConfig(const CConfig& config)
closeClients(config); closeClients(config);
// cut over // cut over
m_config = config;
processOptions(); processOptions();
// add ScrollLock as a hotkey to lock to the screen. this was a // add ScrollLock as a hotkey to lock to the screen. this was a
@ -269,7 +272,7 @@ CServer::setConfig(const CConfig& config)
// we will unfortunately generate a warning. if the user has // we will unfortunately generate a warning. if the user has
// configured a CLockCursorToScreenAction then we don't add // configured a CLockCursorToScreenAction then we don't add
// ScrollLock as a hotkey. // ScrollLock as a hotkey.
if (!m_config.hasLockToScreenAction()) { if (!m_config->hasLockToScreenAction()) {
IPlatformScreen::CKeyInfo* key = IPlatformScreen::CKeyInfo* key =
IPlatformScreen::CKeyInfo::alloc(kKeyScrollLock, 0, 0, 0); IPlatformScreen::CKeyInfo::alloc(kKeyScrollLock, 0, 0, 0);
CInputFilter::CRule rule(new CInputFilter::CKeystrokeCondition(m_events, key)); CInputFilter::CRule rule(new CInputFilter::CKeystrokeCondition(m_events, key));
@ -301,7 +304,7 @@ CServer::adoptClient(CBaseClientProxy* client)
&CServer::handleClientDisconnected, client)); &CServer::handleClientDisconnected, client));
// name must be in our configuration // name must be in our configuration
if (!m_config.isScreen(client->getName())) { if (!m_config->isScreen(client->getName())) {
LOG((CLOG_WARN "unrecognised client name \"%s\", check server config", client->getName().c_str())); LOG((CLOG_WARN "unrecognised client name \"%s\", check server config", client->getName().c_str()));
closeClient(client, kMsgEUnknown); closeClient(client, kMsgEUnknown);
return; return;
@ -375,7 +378,7 @@ CServer::getClients(std::vector<CString>& list) const
CString CString
CServer::getName(const CBaseClientProxy* client) const CServer::getName(const CBaseClientProxy* client) const
{ {
CString name = m_config.getCanonicalName(client->getName()); CString name = m_config->getCanonicalName(client->getName());
if (name.empty()) { if (name.empty()) {
name = client->getName(); name = client->getName();
} }
@ -579,7 +582,7 @@ CServer::hasAnyNeighbor(CBaseClientProxy* client, EDirection dir) const
{ {
assert(client != NULL); assert(client != NULL);
return m_config.hasNeighbor(getName(client), dir); return m_config->hasNeighbor(getName(client), dir);
} }
CBaseClientProxy* CBaseClientProxy*
@ -601,7 +604,7 @@ CServer::getNeighbor(CBaseClientProxy* src,
// search for the closest neighbor that exists in direction dir // search for the closest neighbor that exists in direction dir
float tTmp; float tTmp;
for (;;) { for (;;) {
CString dstName(m_config.getNeighbor(srcName, dir, t, &tTmp)); CString dstName(m_config->getNeighbor(srcName, dir, t, &tTmp));
// if nothing in that direction then return NULL. if the // if nothing in that direction then return NULL. if the
// destination is the source then we can make no more // destination is the source then we can make no more
@ -757,25 +760,25 @@ CServer::avoidJumpZone(CBaseClientProxy* dst,
// don't need to move inwards because that side can't provoke a jump. // don't need to move inwards because that side can't provoke a jump.
switch (dir) { switch (dir) {
case kLeft: case kLeft:
if (!m_config.getNeighbor(dstName, kRight, t, NULL).empty() && if (!m_config->getNeighbor(dstName, kRight, t, NULL).empty() &&
x > dx + dw - 1 - z) x > dx + dw - 1 - z)
x = dx + dw - 1 - z; x = dx + dw - 1 - z;
break; break;
case kRight: case kRight:
if (!m_config.getNeighbor(dstName, kLeft, t, NULL).empty() && if (!m_config->getNeighbor(dstName, kLeft, t, NULL).empty() &&
x < dx + z) x < dx + z)
x = dx + z; x = dx + z;
break; break;
case kTop: case kTop:
if (!m_config.getNeighbor(dstName, kBottom, t, NULL).empty() && if (!m_config->getNeighbor(dstName, kBottom, t, NULL).empty() &&
y > dy + dh - 1 - z) y > dy + dh - 1 - z)
y = dy + dh - 1 - z; y = dy + dh - 1 - z;
break; break;
case kBottom: case kBottom:
if (!m_config.getNeighbor(dstName, kTop, t, NULL).empty() && if (!m_config->getNeighbor(dstName, kTop, t, NULL).empty() &&
y < dy + z) y < dy + z)
y = dy + z; y = dy + z;
break; break;
@ -839,9 +842,9 @@ CServer::isSwitchOkay(CBaseClientProxy* newScreen,
// are we in a locked corner? first check if screen has the option set // are we in a locked corner? first check if screen has the option set
// and, if not, check the global options. // and, if not, check the global options.
const CConfig::CScreenOptions* options = const CConfig::CScreenOptions* options =
m_config.getOptions(getName(m_active)); m_config->getOptions(getName(m_active));
if (options == NULL || options->count(kOptionScreenSwitchCorners) == 0) { if (options == NULL || options->count(kOptionScreenSwitchCorners) == 0) {
options = m_config.getOptions(""); options = m_config->getOptions("");
} }
if (options != NULL && options->count(kOptionScreenSwitchCorners) > 0) { if (options != NULL && options->count(kOptionScreenSwitchCorners) > 0) {
// get corner mask and size // get corner mask and size
@ -1089,7 +1092,7 @@ CServer::sendOptions(CBaseClientProxy* client) const
// look up options for client // look up options for client
const CConfig::CScreenOptions* options = const CConfig::CScreenOptions* options =
m_config.getOptions(getName(client)); m_config->getOptions(getName(client));
if (options != NULL) { if (options != NULL) {
// convert options to a more convenient form for sending // convert options to a more convenient form for sending
optionsList.reserve(2 * options->size()); optionsList.reserve(2 * options->size());
@ -1101,7 +1104,7 @@ CServer::sendOptions(CBaseClientProxy* client) const
} }
// look up global options // look up global options
options = m_config.getOptions(""); options = m_config->getOptions("");
if (options != NULL) { if (options != NULL) {
// convert options to a more convenient form for sending // convert options to a more convenient form for sending
optionsList.reserve(optionsList.size() + 2 * options->size()); optionsList.reserve(optionsList.size() + 2 * options->size());
@ -1120,7 +1123,7 @@ CServer::sendOptions(CBaseClientProxy* client) const
void void
CServer::processOptions() CServer::processOptions()
{ {
const CConfig::CScreenOptions* options = m_config.getOptions(""); const CConfig::CScreenOptions* options = m_config->getOptions("");
if (options == NULL) { if (options == NULL) {
return; return;
} }
@ -1510,6 +1513,13 @@ CServer::handleFakeInputEndEvent(const CEvent&, void*)
m_primaryClient->fakeInputEnd(); m_primaryClient->fakeInputEnd();
} }
void
CServer::handleFileChunkSendingEvent(const CEvent& event, void*)
{
UInt8* data = reinterpret_cast<UInt8*>(event.getData());
onFileChunkSending(data);
}
void void
CServer::onClipboardChanged(CBaseClientProxy* sender, CServer::onClipboardChanged(CBaseClientProxy* sender,
ClipboardID id, UInt32 seqNum) ClipboardID id, UInt32 seqNum)
@ -1970,6 +1980,16 @@ CServer::onGameDeviceTimingReq()
m_active->gameDeviceTimingReq(); m_active->gameDeviceTimingReq();
} }
void
CServer::onFileChunkSending(const UInt8* data)
{
LOG((CLOG_DEBUG1 "onFileChunkSending"));
assert(m_active != NULL);
// relay
m_active->fileChunkSending(data[0], &data[1]);
}
bool bool
CServer::addClient(CBaseClientProxy* client) CServer::addClient(CBaseClientProxy* client)
{ {

View File

@ -103,11 +103,12 @@ public:
client (local screen) \p primaryClient. The client retains client (local screen) \p primaryClient. The client retains
ownership of \p primaryClient. ownership of \p primaryClient.
*/ */
CServer(const CConfig& config, CPrimaryClient* primaryClient, CScreen* screen, IEventQueue* events); CServer(CConfig& config, CPrimaryClient* primaryClient, CScreen* screen, IEventQueue* events);
~CServer(); ~CServer();
#ifdef TEST_ENV #ifdef TEST_ENV
CServer() : m_mock(true), m_events(NULL), m_config(NULL) { } CServer() : m_mock(true), m_config(NULL) { }
void setActive(CBaseClientProxy* active) { m_active = active; }
#endif #endif
//! @name manipulators //! @name manipulators
@ -297,6 +298,7 @@ private:
void handleLockCursorToScreenEvent(const CEvent&, void*); void handleLockCursorToScreenEvent(const CEvent&, void*);
void handleFakeInputBeginEvent(const CEvent&, void*); void handleFakeInputBeginEvent(const CEvent&, void*);
void handleFakeInputEndEvent(const CEvent&, void*); void handleFakeInputEndEvent(const CEvent&, void*);
void handleFileChunkSendingEvent(const CEvent&, void*);
// event processing // event processing
void onClipboardChanged(CBaseClientProxy* sender, void onClipboardChanged(CBaseClientProxy* sender,
@ -316,6 +318,7 @@ private:
void onGameDeviceSticks(GameDeviceID id, SInt16 x1, SInt16 y1, SInt16 x2, SInt16 y2); void onGameDeviceSticks(GameDeviceID id, SInt16 x1, SInt16 y1, SInt16 x2, SInt16 y2);
void onGameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2); void onGameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2);
void onGameDeviceTimingReq(); void onGameDeviceTimingReq();
void onFileChunkSending(const UInt8* data);
// add client to list and attach event handlers for client // add client to list and attach event handlers for client
bool addClient(CBaseClientProxy*); bool addClient(CBaseClientProxy*);
@ -386,7 +389,7 @@ private:
SInt32 m_xDelta2, m_yDelta2; SInt32 m_xDelta2, m_yDelta2;
// current configuration // current configuration
CConfig m_config; CConfig* m_config;
// input filter (from m_config); // input filter (from m_config);
CInputFilter* m_inputFilter; CInputFilter* m_inputFilter;

View File

@ -357,7 +357,7 @@ CApp::initApp(int argc, const char** argv)
void void
CApp::initIpcClient() CApp::initIpcClient()
{ {
m_ipcClient = new CIpcClient(m_events); m_ipcClient = new CIpcClient(m_events, m_socketMultiplexer);
m_ipcClient->connect(); m_ipcClient->connect();
m_events->adoptHandler( m_events->adoptHandler(

View File

@ -35,6 +35,7 @@ class ILogOutputter;
class CFileLogOutputter; class CFileLogOutputter;
class CScreen; class CScreen;
class IEventQueue; class IEventQueue;
class CSocketMultiplexer;
typedef IArchTaskBarReceiver* (*CreateTaskBarReceiverFunc)(const CBufferedLogOutputter*, IEventQueue* events); typedef IArchTaskBarReceiver* (*CreateTaskBarReceiverFunc)(const CBufferedLogOutputter*, IEventQueue* events);
@ -97,6 +98,9 @@ public:
virtual IEventQueue* getEvents() const { return m_events; } virtual IEventQueue* getEvents() const { return m_events; }
void setSocketMultiplexer(CSocketMultiplexer* sm) { m_socketMultiplexer = sm; }
CSocketMultiplexer* getSocketMultiplexer() const { return m_socketMultiplexer; }
private: private:
void handleIpcMessage(const CEvent&, void*); void handleIpcMessage(const CEvent&, void*);
@ -117,6 +121,7 @@ private:
ARCH_APP_UTIL m_appUtil; ARCH_APP_UTIL m_appUtil;
CIpcClient* m_ipcClient; CIpcClient* m_ipcClient;
IEventQueue* m_events; IEventQueue* m_events;
CSocketMultiplexer* m_socketMultiplexer;
}; };
#define BYE "\nTry `%s --help' for more information." #define BYE "\nTry `%s --help' for more information."

View File

@ -391,7 +391,13 @@ CClient*
CClientApp::openClient(const CString& name, const CNetworkAddress& address, CScreen* screen, const CCryptoOptions& crypto) CClientApp::openClient(const CString& name, const CNetworkAddress& address, CScreen* screen, const CCryptoOptions& crypto)
{ {
CClient* client = new CClient( CClient* client = new CClient(
m_events, name, address, new CTCPSocketFactory(m_events), NULL, screen, crypto); m_events,
name,
address,
new CTCPSocketFactory(m_events, getSocketMultiplexer()),
NULL,
screen,
crypto);
try { try {
m_events->adoptHandler( m_events->adoptHandler(
@ -522,6 +528,7 @@ CClientApp::mainLoop()
// create socket multiplexer. this must happen after daemonization // create socket multiplexer. this must happen after daemonization
// on unix because threads evaporate across a fork(). // on unix because threads evaporate across a fork().
CSocketMultiplexer multiplexer; CSocketMultiplexer multiplexer;
setSocketMultiplexer(&multiplexer);
// start client, etc // start client, etc
appUtil().startNode(); appUtil().startNode();

View File

@ -203,7 +203,7 @@ CDaemonApp::mainLoop(bool logToFile)
CSocketMultiplexer multiplexer; CSocketMultiplexer multiplexer;
// uses event queue, must be created here. // uses event queue, must be created here.
m_ipcServer = new CIpcServer(m_events); m_ipcServer = new CIpcServer(m_events, &multiplexer);
// send logging to gui via ipc, log system adopts outputter. // send logging to gui via ipc, log system adopts outputter.
m_ipcLogOutputter = new CIpcLogOutputter(*m_ipcServer); m_ipcLogOutputter = new CIpcLogOutputter(*m_ipcServer);

View File

@ -35,7 +35,8 @@ CScreen::CScreen(IPlatformScreen* platformScreen, IEventQueue* events) :
m_enabled(false), m_enabled(false),
m_entered(m_isPrimary), m_entered(m_isPrimary),
m_screenSaverSync(true), m_screenSaverSync(true),
m_fakeInput(false) m_fakeInput(false),
m_mock(false)
{ {
assert(m_screen != NULL); assert(m_screen != NULL);
@ -47,6 +48,10 @@ CScreen::CScreen(IPlatformScreen* platformScreen, IEventQueue* events) :
CScreen::~CScreen() CScreen::~CScreen()
{ {
if (m_mock) {
return;
}
if (m_enabled) { if (m_enabled) {
disable(); disable();
} }

View File

@ -40,6 +40,10 @@ public:
CScreen(IPlatformScreen* platformScreen, IEventQueue* events); CScreen(IPlatformScreen* platformScreen, IEventQueue* events);
virtual ~CScreen(); virtual ~CScreen();
#ifdef TEST_ENV
CScreen() : m_mock(true) { }
#endif
//! @name manipulators //! @name manipulators
//@{ //@{
@ -49,14 +53,14 @@ public:
For a secondary screen it also means disabling the screen saver if For a secondary screen it also means disabling the screen saver if
synchronizing it and preparing to synthesize events. synchronizing it and preparing to synthesize events.
*/ */
void enable(); virtual void enable();
//! Deactivate screen //! Deactivate screen
/*! /*!
Undoes the operations in activate() and events are no longer Undoes the operations in activate() and events are no longer
reported. It also releases keys that are logically pressed. reported. It also releases keys that are logically pressed.
*/ */
void disable(); virtual void disable();
//! Enter screen //! Enter screen
/*! /*!
@ -208,14 +212,14 @@ public:
/*! /*!
Resets all options to their default values. Resets all options to their default values.
*/ */
void resetOptions(); virtual void resetOptions();
//! Notify of options changes //! Notify of options changes
/*! /*!
Set options to given values. Ignores unknown options and doesn't Set options to given values. Ignores unknown options and doesn't
modify options that aren't given in \c options. modify options that aren't given in \c options.
*/ */
void setOptions(const COptionsList& options); virtual void setOptions(const COptionsList& options);
//! Set clipboard sequence number //! Set clipboard sequence number
/*! /*!
@ -343,6 +347,8 @@ private:
bool m_fakeInput; bool m_fakeInput;
IEventQueue* m_events; IEventQueue* m_events;
bool m_mock;
}; };
#endif #endif

View File

@ -670,8 +670,11 @@ CClientListener*
CServerApp::openClientListener(const CNetworkAddress& address) CServerApp::openClientListener(const CNetworkAddress& address)
{ {
CClientListener* listen = new CClientListener( CClientListener* listen = new CClientListener(
address, new CTCPSocketFactory(m_events), address,
NULL, args().m_crypto, m_events); new CTCPSocketFactory(m_events, getSocketMultiplexer()),
NULL,
args().m_crypto,
m_events);
m_events->adoptHandler( m_events->adoptHandler(
m_events->forCClientListener().connected(), listen, m_events->forCClientListener().connected(), listen,
@ -682,7 +685,7 @@ CServerApp::openClientListener(const CNetworkAddress& address)
} }
CServer* CServer*
CServerApp::openServer(const CConfig& config, CPrimaryClient* primaryClient) CServerApp::openServer(CConfig& config, CPrimaryClient* primaryClient)
{ {
CServer* server = new CServer(config, primaryClient, s_serverScreen, m_events); CServer* server = new CServer(config, primaryClient, s_serverScreen, m_events);
@ -720,6 +723,7 @@ CServerApp::mainLoop()
// create socket multiplexer. this must happen after daemonization // create socket multiplexer. this must happen after daemonization
// on unix because threads evaporate across a fork(). // on unix because threads evaporate across a fork().
CSocketMultiplexer multiplexer; CSocketMultiplexer multiplexer;
setSocketMultiplexer(&multiplexer);
// if configuration has no screens then add this system // if configuration has no screens then add this system
// as the default // as the default

View File

@ -100,7 +100,7 @@ public:
void handleSuspend(const CEvent&, void*); void handleSuspend(const CEvent&, void*);
void handleResume(const CEvent&, void*); void handleResume(const CEvent&, void*);
CClientListener* openClientListener(const CNetworkAddress& address); CClientListener* openClientListener(const CNetworkAddress& address);
CServer* openServer(const CConfig& config, CPrimaryClient* primaryClient); CServer* openServer(CConfig& config, CPrimaryClient* primaryClient);
void handleNoClients(const CEvent&, void*); void handleNoClients(const CEvent&, void*);
bool startServer(); bool startServer();
int mainLoop(); int mainLoop();

View File

@ -16,6 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once
#include "IInterface.h" #include "IInterface.h"
class INode : IInterface { class INode : IInterface {

View File

@ -51,6 +51,7 @@ const char* kMsgDGameSticks = "DGST%1i%2i%2i%2i%2i";
const char* kMsgDGameTriggers = "DGTR%1i%1i%1i"; const char* kMsgDGameTriggers = "DGTR%1i%1i%1i";
const char* kMsgDGameFeedback = "DGFB%1i%2i%2i"; const char* kMsgDGameFeedback = "DGFB%1i%2i%2i";
const char* kMsgDCryptoIv = "DCIV%s"; const char* kMsgDCryptoIv = "DCIV%s";
const char* kMsgDFileTransfer = "DFTR%1i%s";
const char* kMsgQInfo = "QINF"; const char* kMsgQInfo = "QINF";
const char* kMsgEIncompatible = "EICV%2i%2i"; const char* kMsgEIncompatible = "EICV%2i%2i";
const char* kMsgEBusy = "EBSY"; const char* kMsgEBusy = "EBSY";

View File

@ -29,7 +29,7 @@
// adds horizontal mouse scrolling // adds horizontal mouse scrolling
// 1.4: adds game device support // 1.4: adds game device support
static const SInt16 kProtocolMajorVersion = 1; static const SInt16 kProtocolMajorVersion = 1;
static const SInt16 kProtocolMinorVersion = 4; static const SInt16 kProtocolMinorVersion = 5;
// default contact port number // default contact port number
static const UInt16 kDefaultPort = 24800; static const UInt16 kDefaultPort = 24800;
@ -288,6 +288,13 @@ extern const char* kMsgDSetOptions;
// cryptography stream. // cryptography stream.
extern const char* kMsgDCryptoIv; extern const char* kMsgDCryptoIv;
// file data: primary <-> secondary
// transfer file data. A mark is used in the first byte.
// 0 means the content followed is the file size.
// 1 means the content followed is the chunk data.
// 2 means the file transfer is finished.
extern const char* kMsgDFileTransfer;
// //
// query codes // query codes
// //

View File

@ -35,7 +35,7 @@
#include "CString.h" #include "CString.h"
#include "CIpcServerProxy.h" #include "CIpcServerProxy.h"
#include "CIpcMessage.h" #include "CIpcMessage.h"
#include "CSimpleEventQueueBuffer.h" #include "CTestEventQueue.h"
#define TEST_IPC_PORT 24802 #define TEST_IPC_PORT 24802
@ -49,18 +49,9 @@ public:
void sendMessageToServer_serverHandleMessageReceived(const CEvent&, void*); void sendMessageToServer_serverHandleMessageReceived(const CEvent&, void*);
void sendMessageToClient_serverHandleClientConnected(const CEvent&, void*); void sendMessageToClient_serverHandleClientConnected(const CEvent&, void*);
void sendMessageToClient_clientHandleMessageReceived(const CEvent&, void*); void sendMessageToClient_clientHandleMessageReceived(const CEvent&, void*);
void handleQuitTimeout(const CEvent&, void* vclient);
void raiseQuitEvent();
void initQuitTimeout(double timeout);
void cleanupQuitTimeout();
private:
void timeoutThread(void*);
public: public:
CSocketMultiplexer m_multiplexer; CSocketMultiplexer m_multiplexer;
CEventQueue m_events;
CEventQueueTimer* m_quitTimeoutTimer;
bool m_connectToServer_helloMessageReceived; bool m_connectToServer_helloMessageReceived;
bool m_connectToServer_hasClientNode; bool m_connectToServer_hasClientNode;
CIpcServer* m_connectToServer_server; CIpcServer* m_connectToServer_server;
@ -68,12 +59,14 @@ public:
CString m_sendMessageToClient_receivedString; CString m_sendMessageToClient_receivedString;
CIpcClient* m_sendMessageToServer_client; CIpcClient* m_sendMessageToServer_client;
CIpcServer* m_sendMessageToClient_server; CIpcServer* m_sendMessageToClient_server;
CTestEventQueue m_events;
}; };
TEST_F(CIpcTests, connectToServer) TEST_F(CIpcTests, connectToServer)
{ {
CIpcServer server(&m_events, TEST_IPC_PORT); CSocketMultiplexer socketMultiplexer;
CIpcServer server(&m_events, &socketMultiplexer, TEST_IPC_PORT);
server.listen(); server.listen();
m_connectToServer_server = &server; m_connectToServer_server = &server;
@ -82,13 +75,13 @@ TEST_F(CIpcTests, connectToServer)
new TMethodEventJob<CIpcTests>( new TMethodEventJob<CIpcTests>(
this, &CIpcTests::connectToServer_handleMessageReceived)); this, &CIpcTests::connectToServer_handleMessageReceived));
CIpcClient client(&m_events, TEST_IPC_PORT); CIpcClient client(&m_events, &socketMultiplexer, TEST_IPC_PORT);
client.connect(); client.connect();
initQuitTimeout(5); m_events.initQuitTimeout(5);
m_events.loop(); m_events.loop();
m_events.removeHandler(m_events.forCIpcServer().messageReceived(), &server); m_events.removeHandler(m_events.forCIpcServer().messageReceived(), &server);
cleanupQuitTimeout(); m_events.cleanupQuitTimeout();
EXPECT_EQ(true, m_connectToServer_helloMessageReceived); EXPECT_EQ(true, m_connectToServer_helloMessageReceived);
EXPECT_EQ(true, m_connectToServer_hasClientNode); EXPECT_EQ(true, m_connectToServer_hasClientNode);
@ -96,7 +89,8 @@ TEST_F(CIpcTests, connectToServer)
TEST_F(CIpcTests, sendMessageToServer) TEST_F(CIpcTests, sendMessageToServer)
{ {
CIpcServer server(&m_events, TEST_IPC_PORT); CSocketMultiplexer socketMultiplexer;
CIpcServer server(&m_events, &socketMultiplexer, TEST_IPC_PORT);
server.listen(); server.listen();
// event handler sends "test" command to server. // event handler sends "test" command to server.
@ -105,21 +99,22 @@ TEST_F(CIpcTests, sendMessageToServer)
new TMethodEventJob<CIpcTests>( new TMethodEventJob<CIpcTests>(
this, &CIpcTests::sendMessageToServer_serverHandleMessageReceived)); this, &CIpcTests::sendMessageToServer_serverHandleMessageReceived));
CIpcClient client(&m_events, TEST_IPC_PORT); CIpcClient client(&m_events, &socketMultiplexer, TEST_IPC_PORT);
client.connect(); client.connect();
m_sendMessageToServer_client = &client; m_sendMessageToServer_client = &client;
initQuitTimeout(5); m_events.initQuitTimeout(5);
m_events.loop(); m_events.loop();
m_events.removeHandler(m_events.forCIpcServer().messageReceived(), &server); m_events.removeHandler(m_events.forCIpcServer().messageReceived(), &server);
cleanupQuitTimeout(); m_events.cleanupQuitTimeout();
EXPECT_EQ("test", m_sendMessageToServer_receivedString); EXPECT_EQ("test", m_sendMessageToServer_receivedString);
} }
TEST_F(CIpcTests, sendMessageToClient) TEST_F(CIpcTests, sendMessageToClient)
{ {
CIpcServer server(&m_events, TEST_IPC_PORT); CSocketMultiplexer socketMultiplexer;
CIpcServer server(&m_events, &socketMultiplexer, TEST_IPC_PORT);
server.listen(); server.listen();
m_sendMessageToClient_server = &server; m_sendMessageToClient_server = &server;
@ -129,7 +124,7 @@ TEST_F(CIpcTests, sendMessageToClient)
new TMethodEventJob<CIpcTests>( new TMethodEventJob<CIpcTests>(
this, &CIpcTests::sendMessageToClient_serverHandleClientConnected)); this, &CIpcTests::sendMessageToClient_serverHandleClientConnected));
CIpcClient client(&m_events, TEST_IPC_PORT); CIpcClient client(&m_events, &socketMultiplexer, TEST_IPC_PORT);
client.connect(); client.connect();
m_events.adoptHandler( m_events.adoptHandler(
@ -137,17 +132,16 @@ TEST_F(CIpcTests, sendMessageToClient)
new TMethodEventJob<CIpcTests>( new TMethodEventJob<CIpcTests>(
this, &CIpcTests::sendMessageToClient_clientHandleMessageReceived)); this, &CIpcTests::sendMessageToClient_clientHandleMessageReceived));
initQuitTimeout(5); m_events.initQuitTimeout(5);
m_events.loop(); m_events.loop();
m_events.removeHandler(m_events.forCIpcServer().messageReceived(), &server); m_events.removeHandler(m_events.forCIpcServer().messageReceived(), &server);
m_events.removeHandler(m_events.forCIpcClient().messageReceived(), &client); m_events.removeHandler(m_events.forCIpcClient().messageReceived(), &client);
cleanupQuitTimeout(); m_events.cleanupQuitTimeout();
EXPECT_EQ("test", m_sendMessageToClient_receivedString); EXPECT_EQ("test", m_sendMessageToClient_receivedString);
} }
CIpcTests::CIpcTests() : CIpcTests::CIpcTests() :
m_quitTimeoutTimer(nullptr),
m_connectToServer_helloMessageReceived(false), m_connectToServer_helloMessageReceived(false),
m_connectToServer_hasClientNode(false), m_connectToServer_hasClientNode(false),
m_connectToServer_server(nullptr), m_connectToServer_server(nullptr),
@ -168,7 +162,7 @@ CIpcTests::connectToServer_handleMessageReceived(const CEvent& e, void*)
m_connectToServer_hasClientNode = m_connectToServer_hasClientNode =
m_connectToServer_server->hasClients(kIpcClientNode); m_connectToServer_server->hasClients(kIpcClientNode);
m_connectToServer_helloMessageReceived = true; m_connectToServer_helloMessageReceived = true;
raiseQuitEvent(); m_events.raiseQuitEvent();
} }
} }
@ -185,7 +179,7 @@ CIpcTests::sendMessageToServer_serverHandleMessageReceived(const CEvent& e, void
CIpcCommandMessage* cm = static_cast<CIpcCommandMessage*>(m); CIpcCommandMessage* cm = static_cast<CIpcCommandMessage*>(m);
LOG((CLOG_DEBUG "got ipc command message, %d", cm->command().c_str())); LOG((CLOG_DEBUG "got ipc command message, %d", cm->command().c_str()));
m_sendMessageToServer_receivedString = cm->command(); m_sendMessageToServer_receivedString = cm->command();
raiseQuitEvent(); m_events.raiseQuitEvent();
} }
} }
@ -208,37 +202,6 @@ CIpcTests::sendMessageToClient_clientHandleMessageReceived(const CEvent& e, void
CIpcLogLineMessage* llm = static_cast<CIpcLogLineMessage*>(m); CIpcLogLineMessage* llm = static_cast<CIpcLogLineMessage*>(m);
LOG((CLOG_DEBUG "got ipc log message, %d", llm->logLine().c_str())); LOG((CLOG_DEBUG "got ipc log message, %d", llm->logLine().c_str()));
m_sendMessageToClient_receivedString = llm->logLine(); m_sendMessageToClient_receivedString = llm->logLine();
raiseQuitEvent(); m_events.raiseQuitEvent();
} }
} }
void
CIpcTests::raiseQuitEvent()
{
m_events.addEvent(CEvent(CEvent::kQuit));
}
void
CIpcTests::initQuitTimeout(double timeout)
{
assert(m_quitTimeoutTimer == nullptr);
m_quitTimeoutTimer = m_events.newOneShotTimer(timeout, NULL);
m_events.adoptHandler(CEvent::kTimer, m_quitTimeoutTimer,
new TMethodEventJob<CIpcTests>(
this, &CIpcTests::handleQuitTimeout));
}
void
CIpcTests::cleanupQuitTimeout()
{
m_events.removeHandler(CEvent::kTimer, m_quitTimeoutTimer);
delete m_quitTimeoutTimer;
m_quitTimeoutTimer = nullptr;
}
void
CIpcTests::handleQuitTimeout(const CEvent&, void* vclient)
{
LOG((CLOG_ERR "timeout"));
raiseQuitEvent();
}

View File

@ -14,9 +14,16 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
set(h
CTestEventQueue.h
)
set(src set(src
${h}
Main.cpp Main.cpp
CIpcTests.cpp CIpcTests.cpp
NetworkTests.cpp
CTestEventQueue.cpp
) )
if (WIN32) if (WIN32)
@ -50,11 +57,11 @@ set(inc
../../lib/mt ../../lib/mt
../../lib/net ../../lib/net
../../lib/platform ../../lib/platform
../../lib/server
../../lib/synergy ../../lib/synergy
../../../tools/gtest-1.6.0/include ../../../tools/gtest-1.6.0/include
../../../tools/gmock-1.6.0/include ../../../tools/gmock-1.6.0/include
../unittests ../unittests
../unittests/synergy
) )
if (UNIX) if (UNIX)
@ -72,4 +79,4 @@ endif()
include_directories(${inc}) include_directories(${inc})
add_executable(integtests ${src}) add_executable(integtests ${src})
target_link_libraries(integtests target_link_libraries(integtests
arch base client common io ipc mt net platform server synergy gtest gmock ${libs}) arch base client common io ipc mt net platform server synergy gtest gmock cryptopp ${libs})

View File

@ -0,0 +1,52 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* 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/>.
*/
#include "CTestEventQueue.h"
#include "CLog.h"
#include "TMethodEventJob.h"
#include "CSimpleEventQueueBuffer.h"
void
CTestEventQueue::raiseQuitEvent()
{
addEvent(CEvent(CEvent::kQuit));
}
void
CTestEventQueue::initQuitTimeout(double timeout)
{
assert(m_quitTimeoutTimer == nullptr);
m_quitTimeoutTimer = newOneShotTimer(timeout, NULL);
adoptHandler(CEvent::kTimer, m_quitTimeoutTimer,
new TMethodEventJob<CTestEventQueue>(
this, &CTestEventQueue::handleQuitTimeout));
}
void
CTestEventQueue::cleanupQuitTimeout()
{
removeHandler(CEvent::kTimer, m_quitTimeoutTimer);
delete m_quitTimeoutTimer;
m_quitTimeoutTimer = nullptr;
}
void
CTestEventQueue::handleQuitTimeout(const CEvent&, void* vclient)
{
LOG((CLOG_ERR "timeout"));
raiseQuitEvent();
}

View File

@ -0,0 +1,38 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* 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/>.
*/
#pragma once
#include "CEventQueue.h"
class CEventQueueTimer;
class CTestEventQueue : public CEventQueue {
public:
CTestEventQueue() : m_quitTimeoutTimer(nullptr) { }
void handleQuitTimeout(const CEvent&, void* vclient);
void raiseQuitEvent();
void initQuitTimeout(double timeout);
void cleanupQuitTimeout();
private:
void timeoutThread(void*);
private:
CEventQueueTimer* m_quitTimeoutTimer;
};

View File

@ -0,0 +1,188 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* 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/>.
*/
#include <gtest/gtest.h>
#include <iostream>
#include <fstream>
#define TEST_ENV
#include "CLog.h"
#include "CServer.h"
#include "CClient.h"
#include "TMethodEventJob.h"
#include "server/CMockConfig.h"
#include "server/CMockPrimaryClient.h"
#include "synergy/CMockScreen.h"
#include "CClientListener.h"
#include "CNetworkAddress.h"
#include "CTCPSocketFactory.h"
#include "CCryptoOptions.h"
#include "CSocketMultiplexer.h"
#include "CMSWindowsScreen.h"
#include "CGameDevice.h"
#include "CThread.h"
#include "TMethodJob.h"
#include "CTestEventQueue.h"
#include "server/CMockInputFilter.h"
using ::testing::_;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::Invoke;
#define TEST_PORT 24803
#define TEST_HOST "localhost"
const int klargeDataSize = 512;
char g_largeData[klargeDataSize] = "large data:head.1221412312341244213123fdsfasdawdwadwadacwdd.12321412312341244213123fdsfasdawdwadwadacwdawddawdwacawdawd232141231awddawdwacawdawd2321412312341244213123fdsfasdawdwadacwdawddawdwacawdtrtetawdawdwaewe1213412321412312341244213123fdsfasdawdwadacwdawddawdwacawdawdawdwaewe121341awdwaewedacwdawddawdwacawdawd2321412312341244213123fdsfasdawdwadacwdawddawdwacawdtrtetawdawdwaewe1213412321412312341244213123fdsfasdawdwadacwdawddawdwacawdawdawdwaewe121341awdwaewe12134123njk1u31i2nm3e123hu23oi132213njk.tail";
void sendFileToClient_getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h);
void sendFileToClient_getCursorPos(SInt32& x, SInt32& y);
class NetworkTests : public ::testing::Test
{
public:
NetworkTests() { }
void sendData(CServer* server);
void sendFileToClient_handleClientConnected(const CEvent&, void* vlistener);
void sendFileToClient_fileRecieveComplete(const CEvent&, void*);
public:
CTestEventQueue m_events;
};
TEST_F(NetworkTests, sendFileToClient)
{
// server and client
CNetworkAddress serverAddress(TEST_HOST, TEST_PORT);
CCryptoOptions cryptoOptions;
serverAddress.resolve();
// server
CSocketMultiplexer serverSocketMultiplexer;
CTCPSocketFactory* serverSocketFactory = new CTCPSocketFactory(&m_events, &serverSocketMultiplexer);
CClientListener listener(serverAddress, serverSocketFactory, NULL, cryptoOptions, &m_events);
NiceMock<CMockScreen> serverScreen;
NiceMock<CMockPrimaryClient> primaryClient;
NiceMock<CMockConfig> serverConfig;
NiceMock<CMockInputFilter> serverInputFilter;
m_events.adoptHandler(
m_events.forCClientListener().connected(), &listener,
new TMethodEventJob<NetworkTests>(
this, &NetworkTests::sendFileToClient_handleClientConnected, &listener));
ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true));
ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter));
CServer server(serverConfig, &primaryClient, &serverScreen, &m_events);
server.m_mock = true;
listener.setServer(&server);
// client
NiceMock<CMockScreen> clientScreen;
CSocketMultiplexer clientSocketMultiplexer;
CTCPSocketFactory* clientSocketFactory = new CTCPSocketFactory(&m_events, &clientSocketMultiplexer);
ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(sendFileToClient_getShape));
ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(sendFileToClient_getCursorPos));
CClient client(&m_events, "stub", serverAddress, clientSocketFactory, NULL, &clientScreen, cryptoOptions);
m_events.adoptHandler(
m_events.forIScreen().fileRecieveComplete(), &client,
new TMethodEventJob<NetworkTests>(
this, &NetworkTests::sendFileToClient_fileRecieveComplete));
client.connect();
m_events.initQuitTimeout(10);
m_events.loop();
m_events.cleanupQuitTimeout();
}
void
NetworkTests::sendFileToClient_handleClientConnected(const CEvent&, void* vlistener)
{
CClientListener* listener = reinterpret_cast<CClientListener*>(vlistener);
CServer* server = listener->getServer();
CClientProxy* client = listener->getNextClient();
if (client == NULL) {
throw std::exception("client is null");
}
CBaseClientProxy* bcp = reinterpret_cast<CBaseClientProxy*>(client);
server->adoptClient(bcp);
server->setActive(bcp);
sendData(server);
}
void
NetworkTests::sendFileToClient_fileRecieveComplete(const CEvent& event, void*)
{
CClient* client = reinterpret_cast<CClient*>(event.getTarget());
EXPECT_TRUE(client->isReceivedFileSizeValid());
m_events.raiseQuitEvent();
}
void
NetworkTests::sendData(CServer* server)
{
UInt8* largeDataSize = new UInt8[5];
largeDataSize[0] = '0';
largeDataSize[1] = '5';
largeDataSize[2] = '1';
largeDataSize[3] = '1';
largeDataSize[4] = '\0';
// transfer data from server -> client
m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), server, largeDataSize));
UInt8* largeData = new UInt8[klargeDataSize + 1];
largeData[0] = '1';
memcpy(&largeData[1], g_largeData, klargeDataSize);
m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), server, (UInt8*)largeData));
UInt8* transferFinished = new UInt8[2];
transferFinished[0] = '2';
transferFinished[1] = '\0';
m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), server, transferFinished));
}
void
sendFileToClient_getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h)
{
x = 0;
y = 0;
w = 1;
h = 1;
}
void
sendFileToClient_getCursorPos(SInt32& x, SInt32& y)
{
x = 0;
y = 0;
}

View File

@ -27,8 +27,8 @@
#include "CMSWindowsScreen.h" #include "CMSWindowsScreen.h"
#include "CMSWindowsScreenSaver.h" #include "CMSWindowsScreenSaver.h"
#include "TMethodJob.h" #include "TMethodJob.h"
#include "CMockEventQueue.h" #include "synergy/CMockEventQueue.h"
#include "CMockKeyMap.h" #include "synergy/CMockKeyMap.h"
// wParam = flags, HIBYTE(lParam) = virtual key, LOBYTE(lParam) = scan code // wParam = flags, HIBYTE(lParam) = virtual key, LOBYTE(lParam) = scan code
#define SYNERGY_MSG_FAKE_KEY SYNERGY_HOOK_LAST_MSG + 4 #define SYNERGY_MSG_FAKE_KEY SYNERGY_HOOK_LAST_MSG + 4

View File

@ -22,6 +22,10 @@ set(h
io/CMockStream.h io/CMockStream.h
server/CMockServer.h server/CMockServer.h
io/CMockCryptoStream.h io/CMockCryptoStream.h
synergy/CMockScreen.h
server/CMockConfig.h
server/CMockPrimaryClient.h
server/CMockInputFilter.h
) )
set(src set(src

View File

@ -26,7 +26,6 @@ class CMockCryptoStream : public CCryptoStream
public: public:
CMockCryptoStream(IEventQueue* eventQueue, IStream* stream) : CMockCryptoStream(IEventQueue* eventQueue, IStream* stream) :
CCryptoStream(eventQueue, stream, CCryptoOptions("gcm", "stub"), false) { } CCryptoStream(eventQueue, stream, CCryptoOptions("gcm", "stub"), false) { }
MOCK_METHOD2(read, UInt32(void*, UInt32)); MOCK_METHOD2(read, UInt32(void*, UInt32));
MOCK_METHOD2(write, void(const void*, UInt32)); MOCK_METHOD2(write, void(const void*, UInt32));
}; };

View File

@ -0,0 +1,31 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* 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/>.
*/
#pragma once
#include <gmock/gmock.h>
#define TEST_ENV
#include "CConfig.h"
class CMockConfig : public CConfig
{
public:
CMockConfig() : CConfig() { }
MOCK_METHOD0(getInputFilter, CInputFilter*());
MOCK_CONST_METHOD1(isScreen, bool(const CString&));
};

View File

@ -0,0 +1,29 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* 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/>.
*/
#pragma once
#include <gmock/gmock.h>
#define TEST_ENV
#include "CInputFilter.h"
class CMockInputFilter : public CInputFilter
{
public:
MOCK_METHOD1(setPrimaryClient, void(CPrimaryClient*));
};

View File

@ -0,0 +1,40 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* 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/>.
*/
#pragma once
#include <gmock/gmock.h>
#define TEST_ENV
#include "CPrimaryClient.h"
#include "CString.h"
class CMockPrimaryClient : public CPrimaryClient
{
public:
MOCK_CONST_METHOD0(getEventTarget, void*());
MOCK_CONST_METHOD2(getCursorPos, void(SInt32&, SInt32&));
MOCK_CONST_METHOD2(setJumpCursorPos, void(SInt32, SInt32));
MOCK_METHOD1(reconfigure, void(UInt32));
MOCK_METHOD0(resetOptions, void());
MOCK_METHOD1(setOptions, void(const COptionsList&));
MOCK_METHOD0(enable, void());
MOCK_METHOD0(disable, void());
MOCK_METHOD2(registerHotKey, UInt32(KeyID, KeyModifierMask));
MOCK_CONST_METHOD0(getToggleMask, KeyModifierMask());
MOCK_METHOD1(unregisterHotKey, void(UInt32));
};

View File

@ -0,0 +1,35 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* 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/>.
*/
#pragma once
#include <gmock/gmock.h>
#define TEST_ENV
#include "CScreen.h"
class CMockScreen : public CScreen
{
public:
CMockScreen() : CScreen() { }
MOCK_METHOD0(disable, void());
MOCK_CONST_METHOD4(getShape, void(SInt32&, SInt32&, SInt32&, SInt32&));
MOCK_CONST_METHOD2(getCursorPos, void(SInt32&, SInt32&));
MOCK_METHOD0(resetOptions, void());
MOCK_METHOD1(setOptions, void(const COptionsList&));
MOCK_METHOD0(enable, void());
};