Add ability to initiate connection from server (#7222)
* Add a new hidden readonly option "initiateConnectionFromServer" * Add option "clientHostMode" * Add host mode to the client GUI * Hide main window controls for the client host mode * Store client address into the server config file * Add client mode the server * Pass option --host into the client * Add --host parameter into the synergyc * Add InverseSocketFactory * Add InverseClientSocket * Add InverseServerSocket * Add AutoArchSocket * Add listener on the client side * Add connection from the server side * Fix code smells * Additional code smells * Requested changes Co-authored-by: Serhii Hadzhilov <serhii@symless.com>
This commit is contained in:
parent
452c631d6e
commit
c37b0d2fcf
30 changed files with 1468 additions and 116 deletions
|
|
@ -1,3 +1,9 @@
|
||||||
|
1.14.6
|
||||||
|
======
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
- #7222 Ability to initiate connection from server
|
||||||
|
|
||||||
1.14.5
|
1.14.5
|
||||||
======
|
======
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@ cmake_minimum_required (VERSION 3.4)
|
||||||
|
|
||||||
set (SYNERGY_VERSION_MAJOR 1)
|
set (SYNERGY_VERSION_MAJOR 1)
|
||||||
set (SYNERGY_VERSION_MINOR 14)
|
set (SYNERGY_VERSION_MINOR 14)
|
||||||
set (SYNERGY_VERSION_PATCH 5)
|
set (SYNERGY_VERSION_PATCH 6)
|
||||||
set (SYNERGY_VERSION_BUILD 1)
|
set (SYNERGY_VERSION_BUILD 1)
|
||||||
set (SYNERGY_VERSION_STAGE "stable")
|
set (SYNERGY_VERSION_STAGE "snapshot")
|
||||||
|
|
||||||
#
|
#
|
||||||
# Version from CI
|
# Version from CI
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,10 @@ const char* AppConfig::m_SynergySettingsName[] = {
|
||||||
"eliteBackersUrl",
|
"eliteBackersUrl",
|
||||||
"guid",
|
"guid",
|
||||||
"licenseRegistryUrl",
|
"licenseRegistryUrl",
|
||||||
"licenseNextCheck"
|
"licenseNextCheck",
|
||||||
|
"initiateConnectionFromServer",
|
||||||
|
"clientHostMode",
|
||||||
|
"serverClientMode"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* logLevelNames[] =
|
static const char* logLevelNames[] =
|
||||||
|
|
@ -99,19 +102,19 @@ AppConfig::AppConfig() :
|
||||||
m_Port(24800),
|
m_Port(24800),
|
||||||
m_Interface(),
|
m_Interface(),
|
||||||
m_LogLevel(0),
|
m_LogLevel(0),
|
||||||
|
m_LogToFile(),
|
||||||
m_WizardLastRun(0),
|
m_WizardLastRun(0),
|
||||||
m_ProcessMode(DEFAULT_PROCESS_MODE),
|
m_ProcessMode(DEFAULT_PROCESS_MODE),
|
||||||
|
m_StartedBefore(),
|
||||||
m_AutoConfig(true),
|
m_AutoConfig(true),
|
||||||
|
m_AutoConfigServer(),
|
||||||
m_ElevateMode(defaultElevateMode),
|
m_ElevateMode(defaultElevateMode),
|
||||||
|
m_Edition(kUnregistered),
|
||||||
m_CryptoEnabled(false),
|
m_CryptoEnabled(false),
|
||||||
m_AutoHide(false),
|
m_AutoHide(false),
|
||||||
m_LastExpiringWarningTime(0),
|
m_LastExpiringWarningTime(0),
|
||||||
m_AutoConfigServer(),
|
|
||||||
m_MinimizeToTray(false),
|
|
||||||
m_Edition(kUnregistered),
|
|
||||||
m_LogToFile(),
|
|
||||||
m_StartedBefore(),
|
|
||||||
m_ActivationHasRun(),
|
m_ActivationHasRun(),
|
||||||
|
m_MinimizeToTray(false),
|
||||||
m_ServerGroupChecked(),
|
m_ServerGroupChecked(),
|
||||||
m_UseExternalConfig(),
|
m_UseExternalConfig(),
|
||||||
m_UseInternalConfig(),
|
m_UseInternalConfig(),
|
||||||
|
|
@ -253,6 +256,9 @@ void AppConfig::loadSettings()
|
||||||
m_guid = loadCommonSetting(kGuid, QUuid::createUuid()).toString();
|
m_guid = loadCommonSetting(kGuid, QUuid::createUuid()).toString();
|
||||||
m_licenseRegistryUrl = loadCommonSetting(kLicenseRegistryUrl, "https://api2.prod.symless.com/license/register").toString();
|
m_licenseRegistryUrl = loadCommonSetting(kLicenseRegistryUrl, "https://api2.prod.symless.com/license/register").toString();
|
||||||
m_licenseNextCheck = loadCommonSetting(kLicenseNextCheck, 0).toULongLong();
|
m_licenseNextCheck = loadCommonSetting(kLicenseNextCheck, 0).toULongLong();
|
||||||
|
m_ClientHostMode = loadSetting(kClientHostMode, true).toBool();
|
||||||
|
m_ServerClientMode = loadSetting(kServerClientMode, true).toBool();
|
||||||
|
m_InitiateConnectionFromServer = loadSetting(kInitiateConnectionFromServer, false).toBool();
|
||||||
|
|
||||||
//only change the serial key if the settings being loaded contains a key
|
//only change the serial key if the settings being loaded contains a key
|
||||||
bool updateSerial = ConfigWriter::make()
|
bool updateSerial = ConfigWriter::make()
|
||||||
|
|
@ -320,6 +326,8 @@ void AppConfig::saveSettings()
|
||||||
setSetting(kPreventSleep, m_PreventSleep);
|
setSetting(kPreventSleep, m_PreventSleep);
|
||||||
setSetting(kLanguageSync, m_LanguageSync);
|
setSetting(kLanguageSync, m_LanguageSync);
|
||||||
setSetting(kInvertScrollDirection, m_InvertScrollDirection);
|
setSetting(kInvertScrollDirection, m_InvertScrollDirection);
|
||||||
|
setSetting(kClientHostMode, m_ClientHostMode);
|
||||||
|
setSetting(kServerClientMode, m_ServerClientMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_unsavedChanges = false;
|
m_unsavedChanges = false;
|
||||||
|
|
@ -512,6 +520,18 @@ void AppConfig::setLanguageSync(bool newValue) {
|
||||||
|
|
||||||
bool AppConfig::getPreventSleep() const { return m_PreventSleep; }
|
bool AppConfig::getPreventSleep() const { return m_PreventSleep; }
|
||||||
|
|
||||||
|
bool AppConfig::getClientHostMode() const {
|
||||||
|
return (m_ClientHostMode && getInitiateConnectionFromServer());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppConfig::getServerClientMode() const {
|
||||||
|
return (m_ServerClientMode && getInitiateConnectionFromServer());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppConfig::getInitiateConnectionFromServer() const {
|
||||||
|
return m_InitiateConnectionFromServer;
|
||||||
|
}
|
||||||
|
|
||||||
void AppConfig::setPreventSleep(bool newValue) {
|
void AppConfig::setPreventSleep(bool newValue) {
|
||||||
setSettingModified(m_PreventSleep, newValue);
|
setSettingModified(m_PreventSleep, newValue);
|
||||||
}
|
}
|
||||||
|
|
@ -641,6 +661,14 @@ void AppConfig::setServerHostname(const QString& newValue) {
|
||||||
setSettingModified(m_ServerHostname, newValue);
|
setSettingModified(m_ServerHostname, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AppConfig::setClientHostMode(bool newValue) {
|
||||||
|
setSettingModified(m_ClientHostMode, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppConfig::setServerClientMode(bool newValue) {
|
||||||
|
setSettingModified(m_ServerClientMode, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void AppConfig::setSettingModified(T &variable, const T& newValue) {
|
void AppConfig::setSettingModified(T &variable, const T& newValue) {
|
||||||
if (variable != newValue)
|
if (variable != newValue)
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,9 @@ class AppConfig: public QObject, public GUI::Config::ConfigBase
|
||||||
bool getLanguageSync() const;
|
bool getLanguageSync() const;
|
||||||
void setPreventSleep(bool b);
|
void setPreventSleep(bool b);
|
||||||
bool getPreventSleep() const;
|
bool getPreventSleep() const;
|
||||||
|
bool getClientHostMode() const;
|
||||||
|
bool getServerClientMode() const;
|
||||||
|
bool getInitiateConnectionFromServer() const;
|
||||||
#ifndef SYNERGY_ENTERPRISE
|
#ifndef SYNERGY_ENTERPRISE
|
||||||
bool activationHasRun() const;
|
bool activationHasRun() const;
|
||||||
AppConfig& activationHasRun(bool value);
|
AppConfig& activationHasRun(bool value);
|
||||||
|
|
@ -157,6 +160,8 @@ class AppConfig: public QObject, public GUI::Config::ConfigBase
|
||||||
void setUseInternalConfig(bool) ;
|
void setUseInternalConfig(bool) ;
|
||||||
void setClientGroupChecked(bool) ;
|
void setClientGroupChecked(bool) ;
|
||||||
void setServerHostname(const QString&);
|
void setServerHostname(const QString&);
|
||||||
|
void setClientHostMode(bool newValue);
|
||||||
|
void setServerClientMode(bool newValue);
|
||||||
|
|
||||||
/// @brief Set the path to the TLS/SSL certificate file that will be used
|
/// @brief Set the path to the TLS/SSL certificate file that will be used
|
||||||
/// @param [in] path The path to the Certificate
|
/// @param [in] path The path to the Certificate
|
||||||
|
|
@ -219,7 +224,10 @@ protected:
|
||||||
kEliteBackersUrl,
|
kEliteBackersUrl,
|
||||||
kGuid,
|
kGuid,
|
||||||
kLicenseRegistryUrl,
|
kLicenseRegistryUrl,
|
||||||
kLicenseNextCheck
|
kLicenseNextCheck,
|
||||||
|
kInitiateConnectionFromServer,
|
||||||
|
kClientHostMode,
|
||||||
|
kServerClientMode
|
||||||
};
|
};
|
||||||
|
|
||||||
void setScreenName(const QString& s);
|
void setScreenName(const QString& s);
|
||||||
|
|
@ -269,6 +277,9 @@ protected:
|
||||||
bool m_InvertScrollDirection = false;
|
bool m_InvertScrollDirection = false;
|
||||||
bool m_LanguageSync = true;
|
bool m_LanguageSync = true;
|
||||||
bool m_PreventSleep = false;
|
bool m_PreventSleep = false;
|
||||||
|
bool m_InitiateConnectionFromServer = false;
|
||||||
|
bool m_ClientHostMode = true;
|
||||||
|
bool m_ServerClientMode = true;
|
||||||
|
|
||||||
bool m_ServerGroupChecked;
|
bool m_ServerGroupChecked;
|
||||||
bool m_UseExternalConfig;
|
bool m_UseExternalConfig;
|
||||||
|
|
|
||||||
|
|
@ -307,6 +307,7 @@ void MainWindow::loadSettings()
|
||||||
enableClient(appConfig().getClientGroupChecked());
|
enableClient(appConfig().getClientGroupChecked());
|
||||||
|
|
||||||
m_pLineEditHostname->setText(appConfig().getServerHostname());
|
m_pLineEditHostname->setText(appConfig().getServerHostname());
|
||||||
|
m_pLineEditClienIp->setText(serverConfig().getClientAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::initConnections()
|
void MainWindow::initConnections()
|
||||||
|
|
@ -325,6 +326,7 @@ void MainWindow::saveSettings()
|
||||||
appConfig().setServerGroupChecked(m_pRadioGroupServer->isChecked());
|
appConfig().setServerGroupChecked(m_pRadioGroupServer->isChecked());
|
||||||
appConfig().setClientGroupChecked(m_pRadioGroupClient->isChecked());
|
appConfig().setClientGroupChecked(m_pRadioGroupClient->isChecked());
|
||||||
appConfig().setServerHostname(m_pLineEditHostname->text());
|
appConfig().setServerHostname(m_pLineEditHostname->text());
|
||||||
|
serverConfig().setClientAddress(m_pLineEditClienIp->text());
|
||||||
|
|
||||||
/* Save everything */
|
/* Save everything */
|
||||||
GUI::Config::ConfigWriter::make()->globalSave();
|
GUI::Config::ConfigWriter::make()->globalSave();
|
||||||
|
|
@ -836,7 +838,7 @@ bool MainWindow::clientArgs(QStringList& args, QString& app)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (m_pLineEditHostname->text().isEmpty())
|
if (m_pLineEditHostname->text().isEmpty() && !appConfig().getClientHostMode())
|
||||||
{
|
{
|
||||||
#if !defined(SYNERGY_ENTERPRISE) && defined(SYNERGY_AUTOCONFIG)
|
#if !defined(SYNERGY_ENTERPRISE) && defined(SYNERGY_AUTOCONFIG)
|
||||||
//check if autoconfig mode is enabled
|
//check if autoconfig mode is enabled
|
||||||
|
|
@ -858,18 +860,24 @@ bool MainWindow::clientArgs(QStringList& args, QString& app)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QString hostName = m_pLineEditHostname->text();
|
if (appConfig().getClientHostMode()) {
|
||||||
// if interface is IPv6 - ensure that ip is in square brackets
|
args <<"--host";
|
||||||
if (hostName.count(':') > 1) {
|
args <<":" + QString::number(appConfig().port());
|
||||||
if(hostName[0] != '[') {
|
}
|
||||||
hostName.insert(0, '[');
|
else {
|
||||||
}
|
QString hostName = m_pLineEditHostname->text();
|
||||||
if(hostName[hostName.size() - 1] != ']') {
|
// if interface is IPv6 - ensure that ip is in square brackets
|
||||||
hostName.push_back(']');
|
if (hostName.count(':') > 1) {
|
||||||
|
if(hostName[0] != '[') {
|
||||||
|
hostName.insert(0, '[');
|
||||||
|
}
|
||||||
|
if(hostName[hostName.size() - 1] != ']') {
|
||||||
|
hostName.push_back(']');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
args << hostName + ":" + QString::number(appConfig().port());
|
||||||
}
|
}
|
||||||
|
|
||||||
args << hostName + ":" + QString::number(appConfig().port());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -947,6 +955,14 @@ bool MainWindow::serverArgs(QStringList& args, QString& app)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (appConfig().getServerClientMode() &&
|
||||||
|
m_pLineEditClienIp->text().isEmpty())
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, tr("Client IP address or name is empty"),
|
||||||
|
tr("Please fill in a client IP address or name."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
// wrap in quotes so a malicious user can't start \Program.exe as admin.
|
// wrap in quotes so a malicious user can't start \Program.exe as admin.
|
||||||
app = QString("\"%1\"").arg(app);
|
app = QString("\"%1\"").arg(app);
|
||||||
|
|
@ -1330,6 +1346,8 @@ void MainWindow::on_m_pActionSettings_triggered()
|
||||||
auto result = SettingsDialog(this, appConfig()).exec();
|
auto result = SettingsDialog(this, appConfig()).exec();
|
||||||
if(result == QDialog::Accepted)
|
if(result == QDialog::Accepted)
|
||||||
{
|
{
|
||||||
|
enableServer(appConfig().getServerGroupChecked());
|
||||||
|
enableClient(appConfig().getClientGroupChecked());
|
||||||
auto state = synergyState();
|
auto state = synergyState();
|
||||||
if ((state == synergyConnected) || (state == synergyConnecting) || (state == synergyListening)) {
|
if ((state == synergyConnected) || (state == synergyConnecting) || (state == synergyListening)) {
|
||||||
restartSynergy();
|
restartSynergy();
|
||||||
|
|
@ -1497,6 +1515,17 @@ void MainWindow::enableServer(bool enable)
|
||||||
|
|
||||||
if (enable)
|
if (enable)
|
||||||
{
|
{
|
||||||
|
if (m_AppConfig->getServerClientMode()) {
|
||||||
|
m_pLabelClientIp->show();
|
||||||
|
m_pLineEditClienIp->show();
|
||||||
|
m_pButtonConnectToClient->show();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_pLabelClientIp->hide();
|
||||||
|
m_pLineEditClienIp->hide();
|
||||||
|
m_pButtonConnectToClient->hide();
|
||||||
|
}
|
||||||
|
|
||||||
m_pButtonConfigureServer->show();
|
m_pButtonConfigureServer->show();
|
||||||
m_pLabelServerState->show();
|
m_pLabelServerState->show();
|
||||||
updateLocalFingerprint();
|
updateLocalFingerprint();
|
||||||
|
|
@ -1507,6 +1536,9 @@ void MainWindow::enableServer(bool enable)
|
||||||
m_pLabelFingerprint->hide();
|
m_pLabelFingerprint->hide();
|
||||||
m_pButtonConfigureServer->hide();
|
m_pButtonConfigureServer->hide();
|
||||||
m_pLabelServerState->hide();
|
m_pLabelServerState->hide();
|
||||||
|
m_pLabelClientIp->hide();
|
||||||
|
m_pLineEditClienIp->hide();
|
||||||
|
m_pButtonConnectToClient->hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1517,9 +1549,18 @@ void MainWindow::enableClient(bool enable)
|
||||||
|
|
||||||
if (enable)
|
if (enable)
|
||||||
{
|
{
|
||||||
m_pLabelServerName->show();
|
if (m_AppConfig->getClientHostMode())
|
||||||
m_pLineEditHostname->show();
|
{
|
||||||
m_pButtonConnect->show();
|
m_pLabelServerName->hide();
|
||||||
|
m_pLineEditHostname->hide();
|
||||||
|
m_pButtonConnect->hide();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_pLabelServerName->show();
|
||||||
|
m_pLineEditHostname->show();
|
||||||
|
m_pButtonConnect->show();
|
||||||
|
}
|
||||||
m_pButtonToggleStart->setEnabled(enable);
|
m_pButtonToggleStart->setEnabled(enable);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -1558,3 +1599,8 @@ void MainWindow::on_m_pButtonConnect_clicked()
|
||||||
on_m_pButtonApply_clicked();
|
on_m_pButtonApply_clicked();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_m_pButtonConnectToClient_clicked()
|
||||||
|
{
|
||||||
|
on_m_pButtonApply_clicked();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -263,6 +263,7 @@ private slots:
|
||||||
void on_m_pComboServerList_currentIndexChanged(const QString &arg1);
|
void on_m_pComboServerList_currentIndexChanged(const QString &arg1);
|
||||||
|
|
||||||
void on_m_pButtonConnect_clicked();
|
void on_m_pButtonConnect_clicked();
|
||||||
|
void on_m_pButtonConnectToClient_clicked();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void windowShown();
|
void windowShown();
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>720</width>
|
<width>720</width>
|
||||||
<height>658</height>
|
<height>552</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
|
@ -244,11 +244,41 @@ background-color: rgba(192,192,192, 0.1);
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>20</width>
|
<width>20</width>
|
||||||
<height>40</height>
|
<height>20</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="m_pLabelClientIp">
|
||||||
|
<property name="text">
|
||||||
|
<string>Client IP address or name:</string>
|
||||||
|
</property>
|
||||||
|
<property name="indent">
|
||||||
|
<number>20</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>20</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="m_pLineEditClienIp"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="m_pButtonConnectToClient">
|
||||||
|
<property name="text">
|
||||||
|
<string>Connect</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="synergy_widgets::ServerStateLabel" name="m_pLabelServerState">
|
<widget class="synergy_widgets::ServerStateLabel" name="m_pLabelServerState">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|
@ -685,7 +715,6 @@ background-color: rgba(192,192,192, 0.1);
|
||||||
<font>
|
<font>
|
||||||
<family>Arial</family>
|
<family>Arial</family>
|
||||||
<pointsize>-1</pointsize>
|
<pointsize>-1</pointsize>
|
||||||
<weight>87</weight>
|
|
||||||
<bold>true</bold>
|
<bold>true</bold>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,10 @@ void ServerConfig::saveSettings()
|
||||||
settings().setValue("clipboardSharing", clipboardSharing());
|
settings().setValue("clipboardSharing", clipboardSharing());
|
||||||
settings().setValue("clipboardSharingSize", QVariant::fromValue(clipboardSharingSize()));
|
settings().setValue("clipboardSharingSize", QVariant::fromValue(clipboardSharingSize()));
|
||||||
|
|
||||||
|
if (!getClientAddress().isEmpty()) {
|
||||||
|
settings().setValue("clientAddress", getClientAddress());
|
||||||
|
}
|
||||||
|
|
||||||
writeSettings(settings(), switchCorners(), "switchCorner");
|
writeSettings(settings(), switchCorners(), "switchCorner");
|
||||||
|
|
||||||
settings().beginWriteArray("screens");
|
settings().beginWriteArray("screens");
|
||||||
|
|
@ -204,6 +208,7 @@ void ServerConfig::loadSettings()
|
||||||
setClipboardSharingSize(settings().value("clipboardSharingSize",
|
setClipboardSharingSize(settings().value("clipboardSharingSize",
|
||||||
(int) ServerConfig::defaultClipboardSharingSize()).toULongLong());
|
(int) ServerConfig::defaultClipboardSharingSize()).toULongLong());
|
||||||
setClipboardSharing(settings().value("clipboardSharing", true).toBool());
|
setClipboardSharing(settings().value("clipboardSharing", true).toBool());
|
||||||
|
setClientAddress(settings().value("clientAddress", "").toString());
|
||||||
|
|
||||||
readSettings(settings(), switchCorners(), "switchCorner", false, NumSwitchCorners);
|
readSettings(settings(), switchCorners(), "switchCorner", false, NumSwitchCorners);
|
||||||
|
|
||||||
|
|
@ -296,6 +301,10 @@ QTextStream& operator<<(QTextStream& outStream, const ServerConfig& config)
|
||||||
outStream << "\t" << "clipboardSharing = " << (config.clipboardSharing() ? "true" : "false") << endl;
|
outStream << "\t" << "clipboardSharing = " << (config.clipboardSharing() ? "true" : "false") << endl;
|
||||||
outStream << "\t" << "clipboardSharingSize = " << config.clipboardSharingSize() << endl;
|
outStream << "\t" << "clipboardSharingSize = " << config.clipboardSharingSize() << endl;
|
||||||
|
|
||||||
|
if (!config.getClientAddress().isEmpty()) {
|
||||||
|
outStream << "\t" << "clientAddress = " << config.getClientAddress() << endl;
|
||||||
|
}
|
||||||
|
|
||||||
if (config.hasSwitchDelay())
|
if (config.hasSwitchDelay())
|
||||||
outStream << "\t" << "switchDelay = " << config.switchDelay() << endl;
|
outStream << "\t" << "switchDelay = " << config.switchDelay() << endl;
|
||||||
|
|
||||||
|
|
@ -554,6 +563,22 @@ size_t ServerConfig::setClipboardSharingSize(size_t size) {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServerConfig::setClientAddress(const QString& address) {
|
||||||
|
if (m_pAppConfig->getServerClientMode()) {
|
||||||
|
m_clientAddress = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ServerConfig::getClientAddress() const {
|
||||||
|
QString clientAddress;
|
||||||
|
|
||||||
|
if (m_pAppConfig->getServerClientMode()) {
|
||||||
|
clientAddress = m_clientAddress.trimmed();
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientAddress;
|
||||||
|
}
|
||||||
|
|
||||||
QSettings &ServerConfig::settings() {
|
QSettings &ServerConfig::settings() {
|
||||||
using GUI::Config::ConfigWriter;
|
using GUI::Config::ConfigWriter;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,8 @@ class ServerConfig : public BaseConfig, public GUI::Config::ConfigBase
|
||||||
bool isScreenExists(const QString& screenName) const;
|
bool isScreenExists(const QString& screenName) const;
|
||||||
void addClient(const QString& clientName);
|
void addClient(const QString& clientName);
|
||||||
bool isHotkeysAvailable() const;
|
bool isHotkeysAvailable() const;
|
||||||
|
QString getClientAddress() const;
|
||||||
|
void setClientAddress(const QString& address);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QSettings& settings();
|
QSettings& settings();
|
||||||
|
|
@ -147,6 +149,7 @@ class ServerConfig : public BaseConfig, public GUI::Config::ConfigBase
|
||||||
bool m_DisableLockToScreen;
|
bool m_DisableLockToScreen;
|
||||||
bool m_ClipboardSharing;
|
bool m_ClipboardSharing;
|
||||||
size_t m_ClipboardSharingSize;
|
size_t m_ClipboardSharingSize;
|
||||||
|
QString m_clientAddress;
|
||||||
MainWindow* m_pMainWindow;
|
MainWindow* m_pMainWindow;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,11 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) :
|
||||||
buttonBox->button(QDialogButtonBox::Save)->setEnabled(false);
|
buttonBox->button(QDialogButtonBox::Save)->setEnabled(false);
|
||||||
enableControls(appConfig().isWritable());
|
enableControls(appConfig().isWritable());
|
||||||
|
|
||||||
m_pCheckBoxLanguageSync->setVisible(m_pMainWindow->synergyType() == MainWindow::synergyClient);
|
const auto isClientMode = m_pMainWindow->synergyType() == MainWindow::synergyClient;
|
||||||
m_pCheckBoxScrollDirection->setVisible(m_pMainWindow->synergyType() == MainWindow::synergyClient);
|
m_pCheckBoxLanguageSync->setVisible(isClientMode);
|
||||||
|
m_pCheckBoxScrollDirection->setVisible(isClientMode);
|
||||||
|
m_pCheckBoxClientHostMode->setVisible(isClientMode && appConfig().getInitiateConnectionFromServer());
|
||||||
|
m_pCheckBoxServerClientMode->setVisible(!isClientMode && appConfig().getInitiateConnectionFromServer());
|
||||||
|
|
||||||
const auto& serveConfig = m_pMainWindow->serverConfig();
|
const auto& serveConfig = m_pMainWindow->serverConfig();
|
||||||
m_pLineEditScreenName->setValidator(new validators::ScreenNameValidator(m_pLineEditScreenName, m_pLabelNameError, (&serveConfig.screens())));
|
m_pLineEditScreenName->setValidator(new validators::ScreenNameValidator(m_pLineEditScreenName, m_pLabelNameError, (&serveConfig.screens())));
|
||||||
|
|
@ -72,6 +75,8 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) :
|
||||||
connect(m_pComboElevate, SIGNAL(currentIndexChanged(int)), this, SLOT(onChange()));
|
connect(m_pComboElevate, SIGNAL(currentIndexChanged(int)), this, SLOT(onChange()));
|
||||||
connect(m_pCheckBoxLanguageSync, SIGNAL(clicked()), this, SLOT(onChange()));
|
connect(m_pCheckBoxLanguageSync, SIGNAL(clicked()), this, SLOT(onChange()));
|
||||||
connect(m_pCheckBoxScrollDirection, SIGNAL(clicked()), this, SLOT(onChange()));
|
connect(m_pCheckBoxScrollDirection, SIGNAL(clicked()), this, SLOT(onChange()));
|
||||||
|
connect(m_pCheckBoxClientHostMode, SIGNAL(clicked()), this, SLOT(onChange()));
|
||||||
|
connect(m_pCheckBoxServerClientMode,SIGNAL(clicked()), this, SLOT(onChange()));
|
||||||
|
|
||||||
adjustSize();
|
adjustSize();
|
||||||
}
|
}
|
||||||
|
|
@ -96,6 +101,8 @@ void SettingsDialog::accept()
|
||||||
appConfig().setCryptoEnabled(m_pCheckBoxEnableCrypto->isChecked());
|
appConfig().setCryptoEnabled(m_pCheckBoxEnableCrypto->isChecked());
|
||||||
appConfig().setLanguageSync(m_pCheckBoxLanguageSync->isChecked());
|
appConfig().setLanguageSync(m_pCheckBoxLanguageSync->isChecked());
|
||||||
appConfig().setInvertScrollDirection(m_pCheckBoxScrollDirection->isChecked());
|
appConfig().setInvertScrollDirection(m_pCheckBoxScrollDirection->isChecked());
|
||||||
|
appConfig().setClientHostMode(m_pCheckBoxClientHostMode->isChecked());
|
||||||
|
appConfig().setServerClientMode(m_pCheckBoxServerClientMode->isChecked());
|
||||||
|
|
||||||
appConfig().saveSettings();
|
appConfig().saveSettings();
|
||||||
QDialog::accept();
|
QDialog::accept();
|
||||||
|
|
@ -155,6 +162,8 @@ void SettingsDialog::loadFromConfig() {
|
||||||
m_pCheckBoxEnableCrypto->setChecked(m_appConfig.getCryptoEnabled());
|
m_pCheckBoxEnableCrypto->setChecked(m_appConfig.getCryptoEnabled());
|
||||||
m_pCheckBoxLanguageSync->setChecked(m_appConfig.getLanguageSync());
|
m_pCheckBoxLanguageSync->setChecked(m_appConfig.getLanguageSync());
|
||||||
m_pCheckBoxScrollDirection->setChecked(m_appConfig.getInvertScrollDirection());
|
m_pCheckBoxScrollDirection->setChecked(m_appConfig.getInvertScrollDirection());
|
||||||
|
m_pCheckBoxClientHostMode->setChecked(m_appConfig.getClientHostMode());
|
||||||
|
m_pCheckBoxServerClientMode->setChecked(m_appConfig.getServerClientMode());
|
||||||
|
|
||||||
setupSeurity();
|
setupSeurity();
|
||||||
|
|
||||||
|
|
@ -367,6 +376,8 @@ bool SettingsDialog::isModified()
|
||||||
|| appConfig().getCryptoEnabled() != m_pCheckBoxEnableCrypto->isChecked()
|
|| appConfig().getCryptoEnabled() != m_pCheckBoxEnableCrypto->isChecked()
|
||||||
|| appConfig().isSystemScoped() != m_isSystemAtStart
|
|| appConfig().isSystemScoped() != m_isSystemAtStart
|
||||||
|| appConfig().getLanguageSync() != m_pCheckBoxLanguageSync->isChecked()
|
|| appConfig().getLanguageSync() != m_pCheckBoxLanguageSync->isChecked()
|
||||||
|
|| appConfig().getClientHostMode() != m_pCheckBoxClientHostMode->isChecked()
|
||||||
|
|| appConfig().getServerClientMode() != m_pCheckBoxServerClientMode->isChecked()
|
||||||
|| appConfig().getInvertScrollDirection() != m_pCheckBoxScrollDirection->isChecked())
|
|| appConfig().getInvertScrollDirection() != m_pCheckBoxScrollDirection->isChecked())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -390,6 +401,8 @@ void SettingsDialog::enableControls(bool enable) {
|
||||||
m_labelAdminRightsMessage->setVisible(!enable);
|
m_labelAdminRightsMessage->setVisible(!enable);
|
||||||
m_pCheckBoxLanguageSync->setEnabled(enable);
|
m_pCheckBoxLanguageSync->setEnabled(enable);
|
||||||
m_pCheckBoxScrollDirection->setEnabled(enable);
|
m_pCheckBoxScrollDirection->setEnabled(enable);
|
||||||
|
m_pCheckBoxClientHostMode->setEnabled(enable);
|
||||||
|
m_pCheckBoxServerClientMode->setEnabled(enable);
|
||||||
|
|
||||||
if (enable) {
|
if (enable) {
|
||||||
m_pLabelLogPath->setEnabled(m_pCheckBoxLogToFile->isChecked());
|
m_pLabelLogPath->setEnabled(m_pCheckBoxLogToFile->isChecked());
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>404</width>
|
<width>404</width>
|
||||||
<height>620</height>
|
<height>819</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
|
@ -337,62 +337,7 @@ background-color: rgba(192,192,192, 0.1);
|
||||||
<enum>QFrame::Raised</enum>
|
<enum>QFrame::Raised</enum>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_3">
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
<item row="4" column="0">
|
<item row="6" column="1">
|
||||||
<widget class="QLabel" name="m_pLabelElevate">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Elevate privileges</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="0">
|
|
||||||
<spacer name="verticalSpacer_6">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>40</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0" colspan="3">
|
|
||||||
<widget class="QCheckBox" name="m_pCheckBoxLanguageSync">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Use server's keyboard language on this machine</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="m_pLabel_27">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>75</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>User interface language</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>m_pComboLanguage</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="QComboBox" name="m_pComboElevate">
|
<widget class="QComboBox" name="m_pComboElevate">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Specify when the Synergy service should run at an elevated privilege level</string>
|
<string>Specify when the Synergy service should run at an elevated privilege level</string>
|
||||||
|
|
@ -417,16 +362,85 @@ background-color: rgba(192,192,192, 0.1);
|
||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="3" column="0" colspan="3">
|
||||||
|
<widget class="QCheckBox" name="m_pCheckBoxLanguageSync">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Use server's keyboard language on this machine</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QCheckBox" name="m_pCheckBoxClientHostMode">
|
||||||
|
<property name="text">
|
||||||
|
<string>Host mode</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="0">
|
||||||
|
<spacer name="verticalSpacer_6">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="m_pLabelElevate">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Elevate privileges</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
<widget class="QComboBox" name="m_pComboLanguage"/>
|
<widget class="QComboBox" name="m_pComboLanguage"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0" colspan="3">
|
<item row="2" column="0" colspan="3">
|
||||||
<widget class="QCheckBox" name="m_pCheckBoxScrollDirection">
|
<widget class="QCheckBox" name="m_pCheckBoxScrollDirection">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Invert scroll direction on this computer</string>
|
<string>Invert scroll direction on this computer</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="m_pLabel_27">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>75</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>User interface language</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>m_pComboLanguage</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QCheckBox" name="m_pCheckBoxServerClientMode">
|
||||||
|
<property name="text">
|
||||||
|
<string>Client mode</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
@ -695,7 +709,6 @@ background-color: rgba(192,192,192, 0.1);
|
||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<weight>50</weight>
|
|
||||||
<bold>false</bold>
|
<bold>false</bold>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,6 @@ Client::Client(
|
||||||
m_events(events),
|
m_events(events),
|
||||||
m_sendFileThread(nullptr),
|
m_sendFileThread(nullptr),
|
||||||
m_writeToDropDirThread(nullptr),
|
m_writeToDropDirThread(nullptr),
|
||||||
m_socket(NULL),
|
|
||||||
m_useSecureNetwork(args.m_enableCrypto),
|
m_useSecureNetwork(args.m_enableCrypto),
|
||||||
m_args(args),
|
m_args(args),
|
||||||
m_enableClipboard(true),
|
m_enableClipboard(true),
|
||||||
|
|
@ -135,29 +134,32 @@ Client::connect(size_t addressIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// resolve the server hostname. do this every time we connect
|
if (m_args.m_hostMode)
|
||||||
// in case we couldn't resolve the address earlier or the address
|
{
|
||||||
// has changed (which can happen frequently if this is a laptop
|
LOG((CLOG_NOTE "waiting for server conection on %i port", m_serverAddress.getPort()));
|
||||||
// being shuttled between various networks). patch by Brent
|
}
|
||||||
// Priddy.
|
else {
|
||||||
m_resolvedAddressesCount = m_serverAddress.resolve(addressIndex);
|
// resolve the server hostname. do this every time we connect
|
||||||
|
// in case we couldn't resolve the address earlier or the address
|
||||||
// m_serverAddress will be null if the hostname address is not reolved
|
// has changed (which can happen frequently if this is a laptop
|
||||||
if (m_serverAddress.getAddress() != nullptr) {
|
// being shuttled between various networks). patch by Brent
|
||||||
// to help users troubleshoot, show server host name (issue: 60)
|
// Priddy.
|
||||||
LOG((CLOG_NOTE "connecting to '%s': %s:%i",
|
m_resolvedAddressesCount = m_serverAddress.resolve(addressIndex);
|
||||||
m_serverAddress.getHostname().c_str(),
|
|
||||||
ARCH->addrToString(m_serverAddress.getAddress()).c_str(),
|
// m_serverAddress will be null if the hostname address is not reolved
|
||||||
m_serverAddress.getPort()));
|
if (m_serverAddress.getAddress() != nullptr) {
|
||||||
|
// to help users troubleshoot, show server host name (issue: 60)
|
||||||
|
LOG((CLOG_NOTE "connecting to '%s': %s:%i",
|
||||||
|
m_serverAddress.getHostname().c_str(),
|
||||||
|
ARCH->addrToString(m_serverAddress.getAddress()).c_str(),
|
||||||
|
m_serverAddress.getPort()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the socket
|
// create the socket
|
||||||
IDataSocket* socket = m_socketFactory->create(m_useSecureNetwork, ARCH->getAddrFamily(m_serverAddress.getAddress()));
|
IDataSocket* socket = m_socketFactory->create(m_useSecureNetwork, ARCH->getAddrFamily(m_serverAddress.getAddress()));
|
||||||
m_socket = dynamic_cast<TCPSocket*>(socket);
|
|
||||||
|
|
||||||
// filter socket messages, including a packetizing filter
|
// filter socket messages, including a packetizing filter
|
||||||
m_stream = socket;
|
m_stream = new PacketStreamFilter(m_events, socket, true);
|
||||||
m_stream = new PacketStreamFilter(m_events, m_stream, true);
|
|
||||||
|
|
||||||
// connect
|
// connect
|
||||||
LOG((CLOG_DEBUG1 "connecting to server"));
|
LOG((CLOG_DEBUG1 "connecting to server"));
|
||||||
|
|
@ -546,10 +548,12 @@ Client::setupTimer()
|
||||||
{
|
{
|
||||||
assert(m_timer == NULL);
|
assert(m_timer == NULL);
|
||||||
|
|
||||||
m_timer = m_events->newOneShotTimer(2.0, NULL);
|
if (!m_args.m_hostMode) {
|
||||||
m_events->adoptHandler(Event::kTimer, m_timer,
|
m_timer = m_events->newOneShotTimer(2.0, NULL);
|
||||||
new TMethodEventJob<Client>(this,
|
m_events->adoptHandler(Event::kTimer, m_timer,
|
||||||
&Client::handleConnectTimeout));
|
new TMethodEventJob<Client>(this,
|
||||||
|
&Client::handleConnectTimeout));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,6 @@ private:
|
||||||
using AutoThread = std::unique_ptr<Thread>;
|
using AutoThread = std::unique_ptr<Thread>;
|
||||||
AutoThread m_sendFileThread;
|
AutoThread m_sendFileThread;
|
||||||
AutoThread m_writeToDropDirThread;
|
AutoThread m_writeToDropDirThread;
|
||||||
TCPSocket* m_socket;
|
|
||||||
bool m_useSecureNetwork;
|
bool m_useSecureNetwork;
|
||||||
bool m_enableClipboard;
|
bool m_enableClipboard;
|
||||||
size_t m_maximumClipboardSize;
|
size_t m_maximumClipboardSize;
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,14 @@
|
||||||
# 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/>.
|
||||||
|
|
||||||
file(GLOB headers "*.h")
|
file(GLOB headers
|
||||||
file(GLOB sources "*.cpp")
|
"*.h"
|
||||||
|
"InverseSockets/*.h"
|
||||||
|
)
|
||||||
|
file(GLOB sources
|
||||||
|
"*.cpp"
|
||||||
|
"InverseSockets/*.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
if (SYNERGY_ADD_HEADERS)
|
if (SYNERGY_ADD_HEADERS)
|
||||||
list(APPEND sources ${headers})
|
list(APPEND sources ${headers})
|
||||||
|
|
|
||||||
201
src/lib/net/InverseSockets/AutoArchSocket.cpp
Normal file
201
src/lib/net/InverseSockets/AutoArchSocket.cpp
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2012-2022 Symless Ltd.
|
||||||
|
* Copyright (C) 2002 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* 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 LICENSE 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 "AutoArchSocket.h"
|
||||||
|
|
||||||
|
#include "base/Log.h"
|
||||||
|
#include "arch/Arch.h"
|
||||||
|
#include "arch/XArch.h"
|
||||||
|
#include "net/XSocket.h"
|
||||||
|
|
||||||
|
AutoArchSocket::AutoArchSocket(IArchNetwork::EAddressFamily family)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
m_socket = ARCH->newSocket(family, IArchNetwork::kSTREAM);
|
||||||
|
LOG((CLOG_DEBUG "opening new socket: %08X", m_socket));
|
||||||
|
}
|
||||||
|
catch (const XArchNetwork& e) {
|
||||||
|
throw XSocketCreate(e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoArchSocket::~AutoArchSocket()
|
||||||
|
{
|
||||||
|
closeSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoArchSocket::setNoDelayOnSocket(bool value)
|
||||||
|
{
|
||||||
|
if (isValid()) {
|
||||||
|
ARCH->setNoDelayOnSocket(m_socket, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoArchSocket::setReuseAddrOnSocket(bool value)
|
||||||
|
{
|
||||||
|
if (isValid()) {
|
||||||
|
ARCH->setReuseAddrOnSocket(m_socket, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoArchSocket::closeSocket()
|
||||||
|
{
|
||||||
|
if (isValid()) {
|
||||||
|
try {
|
||||||
|
LOG((CLOG_DEBUG "closing socket: %08X", m_socket));
|
||||||
|
ARCH->closeSocket(m_socket);
|
||||||
|
m_socket = nullptr;
|
||||||
|
}
|
||||||
|
catch (const XArchNetwork& e) {
|
||||||
|
// ignore, there's not much we can do
|
||||||
|
LOG((CLOG_WARN "error closing socket: %s", e.what()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LOG((CLOG_WARN "error closing socket because the socket already closed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoArchSocket::bindSocket(const NetworkAddress &addr)
|
||||||
|
{
|
||||||
|
if (isValid()) {
|
||||||
|
try {
|
||||||
|
ARCH->bindSocket(m_socket, addr.getAddress());
|
||||||
|
}
|
||||||
|
catch (const XArchNetworkAddressInUse& e) {
|
||||||
|
throw XSocketAddressInUse(e.what());
|
||||||
|
}
|
||||||
|
catch (const XArchNetwork& e) {
|
||||||
|
throw XSocketBind(e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoArchSocket::bindAndListen(const NetworkAddress &addr)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
setReuseAddrOnSocket();
|
||||||
|
bindSocket(addr);
|
||||||
|
listenOnSocket();
|
||||||
|
}
|
||||||
|
catch (const XArchNetworkAddressInUse& e) {
|
||||||
|
throw XSocketAddressInUse(e.what());
|
||||||
|
}
|
||||||
|
catch (const XArchNetwork& e) {
|
||||||
|
throw XSocketBind(e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoArchSocket::listenOnSocket()
|
||||||
|
{
|
||||||
|
if (isValid()) {
|
||||||
|
ARCH->listenOnSocket(m_socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArchSocket AutoArchSocket::acceptSocket()
|
||||||
|
{
|
||||||
|
return ARCH->acceptSocket(m_socket, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoArchSocket::closeSocketForRead()
|
||||||
|
{
|
||||||
|
if (isValid()) {
|
||||||
|
try {
|
||||||
|
ARCH->closeSocketForRead(m_socket);
|
||||||
|
}
|
||||||
|
catch (const XArchNetwork& e) {
|
||||||
|
// ignore, there's not much we can do
|
||||||
|
LOG((CLOG_WARN "error closing socket: %s", e.what()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoArchSocket::closeSocketForWrite()
|
||||||
|
{
|
||||||
|
if (isValid()) {
|
||||||
|
try {
|
||||||
|
ARCH->closeSocketForWrite(m_socket);
|
||||||
|
}
|
||||||
|
catch (const XArchNetwork& e) {
|
||||||
|
// ignore, there's not much we can do
|
||||||
|
LOG((CLOG_WARN "error closing socket: %s", e.what()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AutoArchSocket::connectSocket(const NetworkAddress &addr)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
if (isValid()) {
|
||||||
|
// turn off Nagle algorithm. we send lots of very short messages
|
||||||
|
// that should be sent without (much) delay. for example, the
|
||||||
|
// mouse motion messages are much less useful if they're delayed.
|
||||||
|
setNoDelayOnSocket();
|
||||||
|
result = ARCH->connectSocket(m_socket, addr.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AutoArchSocket::readSocket(UInt8* buffer, size_t size)
|
||||||
|
{
|
||||||
|
size_t result = 0;
|
||||||
|
|
||||||
|
if (isValid()) {
|
||||||
|
result = ARCH->readSocket(m_socket, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AutoArchSocket::writeSocket(const UInt8* buffer, size_t size)
|
||||||
|
{
|
||||||
|
size_t result = 0;
|
||||||
|
|
||||||
|
if (isValid()) {
|
||||||
|
result = ARCH->writeSocket(m_socket, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoArchSocket::throwErrorOnSocket()
|
||||||
|
{
|
||||||
|
if (isValid()) {
|
||||||
|
ARCH->throwErrorOnSocket(m_socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArchSocket AutoArchSocket::getRawSocket() const
|
||||||
|
{
|
||||||
|
return m_socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AutoArchSocket::isValid() const
|
||||||
|
{
|
||||||
|
return (m_socket != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoArchSocket::operator =(ArchSocket socket)
|
||||||
|
{
|
||||||
|
if (isValid()) {
|
||||||
|
closeSocket();
|
||||||
|
}
|
||||||
|
m_socket = socket;
|
||||||
|
}
|
||||||
53
src/lib/net/InverseSockets/AutoArchSocket.h
Normal file
53
src/lib/net/InverseSockets/AutoArchSocket.h
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2012-2022 Symless Ltd.
|
||||||
|
* Copyright (C) 2002 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* 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 LICENSE 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 "net/NetworkAddress.h"
|
||||||
|
|
||||||
|
class AutoArchSocket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AutoArchSocket(IArchNetwork::EAddressFamily family);
|
||||||
|
~AutoArchSocket();
|
||||||
|
|
||||||
|
AutoArchSocket(const AutoArchSocket&) = delete;
|
||||||
|
AutoArchSocket& operator =(const AutoArchSocket&) = delete;
|
||||||
|
|
||||||
|
void setNoDelayOnSocket(bool value = true);
|
||||||
|
void setReuseAddrOnSocket(bool value = true);
|
||||||
|
|
||||||
|
void listenOnSocket();
|
||||||
|
ArchSocket acceptSocket();
|
||||||
|
void bindSocket(const NetworkAddress& addr);
|
||||||
|
bool connectSocket(const NetworkAddress& addr);
|
||||||
|
void bindAndListen(const NetworkAddress& addr);
|
||||||
|
|
||||||
|
void closeSocket();
|
||||||
|
void closeSocketForRead();
|
||||||
|
void closeSocketForWrite();
|
||||||
|
|
||||||
|
size_t readSocket(UInt8* buffer, size_t size);
|
||||||
|
size_t writeSocket(const UInt8* buffer, size_t size);
|
||||||
|
void throwErrorOnSocket();
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
ArchSocket getRawSocket() const;
|
||||||
|
void operator =(ArchSocket socket);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ArchSocket m_socket = nullptr;
|
||||||
|
};
|
||||||
452
src/lib/net/InverseSockets/InverseClientSocket.cpp
Normal file
452
src/lib/net/InverseSockets/InverseClientSocket.cpp
Normal file
|
|
@ -0,0 +1,452 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2012-2022 Symless Ltd.
|
||||||
|
* Copyright (C) 2002 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* 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 LICENSE 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 "InverseClientSocket.h"
|
||||||
|
|
||||||
|
#include "net/NetworkAddress.h"
|
||||||
|
#include "net/SocketMultiplexer.h"
|
||||||
|
#include "net/TSocketMultiplexerMethodJob.h"
|
||||||
|
#include "net/XSocket.h"
|
||||||
|
#include "mt/Lock.h"
|
||||||
|
#include "arch/Arch.h"
|
||||||
|
#include "arch/XArch.h"
|
||||||
|
#include "base/Log.h"
|
||||||
|
#include "base/IEventQueue.h"
|
||||||
|
#include "base/IEventJob.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
//
|
||||||
|
// InverseClientSocket
|
||||||
|
//
|
||||||
|
|
||||||
|
InverseClientSocket::InverseClientSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family) :
|
||||||
|
IDataSocket(events),
|
||||||
|
m_events(events),
|
||||||
|
m_socket(family),
|
||||||
|
m_listener(family),
|
||||||
|
m_flushed(&m_mutex, true),
|
||||||
|
m_socketMultiplexer(socketMultiplexer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
InverseClientSocket::~InverseClientSocket()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// warning virtual function in destructor is very danger practice
|
||||||
|
InverseClientSocket::close();
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
LOG((CLOG_DEBUG "error while TCP socket destruction"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseClientSocket::bind(const NetworkAddress& addr)
|
||||||
|
{
|
||||||
|
m_socket.bindSocket(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseClientSocket::close()
|
||||||
|
{
|
||||||
|
setJob(nullptr);
|
||||||
|
|
||||||
|
Lock lock(&m_mutex);
|
||||||
|
onDisconnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
InverseClientSocket::getEventTarget() const
|
||||||
|
{
|
||||||
|
return const_cast<void*>(static_cast<const void*>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32
|
||||||
|
InverseClientSocket::read(void* buffer, UInt32 n)
|
||||||
|
{
|
||||||
|
// copy data directly from our input buffer
|
||||||
|
Lock lock(&m_mutex);
|
||||||
|
UInt32 size = m_inputBuffer.getSize();
|
||||||
|
if (n > size) {
|
||||||
|
n = size;
|
||||||
|
}
|
||||||
|
if (buffer != nullptr && n != 0) {
|
||||||
|
memcpy(buffer, m_inputBuffer.peek(n), n);
|
||||||
|
}
|
||||||
|
m_inputBuffer.pop(n);
|
||||||
|
|
||||||
|
// if no more data and we cannot read or write then send disconnected
|
||||||
|
if (n > 0 && m_inputBuffer.getSize() == 0 && !m_readable && !m_writable) {
|
||||||
|
sendEvent(m_events->forISocket().disconnected());
|
||||||
|
m_connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseClientSocket::write(const void* buffer, UInt32 n)
|
||||||
|
{
|
||||||
|
bool wasEmpty;
|
||||||
|
{
|
||||||
|
Lock lock(&m_mutex);
|
||||||
|
|
||||||
|
// must not have shutdown output
|
||||||
|
if (!m_writable) {
|
||||||
|
sendEvent(m_events->forIStream().outputError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore empty writes
|
||||||
|
if (n == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy data to the output buffer
|
||||||
|
wasEmpty = (m_outputBuffer.getSize() == 0);
|
||||||
|
m_outputBuffer.write(buffer, n);
|
||||||
|
|
||||||
|
// there's data to write
|
||||||
|
m_flushed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we're waiting to write
|
||||||
|
if (wasEmpty) {
|
||||||
|
setJob(newJob(m_socket.getRawSocket()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseClientSocket::flush()
|
||||||
|
{
|
||||||
|
Lock lock(&m_mutex);
|
||||||
|
while (m_flushed == false) {
|
||||||
|
m_flushed.wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseClientSocket::shutdownInput()
|
||||||
|
{
|
||||||
|
bool useNewJob = false;
|
||||||
|
{
|
||||||
|
Lock lock(&m_mutex);
|
||||||
|
|
||||||
|
// shutdown socket for reading
|
||||||
|
m_socket.closeSocketForRead();
|
||||||
|
|
||||||
|
// shutdown buffer for reading
|
||||||
|
if (m_readable) {
|
||||||
|
sendEvent(m_events->forIStream().inputShutdown());
|
||||||
|
onInputShutdown();
|
||||||
|
useNewJob = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (useNewJob) {
|
||||||
|
setJob(newJob(m_socket.getRawSocket()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseClientSocket::shutdownOutput()
|
||||||
|
{
|
||||||
|
bool useNewJob = false;
|
||||||
|
{
|
||||||
|
Lock lock(&m_mutex);
|
||||||
|
|
||||||
|
// shutdown socket for writing
|
||||||
|
m_socket.closeSocketForWrite();
|
||||||
|
|
||||||
|
// shutdown buffer for writing
|
||||||
|
if (m_writable) {
|
||||||
|
sendEvent(m_events->forIStream().outputShutdown());
|
||||||
|
onOutputShutdown();
|
||||||
|
useNewJob = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (useNewJob) {
|
||||||
|
setJob(newJob(m_socket.getRawSocket()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
InverseClientSocket::isReady() const
|
||||||
|
{
|
||||||
|
Lock lock(&m_mutex);
|
||||||
|
return (m_inputBuffer.getSize() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
InverseClientSocket::isFatal() const
|
||||||
|
{
|
||||||
|
// TCP sockets aren't ever left in a fatal state.
|
||||||
|
LOG((CLOG_ERR "isFatal() not valid for non-secure connections"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32
|
||||||
|
InverseClientSocket::getSize() const
|
||||||
|
{
|
||||||
|
Lock lock(&m_mutex);
|
||||||
|
return m_inputBuffer.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseClientSocket::connect(const NetworkAddress& addr)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Lock lock(&m_mutex);
|
||||||
|
m_listener.bindAndListen(addr);
|
||||||
|
m_writable = true;
|
||||||
|
m_readable = true;
|
||||||
|
}
|
||||||
|
setJob(newJob(m_listener.getRawSocket()));
|
||||||
|
}
|
||||||
|
|
||||||
|
InverseClientSocket::EJobResult
|
||||||
|
InverseClientSocket::doRead()
|
||||||
|
{
|
||||||
|
UInt8 buffer[4096] = {0};
|
||||||
|
size_t bytesRead = m_socket.readSocket(buffer, sizeof(buffer));
|
||||||
|
|
||||||
|
if (bytesRead > 0) {
|
||||||
|
bool wasEmpty = (m_inputBuffer.getSize() == 0);
|
||||||
|
|
||||||
|
// slurp up as much as possible
|
||||||
|
do {
|
||||||
|
m_inputBuffer.write(buffer, static_cast<UInt32>(bytesRead));
|
||||||
|
|
||||||
|
bytesRead = m_socket.readSocket(buffer, sizeof(buffer));
|
||||||
|
} while (bytesRead > 0);
|
||||||
|
|
||||||
|
// send input ready if input buffer was empty
|
||||||
|
if (wasEmpty) {
|
||||||
|
sendEvent(m_events->forIStream().inputReady());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// remote write end of stream hungup. our input side
|
||||||
|
// has therefore shutdown but don't flush our buffer
|
||||||
|
// since there's still data to be read.
|
||||||
|
sendEvent(m_events->forIStream().inputShutdown());
|
||||||
|
if (!m_writable && m_inputBuffer.getSize() == 0) {
|
||||||
|
sendEvent(m_events->forISocket().disconnected());
|
||||||
|
m_connected = false;
|
||||||
|
}
|
||||||
|
m_readable = false;
|
||||||
|
return InverseClientSocket::EJobResult::kNew;
|
||||||
|
}
|
||||||
|
|
||||||
|
return InverseClientSocket::EJobResult::kRetry;
|
||||||
|
}
|
||||||
|
|
||||||
|
InverseClientSocket::EJobResult
|
||||||
|
InverseClientSocket::doWrite()
|
||||||
|
{
|
||||||
|
UInt32 bufferSize = m_outputBuffer.getSize();
|
||||||
|
auto buffer = static_cast<const UInt8*>(m_outputBuffer.peek(bufferSize));
|
||||||
|
const auto bytesWrote = static_cast<UInt32>(m_socket.writeSocket(buffer, bufferSize));
|
||||||
|
|
||||||
|
if (bytesWrote > 0) {
|
||||||
|
discardWrittenData(bytesWrote);
|
||||||
|
return InverseClientSocket::EJobResult::kNew;
|
||||||
|
}
|
||||||
|
|
||||||
|
return InverseClientSocket::EJobResult::kRetry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseClientSocket::setJob(ISocketMultiplexerJob* job)
|
||||||
|
{
|
||||||
|
// multiplexer will delete the old job
|
||||||
|
if (job == nullptr) {
|
||||||
|
m_socketMultiplexer->removeSocket(this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_socketMultiplexer->addSocket(this, job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ISocketMultiplexerJob*
|
||||||
|
InverseClientSocket::newJob(ArchSocket socket)
|
||||||
|
{
|
||||||
|
// note -- must have m_mutex locked on entry
|
||||||
|
|
||||||
|
ISocketMultiplexerJob* result = nullptr;
|
||||||
|
|
||||||
|
if (socket) {
|
||||||
|
auto isWritable = m_writable;
|
||||||
|
auto handler = &InverseClientSocket::serviceConnecting;
|
||||||
|
|
||||||
|
if (m_connected) {
|
||||||
|
handler = &InverseClientSocket::serviceConnected;
|
||||||
|
isWritable = (isWritable && (m_outputBuffer.getSize() > 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_readable || isWritable) {
|
||||||
|
result = new TSocketMultiplexerMethodJob<InverseClientSocket>(this, handler, socket, m_readable, isWritable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseClientSocket::sendConnectionFailedEvent(const char* msg)
|
||||||
|
{
|
||||||
|
auto info = new ConnectionFailedInfo(msg);
|
||||||
|
m_events->addEvent(Event(m_events->forIDataSocket().connectionFailed(),
|
||||||
|
getEventTarget(), info, Event::kDontFreeData));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseClientSocket::sendEvent(Event::Type type)
|
||||||
|
{
|
||||||
|
m_events->addEvent(Event(type, getEventTarget()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseClientSocket::discardWrittenData(int bytesWrote)
|
||||||
|
{
|
||||||
|
m_outputBuffer.pop(bytesWrote);
|
||||||
|
if (m_outputBuffer.getSize() == 0) {
|
||||||
|
sendEvent(m_events->forIStream().outputFlushed());
|
||||||
|
m_flushed = true;
|
||||||
|
m_flushed.broadcast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseClientSocket::onConnected()
|
||||||
|
{
|
||||||
|
sendEvent(m_events->forIDataSocket().connected());
|
||||||
|
m_connected = true;
|
||||||
|
m_readable = true;
|
||||||
|
m_writable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseClientSocket::onInputShutdown()
|
||||||
|
{
|
||||||
|
m_inputBuffer.pop(m_inputBuffer.getSize());
|
||||||
|
m_readable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseClientSocket::onOutputShutdown()
|
||||||
|
{
|
||||||
|
m_outputBuffer.pop(m_outputBuffer.getSize());
|
||||||
|
m_writable = false;
|
||||||
|
|
||||||
|
// we're now flushed
|
||||||
|
m_flushed = true;
|
||||||
|
m_flushed.broadcast();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseClientSocket::onDisconnected()
|
||||||
|
{
|
||||||
|
if (m_connected) {
|
||||||
|
sendEvent(m_events->forISocket().disconnected());
|
||||||
|
}
|
||||||
|
// disconnected
|
||||||
|
onInputShutdown();
|
||||||
|
onOutputShutdown();
|
||||||
|
m_connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ISocketMultiplexerJob*
|
||||||
|
InverseClientSocket::serviceConnecting(ISocketMultiplexerJob* job,
|
||||||
|
bool read, bool, bool)
|
||||||
|
{
|
||||||
|
Lock lock(&m_mutex);
|
||||||
|
|
||||||
|
if (read) {
|
||||||
|
m_socket = m_listener.acceptSocket();
|
||||||
|
onConnected();
|
||||||
|
return newJob(m_socket.getRawSocket());
|
||||||
|
}
|
||||||
|
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
ISocketMultiplexerJob*
|
||||||
|
InverseClientSocket::serviceConnected(ISocketMultiplexerJob* job,
|
||||||
|
bool read, bool write, bool error)
|
||||||
|
{
|
||||||
|
Lock lock(&m_mutex);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
onDisconnected();
|
||||||
|
return newJob(m_listener.getRawSocket());
|
||||||
|
}
|
||||||
|
|
||||||
|
EJobResult result = InverseClientSocket::EJobResult::kRetry;
|
||||||
|
if (write) {
|
||||||
|
try {
|
||||||
|
result = doWrite();
|
||||||
|
}
|
||||||
|
catch (const XArchNetworkShutdown&) {
|
||||||
|
// remote read end of stream hungup. our output side
|
||||||
|
// has therefore shutdown.
|
||||||
|
onOutputShutdown();
|
||||||
|
sendEvent(m_events->forIStream().outputShutdown());
|
||||||
|
if (!m_readable && m_inputBuffer.getSize() == 0) {
|
||||||
|
sendEvent(m_events->forISocket().disconnected());
|
||||||
|
m_connected = false;
|
||||||
|
}
|
||||||
|
result = InverseClientSocket::EJobResult::kNew;
|
||||||
|
}
|
||||||
|
catch (const XArchNetworkDisconnected&) {
|
||||||
|
// stream hungup
|
||||||
|
onDisconnected();
|
||||||
|
result = InverseClientSocket::EJobResult::kNew;
|
||||||
|
}
|
||||||
|
catch (const XArchNetwork& e) {
|
||||||
|
// other write error
|
||||||
|
LOG((CLOG_WARN "error writing socket: %s", e.what()));
|
||||||
|
onDisconnected();
|
||||||
|
sendEvent(m_events->forIStream().outputError());
|
||||||
|
result = InverseClientSocket::EJobResult::kNew;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read && m_readable) {
|
||||||
|
try {
|
||||||
|
result = doRead();
|
||||||
|
}
|
||||||
|
catch (const XArchNetworkDisconnected&) {
|
||||||
|
// stream hungup
|
||||||
|
onDisconnected();
|
||||||
|
result = InverseClientSocket::EJobResult::kNew;
|
||||||
|
}
|
||||||
|
catch (const XArchNetwork& e) {
|
||||||
|
// ignore other read error
|
||||||
|
LOG((CLOG_WARN "error reading socket: %s", e.what()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == InverseClientSocket::EJobResult::kBreak) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result == InverseClientSocket::EJobResult::kNew ? newJob(m_socket.getRawSocket()) : job;
|
||||||
|
}
|
||||||
111
src/lib/net/InverseSockets/InverseClientSocket.h
Normal file
111
src/lib/net/InverseSockets/InverseClientSocket.h
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2012-2022 Symless Ltd.
|
||||||
|
* Copyright (C) 2002 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* 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 LICENSE 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 "net/IDataSocket.h"
|
||||||
|
#include "io/StreamBuffer.h"
|
||||||
|
#include "mt/CondVar.h"
|
||||||
|
#include "mt/Mutex.h"
|
||||||
|
#include "arch/IArchNetwork.h"
|
||||||
|
#include "AutoArchSocket.h"
|
||||||
|
|
||||||
|
class ISocketMultiplexerJob;
|
||||||
|
class IEventQueue;
|
||||||
|
class SocketMultiplexer;
|
||||||
|
|
||||||
|
class InverseClientSocket : public IDataSocket {
|
||||||
|
public:
|
||||||
|
InverseClientSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family = IArchNetwork::kINET);
|
||||||
|
InverseClientSocket(InverseClientSocket const &) =delete;
|
||||||
|
InverseClientSocket(InverseClientSocket &&) =delete;
|
||||||
|
~InverseClientSocket() override;
|
||||||
|
|
||||||
|
InverseClientSocket& operator=(InverseClientSocket const &) =delete;
|
||||||
|
InverseClientSocket& operator=(InverseClientSocket &&) =delete;
|
||||||
|
|
||||||
|
// ISocket overrides
|
||||||
|
void bind(const NetworkAddress&) override;
|
||||||
|
void close() override;
|
||||||
|
void* getEventTarget() const override;
|
||||||
|
|
||||||
|
// IStream overrides
|
||||||
|
UInt32 read(void* buffer, UInt32 n) override;
|
||||||
|
void write(const void* buffer, UInt32 n) override;
|
||||||
|
void flush() override;
|
||||||
|
void shutdownInput() override;
|
||||||
|
void shutdownOutput() override;
|
||||||
|
bool isReady() const override;
|
||||||
|
bool isFatal() const override;
|
||||||
|
UInt32 getSize() const override;
|
||||||
|
|
||||||
|
// IDataSocket overrides
|
||||||
|
void connect(const NetworkAddress&) override;
|
||||||
|
|
||||||
|
|
||||||
|
virtual ISocketMultiplexerJob*
|
||||||
|
newJob(ArchSocket socket);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum class EJobResult {
|
||||||
|
kBreak = -1, //!< Break the Job chain
|
||||||
|
kRetry, //!< Retry the same job
|
||||||
|
kNew //!< Require a new job
|
||||||
|
};
|
||||||
|
|
||||||
|
ArchSocket getSocket() { return m_socket.getRawSocket(); }
|
||||||
|
IEventQueue* getEvents() { return m_events; }
|
||||||
|
virtual EJobResult doRead();
|
||||||
|
virtual EJobResult doWrite();
|
||||||
|
|
||||||
|
void setJob(ISocketMultiplexerJob*);
|
||||||
|
|
||||||
|
bool isReadable() const { return m_readable; }
|
||||||
|
bool isWritable() const { return m_writable; }
|
||||||
|
|
||||||
|
Mutex& getMutex() { return m_mutex; }
|
||||||
|
|
||||||
|
void sendEvent(Event::Type);
|
||||||
|
void discardWrittenData(int bytesWrote);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void sendConnectionFailedEvent(const char*);
|
||||||
|
void onConnected();
|
||||||
|
void onInputShutdown();
|
||||||
|
void onOutputShutdown();
|
||||||
|
void onDisconnected();
|
||||||
|
|
||||||
|
ISocketMultiplexerJob*
|
||||||
|
serviceConnecting(ISocketMultiplexerJob*,
|
||||||
|
bool, bool, bool);
|
||||||
|
ISocketMultiplexerJob*
|
||||||
|
serviceConnected(ISocketMultiplexerJob*,
|
||||||
|
bool, bool, bool);
|
||||||
|
|
||||||
|
bool m_readable = false;
|
||||||
|
bool m_writable = false;
|
||||||
|
bool m_connected = false;
|
||||||
|
IEventQueue* m_events;
|
||||||
|
StreamBuffer m_inputBuffer;
|
||||||
|
StreamBuffer m_outputBuffer;
|
||||||
|
Mutex m_mutex;
|
||||||
|
AutoArchSocket m_socket;
|
||||||
|
AutoArchSocket m_listener;
|
||||||
|
CondVar<bool> m_flushed;
|
||||||
|
SocketMultiplexer* m_socketMultiplexer;
|
||||||
|
};
|
||||||
122
src/lib/net/InverseSockets/InverseServerSocket.cpp
Normal file
122
src/lib/net/InverseSockets/InverseServerSocket.cpp
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2012-2022 Symless Ltd.
|
||||||
|
* Copyright (C) 2002 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* 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 LICENSE 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 "InverseServerSocket.h"
|
||||||
|
|
||||||
|
#include "net/NetworkAddress.h"
|
||||||
|
#include "net/SocketMultiplexer.h"
|
||||||
|
#include "net/TCPSocket.h"
|
||||||
|
#include "net/TSocketMultiplexerMethodJob.h"
|
||||||
|
#include "net/XSocket.h"
|
||||||
|
#include "io/XIO.h"
|
||||||
|
#include "mt/Lock.h"
|
||||||
|
#include "mt/Mutex.h"
|
||||||
|
#include "arch/Arch.h"
|
||||||
|
#include "arch/XArch.h"
|
||||||
|
#include "base/Log.h"
|
||||||
|
#include "base/IEventQueue.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// InverseServerSocket
|
||||||
|
//
|
||||||
|
|
||||||
|
InverseServerSocket::InverseServerSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family) :
|
||||||
|
m_socket(family),
|
||||||
|
m_events(events),
|
||||||
|
m_socketMultiplexer(socketMultiplexer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
InverseServerSocket::~InverseServerSocket()
|
||||||
|
{
|
||||||
|
m_socketMultiplexer->removeSocket(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseServerSocket::bind(const NetworkAddress& addr)
|
||||||
|
{
|
||||||
|
Lock lock(&m_mutex);
|
||||||
|
m_socket.connectSocket(addr);
|
||||||
|
setListeningJob(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseServerSocket::close()
|
||||||
|
{
|
||||||
|
Lock lock(&m_mutex);
|
||||||
|
m_socketMultiplexer->removeSocket(this);
|
||||||
|
m_socket.closeSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
InverseServerSocket::getEventTarget() const
|
||||||
|
{
|
||||||
|
return const_cast<void*>(static_cast<const void*>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
IDataSocket*
|
||||||
|
InverseServerSocket::accept()
|
||||||
|
{
|
||||||
|
IDataSocket* socket = nullptr;
|
||||||
|
try {
|
||||||
|
socket = new TCPSocket(m_events, m_socketMultiplexer, m_socket.getRawSocket());
|
||||||
|
if (socket != nullptr) {
|
||||||
|
setListeningJob();
|
||||||
|
}
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
catch (const XArchNetwork&) {
|
||||||
|
if (socket != nullptr) {
|
||||||
|
delete socket;
|
||||||
|
setListeningJob();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
catch (const std::exception &ex) {
|
||||||
|
if (socket != nullptr) {
|
||||||
|
delete socket;
|
||||||
|
setListeningJob();
|
||||||
|
}
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InverseServerSocket::setListeningJob(bool read)
|
||||||
|
{
|
||||||
|
m_socketMultiplexer->addSocket(this,
|
||||||
|
new TSocketMultiplexerMethodJob<InverseServerSocket>(
|
||||||
|
this, &InverseServerSocket::serviceListening,
|
||||||
|
m_socket.getRawSocket(), true, read));
|
||||||
|
}
|
||||||
|
|
||||||
|
ISocketMultiplexerJob*
|
||||||
|
InverseServerSocket::serviceListening(ISocketMultiplexerJob* job,
|
||||||
|
bool, bool write, bool error)
|
||||||
|
{
|
||||||
|
if (error) {
|
||||||
|
close();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (write) {
|
||||||
|
m_events->addEvent(Event(m_events->forIListenSocket().connecting(), this));
|
||||||
|
// stop polling on this socket until the client accepts
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return job;
|
||||||
|
}
|
||||||
61
src/lib/net/InverseSockets/InverseServerSocket.h
Normal file
61
src/lib/net/InverseSockets/InverseServerSocket.h
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2012-2022 Symless Ltd.
|
||||||
|
* Copyright (C) 2002 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* 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 LICENSE 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 "net/IListenSocket.h"
|
||||||
|
#include "arch/IArchNetwork.h"
|
||||||
|
#include "mt/Mutex.h"
|
||||||
|
#include "AutoArchSocket.h"
|
||||||
|
|
||||||
|
class ISocketMultiplexerJob;
|
||||||
|
class IEventQueue;
|
||||||
|
class SocketMultiplexer;
|
||||||
|
|
||||||
|
class InverseServerSocket : public IListenSocket {
|
||||||
|
public:
|
||||||
|
InverseServerSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family);
|
||||||
|
InverseServerSocket(InverseServerSocket const &) =delete;
|
||||||
|
InverseServerSocket(InverseServerSocket &&) =delete;
|
||||||
|
~InverseServerSocket() override;
|
||||||
|
|
||||||
|
InverseServerSocket& operator=(InverseServerSocket const &) =delete;
|
||||||
|
InverseServerSocket& operator=(InverseServerSocket &&) =delete;
|
||||||
|
|
||||||
|
// ISocket overrides
|
||||||
|
void bind(const NetworkAddress&) override;
|
||||||
|
void close() override;
|
||||||
|
void* getEventTarget() const override;
|
||||||
|
|
||||||
|
// IListenSocket overrides
|
||||||
|
IDataSocket* accept() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setListeningJob(bool read = false);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ISocketMultiplexerJob*
|
||||||
|
serviceListening(ISocketMultiplexerJob*,
|
||||||
|
bool, bool, bool);
|
||||||
|
|
||||||
|
private:
|
||||||
|
AutoArchSocket m_socket;
|
||||||
|
Mutex m_mutex;
|
||||||
|
IEventQueue* m_events;
|
||||||
|
SocketMultiplexer* m_socketMultiplexer;
|
||||||
|
};
|
||||||
59
src/lib/net/InverseSockets/InverseSocketFactory.cpp
Normal file
59
src/lib/net/InverseSockets/InverseSocketFactory.cpp
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2012-2022 Symless 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 LICENSE 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 "InverseSocketFactory.h"
|
||||||
|
#include "net/InverseSockets/InverseClientSocket.h"
|
||||||
|
#include "net/InverseSockets/InverseServerSocket.h"
|
||||||
|
#include "net/SecureSocket.h"
|
||||||
|
#include "net/SecureListenSocket.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// InverseSocketFactory
|
||||||
|
//
|
||||||
|
|
||||||
|
InverseSocketFactory::InverseSocketFactory(IEventQueue* events, SocketMultiplexer* socketMultiplexer) :
|
||||||
|
m_events(events),
|
||||||
|
m_socketMultiplexer(socketMultiplexer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
IDataSocket*
|
||||||
|
InverseSocketFactory::create(bool secure, IArchNetwork::EAddressFamily family) const
|
||||||
|
{
|
||||||
|
if (secure) {
|
||||||
|
auto secureSocket = new SecureSocket(m_events, m_socketMultiplexer, family);
|
||||||
|
secureSocket->initSsl (false);
|
||||||
|
return secureSocket;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new InverseClientSocket(m_events, m_socketMultiplexer, family);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IListenSocket*
|
||||||
|
InverseSocketFactory::createListen(bool secure, IArchNetwork::EAddressFamily family) const
|
||||||
|
{
|
||||||
|
IListenSocket* socket = nullptr;
|
||||||
|
|
||||||
|
if (secure) {
|
||||||
|
socket = new SecureListenSocket(m_events, m_socketMultiplexer, family);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
socket = new InverseServerSocket(m_events, m_socketMultiplexer, family);
|
||||||
|
}
|
||||||
|
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
34
src/lib/net/InverseSockets/InverseSocketFactory.h
Normal file
34
src/lib/net/InverseSockets/InverseSocketFactory.h
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2012-2022 Symless 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 LICENSE 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 "net/ISocketFactory.h"
|
||||||
|
|
||||||
|
class IEventQueue;
|
||||||
|
class SocketMultiplexer;
|
||||||
|
|
||||||
|
class InverseSocketFactory : public ISocketFactory {
|
||||||
|
public:
|
||||||
|
InverseSocketFactory(IEventQueue* events, SocketMultiplexer* socketMultiplexer);
|
||||||
|
|
||||||
|
// ISocketFactory overrides
|
||||||
|
IDataSocket* create(bool secure, IArchNetwork::EAddressFamily family = IArchNetwork::kINET) const override;
|
||||||
|
IListenSocket* createListen(bool secure, IArchNetwork::EAddressFamily family = IArchNetwork::kINET) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
IEventQueue* m_events = nullptr;
|
||||||
|
SocketMultiplexer* m_socketMultiplexer = nullptr;
|
||||||
|
};
|
||||||
|
|
@ -653,6 +653,11 @@ Config::formatInterval(const Interval& x)
|
||||||
(int)(x.second * 100.0f + 0.5f));
|
(int)(x.second * 100.0f + 0.5f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String Config::getClientAddress() const
|
||||||
|
{
|
||||||
|
return m_clientAddress;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Config::readSection(ConfigReadContext& s)
|
Config::readSection(ConfigReadContext& s)
|
||||||
{
|
{
|
||||||
|
|
@ -773,6 +778,9 @@ Config::readSectionOptions(ConfigReadContext& s)
|
||||||
else if (name == "clipboardSharingSize") {
|
else if (name == "clipboardSharingSize") {
|
||||||
addOption("", kOptionClipboardSharingSize, s.parseInt(value));
|
addOption("", kOptionClipboardSharingSize, s.parseInt(value));
|
||||||
}
|
}
|
||||||
|
else if (name == "clientAddress") {
|
||||||
|
m_clientAddress = value;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
handled = false;
|
handled = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -450,6 +450,11 @@ public:
|
||||||
*/
|
*/
|
||||||
static String formatInterval(const Interval&);
|
static String formatInterval(const Interval&);
|
||||||
|
|
||||||
|
//! Get client address as a string
|
||||||
|
/*!
|
||||||
|
* Return client address a string.
|
||||||
|
*/
|
||||||
|
String getClientAddress() const;
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -481,6 +486,7 @@ private:
|
||||||
InputFilter m_inputFilter;
|
InputFilter m_inputFilter;
|
||||||
bool m_hasLockToScreenAction;
|
bool m_hasLockToScreenAction;
|
||||||
IEventQueue* m_events;
|
IEventQueue* m_events;
|
||||||
|
String m_clientAddress;
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Configuration read context
|
//! Configuration read context
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,9 @@ ArgParser::parseClientArgs(lib::synergy::ClientArgs& args, int argc, const char*
|
||||||
else if (isArg(i, argc, argv, nullptr, "--invert-scroll")) {
|
else if (isArg(i, argc, argv, nullptr, "--invert-scroll")) {
|
||||||
args.m_clientScrollDirection = lib::synergy::ClientScrollDirection::INVERT_SERVER;
|
args.m_clientScrollDirection = lib::synergy::ClientScrollDirection::INVERT_SERVER;
|
||||||
}
|
}
|
||||||
|
else if (isArg(i, argc, argv, nullptr, "--host")) {
|
||||||
|
args.m_hostMode = true;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
if (i + 1 == argc) {
|
if (i + 1 == argc) {
|
||||||
args.m_synergyAddress = argv[i];
|
args.m_synergyAddress = argv[i];
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
#include "synergy/ClientArgs.h"
|
#include "synergy/ClientArgs.h"
|
||||||
#include "net/NetworkAddress.h"
|
#include "net/NetworkAddress.h"
|
||||||
#include "net/TCPSocketFactory.h"
|
#include "net/TCPSocketFactory.h"
|
||||||
|
#include "net/InverseSockets/InverseSocketFactory.h"
|
||||||
#include "net/SocketMultiplexer.h"
|
#include "net/SocketMultiplexer.h"
|
||||||
#include "net/XSocket.h"
|
#include "net/XSocket.h"
|
||||||
#include "mt/Thread.h"
|
#include "mt/Thread.h"
|
||||||
|
|
@ -128,6 +129,7 @@ ClientApp::help()
|
||||||
" [--yscroll <delta>]"
|
" [--yscroll <delta>]"
|
||||||
" [--sync-language]"
|
" [--sync-language]"
|
||||||
" [--invert-scroll]"
|
" [--invert-scroll]"
|
||||||
|
" [--host]"
|
||||||
WINAPI_ARG
|
WINAPI_ARG
|
||||||
HELP_SYS_ARGS
|
HELP_SYS_ARGS
|
||||||
HELP_COMMON_ARGS
|
HELP_COMMON_ARGS
|
||||||
|
|
@ -142,6 +144,7 @@ ClientApp::help()
|
||||||
" 120 by default.\n"
|
" 120 by default.\n"
|
||||||
" --sync-language set this parameter to enable language synchronization.\n"
|
" --sync-language set this parameter to enable language synchronization.\n"
|
||||||
" --invert-scroll invert scroll direction on this computer.\n"
|
" --invert-scroll invert scroll direction on this computer.\n"
|
||||||
|
" --host client starts a listener and waits for a server connection.\n"
|
||||||
HELP_COMMON_INFO_2
|
HELP_COMMON_INFO_2
|
||||||
"\n"
|
"\n"
|
||||||
"* marks defaults.\n"
|
"* marks defaults.\n"
|
||||||
|
|
@ -366,7 +369,7 @@ ClientApp::openClient(const String& name, const NetworkAddress& address,
|
||||||
m_events,
|
m_events,
|
||||||
name,
|
name,
|
||||||
address,
|
address,
|
||||||
new TCPSocketFactory(m_events, getSocketMultiplexer()),
|
getSocketFactory(),
|
||||||
screen,
|
screen,
|
||||||
args());
|
args());
|
||||||
|
|
||||||
|
|
@ -599,3 +602,17 @@ ClientApp::startNode()
|
||||||
m_bye(kExitFailed);
|
m_bye(kExitFailed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ISocketFactory* ClientApp::getSocketFactory() const
|
||||||
|
{
|
||||||
|
ISocketFactory* socketFactory = nullptr;
|
||||||
|
|
||||||
|
if (args().m_hostMode) {
|
||||||
|
socketFactory = new InverseSocketFactory(m_events, getSocketMultiplexer());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
socketFactory = new TCPSocketFactory(m_events, getSocketMultiplexer());
|
||||||
|
}
|
||||||
|
|
||||||
|
return socketFactory;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ class Event;
|
||||||
class Client;
|
class Client;
|
||||||
class NetworkAddress;
|
class NetworkAddress;
|
||||||
class Thread;
|
class Thread;
|
||||||
|
class ISocketFactory;
|
||||||
namespace lib {
|
namespace lib {
|
||||||
namespace synergy {
|
namespace synergy {
|
||||||
class ClientArgs;
|
class ClientArgs;
|
||||||
|
|
@ -82,6 +83,8 @@ public:
|
||||||
Client* getClientPtr() { return m_client; }
|
Client* getClientPtr() { return m_client; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
ISocketFactory* getSocketFactory() const;
|
||||||
|
|
||||||
Client* m_client;
|
Client* m_client;
|
||||||
synergy::Screen* m_clientScreen;
|
synergy::Screen* m_clientScreen;
|
||||||
NetworkAddress* m_serverAddress;
|
NetworkAddress* m_serverAddress;
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,12 @@ namespace lib {
|
||||||
*/
|
*/
|
||||||
ClientScrollDirection m_clientScrollDirection = ClientScrollDirection::SERVER;
|
ClientScrollDirection m_clientScrollDirection = ClientScrollDirection::SERVER;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief m_hostMode - activates host mode.
|
||||||
|
* Client starts a listener and waits for a server connection.
|
||||||
|
*/
|
||||||
|
bool m_hostMode = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
#include "synergy/ServerArgs.h"
|
#include "synergy/ServerArgs.h"
|
||||||
#include "net/SocketMultiplexer.h"
|
#include "net/SocketMultiplexer.h"
|
||||||
#include "net/TCPSocketFactory.h"
|
#include "net/TCPSocketFactory.h"
|
||||||
|
#include "net/InverseSockets/InverseSocketFactory.h"
|
||||||
#include "net/XSocket.h"
|
#include "net/XSocket.h"
|
||||||
#include "arch/Arch.h"
|
#include "arch/Arch.h"
|
||||||
#include "base/EventQueue.h"
|
#include "base/EventQueue.h"
|
||||||
|
|
@ -633,8 +634,8 @@ ClientListener*
|
||||||
ServerApp::openClientListener(const NetworkAddress& address)
|
ServerApp::openClientListener(const NetworkAddress& address)
|
||||||
{
|
{
|
||||||
ClientListener* listen = new ClientListener(
|
ClientListener* listen = new ClientListener(
|
||||||
address,
|
getAddress(address),
|
||||||
new TCPSocketFactory(m_events, getSocketMultiplexer()),
|
getSocketFactory(),
|
||||||
m_events,
|
m_events,
|
||||||
args().m_enableCrypto);
|
args().m_enableCrypto);
|
||||||
|
|
||||||
|
|
@ -678,6 +679,34 @@ ServerApp::handleScreenSwitched(const Event& e, void*)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ISocketFactory* ServerApp::getSocketFactory() const
|
||||||
|
{
|
||||||
|
ISocketFactory* socketFactory = nullptr;
|
||||||
|
const auto clientAddress = args().m_config->getClientAddress();
|
||||||
|
|
||||||
|
if (clientAddress.empty()) {
|
||||||
|
socketFactory = new TCPSocketFactory(m_events, getSocketMultiplexer());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
socketFactory = new InverseSocketFactory(m_events, getSocketMultiplexer());
|
||||||
|
}
|
||||||
|
|
||||||
|
return socketFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkAddress ServerApp::getAddress(const NetworkAddress& address) const
|
||||||
|
{
|
||||||
|
const auto clientAddress = args().m_config->getClientAddress();
|
||||||
|
if (clientAddress.empty()) {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
NetworkAddress addr(clientAddress.c_str(), kDefaultPort);
|
||||||
|
addr.resolve();
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
ServerApp::mainLoop()
|
ServerApp::mainLoop()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ class ClientListener;
|
||||||
class EventQueueTimer;
|
class EventQueueTimer;
|
||||||
class ILogOutputter;
|
class ILogOutputter;
|
||||||
class IEventQueue;
|
class IEventQueue;
|
||||||
|
class ISocketFactory;
|
||||||
namespace lib {
|
namespace lib {
|
||||||
namespace synergy{
|
namespace synergy{
|
||||||
class ServerArgs;
|
class ServerArgs;
|
||||||
|
|
@ -119,6 +120,8 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleScreenSwitched(const Event&, void* data);
|
void handleScreenSwitched(const Event&, void* data);
|
||||||
|
ISocketFactory* getSocketFactory() const;
|
||||||
|
NetworkAddress getAddress(const NetworkAddress& address) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// configuration file name
|
// configuration file name
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue