diff --git a/.gitignore b/.gitignore index 72ceca0cb..c0a47f406 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ Brewfile.lock.json *.code-workspace .env* /scripts/*.egg-info +/*.user +*.ui.autosave diff --git a/.vscode/launch.json b/.vscode/launch.json index ca1e56e0e..f0639c554 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,55 +2,68 @@ "version": "0.2.0", "configurations": [ { - "name": "gui lldb", + "name": "gui unix", "type": "lldb", "request": "launch", "program": "${workspaceFolder}/build/bin/synergy", - "preLaunchTask": "build-kill" + "preLaunchTask": "kill-build" }, { - "name": "unittests lldb", + "name": "gui windows", + "type": "cppvsdbg", + "cwd": "${workspaceRoot}/build/bin", + "request": "launch", + "program": "${workspaceFolder}/build/bin/synergy", + "internalConsoleOptions": "openOnSessionStart", + "preLaunchTask": "kill-build" + }, + { + "name": "unittests unix", "type": "lldb", "request": "launch", "program": "${workspaceFolder}/build/bin/unittests", "preLaunchTask": "build" }, { - "name": "integtests lldb", + "name": "unittests windows", + "type": "cppvsdbg", + "cwd": "${workspaceRoot}/build/bin", + "request": "launch", + "program": "${workspaceFolder}/build/bin/unittests", + "internalConsoleOptions": "openOnSessionStart", + "preLaunchTask": "build" + }, + { + "name": "integtests unix", "type": "lldb", "request": "launch", "program": "${workspaceFolder}/build/bin/integtests", "preLaunchTask": "build" }, { - "name": "win daemon attach", + "name": "integtests windows", + "type": "cppvsdbg", + "cwd": "${workspaceRoot}/build/bin", + "request": "launch", + "program": "${workspaceFolder}/build/bin/integtests", + "internalConsoleOptions": "openOnSessionStart", + "preLaunchTask": "build" + }, + { + "name": "daemon windows", + "type": "cppvsdbg", + "cwd": "${workspaceRoot}/build/bin", + "request": "launch", + "program": "${workspaceFolder}/build/bin/synergyd", + "args": ["-f"], + "internalConsoleOptions": "openOnSessionStart", + "preLaunchTask": "build" + }, + { + "name": "daemon windows attach", "type": "cppvsdbg", "request": "attach", "processId": "${command:pickProcess}" - }, - { - "name": "win daemon attach - lldb", - "type": "lldb", - "request": "attach", - "program": "${workspaceFolder}/build/bin/synergyd" - }, - { - "name": "win daemon launch", - "type": "cppvsdbg", - "cwd": "${workspaceRoot}/build/bin", - "request": "launch", - "program": "${workspaceFolder}/build/bin/synergyd", - "args": ["-f"], - "internalConsoleOptions": "openOnSessionStart" - }, - { - "name": "win daemon launch - rebuild", - "type": "cppvsdbg", - "cwd": "${workspaceRoot}/build/bin", - "request": "launch", - "program": "${workspaceFolder}/build/bin/synergyd", - "args": ["-f"], - "preLaunchTask": "build" } ] } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 10615f208..822405ca9 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -22,7 +22,7 @@ }, { "label": "tests", - "dependsOn": ["unittests", "integtests"], + "dependsOn": ["integtests", "unittests"], "problemMatcher": [] }, { @@ -31,24 +31,43 @@ "command": "killall synergy; killall synergyc; killall synergys || true", "windows": { "command": "taskkill /F /IM synergy.exe /IM synergyc.exe /IM synergys.exe; $true" + }, + "presentation": { + "reveal": "silent" } }, { - "label": "build-kill", - "dependsOn": ["build", "kill"] + "label": "kill-build", + "dependsOn": ["kill", "build"], + "problemMatcher": [] }, { "label": "gui", "type": "process", "command": "${workspaceFolder}/build/bin/synergy", - "dependsOn": ["build", "kill"] + "dependsOn": ["build", "kill"], + "problemMatcher": [], + "windows": { + "command": "${workspaceFolder}/build/bin/synergy.exe" + } }, { - "label": "windows daemon", + "label": "restart daemon", "type": "shell", - "command": "python scripts/windows_daemon.py", + "command": "python scripts/daemon.py --restart", "dependsOn": ["build"] }, + { + "label": "reinstall daemon", + "type": "shell", + "command": "python scripts/daemon.py --reinstall", + "dependsOn": ["build"] + }, + { + "label": "stop daemon", + "type": "shell", + "command": "python scripts/daemon.py --stop" + }, { "label": "clean-gcda", "type": "shell", diff --git a/ChangeLog b/ChangeLog index 15b5181ea..220fe70df 100644 --- a/ChangeLog +++ b/ChangeLog @@ -57,6 +57,7 @@ Enhancements: - #7403 Solve low hanging reliability and maintainability issues - #7404 Restore integtests and add to CI as warning comment on failure - #7406 Migrate scripts from `requirements.txt` to `pyproject.toml` +- #7407 Implement safer memory use, improve dev env, fixed GUI bugs # 1.14.6 diff --git a/cmake/Libraries.cmake b/cmake/Libraries.cmake index ea590395c..1bff3664a 100644 --- a/cmake/Libraries.cmake +++ b/cmake/Libraries.cmake @@ -1,19 +1,3 @@ -# Synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012-2024 Symless Ltd. -# Copyright (C) 2009-2012 Nick Bolton -# -# 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 . - macro(configure_libs) set(libs) @@ -295,6 +279,9 @@ macro(configure_windows_libs) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/res/win/version.rc.in ${CMAKE_BINARY_DIR}/src/version.rc @ONLY) + find_openssl_dir_win32(OPENSSL_PATH) + add_definitions(-DOPENSSL_PATH="${OPENSSL_PATH}") + endmacro() macro(configure_openssl) @@ -383,3 +370,36 @@ macro(configure_python) set(PYTHON_BIN "${CMAKE_BINARY_DIR}/python/bin/python") endif() endmacro() + +# +# Find the OpenSSL directory on Windows based on the location of the first +# `openssl` binary found. +# +function(find_openssl_dir_win32 result) + + execute_process( + COMMAND where openssl + OUTPUT_VARIABLE OPENSSL_PATH + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # It's possible that there are multiple OpenSSL installations on the system, + # which is the case on GitHub runners. For now we'll pick the first one, but + # that's probably not very robust. Maybe our choco config could install to a + # specific location? + string(REGEX REPLACE "\r?\n" ";" OPENSSL_PATH_LIST ${OPENSSL_PATH}) + message(STATUS "Found OpenSSL binaries at: ${OPENSSL_PATH_LIST}") + + list(GET OPENSSL_PATH_LIST 0 OPENSSL_FIRST_PATH) + message(STATUS "First OpenSSL binary: ${OPENSSL_FIRST_PATH}") + + get_filename_component(OPENSSL_BIN_DIR ${OPENSSL_FIRST_PATH} DIRECTORY) + message(STATUS "OpenSSL bin dir: ${OPENSSL_BIN_DIR}") + + get_filename_component(OPENSSL_DIR ${OPENSSL_BIN_DIR} DIRECTORY) + message(STATUS "OpenSSL install root dir: ${OPENSSL_DIR}") + + set(${result} + ${OPENSSL_DIR} + PARENT_SCOPE) + +endfunction() diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index 441b0e470..0b03ce1db 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -51,8 +51,6 @@ macro(configure_windows_packaging) set(CPACK_PACKAGE_VERSION ${SYNERGY_VERSION_MS}) set(QT_PATH $ENV{CMAKE_PREFIX_PATH}) - find_openssl_dir_win32(OPENSSL_PATH) - configure_files(${CMAKE_CURRENT_SOURCE_DIR}/res/dist/wix ${CMAKE_BINARY_DIR}/installer) @@ -157,36 +155,3 @@ macro(configure_files srcDir destDir) endforeach(templateFile) endmacro(configure_files) - -# -# Find the OpenSSL directory on Windows based on the location of the first -# `openssl` binary found. -# -function(find_openssl_dir_win32 result) - - execute_process( - COMMAND where openssl - OUTPUT_VARIABLE OPENSSL_PATH - OUTPUT_STRIP_TRAILING_WHITESPACE) - - # It's possible that there are multiple OpenSSL installations on the system, - # which is the case on GitHub runners. For now we'll pick the first one, but - # that's probably not very robust. Maybe our choco config could install to a - # specific location? - string(REGEX REPLACE "\r?\n" ";" OPENSSL_PATH_LIST ${OPENSSL_PATH}) - message(STATUS "Found OpenSSL binaries at: ${OPENSSL_PATH_LIST}") - - list(GET OPENSSL_PATH_LIST 0 OPENSSL_FIRST_PATH) - message(STATUS "First OpenSSL binary: ${OPENSSL_FIRST_PATH}") - - get_filename_component(OPENSSL_BIN_DIR ${OPENSSL_FIRST_PATH} DIRECTORY) - message(STATUS "OpenSSL bin dir: ${OPENSSL_BIN_DIR}") - - get_filename_component(OPENSSL_DIR ${OPENSSL_BIN_DIR} DIRECTORY) - message(STATUS "OpenSSL install root dir: ${OPENSSL_DIR}") - - set(${result} - ${OPENSSL_DIR} - PARENT_SCOPE) - -endfunction() diff --git a/scripts/daemon.py b/scripts/daemon.py new file mode 100644 index 000000000..40a34ddd7 --- /dev/null +++ b/scripts/daemon.py @@ -0,0 +1,192 @@ +import os, sys, time, subprocess, argparse +import lib.windows as windows +import lib.file_utils as file_utils +import lib.env as env +import lib.colors as colors +import psutil + +DEFAULT_BIN_NAME = "synergyd" +DEFAULT_SOURCE_DIR = os.path.join("build", "temp", "bin") +DEFAULT_TARGET_DIR = os.path.join("build", "bin") +SERVICE_NOT_RUNNING_ERROR = 2 +ERROR_ACCESS_VIOLATION = 0xC0000005 +IGNORE_PROCESSES = ["synergy.exe"] + + +class Context: + def __init__(self, verbose): + self.verbose = verbose + + +def main(): + """Entry point for the script.""" + + parser = argparse.ArgumentParser() + parser.add_argument("--reinstall", action="store_true") + parser.add_argument("--stop", action="store_true") + parser.add_argument("--restart", action="store_true") + parser.add_argument("--pause-on-exit", action="store_true") + parser.add_argument("--source-dir", default=DEFAULT_SOURCE_DIR) + parser.add_argument("--target-dir", default=DEFAULT_TARGET_DIR) + parser.add_argument("--bin-name", default=DEFAULT_BIN_NAME) + parser.add_argument("--verbose", action="store_true") + args = parser.parse_args() + + if not env.is_windows(): + print( + f"{colors.ERROR_TEXT} This script is only supported on Windows", + file=sys.stderr, + ) + sys.exit(1) + + context = Context(args.verbose) + + try: + if args.reinstall: + reinstall(context, args.source_dir, args.target_dir, args.bin_name) + elif args.stop: + stop(context, args.target_dir) + elif args.restart: + restart(context, args.source_dir, args.target_dir) + else: + print("No action specified", file=sys.stderr) + exit(1) + except Exception as e: + print(f"{colors.ERROR_TEXT} {e}", file=sys.stderr) + + if args.pause_on_exit: + input("Press enter to continue...") + + +def print_verbose(context, message): + if context.verbose: + print(message) + + +def ensure_admin(): + if not windows.is_admin(): + windows.relaunch_as_admin(__file__) + sys.exit() + + +def restart(context, source_dir, target_dir): + """Stops the daemon service, copies files, and restarts the daemon service.""" + + ensure_admin() + stop(context, target_dir) + copy_files(source_dir, target_dir) + start() + + +def reinstall(context, source_dir, target_dir, bin_name): + """Stops and uninstalls daemon service, copies files, and reinstalls the daemon service.""" + + ensure_admin() + stop(context, target_dir) + + source_bin_path = f"{os.path.join(source_dir, bin_name)}.exe" + + copy_files(source_dir, target_dir) + + print("Removing old daemon service") + try: + subprocess.run([source_bin_path, "/uninstall"], shell=True, check=True) + except subprocess.CalledProcessError as e: + check_access_violation(e.returncode, source_bin_path) + if e.returncode != 0: + print( + f"{colors.WARNING_TEXT} Uninstall failed, return code: {e.returncode}", + file=sys.stderr, + ) + + target_bin_path = os.path.join(target_dir, bin_name + ".exe") + + try: + print("Installing daemon service") + subprocess.run([target_bin_path, "/install"], shell=True, check=True) + except subprocess.CalledProcessError as e: + check_access_violation(e.returncode, target_bin_path) + if e.returncode != 0: + print(f"{colors.WARNING_TEXT} Install failed, return code: {e.returncode}") + + +def copy_files(source_dir, target_dir): + options = file_utils.CopyOptions(ignore_errors=True, verbose=False) + print(f"Copying files from {source_dir} to {target_dir}") + file_utils.copy(f"{source_dir}/*", target_dir, options) + + +def stop(context, target_dir): + ensure_admin() + print("Stopping daemon service") + try: + subprocess.run(["net", "stop", "synergy"], shell=True, check=True) + except subprocess.CalledProcessError as e: + if e.returncode == SERVICE_NOT_RUNNING_ERROR: + print_verbose(context, "Daemon service not running") + else: + raise e + + # Wait for Windows to release the file handles after process termination. + wait_for_stop(context, target_dir) + + +def start(): + ensure_admin() + print("Starting daemon service") + subprocess.run(["net", "start", "synergy"], shell=True, check=True) + + +def wait_for_stop(context, target_dir): + if is_any_process_running(context, target_dir): + print("Waiting for file handles to release...", end="", flush=True) + while is_any_process_running(context, target_dir): + if not context.verbose: + print(".", end="", flush=True) + time.sleep(1) + if not context.verbose: + print() + + +def check_access_violation(return_code, bin_path): + if return_code == ERROR_ACCESS_VIOLATION: + print( + f"{colors.WARNING_TEXT} Process crashed with memory access violation: {bin_path}", + file=sys.stderr, + ) + + +def is_ignored_process(exe): + for ignore_process in IGNORE_PROCESSES: + if exe.endswith(ignore_process): + return True + return False + + +def is_any_process_running(context, dir): + """Check if there is any running process that contains the given directory.""" + + print_verbose(context, f"Checking if any process is running in: {dir}") + + for proc in psutil.process_iter(attrs=["name", "exe"]): + exe = proc.info["exe"] + + if not exe: + print_verbose(context, f"Skipping process with no exe: {proc}") + continue + + if is_ignored_process(exe): + print_verbose(context, f"Ignoring process: {exe}") + continue + + try: + if dir.lower() in exe.lower(): + print_verbose(context, f"Process found: {exe}") + return True + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + + return False + + +main() diff --git a/scripts/fancy_copy.py b/scripts/fancy_copy.py index 3d6e69a43..b6490efc7 100644 --- a/scripts/fancy_copy.py +++ b/scripts/fancy_copy.py @@ -3,6 +3,7 @@ import argparse import lib.env as env import lib.file_utils as file_utils +import lib.colors as colors def main(): @@ -34,7 +35,7 @@ def main(): if not args.ignore_errors: raise e else: - print(f"Error: {e}") + print(f"{colors.ERROR_TEXT} {e}") if __name__ == "__main__": diff --git a/scripts/lib/colors.py b/scripts/lib/colors.py new file mode 100644 index 000000000..64956cf4d --- /dev/null +++ b/scripts/lib/colors.py @@ -0,0 +1,8 @@ +import colorama # type: ignore +from colorama import Fore # type: ignore + +colorama.init() + +ERROR_TEXT = f"{Fore.RED}Error:{Fore.RESET}" +WARNING_TEXT = f"{Fore.YELLOW}Warning:{Fore.RESET}" +HINT_TEXT = f"{Fore.LIGHTBLUE_EX}Hint:{Fore.RESET}" diff --git a/scripts/lib/file_utils.py b/scripts/lib/file_utils.py index 278c0cfae..9b4a04c79 100644 --- a/scripts/lib/file_utils.py +++ b/scripts/lib/file_utils.py @@ -1,9 +1,6 @@ import glob, os, shutil, sys - -import colorama # type: ignore -from colorama import Fore # type: ignore - -colorama.init() +import lib.env as env +import lib.colors as colors class CopyOptions: @@ -24,24 +21,24 @@ def copy(source, target, options): context = CopyContext() if options.verbose: print(f"Copying files from {source} to {target}") - for match in glob.glob(source): - if os.path.isfile(match): - copy_file(match, target, options, context) - elif os.path.isdir(match): - copy_dir(match, target, options, context) - else: - raise RuntimeError(f"Path {match} is not a file or directory") + try: + for match in glob.glob(source): - if context.errors and options.ignore_errors: - print( - f"{Fore.YELLOW}WARNING:{Fore.RESET} Ignored {context.errors} copy error(s)" - ) + if os.path.isfile(match): + copy_file(match, target, options, context) + elif os.path.isdir(match): + copy_dir(match, target, options, context) + else: + raise RuntimeError(f"Path {match} is not a file or directory") + finally: + if context.errors and options.ignore_errors: + print(f"{colors.WARNING_TEXT} Ignored {context.errors} copy error(s)") - if context.permission_error: - print( - f"{Fore.BLUE}HINT:{Fore.RESET} A permission error may mean that the file is in use" - ) + if context.permission_error and env.is_windows(): + print( + f"{colors.HINT_TEXT} A Windows file permission error may mean that the file is in use" + ) def copy_dir(match, target, options, context): @@ -50,12 +47,8 @@ def copy_dir(match, target, options, context): try: shutil.copytree(match, target, dirs_exist_ok=True) - - except PermissionError as e: - context.permission_error = True - handle_copy_error(e, options, context) except Exception as e: - handle_copy_error(e, options, context) + handle_all_copy_errors(e, options, context) def copy_file(match, target, options, context): @@ -64,17 +57,33 @@ def copy_file(match, target, options, context): try: shutil.copy(match, target) - except PermissionError as e: - context.permission_error = True - handle_copy_error(e, options, context) except Exception as e: - handle_copy_error(e, options, context) + handle_all_copy_errors(e, options, context) -def handle_copy_error(e, options, context): - context.errors += 1 +def handle_all_copy_errors(error, options, context): + + if isinstance(error, shutil.Error): + for _, _, file_error in error.args[0]: + handle_copy_error(file_error, options, context) + else: + handle_copy_error(error, options, context) if not options.ignore_errors: - raise e - else: - print(f"{Fore.YELLOW}WARNING:{Fore.RESET} Copy failed: {e}", file=sys.stderr) + raise error + + +def handle_copy_error(error, options, context): + if isinstance(error, PermissionError): + context.permission_error = True + + if isinstance(error, str): + context.permission_error = error.startswith("[Errno 13] Permission denied") + + context.errors += 1 + + if options.ignore_errors: + print( + f"{colors.WARNING_TEXT} Copy failed: {error}", + file=sys.stderr, + ) diff --git a/scripts/windows_daemon.py b/scripts/windows_daemon.py deleted file mode 100644 index daf36a1d0..000000000 --- a/scripts/windows_daemon.py +++ /dev/null @@ -1,112 +0,0 @@ -import os, sys, time, subprocess, argparse, glob -import lib.windows as windows -import lib.file_utils as file_utils -import psutil - -DEFAULT_BIN_NAME = "synergyd" -DEFAULT_SOURCE_DIR = os.path.join("build", "temp", "bin") -DEFAULT_TARGET_DIR = os.path.join("build", "bin") -SERVICE_NOT_RUNNING_ERROR = 2 -ERROR_ACCESS_VIOLATION = 0xC0000005 - - -def main(): - """Entry point for the script.""" - - parser = argparse.ArgumentParser() - parser.add_argument("--pause-on-exit", action="store_true") - parser.add_argument("--source-dir", default=DEFAULT_SOURCE_DIR) - parser.add_argument("--target-dir", default=DEFAULT_TARGET_DIR) - parser.add_argument("--bin-name", default=DEFAULT_BIN_NAME) - args = parser.parse_args() - - if not windows.is_admin(): - windows.relaunch_as_admin(__file__) - sys.exit() - - try: - reinstall( - args.source_dir, - args.target_dir, - args.bin_name, - ) - except Exception as e: - print(f"Error: {e}", file=sys.stderr) - - if args.pause_on_exit: - input("Press enter to continue...") - - -def reinstall(source_dir, target_dir, bin_name): - """Stops the running daemon service, copies files, and reinstalls.""" - - print("Stopping daemon service") - try: - subprocess.run(["net", "stop", "synergy"], shell=True, check=True) - except subprocess.CalledProcessError as e: - if e.returncode == SERVICE_NOT_RUNNING_ERROR: - print("Daemon service not running") - else: - raise e - - source_bin_path = f"{os.path.join(source_dir, bin_name)}.exe" - - # Wait for Windows to release the file handles after process termination. - while is_any_process_running(target_dir): - print("Waiting for file handles to release") - time.sleep(1) - - options = file_utils.CopyOptions(ignore_errors=True, verbose=False) - print(f"Copying files from {source_dir} to {target_dir}") - file_utils.copy(f"{source_dir}/*", target_dir, options) - - print("Removing old daemon service") - try: - subprocess.run([source_bin_path, "/uninstall"], shell=True, check=True) - except subprocess.CalledProcessError as e: - check_access_violation(e.returncode, source_bin_path) - if e.returncode != 0: - print( - f"Warning: Uninstall failed, return code: {e.returncode}", - file=sys.stderr, - ) - - target_bin_path = os.path.join(target_dir, bin_name + ".exe") - - try: - print("Installing daemon service") - subprocess.run([target_bin_path, "/install"], shell=True, check=True) - except subprocess.CalledProcessError as e: - check_access_violation(e.returncode, target_bin_path) - if e.returncode != 0: - print(f"Warning: Install failed, return code: {e.returncode}") - - -def check_access_violation(return_code, bin_path): - if return_code == ERROR_ACCESS_VIOLATION: - print( - f"Warning: Process crashed with memory access violation: {bin_path}", - file=sys.stderr, - ) - - -def is_any_process_running(dir): - """Check if there is any running process that contains the given directory.""" - - print(f"Checking if any process is running in: {dir}") - for proc in psutil.process_iter(attrs=["name", "exe"]): - exe = proc.info["exe"] - - if not exe: - continue - - try: - if dir.lower() in exe.lower(): - print(f"Process found: {exe}") - return True - except (psutil.NoSuchProcess, psutil.AccessDenied): - pass - return False - - -main() diff --git a/src/gui/src/AboutDialog.cpp b/src/gui/src/AboutDialog.cpp index 03f3adc93..c06a4fab7 100644 --- a/src/gui/src/AboutDialog.cpp +++ b/src/gui/src/AboutDialog.cpp @@ -24,7 +24,7 @@ AboutDialog::AboutDialog(MainWindow *parent, const AppConfig &config) Ui::AboutDialogBase() { setupUi(this); - m_versionChecker.setApp(parent->appPath(config.synergycName())); + m_versionChecker.setApp(parent->appPath(config.coreClientName())); QString version = SYNERGY_VERSION; #ifdef GIT_SHA_SHORT diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index c119be711..f2cabf067 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -1,17 +1,31 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Synergy 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 . + */ + #include "ActivationDialog.h" -#include "ActivationNotifier.h" #include "AppConfig.h" #include "CancelActivationDialog.h" -#include "FailedLoginDialog.h" #include "LicenseManager.h" #include "MainWindow.h" -#include "QUtility.h" +#include "shared/EditionType.h" #include "ui_ActivationDialog.h" -#include +#include #include #include -#include ActivationDialog::ActivationDialog( QWidget *parent, AppConfig &appConfig, LicenseManager &licenseManager) @@ -38,15 +52,18 @@ void ActivationDialog::refreshSerialKey() { ActivationDialog::~ActivationDialog() { delete ui; } void ActivationDialog::reject() { - if (m_LicenseManager->activeEdition() == kUnregistered) { - CancelActivationDialog cancelActivationDialog(this); - if (QDialog::Accepted == cancelActivationDialog.exec()) { - m_LicenseManager->skipActivation(); - } else { - return; - } + // don't show the cancel confirmation dialog if they've already registered, + // since it's not revent to customers who are changing their serial key. + if (m_LicenseManager->activeEdition() != kUnregistered) { + QDialog::reject(); + return; + } + + // the user is told that the 'No' button will exit the app. + CancelActivationDialog cancelActivationDialog(this); + if (cancelActivationDialog.exec() == QDialog::Rejected) { + QApplication::exit(); } - QDialog::reject(); } void ActivationDialog::accept() { @@ -82,7 +99,7 @@ void ActivationDialog::accept() { .arg((daysLeft == 1) ? "" : "s") .arg((daysLeft == 1) ? "s" : ""); - if (m_appConfig->isCryptoAvailable()) { + if (m_appConfig->cryptoAvailable()) { m_appConfig->generateCertificate(); thanksMessage = thanksMessage.arg("If you're using SSL, " diff --git a/src/gui/src/ActivationDialog.h b/src/gui/src/ActivationDialog.h index b752d1fc6..ff50ebe56 100644 --- a/src/gui/src/ActivationDialog.h +++ b/src/gui/src/ActivationDialog.h @@ -1,5 +1,21 @@ -#ifndef ACTIVATIONDIALOG_H -#define ACTIVATIONDIALOG_H +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Synergy 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 . + */ + +#pragma once #include #include @@ -30,5 +46,3 @@ private: AppConfig *m_appConfig; LicenseManager *m_LicenseManager; }; - -#endif // ACTIVATIONDIALOG_H diff --git a/src/gui/src/ActivationDialog.ui b/src/gui/src/ActivationDialog.ui index 7180b7757..e89c2ac91 100644 --- a/src/gui/src/ActivationDialog.ui +++ b/src/gui/src/ActivationDialog.ui @@ -6,8 +6,8 @@ 0 0 - 410 - 211 + 477 + 241 @@ -18,7 +18,6 @@ - 75 true @@ -30,7 +29,7 @@ - <html><head/><body><p>This can be found on your <a href="https://symless.com/synergy/account?source=gui"><span style=" text-decoration: none; color: #4285F4;">account</span></a> page.</p></body></html> + <html><head/><body><p>Your serial key is on your <a href="https://symless.com/synergy/account?source=gui"><span style=" text-decoration: underline; color:#4285f4;">account</span></a> page. Don't have a license? <a href="https://symless.com/synergy/purchase?source=gui"><span style=" text-decoration: underline; color:#007af4;">Purchase Synergy</span></a></p></body></html> true @@ -47,9 +46,12 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'.AppleSystemUIFont'; font-size:13pt; font-weight:400; font-style:normal;"> +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +</style></head><body style=" font-family:'Segoe UI'; font-size:9pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans'; font-size:10pt;"><br /></p></body></html> diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index f5e844a17..e095b0c1f 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) * * This package is free software; you can redistribute it and/or @@ -18,32 +18,35 @@ #include "AppConfig.h" -#include "ConfigWriter.h" +#include "Config.h" #include "SslCertificate.h" +#include #include #include #include #include +#include + +using synergy::gui::Config; + +// this should be incremented each time the wizard is changed, +// which will force it to re-run for existing installations. +const int kWizardVersion = 8; -using GUI::Config::ConfigWriter; #if defined(Q_OS_WIN) -const char AppConfig::m_SynergysName[] = "synergys.exe"; -const char AppConfig::m_SynergycName[] = "synergyc.exe"; -const char AppConfig::m_SynergyLogDir[] = "log/"; -const char AppConfig::m_SynergyConfigName[] = "synergy.sgc"; -const ProcessMode kDefaultProcessMode = ProcessMode::kService; +const char AppConfig::m_CoreServerName[] = "synergys.exe"; +const char AppConfig::m_CoreClientName[] = "synergyc.exe"; +const char AppConfig::m_LogDir[] = "log/"; +const char AppConfig::m_ConfigFilename[] = "synergy.sgc"; #else -const char AppConfig::m_SynergysName[] = "synergys"; -const char AppConfig::m_SynergycName[] = "synergyc"; -const char AppConfig::m_SynergyLogDir[] = "/var/log/"; -const char AppConfig::m_SynergyConfigName[] = "synergy.conf"; -const ProcessMode kDefaultProcessMode = ProcessMode::kDesktop; +const char AppConfig::m_CoreServerName[] = "synergys"; +const char AppConfig::m_CoreClientName[] = "synergyc"; +const char AppConfig::m_LogDir[] = "/var/log/"; +const char AppConfig::m_ConfigFilename[] = "synergy.conf"; #endif -const ElevateMode kDefaultElevateMode = ElevateAsNeeded; - -const char *AppConfig::m_SynergySettingsName[] = { +const char *const AppConfig::m_SettingsName[] = { "screenName", "port", "interface", @@ -80,55 +83,38 @@ const char *AppConfig::m_SynergySettingsName[] = { "licenseNextCheck", "initiateConnectionFromServer", "clientHostMode", - "serverClientMode"}; + "serverClientMode", + "serviceEnabled", + "minimizeOnClose"}; static const char *logLevelNames[] = {"INFO", "DEBUG", "DEBUG1", "DEBUG2"}; -AppConfig::AppConfig(bool globalLoad) - : m_ScreenName(), - m_Port(24800), - m_Interface(), - m_LogLevel(0), - m_LogToFile(), - m_WizardLastRun(0), - m_ProcessMode(kDefaultProcessMode), - m_StartedBefore(), - m_ElevateMode(kDefaultElevateMode), - m_Edition(kUnregistered), - m_CryptoEnabled(false), - m_AutoHide(false), - m_LastExpiringWarningTime(0), - m_ActivationHasRun(), - m_MinimizeToTray(false), - m_ServerGroupChecked(), - m_UseExternalConfig(), - m_UseInternalConfig(), - m_ClientGroupChecked(), - m_LoadFromSystemScope() { +AppConfig::AppConfig() { + m_Config.registerReceiever(this); + load(); +} - auto writer = ConfigWriter::make(); - - // Register this class to receive global load and saves - writer->registerClass(this); - - // HACK: enable global load by default but allow it to be disabled for tests. - // when run in a test environment, this function causes a segfault. - if (globalLoad) { - writer->globalLoad(); - } +void AppConfig::load() { + m_Config.loadAll(); // User settings exist and the load from system scope variable is true - if (writer->hasSetting( - settingName(Setting::kLoadSystemSettings), ConfigWriter::kUser)) { + if (m_Config.hasSetting( + settingName(Setting::kLoadSystemSettings), Config::Scope::User)) { setLoadFromSystemScope(m_LoadFromSystemScope); } // If user setting don't exist but system ones do, load the system settings - else if (writer->hasSetting( - settingName(Setting::kScreenName), ConfigWriter::kSystem)) { + else if (m_Config.hasSetting( + settingName(Setting::kScreenName), Config::Scope::System)) { setLoadFromSystemScope(true); } } +void AppConfig::applyAppSettings() const { + QApplication::setQuitOnLastWindowClosed(!m_MinimizeOnClose); +} + +Config &AppConfig::config() { return m_Config; } + const QString &AppConfig::screenName() const { return m_ScreenName; } int AppConfig::port() const { return m_Port; } @@ -141,18 +127,13 @@ bool AppConfig::logToFile() const { return m_LogToFile; } const QString &AppConfig::logFilename() const { return m_LogFilename; } -QString AppConfig::synergyLogDir() const { +QString AppConfig::logDir() const { // by default log to home dir return QDir::home().absolutePath() + "/"; } -QString AppConfig::synergyProgramDir() const { - // synergy binaries should be in the same dir. - return QCoreApplication::applicationDirPath() + "/"; -} - -void AppConfig::persistLogDir() { - QDir dir = synergyLogDir(); +void AppConfig::persistLogDir() const { + QDir dir = logDir(); // persist the log directory if (!dir.exists()) { @@ -160,18 +141,11 @@ void AppConfig::persistLogDir() { } } -const QString AppConfig::logFilenameCmd() const { - QString filename = m_LogFilename; -#if defined(Q_OS_WIN) - // wrap in quotes in case username contains spaces. - filename = QString("\"%1\"").arg(filename); -#endif - return filename; -} - QString AppConfig::logLevelText() const { return logLevelNames[logLevel()]; } -ProcessMode AppConfig::processMode() const { return m_ProcessMode; } +ProcessMode AppConfig::processMode() const { + return m_ServiceEnabled ? ProcessMode::kService : ProcessMode::kDesktop; +} bool AppConfig::wizardShouldRun() const { return m_WizardLastRun < kWizardVersion; @@ -180,141 +154,137 @@ bool AppConfig::wizardShouldRun() const { bool AppConfig::startedBefore() const { return m_StartedBefore; } void AppConfig::loadSettings() { + using enum AppConfig::Setting; + m_ScreenName = - loadSetting(Setting::kScreenName, QHostInfo::localHostName()).toString(); + loadSetting(kScreenName, QHostInfo::localHostName()).toString(); if (m_ScreenName.isEmpty()) { m_ScreenName = QHostInfo::localHostName(); } - m_Port = loadSetting(Setting::kPort, 24800).toInt(); - m_Interface = loadSetting(Setting::kInterfaceSetting).toString(); - m_LogLevel = loadSetting(Setting::kLogLevel, 0).toInt(); - m_LogToFile = loadSetting(Setting::kLogToFile, false).toBool(); + m_Port = loadSetting(kPort, 24800).toInt(); + m_Interface = loadSetting(kInterfaceSetting).toString(); + m_LogLevel = loadSetting(kLogLevel, 0).toInt(); + m_LogToFile = loadSetting(kLogToFile, false).toBool(); m_LogFilename = - loadSetting(Setting::kLogFilename, synergyLogDir() + "synergy.log") - .toString(); - m_WizardLastRun = loadCommonSetting(Setting::kWizardLastRun, 0).toInt(); - m_StartedBefore = loadSetting(Setting::kStartedBefore, false).toBool(); + loadSetting(kLogFilename, logDir() + "synergy.log").toString(); + m_WizardLastRun = loadCommonSetting(kWizardLastRun, 0).toInt(); + m_StartedBefore = loadSetting(kStartedBefore, false).toBool(); - { // Scope related code together - // TODO Investigate why kElevateModeEnum isn't loaded fully - QVariant elevateMode = loadSetting(Setting::kElevateModeEnum); - if (!elevateMode.isValid()) { - elevateMode = loadSetting( - Setting::kElevateModeSetting, - QVariant(static_cast(kDefaultElevateMode))); - } - m_ElevateMode = static_cast(elevateMode.toInt()); + QVariant elevateMode = loadSetting(kElevateModeEnum); + if (!elevateMode.isValid()) { + elevateMode = loadSetting( + kElevateModeSetting, QVariant(static_cast(kDefaultElevateMode))); } + m_ElevateMode = static_cast(elevateMode.toInt()); - m_ActivateEmail = loadSetting(Setting::kActivateEmail, "").toString(); - m_CryptoEnabled = loadSetting(Setting::kCryptoEnabled, true).toBool(); - m_AutoHide = loadSetting(Setting::kAutoHide, false).toBool(); - m_lastVersion = loadSetting(Setting::kLastVersion, "Unknown").toString(); - m_LastExpiringWarningTime = - loadSetting(Setting::kLastExpireWarningTime, 0).toInt(); - m_ActivationHasRun = loadSetting(Setting::kActivationHasRun, false).toBool(); - m_MinimizeToTray = loadSetting(Setting::kMinimizeToTray, false).toBool(); + m_ActivateEmail = loadSetting(kActivateEmail, "").toString(); + m_CryptoEnabled = loadSetting(kCryptoEnabled, true).toBool(); + m_AutoHide = loadSetting(kAutoHide, false).toBool(); + m_LastVersion = loadSetting(kLastVersion, "Unknown").toString(); + m_LastExpiringWarningTime = loadSetting(kLastExpireWarningTime, 0).toInt(); + m_ActivationHasRun = loadSetting(kActivationHasRun, false).toBool(); + m_MinimizeToTray = loadSetting(kMinimizeToTray, false).toBool(); m_LoadFromSystemScope = - loadCommonSetting(Setting::kLoadSystemSettings, false).toBool(); - m_ServerGroupChecked = - loadSetting(Setting::kGroupServerCheck, false).toBool(); - m_UseExternalConfig = - loadSetting(Setting::kUseExternalConfig, false).toBool(); + loadCommonSetting(kLoadSystemSettings, false).toBool(); + m_ServerGroupChecked = loadSetting(kGroupServerCheck, false).toBool(); + m_UseExternalConfig = loadSetting(kUseExternalConfig, false).toBool(); m_ConfigFile = - loadSetting( - Setting::kConfigFile, QDir::homePath() + "/" + m_SynergyConfigName) + loadSetting(kConfigFile, QDir::homePath() + "/" + m_ConfigFilename) .toString(); - m_UseInternalConfig = - loadSetting(Setting::kUseInternalConfig, false).toBool(); - m_ClientGroupChecked = - loadSetting(Setting::kGroupClientCheck, false).toBool(); - m_ServerHostname = loadSetting(Setting::kServerHostname).toString(); - m_PreventSleep = loadSetting(Setting::kPreventSleep, false).toBool(); - m_LanguageSync = loadSetting(Setting::kLanguageSync, false).toBool(); - m_InvertScrollDirection = - loadSetting(Setting::kInvertScrollDirection, false).toBool(); - m_guid = loadCommonSetting(Setting::kGuid, QUuid::createUuid()).toString(); - m_licenseRegistryUrl = loadCommonSetting( - Setting::kLicenseRegistryUrl, - "https://api2.prod.symless.com/license/register") - .toString(); - m_licenseNextCheck = - loadCommonSetting(Setting::kLicenseNextCheck, 0).toULongLong(); - m_ClientHostMode = loadSetting(Setting::kClientHostMode, true).toBool(); - m_ServerClientMode = loadSetting(Setting::kServerClientMode, true).toBool(); + m_UseInternalConfig = loadSetting(kUseInternalConfig, false).toBool(); + m_ClientGroupChecked = loadSetting(kGroupClientCheck, false).toBool(); + m_ServerHostname = loadSetting(kServerHostname).toString(); + m_PreventSleep = loadSetting(kPreventSleep, false).toBool(); + m_LanguageSync = loadSetting(kLanguageSync, false).toBool(); + m_InvertScrollDirection = loadSetting(kInvertScrollDirection, false).toBool(); + m_Guid = loadCommonSetting(kGuid, QUuid::createUuid()).toString(); + m_licenseNextCheck = loadCommonSetting(kLicenseNextCheck, 0).toULongLong(); + m_ClientHostMode = loadSetting(kClientHostMode, true).toBool(); + m_ServerClientMode = loadSetting(kServerClientMode, true).toBool(); m_InitiateConnectionFromServer = - loadSetting(Setting::kInitiateConnectionFromServer, false).toBool(); + loadSetting(kInitiateConnectionFromServer, false).toBool(); // only change the serial key if the settings being loaded contains a key - bool updateSerial = ConfigWriter::make()->hasSetting( - settingName(Setting::kLoadSystemSettings), ConfigWriter::kCurrent); + bool updateSerial = m_Config.hasSetting( + settingName(kLoadSystemSettings), Config::Scope::Current); // if the setting exists and is not empty - updateSerial = - updateSerial && - !loadSetting(Setting::kSerialKey, "").toString().trimmed().isEmpty(); + updateSerial = updateSerial && + !loadSetting(kSerialKey, "").toString().trimmed().isEmpty(); if (updateSerial) { - m_Serialkey = loadSetting(Setting::kSerialKey, "").toString().trimmed(); + m_Serialkey = loadSetting(kSerialKey, "").toString().trimmed(); m_Edition = static_cast( - loadSetting(Setting::kEditionSetting, kUnregistered).toInt()); + loadSetting(kEditionSetting, kUnregistered).toInt()); } - // Set the default path of the TLS certificate file in the users DIR - QString certificateFilename = - QString("%1/%2/%3") - .arg(m_CoreInterface.getProfileDir(), "SSL", "Synergy.pem"); + m_ServiceEnabled = loadSetting(kServiceEnabled, m_ServiceEnabled).toBool(); - m_TLSCertificatePath = - loadSetting(Setting::kTLSCertPath, certificateFilename).toString(); - m_TLSKeyLength = loadSetting(Setting::kTLSKeyLength, "2048").toString(); + try { + // Set the default path of the TLS certificate file in the users DIR + QString certificateFilename = + QString("%1/%2/%3") + .arg(m_CoreInterface.getProfileDir(), "SSL", "Synergy.pem"); - if (getCryptoEnabled()) { + m_TlsCertPath = loadSetting(kTlsCertPath, certificateFilename).toString(); + m_TlsKeyLength = loadSetting(kTlsKeyLength, "2048").toString(); + } catch (const std::exception &e) { + qDebug() << e.what(); + qFatal("Failed to get profile dir, unable to configure TLS"); + } + + if (cryptoEnabled()) { generateCertificate(); } + + applyAppSettings(); } void AppConfig::saveSettings() { - setCommonSetting(Setting::kWizardLastRun, m_WizardLastRun); - setCommonSetting(Setting::kLoadSystemSettings, m_LoadFromSystemScope); - setCommonSetting(Setting::kGroupClientCheck, m_ClientGroupChecked); - setCommonSetting(Setting::kGroupServerCheck, m_ServerGroupChecked); - setCommonSetting(Setting::kGuid, m_guid); - setCommonSetting(Setting::kLicenseRegistryUrl, m_licenseRegistryUrl); - setCommonSetting(Setting::kLicenseNextCheck, m_licenseNextCheck); + using enum Setting; + + setCommonSetting(kWizardLastRun, m_WizardLastRun); + setCommonSetting(kLoadSystemSettings, m_LoadFromSystemScope); + setCommonSetting(kGroupClientCheck, m_ClientGroupChecked); + setCommonSetting(kGroupServerCheck, m_ServerGroupChecked); + setCommonSetting(kGuid, m_Guid); + setCommonSetting(kLicenseNextCheck, m_licenseNextCheck); if (isWritable()) { - setSetting(Setting::kScreenName, m_ScreenName); - setSetting(Setting::kPort, m_Port); - setSetting(Setting::kInterfaceSetting, m_Interface); - setSetting(Setting::kLogLevel, m_LogLevel); - setSetting(Setting::kLogToFile, m_LogToFile); - setSetting(Setting::kLogFilename, m_LogFilename); - setSetting(Setting::kStartedBefore, m_StartedBefore); - // Refer to enum ElevateMode declaration for insight in to why this - // flag is mapped this way - setSetting(Setting::kElevateModeSetting, m_ElevateMode == ElevateAlways); - setSetting(Setting::kElevateModeEnum, static_cast(m_ElevateMode)); - setSetting(Setting::kEditionSetting, m_Edition); - setSetting(Setting::kCryptoEnabled, m_CryptoEnabled); - setSetting(Setting::kAutoHide, m_AutoHide); - setSetting(Setting::kSerialKey, m_Serialkey); - setSetting(Setting::kLastVersion, m_lastVersion); - setSetting(Setting::kLastExpireWarningTime, m_LastExpiringWarningTime); - setSetting(Setting::kActivationHasRun, m_ActivationHasRun); - setSetting(Setting::kMinimizeToTray, m_MinimizeToTray); - setSetting(Setting::kUseExternalConfig, m_UseExternalConfig); - setSetting(Setting::kConfigFile, m_ConfigFile); - setSetting(Setting::kUseInternalConfig, m_UseInternalConfig); - setSetting(Setting::kServerHostname, m_ServerHostname); - setSetting(Setting::kPreventSleep, m_PreventSleep); - setSetting(Setting::kLanguageSync, m_LanguageSync); - setSetting(Setting::kInvertScrollDirection, m_InvertScrollDirection); - setSetting(Setting::kClientHostMode, m_ClientHostMode); - setSetting(Setting::kServerClientMode, m_ServerClientMode); + setSetting(kScreenName, m_ScreenName); + setSetting(kPort, m_Port); + setSetting(kInterfaceSetting, m_Interface); + setSetting(kLogLevel, m_LogLevel); + setSetting(kLogToFile, m_LogToFile); + setSetting(kLogFilename, m_LogFilename); + setSetting(kStartedBefore, m_StartedBefore); + setSetting(kElevateModeEnum, static_cast(m_ElevateMode)); + setSetting(kEditionSetting, m_Edition); + setSetting(kCryptoEnabled, m_CryptoEnabled); + setSetting(kAutoHide, m_AutoHide); + setSetting(kSerialKey, m_Serialkey); + setSetting(kLastVersion, m_LastVersion); + setSetting(kLastExpireWarningTime, m_LastExpiringWarningTime); + setSetting(kActivationHasRun, m_ActivationHasRun); + setSetting(kMinimizeToTray, m_MinimizeToTray); + setSetting(kUseExternalConfig, m_UseExternalConfig); + setSetting(kConfigFile, m_ConfigFile); + setSetting(kUseInternalConfig, m_UseInternalConfig); + setSetting(kServerHostname, m_ServerHostname); + setSetting(kPreventSleep, m_PreventSleep); + setSetting(kLanguageSync, m_LanguageSync); + setSetting(kInvertScrollDirection, m_InvertScrollDirection); + setSetting(kClientHostMode, m_ClientHostMode); + setSetting(kServerClientMode, m_ServerClientMode); + setSetting(kServiceEnabled, m_ServiceEnabled); + setSetting(kMinimizeOnClose, m_MinimizeOnClose); + + // See enum ElevateMode declaration to understand why this setting is bool + setSetting(kElevateModeSetting, m_ElevateMode == ElevateAlways); } - m_unsavedChanges = false; + setModified(false); + applyAppSettings(); } #ifdef SYNERGY_ENABLE_LICENSING @@ -326,10 +296,10 @@ AppConfig &AppConfig::activationHasRun(bool value) { } #endif -QString AppConfig::lastVersion() const { return m_lastVersion; } +QString AppConfig::lastVersion() const { return m_LastVersion; } void AppConfig::setLastVersion(const QString &version) { - setSettingModified(m_lastVersion, version); + setSettingModified(m_LastVersion, version); } void AppConfig::setScreenName(const QString &s) { @@ -389,9 +359,9 @@ void AppConfig::setLastExpiringWarningTime(int newValue) { } #endif -QString AppConfig::synergysName() const { return m_SynergysName; } +QString AppConfig::coreServerName() const { return m_CoreServerName; } -QString AppConfig::synergycName() const { return m_SynergycName; } +QString AppConfig::coreClientName() const { return m_CoreClientName; } ElevateMode AppConfig::elevateMode() { return m_ElevateMode; } @@ -404,7 +374,7 @@ void AppConfig::setCryptoEnabled(bool newValue) { setSettingModified(m_CryptoEnabled, newValue); } -bool AppConfig::isCryptoAvailable() const { +bool AppConfig::cryptoAvailable() const { bool result{true}; #ifdef SYNERGY_ENABLE_LICENSING @@ -416,19 +386,19 @@ bool AppConfig::isCryptoAvailable() const { return result; } -bool AppConfig::getCryptoEnabled() const { - return isCryptoAvailable() && m_CryptoEnabled; +bool AppConfig::cryptoEnabled() const { + return cryptoAvailable() && m_CryptoEnabled; } void AppConfig::setAutoHide(bool b) { setSettingModified(m_AutoHide, b); } -bool AppConfig::getAutoHide() { return m_AutoHide; } +bool AppConfig::autoHide() { return m_AutoHide; } void AppConfig::setMinimizeToTray(bool newValue) { setSettingModified(m_MinimizeToTray, newValue); } -bool AppConfig::getInvertScrollDirection() const { +bool AppConfig::invertScrollDirection() const { return m_InvertScrollDirection; } @@ -436,17 +406,13 @@ void AppConfig::setLicenseNextCheck(unsigned long long time) { setSettingModified(m_licenseNextCheck, time); } -const QString &AppConfig::getLicenseRegistryUrl() const { - return m_licenseRegistryUrl; -} - -unsigned long long AppConfig::getLicenseNextCheck() const { +unsigned long long AppConfig::licenseNextCheck() const { return m_licenseNextCheck; } -const QString &AppConfig::getGuid() const { return m_guid; } +const QString &AppConfig::guid() const { return m_Guid; } -bool AppConfig::getLanguageSync() const { return m_LanguageSync; } +bool AppConfig::languageSync() const { return m_LanguageSync; } void AppConfig::setInvertScrollDirection(bool newValue) { setSettingModified(m_InvertScrollDirection, newValue); @@ -456,17 +422,17 @@ void AppConfig::setLanguageSync(bool newValue) { setSettingModified(m_LanguageSync, newValue); } -bool AppConfig::getPreventSleep() const { return m_PreventSleep; } +bool AppConfig::preventSleep() const { return m_PreventSleep; } -bool AppConfig::getClientHostMode() const { - return (m_ClientHostMode && getInitiateConnectionFromServer()); +bool AppConfig::clientHostMode() const { + return (m_ClientHostMode && initiateConnectionFromServer()); } -bool AppConfig::getServerClientMode() const { - return (m_ServerClientMode && getInitiateConnectionFromServer()); +bool AppConfig::serverClientMode() const { + return (m_ServerClientMode && initiateConnectionFromServer()); } -bool AppConfig::getInitiateConnectionFromServer() const { +bool AppConfig::initiateConnectionFromServer() const { return m_InitiateConnectionFromServer; } @@ -474,57 +440,53 @@ void AppConfig::setPreventSleep(bool newValue) { setSettingModified(m_PreventSleep, newValue); } -bool AppConfig::getMinimizeToTray() { return m_MinimizeToTray; } +bool AppConfig::minimizeToTray() { return m_MinimizeToTray; } QString AppConfig::settingName(Setting name) { auto index = static_cast(name); - return m_SynergySettingsName[index]; + return m_SettingsName[index]; } template void AppConfig::setSetting(Setting name, T value) { - ConfigWriter::make()->setSetting(settingName(name), value); + m_Config.setSetting(settingName(name), value); } template void AppConfig::setCommonSetting(Setting name, T value) { - ConfigWriter::make()->setSetting( - settingName(name), value, ConfigWriter::kUser); - ConfigWriter::make()->setSetting( - settingName(name), value, ConfigWriter::kSystem); + m_Config.setSetting(settingName(name), value, Config::Scope::User); + m_Config.setSetting(settingName(name), value, Config::Scope::System); } QVariant AppConfig::loadSetting(Setting name, const QVariant &defaultValue) { - return ConfigWriter::make()->loadSetting(settingName(name), defaultValue); + return m_Config.loadSetting(settingName(name), defaultValue); } QVariant AppConfig::loadCommonSetting(Setting name, const QVariant &defaultValue) const { QVariant result(defaultValue); QString setting(settingName(name)); - auto &writer = *ConfigWriter::make(); - if (writer.hasSetting(setting)) { - result = writer.loadSetting(setting, defaultValue); - } else if (writer.getScope() == ConfigWriter::kSystem) { - if (writer.hasSetting(setting, ConfigWriter::kUser)) { - result = writer.loadSetting(setting, defaultValue, ConfigWriter::kUser); + if (m_Config.hasSetting(setting)) { + result = m_Config.loadSetting(setting, defaultValue); + } else if (m_Config.getScope() == Config::Scope::System) { + if (m_Config.hasSetting(setting, Config::Scope::User)) { + result = m_Config.loadSetting(setting, defaultValue, Config::Scope::User); } - } else if (writer.hasSetting(setting, ConfigWriter::kSystem)) { - result = writer.loadSetting(setting, defaultValue, ConfigWriter::kSystem); + } else if (m_Config.hasSetting(setting, Config::Scope::System)) { + result = m_Config.loadSetting(setting, defaultValue, Config::Scope::System); } return result; } -void AppConfig::loadScope(ConfigWriter::Scope scope) { - auto writer = ConfigWriter::make(); +void AppConfig::loadScope(Config::Scope scope) { - if (writer->getScope() != scope) { + if (m_Config.getScope() != scope) { setDefaultValues(); - writer->setScope(scope); - if (writer->hasSetting( - settingName(Setting::kScreenName), writer->getScope())) { + m_Config.setScope(scope); + if (m_Config.hasSetting( + settingName(Setting::kScreenName), m_Config.getScope())) { // If the user already has settings, then load them up now. - writer->globalLoad(); + m_Config.loadAll(); } } } @@ -534,9 +496,9 @@ void AppConfig::setDefaultValues() { m_InitiateConnectionFromServer = false; } void AppConfig::setLoadFromSystemScope(bool value) { if (value) { - loadScope(ConfigWriter::kSystem); + loadScope(Config::Scope::System); } else { - loadScope(ConfigWriter::kUser); + loadScope(Config::Scope::User); } /* @@ -546,25 +508,23 @@ void AppConfig::setLoadFromSystemScope(bool value) { m_LoadFromSystemScope = value; } -bool AppConfig::isWritable() const { - return ConfigWriter::make()->isWritable(); -} +bool AppConfig::isWritable() const { return m_Config.isWritable(); } bool AppConfig::isSystemScoped() const { - return ConfigWriter::make()->getScope() == ConfigWriter::kSystem; + return m_Config.getScope() == Config::Scope::System; } -bool AppConfig::getServerGroupChecked() const { return m_ServerGroupChecked; } +bool AppConfig::serverGroupChecked() const { return m_ServerGroupChecked; } -bool AppConfig::getUseExternalConfig() const { return m_UseExternalConfig; } +bool AppConfig::useExternalConfig() const { return m_UseExternalConfig; } -const QString &AppConfig::getConfigFile() const { return m_ConfigFile; } +const QString &AppConfig::configFile() const { return m_ConfigFile; } -bool AppConfig::getUseInternalConfig() const { return m_UseInternalConfig; } +bool AppConfig::useInternalConfig() const { return m_UseInternalConfig; } -bool AppConfig::getClientGroupChecked() const { return m_ClientGroupChecked; } +bool AppConfig::clientGroupChecked() const { return m_ClientGroupChecked; } -QString AppConfig::getServerHostname() const { return m_ServerHostname; } +QString AppConfig::serverHostname() const { return m_ServerHostname; } void AppConfig::setServerGroupChecked(bool newValue) { setSettingModified(m_ServerGroupChecked, newValue); @@ -602,28 +562,43 @@ template void AppConfig::setSettingModified(T &variable, const T &newValue) { if (variable != newValue) { variable = newValue; - m_unsavedChanges = true; + setModified(true); } } -void AppConfig::setTLSCertPath(const QString &path) { - m_TLSCertificatePath = path; -} +void AppConfig::setTlsCertPath(const QString &path) { m_TlsCertPath = path; } -QString AppConfig::getTLSCertPath() const { return m_TLSCertificatePath; } +QString AppConfig::tlsCertPath() const { return m_TlsCertPath; } -QString AppConfig::getTLSKeyLength() const { return m_TLSKeyLength; } +QString AppConfig::tlsKeyLength() const { return m_TlsKeyLength; } -void AppConfig::setTLSKeyLength(const QString &length) { - if (m_TLSKeyLength != length) { - m_TLSKeyLength = length; +void AppConfig::setTlsKeyLength(const QString &length) { + if (m_TlsKeyLength != length) { + m_TlsKeyLength = length; generateCertificate(true); } } void AppConfig::generateCertificate(bool forceGeneration) const { - SslCertificate sslCertificate; - sslCertificate.generateCertificate( - getTLSCertPath(), getTLSKeyLength(), forceGeneration); - emit sslToggled(); + try { + SslCertificate sslCertificate; + sslCertificate.generateCertificate( + tlsCertPath(), tlsKeyLength(), forceGeneration); + emit sslToggled(); + } catch (const std::exception &e) { + qDebug() << e.what(); + qFatal("Failed to configure TLS"); + } } + +void AppConfig::setServiceEnabled(bool enabled) { + setSettingModified(m_ServiceEnabled, enabled); +} + +bool AppConfig::serviceEnabled() const { return m_ServiceEnabled; } + +void AppConfig::setMinimizeOnClose(bool minimize) { + setSettingModified(m_MinimizeOnClose, minimize); +} + +bool AppConfig::minimizeOnClose() const { return m_MinimizeOnClose; } diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index d66c50f9b..72124fba0 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) * * This package is free software; you can redistribute it and/or @@ -16,165 +16,122 @@ * along with this program. If not, see . */ -#if !defined(APPCONFIG_H) +#pragma once -#define APPCONFIG_H - -#include "ConfigBase.h" -#include "ConfigWriter.h" +#include "CommonConfig.h" +#include "Config.h" #include "CoreInterface.h" #include "ElevateMode.h" +#include "LicenseManager.h" +#include "shared/EditionType.h" + #include #include #include #include -#include - -// this should be incremented each time a new page is added. this is -// saved to settings when the user finishes running the wizard. if -// the saved wizard version is lower than this number, the wizard -// will be displayed. each version incrememnt should be described -// here... -// -// 1: first version -// 2: added language page -// 3: added premium page and removed -// 4: ssl plugin 'ns' v1.0 -// 5: ssl plugin 'ns' v1.1 -// 6: ssl plugin 'ns' v1.2 -// 7: serial key activation -// 8: Visual Studio 2015 support -// -const int kWizardVersion = 8; class QSettings; class SettingsDialog; -class ServerConfig; class LicenseRegister; +class ServerConfig; +class LicenseManager; +class ActivationDialog; +class LicenseRegistry; enum class ProcessMode { kService, kDesktop }; -class AppConfig : public QObject, public GUI::Config::ConfigBase { +const ElevateMode kDefaultElevateMode = ElevateAsNeeded; + +#if defined(Q_OS_WIN) +const ProcessMode kDefaultProcessMode = ProcessMode::kService; +#else +const ProcessMode kDefaultProcessMode = ProcessMode::kDesktop; +#endif + +/// @brief Reads and writes application specific settings +class AppConfig : public QObject, public synergy::gui::CommonConfig { Q_OBJECT friend class SettingsDialog; friend class MainWindow; friend class SetupWizard; friend class ServerConfig; + friend class LicenseManager; + friend class ActivationDialog; + friend class LicenseRegistry; public: - explicit AppConfig(bool globalLoad = true); + explicit AppConfig(); + + /// @brief Underlying configuration reader/writer + synergy::gui::Config &config(); + + void saveSettings() override; + void applyAppSettings() const; + + /// @brief Generates TLS certificate + /// @param [in] bool forceGeneration Generate certificate even if it's exists. + void generateCertificate(bool forceGeneration = false) const; + + /** + * Getters + */ bool isWritable() const; bool isSystemScoped() const; - const QString &screenName() const; int port() const; const QString &networkInterface() const; int logLevel() const; bool logToFile() const; const QString &logFilename() const; - const QString logFilenameCmd() const; QString logLevelText() const; ProcessMode processMode() const; bool wizardShouldRun() const; bool startedBefore() const; - -#ifdef SYNERGY_ENABLE_LICENSING - void setEdition(Edition); - Edition edition() const; - void setSerialKey(const QString &serial); - void clearSerialKey(); - QString serialKey() const; - int lastExpiringWarningTime() const; - void setLastExpiringWarningTime(int t); -#endif - - QString synergysName() const; - QString synergycName() const; - QString synergyProgramDir() const; - QString synergyLogDir() const; - - void persistLogDir(); + QString coreServerName() const; + QString coreClientName() const; + QString logDir() const; + void persistLogDir() const; ElevateMode elevateMode(); - - bool isCryptoAvailable() const; - void setCryptoEnabled(bool e); - bool getCryptoEnabled() const; - - void setAutoHide(bool b); - bool getAutoHide(); - void setInvertScrollDirection(bool b); - bool getInvertScrollDirection() const; - void setLicenseNextCheck(unsigned long long); - const QString &getLicenseRegistryUrl() const; - unsigned long long getLicenseNextCheck() const; - const QString &getGuid() const; - void setLanguageSync(bool b); - bool getLanguageSync() const; - void setPreventSleep(bool b); - bool getPreventSleep() const; - bool getClientHostMode() const; - bool getServerClientMode() const; - bool getInitiateConnectionFromServer() const; -#ifdef SYNERGY_ENABLE_LICENSING - bool activationHasRun() const; - AppConfig &activationHasRun(bool value); -#endif - /// @brief Sets the user preference to load from SystemScope. - /// @param [in] value - /// True - This will set the variable and load the global scope - /// settings. False - This will set the variable and load the user - /// scope settings. - void setLoadFromSystemScope(bool value); - - bool getServerGroupChecked() const; - bool getUseExternalConfig() const; - const QString &getConfigFile() const; - bool getUseInternalConfig() const; - bool getClientGroupChecked() const; - QString getServerHostname() const; + bool cryptoAvailable() const; + bool cryptoEnabled() const; + bool autoHide(); + bool invertScrollDirection() const; + unsigned long long licenseNextCheck() const; + const QString &guid() const; + bool languageSync() const; + bool preventSleep() const; + bool clientHostMode() const; + bool serverClientMode() const; + bool initiateConnectionFromServer() const; + bool serverGroupChecked() const; + bool useExternalConfig() const; + const QString &configFile() const; + bool useInternalConfig() const; + bool clientGroupChecked() const; + QString serverHostname() const; + QString lastVersion() const; + bool serviceEnabled() const; + bool minimizeToTray(); + bool minimizeOnClose() const; /// @brief Gets the current TLS certificate path /// @return QString The path to the cert - QString getTLSCertPath() const; + QString tlsCertPath() const; /// @brief Get the key length to be used for the private key of a TLS cert /// @return QString The key length in bits - QString getTLSKeyLength() const; + QString tlsKeyLength() const; - void setServerGroupChecked(bool); - void setUseExternalConfig(bool); - void setConfigFile(const QString &); - void setUseInternalConfig(bool); - void setClientGroupChecked(bool); - 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 - /// @param [in] path The path to the Certificate - void setTLSCertPath(const QString &path); - - /// @brief Sets the key length of the private key to use in a TLS connection - /// @param [in] QString length The key length eg: 1024, 2048, 4096 - void setTLSKeyLength(const QString &length); - - QString lastVersion() const; - - void setMinimizeToTray(bool b); - bool getMinimizeToTray(); - - void saveSettings() override; - void setLastVersion(const QString &version); - - /// @brief Generates TLS certificate - /// @param [in] bool forceGeneration Generate certificate even if it's exists. - void generateCertificate(bool forceGeneration = false) const; +#ifdef SYNERGY_ENABLE_LICENSING + Edition edition() const; + QString serialKey() const; + int lastExpiringWarningTime() const; + bool activationHasRun() const; +#endif protected: - /// @brief The enumeration to easily access the names of the setting inside - /// m_SynergySettingsName enum class Setting { kScreenName, kPort, @@ -202,8 +159,8 @@ protected: kUseInternalConfig, kGroupClientCheck, kServerHostname, - kTLSCertPath, - kTLSKeyLength, + kTlsCertPath, + kTlsKeyLength, kPreventSleep, kLanguageSync, kInvertScrollDirection, @@ -212,9 +169,20 @@ protected: kLicenseNextCheck, kInitiateConnectionFromServer, kClientHostMode, - kServerClientMode + kServerClientMode, + kServiceEnabled, + kMinimizeOnClose }; + static QString settingName(AppConfig::Setting name); + + /// @brief Loads the setting from the current scope + void loadSettings() override; + + /** + * Setters + */ + void setScreenName(const QString &s); void setPort(int i); void setNetworkInterface(const QString &s); @@ -224,70 +192,48 @@ protected: void setWizardHasRun(); void setStartedBefore(bool b); void setElevateMode(ElevateMode em); + void setCryptoEnabled(bool e); + void setEdition(Edition); + void setSerialKey(const QString &serial); + void clearSerialKey(); + void setLastExpiringWarningTime(int t); + void setAutoHide(bool b); + void setInvertScrollDirection(bool b); + void setLicenseNextCheck(unsigned long long); + void setLanguageSync(bool b); + void setPreventSleep(bool b); + void setServerGroupChecked(bool); + void setUseExternalConfig(bool); + void setConfigFile(const QString &); + void setUseInternalConfig(bool); + void setClientGroupChecked(bool); + void setServerHostname(const QString &); + void setClientHostMode(bool newValue); + void setServerClientMode(bool newValue); + AppConfig &activationHasRun(bool value); + void setMinimizeToTray(bool b); + void setLastVersion(const QString &version); + void setServiceEnabled(bool enabled); + void setMinimizeOnClose(bool minimize); - /// @brief loads the setting from the current scope - /// @param ignoreSystem should the load feature ignore the globalScope setting - /// that was saved - void loadSettings() override; - static QString settingName(AppConfig::Setting name); + /// @brief Sets the user preference to load from SystemScope. + /// @param [in] value + /// True - This will set the variable and load the global scope + /// settings. False - This will set the variable and load the user + /// scope settings. + void setLoadFromSystemScope(bool value); + + /// @brief Set the path to the TLS/SSL certificate file that will be used + /// @param [in] path The path to the Certificate + void setTlsCertPath(const QString &path); + + /// @brief Sets the key length of the private key to use in a TLS connection + /// @param [in] QString length The key length eg: 1024, 2048, 4096 + void setTlsKeyLength(const QString &length); private: - QString m_ScreenName; - int m_Port; - QString m_Interface; - int m_LogLevel; - bool m_LogToFile; - QString m_LogFilename; - int m_WizardLastRun; - ProcessMode m_ProcessMode; - bool m_StartedBefore; - ElevateMode m_ElevateMode; - Edition m_Edition; - QString m_ActivateEmail; - bool m_CryptoEnabled; - bool m_AutoHide; - QString m_Serialkey; - QString m_lastVersion; - QString m_guid; - QString m_licenseRegistryUrl; - unsigned long long m_licenseNextCheck; - int m_LastExpiringWarningTime; - bool m_ActivationHasRun; - bool m_MinimizeToTray; - bool m_InvertScrollDirection = false; - bool m_LanguageSync = true; - bool m_PreventSleep = false; - bool m_InitiateConnectionFromServer = false; - bool m_ClientHostMode = true; - bool m_ServerClientMode = true; - - bool m_ServerGroupChecked; - bool m_UseExternalConfig; - QString m_ConfigFile; - bool m_UseInternalConfig; - bool m_ClientGroupChecked; - QString m_ServerHostname; - - QString m_TLSCertificatePath; /// @brief The path to the TLS certificate file - QString m_TLSKeyLength; /// @brief The key length of the TLS cert to make - - bool m_LoadFromSystemScope; /// @brief should the setting be loaded from - /// SystemScope - /// If the user has settings but this is - /// true then system settings will be - /// loaded instead of the users - - CoreInterface m_CoreInterface; - - static const char m_SynergysName[]; - static const char m_SynergycName[]; - static const char m_SynergyLogDir[]; - - /// @brief Contains the string values of the settings names that will be saved - static const char *m_SynergySettingsName[]; - - /// @brief Contains the name of the default configuration filename - static const char m_SynergyConfigName[]; + /// @brief Loads config from the underlying reader/writer + void load(); /// @brief Sets the value of a setting /// @param [in] name The Setting to be saved @@ -312,12 +258,6 @@ private: QVariant loadCommonSetting( AppConfig::Setting name, const QVariant &defaultValue = QVariant()) const; - /// @brief As the settings will be accessible by multiple objects this lock - /// will ensure that - /// it cant be modified by more that one object at a time if the - /// setting is being switched from system to user. - std::mutex m_settings_lock; - /// @brief Sets the setting in the config checking if it has changed and /// flagging that settings /// needs to be saved if the setting was different @@ -327,15 +267,79 @@ private: /// @brief This method loads config from specified scope /// @param [in] scope which should be loaded. - void loadScope(GUI::Config::ConfigWriter::Scope scope); + void loadScope(synergy::gui::Config::Scope scope); /// @brief This function sets default values /// for settings that shouldn't be copied from between scopes. void setDefaultValues(); + synergy::gui::Config m_Config; + CoreInterface m_CoreInterface; + QString m_ScreenName = ""; + int m_Port = 24800; + QString m_Interface = ""; + int m_LogLevel = 0; + bool m_LogToFile = false; + QString m_LogFilename = ""; + int m_WizardLastRun = 0; + bool m_StartedBefore = false; + ElevateMode m_ElevateMode = kDefaultElevateMode; + Edition m_Edition = Edition::kUnregistered; + QString m_ActivateEmail = ""; + bool m_CryptoEnabled = false; + bool m_AutoHide = false; + QString m_Serialkey = ""; + QString m_LastVersion = ""; + QString m_Guid = ""; + unsigned long long m_licenseNextCheck = 0; + int m_LastExpiringWarningTime = 0; + bool m_ActivationHasRun = false; + bool m_MinimizeToTray = true; + bool m_InvertScrollDirection = false; + bool m_LanguageSync = true; + bool m_PreventSleep = false; + bool m_InitiateConnectionFromServer = false; + bool m_ClientHostMode = true; + bool m_ServerClientMode = true; + bool m_ServerGroupChecked = false; + bool m_UseExternalConfig = false; + QString m_ConfigFile = ""; + bool m_UseInternalConfig = false; + bool m_ClientGroupChecked = false; + QString m_ServerHostname = ""; + bool m_ServiceEnabled = kDefaultProcessMode == ProcessMode::kService; + bool m_MinimizeOnClose = true; + + /// @brief The path to the TLS certificate file + QString m_TlsCertPath = ""; + + /// @brief The key length of the TLS cert to make + QString m_TlsKeyLength = ""; + + /// @brief should the setting be loaded from + /// SystemScope + /// If the user has settings but this is + /// true then system settings will be + /// loaded instead of the users + bool m_LoadFromSystemScope = false; + + /// @brief As the settings will be accessible by multiple objects this lock + /// will ensure that + /// it cant be modified by more that one object at a time if the + /// setting is being switched from system to user. + std::mutex m_settings_lock; + + static const char m_CoreServerName[]; + static const char m_CoreClientName[]; + static const char m_LogDir[]; + + /// @brief Contains the string values of the settings names that will be saved + static const char *const m_SettingsName[]; + + /// @brief Contains the name of the default configuration filename + static const char m_ConfigFilename[]; + signals: void sslToggled() const; void screenNameChanged() const; }; - -#endif diff --git a/src/gui/src/CancelActivationDialog.ui b/src/gui/src/CancelActivationDialog.ui index 054b3fe6f..c34d487ad 100644 --- a/src/gui/src/CancelActivationDialog.ui +++ b/src/gui/src/CancelActivationDialog.ui @@ -7,7 +7,7 @@ 0 0 400 - 165 + 246 @@ -17,9 +17,7 @@ - Are you sure? - -If you don't activate Synergy you'll be missing out on some great features. + <html><head/><body><p>You'll need to purchase a license to use this build of Synergy.</p><p><a href="https://symless.com/synergy/purchase?source=gui"><span style=" text-decoration: underline; color:#007af4;">Purchase Synergy</span></a></p><p>If you'd prefer to use the community edition instead, visit us on GitHub.</p><p><a href="https://github.com/symless/synergy-core"><span style=" text-decoration: underline; color:#007af4;">GitHub project</span></a></p><p>Would you like to go back and enter a serial key?</p><p>Choosing 'No' will quit Synergy.</p></body></html> true @@ -30,14 +28,17 @@ If you don't activate Synergy you'll be missing out on some great features. - - - <html><head/><body><p><a href="https://symless.com/pricing?source=gui"><span style=" text-decoration: underline; color:#0000ff;">Buy now</span></a></p></body></html> + + + Qt::Vertical - - true + + + 20 + 40 + - + diff --git a/src/gui/src/ClientConnection.cpp b/src/gui/src/ClientConnection.cpp index fb25b0eeb..2197caa94 100644 --- a/src/gui/src/ClientConnection.cpp +++ b/src/gui/src/ClientConnection.cpp @@ -59,13 +59,13 @@ QString ClientConnection::getMessage(const QString &line) const { message = QObject::tr("Connection failed.\nYou can’t name 2 computers the same."); } else { - QHostAddress address(m_parent.appConfig().getServerHostname()); + QHostAddress address(m_parent.appConfig().serverHostname()); if (address.isNull()) { message = QObject::tr( "We can’t connect to the server \"%1\" try to connect using the " "server IP address and check your firewall settings.") - .arg(m_parent.appConfig().getServerHostname()); + .arg(m_parent.appConfig().serverHostname()); } } diff --git a/src/gui/src/CommandProcess.cpp b/src/gui/src/CommandProcess.cpp index dcaa63ce2..8a54e6554 100644 --- a/src/gui/src/CommandProcess.cpp +++ b/src/gui/src/CommandProcess.cpp @@ -18,7 +18,6 @@ #include "CommandProcess.h" #include -#include CommandProcess::CommandProcess( QString cmd, QStringList arguments, QString input) @@ -32,7 +31,8 @@ QString CommandProcess::run() { process.start(m_Command, m_Arguments); bool success = process.waitForStarted(); - QString output, error; + QString output; + QString error; if (success) { if (!m_Input.isEmpty()) { process.write(m_Input.toStdString().c_str()); @@ -45,12 +45,11 @@ QString CommandProcess::run() { } } - int code = process.exitCode(); - if (!error.isEmpty() || !success || code != 0) { - throw std::runtime_error(QString("Code: %1\nError: %2") - .arg(process.exitCode()) - .arg(error.isEmpty() ? "Unknown" : error) - .toStdString()); + if (int code = process.exitCode(); !success || code != 0) { + qFatal( + "Command failed: %s %s\nCode: %d\nError: %s", qUtf8Printable(m_Command), + qUtf8Printable(m_Arguments.join(" ")), code, + error.isEmpty() ? "None" : qUtf8Printable(error)); } emit finished(); diff --git a/src/gui/src/CommonConfig.h b/src/gui/src/CommonConfig.h new file mode 100644 index 000000000..e6fae87c8 --- /dev/null +++ b/src/gui/src/CommonConfig.h @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2020 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 . + */ + +#pragma once + +namespace synergy::gui { + +/// @brief Common configuration interface +class CommonConfig { +public: + CommonConfig() = default; + virtual ~CommonConfig() = default; + virtual void loadSettings() = 0; + virtual void saveSettings() = 0; + bool modified() const { return m_modified; } + void setModified(bool modified) { m_modified = modified; } + +private: + bool m_modified = false; +}; + +} // namespace synergy::gui diff --git a/src/gui/src/ConfigWriter.cpp b/src/gui/src/Config.cpp similarity index 50% rename from src/gui/src/ConfigWriter.cpp rename to src/gui/src/Config.cpp index 26b04b4c1..81444542d 100644 --- a/src/gui/src/ConfigWriter.cpp +++ b/src/gui/src/Config.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2020-2020 Symless Ltd. + * Copyright (C) 2020 Symless Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -15,21 +15,21 @@ * along with this program. If not, see . */ -#include +#include "Config.h" + +#include "CommonConfig.h" #include #include +#include +#include -#include "ConfigBase.h" -#include "ConfigWriter.h" - -namespace { +namespace synergy::gui { QString getSystemSettingPath() { const QString settingFilename("SystemConfig.ini"); QString path; #if defined(Q_OS_WIN) - // Program file path = QCoreApplication::applicationDirPath() + "\\"; #elif defined(Q_OS_DARWIN) // Global preferances dir @@ -46,7 +46,7 @@ QString getSystemSettingPath() { } #if defined(Q_OS_WIN) -void loadOldSystemSettings(QSettings &settings) { +void loadWindowsLegacy(QSettings &settings) { if (!QFile(settings.fileName()).exists()) { QSettings::setPath( QSettings::IniFormat, QSettings::SystemScope, "SystemConfig.ini"); @@ -68,147 +68,114 @@ void loadOldSystemSettings(QSettings &settings) { } #endif -} // namespace - -namespace GUI { -namespace Config { -// Assignment of static variable -ConfigWriter *ConfigWriter::s_pConfiguration = nullptr; - -ConfigWriter *ConfigWriter::make() { - // Only one ConfigWriter can exist at any one time (Singleton) - if (!s_pConfiguration) { - s_pConfiguration = new ConfigWriter(); - } - return s_pConfiguration; -} - -ConfigWriter::ConfigWriter() { +Config::Config() { QSettings::setPath( QSettings::Format::IniFormat, QSettings::Scope::SystemScope, getSystemSettingPath()); // Config will default to User settings if they exist, // otherwise it will load System setting and save them to User settings - m_pSettingsSystem = new QSettings( + m_pSystemSettings = std::make_unique( QSettings::Format::IniFormat, QSettings::Scope::SystemScope, QCoreApplication::organizationName(), QCoreApplication::applicationName()); + // default to user scope. + // if we set the scope specifically then we also have to set the application + // name and the organisation name which breaks backwards compatibility. + m_pUserSettings = std::make_unique(); + + load(); +} + +Config::~Config() { + while (!m_pReceievers.empty()) { + m_pReceievers.pop_back(); + } +} + +void Config::load() { #if defined(Q_OS_WIN) // This call is needed for backwardcapability with old settings. - loadOldSystemSettings(*m_pSettingsSystem); + loadWindowsLegacy(*m_pSystemSettings); #endif - - // defaults to user scope, if we set the scope specifically then we also have - // to set - // the application name and the organisation name which breaks backwards - // compatibility See #6730 - m_pSettingsUser = new QSettings(); - - // Set scope to user for initially - m_pSettingsCurrent = m_pSettingsUser; } -void ConfigWriter::destroy() { destroy(s_pConfiguration); } - -ConfigWriter::~ConfigWriter() { - while (!m_pCallerList.empty()) { - m_pCallerList.pop_back(); - } - m_pSettingsCurrent = nullptr; // this only references other pointers - destroy(m_pSettingsSystem); - destroy(m_pSettingsUser); -} - -bool ConfigWriter::hasSetting(const QString &name, Scope scope) const { +bool Config::hasSetting(const QString &name, Scope scope) const { switch (scope) { - case kUser: - return m_pSettingsUser->contains(name); - case kSystem: - return m_pSettingsSystem->contains(name); + case Scope::User: + return m_pUserSettings->contains(name); + case Scope::System: + return m_pSystemSettings->contains(name); default: - return m_pSettingsCurrent->contains(name); + return currentSettings()->contains(name); } } -bool ConfigWriter::isWritable() const { - return m_pSettingsCurrent->isWritable(); -} +bool Config::isWritable() const { return currentSettings()->isWritable(); } -QVariant ConfigWriter::loadSetting( - const QString &name, const QVariant &defaultValue, Scope scope) { +QVariant Config::loadSetting( + const QString &name, const QVariant &defaultValue, Scope scope) const { switch (scope) { - case kUser: - return m_pSettingsUser->value(name, defaultValue); - case kSystem: - return m_pSettingsSystem->value(name, defaultValue); + case Scope::User: + return m_pUserSettings->value(name, defaultValue); + case Scope::System: + return m_pSystemSettings->value(name, defaultValue); default: - return m_pSettingsCurrent->value(name, defaultValue); + return currentSettings()->value(name, defaultValue); } } -void ConfigWriter::setScope(ConfigWriter::Scope scope) { - if (m_CurrentScope != scope) { - m_CurrentScope = scope; - switch (scope) { - case kUser: - m_pSettingsCurrent = m_pSettingsUser; - break; - case kSystem: - m_pSettingsCurrent = m_pSettingsSystem; - break; - default: - // setScope should never be kCurrent - assert(scope); - } - } -} +void Config::setScope(Config::Scope scope) { m_CurrentScope = scope; } -ConfigWriter::Scope ConfigWriter::getScope() const { return m_CurrentScope; } +Config::Scope Config::getScope() const { return m_CurrentScope; } -void ConfigWriter::globalLoad() { - for (auto &i : m_pCallerList) { +void Config::loadAll() { + for (auto &i : m_pReceievers) { i->loadSettings(); } } -void ConfigWriter::globalSave() { +void Config::saveAll() { // Save if there are any unsaved changes otherwise skip if (unsavedChanges()) { - for (auto &i : m_pCallerList) { + for (auto &i : m_pReceievers) { i->saveSettings(); } - m_pSettingsUser->sync(); - m_pSettingsSystem->sync(); + m_pUserSettings->sync(); + m_pSystemSettings->sync(); m_unsavedChanges = false; } } -QSettings &ConfigWriter::settings() { return *m_pSettingsCurrent; } - -void ConfigWriter::registerClass(ConfigBase *receiver) { - m_pCallerList.push_back(receiver); +QSettings *Config::currentSettings() const { + if (m_CurrentScope == Scope::User) { + return m_pUserSettings.get(); + } else { + return m_pSystemSettings.get(); + } } -bool ConfigWriter::unsavedChanges() const { +void Config::registerReceiever(CommonConfig *receiver) { + m_pReceievers.push_back(receiver); +} + +bool Config::unsavedChanges() const { if (m_unsavedChanges) { return true; } - for (const auto &i : m_pCallerList) { + for (const auto &i : m_pReceievers) { if (i->modified()) { - // If any class returns true there is no point checking more return true; } } - // If this line is reached no class has unsaved changes return false; } -void ConfigWriter::markUnsaved() { m_unsavedChanges = true; } -} // namespace Config -} // namespace GUI +void Config::markUnsaved() { m_unsavedChanges = true; } + +} // namespace synergy::gui diff --git a/src/gui/src/Config.h b/src/gui/src/Config.h new file mode 100644 index 000000000..24da15148 --- /dev/null +++ b/src/gui/src/Config.h @@ -0,0 +1,115 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2020 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 . + */ +#pragma once + +#include +#include +#include + +namespace synergy::gui { + +class CommonConfig; + +/// @brief A general config reader and writer for user and gloabl settings +class Config : private QObject { + +public: + enum class Scope { Current, System, User }; + + explicit Config(); + ~Config() override; + + /// @brief Checks if the setting exists + /// @param [in] name The name of the setting to check + /// @param [in] scope The scope to search in + /// @return bool True if the current scope has the named setting + bool hasSetting(const QString &name, Scope scope = Scope::Current) const; + + /// @brief Checks if the current scope settings writable + /// @return bool True if the current scope writable + bool isWritable() const; + + /// @brief Sets the value of a setting + /// @param [in] name The Setting to be saved + /// @param [in] value The Value to be saved (Templated) + /// @param [in] scope The scope to get the value from, default is current + /// scope + template + void setSetting(const QString &name, T value, Scope scope = Scope::Current); + + /// @brief Loads a setting + /// @param [in] name The setting to be loaded + /// @param [in] defaultValue The default value of the setting + /// @param [in] scope The scope to get the value from, default is current + /// scope + QVariant loadSetting( + const QString &name, const QVariant &defaultValue = QVariant(), + Scope scope = Scope::Current) const; + + /// @brief Changes the setting save and load location between System and User + /// scope + /// @param [in] scope The scope to set + void setScope(Scope scope = Scope::User); + + /// @brief Get the current scope the settings are loading and save from. + /// @return Scope An enum defining the current scope + Scope getScope() const; + + /// @brief trigger a config load across all registered classes + void loadAll(); + + /// @brief trigger a config save across all registered classes + void saveAll(); + + /// @brief Returns the current scopes settings object + /// If more specialize control into the settings is needed this can + /// provide direct access to the settings file handler + /// @return QSettings The Settings object as a reference + QSettings *currentSettings() const; + + /// @brief This marks the settings as unsaved if the settings() was used to + /// directly affect the config file + void markUnsaved(); + + /// @brief Register a class to receives requests to save and load settings + void registerReceiever(CommonConfig *receiver); + + /// @brief Checks if any registered class has any unsaved changes + /// @return bool True if any registered class has unsaved changes + bool unsavedChanges() const; + +private: + void load(); + + Scope m_CurrentScope = Scope::User; + std::unique_ptr m_pUserSettings; + std::unique_ptr m_pSystemSettings; + + /// @brief Receivers of load/save callbacks + std::list m_pReceievers; + + /// @brief Is set to true when settings are changed + bool m_unsavedChanges = false; +}; + +template +void Config::setSetting(const QString &name, T value, Scope scope) { + currentSettings()->setValue(name, value); + m_unsavedChanges = true; +} + +} // namespace synergy::gui diff --git a/src/gui/src/ConfigBase.h b/src/gui/src/ConfigBase.h deleted file mode 100644 index 49ac9ee99..000000000 --- a/src/gui/src/ConfigBase.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2020 - 2020 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 . - */ - -#ifndef SYNERGY_CORE_CONFIGBASE_H -#define SYNERGY_CORE_CONFIGBASE_H - -namespace GUI { -namespace Config { - -///@brief This abstract class will be used by all classes that use the -/// ConfigWriter -/// to allow global saving and loading -class ConfigBase { -public: - ConfigBase() = default; - - virtual ~ConfigBase() = default; - - /// @brief The function that is called when the settings need to be loaded - /// from file - virtual void loadSettings() = 0; - - /// @brief The function that is called when the settings need to be saved to - /// file - virtual void saveSettings() = 0; - - /// @brief Returns true if the class has marked itself with having unsaved - /// changes - bool modified() const { return m_unsavedChanges; } - -protected: - /// @brief Does the class have unsaved changes in it. - bool m_unsavedChanges = false; -}; -} // namespace Config -} // namespace GUI -#endif // SYNERGY_CORE_CONFIGBASE_H diff --git a/src/gui/src/ConfigWriter.h b/src/gui/src/ConfigWriter.h deleted file mode 100644 index 870e0d1a8..000000000 --- a/src/gui/src/ConfigWriter.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2020 - 2020 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 . - */ -#ifndef SYNERGY_CORE_CONFIGWRITER_H -#define SYNERGY_CORE_CONFIGWRITER_H - -#include -#include - -/// @brief Contains GUI code -namespace GUI { -/// @brief Contains Configuration code -namespace Config { - -// Forward declare the class referenced by pointer -class ConfigBase; - -class ConfigWriter : private QObject { - -public: - /// @brief the public way to construct the configuration calls - /// The pointer returned is owned by this class and should not be - /// stored by other classes. - static ConfigWriter *make(); - - /// @brief the public way to destroy the configuration class - static void destroy(); - - ~ConfigWriter() override; - - ///@brief An Enumeration of all the scopes available - enum Scope { kCurrent, kSystem, kUser }; - - /// @brief Checks if the setting exists - /// @param [in] name The name of the setting to check - /// @param [in] scope The scope to search in - /// @return bool True if the current scope has the named setting - bool hasSetting(const QString &name, Scope scope = kCurrent) const; - - /// @brief Checks if the current scope settings writable - /// @return bool True if the current scope writable - bool isWritable() const; - - /// @brief Sets the value of a setting - /// @param [in] name The Setting to be saved - /// @param [in] value The Value to be saved (Templated) - /// @param [in] scope The scope to get the value from, default is current - /// scope - template - void setSetting(const QString &name, T value, Scope scope = kCurrent); - - /// @brief Loads a setting - /// @param [in] name The setting to be loaded - /// @param [in] defaultValue The default value of the setting - /// @param [in] scope The scope to get the value from, default is current - /// scope - QVariant loadSetting( - const QString &name, const QVariant &defaultValue = QVariant(), - Scope scope = kCurrent); - - /// @brief Changes the setting save and load location between System and User - /// scope - /// @param [in] scope The scope to set - void setScope(Scope scope = kUser); - - /// @brief Get the current scope the settings are loading and save from. - /// @return Scope An enum defining the current scope - Scope getScope() const; - - /// @brief trigger a config load across all registered classes - void globalLoad(); - - /// @brief trigger a config save across all registered classes - void globalSave(); - - /// @brief Returns the current scopes settings object - /// If more specialize control into the settings is needed this can - /// provide direct access to the settings file handler - /// @return QSettings The Settings object as a reference - QSettings &settings(); - - /// @brief This marks the settings as unsaved if the settings() was used to - /// directly affect the config file - void markUnsaved(); - - /// @brief Register a class to receives globalLoad and globalSave events - /// @param [in] ConfigBase The class that will receive the events - void registerClass(ConfigBase *receiver); - - /// @brief Checks if any registered class has any unsaved changes - /// @return bool True if any registered class has unsaved changes - bool unsavedChanges() const; - -protected: - Scope m_CurrentScope = kUser; /// @brief The current scope of the settings - - QSettings *m_pSettingsCurrent = - nullptr; /// @brief The currently active settings - QSettings *m_pSettingsUser = nullptr; /// @brief The user specific settings - QSettings *m_pSettingsSystem = nullptr; /// @brief The system wide settings - -private: - /// @brief Contains a list all all classes that hook into the writer. - /// This allows all classes that save settings to be called an updated - /// on a save and reload by any other class - std::list m_pCallerList; - - /// @brief if this class modified settings then set the flag - bool m_unsavedChanges = false; - - /// @brief The constructor, as this is a singolton we want to control who can - /// call the constructor - ConfigWriter(); - - /// @brief the pointer of the ConfigWriter for singolton use - static ConfigWriter *s_pConfiguration; - - /// @brief deletes pointers and sets the value to null - template static inline void destroy(T *&p) { - delete p; - p = 0; - } -}; - -// Implementation of a template function needs to be visible to all calls thus -// is must be in the header Moved so its not bulking out the class definition -template -void ConfigWriter::setSetting(const QString &name, T value, Scope scope) { - switch (scope) { - case kUser: - m_pSettingsUser->setValue(name, value); - break; - case kSystem: - m_pSettingsSystem->setValue(name, value); - break; - default: - m_pSettingsCurrent->setValue(name, value); - break; - } - m_unsavedChanges = true; -} -} // namespace Config -} // namespace GUI -#endif // SYNERGY_CORE_CONFIGWRITER_H diff --git a/src/gui/src/CoreInterface.cpp b/src/gui/src/CoreInterface.cpp index 6b9c6b7fd..43b95c3ba 100644 --- a/src/gui/src/CoreInterface.cpp +++ b/src/gui/src/CoreInterface.cpp @@ -18,13 +18,11 @@ #include "CoreInterface.h" #include "CommandProcess.h" -#include "QUtility.h" #include #include #include #include -#include static const char kCoreBinary[] = "syntool"; @@ -34,16 +32,14 @@ static const char kSerialKeyFilename[] = "Synergy.subkey"; static const char kSerialKeyFilename[] = ".synergy.subkey"; #endif -CoreInterface::CoreInterface() {} - QString CoreInterface::getProfileDir() { QStringList args("--get-profile-dir"); - return run(args); + return QDir::cleanPath(run(args)); } QString CoreInterface::getInstalledDir() { QStringList args("--get-installed-dir"); - return run(args); + return QDir::cleanPath(run(args)); } QString CoreInterface::getArch() { @@ -52,11 +48,12 @@ QString CoreInterface::getArch() { } QString CoreInterface::getSerialKeyFilePath() { - QString filename = getProfileDir() + QDir::separator() + kSerialKeyFilename; - return filename; + auto filename = getProfileDir() + QDir::separator() + kSerialKeyFilename; + return QDir::cleanPath(filename); } -QString CoreInterface::run(const QStringList &args, const QString &input) { +QString +CoreInterface::run(const QStringList &args, const QString &input) const { QString program(QCoreApplication::applicationDirPath() + "/" + kCoreBinary); CommandProcess commandProcess(program, args, input); diff --git a/src/gui/src/CoreInterface.h b/src/gui/src/CoreInterface.h index ffce03747..8cabff22c 100644 --- a/src/gui/src/CoreInterface.h +++ b/src/gui/src/CoreInterface.h @@ -21,11 +21,9 @@ class CoreInterface { public: - CoreInterface(); - QString getProfileDir(); QString getInstalledDir(); QString getArch(); QString getSerialKeyFilePath(); - QString run(const QStringList &args, const QString &input = ""); + QString run(const QStringList &args, const QString &input = "") const; }; diff --git a/src/gui/src/ElevateMode.h b/src/gui/src/ElevateMode.h index 0d87f4e98..d2099912e 100644 --- a/src/gui/src/ElevateMode.h +++ b/src/gui/src/ElevateMode.h @@ -33,5 +33,3 @@ // ElevateNever | false | false // enum ElevateMode { ElevateAsNeeded = 0, ElevateAlways = 1, ElevateNever = 2 }; - -extern const ElevateMode kDefaultElevateMode; diff --git a/src/gui/src/LicenseManager.cpp b/src/gui/src/LicenseManager.cpp index 39ef75d5b..1ea44b40a 100644 --- a/src/gui/src/LicenseManager.cpp +++ b/src/gui/src/LicenseManager.cpp @@ -16,7 +16,10 @@ */ #include "LicenseManager.h" + +#include "ActivationNotifier.h" #include "AppConfig.h" + #include #include #include @@ -66,7 +69,7 @@ void checkSerialKey(const SerialKey &serialKey, bool acceptExpired) { } // namespace LicenseManager::LicenseManager(AppConfig *appConfig) - : m_AppConfig(appConfig), + : m_pAppConfig(appConfig), m_serialKey(appConfig->edition()), m_registry(*appConfig) {} @@ -76,14 +79,14 @@ void LicenseManager::setSerialKey(SerialKey serialKey, bool acceptExpired) { if (serialKey != m_serialKey) { using std::swap; swap(serialKey, m_serialKey); - m_AppConfig->setSerialKey(QString::fromStdString(m_serialKey.toString())); + m_pAppConfig->setSerialKey(QString::fromStdString(m_serialKey.toString())); emit showLicenseNotice(getLicenseNotice()); validateSerialKey(); m_registry.scheduleRegistration(); if (m_serialKey.edition() != serialKey.edition()) { - m_AppConfig->setEdition(m_serialKey.edition()); + m_pAppConfig->setEdition(m_serialKey.edition()); emit editionChanged(m_serialKey.edition()); } } @@ -119,13 +122,14 @@ QString LicenseManager::activeEditionName() const { const SerialKey &LicenseManager::serialKey() const { return m_serialKey; } void LicenseManager::refresh() { - if (!m_AppConfig->serialKey().isEmpty()) { + if (!m_pAppConfig->serialKey().isEmpty()) { try { - SerialKey serialKey(m_AppConfig->serialKey().toStdString()); + SerialKey serialKey(m_pAppConfig->serialKey().toStdString()); setSerialKey(serialKey, true); - } catch (...) { + } catch (const std::exception &e) { + qDebug() << e.what(); m_serialKey = SerialKey(); - m_AppConfig->clearSerialKey(); + m_pAppConfig->clearSerialKey(); } } if (!m_serialKey.isValid()) { @@ -133,10 +137,6 @@ void LicenseManager::refresh() { } } -void LicenseManager::skipActivation() const { - notifyActivation("skip:unknown"); -} - QString LicenseManager::getEditionName(Edition const edition, bool trial) { SerialKeyEdition KeyEdition(edition); std::string name = KeyEdition.getProductName(); diff --git a/src/gui/src/LicenseManager.h b/src/gui/src/LicenseManager.h index 69473f781..42caf1b70 100644 --- a/src/gui/src/LicenseManager.h +++ b/src/gui/src/LicenseManager.h @@ -17,13 +17,11 @@ #pragma once -#include -#include -#include -#include -#include - #include "LicenseRegistry.h" +#include "shared/EditionType.h" +#include "shared/SerialKey.h" + +#include class AppConfig; @@ -37,14 +35,13 @@ public: Edition activeEdition() const; QString activeEditionName() const; const SerialKey &serialKey() const; - void skipActivation() const; void notifyUpdate(QString fromVersion, QString toVersion) const; static QString getEditionName(Edition edition, bool trial = false); void notifyActivation(QString identity) const; QString getLicenseNotice() const; private: - AppConfig *m_AppConfig; + AppConfig *m_pAppConfig; SerialKey m_serialKey; LicenseRegistry m_registry; diff --git a/src/gui/src/LicenseRegistry.cpp b/src/gui/src/LicenseRegistry.cpp index 45d09f3bd..9b36708bc 100644 --- a/src/gui/src/LicenseRegistry.cpp +++ b/src/gui/src/LicenseRegistry.cpp @@ -23,6 +23,9 @@ #include "LicenseRegistry.h" #include +const char *const kLicenseRegistryUrl = + "https://api2.prod.symless.com/license/register"; + LicenseRegistry::LicenseRegistry(AppConfig &config) : m_config(config) { connect(&m_timer, SIGNAL(timeout()), this, SLOT(registerLicense())); } @@ -30,8 +33,7 @@ LicenseRegistry::LicenseRegistry(AppConfig &config) : m_config(config) { void LicenseRegistry::registerLicense() { m_timer.stop(); if (m_config.edition() == Edition::kBusiness) { - const auto REGISTER_LICENSE_URL = m_config.getLicenseRegistryUrl(); - const auto url = QUrl(REGISTER_LICENSE_URL); + const auto url = QUrl(kLicenseRegistryUrl); auto request = QNetworkRequest(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); @@ -73,18 +75,18 @@ QByteArray LicenseRegistry::getRequestData() const { data["guid"] = guid; data["guid_type"] = "system"; } else { - data["guid"] = m_config.getGuid(); + data["guid"] = m_config.guid(); data["guid_type"] = "synergy"; } data["key"] = m_config.serialKey(); - data["is_server"] = m_config.getServerGroupChecked(); + data["is_server"] = m_config.serverGroupChecked(); return QJsonDocument(data).toJson(); } void LicenseRegistry::scheduleRegistration() { - const auto nextCheck = m_config.getLicenseNextCheck(); + const auto nextCheck = m_config.licenseNextCheck(); const auto currentTimestamp = static_cast(time(nullptr)); if (currentTimestamp >= nextCheck) { diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 110382584..bc617f969 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -16,11 +16,6 @@ * along with this program. If not, see . */ -#define DOWNLOAD_URL "http://symless.com/?source=gui" -#define HELP_URL "http://symless.com/help?source=gui" - -#include - #include "MainWindow.h" #include "AboutDialog.h" @@ -29,8 +24,8 @@ #include "LicenseManager.h" #include "ServerConfigDialog.h" #include "SettingsDialog.h" -#include -#include +#include "shared/EditionType.h" +#include #if defined(Q_OS_MAC) #include "OSXHelpers.h" @@ -42,39 +37,46 @@ #include #include #include +#include #include #include #include #include +#include #if defined(Q_OS_MAC) #include #endif -static const int debugLogLevel = 1; +static const char *const kDownloadUrl = "https://symless.com/?source=gui"; +static const char *const kHelpUrl = "https://symless.com/help?source=gui"; +static const int kRetryDelay = 1000; +static const int kDebugLogLevel = 1; -static const char *synergyLightIconFiles[] = { +#if defined(Q_OS_MAC) + +static const char *const kLightIconFiles[] = { ":/res/icons/64x64/synergy-light-disconnected.png", ":/res/icons/64x64/synergy-light-disconnected.png", ":/res/icons/64x64/synergy-light-connected.png", ":/res/icons/64x64/synergy-light-transfering.png", ":/res/icons/64x64/synergy-light-disconnected.png"}; -static const char *synergyDarkIconFiles[] = { +static const char *const kDarkIconFiles[] = { ":/res/icons/64x64/synergy-dark-disconnected.png", ":/res/icons/64x64/synergy-dark-disconnected.png", ":/res/icons/64x64/synergy-dark-connected.png", ":/res/icons/64x64/synergy-dark-transfering.png", - ":/res/icons/64x64/synergy-dark-disconnected.png" // synergyPendingRetry -}; + ":/res/icons/64x64/synergy-dark-disconnected.png"}; -static const char *synergyDefaultIconFiles[] = { - ":/res/icons/16x16/synergy-disconnected.png", // synergyDisconnected - ":/res/icons/16x16/synergy-disconnected.png", // synergyConnecting - ":/res/icons/16x16/synergy-connected.png", // synergyConnected - ":/res/icons/16x16/synergy-transfering.png", // synergyListening - ":/res/icons/16x16/synergy-disconnected.png" // synergyPendingRetry -}; +#endif + +static const char *const kDefaultIconFiles[] = { + ":/res/icons/16x16/synergy-disconnected.png", + ":/res/icons/16x16/synergy-disconnected.png", + ":/res/icons/16x16/synergy-connected.png", + ":/res/icons/16x16/synergy-transfering.png", + ":/res/icons/16x16/synergy-disconnected.png"}; #ifdef SYNERGY_ENABLE_LICENSING MainWindow::MainWindow(AppConfig &appConfig, LicenseManager &licenseManager) @@ -84,21 +86,9 @@ MainWindow::MainWindow(AppConfig &appConfig) : #ifdef SYNERGY_ENABLE_LICENSING m_LicenseManager(&licenseManager), - m_ActivationDialogRunning(false), #endif - m_AppConfig(&appConfig), - m_pSynergy(NULL), - m_SynergyState(synergyDisconnected), - m_ServerConfig(5, 3, m_AppConfig, this), - m_AlreadyHidden(false), - m_pMenuBar(NULL), - m_pMenuFile(NULL), - m_pMenuEdit(NULL), - m_pMenuWindow(NULL), - m_pMenuHelp(NULL), - m_pCancelButton(NULL), - m_ExpectedRunningState(kStopped), - m_SecureSocket(false), + m_AppConfig(appConfig), + m_ServerConfig(5, 3, &m_AppConfig, this), m_serverConnection(*this), m_clientConnection(*this) { @@ -109,16 +99,19 @@ MainWindow::MainWindow(AppConfig &appConfig) m_pRadioGroupClient->setAttribute(Qt::WA_MacShowFocusRect, 0); #endif + m_ServerConfig.loadSettings(); + createMenuBar(); loadSettings(); initConnections(); m_pWidgetUpdate->hide(); - m_VersionChecker.setApp(appPath(appConfig.synergycName())); + m_VersionChecker.setApp(appPath(appConfig.coreClientName())); updateScreenName(); connect( - m_AppConfig, SIGNAL(screenNameChanged()), this, SLOT(updateScreenName())); + appConfigPtr(), SIGNAL(screenNameChanged()), this, + SLOT(updateScreenName())); m_pLabelIpAddresses->setText( tr("This computer's IP addresses: %1").arg(getIPAddresses())); @@ -172,14 +165,14 @@ MainWindow::MainWindow(AppConfig &appConfig) #endif connect( - m_AppConfig, SIGNAL(sslToggled()), this, SLOT(updateLocalFingerprint()), - Qt::QueuedConnection); + appConfigPtr(), SIGNAL(sslToggled()), this, + SLOT(updateLocalFingerprint()), Qt::QueuedConnection); updateWindowTitle(); - QString lastVersion = m_AppConfig->lastVersion(); + QString lastVersion = m_AppConfig.lastVersion(); if (lastVersion != SYNERGY_VERSION) { - m_AppConfig->setLastVersion(SYNERGY_VERSION); + m_AppConfig.setLastVersion(SYNERGY_VERSION); #ifdef SYNERGY_ENABLE_LICENSING m_LicenseManager->notifyUpdate(lastVersion, SYNERGY_VERSION); @@ -193,13 +186,12 @@ MainWindow::MainWindow(AppConfig &appConfig) MainWindow::~MainWindow() { if (appConfig().processMode() == ProcessMode::kDesktop) { - m_ExpectedRunningState = kStopped; + m_ExpectedRunningState = RuningState::Stopped; try { stopDesktop(); - } catch (...) { - // do not throw, since throwing from a dtor can result in unreliable - // behaviour. - qCritical() << "error stopping desktop in main window destructor"; + } catch (const std::exception &e) { + qDebug() << e.what(); + qFatal("Failed to stop synergy desktop process"); } } } @@ -207,16 +199,15 @@ MainWindow::~MainWindow() { void MainWindow::open() { std::array trayMenu = { - m_pActionStartSynergy, m_pActionStopSynergy, nullptr, - m_pActionMinimize, m_pActionRestore, nullptr, - m_pActionQuit}; + m_pActionStartCore, m_pActionStopCore, nullptr, m_pActionMinimize, + m_pActionRestore, nullptr, m_pActionQuit}; m_trayIcon.create(trayMenu, [this](QObject const *o, const char *s) { connect(o, s, this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason))); - setIcon(synergyDisconnected); + setIcon(CoreState::Disconnected); }); - if (appConfig().getAutoHide()) { + if (appConfig().autoHide()) { hide(); } else { showNormal(); @@ -229,7 +220,7 @@ void MainWindow::open() { // confuses first time users, who think synergy has crashed). if (appConfig().startedBefore() && appConfig().processMode() == ProcessMode::kDesktop) { - startSynergy(); + startCore(); } } @@ -251,8 +242,8 @@ void MainWindow::createMenuBar() { #endif m_pMenuBar->addAction(m_pMenuHelp->menuAction()); - m_pMenuFile->addAction(m_pActionStartSynergy); - m_pMenuFile->addAction(m_pActionStopSynergy); + m_pMenuFile->addAction(m_pActionStartCore); + m_pMenuFile->addAction(m_pActionStopCore); m_pMenuFile->addSeparator(); m_pMenuFile->addAction(m_pActivate); m_pMenuFile->addSeparator(); @@ -269,19 +260,18 @@ void MainWindow::createMenuBar() { } void MainWindow::loadSettings() { - enableServer(appConfig().getServerGroupChecked()); - enableClient(appConfig().getClientGroupChecked()); + enableServer(appConfig().serverGroupChecked()); + enableClient(appConfig().clientGroupChecked()); - m_pLineEditHostname->setText(appConfig().getServerHostname()); + m_pLineEditHostname->setText(appConfig().serverHostname()); m_pLineEditClienIp->setText(serverConfig().getClientAddress()); } void MainWindow::initConnections() { connect(m_pActionMinimize, SIGNAL(triggered()), this, SLOT(hide())); connect(m_pActionRestore, SIGNAL(triggered()), this, SLOT(showNormal())); - connect( - m_pActionStartSynergy, SIGNAL(triggered()), this, SLOT(actionStart())); - connect(m_pActionStopSynergy, SIGNAL(triggered()), this, SLOT(stopSynergy())); + connect(m_pActionStartCore, SIGNAL(triggered()), this, SLOT(actionStart())); + connect(m_pActionStopCore, SIGNAL(triggered()), this, SLOT(stopCore())); connect(m_pActionQuit, SIGNAL(triggered()), qApp, SLOT(quit())); connect( &m_VersionChecker, SIGNAL(updateFound(const QString &)), this, @@ -289,35 +279,34 @@ void MainWindow::initConnections() { } void MainWindow::saveSettings() { - // program settings appConfig().setServerGroupChecked(m_pRadioGroupServer->isChecked()); appConfig().setClientGroupChecked(m_pRadioGroupClient->isChecked()); appConfig().setServerHostname(m_pLineEditHostname->text()); serverConfig().setClientAddress(m_pLineEditClienIp->text()); - /* Save everything */ - GUI::Config::ConfigWriter::make()->globalSave(); + appConfig().config().saveAll(); } -void MainWindow::setIcon(qSynergyState state) const { +void MainWindow::setIcon(CoreState state) const { QIcon icon; + auto index = static_cast(state); #ifdef Q_OS_MAC switch (getOSXIconsTheme()) { case IconsTheme::ICONS_DARK: - icon.addFile(synergyDarkIconFiles[state]); + icon.addFile(kDarkIconFiles[index]); break; case IconsTheme::ICONS_LIGHT: - icon.addFile(synergyLightIconFiles[state]); + icon.addFile(kLightIconFiles[index]); break; case IconsTheme::ICONS_TEMPLATE: default: - icon.addFile(synergyDarkIconFiles[state]); + icon.addFile(kDarkIconFiles[index]); icon.setIsMask(true); break; } #else - icon.addFile(synergyDefaultIconFiles[state]); + icon.addFile(kDefaultIconFiles[index]); #endif m_trayIcon.set(icon); @@ -335,8 +324,8 @@ void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason reason) { } void MainWindow::logOutput() { - if (m_pSynergy) { - QString text(m_pSynergy->readAllStandardOutput()); + if (m_pCoreProcess) { + QString text(m_pCoreProcess->readAllStandardOutput()); for (QString line : text.split(QRegularExpression("\r|\n|\r\n"))) { if (!line.isEmpty()) { appendLogRaw(line); @@ -346,8 +335,8 @@ void MainWindow::logOutput() { } void MainWindow::logError() { - if (m_pSynergy) { - appendLogRaw(m_pSynergy->readAllStandardError()); + if (m_pCoreProcess) { + appendLogRaw(m_pCoreProcess->readAllStandardError()); } } @@ -357,7 +346,7 @@ void MainWindow::updateFound(const QString &version) { "Version %1 is now available to " "download.

