1 /*
2  * synergy -- mouse and keyboard sharing utility
3  * Copyright (C) 2012-2016 Symless Ltd.
4  * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
5  *
6  * This package is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * found in the file LICENSE that should have accompanied this file.
9  *
10  * This package is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #define DOWNLOAD_URL "http://symless.com/?source=gui"
20 #define HELP_URL     "http://symless.com/help?source=gui"
21 
22 #include <array>
23 
24 #include "MainWindow.h"
25 
26 #include "Fingerprint.h"
27 #include "AboutDialog.h"
28 #include "ServerConfigDialog.h"
29 #include "SettingsDialog.h"
30 #include "ActivationDialog.h"
31 #include "DataDownloader.h"
32 #include "CommandProcess.h"
33 #include "LicenseManager.h"
34 #include <shared/EditionType.h>
35 #include "QUtility.h"
36 #include "ProcessorArch.h"
37 #include "SslCertificate.h"
38 #include "Zeroconf.h"
39 #include <QPushButton>
40 
41 #if defined(Q_OS_MAC)
42 #include "OSXHelpers.h"
43 #endif
44 
45 #include <QtCore>
46 #include <QtGui>
47 #include <QtNetwork>
48 #include <QNetworkAccessManager>
49 #include <QMenu>
50 #include <QMenuBar>
51 #include <QMessageBox>
52 #include <QFileDialog>
53 #include <QDesktopServices>
54 #include <QDesktopWidget>
55 
56 #if defined(Q_OS_MAC)
57 #include <ApplicationServices/ApplicationServices.h>
58 #endif
59 
60 static const char* tlsCheckString = "network encryption protocol: ";
61 
62 static const int debugLogLevel = 1;
63 
64 static const char* synergyLightIconFiles[] =
65 {
66     ":/res/icons/64x64/synergy-light-disconnected.png",
67     ":/res/icons/64x64/synergy-light-disconnected.png",
68     ":/res/icons/64x64/synergy-light-connected.png",
69     ":/res/icons/64x64/synergy-light-transfering.png",
70     ":/res/icons/64x64/synergy-light-disconnected.png"
71 };
72 
73 static const char* synergyDarkIconFiles[] =
74 {
75     ":/res/icons/64x64/synergy-dark-disconnected.png",
76     ":/res/icons/64x64/synergy-dark-disconnected.png",
77     ":/res/icons/64x64/synergy-dark-connected.png",
78     ":/res/icons/64x64/synergy-dark-transfering.png",
79     ":/res/icons/64x64/synergy-dark-disconnected.png"    //synergyPendingRetry
80 };
81 
82 static const char* synergyDefaultIconFiles[] =
83 {
84     ":/res/icons/16x16/synergy-disconnected.png",   //synergyDisconnected
85     ":/res/icons/16x16/synergy-disconnected.png",   //synergyConnecting
86     ":/res/icons/16x16/synergy-connected.png",      //synergyConnected
87     ":/res/icons/16x16/synergy-transfering.png",    //synergyListening
88     ":/res/icons/16x16/synergy-disconnected.png"    //synergyPendingRetry
89 };
90 
91 #ifdef SYNERGY_ENTERPRISE
MainWindow(AppConfig & appConfig)92 MainWindow::MainWindow (AppConfig& appConfig)
93 #else
94 MainWindow::MainWindow (AppConfig& appConfig,
95                         LicenseManager& licenseManager)
96 #endif
97 :
98 #ifndef SYNERGY_ENTERPRISE
99     m_LicenseManager(&licenseManager),
100     m_ActivationDialogRunning(false),
101 #endif
102     m_pZeroconf(nullptr),
103     m_AppConfig(&appConfig),
104     m_pSynergy(NULL),
105     m_SynergyState(synergyDisconnected),
106     m_ServerConfig(5, 3, m_AppConfig, this),
107     m_AlreadyHidden(false),
108     m_pMenuBar(NULL),
109     m_pMenuFile(NULL),
110     m_pMenuEdit(NULL),
111     m_pMenuWindow(NULL),
112     m_pMenuHelp(NULL),
113     m_pCancelButton(NULL),
114     m_ExpectedRunningState(kStopped),
115     m_SecureSocket(false),
116     m_serverConnection(*this),
117     m_clientConnection(*this)
118 {
119 #if !defined(SYNERGY_ENTERPRISE) && defined(SYNERGY_AUTOCONFIG)
120     m_pZeroconf = new Zeroconf(this);
121 #endif
122 
123     setupUi(this);
124     updateAutoConfigWidgets();
125 
126     createMenuBar();
127     loadSettings();
128     initConnections();
129 
130     m_pWidgetUpdate->hide();
131     m_VersionChecker.setApp(appPath(appConfig.synergycName()));
132 
133     updateScreenName();
134     connect(m_AppConfig, SIGNAL(screenNameChanged()), this, SLOT(updateScreenName()));
135     m_pLabelIpAddresses->setText(tr("This computer's IP addresses: %1").arg(getIPAddresses()));
136 
137 #if defined(Q_OS_WIN)
138     // ipc must always be enabled, so that we can disable command when switching to desktop mode.
139     connect(&m_IpcClient, SIGNAL(readLogLine(const QString&)), this, SLOT(appendLogRaw(const QString&)));
140     connect(&m_IpcClient, SIGNAL(errorMessage(const QString&)), this, SLOT(appendLogError(const QString&)));
141     connect(&m_IpcClient, SIGNAL(infoMessage(const QString&)), this, SLOT(appendLogInfo(const QString&)));
142     m_IpcClient.connectToHost();
143 #endif
144 
145     // change default size based on os
146 #if defined(Q_OS_MAC)
147     resize(720, 550);
148     setMinimumSize(size());
149 #elif defined(Q_OS_LINUX)
150     resize(700, 530);
151     setMinimumSize(size());
152 #endif
153 
154     m_trialLabel->hide();
155 
156     // hide padlock icon
157     secureSocket(false);
158 
159 
160 
161     connect (this, SIGNAL(windowShown()),
162              this, SLOT(on_windowShown()), Qt::QueuedConnection);
163 #ifndef SYNERGY_ENTERPRISE
164     connect (m_LicenseManager, SIGNAL(editionChanged(Edition)),
165              this, SLOT(setEdition(Edition)), Qt::QueuedConnection);
166 
167     connect (m_LicenseManager, SIGNAL(showLicenseNotice(QString)),
168              this, SLOT(showLicenseNotice(QString)), Qt::QueuedConnection);
169 
170     connect (m_LicenseManager, SIGNAL(InvalidLicense()),
171              this, SLOT(InvalidLicense()), Qt::QueuedConnection);
172 #endif
173 
174     connect (m_AppConfig, SIGNAL(sslToggled()),
175              this, SLOT(updateLocalFingerprint()), Qt::QueuedConnection);
176 
177     connect (m_AppConfig, SIGNAL(zeroConfToggled()),
178              this, SLOT(zeroConfToggled()), Qt::QueuedConnection);
179 
180 #ifdef SYNERGY_ENTERPRISE
181     setWindowTitle ("Synergy 1 Enterprise");
182 #else
183     setWindowTitle (m_LicenseManager->activeEditionName());
184     m_LicenseManager->refresh();
185 #endif
186 
187     QString lastVersion = m_AppConfig->lastVersion();
188     QString currentVersion = m_VersionChecker.getVersion();
189     if (lastVersion != currentVersion) {
190         m_AppConfig->setLastVersion (currentVersion);
191 #ifndef SYNERGY_ENTERPRISE
192         m_LicenseManager->notifyUpdate (lastVersion, currentVersion);
193 #endif
194     }
195 
196 #ifdef SYNERGY_ENTERPRISE
197     m_pActivate->setVisible(false);
198 #endif
199 
200 #if !defined(SYNERGY_ENTERPRISE) && defined(SYNERGY_AUTOCONFIG)
201     updateZeroconfService();
202 
203     addZeroconfServer(m_AppConfig->autoConfigServer());
204 
205     updateAutoConfigWidgets();
206 #endif
207 }
208 
~MainWindow()209 MainWindow::~MainWindow()
210 {
211     if (appConfig().processMode() == Desktop) {
212         m_ExpectedRunningState = kStopped;
213         stopDesktop();
214     }
215 
216 #if !defined(SYNERGY_ENTERPRISE) && defined(SYNERGY_AUTOCONFIG)
217     delete m_pZeroconf;
218 #endif
219 }
220 
open()221 void MainWindow::open()
222 {
223     std::array<QAction *, 7> trayMenu = {
224         m_pActionStartSynergy,
225         m_pActionStopSynergy,
226         nullptr,
227         m_pActionMinimize,
228         m_pActionRestore,
229         nullptr,
230         m_pActionQuit
231     };
232 
233     m_trayIcon.create(trayMenu, [this](QObject const *o, const char *s) {
234         connect(o, s, this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
235         setIcon(synergyDisconnected);
236     });
237 
238     if (appConfig().getAutoHide()) {
239         hide();
240     } else {
241         showNormal();
242     }
243 
244     m_VersionChecker.checkLatest();
245 
246     // only start if user has previously started. this stops the gui from
247     // auto hiding before the user has configured synergy (which of course
248     // confuses first time users, who think synergy has crashed).
249     if (appConfig().startedBefore() && appConfig().processMode() == Desktop) {
250         startSynergy();
251     }
252 }
253 
setStatus(const QString & status)254 void MainWindow::setStatus(const QString &status)
255 {
256     m_pStatusLabel->setText(status);
257 }
258 
retranslateMenuBar()259 void MainWindow::retranslateMenuBar()
260 {
261     m_pMenuFile->setTitle(tr("&File"));
262     m_pMenuEdit->setTitle(tr("&Edit"));
263     m_pMenuWindow->setTitle(tr("&Window"));
264     m_pMenuHelp->setTitle(tr("&Help"));
265 }
266 
createMenuBar()267 void MainWindow::createMenuBar()
268 {
269     m_pMenuBar = new QMenuBar(this);
270     m_pMenuFile = new QMenu("", m_pMenuBar);
271     m_pMenuEdit = new QMenu("", m_pMenuBar);
272     m_pMenuWindow = new QMenu("", m_pMenuBar);
273     m_pMenuHelp = new QMenu("", m_pMenuBar);
274     retranslateMenuBar();
275 
276     m_pMenuBar->addAction(m_pMenuFile->menuAction());
277     m_pMenuBar->addAction(m_pMenuEdit->menuAction());
278 #if !defined(Q_OS_MAC)
279     m_pMenuBar->addAction(m_pMenuWindow->menuAction());
280 #endif
281     m_pMenuBar->addAction(m_pMenuHelp->menuAction());
282 
283     m_pMenuFile->addAction(m_pActionStartSynergy);
284     m_pMenuFile->addAction(m_pActionStopSynergy);
285     m_pMenuFile->addSeparator();
286     m_pMenuFile->addAction(m_pActivate);
287     m_pMenuFile->addSeparator();
288     m_pMenuFile->addAction(m_pActionSave);
289     m_pMenuFile->addSeparator();
290     m_pMenuFile->addAction(m_pActionQuit);
291     m_pMenuEdit->addAction(m_pActionSettings);
292     m_pMenuWindow->addAction(m_pActionMinimize);
293     m_pMenuWindow->addAction(m_pActionRestore);
294     m_pMenuHelp->addAction(m_pActionAbout);
295     m_pMenuHelp->addAction(m_pActionHelp);
296 
297 
298     setMenuBar(m_pMenuBar);
299 }
300 
loadSettings()301 void MainWindow::loadSettings()
302 {
303     enableServer(appConfig().getServerGroupChecked());
304     enableClient(appConfig().getClientGroupChecked());
305 
306     m_pLineEditHostname->setText(appConfig().getServerHostname());
307 }
308 
initConnections()309 void MainWindow::initConnections()
310 {
311     connect(m_pActionMinimize, SIGNAL(triggered()), this, SLOT(hide()));
312     connect(m_pActionRestore, SIGNAL(triggered()), this, SLOT(showNormal()));
313     connect(m_pActionStartSynergy, SIGNAL(triggered()), this, SLOT(actionStart()));
314     connect(m_pActionStopSynergy, SIGNAL(triggered()), this, SLOT(stopSynergy()));
315     connect(m_pActionQuit, SIGNAL(triggered()), qApp, SLOT(quit()));
316     connect(&m_VersionChecker, SIGNAL(updateFound(const QString&)), this, SLOT(updateFound(const QString&)));
317 }
318 
saveSettings()319 void MainWindow::saveSettings()
320 {
321     // program settings
322     appConfig().setServerGroupChecked(m_pRadioGroupServer->isChecked());
323     appConfig().setClientGroupChecked(m_pRadioGroupClient->isChecked());
324     appConfig().setServerHostname(m_pLineEditHostname->text());
325 
326     /* Save everything */
327     GUI::Config::ConfigWriter::make()->globalSave();
328 }
329 
zeroConfToggled()330 void MainWindow::zeroConfToggled() {
331 #if !defined(SYNERGY_ENTERPRISE) && defined(SYNERGY_AUTOCONFIG)
332     updateZeroconfService();
333 
334     addZeroconfServer(m_AppConfig->autoConfigServer());
335 
336     updateAutoConfigWidgets();
337 #endif
338 }
339 
setIcon(qSynergyState state) const340 void MainWindow::setIcon(qSynergyState state) const
341 {
342     QIcon icon;
343 
344 #ifdef Q_OS_MAC
345     if (isOSXUseDarkIcons())
346         icon.addFile(synergyDarkIconFiles[state]);
347     else
348         icon.addFile(synergyLightIconFiles[state]);
349 #else
350     icon.addFile(synergyDefaultIconFiles[state]);
351 #endif
352 
353     m_trayIcon.set(icon);
354 }
355 
trayActivated(QSystemTrayIcon::ActivationReason reason)356 void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason reason)
357 {
358     if (reason == QSystemTrayIcon::DoubleClick)
359     {
360         if (isVisible())
361         {
362             hide();
363         }
364         else
365         {
366             showNormal();
367             activateWindow();
368         }
369     }
370 }
371 
logOutput()372 void MainWindow::logOutput()
373 {
374     if (m_pSynergy)
375     {
376         QString text(m_pSynergy->readAllStandardOutput());
377         foreach(QString line, text.split(QRegExp("\r|\n|\r\n")))
378         {
379             if (!line.isEmpty())
380             {
381                 appendLogRaw(line);
382             }
383         }
384     }
385 }
386 
logError()387 void MainWindow::logError()
388 {
389     if (m_pSynergy)
390     {
391         appendLogRaw(m_pSynergy->readAllStandardError());
392     }
393 }
394 
updateFound(const QString & version)395 void MainWindow::updateFound(const QString &version)
396 {
397     m_pWidgetUpdate->show();
398     m_pLabelUpdate->setText(
399         tr("<p>Your version of Synergy is out of date. "
400            "Version <b>%1</b> is now available to "
401            "<a href=\"%2\">download</a>.</p>")
402         .arg(version).arg(DOWNLOAD_URL));
403 }
404 
appendLogInfo(const QString & text)405 void MainWindow::appendLogInfo(const QString& text)
406 {
407     appendLogRaw(getTimeStamp() + " INFO: " + text);
408 }
409 
appendLogDebug(const QString & text)410 void MainWindow::appendLogDebug(const QString& text) {
411     if (appConfig().logLevel() >= debugLogLevel) {
412         appendLogRaw(getTimeStamp() + " DEBUG: " + text);
413     }
414 }
415 
appendLogError(const QString & text)416 void MainWindow::appendLogError(const QString& text)
417 {
418     appendLogRaw(getTimeStamp() + " ERROR: " + text);
419 }
420 
appendLogRaw(const QString & text)421 void MainWindow::appendLogRaw(const QString& text)
422 {
423     foreach(QString line, text.split(QRegExp("\r|\n|\r\n"))) {
424         if (!line.isEmpty()) {
425 
426             // HACK: macOS 10.13.4+ spamming error lines in logs making them
427             // impossible to read and debug; giving users a red herring.
428             if (line.contains("calling TIS/TSM in non-main thread environment")) {
429                 continue;
430             }
431 
432             m_pLogOutput->appendPlainText(line);
433             updateFromLogLine(line);
434         }
435     }
436 }
437 
updateFromLogLine(const QString & line)438 void MainWindow::updateFromLogLine(const QString &line)
439 {
440     // TODO: This shouldn't be updating from log needs a better way of doing this
441     checkConnected(line);
442     checkFingerprint(line);
443     checkSecureSocket(line);
444 
445 #ifndef SYNERGY_ENTERPRISE
446     checkLicense(line);
447 #endif
448 }
449 
checkConnected(const QString & line)450 void MainWindow::checkConnected(const QString& line)
451 {
452     // TODO: implement ipc connection state messages to replace this hack.
453     if (m_pRadioGroupServer->isChecked())
454     {
455         m_serverConnection.update(line);
456         m_pLabelServerState->updateServerState(line);
457     }
458     else
459     {
460         m_clientConnection.update(line);
461         m_pLabelClientState->updateClientState(line);
462     }
463 
464     if (line.contains("connected to server") || line.contains("has connected"))
465     {
466         setSynergyState(synergyConnected);
467 
468         if (!appConfig().startedBefore() && isVisible()) {
469                 QMessageBox::information(
470                     this, "Synergy",
471                     tr("Synergy is now connected. You can close the "
472                     "config window and Synergy will remain connected in "
473                     "the background."));
474 
475             appConfig().setStartedBefore(true);
476         }
477     }
478     else if (line.contains("started server"))
479     {
480         setSynergyState(synergyListening);
481     }
482     else if (line.contains("disconnected from server") || line.contains("process exited"))
483     {
484         setSynergyState(synergyDisconnected);
485     }
486     else if (line.contains("connecting to"))
487     {
488         setSynergyState(synergyConnecting);
489     }
490 }
491 
492 #ifndef SYNERGY_ENTERPRISE
checkLicense(const QString & line)493 void MainWindow::checkLicense(const QString &line)
494 {
495     if (line.contains("trial has expired")) {
496         licenseManager().refresh();
497         raiseActivationDialog();
498     }
499 }
500 #endif
501 
checkFingerprint(const QString & line)502 void MainWindow::checkFingerprint(const QString& line)
503 {
504     QRegExp fingerprintRegex(".*server fingerprint: ([A-F0-9:]+)");
505     if (!fingerprintRegex.exactMatch(line)) {
506         return;
507     }
508 
509     QString fingerprint = fingerprintRegex.cap(1);
510     if (Fingerprint::trustedServers().isTrusted(fingerprint)) {
511         return;
512     }
513 
514     static bool messageBoxAlreadyShown = false;
515 
516     if (!messageBoxAlreadyShown) {
517         stopSynergy();
518 
519         messageBoxAlreadyShown = true;
520         QMessageBox::StandardButton fingerprintReply =
521             QMessageBox::information(
522             this, tr("Security question"),
523             tr("You are connecting to a server. Here is it's fingerprint:\n\n"
524                "%1\n\n"
525                "Compare this fingerprint to the one on your server's screen."
526                "If the two don't match exactly, then it's probably not the server "
527                "you're expecting (it could be a malicious user).\n\n"
528                "To automatically trust this fingerprint for future "
529                "connections, click Yes. To reject this fingerprint and "
530                "disconnect from the server, click No.")
531             .arg(fingerprint),
532             QMessageBox::Yes | QMessageBox::No);
533 
534         if (fingerprintReply == QMessageBox::Yes) {
535             // restart core process after trusting fingerprint.
536             Fingerprint::trustedServers().trust(fingerprint);
537             startSynergy();
538         }
539 
540         messageBoxAlreadyShown = false;
541     }
542 }
543 
checkSecureSocket(const QString & line)544 void MainWindow::checkSecureSocket(const QString& line)
545 {
546     // obviously not very secure, since this can be tricked by injecting something
547     // into the log. however, since we don't have IPC between core and GUI... patches welcome.
548     const int index = line.indexOf(tlsCheckString, 0, Qt::CaseInsensitive);
549     if (index > 0) {
550         secureSocket(true);
551 
552         //Get the protocol version from the line
553         m_SecureSocketVersion = line.mid(index + strlen(tlsCheckString)); // Compliant: we made sure that tlsCheckString variable ended with null(static const char* declaration)
554     }
555 }
getTimeStamp()556 QString MainWindow::getTimeStamp()
557 {
558     QDateTime current = QDateTime::currentDateTime();
559     return '[' + current.toString(Qt::ISODate) + ']';
560 }
561 
restartSynergy()562 void MainWindow::restartSynergy()
563 {
564     stopSynergy();
565     startSynergy();
566 }
567 
proofreadInfo()568 void MainWindow::proofreadInfo()
569 {
570 #ifndef SYNERGY_ENTERPRISE
571     setEdition(m_AppConfig->edition()); // Why is this here?
572 #endif
573     int oldState = m_SynergyState;
574     m_SynergyState = synergyDisconnected;
575     setSynergyState((qSynergyState)oldState);
576 }
577 
showEvent(QShowEvent * event)578 void MainWindow::showEvent(QShowEvent* event)
579 {
580     QMainWindow::showEvent(event);
581     emit windowShown();
582 }
583 
clearLog()584 void MainWindow::clearLog()
585 {
586     m_pLogOutput->clear();
587 }
588 
startSynergy()589 void MainWindow::startSynergy()
590 {
591     saveSettings();
592 
593 #ifndef SYNERGY_ENTERPRISE
594     SerialKey serialKey = m_LicenseManager->serialKey();
595     if (!serialKey.isValid()) {
596         if (QDialog::Rejected == raiseActivationDialog()) {
597             return;
598         }
599     }
600 #endif
601     bool desktopMode = appConfig().processMode() == Desktop;
602     bool serviceMode = appConfig().processMode() == Service;
603 
604     appendLogDebug("starting process");
605     m_ExpectedRunningState = kStarted;
606     setSynergyState(synergyConnecting);
607 
608     QString app;
609     QStringList args;
610 
611     args << "-f" << "--no-tray" << "--debug" << appConfig().logLevelText();
612 
613 
614     args << "--name" << appConfig().screenName();
615 
616     if (desktopMode)
617     {
618         setSynergyProcess(new QProcess(this));
619     }
620     else
621     {
622         // tell client/server to talk to daemon through ipc.
623         args << "--ipc";
624 
625 #if defined(Q_OS_WIN)
626         // tell the client/server to shut down when a ms windows desk
627         // is switched; this is because we may need to elevate or not
628         // based on which desk the user is in (login always needs
629         // elevation, where as default desk does not).
630         // Note that this is only enabled when synergy is set to elevate
631         // 'as needed' (e.g. on a UAC dialog popup) in order to prevent
632         // unnecessary restarts when synergy was started elevated or
633         // when it is not allowed to elevate. In these cases restarting
634         // the server is fruitless.
635         if (appConfig().elevateMode() == ElevateAsNeeded) {
636                 args << "--stop-on-desk-switch";
637         }
638 #endif
639     }
640 
641 #ifndef Q_OS_LINUX
642 
643     if (m_ServerConfig.enableDragAndDrop()) {
644         args << "--enable-drag-drop";
645     }
646 
647 #endif
648 
649 #if defined(Q_OS_WIN)
650     if (m_AppConfig->getCryptoEnabled()) {
651         args << "--enable-crypto";
652         args << "--tls-cert" <<  QString("\"%1\"").arg(m_AppConfig->getTLSCertPath());
653     }
654     // on windows, the profile directory changes depending on the user that
655     // launched the process (e.g. when launched with elevation). setting the
656     // profile dir on launch ensures it uses the same profile dir is used
657     // no matter how its relaunched.
658     args << "--profile-dir" << getProfileRootForArg();
659 
660 #else
661     if (m_AppConfig->getCryptoEnabled()) {
662         args << "--enable-crypto";
663         args << "--tls-cert" << m_AppConfig->getTLSCertPath();
664     }
665 #endif
666 
667     // put a space between last log output and new instance.
668     if (!m_pLogOutput->toPlainText().isEmpty())
669         appendLogRaw("");
670 
671     appendLogInfo("starting " + QString(synergyType() == synergyServer ? "server" : "client"));
672 
673     if ((synergyType() == synergyClient && !clientArgs(args, app))
674         || (synergyType() == synergyServer && !serverArgs(args, app)))
675     {
676         stopSynergy();
677         return;
678     }
679 
680     if (desktopMode)
681     {
682         connect(synergyProcess(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(synergyFinished(int, QProcess::ExitStatus)));
683         connect(synergyProcess(), SIGNAL(readyReadStandardOutput()), this, SLOT(logOutput()));
684         connect(synergyProcess(), SIGNAL(readyReadStandardError()), this, SLOT(logError()));
685     }
686 
687     qDebug() << args;
688 
689     // show command if debug log level...
690     if (appConfig().logLevel() >= 4) {
691         appendLogInfo(QString("command: %1 %2").arg(app, args.join(" ")));
692     }
693 
694     appendLogInfo("log level: " + appConfig().logLevelText());
695 
696     if (appConfig().logToFile())
697         appendLogInfo("log file: " + appConfig().logFilename());
698 
699     if (desktopMode)
700     {
701         synergyProcess()->start(app, args);
702         if (!synergyProcess()->waitForStarted())
703         {
704             show();
705             QMessageBox::warning(this, tr("Program can not be started"), QString(tr("The executable<br><br>%1<br><br>could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.").arg(app)));
706             return;
707         }
708     }
709 
710     if (serviceMode)
711     {
712         QString command(app + " " + args.join(" "));
713         m_IpcClient.sendCommand(command, appConfig().elevateMode());
714     }
715 }
716 
actionStart()717 void MainWindow::actionStart()
718 {
719     m_clientConnection.setCheckConnection(true);
720     startSynergy();
721 }
722 
retryStart()723 void MainWindow::retryStart()
724 {
725     //This function is only called after a failed start
726     //Only start synergy if the current state is pending retry
727     if (m_SynergyState == synergyPendingRetry)
728     {
729         startSynergy();
730     }
731 }
732 
clientArgs(QStringList & args,QString & app)733 bool MainWindow::clientArgs(QStringList& args, QString& app)
734 {
735     app = appPath(appConfig().synergycName());
736 
737     if (!QFile::exists(app))
738     {
739         show();
740         QMessageBox::warning(this, tr("Synergy client not found"),
741                              tr("The executable for the synergy client does not exist."));
742         return false;
743     }
744 
745 #if defined(Q_OS_WIN)
746     // wrap in quotes so a malicious user can't start \Program.exe as admin.
747     app = QString("\"%1\"").arg(app);
748 #endif
749 
750     if (appConfig().logToFile())
751     {
752         appConfig().persistLogDir();
753         args << "--log" << appConfig().logFilenameCmd();
754     }
755 
756 
757 #if !defined(SYNERGY_ENTERPRISE) && defined(SYNERGY_AUTOCONFIG)
758     // check auto config first, if it is disabled or no server detected,
759     // use line edit host name if it is not empty
760     if (appConfig().autoConfig()) {
761         if (m_pComboServerList->count() != 0) {
762             QString serverIp = m_pComboServerList->currentText();
763             args << serverIp + ":" + QString::number(appConfig().port());
764             return true;
765         }
766         else {
767             show();
768             QMessageBox::warning(
769                 this, tr("No server selected"),
770                 tr("No auto config server was selected, try manual mode instead."));
771             return false;
772         }
773     }
774 #endif
775 
776     if (m_pLineEditHostname->text().isEmpty())
777     {
778 #if !defined(SYNERGY_ENTERPRISE) && defined(SYNERGY_AUTOCONFIG)
779         //check if autoconfig mode is enabled
780         if (!appConfig().autoConfig())
781         {
782 #endif
783             show();
784             QMessageBox::warning(
785                 this, tr("Hostname is empty"),
786                 tr("Please fill in a hostname for the synergy client to connect to."));
787             return false;
788 
789 #if !defined(SYNERGY_ENTERPRISE) && defined(SYNERGY_AUTOCONFIG)
790         }
791         else
792         {
793             return false;
794         }
795 #endif
796     }
797 
798     QString hostName = m_pLineEditHostname->text();
799     // if interface is IPv6 - ensure that ip is in square brackets
800     if (hostName.count(':') > 1) {
801         if(hostName[0] != '[') {
802             hostName.insert(0, '[');
803         }
804         if(hostName[hostName.size() - 1] != ']') {
805             hostName.push_back(']');
806         }
807     }
808 
809     args << hostName + ":" + QString::number(appConfig().port());
810     return true;
811 }
812 
configFilename()813 QString MainWindow::configFilename()
814 {
815     QString configFullPath;
816     if (appConfig().getUseExternalConfig())
817     {
818         configFullPath = appConfig().getConfigFile();
819     }
820     else
821     {
822         QStringList errors;
823         for (auto path : {QStandardPaths::AppDataLocation,
824                           QStandardPaths::AppConfigLocation})
825         {
826             auto configDirPath = QStandardPaths::writableLocation(path);
827             if (!QDir().mkpath(configDirPath))
828             {
829                 errors.push_back(tr("Failed to create config folder \"%1\"").arg(configDirPath));
830                 continue;
831             }
832 
833             QFile configFile(configDirPath + "/LastConfig.cfg");
834             if (!configFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
835             {
836                 errors.push_back(tr("File:\"%1\" Error:%2").arg(configFile.fileName(), configFile.errorString()));
837                 continue;
838             }
839 
840             serverConfig().save(configFile);
841             configFile.close();
842             configFullPath = configFile.fileName();
843 
844             break;
845         }
846 
847         if (configFullPath.isEmpty())
848         {
849             QMessageBox::critical(this, tr("Cannot write configuration file"), errors.join('\n'));
850         }
851     }
852 
853     return configFullPath;
854 }
855 
address() const856 QString MainWindow::address() const
857 {
858     QString i = appConfig().networkInterface();
859     // if interface is IPv6 - ensure that ip is in square brackets
860     if (i.count(':') > 1) {
861         if(i[0] != '[') {
862             i.insert(0, '[');
863         }
864         if(i[i.size() - 1] != ']') {
865             i.push_back(']');
866         }
867     }
868     return (!i.isEmpty() ? i : "") + ":" + QString::number(appConfig().port());
869 }
870 
appPath(const QString & name)871 QString MainWindow::appPath(const QString& name)
872 {
873     return appConfig().synergyProgramDir() + name;
874 }
875 
serverArgs(QStringList & args,QString & app)876 bool MainWindow::serverArgs(QStringList& args, QString& app)
877 {
878     app = appPath(appConfig().synergysName());
879 
880     if (!QFile::exists(app))
881     {
882         QMessageBox::warning(this, tr("Synergy server not found"),
883                              tr("The executable for the synergy server does not exist."));
884         return false;
885     }
886 
887 #if defined(Q_OS_WIN)
888     // wrap in quotes so a malicious user can't start \Program.exe as admin.
889     app = QString("\"%1\"").arg(app);
890 #endif
891 
892     if (appConfig().logToFile())
893     {
894         appConfig().persistLogDir();
895 
896         args << "--log" << appConfig().logFilenameCmd();
897     }
898 
899     QString configFilename = this->configFilename();
900     if (configFilename.isEmpty())
901     {
902         return false;
903     }
904 #if defined(Q_OS_WIN)
905     // wrap in quotes in case username contains spaces.
906     configFilename = QString("\"%1\"").arg(configFilename);
907 #endif
908     args << "-c" << configFilename << "--address" << address();
909     appendLogInfo("config file: " + configFilename);
910 
911 #ifndef SYNERGY_ENTERPRISE
912     if (!appConfig().serialKey().isEmpty()) {
913         args << "--serial-key" << appConfig().serialKey();
914     }
915 #endif
916 
917     return true;
918 }
919 
stopSynergy()920 void MainWindow::stopSynergy()
921 {
922     appendLogDebug("stopping process");
923 
924     m_ExpectedRunningState = kStopped;
925 
926     if (appConfig().processMode() == Service)
927     {
928         stopService();
929     }
930     else if (appConfig().processMode() == Desktop)
931     {
932         stopDesktop();
933     }
934 
935     setSynergyState(synergyDisconnected);
936 
937     // reset so that new connects cause auto-hide.
938     m_AlreadyHidden = false;
939 }
940 
stopService()941 void MainWindow::stopService()
942 {
943     // send empty command to stop service from laucning anything.
944     m_IpcClient.sendCommand("", appConfig().elevateMode());
945 }
946 
stopDesktop()947 void MainWindow::stopDesktop()
948 {
949     QMutexLocker locker(&m_StopDesktopMutex);
950     if (!synergyProcess()) {
951         return;
952     }
953 
954     appendLogInfo("stopping synergy desktop process");
955 
956     if (synergyProcess()->isOpen()) {
957         synergyProcess()->close();
958     }
959 
960     delete synergyProcess();
961     setSynergyProcess(NULL);
962 }
963 
synergyFinished(int exitCode,QProcess::ExitStatus)964 void MainWindow::synergyFinished(int exitCode, QProcess::ExitStatus)
965 {
966     if (exitCode == 0) {
967         appendLogInfo(QString("process exited normally"));
968     }
969     else {
970         appendLogError(QString("process exited with error code: %1").arg(exitCode));
971     }
972 
973     if (m_ExpectedRunningState == kStarted) {
974 
975         setSynergyState(synergyPendingRetry);
976         QTimer::singleShot(1000, this, SLOT(retryStart()));
977         appendLogInfo(QString("detected process not running, auto restarting"));
978     }
979     else {
980         setSynergyState(synergyDisconnected);
981     }
982 }
983 
setSynergyState(qSynergyState state)984 void MainWindow::setSynergyState(qSynergyState state)
985 {
986     // always assume connection is not secure when connection changes
987     // to anything except connected. the only way the padlock shows is
988     // when the correct TLS version string is detected.
989     if (state != synergyConnected) {
990         secureSocket(false);
991     }
992 
993     if (synergyState() == state)
994         return;
995 
996     if ((state == synergyConnected) || (state == synergyConnecting) || (state == synergyListening) || (state == synergyPendingRetry))
997     {
998         disconnect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger()));
999         connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger()));
1000         m_pButtonToggleStart->setText(tr("&Stop"));
1001         m_pButtonApply->setEnabled(true);
1002     }
1003     else if (state == synergyDisconnected)
1004     {
1005         disconnect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger()));
1006         connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger()));
1007         m_pButtonToggleStart->setText(tr("&Start"));
1008         m_pButtonApply->setEnabled(false);
1009     }
1010 
1011     bool running = false;
1012     if (state == synergyConnected || state == synergyListening) {
1013         running = true;
1014     }
1015 
1016     m_pActionStartSynergy->setEnabled(!running);
1017     m_pActionStopSynergy->setEnabled(running);
1018 
1019     switch (state)
1020     {
1021     case synergyListening: {
1022         if (synergyType() == synergyServer) {
1023             setStatus(tr("Synergy is waiting for clients").arg(m_SecureSocketVersion));
1024         }
1025 
1026         break;
1027     }
1028     case synergyConnected: {
1029         if (m_SecureSocket) {
1030             setStatus(tr("Synergy is connected (with %1)").arg(m_SecureSocketVersion));
1031         }
1032         else {
1033             setStatus(tr("Synergy is running (without TLS encryption)").arg(m_SecureSocketVersion));
1034         }
1035         break;
1036     }
1037     case synergyConnecting:
1038         setStatus(tr("Synergy is starting..."));
1039         break;
1040     case synergyPendingRetry:
1041         setStatus(tr("There was an error, retrying..."));
1042         break;
1043     case synergyDisconnected:
1044         setStatus(tr("Synergy is not running"));
1045         break;
1046     }
1047 
1048     setIcon(state);
1049 
1050     m_SynergyState = state;
1051 }
1052 
setVisible(bool visible)1053 void MainWindow::setVisible(bool visible)
1054 {
1055     QMainWindow::setVisible(visible);
1056     m_pActionMinimize->setEnabled(visible);
1057     m_pActionRestore->setEnabled(!visible);
1058 
1059 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 // lion
1060     // dock hide only supported on lion :(
1061     ProcessSerialNumber psn = { 0, kCurrentProcess };
1062 #pragma GCC diagnostic push
1063 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1064     GetCurrentProcess(&psn);
1065 #pragma GCC diagnostic pop
1066     if (visible)
1067         TransformProcessType(&psn, kProcessTransformToForegroundApplication);
1068     else
1069         TransformProcessType(&psn, kProcessTransformToBackgroundApplication);
1070 #endif
1071 }
1072 
getIPAddresses()1073 QString MainWindow::getIPAddresses()
1074 {
1075     QStringList result;
1076     bool hinted = false;
1077     const auto localnet = QHostAddress::parseSubnet("192.168.0.0/16");
1078     const QList<QHostAddress> addresses = QNetworkInterface::allAddresses();
1079 
1080     for (const auto& address : addresses) {
1081         if (address.protocol() == QAbstractSocket::IPv4Protocol &&
1082             address != QHostAddress(QHostAddress::LocalHost) &&
1083             !address.isInSubnet(QHostAddress::parseSubnet("169.254.0.0/16"))) {
1084 
1085             // usually 192.168.x.x is a useful ip for the user, so indicate
1086             // this by making it bold.
1087             if (!hinted && address.isInSubnet(localnet)) {
1088                 QString format = "<span style=\"color:#4285F4;\">%1</span>";
1089                 result.append(format.arg(address.toString()));
1090                 hinted = true;
1091             }
1092             else {
1093                 result.append(address.toString());
1094             }
1095         }
1096     }
1097 
1098     if (result.isEmpty()) {
1099         result.append(tr("Unknown"));
1100     }
1101 
1102     return result.join(", ");
1103 }
1104 
changeEvent(QEvent * event)1105 void MainWindow::changeEvent(QEvent* event)
1106 {
1107     if (event != 0)
1108     {
1109         switch (event->type())
1110         {
1111         case QEvent::LanguageChange:
1112         {
1113             retranslateUi(this);
1114             retranslateMenuBar();
1115 
1116             proofreadInfo();
1117 
1118             break;
1119         }
1120         case QEvent::WindowStateChange:
1121         {
1122             windowStateChanged();
1123             break;
1124         }
1125         }
1126     }
1127     // all that do not return are allowing the event to propagate
1128     QMainWindow::changeEvent(event);
1129 }
1130 
addZeroconfServer(const QString name)1131 void MainWindow::addZeroconfServer(const QString name)
1132 {
1133     // don't add yourself to the server list.
1134     if (getIPAddresses().contains(name)) {
1135         return;
1136     }
1137 
1138     if (m_pComboServerList->findText(name) == -1) {
1139         m_pComboServerList->addItem(name);
1140     }
1141 }
1142 
setEdition(Edition edition)1143 void MainWindow::setEdition(Edition edition)
1144 {
1145 #ifndef SYNERGY_ENTERPRISE
1146     setWindowTitle(m_LicenseManager->getEditionName (edition));
1147 #endif
1148 }
1149 
1150 #ifndef SYNERGY_ENTERPRISE
InvalidLicense()1151 void MainWindow::InvalidLicense()
1152 {
1153    stopSynergy();
1154    m_AppConfig->activationHasRun(false);
1155 }
1156 
showLicenseNotice(const QString & notice)1157 void MainWindow::showLicenseNotice(const QString& notice)
1158 {
1159     this->m_trialLabel->hide();
1160 
1161     if (!notice.isEmpty()) {
1162         this->m_trialLabel->setText(notice);
1163         this->m_trialLabel->show();
1164     }
1165 
1166     setWindowTitle (m_LicenseManager->activeEditionName());
1167 }
1168 #endif
1169 
updateLocalFingerprint()1170 void MainWindow::updateLocalFingerprint()
1171 {
1172     if (m_AppConfig->getCryptoEnabled() &&
1173         Fingerprint::local().fileExists() &&
1174         m_pRadioGroupServer->isChecked())
1175     {
1176         m_pLabelFingerprint->setVisible(true);
1177     }
1178     else
1179     {
1180         m_pLabelFingerprint->setVisible(false);
1181     }
1182 }
1183 
1184 #ifndef SYNERGY_ENTERPRISE
1185 LicenseManager&
licenseManager() const1186 MainWindow::licenseManager() const
1187 {
1188     return *m_LicenseManager;
1189 }
1190 #endif
1191 
on_m_pActionSave_triggered()1192 bool  MainWindow::on_m_pActionSave_triggered()
1193 {
1194     QString fileName = QFileDialog::getSaveFileName(this, tr("Save configuration as..."));
1195 
1196     if (!fileName.isEmpty() && !serverConfig().save(fileName))
1197     {
1198         QMessageBox::warning(this, tr("Save failed"), tr("Could not save configuration to file."));
1199         return true;
1200     }
1201 
1202     return false;
1203 }
1204 
on_m_pActionAbout_triggered()1205 void MainWindow::on_m_pActionAbout_triggered()
1206 {
1207     AboutDialog dlg(this, appPath(appConfig().synergycName()));
1208     dlg.exec();
1209 }
1210 
on_m_pActionHelp_triggered()1211 void MainWindow::on_m_pActionHelp_triggered()
1212 {
1213     QDesktopServices::openUrl(QUrl(HELP_URL));
1214 }
1215 
updateZeroconfService()1216 void MainWindow::updateZeroconfService()
1217 {
1218 #if !defined(SYNERGY_ENTERPRISE) && defined(SYNERGY_AUTOCONFIG)
1219 
1220     // reset the server list in case one has gone away.
1221     // it'll be re-added after the zeroconf service restarts.
1222     m_pComboServerList->clear();
1223 
1224     if (m_pZeroconf != nullptr) {
1225         if (appConfig().autoConfig()) {
1226             m_pZeroconf->startService();
1227         }
1228         else {
1229             m_pZeroconf->stopService();
1230         }
1231     }
1232 #endif
1233 }
1234 
updateAutoConfigWidgets()1235 void MainWindow::updateAutoConfigWidgets()
1236 {
1237    m_pLabelServerName->show();
1238    m_pLineEditHostname->show();
1239 
1240    m_pLabelAutoDetected->hide();
1241    m_pComboServerList->hide();
1242 }
1243 
on_m_pActionSettings_triggered()1244 void MainWindow::on_m_pActionSettings_triggered()
1245 {
1246     SettingsDialog(this, appConfig()).exec();
1247 }
1248 
autoAddScreen(const QString name)1249 void MainWindow::autoAddScreen(const QString name)
1250 {
1251     if (m_ServerConfig.ignoreAutoConfigClient()) {
1252         appendLogDebug(QString("ignoring zeroconf screen: %1").arg(name));
1253         return;
1254     }
1255 
1256 #ifndef SYNERGY_ENTERPRISE
1257     if (m_ActivationDialogRunning) {
1258         // TODO: refactor this code
1259         // add this screen to the pending list and check this list until
1260         // users finish activation dialog
1261         m_PendingClientNames.append(name);
1262         return;
1263     }
1264 #endif
1265 
1266     int r = m_ServerConfig.autoAddScreen(name);
1267     if (r != kAutoAddScreenOk) {
1268         switch (r) {
1269         case kAutoAddScreenManualServer:
1270             showConfigureServer(
1271                 tr("Please add the server (%1) to the grid.")
1272                     .arg(appConfig().screenName()));
1273             break;
1274 
1275         case kAutoAddScreenManualClient:
1276             showConfigureServer(
1277                 tr("Please drag the new client screen (%1) "
1278                     "to the desired position on the grid.")
1279                     .arg(name));
1280             break;
1281         }
1282     }
1283 }
1284 
showConfigureServer(const QString & message)1285 void MainWindow::showConfigureServer(const QString& message)
1286 {
1287     ServerConfigDialog dlg(this, serverConfig());
1288     dlg.message(message);
1289     dlg.exec();
1290 }
1291 
on_m_pButtonConfigureServer_clicked()1292 void MainWindow::on_m_pButtonConfigureServer_clicked()
1293 {
1294     showConfigureServer();
1295 }
1296 
on_m_pActivate_triggered()1297 void MainWindow::on_m_pActivate_triggered()
1298 {
1299 #ifndef SYNERGY_ENTERPRISE
1300     raiseActivationDialog();
1301 #endif
1302 }
1303 
on_m_pButtonApply_clicked()1304 void MainWindow::on_m_pButtonApply_clicked()
1305 {
1306     m_clientConnection.setCheckConnection(true);
1307     restartSynergy();
1308 }
1309 
1310 #ifndef SYNERGY_ENTERPRISE
raiseActivationDialog()1311 int MainWindow::raiseActivationDialog()
1312 {
1313     if (m_ActivationDialogRunning) {
1314         return QDialog::Rejected;
1315     }
1316     ActivationDialog activationDialog (this, appConfig(), licenseManager());
1317     m_ActivationDialogRunning = true;
1318     int result = activationDialog.exec();
1319     m_ActivationDialogRunning = false;
1320     if (!m_PendingClientNames.empty()) {
1321         foreach (const QString& name, m_PendingClientNames) {
1322             autoAddScreen(name);
1323         }
1324 
1325         m_PendingClientNames.clear();
1326     }
1327     return result;
1328 }
1329 #endif
1330 
on_windowShown()1331 void MainWindow::on_windowShown()
1332 {
1333 #ifndef SYNERGY_ENTERPRISE
1334 	if (!m_AppConfig->activationHasRun() &&
1335 		!m_LicenseManager->serialKey().isValid()){
1336 			raiseActivationDialog();
1337 	}
1338 #endif
1339 }
1340 
getProfileRootForArg()1341 QString MainWindow::getProfileRootForArg()
1342 {
1343     CoreInterface coreInterface;
1344     QString dir = coreInterface.getProfileDir();
1345 
1346     // HACK: strip our app name since we're returning the root dir.
1347 #if defined(Q_OS_WIN)
1348     dir.replace("\\Synergy", "");
1349 #else
1350     dir.replace("/.synergy", "");
1351 #endif
1352 
1353     return QString("\"%1\"").arg(dir);
1354 }
1355 
secureSocket(bool secureSocket)1356 void MainWindow::secureSocket(bool secureSocket)
1357 {
1358     m_SecureSocket = secureSocket;
1359     if (secureSocket) {
1360         m_pLabelPadlock->show();
1361     }
1362     else {
1363         m_pLabelPadlock->hide();
1364     }
1365 }
1366 
on_m_pLabelComputerName_linkActivated(const QString &)1367 void MainWindow::on_m_pLabelComputerName_linkActivated(const QString&)
1368 {
1369    m_pActionSettings->trigger();
1370 }
1371 
on_m_pLabelFingerprint_linkActivated(const QString &)1372 void MainWindow::on_m_pLabelFingerprint_linkActivated(const QString&)
1373 {
1374     QMessageBox::information(this, "SSL/TLS fingerprint", Fingerprint::local().readFirst());
1375 }
1376 
on_m_pComboServerList_currentIndexChanged(const QString & server)1377 void MainWindow::on_m_pComboServerList_currentIndexChanged(const QString &server)
1378 {
1379     appConfig().setAutoConfigServer(server);
1380 }
1381 
windowStateChanged()1382 void MainWindow::windowStateChanged()
1383 {
1384     if (windowState() == Qt::WindowMinimized && appConfig().getMinimizeToTray())
1385         hide();
1386 }
1387 
updateScreenName()1388 void MainWindow::updateScreenName()
1389 {
1390     m_pLabelComputerName->setText(tr("This computer's name: %1 (<a href=\"#\" style=\"text-decoration: none; color: #4285F4;\">Preferences</a>)").arg(appConfig().screenName()));
1391     serverConfig().updateServerName();
1392 }
1393 
enableServer(bool enable)1394 void MainWindow::enableServer(bool enable)
1395 {
1396     m_pRadioGroupServer->setChecked(enable);
1397 
1398     if (enable)
1399     {
1400         m_pButtonConfigureServer->show();
1401         m_pLabelServerState->show();
1402         updateLocalFingerprint();
1403         m_pButtonToggleStart->setEnabled(enable);
1404     }
1405     else
1406     {
1407         m_pLabelFingerprint->hide();
1408         m_pButtonConfigureServer->hide();
1409         m_pLabelServerState->hide();
1410     }
1411 }
1412 
enableClient(bool enable)1413 void MainWindow::enableClient(bool enable)
1414 {
1415     m_pRadioGroupClient->setChecked(enable);
1416 
1417     if (enable)
1418     {
1419         m_pLabelServerName->show();
1420         m_pLineEditHostname->show();
1421         m_pButtonConnect->show();
1422         m_pButtonToggleStart->setEnabled(enable);
1423     }
1424     else
1425     {
1426         m_pLabelClientState->hide();
1427         m_pLabelServerName->hide();
1428         m_pLineEditHostname->hide();
1429         m_pButtonConnect->hide();
1430     }
1431 }
1432 
1433 
on_m_pRadioGroupServer_clicked(bool)1434 void MainWindow::on_m_pRadioGroupServer_clicked(bool)
1435 {
1436     enableServer(true);
1437     enableClient(false);
1438 }
1439 
on_m_pRadioGroupClient_clicked(bool)1440 void MainWindow::on_m_pRadioGroupClient_clicked(bool)
1441 {
1442     enableClient(true);
1443     enableServer(false);
1444 }
1445 
on_m_pButtonConnect_clicked()1446 void MainWindow::on_m_pButtonConnect_clicked()
1447 {
1448     on_m_pButtonApply_clicked();
1449 }
1450 
1451