") .arg(version) - .arg(DOWNLOAD_URL)); + .arg(kDownloadUrl)); } void MainWindow::appendLogInfo(const QString &text) { @@ -365,7 +354,7 @@ void MainWindow::appendLogInfo(const QString &text) { } void MainWindow::appendLogDebug(const QString &text) { - if (appConfig().logLevel() >= debugLogLevel) { + if (appConfig().logLevel() >= kDebugLogLevel) { appendLogRaw(getTimeStamp() + " DEBUG: " + text); } } @@ -395,7 +384,7 @@ void MainWindow::handleIdleService(const QString &text) { // only start if there is no active service running if (!line.isEmpty() && line.contains("service status: idle") && appConfig().startedBefore()) { - startSynergy(); + startCore(); } } } @@ -428,7 +417,7 @@ void MainWindow::checkConnected(const QString &line) { } if (line.contains("connected to server") || line.contains("has connected")) { - setSynergyState(synergyConnected); + setCoreState(CoreState::Connected); if (!appConfig().startedBefore() && isVisible()) { QMessageBox::information( @@ -440,13 +429,13 @@ void MainWindow::checkConnected(const QString &line) { appConfig().setStartedBefore(true); } } else if (line.contains("started server")) { - setSynergyState(synergyListening); + setCoreState(CoreState::Listening); } else if ( line.contains("disconnected from server") || line.contains("process exited")) { - setSynergyState(synergyDisconnected); + setCoreState(CoreState::Disconnected); } else if (line.contains("connecting to")) { - setSynergyState(synergyConnecting); + setCoreState(CoreState::Connecting); } } @@ -474,7 +463,7 @@ void MainWindow::checkFingerprint(const QString &line) { static bool messageBoxAlreadyShown = false; if (!messageBoxAlreadyShown) { - stopSynergy(); + stopCore(); messageBoxAlreadyShown = true; QMessageBox::StandardButton fingerprintReply = QMessageBox::information( @@ -493,7 +482,7 @@ void MainWindow::checkFingerprint(const QString &line) { if (fingerprintReply == QMessageBox::Yes) { // restart core process after trusting fingerprint. Fingerprint::trustedServers().trust(fingerprint); - startSynergy(); + startCore(); } messageBoxAlreadyShown = false; @@ -536,9 +525,9 @@ QString MainWindow::getTimeStamp() { return '[' + current.toString(Qt::ISODate) + ']'; } -void MainWindow::restartSynergy() { - stopSynergy(); - startSynergy(); +void MainWindow::restartCore() { + stopCore(); + startCore(); } void MainWindow::showEvent(QShowEvent *event) { @@ -548,7 +537,7 @@ void MainWindow::showEvent(QShowEvent *event) { void MainWindow::clearLog() { m_pLogOutput->clear(); } -void MainWindow::startSynergy() { +void MainWindow::startCore() { saveSettings(); #ifdef Q_OS_MAC @@ -564,12 +553,10 @@ void MainWindow::startSynergy() { } m_LicenseManager->registerLicense(); #endif - bool desktopMode = appConfig().processMode() == ProcessMode::kDesktop; - bool serviceMode = appConfig().processMode() == ProcessMode::kService; appendLogDebug("starting process"); - m_ExpectedRunningState = kStarted; - setSynergyState(synergyConnecting); + m_ExpectedRunningState = RuningState::Started; + setCoreState(CoreState::Connecting); QString app; QStringList args; @@ -580,8 +567,10 @@ void MainWindow::startSynergy() { args << "--name" << appConfig().screenName(); - if (desktopMode) { - setSynergyProcess(new QProcess(this)); + ProcessMode mode = appConfig().processMode(); + + if (mode == ProcessMode::kDesktop) { + m_pCoreProcess = std::make_unique(this); } else { // tell client/server to talk to daemon through ipc. args << "--ipc"; @@ -611,25 +600,30 @@ void MainWindow::startSynergy() { #endif #if defined(Q_OS_WIN) - if (m_AppConfig->getCryptoEnabled()) { + if (m_AppConfig.cryptoEnabled()) { args << "--enable-crypto"; - args << "--tls-cert" - << QString("\"%1\"").arg(m_AppConfig->getTLSCertPath()); + args << "--tls-cert" << m_AppConfig.tlsCertPath(); + } + + try { + // on windows, the profile directory changes depending on the user that + // launched the process (e.g. when launched with elevation). setting the + // profile dir on launch ensures it uses the same profile dir is used + // no matter how its relaunched. + args << "--profile-dir" << getProfileRootForArg(); + } catch (const std::exception &e) { + qDebug() << e.what(); + qFatal("Failed to get profile dir, skipping arg"); } - // on windows, the profile directory changes depending on the user that - // launched the process (e.g. when launched with elevation). setting the - // profile dir on launch ensures it uses the same profile dir is used - // no matter how its relaunched. - args << "--profile-dir" << getProfileRootForArg(); #else - if (m_AppConfig->getCryptoEnabled()) { + if (m_AppConfig.cryptoEnabled()) { args << "--enable-crypto"; - args << "--tls-cert" << m_AppConfig->getTLSCertPath(); + args << "--tls-cert" << m_AppConfig.tlsCertPath(); } #endif - if (m_AppConfig->getPreventSleep()) { + if (m_AppConfig.preventSleep()) { args << "--prevent-sleep"; } @@ -639,28 +633,26 @@ void MainWindow::startSynergy() { appendLogInfo( "starting " + - QString(synergyType() == synergyServer ? "server" : "client")); + QString(coreMode() == CoreMode::Server ? "server" : "client")); - if ((synergyType() == synergyClient && !clientArgs(args, app)) || - (synergyType() == synergyServer && !serverArgs(args, app))) { - stopSynergy(); + if ((coreMode() == CoreMode::Client && !clientArgs(args, app)) || + (coreMode() == CoreMode::Server && !serverArgs(args, app))) { + stopCore(); return; } - if (desktopMode) { + if (mode == ProcessMode::kDesktop) { connect( - synergyProcess(), SIGNAL(finished(int, QProcess::ExitStatus)), this, - SLOT(synergyFinished(int, QProcess::ExitStatus))); + m_pCoreProcess.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, + SLOT(coreProcessExit(int, QProcess::ExitStatus))); connect( - synergyProcess(), SIGNAL(readyReadStandardOutput()), this, + m_pCoreProcess.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(logOutput())); connect( - synergyProcess(), SIGNAL(readyReadStandardError()), this, + m_pCoreProcess.get(), SIGNAL(readyReadStandardError()), this, SLOT(logError())); } - qDebug() << args; - // show command if debug log level... if (appConfig().logLevel() >= 4) { appendLogInfo(QString("command: %1 %2").arg(app, args.join(" "))); @@ -671,9 +663,9 @@ void MainWindow::startSynergy() { if (appConfig().logToFile()) appendLogInfo("log file: " + appConfig().logFilename()); - if (desktopMode) { - synergyProcess()->start(app, args); - if (!synergyProcess()->waitForStarted()) { + if (mode == ProcessMode::kDesktop) { + m_pCoreProcess->start(app, args); + if (!m_pCoreProcess->waitForStarted()) { show(); QMessageBox::warning( this, tr("Program can not be started"), @@ -684,9 +676,7 @@ void MainWindow::startSynergy() { .arg(app))); return; } - } - - if (serviceMode) { + } else if (mode == ProcessMode::kService) { QString command(app + " " + args.join(" ")); m_IpcClient.sendCommand(command, appConfig().elevateMode()); } @@ -694,19 +684,19 @@ void MainWindow::startSynergy() { void MainWindow::actionStart() { m_clientConnection.setCheckConnection(true); - startSynergy(); + startCore(); } void MainWindow::retryStart() { // This function is only called after a failed start // Only start synergy if the current state is pending retry - if (m_SynergyState == synergyPendingRetry) { - startSynergy(); + if (m_CoreState == CoreState::PendingRetry) { + startCore(); } } bool MainWindow::clientArgs(QStringList &args, QString &app) { - app = appPath(appConfig().synergycName()); + app = appPath(appConfig().coreClientName()); if (!QFile::exists(app)) { show(); @@ -716,38 +706,31 @@ bool MainWindow::clientArgs(QStringList &args, QString &app) { return false; } -#if defined(Q_OS_WIN) - // wrap in quotes so a malicious user can't start \Program.exe as admin. - app = QString("\"%1\"").arg(app); -#endif - if (appConfig().logToFile()) { appConfig().persistLogDir(); - args << "--log" << appConfig().logFilenameCmd(); + args << "--log" << appConfig().logFilename(); } - if (appConfig().getLanguageSync()) { + if (appConfig().languageSync()) { args << "--sync-language"; } - if (appConfig().getInvertScrollDirection()) { + if (appConfig().invertScrollDirection()) { args << "--invert-scroll"; } - if (m_pLineEditHostname->text().isEmpty() && - !appConfig().getClientHostMode()) { - show(); - QMessageBox::warning( - this, tr("Hostname is empty"), - tr("Please fill in a hostname for the synergy " - "client to connect to.")); - return false; - } - - if (appConfig().getClientHostMode()) { + if (appConfig().clientHostMode()) { args << "--host"; args << ":" + QString::number(appConfig().port()); } else { + if (m_pLineEditHostname->text().isEmpty()) { + show(); + QMessageBox::warning( + this, tr("IP/hostname is empty"), + tr("Please enter a server hostname or IP address.")); + return false; + } + QString hostName = m_pLineEditHostname->text(); // if interface is IPv6 - ensure that ip is in square brackets if (hostName.count(':') > 1) { @@ -766,8 +749,8 @@ bool MainWindow::clientArgs(QStringList &args, QString &app) { QString MainWindow::configFilename() { QString configFullPath; - if (appConfig().getUseExternalConfig()) { - configFullPath = appConfig().getConfigFile(); + if (appConfig().useExternalConfig()) { + configFullPath = appConfig().configFile(); } else { QStringList errors; for (auto path : @@ -818,11 +801,12 @@ QString MainWindow::address() const { } QString MainWindow::appPath(const QString &name) { - return appConfig().synergyProgramDir() + name; + QDir dir(QCoreApplication::applicationDirPath()); + return dir.filePath(name); } bool MainWindow::serverArgs(QStringList &args, QString &app) { - app = appPath(appConfig().synergysName()); + app = appPath(appConfig().coreServerName()); if (!QFile::exists(app)) { QMessageBox::warning( @@ -831,33 +815,24 @@ bool MainWindow::serverArgs(QStringList &args, QString &app) { return false; } - if (appConfig().getServerClientMode() && - m_pLineEditClienIp->text().isEmpty()) { + if (appConfig().serverClientMode() && 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) - // wrap in quotes so a malicious user can't start \Program.exe as admin. - app = QString("\"%1\"").arg(app); -#endif - if (appConfig().logToFile()) { appConfig().persistLogDir(); - args << "--log" << appConfig().logFilenameCmd(); + args << "--log" << appConfig().logFilename(); } QString configFilename = this->configFilename(); if (configFilename.isEmpty()) { return false; } -#if defined(Q_OS_WIN) - // wrap in quotes in case username contains spaces. - configFilename = QString("\"%1\"").arg(configFilename); -#endif + args << "-c" << configFilename << "--address" << address(); appendLogInfo("config file: " + configFilename); @@ -870,10 +845,10 @@ bool MainWindow::serverArgs(QStringList &args, QString &app) { return true; } -void MainWindow::stopSynergy() { +void MainWindow::stopCore() { appendLogDebug("stopping process"); - m_ExpectedRunningState = kStopped; + m_ExpectedRunningState = RuningState::Stopped; if (appConfig().processMode() == ProcessMode::kService) { stopService(); @@ -881,7 +856,7 @@ void MainWindow::stopSynergy() { stopDesktop(); } - setSynergyState(synergyDisconnected); + setCoreState(CoreState::Disconnected); // reset so that new connects cause auto-hide. m_AlreadyHidden = false; @@ -894,87 +869,92 @@ void MainWindow::stopService() { void MainWindow::stopDesktop() { QMutexLocker locker(&m_StopDesktopMutex); - if (!synergyProcess()) { + if (!m_pCoreProcess) { return; } appendLogInfo("stopping synergy desktop process"); - if (synergyProcess()->isOpen()) { - synergyProcess()->close(); + if (m_pCoreProcess->isOpen()) { + m_pCoreProcess->close(); } - delete synergyProcess(); - setSynergyProcess(NULL); + m_pCoreProcess->reset(); } -void MainWindow::synergyFinished(int exitCode, QProcess::ExitStatus) { +void MainWindow::coreProcessExit(int exitCode, QProcess::ExitStatus) { if (exitCode == 0) { - appendLogInfo(QString("process exited normally")); + appendLogInfo("process exited normally"); } else { appendLogError(QString("process exited with error code: %1").arg(exitCode)); } - if (m_ExpectedRunningState == kStarted) { + if (m_ExpectedRunningState == RuningState::Started) { - setSynergyState(synergyPendingRetry); - QTimer::singleShot(1000, this, SLOT(retryStart())); - appendLogInfo(QString("detected process not running, auto restarting")); + if (coreState() != CoreState::PendingRetry) { + QTimer::singleShot(kRetryDelay, this, SLOT(retryStart())); + appendLogInfo("detected process not running, auto restarting"); + } else { + appendLogInfo("detected process not running, already auto restarting"); + } + + setCoreState(CoreState::PendingRetry); } else { - setSynergyState(synergyDisconnected); + setCoreState(CoreState::Disconnected); } } -void MainWindow::setSynergyState(qSynergyState state) { +void MainWindow::setCoreState(CoreState state) { // always assume connection is not secure when connection changes // to anything except connected. the only way the padlock shows is // when the correct TLS version string is detected. - if (state != synergyConnected) { + if (state != CoreState::Connected) { secureSocket(false); } - if (synergyState() == state) + if (coreState() == state) return; - if ((state == synergyConnected) || (state == synergyConnecting) || - (state == synergyListening) || (state == synergyPendingRetry)) { + if ((state == CoreState::Connected) || (state == CoreState::Connecting) || + (state == CoreState::Listening) || (state == CoreState::PendingRetry)) { disconnect( - m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, + m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartCore, SLOT(trigger())); connect( - m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, + m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopCore, SLOT(trigger())); + m_pButtonToggleStart->setText(tr("&Stop")); m_pButtonApply->setEnabled(true); - } else if (state == synergyDisconnected) { + + m_pActionStartCore->setEnabled(false); + m_pActionStopCore->setEnabled(true); + + } else if (state == CoreState::Disconnected) { disconnect( - m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, + m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopCore, SLOT(trigger())); connect( - m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, + m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartCore, SLOT(trigger())); + m_pButtonToggleStart->setText(tr("&Start")); m_pButtonApply->setEnabled(false); - } - bool running = false; - if (state == synergyConnected || state == synergyListening) { - running = true; + m_pActionStartCore->setEnabled(true); + m_pActionStopCore->setEnabled(false); } - m_pActionStartSynergy->setEnabled(!running); - m_pActionStopSynergy->setEnabled(running); - switch (state) { - case synergyListening: { - if (synergyType() == synergyServer) { + case CoreState::Listening: { + if (coreMode() == CoreMode::Server) { setStatus( tr("Synergy is waiting for clients").arg(m_SecureSocketVersion)); } break; } - case synergyConnected: { + case CoreState::Connected: { if (m_SecureSocket) { setStatus( tr("Synergy is connected (with %1)").arg(m_SecureSocketVersion)); @@ -984,20 +964,20 @@ void MainWindow::setSynergyState(qSynergyState state) { } break; } - case synergyConnecting: + case CoreState::Connecting: setStatus(tr("Synergy is starting...")); break; - case synergyPendingRetry: + case CoreState::PendingRetry: setStatus(tr("There was an error, retrying...")); break; - case synergyDisconnected: + case CoreState::Disconnected: setStatus(tr("Synergy is not running")); break; } setIcon(state); - m_SynergyState = state; + m_CoreState = state; } void MainWindow::setVisible(bool visible) { @@ -1057,8 +1037,8 @@ void MainWindow::setEdition(Edition edition) { #ifdef SYNERGY_ENABLE_LICENSING void MainWindow::InvalidLicense() { - stopSynergy(); - m_AppConfig->activationHasRun(false); + stopCore(); + m_AppConfig.activationHasRun(false); } void MainWindow::showLicenseNotice(const QString ¬ice) { @@ -1074,7 +1054,15 @@ void MainWindow::showLicenseNotice(const QString ¬ice) { #endif void MainWindow::updateLocalFingerprint() { - if (m_AppConfig->getCryptoEnabled() && Fingerprint::local().fileExists() && + bool fingerprintExists = false; + try { + fingerprintExists = Fingerprint::local().fileExists(); + } catch (const std::exception &e) { + qDebug() << e.what(); + qFatal("Failed to check if fingerprint exists"); + } + + if (m_AppConfig.cryptoEnabled() && fingerprintExists && m_pRadioGroupServer->isChecked()) { m_pLabelFingerprint->setVisible(true); } else { @@ -1105,7 +1093,7 @@ void MainWindow::on_m_pActionAbout_triggered() { } void MainWindow::on_m_pActionHelp_triggered() { - QDesktopServices::openUrl(QUrl(HELP_URL)); + QDesktopServices::openUrl(QUrl(kHelpUrl)); } void MainWindow::updateWindowTitle() { @@ -1123,12 +1111,12 @@ void MainWindow::updateWindowTitle() { void MainWindow::on_m_pActionSettings_triggered() { auto result = SettingsDialog(this, appConfig()).exec(); if (result == QDialog::Accepted) { - enableServer(appConfig().getServerGroupChecked()); - enableClient(appConfig().getClientGroupChecked()); - auto state = synergyState(); - if ((state == synergyConnected) || (state == synergyConnecting) || - (state == synergyListening)) { - restartSynergy(); + enableServer(appConfig().serverGroupChecked()); + enableClient(appConfig().clientGroupChecked()); + auto state = coreState(); + if ((state == CoreState::Connected) || (state == CoreState::Connecting) || + (state == CoreState::Listening)) { + restartCore(); } } } @@ -1168,10 +1156,10 @@ void MainWindow::showConfigureServer(const QString &message) { auto result = dlg.exec(); if (result == QDialog::Accepted) { - auto state = synergyState(); - if ((state == synergyConnected) || (state == synergyConnecting) || - (state == synergyListening)) { - restartSynergy(); + auto state = coreState(); + if ((state == CoreState::Connected) || (state == CoreState::Connecting) || + (state == CoreState::Listening)) { + restartCore(); } } } @@ -1188,7 +1176,7 @@ void MainWindow::on_m_pActivate_triggered() { void MainWindow::on_m_pButtonApply_clicked() { m_clientConnection.setCheckConnection(true); - restartSynergy(); + restartCore(); } #ifdef SYNERGY_ENABLE_LICENSING @@ -1214,7 +1202,7 @@ int MainWindow::raiseActivationDialog() { void MainWindow::on_windowShown() { #ifdef SYNERGY_ENABLE_LICENSING auto serialKey = m_LicenseManager->serialKey(); - if (!m_AppConfig->activationHasRun() && !serialKey.isValid()) { + if (!m_AppConfig.activationHasRun() && !serialKey.isValid()) { setEdition(Edition::kUnregistered); raiseActivationDialog(); } @@ -1232,7 +1220,7 @@ QString MainWindow::getProfileRootForArg() { dir.replace("/.synergy", ""); #endif - return QString("\"%1\"").arg(dir); + return dir; } void MainWindow::secureSocket(bool secureSocket) { @@ -1254,7 +1242,7 @@ void MainWindow::on_m_pLabelFingerprint_linkActivated(const QString &) { } void MainWindow::windowStateChanged() { - if (windowState() == Qt::WindowMinimized && appConfig().getMinimizeToTray()) + if (windowState() == Qt::WindowMinimized && appConfig().minimizeToTray()) hide(); } @@ -1267,11 +1255,11 @@ void MainWindow::updateScreenName() { } void MainWindow::enableServer(bool enable) { - m_AppConfig->setServerGroupChecked(enable); + m_AppConfig.setServerGroupChecked(enable); m_pRadioGroupServer->setChecked(enable); if (enable) { - if (m_AppConfig->getServerClientMode()) { + if (m_AppConfig.serverClientMode()) { m_pLabelClientIp->show(); m_pLineEditClienIp->show(); m_pButtonConnectToClient->show(); @@ -1296,11 +1284,11 @@ void MainWindow::enableServer(bool enable) { } void MainWindow::enableClient(bool enable) { - m_AppConfig->setClientGroupChecked(enable); + m_AppConfig.setClientGroupChecked(enable); m_pRadioGroupClient->setChecked(enable); if (enable) { - if (m_AppConfig->getClientHostMode()) { + if (m_AppConfig.clientHostMode()) { m_pLabelServerName->hide(); m_pLineEditHostname->hide(); m_pButtonConnect->hide(); @@ -1318,23 +1306,16 @@ void MainWindow::enableClient(bool enable) { } } -void MainWindow::closeEvent(QCloseEvent *event) { -#if defined(Q_OS_LINUX) - QCoreApplication::quit(); -#endif - QWidget::closeEvent(event); -} - void MainWindow::on_m_pRadioGroupServer_clicked(bool) { enableServer(true); enableClient(false); - m_AppConfig->saveSettings(); + m_AppConfig.saveSettings(); } void MainWindow::on_m_pRadioGroupClient_clicked(bool) { enableClient(true); enableServer(false); - m_AppConfig->saveSettings(); + m_AppConfig.saveSettings(); } void MainWindow::on_m_pButtonConnect_clicked() { on_m_pButtonApply_clicked(); } diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index fe808d2fd..0b3e740bb 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -24,13 +24,14 @@ #include #include #include +#include #include "ui_MainWindowBase.h" #include "ActivationDialog.h" #include "AppConfig.h" #include "ClientConnection.h" -#include "ConfigWriter.h" +#include "Config.h" #include "QIpcClient.h" #include "ServerConfig.h" #include "ServerConnection.h" @@ -70,34 +71,35 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase { friend class ClientConnection; public: - enum qSynergyState { - synergyDisconnected, - synergyConnecting, - synergyConnected, - synergyListening, - synergyPendingRetry + enum class CoreState { + Disconnected, + Connecting, + Connected, + Listening, + PendingRetry }; - enum qSynergyType { synergyClient, synergyServer }; + enum class CoreMode { Client, Server }; - enum qLevel { Error, Info }; + enum class LogLevel { Error, Info }; - enum qRuningState { kStarted, kStopped }; + enum class RuningState { Started, Stopped }; public: #ifdef SYNERGY_ENABLE_LICENSING MainWindow(AppConfig &appConfig, LicenseManager &licenseManager); #else - MainWindow(AppConfig &appConfig); + explicit MainWindow(AppConfig &appConfig); #endif - ~MainWindow(); + ~MainWindow() override; public: void setVisible(bool visible); - int synergyType() const { - return m_pRadioGroupClient->isChecked() ? synergyClient : synergyServer; + CoreMode coreMode() const { + auto isClient = m_pRadioGroupClient->isChecked(); + return isClient ? CoreMode::Client : CoreMode::Server; } - int synergyState() const { return m_SynergyState; } + CoreState coreState() const { return m_CoreState; } QString hostname() const { return m_pLineEditHostname->text(); } QString configFilename(); QString address() const; @@ -124,8 +126,8 @@ public slots: void appendLogInfo(const QString &text); void appendLogDebug(const QString &text); void appendLogError(const QString &text); - void startSynergy(); - void retryStart(); // If the connection failed this will retry a startSynergy + void startCore(); + void retryStart(); void actionStart(); void handleIdleService(const QString &text); @@ -140,30 +142,26 @@ protected slots: void on_m_pActionHelp_triggered(); void on_m_pActionSettings_triggered(); void on_m_pActivate_triggered(); - void synergyFinished(int exitCode, QProcess::ExitStatus); + void coreProcessExit(int exitCode, QProcess::ExitStatus); void trayActivated(QSystemTrayIcon::ActivationReason reason); - void stopSynergy(); + void stopCore(); void logOutput(); void logError(); void updateFound(const QString &version); void saveSettings(); protected: - // TODO This should be properly using the ConfigWriter system. - QSettings &settings() { - return GUI::Config::ConfigWriter::make()->settings(); - } - AppConfig &appConfig() { return *m_AppConfig; } - AppConfig const &appConfig() const { return *m_AppConfig; } - QProcess *synergyProcess() { return m_pSynergy; } - void setSynergyProcess(QProcess *p) { m_pSynergy = p; } + QSettings &settings() { return *appConfig().config().currentSettings(); } + AppConfig &appConfig() { return m_AppConfig; } + AppConfig const &appConfig() const { return m_AppConfig; } + AppConfig *appConfigPtr() { return &m_AppConfig; } void initConnections(); void createMenuBar(); void createStatusBar(); void createTrayIcon(); void loadSettings(); - void setIcon(qSynergyState state) const; - void setSynergyState(qSynergyState state); + void setIcon(CoreState state) const; + void setCoreState(CoreState state); bool checkForApp(int which, QString &app); bool clientArgs(QStringList &args, QString &app); bool serverArgs(QStringList &args, QString &app); @@ -175,13 +173,6 @@ protected: void stopDesktop(); void enableServer(bool enable); void enableClient(bool enable); - void closeEvent(QCloseEvent *event) override; - -#if defined(Q_OS_WIN) - bool isServiceRunning(QString name); -#else - bool isServiceRunning(); -#endif QString getProfileRootForArg(); void checkConnected(const QString &line); @@ -194,43 +185,50 @@ protected: void checkLicense(const QString &line); #endif QString getTimeStamp(); - void restartSynergy(); + void restartCore(); - void showEvent(QShowEvent *); + void showEvent(QShowEvent *) override; void secureSocket(bool secureSocket); void windowStateChanged(); private: + void updateWindowTitle(); + #ifdef SYNERGY_ENABLE_LICENSING - LicenseManager *m_LicenseManager; - bool m_ActivationDialogRunning; - QStringList m_PendingClientNames; + LicenseManager *m_LicenseManager = nullptr; #endif - AppConfig *m_AppConfig; - QProcess *m_pSynergy; - int m_SynergyState; + + AppConfig &m_AppConfig; ServerConfig m_ServerConfig; - bool m_AlreadyHidden; - VersionChecker m_VersionChecker; - QIpcClient m_IpcClient; - QMenuBar *m_pMenuBar; - QMenu *m_pMenuFile; - QMenu *m_pMenuEdit; - QMenu *m_pMenuWindow; - QMenu *m_pMenuHelp; - QAbstractButton *m_pCancelButton; - TrayIcon m_trayIcon; - qRuningState m_ExpectedRunningState; - QMutex m_StopDesktopMutex; - bool m_SecureSocket; // brief Is the program running a secure socket protocol - // (SSL/TLS) - QString m_SecureSocketVersion; // brief Contains the version of the Secure - // Socket currently active ServerConnection m_serverConnection; ClientConnection m_clientConnection; + VersionChecker m_VersionChecker; + QIpcClient m_IpcClient; + TrayIcon m_trayIcon; + QMutex m_StopDesktopMutex; - void updateWindowTitle(); +#ifdef SYNERGY_ENABLE_LICENSING + bool m_ActivationDialogRunning = false; + QStringList m_PendingClientNames; +#endif + + RuningState m_ExpectedRunningState = RuningState::Stopped; + std::unique_ptr m_pCoreProcess; + QMenuBar *m_pMenuBar = nullptr; + QMenu *m_pMenuFile = nullptr; + QMenu *m_pMenuEdit = nullptr; + QMenu *m_pMenuWindow = nullptr; + QMenu *m_pMenuHelp = nullptr; + QAbstractButton *m_pCancelButton = nullptr; + CoreState m_CoreState = CoreState::Disconnected; + bool m_AlreadyHidden = false; + + /// @brief Is the program running a secure socket protocol (SSL/TLS) + bool m_SecureSocket = false; + + /// @brief Contains the version of the Secure Socket currently active + QString m_SecureSocketVersion = ""; private slots: void on_m_pButtonApply_clicked(); diff --git a/src/gui/src/MainWindowBase.ui b/src/gui/src/MainWindowBase.ui index de3593ecf..999a175e8 100644 --- a/src/gui/src/MainWindowBase.ui +++ b/src/gui/src/MainWindowBase.ui @@ -6,8 +6,8 @@ 0 0 - 720 - 552 + 750 + 650
@@ -18,8 +18,8 @@ - 720 - 552 + 750 + 550 @@ -357,7 +357,7 @@ background-color: rgba(192,192,192, 0.1); - &Configure server + &Configure
@@ -692,7 +692,6 @@ background-color: rgba(192,192,192, 0.1); Arial -1 - 88 true
@@ -789,7 +788,7 @@ top: 3px; Ctrl+Q - + &Start @@ -797,10 +796,10 @@ top: 3px; Run - Ctrl+S + Ctrl+S - + false @@ -811,7 +810,7 @@ top: 3px; Stop - Ctrl+T + Ctrl+T @@ -894,7 +893,7 @@ top: 3px; m_pButtonToggleStart clicked() - m_pActionStartSynergy + m_pActionStartCore trigger() diff --git a/src/gui/src/QSynergyApplication.cpp b/src/gui/src/QSynergyApplication.cpp index 8692971da..ce6a6bc30 100644 --- a/src/gui/src/QSynergyApplication.cpp +++ b/src/gui/src/QSynergyApplication.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) * * This package is free software; you can redistribute it and/or @@ -17,32 +17,31 @@ */ #include "QSynergyApplication.h" + #include "MainWindow.h" #include #include -QSynergyApplication *QSynergyApplication::s_Instance = NULL; - QSynergyApplication::QSynergyApplication(int &argc, char **argv) : QApplication(argc, argv) { - s_Instance = this; QFontDatabase::addApplicationFont(":/res/fonts/Arial.ttf"); QFont Arial("Arial"); Arial.setPixelSize(13); Arial.setStyleHint(QFont::SansSerif); setFont(Arial); + + // Setting the style to 'Fusion' seems to fix issues such as text being + // rendered as black on black. This may not be the style we want long-term + // but it does fix the style issues for now. + setStyle("Fusion"); } -QSynergyApplication::~QSynergyApplication() {} - -void QSynergyApplication::commitData(QSessionManager &) { +void QSynergyApplication::commitData(const QSessionManager &) const { foreach (QWidget *widget, topLevelWidgets()) { MainWindow *mainWindow = qobject_cast(widget); if (mainWindow) mainWindow->saveSettings(); } } - -QSynergyApplication *QSynergyApplication::getInstance() { return s_Instance; } diff --git a/src/gui/src/QSynergyApplication.h b/src/gui/src/QSynergyApplication.h index c483e5878..be4a47dea 100644 --- a/src/gui/src/QSynergyApplication.h +++ b/src/gui/src/QSynergyApplication.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) * * This package is free software; you can redistribute it and/or @@ -16,9 +16,7 @@ * along with this program. If not, see . */ -#if !defined(QSYNERGYAPPLICATION__H) - -#define QSYNERGYAPPLICATION__H +#pragma once #include @@ -27,15 +25,7 @@ class QSessionManager; class QSynergyApplication : public QApplication { public: QSynergyApplication(int &argc, char **argv); - ~QSynergyApplication(); + ~QSynergyApplication() override = default; -public: - void commitData(QSessionManager &manager); - - static QSynergyApplication *getInstance(); - -private: - static QSynergyApplication *s_Instance; + void commitData(const QSessionManager &manager) const; }; - -#endif diff --git a/src/gui/src/Screen.h b/src/gui/src/Screen.h index f8a6221ca..4b2253506 100644 --- a/src/gui/src/Screen.h +++ b/src/gui/src/Screen.h @@ -16,23 +16,20 @@ * along with this program. If not, see . */ -#if !defined(SCREEN__H) +#pragma once -#define SCREEN__H +#include "ScreenConfig.h" #include #include #include #include -#include "BaseConfig.h" - class QSettings; class QTextStream; - class ScreenSettingsDialog; -class Screen : public BaseConfig { +class Screen : public ScreenConfig { friend QDataStream &operator<<(QDataStream &outStream, const Screen &screen); friend QDataStream &operator>>(QDataStream &inStream, Screen &screen); friend class ScreenSettingsDialog; @@ -102,5 +99,3 @@ private: QDataStream &operator<<(QDataStream &outStream, const Screen &screen); QDataStream &operator>>(QDataStream &inStream, Screen &screen); - -#endif diff --git a/src/gui/src/BaseConfig.cpp b/src/gui/src/ScreenConfig.cpp similarity index 77% rename from src/gui/src/BaseConfig.cpp rename to src/gui/src/ScreenConfig.cpp index c7dab36e7..55042a76f 100644 --- a/src/gui/src/BaseConfig.cpp +++ b/src/gui/src/ScreenConfig.cpp @@ -16,14 +16,14 @@ * along with this program. If not, see . */ -#include "BaseConfig.h" +#include "ScreenConfig.h" -const char *BaseConfig::m_ModifierNames[] = {"shift", "ctrl", "alt", - "meta", "super", "none"}; +const char *ScreenConfig::m_ModifierNames[] = {"shift", "ctrl", "alt", + "meta", "super", "none"}; -const char *BaseConfig::m_FixNames[] = { +const char *ScreenConfig::m_FixNames[] = { "halfDuplexCapsLock", "halfDuplexNumLock", "halfDuplexScrollLock", "xtestIsXineramaUnaware"}; -const char *BaseConfig::m_SwitchCornerNames[] = { +const char *ScreenConfig::m_SwitchCornerNames[] = { "top-left", "top-right", "bottom-left", "bottom-right"}; diff --git a/src/gui/src/BaseConfig.h b/src/gui/src/ScreenConfig.h similarity index 93% rename from src/gui/src/BaseConfig.h rename to src/gui/src/ScreenConfig.h index becfd623e..61e78b037 100644 --- a/src/gui/src/BaseConfig.h +++ b/src/gui/src/ScreenConfig.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) * * This package is free software; you can redistribute it and/or @@ -16,15 +16,14 @@ * along with this program. If not, see . */ -#if !defined(BASECONFIG_H) - -#define BASECONFIG_H +#pragma once #include #include #include -class BaseConfig { +/// @brief Screen configuration base class +class ScreenConfig { public: enum Modifier { DefaultMod = -1, @@ -46,8 +45,8 @@ public: enum Fix { CapsLock, NumLock, ScrollLock, XTest, NumFixes }; protected: - BaseConfig() {} - virtual ~BaseConfig() {} + explicit ScreenConfig() = default; + ~ScreenConfig() = default; protected: template @@ -101,5 +100,3 @@ private: static const char *m_FixNames[]; static const char *m_SwitchCornerNames[]; }; - -#endif diff --git a/src/gui/src/ServerConfig.cpp b/src/gui/src/ServerConfig.cpp index e8f633823..08264c5d1 100644 --- a/src/gui/src/ServerConfig.cpp +++ b/src/gui/src/ServerConfig.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) * * This package is free software; you can redistribute it and/or @@ -17,7 +17,9 @@ */ #include "ServerConfig.h" + #include "AddClientDialog.h" +#include "Config.h" #include "Hotkey.h" #include "MainWindow.h" @@ -26,6 +28,8 @@ #include #include +using synergy::gui::Config; + static const struct { int x; int y; @@ -42,25 +46,20 @@ const int serverDefaultIndex = 7; ServerConfig::ServerConfig( int numColumns, int numRows, AppConfig *appConfig, MainWindow *mainWindow) - : - + : m_pAppConfig(appConfig), + m_pMainWindow(mainWindow), m_Screens(numColumns), m_NumColumns(numColumns), m_NumRows(numRows), - m_pAppConfig(appConfig), - m_EnableDragAndDrop(false), - m_DisableLockToScreen(false), - m_ClipboardSharing(true), - m_ClipboardSharingSize(defaultClipboardSharingSize()), - m_pMainWindow(mainWindow) { - GUI::Config::ConfigWriter::make()->registerClass(this); - ServerConfig::loadSettings(); + m_ClipboardSharingSize(defaultClipboardSharingSize()) { + appConfig->config().registerReceiever(this); } ServerConfig::~ServerConfig() { try { ServerConfig::saveSettings(); } catch (const std::exception &e) { + qDebug() << e.what(); m_pMainWindow->appendLogError(e.what()); } } @@ -165,8 +164,7 @@ void ServerConfig::saveSettings() { settings().endGroup(); m_pAppConfig->saveSettings(); - // Tell the config writer there are changes - GUI::Config::ConfigWriter::make()->markUnsaved(); + m_pAppConfig->config().markUnsaved(); } void ServerConfig::loadSettings() { @@ -422,12 +420,12 @@ void ServerConfig::updateServerName() { } } -const QString &ServerConfig::getConfigFile() const { - return m_pAppConfig->getConfigFile(); +const QString &ServerConfig::configFile() const { + return m_pAppConfig->configFile(); } -bool ServerConfig::getUseExternalConfig() const { - return m_pAppConfig->getUseExternalConfig(); +bool ServerConfig::useExternalConfig() const { + return m_pAppConfig->useExternalConfig(); } bool ServerConfig::isFull() const { @@ -543,25 +541,23 @@ size_t ServerConfig::setClipboardSharingSize(size_t size) { } void ServerConfig::setClientAddress(const QString &address) { - if (m_pAppConfig->getServerClientMode()) { - m_clientAddress = address; + if (m_pAppConfig->serverClientMode()) { + m_ClientAddress = address; } } QString ServerConfig::getClientAddress() const { QString clientAddress; - if (m_pAppConfig->getServerClientMode()) { - clientAddress = m_clientAddress.trimmed(); + if (m_pAppConfig->serverClientMode()) { + clientAddress = m_ClientAddress.trimmed(); } return clientAddress; } QSettings &ServerConfig::settings() { - using GUI::Config::ConfigWriter; - - return ConfigWriter::make()->settings(); + return *m_pAppConfig->config().currentSettings(); } bool ServerConfig::isHotkeysAvailable() const { diff --git a/src/gui/src/ServerConfig.h b/src/gui/src/ServerConfig.h index 7b3610434..d0e13655b 100644 --- a/src/gui/src/ServerConfig.h +++ b/src/gui/src/ServerConfig.h @@ -16,15 +16,13 @@ * along with this program. If not, see . */ -#if !defined(SERVERCONFIG__H) - -#define SERVERCONFIG__H +#pragma once #include -#include "BaseConfig.h" -#include "ConfigBase.h" +#include "CommonConfig.h" #include "Hotkey.h" +#include "ScreenConfig.h" #include "ScreenList.h" class QTextStream; @@ -35,7 +33,7 @@ class ServerConfigDialog; class MainWindow; class AppConfig; -class ServerConfig : public BaseConfig, public GUI::Config::ConfigBase { +class ServerConfig : public ScreenConfig, public synergy::gui::CommonConfig { friend class ServerConfigDialog; friend class ServerConnection; friend QTextStream & @@ -45,16 +43,10 @@ public: ServerConfig( int numColumns, int numRows, AppConfig *appConfig, MainWindow *mainWindow); - - ServerConfig(const ServerConfig &src) = default; - ServerConfig(ServerConfig &&) = default; - ~ServerConfig(); - ServerConfig &operator=(const ServerConfig &src) = default; - ServerConfig &operator=(ServerConfig &&) = delete; + ~ServerConfig() override; bool operator==(const ServerConfig &sc) const; -public: const ScreenList &screens() const { return m_Screens; } int numColumns() const { return m_NumColumns; } int numRows() const { return m_NumRows; } @@ -84,8 +76,8 @@ public: int autoAddScreen(const QString name); const QString &getServerName() const; void updateServerName(); - const QString &getConfigFile() const; - bool getUseExternalConfig() const; + const QString &configFile() const; + bool useExternalConfig() const; bool isFull() const; bool isScreenExists(const QString &screenName) const; void addClient(const QString &clientName); @@ -129,27 +121,28 @@ private: void addToFirstEmptyGrid(const QString &clientName); private: + bool m_HasHeartbeat = false; + int m_Heartbeat = 0; + bool m_RelativeMouseMoves = false; + bool m_Win32KeepForeground = false; + bool m_HasSwitchDelay = false; + int m_SwitchDelay = 0; + bool m_HasSwitchDoubleTap = false; + int m_SwitchDoubleTap = 0; + int m_SwitchCornerSize = 0; + bool m_EnableDragAndDrop = false; + bool m_DisableLockToScreen = false; + bool m_ClipboardSharing = true; + QString m_ClientAddress = ""; + QList m_SwitchCorners; + HotkeyList m_Hotkeys; + + AppConfig *m_pAppConfig; + MainWindow *m_pMainWindow; ScreenList m_Screens; int m_NumColumns; int m_NumRows; - bool m_HasHeartbeat; - int m_Heartbeat; - bool m_RelativeMouseMoves; - bool m_Win32KeepForeground; - bool m_HasSwitchDelay; - int m_SwitchDelay; - bool m_HasSwitchDoubleTap; - int m_SwitchDoubleTap; - int m_SwitchCornerSize; - QList m_SwitchCorners; - HotkeyList m_Hotkeys; - AppConfig *m_pAppConfig; - bool m_EnableDragAndDrop; - bool m_DisableLockToScreen; - bool m_ClipboardSharing; size_t m_ClipboardSharingSize; - QString m_clientAddress; - MainWindow *m_pMainWindow; }; QTextStream &operator<<(QTextStream &outStream, const ServerConfig &config); @@ -160,5 +153,3 @@ enum { kAutoAddScreenManualClient, kAutoAddScreenIgnore }; - -#endif diff --git a/src/gui/src/ServerConfigDialog.cpp b/src/gui/src/ServerConfigDialog.cpp index edaee081b..3b49881da 100644 --- a/src/gui/src/ServerConfigDialog.cpp +++ b/src/gui/src/ServerConfigDialog.cpp @@ -33,9 +33,9 @@ ServerConfigDialog::ServerConfigDialog( QWidget *parent, ServerConfig &config, AppConfig &appConfig) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), Ui::ServerConfigDialogBase(), - m_OrigServerConfig(config), - m_OrigServerAppConfigUseExternalConfig(config.getUseExternalConfig()), - m_OrigServerAppConfigExternalConfigFile(config.getConfigFile()), + m_OriginalServerConfig(config), + m_OriginalServerConfigIsExternal(config.useExternalConfig()), + m_OriginalServerConfigUsesExternalFile(config.configFile()), m_ServerConfig(config), m_ScreenSetupModel( serverConfig().screens(), serverConfig().numColumns(), @@ -44,9 +44,8 @@ ServerConfigDialog::ServerConfigDialog( m_appConfig(appConfig) { setupUi(this); - m_pEditConfigFile->setText(serverConfig().getConfigFile()); - m_pCheckBoxUseExternalConfig->setChecked( - serverConfig().getUseExternalConfig()); + m_pEditConfigFile->setText(serverConfig().configFile()); + m_pCheckBoxUseExternalConfig->setChecked(serverConfig().useExternalConfig()); m_pCheckBoxHeartbeat->setChecked(serverConfig().hasHeartbeat()); m_pSpinBoxHeartbeat->setValue(serverConfig().heartbeat()); @@ -62,13 +61,13 @@ ServerConfigDialog::ServerConfigDialog( m_pSpinBoxSwitchDoubleTap->setValue(serverConfig().switchDoubleTap()); m_pCheckBoxCornerTopLeft->setChecked( - serverConfig().switchCorner(BaseConfig::TopLeft)); + serverConfig().switchCorner(ScreenConfig::TopLeft)); m_pCheckBoxCornerTopRight->setChecked( - serverConfig().switchCorner(BaseConfig::TopRight)); + serverConfig().switchCorner(ScreenConfig::TopRight)); m_pCheckBoxCornerBottomLeft->setChecked( - serverConfig().switchCorner(BaseConfig::BottomLeft)); + serverConfig().switchCorner(ScreenConfig::BottomLeft)); m_pCheckBoxCornerBottomRight->setChecked( - serverConfig().switchCorner(BaseConfig::BottomRight)); + serverConfig().switchCorner(ScreenConfig::BottomRight)); m_pSpinBoxSwitchCornerSize->setValue(serverConfig().switchCornerSize()); m_pCheckBoxDisableLockToScreen->setChecked( serverConfig().disableLockToScreen()); @@ -186,25 +185,25 @@ ServerConfigDialog::ServerConfigDialog( connect( m_pCheckBoxCornerTopLeft, &QCheckBox::stateChanged, this, [this](const int &v) { - serverConfig().setSwitchCorner(BaseConfig::TopLeft, v); + serverConfig().setSwitchCorner(ScreenConfig::TopLeft, v); onChange(); }); connect( m_pCheckBoxCornerTopRight, &QCheckBox::stateChanged, this, [this](const int &v) { - serverConfig().setSwitchCorner(BaseConfig::TopRight, v); + serverConfig().setSwitchCorner(ScreenConfig::TopRight, v); onChange(); }); connect( m_pCheckBoxCornerBottomLeft, &QCheckBox::stateChanged, this, [this](const int &v) { - serverConfig().setSwitchCorner(BaseConfig::BottomLeft, v); + serverConfig().setSwitchCorner(ScreenConfig::BottomLeft, v); onChange(); }); connect( m_pCheckBoxCornerBottomRight, &QCheckBox::stateChanged, this, [this](const int &v) { - serverConfig().setSwitchCorner(BaseConfig::BottomRight, v); + serverConfig().setSwitchCorner(ScreenConfig::BottomRight, v); onChange(); }); connect( @@ -261,14 +260,14 @@ void ServerConfigDialog::accept() { // now that the dialog has been accepted, copy the new server config to the // original one, which is a reference to the one in MainWindow. - setOrigServerConfig(serverConfig()); + setOriginalServerConfig(serverConfig()); QDialog::accept(); } void ServerConfigDialog::reject() { - serverConfig().setUseExternalConfig(m_OrigServerAppConfigUseExternalConfig); - serverConfig().setConfigFile(m_OrigServerAppConfigExternalConfigFile); + serverConfig().setUseExternalConfig(m_OriginalServerConfigIsExternal); + serverConfig().setConfigFile(m_OriginalServerConfigUsesExternalFile); QDialog::reject(); } @@ -460,10 +459,9 @@ bool ServerConfigDialog::addComputer(const QString &clientName, bool doSilent) { void ServerConfigDialog::onChange() { bool isAppConfigDataEqual = - m_OrigServerAppConfigUseExternalConfig == - serverConfig().getUseExternalConfig() && - m_OrigServerAppConfigExternalConfigFile == serverConfig().getConfigFile(); + m_OriginalServerConfigIsExternal == serverConfig().useExternalConfig() && + m_OriginalServerConfigUsesExternalFile == serverConfig().configFile(); m_pButtonBox->button(QDialogButtonBox::Ok) ->setEnabled( - !isAppConfigDataEqual || !(m_OrigServerConfig == m_ServerConfig)); + !isAppConfigDataEqual || !(m_OriginalServerConfig == m_ServerConfig)); } diff --git a/src/gui/src/ServerConfigDialog.h b/src/gui/src/ServerConfigDialog.h index 31722f753..3fbf7f8d7 100644 --- a/src/gui/src/ServerConfigDialog.h +++ b/src/gui/src/ServerConfigDialog.h @@ -61,15 +61,17 @@ protected slots: protected: bool addComputer(const QString &clientName, bool doSilent); ServerConfig &serverConfig() { return m_ServerConfig; } - void setOrigServerConfig(const ServerConfig &s) { m_OrigServerConfig = s; } + void setOriginalServerConfig(const ServerConfig &s) { + m_OriginalServerConfig = s; + } ScreenSetupModel &model() { return m_ScreenSetupModel; } AppConfig &appConfig() { return m_appConfig; } private: - ServerConfig &m_OrigServerConfig; - bool m_OrigServerAppConfigUseExternalConfig; - QString m_OrigServerAppConfigExternalConfigFile; + ServerConfig &m_OriginalServerConfig; ServerConfig m_ServerConfig; + bool m_OriginalServerConfigIsExternal; + QString m_OriginalServerConfigUsesExternalFile; ScreenSetupModel m_ScreenSetupModel; QString m_Message; AppConfig &m_appConfig; diff --git a/src/gui/src/ServerConnection.cpp b/src/gui/src/ServerConnection.cpp index 2528a2b25..a5359667d 100644 --- a/src/gui/src/ServerConnection.cpp +++ b/src/gui/src/ServerConnection.cpp @@ -30,7 +30,7 @@ ServerConnection::ServerConnection(MainWindow &parent) : m_parent(parent) {} void ServerConnection::update(const QString &line) { ServerMessage message(line); - if (!m_parent.appConfig().getUseExternalConfig() && + if (!m_parent.appConfig().useExternalConfig() && message.isNewClientMessage() && !m_ignoredClients.contains(message.getClientName())) { addClient(message.getClientName()); @@ -73,6 +73,6 @@ void ServerConnection::configureClient(const QString &clientName) { &m_parent, m_parent.serverConfig(), m_parent.appConfig()); if (dlg.addClient(clientName) && dlg.exec() == QDialog::Accepted) { - m_parent.restartSynergy(); + m_parent.restartCore(); } } diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp index f28117bde..6e233f914 100644 --- a/src/gui/src/SettingsDialog.cpp +++ b/src/gui/src/SettingsDialog.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) * * This package is free software; you can redistribute it and/or @@ -15,14 +15,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + #include "SettingsDialog.h" -#include "validators/ScreenNameValidator.h" #include "AppConfig.h" -#include "CoreInterface.h" #include "MainWindow.h" -#include "QSynergyApplication.h" -#include "QUtility.h" #include "SslCertificate.h" #include "UpgradeDialog.h" @@ -31,6 +28,7 @@ #include #include #include +#include SettingsDialog::SettingsDialog(QWidget *parent, AppConfig &config) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), @@ -43,15 +41,15 @@ SettingsDialog::SettingsDialog(QWidget *parent, AppConfig &config) loadFromConfig(); m_isSystemAtStart = appConfig().isSystemScoped(); - buttonBox->button(QDialogButtonBox::Save)->setEnabled(false); - enableControls(appConfig().isWritable()); - - m_pCheckBoxLanguageSync->setEnabled(isClientMode()); - m_pCheckBoxScrollDirection->setEnabled(isClientMode()); + updateControlsEnabled(); const auto &serveConfig = m_pMainWindow->serverConfig(); - m_pLineEditScreenName->setValidator(new validators::ScreenNameValidator( - m_pLineEditScreenName, m_pLabelNameError, (&serveConfig.screens()))); + m_screenNameValidator = std::make_unique( + m_pLineEditScreenName, nullptr, (&serveConfig.screens())); + connect( + m_screenNameValidator.get(), SIGNAL(finished(QString)), this, + SLOT(on_m_pScreenNameValidator_finished(QString))); + m_pLineEditScreenName->setValidator(m_screenNameValidator.get()); connect( m_pLineEditLogFilename, SIGNAL(textChanged(QString)), this, @@ -86,6 +84,11 @@ SettingsDialog::SettingsDialog(QWidget *parent, AppConfig &config) } void SettingsDialog::accept() { + if (!m_nameError.isEmpty()) { + QMessageBox::warning(this, tr("Invalid screen name"), m_nameError); + return; + } + appConfig().setLoadFromSystemScope(m_pRadioSystemScope->isChecked()); appConfig().setScreenName(m_pLineEditScreenName->text()); appConfig().setPort(m_pSpinBoxPort->value()); @@ -98,13 +101,15 @@ void SettingsDialog::accept() { appConfig().setAutoHide(m_pCheckBoxAutoHide->isChecked()); appConfig().setPreventSleep(m_pCheckBoxPreventSleep->isChecked()); appConfig().setMinimizeToTray(m_pCheckBoxMinimizeToTray->isChecked()); - appConfig().setTLSCertPath(m_pLineEditCertificatePath->text()); - appConfig().setTLSKeyLength(m_pComboBoxKeyLength->currentText()); + appConfig().setTlsCertPath(m_pLineEditCertificatePath->text()); + appConfig().setTlsKeyLength(m_pComboBoxKeyLength->currentText()); appConfig().setCryptoEnabled(m_pCheckBoxEnableCrypto->isChecked()); appConfig().setLanguageSync(m_pCheckBoxLanguageSync->isChecked()); appConfig().setInvertScrollDirection(m_pCheckBoxScrollDirection->isChecked()); appConfig().setClientHostMode(m_pCheckBoxClientHostMode->isChecked()); appConfig().setServerClientMode(m_pCheckBoxServerClientMode->isChecked()); + appConfig().setServiceEnabled(m_pCheckBoxServiceEnabled->isChecked()); + appConfig().setMinimizeOnClose(m_pCheckBoxMinimizeOnClose->isChecked()); appConfig().saveSettings(); QDialog::accept(); @@ -127,18 +132,17 @@ void SettingsDialog::loadFromConfig() { m_pComboLogLevel->setCurrentIndex(appConfig().logLevel()); m_pCheckBoxLogToFile->setChecked(appConfig().logToFile()); m_pLineEditLogFilename->setText(appConfig().logFilename()); - m_pCheckBoxAutoHide->setChecked(appConfig().getAutoHide()); - m_pCheckBoxPreventSleep->setChecked(appConfig().getPreventSleep()); - m_pCheckBoxMinimizeToTray->setChecked(appConfig().getMinimizeToTray()); - m_pLineEditCertificatePath->setText(appConfig().getTLSCertPath()); - m_pCheckBoxEnableCrypto->setChecked(m_appConfig.getCryptoEnabled()); - m_pCheckBoxLanguageSync->setChecked(m_appConfig.getLanguageSync()); - m_pCheckBoxScrollDirection->setChecked( - m_appConfig.getInvertScrollDirection()); - m_pCheckBoxClientHostMode->setChecked(m_appConfig.getClientHostMode()); - m_pCheckBoxServerClientMode->setChecked(m_appConfig.getServerClientMode()); - - setupSeurity(); + m_pCheckBoxAutoHide->setChecked(appConfig().autoHide()); + m_pCheckBoxPreventSleep->setChecked(appConfig().preventSleep()); + m_pCheckBoxMinimizeToTray->setChecked(appConfig().minimizeToTray()); + m_pLineEditCertificatePath->setText(appConfig().tlsCertPath()); + m_pCheckBoxEnableCrypto->setChecked(m_appConfig.cryptoEnabled()); + m_pCheckBoxLanguageSync->setChecked(m_appConfig.languageSync()); + m_pCheckBoxScrollDirection->setChecked(m_appConfig.invertScrollDirection()); + m_pCheckBoxClientHostMode->setChecked(m_appConfig.clientHostMode()); + m_pCheckBoxServerClientMode->setChecked(m_appConfig.serverClientMode()); + m_pCheckBoxServiceEnabled->setChecked(m_appConfig.serviceEnabled()); + m_pCheckBoxMinimizeOnClose->setChecked(m_appConfig.minimizeOnClose()); if (m_appConfig.isSystemScoped()) { m_pRadioSystemScope->setChecked(true); @@ -146,44 +150,42 @@ void SettingsDialog::loadFromConfig() { m_pRadioUserScope->setChecked(true); } -#if defined(Q_OS_WIN) - m_pComboElevate->setCurrentIndex(static_cast(appConfig().elevateMode())); - -#else - // elevate checkbox is only useful on ms windows. - m_pLabelElevate->hide(); - m_pComboElevate->hide(); -#endif - - m_pCheckBoxClientHostMode->setVisible( - isClientMode() && appConfig().getInitiateConnectionFromServer()); - m_pCheckBoxServerClientMode->setVisible( - !isClientMode() && appConfig().getInitiateConnectionFromServer()); + updateTlsControls(); } -void SettingsDialog::setupSeurity() { - // If the tls file exists test its key length - if (QFile(appConfig().getTLSCertPath()).exists()) { - updateKeyLengthOnFile(appConfig().getTLSCertPath()); +void SettingsDialog::updateTlsControls() { + if (QFile(appConfig().tlsCertPath()).exists()) { + updateKeyLengthOnFile(appConfig().tlsCertPath()); } else { m_pComboBoxKeyLength->setCurrentIndex( - m_pComboBoxKeyLength->findText(appConfig().getTLSKeyLength())); + m_pComboBoxKeyLength->findText(appConfig().tlsKeyLength())); } - m_pCheckBoxEnableCrypto->setChecked(m_appConfig.getCryptoEnabled()); + m_pCheckBoxEnableCrypto->setChecked(m_appConfig.cryptoEnabled()); - if (appConfig().getClientGroupChecked()) { - m_pLabelKeyLength->hide(); - m_pComboBoxKeyLength->hide(); - m_pLabelCertificate->hide(); - m_pLineEditCertificatePath->hide(); - m_pPushButtonBrowseCert->hide(); - m_pPushButtonRegenCert->hide(); - } + updateTlsControlsEnabled(); +} + +void SettingsDialog::updateTlsControlsEnabled() { + auto clientMode = appConfig().clientGroupChecked(); + auto cryptoAvailable = appConfig().cryptoAvailable(); + auto tlsChecked = m_pCheckBoxEnableCrypto->isChecked(); + auto enabled = !clientMode && cryptoAvailable && tlsChecked; + + qDebug( + "TLS controls enabled=%d, client=%d, crypto=%d, checked=%d", enabled, + clientMode, cryptoAvailable, tlsChecked); + + m_pLabelKeyLength->setEnabled(enabled); + m_pComboBoxKeyLength->setEnabled(enabled); + m_pLabelCertificate->setEnabled(enabled); + m_pLineEditCertificatePath->setEnabled(enabled); + m_pPushButtonBrowseCert->setEnabled(enabled); + m_pPushButtonRegenCert->setEnabled(enabled); } bool SettingsDialog::isClientMode() const { - return (m_pMainWindow->synergyType() == MainWindow::synergyClient); + return (m_pMainWindow->coreMode() == MainWindow::CoreMode::Client); } void SettingsDialog::on_m_pCheckBoxLogToFile_stateChanged(int i) { @@ -192,7 +194,6 @@ void SettingsDialog::on_m_pCheckBoxLogToFile_stateChanged(int i) { m_pLabelLogPath->setEnabled(checked); m_pLineEditLogFilename->setEnabled(checked); m_pButtonBrowseLog->setEnabled(checked); - buttonBox->button(QDialogButtonBox::Save)->setEnabled(isModified()); } void SettingsDialog::on_m_pButtonBrowseLog_clicked() { @@ -206,17 +207,9 @@ void SettingsDialog::on_m_pButtonBrowseLog_clicked() { } void SettingsDialog::on_m_pCheckBoxEnableCrypto_clicked(bool checked) { - if (appConfig().isCryptoAvailable()) { - m_pLabelKeyLength->setEnabled(checked); - m_pComboBoxKeyLength->setEnabled(checked); - m_pLabelCertificate->setEnabled(checked); - m_pLineEditCertificatePath->setEnabled(checked); - m_pPushButtonBrowseCert->setEnabled(checked); - m_pPushButtonRegenCert->setEnabled(checked); + updateTlsControlsEnabled(); - buttonBox->button(QDialogButtonBox::Save)->setEnabled(isModified()); - } else { - m_pCheckBoxEnableCrypto->setChecked(false); + if (!appConfig().cryptoAvailable()) { #ifdef SYNERGY_ENABLE_LICENSING auto edition = appConfig().edition(); @@ -238,9 +231,7 @@ void SettingsDialog::on_m_pRadioSystemScope_toggled(bool checked) { // We only need to test the System scoped Radio as they are connected appConfig().setLoadFromSystemScope(checked); loadFromConfig(); - buttonBox->button(QDialogButtonBox::Save) - ->setEnabled(m_isSystemAtStart != checked); - enableControls(appConfig().isWritable()); + updateControlsEnabled(); } void SettingsDialog::on_m_pPushButtonBrowseCert_clicked() { @@ -252,24 +243,23 @@ void SettingsDialog::on_m_pPushButtonBrowseCert_clicked() { if (!fileName.isEmpty()) { m_pLineEditCertificatePath->setText(fileName); // If the tls file exists test its key length and update - if (QFile(appConfig().getTLSCertPath()).exists()) { + if (QFile(appConfig().tlsCertPath()).exists()) { updateKeyLengthOnFile(fileName); } } - updateRegenButton(); + updateTlsRegenerateButton(); } void SettingsDialog::on_m_pComboBoxKeyLength_currentIndexChanged(int index) { - buttonBox->button(QDialogButtonBox::Save)->setEnabled(isModified()); - updateRegenButton(); + updateTlsRegenerateButton(); } -void SettingsDialog::updateRegenButton() { +void SettingsDialog::updateTlsRegenerateButton() { // Disable the Regenerate cert button if the key length is different to saved auto keyChanged = - appConfig().getTLSKeyLength() != m_pComboBoxKeyLength->currentText(); + appConfig().tlsKeyLength() != m_pComboBoxKeyLength->currentText(); auto pathChanged = - appConfig().getTLSCertPath() != m_pLineEditCertificatePath->text(); + appConfig().tlsCertPath() != m_pLineEditCertificatePath->text(); // NOR the above bools, if any have changed regen should be disabled as it // will be done on save auto nor = !(keyChanged || pathChanged); @@ -287,82 +277,65 @@ void SettingsDialog::updateKeyLengthOnFile(const QString &path) { auto index = m_pComboBoxKeyLength->findText(length); m_pComboBoxKeyLength->setCurrentIndex(index); // Also update what is in the appconfig to match the file itself - appConfig().setTLSKeyLength(length); + appConfig().setTlsKeyLength(length); } -bool SettingsDialog::isModified() { - return ( - !m_pLineEditScreenName->text().isEmpty() && - m_pLabelNameError->text().isEmpty() && - (appConfig().screenName() != m_pLineEditScreenName->text() || - appConfig().port() != m_pSpinBoxPort->value() || - appConfig().networkInterface() != m_pLineEditInterface->text() || - appConfig().logLevel() != m_pComboLogLevel->currentIndex() || - appConfig().logToFile() != m_pCheckBoxLogToFile->isChecked() || - appConfig().logFilename() != m_pLineEditLogFilename->text() || - appConfig().elevateMode() != - static_cast(m_pComboElevate->currentIndex()) || - appConfig().getAutoHide() != m_pCheckBoxAutoHide->isChecked() || - appConfig().getPreventSleep() != m_pCheckBoxPreventSleep->isChecked() || - appConfig().getMinimizeToTray() != - m_pCheckBoxMinimizeToTray->isChecked() || - appConfig().getTLSCertPath() != m_pLineEditCertificatePath->text() || - appConfig().getTLSKeyLength() != m_pComboBoxKeyLength->currentText() || - appConfig().getCryptoEnabled() != m_pCheckBoxEnableCrypto->isChecked() || - appConfig().isSystemScoped() != m_isSystemAtStart || - appConfig().getLanguageSync() != m_pCheckBoxLanguageSync->isChecked() || - appConfig().getClientHostMode() != - m_pCheckBoxClientHostMode->isChecked() || - appConfig().getServerClientMode() != - m_pCheckBoxServerClientMode->isChecked() || - appConfig().getInvertScrollDirection() != - m_pCheckBoxScrollDirection->isChecked())); -} +void SettingsDialog::updateControlsEnabled() { + bool writable = appConfig().isWritable(); -void SettingsDialog::enableControls(bool enable) { - m_pLineEditScreenName->setEnabled(enable); - m_pSpinBoxPort->setEnabled(enable); - m_pLineEditInterface->setEnabled(enable); - m_pComboLogLevel->setEnabled(enable); - m_pCheckBoxLogToFile->setEnabled(enable); - m_pComboElevate->setEnabled(enable); - m_pCheckBoxAutoHide->setEnabled(enable); - m_pCheckBoxPreventSleep->setEnabled(enable); - m_pCheckBoxMinimizeToTray->setEnabled(enable); - m_pLineEditCertificatePath->setEnabled(enable); - m_pComboBoxKeyLength->setEnabled(enable); - m_pPushButtonBrowseCert->setEnabled(enable); - m_pCheckBoxEnableCrypto->setEnabled(enable); - m_labelAdminRightsMessage->setVisible(!enable); - m_pCheckBoxLanguageSync->setEnabled(enable); - m_pCheckBoxScrollDirection->setEnabled(enable); - m_pCheckBoxClientHostMode->setEnabled(enable); - m_pCheckBoxServerClientMode->setEnabled(enable); + m_pLineEditScreenName->setEnabled(writable); + m_pSpinBoxPort->setEnabled(writable); + m_pLineEditInterface->setEnabled(writable); + m_pComboLogLevel->setEnabled(writable); + m_pCheckBoxLogToFile->setEnabled(writable); + m_pComboElevate->setEnabled(writable); + m_pCheckBoxAutoHide->setEnabled(writable); + m_pCheckBoxPreventSleep->setEnabled(writable); + m_pCheckBoxMinimizeToTray->setEnabled(writable); + m_pLineEditCertificatePath->setEnabled(writable); + m_pComboBoxKeyLength->setEnabled(writable); + m_pPushButtonBrowseCert->setEnabled(writable); + m_pCheckBoxEnableCrypto->setEnabled(writable); + m_pCheckBoxClientHostMode->setEnabled(writable); + m_pCheckBoxServerClientMode->setEnabled(writable); + m_pCheckBoxServiceEnabled->setEnabled(writable); + m_pCheckBoxMinimizeOnClose->setEnabled(writable); - if (enable) { - m_pLabelLogPath->setEnabled(m_pCheckBoxLogToFile->isChecked()); - m_pLineEditLogFilename->setEnabled(m_pCheckBoxLogToFile->isChecked()); - m_pButtonBrowseLog->setEnabled(m_pCheckBoxLogToFile->isChecked()); - m_pLabelKeyLength->setEnabled(m_pCheckBoxEnableCrypto->isChecked()); - m_pComboBoxKeyLength->setEnabled(m_pCheckBoxEnableCrypto->isChecked()); - m_pLabelCertificate->setEnabled(m_pCheckBoxEnableCrypto->isChecked()); - m_pLineEditCertificatePath->setEnabled( - m_pCheckBoxEnableCrypto->isChecked()); - m_pPushButtonBrowseCert->setEnabled(m_pCheckBoxEnableCrypto->isChecked()); - updateRegenButton(); - } else { - m_pLabelLogPath->setEnabled(enable); - m_pLineEditLogFilename->setEnabled(enable); - m_pButtonBrowseLog->setEnabled(enable); - m_pLabelKeyLength->setEnabled(enable); - m_pComboBoxKeyLength->setEnabled(enable); - m_pLabelCertificate->setEnabled(enable); - m_pLineEditCertificatePath->setEnabled(enable); - m_pPushButtonBrowseCert->setEnabled(enable); - m_pPushButtonRegenCert->setEnabled(enable); + m_pCheckBoxLanguageSync->setEnabled(writable && isClientMode()); + m_pCheckBoxScrollDirection->setEnabled(writable && isClientMode()); + +#if !defined(Q_OS_WIN) + m_pCheckBoxServiceEnabled->setEnabled(false); +#endif + + m_pCheckBoxClientHostMode->setEnabled( + writable && isClientMode() && appConfig().initiateConnectionFromServer()); + m_pCheckBoxServerClientMode->setEnabled( + writable && !isClientMode() && + appConfig().initiateConnectionFromServer()); + + m_pLabelLogPath->setEnabled(writable && m_pCheckBoxLogToFile->isChecked()); + m_pLineEditLogFilename->setEnabled( + writable && m_pCheckBoxLogToFile->isChecked()); + m_pButtonBrowseLog->setEnabled(writable && m_pCheckBoxLogToFile->isChecked()); + + updateTlsControlsEnabled(); + + if (writable) { + updateTlsRegenerateButton(); } + +#if defined(Q_OS_WIN) + m_pComboElevate->setCurrentIndex(static_cast(appConfig().elevateMode())); +#else + // elevate checkbox is only usable on ms windows. + m_pLabelElevate->setEnabled(false); + m_pComboElevate->setEnabled(false); +#endif + + updateTlsControls(); } -void SettingsDialog::onChange() { - buttonBox->button(QDialogButtonBox::Save)->setEnabled(isModified()); +void SettingsDialog::on_m_pScreenNameValidator_finished(const QString &error) { + m_nameError = error; } diff --git a/src/gui/src/SettingsDialog.h b/src/gui/src/SettingsDialog.h index d0af1287d..e1917dede 100644 --- a/src/gui/src/SettingsDialog.h +++ b/src/gui/src/SettingsDialog.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) * * This package is free software; you can redistribute it and/or @@ -16,15 +16,15 @@ * along with this program. If not, see . */ -#if !defined(SETTINGSDIALOG_H) - -#define SETTINGSDIALOG_H +#pragma once #include "ui_SettingsDialogBase.h" -#include -#include #include "CoreInterface.h" +#include "validators/ScreenNameValidator.h" + +#include +#include class MainWindow; class AppConfig; @@ -35,49 +35,46 @@ class SettingsDialog : public QDialog, public Ui::SettingsDialogBase { public: SettingsDialog(QWidget *parent, AppConfig &config); static QString browseForSynergyc( - QWidget *parent, const QString &programDir, const QString &synergycName); + QWidget *parent, const QString &programDir, + const QString &coreClientName); static QString browseForSynergys( - QWidget *parent, const QString &programDir, const QString &synergysName); + QWidget *parent, const QString &programDir, + const QString &coreServerName); protected: void accept() override; void reject() override; AppConfig &appConfig() { return m_appConfig; } - /// @brief Causes the dialog to load all the settings from m_appConfig + /// @brief Load all settings. void loadFromConfig(); - /// @brief Check if the regenerate button should be enabled or disabled and - /// sets it - void updateRegenButton(); + /// @brief Enables or disables the TLS regenerate button. + void updateTlsRegenerateButton(); - /// @brief Updates the key length value based on the loaded file - /// @param [in] QString path The path to the file to test + /// @brief Updates the key length value based on the loaded file. void updateKeyLengthOnFile(const QString &path); - /// @brief Check if there are modifications. - /// @return true if there are modifications. - bool isModified(); + /// @brief Enables controls when they should be. + void updateControlsEnabled(); - /// @brief Enables\disables all controls. - void enableControls(bool enabled); - - /// @brief This method setups security section in setting - void setupSeurity(); - - /// @brief Returns true if current mode is a client mode bool isClientMode() const; + void updateTlsControls(); + void updateTlsControlsEnabled(); private: MainWindow *m_pMainWindow; AppConfig &m_appConfig; CoreInterface m_CoreInterface; + std::unique_ptr m_screenNameValidator; /// @brief Stores settings scope at start of settings dialog /// This is neccessary to restore state if user changes /// the scope and doesn't save changes bool m_isSystemAtStart = false; + QString m_nameError = ""; + private slots: void on_m_pCheckBoxEnableCrypto_clicked(bool checked); void on_m_pCheckBoxLogToFile_stateChanged(int); @@ -100,8 +97,5 @@ private slots: /// haven't changed void on_m_pPushButtonRegenCert_clicked(); - /// @brief This slot handles common functionality for all fields. - void onChange(); + void on_m_pScreenNameValidator_finished(const QString &error); }; - -#endif diff --git a/src/gui/src/SettingsDialogBase.ui b/src/gui/src/SettingsDialogBase.ui index a92485191..5d9bfda30 100644 --- a/src/gui/src/SettingsDialogBase.ui +++ b/src/gui/src/SettingsDialogBase.ui @@ -6,8 +6,8 @@ 0 0 - 404 - 819 + 417 + 786 @@ -38,64 +38,6 @@ 5 - - - - - 0 - 0 - - - - Security - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 3 - - - - - - - - - 0 - 0 - - - - Advanced - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 3 - - - - @@ -181,7 +123,7 @@ background-color: rgba(192,192,192, 0.1); - &Level + Level m_pComboLogLevel @@ -285,8 +227,213 @@ background-color: rgba(192,192,192, 0.1); - - + + + + .QFrame{ +border: 1px solid rgba(192,192,192, 0.2); +border-radius: 4px; +background-color: rgba(192,192,192, 0.1); +} + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 17 + + + 17 + + + + + + + + 0 + 0 + + + + + 75 + 0 + + + + Computer &name + + + m_pLineEditScreenName + + + + + + + Qt::Horizontal + + + QSizePolicy::Maximum + + + + 20 + 20 + + + + + + + + true + + + 255 + + + + + + + + + 0 + + + + + + 0 + 0 + + + + P&ort + + + m_pSpinBoxPort + + + + + + + true + + + + 0 + 0 + + + + 65535 + + + 24800 + + + + + + + + 0 + 0 + + + + Network IP + + + m_pLineEditInterface + + + + + + + true + + + + + + + + + 0 + + + + + Minimize to tray on close + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Qt::LeftToRight + + + Minimize to system &tray + + + + + + + + + Hide window on start + + + + + + + + 0 @@ -294,12 +441,12 @@ background-color: rgba(192,192,192, 0.1); - Logs + Security - - + + Qt::Vertical @@ -337,104 +484,125 @@ background-color: rgba(192,192,192, 0.1); QFrame::Raised - + - Invert scroll direction on this computer + Invert scroll direction on this computer (client mode) - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Host mode - - - - - - - Specify when the Synergy service should run at an elevated privilege level - - - 0 - - - - As Needed - - - - - Always - - - - - Never - - - - - + - &Use server's keyboard language on this machine + Use server's keyboard language on this computer (client mode) - - + + - Client mode + Use background service (daemon) - - - - - 0 - 0 - - + + + + + + + 0 + 0 + + + + Elevate privileges (service required) + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Specify when the Synergy service should run at an elevated privilege level + + + 0 + + + + As Needed + + + + + Always + + + + + Never + + + + + + + + - Elevate privileges + Stop this computer from sleeping + + + + QLayout::SetNoConstraint + + + + + Use server as a client + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Use client in host mode + + + + + - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 3 - - - - @@ -472,7 +640,7 @@ background-color: rgba(192,192,192, 0.1); true - Enable &TLS encryption + Enable TLS encryption @@ -628,6 +796,61 @@ background-color: rgba(192,192,192, 0.1); + + + + + 0 + 0 + + + + Advanced + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + Logs + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 3 + + + + @@ -654,13 +877,6 @@ background-color: rgba(192,192,192, 0.1); 6 - - - - Current user - - - @@ -671,33 +887,18 @@ background-color: rgba(192,192,192, 0.1); - - - - false - - - - 0 - 0 - - - - - 50 - false - - + + - Note: Only Admins can edit settings for all users. + Current user - - + + Qt::Horizontal @@ -706,56 +907,8 @@ background-color: rgba(192,192,192, 0.1); - - - - 0 - - - 1 - - - 0 - - - 6 - - - - - false - - - - 0 - 0 - - - - Enable Auto Config - - - - - - - - 0 - 0 - - - - <html><head/><body><p><a href="#"><span style=" text-decoration: underline; color:#007af4;">Install Bonjour</span></a></p></body></html> - - - Qt::RichText - - - - - - - + + Qt::Vertical @@ -770,244 +923,56 @@ background-color: rgba(192,192,192, 0.1); - - - - .QFrame{ -border: 1px solid rgba(192,192,192, 0.2); -border-radius: 4px; -background-color: rgba(192,192,192, 0.1); -} - + + + + Qt::Vertical - - QFrame::StyledPanel + + QSizePolicy::Fixed - - QFrame::Raised + + + 20 + 3 + - - - 17 - - - 17 - - - - - - - - 0 - 0 - - - - - 75 - 0 - - - - Computer name - - - m_pLineEditScreenName - - - - - - - Qt::Horizontal - - - QSizePolicy::Maximum - - - - 20 - 20 - - - - - - - - true - - - 255 - - - - - - - - - - 0 - 0 - - - - color: #EC4C47; -font-size: 13px; -font-family: Arial; -font-weight: bold; - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 0 - - - - - - 0 - 0 - - - - P&ort - - - m_pSpinBoxPort - - - - - - - true - - - - 0 - 0 - - - - 65535 - - - 24800 - - - - - - - - 0 - 0 - - - - Network IP - - - m_pLineEditInterface - - - - - - - true - - - - - - - - - 0 - - - - - &Hide on startup - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Qt::LeftToRight - - - Minimize to system &tray - - - - - - - - - Stop this computer from sleeping - - - - - + - m_pCheckBoxAutoConfig + m_pLineEditScreenName + m_pSpinBoxPort + m_pLineEditInterface + m_pCheckBoxMinimizeOnClose + m_pCheckBoxMinimizeToTray + m_pCheckBoxAutoHide + m_pCheckBoxEnableCrypto + m_pComboBoxKeyLength + m_pLineEditCertificatePath + m_pPushButtonBrowseCert + m_pPushButtonRegenCert + m_pCheckBoxLogToFile + m_pComboLogLevel + m_pLineEditLogFilename + m_pButtonBrowseLog + m_pRadioUserScope + m_pRadioSystemScope + m_pCheckBoxPreventSleep + m_pCheckBoxServiceEnabled + m_pComboElevate + m_pCheckBoxScrollDirection + m_pCheckBoxLanguageSync + m_pCheckBoxServerClientMode + m_pCheckBoxClientHostMode - buttonBox + m_pButtonBox accepted() SettingsDialogBase accept() @@ -1023,7 +988,7 @@ font-weight: bold; - buttonBox + m_pButtonBox rejected() SettingsDialogBase reject() diff --git a/src/gui/src/SslCertificate.cpp b/src/gui/src/SslCertificate.cpp index 98224cde8..5cec5608c 100644 --- a/src/gui/src/SslCertificate.cpp +++ b/src/gui/src/SslCertificate.cpp @@ -23,21 +23,72 @@ #include #include -static const char kCertificateKeyLength[] = - "rsa:"; // RSA Bit length (e.g. 1024/2048/4096) -static const char kCertificateHashAlgorithm[] = - "-sha256"; // fingerprint hashing algorithm -static const char kCertificateLifetime[] = "365"; -static const char kCertificateSubjectInfo[] = "/CN=Synergy"; -static const char kCertificateFilename[] = "Synergy.pem"; -static const char kSslDir[] = "SSL"; -static const char kUnixOpenSslCommand[] = "openssl"; +// RSA Bit length (e.g. 1024/2048/4096) +static const char *const kCertificateKeyLength = "rsa:"; + +// fingerprint hashing algorithm +static const char *const kCertificateHashAlgorithm = "-sha256"; + +static const char *const kCertificateLifetime = "365"; +static const char *const kCertificateSubjectInfo = "/CN=Synergy"; +static const char *const kCertificateFilename = "Synergy.pem"; +static const char *const kSslDir = "SSL"; #if defined(Q_OS_WIN) -static const char kWinOpenSslBinary[] = "OpenSSL\\openssl.exe"; -static const char kConfigFile[] = "OpenSSL\\synergy.conf"; +static const char *const kWinOpenSslDir = "OpenSSL"; +static const char *const kWinOpenSslBinary = "openssl.exe"; +static const char *const kConfigFile = "synergy.conf"; +#elif defined(Q_OS_UNIX) +static const char *const kUnixOpenSslCommand = "openssl"; #endif +namespace synergy::gui { +#if defined(Q_OS_WIN) + +QString openSslWindowsDir() { + + auto appDir = QDir(QCoreApplication::applicationDirPath()); + auto openSslDir = QDir(appDir.filePath(kWinOpenSslDir)); + + // in production, openssl is deployed with the app. + // in development, we can use the openssl path available at compile-time. + if (!openSslDir.exists()) { + openSslDir = QDir(OPENSSL_PATH); + } + + // if the path still isn't found, something is seriously wrong. + if (!openSslDir.exists()) { + qFatal() << "OpenSSL dir not found: " << openSslDir; + } + + return QDir::cleanPath(openSslDir.absolutePath()); +} + +QString openSslWindowsBinary() { + auto dir = QDir(openSslWindowsDir()); + auto path = dir.filePath(kWinOpenSslBinary); + + // when installed, there is no openssl bin dir; it's installed at the base. + // in development, we use the standard dir structure for openssl (bin dir). + if (!QFile::exists(path)) { + auto binDir = QDir(dir.filePath("bin")); + path = binDir.filePath(kWinOpenSslBinary); + } + + // if the path still isn't found, something is seriously wrong. + if (!QFile::exists(path)) { + qFatal() << "OpenSSL binary not found: " << path; + } + + return path; +} + +#endif + +} // namespace synergy::gui + +using namespace synergy::gui; + SslCertificate::SslCertificate(QObject *parent) : QObject(parent) { m_ProfileDir = m_CoreInterface.getProfileDir(); if (m_ProfileDir.isEmpty()) { @@ -48,17 +99,16 @@ SslCertificate::SslCertificate(QObject *parent) : QObject(parent) { bool SslCertificate::runTool(const QStringList &args) { QString program; #if defined(Q_OS_WIN) - program = QCoreApplication::applicationDirPath(); - program.append("\\").append(kWinOpenSslBinary); + program = openSslWindowsBinary(); #else program = kUnixOpenSslCommand; #endif QStringList environment; #if defined(Q_OS_WIN) - environment << QString("OPENSSL_CONF=%1\\%2") - .arg(QCoreApplication::applicationDirPath()) - .arg(kConfigFile); + auto openSslDir = QDir(openSslWindowsDir()); + auto config = QDir::cleanPath(openSslDir.filePath(kConfigFile)); + environment << QString("OPENSSL_CONF=%1").arg(config); #endif QProcess process; @@ -73,8 +123,7 @@ bool SslCertificate::runTool(const QStringList &args) { standardError = process.readAllStandardError().trimmed(); } - int code = process.exitCode(); - if (!success || code != 0) { + if (int code = process.exitCode(); !success || code != 0) { emit error(QString("SSL tool failed: %1\n\nCode: %2\nError: %3") .arg(program) .arg(process.exitCode()) @@ -99,9 +148,7 @@ void SslCertificate::generateCertificate( const QString pathToUse = path.isEmpty() ? filename : path; - // If path is empty use filename - QFile file(pathToUse); - if (!file.exists() || forceGen) { + if (QFile file(pathToUse); !file.exists() || forceGen) { QStringList arguments; // self signed certificate @@ -123,8 +170,7 @@ void SslCertificate::generateCertificate( arguments.append("-newkey"); arguments.append(keySize); - QDir sslDir(sslDirPath); - if (!sslDir.exists()) { + if (QDir sslDir(sslDirPath); !sslDir.exists()) { sslDir.mkpath("."); } @@ -161,7 +207,7 @@ void SslCertificate::generateFingerprint(const QString &certificateFilename) { } // find the fingerprint from the tool output - int i = m_ToolOutput.indexOf("="); + auto i = m_ToolOutput.indexOf("="); if (i != -1) { i++; QString fingerprint = m_ToolOutput.mid(i, m_ToolOutput.size() - i); diff --git a/src/gui/src/main.cpp b/src/gui/src/main.cpp index 1b8d6e1f4..3626475af 100644 --- a/src/gui/src/main.cpp +++ b/src/gui/src/main.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) * * This package is free software; you can redistribute it and/or @@ -35,6 +35,8 @@ #include #endif +using namespace synergy::gui; + class QThreadImpl : public QThread { public: static void msleep(unsigned long msecs) { QThread::msleep(msecs); } @@ -53,10 +55,6 @@ int main(int argc, char *argv[]) { ::setenv("QT_BEARER_POLL_TIMEOUT", "-1", 1); #endif -#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); -#endif - QCoreApplication::setOrganizationName("Synergy"); QCoreApplication::setOrganizationDomain("http://symless.com/"); QCoreApplication::setApplicationName("Synergy"); @@ -78,17 +76,11 @@ int main(int argc, char *argv[]) { } #endif -#ifndef Q_OS_WIN - QApplication::setQuitOnLastWindowClosed(false); -#endif - AppConfig appConfig; qRegisterMetaType("Edition"); -#ifdef SYNERGY_ENABLE_LICENSING - LicenseManager licenseManager(&appConfig); -#endif #ifdef SYNERGY_ENABLE_LICENSING + LicenseManager licenseManager(&appConfig); MainWindow mainWindow(appConfig, licenseManager); #else MainWindow mainWindow(appConfig); @@ -114,7 +106,7 @@ int main(int argc, char *argv[]) { mainWindow.open(); } - return app.exec(); + return QSynergyApplication::exec(); } #if defined(Q_OS_MAC) diff --git a/src/gui/src/validators/LineEditValidator.cpp b/src/gui/src/validators/LineEditValidator.cpp index 7604437c4..d0192bed6 100644 --- a/src/gui/src/validators/LineEditValidator.cpp +++ b/src/gui/src/validators/LineEditValidator.cpp @@ -33,19 +33,27 @@ void LineEditValidator::addValidator( } QValidator::State LineEditValidator::validate(QString &input, int &pos) const { - if (m_pControl) { - showError(""); - m_pControl->setStyleSheet(""); + if (!m_pControl) { + qFatal("Validator control not set"); + return Invalid; + } - for (const auto &validator : m_Validators) { - if (!validator->validate(input)) { - m_pControl->setStyleSheet("border: 1px solid #EC4C47"); - showError(validator->getMessage()); - break; - } + QString error; + for (const auto &validator : m_Validators) { + if (!validator->validate(input)) { + error = validator->getMessage(); + break; } } + if (error.isEmpty()) { + m_pControl->setStyleSheet(""); + } else { + showError(error); + m_pControl->setStyleSheet("border: 1px solid #EC4C47"); + } + + finished(error); return Acceptable; } diff --git a/src/gui/src/validators/LineEditValidator.h b/src/gui/src/validators/LineEditValidator.h index c7c87aa0e..b82af7e55 100644 --- a/src/gui/src/validators/LineEditValidator.h +++ b/src/gui/src/validators/LineEditValidator.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2021 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) * * This package is free software; you can redistribute it and/or @@ -15,27 +15,31 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef LINEEDITVALIDATOR_H -#define LINEEDITVALIDATOR_H -#include -#include - -#include -#include -#include +#pragma once #include "IStringValidator.h" +#include +#include +#include +#include +#include + namespace validators { class LineEditValidator : public QValidator { + Q_OBJECT + public: explicit LineEditValidator( QLineEdit *parent = nullptr, QLabel *errors = nullptr); QValidator::State validate(QString &input, int &pos) const override; void addValidator(std::unique_ptr validator); +signals: + void finished(const QString &message) const; + private: QLabel *m_pErrors = nullptr; QLineEdit *m_pControl = nullptr; @@ -45,5 +49,3 @@ private: }; } // namespace validators - -#endif // LINEEDITVALIDATOR_H diff --git a/src/lib/server/Config.cpp b/src/lib/server/Config.cpp index 2ce3edddf..37dbac3ce 100644 --- a/src/lib/server/Config.cpp +++ b/src/lib/server/Config.cpp @@ -558,9 +558,9 @@ String Config::formatInterval(const Interval &x) { (int)(x.second * 100.0f + 0.5f)); } -String Config::getClientAddress() const { return m_clientAddress; } +String Config::getClientAddress() const { return m_ClientAddress; } -bool Config::isClientMode() const { return (!m_clientAddress.empty()); } +bool Config::isClientMode() const { return (!m_ClientAddress.empty()); } void Config::readSection(ConfigReadContext &s) { static const char s_section[] = "section:"; @@ -659,7 +659,7 @@ void Config::readSectionOptions(ConfigReadContext &s) { } else if (name == "clipboardSharingSize") { addOption("", kOptionClipboardSharingSize, s.parseInt(value)); } else if (name == "clientAddress") { - m_clientAddress = value; + m_ClientAddress = value; } else { handled = false; } diff --git a/src/lib/server/Config.h b/src/lib/server/Config.h index bee6692e1..f3149f590 100644 --- a/src/lib/server/Config.h +++ b/src/lib/server/Config.h @@ -479,7 +479,7 @@ private: InputFilter m_inputFilter; bool m_hasLockToScreenAction; IEventQueue *m_events; - String m_clientAddress; + String m_ClientAddress; }; //! Configuration read context diff --git a/src/test/integtests/gui/MainWindowTests.cpp b/src/test/integtests/gui/MainWindowTests.cpp index ec25d9f6b..4f2c621ec 100644 --- a/src/test/integtests/gui/MainWindowTests.cpp +++ b/src/test/integtests/gui/MainWindowTests.cpp @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -// TODO: fix randomly freezing on windows +// TODO: fix test freezing only on windows #ifndef WIN32 #include "MainWindow.h" @@ -51,18 +51,16 @@ public: }; TestMainWindow() { - m_appConfig = std::make_shared(false); - #ifdef SYNERGY_ENABLE_LICENSING - m_licenseManager = std::make_shared(m_appConfig.get()); + m_licenseManager = std::make_shared(&m_appConfig); m_mainWindow = - std::make_shared(*m_appConfig, *m_licenseManager); + std::make_shared(m_appConfig, *m_licenseManager); #else - m_mainWindow = std::make_shared(*m_appConfig); + m_mainWindow = std::make_shared(m_appConfig); #endif } - std::shared_ptr m_appConfig; + AppConfig m_appConfig; std::shared_ptr m_licenseManager; std::shared_ptr m_mainWindow; }; diff --git a/src/test/integtests/main.cpp b/src/test/integtests/main.cpp index 7052dd96a..b46889f8b 100644 --- a/src/test/integtests/main.cpp +++ b/src/test/integtests/main.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or @@ -18,21 +18,19 @@ #include "arch/Arch.h" #include "base/Log.h" +#include "shared/ExitTimeout.h" #if SYSAPI_WIN32 #include "arch/win32/ArchMiscWindows.h" #endif -#include #include -#include -#define LOCK_TIMEOUT 30 - -void lock(const std::string &lockFile); -void unlock(const std::string &lockFile); +using synergy::test::ExitTimeout; int main(int argc, char **argv) { + ExitTimeout exitTimeout(1, "Integration tests"); + #if SYSAPI_WIN32 // record window instance for tray icon, etc ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL)); @@ -44,53 +42,10 @@ int main(int argc, char **argv) { Log log; log.setFilter(kDEBUG2); - std::string lockFile; - for (int i = 0; i < argc; i++) { - const std::string option(argv[i]); - if (option.find("--lock-file") != std::string::npos) { - lockFile = argv[i + 1]; - } - } - - if (!lockFile.empty()) { - lock(lockFile); - } - ::testing::GTEST_FLAG(throw_on_failure) = true; testing::InitGoogleTest(&argc, argv); - int result = RUN_ALL_TESTS(); - - if (!lockFile.empty()) { - unlock(lockFile); - } - // return code 1 means the test failed. // any other non-zero code is probably a memory error. - return result; + return RUN_ALL_TESTS(); } - -void lock(const std::string &lockFile) { - double start = ARCH->time(); - - // keep checking until timeout is reached. - while ((ARCH->time() - start) < LOCK_TIMEOUT) { - - std::ifstream is(lockFile.c_str()); - bool noLock = !is; - is.close(); - - if (noLock) { - break; - } - - // check every second if file has gone. - ARCH->sleep(1); - } - - // write empty lock file. - std::ofstream os(lockFile.c_str()); - os.close(); -} - -void unlock(const std::string &lockFile) { remove(lockFile.c_str()); } diff --git a/src/test/shared/ExitTimeout.cpp b/src/test/shared/ExitTimeout.cpp new file mode 100644 index 000000000..3aa33e8f3 --- /dev/null +++ b/src/test/shared/ExitTimeout.cpp @@ -0,0 +1,61 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 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 . + */ + +#include "ExitTimeout.h" + +#include +#include +#include +#include +#include + +namespace synergy::test { + +const auto checkMilliseconds = std::chrono::milliseconds(100); + +using std::chrono::duration_cast; +using std::chrono::steady_clock; + +bool timeoutReached(const steady_clock::time_point &start, const int minutes) { + auto now = steady_clock::now(); + auto duration = duration_cast(now - start); + return duration.count() >= minutes; +} + +ExitTimeout::ExitTimeout(const int minutes, const std::string_view &name) + : m_minutes(minutes), + m_name(name), + m_thread(std::make_unique([this]() { run(); })) {} + +ExitTimeout::~ExitTimeout() { + m_running = false; + m_thread->join(); +} + +void ExitTimeout::run() const { + auto start = steady_clock::now(); + while (m_running) { + std::this_thread::sleep_for(checkMilliseconds); + if (timeoutReached(start, m_minutes)) { + std::cerr << m_name << " timed out after " << m_minutes << " minute(s)" + << std::endl; + std::exit(EXIT_FAILURE); + } + } +} + +} // namespace synergy::test diff --git a/src/test/shared/ExitTimeout.h b/src/test/shared/ExitTimeout.h new file mode 100644 index 000000000..1f9c71536 --- /dev/null +++ b/src/test/shared/ExitTimeout.h @@ -0,0 +1,47 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 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 . + */ + +#pragma once + +#include +#include + +namespace synergy::test { + +/** + * @brief Exits the program after a specified timeout, unless destroyed. + * + * The `std::thread` class is used instead of `std::jthread` as Apple Clang has + * no `std::jthread` support: https://en.cppreference.com/w/cpp/compiler_support + * + * TODO: Switch to regular Clang instead of Apple Clang: + * https://symless.atlassian.net/browse/S1-1754 + */ +class ExitTimeout { +public: + ExitTimeout(const int minutes, const std::string_view &name); + ~ExitTimeout(); + void run() const; + +private: + bool m_running = true; + int m_minutes = 0; + std::string_view m_name; + std::unique_ptr m_thread; +}; + +} // namespace synergy::test diff --git a/src/test/unittests/main.cpp b/src/test/unittests/main.cpp index 86b4ad7d9..cff778469 100644 --- a/src/test/unittests/main.cpp +++ b/src/test/unittests/main.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or @@ -18,15 +18,19 @@ #include "arch/Arch.h" #include "base/Log.h" +#include "shared/ExitTimeout.h" #if SYSAPI_WIN32 #include "arch/win32/ArchMiscWindows.h" #endif -#include #include +using synergy::test::ExitTimeout; + int main(int argc, char **argv) { + ExitTimeout exitTimeout(1, "Integration tests"); + #if SYSAPI_WIN32 // HACK: shouldn't be needed, but logging fails without this. ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));